about summary refs log tree commit diff
path: root/pkgs/games
diff options
context:
space:
mode:
authoraszlig <aszlig@nix.build>2018-07-14 22:48:31 +0200
committeraszlig <aszlig@nix.build>2018-07-15 10:34:09 +0200
commit8e5d3291b2aa3901c6dc77a982450667d930dc86 (patch)
tree5c8963ea351c4a1e3735628302818dfe4a1769d4 /pkgs/games
parent8005779640207a46089476721ce1a5441b579478 (diff)
games/bastion: Refactor Nix expression and update
With fetchHumbleBundle finally being able to fetch the game with my
account, I think it's time to update the Nix expression. :-)

The game's binary is basically nothing more than just a wrapped Mono, so
we can use directly Mono from <nixpkgs> instead of patching the wrapper.

I also stubbed out the symbols for libsteam_api.so, which might be a
good idea to provide for other games as well but in a more generic way.

Another thing that was needed solely because of my stubbornness was to
patch out the system() libc library call in libfmodex.so, because I
didn't want to add /bin/sh to the sandbox.

So in summary: The new expression now is sandboxed, doesn't use the
wrapper anymore and also has a desktop entry :-)

Unfortunately I haven't found a changelog online to see what has changed
between the 20120620 and 20161016.

Signed-off-by: aszlig <aszlig@nix.build>
Cc: @layus
Diffstat (limited to 'pkgs/games')
-rw-r--r--pkgs/games/humblebundle/bastion.nix188
1 files changed, 134 insertions, 54 deletions
diff --git a/pkgs/games/humblebundle/bastion.nix b/pkgs/games/humblebundle/bastion.nix
index b4acda6b..7edfec2e 100644
--- a/pkgs/games/humblebundle/bastion.nix
+++ b/pkgs/games/humblebundle/bastion.nix
@@ -1,62 +1,142 @@
-{ stdenv, fetchHumbleBundle, lzma, xorg, libpulseaudio}:
+{ lib, stdenv, buildGame, fetchHumbleBundle, makeWrapper, unzip, imagemagick
+, mono, SDL2, SDL2_image, openal, libvorbis, libGL
+}:
 
