From f2ebaad3d843509913536677a49f4e3c8fde89d6 Mon Sep 17 00:00:00 2001 From: Filipe Rodrigues Date: Sun, 12 Apr 2020 18:29:23 +0100 Subject: [PATCH] Added various lints. Removed `Structure` interface. --- src/game.rs | 9 +- src/game/card/digimon.rs | 433 ++++++++++---------- src/game/card/digivolve.rs | 4 +- src/game/card/item.rs | 4 +- src/game/card/property.rs | 7 +- src/game/card/property/moves.rs | 2 +- src/game/card/property/support_condition.rs | 14 +- src/game/card/property/support_effect.rs | 86 ++-- src/game/card/table.rs | 136 +++--- src/game/deck.rs | 2 + src/game/deck/table.rs | 22 +- src/game/structure.rs | 31 -- src/game/util.rs | 1 + src/io.rs | 1 - src/io/address.rs | 18 +- src/io/address/data.rs | 208 ++++------ src/io/address/real.rs | 83 ++-- src/io/game_file.rs | 2 +- src/lib.rs | 42 +- 19 files changed, 529 insertions(+), 576 deletions(-) delete mode 100644 src/game/structure.rs diff --git a/src/game.rs b/src/game.rs index 853fb25..d68420d 100644 --- a/src/game.rs +++ b/src/game.rs @@ -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; diff --git a/src/game/card/digimon.rs b/src/game/card/digimon.rs index 28f1c9e..3386ac8 100644 --- a/src/game/card/digimon.rs +++ b/src/game/card/digimon.rs @@ -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, - - /// The effect description - pub description: [String; 4], - - /// The effect arrow color - pub arrow_color: Option, - - /// 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, - pub second: Option, - pub third : Option, - } + /// The cross move effect + pub cross_move: Option, - /// All of the support effect conditions - #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] - #[derive(serde::Serialize, serde::Deserialize)] - pub struct SupportConditions - { - pub first : Option, - pub second: Option, - } + /// 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, - /// 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, + pub second: Option, + pub third : Option, +} + +/// All of the support effect conditions +#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] +#[derive(serde::Serialize, serde::Deserialize)] +pub struct SupportConditions +{ + pub first : Option, + pub second: Option, +} + +/// 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 { + // 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(), diff --git a/src/game/card/digivolve.rs b/src/game/card/digivolve.rs index e0a3035..2232c93 100644 --- a/src/game/card/digivolve.rs +++ b/src/game/card/digivolve.rs @@ -1,3 +1,5 @@ +//! Digivolve + // Crate //-------------------------------------------------------------------------------------------------- // Game @@ -142,7 +144,7 @@ use serde::Deserialize; fn from_bytes(bytes: &[u8]) -> Result { - Ok( Digivolve { + Ok( Self { basic: Basic { name: util::read_null_terminated_string( &bytes[0x0..0x15] ).map_err(FromBytesError::NameToString)?.to_string(), }, diff --git a/src/game/card/item.rs b/src/game/card/item.rs index bfec5b5..02d943f 100644 --- a/src/game/card/item.rs +++ b/src/game/card/item.rs @@ -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(), diff --git a/src/game/card/property.rs b/src/game/card/property.rs index 49e03f6..c34ff04 100644 --- a/src/game/card/property.rs +++ b/src/game/card/property.rs @@ -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 => ::BUF_BYTE_SIZE, - CardType::Item => ::BUF_BYTE_SIZE, - CardType::Digivolve => ::BUF_BYTE_SIZE, + Self::Digimon => ::BUF_BYTE_SIZE, + Self::Item => ::BUF_BYTE_SIZE, + Self::Digivolve => ::BUF_BYTE_SIZE, } } } diff --git a/src/game/card/property/moves.rs b/src/game/card/property/moves.rs index 12c885f..83f24e5 100644 --- a/src/game/card/property/moves.rs +++ b/src/game/card/property/moves.rs @@ -63,7 +63,7 @@ use byteorder::LittleEndian; fn from_bytes(bytes: &[u8]) -> Result { // 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] ), diff --git a/src/game/card/property/support_condition.rs b/src/game/card/property/support_condition.rs index 2092ee4..fd3a985 100644 --- a/src/game/card/property/support_condition.rs +++ b/src/game/card/property/support_condition.rs @@ -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(()) diff --git a/src/game/card/property/support_effect.rs b/src/game/card/property/support_effect.rs index d6a3f75..432bb3d 100644 --- a/src/game/card/property/support_effect.rs +++ b/src/game/card/property/support_effect.rs @@ -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(()) diff --git a/src/game/card/table.rs b/src/game/card/table.rs index 5590351..2d778ba 100644 --- a/src/game/card/table.rs +++ b/src/game/card/table.rs @@ -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(game_file: &mut GameFile) -> Result { - // 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 [`::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) { - (Self::HEADER_BYTE_SIZE, Some(Self::MAX_BYTE_SIZE)) - } - - fn deserialize(file: &mut GameFile) -> Result { +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 ) ) ) + .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(&self, _file: &mut GameFile) -> Result<(), Self::SerializeError> { + pub fn serialize(&self, _file: &mut GameFile) -> Result<(), !> { todo!(); /* diff --git a/src/game/deck.rs b/src/game/deck.rs index 06b78c3..9bc7923 100644 --- a/src/game/deck.rs +++ b/src/game/deck.rs @@ -1,3 +1,5 @@ +//! Deck + // Modules //-------------------------------------------------------------------------------------------------- //pub mod deck; diff --git a/src/game/deck/table.rs b/src/game/deck/table.rs index 4cb2bf9..4ac610b 100644 --- a/src/game/deck/table.rs +++ b/src/game/deck/table.rs @@ -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(game_file: &mut GameFile) -> Result + pub fn new(game_file: &mut GameFile) -> Result 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(&self, _game_file: &mut GameFile) -> Result<(), TableWriteError> + pub fn write_to_file(&self, _game_file: &mut GameFile) -> Result<(), WriteError> where F: Read + Write + Seek { @@ -204,6 +205,7 @@ use serde::Deserialize; Ok(()) } + */ //-------------------------------------------------------------------------------------------------- } //-------------------------------------------------------------------------------------------------- diff --git a/src/game/structure.rs b/src/game/structure.rs deleted file mode 100644 index 6fb6be5..0000000 --- a/src/game/structure.rs +++ /dev/null @@ -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); - - /// Attempts to deserialize this data structure from a game file - fn deserialize(file: &mut GameFile) -> Result; - - /// Attempts to serialize the structure to the game file - fn serialize(&self, file: &mut GameFile) -> Result<(), Self::SerializeError>; -} diff --git a/src/game/util.rs b/src/game/util.rs index 6c11bef..0928309 100644 --- a/src/game/util.rs +++ b/src/game/util.rs @@ -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 { diff --git a/src/io.rs b/src/io.rs index 00bfd43..488542c 100644 --- a/src/io.rs +++ b/src/io.rs @@ -11,4 +11,3 @@ pub mod address; // Exports pub use game_file::GameFile; -pub use address::Data as DataAddress; diff --git a/src/io/address.rs b/src/io/address.rs index 52d5c6c..4d0fd8b 100644 --- a/src/io/address.rs +++ b/src/io/address.rs @@ -15,8 +15,7 @@ pub use data::Data; /// Error type for `TryFrom 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 for Data -{ +impl std::convert::TryFrom for Data { type Error = RealToDataError; - fn try_from(real_address: Real) -> Result + 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) ); } @@ -47,7 +45,7 @@ impl std::convert::TryFrom 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 for Data // Data -> Real impl From 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 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 ) } diff --git a/src/io/address/data.rs b/src/io/address/data.rs index 41a4f08..4254e73 100644 --- a/src/io/address/data.rs +++ b/src/io/address/data.rs @@ -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 for u64 { fn from(address: Data) -> u64 { address.0 } } - impl From 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 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 for Data - { - fn add_assign(&mut self, offset: i64) { *self = *self + offset; } - } - - // Data + absolute - impl std::ops::Add for Data - { - type Output = Data; - - fn add(self, absolute: u64) -> Data { - Self::from( self.0 + absolute ) - } - } - - // Data += absolute - impl std::ops::AddAssign for Data - { - fn add_assign(&mut self, absolute: u64) { *self = *self + absolute; } - } - - // Data - absolute - impl std::ops::Sub for Data - { - type Output = Data; - - fn sub(self, absolute: u64) -> Data { - Self::from( self.0 - absolute ) - } - } - - // Data -= absolute - impl std::ops::SubAssign for Data - { - fn sub_assign(&mut self, absolute: u64) { *self = *self - absolute; } - } - - // Data - Data - impl std::ops::Sub 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 for u64 { fn from(address: Data) -> Self { address.0 } } +impl From for Data { fn from(address: u64 ) -> Self { Self(address) } } + +// Data + Offset +impl std::ops::Add 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 for Data +{ + fn add_assign(&mut self, offset: i64) { *self = *self + offset; } +} + +// Data + absolute +impl std::ops::Add for Data { + type Output = Self; + + fn add(self, absolute: u64) -> Self { + Self::from( self.0 + absolute ) + } +} + +// Data += absolute +impl std::ops::AddAssign for Data { + fn add_assign(&mut self, absolute: u64) { *self = *self + absolute; } +} + +// Data - absolute +impl std::ops::Sub for Data { + type Output = Self; + + fn sub(self, absolute: u64) -> Self { + Self::from( self.0 - absolute ) + } +} + +// Data -= absolute +impl std::ops::SubAssign for Data { + fn sub_assign(&mut self, absolute: u64) { *self = *self - absolute; } +} + +// Data - Data +impl std::ops::Sub 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)) + } +} diff --git a/src/io/address/real.rs b/src/io/address/real.rs index ca7fad5..e8d6404 100644 --- a/src/io/address/real.rs +++ b/src/io/address/real.rs @@ -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 = 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 for u64 { fn from(address: Real) -> u64 { address.0 } } -impl From for Real { fn from(address: u64 ) -> Real { Real(address) } } -// Conversions from and into i64 -impl From for i64 { fn from(address: Real) -> i64 { u64::from(address ) as i64 } } -impl From for Real { fn from(address: i64 ) -> Real { Real::from(address as u64) } } +// Real + Offset +impl std::ops::Add 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 for Real - { - type Output = Real; - - fn add(self, offset: i64) -> Real - { - Self::from( i64::from(self) + offset ) - } - } +// Real += Offset +impl std::ops::AddAssign for Real { + fn add_assign(&mut self, offset: i64) { *self = *self + offset; } +} + +// Real - Real +impl std::ops::Sub for Real +{ + type Output = i64; - // Real += Offset - impl std::ops::AddAssign 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 for Real - { - type Output = i64; - - fn sub(self, address: Real) -> i64 - { - i64::from(self) - i64::from(address) - } - } -//-------------------------------------------------------------------------------------------------- +} // Display diff --git a/src/io/game_file.rs b/src/io/game_file.rs index c6ce26f..dbd3de8 100644 --- a/src/io/game_file.rs +++ b/src/io/game_file.rs @@ -239,7 +239,7 @@ impl Seek for GameFile data_offset )) ), - SeekFrom::End(_) => { unimplemented!(); } + SeekFrom::End(_) => { todo!(); } }; // Seek to the real position and get where we are right now diff --git a/src/lib.rs b/src/lib.rs index 658e5a5..338ed29 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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> { //! # 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; 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;