about summary refs log tree commit diff
path: root/pkgs/games/gog
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/games/gog')
-rw-r--r--pkgs/games/gog/crosscode.nix4
-rw-r--r--pkgs/games/gog/default.nix4
-rw-r--r--pkgs/games/gog/fetch-gog/default.nix40
-rw-r--r--pkgs/games/gog/homm3/default.nix97
-rw-r--r--pkgs/games/gog/homm3/launcher-execl.patch36
-rw-r--r--pkgs/games/gog/kingdoms-and-castles.nix14
-rw-r--r--pkgs/games/gog/the-longest-journey/default.nix106
-rw-r--r--pkgs/games/gog/the-longest-journey/predefined-config.patch29
-rw-r--r--pkgs/games/gog/warcraft2/default.nix148
-rw-r--r--pkgs/games/gog/warcraft2/xdg.patch37
10 files changed, 503 insertions, 12 deletions
diff --git a/pkgs/games/gog/crosscode.nix b/pkgs/games/gog/crosscode.nix
index 9e60ab08..f33cdb45 100644
--- a/pkgs/games/gog/crosscode.nix
+++ b/pkgs/games/gog/crosscode.nix
@@ -2,12 +2,12 @@
 
 buildGame rec {
   name = "crosscode-${version}";
-  version = "1.0.2";
+  version = "1.1.0";
 
   src = fetchGog {
     productId = 1252295864;
     downloadName = "en3installer0";
-    sha256 = "0gd3i99g79w7nr6dnkjkpfq5s2y20dwrf706ipzkggknygmg9xad";
+    sha256 = "1rqf1vlg151hxy5f9nwldmb4l3853dmvcf7fiakab8vzsmjmldlm";
   };
 
   nativeBuildInputs = [ makeWrapper ];
diff --git a/pkgs/games/gog/default.nix b/pkgs/games/gog/default.nix
index 1ef58c30..f1010a00 100644
--- a/pkgs/games/gog/default.nix
+++ b/pkgs/games/gog/default.nix
@@ -15,13 +15,17 @@ let
     crosscode = callPackage ./crosscode.nix {};
     dungeons3 = callPackage ./dungeons3.nix {};
     epistory = callPackage ./epistory.nix { };
+    homm3 = callPackage ./homm3 {};
+    kingdoms-and-castles = callPackage ./kingdoms-and-castles.nix {};
     overload = callPackage ./overload.nix {};
     party-hard = callPackage ./party-hard.nix {};
     satellite-reign = callPackage ./satellite-reign.nix {};
     settlers2 = callPackage ./settlers2.nix {};
     stardew-valley = callPackage ./stardew-valley.nix {};
+    the-longest-journey = callPackage ./the-longest-journey {};
     thimbleweed-park = callPackage ./thimbleweed-park.nix {};
     war-for-the-overworld = callPackage ./war-for-the-overworld.nix {};
+    warcraft2 = callPackage ./warcraft2 {};
     wizard-of-legend = callPackage ./wizard-of-legend.nix {};
     xeen = callPackage ./xeen.nix {};
   };
diff --git a/pkgs/games/gog/fetch-gog/default.nix b/pkgs/games/gog/fetch-gog/default.nix
index b49f0dab..c1d350ce 100644
--- a/pkgs/games/gog/fetch-gog/default.nix
+++ b/pkgs/games/gog/fetch-gog/default.nix
@@ -147,13 +147,24 @@ let
       }
     '';
 
-  in runCommandCC "get-captcha" {
-    nativeBuildInputs = [ pkgconfig ];
+  in stdenv.mkDerivation {
+    name = "get-captcha";
+
+    dontUnpack = true;
+
+    nativeBuildInputs = [ pkgconfig (qt5.wrapQtAppsHook or null) ];
     buildInputs = [ qt5.qtbase qt5.qtwebengine ];
-  } ''
-    g++ $(pkg-config --libs --cflags Qt5WebEngineWidgets Qt5WebEngine) \
-      -Wall -std=c++11 -o "$out" ${application}
-  '';
+    preferLocalBuild = true;
+
+    buildPhase = ''
+      g++ $(pkg-config --libs --cflags Qt5WebEngineWidgets Qt5WebEngine) \
+        -Wall -std=c++11 -o get-captcha ${application}
+    '';
+
+    installPhase = ''
+      install -vD get-captcha "$out/bin/get-captcha"
+    '';
+  };
 
   mkPyStr = str: "'${stdenv.lib.escape ["'" "\\"] (toString str)}'";
 
