Since Baldur’s Gate: Siege of Dragonspear came to Android, I’ve been gripped by nostalgia for turn-of-the-millenium Bioware RPGs. I barely played Neverwinter Nights back in the day (I didn’t buy it at release, and when I got it I was already busy with university and mostly abandoned gaming for a few years), so I bought last year’s Enhanced Edition from Beamdog to install on my GPD Pocket. The game is also available via Steam, but I decided to buy directly from the publisher, advertised as DRM-free. Sadly, while that may be strictly true, I found that the game is meant to be launched via the Beamdog Client application which requires an internet connection to authenticate.
“DRM-free” - But Online Authentication Required?!?
The client offers to continue in offline mode, but the button to launch the game will be greyed out. This annoyed me for practical reasons (I’m not always connected to the internet, sometimes quite intentionally) and on principle (I bought the game, so I should be able to play it how, when and where I want; enforcing online authentication is merely another - and particularly inconvenient - form of DRM).
So I set about finding out if we can run the game without the Beamdog Client and an active internet connection. As it turns out, the answer is yes. If you only care about the solution, jump straight to the conclusion. If you want to understand what’s happening, read on.
Locating The Binary
Conveniently, the Beamdog Client leads us straight to the binary - there is a button to “Open Game Location”. On my machine, that was
~/Beamdog Library/00785/ and we can drill down to the binary at
bin/linux-x86/nwmain-linux. But when I tried to run it by double-clicking, I got an error saying there is no application installed to open “shared library” files.
Checking the file on the command line shows that
nwmain-linux is a ELF 32-bit binary:
$ file nwmain-linux nwmain-linux: ELF 32-bit LSB shared object, Intel 80386, version 1 (GNU/Linux), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=fa7fe51896abe7ffad299687f367ceb37b1a49cd, stripped
If we try to run it from the command line (
./nwmain-linux in the install directory), we fail again, but this time we get an informative error:
./nwmain-linux: error while loading shared libraries: libopenal.so.1: cannot open shared object file: No such file or directory
Aha! This tells us the binary relies on a shared library that is not present in the default path searched by the dynamic linker at run time. Using
ldd on the binary lists all dynamically-linked libraries required by our binary, and
libopenal.so.1 is the only one it cannot find:
$ ldd nwmain-linux linux-gate.so.1 => (0xf7f56000) libGL.so.1 => /usr/lib/i386-linux-gnu/mesa/libGL.so.1 (0xf1c20000) librt.so.1 => /lib/i386-linux-gnu/librt.so.1 (0xf1c17000) libopenal.so.1 => not found // <-- HERE! libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0xf1c12000) libstdc++.so.6 => /usr/lib/i386-linux-gnu/libstdc++.so.6 (0xf1a9b000) libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xf1a45000) libgcc_s.so.1 => /lib/i386-linux-gnu/libgcc_s.so.1 (0xf1a28000) libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0xf1a0b000) libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf1855000) libexpat.so.1 => /lib/i386-linux-gnu/libexpat.so.1 (0xf182b000) libxcb-dri3.so.0 => /usr/lib/i386-linux-gnu/libxcb-dri3.so.0 (0xf1826000) libxcb-present.so.0 => /usr/lib/i386-linux-gnu/libxcb-present.so.0 (0xf1822000) libxcb-sync.so.1 => /usr/lib/i386-linux-gnu/libxcb-sync.so.1 (0xf181a000) libxshmfence.so.1 => /usr/lib/i386-linux-gnu/libxshmfence.so.1 (0xf1817000) libglapi.so.0 => /usr/lib/i386-linux-gnu/libglapi.so.0 (0xf17f9000) libXext.so.6 => /usr/lib/i386-linux-gnu/libXext.so.6 (0xf17e3000) libXdamage.so.1 => /usr/lib/i386-linux-gnu/libXdamage.so.1 (0xf17df000) libXfixes.so.3 => /usr/lib/i386-linux-gnu/libXfixes.so.3 (0xf17d8000) libX11-xcb.so.1 => /usr/lib/i386-linux-gnu/libX11-xcb.so.1 (0xf17d5000) libX11.so.6 => /usr/lib/i386-linux-gnu/libX11.so.6 (0xf168a000) libxcb-glx.so.0 => /usr/lib/i386-linux-gnu/libxcb-glx.so.0 (0xf166e000) libxcb-dri2.so.0 => /usr/lib/i386-linux-gnu/libxcb-dri2.so.0 (0xf1668000) libxcb.so.1 => /usr/lib/i386-linux-gnu/libxcb.so.1 (0xf1642000) libXxf86vm.so.1 => /usr/lib/i386-linux-gnu/libXxf86vm.so.1 (0xf163b000) libdrm.so.2 => /usr/lib/i386-linux-gnu/libdrm.so.2 (0xf1627000) /lib/ld-linux.so.2 (0xf7f58000) libXau.so.6 => /usr/lib/i386-linux-gnu/libXau.so.6 (0xf1622000) libXdmcp.so.6 => /usr/lib/i386-linux-gnu/libXdmcp.so.6 (0xf161b000)
What Is Dynamic Linking, And How Can We Make It Work?
Dynamic linking means that your program relies on code (usually shared with many other programs) that is not included in the compiled binary but expected to be present on your system and looked up (by a utility appropriately called the ‘dynamic linker’,
ld.so on Linux) at run time. All non-trivial programs rely on external code (if just the C Standard Library), and linking at runtime allows smaller binaries and better maintainability than static linking, which resolves all external dependencies at compile time by including them in the binary. Statically linked binaries are huge and need to be recompiled to benefit from updates to the external library1. In the case of Beamdog, I makes sense that their games share code that won’t be present (or even available) on a standard Linux install, so it’s reasonable for them to package these libraries with their client. When their client lauches the game, it will do so using some sort of wrapper that tells it where to find all dependencies.
To run Neverwinter Nights outside the Beamdog Client, we need to do the same: find the shared library and tell the dynamic linker its location when we launch the game2.
To locate the library, we use
sudo to minimize error messages from permission errors traversing the file system):
$ sudo find / | grep 'libopenal\.so\.1' /home/m/.config/Beamdog Client/steam-runtime-release_2014-04-15/amd64/usr/lib/x86_64-linux-gnu/libopenal.so.1 /home/m/.config/Beamdog Client/steam-runtime-release_2014-04-15/amd64/usr/lib/x86_64-linux-gnu/libopenal.so.1.13.0 /home/m/.config/Beamdog Client/steam-runtime-release_2014-04-15/i386/usr/lib/i386-linux-gnu/libopenal.so.1 /home/m/.config/Beamdog Client/steam-runtime-release_2014-04-15/i386/usr/lib/i386-linux-gnu/libopenal.so.1.13.0
m is my username and thus the name of my home directory.)
As expected, the library we need is packaged with the Beamdog Client’s runtime and it comes in 32-bit and 64-bit flavours. We know from the
ldd output above that
nwmain-linux is a 32-bit binary, so we’ll need that version. We can tell the dynamic linker to add directories to its search path by setting
LD_LIBRARY_PATH before the command:
LD_LIBRARY_PATH='<home directory>/.config/Beamdog Client/steam-runtime-release_2014-04-15/i386/usr/lib/i386-linux-gnu/' ./linux-x86/nwmain-linux
And voila! We have Neverwinter Nights running without the Beamdog Client or an active internet connection.
Fixing No-sound Issue (You May Not Have This Problem)
At this point, I encountered another problem: the game ran, but without sound. I checked the console, and found this error:
Cannot open shared library libasound_module_conf_pulse.so
Some googling showed that I was missing the 32-bit version of the Pulse Audio drivers. The fix was straightforward (again, via Google):
sudo dpkg --add-architecture i386 sudo apt-get install libasound2-plugins:i386
Wrapping It Up
Now the game works, but we really want to launch the game without manually
cding into the install directory and typing a command every time, so let’s make a few more improvements. The final command line look like this:
(cd /home/m/Beamdog\ Library/00785/bin/linux-x86/ && LD_LIBRARY_PATH='/home/m/.config/Beamdog Client/steam-runtime-release_2014-04-15/i386/usr/lib/i386-linux-gnu/' /home/m/Beamdog\ Library/00785/bin/linux-x86/nwmain-linux)
/home/m/ with your home directory, and change the Beamdog directory if yours in in a custom location. The parentheses are there to execute the command in a sub-shell that first changes directory to the install directory (without affecting the parent shell’s working directory) - this is because the game won’t work if we are not running it from the directory containing its required assets.
#!/bin/bash (cd /home/m/Beamdog\ Library/00785/bin/linux-x86/ && LD_LIBRARY_PATH='/home/m/.config/Beamdog Client/steam-runtime-release_2014-04-15/i386/usr/lib/i386-linux-gnu/' /home/m/Beamdog\ Library/00785/bin/linux-x86/nwmain-linux)
Now make a
Neverwinter Nights.desktop file which points to the script:
[Desktop Entry] Version=1.0 Name=Neverwinter Nights Exec=/home/m/nwn.sh Path=/home/m/ Terminal=false Icon=/home/m/Beamdog Library/00785/neverwinter-icon.png Type=Application
The icon is the original Neverwinter Nights icon which I found online and saved to the install directory. After
chmoding the script and
.desktop file to be executable, I can use it to run Neverwinter Nights with one click.
Given that Beamdog needs to include shared libraries that may not be present on customers’ machines, I can see why they went the route of installing, updating and launching their games via a monolithic client. It’s basically Steam, packaged just for their games. However, since the games are advertised as DRM-free, online authentication should not be required to launch the client and run installed titles. Either the Beamdog Client should be improved to include a working offline mode, or Beamdog should provide an automated option to do what we just did manually: create a shortcut wrapper to launch the game without opening the client.
- I’m hardly qualified to explain dynamic linking to anyone, but if you want a better overview, I recommend chapter 41 of The Linux Programming Interface ↩
- I have not tried this, but if the game and the client have no other DRM mechanisms, this workaround should also allow you to copy the game files and object libraries to a different machine and run it without ever needing to install the Beamdog Client or authenticate your purchase. That’s fine if you own the game, but it’s piracy if you don’t (it will also cut you off from easy updates). Suffice to say I don’t share the black-and-white view that all piracy is evil, but this game is fairly priced and Beamdog is a wonderful company that does admirable work and is worth supporting. Please do so. ↩