Blog post about Stardew Valley
This commit is contained in:
220
content/post/stardew-valley.md
Normal file
220
content/post/stardew-valley.md
Normal file
@@ -0,0 +1,220 @@
|
||||
+++
|
||||
title = "Stardew Valley on aarch64"
|
||||
date = "2020-01-15"
|
||||
tags = ["PBP", "hacking"]
|
||||
+++
|
||||
|
||||
#### Stardew Valley on aarch64
|
||||
|
||||
At the end of last year I got a [Pinebook Pro](https://pine64.org/pinebook-pro) -
|
||||
mostly for reasons of paranoia. So far, it's been pretty good, but there was
|
||||
one thing that I couldn't get working: [Stardew Valley](https://stardewvalley.com/)
|
||||
|
||||
You could call me a little bit addicted to this game, but it's proprietary,
|
||||
closed-source, and the authors don't release binaries compiled for aarch64 -
|
||||
although they do very kindly release x86_32 and x86_64 Linux binaries, which
|
||||
is more than most companies do.
|
||||
|
||||
I [left a message on the forum](https://community.playstarbound.com/threads/arm-arm64-aarch64-linux-support.158840/)
|
||||
and moved on, confident that it wouldn't ever happen. I vaguely knew it was
|
||||
written in C#, but it's not an ecosystem I have any experience in. I figured it
|
||||
was going to be the kind of thing that comes under "possible, but not trivial" -
|
||||
and aarch64 + linux is super-niche.
|
||||
|
||||
Fast forward a few weeks, I mentioned it in passing on the `#pinebook` IRC
|
||||
channel, which went a little like:
|
||||
|
||||
```
|
||||
<lupine> stardew valley never got back to me *sob*
|
||||
<halosghost> lupine: trying to get Stardew Valley on the pbp as well?
|
||||
<Nadia> The game itself is written in C# so runs through Mono
|
||||
<lupine> they don't distribute aarch64-linux executables
|
||||
<Nadia> You just need to build the libraries it needs and it should run
|
||||
<lupine> it's closed source, so "just need to build" is rather an issue
|
||||
```
|
||||
|
||||
They rather roundly assured me that it wasn't an issue at all, and literally
|
||||
10 minutes later I had a working Stardew Valley setup. This is incredible.
|
||||
|
||||
I'm documenting the steps I took so I can come back to this in the future, but
|
||||
maybe it'll be useful for others too.
|
||||
|
||||
First, you need a copy of the game. I was working with v1.4 as shipppd by
|
||||
[GOG Games](https://www.gog.com/game/stardew_valley). It comes as one of those
|
||||
`.sh` files that contains an archive. That doesn't have aarch64 support, and
|
||||
doesn't run on the PBP. Fortunately, I'd already installed it on an amd64 laptop
|
||||
so I just rsynced that over:
|
||||
|
||||
```
|
||||
lupine@pbp:~$ rsync -avzP '10.0.1.104:GOG Games' .
|
||||
|
||||
# Might as well grab my savegames at the same time
|
||||
lupine@pbp:~$ rsync -avzP 10.0.1.104:.config/StardewValley .config/StardewValley
|
||||
```
|
||||
|
||||
I'm sure it's possible to make it run, I'm just being lazy. I'll update this in
|
||||
the future if I work out how to go from the `.sh` file.
|
||||
|
||||
What does this give us?:
|
||||
|
||||
```
|
||||
lupine@pbp:~/GOG Games/Stardew Valley/game$ ls -lh
|
||||
total 438M
|
||||
-rwxrwxr-x 1 lupine lupine 12K Dec 8 23:58 BmFont.dll
|
||||
drwx--x--x 17 lupine lupine 4.0K Dec 8 23:58 Content
|
||||
-rwxrwxr-x 1 lupine lupine 330K Dec 8 23:58 GalaxyCSharp.dll
|
||||
-rwxrwxr-x 1 lupine lupine 336 Dec 8 23:58 GalaxyCSharp.dll.config
|
||||
-rwxrwxr-x 1 lupine lupine 91K Dec 8 23:58 goggame-1453375253.hashdb
|
||||
-rwxrwxr-x 1 lupine lupine 782 Dec 8 23:58 goggame-1453375253.info
|
||||
drwx--x--x 2 lupine lupine 4.0K Dec 8 23:58 lib
|
||||
drwx--x--x 2 lupine lupine 4.0K Dec 8 23:58 lib64
|
||||
-rwxrwxr-x 1 lupine lupine 197M Dec 8 23:59 libGalaxyPeer64.so
|
||||
-rwxrwxr-x 1 lupine lupine 177M Dec 8 23:59 libGalaxyPeer.so
|
||||
-rwxrwxr-x 1 lupine lupine 5.3M Dec 8 23:59 libSkiaSharp.dll
|
||||
-rwxrwxr-x 1 lupine lupine 119K Dec 8 23:58 Lidgren.Network.dll
|
||||
lrwxrwxrwx 1 lupine lupine 14 Dec 9 00:00 mcs -> mcs.bin.x86_64
|
||||
-rwxrwxr-x 1 lupine lupine 17M Dec 8 23:59 mcs.bin.x86
|
||||
-rwxrwxr-x 1 lupine lupine 16M Dec 8 23:59 mcs.bin.x86_64
|
||||
drwx--x--x 3 lupine lupine 4.0K Dec 8 23:59 mono
|
||||
-rwxrwxr-x 1 lupine lupine 2.5K Dec 8 23:59 monoconfig
|
||||
-rwxrwxr-x 1 lupine lupine 1.3M Dec 8 23:58 MonoGame.Framework.dll
|
||||
-rwxrwxr-x 1 lupine lupine 527 Dec 8 23:58 MonoGame.Framework.dll.config
|
||||
-rwxrwxr-x 1 lupine lupine 203K Dec 8 23:58 Mono.Posix.dll
|
||||
-rwxrwxr-x 1 lupine lupine 328K Dec 8 23:58 Mono.Security.dll
|
||||
-rwxrwxr-x 1 lupine lupine 3.6M Dec 8 23:59 mscorlib.dll
|
||||
-rwxrwxr-x 1 lupine lupine 267K Dec 8 23:58 SkiaSharp.dll
|
||||
-rwxrwxr-x 1 lupine lupine 1.2K Dec 8 23:58 StardewValley
|
||||
-rwxrwxr-x 1 lupine lupine 4.0M Dec 8 23:58 StardewValley.bin.x86
|
||||
-rwxrwxr-x 1 lupine lupine 3.8M Dec 8 23:58 StardewValley.bin.x86_64
|
||||
-rwxrwxr-x 1 lupine lupine 4.1M Dec 8 23:58 StardewValley.exe
|
||||
-rwxrwxr-x 1 lupine lupine 6.5K Dec 8 23:58 StardewValley.GameData.dll
|
||||
-rwxrwxr-x 1 lupine lupine 127K Dec 8 23:58 System.Configuration.dll
|
||||
-rwxrwxr-x 1 lupine lupine 879K Dec 8 23:58 System.Core.dll
|
||||
-rwxrwxr-x 1 lupine lupine 2.0M Dec 8 23:58 System.Data.dll
|
||||
-rwxrwxr-x 1 lupine lupine 2.2M Dec 8 23:58 System.dll
|
||||
-rwxrwxr-x 1 lupine lupine 442K Dec 8 23:58 System.Drawing.dll
|
||||
-rwxrwxr-x 1 lupine lupine 966K Dec 8 23:58 System.Runtime.Serialization.dll
|
||||
-rwxrwxr-x 1 lupine lupine 130K Dec 8 23:58 System.Security.dll
|
||||
-rwxrwxr-x 1 lupine lupine 3.1M Dec 8 23:58 System.Xml.dll
|
||||
-rwxrwxr-x 1 lupine lupine 131K Dec 8 23:58 System.Xml.Linq.dll
|
||||
-rwxrwxr-x 1 lupine lupine 161K Dec 8 23:58 WindowsBase.dll
|
||||
-rwxrwxr-x 1 lupine lupine 48K Dec 8 23:59 xTile.dll
|
||||
-rwxrwxr-x 1 lupine lupine 9.0K Dec 8 23:59 xTilePipeline.dll
|
||||
```
|
||||
|
||||
The magic here is that not all the `.dll` and `.exe` files here are **Windows**
|
||||
object files. Instead, many of the are just Mono bytecode, which is analogous to
|
||||
JVM bytecode:
|
||||
|
||||
```
|
||||
lupine@pbp:~/GOG Games/Stardew Valley/game$ file StardewValley*
|
||||
StardewValley: Bourne-Again shell script, ASCII text executable
|
||||
StardewValley.bin.x86: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=4801f8881feefa8aa515f9fadc02c01598c44131, not stripped
|
||||
StardewValley.bin.x86_64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=04fe4f2c2ca8b4dc7faf70c643417bf0df632a9e, not stripped
|
||||
StardewValley.exe: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows
|
||||
StardewValley.GameData.dll: PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows
|
||||
```
|
||||
|
||||
We need Mono to run these assemblies. I guess the `StardewVelly.bin.*` files are
|
||||
just stripped-down Mono runtimes that invoke `StardewValley.exe`!
|
||||
|
||||
The `mcs.*` binaries are "Mono C Sharp" - I don't know C#, but I assume it's
|
||||
another essential part of the runtime.
|
||||
|
||||
Debian has Mono + MCS already, so...
|
||||
|
||||
```
|
||||
lupine@pbp~/GOG Games/Stardew Valley/game$ sudo apt install mono-runtime mono-rcs
|
||||
lupine@pbp~/GOG Games/Stardew Valley/game$ ln -sf `which mcs`
|
||||
```
|
||||
|
||||
We can then try to run the game:
|
||||
|
||||
```
|
||||
lupine@pbp~/GOG Games/Stardew Valley/game$ mono StardewValley.exe
|
||||
```
|
||||
|
||||
Amazingly, that's **almost sufficient**, all by itself, to get a fully working
|
||||
game, at least for me. It starts up, and the only obviously broken thing is
|
||||
sound. There are some complaints on the comamnd line that don't seem to get in
|
||||
the way of actually playing it.
|
||||
|
||||
Wat.
|
||||
|
||||
No sound is annoying though, how about we fix that?
|
||||
|
||||
Turns out Stardew Valley only **requires** two external libraries: SDL and
|
||||
libasound. I've no idea if the graphics is working even though it can't find
|
||||
SDL, or if it can find my native SDL libary but not the libasound one, or what,
|
||||
but it's trivial to fix. Edit `MonoGame.Framework.dll.config` and add these
|
||||
two lines:
|
||||
|
||||
```
|
||||
<dllmap dll="SDL2.dll" os="linux" cpu="armv8" target="./libaarch64/libSDL2-2.0.so.0"/>
|
||||
<dllmap dll="soft_oal.dll" os="linux" cpu="armv8" target="./libaarch64/libopenal.so.1" />
|
||||
```
|
||||
|
||||
Now you just need to put those two .so files into that directory locally, and
|
||||
sound begins to work!
|
||||
|
||||
(I just symlinked `/usr/lib/aarch64-linux-gnu` into place, which does the same
|
||||
job).
|
||||
|
||||
At this point the game works perfectly, including LAN multiplayer - which is
|
||||
ridiculous - and despite worries about endianness, it can load and run my saves
|
||||
as well.
|
||||
|
||||
There are a few complaints on the console though. Let's see what we can do
|
||||
about them.
|
||||
|
||||
|
||||
```
|
||||
Your mono runtime and class libraries are out of sync.
|
||||
The out of sync library is: /home/lupine/GOG Games/Stardew Valley/game/System.dll
|
||||
```
|
||||
|
||||
OK, these are shipped with `mono-runtime` (actually in `libmono-system4.0-cil`)
|
||||
anyway. The complaint is that these assemblies were compiled with a different
|
||||
version of Mono, but it's falling back to the main ones anyway, so we can just
|
||||
move these out of the way.
|
||||
|
||||
The only `System.*.dll` file we need to keep is `System.Runtime.Serialization.dll` -
|
||||
the rest can be moved out of the way.
|
||||
|
||||
```
|
||||
System.TypeInitializationException: The type initializer for 'Galaxy.Api.GalaxyInstance' threw an exception. ---> System.TypeInitializationException: The type initializer for 'CustomExceptionHelper' threw an exception. ---> System.DllNotFoundException: GalaxyCSharpGlue
|
||||
at (wrapper managed-to-native) Galaxy.Api.GalaxyInstance+CustomExceptionHelper.CustomExceptionRegisterCallback(Galaxy.Api.GalaxyInstance/CustomExceptionHelper/CustomExceptionDelegate)
|
||||
at Galaxy.Api.GalaxyInstance+CustomExceptionHelper..cctor () [0x00011] in <22373852dcce42128dc7e065ea92368d>:0
|
||||
--- End of inner exception stack trace ---
|
||||
at (wrapper managed-to-native) System.Object.__icall_wrapper_mono_generic_class_init(intptr)
|
||||
at Galaxy.Api.GalaxyInstance..cctor () [0x00000] in <22373852dcce42128dc7e065ea92368d>:0
|
||||
--- End of inner exception stack trace ---
|
||||
at StardewValley.SDKs.GalaxyHelper.Initialize () [0x00000] in <1ed49e648be548bcae8e4508597c9f4c>:0
|
||||
|
||||
```
|
||||
|
||||
I am **astonished** that this one isn't a fatal error - but the game runs fine
|
||||
even though it can't find an external library. Ridiculous.
|
||||
|
||||
Galaxy is GOG's multiplayer gubbins. If you've got a Steam game, it's different,
|
||||
I'm sure, but the functionality this stuff is *for* is to negotiate multiplayer
|
||||
games with strangers.
|
||||
|
||||
I have no use for this myself, but `libGalaxyCSharpGlue.so` is looked up via
|
||||
another dllmap in `GalaxyCSharp.dll.config` - it's not packaged by Debian, and
|
||||
it may even be proprietary GOG code, but if we can get an aarch64 version of it,
|
||||
making it work should be as simple as adding an entry there.
|
||||
|
||||
If this did become a fatal error at some point, the minimum work would be a stub
|
||||
implementation that meets the ABI but always says "no games available" or some
|
||||
such.
|
||||
|
||||
And... that's all the errors. Despite a different architecture, despite being
|
||||
short some libraries, and despite running reverse-engineered (Panfrost) graphics
|
||||
drivers with only a bare whisper of OpenGL support, my favourite game is running
|
||||
at normal speed on an architecture its authors and publishers didn't even think
|
||||
about.
|
||||
|
||||
Again I say: ridiculous
|
||||
|
||||
...maybe I should learn some CSharp?
|
Reference in New Issue
Block a user