From 72abbc69b13dcf40bac429147dc18a8b8c8bae7b Mon Sep 17 00:00:00 2001 From: aszlig Date: Sun, 1 Jul 2018 05:32:49 +0200 Subject: games/itch: Add Towerfall Ascension Packaging is a bit similar to what we had to do back then with Opus Magnum (see commit 523dcef1f77251a4cfeb3428a0b13c0ec1d9c342) where some game data files were tried to open in read-write mode. I'm using the same patcher (which is using Mono.Cecil) that I have used for Opus Magnum back then but I've cleaned it up a bit. In the long term I'd try to make the patcher a bit more generic so that it can be used for other Mono-based games, because opening game data files in read-only mode seems to be fairly common among a few other games I haven't fully packaged yet. The game also tries to execute "xdg-open error_log.txt" in the event a uncaught exception occurs, which then fails because no xdg-open is available. So instead, I made a small dummy wrapper which just runs "head" on the error.log instead. Another thing which unfortunately is currently not working is the editor, because it tries to create a directory ("Workshop") within the game's content directory, which - again - doesn't work within the read-only Nix store. Signed-off-by: aszlig --- pkgs/games/itch/towerfall-ascension.nix | 178 ++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 pkgs/games/itch/towerfall-ascension.nix (limited to 'pkgs/games/itch/towerfall-ascension.nix') diff --git a/pkgs/games/itch/towerfall-ascension.nix b/pkgs/games/itch/towerfall-ascension.nix new file mode 100644 index 00000000..a4ba5abf --- /dev/null +++ b/pkgs/games/itch/towerfall-ascension.nix @@ -0,0 +1,178 @@ +{ stdenv, lib, buildGame, fetchItch, makeWrapper, p7zip, unzip, mono50 +, SDL2, SDL2_image, libGL, libvorbis, openal + +, writeScriptBin, coreutils +, fetchNuGet, writeText + +, 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 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() { + "Texture", "IntroScene", "SFX", "SFXVaried" + }; + new patcher(typesToPatch); + } + } + ''; + +in buildGame rec { + name = "towerfall-ascension-${version}"; + version = "20160723"; + + srcs = lib.singleton (fetchItch { + name = "${name}.bin"; + gameId = 22755; + uploadId = 243755; + sha256 = "01ipq3z0c2k4h88r7j17nfp43p5sav12a9syangqm0syflvwqxb6"; + }) ++ lib.optional darkWorldExpansion (fetchItch { + name = "towerfall-darkworld.zip"; + gameId = 24962; + uploadId = 216070; + sha256 = "1nb26m2l74rsnlwv9mv33l2s5n873867k9zypc84sm3iljvrdkmg"; + }); + + unpackCmd = '' + case "$curSrc" in + *.bin) ${p7zip}/bin/7z x "$curSrc" data;; + *.zip) ${unzip}/bin/unzip -qq "$curSrc" -d data;; + *) false;; + esac + ''; + + 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 + } + ''; + + nativeBuildInputs = [ makeWrapper mono50 ]; + + libdir = if stdenv.system == "x86_64-linux" then "lib64" else "lib"; + + buildPhase = let + dllmap = { + SDL2 = "${SDL2}/lib/libSDL2.so"; + SDL2_image = "${SDL2_image}/lib/libSDL2_image.so"; + soft_oal = "${openal}/lib/libopenal.so"; + libvorbisfile-3 = "${libvorbis}/lib/libvorbisfile.so"; + MojoShader = "$out/libexec/towerfall-ascension/libmojoshader.so"; + }; + in lib.concatStrings (lib.mapAttrsToList (dll: target: '' + sed -i -e '/&2 + exit 1 + fi + ''; + + installPhase = '' + mkdir -p "$out/bin" \ + "$out/share/towerfall-ascension" \ + "$out/libexec/towerfall-ascension" + cp -rvt "$out/share/towerfall-ascension" Content + cp -rv mono/config "$out/libexec/towerfall-ascension/TowerFall.exe.config" + cp -rvt "$out/libexec/towerfall-ascension" TowerFall.exe FNA.dll* \ + "$libdir/libmojoshader.so" + ln -s "$out/share/towerfall-ascension/Content" \ + "$out/libexec/towerfall-ascension/Content" + + if [ -e "TowerFall Dark World Expansion" ]; then + cp -rvt "$out/share/towerfall-ascension" \ + "TowerFall Dark World Expansion/DarkWorldContent" + fi + + makeWrapper ${lib.escapeShellArg mono50}/bin/mono \ + "$out/bin/towerfall-ascension" \ + --set SDL_OPENGL_LIBRARY ${lib.escapeShellArg "${libGL}/lib/libGL.so"} \ + --set PATH "$dummyXdgOpen/bin" \ + --add-flags "$out/libexec/towerfall-ascension/TowerFall.exe" \ + --run "cd '$out/share/towerfall-ascension'" + ''; + + sandbox.paths.required = [ "$XDG_DATA_HOME/TowerFall" ]; +} -- cgit 1.4.1