mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-09 03:40:23 +00:00
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:
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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
167
src/util/impl_bytes.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user