I bought a Raspberry Pi 4 not too long ago. Intended as a lightweight, “get back into programming” development machine (and to be used as an at-home gadget central) I didn’t really investigated the state of gaming for it.
But once it was up and running with a 20.10 Ubuntu Mate 64-bit, I started wondering: can I play something on this thing? Nothing serious of course, the hardware was never intended for that (although I fully intend to push it to the limit) but surely I could run a few sessions of FTL in-between coding and stuff?
The Raspberry Pi is a computer. Just a different kind of computer.
Without going into too much detail: the Raspberry Pi 4 uses a 64-bit ARM processor. (Similar to the M1 Macs.) Which means that any and all programs compiled for an x86-based architecture (which is roughly 99,99% of binaries) won’t run on it out-of-the-box.
So, if we’re to restrict the current discussion for games, and even further to FTL in particular, I either needed an arm64 binary or a way to run an amd64 binary on my arm64 system.
Sidenote: the Raspberry Pi, by default (and ‘by default’ I mean ‘by recommendation’) runs a 32-bit OS called Raspbian. It’s based on Debian, and it’s fine – but I wanted to run a 64-bit OS on my 64-bit hardware, and take full advantage of the 8GB of RAM the board is configured with. This complicates things a bit, because most how-tos and other articles are based on using either the 32-bit Raspbian or some similar alternative (like Twister OS for games); and even then much of that information is outdated by a lot. That’s why I figured I’d record my experience with FTL (and, in the future, with other things) and contribute to the discussion from my particular viewpoint: running a 64-bit OS on the Raspberry Pi 4.
Neither FTL, nor most other applications come with arm64 binaries. Understandably: for the vast majority of developers, the niche use case of an arm64 platform isn’t worth the trouble of compiling and maintaining arm64-specific binaries. This might change with the M1 chip, but the Rosetta translation layer and other conveniences will still hold that gap open for a while.
But, as you can see from the cover image, I did manage to get FTL running on my 64-bit Raspberry Pi. So how did I do it?
An amazing developer by the name of ptitSeb has created box86: an emulation layer for non-x86 systems to run x86-based applications. Now, box86 isn’t really what I was looking for, since I was running a 64-bit OS. But, as luck would have it, pitSeb also maintains box64, the 64-bit version of the same tool. Although I was fully prepared to venture into the dungeon of chrooting a 32-bit environment so I could run an x86-emulation layer… thanks to box64 I didn’t have to.
Setting up box64 on my RPi4 was a piece of cake, just needed to follow the instructions:
git clone https://github.com/ptitSeb/box64 cd box64 mkdir build; cd build; cmake .. -DRPI4ARM64=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo make -j4 sudo make install sudo systemctl restart systemd-binfmt
This compiles and installs box64 on the RPi4.
From this point on, I can run an x86-64 binary automagically, or specify it with
Caveat: box64 does an amazing job out of the box. But it’s not a magic bullet, and isn’t capable of turning the arm64 system into an amd64 one. Some additional assembly may be required. Which brings us to:
FTL on arm64
Now that I was capable of running amd64 applications on my arm64 RPi4, it was time to see a man about a spaceship.
I downloaded my copy for the Linux version of FTL from GoG. The GoG Linux games come in the form of a .sh file, that is the installer for the game. Unfortunately, running that didn’t work:
After a bit of Googling, I found from a forum post explaining that I can unpack the GoG installer like so:
unzip path/to/gog/sh/installer -d /path/to/unpack
Which worked just fine. (I forgot to bookmark said blog post, and now for the life of me cannot find it to give credit. Sorry.)
Now I had another .sh file in the form of
~/apps/ftl/data/noarch/start.sh. However, running that didn’t work either:
From what I could tell (by running a cat start.sh) the binary would check if the system was amd64. Mine isn’t. If not, it’d default to x86. Which mine isn’t either.
As I was digging through the folders, I found a number of binaries:
dinchamion@tranquility-pi:~$ ls -l apps/ftl/data/noarch/game/data/ total 400112 -rwxrwxr-x 1 dinchamion dinchamion 16438 jan 9 2020 exe_icon.bmp -rwxrwxr-x 1 dinchamion dinchamion 394 júl 18 11:25 FTL -rwxrwxr-x 1 dinchamion dinchamion 72161049 jan 9 2020 FTL.amd64 -rwxrwxr-x 1 dinchamion dinchamion 278197004 jan 9 2020 ftl.dat -rw-rw-r-- 1 dinchamion dinchamion 502 júl 18 10:42 FTL.log -rwxrwxr-x 1 dinchamion dinchamion 59309550 jan 9 2020 FTL.x86 drwxrwxr-x 2 dinchamion dinchamion 4096 jan 9 2020 licenses
Interesting. So I tried to run the amd64 binary directly…
dinchamion@tranquility-pi:~$ apps/ftl/data/noarch/game/data/FTL.amd64 Box64 with Dynarec v0.1.3 ba33593 built on Jul 17 2021 14:21:17 Using default BOX64_LD_LIBRARY_PATH: ./:lib/:lib64/:x86_64/:bin64/:libs64/ Using default BOX64_PATH: ./:bin/ Counted 61 Env var Looking for apps/ftl/data/noarch/game/data/FTL.amd64 Using native(wrapped) libGL.so.1 Using native(wrapped) libX11.so.6 Using native(wrapped) libasound.so.2 Using native(wrapped) libdl.so.2 Using native(wrapped) libpthread.so.0 Using native(wrapped) librt.so.1 Using native(wrapped) libm.so.6 Using native(wrapped) libc.so.6 Using native(wrapped) ld-linux-x86-64.so.2 Using native(wrapped) libXi.so.6 Using native(wrapped) libXext.so.6 Using native(wrapped) libxcb.so.1 Using native(wrapped) libXau.so.6 Using native(wrapped) libXdmcp.so.6 Using native(wrapped) libXinerama.so.1 Using native(wrapped) libXrandr.so.2 Using native(wrapped) libXrender.so.1 Using native(wrapped) libXxf86vm.so.1 Version: 1.6.12 Loading settings Initializing Crash Catcher... Starting up Loading text Initializing Video Couldn't find in dictionary: window_title Video: 1280x720, windowed Video Initialized Renderer: OpenGL version 2.1 (GL_VERSION: 2.1 Mesa 20.2.6) Creating FBO...
… aaaaaaand no dice. :-(
At this point, Google was useless, as I didn’t have anything specific to search for, and – as mentioned – most hits would be for a 32-bit system.
I was close to giving up. “It’s fine,” I told myself. “You have FTL on your iPad. And this was meant to be a work computer anyhow.”
But I’m the stubborn sort when it comes to problem solving. And my brain just couldn’t let it go.
And the secret ingredient is…
After a day or two of pointedly not messing with it, I went back into the unzipped folders, looking for a logfile or some other piece of information I could use for a Google search. (Because we don’t know things these days. We know how to google for them. External memory, y’all, we’re cyborgs.) I didn’t find any.
What I did find, however, was that the
FTL file (that I thought was just a generic executable binary) had a size that didn’t really make sense. 394 bytes. Hm.
So I opened it in VS Code. Because why not.
As I was looking at it, I noticed the IF statement conditions on line 11:
&& ( [ "$machine" = 'x86_64' ] || [ "$machine" = 'amd64' ] )
And I thought to myself: can it be that easy? So I added another condition:
&& ( [ "$machine" = 'x86_64' ] || [ "$machine" = 'amd64' ] || [ "$machine" = 'aarch64' ] )
So my “unknown” (for the game) architecture would be interpreted as amd64, prompting the loading of the binary. Save, and pray:
dinchamion@tranquility-pi:~$ apps/ftl/data/noarch/start.sh Running FTL: Advanced Edition Loading Arch = amd64 Box64 with Dynarec v0.1.3 ba33593 built on Jul 17 2021 14:21:17 Using default BOX64_LD_LIBRARY_PATH: ./:lib/:lib64/:x86_64/:bin64/:libs64/ Using default BOX64_PATH: ./:bin/ Counted 62 Env var Looking for /home/dinchamion/apps/ftl/data/noarch/game/data/FTL.amd64 Using native(wrapped) libGL.so.1 Using native(wrapped) libX11.so.6 Using native(wrapped) libasound.so.2 Using native(wrapped) libdl.so.2 Using native(wrapped) libpthread.so.0 Using native(wrapped) librt.so.1 Using native(wrapped) libm.so.6 Using native(wrapped) libc.so.6 Using native(wrapped) ld-linux-x86-64.so.2 Using native(wrapped) libXi.so.6 Using native(wrapped) libXext.so.6 Using native(wrapped) libxcb.so.1 Using native(wrapped) libXau.so.6 Using native(wrapped) libXdmcp.so.6 Using native(wrapped) libXinerama.so.1 Using native(wrapped) libXrandr.so.2 Using native(wrapped) libXrender.so.1 Using native(wrapped) libXxf86vm.so.1 Version: 1.6.12 Loading settings Initializing Crash Catcher... Starting up Loading text Initializing Video Video: 1280x720, windowed Video Initialized Renderer: OpenGL version 2.1 (GL_VERSION: 2.1 Mesa 20.2.6) Creating FBO... Starting audio library... Audio Initialized! Resource Preload: 16.310 Initializing animations... Animations Initialized! Loading Ship Blueprints.... Blueprints Loaded! Initializing Sound Data.... Generating world... Loading achievements... Loading score file... Running Game!
I’m super happy to be able to play FTL, smoothly and with no issues, on my RPi4. And I’m even happier to have been able to figure this out on my own.
Granted, I’m not entirely sure why it worked. And I’m definitely standing on the shoulder of giants: without ptitSeb’s box64, I couldn’t have run it at all.
Still, I count this as a victory. I can figure out the exact paramaters of the “why” later. I can experiment and learn more from this as I progress. Every journey starts with the first step, and I’m happy I took this one.
Now, let’s see about that spaceship.