This patch fixes the missing :tm: texture on the Linux intro screen, as well as every other missing texture that I encountered while trying to get this running on my Steam Deck.
SM64+ can load custom textures from PNG files on the disk. It knows where to find those files because in each leveldata.c there exist a number of texture declarations that look like this:
This is a hex literal representation of levels/intro/3_tm.rgba16. When the game loads the :tm: texture, it looks up the symbol intro_seg7_texture_0700C4A0 in leveldata.o, which, per my hex editor, looks like this:
The question remains: why isn't there a null terminator there? Well, that's easy. We didn't put one there when we generated the file. 3_tm.rgba16.inc.c doesn't have 0x00 at the end. And because the path's length is a multiple of eight bytes, the compiler doesn't bother to pad the end of it. So these two strings run together, and we fail to load the texture.
Why doesn't this happen on Windows? I have no idea. I know nothing about the Windows build process. My guess is that the linking process is just fundamentally different and we get lucky.
Why doesn't this happen to every texture on Linux? Because this only occurs when a texture is preceded immediately by another texture with a path length divisible by eight. There aren't a lot of those.
Why does rebuilding the game in a different directory seem to fix this? I don't know, and I couldn't reproduce that behavior on my Steam Deck, or on WSL, or in the Docker container.
To fix this, I added a null terminator to the end of all the .inc.c files. Now they behave like normal C strings. This may fix #85.
This patch fixes the missing :tm: texture on the Linux intro screen, as well as every other missing texture that I encountered while trying to get this running on my Steam Deck.
SM64+ can load custom textures from PNG files on the disk. It knows where to find those files because in each
leveldata.c
there exist a number of texture declarations that look like this:Those
#include
d files look like this:This is a hex literal representation of
levels/intro/3_tm.rgba16
. When the game loads the :tm: texture, it looks up the symbolintro_seg7_texture_0700C4A0
inleveldata.o
, which, per my hex editor, looks like this:The game then tries to load a file named
levels/intro/3_tm.rgba16levels/intro/2_copyright.rgba16.png
. Naturally, because there is no file by the name, the lookup fails, and we are left with the vague impression that we've forgotten to install Counter-Strike: Source. But the ©️ texture loads just fine. What's going on here?Let's inspect that last line. Why is
.rgba16
followed by a bunch of zero bytes? Because theseTexture
s areALIGNED8
, or__attribute__((aligned(8)))
; that is, we've told the compiler to pad them such that they always start on an offset divisible by eight. So when we execute thesnprintf
that resolves the path for custom textures, it reads to the first00
byte and interprets it as a null terminator. But when we read the :tm: texture path, we're starting at the top line of the hex output, andsnprintf
will read all the way to that same00
byte, including the ©️ path with it.The question remains: why isn't there a null terminator there? Well, that's easy. We didn't put one there when we generated the file.
3_tm.rgba16.inc.c
doesn't have0x00
at the end. And because the path's length is a multiple of eight bytes, the compiler doesn't bother to pad the end of it. So these two strings run together, and we fail to load the texture.Why doesn't this happen on Windows? I have no idea. I know nothing about the Windows build process. My guess is that the linking process is just fundamentally different and we get lucky.
Why doesn't this happen to every texture on Linux? Because this only occurs when a texture is preceded immediately by another texture with a path length divisible by eight. There aren't a lot of those.
Why does rebuilding the game in a different directory seem to fix this? I don't know, and I couldn't reproduce that behavior on my Steam Deck, or on WSL, or in the Docker container.
To fix this, I added a null terminator to the end of all the
.inc.c
files. Now they behave like normal C strings. This may fix #85.