Skip to content
Snippets Groups Projects
state.rs 51.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • //! System save state (BOS and [BESS](https://github.com/LIJI32/SameBoy/blob/master/BESS.md) formats) functions and structures.
    
    //!
    //! The BOS (Boytacean Save) format is a custom save state format that contains the emulator state and the frame buffer.
    //! Its serialization includes header, info, image buffer and then a BESS (Best Effort Save State) footer with the state itself.
    //!
    //! The [BESS](https://github.com/LIJI32/SameBoy/blob/master/BESS.md) format is a format developed by the [SameBoy](https://sameboy.github.io/) emulator and is used to store the emulator state
    //! in agnostic and compatible way.
    
    use boytacean_common::error::Error;
    
    use std::{
        convert::TryInto,
    
        fmt::{self, Display, Formatter},
    
        fs::File,
    
        io::{Cursor, Read, Seek, SeekFrom, Write},
        mem::size_of,
    
    use crate::{
        gb::{GameBoy, GameBoySpeed},
        info::Info,
    
        ppu::{DISPLAY_HEIGHT, DISPLAY_WIDTH, FRAME_BUFFER_SIZE},
    
    João Magalhães's avatar
    João Magalhães committed
        rom::{CgbMode, MbcType},
    
        util::{get_timestamp, save_bmp},
    
    #[cfg(feature = "wasm")]
    use wasm_bindgen::prelude::*;
    
    
    /// Magic string for the BOS (Boytacean Save) format.
    
    pub const BOS_MAGIC: &str = "BOS\0";
    
    
    /// Magic string ("BOS\0") in little endian unsigned 32 bit format.
    pub const BOS_MAGIC_UINT: u32 = 0x00534f42;
    
    
    /// Current version of the BOS (Boytacean Save) format.
    
    pub const BOS_VERSION: u8 = 1;
    
    /// Magic number for the BESS file format.
    pub const BESS_MAGIC: u32 = 0x53534542;
    
    
    #[cfg_attr(feature = "wasm", wasm_bindgen)]
    
    pub enum SaveStateFormat {
        Bos,
        Bess,
    }
    
    
    #[derive(Clone, Copy)]
    
    pub enum BosBlockKind {
    
        ImageBuffer = 0x02,
    
        Unknown = 0xff,
    }
    
    impl BosBlockKind {
        fn from_u8(value: u8) -> Self {
            match value {
    
                0x01 => Self::Info,
    
                0x02 => Self::ImageBuffer,
                _ => Self::Unknown,
            }
        }
    
    impl From<u8> for BosBlockKind {
        fn from(value: u8) -> Self {
            Self::from_u8(value)
        }
    }
    
    
    pub trait Serialize {
    
        /// Writes the data from the internal structure into the
        /// provided buffer.
    
        fn write(&mut self, buffer: &mut Cursor<Vec<u8>>);
    
    
        /// Reads the data from the provided buffer and populates
        /// the internal structure with it.
        fn read(&mut self, data: &mut Cursor<Vec<u8>>);
    
    pub trait State {
    
        /// Obtains a new instance of the state from the provided
        /// `GameBoy` instance and returns it.
    
        fn from_gb(gb: &mut GameBoy) -> Result<Self, Error>
    
    
        /// Applies the state to the provided `GameBoy` instance.
    
        fn to_gb(&self, gb: &mut GameBoy) -> Result<(), Error>;
    
    pub trait StateBox {
        /// Obtains a new instance of the state from the provided
        /// `GameBoy` instance and returns it as a boxed value.
        fn from_gb(gb: &mut GameBoy) -> Result<Box<Self>, Error>
        where
            Self: Sized;
    
        /// Applies the state to the provided `GameBoy` instance.
        fn to_gb(&self, gb: &mut GameBoy) -> Result<(), Error>;
    }
    
    
    #[cfg_attr(feature = "wasm", wasm_bindgen)]
    
    #[derive(Default)]
    pub struct BosState {
        magic: u32,
        version: u8,
    
        info: Option<BosInfo>,
    
        image_buffer: Option<BosImageBuffer>,
    
        bess: BessState,
    }
    
    impl BosState {
        /// Checks if the data contained in the provided
    
        /// buffer represents a valid BOS (Boytacean Save)
    
        /// file structure, thought magic string validation.
        pub fn is_bos(data: &mut Cursor<Vec<u8>>) -> bool {
    
            let mut buffer = [0x00; size_of::<u32>()];
    
            data.read_exact(&mut buffer).unwrap();
            let magic = u32::from_le_bytes(buffer);
    
    João Magalhães's avatar
    João Magalhães committed
            data.rewind().unwrap();
    
            magic == BOS_MAGIC_UINT
        }
    
    
        pub fn verify(&self) -> Result<(), Error> {
    
            if self.magic != BOS_MAGIC_UINT {
    
                return Err(Error::CustomError(String::from("Invalid magic")));
    
            }
            self.bess.verify()?;
            Ok(())
        }
    
        pub fn save_image_bmp(&self, file_path: &str) -> Result<(), Error> {
    
            if let Some(image_buffer) = &self.image_buffer {
                image_buffer.save_bmp(file_path)?;
                Ok(())
            } else {
    
                Err(Error::CustomError(String::from("No image buffer found")))
    
            }
        }
    
        fn build_block_count(&self) -> u8 {
            let mut count = 0_u8;
    
            if self.info.is_some() {
                count += 1;
            }
    
            if self.image_buffer.is_some() {
                count += 1;
            }
            count
        }
    
    impl BosState {
    
        pub fn timestamp(&self) -> Result<u64, Error> {
    
            if let Some(info) = &self.info {
                Ok(info.timestamp)
            } else {
    
                Err(Error::CustomError(String::from("No timestamp available")))
    
        pub fn agent(&self) -> Result<String, Error> {
    
            if let Some(info) = &self.info {
                Ok(format!("{}/{}", info.agent, info.agent_version))
            } else {
    
                Err(Error::CustomError(String::from("No agent available")))
    
        pub fn model(&self) -> Result<String, Error> {
    
            if let Some(info) = &self.info {
                Ok(info.model.clone())
            } else {
    
                Err(Error::CustomError(String::from("No model available")))
    
        pub fn image_eager(&self) -> Result<Vec<u8>, Error> {
    
            if let Some(image_buffer) = &self.image_buffer {
                Ok(image_buffer.image.to_vec())
            } else {
    
                Err(Error::CustomError(String::from("No image available")))
    
    #[cfg(feature = "wasm")]
    #[cfg_attr(feature = "wasm", wasm_bindgen)]
    impl BosState {
    
        pub fn timestamp_wa(&self) -> Result<u64, String> {
    
            Self::timestamp(self).map_err(|e| e.to_string())
        }
    
    
        pub fn agent_wa(&self) -> Result<String, String> {
    
            Self::agent(self).map_err(|e| e.to_string())
        }
    
    
        pub fn model_wa(&self) -> Result<String, String> {
    
            Self::model(self).map_err(|e| e.to_string())
        }
    
    
        pub fn image_eager_wa(&self) -> Result<Vec<u8>, String> {
    
            Self::image_eager(self).map_err(|e| e.to_string())
        }
    }
    
    
    impl Serialize for BosState {
        fn write(&mut self, buffer: &mut Cursor<Vec<u8>>) {
    
            self.block_count = self.build_block_count();
    
    
            buffer.write_all(&self.magic.to_le_bytes()).unwrap();
            buffer.write_all(&self.version.to_le_bytes()).unwrap();
    
            buffer.write_all(&self.block_count.to_le_bytes()).unwrap();
    
    
            if let Some(info) = &mut self.info {
                info.write(buffer);
            }
    
            if let Some(image_buffer) = &mut self.image_buffer {
                image_buffer.write(buffer);
            }
    
    
            self.bess.write(buffer);
        }
    
        fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
    
            let mut buffer = [0x00; size_of::<u32>()];
    
            data.read_exact(&mut buffer).unwrap();
            self.magic = u32::from_le_bytes(buffer);
    
            let mut buffer = [0x00; size_of::<u8>()];
    
            data.read_exact(&mut buffer).unwrap();
            self.version = u8::from_le_bytes(buffer);
    
            let mut buffer = [0x00; size_of::<u8>()];
    
            data.read_exact(&mut buffer).unwrap();
            self.block_count = u8::from_le_bytes(buffer);
    
            for _ in 0..self.block_count {
                let block = BosBlock::from_data(data);
                let offset = -((size_of::<u8>() + size_of::<u32>()) as i64);
                data.seek(SeekFrom::Current(offset)).unwrap();
    
                match block.kind {
    
                    BosBlockKind::Info => {
                        self.info = Some(BosInfo::from_data(data));
                    }
    
                    BosBlockKind::ImageBuffer => {
                        self.image_buffer = Some(BosImageBuffer::from_data(data));
                    }
                    _ => {
                        data.seek(SeekFrom::Current(-offset)).unwrap();
                        data.seek(SeekFrom::Current(block.size as i64)).unwrap();
                    }
                }
            }
    
            self.block_count = self.build_block_count();
    
    
    impl StateBox for BosState {
        fn from_gb(gb: &mut GameBoy) -> Result<Box<Self>, Error> {
            Ok(Box::new(Self {
    
                magic: BOS_MAGIC_UINT,
                version: BOS_VERSION,
    
                block_count: 2,
                info: Some(BosInfo::from_gb(gb)?),
    
                image_buffer: Some(BosImageBuffer::from_gb(gb)?),
    
                bess: *BessState::from_gb(gb)?,
            }))
    
        fn to_gb(&self, gb: &mut GameBoy) -> Result<(), Error> {
    
            self.verify()?;
            self.bess.to_gb(gb)?;
            Ok(())
        }
    }
    
    pub struct BosBlock {
        kind: BosBlockKind,
        size: u32,
    
    }
    
    impl BosBlock {
        pub fn new(kind: BosBlockKind, size: u32) -> Self {
            Self { kind, size }
        }
    
        pub fn from_data(data: &mut Cursor<Vec<u8>>) -> Self {
            let mut instance = Self::default();
            instance.read(data);
            instance
        }
    }
    
    impl Serialize for BosBlock {
        fn write(&mut self, buffer: &mut Cursor<Vec<u8>>) {
            buffer.write_all(&(self.kind as u8).to_le_bytes()).unwrap();
            buffer.write_all(&self.size.to_le_bytes()).unwrap();
        }
    
        fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
    
            let mut buffer = [0x00; size_of::<u8>()];
    
            data.read_exact(&mut buffer).unwrap();
            self.kind = BosBlockKind::from_u8(u8::from_le_bytes(buffer));
    
            let mut buffer = [0x00; size_of::<u32>()];
    
            data.read_exact(&mut buffer).unwrap();
            self.size = u32::from_le_bytes(buffer);
        }
    }
    
    impl Default for BosBlock {
        fn default() -> Self {
    
            Self::new(BosBlockKind::Info, 0)
        }
    }
    
    pub struct BosInfo {
        header: BosBlock,
        timestamp: u64,
        agent: String,
        agent_version: String,
        model: String,
    }
    
    impl BosInfo {
        pub fn new(model: String, timestamp: u64, agent: String, agent_version: String) -> Self {
            Self {
                header: BosBlock::new(
                    BosBlockKind::Info,
                    (size_of::<u64>()
                        + size_of::<u8>() * agent.len()
                        + size_of::<u8>() * agent_version.len()
                        + size_of::<u8>() * model.len()
                        + size_of::<u32>() * 4) as u32,
                ),
                model,
                timestamp,
                agent,
                agent_version,
            }
        }
    
        pub fn from_data(data: &mut Cursor<Vec<u8>>) -> Self {
            let mut instance = Self::default();
            instance.read(data);
            instance
        }
    }
    
    impl Serialize for BosInfo {
        fn write(&mut self, buffer: &mut Cursor<Vec<u8>>) {
            self.header.write(buffer);
    
            buffer
                .write_all(&(size_of::<u64>() as u32).to_le_bytes())
                .unwrap();
            buffer.write_all(&self.timestamp.to_le_bytes()).unwrap();
    
            buffer
                .write_all(&(self.agent.as_bytes().len() as u32).to_le_bytes())
                .unwrap();
            buffer.write_all(self.agent.as_bytes()).unwrap();
    
            buffer
                .write_all(&(self.agent_version.as_bytes().len() as u32).to_le_bytes())
                .unwrap();
            buffer.write_all(self.agent_version.as_bytes()).unwrap();
    
            buffer
                .write_all(&(self.model.as_bytes().len() as u32).to_le_bytes())
                .unwrap();
            buffer.write_all(self.model.as_bytes()).unwrap();
        }
    
        fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
            self.header.read(data);
    
            let mut buffer = [0x00; size_of::<u32>()];
            data.read_exact(&mut buffer).unwrap();
            let mut buffer = vec![0x00; u32::from_le_bytes(buffer) as usize];
            data.read_exact(&mut buffer).unwrap();
            self.timestamp = u64::from_le_bytes(buffer.try_into().unwrap());
    
            let mut buffer = [0x00; size_of::<u32>()];
            data.read_exact(&mut buffer).unwrap();
            let mut buffer = vec![0x00; u32::from_le_bytes(buffer) as usize];
            data.read_exact(&mut buffer).unwrap();
            self.agent = String::from_utf8(buffer).unwrap();
    
            let mut buffer = [0x00; size_of::<u32>()];
            data.read_exact(&mut buffer).unwrap();
            let mut buffer = vec![0x00; u32::from_le_bytes(buffer) as usize];
            data.read_exact(&mut buffer).unwrap();
            self.agent_version = String::from_utf8(buffer).unwrap();
    
            let mut buffer = [0x00; size_of::<u32>()];
            data.read_exact(&mut buffer).unwrap();
            let mut buffer = vec![0x00; u32::from_le_bytes(buffer) as usize];
            data.read_exact(&mut buffer).unwrap();
            self.model = String::from_utf8(buffer).unwrap();
        }
    }
    
    impl State for BosInfo {
    
        fn from_gb(gb: &mut GameBoy) -> Result<Self, Error> {
    
            let timestamp = get_timestamp();
            Ok(Self::new(
                gb.mode().to_string(Some(true)),
                timestamp,
                Info::name(),
                Info::version(),
            ))
        }
    
    
        fn to_gb(&self, _gb: &mut GameBoy) -> Result<(), Error> {
    
            Ok(())
        }
    }
    
    impl Default for BosInfo {
        fn default() -> Self {
            Self::new(String::from(""), 0, String::from(""), String::from(""))
    
        }
    }
    
    pub struct BosImageBuffer {
        header: BosBlock,
        image: [u8; FRAME_BUFFER_SIZE],
    }
    
    impl BosImageBuffer {
        pub fn new(image: [u8; FRAME_BUFFER_SIZE]) -> Self {
            Self {
                header: BosBlock::new(
                    BosBlockKind::ImageBuffer,
                    (size_of::<u8>() * FRAME_BUFFER_SIZE) as u32,
                ),
                image,
            }
        }
    
        pub fn from_data(data: &mut Cursor<Vec<u8>>) -> Self {
            let mut instance = Self::default();
            instance.read(data);
            instance
        }
    
    
        pub fn save_bmp(&self, file_path: &str) -> Result<(), Error> {
    
            save_bmp(
                file_path,
                &self.image,
                DISPLAY_WIDTH as u32,
                DISPLAY_HEIGHT as u32,
            )?;
            Ok(())
        }
    }
    
    impl Serialize for BosImageBuffer {
        fn write(&mut self, buffer: &mut Cursor<Vec<u8>>) {
            self.header.write(buffer);
            buffer.write_all(&self.image).unwrap();
        }
    
        fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
            self.header.read(data);
            data.read_exact(&mut self.image).unwrap();
        }
    }
    
    impl State for BosImageBuffer {
    
        fn from_gb(gb: &mut GameBoy) -> Result<Self, Error> {
    
            Ok(Self::new(gb.ppu_i().frame_buffer_raw()))
    
        fn to_gb(&self, _gb: &mut GameBoy) -> Result<(), Error> {
    
            Ok(())
        }
    }
    
    impl Default for BosImageBuffer {
        fn default() -> Self {
            Self::new([0x00; FRAME_BUFFER_SIZE])
        }
    
    #[cfg_attr(feature = "wasm", wasm_bindgen)]
    
    João Magalhães's avatar
    João Magalhães committed
    #[derive(Default)]
    
    pub struct BessState {
        footer: BessFooter,
        name: BessName,
        info: BessInfo,
        core: BessCore,
        mbc: BessMbc,
        end: BessBlock,
    
    impl BessState {
    
        /// Checks if the data contained in the provided
        /// buffer represents a valid BESS (Best Effort Save State)
        /// file structure, thought magic string validation.
        pub fn is_bess(data: &mut Cursor<Vec<u8>>) -> bool {
    
            data.seek(SeekFrom::End(-4)).unwrap();
    
            let mut buffer = [0x00; size_of::<u32>()];
    
            data.read_exact(&mut buffer).unwrap();
            let magic = u32::from_le_bytes(buffer);
    
    João Magalhães's avatar
    João Magalhães committed
            data.rewind().unwrap();
    
        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);
    
            let version_l: String = format!("{:width$}", "Version", width = column_length);
            let model_l: String = format!("{:width$}", "Model", width = column_length);
    
            let ram_l: String = format!("{:width$}", "RAM", width = column_length);
            let vram_l: String = format!("{:width$}", "VRAM", width = column_length);
    
            let pc_l: String = format!("{:width$}", "PC", width = column_length);
            let sp_l: String = format!("{:width$}", "SP", width = column_length);
    
                "{}  {}\n{}  {}\n{}  {}.{}\n{}  {}\n{}  {}\n{}  {}\n{}  0x{:04X}\n{}  0x{:04X}\n",
    
                emulator_l,
                self.name.name,
                title_l,
                self.info.title(),
    
                version_l,
                self.core.major,
                self.core.minor,
                model_l,
                self.core.model,
    
                ram_l,
                self.core.ram.size,
                vram_l,
                self.core.vram.size,
    
                pc_l,
                self.core.pc,
                sp_l,
                self.core.sp
    
        pub fn verify(&self) -> Result<(), Error> {
    
    João Magalhães's avatar
    João Magalhães committed
            self.footer.verify()?;
            self.core.verify()?;
    
        /// Dumps the core data into the provided buffer and returns.
        /// This will effectively populate the majority of the save
        /// file with the core emulator contents.
    
        fn dump_core(&mut self, buffer: &mut Cursor<Vec<u8>>) {
    
    João Magalhães's avatar
    João Magalhães committed
            let mut buffers = [
    
                &mut self.core.ram,
                &mut self.core.vram,
                &mut self.core.mbc_ram,
                &mut self.core.oam,
                &mut self.core.hram,
                &mut self.core.background_palettes,
                &mut self.core.object_palettes,
            ];
    
            for item in buffers.iter_mut() {
    
                item.offset = buffer.position() as u32;
    
                buffer.write_all(&item.buffer).unwrap();
    
    impl Serialize for BessState {
    
        fn write(&mut self, buffer: &mut Cursor<Vec<u8>>) {
            self.dump_core(buffer);
            self.footer.start_offset = buffer.position() as u32;
    
            self.name.write(buffer);
            self.info.write(buffer);
            self.core.write(buffer);
            self.mbc.write(buffer);
            self.end.write(buffer);
            self.footer.write(buffer);
    
        fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
    
            // moves the cursor to the end of the file
            // to read the footer, and then places the
    
            // the cursor in the start of the BESS data
    
            // according to the footer information
            data.seek(SeekFrom::End(-8)).unwrap();
    
            self.footer.read(data);
    
            data.seek(SeekFrom::Start(self.footer.start_offset as u64))
                .unwrap();
    
    
                // reads the block header information and then moves the
                // cursor back to the original position to be able to
                // re-read the block data
    
                let block = BessBlockHeader::from_data(data);
    
                let offset = -((size_of::<u32>() * 2) as i64);
    
                data.seek(SeekFrom::Current(offset)).unwrap();
    
    
                match block.magic.as_str() {
    
                    "NAME" => self.name = BessName::from_data(data),
                    "INFO" => self.info = BessInfo::from_data(data),
                    "CORE" => self.core = BessCore::from_data(data),
                    "MBC " => self.mbc = BessMbc::from_data(data),
                    "END " => self.end = BessBlock::from_data(data),
    
    João Magalhães's avatar
    João Magalhães committed
                    _ => {
    
                        BessBlock::from_data(data);
    
    impl StateBox for BessState {
        fn from_gb(gb: &mut GameBoy) -> Result<Box<Self>, Error> {
            Ok(Box::new(Self {
    
                footer: BessFooter::default(),
                name: BessName::from_gb(gb)?,
                info: BessInfo::from_gb(gb)?,
                core: BessCore::from_gb(gb)?,
                mbc: BessMbc::from_gb(gb)?,
                end: BessBlock::from_magic(String::from("END ")),
    
        fn to_gb(&self, gb: &mut GameBoy) -> Result<(), Error> {
    
            self.verify()?;
            self.name.to_gb(gb)?;
            self.info.to_gb(gb)?;
            self.core.to_gb(gb)?;
    
            self.mbc.to_gb(gb)?;
    
    impl Display for BessState {
    
        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
            write!(f, "{}", self.description(9))
    
    pub struct BessBlockHeader {
    
    impl BessBlockHeader {
    
        pub fn new(magic: String, size: u32) -> Self {
            Self { magic, size }
        }
    
    
        pub fn from_data(data: &mut Cursor<Vec<u8>>) -> Self {
            let mut instance = Self::default();
    
            instance.read(data);
    
            instance
        }
    
        pub fn is_end(&self) -> bool {
            self.magic == "END "
        }
    
    impl Serialize for BessBlockHeader {
    
        fn write(&mut self, buffer: &mut Cursor<Vec<u8>>) {
    
    João Magalhães's avatar
    João Magalhães committed
            buffer.write_all(self.magic.as_bytes()).unwrap();
    
            buffer.write_all(&self.size.to_le_bytes()).unwrap();
        }
    
    
        fn read(&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();
    
            let mut buffer = [0x00; size_of::<u32>()];
    
            data.read_exact(&mut buffer).unwrap();
    
    João Magalhães's avatar
    João Magalhães committed
            self.size = u32::from_le_bytes(buffer);
    
    impl Default for BessBlockHeader {
    
        fn default() -> Self {
            Self::new(String::from("    "), 0)
        }
    }
    
    
    pub struct BessBlock {
        header: BessBlockHeader,
    
    impl BessBlock {
        pub fn new(header: BessBlockHeader, buffer: Vec<u8>) -> Self {
    
            Self { header, buffer }
        }
    
        pub fn from_magic(magic: String) -> Self {
    
            Self::new(BessBlockHeader::new(magic, 0), vec![])
    
        }
    
        pub fn from_data(data: &mut Cursor<Vec<u8>>) -> Self {
            let mut instance = Self::default();
    
            instance.read(data);
    
            instance
        }
    
        pub fn magic(&self) -> &String {
            &self.header.magic
        }
    
        pub fn is_end(&self) -> bool {
    
            self.header.is_end()
    
    impl Serialize for BessBlock {
    
        fn write(&mut self, buffer: &mut Cursor<Vec<u8>>) {
    
            self.header.write(buffer);
    
            buffer.write_all(&self.buffer).unwrap();
        }
    
    
        fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
            self.header.read(data);
            self.buffer.reserve_exact(self.header.size as usize);
            data.read_exact(&mut self.buffer).unwrap();
    
    impl Default for BessBlock {
    
        fn default() -> Self {
    
            Self::new(BessBlockHeader::default(), vec![])
    
    pub struct BessBuffer {
    
    impl BessBuffer {
    
        pub fn new(size: u32, offset: u32, buffer: Vec<u8>) -> Self {
            Self {
                size,
                offset,
                buffer,
            }
        }
    
    
        /// Fills the buffer with new data and updating the size
        /// value accordingly.
        fn fill_buffer(&mut self, data: &[u8]) {
    
            self.size = data.len() as u32;
            self.buffer = data.to_vec();
        }
    
        /// Loads the internal buffer structure with the provided
        /// data according to the size and offset defined.
    
        fn load_buffer(&self, data: &mut Cursor<Vec<u8>>) -> Vec<u8> {
            let mut buffer = vec![0x00; self.size as usize];
            let position = data.position();
            data.seek(SeekFrom::Start(self.offset as u64)).unwrap();
            data.read_exact(&mut buffer).unwrap();
            data.set_position(position);
            buffer
        }
    
    impl Serialize for BessBuffer {
    
        fn write(&mut self, buffer: &mut Cursor<Vec<u8>>) {
    
            buffer.write_all(&self.size.to_le_bytes()).unwrap();
            buffer.write_all(&self.offset.to_le_bytes()).unwrap();
        }
    
    
        fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
    
            let mut buffer = [0x00; size_of::<u32>()];
    
            data.read_exact(&mut buffer).unwrap();
    
    João Magalhães's avatar
    João Magalhães committed
            self.size = u32::from_le_bytes(buffer);
    
            let mut buffer = [0x00; size_of::<u32>()];
    
            data.read_exact(&mut buffer).unwrap();
    
    João Magalhães's avatar
    João Magalhães committed
            self.offset = u32::from_le_bytes(buffer);
    
            self.buffer = self.load_buffer(data);
        }
    }
    
    
    impl Default for BessBuffer {
    
        fn default() -> Self {
            Self::new(0, 0, vec![])
    
    pub struct BessFooter {
    
        start_offset: u32,
        magic: u32,
    
    impl BessFooter {
    
        pub fn new(start_offset: u32, magic: u32) -> Self {
            Self {
                start_offset,
                magic,
            }
        }
    
        pub fn verify(&self) -> Result<(), Error> {
    
            if self.magic != BESS_MAGIC {
    
                return Err(Error::CustomError(String::from("Invalid magic")));
    
    impl Serialize for BessFooter {
    
        fn write(&mut self, buffer: &mut Cursor<Vec<u8>>) {
    
            buffer.write_all(&self.start_offset.to_le_bytes()).unwrap();
            buffer.write_all(&self.magic.to_le_bytes()).unwrap();
        }
    
    
        fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
    
            let mut buffer = [0x00; size_of::<u32>()];
    
            data.read_exact(&mut buffer).unwrap();
    
    João Magalhães's avatar
    João Magalhães committed
            self.start_offset = u32::from_le_bytes(buffer);
    
            let mut buffer = [0x00; size_of::<u32>()];
    
            data.read_exact(&mut buffer).unwrap();
    
    João Magalhães's avatar
    João Magalhães committed
            self.magic = u32::from_le_bytes(buffer);
    
    impl Default for BessFooter {
    
        fn default() -> Self {
    
            Self::new(0x00, BESS_MAGIC)
    
    pub struct BessName {
        header: BessBlockHeader,
    
    impl BessName {
    
        pub fn new(name: String) -> Self {
            Self {
    
                header: BessBlockHeader::new(String::from("NAME"), name.len() as u32),
    
    
        pub fn from_data(data: &mut Cursor<Vec<u8>>) -> Self {
            let mut instance = Self::default();
    
            instance.read(data);
    
    impl Serialize for BessName {
    
        fn write(&mut self, buffer: &mut Cursor<Vec<u8>>) {
    
            self.header.write(buffer);
    
            buffer.write_all(self.name.as_bytes()).unwrap();
        }
    
    
        fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
            self.header.read(data);
    
            let mut buffer = vec![0x00; self.header.size as usize];
    
            data.read_exact(&mut buffer).unwrap();
    
    João Magalhães's avatar
    João Magalhães committed
            self.name = String::from_utf8(buffer).unwrap();
    
    impl State for BessName {
    
        fn from_gb(_gb: &mut GameBoy) -> Result<Self, Error> {
    
            Ok(Self::new(format!("{} v{}", Info::name(), Info::version())))
    
        fn to_gb(&self, _gb: &mut GameBoy) -> Result<(), Error> {
    
    impl Default for BessName {
    
        fn default() -> Self {
            Self::new(String::from(""))
        }
    }
    
    
    pub struct BessInfo {
        header: BessBlockHeader,
    
        title: [u8; 16],
        checksum: [u8; 2],
    
    impl BessInfo {
    
        pub fn new(title: &[u8], checksum: &[u8]) -> Self {
            Self {
    
                header: BessBlockHeader::new(
    
                    String::from("INFO"),
                    title.len() as u32 + checksum.len() as u32,
                ),
                title: title.try_into().unwrap(),
                checksum: checksum.try_into().unwrap(),
            }
        }
    
        pub fn from_data(data: &mut Cursor<Vec<u8>>) -> Self {
            let mut instance = Self::default();
    
            instance.read(data);
    
        pub fn title(&self) -> String {
    
            let mut final_index = 16;
    
            for (offset, byte) in self.title.iter().enumerate() {
    
                if *byte == 0u8 {
    
                    final_index = offset;
                    break;
                }
    
                // in we're at the final byte of the title and the value
                // is one that is reserved for CGB compatibility testing
                // then we must ignore it for title processing purposes
                if offset > 14
                    && (*byte == CgbMode::CgbCompatible as u8 || *byte == CgbMode::CgbOnly as u8)
                {
                    final_index = offset;
    
                String::from_utf8(Vec::from(&self.title[..final_index]))
    
                    .unwrap()
                    .trim_matches(char::from(0))
                    .trim(),
            )
    
    impl Serialize for BessInfo {
    
        fn write(&mut self, buffer: &mut Cursor<Vec<u8>>) {
    
            self.header.write(buffer);
    
            buffer.write_all(&self.title).unwrap();
            buffer.write_all(&self.checksum).unwrap();
        }
    
        fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
            self.header.read(data);
    
            data.read_exact(&mut self.title).unwrap();
            data.read_exact(&mut self.checksum).unwrap();
    
    impl State for BessInfo {
    
        fn from_gb(gb: &mut GameBoy) -> Result<Self, Error> {
    
                &gb.cartridge_i().rom_data()[0x0134..=0x0143],
                &gb.cartridge_i().rom_data()[0x014e..=0x014f],
    
        fn to_gb(&self, gb: &mut GameBoy) -> Result<(), Error> {
    
            if self.title() != gb.rom_i().title() {
    
                return Err(Error::CustomError(format!(
    
                    "Invalid ROM loaded, expected '{}' (len {}) got '{}' (len {})",
                    self.title(),
                    self.title().len(),
                    gb.rom_i().title(),
                    gb.rom_i().title().len(),
    
    impl Default for BessInfo {
    
        fn default() -> Self {
            Self::new(&[0_u8; 16], &[0_u8; 2])
        }
    }
    
    
    pub struct BessCore {
        header: BessBlockHeader,
    
        model: String,
    
        pc: u16,
        af: u16,
        bc: u16,
        de: u16,
        hl: u16,
        sp: u16,
    
        // 0 = running; 1 = halted; 2 = stopped