metroid-maniac / gba-auto-batteryless-patcher

MIT License
76 stars 4 forks source link

Getting my head around the code #18

Closed paweljacewicz closed 3 months ago

paweljacewicz commented 3 months ago

Hi!

First, I'd like to thank you for your efforts in making this code and breathing new life into the GBA and retro gaming community!

I'd like to ask about the code, especially some fragments of it that I don't quite understand the notation - last time I wrote anything in C was so long ago, that I could have forgotten something, but googling didn't help either...

There is this section in the payload.c:

asm(R"(.text

original_entrypoint:
    .word 0x080000c0
flush_mode:
    .word 0
save_size:
    .word 0x20000
    .word patched_entrypoint
    .word write_sram_patched + 1
    .word write_eeprom_patched + 1
    .word write_flash_patched + 1
    .word write_eeprom_v111_posthook + 1

which I assume is somehow connected to code in patcher.c:

enum payload_offsets {
    ORIGINAL_ENTRYPOINT_ADDR,
    FLUSH_MODE,
    SAVE_SIZE,
    PATCHED_ENTRYPOINT,
    WRITE_SRAM_PATCHED,
    WRITE_EEPROM_PATCHED,
    WRITE_FLASH_PATCHED,
    WRITE_EEPROM_V111_POSTHOOK
};

Later in code I see fragments like: FLUSH_MODE[(uint32_t*) &rom[payload_base]] = mode; or SAVE_SIZE[(uint32_t*) &rom[payload_base]] = 0x8000;

If you could explain or point me to some documentation online how to interpret this ENUM use. I somewhat guess this is used for addressing/offsetting but can't grasp where do the values come from especially in connection between ASM code and later using it in C. Like what is initial value of SAVE_SIZE in the enum structure, so it can later be used to (I assume) modifying an address space in ROM which corresponds to save_size variable in ASM. This is magic to me :)

Thanks in advance! PJ

metroid-maniac commented 3 months ago

The start of the payload, which is the definitions you highlighted, is an array of words containing important constants. The enum gives integer indexes into this array which match the labels in the ASM. In other words, uint32_t payload[]; payload[SAVE_SIZE]; references the same word as the asm label save_size. The code fragments with the enums you highlighted are just array indexes of that sort.

original_entrypoint, flush_mode and save_size are set by the patcher and read by the payload. They have sane defaults, but it's expected they will be overwritten. patched_entrypoint, write_sram_patched, write_eeprom_patched, write_flash_patched and write_eeprom_v111_posthook are set by the payload and read by the patcher.

I admit that the syntax is a little wonky, sorry about that 😅

paweljacewicz commented 3 months ago

The start of the payload, which is the definitions you highlighted, is an array of words containing important constants. The enum gives integer indexes into this array which match the labels in the ASM. In other words, uint32_t payload[]; payload[SAVE_SIZE]; references the same word as the asm label save_size. The code fragments with the enums you highlighted are just array indexes of that sort.

Oh, ok, makes sense now. I just assume there is some magic glue between those two areas of code. But that is my lack of knowledge how compilers arrange memory. Thanks for the explanation!

I admit that the syntax is a little wonky, sorry about that 😅

Hehe, it's not wonky - it actually is quite readable when notation becomes more clear and I actually remember some of my university coding lessons and how compilers work.

I am actually looking into a way of marking the save area in ROM somehow for easy carving/exporting and importing of save files. So far I found a tool but only dedicated to Pokemon games - https://projectpokemon.org/home/files/file/4104-bootleg-pok%C3%A9mon-gba-save-extractor-and-injector/ And it actually worked ok, though it's operation is quite strange. You first have to save a game at least once and then the tool looks through ROM for the the save (I assume it tries to match some save file structures) and dumps it. Then I was able to import my .sav file which I copied from EZ-Flash Omega DE and import it. Don't know whether it is 1-to-1 binary copy of the .sav into the ROM, but after that the game started from bootleg and my save was there and working fine.

That's why I wonder whether it is even possible/feasible to implement something like your "<3 from Maniac" signature but before the beginning and after the end of the ROM save area to easy import/export saves later. That would be just AWESOME :)

Nevertheless, thanks for the programming lesson! :)

metroid-maniac commented 3 months ago

Yeah - bootleg batteryless patches don't expose an "easy" way to programmatically find the save file location.

You could probably use a heuristic technique, like checking every 0x20000 bytes for a GBA save-sized chunk of data in an otherwise FF or 00-filled region at the end of the ROM, but that would be imprecise guesswork.

Otherwise, maybe you'd have to run an emulator. You could run the game until you see where the data used to fill SRAM on boot are loaded from. Reportedly the Joey Jr does this for GB/C batteryless games, but there's no sources available for that.

paweljacewicz commented 3 months ago

Yup, that's why I was thinking about marking such space while patching so later it can be found easily. Thanks again for your work!