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