Moved helper macros to implement Bytes to util.

Discovered more fields of `Deck`.
Fixed `DeckTable` having the wrong address for the table.
This commit is contained in:
2020-07-21 18:18:46 +01:00
parent 1e171b7071
commit 7873c5eed3
7 changed files with 317 additions and 178 deletions

View File

@@ -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<Self, Self::FromError>
{
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<E>` 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<Self, Self::FromError>
{
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 {

View File

@@ -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;

View File

@@ -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<City>,
/// Armor digivolution
pub armor_evo: Option<ArmorEvo>,
/// Battle music
pub battle_music: Option<Music>,
/// Polygon music
pub polygon_music: Option<Music>,
/// 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<Self, Self::FromError> {
// 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::<City>::from_bytes(bytes.city).map_err(FromBytesError::City)?,
armor_evo: Option::<ArmorEvo>::from_bytes(bytes.armor_evo).map_err(FromBytesError::ArmorEvo)?,
battle_music: Option::<Music>::from_bytes(bytes.battle_music).map_err(FromBytesError::BattleMusic)?,
polygon_music: Option::<Music>::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(())

View File

@@ -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 {

View File

@@ -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};

View File

@@ -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

167
src/util/impl_bytes.rs Normal file
View File

@@ -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<Self, Self::FromError>
{
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<E>` 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<Self, Self::FromError>
{
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(())
}
}
)*
}
}