diff --git a/src/game/card/table.rs b/src/game/card/table.rs index b3d16a3..3b68792 100644 --- a/src/game/card/table.rs +++ b/src/game/card/table.rs @@ -80,7 +80,7 @@ impl Table { /// Deserializes the card table from a game file pub fn deserialize(file: &mut GameFile) -> Result { // Seek to the table - file.seek(std::io::SeekFrom::Start(u64::from(Self::START_ADDRESS))) + file.seek(std::io::SeekFrom::Start(Self::START_ADDRESS.as_u64())) .map_err(DeserializeError::Seek)?; // Read header @@ -196,7 +196,7 @@ impl Table { } // Seek to the beginning of the card table - file.seek(std::io::SeekFrom::Start(u64::from(Self::START_ADDRESS))) + file.seek(std::io::SeekFrom::Start(Self::START_ADDRESS.as_u64())) .map_err(SerializeError::Seek)?; // Write header @@ -267,6 +267,7 @@ impl Table { // Write all cards #[rustfmt::skip] { + // Buffer , Offset , Type , Error variant write_card! { self.digimons , 0 , Digimon , SerializeDigimonCard } write_card! { self.items , self.digimons.len() , Item , SerializeItemCard } write_card! { self.digivolves, self.digimons.len() + self.items.len(), Digivolve, SerializeDigivolveCard } diff --git a/src/game/deck/table.rs b/src/game/deck/table.rs index abfbd7f..eca733e 100644 --- a/src/game/deck/table.rs +++ b/src/game/deck/table.rs @@ -49,7 +49,7 @@ impl Table { R: Read + Write + Seek, { // Seek to the beginning of the deck table - file.seek(std::io::SeekFrom::Start(u64::from(Self::START_ADDRESS))) + file.seek(std::io::SeekFrom::Start(Self::START_ADDRESS.as_u64())) .map_err(DeserializeError::Seek)?; // Read header @@ -105,7 +105,7 @@ impl Table { } // Seek to the beginning of the deck table - file.seek(std::io::SeekFrom::Start(u64::from(Self::START_ADDRESS))) + file.seek(std::io::SeekFrom::Start(Self::START_ADDRESS.as_u64())) .map_err(SerializeError::Seek)?; // Write header diff --git a/src/io/address.rs b/src/io/address.rs index 6062f87..7286450 100644 --- a/src/io/address.rs +++ b/src/io/address.rs @@ -12,54 +12,3 @@ pub mod real; // Exports pub use data::Data; pub use real::Real; - -/// Error type for `TryFrom for Data` -#[derive(PartialEq, Eq, Clone, Copy, Debug, thiserror::Error)] -pub enum RealToDataError { - /// Occurs when the Real is outside of the data section of the sector - #[error("The real address {} could not be converted to a data address as it is not in the data section", .0)] - OutsideDataSection(Real), -} - -// Real -> Data -impl std::convert::TryFrom for Data { - type Error = RealToDataError; - - fn try_from(real_address: Real) -> Result { - // If the real address isn't in the data section, then return err - if !real_address.in_data_section() { - return Err(Self::Error::OutsideDataSection(real_address)); - } - - // Else get the sector and offset - let real_sector = real_address.sector(); - let real_sector_offset = real_address.offset(); - - // The data address is just converting the real_sector - // to a data_sector and subtracting the header from the - // real offset to get the data offset - #[rustfmt::skip] - Ok(Self::from( - Real::SECTOR_BYTE_SIZE * real_sector + // Base of data sector - real_sector_offset - Real::HEADER_BYTE_SIZE, // Data offset (skipping header) - )) - } -} - -// Data -> Real -impl From for Real { - fn from(data_address: Data) -> Self { - // Get the sector and offset - let data_sector = data_address.sector(); - let data_sector_offset = data_address.offset(); - - // Then the real address is just converting the data_sector - // to a real_sector and adding the header plus the offset - #[rustfmt::skip] - Self::from( - Self::SECTOR_BYTE_SIZE * data_sector + // Base of real sector - Self::HEADER_BYTE_SIZE + // Skip header - data_sector_offset, // Offset inside data sector - ) - } -} diff --git a/src/io/address/data.rs b/src/io/address/data.rs index dabce2b..aeb669a 100644 --- a/src/io/address/data.rs +++ b/src/io/address/data.rs @@ -27,6 +27,23 @@ impl Data { self.0 } + /// Converts this data offset to a real offset + #[must_use] + pub const fn to_real(self) -> Real { + // Get the sector and offset + let data_sector = self.sector(); + let data_sector_offset = self.offset(); + + // Then the real address is just converting the data_sector + // to a real_sector and adding the header plus the offset + #[rustfmt::skip] + Real::from_u64( + Real::SECTOR_BYTE_SIZE * data_sector + // Base of real sector + Real::HEADER_BYTE_SIZE + // Skip header + data_sector_offset, // Offset inside data sector + ) + } + /// Returns the sector associated with this address #[must_use] pub const fn sector(self) -> u64 { @@ -115,3 +132,9 @@ impl std::fmt::Display for Data { write!(f, "{:x}", u64::from(*self)) } } + +impl From for Real { + fn from(data_address: Data) -> Self { + data_address.to_real() + } +} diff --git a/src/io/address/real.rs b/src/io/address/real.rs index 035e14b..e2a582c 100644 --- a/src/io/address/real.rs +++ b/src/io/address/real.rs @@ -1,7 +1,10 @@ //! File real addresses // Imports -use crate::util::{abs_diff, signed_offset}; +use crate::{ + io::address::Data, + util::{abs_diff, signed_offset}, +}; /// A type for defining addresses on the `.bin` file. /// @@ -11,6 +14,14 @@ use crate::util::{abs_diff, signed_offset}; #[derive(derive_more::From, derive_more::Into)] pub struct Real(u64); +/// Error type for [`Real::to_data`] +#[derive(PartialEq, Eq, Clone, Copy, Debug, thiserror::Error)] +pub enum ToDataError { + /// Occurs when the Real is outside of the data section of the sector + #[error("Unable to convert real address {} to a data address, as it was not in the data section", .0)] + OutsideDataSection(Real), +} + // Constants impl Real { /// The number of bytes the data section takes up in the sector @@ -42,6 +53,27 @@ impl Real { self.0 } + /// Converts this real sector into a data sector + pub const fn try_to_data(self) -> Result { + // If the real address isn't in the data section, then return err + if !self.in_data_section() { + return Err(ToDataError::OutsideDataSection(self)); + } + + // Else get the sector and offset + let real_sector = self.sector(); + let real_sector_offset = self.offset(); + + // The data address is just converting the real_sector + // to a data_sector and subtracting the header from the + // real offset to get the data offset + #[rustfmt::skip] + Ok(Data::from_u64( + Self::SECTOR_BYTE_SIZE * real_sector + // Base of data sector + real_sector_offset - Self::HEADER_BYTE_SIZE, // Data offset (skipping header) + )) + } + /// Returns the real sector associated with this address #[must_use] pub const fn sector(self) -> u64 { @@ -119,13 +151,22 @@ impl std::ops::Sub for Real { type Output = i64; fn sub(self, address: Self) -> i64 { - abs_diff(u64::from(self), u64::from(address)) + abs_diff(self.as_u64(), address.as_u64()) } } // Display impl std::fmt::Display for Real { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:x}", u64::from(*self)) + write!(f, "{:x}", self.as_u64()) + } +} + +// Real -> Data +impl std::convert::TryFrom for Data { + type Error = ToDataError; + + fn try_from(real_address: Real) -> Result { + real_address.try_to_data() } } diff --git a/src/io/game_file.rs b/src/io/game_file.rs index 4ccf6a5..542f34d 100644 --- a/src/io/game_file.rs +++ b/src/io/game_file.rs @@ -3,10 +3,10 @@ //! See [`GameFile`] for details // Imports -use crate::io::address::{Data as DataAddress, Real as RealAddress, RealToDataError}; +use crate::io::address::{real, Data as DataAddress, Real as RealAddress}; use std::{ convert::TryInto, - io::{Read, Seek, Write}, + io::{Read, Seek, SeekFrom, Write}, }; /// A type that abstracts over a the game reader. @@ -28,6 +28,14 @@ use std::{ /// `GameFile` is generic over `R`, this being any type that implements /// `Read`, `Write` and `Seek`, thus being able to read from either a /// reader, a buffer in memory or even some remote network location. +/// +/// # Read/Write Strategy +/// The strategy this employs for reading and writing currently is to +/// get the current 2048 byte block and work on it until it is exhausted, +/// then to get a new 2048 byte block until the operation is complete. +/// This will require an `io` call for every single 2048 byte block instead +/// of an unique call for all of the block, but due to the invariants required, +/// this is the strategy employed. #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default, Hash, Debug)] pub struct GameFile { /// The type to read and write from @@ -48,7 +56,7 @@ impl GameFile { pub fn from_reader(mut reader: R) -> Result { // Seek the reader to the beginning of the data section reader - .seek(std::io::SeekFrom::Start(RealAddress::DATA_START)) + .seek(SeekFrom::Start(DataAddress::from_u64(0).to_real().as_u64())) .map_err(NewGameFileError::SeekData)?; Ok(Self { reader }) @@ -78,7 +86,7 @@ impl Read for GameFile { // If we're at the end of the data section, seek to the next data section if cur_real_address == data_section_end { // Seek ahead by skipping the footer and next header - self.reader.seek(std::io::SeekFrom::Current( + self.reader.seek(SeekFrom::Current( (RealAddress::FOOTER_BYTE_SIZE + RealAddress::HEADER_BYTE_SIZE) .try_into() .expect("Sector offset didn't fit into `u64`"), @@ -151,7 +159,7 @@ impl Write for GameFile { // If we're at the end of the data section, seek to the next data section if cur_real_address == data_section_end { // Seek ahead by skipping the footer and next header - self.reader.seek(std::io::SeekFrom::Current( + self.reader.seek(SeekFrom::Current( (RealAddress::FOOTER_BYTE_SIZE + RealAddress::HEADER_BYTE_SIZE) .try_into() .expect("Sector offset didn't fit into `u64`"), @@ -208,35 +216,42 @@ impl Write for GameFile { /// Returned when, after seeking, we ended up in a non-data section #[derive(PartialEq, Eq, Clone, Copy, Debug, thiserror::Error)] #[error("Reader seeked into a non-data section")] -pub struct SeekNonDataError(#[source] RealToDataError); +pub struct SeekNonDataError(#[source] real::ToDataError); impl Seek for GameFile { - fn seek(&mut self, data_pos: std::io::SeekFrom) -> std::io::Result { - use std::{convert::TryFrom, io::SeekFrom}; + fn seek(&mut self, data_pos: SeekFrom) -> std::io::Result { + // Imports + use std::ops::Add; // Calculate the real position let real_pos = match data_pos { - SeekFrom::Start(data_address) => SeekFrom::Start(u64::from(RealAddress::from(DataAddress::from(data_address)))), - SeekFrom::Current(data_offset) => SeekFrom::Start(u64::from(RealAddress::from( - DataAddress::try_from(RealAddress::from(self.reader.stream_position()?)) - .map_err(SeekNonDataError) - .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))? + - data_offset, - ))), + SeekFrom::Start(data_address) => SeekFrom::Start( + // Parse the address as data, then convert it to real + DataAddress::from(data_address).to_real().as_u64(), + ), + SeekFrom::Current(data_offset) => SeekFrom::Start( + // Get the real address, convert it to data, add the offset in data units, then convert it back into real + RealAddress::from(self.reader.stream_position()?) + .try_to_data() + .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, SeekNonDataError(err)))? + .add(data_offset) + .to_real() + .as_u64(), + ), SeekFrom::End(_) => { - todo!("SeekFrom::End isn't currently implemented"); + todo!("`SeekFrom::End` seeking isn't currently implemented"); }, }; // Seek to the real position and get where we are right now - let cur_real_address = self.reader.seek(real_pos)?; + let cur_real_address = RealAddress::from(self.reader.seek(real_pos)?); // Get the data address - let data_address = DataAddress::try_from(RealAddress::from(cur_real_address)) - .map_err(SeekNonDataError) - .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?; + let data_address = cur_real_address + .try_to_data() + .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, SeekNonDataError(err)))?; // And return the new data address - Ok(u64::from(data_address)) + Ok(data_address.as_u64()) } }