Skip to content

tpm, mgs: memory cards (the games' own saves survive firmware updates) - #13

Open
finger563 wants to merge 1 commit into
mainfrom
feat/memory-cards
Open

finger563 wants to merge 1 commit into
mainfrom
feat/memory-cards

Conversation

@finger563

Copy link
Copy Markdown
Contributor

The Phantom Menace and Metal Gear Solid can now save to a memory card in port 1. Each card is a raw 128 KB image on the SD card:

  • saves/tpm_card1.mcd
  • saves/mgs_card1.mcd

These are the same files PC emulators like DuckStation and ePSXe use, and the game makes one the first time it looks for a card.

Save states hold raw memory full of addresses, so they only load into the firmware build that wrote them (TPM) or one with the same layout (MGS). Card saves are the game's own data, so they survive any rebuild.

Changes

  • TPM (tpm-decomp fea7648, d51af21):
    • The memory card functions TPM calls (Psy-Q's libmcrd) now use the card image instead of reporting "no card".
    • recomp.py now treats a jr to the function's own return address as a return. The save screen's bit packer (0x80054D44) keeps its return address in $a3, and that path ended in a fatal "not a function" error.
    • The desktop runner gains --memcard FILE and RT_PRESS_HOLD; the menus need holds of about 20 vblanks.
  • psyz (3245d54 on tpm): mcd, a card image layer (format, list, create, delete, read, write) plus the BIOS's buXX: file device.
  • MGS (mgs_reversing 6d58e9f on esp32p4):
    • The port's file calls route bu00: to the card.
    • memcard.c's card hardware calls are answered at once, through the handlers the BIOS events would have called.
    • An empty port still means "no card".
  • tab5-emu:
    • Cart::get_memory_card_path() names the file after the game's folder.
    • The TPM and MGS configs take the path.
    • The READMEs are updated.

Testing

  • Card layer: unit tests on the Mac with ASan and UBSan cover format, create, write, read, delete, name patterns, and the bu00: device in the way memcard.c uses it.
  • TPM on the desktop:
    • Saved from the pause menu (Save Game) in level 1.
    • The game wrote a standard save: an SC header with the title "PM00:02:Trade Federation Ship".
    • A fresh run listed the saves under Load Game, loaded one, and resumed in that room.
  • TPM on the board:
    • A scripted test build saved, and a second, different build loaded that save and played the level at about 28 game fps.
    • That second build is what shows saves surviving a rebuild.
  • MGS on the board: at boot the game finds the card ("this memcard is OK") and reads its empty directory.
  • Not yet tested: an actual MGS save and load through Mei Ling's codec on the board.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SFCMzomCZ5wRHJN4sxwkvY

The Phantom Menace and Metal Gear Solid save to a memory card in port 1:
saves/tpm_card1.mcd and saves/mgs_card1.mcd, raw 128 KB card images as PC
emulators use, made the first time the game looks. Unlike save states,
which hold raw memory and load only into the build that wrote them, these
survive firmware updates.

- tpm-decomp fea7648..d51af21: libmcrd natively on the card image (tested
  on the desktop and the board: a save from one build loaded into another);
  recomp.py treats a jr to the function's own return address as a return
  (the save screen's bit packer returns through $a3)
- psyz 3245d54: mcd, the card image and the BIOS's buXX: device
- mgs_reversing 6d58e9f: the port's bu00: device and memcard.c on it (the
  game finds the card at boot)
- Cart::get_memory_card_path names the image after the game's directory

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SFCMzomCZ5wRHJN4sxwkvY
@github-actions

Copy link
Copy Markdown

ESP-IDF Size Report for 'Tab5 Emu'

Metric Base PR Delta
FLASH 0 bytes (0.00%) 0 bytes (0.00%) 0 bytes (0.00%)
DRAM 267,182 bytes (60.54%) 267,206 bytes (60.55%) 🔺 +24 bytes (+0.01%)
IRAM 0 bytes 0 bytes 0 bytes
RAM (DRAM+IRAM) 267,182 bytes 267,206 bytes 🔺 +24 bytes (+0.01%)

FLASH uses app .bin size or json2 flash sum. RAM sums DRAM+IRAM via idf_size. Percentages shown when totals are available.
DRAM/IRAM usage does not include memory used by the heap allocator at runtime.
This report was generated by esp-idf-size-delta.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant