Playing Modern FNA Games on the ODROID Platform

FNA (http://fna-xna.github.io/) is an open source re-implementation of XNA. XNA is a game framework created by Microsoft Corp., and is quite popular. A few notable games have been written with XNA (which is for MS Windows only) and later ported to Linux and MacOS using FNA and/or MonoGames.

Amongst those games, you can find some gems like Bastion, FEZ, Stardew Valley, Owlboy and Terraria, to list just a few. To have a better idea of what game use this framework, go to https://goo.gl/4MGnys or http://www.monogame.net/showcase/

Figure 01 - Stardew Valley

Now, this framework is interesting for ODROID SBCs, because it is not based on C or C++, but on C#. The big advantage of games compiled with C# is that they can run on any Linux variant (any CPU architecture) as long as Mono is supported and running on it. With a bit of work, most of those games can be made to work without having the source code of the game. It is suffice to just use binaries built for x86 Linux.

Pre-requisites Of course, to launch those games, some preparation and compilation will be needed. Along with Mono, we will need some libraries to support the games, like SDL2 and also a few specific libraries required by the games. We cannot use them here because of the ARM architecture of the ODROID. We will also need a fresh version of GL4ES because all those games use OpenGL 2.1+ extensions at the minimum. This guide should work with the default HardKernel image or with ODROID GameStation Turbo (OGST).  First, let us make sure everything is up to date (do not forget to answer ‘Yes’ if asking to upgrade):

$ sudo apt update && sudo apt upgrade
Now that latest version of everything is installed, let’s install the standard libraries we will use. Start with SDL2 and related libraries:
$ sudo apt install libsdl2-2.0 libsdl2-net-2.0 libsdl2-mixer-2.0 libsdl2-ttf-2.0
To install Mono, run the following command:
$ sudo apt install mono-complete mono-xbuild
After those installations, we are almost ready. The problem is that SDL2 may not be compiled for OpenGL support, and some other libraries are missing from repo and needs to be built from sources. So, just to be on the safe side, lets install a few development stuff (you may already have most or all of them installed):
$ sudo apt install build-essential git mercurial cmake libgl1-mesa-dev
$ sudo apt install libtheora-dev libvorbis-dev
If you use OGST and do not want to build all those libraries, you can simply use the one built by @meveric for you with the following command:
$ sudo apt install monolibs-odroid
For the others who want to build needed components themselves, get ready for some serious building. Build some libraries We will build some libraries and put them in a easy to find folder, so we can direct the search of those libraries with the LD_LIBRARY_PATH trick, so let us create that folder, named monolibs in your ~home folder:
$ mkdir ~/monolibs
Now, let us build the libraries we need, namely: gl4es, SDL2 with OpenGL support, mojoshaders and libtheroaplay.

GL4ES This library allows the use of many OpenGL 1.x and 2.x software/games on GLES hardware. It is the central piece of software, along with Mono, that allows all those game to run on the ODROID. The sources are on my github account, so let us get the latest sources:

$ cd ~
$ git clone https://github.com/ptitSeb/gl4es.git
Once you have cloned the repo, to get latest sources, you simply go inside the repo and type “git pull”. Now, configure the build for the ODROID:
$ cd gl4es
$ cmake -DODROID=1  -DCMAKE_BUILD_TYPE=RelWithDebInfo .
and build the libraries:
$ make -j2
Then, simply copy it in the “monolibs” folder for later use: $ cp lib/libGL.so.1 ~/monolibs/

SDL2 We already have SDL2 installed, but it may be the version that only supports GLES and not OpenGL. So it is safe to build a new version. It is not that complicated anyway. Let us use the version that is on my GitHub account. Any other version will work, and it is just for convenience to use mine:

$ cd ~
$ git clone https://github.com/ptitSeb/SDL2.git
Now configure the build for OpenGL (we will do out-of-tree build this time):
$ cd SDL2
$ mkdir build
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo.
The configure step will run for a bit. You should see, among a lot of other things: “--VIDEO_OPENGL (Wanted: ON): ON”. So now, let us build this library (it will take a bit longer than gl4es):
$ make -j2
We can now copy the freshly built library to the monolibs directory:
$ cp libSDL2-2.0.so.0 ~/monolibs/
mojoshaders That library is one of the utility libraries made by Icculus to help porting windows code to Linux. This particular library converts shaders written for DirectX to GLSL shaders for OpenGL (or Metal). This library is used by FNA to transparently use the DirectX shaders on OpenGL. We will get the source directly from the Icculus’s repo, using mercurial this time:
$ cd ~
$ hg clone http://hg.icculus.org/icculus/mojoshader
Let us configure (to produce a shared library, because by default, it does not)
$ cd mojoshader
$ cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED=ON \
-DDEPTH_CLIPPING=ON -DFLIP_VIEWPORT=ON
and make the lib:
$ make -j2
We will copy this library also and go to the next:
$ cp libmojoshader.so ~/monolibs/
XNAFileDialog Some games use this library. Let us build a version of the native part just in case.
$ cd ~
$ git clone https://github.com/flibitijibibo/XNAFileDialog.git
$ cd XNAFileDialog/native
$ make
$ cp libXNAFileDialog.so ~/monolibs/
LibTheoraPlay Some games use libtheoraplay for the videos (“A Virus Named TOM” for example). This lib is a bit tricky because it comes in 2 parts - the C part and the C# part, but the C# part needs some adjustment to run on ARM, as there is a workaround for some issue on Mono/x86 that does not apply here (and breaks things).
$ cd ~
$ git clone https://github.com/flibitijibibo/TheoraPlay-CS.git
The patch is simple: open TheoraPlay.cs from ~/TheoraPlay-CS with your favorite text editor and go to around line 155. Search for the following line: /* This is only a problem for Mono. Ignore for Win32 */ if (Environment.OSVersion.Platform != PlatformID.Win32NT && IntPtr.Size == 4) and replace the big “if” that is split in 2 lines with a simple if ( false ) Now, let us build the C part of the lib and copy in monolibs:
$ cd TheoraPlay-CS/TheoraPlay
$ cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .
$ make
$ cp libtheoraplay.so ~/monolibs/
Now, let us build the C# part of the library (it is an MS Windows dll) and copy the dll to the same place, for future use. We can safely ignore the .dll.config file, we will not need it:
$ cd ..
$ xbuild /p:Configuration=Release 
$ cp bin/Release/TheoraPlay-CS.dll ~/monolibs/
TheoraFile Some games use this library. Let us build a version of the native part just in case.
$ cd ~
$ git clone https://github.com/FNA-XNA/Theorafile.git
$ cd Theorafile
$ make
$ cp libtheorafile.so ~/monolibs/
Other libraries Some games may ask for other libraries that are more difficult to build or not opensource. For example, Hacknet will ask for libcef (that is basically “Chrome in a lib”), or Bastion will ask for FMODex (that is closed source). For those games, you are on your own, but if you have a working solution, do not hesitate to go to the ODROID forum and add a post about that. FMOD can be downloaded for ARMHF, but does not seem to exist for ARM64 (so I could not test on my N1). To get FMOD, you need to register at http://www.fmod.com and download fmodstudioapi for linux (you will get an archive like, fmodstudioapi11006linux.tar.gz). Extract and copy libfmod to monolibs with:
$ cd ~
$ tar xf fmodstudioapi11006linux.tar.gz
$ cp api/lowlevel/lib/armhf/libfmod.so ~/monolibs/
For FMODex, you can then use the little wrapper I wrote:
$ cd ~
$ git clone https://github.com/ptitSeb/fakemodex.git
$ cd fakemodex
$ cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .
$ make -j2
$ cp lib/libfmodex.so ~/monolibs/
Extract games Now, we need to install some games on the ODROID. The games need to use FNA or MonoGames here, coming from GoG, HumbleBundle, or even Steam for some of them. They all need to be the Linux version of the game. Most of the time, the Windows version will not work. You should note that the Steam version of some games will also not run without Steam launched (DRM), and because we do not have Steam on the ODROID, that will simply prevent us to run the game on the ODROID. Use the GOG or HB (or any other DRM-free) version for games you want to run on your ODROID.

Steam or other Linux installed version If you have an installed Linux version of the game, simply copy the entire folder of the game and you are ready to go. The Steam version of FEZ or Owlboy, for example, can be used.

Humble Bundle version Many games now come as a large single file ending with “-bin”. These games can be easily extracted from this - there a zip file embedded and all games is inside the “data” folder. For example, “A Virus Named TOM” comes as “avnt-10192013-bin”, and “Towerfall:Assension” is “towerfall-07212016-bin”. Also, because some game have a “data” that will conflict with extracted “data” folder, let us temporarily renamed it to “ODROID”. To prepare A Virus Named TOM, you can do:

$ cd ~
$ mkdir AvirusNamedTOM
$ cd AVirusNamedTOM
$ unzip ~/avnt-10192013-bin data/*
$ mv data ODROID
$ mv ODROID/* .
$ rm -r ODROID
GOG version Games packages by GOG are quite similar to extract. For bastion, I have a ”gog_bastion_2.0.0.1.sh” that also contains a zip file:
$ cd ~
$ mkdir Bastion
$ cd Bastion
$ unzip ~/gog_bastion_2.0.0.1.sh data/noarch/game/*
$ mv data ODROID
$ mv ODROID/noarch/game/* .
$ rm -r ODROID
Figure 02 - FEZ01

Launch the games Finally, we are ready for some action. We need to remove a few libraries from the install first. Because we will use the version of Mono that comes with the ODROID, and not the one embedded in the games, there is some cleanup to do first:

$ cd ~/AvirusNamedTOM
$ rm mscorlib.dll
$ rm System.*dll
$ rm Mono.*.dll
Now, some games (like A Virus Named Tom), use TheoraPlay. It can be under 2 names: “TheoraPlay-CS.dll” or “TheoraPlay#.dll”. If you see any of this, be sure to replace with the one we built earlier or you will have crashes when video start (only on 32bits, 64bits are safe). For the HB version of A Virus Named TOM, that will be:
$ cd ~/AvirusNamedTOM
$ cp ~/monolibs/TheoraPlay-CS.dll TheoraPlay#.dll
Figure 03 - TwerFall04

Now we can run the game. We need to setup a few things to have GL4ES emulating OpenGL2 and we also need to use all the libraries in monolibs. Locate the “.exe” file and simply run it with mono. For “A Virus Named TOM” the commands are:

$ cd AvirusNamedTOM
$ LC_ALL=”C” LIBGL_ES=2 LIBGL_GL=21 LIBGL_DEFAULTWRAP=2 \
LIBGL_FBOFORCETEX=1 LD_LIBRARY_PATH=~/monolibs mono CircuitGame.exe
We can go further, with additional components.

Resampling Audio You may notice some games take some time to initialize and use quite a lot of memory. Most of the time, this is due to the sound part of the game, where everything is loaded into memory at start. If you have the memory issue or simply want to experiment, I have developed a small tool that can be used to resample the data. The tool is easily built using the following commands:

$ sudo apt install libsox-dev
$ cd ~
$ git clone https://github.com/ptitSeb/rexwb.git
$ cd rexwb
$ cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .
$ make
Using the tool is fairly easy. You have to understand “xwb” WaveBank is likely sampled at 44kHz, using MSADPCM compression. While this is pretty efficient in Windows, most versions on Linux expand the MSADPCM to classic PCM format (so size * 4), leading to having large chunks of the sound file in memory. Resampling the sounds in wavebanks to Mono (instead of Stereo) and resampling to 22kHz (or lower) lower the memory pressure. Games that have xwb include “A Virus Named TOM” and “Stardew Valley”. You will find the wavebanks inside Content/Audio. Note that rexwb always work on a copy of the wavebanks. To resample TOM’s wavebank (this games has 2 wavebanks, only the BGM one can be resample, or both, depend on individual choices) to mono/22kHz you will use the following commands:
$ cd ~/AvirusNamedTOM
$ cd Content/Audio
$ mv BGMwaves.xwb BGMwaves.xwb.sav
$ ~/rexwb/rexwb BGMwaves.xwb.sav BGMwaves.xwb 22050 -f -m
$ mv SFXwaves.xwb SFXwaves.xwb.sav
$ ~/rexwb/rexwb SFXwaves.xwb.sav SFXwaves.xwb 22050 -f -m
FEZ On FEZ, I had an issue on my N1 prototype with “Hardware Instancing”. This is certainly some bug in GL4ES I have to track down (I did not have those issues on OpenPandora), so if you have a crash at start, simply disable Instancing in option menu. Also, this game uses a huge drawing list of more the 400,000 triangles to draw those stars in the Menu screen and a few game screens. While some power beast ODROIDs like the N1 can handle that, some other models may have issue with that kind of drawing. You can activate a special hack in GL4ES to avoid this draw. With your preferred text editor, go into the gl4es folder and edit src/gl/drawing.c. Look for “#if 0” in that file (around line 207) and change it to “#if 1”. Rebuild the library and copy it to monolibs to have a version that will not draw the starfield for a smoother main menu.

Figure 04 - FEZ

Stardew Valley I have noticed a few graphics issue with Stardew Valley, but nothing serious using the Steam version. One thing to note is that some dll’s want to load “oal_soft.dll”. It should be redirected to “libopenal.so” but somehow, it is not. Easier way is to create a symlink inside Stardew valley folder to “libopenal.so” named “liboal_soft.so” and it will work. On my N1, which is ARM64, the command would be:

$ cd ~/StardewValley
$ ln -s /usr/lib/aarch64-linux-gnu/libopenal.so libsoft_oal.so
But it will be similar on 32-bit ARM:
$ cd ~/StardewValley
$ ln -s /usr/lib/arm-linux-gnueabihf/libopenal.so libsoft_oal.so
Figure 05 - Stardew Valley

A Virus Named TOM I have tested this game on the ODROID-N1. I had some graphical issues with this game, where the image is limited to a subpart of the whole picture. It is probably a bug of gl4es, but it may also be a bug in GLES driver of the N. I have not seen any issue with the OpenPandora during my testing.

Figure 06 - TOM

Bastion This one basically needs FMODex. So you probably need to download FMOD and build the fakemodex wrapper to play this one.

Hammerwatch While it is not an FNA game, Hammerwatch use a custom engine and is also done in C#. Hammerwatch can be run in the same way. However, note that the latest version (1.32) uses FMOD for music and sound, so you need to get the native version of it (FMOD, not FMODex). THe older version (without the dlc) does not use fmod.

Other games

Figure 07 - Dust: An Elysian Tail

Figure 08 - Towerfall

Figure 09 - Owlboy

Figure 10 - Owlboy

Do not hesitate to go to the ODROID forums and create some posts to discuss about your successes and failures with FNA.

Be the first to comment

Leave a Reply