-let
-  arch = {
-    "i686-linux" = "x86";
-    "x86_64-linux" = "x86_64";
-  }.${stdenv.system};
-in stdenv.mkDerivation rec {
-  name = "bastion-1.4";
+buildGame rec {
+  name = "bastion-${version}";
+  version = "20161016";
 
   src = fetchHumbleBundle {
-    name = "Bastion-HIB-2012-06-20.sh";
+    name = "bastion-20161016.bin";
     machineName = "bastion_linux";
-    downloadName = ".sh";
-    md5 = "aa6ccaead3b4b8a5fbd156f4019e8c8b";
+    downloadName = "Download";
+    md5 = "19fea173ff2da0f990f60bd5e7c3b237";
   };
 
-  dontStrip = true;
-  phases = ["installPhase"];
-
-  installPhase = let
-    rpath = stdenv.lib.makeLibraryPath [
-      "$dest"
-      xorg.libX11
-      xorg.libXi
-      stdenv.cc.cc
-      libpulseaudio
-    ];
-   in ''
-    dest="$out/opt/games/bastion"
-    libdir="$dest/lib" #${stdenv.lib.optionalString (arch=="x86_64") "64"}"
-
-    # Unpack binaries and data into $dest
-    mkdir -p "$dest"
-    sh "$src" --tar xf ./instarchive_all -O           | ${lzma}/bin/lzcat | tar x -C "$dest"
-    sh "$src" --tar xf ./instarchive_linux_${arch} -O | ${lzma}/bin/lzcat | tar x -C "$dest"
-
-    # Ensure that $dest is a valid library path.
-    mv $dest/lib64 $libdir || true
-
-    # Patch heavily :-)
-    patchelf \
-      --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \
-      --set-rpath "${rpath}" "$dest/Bastion.bin.${arch}"
-    patchelf --set-rpath "${rpath}" "$libdir/libmono-2.0.so.1"
-    patchelf --set-rpath "${rpath}" "$libdir/libfmodex.so"
-    patchelf --set-rpath "${rpath}" "$libdir/libSDL-1.2.so.0"
-
-    # Fixup permissions, just to be sure.
-    find "$dest" -type f -exec chmod 644 "{}" +
-    chmod 755 "$dest/Bastion.bin.${arch}"
-
-    # Taken from ArchLinux; might be useful to actually implement
-    #install -Dm644 "''${pkgname}".desktop "''${pkgdir}"/usr/share/applications/"''${pkgname}".desktop
-    #install -Dm755 "mesa''${pkgname}" "''${pkgdir}"/usr/bin/"''${pkgname}"mesa
-    #install -Dm644 "''${pkgdir}"/opt/games/Bastion/Bastion.png "''${pkgdir}"/usr/share/icons/"''${pkgname}".png
-
-    # XXX: Make wrapper instead of symlink ? See ArchLinux's bastionmesa above.
-    mkdir -p "$out/bin"
-    ln -s "$dest/Bastion.bin.${arch}" "$out/bin/bastion"
+  unpackCmd = "${unzip}/bin/unzip -qq \"$curSrc\" 'data/*' || :";
+
+  nativeBuildInputs = [ makeWrapper imagemagick ];
+
+  libDir =
+    if stdenv.system == "i686-linux" then "lib"
+    else if stdenv.system == "x86_64-linux" then "lib64"
+    else throw "Unsupported architecture ${stdenv.system}.";
+
+  buildPhase = let
+    dllmap = {
+      SDL2 = "${SDL2}/lib/libSDL2.so";
+      SDL2_image = "${SDL2_image}/lib/libSDL2_image.so";
+      soft_oal = "${openal}/lib/libopenal.so";
+      libvorbisfile = "${libvorbis}/lib/libvorbisfile.so";
+      MojoShader = "$out/libexec/bastion/libmojoshader.so";
+    };
+  in lib.concatStrings (lib.mapAttrsToList (dll: target: ''
+    sed -i -e '/<dllmap.*dll="${dll}\.dll".*os="linux"/ {
+      s!target="[^"]*"!target="'"${target}"'"!
+    }' FNA.dll.config
+  '') dllmap) + ''
+    # This is not needed, so let's remove it.
+    sed -i -e '/libtheoraplay/d' FNA.dll.config
+
+    # We don't want any Steam libraries, that's why we bought the non-DRM
+    # version, right?
+    cc -Wall -std=c11 -shared -xc - -o "$libDir/libSteamStub.so" -fPIC <<EOF
+    #include <stddef.h>
+    int SteamAPI_Init(void) { return 0; }
+    void SteamAPI_Shutdown(void) {}
+    void SteamAPI_RunCallbacks() {}
+    /* All the symbols for the global accessors for Steamworks C++ APIs */
+    ${lib.concatMapStrings (sym: ''
+      void *Steam${sym}(void) { return NULL; }
+    '') [
+      "Client" "User" "Friends" "Utils" "Matchmaking" "UserStats" "Apps"
+      "Networking" "MatchmakingServers" "RemoteStorage" "Screenshots" "HTTP"
+      "UnifiedMessages" "Controller" "UGC" "AppList" "Music" "MusicRemote"
+      "HTMLSurface" "Inventory" "Video"
+    ]}
+    EOF
+    patchelf --remove-needed libsteam_api.so "$libDir/libSteamWrapper.so"
+    patchelf --add-needed libSteamStub.so "$libDir/libSteamWrapper.so"
+
+    # FMOD Ex tries to run /bin/sh -c 'pulseaudio --check > /dev/null 2>&1', so
+    # we need to prevent this by replacing the system() call with a successful
+    # return value (0). If someone doesn't have or want to have PulseAudio,
+    # FMOD Ex still falls back to ALSA if it can't load libpulse-simple.so.
+    if [ "$libDir" = lib64 ]; then
+      addr="$(objdump -d lib64/libfmodex.so | sed -n -e '
+        /callq.*system@plt/s/^ *\([^:]\+\).*/\1/p
+      ')"
+
+      # This is quite easy, just replace the system() call with XOR EAX so we
+      # get a return value of 0 and pad the rest with NOP.
+      offset=$(("0x$addr"))
+      ( printf '\x31\xc0'     # XOR the EAX register
+        printf '\x90\x90\x90' # Fill with NOPs
+      ) | dd of="$libDir/libfmodex.so" obs=1 seek=$offset count=5 conv=notrunc
+    else
+      relocSystem="$(readelf -r lib/libfmodex.so | sed -n -e '
+        /system@/s/^0*\([^ ]\+\).*/\1/p
+      ')"
+      addr="$(objdump -d lib/libfmodex.so | sed -n -e '
+        /call *'"$relocSystem"' /s/^ *\([^:]\+\).*/\1/p
+      ')"
+
+      # For the 32 bit library it's not so easy as the 4 bytes coming after the
+      # CALL opcode will be replaced by the dynamic linker, so we just XOR the
+      # EAX register with the relocation address and replace the TEST opcode
+      # afterwards.
+      offset=$(("0x$addr"))
+      ( printf '\x35\xfc\xff\xff\xff' # XOR EAX with the relocation address
+        printf '\x39\xc0'             # CMP EAX, EAX
+      ) | dd of="$libDir/libfmodex.so" obs=1 seek=$offset conv=notrunc
+    fi
+
+    # For the Desktop icon
+    convert Bastion.bmp Bastion.png
+  '';
+
+  installPhase = ''
+    for lib in fmodex mojoshader SteamWrapper SteamStub; do
+      install -vD "$libDir/lib$lib.so" "$out/libexec/bastion/lib$lib.so"
+    done
+
+    cp -rvt "$out/libexec/bastion" Bastion.exe FNA.dll* \
+      FMOD.dll Lidgren.Network.dll MonoGame.Framework.Net.dll
+
+    mkdir -p "$out/share"
+    cp -rv Content "$out/share/bastion"
+    cp -vt "$out/libexec/bastion" Bastion.bmp
+    ln -s "$out/share/bastion" "$out/libexec/bastion/Content"
+
+    makeWrapper ${lib.escapeShellArg mono}/bin/mono "$out/bin/bastion" \
+      --add-flags "$out/libexec/bastion/Bastion.exe" \
+      --set SDL_OPENGL_LIBRARY ${lib.escapeShellArg "${libGL}/lib/libGL.so"} \
+      --run "cd '$out/libexec/bastion'"
+
+    install -vD -m 0644 Bastion.png "$out/share/icons/bastion.png"
+
+    mkdir -p "$out/share/applications"
+    cat > "$out/share/applications/bastion.desktop" <<EOF
+    [Desktop Entry]
+    Name=Bastion
+    Type=Application
+    Version=1.1
+    Exec=$out/bin/bastion
+    Icon=$out/share/icons/bastion.png
+    Categories=Game
+    EOF
+  '';
+
+  # Add all of the libraries in $runtimeDependencies to the FMOD Ex library,
+  # because it tries to dlopen() libpulse-simple and libasound.so and we don't
+  # have a main ELF binary where we could add that search path.
+  postFixup = ''
+    rpath="$(patchelf --print-rpath "$out/libexec/bastion/libfmodex.so")"
+    for dep in $runtimeDependencies; do
+      rpath="$rpath${rpath:+:}$dep/lib"
+    done
+    patchelf --set-rpath "$rpath" "$out/libexec/bastion/libfmodex.so"
   '';
+
+  sandbox.paths.required = [ "$XDG_DATA_HOME/Bastion" ];
 }