@@ -177,7 +188,7 @@ let
       def login(self):
         browser = mechanicalsoup.StatefulBrowser()
         response = browser.open(${mkPyStr authURL})
-        if "google.com/recaptcha" in response.text:
+        if "https://www.recaptcha.net/recaptcha" in response.text:
           token_url = self.login_with_captcha()
         else:
           browser.select_form('form[name="login"]')
@@ -185,13 +196,19 @@ let
           browser['login[password]'] = ${mkPyStr password}
           browser.submit_selected()
 
-          auth_code = parse_qs(urlsplit(browser.get_url()).query)['code']
+          query = parse_qs(urlsplit(browser.get_url()).query)
+
+          if 'code' not in query:
+            sys.stderr.write(
+              "Unable to login with the provided GOG credentials.\n"
+            )
+            raise SystemExit(1)
 
           token_url = "https://auth.gog.com/token?" + urlencode({
             'client_id': ${mkPyStr clientId},
             'client_secret': ${mkPyStr clientSecret},
             'grant_type': 'authorization_code',
-            'code': auth_code,
+            'code': query['code'],
             'redirect_uri': ${mkPyStr redirectUri}
           })
 
@@ -203,7 +220,8 @@ let
 
       def login_with_captcha(self):
         sys.stderr.write("Solving a captcha is required to log in.\n")
-        sys.stderr.write("Please run " ${mkPyStr getCaptcha} " now.\n")
+        sys.stderr.write("Please run " ${mkPyStr getCaptcha}
+                         "/bin/get-captcha now.\n")
         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         sys.stderr.write("Waiting for connection")
         i = 0
