use anyhow::Result; use rand::Rng; use std::{ fs::{self, File}, io::{Read, Seek as _, SeekFrom, Write as _}, path::{Path, PathBuf}, }; use crate::emulator::SimId; pub struct Cart { pub file_path: PathBuf, pub rom: Vec, sram_file: File, pub sram: Vec, } impl Cart { pub fn load(file_path: &Path, sim_id: SimId) -> Result { let rom = fs::read(file_path)?; let rom = try_parse_elf(&rom).unwrap_or(rom); let mut sram_file = File::options() .read(true) .write(true) .create(true) .truncate(false) .open(sram_path(file_path, sim_id))?; let sram = if sram_file.metadata()?.len() == 0 { // new SRAM file, randomize the contents let mut sram = vec![0; 16 * 1024]; let mut rng = rand::rng(); for dst in sram.iter_mut().step_by(2) { *dst = rng.random(); } sram } else { let mut sram = Vec::with_capacity(16 * 1024); sram_file.read_to_end(&mut sram)?; sram }; Ok(Cart { file_path: file_path.to_path_buf(), rom, sram_file, sram, }) } pub fn save_sram(&mut self) -> Result<()> { self.sram_file.seek(SeekFrom::Start(0))?; self.sram_file.write_all(&self.sram)?; Ok(()) } } fn try_parse_elf(data: &[u8]) -> Option> { let parsed = elf::ElfBytes::::minimal_parse(data).ok()?; let mut bytes = vec![]; let mut pstart = None; for phdr in parsed.segments()? { if phdr.p_filesz == 0 { continue; } let start = pstart.unwrap_or(phdr.p_paddr); pstart = Some(start); bytes.resize((phdr.p_paddr - start) as usize, 0); let data = parsed.segment_data(&phdr).ok()?; bytes.extend_from_slice(data); } Some(bytes) } fn sram_path(file_path: &Path, sim_id: SimId) -> PathBuf { match sim_id { SimId::Player1 => file_path.with_extension("p1.sram"), SimId::Player2 => file_path.with_extension("p2.sram"), } }