Skip to content
Snippets Groups Projects
state.rs 34 KiB
Newer Older
impl BessMbc {
    pub fn new(registers: Vec<BessMbrRegister>) -> Self {
            header: BessBlockHeader::new(
                String::from("MBC "),
                ((size_of::<u8>() + size_of::<u16>()) * registers.len()) as u32,
            ),
            registers,
        }
    }

    pub fn from_data(data: &mut Cursor<Vec<u8>>) -> Self {
        let mut instance = Self::default();
        instance.read(data);
impl Serialize for BessMbc {
    fn write(&mut self, buffer: &mut Cursor<Vec<u8>>) {
        self.header.write(buffer);
        for register in self.registers.iter() {
            buffer.write_all(&register.address.to_le_bytes()).unwrap();
            buffer.write_all(&register.value.to_le_bytes()).unwrap();
        }
    }

    fn read(&mut self, data: &mut Cursor<Vec<u8>>) {
        self.header.read(data);
        for _ in 0..(self.header.size / 3) {
            let mut buffer = [0x00; 2];
            data.read_exact(&mut buffer).unwrap();
            let address = u16::from_le_bytes(buffer);
            let mut buffer = [0x00; 1];
            data.read_exact(&mut buffer).unwrap();
            let value = u8::from_le_bytes(buffer);
            self.registers.push(BessMbrRegister::new(address, value));
impl State for BessMbc {
    fn from_gb(gb: &mut GameBoy) -> Result<Self, String> {
        let mut registers = vec![];
        match gb.cartridge().rom_type().mbc_type() {
            MbcType::NoMbc => (),
            MbcType::Mbc1 => {
                registers.push(BessMbrRegister::new(
João Magalhães's avatar
João Magalhães committed
                    0x0000,
                    if gb.rom().ram_enabled() {
                        0x0a_u8
                    } else {
                        0x00_u8
                    },
                ));
                registers.push(BessMbrRegister::new(
                    0x2000,
                    gb.rom().rom_bank() as u8 & 0x1f,
                ));
                registers.push(BessMbrRegister::new(0x4000, gb.rom().ram_bank()));
                registers.push(BessMbrRegister::new(0x6000, 0x00_u8));
            MbcType::Mbc3 => {
                registers.push(BessMbrRegister::new(
                    0x0000,
                    if gb.rom().ram_enabled() {
                        0x0a_u8
                    } else {
                        0x00_u8
                    },
                ));
                registers.push(BessMbrRegister::new(0x2000, gb.rom().rom_bank() as u8));
                registers.push(BessMbrRegister::new(0x4000, gb.rom().ram_bank()));
            }
            MbcType::Mbc5 => {
                registers.push(BessMbrRegister::new(
                    0x0000,
                    if gb.rom().ram_enabled() {
                        0x0a_u8
                    } else {
                        0x00_u8
                    },
                ));
                registers.push(BessMbrRegister::new(0x2000, gb.rom().rom_bank() as u8));
                registers.push(BessMbrRegister::new(
                    0x3000,
                    (gb.rom().rom_bank() >> 8) as u8 & 0x01,
                ));
                registers.push(BessMbrRegister::new(0x4000, gb.rom().ram_bank()));
            }
            _ => unimplemented!(),
        Ok(Self::new(registers))
    }

    fn to_gb(&self, gb: &mut GameBoy) -> Result<(), String> {
        for register in self.registers.iter() {
            gb.mmu().write(register.address, register.value);
        }
        Ok(())
    }
}

impl Default for BessMbc {
    fn default() -> Self {
        Self::new(vec![])
    }
}

/// Top level manager structure containing the
/// entrypoint static methods for saving and loading
/// [BESS](https://github.com/LIJI32/SameBoy/blob/master/BESS.md) state
/// files and buffers for the Game Boy.
pub struct StateManager;

impl StateManager {
    pub fn save_file(
        file_path: &str,
        gb: &mut GameBoy,
        format: Option<SaveStateFormat>,
    ) -> Result<(), String> {
        let mut file = match File::create(file_path) {
            Ok(file) => file,
            Err(_) => return Err(format!("Failed to open file: {}", file_path)),
        };
        let data = Self::save(gb, format)?;
        file.write_all(&data).unwrap();
        Ok(())
    }

    pub fn save(gb: &mut GameBoy, format: Option<SaveStateFormat>) -> Result<Vec<u8>, String> {
        let mut data = Cursor::new(vec![]);
        match format {
            Some(SaveStateFormat::Bos) | None => {
                let mut state = BosState::from_gb(gb)?;
                state.write(&mut data);
            }
            Some(SaveStateFormat::Bess) => {
                let mut state = BessState::from_gb(gb)?;
                state.write(&mut data);
            }
        }
        Ok(data.into_inner())
    }

    pub fn load_file(file_path: &str, gb: &mut GameBoy) -> Result<(), String> {
        let mut file = match File::open(file_path) {
            Ok(file) => file,
            Err(_) => return Err(format!("Failed to open file: {}", file_path)),
        };
        let mut data = vec![];
        file.read_to_end(&mut data).unwrap();
        Self::load(&data, gb)?;
        Ok(())
    }

    pub fn load(data: &[u8], gb: &mut GameBoy) -> Result<(), String> {
        let data = &mut Cursor::new(data.to_vec());
        if BosState::is_bos(data) {
            let mut state = BosState::default();
            state.read(data);
            state.to_gb(gb)?;
        } else if BessState::is_bess(data) {
            let mut state = BessState::default();
            state.read(data);
            state.to_gb(gb)?;
        } else {
            return Err(String::from("Invalid state file"));
        }