mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-09 03:40:23 +00:00
Added try_to_data and to_real methods to the addresses.
Started using the new address methods instead of `From` / `TryFrom`. Moved definition of `TryFrom` and `From` for addresses into their respective modules. Revised implementation of `Seek` for `GameFile`.
This commit is contained in:
@@ -80,7 +80,7 @@ impl Table {
|
||||
/// Deserializes the card table from a game file
|
||||
pub fn deserialize<R: Read + Write + Seek>(file: &mut GameFile<R>) -> Result<Self, DeserializeError> {
|
||||
// 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 }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -12,54 +12,3 @@ pub mod real;
|
||||
// Exports
|
||||
pub use data::Data;
|
||||
pub use real::Real;
|
||||
|
||||
/// Error type for `TryFrom<Real> 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<Real> for Data {
|
||||
type Error = RealToDataError;
|
||||
|
||||
fn try_from(real_address: Real) -> Result<Self, Self::Error> {
|
||||
// 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<Data> 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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Data> for Real {
|
||||
fn from(data_address: Data) -> Self {
|
||||
data_address.to_real()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Data, ToDataError> {
|
||||
// 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<Real> 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<Real> for Data {
|
||||
type Error = ToDataError;
|
||||
|
||||
fn try_from(real_address: Real) -> Result<Self, Self::Error> {
|
||||
real_address.try_to_data()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<R: Read + Write + Seek> {
|
||||
/// The type to read and write from
|
||||
@@ -48,7 +56,7 @@ impl<R: Read + Write + Seek> GameFile<R> {
|
||||
pub fn from_reader(mut reader: R) -> Result<Self, NewGameFileError> {
|
||||
// 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<R: Read + Write + Seek> Read for GameFile<R> {
|
||||
// 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<R: Read + Write + Seek> Write for GameFile<R> {
|
||||
// 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<R: Read + Write + Seek> Write for GameFile<R> {
|
||||
/// 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<R: Read + Write + Seek> Seek for GameFile<R> {
|
||||
fn seek(&mut self, data_pos: std::io::SeekFrom) -> std::io::Result<u64> {
|
||||
use std::{convert::TryFrom, io::SeekFrom};
|
||||
fn seek(&mut self, data_pos: SeekFrom) -> std::io::Result<u64> {
|
||||
// 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())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user