Skip to content

Save states and audio mute - #9

Merged
raphamorim merged 1 commit into
mainfrom
save-states
Oct 23, 2026
Merged

raphamorim merged 1 commit into
mainfrom
save-states

Conversation

@raphamorim

Copy link
Copy Markdown
Owner

Adds save states to the core and two FFI helpers a host needs for save states and fast-forward.

Save states

  • Gameboy::save_state() / load_state() snapshot the whole machine: CPU, WRAM/HRAM, PPU (including CGB palettes and VRAM banks), HDMA, timer, joypad, serial, sound channels, and the cartridge's banking registers, RAM and RTC.
  • New src/state.rs holds a small binary format, with no new dependencies. Each component lists its fields in order through an impl_state! macro.
  • Header: magic, a format version, and the cartridge's header and global checksums. A state for another game is rejected.
  • A load that fails partway rolls back, so the running game is never left half-restored.
  • Sound sample buffers are cleared on load rather than saved. The audio player and serial callback aren't part of the state.

FFI

  • save_state(path) writes through a temp file plus rename (reusing the save-file writer, whose temp name now works for any extension). load_state(path) restores a state.
  • set_audio_muted(bool) drops samples while set, and clears the queue when muting starts. Hosts use it to fast-forward without building up seconds of audio lag.

Other

  • Silences the unused-variable warnings in CpalPlayer::new, which appear in release builds because the errors are only printed with debug assertions.

Tests

  • Two unit tests with a synthetic ROM: a restored state replays bit-identically, and other games', truncated and garbage states are rejected without touching the running game.
  • Tried in the Fliper macOS app with Pokémon Silver:
    • quick save and quick load
    • resume after quitting
    • fast-forward

Snapshot and restore the whole machine (CPU, memory, PPU, timer, sound,
cartridge banking and RAM). States carry a versioned header and the
cartridge checksums, and a rejected load leaves the game untouched.

FFI: save_state(path), load_state(path) and set_audio_muted(bool), for
hosts that fast-forward. Also silences the release-only unused-variable
warnings in the cpal player.
@raphamorim
raphamorim merged commit 7b5f598 into main Oct 23, 2026
2 checks passed
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