From e73e6907172af0a041de5e5e2e6694ad159a7b63 Mon Sep 17 00:00:00 2001 From: Morgan Jones Date: Sat, 30 Mar 2024 20:36:51 -0700 Subject: [PATCH] dwarf-fortress: update to 50 --- pkgs/games/dwarf-fortress/default.nix | 23 ++- pkgs/games/dwarf-fortress/dfhack/default.nix | 134 ++++++++++++++---- .../dwarf-therapist/default.nix | 16 ++- .../dwarf-therapist/wrapper.nix | 19 +-- pkgs/games/dwarf-fortress/game.json | 9 ++ pkgs/games/dwarf-fortress/game.nix | 81 +++++++++-- pkgs/games/dwarf-fortress/lazy-pack.nix | 3 + pkgs/games/dwarf-fortress/unfuck.nix | 33 +++-- pkgs/games/dwarf-fortress/update.sh | 4 +- pkgs/games/dwarf-fortress/wrapper/default.nix | 118 +++++++++++---- pkgs/games/dwarf-fortress/wrapper/dfhack.in | 11 -- .../wrapper/dwarf-fortress-init.in | 110 +++++++++++--- .../dwarf-fortress/wrapper/dwarf-fortress.in | 38 ++++- 13 files changed, 460 insertions(+), 139 deletions(-) delete mode 100755 pkgs/games/dwarf-fortress/wrapper/dfhack.in diff --git a/pkgs/games/dwarf-fortress/default.nix b/pkgs/games/dwarf-fortress/default.nix index 777866d8fb0e..ce2f9f401e6e 100644 --- a/pkgs/games/dwarf-fortress/default.nix +++ b/pkgs/games/dwarf-fortress/default.nix @@ -46,31 +46,28 @@ let # The latest Dwarf Fortress version. Maintainers: when a new version comes # out, ensure that (unfuck|dfhack|twbt) are all up to date before changing - # this. - latestVersion = "0.47.05"; + # this. Note that unfuck and twbt are not required for v50. + latestVersion = "50.12"; # Converts a version to a package name. versionToName = version: "dwarf-fortress_${replaceStrings ["."] ["_"] version}"; - dwarf-therapist-original = libsForQt5.callPackage ./dwarf-therapist { - texlive = texliveBasic.withPackages (ps: with ps; [ float caption wrapfig adjmulticol sidecap preprint enumitem ]); - }; - # A map of names to each Dwarf Fortress package we know about. df-games = listToAttrs (map (dfVersion: { name = versionToName dfVersion; value = let - # I can't believe this syntax works. Spikes of Nix code indeed... + isV50 = lib.versionAtLeast dfVersion "50.0"; + + dwarf-fortress-unfuck = if isV50 then null else callPackage ./unfuck.nix { inherit dfVersion; }; + dwarf-fortress = callPackage ./game.nix { inherit dfVersion; inherit dwarf-fortress-unfuck; }; - dwarf-fortress-unfuck = callPackage ./unfuck.nix { inherit dfVersion; }; - - twbt = callPackage ./twbt { inherit dfVersion; }; + twbt = if isV50 then null else callPackage ./twbt { inherit dfVersion; }; dfhack = callPackage ./dfhack { inherit (perlPackages) XMLLibXML XMLLibXSLT; @@ -80,7 +77,10 @@ let dwarf-therapist = libsForQt5.callPackage ./dwarf-therapist/wrapper.nix { inherit dwarf-fortress; - dwarf-therapist = dwarf-therapist-original; + dwarf-therapist = libsForQt5.callPackage ./dwarf-therapist { + texlive = texliveBasic.withPackages (ps: with ps; [ float caption wrapfig adjmulticol sidecap preprint enumitem ]); + inherit isV50; + }; }; in callPackage ./wrapper { @@ -97,7 +97,6 @@ let # Aliases for the latest Dwarf Fortress and the selected Therapist install dwarf-fortress = getAttr (versionToName latestVersion) df-games; - inherit dwarf-therapist-original; dwarf-therapist = dwarf-fortress.dwarf-therapist; dwarf-fortress-original = dwarf-fortress.dwarf-fortress; diff --git a/pkgs/games/dwarf-fortress/dfhack/default.nix b/pkgs/games/dwarf-fortress/dfhack/default.nix index 12d097c71415..fea74f3de07c 100644 --- a/pkgs/games/dwarf-fortress/dfhack/default.nix +++ b/pkgs/games/dwarf-fortress/dfhack/default.nix @@ -1,20 +1,27 @@ { stdenv -, buildEnv , lib , fetchFromGitHub , fetchpatch , cmake +, ninja , writeScriptBin , perl , XMLLibXML , XMLLibXSLT +, makeWrapper , zlib -, ruby , enableStoneSense ? false , allegro5 , libGLU , libGL , SDL +, SDL2 +, coreutils +, util-linux +, ncurses +, strace +, binutils +, gnused , dfVersion }: @@ -28,48 +35,73 @@ let optional optionals optionalString - platforms versionOlder + versionAtLeast ; dfhack-releases = { "0.44.10" = { dfHackRelease = "0.44.10-r2"; - sha256 = "19bxsghxzw3bilhr8sm4axz7p7z8lrvbdsd1vdjf5zbg04rs866i"; + hash = "sha256-0RikMwFv/eJk26Hptnam6J97flekapQhjWvw3+HTfaU="; xmlRev = "321bd48b10c4c3f694cc801a7dee6be392c09b7b"; - prerelease = false; }; "0.44.11" = { dfHackRelease = "0.44.11-beta2.1"; - sha256 = "1jgwcqg9m1ybv3szgnklp6zfpiw5mswla464dlj2gfi5v82zqbv2"; + hash = "sha256-Yi/8BdoluickbcQQRbmuhcfrvrl02vf12MuHmh5m/Mk="; xmlRev = "f27ebae6aa8fb12c46217adec5a812cd49a905c8"; prerelease = true; }; "0.44.12" = { dfHackRelease = "0.44.12-r1"; - sha256 = "0j03lq6j6w378z6cvm7jspxc7hhrqm8jaszlq0mzfvap0k13fgyy"; + hash = "sha256-3j83wgRXbfcrwPRrJVHFGcLD+tXy1M3MR2dwIw2mA0g="; xmlRev = "23500e4e9bd1885365d0a2ef1746c321c1dd5094"; - prerelease = false; }; "0.47.02" = { dfHackRelease = "0.47.02-alpha0"; - sha256 = "19lgykgqm0si9vd9hx4zw8b5m9188gg8r1a6h25np2m2ziqwbjj9"; + hash = "sha256-ScrFcfyiimuLgEaFjN5DKKRaFuKfdJjaTlGDit/0j6Y="; xmlRev = "23500e4e9bd1885365d0a2ef1746c321c1dd509a"; prerelease = true; }; "0.47.04" = { dfHackRelease = "0.47.04-r5"; - sha256 = "sha256-0s+/LKbqsS/mrxKPDeniqykE5+Gy3ZzCa8yEDzMyssY="; + hash = "sha256-0s+/LKbqsS/mrxKPDeniqykE5+Gy3ZzCa8yEDzMyssY="; xmlRev = "be0444cc165a1abff053d5893dc1f780f06526b7"; - prerelease = false; }; "0.47.05" = { dfHackRelease = "0.47.05-r7"; - sha256 = "sha256-vBKUTSjfCnalkBzfjaIKcxUuqsGGOTtoJC1RHJIDlNc="; + hash = "sha256-vBKUTSjfCnalkBzfjaIKcxUuqsGGOTtoJC1RHJIDlNc="; xmlRev = "f5019a5c6f19ef05a28bd974c3e8668b78e6e2a4"; + }; + "50.10" = { + dfHackRelease = "50.10-r1.1"; + hash = "sha256-k2j8G4kJ/RYE8W0YDOxcsRb5qjjn4El+rigf0v3AqZU="; + xmlRev = "041493b221e0799c106abeac1f86df4535ab80d3"; + needsPatches = true; + }; + "50.11" = { + dfHackRelease = "50.11-r7"; + hash = "sha256-3KsFc0i4XkzoeRvcl5GUlx/fJB1HyqfZm+xL6T4oT/A="; + xmlRev = "cca87907c1cbfcf4af957b0bea3a961a345b1581"; + needsPatches = true; + }; + "50.10" = { + dfHackRelease = "50.10-r1.1"; + hash = "sha256-k2j8G4kJ/RYE8W0YDOxcsRb5qjjn4El+rigf0v3AqZU="; + xmlRev = "041493b221e0799c106abeac1f86df4535ab80d3"; prerelease = false; }; - + "50.11" = { + dfHackRelease = "50.11-r7"; + hash = "sha256-3KsFc0i4XkzoeRvcl5GUlx/fJB1HyqfZm+xL6T4oT/A="; + xmlRev = "cca87907c1cbfcf4af957b0bea3a961a345b1581"; + prerelease = false; + }; + "50.12" = { + dfHackRelease = "50.12-r3rc1"; + hash = "sha256-EcM/FLulGVJgaERFMpYi9O5i1QKZyFb0X4HQagVnO8k="; + xmlRev = "425bb89041565432bb5e9574baadbc15a7c5db0e"; + prerelease = true; + }; }; release = @@ -80,6 +112,8 @@ let else throw "[DFHack] Unsupported Dwarf Fortress version: ${dfVersion}"; version = release.dfHackRelease; + isV50 = versionAtLeast version "50.0"; + needsV50Patches = isV50 && (release.needsPatches or false); # revision of library/xml submodule xmlRev = release.xmlRev; @@ -119,7 +153,7 @@ in owner = "DFHack"; repo = "dfhack"; rev = release.dfHackRelease; - sha256 = release.sha256; + inherit (release) hash; fetchSubmodules = true; }; @@ -133,6 +167,14 @@ in name = "fix-protobuf.patch"; url = "https://github.com/DFHack/dfhack/commit/7bdf958518d2892ee89a7173224a069c4a2190d8.patch"; hash = "sha256-p+mKhmYbnhWKNiGPMjbYO505Gcg634n0nudqH0NX3KY="; + }) ++ optional needsV50Patches (fetchpatch { + name = "use-system-sdl2.patch"; + url = "https://github.com/DFHack/dfhack/commit/734fb730d72e53ebe67f4a041a24dd7c50307ee3.patch"; + hash = "sha256-uLX0gdVSzKEVibyUc1UxcQzdYkRm6D8DF+1eSOxM+qU="; + }) ++ optional needsV50Patches (fetchpatch { + name = "rename-lerp.patch"; + url = "https://github.com/DFHack/dfhack/commit/389dcf5cfcdb8bfb8deeb05fa5756c9f4f5709d1.patch"; + hash = "sha256-QuDtGURhP+nM+x+8GIKO5LrMcmBkl9JSHHIeqzqGIPQ="; }); # gcc 11 fix @@ -150,27 +192,71 @@ in sed -i 's@cached_path = path_string.*@cached_path = getenv("DF_DIR");@' library/Process-linux.cpp ''; - nativeBuildInputs = [ cmake perl XMLLibXML XMLLibXSLT fakegit ]; + nativeBuildInputs = [ cmake ninja perl XMLLibXML XMLLibXSLT makeWrapper fakegit ]; + # We don't use system libraries because dfhack needs old C++ ABI. - buildInputs = [ zlib SDL ] + buildInputs = [ zlib ] + ++ optional isV50 SDL2 + ++ optional (!isV50) SDL ++ optionals enableStoneSense [ allegro5 libGLU libGL ]; preConfigure = '' - # Trick build system into believing we have .git + # Trick the build system into believing we have .git. mkdir -p .git/modules/library/xml touch .git/index .git/modules/library/xml/index ''; - cmakeFlags = [ "-DDFHACK_BUILD_ARCH=${arch}" "-DDOWNLOAD_RUBY=OFF" ] - ++ optionals enableStoneSense [ "-DBUILD_STONESENSE=ON" "-DSTONESENSE_INTERNAL_SO=OFF" ]; + cmakeFlags = [ + # Race condition in `Generating codegen.out.xml and df/headers` that is fixed when using Ninja. + "-GNinja" + "-DDFHACK_BUILD_ARCH=${arch}" - # dfhack expects an unversioned libruby.so to be present in the hack - # subdirectory for ruby plugins to function. - postInstall = '' - ln -s ${ruby}/lib/libruby-*.so $out/hack/libruby.so + # Don't download anything. + "-DDOWNLOAD_RUBY=OFF" + "-DUSE_SYSTEM_SDL2=ON" + + # Ruby support with dfhack is very spotty and was removed in version 50. + "-DBUILD_RUBY=OFF" + ] ++ optionals enableStoneSense [ "-DBUILD_STONESENSE=ON" "-DSTONESENSE_INTERNAL_SO=OFF" ]; + + NIX_CFLAGS_COMPILE = [ "-Wno-error=deprecated-enum-enum-conversion" ] + ++ optionals (versionOlder version "0.47") [ "-fpermissive" ]; + + preFixup = '' + # Wrap dfhack scripts. + if [ -f $out/dfhack ]; then + wrapProgram $out/dfhack \ + --inherit-argv0 \ + --set-default SteamAppId 0 \ + --set-default DFHACK_NO_RENAME_LIBSTDCXX 1 \ + --suffix PATH : ${lib.makeBinPath [ + coreutils util-linux strace gnused binutils ncurses + ]} + fi + + if [ -f $out/dfhack-run ]; then + wrapProgram $out/dfhack-run \ + --inherit-argv0 \ + --suffix PATH : ${lib.makeBinPath [ + coreutils + ]} + fi + + # Create a dfhackrc that changes to the correct home directory. + cat < $out/.dfhackrc + #!/usr/bin/env bash + # nixpkgs dfhackrc helper + if [ -d "\$NIXPKGS_DF_HOME" ]; then + cd "\$NIXPKGS_DF_HOME" + DF_DIR="\$NIXPKGS_DF_HOME" + fi + export DF_DIR + EOF ''; - passthru = { inherit dfVersion; }; + passthru = { + inherit dfVersion; + }; meta = { description = "Memory hacking library for Dwarf Fortress and a set of tools that use it"; diff --git a/pkgs/games/dwarf-fortress/dwarf-therapist/default.nix b/pkgs/games/dwarf-fortress/dwarf-therapist/default.nix index 55de9ffdf45e..e4bfe2d685dc 100644 --- a/pkgs/games/dwarf-fortress/dwarf-therapist/default.nix +++ b/pkgs/games/dwarf-fortress/dwarf-therapist/default.nix @@ -6,22 +6,32 @@ , cmake , texlive , ninja +, isV50 ? true }: stdenv.mkDerivation rec { pname = "dwarf-therapist"; - version = "41.2.2"; + + # 41.2.5 is the last version to support Dwarf Fortress 0.47. + version = if isV50 then "42.1.5" else "41.2.5"; src = fetchFromGitHub { owner = "Dwarf-Therapist"; repo = "Dwarf-Therapist"; rev = "v${version}"; - sha256 = "sha256-zsEG68ioSw64UfmqlTLO1i5sObg8C4zxvdPxdQGMhhU="; + hash = if isV50 then # latest + "sha256-aUakfUjnIZWNDhCkG3A6u7BaaCG8kPMV/Fu2S73CoDg=" + else # 41.2.5 + "sha256-xfYBtnO1n6OcliVt07GsQ9alDJIfWdVhtuyWwuvXSZs="; }; nativeBuildInputs = [ texlive cmake ninja ]; buildInputs = [ qtbase qtdeclarative ]; + enableParallelBuilding = true; + + cmakeFlags = [ "-GNinja" ]; + installPhase = if stdenv.isDarwin then '' mkdir -p $out/Applications @@ -31,8 +41,8 @@ stdenv.mkDerivation rec { dontWrapQtApps = true; meta = with lib; { - description = "Tool to manage dwarves in a running game of Dwarf Fortress"; mainProgram = "dwarftherapist"; + description = "Tool to manage dwarves in a running game of Dwarf Fortress"; maintainers = with maintainers; [ abbradar bendlas numinit jonringer ]; license = licenses.mit; platforms = platforms.x86; diff --git a/pkgs/games/dwarf-fortress/dwarf-therapist/wrapper.nix b/pkgs/games/dwarf-fortress/dwarf-therapist/wrapper.nix index eaf391bbe6b1..503dff90cd45 100644 --- a/pkgs/games/dwarf-fortress/dwarf-therapist/wrapper.nix +++ b/pkgs/games/dwarf-fortress/dwarf-therapist/wrapper.nix @@ -1,10 +1,12 @@ -{ stdenv, dwarf-therapist, dwarf-fortress, substituteAll, coreutils, wrapQtAppsHook }: +{ stdenv, dwarf-therapist, dwarf-fortress, substituteAll, coreutils, wrapQtAppsHook +}: let - platformSlug = - if stdenv.hostPlatform.is32bit then - "linux32" else "linux64"; - inifile = "linux/v0.${dwarf-fortress.baseVersion}.${dwarf-fortress.patchVersion}_${platformSlug}.ini"; + platformSlug = let + prefix = if dwarf-fortress.baseVersion >= 50 then "-classic_" else "_"; + base = if stdenv.hostPlatform.is32bit then "linux32" else "linux64"; + in prefix + base; + inifile = "linux/v0.${builtins.toString dwarf-fortress.baseVersion}.${dwarf-fortress.patchVersion}${platformSlug}.ini"; in @@ -40,8 +42,9 @@ stdenv.mkDerivation { wrapQtApp $out/bin/dwarftherapist # Fix up memory layouts - rm -rf $out/share/dwarftherapist/memory_layouts/linux - mkdir -p $out/share/dwarftherapist/memory_layouts/linux + ini_path="$out/share/dwarftherapist/memory_layouts/${inifile}" + rm -f "$ini_path" + mkdir -p "$(dirname -- "$ini_path")" orig_md5=$(cat "${dwarf-fortress}/hash.md5.orig" | cut -c1-8) patched_md5=$(cat "${dwarf-fortress}/hash.md5" | cut -c1-8) input_file="${dwarf-therapist}/share/dwarftherapist/memory_layouts/${inifile}" @@ -53,7 +56,7 @@ stdenv.mkDerivation { echo " Output: $output_file" echo " Replace: $patched_md5" - substitute "$input_file" "$output_file" --replace "$orig_md5" "$patched_md5" + substitute "$input_file" "$output_file" --replace-fail "$orig_md5" "$patched_md5" ''; preferLocalBuild = true; diff --git a/pkgs/games/dwarf-fortress/game.json b/pkgs/games/dwarf-fortress/game.json index 522cccdcda45..c287a4dd8449 100644 --- a/pkgs/games/dwarf-fortress/game.json +++ b/pkgs/games/dwarf-fortress/game.json @@ -135,5 +135,14 @@ "legacy_s": "1rb7h8lzlsjs08rvhhl3nwbrpj54zijijp4y0qdp4vyzsig6nisk", "legacy32": "0ayw09x9smihh8qp5pdvr6vvhwkvcqz36h3lh4g1b5kzxj7g9cyf", "legacy32_s": "10gfxlysfs9gyi1mv52idp5xk45g9h517g2jq4a8cqp2j7594v9c" + }, + "50.10": { + "linux": "13s5p7205r9ha2j5n7carrwd0y7krq34bcdl08khp0kh2v4470a3" + }, + "50.11": { + "linux": "0iz2d88gzvn0vjxlr99f13j4awhvh2lggjmipdwpbxhfsqih7dx0" + }, + "50.12": { + "linux": "070014fzwszfgjyxjyij0k0hadah6s62lpi91ykp3vs220azya1m" } } diff --git a/pkgs/games/dwarf-fortress/game.nix b/pkgs/games/dwarf-fortress/game.nix index 8d351cdbbd0d..bc79eadbc9fb 100644 --- a/pkgs/games/dwarf-fortress/game.nix +++ b/pkgs/games/dwarf-fortress/game.nix @@ -2,11 +2,15 @@ , lib , fetchurl , SDL +, SDL2 +, SDL2_image +, SDL2_mixer +, fmodex , dwarf-fortress-unfuck +, autoPatchelfHook # Our own "unfuck" libs for macOS , ncurses -, fmodex , gcc , dfVersion @@ -23,12 +27,13 @@ let licenses maintainers makeLibraryPath + optional + optionals optionalString splitVersion + toInt ; - libpath = makeLibraryPath [ stdenv.cc.cc stdenv.cc.libc dwarf-fortress-unfuck SDL ]; - # Map Dwarf Fortress platform names to Nixpkgs platform names. # Other srcs are avilable like 32-bit mac & win, but I have only # included the ones most likely to be needed by Nixpkgs users. @@ -41,9 +46,21 @@ let i686-cygwin = "win32"; }; - dfVersionTriple = splitVersion dfVersion; - baseVersion = elemAt dfVersionTriple 1; - patchVersion = elemAt dfVersionTriple 2; + dfVersionTuple = splitVersion dfVersion; + dfVersionBaseIndex = let + x = (builtins.length dfVersionTuple) - 2; + in if x >= 0 then x else 0; + baseVersion = toInt (elemAt dfVersionTuple dfVersionBaseIndex); + patchVersion = elemAt dfVersionTuple (dfVersionBaseIndex + 1); + + isV50 = baseVersion >= 50; + enableUnfuck = !isV50 && dwarf-fortress-unfuck != null; + + libpath = makeLibraryPath ( + [ stdenv.cc.cc stdenv.cc.libc ] + ++ optional (!isV50) SDL + ++ optional enableUnfuck dwarf-fortress-unfuck + ); game = if hasAttr dfVersion df-hashes @@ -57,7 +74,10 @@ let if hasAttr dfPlatform game then getAttr dfPlatform game else throw "Unsupported dfPlatform: ${dfPlatform}"; - + exe = if stdenv.isLinux then + if baseVersion >= 50 then "dwarfort" else "libs/Dwarf_Fortress" + else + "dwarfort.exe"; in stdenv.mkDerivation { @@ -65,21 +85,44 @@ stdenv.mkDerivation { version = dfVersion; src = fetchurl { - url = "https://www.bay12games.com/dwarves/df_${baseVersion}_${patchVersion}_${dfPlatform}.tar.bz2"; + url = "https://www.bay12games.com/dwarves/df_${toString baseVersion}_${toString patchVersion}_${dfPlatform}.tar.bz2"; inherit sha256; }; + sourceRoot = "."; + + postUnpack = optionalString stdenv.isLinux '' + if [ -d df_linux ]; then + mv df_linux/* . + fi + '' + optionalString stdenv.isDarwin '' + if [ -d df_osx ]; then + mv df_osx/* . + fi + ''; + + nativeBuildInputs = optional isV50 autoPatchelfHook; + buildInputs = optionals isV50 [ SDL2 SDL2_image SDL2_mixer stdenv.cc.cc.lib ]; + installPhase = '' + runHook preInstall + + exe=$out/${exe} mkdir -p $out cp -r * $out - rm $out/libs/lib* - exe=$out/${if stdenv.isLinux then "libs/Dwarf_Fortress" - else "dwarfort.exe"} + # Lots of files are +x in the newer releases... + find $out -type d -exec chmod 0755 {} \; + find $out -type f -exec chmod 0644 {} \; + chmod +x $exe + [ -f $out/df ] && chmod +x $out/df + [ -f $out/run_df ] && chmod +x $out/run_df # Store the original hash md5sum $exe | awk '{ print $1 }' > $out/hash.md5.orig - '' + optionalString stdenv.isLinux '' + echo "Original MD5: $(<$out/hash.md5.orig)" >&2 + '' + optionalString (!isV50 && stdenv.isLinux) '' + rm -f $out/libs/*.so patchelf \ --set-interpreter $(cat ${stdenv.cc}/nix-support/dynamic-linker) \ --set-rpath "${libpath}" \ @@ -101,12 +144,22 @@ stdenv.mkDerivation { @executable_path/libs/libstdc++.6.dylib \ $exe '' + '' - # Store the new hash + ls -al $out + runHook postInstall + ''; + + fixupPhase = '' + runHook preFixup + runHook postFixup + + # Store the new hash as the very last step. + exe=$out/${exe} md5sum $exe | awk '{ print $1 }' > $out/hash.md5 + echo "Patched MD5: $(<$out/hash.md5)" >&2 ''; passthru = { - inherit baseVersion patchVersion dfVersion; + inherit baseVersion patchVersion dfVersion exe; updateScript = ./update.sh; }; diff --git a/pkgs/games/dwarf-fortress/lazy-pack.nix b/pkgs/games/dwarf-fortress/lazy-pack.nix index dcaa8102ae98..d47292484561 100644 --- a/pkgs/games/dwarf-fortress/lazy-pack.nix +++ b/pkgs/games/dwarf-fortress/lazy-pack.nix @@ -41,6 +41,8 @@ let then getAttr dfGame df-games else throw "Unknown Dwarf Fortress version: ${dfVersion}"; dwarf-therapist = dwarf-fortress.dwarf-therapist; + + mainProgram = if enableDFHack then "dfhack" else "dwarf-fortress"; in buildEnv { name = "dwarf-fortress-full"; @@ -54,6 +56,7 @@ buildEnv { ++ optional enableLegendsBrowser legends-browser; meta = { + inherit mainProgram; description = "An opinionated wrapper for Dwarf Fortress"; maintainers = with maintainers; [ Baughn numinit ]; license = licenses.mit; diff --git a/pkgs/games/dwarf-fortress/unfuck.nix b/pkgs/games/dwarf-fortress/unfuck.nix index 3aeee27d87a5..a1baa2d83f75 100644 --- a/pkgs/games/dwarf-fortress/unfuck.nix +++ b/pkgs/games/dwarf-fortress/unfuck.nix @@ -1,6 +1,7 @@ { stdenv , lib , fetchFromGitHub +, fetchpatch , cmake , libGL , libSM @@ -32,43 +33,43 @@ let unfuck-releases = { "0.43.05" = { unfuckRelease = "0.43.05"; - sha256 = "173dyrbxlzqvjf1j3n7vpns4gfjkpyvk9z16430xnmd5m6nda8p2"; + hash = "sha256-4iLVrKmlVdvBICb8NLe/U7pHtL372CGDkxt/2lf2bZw="; }; "0.44.05" = { unfuckRelease = "0.44.05"; - sha256 = "00yj4l4gazxg4i6fj9rwri6vm17i6bviy2mpkx0z5c0mvsr7s14b"; + hash = "sha256-iwR9st4VsPJBn7cKH/cy8YS6Tcw8J+lMJK9/9Qgl0gM="; }; "0.44.09" = { unfuckRelease = "0.44.09"; - sha256 = "138p0v8z2x47f0fk9k6g75ikw5wb3vxldwv5ggbkf4hhvlw6lvzm"; + hash = "sha256-9W9qON0QEjfXe2XzRvseixc+YznPzDQdcId08dEGF40="; }; "0.44.10" = { unfuckRelease = "0.44.10"; - sha256 = "0vb19qx2ibc79j4bgbk9lskb883qfb0815zw1dfz9k7rqwal8mzj"; + hash = "sha256-8ldEFcf5zPRdC/yXgMByeCC0pqZprreITIetKDpOYW0="; }; "0.44.11" = { unfuckRelease = "0.44.11.1"; - sha256 = "1kszkb1d1vll8p04ja41nangsaxb5lv4p3xh2jhmsmipfixw7nvz"; + hash = "sha256-f9vDe3Q3Vl2hFLCPSzYtqyv9rLKBKEnARZTu0MKaX88="; }; "0.44.12" = { unfuckRelease = "0.44.12"; - sha256 = "1kszkb1d1vll8p04ja41nangsaxb5lv4p3xh2jhmsmipfixw7nvz"; + hash = "sha256-f9vDe3Q3Vl2hFLCPSzYtqyv9rLKBKEnARZTu0MKaX88="; }; "0.47.01" = { unfuckRelease = "0.47.01"; - sha256 = "11xvb3qh4crdf59pwfwpi73rzm3ysd1r1xp2k1jp7527jmqapk4k"; + hash = "sha256-k8yrcJVHlHNlmOL2kEPTftSfx4mXO35TcS0zAvFYu4c="; }; "0.47.02" = { unfuckRelease = "0.47.01"; - sha256 = "11xvb3qh4crdf59pwfwpi73rzm3ysd1r1xp2k1jp7527jmqapk4k"; + hash = "sha256-k8yrcJVHlHNlmOL2kEPTftSfx4mXO35TcS0zAvFYu4c="; }; "0.47.04" = { unfuckRelease = "0.47.04"; - sha256 = "1wa990xbsyiiz7abq153xmafvvk1dmgz33rp907d005kzl1z86i9"; + hash = "sha256-KRr0A/2zANAOSDeP8V9tYe7tVO2jBLzU+TF6vTpISfE="; }; "0.47.05" = { - unfuckRelease = "0.47.04"; - sha256 = "1wa990xbsyiiz7abq153xmafvvk1dmgz33rp907d005kzl1z86i9"; + unfuckRelease = "0.47.05-final"; + hash = "sha256-kBdzU6KDpODOBP9XHM7lQRIEWUGOj838vXF1FbSr0Xw="; }; }; @@ -86,9 +87,17 @@ stdenv.mkDerivation { owner = "svenstaro"; repo = "dwarf_fortress_unfuck"; rev = release.unfuckRelease; - sha256 = release.sha256; + inherit (release) hash; }; + patches = lib.optionals (versionOlder release.unfuckRelease "0.47.05") [( + fetchpatch { + name = "fix-noreturn-returning.patch"; + url = "https://github.com/svenstaro/dwarf_fortress_unfuck/commit/6dcfe5ae869fddd51940c6c37a95f7bc639f4389.patch"; + hash = "sha256-b9eI3iR7dmFqCrktPyn6QJ9U2A/7LvfYRS+vE3BOaqk="; + } + )]; + postPatch = '' # https://github.com/svenstaro/dwarf_fortress_unfuck/pull/27 substituteInPlace CMakeLists.txt --replace \''${GLEW_LIBRARIES} GLEW::glew diff --git a/pkgs/games/dwarf-fortress/update.sh b/pkgs/games/dwarf-fortress/update.sh index 5b99dff8aa77..892e031f7883 100755 --- a/pkgs/games/dwarf-fortress/update.sh +++ b/pkgs/games/dwarf-fortress/update.sh @@ -2,9 +2,7 @@ #! nix-shell -i bash -p jq nix coreutils curl # systems to generate hashes for -systems='linux linux32 osx osx32 - win win_s win32 win32_s - legacy legacy_s legacy32 legacy32_s' +systems='linux osx' if [ $# -eq 0 ]; then versions="$(curl http://www.bay12games.com/dwarves/ \ diff --git a/pkgs/games/dwarf-fortress/wrapper/default.nix b/pkgs/games/dwarf-fortress/wrapper/default.nix index a4433821d20d..3a207c548534 100644 --- a/pkgs/games/dwarf-fortress/wrapper/default.nix +++ b/pkgs/games/dwarf-fortress/wrapper/default.nix @@ -2,6 +2,7 @@ , lib , buildEnv , substituteAll +, makeWrapper , runCommand , coreutils , gawk @@ -12,6 +13,9 @@ , enableSoundSense ? false , soundSense , jdk +, expect +, xvfb-run +, writeText , enableStoneSense ? false , enableTWBT ? false , twbt @@ -31,10 +35,14 @@ }: let - dfhack_ = dfhack.override { + dfhack' = dfhack.override { inherit enableStoneSense; }; + isV50 = dwarf-fortress.baseVersion >= 50; + + enableTWBT' = enableTWBT && (twbt != null); + ptheme = if builtins.isString theme then builtins.getAttr theme themes @@ -46,19 +54,19 @@ let # These are in inverse order for first packages to override the next ones. paths = extraPackages ++ lib.optional (theme != null) ptheme - ++ lib.optional enableDFHack dfhack_ + ++ lib.optional enableDFHack dfhack' ++ lib.optional enableSoundSense soundSense - ++ lib.optionals enableTWBT [ twbt.lib twbt.art ] + ++ lib.optionals enableTWBT' [ twbt.lib twbt.art ] ++ [ dwarf-fortress ]; ignoreCollisions = true; }; - settings_ = lib.recursiveUpdate { + settings' = lib.recursiveUpdate { init = { PRINT_MODE = if enableTextMode then "TEXT" - else if enableTWBT then "TWBT" + else if enableTWBT' then "TWBT" else if stdenv.hostPlatform.isDarwin then "STANDARD" # https://www.bay12games.com/dwarves/mantisbt/view.php?id=11680 else null; INTRO = enableIntro; @@ -77,23 +85,31 @@ let else throw "dwarf-fortress: unsupported configuration value ${toString v}"; config = runCommand "dwarf-fortress-config" { - nativeBuildInputs = [ gawk ]; + nativeBuildInputs = [ gawk makeWrapper ]; } ('' mkdir -p $out/data/init edit_setting() { v=''${v//'&'/'\&'} - if ! gawk -i inplace -v RS='\r?\n' ' - { n += sub("\\[" ENVIRON["k"] ":[^]]*\\]", "[" ENVIRON["k"] ":" ENVIRON["v"] "]"); print } - END { exit(!n) } - ' "$out/$file"; then - echo "error: no setting named '$k' in $file" >&2 - exit 1 + if [ -f "$out/$file" ]; then + if ! gawk -i inplace -v RS='\r?\n' ' + { n += sub("\\[" ENVIRON["k"] ":[^]]*\\]", "[" ENVIRON["k"] ":" ENVIRON["v"] "]"); print } + END { exit(!n) } + ' "$out/$file"; then + echo "error: no setting named '$k' in $out/$file" >&2 + exit 1 + fi + else + echo "warning: no file $out/$file; cannot edit" >&2 fi } - '' + forEach settings_ (file: kv: '' + '' + forEach settings' (file: kv: '' file=data/init/${lib.escapeShellArg file}.txt - cp ${baseEnv}/"$file" "$out/$file" + if [ -f "${baseEnv}/$file" ]; then + cp "${baseEnv}/$file" "$out/$file" + else + echo "warning: no file ${baseEnv}/$file; cannot copy" >&2 + fi '' + forEach kv (k: v: lib.optionalString (v != null) '' export k=${lib.escapeShellArg k} v=${lib.escapeShellArg (toTxt v)} edit_setting @@ -103,7 +119,7 @@ let # Patch the MD5 orig_md5=$(< "${dwarf-fortress}/hash.md5.orig") patched_md5=$(< "${dwarf-fortress}/hash.md5") - input_file="${dfhack_}/hack/symbols.xml" + input_file="${dfhack'}/hack/symbols.xml" output_file="$out/hack/symbols.xml" echo "[DFHack Wrapper] Fixing Dwarf Fortress MD5:" @@ -112,7 +128,7 @@ let echo " Output: $output_file" echo " Replace: $patched_md5" - substitute "$input_file" "$output_file" --replace "$orig_md5" "$patched_md5" + substitute "$input_file" "$output_file" --replace-fail "$orig_md5" "$patched_md5" ''); # This is a separate environment because the config files to modify may come @@ -124,11 +140,11 @@ let }; in -lib.throwIf (enableTWBT && !enableDFHack) "dwarf-fortress: TWBT requires DFHack to be enabled" +lib.throwIf (enableTWBT' && !enableDFHack) "dwarf-fortress: TWBT requires DFHack to be enabled" lib.throwIf (enableStoneSense && !enableDFHack) "dwarf-fortress: StoneSense requires DFHack to be enabled" -lib.throwIf (enableTextMode && enableTWBT) "dwarf-fortress: text mode and TWBT are mutually exclusive" +lib.throwIf (enableTextMode && enableTWBT') "dwarf-fortress: text mode and TWBT are mutually exclusive" -stdenv.mkDerivation { +stdenv.mkDerivation rec { pname = "dwarf-fortress"; version = dwarf-fortress.dfVersion; @@ -136,36 +152,40 @@ stdenv.mkDerivation { name = "dwarf-fortress-init"; src = ./dwarf-fortress-init.in; inherit env; - exe = - if stdenv.isLinux then "libs/Dwarf_Fortress" - else "dwarfort.exe"; + inherit (dwarf-fortress) exe; stdenv_shell = "${stdenv.shell}"; cp = "${coreutils}/bin/cp"; rm = "${coreutils}/bin/rm"; ln = "${coreutils}/bin/ln"; cat = "${coreutils}/bin/cat"; mkdir = "${coreutils}/bin/mkdir"; + printf = "${coreutils}/bin/printf"; + uname = "${coreutils}/bin/uname"; }; runDF = ./dwarf-fortress.in; - runDFHack = ./dfhack.in; runSoundSense = ./soundSense.in; passthru = { inherit dwarf-fortress dwarf-therapist twbt env; - dfhack = dfhack_; + dfhack = dfhack'; }; - buildCommand = '' + dontUnpack = true; + dontBuild = true; + preferLocalBuild = true; + installPhase = '' mkdir -p $out/bin substitute $runDF $out/bin/dwarf-fortress \ --subst-var-by stdenv_shell ${stdenv.shell} \ + --subst-var-by dfExe ${dwarf-fortress.exe} \ --subst-var dfInit chmod 755 $out/bin/dwarf-fortress '' + lib.optionalString enableDFHack '' - substitute $runDFHack $out/bin/dfhack \ + substitute $runDF $out/bin/dfhack \ --subst-var-by stdenv_shell ${stdenv.shell} \ + --subst-var-by dfExe dfhack \ --subst-var dfInit chmod 755 $out/bin/dfhack '' + lib.optionalString enableSoundSense '' @@ -176,7 +196,51 @@ stdenv.mkDerivation { chmod 755 $out/bin/soundsense ''; - preferLocalBuild = true; + doInstallCheck = true; + nativeInstallCheckInputs = [ expect xvfb-run ]; + + installCheckPhase = let + commonExpectComponents = fmod: '' + ${lib.optionalString isV50 ''expect "Loading audio..."''} + ${lib.optionalString (!fmod && isV50) ''expect "Failed to load fmod, trying SDL_mixer"''} + ${lib.optionalString isV50 ''expect "Audio loaded successfully!"''} + expect "Loading bindings from data/init/interface.txt" + ''; + dfHackExpectScript = writeText "dfhack-test.exp" '' + spawn xvfb-run $env(out)/bin/dfhack + ${commonExpectComponents false} + expect "DFHack version ${version}" + expect "\[DFHack\]#" + send -- "lua print(os.getenv('out'))\r" + expect "$env(out)" + # Don't send 'die' here; just exit. Some versions of dfhack crash on exit. + exit 0 + ''; + vanillaExpectScript = fmod: writeText "vanilla-test.exp" '' + spawn ${lib.optionalString fmod "env NIXPKGS_DF_OPTS=fmod"} xvfb-run $env(out)/bin/dwarf-fortress + ${commonExpectComponents fmod} + exit 0 + ''; + in + '' + export HOME="$(mktemp -dt dwarf-fortress.XXXXXX)" + '' + lib.optionalString enableDFHack '' + expect ${dfHackExpectScript} + df_home="$(find ~ -name "df_*" | head -n1)" + test -f "$df_home/dfhack" + '' + lib.optionalString isV50 '' + expect ${vanillaExpectScript true} + df_home="$(find ~ -name "df_*" | head -n1)" + test ! -f "$df_home/dfhack" + test -f "$df_home/libfmod_plugin.so" + '' + '' + expect ${vanillaExpectScript false} + df_home="$(find ~ -name "df_*" | head -n1)" + test ! -f "$df_home/dfhack" + test ! -f "$df_home/libfmod_plugin.so" + '' + '' + test -d "$df_home/data" + ''; inherit (dwarf-fortress) meta; } diff --git a/pkgs/games/dwarf-fortress/wrapper/dfhack.in b/pkgs/games/dwarf-fortress/wrapper/dfhack.in deleted file mode 100755 index 0f74674baf29..000000000000 --- a/pkgs/games/dwarf-fortress/wrapper/dfhack.in +++ /dev/null @@ -1,11 +0,0 @@ -#!@stdenv_shell@ -e - -source @dfInit@ - -for i in *.init *.init-example dfhack-config/default dfhack-config/init hack/* stonesense/*; do - if [ -e "$i" ]; then update_path "$i"; fi -done - -cd "$DF_DIR" -LD_LIBRARY_PATH="$env_dir/hack/libs:$env_dir/hack${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH" \ - LD_PRELOAD="$env_dir/hack/libdfhack.so:$LD_PRELOAD" exec $env_dir/libs/Dwarf_Fortress "$@" diff --git a/pkgs/games/dwarf-fortress/wrapper/dwarf-fortress-init.in b/pkgs/games/dwarf-fortress/wrapper/dwarf-fortress-init.in index 27639e57f212..d6fb9267ecfa 100644 --- a/pkgs/games/dwarf-fortress/wrapper/dwarf-fortress-init.in +++ b/pkgs/games/dwarf-fortress/wrapper/dwarf-fortress-init.in @@ -1,45 +1,113 @@ #!@stdenv_shell@ -e shopt -s extglob -[ -z "$DF_DIR" ] && export DF_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/df_linux" env_dir="@env@" -exe="$env_dir/@exe@" + +if [ -n "$DF_DIR" ]; then + # Compatibility for users that were using DF_DIR, since the dfhack script clobbers this variable. + export NIXPKGS_DF_HOME="$DF_DIR" +fi + +if [ -z "$NIXPKGS_DF_HOME" ]; then + export NIXPKGS_DF_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/df_linux" +fi + +# Compatibility. +export DF_DIR="$NIXPKGS_DF_HOME" update_path() { local path="$1" - @mkdir@ -p "$DF_DIR/$(dirname "$path")" + @mkdir@ -p "$NIXPKGS_DF_HOME/$(dirname "$path")" + # If user has replaced these data directories, let them stay. - if [ ! -e "$DF_DIR/$path" ] || [ -L "$DF_DIR/$path" ]; then - @rm@ -f "$DF_DIR/$path" - @ln@ -s "$env_dir/$path" "$DF_DIR/$path" + if [ ! -e "$NIXPKGS_DF_HOME/$path" ] || [ -L "$NIXPKGS_DF_HOME/$path" ]; then + @rm@ -f "$NIXPKGS_DF_HOME/$path" + @ln@ -s "$env_dir/$path" "$NIXPKGS_DF_HOME/$path" + fi +} + +cleanup_path() { + local path="$1" + + # Let them stay if not a link. + if [ ! -e "$NIXPKGS_DF_HOME/$path" ] || [ -L "$NIXPKGS_DF_HOME/$path" ]; then + @rm@ -f "$NIXPKGS_DF_HOME/$path" fi } forcecopy_path() { local path="$1" - @mkdir@ -p "$DF_DIR/$(dirname "$path")" - @rm@ -rf "$DF_DIR/$path" - @cp@ -rL --no-preserve=all "$env_dir/$path" "$DF_DIR/$path" + @mkdir@ -p "$NIXPKGS_DF_HOME/$(dirname "$path")" + @rm@ -rf "$NIXPKGS_DF_HOME/$path" + @cp@ -rL --no-preserve=all "$env_dir/$path" "$NIXPKGS_DF_HOME/$path" } -@mkdir@ -p "$DF_DIR" +declare -A _NIXPKGS_DF_OPTS + +# Don't use fmod by default. +_NIXPKGS_DF_OPTS[fmod]=0 + +IFS=',' read -ra split_options <<< "$NIXPKGS_DF_OPTS" +for option in "${split_options[@]}"; do + key="${option%=*}" + value="${option##*=}" + if [ -z "$value" ] || [ "$key" == "$value" ]; then + value=1 + fi + _NIXPKGS_DF_OPTS["$key"]="$value" +done + +@mkdir@ -p "$NIXPKGS_DF_HOME" @cat@ <&2 -Using $DF_DIR as Dwarf Fortress overlay directory. -If you do any changes in it, don't forget to clean it when updating the game version! -We try to detect changes based on data directories being symbolic links -- keep this in mind. - +/------------------------------------------------------------------------------\\ +| Hello from the nixpkgs Dwarf Fortress wrapper! | +| | +| Using the following Dwarf Fortress overlay directory as NIXPKGS_DF_HOME: | +| $(@printf@ '% -76s' "$NIXPKGS_DF_HOME") | +| | +| If you make any changes in it, don't forget to clean it when updating the | +| game version! We detect changes if data directories are symbolic links. | +| | +| Even though we do our best on our own, this script may miss some. Submit a | +| pull request if there are any that become a problem. | +| | +| We started with the following nixpkgs launch options as NIXPKGS_DF_OPTS: | +| $(@printf@ '% -76s' "$(IFS=',' echo "${_NIXPKGS_DF_OPTS[@]@K}")") | +| | +| If you want to try fmod over SDL_mixer, set NIXPKGS_DF_OPTS=fmod. | +\\------------------------------------------------------------------------------/ EOF cd "$env_dir" -for i in data/init/* data/!(init|index|announcement) raw; do - update_path "$i" + +# All potential important files in DF 50 and below. +for path in dwarfort *.so libs raw data/init/* data/!(init|index|announcement); do + force_delete=0 + if [[ "$path" == libfmod*.so* ]] && [ "${_NIXPKGS_DF_OPTS[fmod]}" -eq 0 ]; then + force_delete=1 + fi + + if [ -e "$path" ] && [ "$force_delete" -eq 0 ]; then + update_path "$path" + else + cleanup_path "$path" + fi done -forcecopy_path data/index -# For some reason, it's needed to be writable... -forcecopy_path data/announcement -forcecopy_path data/help -forcecopy_path data/dipscript +# These need to be copied due to read only flags on older versions of DF. +for path in index announcement help dipscript; do + if [ -e "data/$path" ]; then + forcecopy_path "data/$path" + else + @rm@ -f "$NIXPKGS_DF_HOME/$path" &>/dev/null + fi +done + +# Handle library paths on Darwin. +if [ "$(@uname@)" == Darwin ]; then + export DYLD_LIBRARY_PATH="$env_dir/libs" + export DYLD_FRAMEWORK_PATH="$env_dir/libs" +fi diff --git a/pkgs/games/dwarf-fortress/wrapper/dwarf-fortress.in b/pkgs/games/dwarf-fortress/wrapper/dwarf-fortress.in index 4448bd05fda5..55c259e919b0 100644 --- a/pkgs/games/dwarf-fortress/wrapper/dwarf-fortress.in +++ b/pkgs/games/dwarf-fortress/wrapper/dwarf-fortress.in @@ -2,8 +2,38 @@ source @dfInit@ -export DYLD_LIBRARY_PATH="$env_dir/libs" -export DYLD_FRAMEWORK_PATH="$env_dir/libs" +set -euo pipefail -cd "$DF_DIR" -exec "$exe" "$@" +exe="@dfExe@" + +# If we're switching back from dfhack to vanilla, cleanup all dfhack +# links so Dwarf Fortress doesn't autoload its leftover libdfhooks.so. +# Otherwise, populate them. +dfhack_files=( + dfhack + dfhack-run + .dfhackrc + libdfhooks.so + dfhack-config/default + dfhack-config/init + hack/* + stonesense/* + *.init *.init-example +) + +if [ "${exe##*/}" == dfhack ]; then + for i in "${dfhack_files[@]}"; do + if [ -e "$i" ]; then + update_path "$i" + else + cleanup_path "$i" + fi + done +else + for i in "${dfhack_files[@]}"; do + cleanup_path "$i" + done +fi + +# Go! +cd "$NIXPKGS_DF_HOME" && exec "./$exe" "$@"