Added various lints.

Removed `Structure` interface.
This commit is contained in:
2020-04-12 18:29:23 +01:00
parent bfd4f4cf01
commit f2ebaad3d8
19 changed files with 529 additions and 576 deletions

View File

@@ -1,10 +1,10 @@
//! Game Data
//!
//! The game module is where all of the game data is defined, this game data
//! can read from the [GameFile](crate::io::GameFile) in the `io` module.
//! can read from the [`GameFile`](crate::io::GameFile) in the `io` module.
//!
//! Some notable types within this module are [CardTable](crate::game::card::Table), the table which
//! stores all cards and [DeckTable](crate::game::deck::Table), the table which stores all cards available.
//! Some notable types within this module are [`CardTable`](crate::game::card::Table), the table which
//! stores all cards and [`DeckTable`](crate::game::deck::Table), the table which stores all cards available.
//!
//! # Strings
//! A lot of types in this module have strings that they must read and write from the game.
@@ -17,8 +17,6 @@
// Modules
#[macro_use] pub mod util;
pub mod structure;
pub mod bytes;
pub mod from_bytes;
pub mod to_bytes;
@@ -32,6 +30,5 @@ pub mod deck;
pub use bytes::Bytes;
pub use from_bytes::FromBytes;
pub use to_bytes::ToBytes;
pub use structure::Structure;
pub use card::Digimon;

View File

