isaacschemm: A cartoon of myself as a snail (snail8)
isaacschemm ([personal profile] isaacschemm) wrote in [community profile] snailsharp2023-02-27 04:26 pm
Entry tags:

goombasav

The Game Boy Advance (GBA) provided backwards compatibility with Game Boy and Game Boy Color games by including a second CPU (and a clever voltage switch inside the cartridge slot), but the GBA was easily been powerful enough to emulate the GBC in software. I don't think any commercial releases ever did anything like this, but on the homebrew front, the Goomba family of emulators proved plenty useful for people with unofficial GBA flashcards (as the system can't simply switch into GBC mode in software, or anything like that), and later came in handy on the Micro and the DS.

Goomba runs on the GBA as its host platform, which means anything it needs to save, it needs to save in its own SRAM. It's designed to work with 32 KiB of save memory - within which it has to store the emulated games' SRAM and savestates (Goomba can have more than one Game Boy ROM appended to its own ROM), as well as its configuration settings. Here's a thread that explains how the saving system works. Goomba's save data is stored sequentially (with headers) starting at the beginning of SRAM; each chunk of data is either emulator config, a Game Boy savestate, or Game Boy SRAM (the latter two compressed with LZO). Meanwhile, the area at the end of GBA SRAM (56 KiB - 64 KiB) is reserved for an uncompressed working copy of the current GBC game's SRAM, if any (although I don't think Goomba Color uses this).

The goombasav code I put together is designed to extract and replace the GBC SRAM stored inside the GBA SRAM - it's for use on platforms other than the GBA, so you can transfer the save data to other emulators.

"Why not just use the GameCube and a flashcard?""'Cause I'm gonna need a topic for a blog post about adecade from now"

When I wrote goomabsav about ten years ago, I put it into three apps: a fork of TGB Dual (a GBC emulator for Win32 that emulates two Game Boys connected via link cable), a fork of an N64 emulator input plugin (for use with Transfer Pak emulation in the Pokémon games), and VBA GX (a GBC/GBA emulator for the GameCube and Wii). These apps (all written in C++) do pretty much the same thing with goombasav:

  • Detect whether the .sav is a Goomba .sav (by reading the beginning of the file)
  • Find uncompressed data in the last 8 KiB and move/compress to the main section, if needed
  • Search through all headers in the file, looking for SRAM for the current game (this uses the game's title, given in the Game Boy ROM header and stored in the .sav by Goomba)
  • Extract (if loading) or replace (if saving) the existing LZO-compressed save data (data after this will be moved to fit - no gaps left over)
  • If saving, instead of writing the raw Game Boy SRAM to disk, write the updated Goomba SRAM

(This requires a hook into the load/save process, which would make it hard to implement in something like RetroArch, I think.)

The VBA GX implementation was a little tricky to get working, though. The GameCube and its successors (Wii / Wii U) run on PowerPC processors, which store integers in memory in big-endian order (most significant byte first), whereas ARM (the GBA uses an ARM processor) and x86 both use little-endian. So any time the code deals with a two-byte or four-byte integer, it's important to make sure the bytes are in the right order. When checking this particular part of the application, I was able to smooth out the debugging process by compiling and running goombasav's command-line frontend on a PowerMac running Ubuntu 12.04. I ended up just running every 16-bit and 32-bit integer through a function whenever reading from or writing to a Goomba-defined data structure, so the bare variables are in native byte order:

uint32_t little_endian_conv_32(uint32_t value) {
    const char* endian_check = "\0\xff";
    if (*(uint16_t*)endian_check < 0x100) {
        uint32_t buffer;
        ((char*)&buffer)[0] = ((char*)&value)[3];
        ((char*)&buffer)[1] = ((char*)&value)[2];
        ((char*)&buffer)[2] = ((char*)&value)[1];
        ((char*)&buffer)[3] = ((char*)&value)[0];
        return buffer;
    } else {
        return value;
    }
}

This is a function that swaps the bytes of a uint32_t on a big-endian processor, but does nothing on a little-endian one; the check is performed by defining two known bytes in order (as a string) and then checking how they would be interpreted as an integer. A similar function is defined for uint16_t.

What might be more useful are the frontends to work directly with goombasav:

  • The goombasav command line application, which allows you to:
    • e[x]tract GB SRAM from GBA SRAM
    • [r]eplace GB SRAM in GBA SRAM
    • [c]lean the file by moving and compressing any data at 0xE000
    • [isok]: check if the file is GBA SRAM that goombasav can work with)
  • The "Goomba Save Manager" WinForms GUI (and its GoombasavCore C++/CLI library which exposes the functions of goombasav to .NET code)

The GameBoyAdvanceSRAM "ref class" (a .NET class in the C++/CLI code) gets to use the destructor and finalizer to free some memory allocated with malloc, which is fun:

~GameBoyAdvanceSRAM() {
    this->!GameBoyAdvanceSRAM(); // call finalizer
}

!GameBoyAdvanceSRAM() {
    if (data != NULL) { // safe to run more than once, because Dispose() will call it
        free(data);
        data = NULL;
    }
}

As for "Goomba Save Manager", the WinForms app, it's written in fairly standard C#, as a frontend to GoombasavCore, and it's probably what you want to use if you just want to extract or replace a save file inside the Goomba save file. You can get it here. It also includes a feature that'll look through a Goomba ROM (or really, any data file) and try to find valid Game Boy ROMs that are contained within it; this is a lot simpler than it sounds, and it's really just to let VBA GX pick up on Goomba files and emulate the game it contains directly if that's what you want to do.


Post a comment in response:

This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting