Flow D: Save state serialization
SameBoy's save state system mirrors the GB_SECTION layout in gb.h, enabling versioned migration and optional BESS cross-emulator compatibility.
Trigger
- User hotkey / menu →
GB_save_state(gb, path) - Rewind →
GB_save_state_to_buffer_no_besseach vblank - Debugger undo → internal buffer save
On-disk layout
GB_SECTION macro design
From save_state.h, each section in gb.h uses:
GB_SECTION(name,
// fields...
)
This generates offsetof/size metadata used by save_state_internal to read/write each block independently. STRUCT_VERSION (currently 15) in header validates compatibility on load.
The unsaved section is never serialized — callbacks, ROM pointers, debugger state, rewind buffers are re-established by the frontend after load.
Save path — GB_save_state
GB_ASSERT_NOT_RUNNING— must not save mid-instruction- Serialize each section to file via
save_state_internal - Append external memory: MBC RAM, WRAM, VRAM with size headers
- Optionally write BESS blocks for mGBA/BizHawk interoperability
In-memory variant: GB_save_state_to_buffer / GB_get_save_state_size.
Rewind uses _no_bess variants for smaller/faster snapshots.
Load path — GB_load_state
- Read header magic and version — reject if incompatible
- Validate model matches or allow cross-model rules
- Restore sections in order; remap MBC bank pointers via
GB_update_mbc_mappings - Load RAM/VRAM/MBC RAM blobs — validate sizes against cart config
- Parse BESS footer if present (import from other emulators)
- Re-sync PPU:
GB_display_sync, palette caches
GB_get_state_model reads model from file without full load.
Failure returns negative errno-style codes; corrupt files do not partially apply (atomic read into buffer first for memory loads).
BESS format (cross-emulator)
Documented in BESS.md. Key blocks in save_state.c:
BESS_CORE_t— registers, IE, IO, RAM/VRAM/OAM buffer descriptorsBESS_XOAM_t— extra OAM (CGB)BESS_SGB_t— SGB border/palette stateBESS_INFO_t— ROM title + checksum
SameBoy writes BESS footers on save for interoperability; can load BESS from mGBA and others with best-effort semantics.
Battery saves (related but distinct)
GB_save_battery / GB_load_battery in gb.c persist only MBC external RAM + RTC — not full CPU/PPU state. Supports multiple legacy formats (VBA, TPP1, HuC3). Triggered on vblank when battery_dirty or on exit.
Failure modes
- Version mismatch → load rejected (user must use matching SameBoy version)
- ROM changed since save → BESS INFO checksum may warn
- Saving during
GB_run→ assertion failure - Rewind buffer OOM → rewind silently stops pushing (check
GB_rewind_popreturn)