@@ -5,27 +5,30 @@
//! # Layout
//! The digimon card has a size of 0x138 bytes, and it's layout is the following:
//!
//! | Offset | Size | Name | Location | Details |
//! |--------|------|---------------------------|------------------------------|-------------------------------------------------------------------------------------|
//! | 0x0 | 0x15 | Name | basic.name | |
//! | 0x15 | 0x2 | Unknown | basic.unknown_1 | Most likely contains the digimon's model |
//! | 0x17 | 0x1 | Speciality & Level | basic.speciality basic.level | The bottom nibble of this byte is the level, while the top nibble is the speciality |
//! | 0x18 | 0x1 | DP | basic.dp_cost | |
//! | 0x19 | 0x1 | +P | basic.dp_give | |
//! | 0x1a | 0x1 | Unknown | basic.unknown_0 | Is `0` for all digimon |
//! | 0x1b | 0x2 | Health | basic.hp | |
//! | 0x1d | 0x1c | Circle Move | moves.circle | |
//! | 0x39 | 0x1c | Triangle move | moves.triangle | |
//! | 0x55 | 0x1c | Cross move | moves.cross | |
//! | 0x71 | 0x20 | First condition | effects.conditions.first | |
//! | 0x91 | 0x20 | Second condition | effects.conditions.second | |
//! | 0xb1 | 0x10 | First effect | support.effects.first | |
//! | 0xc1 | 0x10 | Second effect | support.effects.second | |
//! | 0xd1 | 0x10 | Third effect | support.effects.third | |
//! | 0xe1 | 0x1 | Cross move effect | support.cross_move | |
//! | 0xe2 | 0x1 | Unknown | support.unknown | |
//! | 0xe3 | 0x1 | Effect arrow color | effects.arrow_color | |
//! | 0xe4 | 0x54 | Effect description lines | effects.description | Each line is `0x15` bytes, split over 4 lines |
//! | Offset | Size | Type | Name | Location | Details |
//! |--------|------|----------------------|---------------------------|--------------------------------|-------------------------------------------------------------------------------------|
//! | 0x0 | 0x15 | `char[0x15]` | Name |` basic.name` | |
//! | 0x15 | 0x2 | `u16` | Unknown |` basic.unknown_1` | Most likely contains the digimon's model |
//! | 0x17 | 0x1 | `u8` | Speciality & Level |` basic.speciality basic.level` | The bottom nibble of this byte is the level, while the top nibble is the speciality |
//! | 0x18 | 0x1 | `u8` | DP |` basic.dp_cost` | |
//! | 0x19 | 0x1 | `u8` | +P |` basic.dp_give` | |
//! | 0x1a | 0x1 | `u8` | Unknown |` basic.unknown_0` | Is` 0` for all digimon |
//! | 0x1b | 0x2 | `u16` | Health |` basic.hp` | |
//! | 0x1d | 0x1c | [`Move`] | Circle Move |` moves.circle` | |
//! | 0x39 | 0x1c | [`Move`] | Triangle move |` moves.triangle` | |
//! | 0x55 | 0x1c | [`Move`] | Cross move |` moves.cross` | |
//! | 0x71 | 0x20 | [`SupportCondition`] | First condition |` effects.conditions.first` | |
//! | 0x91 | 0x20 | [`SupportCondition`] | Second condition |` effects.conditions.second` | |
//! | 0xb1 | 0x10 | [`SupportEffect`] | First effect |` support.effects.first` | |
//! | 0xc1 | 0x10 | [`SupportEffect`] | Second effect |` support.effects.second` | |
//! | 0xd1 | 0x10 | [`SupportEffect`] | Third effect |` support.effects.third` | |
//! | 0xe1 | 0x1 | [`CrossMoveEffect`] | Cross move effect |` support.cross_move` | |
//! | 0xe2 | 0x1 | `u8` | Unknown |` support.unknown` | |
//! | 0xe3 | 0x1 | [`ArrowColor`] | Effect arrow color |` effects.arrow_color` | |
//! | 0xe4 | 0x54 | `char[0x15][4]` | Effect description lines |` effects.description` | Each line is` 0x15` bytes, split over 4 lines |
// byteorder
use byteorder::{ByteOrder, LittleEndian};
// Crate
use crate::game::{
@@ -36,211 +39,205 @@ use crate::game::{
}
};
// byteorder
use byteorder::{ByteOrder, LittleEndian};
/// A digimon card
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Digimon
{
/// The basic info of the digimon
pub basic: Basic,
/// The moves
pub moves: Moves,
/// The support
pub support: Support,
}
// Types
//--------------------------------------------------------------------------------------------------
/// A digimon card
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Digimon
{
/// The basic info of the digimon
pub basic: Basic,
/// The moves
pub moves: Moves,
/// The support
pub support: Support,
}
/// The basic properties of a digimon
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Basic
{
pub name: String,
pub speciality: Speciality,
pub level: Level,
pub hp: u16,
/// The basic properties of a digimon
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Basic
{
pub name: String,
pub speciality: Speciality,
pub level: Level,
pub hp: u16,
/// `DP` in the game.
pub dp_cost: u8,
/// `+P` in the game.
pub dp_give: u8,
// Unknown fields
pub unknown_0: u8,
pub unknown_1: u16,
}
/// `DP` in the game.
pub dp_cost: u8,
/// The moves a digimon has
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Moves
{
pub circle : Move,
pub triangle: Move,
pub cross : Move,
}
/// `+P` in the game.
pub dp_give: u8,
/// The support effect of a digimon
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Support
{
/// Unknown field
pub unknown: u8,
/// The cross move effect
pub cross_move: Option<CrossMoveEffect>,
/// The effect description
pub description: [String; 4],
/// The effect arrow color
pub arrow_color: Option<ArrowColor>,
/// The effect conditions
pub conditions: SupportConditions,
/// The effects themselves
pub effects: SupportSupport,
}
// Unknown fields
pub unknown_0: u8,
pub unknown_1: u16,
}
/// The moves a digimon has
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Moves
{
pub circle : Move,
pub triangle: Move,
pub cross : Move,
}
/// The support effect of a digimon
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Support
{
/// Unknown field
pub unknown: u8,
/// All of the support effects
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct SupportSupport
{
pub first : Option<SupportEffect>,
pub second: Option<SupportEffect>,
pub third : Option<SupportEffect>,
}
/// The cross move effect
pub cross_move: Option<CrossMoveEffect>,
/// All of the support effect conditions
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct SupportConditions
{
pub first : Option<SupportCondition>,
pub second: Option<SupportCondition>,
}
/// The effect description
pub description: [String; 4],
/// The error type thrown by [`FromBytes`]
#[derive(Debug, derive_more::Display)]
pub enum FromBytesError
{
/// Unable to convert name to a string
#[display(fmt = "Unable to convert name to a string")]
NameToString( util::ReadNullTerminatedStringError ),
/// Unable to convert one of the support effect descriptions to a string
#[display(fmt = "The {} support effect description could not be converted to a string", rank)]
SupportEffectDescriptionToString {
rank: &'static str,
err: util::ReadNullTerminatedStringError,
},
/// An unknown speciality was found
#[display(fmt = "Unknown speciality found")]
UnknownSpeciality( crate::game::card::property::speciality::UnknownSpeciality ),
/// An unknown level was found
#[display(fmt = "Unknown level found")]
UnknownLevel( crate::game::card::property::level::UnknownLevel ),
/// An unknown effect arrow color was found
#[display(fmt = "Unknown effect arrow color found")]
UnknownEffectArrowColor( crate::game::card::property::arrow_color::UnknownArrowColor ),
/// An unknown cross move effect was found
#[display(fmt = "Unknown cross move effect found")]
UnknownCrossMoveEffect( crate::game::card::property::cross_move_effect::UnknownCrossMoveEffect ),
/// Unable to read a support effect condition
#[display(fmt = "Unable to read the {0} support effect condition", rank)]
SupportCondition {
rank: &'static str,
err: crate::game::card::property::support_condition::FromBytesError,
},
/// Unable to read a support effect
#[display(fmt = "Unable to read the {} support effect", rank)]
SupportEffect {
rank: &'static str,
err: crate::game::card::property::support_effect::FromBytesError,
},
/// Unable to read a move
#[display(fmt = "Unable to read the {} move", name)]
Move {
name: &'static str,
err: crate::game::card::property::moves::FromBytesError,
},
}
/// The effect arrow color
pub arrow_color: Option<ArrowColor>,
/// The error type thrown by `ToBytes`
#[derive(Debug, derive_more::Display)]
pub enum ToBytesError
{
/// The name was too long to be written to file
#[display(fmt = r#"The name "{}" is too long to be written to file"#, name)]
NameTooLong {
name: String,
err: crate::game::util::WriteNullTerminatedStringError,
},
/// The name was not ascii
#[display(fmt = r#"The name "{}" is not valid ascii"#, name)]
NameNotAscii {
name: String,
},
/// The effect conditions
pub conditions: SupportConditions,
/// The effects themselves
pub effects: SupportSupport,
}
/// All of the support effects
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct SupportSupport
{
pub first : Option<SupportEffect>,
pub second: Option<SupportEffect>,
pub third : Option<SupportEffect>,
}
/// All of the support effect conditions
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct SupportConditions
{
pub first : Option<SupportCondition>,
pub second: Option<SupportCondition>,
}
/// The error type thrown by [`FromBytes`]
#[derive(Debug, derive_more::Display)]
pub enum FromBytesError
{
/// Unable to convert name to a string
#[display(fmt = "Unable to convert name to a string")]
NameToString( util::ReadNullTerminatedStringError ),
/// Unable to convert one of the support effect descriptions to a string
#[display(fmt = "The {} support effect description could not be converted to a string", rank)]
SupportEffectDescriptionToString {
rank: &'static str,
/// A support effect description was too long to be written to file
#[display(fmt = r#"The {0} support effect description "{1}" is too long to be written to file"#, rank, string)]
SupportEffectDescriptionTooLong {
string: String,
rank: String,
err: crate::game::util::WriteNullTerminatedStringError,
},
/// A support effect description was not ascii
#[display(fmt = r#"The {0} support effect description "{1}" is not valid ascii"#, rank, name)]
SupportEffectDescriptionNotAscii {
name: String,
rank: String,
},
err: util::ReadNullTerminatedStringError,
},
/// An unknown speciality was found
#[display(fmt = "Unknown speciality found")]
UnknownSpeciality( crate::game::card::property::speciality::UnknownSpeciality ),
/// An unknown level was found
#[display(fmt = "Unknown level found")]
UnknownLevel( crate::game::card::property::level::UnknownLevel ),
/// An unknown effect arrow color was found
#[display(fmt = "Unknown effect arrow color found")]
UnknownEffectArrowColor( crate::game::card::property::arrow_color::UnknownArrowColor ),
/// An unknown cross move effect was found
#[display(fmt = "Unknown cross move effect found")]
UnknownCrossMoveEffect( crate::game::card::property::cross_move_effect::UnknownCrossMoveEffect ),
/// Unable to read a support effect condition
#[display(fmt = "Unable to read the {0} support effect condition", rank)]
SupportCondition {
rank: &'static str,
err: crate::game::card::property::support_condition::FromBytesError,
},
/// Unable to read a support effect
#[display(fmt = "Unable to read the {} support effect", rank)]
SupportEffect {
rank: &'static str,
/// Unable to write a move
#[display(fmt = "Unable to write the {} move", name)]
Move {
name: &'static str,
err: crate::game::card::property::moves::ToBytesError,
},
}
//--------------------------------------------------------------------------------------------------
err: crate::game::card::property::support_effect::FromBytesError,
},
/// Unable to read a move
#[display(fmt = "Unable to read the {} move", name)]
Move {
name: &'static str,
err: crate::game::card::property::moves::FromBytesError,
},
}
/// The error type thrown by `ToBytes`
#[derive(Debug, derive_more::Display)]
pub enum ToBytesError
{
/// The name was too long to be written to file
#[display(fmt = r#"The name "{}" is too long to be written to file"#, name)]
NameTooLong {
name: String,
err: crate::game::util::WriteNullTerminatedStringError,
},
/// The name was not ascii
#[display(fmt = r#"The name "{}" is not valid ascii"#, name)]
NameNotAscii {
name: String,
},
/// A support effect description was too long to be written to file
#[display(fmt = r#"The {0} support effect description "{1}" is too long to be written to file"#, rank, string)]
SupportEffectDescriptionTooLong {
string: String,
rank: String,
err: crate::game::util::WriteNullTerminatedStringError,
},
/// A support effect description was not ascii
#[display(fmt = r#"The {0} support effect description "{1}" is not valid ascii"#, rank, name)]
SupportEffectDescriptionNotAscii {
name: String,
rank: String,
},
/// Unable to write a move
#[display(fmt = "Unable to write the {} move", name)]
Move {
name: &'static str,
err: crate::game::card::property::moves::ToBytesError,
},
}
// Impl
//--------------------------------------------------------------------------------------------------
@@ -257,8 +254,16 @@ use byteorder::{ByteOrder, LittleEndian};
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error>
{
// Note: We can't use `TryInto` because it only supports arrays up to 32
// SAFETY: Safe as we checked the length
assert!(bytes.len() == Self::BUF_BYTE_SIZE);
let bytes: &[u8; Self::BUF_BYTE_SIZE] = unsafe {
#[allow(clippy::as_conversions)]
&*( bytes.as_ptr() as *const [u8; Self::BUF_BYTE_SIZE] )
};
// Return the struct after building it
Ok( Digimon {
Ok( Self {
// 0x0 - 0x1d
basic: Basic {
name : util::read_null_terminated_string( &bytes[0x0..0x15] ) .map_err(FromBytesError::NameToString)?.to_string(),

View File

@@ -1,3 +1,5 @@
//! Digivolve
// Crate
//--------------------------------------------------------------------------------------------------
// Game
@@ -142,7 +144,7 @@ use serde::Deserialize;
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error>
{
Ok( Digivolve {
Ok( Self {
basic: Basic {
name: util::read_null_terminated_string( &bytes[0x0..0x15] ).map_err(FromBytesError::NameToString)?.to_string(),
},

View File

@@ -1,3 +1,5 @@
//! Item
// Crate
//--------------------------------------------------------------------------------------------------
// Game
@@ -142,7 +144,7 @@ use serde::Deserialize;
//assert_eq!(bytes[0x1a], 0);
// And return the struct
Ok( Item {
Ok( Self {
basic: Basic {
name: util::read_null_terminated_string( &bytes[0x0..0x15] ).map_err(FromBytesError::NameToString)?.to_string(),

View File

@@ -151,15 +151,16 @@
impl CardType
{
/// Returns the byte size of the corresponding card
#[must_use]
pub fn card_byte_size(self) -> usize
{
use crate::game::Bytes;
match self
{
CardType::Digimon => <crate::game::card::Digimon as Bytes>::BUF_BYTE_SIZE,
CardType::Item => <crate::game::card::Item as Bytes>::BUF_BYTE_SIZE,
CardType::Digivolve => <crate::game::card::Digivolve as Bytes>::BUF_BYTE_SIZE,
Self::Digimon => <crate::game::card::Digimon as Bytes>::BUF_BYTE_SIZE,
Self::Item => <crate::game::card::Item as Bytes>::BUF_BYTE_SIZE,
Self::Digivolve => <crate::game::card::Digivolve as Bytes>::BUF_BYTE_SIZE,
}
}
}

View File

@@ -63,7 +63,7 @@ use byteorder::LittleEndian;
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error>
{
// And return the move
Ok( Move {
Ok( Self {
name : util::read_null_terminated_string( &bytes[0x6..0x1c] ).map_err(FromBytesError::NameToString)?.to_string(),
power : LittleEndian::read_u16( &bytes[0x0..0x2] ),
unknown: LittleEndian::read_u32( &bytes[0x2..0x6] ),

View File

@@ -76,7 +76,7 @@ use byteorder::{ByteOrder, LittleEndian};
let cond = DigimonProperty::from_bytes( &bytes[0x2..0x3] ).map_err(FromBytesError::Condition)?;
// And return the move
Ok( SupportCondition {
Ok( Self {
misfire: { bytes[0x0] != 0 },
cond,
@@ -112,10 +112,12 @@ use byteorder::{ByteOrder, LittleEndian};
bytes[0x1] = 0;
// 0x2 - Condition
self.cond.to_bytes(&mut bytes[0x2..0x3])?;
#[allow(clippy::diverging_sub_expression)] { // False positive
self.cond.to_bytes(&mut bytes[0x2..0x3])?;
}
// 0x3..0x8 - Unknown[0..5]
for i in 0..5 { bytes[0x3 + i] = self.unknown[0 + i]; }
bytes[0x3..0x8].copy_from_slice( &self.unknown[0..5] );
// 0x8 - Type arg / 0 if None
if let Some(type_arg) = self.type_arg {
@@ -124,13 +126,15 @@ use byteorder::{ByteOrder, LittleEndian};
else { bytes[0x8] = 0; }
// 0x9..0x14 - Unknown[5..16]
for i in 0..11 { bytes[0x9 + i] = self.unknown[5 + i]; }
bytes[0x9..0x14].copy_from_slice( &self.unknown[5..16] );
// 0x14..0x16 - Number arg
LittleEndian::write_u16(&mut bytes[0x14..0x16], self.num_arg);
// 0x1a - Operation arg
self.operation.to_bytes(&mut bytes[0x1a..0x1b])?;
#[allow(clippy::diverging_sub_expression)] { // False positive
self.operation.to_bytes(&mut bytes[0x1a..0x1b])?;
}
// And return OK
Ok(())

View File

@@ -1,19 +1,32 @@
// Crate
//--------------------------------------------------------------------------------------------------
// Game
use crate::game::{Bytes, FromBytes, ToBytes};
use crate::game::card::property::{DigimonProperty, SupportEffectOperation, AttackType, PlayerType, Slot};
//--------------------------------------------------------------------------------------------------
//! Support effects
// Lints
#![allow(
// We have a lot of `a, b, c, x, y` from the formulas,
// but we can't change those names since they're the actual
// names of the variables in the formulas
clippy::many_single_char_names
)]
// byteorder
use byteorder::{ByteOrder, LittleEndian};
// Crate
use crate::{
game::{
Bytes, FromBytes, ToBytes,
card::property::{DigimonProperty, SupportEffectOperation, AttackType, PlayerType, Slot},
},
};
// Types
//--------------------------------------------------------------------------------------------------
/// A digimon's support effects
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(tag = "type")]
// TODO: Move this `allow` to the variant once clippy allows
#[allow(clippy::pub_enum_variant_names)] // `Effect` on `VoidOpponentSupportEffect` isn't refering to the enum
pub enum SupportEffect
{
/// Changes a property of either digimon
@@ -218,54 +231,56 @@ use byteorder::{ByteOrder, LittleEndian};
match effect_type_byte
{
0..=13 => {
Ok( SupportEffect::ChangeProperty {
Ok( Self::ChangeProperty {
// Note: unwrapping is fine here because we know that `effect_type_byte+1` is between 1 and 14 inclusive
property: DigimonProperty::from_bytes( &[ effect_type_byte+1 ] ).unwrap(),
property: DigimonProperty::from_bytes( &[ effect_type_byte+1 ] )
.expect("Unable to get digimon property from bytes"),
a, b, c, x, y, op,
})
},
16 => { Ok( SupportEffect::UseAttack{ player: PlayerType::Player , attack: AttackType::from_bytes( &[x as u8] ) .map_err(FromBytesError::AttackType)? } ) },
17 => { Ok( SupportEffect::UseAttack{ player: PlayerType::Opponent, attack: AttackType::from_bytes( &[x as u8] ) .map_err(FromBytesError::AttackType)? } ) },
// Take lower byte from `x` for these
16 => { Ok( Self::UseAttack{ player: PlayerType::Player , attack: AttackType::from_bytes( &[x.to_le_bytes()[0]] ) .map_err(FromBytesError::AttackType)? } ) },
17 => { Ok( Self::UseAttack{ player: PlayerType::Opponent, attack: AttackType::from_bytes( &[x.to_le_bytes()[0]] ) .map_err(FromBytesError::AttackType)? } ) },
25 => { Ok( SupportEffect::SetTempSlot{ a, b, c, op } ) },
25 => { Ok( Self::SetTempSlot{ a, b, c, op } ) },
26 => { Ok( SupportEffect::MoveCards{ player: PlayerType::Player , source: Slot::Hand , destination: Slot::Offline, count: y } ) },
27 => { Ok( SupportEffect::MoveCards{ player: PlayerType::Opponent, source: Slot::Hand , destination: Slot::Offline, count: y } ) },
26 => { Ok( Self::MoveCards{ player: PlayerType::Player , source: Slot::Hand , destination: Slot::Offline, count: y } ) },
27 => { Ok( Self::MoveCards{ player: PlayerType::Opponent, source: Slot::Hand , destination: Slot::Offline, count: y } ) },
30 => { Ok( SupportEffect::MoveCards{ player: PlayerType::Player , source: Slot::Hand , destination: Slot::Online , count: y } ) },
31 => { Ok( SupportEffect::MoveCards{ player: PlayerType::Opponent, source: Slot::Hand , destination: Slot::Online , count: y } ) },
30 => { Ok( Self::MoveCards{ player: PlayerType::Player , source: Slot::Hand , destination: Slot::Online , count: y } ) },
31 => { Ok( Self::MoveCards{ player: PlayerType::Opponent, source: Slot::Hand , destination: Slot::Online , count: y } ) },
32 => { Ok( SupportEffect::MoveCards{ player: PlayerType::Player , source: Slot::Online , destination: Slot::Offline, count: y } ) },
33 => { Ok( SupportEffect::MoveCards{ player: PlayerType::Opponent, source: Slot::Online , destination: Slot::Offline, count: y } ) },
32 => { Ok( Self::MoveCards{ player: PlayerType::Player , source: Slot::Online , destination: Slot::Offline, count: y } ) },
33 => { Ok( Self::MoveCards{ player: PlayerType::Opponent, source: Slot::Online , destination: Slot::Offline, count: y } ) },
34 => { Ok( SupportEffect::MoveCards{ player: PlayerType::Player , source: Slot::Offline, destination: Slot::Online , count: y } ) },
35 => { Ok( SupportEffect::MoveCards{ player: PlayerType::Opponent, source: Slot::Offline, destination: Slot::Online , count: y } ) },
34 => { Ok( Self::MoveCards{ player: PlayerType::Player , source: Slot::Offline, destination: Slot::Online , count: y } ) },
35 => { Ok( Self::MoveCards{ player: PlayerType::Opponent, source: Slot::Offline, destination: Slot::Online , count: y } ) },
36 => { Ok( SupportEffect::MoveCards{ player: PlayerType::Player , source: Slot::Dp , destination: Slot::Offline, count: y } ) },
37 => { Ok( SupportEffect::MoveCards{ player: PlayerType::Opponent, source: Slot::Dp , destination: Slot::Offline, count: y } ) },
36 => { Ok( Self::MoveCards{ player: PlayerType::Player , source: Slot::Dp , destination: Slot::Offline, count: y } ) },
37 => { Ok( Self::MoveCards{ player: PlayerType::Opponent, source: Slot::Dp , destination: Slot::Offline, count: y } ) },
42 => { Ok( SupportEffect::ShuffleOnlineDeck{ player: PlayerType::Player } ) },
43 => { Ok( SupportEffect::ShuffleOnlineDeck{ player: PlayerType::Opponent } ) },
42 => { Ok( Self::ShuffleOnlineDeck{ player: PlayerType::Player } ) },
43 => { Ok( Self::ShuffleOnlineDeck{ player: PlayerType::Opponent } ) },
44 => { Ok( SupportEffect::VoidOpponentSupportEffect ) },
45 => { Ok( SupportEffect::VoidOpponentSupportOptionEffect ) },
44 => { Ok( Self::VoidOpponentSupportEffect ) },
45 => { Ok( Self::VoidOpponentSupportOptionEffect ) },
46 => { Ok( SupportEffect::PickPartnerCard ) },
46 => { Ok( Self::PickPartnerCard ) },
47 => { Ok( SupportEffect::CycleOpponentAttackType ) },
47 => { Ok( Self::CycleOpponentAttackType ) },
48 => { Ok( SupportEffect::KoDigimonRevives{ health: y } ) },
48 => { Ok( Self::KoDigimonRevives{ health: y } ) },
49 => { Ok( SupportEffect::DrawCards{ player: PlayerType::Player , count: y } ) },
50 => { Ok( SupportEffect::DrawCards{ player: PlayerType::Opponent, count: y } ) },
49 => { Ok( Self::DrawCards{ player: PlayerType::Player , count: y } ) },
50 => { Ok( Self::DrawCards{ player: PlayerType::Opponent, count: y } ) },
51 => { Ok( SupportEffect::OwnAttackBecomesEatUpHP ) },
51 => { Ok( Self::OwnAttackBecomesEatUpHP ) },
52 => { Ok( SupportEffect::AttackFirst{ player: PlayerType::Player } ) },
53 => { Ok( SupportEffect::AttackFirst{ player: PlayerType::Opponent } ) },
52 => { Ok( Self::AttackFirst{ player: PlayerType::Player } ) },
53 => { Ok( Self::AttackFirst{ player: PlayerType::Opponent } ) },
_ => Err( FromBytesError::UnknownEffectType{ byte: effect_type_byte } ),
}
@@ -280,10 +295,13 @@ use byteorder::{ByteOrder, LittleEndian};
fn to_bytes(&self, _bytes: &mut [u8]) -> Result<(), Self::Error>
{
// Match which effect we are
todo!()
/*
match self
{
_ => { unimplemented!(); }
_ => { todo!(); }
}
*/
// Return Ok
//Ok(())

View File

@@ -8,34 +8,36 @@
//! The digimon table has a max size of [0x14950](Table::MAX_BYTE_SIZE), but does not
//! necessary use all of this space, but it does follow this layout:
//!
//! | Offset | Size | Type | Name | Details |
//! |--------|----------|---------------|----------------------|-------------------------------------------------------------------------|
//! | 0x0 | 0x4 | u32 | Magic | Always contains the string "0ACD" (= [0x44434130](Table::HEADER_MAGIC)) |
//! | 0x4 | 0x2 | u16 | Number of digimon | |
//! | 0x6 | 0x1 | u8 | Number of items | |
//! | 0x7 | 0x1 | u8 | Number of digivolves | |
//! | 0x8 | variable | \[CardEntry\] | Card Entries | A contigous array of [Card Entry](#card-entry-layout) |
//! | Offset | Size | Type | Name | Details |
//! |--------|----------|-----------------|----------------------|-------------------------------------------------------------------------|
//! | 0x0 | 0x4 | u32 | Magic | Always contains the string "0ACD" (= [0x44434130](Table::HEADER_MAGIC)) |
//! | 0x4 | 0x2 | u16 | Number of digimon | |
//! | 0x6 | 0x1 | u8 | Number of items | |
//! | 0x7 | 0x1 | u8 | Number of digivolves | |
//! | 0x8 | variable | \[`CardEntry`\] | Card Entries | A contigous array of [Card Entry](#card-entry-layout) |
//!
//! # Card Entry Layout
//! Each card entry consists of a header of the card
//!
//! | Offset | Size | Type | Name | Details |
//! |--------|----------|------------------------------------|-----------------|----------------------------------------------|
//! | 0x0 | 0x3 | [Card Header](#card-header-layout) | Card Header | The card's header |
//! | 0x3 | variable | | Card | Either a [Digimon], [Item] or [Digivolve] |
//! | ... | 0x1 | u8 | Null terminator | A null terminator for the card (must be `0`) |
//! | Offset | Size | Type | Name | Details |
//! |--------|----------|--------------------------------------|-----------------|----------------------------------------------|
//! | 0x0 | 0x3 | [`Card Header`](#card-header-layout) | Card Header | The card's header |
//! | 0x3 | variable | | Card | Either a [Digimon], [Item] or [Digivolve] |
//! | ... | 0x1 | u8 | Null terminator | A null terminator for the card (must be `0`) |
//!
//! # Card Header Layout
//! The card header determines which type of card this card entry has.
//!
//! | Offset | Size | Type | Name | Details |
//! |--------|------|------------|-----------|--------------------------------------------------|
//! | 0x0 | 0x2 | u16 | Card id | This card's ID |
//! | 0x2 | 0x1 | [CardType] | Card type | The card type ([Digimon], [Item] or [Digivolve]) |
//!
//! # Todo
//! [Table] might be changed from it's [Table::new] and [Table::write_to_file] interface to
//! using the [ToBytes](crate::game::ToBytes) and [FromBytes](crate::game::FromBytes) traits, once it is fully figured out.
//! | Offset | Size | Type | Name | Details |
//! |--------|------|--------------|-----------|--------------------------------------------------|
//! | 0x0 | 0x2 | u16 | Card id | This card's ID |
//! | 0x2 | 0x1 | [`CardType`] | Card type | The card type ([Digimon], [Item] or [Digivolve]) |
// Io Traits
use std::io::{Read, Write, Seek};
// byteorder
use byteorder::{ByteOrder, LittleEndian};
// Dcb
use crate::{
@@ -46,16 +48,9 @@ use crate::{
property::{CardType, card_type::UnknownCardType},
},
Bytes, FromBytes,
Structure,
}
};
// Io Traits
use std::io::{Read, Write, Seek};
// byteorder
use byteorder::{ByteOrder, LittleEndian};
/// The table storing all cards
#[derive(Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
@@ -81,46 +76,10 @@ impl Table {
pub const HEADER_MAGIC: u32 = 0x44434130;
}
// Error type for [`Table::from_game_file`]
#[derive(Debug)]
#[derive(derive_more::Display)]
pub enum TableFromGameFileError {
/// Unable to seek game file
#[display(fmt = "Unable to seek game file")]
Seek( std::io::Error ),
/// Unable to deserialize table
#[display(fmt = "Unable to deserialize table from game file")]
Deserialize( DeserializeError ),
}
impl std::error::Error for TableFromGameFileError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Seek (err) => Some(err),
Self::Deserialize(err) => Some(err),
}
}
}
// Constructors
impl Table {
/// Retrieves the card table from a game file
pub fn from_game_file<R: Read + Write + Seek>(game_file: &mut GameFile<R>) -> Result<Table, TableFromGameFileError> {
// Seek to the table address
game_file.seek( std::io::SeekFrom::Start( u64::from( Self::START_ADDRESS ) ) )
.map_err(TableFromGameFileError::Seek)?;
// Deserialize it and return it
let table = Table::deserialize(game_file)
.map_err(TableFromGameFileError::Deserialize)?;
Ok(table)
}
}
// Utils
impl Table {
/// Returns how many cards are in this table
#[must_use]
pub fn card_count(&self) -> usize {
self.digimons .len() +
self.items .len() +
@@ -129,13 +88,17 @@ impl Table {
}
/// Error type for [`<Table as Structure>::DeserializeError`]
/// Error type for [`Structure::DeserializeError`]
#[derive(Debug)]
#[derive(derive_more::Display)]
pub enum DeserializeError {
/// Unable to seek game file
#[display(fmt = "Unable to seek game file to card table")]
Seek( std::io::Error ),
/// Unable to read table header
#[display(fmt = "Unable to read table header")]
ReadTableHeader( std::io::Error ),
ReadHeader( std::io::Error ),
/// The magic of the table was wrong
#[display(fmt = "Found wrong table header magic (expected {:x}, found {:x})", Table::HEADER_MAGIC, "magic")]
@@ -148,11 +111,9 @@ pub enum DeserializeError {
"digimon_cards",
"item_cards",
"digivolve_cards",
r#"
digimon_cards * (0x3 + Digimon ::BUF_BYTE_SIZE + 0x1) +
" digimon_cards * (0x3 + Digimon ::BUF_BYTE_SIZE + 0x1) +
item_cards * (0x3 + Item ::BUF_BYTE_SIZE + 0x1) +
digivolve_cards * (0x3 + Digivolve::BUF_BYTE_SIZE + 0x1)
"#,
digivolve_cards * (0x3 + Digivolve::BUF_BYTE_SIZE + 0x1)",
Table::MAX_BYTE_SIZE
)]
TooManyCards {
@@ -186,7 +147,8 @@ pub enum DeserializeError {
impl std::error::Error for DeserializeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::ReadTableHeader(err) |
Self::Seek(err) |
Self::ReadHeader(err) |
Self::ReadCardHeader { err, .. } |
Self::ReadCardFooter { err, .. } => Some(err),
Self::UnknownCardType { err, .. } => Some(err),
@@ -196,23 +158,21 @@ impl std::error::Error for DeserializeError {
}
}
impl Structure for Table {
type SerializeError = !;
type DeserializeError = DeserializeError;
fn size() -> (usize, Option<usize>) {
(Self::HEADER_BYTE_SIZE, Some(Self::MAX_BYTE_SIZE))
}
fn deserialize<R: Read + Write + Seek>(file: &mut GameFile<R>) -> Result<Self, Self::DeserializeError> {
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 ) ) )
.map_err(DeserializeError::Seek)?;
// Read header
let mut header_bytes = [0u8; 0x8];
file.read_exact(&mut header_bytes)
.map_err(Self::DeserializeError::ReadTableHeader)?;
.map_err(DeserializeError::ReadHeader)?;
// Check if the magic is right
let magic = LittleEndian::read_u32( &header_bytes[0x0..0x4] );
if magic != Table::HEADER_MAGIC { return Err( Self::DeserializeError::HeaderMagic{ magic } ); }
if magic != Self::HEADER_MAGIC { return Err( DeserializeError::HeaderMagic{ magic } ); }
// Then check the number of each card
let digimon_cards = LittleEndian::read_u16( &header_bytes[0x4..0x6] ) as usize;
@@ -226,7 +186,7 @@ impl Structure for Table {
let table_size = digimon_cards * (0x3 + Digimon ::BUF_BYTE_SIZE + 0x1) +
item_cards * (0x3 + Item ::BUF_BYTE_SIZE + 0x1) +
digivolve_cards * (0x3 + Digivolve::BUF_BYTE_SIZE + 0x1);
if table_size > Table::MAX_BYTE_SIZE { return Err( Self::DeserializeError::TooManyCards {
if table_size > Self::MAX_BYTE_SIZE { return Err( DeserializeError::TooManyCards {
digimon_cards,
item_cards,
digivolve_cards,
@@ -244,12 +204,12 @@ impl Structure for Table {
// Read card header bytes
let mut card_header_bytes = [0u8; 0x3];
file.read_exact(&mut card_header_bytes)
.map_err(|err| Self::DeserializeError::ReadCardHeader { id: cur_id, err })?;
.map_err(|err| DeserializeError::ReadCardHeader { id: cur_id, err })?;
// Read the header
let card_id = LittleEndian::read_u16( &card_header_bytes[0x0..0x2] );
let card_type = CardType::from_bytes( &card_header_bytes[0x2..0x3] )
.map_err(|err| Self::DeserializeError::UnknownCardType{ id: cur_id, err } )?;
.map_err(|err| DeserializeError::UnknownCardType{ id: cur_id, err } )?;
// If the card id isn't what we expected, log warning
if usize::from(card_id) != cur_id {
@@ -287,21 +247,21 @@ impl Structure for Table {
// Skip null terminator
let mut null_terminator = [0; 1];
file.read_exact(&mut null_terminator)
.map_err(|err| Self::DeserializeError::ReadCardFooter { id: cur_id, err })?;
.map_err(|err| DeserializeError::ReadCardFooter { id: cur_id, err })?;
if null_terminator[0] != 0 {
log::warn!("Card with id {}'s null terminator was {} instead of 0", cur_id, null_terminator[0]);
}
}
// Return the table
Ok( Table {
Ok( Self {
digimons,
items,
digivolves,
})
}
fn serialize<R: Read + Write + Seek>(&self, _file: &mut GameFile<R>) -> Result<(), Self::SerializeError> {
pub fn serialize<R: Read + Write + Seek>(&self, _file: &mut GameFile<R>) -> Result<(), !> {
todo!();
/*

View File

@@ -1,3 +1,5 @@
//! Deck
// Modules
//--------------------------------------------------------------------------------------------------
//pub mod deck;

View File

@@ -47,7 +47,7 @@ use serde::Deserialize;
/// Error type for `Table::new`
#[derive(Debug, derive_more::Display)]
pub enum TableNewError
pub enum NewError
{
/// Could not seek tothe beginning of the deck table
#[display(fmt = "Could not seek to the beginning of the deck table")]
@@ -78,7 +78,7 @@ use serde::Deserialize;
*/
}
impl std::error::Error for TableNewError {
impl std::error::Error for NewError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::SeekTableBegin(err) |
@@ -90,7 +90,7 @@ use serde::Deserialize;
/// Error type for `Table::write_to_file`
#[derive(Debug, derive_more::Display)]
pub enum TableWriteError
pub enum WriteError
{
/// The deck table was too big
#[display(fmt = "The deck table was too big (is {}, should be 65536 max)", _0)]
@@ -127,7 +127,7 @@ use serde::Deserialize;
// Constructors
//--------------------------------------------------------------------------------------------------
/// Reads the deck table from a dcb bin file
pub fn new<F>(game_file: &mut GameFile<F>) -> Result<Table, TableNewError>
pub fn new<F>(game_file: &mut GameFile<F>) -> Result<Self, NewError>
where
F: Read + Write + Seek
{
@@ -136,7 +136,7 @@ use serde::Deserialize;
// Seek to the beginning of the deck table
game_file.seek( std::io::SeekFrom::Start( u64::from( Table::DECK_TABLE_START_ADDRESS) ) ).map_err(TableNewError::SeekTableBegin)?;
game_file.seek( std::io::SeekFrom::Start( u64::from( Self::DECK_TABLE_START_ADDRESS) ) ).map_err(NewError::SeekTableBegin)?;
// Then loop until we're at the end of the table
//'table_loop: loop
@@ -144,15 +144,15 @@ use serde::Deserialize;
{
// Read the deck
let mut buf = [0u8; 110];
game_file.read_exact(&mut buf).map_err(TableNewError::DeckEntry).unwrap();
game_file.read_exact(&mut buf)
.map_err(NewError::DeckEntry)?;
// And construct the deck
let deck = Deck {
cards: {
let mut cards_buf = [0u16; 30];
for card_id in 0..30
{
for card_id in 0..30 {
cards_buf[card_id] = LittleEndian::read_u16( &buf[0x0 + card_id*2 .. 0x2 + card_id*2] );
}
@@ -165,7 +165,7 @@ use serde::Deserialize;
}
// And return the table
Ok( Table {
Ok( Self {
decks,
})
}
@@ -173,8 +173,9 @@ use serde::Deserialize;
// Write
//--------------------------------------------------------------------------------------------------
/*
/// Writes this table to a dcb bin file
pub fn write_to_file<F>(&self, _game_file: &mut GameFile<F>) -> Result<(), TableWriteError>
pub fn write_to_file<F>(&self, _game_file: &mut GameFile<F>) -> Result<(), WriteError>
where
F: Read + Write + Seek
{
@@ -204,6 +205,7 @@ use serde::Deserialize;
Ok(())
}
*/
//--------------------------------------------------------------------------------------------------
}
//--------------------------------------------------------------------------------------------------

View File

@@ -1,31 +0,0 @@
//! Game structure informations and serialization / deserialization
// Std
use std::{
io::{Read, Write, Seek},
error::Error,
};
// Crate
use crate::io::GameFile;
/// Trait that stores information about a game structure
pub trait Structure
where
Self: Sized
{
/// Error type for [`Self::serialize`]
type SerializeError: Error;
/// Error type for [`Self::deserialize`]
type DeserializeError: Error;
/// Returns the size of this structure
fn size() -> (usize, Option<usize>);
/// Attempts to deserialize this data structure from a game file
fn deserialize<R: Read + Write + Seek>(file: &mut GameFile<R>) -> Result<Self, Self::DeserializeError>;
/// Attempts to serialize the structure to the game file
fn serialize<R: Read + Write + Seek>(&self, file: &mut GameFile<R>) -> Result<(), Self::SerializeError>;
}

View File

@@ -132,6 +132,7 @@
}
/// Returns an ordinal string from a u64
#[must_use]
pub fn as_ordinal(num: u64) -> String
{
format!("{0}{1}", num, match num % 10 {

View File

@@ -11,4 +11,3 @@ pub mod address;
// Exports
pub use game_file::GameFile;
pub use address::Data as DataAddress;

View File

@@ -15,8 +15,7 @@ pub use data::Data;
/// Error type for `TryFrom<Real> for Data`
#[derive(Debug, derive_more::Display)]
pub enum RealToDataError
{
pub enum RealToDataError {
/// Occurs when the Real is outside of the data section of the sector
#[display(fmt = "The real address {} could not be converted to a data address as it is not in the data section", _0)]
OutsideDataSection(Real),
@@ -31,11 +30,10 @@ impl std::error::Error for RealToDataError {
}
// Real -> Data
impl std::convert::TryFrom<Real> for Data
{
impl std::convert::TryFrom<Real> for Data {
type Error = RealToDataError;
fn try_from(real_address: Real) -> Result<Data, Self::Error>
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) ); }
@@ -47,7 +45,7 @@ impl std::convert::TryFrom<Real> for Data
// 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
Ok( Data::from(
Ok( Self::from(
Real::SECTOR_BYTE_SIZE * real_sector + // Base of data sector
real_sector_offset - Real::HEADER_BYTE_SIZE // Data offset
))
@@ -57,7 +55,7 @@ impl std::convert::TryFrom<Real> for Data
// Data -> Real
impl From<Data> for Real
{
fn from(data_address: Data) -> Real
fn from(data_address: Data) -> Self
{
// Get the sector and offset
let data_sector = data_address.sector();
@@ -65,9 +63,9 @@ impl From<Data> for Real
// Then the real address is just convering the data_sector
// to a real_sector and adding the header plus the offset
Real::from(
Real::SECTOR_BYTE_SIZE * data_sector + // Base of real sector
Real::HEADER_BYTE_SIZE + // Skip header
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
)
}

View File

@@ -3,127 +3,101 @@
// Address
use crate::io::address::Real;
// Types
//--------------------------------------------------------------------------------------------------
/// A type for defining addresses on the data parts of `.bin` file.
///
/// # Details
/// All addresses of type `Data` will represent the position
/// within *only* the data sections on the file.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Data(u64);
//--------------------------------------------------------------------------------------------------
/// A type for defining addresses on the data parts of `.bin` file.
///
/// # Details
/// All addresses of type `Data` will represent the position
/// within *only* the data sections on the file.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Data(u64);
// Impl
//--------------------------------------------------------------------------------------------------
impl Data
{
// Constructors
//--------------------------------------------------------------------------------------------------
/// Constructs a data address from it's representation in u64
///
/// # Note
/// `address` is not a real address, but a data address represented in `u64`
pub const fn from_u64(address: u64) -> Self {
Self( address )
}
//--------------------------------------------------------------------------------------------------
// Conversions
//--------------------------------------------------------------------------------------------------
/// Returns the sector associated with this address
pub fn sector(self) -> u64
{
u64::from(self) / Real::DATA_BYTE_SIZE
}
/// Returns the offset into the data section of this address
pub fn offset(self) -> u64
{
u64::from(self) % Real::DATA_BYTE_SIZE
}
//--------------------------------------------------------------------------------------------------
impl Data
{
/// Constructs a data address from it's `u64` representation
#[must_use]
pub const fn from_u64(address: u64) -> Self {
Self(address)
}
// Conversions from and into u64
impl From<Data> for u64 { fn from(address: Data) -> u64 { address.0 } }
impl From<u64 > for Data { fn from(address: u64 ) -> Data { Data(address) } }
/// Returns the sector associated with this address
#[must_use]
#[allow(clippy::integer_division)] // We want to get the whole division
pub fn sector(self) -> u64 {
u64::from(self) / Real::DATA_BYTE_SIZE
}
// Operations
//--------------------------------------------------------------------------------------------------
// Data + Offset
impl std::ops::Add<i64> for Data
{
type Output = Data;
fn add(self, offset: i64) -> Data
{
if offset > 0 {
self + (offset as u64)
} else {
self - (-offset as u64)
}
}
}
// Data += Offset
impl std::ops::AddAssign<i64> for Data
{
fn add_assign(&mut self, offset: i64) { *self = *self + offset; }
}
// Data + absolute
impl std::ops::Add<u64> for Data
{
type Output = Data;
fn add(self, absolute: u64) -> Data {
Self::from( self.0 + absolute )
}
}
// Data += absolute
impl std::ops::AddAssign<u64> for Data
{
fn add_assign(&mut self, absolute: u64) { *self = *self + absolute; }
}
// Data - absolute
impl std::ops::Sub<u64> for Data
{
type Output = Data;
fn sub(self, absolute: u64) -> Data {
Self::from( self.0 - absolute )
}
}
// Data -= absolute
impl std::ops::SubAssign<u64> for Data
{
fn sub_assign(&mut self, absolute: u64) { *self = *self - absolute; }
}
// Data - Data
impl std::ops::Sub<Data> for Data
{
type Output = i64;
fn sub(self, address: Data) -> i64
{
// TODO: Do this another way?
self.0 as i64 -
address.0 as i64
}
}
//--------------------------------------------------------------------------------------------------
/// Returns the offset into the data section of this address
#[must_use]
pub fn offset(self) -> u64 {
u64::from(self) % Real::DATA_BYTE_SIZE
}
}
// Conversions from and into u64
impl From<Data> for u64 { fn from(address: Data) -> Self { address.0 } }
impl From<u64 > for Data { fn from(address: u64 ) -> Self { Self(address) } }
// Data + Offset
impl std::ops::Add<i64> for Data
{
type Output = Self;
// Display
impl std::fmt::Display for Data
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
{
write!(f, "{:x}", u64::from(*self))
fn add(self, offset: i64) -> Self {
if offset > 0 {
self + (offset as u64)
} else {
self - (-offset as u64)
}
}
//--------------------------------------------------------------------------------------------------
}
// Data += Offset
impl std::ops::AddAssign<i64> for Data
{
fn add_assign(&mut self, offset: i64) { *self = *self + offset; }
}
// Data + absolute
impl std::ops::Add<u64> for Data {
type Output = Self;
fn add(self, absolute: u64) -> Self {
Self::from( self.0 + absolute )
}
}
// Data += absolute
impl std::ops::AddAssign<u64> for Data {
fn add_assign(&mut self, absolute: u64) { *self = *self + absolute; }
}
// Data - absolute
impl std::ops::Sub<u64> for Data {
type Output = Self;
fn sub(self, absolute: u64) -> Self {
Self::from( self.0 - absolute )
}
}
// Data -= absolute
impl std::ops::SubAssign<u64> for Data {
fn sub_assign(&mut self, absolute: u64) { *self = *self - absolute; }
}
// Data - Data
impl std::ops::Sub<Data> for Data {
type Output = i64;
fn sub(self, address: Self) -> i64 {
self.0 as i64 -
address.0 as i64
}
}
// Display
impl std::fmt::Display for Data {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:x}", u64::from(*self))
}
}

View File

@@ -2,15 +2,14 @@
/// A type for defining addresses on the `.bin` file.
///
/// # Details
/// All addresses of type `Real` will represent the *real* position
/// on the file.
/// All real addresses will depict the actual position
/// within the game file, including headers from the `.bin` file format.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[derive(derive_more::From, derive_more::Into)]
pub struct Real(u64);
// Constants
impl Real
{
impl Real {
/// The number of bytes within a whole sector
pub const SECTOR_BYTE_SIZE: u64 = 2352;
@@ -33,76 +32,66 @@ impl Real
pub const DATA_RANGE: std::ops::Range<u64> = Self::DATA_START .. Self::DATA_END;
}
impl Real
{
impl Real {
/// Returns the real sector associated with this address
#[must_use]
#[allow(clippy::integer_division)] // We want to get the whole division
pub fn sector(self) -> u64 {
u64::from(self) / Self::SECTOR_BYTE_SIZE
}
/// Returns the real offset into the sector of this address
/// Returns the offset into the sector of this address
#[must_use]
pub fn offset(self) -> u64 {
u64::from(self) % Self::SECTOR_BYTE_SIZE
}
/// Returns the real end address of the data section
/// Returns the address of the end of the data section in this sector.
#[must_use]
pub fn data_section_end(self) -> Self {
// Get the sector
let real_sector = self.sector();
// The end of the real data section is after the header and data sections
Self::from(
Real::SECTOR_BYTE_SIZE * real_sector + // Beginning of sector
Real::HEADER_BYTE_SIZE + // Skip Header
Real:: DATA_BYTE_SIZE // Skip Data
Self::SECTOR_BYTE_SIZE * real_sector + // Beginning of sector
Self::HEADER_BYTE_SIZE + // Skip Header
Self:: DATA_BYTE_SIZE // Skip Data
)
}
/// Checks if a real address lies within the data section
/// Checks if this address is within the real data section
#[must_use]
pub fn in_data_section(self) -> bool {
// If our offset is within the data range
Self::DATA_RANGE.contains( &self.offset() )
}
}
// Conversions from and into u64
impl From<Real> for u64 { fn from(address: Real) -> u64 { address.0 } }
impl From<u64 > for Real { fn from(address: u64 ) -> Real { Real(address) } }
// Conversions from and into i64
impl From<Real> for i64 { fn from(address: Real) -> i64 { u64::from(address ) as i64 } }
impl From<i64 > for Real { fn from(address: i64 ) -> Real { Real::from(address as u64) } }
// Real + Offset
impl std::ops::Add<i64> for Real {
type Output = Self;
fn add(self, offset: i64) -> Self {
Self::from( ( u64::from(self) as i64 + offset) as u64 )
}
}
// Operations
//--------------------------------------------------------------------------------------------------
// Real + Offset
impl std::ops::Add<i64> for Real
{
type Output = Real;
fn add(self, offset: i64) -> Real
{
Self::from( i64::from(self) + offset )
}
}
// Real += Offset
impl std::ops::AddAssign<i64> for Real {
fn add_assign(&mut self, offset: i64) { *self = *self + offset; }
}
// Real - Real
impl std::ops::Sub<Real> for Real
{
type Output = i64;
// Real += Offset
impl std::ops::AddAssign<i64> for Real
{
fn add_assign(&mut self, offset: i64) { *self = *self + offset; }
fn sub(self, address: Self) -> i64 {
u64::from(self) as i64 - u64::from(address) as i64
}
// Real - Real
impl std::ops::Sub<Real> for Real
{
type Output = i64;
fn sub(self, address: Real) -> i64
{
i64::from(self) - i64::from(address)
}
}
//--------------------------------------------------------------------------------------------------
}
// Display

View File

@@ -239,7 +239,7 @@ impl<R: Read + Write + Seek> Seek for GameFile<R>
data_offset
))
),
SeekFrom::End(_) => { unimplemented!(); }
SeekFrom::End(_) => { todo!(); }
};
// Seek to the real position and get where we are right now

View File

@@ -2,8 +2,8 @@
//! a PSX game.
//!
//! # Modules
//! `dcb` split itself into 2 main modules, [io], which interacts with the game file
//! as well as general input / output operations and [game], which is where most of
//! `dcb` splits itself into 2 main modules, [`io`], which interacts with the game file
//! as well as general input / output operations and [`game`], which is where most of
//! the game's data types are defined.
//!
//! # Example
@@ -14,7 +14,7 @@
//! ```no_run
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # use std::fs::File;
//! let mut game_file = dcb::GameFile::from_reader( File::open("resources/Digimon Digital Card Battle.bin")? );
//! let mut game_file = dcb::GameFile::from_reader( File::open("Digimon Digital Card Battle.bin")? );
//! let card_table = dcb::game::card::Table::new( &mut game_file )?;
//! println!("Card table: {:?}", card_table);
//! # Ok(())
@@ -22,9 +22,12 @@
//! ```
// Features
#![feature(seek_convenience)]
#![feature(never_type)]
#![feature(trait_alias)]
#![feature(
seek_convenience,
never_type,
trait_alias,
unsized_locals,
)]
// Lints
#![warn(
@@ -32,6 +35,33 @@
clippy::pedantic,
clippy::nursery,
)]
#![allow(
clippy::missing_inline_in_public_items, // Dubious lint
clippy::implicit_return, // We prefer tail returns where possible
clippy::shadow_reuse, // Very useful for arguments `arg: impl Into<U>; let arg = arg.into()`
clippy::if_not_else, // Sometimes it's easier to read with a negation
clippy::result_expect_used,
clippy::option_expect_used, // We use `.expect` when there is no safe alternative and the program is corrupt
clippy::unreadable_literal, // More important to be able to copy the number with no formatting than it being readable
clippy::multiple_inherent_impl, // We prefer to separate certain methods by type and insert error types in between methods
clippy::identity_op, // Makes sense sometimes for symmetry
// TODO: Deal with casts eventually
clippy::cast_possible_wrap,
clippy::cast_sign_loss,
clippy::cast_possible_truncation,
// TODO: Remove these once all modules are ported
clippy::missing_docs_in_private_items,
clippy::as_conversions,
clippy::panic,
clippy::indexing_slicing,
clippy::unseparated_literal_suffix,
clippy::integer_arithmetic,
clippy::unreachable,
clippy::todo,
clippy::missing_errors_doc,
)]
// Modules
pub mod io;