Skip to content
Snippets Groups Projects
Verified Commit b05541bf authored by João Magalhães's avatar João Magalhães :rocket:
Browse files

chore: initial tentative support for BOSC

Marks the initial step towards a compressed file format.
parent 4e11d02a
No related branches found
No related tags found
1 merge request!53Support for BOSC (Boytacean Save Compressed) format
Pipeline #4773 passed
......@@ -7,6 +7,7 @@
//! in agnostic and compatible way.
use boytacean_common::error::Error;
use boytacean_encoding::zippy::{decode_zippy, encode_zippy};
use std::{
convert::TryInto,
fmt::{self, Display, Formatter},
......@@ -32,15 +33,31 @@ 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;
/// Magic string for the BOSC (Boytacean Save Compressed) format.
pub const BOSC_MAGIC: &str = "BOSC\0";
/// Magic string ("BOSC") in little endian unsigned 32 bit format.
pub const BOSC_MAGIC_UINT: u32 = 0x43534f42;
/// Current version of the BOS (Boytacean Save) format.
pub const BOS_VERSION: u8 = 1;
/// Current version of the BOS (Boytacean Save Compressed) format.
pub const BOSC_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 {
/// Boytacean Save format (uncompressed) (BOS).
Bos,
/// Boytacean Save Compressed format (BOSC).
/// The format uses the Zippy compression algorithm.
Bosc,
/// Best Effort Save State format (BESS).
Bess,
}
......@@ -99,6 +116,84 @@ pub trait StateBox {
fn to_gb(&self, gb: &mut GameBoy) -> Result<(), Error>;
}
#[cfg_attr(feature = "wasm", wasm_bindgen)]
#[derive(Default)]
pub struct BoscState {
magic: u32,
version: u8,
bos: BosState,
}
impl BoscState {
/// Checks if the data contained in the provided
/// buffer represents a valid BOSC (Boytacean Save
/// Compressed) file structure, thought magic
/// string validation.
pub fn is_bosc(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);
data.rewind().unwrap();
magic == BOSC_MAGIC_UINT
}
pub fn verify(&self) -> Result<(), Error> {
if self.magic != BOSC_MAGIC_UINT {
return Err(Error::CustomError(String::from("Invalid magic")));
}
self.bos.verify()?;
Ok(())
}
}
impl Serialize for BoscState {
fn write(&mut self, buffer: &mut Cursor<Vec<u8>>) {
buffer.write_all(&self.magic.to_le_bytes()).unwrap();
buffer.write_all(&self.version.to_le_bytes()).unwrap();
let mut cursor = Cursor::new(vec![]);
self.bos.write(&mut cursor);
cursor.rewind().unwrap();
let mut bos_buffer = vec![];
cursor.read_to_end(&mut bos_buffer).unwrap();
let bos_compressed = encode_zippy(&bos_buffer).unwrap();
buffer.write_all(&bos_compressed).unwrap();
}
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 bos_compressed = vec![];
data.read_to_end(&mut bos_compressed).unwrap();
let bos_buffer = decode_zippy(&bos_compressed).unwrap();
let mut bos_cursor = Cursor::new(bos_buffer);
self.bos.read(&mut bos_cursor);
}
}
impl StateBox for BoscState {
fn from_gb(gb: &mut GameBoy) -> Result<Box<Self>, Error> {
Ok(Box::new(Self {
magic: BOSC_MAGIC_UINT,
version: BOSC_VERSION,
bos: *BosState::from_gb(gb)?,
}))
}
fn to_gb(&self, gb: &mut GameBoy) -> Result<(), Error> {
self.verify()?;
self.bos.to_gb(gb)?;
Ok(())
}
}
#[cfg_attr(feature = "wasm", wasm_bindgen)]
#[derive(Default)]
pub struct BosState {
......@@ -1515,10 +1610,14 @@ impl StateManager {
pub fn save(gb: &mut GameBoy, format: Option<SaveStateFormat>) -> Result<Vec<u8>, Error> {
let mut data = Cursor::new(vec![]);
match format {
Some(SaveStateFormat::Bos) | None => {
Some(SaveStateFormat::Bos) => {
let mut state = BosState::from_gb(gb)?;
state.write(&mut data);
}
Some(SaveStateFormat::Bosc) | None => {
let mut state = BoscState::from_gb(gb)?;
state.write(&mut data);
}
Some(SaveStateFormat::Bess) => {
let mut state = BessState::from_gb(gb)?;
state.write(&mut data);
......@@ -1538,6 +1637,8 @@ impl StateManager {
None => {
if BosState::is_bos(data) {
SaveStateFormat::Bos
} else if BoscState::is_bosc(data) {
SaveStateFormat::Bosc
} else if BessState::is_bess(data) {
SaveStateFormat::Bess
} else {
......@@ -1553,6 +1654,11 @@ impl StateManager {
state.read(data);
state.to_gb(gb)?;
}
SaveStateFormat::Bosc => {
let mut state = BosState::default();
state.read(data);
state.to_gb(gb)?;
}
SaveStateFormat::Bess => {
let mut state = BessState::default();
state.read(data);
......@@ -1569,6 +1675,13 @@ impl StateManager {
Ok(state)
}
pub fn read_bosc(data: &[u8]) -> Result<BoscState, Error> {
let data = &mut Cursor::new(data.to_vec());
let mut state = BoscState::default();
state.read(data);
Ok(state)
}
pub fn read_bess(data: &[u8]) -> Result<BessState, Error> {
let data = &mut Cursor::new(data.to_vec());
let mut state = BessState::default();
......@@ -1602,6 +1715,11 @@ impl StateManager {
state.read(data);
Ok(state.image_buffer.unwrap().image.to_vec())
}
SaveStateFormat::Bosc => {
let mut state = BoscState::default();
state.read(data);
Ok(state.bos.image_buffer.unwrap().image.to_vec())
}
SaveStateFormat::Bess => Err(Error::CustomError(String::from(
"Format foes not support thumbnail",
))),
......@@ -1628,6 +1746,10 @@ impl StateManager {
Self::read_bos(data).map_err(|e| e.to_string())
}
pub fn read_bosc_wa(data: &[u8]) -> Result<BoscState, String> {
Self::read_bosc(data).map_err(|e| e.to_string())
}
pub fn read_bess_wa(data: &[u8]) -> Result<BessState, String> {
Self::read_bess(data).map_err(|e| e.to_string())
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment