Updated names relating to support conditions / effects.

Added documentation to `EffectCondition`.
Changed `array_split` to behave like `array_split_mut`.
Instead of using `?` on `Result<T, !>` , we use `Result::into_ok`.
This commit is contained in:
2020-04-26 08:54:40 +01:00
parent 8b4b11113a
commit e52cc554ff
12 changed files with 635 additions and 695 deletions

View File

@@ -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<SupportCondition>; 2],
pub effect_conditions: [Option<EffectCondition>; 2],
/// The effects themselves
#[serde(default)]
pub effects: [Option<SupportEffect>; 3],
pub effects: [Option<Effect>; 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::<EffectCondition>::from_bytes( bytes.condition_first )
.map_err(FromBytesError::EffectConditionFirst)?,
(bytes.condition_second[2] != 0)
.then(|| SupportCondition::from_bytes( bytes.condition_second ) )
.transpose()
Option::<EffectCondition>::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 )? };

View File

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

View File

@@ -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<SupportEffect>,
second: Option<SupportEffect>,
third : Option<SupportEffect>,
first : Option<Effect>,
second: Option<Effect>,
third : Option<Effect>,
}
#[derive(Debug, Serialize, Deserialize)]
struct SupportConditions
{
first : Option<SupportCondition>,
second: Option<SupportCondition>,
first : Option<EffectCondition>,
second: Option<EffectCondition>,
}
/// 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::<EffectCondition>::from_bytes( array_ref!(bytes, 0x19, 0x20) )
.map_err(|err| FromBytesError::SupportCondition{ rank: "1st", item_pos: 0x19, err })?,
second: Option::<EffectCondition>::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

View File

@@ -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::< <crate::game::card::Digimon as Bytes>::ByteArray >(),
Self::Item => std::mem::size_of::< <crate::game::card::Item as Bytes>::ByteArray >(),
Self::Digivolve => std::mem::size_of::< <crate::game::card::Digivolve as Bytes>::ByteArray >(),
}
Self::Digimon => std::mem::size_of::< <crate::game::card::Digimon as crate::game::Bytes>::ByteArray >(),
Self::Item => std::mem::size_of::< <crate::game::card::Item as crate::game::Bytes>::ByteArray >(),
Self::Digivolve => std::mem::size_of::< <crate::game::card::Digivolve as crate::game::Bytes>::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;

View File

@@ -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<DigimonProperty>,
c: Option<DigimonProperty>,
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!(),

View File

@@ -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<DigimonProperty>,
/// 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<EffectCondition>
{
type ByteArray = [u8; 0x20];
type FromError = FromBytesError;
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError>
{
// 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(())
}
}

View File

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

View File

@@ -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<DigimonProperty>,
/// 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<Self, Self::FromError>
{
// 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(())
}
}

View File

@@ -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<R: Read + Write + Seek, C: Bytes>(_file: &mut GameFile<R>, _card: &C, _cur_id: usize) {
/*
// Get the bytes
let mut bytes = [0u8; <C as Bytes>::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(())
}

View File

@@ -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] ) )?

View File

@@ -239,7 +239,9 @@ impl<R: Read + Write + Seek> Seek for GameFile<R>
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

View File

@@ -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<T, !>` 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