From 7873c5eed37cc68b3392c50caa6748788aafd000 Mon Sep 17 00:00:00 2001 From: Filipe Rodrigues Date: Tue, 21 Jul 2020 18:18:46 +0100 Subject: [PATCH] Moved helper macros to implement `Bytes` to `util`. Discovered more fields of `Deck`. Fixed `DeckTable` having the wrong address for the table. --- src/game/card/property.rs | 165 +------------------------------------ src/game/deck.rs | 65 +++++++++++++++ src/game/deck/deck.rs | 93 ++++++++++++++++++--- src/game/deck/table.rs | 2 +- src/util.rs | 2 + src/util/array_split.rs | 1 - src/util/impl_bytes.rs | 167 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 317 insertions(+), 178 deletions(-) create mode 100644 src/util/impl_bytes.rs diff --git a/src/game/card/property.rs b/src/game/card/property.rs index fc5c957..bafc240 100644 --- a/src/game/card/property.rs +++ b/src/game/card/property.rs @@ -1,169 +1,6 @@ //! Card properties -/// Defines and implements a property enum -// TODO: Make better documentation -// TODO: Turn into a `macro` once they work -macro_rules! generate_enum_property_mod -{ - // Entry point - ( - // The modules - $( - // Module - $mod_vis:vis mod $mod_name:ident - { - // Enum attributes - $( #[$enum_attr:meta] )* - - // Enum - enum $enum_name:ident - { - // Enum variants - $( - // Attributes - $( #[$enum_variant_attr:meta] )* - - // Variant - // Note: Must have no data - $enum_variant_name:ident - - // `Display` conversion name - ($enum_variant_rename:literal) - - => - - // Variant value - $enum_variant_value:literal, - )+ - - // Extra fields for `Bytes::from_bytes`. - $( - $from_bytes_value:literal => $from_bytes_body:tt, - )* - - // Error - _ => $error_unknown_value_display:literal - - $(,)? - } - - // Any further definitions inside the module - $( $extra_defs:tt )* - } - )* - ) => - { - // Modules - $( - // The module - $mod_vis mod $mod_name - { - // The property enum - $( #[$enum_attr] )* - #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] - #[derive(::serde::Serialize, ::serde::Deserialize)] - #[derive(::derive_more::Display)] - pub enum $enum_name - { - $( - $( #[$enum_variant_attr] )* - #[serde(rename = $enum_variant_rename)] - #[display(fmt = $enum_variant_rename)] - $enum_variant_name = $enum_variant_value, - )+ - } - - /// Error type for [`$crate::game::Bytes::from_bytes`] - #[derive(PartialEq, Eq, Clone, Copy, ::std::fmt::Debug, ::thiserror::Error)] - pub enum FromBytesError { - - /// Unknown value - #[error($error_unknown_value_display, byte)] - UnknownValue { - byte: u8, - } - } - - impl $crate::game::Bytes for $enum_name - { - type ByteArray = u8; - - type FromError = FromBytesError; - fn from_bytes(byte: &Self::ByteArray) -> Result - { - match byte { - $( - $enum_variant_value => - Ok( <$enum_name>::$enum_variant_name ), - )+ - - $( - $from_bytes_value => { - Ok( { $from_bytes_body } ) - } - )* - - &byte => Err( Self::FromError::UnknownValue{ byte } ), - } - } - - type ToError = !; - #[allow(unreachable_code, unused_variables)] // For when there are multiple values - fn to_bytes(&self, byte: &mut Self::ByteArray) -> Result<(), Self::ToError> - { - *byte = match self { - $( - <$enum_name>::$enum_variant_name => $enum_variant_value, - )+ - }; - - Ok(()) - } - } - - // Extra definitions - $( $extra_defs )* - } - )* - } -} - -/// Implements [`Bytes`](crate::game::Bytes) for `Option` where `E` -/// is the first argument of this macro and an enum. -/// -/// This is done by supplying a sentinel value which is read/written as `None`. -macro generate_enum_property_option { - ( - $( $enum_name:ty => $sentinel_value:literal ),* $(,)? - ) => { - $( - #[allow(clippy::diverging_sub_expression)] // Errors might be `!` - impl $crate::game::Bytes for Option<$enum_name> { - type ByteArray = <$enum_name as $crate::game::Bytes>::ByteArray; - - type FromError = <$enum_name as $crate::game::Bytes>::FromError; - fn from_bytes(bytes: &Self::ByteArray) -> Result - { - match bytes { - $sentinel_value => Ok( None ), - _ => Ok( Some( $crate::game::Bytes::from_bytes(bytes)? ) ), - } - } - - type ToError = <$enum_name as $crate::game::Bytes>::ToError; - fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> - { - match self { - Some(value) => $crate::game::Bytes::to_bytes(value, bytes)?, - None => *bytes = $sentinel_value, - } - - Ok(()) - } - } - )* - } -} +use crate::{generate_enum_property_mod, util::impl_bytes::generate_enum_property_option}; generate_enum_property_mod!( pub mod slot { diff --git a/src/game/deck.rs b/src/game/deck.rs index 4b3c639..55a4a60 100644 --- a/src/game/deck.rs +++ b/src/game/deck.rs @@ -1,9 +1,74 @@ //! Deck +// Imports +use crate::{generate_enum_property_mod, util::impl_bytes::generate_enum_property_option}; + +generate_enum_property_mod! { + pub mod city { + /// A deck city + enum City + { + Starter ("Starter" ) => 32, + Fire ("Fire" ) => 33, + Jungle ("Jungle" ) => 34, + Ice ("Ice" ) => 35, + Junk ("Junk" ) => 36, + Dark ("Dark" ) => 37, + Pyramid ("Pyramid" ) => 38, + Desert ("Desert" ) => 39, + Cloud ("Cloud" ) => 40, + Road ("Road" ) => 41, + WisemanTower ("Wiseman Tower" ) => 42, + InfinityTower("Infinity Tower") => 43, + + _ => "Unknown byte {:#x} for a city" + } + } + + pub mod armor_evo { + /// An armor evolution + enum ArmorEvo + { + First ("First" ) => 1, + Second("Second") => 2, + Third ("Third" ) => 3, + + _ => "Unknown byte {:#x} for an armor evolution" + } + } + + pub mod music { + /// Music + enum Music + { + BattleProtag ("Battle Protag" ) => 46, + BattleWorm ("Battle Worm" ) => 47, + BattleBasic ("Battle Basic" ) => 143, + BattleVillain("Battle Villain") => 144, + + PolygonProtag ("Polygon Protag" ) => 37, + PolygonWorm ("Polygon Worm" ) => 44, + PolygonBasic ("Polygon Basic" ) => 147, + PolygonVillain("Polygon Villain") => 148, + + _ => "Unknown byte {:#x} for a music" + } + } +} + +generate_enum_property_option!( + City => 0, + ArmorEvo => 0, + Music => 0, +); + // Modules pub mod deck; pub mod table; // Exports +pub use armor_evo::ArmorEvo; +pub use city::City; pub use deck::Deck; +pub use music::Music; pub use table::Table; diff --git a/src/game/deck/deck.rs b/src/game/deck/deck.rs index ac6cd0a..2ae79ef 100644 --- a/src/game/deck/deck.rs +++ b/src/game/deck/deck.rs @@ -2,7 +2,10 @@ // Imports use crate::{ - game::Bytes, + game::{ + deck::{armor_evo, city, music, ArmorEvo, City, Music}, + Bytes, + }, util::{ array_split, array_split_mut, null_ascii_string::{self, NullAsciiString}, @@ -26,8 +29,26 @@ pub struct Deck { /// All of the card ids that make up this deck pub cards: [CardId; 30], + /// Experience gained by beating this deck + pub experience: u8, + + /// City of the deck + pub city: Option, + + /// Armor digivolution + pub armor_evo: Option, + + /// Battle music + pub battle_music: Option, + + /// Polygon music + pub polygon_music: Option, + /// Unknown data at `0x62` - unknown_62: [u8; 0xc], + unknown_64: [u8; 0x4], + + /// Unknown data at `0x6a` + unknown_6a: u8, } /// Error type for [`Bytes::from_bytes`] @@ -40,6 +61,22 @@ pub enum FromBytesError { /// Unable to read the deck owner #[error("Unable to read the deck owner")] Owner(#[source] null_ascii_string::ReadError), + + /// Unable to read the deck city + #[error("Unable to read the deck city")] + City(#[source] city::FromBytesError), + + /// Unable to read the armor digivolution + #[error("Unable to read the deck armor digivolution")] + ArmorEvo(#[source] armor_evo::FromBytesError), + + /// Unable to read the battle music + #[error("Unable to read the deck battle music")] + BattleMusic(#[source] music::FromBytesError), + + /// Unable to read the polygon music + #[error("Unable to read the deck polygon music")] + PolygonMusic(#[source] music::FromBytesError), } /// Error type for [`Bytes::to_bytes`] @@ -62,10 +99,16 @@ impl Bytes for Deck { fn from_bytes(bytes: &Self::ByteArray) -> Result { // Split the bytes let bytes = array_split!(bytes, - deck : [0x3c], - name : [0x13], - owner : [0x13], - unknown_62: [0xc], + deck : [0x3c], + name : [0x13], + owner : [0x15], + unknown_64 : [0x4], + battle_music : 1, + polygon_music: 1, + city : 1, + unknown_6a : 1, + experience : 1, + armor_evo : 1, ); let mut cards = [0; 30]; @@ -80,17 +123,29 @@ impl Bytes for Deck { name: bytes.name.read_string().map_err(FromBytesError::Name)?.to_ascii_string(), owner: bytes.owner.read_string().map_err(FromBytesError::Owner)?.to_ascii_string(), cards, - unknown_62: *bytes.unknown_62, + city: Option::::from_bytes(bytes.city).map_err(FromBytesError::City)?, + armor_evo: Option::::from_bytes(bytes.armor_evo).map_err(FromBytesError::ArmorEvo)?, + battle_music: Option::::from_bytes(bytes.battle_music).map_err(FromBytesError::BattleMusic)?, + polygon_music: Option::::from_bytes(bytes.polygon_music).map_err(FromBytesError::PolygonMusic)?, + experience: *bytes.experience, + unknown_64: *bytes.unknown_64, + unknown_6a: *bytes.unknown_6a, }) } fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> { // Split the bytes let bytes = array_split_mut!(bytes, - deck : [0x3c], - name : [0x13], - owner : [0x13], - unknown_62: [0xc], + deck : [0x3c], + name : [0x13], + owner : [0x15], + unknown_64 : [0x4], + battle_music : 1, + polygon_music: 1, + city : 1, + unknown_6a : 1, + experience : 1, + armor_evo : 1, ); // Name / Owner @@ -105,8 +160,22 @@ impl Bytes for Deck { LittleEndian::write_u16(&mut bytes.deck[offset..offset + CARD_ID_SIZE], *card); } + // Experience + *bytes.experience = self.experience; + + // City + self.city.to_bytes(bytes.city).into_ok(); + + // Armor evo + self.armor_evo.to_bytes(bytes.armor_evo).into_ok(); + + // Music + self.battle_music.to_bytes(bytes.battle_music).into_ok(); + self.polygon_music.to_bytes(bytes.polygon_music).into_ok(); + // Unknown - *bytes.unknown_62 = self.unknown_62; + *bytes.unknown_64 = self.unknown_64; + *bytes.unknown_6a = self.unknown_6a; // And return Ok Ok(()) diff --git a/src/game/deck/table.rs b/src/game/deck/table.rs index 6a7f309..bf88fd7 100644 --- a/src/game/deck/table.rs +++ b/src/game/deck/table.rs @@ -37,7 +37,7 @@ impl Table { // TODO: Verify this pub const MAX_BYTE_SIZE: usize = 0x4452; /// The start address of the decks table - const START_ADDRESS: Data = Data::from_u64(0x21a6808); + const START_ADDRESS: Data = Data::from_u64(0x21a6800); } impl Table { diff --git a/src/util.rs b/src/util.rs index 02268d1..06b5fa8 100644 --- a/src/util.rs +++ b/src/util.rs @@ -9,6 +9,8 @@ // Modules pub mod array_split; pub mod null_ascii_string; +#[macro_use] +pub mod impl_bytes; // Exports pub use array_split::{array_split, array_split_mut}; diff --git a/src/util/array_split.rs b/src/util/array_split.rs index 83b0185..eb73e3f 100644 --- a/src/util/array_split.rs +++ b/src/util/array_split.rs @@ -69,7 +69,6 @@ pub macro array_split_mut { clippy::used_underscore_binding, clippy::ptr_offset_with_cast, clippy::indexing_slicing, - dead_code )] // Struct holding all fields diff --git a/src/util/impl_bytes.rs b/src/util/impl_bytes.rs new file mode 100644 index 0000000..2a46286 --- /dev/null +++ b/src/util/impl_bytes.rs @@ -0,0 +1,167 @@ +//! Helper macros to implement [`Bytes`](crate::Bytes) + +/// Defines and implements a property enum +// TODO: Make better documentation +// TODO: Turn into a `macro` once they work +#[macro_export] +macro_rules! generate_enum_property_mod +{ + // Entry point + ( + // The modules + $( + // Module + $mod_vis:vis mod $mod_name:ident + { + // Enum attributes + $( #[$enum_attr:meta] )* + + // Enum + enum $enum_name:ident + { + // Enum variants + $( + // Attributes + $( #[$enum_variant_attr:meta] )* + + // Variant + // Note: Must have no data + $enum_variant_name:ident + + // `Display` conversion name + ($enum_variant_rename:literal) + + => + + // Variant value + $enum_variant_value:literal, + )+ + + // Extra fields for `Bytes::from_bytes`. + $( + $from_bytes_value:literal => $from_bytes_body:tt, + )* + + // Error + _ => $error_unknown_value_display:literal + + $(,)? + } + + // Any further definitions inside the module + $( $extra_defs:tt )* + } + )* + ) => + { + // Modules + $( + // The module + $mod_vis mod $mod_name + { + // The property enum + $( #[$enum_attr] )* + #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] + #[derive(::serde::Serialize, ::serde::Deserialize)] + #[derive(::derive_more::Display)] + pub enum $enum_name + { + $( + $( #[$enum_variant_attr] )* + #[serde(rename = $enum_variant_rename)] + #[display(fmt = $enum_variant_rename)] + $enum_variant_name = $enum_variant_value, + )+ + } + + /// Error type for [`$crate::game::Bytes::from_bytes`] + #[derive(PartialEq, Eq, Clone, Copy, ::std::fmt::Debug, ::thiserror::Error)] + pub enum FromBytesError { + + /// Unknown value + #[error($error_unknown_value_display, byte)] + UnknownValue { + byte: u8, + } + } + + impl $crate::game::Bytes for $enum_name + { + type ByteArray = u8; + + type FromError = FromBytesError; + fn from_bytes(byte: &Self::ByteArray) -> Result + { + match byte { + $( + $enum_variant_value => + Ok( <$enum_name>::$enum_variant_name ), + )+ + + $( + $from_bytes_value => { + Ok( { $from_bytes_body } ) + } + )* + + &byte => Err( Self::FromError::UnknownValue{ byte } ), + } + } + + type ToError = !; + #[allow(unreachable_code, unused_variables)] // For when there are multiple values + fn to_bytes(&self, byte: &mut Self::ByteArray) -> Result<(), Self::ToError> + { + *byte = match self { + $( + <$enum_name>::$enum_variant_name => $enum_variant_value, + )+ + }; + + Ok(()) + } + } + + // Extra definitions + $( $extra_defs )* + } + )* + } +} + +/// Implements [`Bytes`](crate::game::Bytes) for `Option` where `E` +/// is the first argument of this macro and an enum. +/// +/// This is done by supplying a sentinel value which is read/written as `None`. +pub macro generate_enum_property_option { + ( + $( $enum_name:ty => $sentinel_value:literal ),* $(,)? + ) => { + $( + #[allow(clippy::diverging_sub_expression)] // Errors might be `!` + impl $crate::game::Bytes for Option<$enum_name> { + type ByteArray = <$enum_name as $crate::game::Bytes>::ByteArray; + + type FromError = <$enum_name as $crate::game::Bytes>::FromError; + fn from_bytes(bytes: &Self::ByteArray) -> Result + { + match bytes { + $sentinel_value => Ok( None ), + _ => Ok( Some( $crate::game::Bytes::from_bytes(bytes)? ) ), + } + } + + type ToError = <$enum_name as $crate::game::Bytes>::ToError; + fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> + { + match self { + Some(value) => $crate::game::Bytes::to_bytes(value, bytes)?, + None => *bytes = $sentinel_value, + } + + Ok(()) + } + } + )* + } +}