@@ -278,6 +296,8 @@ in stdenv.mkDerivation {
   outputHashAlgo = "sha256";
   outputHash = sha256;
 
+  preferLocalBuild = true;
+
   nativeBuildInputs = [
     curl python3Packages.tabulate python3Packages.MechanicalSoup
   ];
diff --git a/pkgs/games/gog/homm3/default.nix b/pkgs/games/gog/homm3/default.nix
new file mode 100644
index 00000000..5aecb8f2
--- /dev/null
+++ b/pkgs/games/gog/homm3/default.nix
@@ -0,0 +1,97 @@
+{ stdenv, lib, buildSandbox, fetchGog, runCommand, makeWrapper, fetchFromGitHub
+, cmake, pkgconfig, python3, boost, zlib, minizip, qt5
+, SDL2, SDL2_image, SDL2_mixer, SDL2_ttf
+, innoextract, parallel, ffmpeg
+}:
+
+let
+  data = runCommand "homm3-complete-data" rec {
+    version = "4.0";
+
+    # We need a newer version that 1.7, because GOG uses a newer archive
+    # format.
+    nativeBuildInputs = lib.singleton (innoextract.overrideAttrs (drv: {
+      src = fetchFromGitHub {
+        owner = "dscharrer";
+        repo = "innoextract";
+        rev = "4c61bc4da822fc89f2e05bdb2c45e6c4dd7a3673";
+        sha256 = "197pr7dzlza4isssvhqhvnrr7wzc9c4b3wnnp03sxpmhviyidln1";
+      };
+    })) ++ [ parallel ffmpeg ];
+
+    data = fetchGog {
+      name = "setup_homm_3_complete_${version}.bin";
+      productId = 1207658787;
+      downloadName = "en1installer1";
+      sha256 = "1wfly3024yi64kaczfdca4wx5g09053dpc1gwp08w637833n4kq4";
+    };
+
+    setup = fetchGog {
+      name = "setup_homm_3_complete_${version}.exe";
+      productId = 1207658787;
+      downloadName = "en1installer0";
+      sha256 = "1cwr28ml9z3iq6q9z1vs1jkbnjjrkv2m39bhqw78a5hvj43mgxza";
+    };
+  } ''
+    ln -s "$data" archive-1.bin
+    ln -s "$setup" archive.exe
+    innoextract -L -I Data -I Maps -I Mp3 archive.exe
+    mkdir -p "$out/music"
+    parallel -v ffmpeg -hide_banner -loglevel warning -i {} -acodec libvorbis \
+      "$out/music/{/.}.ogg" ::: mp3/*.mp3
+    mv -t "$out" data maps
+  '';
+
+  engine = stdenv.mkDerivation rec {
+    name = "vcmi-${version}";
+    version = "20190609";
+
+    src = fetchFromGitHub {
+      owner = "vcmi";
+      repo = "vcmi";
+      rev = "e7bced112cf36007da8f418ba3313d2dd4b3e045";
+      sha256 = "0qk0mpz3amg2kw5m99bk3qi19rwcwjj6s1lclby1ws0v8nxh2cmb";
+      fetchSubmodules = true;
+    };
+
+    inherit data;
+
+    patches = [ ./launcher-execl.patch ];
+
+    postPatch = ''
+      find -type f -name '*.cpp' -exec sed -i -e '/^ *# *include/ {
+        s!["<]SDL_\(ttf\|image\|mixer\)\.h[">]!<SDL2/SDL_\1.h>!
+      }' {} +
+
+      sed -i -e 's/"Mp3"/"music"/' config/filesystem.json
+    '';
+
+    cmakeFlags = [ "-DCMAKE_INSTALL_LIBDIR=lib" "-DENABLE_TEST=0" ];
+    enableParallelBuilding = true;
+    nativeBuildInputs = [ cmake pkgconfig python3 makeWrapper ];
+    buildInputs = [
+      boost zlib minizip SDL2 SDL2_image SDL2_mixer SDL2_ttf ffmpeg
+      qt5.qtbase
+    ];
+    postInstall = let
+      inherit (qt5.qtbase) qtPluginPrefix;
+      qtPlugins = "${qt5.qtbase}/${qtPluginPrefix}";
+    in ''
+      rm "$out/bin/vcmibuilder"
+      for i in "$out/bin/"*; do
+        rpath="$(patchelf --print-rpath "$i")"
+        patchelf --set-rpath "$out/lib/vcmi:$rpath" "$i"
+      done
+
+      wrapProgram "$out/bin/vcmilauncher" \
+        --suffix QT_PLUGIN_PATH : ${lib.escapeShellArg qtPlugins}
+      cp -rst "$out/share/vcmi" "$data"/*
+    '';
+    dontStrip = true;
+  };
+
+in buildSandbox engine {
+  allowBinSh = true;
+  paths.required = [ "$XDG_DATA_HOME/vcmi" "$XDG_CONFIG_HOME/vcmi" ];
+  paths.runtimeVars = [ "LD_LIBRARY_PATH" "LOCALE_ARCHIVE" ];
+}
diff --git a/pkgs/games/gog/homm3/launcher-execl.patch b/pkgs/games/gog/homm3/launcher-execl.patch
new file mode 100644
index 00000000..fae0fa80
--- /dev/null
+++ b/pkgs/games/gog/homm3/launcher-execl.patch
@@ -0,0 +1,36 @@
+diff --git a/launcher/mainwindow_moc.cpp b/launcher/mainwindow_moc.cpp
+index a07774ed2..3275af71a 100644
+--- a/launcher/mainwindow_moc.cpp
++++ b/launcher/mainwindow_moc.cpp
+@@ -11,7 +11,7 @@
+ #include "mainwindow_moc.h"
+ #include "ui_mainwindow_moc.h"
+ 
+-#include <QProcess>
++#include <unistd.h>
+ #include <QDir>
+ 
+ #include "../lib/CConfigHandler.h"
+@@ -77,19 +77,11 @@ void MainWindow::on_startGameButton_clicked()
+ 
+ void MainWindow::startExecutable(QString name)
+ {
+-	QProcess process;
+-
+-	// Start the executable
+-	if(process.startDetached(name))
+-	{
+-		close(); // exit launcher
+-	}
+-	else
+-	{
++	if (execl(name.toLatin1().data(), "vcmiclient", nullptr) == -1) {
++		QString msg("Failed to start %1\nReason: %2");
+ 		QMessageBox::critical(this,
+ 		                      "Error starting executable",
+-		                      "Failed to start " + name + "\n"
+-		                      "Reason: " + process.errorString(),
++							  msg.arg(name).arg(strerror(errno)),
+ 		                      QMessageBox::Ok,
+ 		                      QMessageBox::Ok);
+ 		return;
diff --git a/pkgs/games/gog/kingdoms-and-castles.nix b/pkgs/games/gog/kingdoms-and-castles.nix
new file mode 100644
index 00000000..e31551cc
--- /dev/null
+++ b/pkgs/games/gog/kingdoms-and-castles.nix
@@ -0,0 +1,14 @@
+{ buildUnity, fetchGog }:
+
+buildUnity {
+  name = "kingdoms-and-castles";
+  fullName = "KingdomsAndCastles";
+  saveDir = "LionShield/Kingdoms and Castles";
+  version = "115r12";
+
+  src = fetchGog {
+    productId = 2067763543;
+    downloadName = "en3installer0";
+    sha256 = "1ag03piq09z7hljcbs145hyj8z0gjcvffj99znf3mnbw2qipb7pq";
+  };
+}
diff --git a/pkgs/games/gog/the-longest-journey/default.nix b/pkgs/games/gog/the-longest-journey/default.nix
new file mode 100644
index 00000000..9dca199a
--- /dev/null
+++ b/pkgs/games/gog/the-longest-journey/default.nix
@@ -0,0 +1,106 @@
+{ stdenv, lib, fetchGog, fetchFromGitHub, innoextract, runCommand, buildSandbox
+, SDL2, SDL2_net, freetype, libGLU_combined, glew, alsaLib
+, libogg, libvorbis, xvfb_run
+}:
+
+let
+  gameData = runCommand "the-longest-journey-data" rec {
+    version = "142";
+
+    # We need a newer version that 1.7, because GOG uses a newer archive
+    # format.
+    nativeBuildInputs = lib.singleton (innoextract.overrideAttrs (drv: {
+      src = fetchFromGitHub {
+        owner = "dscharrer";
+        repo = "innoextract";
+        rev = "4c61bc4da822fc89f2e05bdb2c45e6c4dd7a3673";
+        sha256 = "197pr7dzlza4isssvhqhvnrr7wzc9c4b3wnnp03sxpmhviyidln1";
+      };
+    }));
+
+    data = fetchGog {
+      name = "the-longest-journey-${version}.bin";
+      productId = 1207658794;
+      downloadName = "en1installer1";
+      sha256 = "08jg5snlxkzxppq37lsmbhgv9zhwnk1zr4cid5gynzq9b1048rzc";
+    };
+
+    setup = fetchGog {
+      name = "the-longest-journey-${version}.exe";
+      productId = 1207658794;
+      downloadName = "en1installer0";
+      sha256 = "1h4c2bhf5mhz004r37dwdydl3rhpg1wyr4kyvxxwma7x9grxqyzc";
+    };
+  } ''
+    ln -s "$data" archive-1.bin
+    ln -s "$setup" archive.exe
+    innoextract -L -m archive.exe
+    mkdir "$out"
+    mv -t "$out" \
+      game.exe gui.ini chapters.ini language.ini x.xarc \
+      static global fonts [a-f0-9][a-f0-9]
+  '';
+
+  residualvm = stdenv.mkDerivation rec {
+    name = "residualvm-${version}";
+    version = "20190611";
+
+    src = fetchFromGitHub {
+      owner = "residualvm";
+      repo = "residualvm";
+      rev = "ae1a7fbf6fa6bf88a7adebaedb2cd713d5ccc718";
+      sha256 = "1521578jis9s3ilz0ws0msanviyqf70dp54db3d6ssfikc0w3myx";
+    };
+
+    patches = [ ./predefined-config.patch ];
+
+    # Current Git version has an --enable-static option so the stdenv setup
+    # thinks that there is --disable-static as well, which doesn't exist.
+    dontDisableStatic = true;
+
+    enableParallelBuilding = true;
+    buildInputs = [
+      SDL2 SDL2_net freetype libGLU_combined glew alsaLib
+      libogg libvorbis
+    ];
+
+    configureFlags = [ "--disable-all-engines" "--enable-engine=stark" ];
+  };
+
+  configFile = runCommand "residualvm-stark.ini" {
+    nativeBuildInputs = [ xvfb_run residualvm ];
+    inherit gameData;
+  } ''
+    xvfb-run residualvm -p "$gameData" -a
+    sed -e '/^\[residualvm\]/a enable_unsupported_game_warning=false' \
+      residualvm.ini > "$out"
+  '';
+
+  unsandboxed = runCommand "the-longest-journey-${gameData.version}" {
+    residualCmd = "${residualvm}/bin/residualvm";
+    configArgs = let
+      mkXdg = what: fallback: extra: let
+        basePath = "\${XDG_${what}_HOME:-$HOME/${fallback}}";
+      in "\"${basePath}/the-longest-journey${extra}\"";
+    in [
+      "--savepath=${mkXdg "DATA" ".local/share" ""}"
+      "--config=${mkXdg "CONFIG" ".config" "/settings.ini"}"
+    ];
+    inherit (stdenv) shell;
+    residualArgs = lib.escapeShellArgs [ "--predefined-config=${configFile}" ];
+  } ''
+    mkdir -p "$out/bin"
+    cat > "$out/bin/the-longest-journey" <<EOF
+    #!$shell
+    exec $residualCmd $residualArgs $configArgs "\$@" tlj-win
+    EOF
+    chmod +x "$out/bin/the-longest-journey"
+  '';
+
+in buildSandbox unsandboxed {
+  paths.required = [
+    "$XDG_CONFIG_HOME/the-longest-journey"
+    "$XDG_DATA_HOME/the-longest-journey"
+  ];
+  paths.runtimeVars = [ "LD_LIBRARY_PATH" ];
+}
diff --git a/pkgs/games/gog/the-longest-journey/predefined-config.patch b/pkgs/games/gog/the-longest-journey/predefined-config.patch
new file mode 100644
index 00000000..504e3867
--- /dev/null
+++ b/pkgs/games/gog/the-longest-journey/predefined-config.patch
@@ -0,0 +1,29 @@
+diff --git a/base/commandLine.cpp b/base/commandLine.cpp
+index ab741917..8723fc0d 100644
+--- a/base/commandLine.cpp
++++ b/base/commandLine.cpp
+@@ -425,6 +425,9 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha
+ 			DO_LONG_COMMAND("list-saves")
+ 			END_COMMAND
+ 
++			DO_LONG_OPTION("predefined-config")
++			END_OPTION
++
+ 			DO_OPTION('c', "config")
+ 			END_OPTION
+ 
+diff --git a/base/main.cpp b/base/main.cpp
+index 2fbfc679..d74b15e5 100644
+--- a/base/main.cpp
++++ b/base/main.cpp
+@@ -394,6 +394,10 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
+ 	Common::StringMap settings;
+ 	command = Base::parseCommandLine(settings, argc, argv);
+ 
++	// Load config file with predefined options
++	if (settings.contains("predefined-config"))
++		ConfMan.loadConfigFile(settings["predefined-config"]);
++
+ 	// Load the config file (possibly overridden via command line):
+ 	if (settings.contains("config")) {
+ 		ConfMan.loadConfigFile(settings["config"]);
diff --git a/pkgs/games/gog/warcraft2/default.nix b/pkgs/games/gog/warcraft2/default.nix
new file mode 100644
index 00000000..c576439f
--- /dev/null
+++ b/pkgs/games/gog/warcraft2/default.nix
@@ -0,0 +1,148 @@
+{ stdenv, lib, buildSandbox, writeTextFile, runCommand, fetchGog, fetchurl
+, fetchFromGitHub, winePackages, xvfb_run, ffmpeg, rename
+
+# Dependencies for the Stratagus engine
+, cmake, pkgconfig, toluapp, lua5_1, libpng, libmng, zlib, SDL, fluidsynth
+, bzip2, libmikmod, libogg, libvorbis, libtheora, libGLU_combined, sqlite
+}:
+
+let
+  timgm6mb = fetchurl {
+    name = "TimGM6mb.sf2";
+    url = "https://sourceforge.net/p/mscore/code/3412/"
+        + "tree/trunk/mscore/share/sound/TimGM6mb.sf2?format=raw";
+    sha256 = "0m68a5z43nirirq9rj2xzz6z5qpyhdwk40s83sqhr4lc09i8ndy5";
+  };
+
+  stratagus = stdenv.mkDerivation {
+    name = "stratagus-${version}";
+    version = "2.4.2git20190615";
+
+    src = fetchFromGitHub {
+      owner = "Wargus";
+      repo = "stratagus";
+      rev = "c7fc80ff7e89ab969d867121b6f679f81ea60ecb";
+      sha256 = "1n39lxd8qg03kw884llcal3h95y34lys44hib2mdb3qhd5dg9a18";
+    };
+
+    patches = [ ./xdg.patch ];
+
+    # Both check_version and detectPresence in addition to a bunch of functions
+    # in stratagus-tinyfiledialogs.h are trying to run various tools via
+    # /bin/sh, so let's NOP them out.
+    postPatch = ''
+      sed -i -e '/^int/!s/\(check_version\|detectPresence\)([^)]*)/1/g' \
+        gameheaders/stratagus-game-launcher.h
+      sed -i -e '/^\(static \+\)\?int/!s/[a-zA-Z0-9]\+Present *([^)]*)/0/g' \
+        gameheaders/stratagus-tinyfiledialogs.h
+    '';
+
+    NIX_CFLAGS_COMPILE = [ "-Wno-error=format-overflow" ];
+    cmakeFlags = [
+      "-DOpenGL_GL_PREFERENCE=GLVND" "-DENABLE_DEV=ON"
+      "-DGAMEDIR=${placeholder "out"}/bin"
+    ];
+    nativeBuildInputs = [ cmake pkgconfig toluapp ];
+    buildInputs = [
+      toluapp lua5_1 libpng libmng zlib SDL fluidsynth bzip2 libmikmod libogg
+      libvorbis libtheora libGLU_combined sqlite
+    ];
+  };
+
+  stormlib = stdenv.mkDerivation {
+    name = "stormlib-${version}";
+    version = "9.22git20190615";
+
+    src = fetchFromGitHub {
+      owner = "ladislav-zezula";
+      repo = "StormLib";
+      rev = "2f0e0e69e6b3739d7c450ac3d38816aee45ac3c2";
+      sha256 = "04f43c6bwfxiiw1kplxb3ds8g9r633y587z8ir97hrrzw5nmni3w";
+    };
+
+    nativeBuildInputs = [ cmake ];
+    buildInputs = [ zlib bzip2 ];
+  };
+
+  wargus = stdenv.mkDerivation {
+    name = "wargus-${version}";
+    version = "2.4.2git20190615";
+
+    src = fetchFromGitHub {
+      owner = "Wargus";
+      repo = "wargus";
+      rev = "932c4974dfea9805f6710f254de191e65dadb50d";
+      sha256 = "13vpfd4yx43n5sqzj79km7gcv9al3mqskkij335f0c9p28rqf47v";
+    };
+
+    # This fixes up the data path by letting it be set using an environment
+    # variable later in the wrapper and also hardcodes the MuseScore sound
+    # font.
+    #
+    # In addition, wartool.cpp contains a few lines like this:
+    #
+    #   sprintf(extract, "%s.something", extract);
+    #
+    # The problem here is that sprintf() modifies the data pointed by extract
+    # *IN PLACE*, so this essentially truncates the contents to ".something".
+    #
+    # While it may be even better to use strncat() here, the application isn't
+    # critical for security, so for the sake of laziness, let's just use
+    # strcat() instead.
+    postPatch = ''
+      sed -i -e '
+        s/^\( *# *define \+DATA_PATH\).*/\1 getenv("WARGUS_DATAPATH")/
+      ' wargus.cpp
+
+      sed -i -e 's!"[^"]*\/TimGM6mb.sf2"!"${timgm6mb}"!g' \
+        wargus.cpp scripts/stratagus.lua
+
+      sed -i -e '
+        s/sprintf( *\([^,]\+\), "%s\([^"]\+\)", *\1 *)/strcat(\1, "\2")/
+      ' wartool.cpp
+
+      # XXX: Crashes the game, see https://github.com/Wargus/wargus/issues/260
+      rm -r scripts/lists/campaigns/For\ the\ Motherland\ *
+    '';
+
+    cmakeFlags = [ "-DGAMEDIR=${placeholder "out"}/bin" ];
+    nativeBuildInputs = [ cmake pkgconfig ];
+    buildInputs = [ stratagus zlib libpng bzip2 stormlib ];
+  };
+
+  version = "2.02.4";
+
+  # XXX: Unfortunately, innoextract (even Git master) doesn't support this
+  #      archive, so let's resort to a headless wine for extraction.
+  gameData = runCommand "warcraft2-data-${version}" {
+    src = fetchGog {
+      name = "setup-warcraft-2-${version}.exe";
+      productId = 1418669891;
+      downloadName = "en1installer0";
+      sha256 = "1cf3c1ylgdrvkk7y25v47f66m6lp9m4wvl2aldpxzrrqdrlk34k3";
+    };
+    nativeBuildInputs = [ winePackages.minimal xvfb_run ffmpeg wargus rename ];
+  } ''
+    export WINEPREFIX="$PWD"
+    wine wineboot.exe
+    xvfb-run -s '-screen 0 1024x768x24' wine "$src" /sp- /lang=english /silent
+    gameDir='drive_c/GOG Games/Warcraft II BNE'
+    find "$gameDir" -mindepth 1 -depth \
+      -exec rename 's/(.*)\/([^\/]*)/$1\/\L$2/' {} \;
+    wartool -v -r "$gameDir" "$out"
+    cp -rnst "$out" ${lib.escapeShellArg wargus}/share/games/stratagus/wargus/*
+  '';
+
+in buildSandbox (writeTextFile {
+  name = "warcraft2-${version}";
+  destination = "/bin/warcraft2";
+  executable = true;
+  text = ''
+    #!${stdenv.shell}
+    export WARGUS_DATAPATH=${lib.escapeShellArg gameData}
+    exec ${lib.escapeShellArg "${wargus}/bin/wargus"} "$@"
+  '';
+}) {
+  paths.required = [ "$XDG_DATA_HOME/wc2" ];
+  paths.runtimeVars = [ "LD_LIBRARY_PATH" ];
+}
diff --git a/pkgs/games/gog/warcraft2/xdg.patch b/pkgs/games/gog/warcraft2/xdg.patch
new file mode 100644
index 00000000..979a86e3
--- /dev/null
+++ b/pkgs/games/gog/warcraft2/xdg.patch
@@ -0,0 +1,37 @@
+diff --git a/src/stratagus/parameters.cpp b/src/stratagus/parameters.cpp
+index a705ba21d..8585856b0 100644
+--- a/src/stratagus/parameters.cpp
++++ b/src/stratagus/parameters.cpp
+@@ -48,26 +48,13 @@ void Parameters::SetDefaultValues()
+ 
+ void Parameters::SetDefaultUserDirectory()
+ {
+-#ifdef USE_GAME_DIR
+-	userDirectory = StratagusLibPath;
+-#elif USE_WIN32
+-	userDirectory = getenv("APPDATA");
+-#else
+-	userDirectory = getenv("HOME");
+-#endif
+-
+-	if (!userDirectory.empty()) {
+-		userDirectory += "/";
++	const char *xdg_data_home = getenv("XDG_DATA_HOME");
++	if (xdg_data_home == NULL) {
++		userDirectory = getenv("HOME");
++		userDirectory += "/.local/share";
++	} else {
++		userDirectory = xdg_data_home;
+ 	}
+-
+-#ifdef USE_GAME_DIR
+-#elif USE_WIN32
+-	userDirectory += "Stratagus";
+-#elif defined(USE_MAC)
+-	userDirectory += "Library/Stratagus";
+-#else
+-	userDirectory += ".stratagus";
+-#endif
+ }
+ 
+ static std::string GetLocalPlayerNameFromEnv()