about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMorgan Jones <me@numin.it>2024-03-30 20:36:51 -0700
committerMorgan Jones <me@numin.it>2024-04-11 20:05:50 -0700
commite73e6907172af0a041de5e5e2e6694ad159a7b63 (patch)
tree503a94c7e38a556fb781ca0576cabdc49e5bb98d
parent55b7756b7d64a2ee9b2220475a5a8fd8dd1d8b90 (diff)
dwarf-fortress: update to 50
-rw-r--r--pkgs/games/dwarf-fortress/default.nix23
-rw-r--r--pkgs/games/dwarf-fortress/dfhack/default.nix134
-rw-r--r--pkgs/games/dwarf-fortress/dwarf-therapist/default.nix16
-rw-r--r--pkgs/games/dwarf-fortress/dwarf-therapist/wrapper.nix19
-rw-r--r--pkgs/games/dwarf-fortress/game.json9
-rw-r--r--pkgs/games/dwarf-fortress/game.nix81
-rw-r--r--pkgs/games/dwarf-fortress/lazy-pack.nix3
-rw-r--r--pkgs/games/dwarf-fortress/unfuck.nix33
-rwxr-xr-xpkgs/games/dwarf-fortress/update.sh4
-rw-r--r--pkgs/games/dwarf-fortress/wrapper/default.nix118
-rwxr-xr-xpkgs/games/dwarf-fortress/wrapper/dfhack.in11
-rw-r--r--pkgs/games/dwarf-fortress/wrapper/dwarf-fortress-init.in110
-rw-r--r--pkgs/games/dwarf-fortress/wrapper/dwarf-fortress.in38
13 files changed, 460 insertions, 139 deletions
diff --git a/pkgs/games/dwarf-fortress/default.nix b/pkgs/games/dwarf-fortress/default.nix
index 777866d8fb0e5..ce2f9f401e6e8 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 12d097c714155..fea74f3de07ca 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}"
+
+      # 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
 
-    # 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
+      # Create a dfhackrc that changes to the correct home directory.
+      cat <<EOF > $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 55de9ffdf45e4..e4bfe2d685dc3 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 eaf391bbe6b18..503dff90cd45a 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 522cccdcda45b..c287a4dd84490 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 8d351cdbbd0d1..bc79eadbc9fb6 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 dcaa8102ae989..d472924845618 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 3aeee27d87a50..a1baa2d83f757 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 5b99dff8aa77a..892e031f7883e 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 a4433821d20de..3a207c548534a 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 0f74674baf299..0000000000000
--- 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 27639e57f212e..d6fb9267ecfaf 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
 
-@cat@ <<EOF >&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.
+# 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@ <<EOF >&2
+/------------------------------------------------------------------------------\\
+| 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
+
+# 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
 
-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
+# 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 4448bd05fda57..55c259e919b0b 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" "$@"