diff options
author | aszlig <aszlig@nix.build> | 2018-07-16 07:09:38 +0200 |
---|---|---|
committer | aszlig <aszlig@nix.build> | 2018-07-19 06:35:06 +0200 |
commit | c9dfe485e9b922b35c9412e6c4fdbe119f17f4b5 (patch) | |
tree | 96180ef3647607a6eb764bfe2aa4c48d0fe0999e /pkgs/games/build-support/monogame-patcher | |
parent | bc0be07dd9920c8dfc16be8f5bdb269bc9e3f986 (diff) |
towerfall-ascension: Move patcher into own pkg
The patcher using Cecil is now in its own derivation and can thus be easily added via nativeBuildInputs. Patching FileStream types is so common that it comes in handy for other games using Mono. I also improved the patcher a little bit so it accepts command line arguments and it's easier to add the types that needed to be patched directly via command line arguments. Signed-off-by: aszlig <aszlig@nix.build>
Diffstat (limited to 'pkgs/games/build-support/monogame-patcher')
-rw-r--r-- | pkgs/games/build-support/monogame-patcher/default.nix | 31 | ||||
-rw-r--r-- | pkgs/games/build-support/monogame-patcher/patcher.cs | 109 |
2 files changed, 140 insertions, 0 deletions
diff --git a/pkgs/games/build-support/monogame-patcher/default.nix b/pkgs/games/build-support/monogame-patcher/default.nix new file mode 100644 index 00000000..fffa6825 --- /dev/null +++ b/pkgs/games/build-support/monogame-patcher/default.nix @@ -0,0 +1,31 @@ +{ lib, runCommand, makeWrapper, fetchNuGet, mono, dotnetPackages }: + +runCommand "monogame-patcher" { + nativeBuildInputs = [ mono makeWrapper ]; + + src = ./patcher.cs; + + cecil = "${fetchNuGet { + baseName = "Mono.Cecil"; + version = "0.10-beta7"; + sha256 = "03bina3llcnylrfrvp5psnwrfn757j7zch5r360rpdn7gmcjjcpl"; + outputFiles = [ "lib/net40/*" ]; + }}/lib/dotnet/Mono.Cecil"; + + cliparser = "${fetchNuGet { + baseName = "CommandLineParser"; + version = "2.2.1"; + sha256 = "0wf8mzr16d2ni008m60rrk738v8ypk74llk6g8mlyx7rrlchnxaf"; + outputFiles = [ "lib/net45/*" ]; + }}/lib/dotnet/CommandLineParser"; + +} '' + mkdir -p "$out/bin" "$out/libexec/monogame-patcher" + mcs "$src" -out:"$out/libexec/monogame-patcher/patcher.exe" \ + -lib:"$cecil" -lib:"$cliparser" \ + -r:Mono.Cecil -r:Mono.Cecil.Rocks -r:CommandLine + + makeWrapper ${lib.escapeShellArg "${mono}/bin/mono"} "$out/bin/$name" \ + --set MONO_PATH "$cecil:$cliparser" \ + --add-flags "$out/libexec/monogame-patcher/patcher.exe" +'' diff --git a/pkgs/games/build-support/monogame-patcher/patcher.cs b/pkgs/games/build-support/monogame-patcher/patcher.cs new file mode 100644 index 00000000..16878831 --- /dev/null +++ b/pkgs/games/build-support/monogame-patcher/patcher.cs @@ -0,0 +1,109 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; + +using Mono.Cecil.Cil; +using Mono.Cecil.Rocks; +using Mono.Cecil; + +using CommandLine; + +class GenericOptions +{ + [Option('i', "infile", Required=true, HelpText="Input file to transform.")] + public string inputFile { get; set; } + [Option('o', "outfile", HelpText="File to write transformed data to.")] + public string outputFile { get; set; } +} + +class Command { + protected string infile; + protected string outfile; + protected ModuleDefinition module; + + public Command(GenericOptions options) { + if (options.outputFile == null) + this.outfile = options.inputFile; + else + this.outfile = options.outputFile; + this.infile = options.inputFile; + + var rp = new ReaderParameters { + ReadWrite = this.infile == this.outfile + }; + this.module = ModuleDefinition.ReadModule(this.infile, rp); + } + + public void save() { + if (this.outfile == this.infile) + this.module.Write(); + else + this.module.Write(this.outfile); + } +} + +[Verb("fix-filestreams", HelpText="Fix System.IO.FileStream constructors" + +" to open files read-only.")] +class FixFileStreamsCmd : GenericOptions { + [Value(0, Required=true, MetaName = "type", HelpText = "Types to patch.")] + public IEnumerable<string> typesToPatch { get; set; } +}; + +class FixFileStreams : Command { + public FixFileStreams(FixFileStreamsCmd options) : base(options) { + var filtered = this.module.Types + .Where(p => options.typesToPatch.Contains(p.Name)); + + foreach (var toPatch in filtered) + patch_type(toPatch); + + this.save(); + } + + private void patch_method(MethodDefinition md) { + var il = md.Body.GetILProcessor(); + + var fileStreams = md.Body.Instructions + .Where(i => i.OpCode == OpCodes.Newobj) + .Where(i => (i.Operand as MethodReference).DeclaringType + .FullName == "System.IO.FileStream"); + + foreach (Instruction i in fileStreams.ToList()) { + var fileAccessRead = il.Create(OpCodes.Ldc_I4_1); + il.InsertBefore(i, fileAccessRead); + + var ctorType = this.module.AssemblyReferences.Select( + x => new { + type = this.module.AssemblyResolver.Resolve(x) + .MainModule.GetType("System.IO.FileStream") + } + ).Where(x => x.type != null).Select(x => x.type).First(); + + string wantedCtor = "System.Void System.IO.FileStream" + + "::.ctor(System.String," + + "System.IO.FileMode," + + "System.IO.FileAccess)"; + + var newCtor = ctorType.GetConstructors() + .Single(x => x.ToString() == wantedCtor); + + var refCtor = this.module.ImportReference(newCtor); + il.Replace(i, il.Create(OpCodes.Newobj, refCtor)); + } + } + + private void patch_type(TypeDefinition td) { + foreach (var nested in td.NestedTypes) patch_type(nested); + foreach (MethodDefinition md in td.Methods) patch_method(md); + } +} + +public class patcher { + + public static int Main(string[] args) { + Parser.Default.ParseArguments<GenericOptions, FixFileStreamsCmd>(args) + .WithParsed<FixFileStreamsCmd>(opts => new FixFileStreams(opts)); + return 0; + } +} |