From cdf25282f2d235f6fea6cc6165c0e6f6b2140b0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com> Date: Sun, 6 Aug 2023 22:31:40 +0100 Subject: [PATCH] chore: initial working loader --- frontends/sdl/src/main.rs | 15 ++- src/gb.rs | 4 + src/state.rs | 195 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 201 insertions(+), 13 deletions(-) diff --git a/frontends/sdl/src/main.rs b/frontends/sdl/src/main.rs index 15cc6f7c..68b5ad1b 100644 --- a/frontends/sdl/src/main.rs +++ b/frontends/sdl/src/main.rs @@ -14,7 +14,7 @@ use boytacean::{ ppu::PaletteInfo, rom::Cartridge, serial::{NullDevice, SerialDevice}, - state::save_state_file, + state::{load_state_file, save_state_file}, util::{replace_ext, write_file}, }; use chrono::Utc; @@ -400,8 +400,17 @@ impl Emulator { match keycode { Keycode::Num0 | Keycode::Num1 | Keycode::Num2 => { save_state_file( - format!("tobias{}.sav", keycode as u32 - Keycode::Num0 as u32) - .as_str(), + format!( + "{}.s{}", + self.rom_name(), + keycode as u32 - Keycode::Num0 as u32 + ) + .as_str(), + &self.system, + ); + //load_state_file("tobias1.sav", &self.system); + load_state_file( + "C:\\Users\\joamag\\Dropbox\\Roms\\gb\\kirby_2.s0", &self.system, ); } diff --git a/src/gb.rs b/src/gb.rs index 1eb2a251..fd07ef51 100644 --- a/src/gb.rs +++ b/src/gb.rs @@ -909,6 +909,10 @@ impl GameBoy { &mut self.cpu } + pub fn cpu_i(&self) -> &Cpu { + &self.cpu + } + pub fn mmu(&mut self) -> &mut Mmu { self.cpu.mmu() } diff --git a/src/state.rs b/src/state.rs index 84d38f84..cf7da9b4 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,7 +1,9 @@ use std::{ convert::TryInto, + fmt::{self, Display, Formatter}, fs::File, - io::{Cursor, Read, Write}, + io::{Cursor, Read, Seek, SeekFrom, Write}, + mem::size_of, }; use crate::{ @@ -19,20 +21,75 @@ pub trait State { } pub struct BeesState { + footer: BeesFooter, name: BeesName, info: BeesInfo, core: BeesCore, } +impl BeesState { + pub fn description(&self, column_length: usize) -> String { + let emulator_l = format!("{:width$}", "Emulator", width = column_length); + let title_l: String = format!("{:width$}", "Title", width = column_length); + format!( + "{} {}\n{} {}\n", + emulator_l, + self.name.name, + title_l, + self.info.title(), + ) + } +} + impl Serialize for BeesState { fn save(&self, buffer: &mut Vec<u8>) { self.name.save(buffer); self.info.save(buffer); self.core.save(buffer); + self.footer.save(buffer); } fn load(&mut self, data: &mut Cursor<Vec<u8>>) { - todo!() + // moves the cursor to the end of the file + // to read the footer, and then places the + // the cursor in the start of the BEES data + // according to the footer information + data.seek(SeekFrom::End(-8)).unwrap(); + self.footer.load(data); + data.seek(SeekFrom::Start(self.footer.start_offset as u64)) + .unwrap(); + + self.name.load(data); + self.info.load(data); + self.core.load(data); + } +} + +impl State for BeesState { + fn from_gb(gb: &GameBoy) -> Self { + Self { + footer: BeesFooter::default(), // @TODO: check if this makes sense + name: BeesName::from_gb(gb), + info: BeesInfo::from_gb(gb), + core: BeesCore::from_gb(gb), + } + } +} + +impl Default for BeesState { + fn default() -> Self { + Self { + footer: BeesFooter::default(), + name: BeesName::default(), + info: BeesInfo::default(), + core: BeesCore::default(), + } + } +} + +impl Display for BeesState { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.description(9)) } } @@ -57,6 +114,7 @@ impl Serialize for BeesBlockHeader { let mut buffer = [0x00; 4]; data.read_exact(&mut buffer).unwrap(); self.magic = String::from_utf8(Vec::from(buffer)).unwrap(); + let mut buffer = [0x00; 4]; data.read_exact(&mut buffer).unwrap(); self.size = u32::from_le_bytes(buffer.try_into().unwrap()); } @@ -72,6 +130,37 @@ pub struct BeesFooter { magic: u32, } +impl BeesFooter { + pub fn new(start_offset: u32, magic: u32) -> Self { + Self { + start_offset, + magic, + } + } +} + +impl Serialize for BeesFooter { + fn save(&self, buffer: &mut Vec<u8>) { + buffer.write_all(&self.start_offset.to_le_bytes()).unwrap(); + buffer.write_all(&self.magic.to_le_bytes()).unwrap(); + } + + fn load(&mut self, data: &mut Cursor<Vec<u8>>) { + let mut buffer = [0x00; 4]; + data.read_exact(&mut buffer).unwrap(); + self.start_offset = u32::from_le_bytes(buffer.try_into().unwrap()); + let mut buffer = [0x00; 4]; + data.read_exact(&mut buffer).unwrap(); + self.magic = u32::from_le_bytes(buffer.try_into().unwrap()); + } +} + +impl Default for BeesFooter { + fn default() -> Self { + Self::new(0x00, 0x53534542) + } +} + pub struct BeesName { header: BeesBlockHeader, name: String, @@ -93,8 +182,8 @@ impl Serialize for BeesName { } fn load(&mut self, data: &mut Cursor<Vec<u8>>) { - let mut buffer = Vec::with_capacity(self.header.size as usize); - buffer.resize(self.header.size as usize, 0); + self.header.load(data); + let mut buffer = vec![0x00; self.header.size as usize]; data.read_exact(&mut buffer).unwrap(); self.name = String::from_utf8(Vec::from(buffer)).unwrap(); } @@ -106,6 +195,12 @@ impl State for BeesName { } } +impl Default for BeesName { + fn default() -> Self { + Self::new(String::from("")) + } +} + pub struct BeesInfo { header: BeesBlockHeader, title: [u8; 16], @@ -123,6 +218,10 @@ impl BeesInfo { checksum: checksum.try_into().unwrap(), } } + + pub fn title(&self) -> String { + String::from_utf8(Vec::from(&self.title[..])).unwrap() + } } impl Serialize for BeesInfo { @@ -133,7 +232,9 @@ impl Serialize for BeesInfo { } fn load(&mut self, data: &mut Cursor<Vec<u8>>) { - todo!() + self.header.load(data); + data.read_exact(&mut self.title).unwrap(); + data.read_exact(&mut self.checksum).unwrap(); } } @@ -146,6 +247,12 @@ impl State for BeesInfo { } } +impl Default for BeesInfo { + fn default() -> Self { + Self::new(&[0_u8; 16], &[0_u8; 2]) + } +} + pub struct BeesCore { header: BeesBlockHeader, @@ -178,12 +285,72 @@ pub struct BeesCore { object_palettes: BeesBuffer, } +impl BeesCore { + pub fn new(model: u32, pc: u16, af: u16, bc: u16, de: u16, hl: u16, sp: u16) -> Self { + Self { + header: BeesBlockHeader::new( + String::from("CORE"), + ((size_of::<u16>() * 2) + + size_of::<u32>() + + (size_of::<u16>() * 6) + + (size_of::<u8>() * 4) + + (size_of::<u8>() * 128) + + ((size_of::<u32>() + size_of::<u32>()) * 7)) as u32, + ), + major: 1, + minor: 1, + model, + pc, + af, + bc, + de, + hl, + sp, + ime: 0, + ie: 0, + execution_mode: 0, + _padding: 0, + io_registers: [0x00; 128], + ram: BeesBuffer { size: 0, offset: 0 }, + vram: BeesBuffer { size: 0, offset: 0 }, + mbc_ram: BeesBuffer { size: 0, offset: 0 }, + oam: BeesBuffer { size: 0, offset: 0 }, + hram: BeesBuffer { size: 0, offset: 0 }, + background_palettes: BeesBuffer { size: 0, offset: 0 }, + object_palettes: BeesBuffer { size: 0, offset: 0 }, + } + } +} + impl Serialize for BeesCore { fn save(&self, buffer: &mut Vec<u8>) {} fn load(&mut self, data: &mut Cursor<Vec<u8>>) {} } +impl State for BeesCore { + fn from_gb(gb: &GameBoy) -> Self { + Self::new( + 0x0002_u32, //GB_MODEL_DMG_B + gb.cpu_i().pc(), + gb.cpu_i().af(), + gb.cpu_i().bc(), + gb.cpu_i().de(), + gb.cpu_i().hl(), + gb.cpu_i().sp(), + ) + } +} + +impl Default for BeesCore { + fn default() -> Self { + Self::new( + 0x0002_u32, //GB_MODEL_DMG_B + 0x0000_u16, 0x0000_u16, 0x0000_u16, 0x0000_u16, 0x0000_u16, 0x0000_u16, + ) + } +} + pub fn save_state_file(file_path: &str, gb: &GameBoy) { let mut file = File::create(file_path).unwrap(); let data = save_state(gb); @@ -192,11 +359,19 @@ pub fn save_state_file(file_path: &str, gb: &GameBoy) { pub fn save_state(gb: &GameBoy) -> Vec<u8> { let mut data: Vec<u8> = vec![]; - - BeesName::from_gb(gb).save(&mut data); - BeesInfo::from_gb(gb).save(&mut data); - + BeesState::from_gb(gb).save(&mut data); data } -pub fn load_state(state: Vec<u8>, gb: &GameBoy) {} +pub fn load_state_file(file_path: &str, gb: &GameBoy) { + let mut file = File::open(file_path).unwrap(); + let mut data = vec![]; + file.read_to_end(&mut data).unwrap(); + load_state(&data, gb); +} + +pub fn load_state(data: &Vec<u8>, gb: &GameBoy) { + let mut state = BeesState::default(); + state.load(&mut Cursor::new(data.to_vec())); + print!("{}", state); +} -- GitLab