From 523dcef1f77251a4cfeb3428a0b13c0ec1d9c342 Mon Sep 17 00:00:00 2001 From: aszlig Date: Wed, 13 Dec 2017 22:39:48 +0100 Subject: games/humblebundle: Add Opus Magnum Upstream URL: http://www.zachtronics.com/opus-magnum/ For this I've ripped apart all of the bundled shared objects including Mono itself, so we're using Mono from instead of the provided one. In addition to that I had to patch the intermediate language because the game really doesn't cope very well when the data files are read-only, which is the case in the Nix store. Instead of replacing the CIL, I could have used an LD preloader as well, but I think this is way less error-prone even though we had to patch a few internal classes where we couldn't match the name properly. Compared to this an LD preloader would involve patching several symbols and also implement some logic to distinguish between files read from the data directory and files read/written to XDG_DATA_HOME. The reason why I added $HOME/Desktop to the required sandbox paths is because the game saves gif images into that directory. Signed-off-by: aszlig --- pkgs/games/humblebundle/default.nix | 1 + pkgs/games/humblebundle/opus-magnum.nix | 118 ++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 pkgs/games/humblebundle/opus-magnum.nix (limited to 'pkgs') diff --git a/pkgs/games/humblebundle/default.nix b/pkgs/games/humblebundle/default.nix index cbffeaa0..3ed0b3b6 100644 --- a/pkgs/games/humblebundle/default.nix +++ b/pkgs/games/humblebundle/default.nix @@ -24,6 +24,7 @@ let jamestown = callPackage ./jamestown.nix {}; liads = callPackage ./liads.nix {}; megabytepunch = callPackage_i686 ./megabytepunch.nix {}; + opus-magnum = callPackage ./opus-magnum.nix {}; pico-8 = callPackage ./pico-8.nix {}; rocketbirds = callPackage ./rocketbirds.nix {}; spaz = callPackage ./spaz.nix {}; diff --git a/pkgs/games/humblebundle/opus-magnum.nix b/pkgs/games/humblebundle/opus-magnum.nix new file mode 100644 index 00000000..9b6d27cd --- /dev/null +++ b/pkgs/games/humblebundle/opus-magnum.nix @@ -0,0 +1,118 @@ +{ stdenv, lib, buildGame, fetchHumbleBundle, makeWrapper, mono50 +, SDL2, SDL2_image, SDL2_mixer, libvorbis + +, fetchNuGet, writeText +}: + +let + cecil = fetchNuGet { + baseName = "Mono.Cecil"; + version = "0.10-beta7"; + sha256 = "03bina3llcnylrfrvp5psnwrfn757j7zch5r360rpdn7gmcjjcpl"; + outputFiles = [ "lib/net40/*" ]; + }; + + 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 { + public static void Main() { + var module = ModuleDefinition.ReadModule("Lightning.exe"); + var typesToPatch = new List() { + "#=qpBcj_0KQZcJ0ffkrxQcNZQ==", + "#=qEMEvBoEQXql_zRuLLk1x6w==", + "#=qZzmwsUfWWbRLjboUcCgm9sc9s0f6yVsIVJ2gWSL_c4g=", + "PsdImage" + }; + + var filtered = module.Types.Where(p => typesToPatch.Contains(p.Name)); + foreach (var toPatch in filtered) { + foreach (MethodDefinition md in toPatch.Methods) { + 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 = module.AssemblyReferences.Select( + x => new { + type = 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 = module.ImportReference(newCtor); + il.Replace(i, il.Create(OpCodes.Newobj, refCtor)); + } + } + } + module.Write("LightningPatched.exe"); + } + } + ''; + +in buildGame rec { + name = "opus-magnum-${version}"; + version = "1"; + + src = fetchHumbleBundle { + machineName = "opus_magnum_4vffp_linux_Z9zWf"; + suffix = "zip"; + md5 = "aefe8aae8cd3dd3915be8d8cd6f3705d"; + }; + + arch = if stdenv.system == "x86_64-linux" then "x86_64" else "x86"; + + nativeBuildInputs = [ makeWrapper mono50 cecil ]; + + 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 LightningPatched.exe Lightning.exe + } + ''; + + buildPhase = let + dllmap = { + SDL2 = "${SDL2}/lib/libSDL2.so"; + SDL2_image = "${SDL2_image}/lib/libSDL2_image.so"; + SDL2_mixer = "${SDL2_mixer}/lib/libSDL2_mixer.so"; + libvorbisfile-3 = "${libvorbis}/lib/libvorbisfile.so"; + }; + in lib.concatStrings (lib.mapAttrsToList (dll: target: '' + sed -i -e '/