about summary refs log tree commit diff
path: root/pkgs/games
diff options
context:
space:
mode:
authoraszlig <aszlig@nix.build>2018-02-19 04:32:32 +0100
committeraszlig <aszlig@nix.build>2018-02-21 20:38:34 +0100
commit77a0f47d06f3d21b78c7f34a129bc3f75d69fbf7 (patch)
treefe50abea68d2ce04b104058f097aa6aa89737bb2 /pkgs/games
parent73b4b0bfb78998a53b73ce2501f4d2b8bc73198d (diff)
games/gog: Add statically recompiled Albion
We only fetch the data files from GOG here and use the port from
OpenPandora (https://repo.openpandora.org/?page=detail&app=albion_sr)
for the main game executable.

Instead of using the binaries of the static recompiler, this is
completely compiled from source and comes with a few changes in the form
of a few patches:

  config.patch:

    Hide the mouse cursor and set the default scaled resolution to
    1280x960 (the window can still be resized) in the default
    albion.cfg.

  error-log-stderr.patch:

    Whenever an error happens, the game usually creates a file called
    "error.log", which contains the actual error message. This patch
    makes sure the file isn't created but the error message is printed
    to stderr.

  scons.patch:

    Fixes a few SConstruct/SConscript files to use the builder's
    environment, so that it can find executables such as gcc.

  sdl2.patch:

    SDL 2 has dropped support for the keysym.unicode field at some
    point, so use keysym.sym instead. This has the disadvantage that it
    doesn't recognize key modifiers, so we might want to switch this to
    use the SDL_TEXTINPUT event.

  xdg-paths.patch:

    We want to use XDG_CONFIG_HOME and XDG_DATA_HOME instead of writing
    everything to one directory, so this patch makes sure that the paths
    are correctly read depending on whether its an XDG directory or one
    of the store paths of the game.

Signed-off-by: aszlig <aszlig@nix.build>
Diffstat (limited to 'pkgs/games')
-rw-r--r--pkgs/games/gog/albion/config.patch19
-rw-r--r--pkgs/games/gog/albion/default.nix183
-rw-r--r--pkgs/games/gog/albion/error-log-stderr.patch100
-rw-r--r--pkgs/games/gog/albion/scons.patch74
-rw-r--r--pkgs/games/gog/albion/sdl2.patch60
-rw-r--r--pkgs/games/gog/albion/xdg-paths.patch259
-rw-r--r--pkgs/games/gog/default.nix2
7 files changed, 697 insertions, 0 deletions
diff --git a/pkgs/games/gog/albion/config.patch b/pkgs/games/gog/albion/config.patch
new file mode 100644
index 00000000..6bac229c
--- /dev/null
+++ b/pkgs/games/gog/albion/config.patch
@@ -0,0 +1,19 @@
+diff --git a/games/Albion/release/linux/Albion.cfg b/games/Albion/release/linux/Albion.cfg
+index 50d4327..8c901f1 100644
+--- a/games/Albion/release/linux/Albion.cfg
++++ b/games/Albion/release/linux/Albion.cfg
+@@ -50,11 +50,11 @@ Audio_MIDI_Device=
+ #     - this setting renders the 3d part of the game in the native resolution instead of rendering it in the original resolution and then scaling it
+ #     - there are some minor issues, read the readme for more information
+ # Display_MouseCursor=normal/minimal/none - shape of SDL mouse cursor in window mode
+-Display_ScaledWidth=720
+-Display_ScaledHeight=480
++Display_ScaledWidth=1280
++Display_ScaledHeight=960
+ Display_Fullscreen=no
+ Display_Enhanced_3D_Rendering=on
+-Display_MouseCursor=normal
++Display_MouseCursor=none
+ 
+ 
+ # Screenshot settings
diff --git a/pkgs/games/gog/albion/default.nix b/pkgs/games/gog/albion/default.nix
new file mode 100644
index 00000000..1242ecb7
--- /dev/null
+++ b/pkgs/games/gog/albion/default.nix
@@ -0,0 +1,183 @@
+{ stdenv, lib, buildSandbox, fetchGog, fetchzip, innoextract, SDL2, SDL2_mixer
+, bchunk, p7zip, alsaLib, writeText
+
+# For static recompilation
+, fetchFromGitHub, scons, judy, python, nasm, autoreconfHook
+
+, language ? "en"
+}:
+
+let
+  version = "1.6.1";
+
+  staticRecompilerSource = fetchFromGitHub {
+    owner = "M-HT";
+    repo = "SR";
+    rev = "albion_v${version}";
+    sha256 = "0yspgssfk5xbrs5krq0sin561rgb0fva4hk7mlxlcrvs0xpqf5z8";
+  };
+
+  mkPatchedWildMidi = variant: stdenv.mkDerivation {
+    name = "${variant}-0.2.3.5patched";
+    src = "${staticRecompilerSource}/midi-libs/${variant}-0.2.3.5svn";
+    nativeBuildInputs = [ autoreconfHook ];
+    buildInputs = [ alsaLib ];
+    postPatch = "sed -i -e '/^CFLAGS/s/-pedantic//' configure.ac";
+  };
+
+  compileMidiPlugin = variant: let
+    commonDrvAttrs = rec {
+      name = "midi-plugin-${variant}";
+      soname = "midi-${variant}";
+      sourceFile = "${soname}.c";
+      midiLib = "WildMidi";
+
+      src = "${staticRecompilerSource}/midi-plugins";
+
+      buildInputs = lib.singleton (mkPatchedWildMidi variant);
+
+      timidityCfg = let
+        gusPatches = lib.overrideDerivation (fetchzip {
+          url = "http://sebt3.openpandora.org/pnd/timidity_midi_installer.pnd";
+          sha256 = "0nznac8lxcbj0fwbg0njlnh3ysa3d3c5i24n2cw0yv5yqji4cdsb";
+          stripRoot = false;
+        }) (lib.const { unpackCmd = "${p7zip}/bin/7z x \"$curSrc\""; });
+      in writeText "timidity-albion.cfg" ''
+        dir ${gusPatches}/eawpats
+        source ${gusPatches}/eawpats/sounds.cfg
+      '';
+
+      postPatch = ''
+        sed -i -e 's!getenv("TIMIDITY_CFG")!"'"$timidityCfg"'"!' \
+          midi-wildmidi.c
+      '';
+
+      buildPhase = ''
+        gcc -shared -Wl,-soname,"$soname.so" -I. \
+          -o "$soname.so" -fpic -m32 -O2 -Wall \
+          "$sourceFile" -l"$midiLib"
+      '';
+
+      installPhase = ''
+        install -vD "$soname.so" "$out/lib/$soname.so"
+      '';
+    };
+
+    extraAttrs = lib.optionalAttrs (variant == "wildmidiA") {
+      soname = "midiA-wildmidi";
+      sourceFile = "albion/midiA-wildmidi.c";
+      midiLib = "WildMidiA";
+    };
+
+  in stdenv.mkDerivation (commonDrvAttrs // extraAttrs);
+
+  udis86 = stdenv.mkDerivation {
+    name = "udis86";
+    src = "${staticRecompilerSource}/SR/udis86-1.6";
+    postPatch = "chmod +x configure mkinstalldirs";
+    preInstall = "mkdir -p \"$out/lib\" \"$out/bin\"";
+  };
+
+  gameData = stdenv.mkDerivation rec {
+    name = "albion-game-data-${version}";
+    version = "3";
+
+    src = fetchGog {
+      productId = 1436955815;
+      downloadName = "${language}1installer1";
+      sha256 = ({
+        de = "0ylhma70kcj255i03gy5xa3adb8hfw2xpk1m2pp5880aqkmr06k7";
+        en = "0x0s2q0x7kjz6qfhb9qs5d959caijiinpc7xv4rx9n7mmb7xlh5m";
+      }).${language};
+    };
+
+    outputs = [ "out" "dev" ];
+
+    nativeBuildInputs = [ innoextract ];
+    phases = [ "unpackPhase" "patchPhase" "installPhase" ];
+    unpackCmd = toString [
+      "innoextract"
+      "--include" "game.gog"
+      "--include" "game.ins"
+      "--include" "MAIN.EXE"
+      "--include" "SETUP.INI"
+      "-m" "\"$curSrc\""
+    ];
+    patchPhase = ''
+      sed -i -e '
+        s,^SOURCE_PATH=.*,SOURCE_PATH=C:\\,
+        s/^\(MODE_[^=]*=\)N$/\1Y/
+      ' SETUP.INI
+    '';
+    installPhase = ''
+      ${bchunk}/bin/bchunk game.gog game.ins game_cd
+      ${p7zip}/bin/7z x game_cd01.iso ALBION
+      mv ALBION "$out"
+      install -vD -m 0644 SETUP.INI "$out/setup.ini"
+      install -vD -m 0644 MAIN.EXE "$dev/albion_main.exe"
+    '';
+  };
+
+in buildSandbox (stdenv.mkDerivation {
+  name = "albion-${version}";
+  inherit version;
+
+  src = staticRecompilerSource;
+
+  patches = [
+    ./scons.patch ./xdg-paths.patch ./config.patch ./sdl2.patch
+    ./error-log-stderr.patch ./cdpath-is-gamedir.patch
+    ./storepaths.patch
+  ];
+
+  wildmidi = compileMidiPlugin "wildmidi";
+  wildmidiA = compileMidiPlugin "wildmidiA";
+
+  postPatch = ''
+    substituteInPlace games/Albion/SR-Main/main.c \
+      --subst-var-by GAME_CONFIG_FILE "$out/etc/albion.cfg" \
+      --subst-var-by GAME_DATA_PATH ${lib.escapeShellArg gameData.out}
+
+    substituteInPlace games/Albion/SR-Main/virtualfs.c \
+      --subst-var-by SETUP_INI_PATH "${gameData.out}/setup.ini"
+
+    substituteInPlace games/Albion/SR-Main/Albion-music-midiplugin.c \
+      --replace ./midi-wildmidi.so "$wildmidi/lib/midi-wildmidi.so" \
+      --replace ./midiA-wildmidi.so "$wildmidiA/lib/midiA-wildmidi.so" \
+  '';
+
+  nativeBuildInputs = [ scons judy python nasm udis86 ];
+  buildInputs = [ SDL2 SDL2_mixer ];
+
+  NIX_CFLAGS_COMPILE = "-I${lib.getDev SDL2}/include/SDL2";
+
+  buildPhase = ''
+    scons -C SR debug=1
+
+    mkdir tmp
+    pushd tmp
+    cp ../SR-games/Albion/SR/x86/*.sci .
+    ../SR/SR.exe ${gameData.dev}/albion_main.exe Albion-main.asm
+    rm *.sci
+    python ../SR-games/Albion/SR/compact_source.py
+    nasm -felf -dELF -O1 -w+orphan-labels -w-number-overflow \
+      -i../SR-games/Albion/SR/x86/ Albion-main_linux.asm 2> a.a || :
+    python ../SR-games/Albion/SR/repair_short_jumps.py
+    popd
+
+    mv tmp/seg*.inc tmp/Albion-main.asm tmp/Albion-main_linux.asm \
+      games/Albion/SR-Main/x86
+    rm -r tmp
+
+    scons -C games/Albion/SR-Main debug=1 device=pc-linux sdl2=1
+  '';
+
+  installPhase = ''
+    install -vD -m 0644 games/Albion/release/linux/Albion.cfg \
+      "$out/etc/albion.cfg"
+    install -vD games/Albion/SR-Main/SR-Main "$out/bin/albion"
+  '';
+}) {
+  paths.required = [ "$XDG_DATA_HOME/albion" "$XDG_CONFIG_HOME/albion" ];
+  paths.runtimeVars = [ "LD_LIBRARY_PATH" ];
+}
diff --git a/pkgs/games/gog/albion/error-log-stderr.patch b/pkgs/games/gog/albion/error-log-stderr.patch
new file mode 100644
index 00000000..9819f1f1
--- /dev/null
+++ b/pkgs/games/gog/albion/error-log-stderr.patch
@@ -0,0 +1,100 @@
+diff --git a/games/Albion/SR-Main/Albion-proc-vfs.c b/games/Albion/SR-Main/Albion-proc-vfs.c
+index c3d2d4f..faa90e7 100644
+--- a/games/Albion/SR-Main/Albion-proc-vfs.c
++++ b/games/Albion/SR-Main/Albion-proc-vfs.c
+@@ -232,6 +232,8 @@ FILE *Game_fopen(const char *filename, const char *mode)
+     fprintf(stderr, "fopen: original name: %s\n", filename);
+ #endif
+ 
++    if (strcasecmp(filename, "error.log") == 0) return stderr;
++
+     vfs_err = vfs_get_real_name(filename, (char *) &temp_str, &realdir);
+ 
+ #if defined(__DEBUG__)
+@@ -260,6 +262,8 @@ int Game_open(const char *pathname, int flags, mode_t mode)
+     fprintf(stderr, "open: original name: %s\n", pathname);
+ #endif
+ 
++    if (strcasecmp(pathname, "error.log") == 0) return STDERR_FILENO;
++
+     vfs_err = vfs_get_real_name(pathname, (char *) &temp_str, &realdir);
+ 
+ #if defined(__DEBUG__)
+@@ -726,6 +730,16 @@ int Game_closedir(struct watcom_dirent *dirp)
+     return 0;
+ }
+ 
++int Game_close(int fd)
++{
++    return fd == STDERR_FILENO ? 0 : close(fd);
++}
++
++int Game_fclose(FILE *stream)
++{
++    return stream == stderr ? 0 : fclose(stream);
++}
++
+ static void Conv_find(struct watcom_find_t *buffer, struct watcom_dirent *direntp)
+ {
+     // file attributes
+diff --git a/games/Albion/SR-Main/Albion-proc-vfs.h b/games/Albion/SR-Main/Albion-proc-vfs.h
+index 0cf4491..2e16671 100644
+--- a/games/Albion/SR-Main/Albion-proc-vfs.h
++++ b/games/Albion/SR-Main/Albion-proc-vfs.h
+@@ -110,6 +110,8 @@ extern int Game_rename(const char *oldpath, const char *newpath);
+ extern struct watcom_dirent *Game_opendir(const char *dirname);
+ extern struct watcom_dirent *Game_readdir(struct watcom_dirent *dirp);
+ extern int Game_closedir(struct watcom_dirent *dirp);
++extern int Game_close(int fd);
++extern int Game_fclose(FILE *stream);
+ extern uint32_t Game_dos_findfirst(const char *path, const uint32_t attributes, struct watcom_find_t *buffer);
+ extern uint32_t Game_dos_findnext(struct watcom_find_t *buffer);
+ extern uint32_t Game_dos_findclose(struct watcom_find_t *buffer);
+diff --git a/games/Albion/SR-Main/x86/SR-asm-calls.asm b/games/Albion/SR-Main/x86/SR-asm-calls.asm
+index 3cb2cc8..e1741dc 100644
+--- a/games/Albion/SR-Main/x86/SR-asm-calls.asm
++++ b/games/Albion/SR-Main/x86/SR-asm-calls.asm
+@@ -71,6 +71,8 @@
+     %define Game_chdir _Game_chdir
+     %define close _close
+     %define Game_closedir _Game_closedir
++    %define Game_close _Game_close
++    %define Game_fclose _Game_fclose
+     %define ctime _ctime
+     %define Game_dos_findclose _Game_dos_findclose
+     %define Game_dos_findnext _Game_dos_findnext
+@@ -171,14 +173,14 @@ extern Game_WaitFor2ndVerticalRetrace
+ ; 1 param
+ extern asctime
+ extern Game_chdir
+-extern close
++extern Game_close
+ extern Game_closedir
+ extern ctime
+ extern Game_dos_findclose
+ extern Game_dos_findnext
+ extern Game_dos_getvect
+ extern Game_ExitMain_Asm
+-extern fclose
++extern Game_fclose
+ extern Game_filelength
+ extern free
+ extern ftime
+@@ -798,7 +800,7 @@ SR_j___close:
+ 
+ ; eax = int handle
+ 
+-        Game_Call_Asm_Reg1 close,'get_errno_val'
++        Game_Call_Asm_Reg1 Game_close,'get_errno_val'
+ 
+ ; end procedure SR___close
+ 
+@@ -875,7 +877,7 @@ SR_fclose:
+ 
+ ; eax = FILE *fp
+ 
+-        Game_Call_Asm_Reg1 fclose,'get_errno_val'
++        Game_Call_Asm_Reg1 Game_fclose,'get_errno_val'
+ 
+ ; end procedure SR_fclose
+ 
diff --git a/pkgs/games/gog/albion/scons.patch b/pkgs/games/gog/albion/scons.patch
new file mode 100644
index 00000000..5a1c0d7b
--- /dev/null
+++ b/pkgs/games/gog/albion/scons.patch
@@ -0,0 +1,74 @@
+diff --git a/SR/SConstruct b/SR/SConstruct
+index 2fb2874..f2fb527 100644
+--- a/SR/SConstruct
++++ b/SR/SConstruct
+@@ -20,6 +20,8 @@
+ #  SOFTWARE.
+ #
+ 
++import os
++
+ udis86_path = './udis86-1.6/'
+ 
+ # set help text
+@@ -30,7 +32,8 @@ Help(vars.GenerateHelpText(env))
+ debug = env['debug']
+ 
+ # default settings
+-env = Environment(CCFLAGS      = '-O2',
++env = Environment(ENV          = os.environ,
++                  CCFLAGS      = '-O2',
+                   CPPPATH      = '.',
+                   INCPREFIX    = '-I' + udis86_path,
+                   LIBPATH      = 'libudis86',
+diff --git a/games/Albion/SR-Main/SConstruct b/games/Albion/SR-Main/SConstruct
+index 96bbefb..6743470 100644
+--- a/games/Albion/SR-Main/SConstruct
++++ b/games/Albion/SR-Main/SConstruct
+@@ -50,12 +50,14 @@ Help(vars.GenerateHelpText(env))
+ if device == 'pc-linux':
+     # default settings
+     if sdl2 > 0:
+-        env = Environment(CCFLAGS      = '-m32 -O2 -DUSE_SDL2',
++        env = Environment(ENV          = os.environ,
++                          CCFLAGS      = '-m32 -O2 -DUSE_SDL2',
+                           LINKFLAGS    = '-m32',
+                           LIBS         = ['SDL2_mixer', 'SDL2', 'pthread', 'm', 'dl']
+                          )
+     else:
+-        env = Environment(CCFLAGS      = '-m32 -O2 -DALLOW_OPENGL',
++        env = Environment(ENV          = os.environ,
++                          CCFLAGS      = '-m32 -O2 -DALLOW_OPENGL',
+                           LINKFLAGS    = '-m32',
+                           LIBS         = ['SDL_mixer', 'SDL', 'pthread', 'm', 'dl', 'GL']
+                          )
+diff --git a/games/Albion/SR-Main/x86/SConscript b/games/Albion/SR-Main/x86/SConscript
+index c88c7e9..848efa8 100644
+--- a/games/Albion/SR-Main/x86/SConscript
++++ b/games/Albion/SR-Main/x86/SConscript
+@@ -20,6 +20,7 @@
+ #  SOFTWARE.
+ #
+ 
++import os
+ import re
+ 
+ Import('device')
+@@ -38,13 +39,13 @@ nasmscan = Scanner(function = nasmfile_scan,
+ SourceFileScanner.add_scanner('.asm', nasmscan)
+ 
+ if device == 'pc-linux':
+-    env = Environment(tools=['nasm'], ASFLAGS = ' -felf -dELF -Ox -w+orphan-labels -w-number-overflow -ix86/')
+-    env2 = Environment(tools=['nasm'], ASFLAGS = ' -felf -dELF -O1 -w+orphan-labels -w-number-overflow -ix86/')
++    env = Environment(ENV=os.environ, tools=['nasm'], ASFLAGS = ' -felf -dELF -Ox -w+orphan-labels -w-number-overflow -ix86/')
++    env2 = Environment(ENV=os.environ, tools=['nasm'], ASFLAGS = ' -felf -dELF -O1 -w+orphan-labels -w-number-overflow -ix86/')
+ 
+     obj = env2.Object('Albion-main_linux.asm')
+ else:
+-    env = Environment(tools=['nasm'], ASFLAGS = ' -fwin32 -Ox -w+orphan-labels -w-number-overflow -ix86/')
+-    env2 = Environment(tools=['nasm'], ASFLAGS = ' -fwin32 -O1 -w+orphan-labels -w-number-overflow -ix86/')
++    env = Environment(ENV=os.environ, tools=['nasm'], ASFLAGS = ' -fwin32 -Ox -w+orphan-labels -w-number-overflow -ix86/')
++    env2 = Environment(ENV=os.environ, tools=['nasm'], ASFLAGS = ' -fwin32 -O1 -w+orphan-labels -w-number-overflow -ix86/')
+ 
+     obj = env2.Object('Albion-main.asm')
+ 
diff --git a/pkgs/games/gog/albion/sdl2.patch b/pkgs/games/gog/albion/sdl2.patch
new file mode 100644
index 00000000..6b4bc93f
--- /dev/null
+++ b/pkgs/games/gog/albion/sdl2.patch
@@ -0,0 +1,60 @@
+diff --git a/games/Albion/SR-Main/Albion-proc-events.c b/games/Albion/SR-Main/Albion-proc-events.c
+index c323530..97eedd1 100644
+--- a/games/Albion/SR-Main/Albion-proc-events.c
++++ b/games/Albion/SR-Main/Albion-proc-events.c
+@@ -893,19 +893,19 @@ void Game_ProcessKEvents(void)
+                     else goto _after_switch1;
+             #endif
+                 }
+-                else if ((cevent->key.keysym.unicode > 0) && (cevent->key.keysym.unicode < 128))
++                else if ((cevent->key.keysym.sym > 0) && (cevent->key.keysym.sym < 128))
+                 {
+-                    scancode = scancode_table[cevent->key.keysym.unicode];
+-                    ascii_code = cevent->key.keysym.unicode;
++                    scancode = scancode_table[cevent->key.keysym.sym];
++                    ascii_code = cevent->key.keysym.sym;
+                 }
+-                else if (cevent->key.keysym.unicode != 0)
++                else if (cevent->key.keysym.sym != 0)
+                 {
+                     scancode = 0;
+                     ascii_code = 0;
+ 
+                     if ((ascii_code == 0) && (Albion_Font_Lang != AL_UNKNOWN))
+                     {
+-                        switch (cevent->key.keysym.unicode)
++                        switch (cevent->key.keysym.sym)
+                         {
+                             case 0x00E4: // ä
+                                 ascii_code = 0x84;
+@@ -935,7 +935,7 @@ void Game_ProcessKEvents(void)
+ 
+                     if ((ascii_code == 0) && (Albion_Font_Lang == AL_ENG_FRE))
+                     {
+-                        switch (cevent->key.keysym.unicode)
++                        switch (cevent->key.keysym.sym)
+                         {
+                             case 0x00E9: // é
+                                 ascii_code = 0x82;
+@@ -998,7 +998,7 @@ void Game_ProcessKEvents(void)
+ 
+                     if ((ascii_code == 0) && (Albion_Font_Lang == AL_CZE))
+                     {
+-                        switch (cevent->key.keysym.unicode)
++                        switch (cevent->key.keysym.sym)
+                         {
+                             case 0x00E9: // é
+                                 ascii_code = 0x82;
+diff --git a/games/Albion/SR-Main/main.c b/games/Albion/SR-Main/main.c
+index c9c3125..0d32bcb 100644
+--- a/games/Albion/SR-Main/main.c
++++ b/games/Albion/SR-Main/main.c
+@@ -930,8 +930,6 @@ static void Game_Initialize2(void)
+     Init_Audio2();
+     Init_Input2();
+ 
+-    SDL_EnableUNICODE(1);
+-
+     Game_VideoAspectX = (360 << 16) / Picture_Width;
+     Game_VideoAspectY = (240 << 16) / Picture_Height;
+ 
diff --git a/pkgs/games/gog/albion/xdg-paths.patch b/pkgs/games/gog/albion/xdg-paths.patch
new file mode 100644
index 00000000..13ba4a1c
--- /dev/null
+++ b/pkgs/games/gog/albion/xdg-paths.patch
@@ -0,0 +1,259 @@
+diff --git a/games/Albion/SR-Main/virtualfs.c b/games/Albion/SR-Main/virtualfs.c
+index 34e0544..7d3acec 100644
+--- a/games/Albion/SR-Main/virtualfs.c
++++ b/games/Albion/SR-Main/virtualfs.c
+@@ -22,7 +22,9 @@
+  *
+  */
+ 
++#define _GNU_SOURCE
+ #define _FILE_OFFSET_BITS 64
++#include <fcntl.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <malloc.h>
+@@ -283,6 +285,177 @@ void vfs_visit_dir(file_entry *vdir)
+     vdir->dir_visited = 1;
+ }
+ 
++#define CONCAT_ENV(path) \
++    if (asprintf(&result, "%s/%s", env, path) == -1) { \
++        perror("asprintf"); \
++        exit(1); \
++    }
++
++#define DEFINE_XDG_GETTER(fun_name, envar, fallback) \
++    static char *fun_name(void) { \
++        const char *env; \
++        static char *result = NULL; \
++        if (result == NULL) { \
++            if ((env = getenv(envar)) != NULL) { \
++                CONCAT_ENV("albion"); \
++            } else if ((env = getenv("HOME")) != NULL) { \
++                CONCAT_ENV(fallback "/albion"); \
++            } else { \
++                fputs("Unable to determine " envar " or HOME.\n", stderr); \
++                exit(1); \
++            } \
++        } \
++        return result; \
++    }
++
++DEFINE_XDG_GETTER(getDataDir, "XDG_DATA_HOME", ".local/share");
++DEFINE_XDG_GETTER(getConfigDir, "XDG_CONFIG_HOME", ".config");
++
++static int makeDirs(const char *path)
++{
++    char *buf, *p;
++
++    if (*path == '\0')
++        return 1;
++
++    if ((buf = strdup(path)) == NULL)
++        return 1;
++
++    for (p = buf + 1; *p != '\0'; p++) {
++        if (*p != '/') continue;
++        *p = '\0';
++        mkdir(buf, 0777);
++        *p = '/';
++    }
++
++    mkdir(buf, 0777);
++
++    free(buf);
++    return 0;
++}
++
++typedef struct {
++    file_entry *dir;
++    const char *root;
++} unix_dir_cache_t;
++
++unix_dir_cache_t *unixDirCache[100];
++
++static void setUnixDir(const char *root, file_entry **dir)
++{
++    int i;
++    file_entry *newdir;
++
++    if (dir == NULL)
++        return;
++
++    for (i = 0; unixDirCache[i] != NULL; ++i) {
++        if (strcmp(unixDirCache[i]->root, root) == 0) {
++            *dir = unixDirCache[i]->dir;
++            return;
++        }
++    }
++
++    if ((newdir = (file_entry *)malloc(sizeof(file_entry))) == NULL)
++        return;
++
++    memset(newdir, 0, sizeof(file_entry));
++
++    newdir->dos_name[0] = 'X';
++    newdir->dos_name[1] = ':';
++    newdir->dos_name[2] = '\0';
++
++    newdir->real_name[0] = '.';
++    newdir->real_name[1] = '\0';
++
++    newdir->dos_fullname = strdup(newdir->dos_name);
++    newdir->real_fullname = strdup(root);
++
++    newdir->attributes = 1;
++    newdir->dir_visited = 0;
++
++    newdir->parent = newdir;
++    newdir->next = NULL;
++    newdir->prev = NULL;
++    newdir->first_child = NULL;
++    *dir = newdir;
++
++    unixDirCache[i] = (unix_dir_cache_t*)malloc(sizeof(unix_dir_cache_t));
++    if (unixDirCache[i] != NULL) {
++        makeDirs(root);
++        unixDirCache[i]->root = strdup(root);
++        unixDirCache[i]->dir = newdir;
++    }
++}
++
++static const char *manglePath(const char *xdg_path, const char *subdir,
++                              const char *path, file_entry **dir)
++{
++    char *buf;
++
++    if (*path == '/' || *path == '\\')
++        path++;
++
++    if (subdir == NULL) {
++        setUnixDir(xdg_path, dir);
++        return path;
++    }
++
++    if (asprintf(&buf, "%s/%s", xdg_path, subdir) == -1)
++        return NULL;
++
++    setUnixDir(buf, dir);
++    free(buf);
++    return path;
++}
++
++#define MANGLE_PATH(xdgpath, subdir, off) \
++    origdosname = manglePath(xdgpath, subdir, origdosname + off, &parse_dir)
++
++static void maybeCreateSetupIni()
++{
++    char *buf;
++    int fd_in, fd_out;
++    static int done = 0;
++
++    if (done) return;
++
++    if (asprintf(&buf, "%s/%s", getConfigDir(), "setup.ini") == -1) {
++        return;
++    }
++
++    if (access(buf, F_OK) == 0) {
++        done = 1;
++        free(buf);
++        return;
++    }
++
++    makeDirs(getConfigDir());
++
++    fd_out = open(buf, O_WRONLY | O_CREAT, 0666);
++    free(buf);
++
++    if (fd_out == -1)
++        return;
++
++    if ((fd_in = open("@SETUP_INI_PATH@", O_RDONLY)) == -1) {
++        close(fd_out);
++        return;
++    }
++
++    buf = malloc(8192);
++
++    while (1) {
++        ssize_t result = read(fd_in, buf, 8192);
++        if (result == -1 || result == 0) break;
++        if (write(fd_out, buf, result) != result) break;
++    }
++
++    close(fd_in);
++    close(fd_out);
++    done = 1;
++}
++
+ /*
+ return value:
+ 0 - dos path found (realdir = found entry)
+@@ -292,9 +465,20 @@ return value:
+ int vfs_get_real_name(const char *origdosname, char *buf, file_entry **realdir)
+ {
+     char upperdosname[MAX_PATH], *dosname, *backslash;
+-    file_entry *parse_dir, *new_parse_dir;
++    file_entry *parse_dir = NULL, *new_parse_dir;
+     int ret;
+ 
++    if (strncasecmp(origdosname, "xldlibs\\current", 15) == 0) {
++        MANGLE_PATH(getDataDir(), "chars", 15);
++    } else if (strncasecmp(origdosname, "saves", 5) == 0) {
++        MANGLE_PATH(getDataDir(), "saves", 5);
++    } else if (strncasecmp(origdosname, "setup.ini", 10) == 0) {
++        maybeCreateSetupIni();
++        MANGLE_PATH(getConfigDir(), NULL, 0);
++    } else if (strncasecmp(origdosname, "setup.tmp", 10) == 0) {
++        MANGLE_PATH(getConfigDir(), NULL, 0);
++    }
++
+     // convert dos name to uppercase
+     {
+         int i;
+@@ -316,28 +500,30 @@ int vfs_get_real_name(const char *origdosname, char *buf, file_entry **realdir)
+ 
+ 
+     // find initial directory for parsing
+-    if (dosname[0] == '\\')
+-    {
+-        parse_dir = &Game_CDir;
+-        dosname++;
+-    }
+-    else if (dosname[0] == 'C' && dosname[1] == ':')
+-    {
+-        if (dosname[2] == '\\')
++    if (parse_dir == NULL) {
++        if (dosname[0] == '\\')
+         {
+             parse_dir = &Game_CDir;
+-            dosname+=3;
++            dosname++;
++        }
++        else if (dosname[0] == 'C' && dosname[1] == ':')
++        {
++            if (dosname[2] == '\\')
++            {
++                parse_dir = &Game_CDir;
++                dosname+=3;
++            }
++            else
++            {
++                parse_dir = Game_Current_Dir;
++                dosname+=2;
++            }
+         }
+         else
+         {
+             parse_dir = Game_Current_Dir;
+-            dosname+=2;
+         }
+     }
+-    else
+-    {
+-        parse_dir = Game_Current_Dir;
+-    }
+ 
+     // find directory
+     for (backslash = strchr(dosname, '\\'); backslash != NULL; backslash = strchr(dosname, '\\'))
diff --git a/pkgs/games/gog/default.nix b/pkgs/games/gog/default.nix
index 111091c3..8d8379a3 100644
--- a/pkgs/games/gog/default.nix
+++ b/pkgs/games/gog/default.nix
@@ -10,6 +10,8 @@ let
     fetchGog = callPackage ./fetch-gog {
       inherit (config.gog) email password;
     };
+
+    albion = callPackage_i686 ./albion { inherit (pkgs) buildSandbox; };
   };
 in {
   options.gog = {