From 0d47c46eed316403cd4c227e91c38ab5f48d8354 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com>
Date: Sat, 5 Aug 2023 18:21:14 +0100
Subject: [PATCH] chore: better state saving

---
 frontends/libretro/src/lib.rs |   3 +-
 frontends/sdl/src/main.rs     |   3 +
 src/state.rs                  | 132 ++++++++++++++++++++++------------
 3 files changed, 93 insertions(+), 45 deletions(-)

diff --git a/frontends/libretro/src/lib.rs b/frontends/libretro/src/lib.rs
index 3343e8e5..bae6cf7f 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 f2c25d0b..ea89dc12 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 6da1420c..72b405aa 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) {}
-- 
GitLab