What every branch in a recording has in common: the machine it all starts
from, and the bot options that shaped what happened - the equivalent of
BizHawk's SyncSettings.json plus its savestate-anchored CoreState
(research/replay-timeline.md).
The ROM bytes themselves are NOT stored here: they are a fixed 1 MB file
already on disk (roms/*.sfc, gitignored) that every caller already has
open, so storing a second copy per recording would only spend disk this
project is asked to keep bounded. Console::restore already refuses a
snapshot taken of a different cartridge by CRC, which is the check that
matters.
16use bincode::{Decode, Encode};
2 (2026-09-22): keyframes are Console::snapshot_fixed, not
Console::snapshot. crate::storage::remove_other_formats deletes older
recordings rather than reading them.
21pub const FORMAT_VERSION: u32 = 2;
console::Console::snapshot() of the machine this recording starts
from - frame 0, shared by every branch in the tree.
28 pub starting_snapshot: Vec<u8>,
The map the bot imported at the start of this recording
(map.19.txt's bytes), empty if there was none.
36impl RunHeader { 37 pub fn new(starting_snapshot: Vec<u8>, imported_map: Vec<u8>, retry_given_up: bool, jev_enabled: bool) -> Self { 38 Self { format_version: FORMAT_VERSION, starting_snapshot, imported_map, retry_given_up, jev_enabled } 39 } 40 41 pub fn load(path: &Path) -> io::Result<Self> { 42 let bytes = fs::read(path)?; 43 bincode::decode_from_slice(&bytes, bincode::config::standard()) 44 .map(|(h, _)| h) 45 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) 46 } 47 48 pub fn save(&self, path: &Path) -> io::Result<()> { 49 let bytes = bincode::encode_to_vec(self, bincode::config::standard()).expect("encoding a header never fails"); 50 let tmp = path.with_extension("bin.part"); 51 fs::write(&tmp, bytes)?; 52 fs::rename(tmp, path) 53 } 54} 55 56#[cfg(test)] 57mod tests { 58 use super::*; 59 60 #[test] 61 fn a_header_round_trips_through_a_file() { 62 let header = RunHeader::new(vec![1, 2, 3, 4, 5], vec![9, 9], true, false); 63 let dir = std::env::temp_dir().join(format!("jev-replay-header-test-{}", std::process::id())); 64 let _ = fs::create_dir_all(&dir); 65 let path = dir.join("header.bin"); 66 header.save(&path).expect("save"); 67 let back = RunHeader::load(&path).expect("load"); 68 assert_eq!(back, header); 69 let _ = fs::remove_dir_all(&dir); 70 } 71}