diff --git a/frontends/libretro/src/lib.rs b/frontends/libretro/src/lib.rs index 3343e8e54846ec7c3a5afbcb2182c9abc2eec0b2..bae6cf7fb7343980a01acea2faf67a093dd509a9 100644 --- a/frontends/libretro/src/lib.rs +++ b/frontends/libretro/src/lib.rs @@ -5,6 +5,7 @@ pub mod consts; use boytacean::{ debugln, gb::{AudioProvider, GameBoy}, + gen::VERSION, pad::PadKey, ppu::{DISPLAY_HEIGHT, DISPLAY_WIDTH, FRAME_BUFFER_SIZE, XRGB8888_SIZE}, rom::Cartridge, @@ -173,7 +174,7 @@ pub extern "C" fn retro_reset() { pub unsafe extern "C" fn retro_get_system_info(info: *mut RetroSystemInfo) { debugln!("retro_get_system_info()"); (*info).library_name = "Boytacean\0".as_ptr() as *const c_char; - (*info).library_version = "v0.9.13\0".as_ptr() as *const c_char; + (*info).library_version = format!("v{}\0", VERSION).as_ptr() as *const c_char; (*info).valid_extensions = "gb|gbc\0".as_ptr() as *const c_char; (*info).need_fullpath = false; (*info).block_extract = false; diff --git a/frontends/sdl/src/main.rs b/frontends/sdl/src/main.rs index f2c25d0bc5360521e4388cf431b4376d61dde4e4..ea89dc12c4ed9a31c7807894b8b62227c0b8da86 100644 --- a/frontends/sdl/src/main.rs +++ b/frontends/sdl/src/main.rs @@ -13,6 +13,7 @@ use boytacean::{ ppu::PaletteInfo, rom::Cartridge, serial::{NullDevice, SerialDevice}, + state::save_state_file, util::{replace_ext, write_file}, }; use chrono::Utc; @@ -816,6 +817,8 @@ fn main() { game_boy.attach_serial(device); game_boy.load(!args.no_boot); + save_state_file("tobias.sav", &game_boy); + // prints the current version of the emulator (informational message) println!("========= Boytacean =========\n{}", game_boy); diff --git a/src/state.rs b/src/state.rs index 6da1420cd0c303b27b38d6c970cea12fe2cd7364..72b405aaaa8e6a8a443b1d75083459daa0ffcc7f 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,44 +1,110 @@ -use std::{convert::TryInto, io::Write}; +use std::{ + convert::TryInto, + fs::File, + io::{Cursor, Read, Write}, +}; + +use crate::{gb::GameBoy, gen::VERSION}; + +pub trait Serialize { + fn save(&self, buffer: &mut Vec<u8>); + fn load(&mut self, data: &mut Cursor<Vec<u8>>); +} -#[repr(packed)] pub struct BeesState { pub name: BeesName, pub info: BeesInfo, pub core: BeesCore, } -#[repr(packed)] +impl Serialize for BeesState { + fn save(&self, buffer: &mut Vec<u8>) { + self.name.save(buffer); + self.info.save(buffer); + self.core.save(buffer); + } + + fn load(&mut self, data: &mut Cursor<Vec<u8>>) { + todo!() + } +} + pub struct BeesBlockHeader { - pub magic: u32, + pub magic: String, pub size: u32, } -#[repr(packed)] +impl BeesBlockHeader { + pub fn new(magic: String, size: u32) -> Self { + Self { magic, size } + } +} + +impl Serialize for BeesBlockHeader { + fn save(&self, buffer: &mut Vec<u8>) { + buffer.write_all(&self.magic.as_bytes()).unwrap(); + buffer.write_all(&self.size.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.magic = String::from_utf8(Vec::from(buffer)).unwrap(); + data.read_exact(&mut buffer).unwrap(); + self.size = u32::from_le_bytes(buffer.try_into().unwrap()); + } +} + pub struct BeesBuffer { pub size: u32, pub offset: u32, } -#[repr(packed)] pub struct BeesFooter { pub start_offset: u32, pub magic: u32, } -#[repr(packed)] pub struct BeesName { pub header: BeesBlockHeader, pub name: String, } -#[repr(packed)] +impl BeesName { + pub fn new(name: String) -> Self { + Self { + header: BeesBlockHeader::new(String::from("NAME"), name.len() as u32), + name, + } + } +} + +impl Serialize for BeesName { + fn save(&self, buffer: &mut Vec<u8>) { + self.header.save(buffer); + buffer.write_all(self.name.as_bytes()).unwrap(); + } + + 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); + data.read_exact(&mut buffer).unwrap(); + self.name = String::from_utf8(Vec::from(buffer)).unwrap(); + } +} + pub struct BeesInfo { pub header: BeesBlockHeader, pub title: [u8; 16], pub checksum: [u8; 2], } -#[repr(packed)] +impl Serialize for BeesInfo { + fn save(&self, buffer: &mut Vec<u8>) {} + + fn load(&mut self, data: &mut Cursor<Vec<u8>>) {} +} + pub struct BeesCore { pub header: BeesBlockHeader, @@ -71,46 +137,24 @@ pub struct BeesCore { pub object_palettes: BeesBuffer, } -trait Serialize { - fn store(&self, buffer: &mut Vec<u8>); - fn load(&mut self, data: &[u8]) -> u32; -} - -impl Serialize for BeesState { - fn store(&self, buffer: &mut Vec<u8>) { - self.info.store(buffer); - } +impl Serialize for BeesCore { + fn save(&self, buffer: &mut Vec<u8>) {} - fn load(&mut self, data: &[u8]) -> u32 { - todo!() - } + fn load(&mut self, data: &mut Cursor<Vec<u8>>) {} } -impl Serialize for BeesBlockHeader { - fn store(&self, buffer: &mut Vec<u8>) { - buffer.write(&self.magic.to_le_bytes()).unwrap(); - buffer.write(&self.size.to_le_bytes()).unwrap(); - } - - fn load(&mut self, data: &[u8]) -> u32 { - self.magic = u32::from_le_bytes(data[0..4].try_into().unwrap()); - self.size = u32::from_le_bytes(data[4..8].try_into().unwrap()); - 8 - } +pub fn save_state_file(file_path: &str, gb: &GameBoy) { + let mut file = File::create(file_path).unwrap(); + let data = save_state(gb); + file.write_all(&data).unwrap(); } -impl Serialize for BeesName { - fn store(&self, buffer: &mut Vec<u8>) {} - - fn load(&mut self, data: &[u8]) -> u32 { - 0 - } -} +pub fn save_state(gb: &GameBoy) -> Vec<u8> { + let mut data: Vec<u8> = vec![]; -impl Serialize for BeesInfo { - fn store(&self, buffer: &mut Vec<u8>) {} + BeesName::new(format!("Boytacean v{}", VERSION)).save(&mut data); - fn load(&mut self, data: &[u8]) -> u32 { - 0 - } + data } + +pub fn load_state(state: Vec<u8>, gb: &GameBoy) {}