Newer
Older
//! System save state (BOS and [BESS](https://github.com/LIJI32/SameBoy/blob/master/BESS.md) formats) functions and structures.
io::{Cursor, Read, Seek, SeekFrom, Write},
mem::size_of,
gb::{GameBoy, GameBoySpeed},
info::Info,
ppu::{DISPLAY_HEIGHT, DISPLAY_WIDTH, FRAME_BUFFER_SIZE},
#[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,
}
Unknown = 0xff,
}
impl BosBlockKind {
fn from_u8(value: u8) -> Self {
match value {
0x02 => Self::ImageBuffer,
_ => Self::Unknown,
}
}
/// 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>>);
/// 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>;
#[cfg_attr(feature = "wasm", wasm_bindgen)]
#[derive(Default)]
pub struct BosState {
magic: u32,
version: u8,
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; 4];
data.read_exact(&mut buffer).unwrap();
let magic = u32::from_le_bytes(buffer);
pub fn verify(&self) -> Result<(), Error> {
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
}
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);
}
Loading
Loading full blame...