Newer
Older
let mut buffer = [0x00; 2];
data.read_exact(&mut buffer).unwrap();
let mut buffer = [0x00; 2];
data.read_exact(&mut buffer).unwrap();
let mut buffer = [0x00; 1];
data.read_exact(&mut buffer).unwrap();
let mut buffer = [0x00; 1];
data.read_exact(&mut buffer).unwrap();
let mut buffer = [0x00; 1];
data.read_exact(&mut buffer).unwrap();
let mut buffer = [0x00; 1];
data.read_exact(&mut buffer).unwrap();
data.read_exact(&mut self.io_registers).unwrap();
self.ram.read(data);
self.vram.read(data);
self.mbc_ram.read(data);
self.oam.read(data);
self.hram.read(data);
self.background_palettes.read(data);
self.object_palettes.read(data);
fn from_gb(gb: &mut GameBoy) -> Result<Self, String> {
let mut core = Self::new(
gb.cpu_i().pc(),
gb.cpu_i().af(),
gb.cpu_i().bc(),
gb.cpu_i().de(),
gb.cpu_i().hl(),
gb.cpu_i().sp(),
gb.cpu_i().ime(),
gb.mmu_i().ie,
// @TODO: these registers cannot be totally retrieved
// because of that some audio noise exists
// The loading of the registers should be done in a much
// more manual way like SameBoy does here
// https://github.com/LIJI32/SameBoy/blob/7e6f1f866e89430adaa6be839aecc4a2ccabd69c/Core/save_state.c#L673
gb.mmu().read_many_unsafe(0xff00, 128).try_into().unwrap(),
core.ram.fill_buffer(gb.mmu().ram());
core.vram.fill_buffer(gb.ppu().vram_device());
core.mbc_ram.fill_buffer(gb.rom_i().ram_data());
core.oam.fill_buffer(&gb.mmu().read_many(0xfe00, 0x00a0));
core.hram.fill_buffer(&gb.mmu().read_many(0xff80, 0x007f));
if gb.is_cgb() {
core.background_palettes
.fill_buffer(&gb.ppu_i().palettes_color()[0]);
core.object_palettes
.fill_buffer(&gb.ppu_i().palettes_color()[1]);
}
fn to_gb(&self, gb: &mut GameBoy) -> Result<(), String> {
gb.cpu().set_pc(self.pc);
gb.cpu().set_af(self.af);
gb.cpu().set_bc(self.bc);
gb.cpu().set_de(self.de);
gb.cpu().set_hl(self.hl);
gb.cpu().set_sp(self.sp);
gb.cpu().set_ime(self.ime);
gb.mmu().ie = self.ie;
match self.execution_mode {
0 => gb.cpu().set_halted(false),
1 => gb.cpu().set_halted(true),
2 => gb.cpu().stop(),
_ => unimplemented!(),
}
// @TODO: we need to be careful about this writing and
// should make this a bit more robust, to handle this
// special case/situations
// The registers should be handled in a more manual manner
// to avoid unwanted side effects
// https://github.com/LIJI32/SameBoy/blob/7e6f1f866e89430adaa6be839aecc4a2ccabd69c/Core/save_state.c#L1003
gb.mmu().write_many_unsafe(0xff00, &self.io_registers);
gb.mmu().set_ram(self.ram.buffer.to_vec());
gb.ppu().set_vram(&self.vram.buffer);
gb.rom().set_ram_data(&self.mbc_ram.buffer);
gb.mmu().write_many(0xfe00, &self.oam.buffer);
gb.mmu().write_many(0xff80, &self.hram.buffer);
// updates the internal palettes for the CGB with the values
gb.ppu().set_palettes_color([
self.background_palettes.buffer.to_vec().try_into().unwrap(),
self.object_palettes.buffer.to_vec().try_into().unwrap(),
]);
// updates the speed of the CGB according to the KEY1 register
let is_double = self.io_registers[0x4d_usize] & 0x80 == 0x80;
gb.mmu().set_speed(if is_double {
GameBoySpeed::Double
} else {
GameBoySpeed::Normal
});
// need to disable DMA transfer to avoid unwanted
// DMA transfers when loading the state
gb.dma().set_active(false);
}
String::from("GD "),
0x0000_u16,
0x0000_u16,
0x0000_u16,
0x0000_u16,
0x0000_u16,
0x0000_u16,
false,
0x00,
0,
[0x00; 128],
address: u16,
value: u8,
}
pub fn new(address: u16, value: u8) -> Self {
Self { address, value }
}
}
pub struct BessMbc {
header: BessBlockHeader,
registers: Vec<BessMbrRegister>,
impl BessMbc {
pub fn new(registers: Vec<BessMbrRegister>) -> Self {
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();
fn write(&mut self, buffer: &mut Cursor<Vec<u8>>) {
for register in self.registers.iter() {
buffer.write_all(®ister.address.to_le_bytes()).unwrap();
buffer.write_all(®ister.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));
fn from_gb(gb: &mut GameBoy) -> Result<Self, String> {
let mut registers = vec![];
match gb.cartridge().rom_type().mbc_type() {
MbcType::NoMbc => (),
MbcType::Mbc1 => {
0x0000,
if gb.rom().ram_enabled() {
0x0a_u8
} else {
0x00_u8
},
));
0x2000,
gb.rom().rom_bank() as u8 & 0x1f,
));
registers.push(BessMbrRegister::new(0x4000, gb.rom().ram_bank()));
registers.push(BessMbrRegister::new(0x6000, 0x00_u8));
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()));
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()));
}
fn to_gb(&self, gb: &mut GameBoy) -> Result<(), String> {
for register in self.registers.iter() {
gb.mmu().write(register.address, register.value);
}
Ok(())
}
}
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
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"));
}