about summary refs log tree commit diff
path: root/pkgs/games
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/games')
-rw-r--r--pkgs/games/build-support/default.nix1
-rw-r--r--pkgs/games/build-support/monogame-patcher/default.nix31
-rw-r--r--pkgs/games/build-support/monogame-patcher/patcher.cs109
-rw-r--r--pkgs/games/itch/towerfall-ascension.nix100
4 files changed, 147 insertions, 94 deletions
diff --git a/pkgs/games/build-support/default.nix b/pkgs/games/build-support/default.nix
index 8e227afc..1832f987 100644
--- a/pkgs/games/build-support/default.nix
+++ b/pkgs/games/build-support/default.nix
@@ -5,4 +5,5 @@
     withPulseAudio = config.pulseaudio or true;
   };
   buildUnity = callPackage ./build-unity.nix {};
+  monogamePatcher = callPackage ./monogame-patcher {};
 }
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;
+    }
+}
diff --git a/pkgs/games/itch/towerfall-ascension.nix b/pkgs/games/itch/towerfall-ascension.nix
index a4ba5abf..21704211 100644
--- a/pkgs/games/itch/towerfall-ascension.nix
+++ b/pkgs/games/itch/towerfall-ascension.nix
@@ -1,94 +1,11 @@
 { stdenv, lib, buildGame, fetchItch, makeWrapper, p7zip, unzip, mono50
-, SDL2, SDL2_image, libGL, libvorbis, openal
-
-, writeScriptBin, coreutils
-, fetchNuGet, writeText
+, SDL2, SDL2_image, libGL, libvorbis, openal, monogamePatcher, writeScriptBin
+, coreutils
 
 , darkWorldExpansion ? true
 }:
 
-let
-  cecil = fetchNuGet {
-    baseName = "Mono.Cecil";
-    version = "0.10-beta7";
-    sha256 = "03bina3llcnylrfrvp5psnwrfn757j7zch5r360rpdn7gmcjjcpl";
-    outputFiles = [ "lib/net40/*" ];
-  };
-
-  # We need to patch a few occurences of System.IO.FileStream which are used
-  # with the default arguments for FileAccess. The defaults open the file in
-  # read-write mode and given that all the game data files are in the read-only
-  # Nix store, we'd get an UnauthorizedAccessException.
-  patcher = writeText "patcher.cs" ''
-    using System.Linq;
-    using System.Collections.Generic;
-    using Mono.Cecil;
-    using Mono.Cecil.Cil;
-    using Mono.Cecil.Rocks;
-
-    public class patcher {
-      private ModuleDefinition module;
-
-      private patcher(List<string> typesToPatch) {
-        this.module = ModuleDefinition.ReadModule("TowerFall.exe");
-
-        var filtered = this.module.Types
-          .Where(p => typesToPatch.Contains(p.Name));
-
-        foreach (var toPatch in filtered) {
-          patch_class(toPatch);
-        }
-
-        this.module.Write("TowerFallPatched.exe");
-      }
-
-      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_class(TypeDefinition td) {
-        foreach (var nested in td.NestedTypes) patch_class(nested);
-        foreach (MethodDefinition md in td.Methods) patch_method(md);
-      }
-
-      public static void Main() {
-        var typesToPatch = new List<string>() {
-          "Texture", "IntroScene", "SFX", "SFXVaried"
-        };
-        new patcher(typesToPatch);
-      }
-    }
-  '';
-
-in buildGame rec {
+buildGame rec {
   name = "towerfall-ascension-${version}";
   version = "20160723";
 
@@ -113,16 +30,11 @@ in buildGame rec {
   '';
 
   patchPhase = ''
-    { export MONO_PATH=${cecil}/lib/dotnet/Mono.Cecil
-      mcs ${lib.escapeShellArg patcher} -out:patcher \
-        -lib:${cecil}/lib/dotnet/Mono.Cecil \
-        -r:Mono.Cecil -r:Mono.Cecil.Rocks
-      mono patcher
-      mv TowerFallPatched.exe TowerFall.exe
-    }
+    monogame-patcher fix-filestreams -i TowerFall.exe \
+      Texture IntroScene SFX SFXVaried
   '';
 
-  nativeBuildInputs = [ makeWrapper mono50 ];
+  nativeBuildInputs = [ makeWrapper mono50 monogamePatcher ];
 
   libdir = if stdenv.system == "x86_64-linux" then "lib64" else "lib";