Skip to content
Snippets Groups Projects
state.rs 30.3 KiB
Newer Older
//! System state (BEES format) functions and structures.

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,
João Magalhães's avatar
João Magalhães committed
    rom::{CgbMode, MbcType},

pub trait Serialize {
    /// Writes the data from the internal structure into the
    /// provided buffer.
    fn write(&mut self, buffer: &mut 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, String>
    where
        Self: Sized;

    /// Applies the state to the provided `GameBoy` instance.
    fn to_gb(&self, gb: &mut GameBoy) -> Result<(), String>;
João Magalhães's avatar
João Magalhães committed
#[derive(Default)]
pub struct BeesState {
    footer: BeesFooter,
    name: BeesName,
    info: BeesInfo,
    core: BeesCore,
    mbc: BeesMbc,
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);
        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<(), String> {
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 Vec<u8>) -> u32 {
        let mut offset = 0x0000_u32;

        let mut buffers = vec![
            &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 = offset;
            buffer.write_all(&item.buffer).unwrap();
            offset += item.size;
impl Serialize for BeesState {
    fn write(&mut self, buffer: &mut Vec<u8>) {
        self.footer.start_offset = self.dump_core(buffer);
        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 BEES 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 = BeesBlockHeader::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 = BeesName::from_data(data),
                "INFO" => self.info = BeesInfo::from_data(data),
                "CORE" => self.core = BeesCore::from_data(data),
                "MBC " => self.mbc = BeesMbc::from_data(data),
                "END " => self.end = BeesBlock::from_data(data),
João Magalhães's avatar
João Magalhães committed
                _ => {
                    BeesBlock::from_data(data);
                }
    }
}

impl State for BeesState {
    fn from_gb(gb: &mut GameBoy) -> Result<Self, String> {
        Ok(Self {
            footer: BeesFooter::default(),
            name: BeesName::from_gb(gb)?,
            info: BeesInfo::from_gb(gb)?,
            core: BeesCore::from_gb(gb)?,
            mbc: BeesMbc::from_gb(gb)?,
João Magalhães's avatar
João Magalhães committed
            end: BeesBlock::from_magic(String::from("END ")),
    fn to_gb(&self, gb: &mut GameBoy) -> Result<(), String> {
        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 BeesState {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.description(9))
pub struct BeesBlockHeader {
impl BeesBlockHeader {
    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);
Loading
Loading full blame...