diff --git a/src/game/card/digimon.rs b/src/game/card/digimon.rs index 6d905ac..8c3415f 100644 --- a/src/game/card/digimon.rs +++ b/src/game/card/digimon.rs @@ -5,27 +5,27 @@ //! # Layout //! The digimon card has a size of `0x138` bytes, and it's layout is the following: //! -//! | Offset | Size | Type | Name | Location | Details | -//! |--------|------|----------------------|---------------------------|------------------------|-------------------------------------------------------------------------------------| -//! | 0x0 | 0x15 | `[char; 0x15]` | Name | `name` | Null-terminated | -//! | 0x15 | 0x2 | `u16` | Unknown | `unknown_15` | Most likely contains the digimon's model | -//! | 0x17 | 0x1 | `u8` | Speciality & Level | `speciality level` | The bottom nibble of this byte is the level, while the top nibble is the speciality | -//! | 0x18 | 0x1 | `u8` | DP | `dp_cost` | | -//! | 0x19 | 0x1 | `u8` | +P | `dp_give` | | -//! | 0x1a | 0x1 | `u8` | Unknown | `unknown_1a` | Is `0` for all digimon | -//! | 0x1b | 0x2 | `u16` | Health | `hp` | | -//! | 0x1d | 0x1c | [`Move`] | Circle Move | `move_circle` | | -//! | 0x39 | 0x1c | [`Move`] | Triangle move | `move_triangle` | | -//! | 0x55 | 0x1c | [`Move`] | Cross move | `move_cross` | | -//! | 0x71 | 0x20 | [`SupportCondition`] | First condition | `effect_conditions[0]` | | -//! | 0x91 | 0x20 | [`SupportCondition`] | Second condition | `effect_conditions[1]` | | -//! | 0xb1 | 0x10 | [`SupportEffect`] | First effect | `effects[0]` | | -//! | 0xc1 | 0x10 | [`SupportEffect`] | Second effect | `effects[1]` | | -//! | 0xd1 | 0x10 | [`SupportEffect`] | Third effect | `effects[2]` | | -//! | 0xe1 | 0x1 | [`CrossMoveEffect`] | Cross move effect | `cross_move_effect` | | -//! | 0xe2 | 0x1 | `u8` | Unknown | `unknown_e2` | | -//! | 0xe3 | 0x1 | [`ArrowColor`] | Effect arrow color | `effect_arrow_color` | | -//! | 0xe4 | 0x54 | `[[char; 0x15]; 4]` | Effect description lines | `effect_description` | Each line is` 0x15` bytes, split over 4 lines, each null terminated | +//! | Offset | Size | Type | Name | Location | Details | +//! |--------|------|---------------------|---------------------------|------------------------|-------------------------------------------------------------------------------------| +//! | 0x0 | 0x15 | `[char; 0x15]` | Name | `name` | Null-terminated | +//! | 0x15 | 0x2 | `u16` | Unknown | `unknown_15` | Most likely contains the digimon's model | +//! | 0x17 | 0x1 | `u8` | Speciality & Level | `speciality level` | The bottom nibble of this byte is the level, while the top nibble is the speciality | +//! | 0x18 | 0x1 | `u8` | DP | `dp_cost` | | +//! | 0x19 | 0x1 | `u8` | +P | `dp_give` | | +//! | 0x1a | 0x1 | `u8` | Unknown | `unknown_1a` | Is `0` for all digimon | +//! | 0x1b | 0x2 | `u16` | Health | `hp` | | +//! | 0x1d | 0x1c | [`Move`] | Circle Move | `move_circle` | | +//! | 0x39 | 0x1c | [`Move`] | Triangle move | `move_triangle` | | +//! | 0x55 | 0x1c | [`Move`] | Cross move | `move_cross` | | +//! | 0x71 | 0x20 | [`EffectCondition`] | First condition | `effect_conditions[0]` | | +//! | 0x91 | 0x20 | [`EffectCondition`] | Second condition | `effect_conditions[1]` | | +//! | 0xb1 | 0x10 | [`Effect`] | First effect | `effects[0]` | | +//! | 0xc1 | 0x10 | [`Effect`] | Second effect | `effects[1]` | | +//! | 0xd1 | 0x10 | [`Effect`] | Third effect | `effects[2]` | | +//! | 0xe1 | 0x1 | [`CrossMoveEffect`] | Cross move effect | `cross_move_effect` | | +//! | 0xe2 | 0x1 | `u8` | Unknown | `unknown_e2` | | +//! | 0xe3 | 0x1 | [`ArrowColor`] | Effect arrow color | `effect_arrow_color` | | +//! | 0xe4 | 0x54 | `[[char; 0x15]; 4]` | Effect description lines | `effect_description` | Each line is` 0x15` bytes, split over 4 lines, each null terminated | // byteorder use byteorder::{ByteOrder, LittleEndian}; @@ -40,8 +40,8 @@ use crate::game::{ Level, Move, CrossMoveEffect, - SupportCondition, - SupportEffect, + EffectCondition, + Effect, ArrowColor, } }; @@ -111,11 +111,11 @@ pub struct Digimon /// The effect conditions #[serde(default)] - pub effect_conditions: [Option; 2], + pub effect_conditions: [Option; 2], /// The effects themselves #[serde(default)] - pub effects: [Option; 3], + pub effects: [Option; 3], } /// Error type for [`Bytes::from_bytes`] @@ -173,23 +173,23 @@ pub enum FromBytesError /// Unable to read the first effect condition #[display(fmt = "Unable to read the first effect condition")] - EffectConditionFirst( #[error(source)] property::support_condition::FromBytesError ), + EffectConditionFirst( #[error(source)] property::effect_condition::FromBytesError ), /// Unable to read the second effect condition #[display(fmt = "Unable to read the second effect condition")] - EffectConditionSecond( #[error(source)] property::support_condition::FromBytesError ), + EffectConditionSecond( #[error(source)] property::effect_condition::FromBytesError ), /// Unable to read the first effect #[display(fmt = "Unable to read the first effect")] - EffectFirst( #[error(source)] property::support_effect::FromBytesError ), + EffectFirst( #[error(source)] property::effect::FromBytesError ), /// Unable to read the second effect #[display(fmt = "Unable to read the second effect")] - EffectSecond( #[error(source)] property::support_effect::FromBytesError ), + EffectSecond( #[error(source)] property::effect::FromBytesError ), /// Unable to read the third effect #[display(fmt = "Unable to read the third effect")] - EffectThird( #[error(source)] property::support_effect::FromBytesError ), + EffectThird( #[error(source)] property::effect::FromBytesError ), } /// Error type for [`Bytes::to_bytes`] #[derive(Debug)] @@ -238,25 +238,28 @@ impl Bytes for Digimon { // Get all byte arrays we need let bytes = util::array_split!(bytes, - 0x00..0x15 => name, - 0x15..0x17 => unknown_15, - =0x17 => speciality_level, - =0x18 => dp_cost, - =0x19 => dp_give, - =0x1a => unknown_1a, - 0x1b..0x1d => hp, - 0x1d..0x39 => move_circle, - 0x39..0x55 => move_triangle, - 0x55..0x71 => move_cross, - 0x71..0x91 => condition_first, - 0x91..0xb1 => condition_second, - 0xb1..0xc1 => effect_first, - 0xc1..0xd1 => effect_second, - 0xd1..0xe1 => effect_third, - 0xe1..0xe2 => cross_move_effect, - 0xe2..0xe3 => unknown_e2, - 0xe3..0xe4 => effect_arrow_color, - 0xe4..0x138 => effect_description, + name : [0x15], + unknown_15 : [0x2], + speciality_level : 0x1, + dp_cost : 0x1, + dp_give : 0x1, + unknown_1a : 0x1, + hp : [0x2], + move_circle : [0x1c], + move_triangle : [0x1c], + move_cross : [0x1c], + condition_first : [0x20], + condition_second : [0x20], + effect_first : [0x10], + effect_second : [0x10], + effect_third : [0x10], + cross_move_effect : 1, + unknown_e2 : 1, + effect_arrow_color : 1, + effect_description_0: [0x15], + effect_description_1: [0x15], + effect_description_2: [0x15], + effect_description_3: [0x15], ); // Return the struct after building it @@ -290,57 +293,53 @@ impl Bytes for Digimon // 0x71 - 0x138 effect_conditions: [ - (bytes.condition_first[2] != 0) - .then(|| SupportCondition::from_bytes( bytes.condition_first ) ) - .transpose() + Option::::from_bytes( bytes.condition_first ) .map_err(FromBytesError::EffectConditionFirst)?, - (bytes.condition_second[2] != 0) - .then(|| SupportCondition::from_bytes( bytes.condition_second ) ) - .transpose() + Option::::from_bytes( bytes.condition_second ) .map_err(FromBytesError::EffectConditionSecond)?, ], effects: [ - (bytes.effect_first[0] != 0) - .then(|| SupportEffect::from_bytes( bytes.effect_first ) ) + (bytes.effect_first[0x0] != 0) + .then(|| Effect::from_bytes( bytes.effect_first ) ) .transpose() .map_err(FromBytesError::EffectFirst)?, - (bytes.effect_second[0] != 0) - .then(|| SupportEffect::from_bytes( bytes.effect_second ) ) + (bytes.effect_second[0x0] != 0) + .then(|| Effect::from_bytes( bytes.effect_second ) ) .transpose() .map_err(FromBytesError::EffectSecond)?, - (bytes.effect_third[0] != 0) - .then(|| SupportEffect::from_bytes( bytes.effect_third ) ) + (bytes.effect_third[0x0] != 0) + .then(|| Effect::from_bytes( bytes.effect_third ) ) .transpose() .map_err(FromBytesError::EffectThird)?, ], - cross_move_effect: (bytes.cross_move_effect[0] != 0) - .then(|| CrossMoveEffect::from_bytes( &bytes.cross_move_effect[0] ) ) + cross_move_effect: (*bytes.cross_move_effect != 0) + .then(|| CrossMoveEffect::from_bytes( bytes.cross_move_effect ) ) .transpose() .map_err(FromBytesError::CrossMoveEffect)?, - unknown_e2: bytes.unknown_e2[0], + unknown_e2: *bytes.unknown_e2, - effect_arrow_color: (bytes.effect_arrow_color[0] != 0) - .then(|| ArrowColor::from_bytes( &bytes.effect_arrow_color[0] ) ) + effect_arrow_color: (*bytes.effect_arrow_color != 0) + .then(|| ArrowColor::from_bytes( bytes.effect_arrow_color ) ) .transpose() .map_err(FromBytesError::ArrowColor)?, effect_description: [ - util::read_null_ascii_string( &bytes.effect_description[0x00..0x15] ) + util::read_null_ascii_string( bytes.effect_description_0 ) .map_err(FromBytesError::EffectDescriptionFirst)? .chars().collect(), - util::read_null_ascii_string( &bytes.effect_description[0x15..0x2a] ) + util::read_null_ascii_string( bytes.effect_description_1 ) .map_err(FromBytesError::EffectDescriptionSecond)? .chars().collect(), - util::read_null_ascii_string( &bytes.effect_description[0x2a..0x3f] ) + util::read_null_ascii_string( bytes.effect_description_2 ) .map_err(FromBytesError::EffectDescriptionThird)? .chars().collect(), - util::read_null_ascii_string( &bytes.effect_description[0x3f..0x54] ) + util::read_null_ascii_string( bytes.effect_description_3 ) .map_err(FromBytesError::EffectDescriptionFourth)? .chars().collect(), ], @@ -410,16 +409,14 @@ impl Bytes for Digimon self.move_triangle.to_bytes( bytes.move_triangle ).map_err(ToBytesError::MoveTriangle)?; self. move_cross.to_bytes( bytes.move_cross ).map_err(ToBytesError::MoveCross )?; - // Support conditions - // Note: Although support conditions and effects aren't written if they're None, - // a bit pattern of all 0s is a valid pattern and means "None" to the game. - if let Some(support_condition) = &self.effect_conditions[0] { support_condition.to_bytes( bytes.condition_first )?; } - if let Some(support_condition) = &self.effect_conditions[1] { support_condition.to_bytes( bytes.condition_second )?; } + // Effect conditions + self.effect_conditions[0].to_bytes( bytes.condition_first ).into_ok(); + self.effect_conditions[1].to_bytes( bytes.condition_second ).into_ok(); - // Support effects - if let Some(support_effect) = &self.effects[0] { support_effect.to_bytes( bytes.effect_first )?; } - if let Some(support_effect) = &self.effects[1] { support_effect.to_bytes( bytes.effect_second )?; } - if let Some(support_effect) = &self.effects[2] { support_effect.to_bytes( bytes.effect_third )?; } + // Effects + if let Some(effect) = &self.effects[0] { effect.to_bytes( bytes.effect_first )?; } + if let Some(effect) = &self.effects[1] { effect.to_bytes( bytes.effect_second )?; } + if let Some(effect) = &self.effects[2] { effect.to_bytes( bytes.effect_third )?; } // Cross move if let Some(move_cross) = self.cross_move_effect { move_cross.to_bytes( bytes.cross_move_effect )? }; diff --git a/src/game/card/digivolve.rs b/src/game/card/digivolve.rs index f79bf52..beb4015 100644 --- a/src/game/card/digivolve.rs +++ b/src/game/card/digivolve.rs @@ -93,14 +93,14 @@ use serde::Deserialize; SupportEffectCondition { rank: &'static str, digivolve_pos: u64, - err: crate::game::card::property::support_condition::FromBytesError, + err: crate::game::card::property::effect_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, + err: crate::game::card::property::effect::FromBytesError, }, } diff --git a/src/game/card/item.rs b/src/game/card/item.rs index 7a4a707..b083f79 100644 --- a/src/game/card/item.rs +++ b/src/game/card/item.rs @@ -5,8 +5,8 @@ // Game use crate::game::util; use crate::game::Bytes; - use crate::game::card::property::SupportCondition; - use crate::game::card::property::SupportEffect; + use crate::game::card::property::EffectCondition; + use crate::game::card::property::Effect; use crate::game::card::property::ArrowColor; //-------------------------------------------------------------------------------------------------- @@ -56,16 +56,16 @@ use serde::Deserialize; #[derive(Debug, Serialize, Deserialize)] struct SupportEffects { - first : Option, - second: Option, - third : Option, + first : Option, + second: Option, + third : Option, } #[derive(Debug, Serialize, Deserialize)] struct SupportConditions { - first : Option, - second: Option, + first : Option, + second: Option, } /// The error type thrown by `FromBytes` @@ -92,14 +92,14 @@ use serde::Deserialize; SupportCondition { rank: &'static str, item_pos: u64, - err: crate::game::card::property::support_condition::FromBytesError, + err: crate::game::card::property::effect_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, + err: crate::game::card::property::effect::FromBytesError, }, } @@ -176,26 +176,23 @@ use serde::Deserialize; } else { None }, conditions: SupportConditions { - first: if bytes[0x19] != 0 { Some( - SupportCondition::from_bytes( array_ref!(bytes, 0x19, 0x20) ).map_err(|err| FromBytesError::SupportCondition{ rank: "1st", item_pos: 0x19, err })? - )} else { None }, - - second: if bytes[0x39] != 0 { Some( - SupportCondition::from_bytes( array_ref!(bytes, 0x39, 0x20) ).map_err(|err| FromBytesError::SupportCondition{ rank: "2nd", item_pos: 0x39, err })? - )} else { None }, + first: Option::::from_bytes( array_ref!(bytes, 0x19, 0x20) ) + .map_err(|err| FromBytesError::SupportCondition{ rank: "1st", item_pos: 0x19, err })?, + second: Option::::from_bytes( array_ref!(bytes, 0x39, 0x20) ) + .map_err(|err| FromBytesError::SupportCondition{ rank: "2nd", item_pos: 0x39, err })?, }, effects: SupportEffects { first: if bytes[0x59] != 0 { Some( - SupportEffect::from_bytes( array_ref!(bytes, 0x59, 0x10) ).map_err(|err| FromBytesError::SupportEffect{ rank: "1st", err })? + Effect::from_bytes( array_ref!(bytes, 0x59, 0x10) ).map_err(|err| FromBytesError::SupportEffect{ rank: "1st", err })? )} else { None }, second: if bytes[0x69] != 0 { Some( - SupportEffect::from_bytes( array_ref!(bytes, 0x69, 0x10) ).map_err(|err| FromBytesError::SupportEffect{ rank: "2nd", err })? + Effect::from_bytes( array_ref!(bytes, 0x69, 0x10) ).map_err(|err| FromBytesError::SupportEffect{ rank: "2nd", err })? )} else { None }, third: if bytes[0x79] != 0 { Some( - SupportEffect::from_bytes( array_ref!(bytes, 0x79, 0x10) ).map_err(|err| FromBytesError::SupportEffect{ rank: "3rd", err })? + Effect::from_bytes( array_ref!(bytes, 0x79, 0x10) ).map_err(|err| FromBytesError::SupportEffect{ rank: "3rd", err })? )} else { None }, }, }, @@ -249,8 +246,8 @@ use serde::Deserialize; if let Some(arrow_color) = self.effects.arrow_color { arrow_color.to_bytes( &mut bytes[0x89] ).expect("Unable to convert arrow color to bytes"); } // If they are None, 0 is a valid value for the conditions - if let Some(support_condition) = &self.effects.conditions.first { support_condition.to_bytes( array_mut_ref!(bytes, 0x19, 0x20) )?; } - if let Some(support_condition) = &self.effects.conditions.second { support_condition.to_bytes( array_mut_ref!(bytes, 0x39, 0x20) )?; } + self.effects.conditions.first .to_bytes( array_mut_ref!(bytes, 0x19, 0x20) )?; + self.effects.conditions.second.to_bytes( array_mut_ref!(bytes, 0x39, 0x20) )?; // If they are None, 0 is a valid value for the effects diff --git a/src/game/card/property.rs b/src/game/card/property.rs index 7583a6c..13b1398 100644 --- a/src/game/card/property.rs +++ b/src/game/card/property.rs @@ -57,7 +57,7 @@ macro_rules! generate_enum_property_mod $( #[$enum_attr] )* #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] #[derive(::serde::Serialize, ::serde::Deserialize)] - #[derive(derive_more::Display)] + #[derive(::derive_more::Display)] $mod_vis enum $enum_name { $( @@ -112,234 +112,223 @@ macro_rules! generate_enum_property_mod } } -// Modules -//-------------------------------------------------------------------------------------------------- - generate_enum_property_mod!( - pub mod slot { - /// A player's card slots - enum Slot - { - Hand ("Hand" ) => 0, - Dp ("Dp" ) => 1, - Online ("Online" ) => 2, - Offline("Offline") => 3, - - _ => "Unknown byte 0x{:x} for a slot" - } - } - - pub mod arrow_color { - /// A digimon effect's arrow color - enum ArrowColor - { - Red ("Red" ) => 1, - Green("Green") => 2, - Blue ("Blue" ) => 3, - - _ => "Unknown byte 0x{:x} for an arrow color" - } - } - - pub mod attack_type { - /// A digimon's attack type - enum AttackType - { - Circle ("Circle" ) => 0, - Triangle("Triangle") => 1, - Cross ("Cross" ) => 2, - - _ => "Unknown byte 0x{:x} for an attack type" - } - } - - pub mod card_type { - /// A card type - enum CardType - { - Digimon ("Digimon" ) => 0, - Item ("Item" ) => 1, - Digivolve("Digivolve") => 2, - - _ => "Unknown byte 0x{:x} for a card type" - } +generate_enum_property_mod!( + pub mod slot { + /// A player's card slots + enum Slot + { + Hand ("Hand" ) => 0, + Dp ("Dp" ) => 1, + Online ("Online" ) => 2, + Offline("Offline") => 3, - impl CardType + _ => "Unknown byte 0x{:x} for a slot" + } + } + + pub mod arrow_color { + /// A digimon effect's arrow color + enum ArrowColor + { + Red ("Red" ) => 1, + Green("Green") => 2, + Blue ("Blue" ) => 3, + + _ => "Unknown byte 0x{:x} for an arrow color" + } + } + + pub mod attack_type { + /// A digimon's attack type + enum AttackType + { + Circle ("Circle" ) => 0, + Triangle("Triangle") => 1, + Cross ("Cross" ) => 2, + + _ => "Unknown byte 0x{:x} for an attack type" + } + } + + pub mod card_type { + /// A card type + enum CardType + { + Digimon ("Digimon" ) => 0, + Item ("Item" ) => 1, + Digivolve("Digivolve") => 2, + + _ => "Unknown byte 0x{:x} for a card type" + } + + impl CardType + { + /// Returns the byte size of the corresponding card + #[must_use] + pub const fn byte_size(self) -> usize { - /// Returns the byte size of the corresponding card - #[must_use] - pub fn byte_size(self) -> usize + match self { - use crate::game::Bytes; - - match self - { - Self::Digimon => std::mem::size_of::< ::ByteArray >(), - Self::Item => std::mem::size_of::< ::ByteArray >(), - Self::Digivolve => std::mem::size_of::< ::ByteArray >(), - } + Self::Digimon => std::mem::size_of::< ::ByteArray >(), + Self::Item => std::mem::size_of::< ::ByteArray >(), + Self::Digivolve => std::mem::size_of::< ::ByteArray >(), } } } - - pub mod player_type { - /// A player type - enum PlayerType - { - Opponent("Opponent") => 0, - Player ("Player" ) => 1, - - _ => "Unknown byte 0x{:x} for a player type", - } - } - - pub mod level { - /// A digimon's level - enum Level - { - Rookie ("Rookie" ) => 0, - Armor ("Armor" ) => 1, - Champion("Champion") => 2, - Ultimate("Ultimate") => 3, - - _ => "Unknown byte 0x{:x} for a level", - } - } - - pub mod speciality { - /// A digimon's speciality - enum Speciality - { - Fire ("Fire" ) => 0, - Ice ("Ice" ) => 1, - Nature ("Nature" ) => 2, - Darkness("Darkness") => 3, - Rare ("Rare" ) => 4, - - _ => "Unknown byte 0x{:x} for a speciality", - } - } - - pub mod support_effect_operation { - /// A digimon's support effect operation - enum SupportEffectOperation - { - Addition ("Addition" ) => 0, - Subtraction ("Subtraction" ) => 1, - Multiplication("Multiplication") => 2, - Division ("Division" ) => 3, - - _ => "Unknown byte 0x{:x} for a support effect operation", - } - } + } + + pub mod player_type { + /// A player type + enum PlayerType + { + Opponent("Opponent") => 0, + Player ("Player" ) => 1, - pub mod support_condition_operation { - /// A digimon's support condition operation - /// - /// # Todo - /// These don't seem to be 100% right, the less than property, sometimes does less than number, might be a range check - enum SupportConditionOperation - { - LessThanProperty ("Less than property" ) => 0, - LessThanNumber ("Less than number" ) => 1, - MoreThanProperty ("More than property" ) => 2, - MoreThanNumber ("More than number" ) => 3, - DifferentFromNumber("Different from number") => 4, - EqualToNumber ("Equal to number" ) => 5, - - _ => "Unknown byte 0x{:x} for a support condition operation", - } + _ => "Unknown byte 0x{:x} for a player type", } - - pub mod cross_move_effect { - /// A digimon's cross move effect - enum CrossMoveEffect - { - FirstAttack("Attack first") => 1, - - CircleTo0("Circle to 0" ) => 2, - TriangleTo0("Triangle to 0") => 3, - CrossTo0("Cross to 0" ) => 4, - - CircleCounter("Circle counter" ) => 5, - TriangleCounter("Triangle counter") => 6, - CrossCounter("Cross counter" ) => 7, - - Crash ("Crash" ) => 8, - EatUpHP("Eat Up HP") => 9, - Jamming("Jamming" ) => 10, - - FireFoe3x("Fire Foe x3" ) => 11, - IceFoe3x("Ice Foe x3" ) => 12, - NatureFoe3x("Nature Foe x3" ) => 13, - DarknessFoe3x("Darkness Foe x3") => 14, - RareFoe3x("Rare Foe x3" ) => 15, - - _ => "Unknown byte 0x{:x} for a cross move effect", - } - } - - pub mod digimon_property { - /// A digimon's property - enum DigimonProperty - { - OwnSpeciality ("Own speciality" ) => 1, - OpnSpeciality ("Opponent speciality" ) => 2, - OwnHP ("Own HP" ) => 3, - OpnHP ("Opponent HP" ) => 4, - OwnCircleAttack ("Own circle attack" ) => 5, - OpnCircleAttack ("Opponent circle attack" ) => 6, - OwnTriangleAttack("Own triangle attack" ) => 7, - OpnTriangleAttack("Opponent triangle attack") => 8, - OwnCrossAttack ("Own cross attack" ) => 9, - OpnCrossAttack ("Opponent cross attack" ) => 10, - OwnAttack ("Own attack" ) => 11, - OpnAttack ("Opponent attack" ) => 12, - OwnLevel ("Own level" ) => 13, - OpnLevel ("Opponent level" ) => 14, - - OwnAttackType("Own attack type" ) => 17, - OpnAttackType("Opponent attack type") => 18, - - AttackOrder ("Attack order" ) => 20, - CardsInOwnHand ("Cards in own hand" ) => 21, - CardsInOpnHand ("Cards in opponent hand" ) => 22, - CardsInOwnDpSlot ("Cards in own dp slot" ) => 23, - CardsInOpnDpSlot ("Cards in opponent dp slot" ) => 24, - CardsInOwnOffDeck("Cards in own offline deck" ) => 25, - TempSlot ("Temp slot" ) => 26, - CardsInOwnOnDeck ("Cards in own online deck" ) => 27, - CardsInOpnOnDeck ("Cards in opponent online deck") => 28, - - _ => "Unknown byte 0x{:x} for a digimon property", - } - } - ); + } + pub mod level { + /// A digimon's level + enum Level + { + Rookie ("Rookie" ) => 0, + Armor ("Armor" ) => 1, + Champion("Champion") => 2, + Ultimate("Ultimate") => 3, + + _ => "Unknown byte 0x{:x} for a level", + } + } - // Complex - pub mod moves; - pub mod support_effect; - pub mod support_condition; -//-------------------------------------------------------------------------------------------------- + pub mod speciality { + /// A digimon's speciality + enum Speciality + { + Fire ("Fire" ) => 0, + Ice ("Ice" ) => 1, + Nature ("Nature" ) => 2, + Darkness("Darkness") => 3, + Rare ("Rare" ) => 4, + + _ => "Unknown byte 0x{:x} for a speciality", + } + } + + pub mod effect_operation { + /// A digimon's support effect operation + enum EffectOperation + { + Addition ("Addition" ) => 0, + Subtraction ("Subtraction" ) => 1, + Multiplication("Multiplication") => 2, + Division ("Division" ) => 3, + + _ => "Unknown byte 0x{:x} for a support effect operation", + } + } + + pub mod effect_condition_operation { + /// A digimon's support condition operation + /// + /// # Todo + /// These don't seem to be 100% right, the less than property, sometimes does less than number, might be a range check + enum EffectConditionOperation + { + LessThanProperty ("Less than property" ) => 0, + LessThanNumber ("Less than number" ) => 1, + MoreThanProperty ("More than property" ) => 2, + MoreThanNumber ("More than number" ) => 3, + DifferentFromNumber("Different from number") => 4, + EqualToNumber ("Equal to number" ) => 5, + + _ => "Unknown byte 0x{:x} for a support condition operation", + } + } + + pub mod cross_move_effect { + /// A digimon's cross move effect + enum CrossMoveEffect + { + FirstAttack("Attack first") => 1, + + CircleTo0("Circle to 0" ) => 2, + TriangleTo0("Triangle to 0") => 3, + CrossTo0("Cross to 0" ) => 4, + + CircleCounter("Circle counter" ) => 5, + TriangleCounter("Triangle counter") => 6, + CrossCounter("Cross counter" ) => 7, + + Crash ("Crash" ) => 8, + EatUpHP("Eat Up HP") => 9, + Jamming("Jamming" ) => 10, + + FireFoe3x("Fire Foe x3" ) => 11, + IceFoe3x("Ice Foe x3" ) => 12, + NatureFoe3x("Nature Foe x3" ) => 13, + DarknessFoe3x("Darkness Foe x3") => 14, + RareFoe3x("Rare Foe x3" ) => 15, + + _ => "Unknown byte 0x{:x} for a cross move effect", + } + } + + pub mod digimon_property { + /// A digimon's property + enum DigimonProperty + { + OwnSpeciality ("Own speciality" ) => 1, + OpnSpeciality ("Opponent speciality" ) => 2, + OwnHP ("Own HP" ) => 3, + OpnHP ("Opponent HP" ) => 4, + OwnCircleAttack ("Own circle attack" ) => 5, + OpnCircleAttack ("Opponent circle attack" ) => 6, + OwnTriangleAttack("Own triangle attack" ) => 7, + OpnTriangleAttack("Opponent triangle attack") => 8, + OwnCrossAttack ("Own cross attack" ) => 9, + OpnCrossAttack ("Opponent cross attack" ) => 10, + OwnAttack ("Own attack" ) => 11, + OpnAttack ("Opponent attack" ) => 12, + OwnLevel ("Own level" ) => 13, + OpnLevel ("Opponent level" ) => 14, + + OwnAttackType("Own attack type" ) => 17, + OpnAttackType("Opponent attack type") => 18, + + AttackOrder ("Attack order" ) => 20, + CardsInOwnHand ("Cards in own hand" ) => 21, + CardsInOpnHand ("Cards in opponent hand" ) => 22, + CardsInOwnDpSlot ("Cards in own dp slot" ) => 23, + CardsInOpnDpSlot ("Cards in opponent dp slot" ) => 24, + CardsInOwnOffDeck("Cards in own offline deck" ) => 25, + TempSlot ("Temp slot" ) => 26, + CardsInOwnOnDeck ("Cards in own online deck" ) => 27, + CardsInOpnOnDeck ("Cards in opponent online deck") => 28, + + _ => "Unknown byte 0x{:x} for a digimon property", + } + } +); + + +// Complex +pub mod moves; // Note: Can't be `move`, as it's a keyword +pub mod effect; +pub mod effect_condition; // Exports pub use level::Level; pub use speciality::Speciality; -pub use moves::Move; pub use cross_move_effect::CrossMoveEffect; - pub use digimon_property::DigimonProperty; - -pub use support_effect::SupportEffect; -pub use support_effect_operation::SupportEffectOperation; - -pub use support_condition::SupportCondition; -pub use support_condition_operation::SupportConditionOperation; - +pub use effect_operation::EffectOperation; +pub use effect_condition_operation::EffectConditionOperation; pub use card_type::CardType; pub use arrow_color::ArrowColor; - pub use attack_type::AttackType; pub use player_type::PlayerType; - pub use slot::Slot; +pub use moves::Move; +pub use effect::Effect; +pub use effect_condition::EffectCondition; diff --git a/src/game/card/property/support_effect.rs b/src/game/card/property/effect.rs similarity index 91% rename from src/game/card/property/support_effect.rs rename to src/game/card/property/effect.rs index ecb7a5d..4fa9368 100644 --- a/src/game/card/property/support_effect.rs +++ b/src/game/card/property/effect.rs @@ -1,15 +1,15 @@ //! A digimon's support effect //! -//! This module contains the [`SupportEffect`] struct, which describes a support effect. +//! This module contains the [`Effect`] struct, which describes a support effect. //! //! # Layout //! Each support effect has a size of `0x10` bytes, and it's general layout is the following: //! -//! | Offset | Size | Type | Name | Location | Details | -//! |--------|------|----------------------|---------------------------|------------------------|---------------------------------------------------------------| -//! | 0x0 | 0x1 | `bool` | Exists | N/A | If `0`, the effect does not exist | -//! | 0x1 | 0x1 | N/A | Effect Type | N/A | Determines which [`SupportEffect`] variant is used. | -//! | 0x2 | 0xe | N/A | Arguments | N/A | The arguments used for the current [`SupportEffect`] variant. | +//! | Offset | Size | Type | Name | Location | Details | +//! |--------|------|----------------------|---------------------------|------------------------|--------------------------------------------------------| +//! | 0x0 | 0x1 | `bool` | Exists | N/A | If `0`, the effect does not exist | +//! | 0x1 | 0x1 | N/A | Effect Type | N/A | Determines which [`Effect`] variant is used. | +//! | 0x2 | 0xe | N/A | Arguments | N/A | The arguments used for the current [`Effect`] variant. | // byteorder use byteorder::{ByteOrder, LittleEndian}; @@ -19,7 +19,7 @@ use crate::game::{ Bytes, util, card::property::{ - self, DigimonProperty, SupportEffectOperation, AttackType, PlayerType, Slot + self, DigimonProperty, EffectOperation, AttackType, PlayerType, Slot }, }; @@ -33,7 +33,7 @@ use crate::game::{ #[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 +pub enum Effect { /// Changes a property of either digimon /// @@ -63,7 +63,7 @@ pub enum SupportEffect x: u16, y: u16, - op: SupportEffectOperation, + op: EffectOperation, }, /// A player uses an attack type @@ -86,7 +86,7 @@ pub enum SupportEffect b: Option, c: Option, - op: SupportEffectOperation, + op: EffectOperation, }, /// Moves cards from a slot to another @@ -178,9 +178,9 @@ pub enum FromBytesError /// Unknown operation argument #[display(fmt = "Unknown operation argument")] - Operation( #[error(source)] property::support_effect_operation::FromBytesError ), + Operation( #[error(source)] property::effect_operation::FromBytesError ), - /// Unknown attack type for [`SupportEffect::UseAttack`] + /// Unknown attack type for [`Effect::UseAttack`] #[display(fmt = "Unknown attack type")] UseAttackAttackType( #[error(source)] property::attack_type::FromBytesError ), @@ -189,7 +189,7 @@ pub enum FromBytesError EffectType { byte: u8 }, } -impl Bytes for SupportEffect +impl Bytes for Effect { type ByteArray = [u8; 0x10]; @@ -218,6 +218,8 @@ impl Bytes for SupportEffect .then(|| DigimonProperty::from_bytes( &bytes[0x6] )) .transpose() .map_err(FromBytesError::ThirdProperty); + + // Lower byte of `x` contains the attack type let get_attack_type = || AttackType::from_bytes( &bytes[0xa] ) // Lower byte of `x` .map_err(FromBytesError::UseAttackAttackType); @@ -226,7 +228,7 @@ impl Bytes for SupportEffect let y = LittleEndian::read_u16( &bytes[0xc..0xe] ); // The operation argument - let op = SupportEffectOperation::from_bytes( &bytes[0xf] ) + let op = EffectOperation::from_bytes( &bytes[0xf] ) .map_err(FromBytesError::Operation)?; // Check what the effect type is @@ -240,7 +242,6 @@ impl Bytes for SupportEffect }), - // Lower byte of `x` contains the attack type 16 => Ok( Self::UseAttack{ player: Player , attack: get_attack_type()? }), 17 => Ok( Self::UseAttack{ player: Opponent, attack: get_attack_type()? }), @@ -313,14 +314,14 @@ impl Bytes for SupportEffect #[allow(clippy::unneeded_field_pattern)] // Placeholder match self { Self::ChangeProperty { property, a, b, c, x, y, op } => { - property.to_bytes(bytes.effect_type)?; + property.to_bytes(bytes.effect_type).into_ok(); *bytes.effect_type -= 1; - if let Some(a) = a { a.to_bytes(bytes.a)?; } - if let Some(b) = b { b.to_bytes(bytes.b)?; } - if let Some(c) = c { c.to_bytes(bytes.c)?; } + if let Some(a) = a { a.to_bytes(bytes.a).into_ok(); } + if let Some(b) = b { b.to_bytes(bytes.b).into_ok(); } + if let Some(c) = c { c.to_bytes(bytes.c).into_ok(); } LittleEndian::write_u16(bytes.x, *x); LittleEndian::write_u16(bytes.y, *y); - op.to_bytes(bytes.op)?; + op.to_bytes(bytes.op).into_ok(); }, Self::UseAttack { player: _, attack: _ } => todo!(), diff --git a/src/game/card/property/effect_condition.rs b/src/game/card/property/effect_condition.rs new file mode 100644 index 0000000..26cf56a --- /dev/null +++ b/src/game/card/property/effect_condition.rs @@ -0,0 +1,162 @@ +//! A digimon's effect condition +//! +//! This module contains the [`EffectCondition`] struct, which describes a condition for an effect. +//! +//! # Layout +//! Each support condition has a size of `0x20` bytes, and it's layout is the following: +//! +//! | Offset | Size | Type | Name | Location | Details | +//! |--------|------|------------------------------|---------------------------|--------------- |------------------------------------------------------------------------------------| +//! | 0x0 | 0x0 | `bool` | Misfire | `misfire` | If the condition throws a misfire when false | +//! | 0x1 | 0x1 | `u8` | | | Always zero | +//! | 0x2 | 0x1 | [`DigimonProperty`] | Property compare | `property_cmp` | The property to compare to for the condition (or 0 if the condition doesn't exist) | +//! | 0x3 | 0x5 | `[u8; 0x5]` | | `unknown_3` | Unknown | +//! | 0x8 | 0x1 | `DigimonProperty` | Property argument | `arg_property` | Property argument for the comparation | +//! | 0x9 | 0xb | `[u8; 0xb]` | | `unknown_9` | Unknown | +//! | 0x14 | 0x2 | `u16` | Number argument | `arg_num` | Number argument for the comparation | +//! | 0x16 | 0x4 | `[u8; 0x4]` | | `unknown_16` | Unknown | +//! | 0x1a | 0x1 | [`EffectConditionOperation`] | Operation | `operation` | Operation to use for the comparation | +//! | 0x1b | 0x5 | `[u8; 0x5]` | | `unknown_1b` | Unknown | + +// byteorder +use byteorder::{ByteOrder, LittleEndian}; + +// Crate +use crate::game::{ + Bytes, + card::property::{ + self, DigimonProperty, EffectConditionOperation + }, + //util, +}; + +/// A digimon's support effect condition +#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] +#[derive(serde::Serialize, serde::Deserialize)] +pub struct EffectCondition +{ + /// If the effect should throw a misfire when false + misfire: bool, + + /// The property to compare to + property_cmp: DigimonProperty, + + /// The property argument + arg_property: Option, + + /// The number argument + arg_num: u16, + + /// The operation + operation: EffectConditionOperation, + + // Unknown + unknown_3 : [u8; 0x5], + unknown_9 : [u8; 0xb], + unknown_16: [u8; 0x4], + unknown_1b: [u8; 0x5], +} + +/// The error type thrown by `FromBytes` +#[derive(Debug)] +#[derive(derive_more::Display, err_impl::Error)] +pub enum FromBytesError +{ + /// Unable to read the condition + #[display(fmt = "Unable to read the effect condition")] + Condition( #[error(source)] property::digimon_property::FromBytesError ), + + /// Unable to read a property argument + #[display(fmt = "Unable to read the property argument")] + PropertyArgument( #[error(source)] property::digimon_property::FromBytesError ), + + /// Unable to read the effect operation + #[display(fmt = "Unable to read the effect operation")] + Operation( #[error(source)] property::effect_condition_operation::FromBytesError ), +} + +// Bytes +impl Bytes for Option +{ + type ByteArray = [u8; 0x20]; + + type FromError = FromBytesError; + fn from_bytes(bytes: &Self::ByteArray) -> Result + { + // If we have no property comparation, return None + if bytes[0x2] == 0 { + return Ok(None); + } + + // Else build the type + Ok( Some( EffectCondition { + misfire: (bytes[0x0] != 0), + property_cmp: DigimonProperty::from_bytes( &bytes[0x2] ) + .map_err(FromBytesError::Condition)?, + + arg_property: (bytes[0x8] != 0) + .then(|| DigimonProperty::from_bytes( &bytes[0x8] )) + .transpose() + .map_err(FromBytesError::PropertyArgument)?, + + arg_num: LittleEndian::read_u16( &bytes[0x14..0x16] ), + + operation: EffectConditionOperation::from_bytes( &bytes[0x1a] ) + .map_err(FromBytesError::Operation)?, + + unknown_3: [ bytes[0x3], bytes[0x4], bytes[0x5], bytes[0x6], bytes[0x7] ], + + unknown_9: [ + bytes[0x9], bytes[0xa ], bytes[0xb ], bytes[0xc ], bytes[0xd ], bytes[0xe], + bytes[0xf], bytes[0x10], bytes[0x11], bytes[0x12], bytes[0x13] + ], + + unknown_16: [ bytes[0x16], bytes[0x17], bytes[0x18], bytes[0x19] ], + + unknown_1b: [ bytes[0x1b], bytes[0x1c], bytes[0x1d], bytes[0x1e], bytes[0x1f] ], + })) + } + + type ToError = !; + fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> + { + // If we don't exist, write a `0` on the property comparation and return + let cond = match self { + Some(cond) => cond, + None => { + bytes[0x2] = 0; + return Ok(()); + } + }; + + // 0x0 - Misfire + bytes[0x0] = if cond.misfire { 1 } else { 0 }; + + // 0x1 - Always zero + bytes[0x1] = 0; + + // 0x2 - Condition + cond.property_cmp.to_bytes(&mut bytes[0x2]).into_ok(); + + // 0x3..0x8 - Unknown[0..5] + //bytes[0x3..0x8].copy_from_slice( &self.unknown[0..5] ); + + // 0x8 - Type arg / 0 if None + if let Some(type_arg) = cond.arg_property { + type_arg.to_bytes(&mut bytes[0x8]).into_ok(); + } + else { bytes[0x8] = 0; } + + // 0x9..0x14 - Unknown[0x5..0x10] + //bytes[0x9..0x14].copy_from_slice( &self.unknown[0x5..0x10] ); + + // 0x14..0x16 - Number arg + LittleEndian::write_u16(&mut bytes[0x14..0x16], cond.arg_num); + + // 0x1a - Operation arg + cond.operation.to_bytes(&mut bytes[0x1a]).into_ok(); + + // And return OK + Ok(()) + } +} diff --git a/src/game/card/property/moves.rs b/src/game/card/property/moves.rs index 4f64b6b..838bfe8 100644 --- a/src/game/card/property/moves.rs +++ b/src/game/card/property/moves.rs @@ -1,6 +1,6 @@ //! A digimon's move //! -//! This module contains the [`Move`] struct, which describes a generic move. +//! This module contains the [`Move`] struct, which describes a generic move over the triangle, circle or cross. //! //! # Layout //! Each move has a size of `0x1c` bytes, and it's layout is the following: @@ -32,7 +32,7 @@ pub struct Move unknown: u32, } -/// Error type for [`Bytes::FromBytes`] +/// Error type for [`Bytes::from_bytes`] #[derive(Debug, derive_more::Display, err_impl::Error)] pub enum FromBytesError { @@ -41,7 +41,7 @@ pub enum FromBytesError Name( #[error(source)] util::ReadNullAsciiStringError ), } -/// Error type for [`Bytes::ToBytes`] +/// Error type for [`Bytes::to_bytes`] #[derive(Debug, derive_more::Display, err_impl::Error)] pub enum ToBytesError { @@ -58,9 +58,9 @@ impl Bytes for Move type FromError = FromBytesError; fn from_bytes(bytes: &Self::ByteArray) -> Result { - // And return the move + // Return the move Ok( Self { - name : util::read_null_ascii_string( &bytes[0x6..0x1c] ) + name : util::read_null_ascii_string( &bytes[0x6..0x1c] ) .map_err(FromBytesError::Name)? .chars().collect(), power : LittleEndian::read_u16( &bytes[0x0..0x2] ), @@ -83,7 +83,7 @@ impl Bytes for Move .map_err(ToBytesError::Name)?; // Then write the power and the unknown - LittleEndian::write_u16(bytes.power , self.power ); + LittleEndian::write_u16(bytes.power , self.power); LittleEndian::write_u32(bytes.unknown, self.unknown); // And return Ok diff --git a/src/game/card/property/support_condition.rs b/src/game/card/property/support_condition.rs deleted file mode 100644 index 262e54b..0000000 --- a/src/game/card/property/support_condition.rs +++ /dev/null @@ -1,134 +0,0 @@ -//! A digimon's support condition -//! -//! This module contains the [`SupportCondition`] struct, which describes a condition for a support effect. -//! -//! # Layout -//! Each support condition has a size of `0x20` bytes, and it's layout is the following: -//! -//! TODO: Layout -//! | Offset | Size | Type | Name | Location | Details | -//! |--------|------|----------------------|---------------------------|------------------------|-----------------------------------| - -// byteorder -use byteorder::{ByteOrder, LittleEndian}; - -// Crate -use crate::game::{ - Bytes, - card::property::{ - self, DigimonProperty, SupportConditionOperation - }, -}; - -/// A digimon's support effect condition -#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] -#[derive(serde::Serialize, serde::Deserialize)] -pub struct SupportCondition -{ - /// If the effect should throw a misfire if the condition isn't met - misfire: bool, - - /// The condition type - cond: DigimonProperty, - - /// The type argument - type_arg: Option, - - /// The number argument - num_arg: u16, - - /// The operation - operation: SupportConditionOperation, - - /// Unknown - unknown: [u8; 16], -} - -/// The error type thrown by `FromBytes` -#[derive(Debug)] -#[derive(derive_more::Display, err_impl::Error)] -pub enum FromBytesError -{ - /// Unable to read the condition - #[display(fmt = "Unable to read the effect condition")] - Condition( #[error(source)] property::digimon_property::FromBytesError ), - - /// Unable to read a property argument - #[display(fmt = "Unable to read the property argument")] - PropertyArgument( #[error(source)] property::digimon_property::FromBytesError ), - - /// Unable to read the effect operation - #[display(fmt = "Unable to read the effect operation")] - Operation( #[error(source)] property::support_condition_operation::FromBytesError ), -} - -// Bytes -impl Bytes for SupportCondition -{ - type ByteArray = [u8; 0x20]; - - type FromError = FromBytesError; - fn from_bytes(bytes: &Self::ByteArray) -> Result - { - // Get the condition - let cond = DigimonProperty::from_bytes( &bytes[0x2] ) - .map_err(FromBytesError::Condition)?; - - // And return the move - Ok( Self { - misfire: (bytes[0x0] != 0), - cond, - - type_arg: (bytes[0x8] != 0) - .then(|| DigimonProperty::from_bytes( &bytes[0x8] )) - .transpose() - .map_err(FromBytesError::PropertyArgument)?, - - num_arg: LittleEndian::read_u16( &bytes[0x14..0x16] ), - - operation: SupportConditionOperation::from_bytes( &bytes[0x1a] ) - .map_err(FromBytesError::Operation)?, - - unknown: [ - bytes[0x3], bytes[0x4], bytes[0x5], bytes[0x6], bytes[0x7], - - bytes[0x9], bytes[0xa ], bytes[0xb ], bytes[0xc ], bytes[0xd ], bytes[0xe], - bytes[0xf], bytes[0x10], bytes[0x11], bytes[0x12], bytes[0x13], - ] - }) - } - - type ToError = !; - fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> - { - // 0x0 - Misfire - bytes[0x0] = if self.misfire { 1 } else { 0 }; - - // 0x1 - Always zero - bytes[0x1] = 0; - - // 0x2 - Condition - self.cond.to_bytes(&mut bytes[0x2])?; - - // 0x3..0x8 - Unknown[0..5] - bytes[0x3..0x8].copy_from_slice( &self.unknown[0..5] ); - - // 0x8 - Type arg / 0 if None - if let Some(type_arg) = self.type_arg { - type_arg.to_bytes(&mut bytes[0x8])?; - } - else { bytes[0x8] = 0; } - - // 0x9..0x14 - Unknown[0x5..0x10] - bytes[0x9..0x14].copy_from_slice( &self.unknown[0x5..0x10] ); - - // 0x14..0x16 - Number arg - LittleEndian::write_u16(&mut bytes[0x14..0x16], self.num_arg); - - // 0x1a - Operation arg - self.operation.to_bytes(&mut bytes[0x1a])?; - - // And return OK - Ok(()) - } -} diff --git a/src/game/card/table.rs b/src/game/card/table.rs index c722ac3..9f6a73e 100644 --- a/src/game/card/table.rs +++ b/src/game/card/table.rs @@ -44,10 +44,12 @@ use crate::{ io::{address::Data, GameFile}, game::{ card::{ + self, Digimon, Item, Digivolve, property::{self, CardType}, }, Bytes, + util, } }; @@ -90,15 +92,15 @@ impl Table { /// Error type for [`Table::deserialize`] #[derive(Debug)] -#[derive(derive_more::Display)] +#[derive(derive_more::Display, err_impl::Error)] pub enum DeserializeError { /// Unable to seek game file #[display(fmt = "Unable to seek game file to card table")] - Seek( std::io::Error ), + Seek( #[error(source)] std::io::Error ), /// Unable to read table header #[display(fmt = "Unable to read table header")] - ReadHeader( std::io::Error ), + ReadHeader( #[error(source)] 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")] @@ -126,6 +128,7 @@ pub enum DeserializeError { #[display(fmt = "Unable to read card header for card id {}", id)] ReadCardHeader { id: usize, + #[error(source)] err: std::io::Error, }, @@ -133,6 +136,7 @@ pub enum DeserializeError { #[display(fmt = "Unknown card type for card id {}", id)] UnknownCardType { id: usize, + #[error(source)] err: property::card_type::FromBytesError, }, @@ -140,35 +144,22 @@ pub enum DeserializeError { #[display(fmt = "Unable to read card footer for card id {}", id)] ReadCardFooter { id: usize, + #[error(source)] err: std::io::Error, }, } -impl std::error::Error for DeserializeError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Self::Seek(err) | - Self::ReadHeader(err) | - Self::ReadCardHeader { err, .. } | - Self::ReadCardFooter { err, .. } => Some(err), - Self::UnknownCardType { err, .. } => Some(err), - Self::HeaderMagic { .. } | - Self::TooManyCards { .. } => None, - } - } -} - /// Error type for [`Table::serialize`] #[derive(Debug)] -#[derive(derive_more::Display)] +#[derive(derive_more::Display, err_impl::Error)] pub enum SerializeError { /// Unable to seek game file #[display(fmt = "Unable to seek game file to card table")] - Seek( std::io::Error ), + Seek( #[error(source)] std::io::Error ), /// Unable to write table header #[display(fmt = "Unable to write table header")] - WriteHeader( std::io::Error ), + WriteHeader( #[error(source)] std::io::Error ), /// There were too many cards #[display(fmt = "Too many cards in table ({} digimon, {} item, {} digivolve, {} / {} bytes max)", @@ -186,31 +177,37 @@ pub enum SerializeError { digivolve_cards: usize, }, - /// Unable to write card header - #[display(fmt = "Unable to write card header for card id {}", id)] - WriteCardHeader { + /// Unable to write a card + #[display(fmt = "Unable to write card with id {}", id)] + WriteCard { id: usize, + #[error(source)] err: std::io::Error, }, - /// Unable to write card footer - #[display(fmt = "Unable to write card footer for card id {}", id)] - WriteCardFooter { + /// Unable to serialize a digimon card + #[display(fmt = "Unable to serialize digimon card with id {}", id)] + DigimonCard { id: usize, - err: std::io::Error, + #[error(source)] + err: card::digimon::ToBytesError, + }, + + /// Unable to write an item card + #[display(fmt = "Unable to write item card with id {}", id)] + ItemCard { + id: usize, + #[error(source)] + err: card::item::ToBytesError, + }, + + /// Unable to write a digivolve card + #[display(fmt = "Unable to write digivolve card with id {}", id)] + DigivolveCard { + id: usize, + #[error(source)] + err: card::digivolve::ToBytesError, }, -} - -impl std::error::Error for SerializeError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Self::Seek(err) | - Self::WriteHeader(err) | - Self::WriteCardHeader { err, .. } | - Self::WriteCardFooter { err, .. } => Some(err), - Self::TooManyCards { .. } => None, - } - } } impl Table { @@ -340,168 +337,89 @@ impl Table { file.seek( std::io::SeekFrom::Start( u64::from( Self::START_ADDRESS ) + 0x8 ) ) .map_err(SerializeError::Seek)?; - // Function to write a card to file - fn write_card(_file: &mut GameFile, _card: &C, _cur_id: usize) { - /* - // Get the bytes - let mut bytes = [0u8; ::BUF_BYTE_SIZE]; - card.to_bytes(&mut bytes) - .expect("Unable to get digimon as bytes"); - - // Write the digimon buffer - file.write_all(&bytes) - .expect("Unable to write digimon card"); - - // And write the 'next' section - let mut buf = [0u8; 0x4]; - - match idx { - num if num + 1 == self.digimons.len() => CardType::Item .to_bytes( &mut buf[0x3..0x4] )?, - _ => CardType::Digimon.to_bytes( &mut buf[0x3..0x4] )?, - } - - LittleEndian::write_u16( &mut buf[0x1..0x3], cur_id+1); - - file.write_all(&buf) - .expect(""); - */ - } - // Write all digimon, items and digivolves - for (id, digimon) in self.digimons.iter().enumerate() { - write_card(file, digimon, id); + for (rel_id, digimon) in self.digimons.iter().enumerate() { + // Current id through the whole table + let cur_id = rel_id; + + // Card bytes + let mut card_bytes = [0; 0x3 + CardType::Digimon.byte_size() + 0x1]; + let bytes = util::array_split_mut!(&mut card_bytes, + header_id : [0x2], + header_type: 1, + digimon : [CardType::Digimon.byte_size()], + footer : 1, + ); + + // Write the header + LittleEndian::write_u16( bytes.header_id, cur_id as u16 ); + CardType::Digimon.to_bytes( bytes.header_type )?; + + // Write the digimon + digimon.to_bytes( bytes.digimon ) + .map_err(|err| SerializeError::DigimonCard { id: cur_id, err })?; + + // Write the footer + *bytes.footer = 0; + + file.write_all(&card_bytes) + .map_err(|err| SerializeError::WriteCard { id: cur_id, err })?; } - for (id, item) in self.items.iter().enumerate() { - write_card(file, item, self.digimons.len() + id); + for (rel_id, item) in self.items.iter().enumerate() { + // Current id through the whole table + let cur_id = self.digimons.len() + rel_id; + + // Card bytes + let mut card_bytes = [0; 0x3 + CardType::Item.byte_size() + 0x1]; + let bytes = util::array_split_mut!(&mut card_bytes, + header_id : [0x2], + header_type: 1, + item : [CardType::Item.byte_size()], + footer : 1, + ); + + // Write the header + LittleEndian::write_u16( bytes.header_id, cur_id as u16 ); + CardType::Item.to_bytes( bytes.header_type )?; + + // Write the item + item.to_bytes( bytes.item ) + .map_err(|err| SerializeError::ItemCard { id: cur_id, err })?; + + // Write the footer + *bytes.footer = 0; + + file.write_all(&card_bytes) + .map_err(|err| SerializeError::WriteCard { id: cur_id, err })?; } - for (id, digivolve) in self.digivolves.iter().enumerate() { - write_card(file, digivolve, self.digimons.len() + self.items.len() + id); + for (rel_id, digivolve) in self.digivolves.iter().enumerate() { + // Current id through the whole table + let cur_id = self.digimons.len() + self.items.len() + rel_id; + + // Card bytes + let mut card_bytes = [0; 0x3 + CardType::Digivolve.byte_size() + 0x1]; + let bytes = util::array_split_mut!(&mut card_bytes, + header_id : [0x2], + header_type: 1, + item : [CardType::Digivolve.byte_size()], + footer : 1, + ); + + // Write the header + LittleEndian::write_u16( bytes.header_id, cur_id as u16 ); + CardType::Digivolve.to_bytes( bytes.header_type )?; + + // Write the digivolve + digivolve.to_bytes( bytes.item ) + .map_err(|err| SerializeError::DigivolveCard { id: cur_id, err })?; + + // Write the footer + *bytes.footer = 0; + + file.write_all(&card_bytes) + .map_err(|err| SerializeError::WriteCard { id: cur_id, err })?; } - /* - enum Card<'a> { - Digimon (&'a Digimon ), - Item (&'a Item ), - Digivolve(&'a Digivolve), - } - - // Then write all cards - for (idx, card) in std::iter::empty() - .chain(self.digimons .iter().map(Card::Digimon )) - .chain(self.items .iter().map(Card::Item )) - .chain(self.digivolves.iter().map(Card::Digivolve)) - .enumerate() - { - let bytes = match card { - Card::Digimon(digimon) => { - let mut bytes = [0; Digimon::BUF_BYTE_SIZE]; - digimon.to_bytes(&mut bytes); - &bytes as &[u8] - }, - _ => &[], - }; - - // Write the buffer - file.write_all(&bytes) - .expect("Unable to write card"); - - // And write the 'next' section - let mut buf = [0u8; 0x4]; - - match idx { - num if num + 1 == self.digimons.len() => CardType::Item .to_bytes( &mut buf[0x3..0x4] )?, - _ => CardType::Digimon.to_bytes( &mut buf[0x3..0x4] )?, - } - - LittleEndian::write_u16( &mut buf[0x1..0x3], (idx+1) as u16); - - file.write_all(&buf) - .expect(""); - } - */ - - /* - // The current id - let mut cur_id = 0u16; - - - - - // Then write all cards, first digimon, then items, then digivolves - for (idx, digimon) in self.digimons.iter().enumerate() - { - // Get the bytes - let mut bytes = [0u8; Digimon::BUF_BYTE_SIZE as usize]; - digimon.to_bytes(&mut bytes) - .expect("Unable to get digimon as bytes"); - - // Write the digimon buffer - file.write_all(&bytes) - .expect("Unable to write digimon card"); - - // And write the 'next' section - let mut buf = [0u8; 0x4]; - - match idx { - num if num + 1 == self.digimons.len() => CardType::Item .to_bytes( &mut buf[0x3..0x4] )?, - _ => CardType::Digimon.to_bytes( &mut buf[0x3..0x4] )?, - } - - LittleEndian::write_u16( &mut buf[0x1..0x3], cur_id+1); - - file.write_all(&buf) - .expect(""); - - cur_id += 1; - } - - for (idx, item) in self.items.iter().enumerate() - { - // Get the bytes - let mut bytes = [0u8; Item::BUF_BYTE_SIZE as usize]; - item.to_bytes(&mut bytes).unwrap();//.map_err(|err| SerializeError::ConvertItem{id: cur_id, err})?; - - // Write the item buffer - file.write_all(&bytes).unwrap();//.map_err(|err| SerializeError::WriteItem{id: cur_id, err})?; - - // And write the 'next' section - let mut buf = [0u8; 0x4]; - - match idx { - num if num + 1 == self.items.len() => { CardType::Digivolve.to_bytes( &mut buf[0x3..0x4] )?; } - _ => { CardType::Item .to_bytes( &mut buf[0x3..0x4] )?; } - } - - LittleEndian::write_u16( &mut buf[0x1..0x3], cur_id+1); - - file.write_all(&buf).unwrap();//.map_err(|err| SerializeError::NextEntryInfo{ id: cur_id, err })?; - - cur_id += 1; - } - - for (idx, digivolve) in self.digivolves.iter().enumerate() - { - // Get the bytes - let mut bytes = [0u8; Digivolve::BUF_BYTE_SIZE as usize]; - digivolve.to_bytes(&mut bytes).unwrap();//.map_err(|err| SerializeError::ConvertDigivolve{id: cur_id, err})?; - - // Write the digimon buffer - file.write_all(&bytes).unwrap();//.map_err(|err| SerializeError::WriteDigivolve{id: cur_id, err})?; - - // And write the 'next' section - let mut buf = [0u8; 0x4]; - - match idx { - num if num + 1 == self.digivolves.len() => { CardType::Digimon .to_bytes( &mut buf[0x3..0x4] )?; LittleEndian::write_u16( &mut buf[0x1..0x3], 0 ); } - _ => { CardType::Digivolve.to_bytes( &mut buf[0x3..0x4] )?; LittleEndian::write_u16( &mut buf[0x1..0x3], cur_id+1); } - } - - file.write_all(&buf).unwrap();//.map_err(|err| SerializeError::NextEntryInfo{ id: cur_id, err })?; - - cur_id += 1; - } - */ - // And return Ok Ok(()) } diff --git a/src/game/util.rs b/src/game/util.rs index d418e10..d486a98 100644 --- a/src/game/util.rs +++ b/src/game/util.rs @@ -9,42 +9,47 @@ pub macro array_split { ( - $arr:ident, + $arr:expr, $( - $( $start:literal..$end:literal => $arr_name:tt )? - $( =$location:literal => $val_name:tt )? - , - )* + $name:ident : + + $( [$arr_size:expr] )? + $( $val_size:literal )? + + ),* $(,)? ) => {{ #![allow(clippy::used_underscore_binding)] + #![allow(clippy::ptr_offset_with_cast )] // Struct holding all fields - struct __Fields<'a, T> { + struct Fields<'a, T> { $( - $( $arr_name: &'a [T; $end - $start], )? - $( $val_name: &'a T, )? + $name: + + $( &'a [T; $arr_size], )? + $( &'a T, #[cfg(os = "Os that does not exist")] __field: [u8; $val_size], )? )* } // Get everything from `array_refs` let ( $( - $( $arr_name, )? - $( $val_name, )? - )* + $name + ),* ) = ::arrayref::array_refs!( $arr, $( - $( $end - $start )? - $( 1 + (0 * $location) )? + $( $arr_size )? + $( $val_size )? ),* ); // And return the fields - __Fields { + Fields { $( - $( $arr_name, )? - $( $val_name: &( $val_name[0] ), )? + $name + $( : &( $name[$val_size - $val_size] ) )? + , )* } }} @@ -52,19 +57,20 @@ pub macro array_split { pub macro array_split_mut { ( - $arr:ident, + $arr:expr, $( $name:ident : - $( [$arr_size:literal] )? + $( [$arr_size:expr] )? $( $val_size:literal )? ),* $(,)? ) => {{ #![allow(clippy::used_underscore_binding)] + #![allow(clippy::ptr_offset_with_cast )] // Struct holding all fields - struct __Fields<'a, T> { + struct Fields<'a, T> { $( $name: @@ -87,7 +93,7 @@ pub macro array_split_mut { ); // And return the fields - __Fields { + Fields { $( $name $( : &mut ( $name[$val_size - $val_size] ) )? diff --git a/src/io/game_file.rs b/src/io/game_file.rs index dbd3de8..81788d9 100644 --- a/src/io/game_file.rs +++ b/src/io/game_file.rs @@ -239,7 +239,9 @@ impl Seek for GameFile data_offset )) ), - SeekFrom::End(_) => { todo!(); } + SeekFrom::End(_) => { + todo!("SeekFrom::End isn't currently implemented"); + } }; // Seek to the real position and get where we are right now diff --git a/src/lib.rs b/src/lib.rs index 5f56470..7c413a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,8 @@ bool_to_option, decl_macro, stmt_expr_attributes, + unwrap_infallible, + const_if_match, )] // Lints @@ -50,8 +52,11 @@ clippy::identity_op, // Makes sense sometimes for symmetry clippy::items_after_statements, // Sometimes we only introduce items when we first use them. clippy::unseparated_literal_suffix, // We only separate them when they are long - clippy::diverging_sub_expression, // We use `?` on `Result` for extracting the result currently, once a method is done for it, we'll use it. clippy::match_same_arms, // Sometimes we separate them for clarify and order + clippy::missing_errors_doc, // We provide documentation on errors on the error type itself + clippy::todo, // Code that is incomplete should be tagged as such. + clippy::unreachable, // Some code should be unreachable and panic when reached. + clippy::integer_arithmetic, // Come on now, we need to use numbers to program // TODO: Deal with casts eventually clippy::cast_possible_wrap, @@ -62,10 +67,7 @@ clippy::missing_docs_in_private_items, clippy::as_conversions, clippy::indexing_slicing, - clippy::integer_arithmetic, - clippy::unreachable, - clippy::todo, - clippy::missing_errors_doc, + )] // Modules