diff --git a/src/game/card/digimon.rs b/src/game/card/digimon.rs index e22fb85..bf30835 100644 --- a/src/game/card/digimon.rs +++ b/src/game/card/digimon.rs @@ -3,7 +3,11 @@ // Imports use crate::game::{ card::property::{self, ArrowColor, CrossMoveEffect, Effect, EffectCondition, Level, Move, Speciality}, - util, Bytes, + util::{ + array_split, array_split_mut, + null_ascii_string::{self, NullAsciiString}, + }, + Bytes, }; use byteorder::{ByteOrder, LittleEndian}; @@ -87,23 +91,23 @@ pub struct Digimon { pub enum FromBytesError { /// Unable to read the digimon name #[error("Unable to read the digimon name")] - Name(#[source] util::ReadNullAsciiStringError), + Name(#[source] null_ascii_string::ReadError), /// Unable to read the first support effect description #[error("Unable to read the first line of the effect description")] - EffectDescriptionFirst(#[source] util::ReadNullAsciiStringError), + EffectDescriptionFirst(#[source] null_ascii_string::ReadError), /// Unable to read the second support effect description #[error("Unable to read the second line of the effect description")] - EffectDescriptionSecond(#[source] util::ReadNullAsciiStringError), + EffectDescriptionSecond(#[source] null_ascii_string::ReadError), /// Unable to read the third support effect description #[error("Unable to read the third line of the effect description")] - EffectDescriptionThird(#[source] util::ReadNullAsciiStringError), + EffectDescriptionThird(#[source] null_ascii_string::ReadError), /// Unable to read the fourth support effect description #[error("Unable to read the fourth line of the effect description")] - EffectDescriptionFourth(#[source] util::ReadNullAsciiStringError), + EffectDescriptionFourth(#[source] null_ascii_string::ReadError), /// An unknown speciality was found #[error("Unknown speciality found")] @@ -159,23 +163,23 @@ pub enum FromBytesError { pub enum ToBytesError { /// Unable to write the digimon name #[error("Unable to write the digimon name")] - Name(#[source] util::WriteNullAsciiStringError), + Name(#[source] null_ascii_string::WriteError), /// Unable to write the first support effect description #[error("Unable to write the first line of the effect description")] - EffectDescriptionFirst(#[source] util::WriteNullAsciiStringError), + EffectDescriptionFirst(#[source] null_ascii_string::WriteError), /// Unable to write the second support effect description #[error("Unable to write the second line of the effect description")] - EffectDescriptionSecond(#[source] util::WriteNullAsciiStringError), + EffectDescriptionSecond(#[source] null_ascii_string::WriteError), /// Unable to write the third support effect description #[error("Unable to write the third line of the effect description")] - EffectDescriptionThird(#[source] util::WriteNullAsciiStringError), + EffectDescriptionThird(#[source] null_ascii_string::WriteError), /// Unable to write the fourth support effect description #[error("Unable to write the fourth line of the effect description")] - EffectDescriptionFourth(#[source] util::WriteNullAsciiStringError), + EffectDescriptionFourth(#[source] null_ascii_string::WriteError), /// Unable to write the circle move #[error("Unable to write the circle move")] @@ -209,7 +213,7 @@ impl Bytes for Digimon { fn from_bytes(bytes: &Self::ByteArray) -> Result { // Split bytes - let bytes = util::array_split!(bytes, + let bytes = array_split!(bytes, name : [0x15], unknown_15 : [0x2], speciality_level : 0x1, @@ -236,7 +240,7 @@ impl Bytes for Digimon { // Return the struct after building it Ok(Self { - name: util::read_null_ascii_string(bytes.name).map_err(FromBytesError::Name)?.to_ascii_string(), + name: bytes.name.read_string().map_err(FromBytesError::Name)?.to_ascii_string(), speciality: Speciality::from_bytes(&((bytes.speciality_level & 0xF0) >> 4)).map_err(FromBytesError::Speciality)?, @@ -269,16 +273,24 @@ impl Bytes for Digimon { effect_arrow_color: Option::::from_bytes(bytes.effect_arrow_color).map_err(FromBytesError::ArrowColor)?, effect_description: [ - util::read_null_ascii_string(bytes.effect_description_0) + bytes + .effect_description_0 + .read_string() .map_err(FromBytesError::EffectDescriptionFirst)? .to_ascii_string(), - util::read_null_ascii_string(bytes.effect_description_1) + bytes + .effect_description_1 + .read_string() .map_err(FromBytesError::EffectDescriptionSecond)? .to_ascii_string(), - util::read_null_ascii_string(bytes.effect_description_2) + bytes + .effect_description_2 + .read_string() .map_err(FromBytesError::EffectDescriptionThird)? .to_ascii_string(), - util::read_null_ascii_string(bytes.effect_description_3) + bytes + .effect_description_3 + .read_string() .map_err(FromBytesError::EffectDescriptionFourth)? .to_ascii_string(), ], @@ -292,7 +304,7 @@ impl Bytes for Digimon { fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> { // Split bytes - let bytes = util::array_split_mut!(bytes, + let bytes = array_split_mut!(bytes, name : [0x15], unknown_15 : [0x2], speciality_level : 0x1, @@ -318,7 +330,7 @@ impl Bytes for Digimon { ); // Name - util::write_null_ascii_string(self.name.as_ref(), bytes.name).map_err(ToBytesError::Name)?; + bytes.name.write_string(&self.name).map_err(ToBytesError::Name)?; // Speciality / Level { @@ -356,13 +368,21 @@ impl Bytes for Digimon { Option::::to_bytes(&self.effect_arrow_color, bytes.effect_arrow_color).into_ok(); - util::write_null_ascii_string(self.effect_description[0].as_ref(), bytes.effect_description_0) + bytes + .effect_description_0 + .write_string(&self.effect_description[0]) .map_err(ToBytesError::EffectDescriptionFirst)?; - util::write_null_ascii_string(self.effect_description[1].as_ref(), bytes.effect_description_1) + bytes + .effect_description_1 + .write_string(&self.effect_description[1]) .map_err(ToBytesError::EffectDescriptionSecond)?; - util::write_null_ascii_string(self.effect_description[2].as_ref(), bytes.effect_description_2) + bytes + .effect_description_2 + .write_string(&self.effect_description[2]) .map_err(ToBytesError::EffectDescriptionThird)?; - util::write_null_ascii_string(self.effect_description[3].as_ref(), bytes.effect_description_3) + bytes + .effect_description_3 + .write_string(&self.effect_description[3]) .map_err(ToBytesError::EffectDescriptionFourth)?; // Unknown diff --git a/src/game/card/digivolve.rs b/src/game/card/digivolve.rs index c608e83..3c9ba90 100644 --- a/src/game/card/digivolve.rs +++ b/src/game/card/digivolve.rs @@ -1,7 +1,13 @@ #![doc(include = "digivolve.md")] // Imports -use crate::game::{util, Bytes}; +use crate::game::{ + util::{ + array_split, array_split_mut, + null_ascii_string::{self, NullAsciiString}, + }, + Bytes, +}; /// A digivolve card /// @@ -29,23 +35,23 @@ pub struct Digivolve { pub enum FromBytesError { /// Unable to read the digimon name #[error("Unable to read the digimon name")] - Name(#[source] util::ReadNullAsciiStringError), + Name(#[source] null_ascii_string::ReadError), /// Unable to read the first support effect description #[error("Unable to read the first line of the effect description")] - EffectDescriptionFirst(#[source] util::ReadNullAsciiStringError), + EffectDescriptionFirst(#[source] null_ascii_string::ReadError), /// Unable to read the second support effect description #[error("Unable to read the second line of the effect description")] - EffectDescriptionSecond(#[source] util::ReadNullAsciiStringError), + EffectDescriptionSecond(#[source] null_ascii_string::ReadError), /// Unable to read the third support effect description #[error("Unable to read the third line of the effect description")] - EffectDescriptionThird(#[source] util::ReadNullAsciiStringError), + EffectDescriptionThird(#[source] null_ascii_string::ReadError), /// Unable to read the fourth support effect description #[error("Unable to read the fourth line of the effect description")] - EffectDescriptionFourth(#[source] util::ReadNullAsciiStringError), + EffectDescriptionFourth(#[source] null_ascii_string::ReadError), } /// Error type for [`Bytes::to_bytes`] @@ -53,23 +59,23 @@ pub enum FromBytesError { pub enum ToBytesError { /// Unable to write the digimon name #[error("Unable to write the digimon name")] - Name(#[source] util::WriteNullAsciiStringError), + Name(#[source] null_ascii_string::WriteError), /// Unable to write the first support effect description #[error("Unable to write the first line of the effect description")] - EffectDescriptionFirst(#[source] util::WriteNullAsciiStringError), + EffectDescriptionFirst(#[source] null_ascii_string::WriteError), /// Unable to write the second support effect description #[error("Unable to write the second line of the effect description")] - EffectDescriptionSecond(#[source] util::WriteNullAsciiStringError), + EffectDescriptionSecond(#[source] null_ascii_string::WriteError), /// Unable to write the third support effect description #[error("Unable to write the third line of the effect description")] - EffectDescriptionThird(#[source] util::WriteNullAsciiStringError), + EffectDescriptionThird(#[source] null_ascii_string::WriteError), /// Unable to write the fourth support effect description #[error("Unable to write the fourth line of the effect description")] - EffectDescriptionFourth(#[source] util::WriteNullAsciiStringError), + EffectDescriptionFourth(#[source] null_ascii_string::WriteError), } impl Bytes for Digivolve { @@ -79,7 +85,7 @@ impl Bytes for Digivolve { fn from_bytes(bytes: &Self::ByteArray) -> Result { // Split bytes - let bytes = util::array_split!(bytes, + let bytes = array_split!(bytes, name : [0x15], unknown_15 : [0x3], effect_description_0: [0x15], @@ -90,20 +96,28 @@ impl Bytes for Digivolve { Ok(Self { // Name - name: util::read_null_ascii_string(bytes.name).map_err(FromBytesError::Name)?.to_ascii_string(), + name: bytes.name.read_string().map_err(FromBytesError::Name)?.to_ascii_string(), // Effect effect_description: [ - util::read_null_ascii_string(bytes.effect_description_0) + bytes + .effect_description_0 + .read_string() .map_err(FromBytesError::EffectDescriptionFirst)? .to_ascii_string(), - util::read_null_ascii_string(bytes.effect_description_1) + bytes + .effect_description_1 + .read_string() .map_err(FromBytesError::EffectDescriptionSecond)? .to_ascii_string(), - util::read_null_ascii_string(bytes.effect_description_2) + bytes + .effect_description_2 + .read_string() .map_err(FromBytesError::EffectDescriptionThird)? .to_ascii_string(), - util::read_null_ascii_string(bytes.effect_description_3) + bytes + .effect_description_3 + .read_string() .map_err(FromBytesError::EffectDescriptionFourth)? .to_ascii_string(), ], @@ -115,7 +129,7 @@ impl Bytes for Digivolve { fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> { // Split bytes - let bytes = util::array_split_mut!(bytes, + let bytes = array_split_mut!(bytes, name : [0x15], unknown_15 : [0x3], effect_description_0: [0x15], @@ -125,16 +139,24 @@ impl Bytes for Digivolve { ); // Name - util::write_null_ascii_string(self.name.as_ref(), bytes.name).map_err(ToBytesError::Name)?; + bytes.name.write_string(&self.name).map_err(ToBytesError::Name)?; // Effects - util::write_null_ascii_string(self.effect_description[0].as_ref(), bytes.effect_description_0) + bytes + .effect_description_0 + .write_string(&self.effect_description[0]) .map_err(ToBytesError::EffectDescriptionFirst)?; - util::write_null_ascii_string(self.effect_description[1].as_ref(), bytes.effect_description_1) + bytes + .effect_description_1 + .write_string(&self.effect_description[1]) .map_err(ToBytesError::EffectDescriptionSecond)?; - util::write_null_ascii_string(self.effect_description[2].as_ref(), bytes.effect_description_2) + bytes + .effect_description_2 + .write_string(&self.effect_description[2]) .map_err(ToBytesError::EffectDescriptionThird)?; - util::write_null_ascii_string(self.effect_description[3].as_ref(), bytes.effect_description_3) + bytes + .effect_description_3 + .write_string(&self.effect_description[3]) .map_err(ToBytesError::EffectDescriptionFourth)?; // Unknown diff --git a/src/game/card/item.rs b/src/game/card/item.rs index 5b09bea..190259c 100644 --- a/src/game/card/item.rs +++ b/src/game/card/item.rs @@ -3,7 +3,11 @@ // Imports use crate::game::{ card::property::{self, ArrowColor, Effect, EffectCondition}, - util, Bytes, + util::{ + array_split, array_split_mut, + null_ascii_string::{self, NullAsciiString}, + }, + Bytes, }; use byteorder::{ByteOrder, LittleEndian}; @@ -45,23 +49,23 @@ pub struct Item { pub enum FromBytesError { /// Unable to read the digimon name #[error("Unable to read the digimon name")] - Name(#[source] util::ReadNullAsciiStringError), + Name(#[source] null_ascii_string::ReadError), /// Unable to read the first support effect description #[error("Unable to read the first line of the effect description")] - EffectDescriptionFirst(#[source] util::ReadNullAsciiStringError), + EffectDescriptionFirst(#[source] null_ascii_string::ReadError), /// Unable to read the second support effect description #[error("Unable to read the second line of the effect description")] - EffectDescriptionSecond(#[source] util::ReadNullAsciiStringError), + EffectDescriptionSecond(#[source] null_ascii_string::ReadError), /// Unable to read the third support effect description #[error("Unable to read the third line of the effect description")] - EffectDescriptionThird(#[source] util::ReadNullAsciiStringError), + EffectDescriptionThird(#[source] null_ascii_string::ReadError), /// Unable to read the fourth support effect description #[error("Unable to read the fourth line of the effect description")] - EffectDescriptionFourth(#[source] util::ReadNullAsciiStringError), + EffectDescriptionFourth(#[source] null_ascii_string::ReadError), /// An unknown effect arrow color was found #[error("Unknown effect arrow color found")] @@ -93,23 +97,23 @@ pub enum FromBytesError { pub enum ToBytesError { /// Unable to write the digimon name #[error("Unable to write the digimon name")] - Name(#[source] util::WriteNullAsciiStringError), + Name(#[source] null_ascii_string::WriteError), /// Unable to write the first support effect description #[error("Unable to write the first line of the effect description")] - EffectDescriptionFirst(#[source] util::WriteNullAsciiStringError), + EffectDescriptionFirst(#[source] null_ascii_string::WriteError), /// Unable to write the second support effect description #[error("Unable to write the second line of the effect description")] - EffectDescriptionSecond(#[source] util::WriteNullAsciiStringError), + EffectDescriptionSecond(#[source] null_ascii_string::WriteError), /// Unable to write the third support effect description #[error("Unable to write the third line of the effect description")] - EffectDescriptionThird(#[source] util::WriteNullAsciiStringError), + EffectDescriptionThird(#[source] null_ascii_string::WriteError), /// Unable to write the fourth support effect description #[error("Unable to write the fourth line of the effect description")] - EffectDescriptionFourth(#[source] util::WriteNullAsciiStringError), + EffectDescriptionFourth(#[source] null_ascii_string::WriteError), /// Unable to write the first effect #[error("Unable to write the first effect")] @@ -131,7 +135,7 @@ impl Bytes for Item { fn from_bytes(bytes: &Self::ByteArray) -> Result { // Split bytes - let bytes = util::array_split!(bytes, + let bytes = array_split!(bytes, name : [0x15], unknown_15 : [0x4], condition_first : [0x20], @@ -148,7 +152,7 @@ impl Bytes for Item { // And return the struct Ok(Self { - name: util::read_null_ascii_string(bytes.name).map_err(FromBytesError::Name)?.to_ascii_string(), + name: bytes.name.read_string().map_err(FromBytesError::Name)?.to_ascii_string(), // Effects effect_conditions: [ @@ -165,16 +169,24 @@ impl Bytes for Item { effect_arrow_color: Option::::from_bytes(bytes.effect_arrow_color).map_err(FromBytesError::ArrowColor)?, effect_description: [ - util::read_null_ascii_string(bytes.effect_description_0) + bytes + .effect_description_0 + .read_string() .map_err(FromBytesError::EffectDescriptionFirst)? .to_ascii_string(), - util::read_null_ascii_string(bytes.effect_description_1) + bytes + .effect_description_1 + .read_string() .map_err(FromBytesError::EffectDescriptionSecond)? .to_ascii_string(), - util::read_null_ascii_string(bytes.effect_description_2) + bytes + .effect_description_2 + .read_string() .map_err(FromBytesError::EffectDescriptionThird)? .to_ascii_string(), - util::read_null_ascii_string(bytes.effect_description_3) + bytes + .effect_description_3 + .read_string() .map_err(FromBytesError::EffectDescriptionFourth)? .to_ascii_string(), ], @@ -186,7 +198,7 @@ impl Bytes for Item { fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> { // Split bytes - let bytes = util::array_split_mut!(bytes, + let bytes = array_split_mut!(bytes, name : [0x15], unknown_15 : [0x4], condition_first : [0x20], @@ -202,7 +214,7 @@ impl Bytes for Item { ); // Name - util::write_null_ascii_string(self.name.as_ref(), bytes.name).map_err(ToBytesError::Name)?; + bytes.name.write_string(&self.name).map_err(ToBytesError::Name)?; // Effects self.effect_conditions[0].to_bytes(bytes.condition_first).into_ok(); @@ -214,13 +226,21 @@ impl Bytes for Item { Option::::to_bytes(&self.effect_arrow_color, bytes.effect_arrow_color).into_ok(); - util::write_null_ascii_string(self.effect_description[0].as_ref(), bytes.effect_description_0) + bytes + .effect_description_0 + .write_string(&self.effect_description[0]) .map_err(ToBytesError::EffectDescriptionFirst)?; - util::write_null_ascii_string(self.effect_description[1].as_ref(), bytes.effect_description_1) + bytes + .effect_description_1 + .write_string(&self.effect_description[1]) .map_err(ToBytesError::EffectDescriptionSecond)?; - util::write_null_ascii_string(self.effect_description[2].as_ref(), bytes.effect_description_2) + bytes + .effect_description_2 + .write_string(&self.effect_description[2]) .map_err(ToBytesError::EffectDescriptionThird)?; - util::write_null_ascii_string(self.effect_description[3].as_ref(), bytes.effect_description_3) + bytes + .effect_description_3 + .write_string(&self.effect_description[3]) .map_err(ToBytesError::EffectDescriptionFourth)?; // Unknown diff --git a/src/game/card/property/effect.rs b/src/game/card/property/effect.rs index 617e760..2df6e9c 100644 --- a/src/game/card/property/effect.rs +++ b/src/game/card/property/effect.rs @@ -3,7 +3,8 @@ // Imports use crate::game::{ card::property::{self, AttackType, DigimonProperty, EffectOperation, PlayerType, Slot}, - util, Bytes, + util::{array_split, array_split_mut}, + Bytes, }; use byteorder::{ByteOrder, LittleEndian}; @@ -214,7 +215,7 @@ impl Bytes for Effect { use Slot::{Dp as DpSlot, Hand, Offline as OfflineDeck, Online as OnlineDeck}; // Get all byte arrays we need - let bytes = util::array_split!(bytes, + let bytes = array_split!(bytes, effect_type: 0x1, a : 0x1, _unknown_3 : 0x1, @@ -323,7 +324,7 @@ impl Bytes for Effect { use Slot::{Dp as DpSlot, Hand, Offline as OfflineDeck, Online as OnlineDeck}; // Get all byte arrays we need - let bytes = util::array_split_mut!(bytes, + let bytes = array_split_mut!(bytes, effect_type: 0x1, a : 0x1, _unknown_3 : 0x1, @@ -464,7 +465,7 @@ impl Bytes for Option { // `bytes` should include the `exists` byte fn from_bytes(bytes: &Self::ByteArray) -> Result { - let bytes = util::array_split!(bytes, + let bytes = array_split!(bytes, exists : 0x1, effect : [0xf], ); @@ -479,7 +480,7 @@ impl Bytes for Option { } fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> { - let bytes = util::array_split_mut!(bytes, + let bytes = array_split_mut!(bytes, exists: 0x1, effect: [0xf], ); diff --git a/src/game/card/property/effect_condition.rs b/src/game/card/property/effect_condition.rs index c372ffa..a03feb8 100644 --- a/src/game/card/property/effect_condition.rs +++ b/src/game/card/property/effect_condition.rs @@ -3,7 +3,8 @@ // Imports use crate::game::{ card::property::{self, DigimonProperty, EffectConditionOperation}, - util, Bytes, + util::{array_split, array_split_mut}, + Bytes, }; use byteorder::{ByteOrder, LittleEndian}; @@ -64,7 +65,7 @@ impl Bytes for EffectCondition { type ToError = !; fn from_bytes(bytes: &Self::ByteArray) -> Result { - let bytes = util::array_split!(bytes, + let bytes = array_split!(bytes, misfire : 0x1, unknown_1 : 0x1, property_cmp: 0x1, @@ -96,7 +97,7 @@ impl Bytes for EffectCondition { } fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> { - let bytes = util::array_split_mut!(bytes, + let bytes = array_split_mut!(bytes, misfire : 0x1, unknown_1 : 0x1, property_cmp: 0x1, diff --git a/src/game/card/property/moves.rs b/src/game/card/property/moves.rs index 7e622fd..3f105b0 100644 --- a/src/game/card/property/moves.rs +++ b/src/game/card/property/moves.rs @@ -5,7 +5,13 @@ mod test; // Imports -use crate::game::{util, Bytes}; +use crate::game::{ + util::{ + array_split, array_split_mut, + null_ascii_string::{self, NullAsciiString}, + }, + Bytes, +}; use byteorder::{ByteOrder, LittleEndian}; /// A digimon's move @@ -27,7 +33,7 @@ pub struct Move { pub enum FromBytesError { /// Unable to read the move name #[error("Unable to read the move name")] - Name(#[source] util::ReadNullAsciiStringError), + Name(#[source] null_ascii_string::ReadError), } /// Error type for [`Bytes::to_bytes`] @@ -35,7 +41,7 @@ pub enum FromBytesError { pub enum ToBytesError { /// Unable to write the move name #[error("Unable to write the move name")] - Name(#[source] util::WriteNullAsciiStringError), + Name(#[source] null_ascii_string::WriteError), } // Bytes @@ -46,7 +52,7 @@ impl Bytes for Move { fn from_bytes(bytes: &Self::ByteArray) -> Result { // Get all byte arrays we need - let bytes = util::array_split!(bytes, + let bytes = array_split!(bytes, power : [0x2], unknown: [0x4], name : [0x16], @@ -54,7 +60,7 @@ impl Bytes for Move { // Return the move Ok(Self { - name: util::read_null_ascii_string(bytes.name).map_err(FromBytesError::Name)?.to_ascii_string(), + name: bytes.name.read_string().map_err(FromBytesError::Name)?.to_ascii_string(), power: LittleEndian::read_u16(bytes.power), unknown: LittleEndian::read_u32(bytes.unknown), }) @@ -62,14 +68,14 @@ impl Bytes for Move { fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> { // Get all byte arrays we need - let bytes = util::array_split_mut!(bytes, + let bytes = array_split_mut!(bytes, power : [0x2], unknown: [0x4], name : [0x16], ); // Write the name - util::write_null_ascii_string(self.name.as_ref(), bytes.name).map_err(ToBytesError::Name)?; + bytes.name.write_string(&self.name).map_err(ToBytesError::Name)?; // Then write the power and the unknown LittleEndian::write_u16(bytes.power, self.power); diff --git a/src/game/card/table.rs b/src/game/card/table.rs index 4eeaa19..3b73540 100644 --- a/src/game/card/table.rs +++ b/src/game/card/table.rs @@ -8,7 +8,8 @@ use crate::{ property::{self, CardType}, Digimon, Digivolve, Item, }, - util, Bytes, + util::array_split_mut, + Bytes, }, io::{address::Data, GameFile}, }; @@ -396,7 +397,7 @@ impl Table { // Write header let mut header_bytes = [0u8; 0x8]; { - let bytes = util::array_split_mut!(&mut header_bytes, + let bytes = array_split_mut!(&mut header_bytes, magic: [0x4], digimons_len: [0x2], @@ -426,7 +427,7 @@ impl Table { // Card bytes let mut card_bytes = [0; 0x3 + CardType::Digimon.byte_size() + 0x1]; - let bytes = util::array_split_mut!(&mut card_bytes, + let bytes = array_split_mut!(&mut card_bytes, header_id : [0x2], header_type: 1, digimon : [CardType::Digimon.byte_size()], @@ -455,7 +456,7 @@ impl Table { // Card bytes let mut card_bytes = [0; 0x3 + CardType::Item.byte_size() + 0x1]; - let bytes = util::array_split_mut!(&mut card_bytes, + let bytes = array_split_mut!(&mut card_bytes, header_id : [0x2], header_type: 1, item : [CardType::Item.byte_size()], @@ -483,7 +484,7 @@ impl Table { // Card bytes let mut card_bytes = [0; 0x3 + CardType::Digivolve.byte_size() + 0x1]; - let bytes = util::array_split_mut!(&mut card_bytes, + let bytes = array_split_mut!(&mut card_bytes, header_id : [0x2], header_type: 1, item : [CardType::Digivolve.byte_size()], diff --git a/src/game/deck/deck.rs b/src/game/deck/deck.rs index bea4b75..2438e68 100644 --- a/src/game/deck/deck.rs +++ b/src/game/deck/deck.rs @@ -1,7 +1,13 @@ //! Decks // Imports -use crate::game::{util, Bytes}; +use crate::game::{ + util::{ + array_split, array_split_mut, + null_ascii_string::{self, NullAsciiString}, + }, + Bytes, +}; use byteorder::{ByteOrder, LittleEndian}; /// A deck @@ -26,11 +32,11 @@ pub struct Deck { pub enum FromBytesError { /// Unable to read the deck name #[error("Unable to read the deck name")] - Name(#[source] util::ReadMaybeNullAsciiStringError), + Name(#[source] null_ascii_string::ReadError), /// Unable to read the deck owner #[error("Unable to read the deck owner")] - Owner(#[source] util::ReadMaybeNullAsciiStringError), + Owner(#[source] null_ascii_string::ReadError), } /// Error type for [`Bytes::to_bytes`] @@ -38,11 +44,11 @@ pub enum FromBytesError { pub enum ToBytesError { /// Unable to write the deck name #[error("Unable to write the deck name")] - Name(#[source] util::WriteMaybeNullAsciiStringError), + Name(#[source] null_ascii_string::WriteError), /// Unable to write the deck owner #[error("Unable to write the deck owner")] - Owner(#[source] util::WriteMaybeNullAsciiStringError), + Owner(#[source] null_ascii_string::WriteError), } impl Bytes for Deck { @@ -52,7 +58,7 @@ impl Bytes for Deck { fn from_bytes(bytes: &Self::ByteArray) -> Result { // Split the bytes - let bytes = util::array_split!(bytes, + let bytes = array_split!(bytes, deck : [0x3c], name : [0x13], owner : [0x13], @@ -60,13 +66,9 @@ impl Bytes for Deck { ); Ok(Self { - name: util::read_maybe_null_ascii_string(bytes.name) - .map_err(FromBytesError::Name)? - .to_ascii_string(), + name: bytes.name.read_string().map_err(FromBytesError::Name)?.to_ascii_string(), - owner: util::read_maybe_null_ascii_string(bytes.owner) - .map_err(FromBytesError::Owner)? - .to_ascii_string(), + owner: bytes.owner.read_string().map_err(FromBytesError::Owner)?.to_ascii_string(), cards: { let mut cards_buf = [0; 0x1e]; @@ -84,7 +86,7 @@ impl Bytes for Deck { fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> { // Split the bytes - let bytes = util::array_split_mut!(bytes, + let bytes = array_split_mut!(bytes, deck : [0x3c], name : [0x13], owner : [0x13], @@ -92,8 +94,8 @@ impl Bytes for Deck { ); // Name / Owner - util::write_maybe_null_ascii_string(&self.name, bytes.name).map_err(ToBytesError::Name)?; - util::write_maybe_null_ascii_string(&self.owner, bytes.owner).map_err(ToBytesError::Owner)?; + bytes.name.write_string(&self.name).map_err(ToBytesError::Name)?; + bytes.owner.write_string(&self.owner).map_err(ToBytesError::Owner)?; // Deck for (card_id, card) in self.cards.iter().enumerate() { diff --git a/src/game/util.rs b/src/game/util.rs index 8ab0f82..cc5b8e3 100644 --- a/src/game/util.rs +++ b/src/game/util.rs @@ -6,215 +6,9 @@ //! All items in this module will eventually be deprecated and moved //! somewhere else, but this change might take some time. -pub macro array_split { - ( - $arr:expr, - $( - $name:ident : +// Modules +pub mod array_split; +pub mod null_ascii_string; - $( [$arr_size:expr] )? - $( $val_size:literal )? - - ),* $(,)? - ) => {{ - #![allow( - clippy::used_underscore_binding, - clippy::ptr_offset_with_cast, - clippy::indexing_slicing, - )] - - // Struct holding all fields - struct Fields<'a, T> { - $( - $name: - - $( &'a [T; $arr_size], )? - $( &'a T, #[cfg(invalid)] __field: [u8; $val_size], )? - )* - } - - // Get everything from `array_refs` - let ( - $( - $name - ),* - ) = ::arrayref::array_refs!( - $arr, - $( - $( $arr_size )? - $( $val_size )? - ),* - ); - - // And return the fields - Fields { - $( - $name - $( : &( $name[$val_size - $val_size] ) )? - , - )* - } - }} -} - -pub macro array_split_mut { - ( - $arr:expr, - $( - $name:ident : - - $( [$arr_size:expr] )? - $( $val_size:literal )? - - ),* $(,)? - ) => {{ - #![allow( - clippy::used_underscore_binding, - clippy::ptr_offset_with_cast, - clippy::indexing_slicing, - )] - - // Struct holding all fields - struct Fields<'a, T> { - $( - $name: - - $( &'a mut [T; $arr_size], )? - $( &'a mut T, #[cfg(invalid)] __field: [u8; $val_size], )? - )* - } - - // Get everything from `array_refs` - let ( - $( - $name - ),* - ) = ::arrayref::mut_array_refs!( - $arr, - $( - $( $arr_size )? - $( $val_size )? - ),* - ); - - // And return the fields - Fields { - $( - $name - $( : &mut ( $name[$val_size - $val_size] ) )? - , - )* - } - }} -} - -/// Error type for [`read_null_ascii_string`] -#[derive(PartialEq, Eq, Clone, Copy, Debug, thiserror::Error)] -pub enum ReadNullAsciiStringError { - /// No null was found in the string - #[error("No null was found on the buffer")] - NoNull, - - /// The string was not ascii - #[error("The buffer did not contain valid Ascii")] - NotAscii(#[source] ascii::AsAsciiStrError), -} - -/// Reads a null-terminated ascii string from a buffer. -pub fn read_null_ascii_string(buf: &impl AsRef<[u8]>) -> Result<&ascii::AsciiStr, ReadNullAsciiStringError> { - // Find the first null and trim the buffer until it - let buf = buf.as_ref(); - let buf = match buf.iter().position(|&b| b == 0) { - Some(null_idx) => &buf[0..null_idx], - None => return Err(ReadNullAsciiStringError::NoNull), - }; - - // Then convert it from Ascii - ascii::AsciiStr::from_ascii(buf).map_err(ReadNullAsciiStringError::NotAscii) -} - -/// Error type for [`read_maybe_null_ascii_string`] -#[derive(PartialEq, Eq, Clone, Copy, Debug, thiserror::Error)] -pub enum ReadMaybeNullAsciiStringError { - /// The string was not ascii - #[error("The buffer did not contain valid Ascii")] - NotAscii(#[source] ascii::AsAsciiStrError), -} - -/// Reads a possibly null-terminated ascii string from a buffer. -pub fn read_maybe_null_ascii_string(buf: &impl AsRef<[u8]>) -> Result<&ascii::AsciiStr, ReadMaybeNullAsciiStringError> { - // Find the first null and trim the buffer until it - let buf = buf.as_ref(); - let buf = match buf.iter().position(|&b| b == 0) { - Some(null_idx) => &buf[0..null_idx], - None => buf, - }; - - // Then convert it from Ascii - ascii::AsciiStr::from_ascii(buf).map_err(ReadMaybeNullAsciiStringError::NotAscii) -} - -/// Error type for [`write_null_ascii_string`] -#[derive(PartialEq, Eq, Clone, Copy, Debug, thiserror::Error)] -#[allow(clippy::missing_docs_in_private_items)] -pub enum WriteNullAsciiStringError { - /// The input string was too large - #[error("Input string was too large for buffer. ({}+1 / {})", input_len, buffer_len)] - TooLarge { input_len: usize, buffer_len: usize }, -} - -/// Writes a null-terminated ascii string to a buffer and returns it -pub fn write_null_ascii_string<'a>(input: &ascii::AsciiStr, buf: &'a mut [u8]) -> Result<&'a mut [u8], WriteNullAsciiStringError> { - // If the input string doesn't fit into the buffer (excluding the null byte), return Err - if input.len() >= buf.len() { - return Err(WriteNullAsciiStringError::TooLarge { - input_len: input.len(), - buffer_len: buf.len(), - }); - } - - // Else copy everything over and set the last byte to null - // Note: We leave all other bytes as they are, no need to set them to 0 - buf[0..input.len()].copy_from_slice(input.as_bytes()); - buf[input.len()] = 0; - - // And return Ok with the buffer - Ok(buf) -} - -/// Error type for [`write_maybe_null_ascii_string`] -#[derive(PartialEq, Eq, Clone, Copy, Debug, thiserror::Error)] -pub enum WriteMaybeNullAsciiStringError { - /// The input string was too large - #[error("Input string was too large for buffer. ({} / {})", input_len, buffer_len)] - TooLarge { - /// Length of input string - input_len: usize, - - /// Length of buffer - buffer_len: usize, - }, -} - -/// Writes a possibly null-terminated ascii string to a buffer and returns it -pub fn write_maybe_null_ascii_string<'a>(input: &ascii::AsciiStr, buf: &'a mut [u8]) -> Result<&'a mut [u8], WriteMaybeNullAsciiStringError> { - // If the input string doesn't fit into the buffer, return Err - if input.len() > buf.len() { - return Err(WriteMaybeNullAsciiStringError::TooLarge { - input_len: input.len(), - buffer_len: buf.len(), - }); - } - - // Copy everything over to the slice - // Note: We leave all other bytes as they are, no need to set them to 0 - buf[0..input.len()].copy_from_slice(input.as_bytes()); - - // If there's a character left, write it to null - if let Some(null_byte) = buf.get_mut(input.len()) { - *null_byte = 0; - } - - // And return Ok with the buffer - Ok(buf) -} +// Exports +pub use array_split::{array_split, array_split_mut}; diff --git a/src/game/util/array_split.rs b/src/game/util/array_split.rs new file mode 100644 index 0000000..eb73e3f --- /dev/null +++ b/src/game/util/array_split.rs @@ -0,0 +1,109 @@ +//! Array splitters + +/// Splits an array into various members +pub macro array_split { + ( + $arr:expr, + $( + $name:ident : + + $( [$arr_size:expr] )? + $( $val_size:literal )? + + ),* $(,)? + ) => {{ + #![allow( + clippy::used_underscore_binding, + clippy::ptr_offset_with_cast, + clippy::indexing_slicing, + )] + + // Struct holding all fields + struct Fields<'a, T> { + $( + $name: + + $( &'a [T; $arr_size], )? + $( &'a T, #[cfg(invalid)] __field: [u8; $val_size], )? + )* + } + + // Get everything from `array_refs` + let ( + $( + $name + ),* + ) = ::arrayref::array_refs!( + $arr, + $( + $( $arr_size )? + $( $val_size )? + ),* + ); + + // And return the fields + Fields { + $( + $name + $( : &( $name[$val_size - $val_size] ) )? + , + )* + } + }} +} + +/// Splits an array into various members mutably +#[allow(clippy::module_name_repetitions)] // `_mut` version should be in the same module +pub macro array_split_mut { + ( + $arr:expr, + $( + $name:ident : + + $( [$arr_size:expr] )? + $( $val_size:literal )? + + ),* $(,)? + ) => {{ + #![allow( + clippy::used_underscore_binding, + clippy::ptr_offset_with_cast, + clippy::indexing_slicing, + )] + + // Struct holding all fields + struct Fields<'a, T> { + $( + $name: + + $( &'a mut [T; $arr_size], )? + // Note: This `cfg` is simply done so that `__field` never appears. + // The `__field` serves to identify when this part should be written. + $( &'a mut T, #[cfg(invalid)] __field: [u8; $val_size], )? + )* + } + + // Get everything from `array_refs` + let ( + $( + $name + ),* + ) = ::arrayref::mut_array_refs!( + $arr, + $( + $( $arr_size )? + $( $val_size )? + ),* + ); + + // And return the fields + Fields { + $( + $name + // Note: This serves to turn a `&mut [u8; 1]` into a `&mut u8`. + $( : &mut ( $name[$val_size - $val_size] ) )? + , + )* + } + }} +} diff --git a/src/game/util/null_ascii_string.rs b/src/game/util/null_ascii_string.rs new file mode 100644 index 0000000..ae0379e --- /dev/null +++ b/src/game/util/null_ascii_string.rs @@ -0,0 +1,47 @@ +//! Null-terminated ascii string helpers + +// Modules +pub mod error; + +// Exports +pub use error::{ReadError, WriteError}; + +/// Trait for reading null terminated ascii strings from a buffer +pub trait NullAsciiString { + /// Reads a null terminated ascii string from this buffer and returns it + fn read_string(&self) -> Result<&ascii::AsciiStr, ReadError>; + + /// Writes a null terminated ascii string to this buffer and returns it + fn write_string(&mut self, s: &ascii::AsciiStr) -> Result<&Self, WriteError>; +} + +impl NullAsciiString for [u8] { + fn read_string(&self) -> Result<&ascii::AsciiStr, ReadError> { + // Find the first null and trim the buffer until it + let buf = match self.iter().position(|&b| b == b'\0') { + Some(idx) => &self[0..idx], + None => return Err(ReadError::NoNull), + }; + + // Then convert it from Ascii + ascii::AsciiStr::from_ascii(buf).map_err(ReadError::NotAscii) + } + + fn write_string(&mut self, input: &ascii::AsciiStr) -> Result<&Self, WriteError> { + // If the input string doesn't fit into the buffer (excluding the null byte), return Err + if input.len() >= self.len() { + return Err(WriteError::TooLarge { + input_len: input.len(), + buffer_len: self.len(), + }); + } + + // Else copy everything over and set the last byte to null + // Note: We leave all other bytes as they are, no need to set them to 0 + self[0..input.len()].copy_from_slice(input.as_bytes()); + self[input.len()] = 0; + + // And return Ok with the buffer + Ok(self) + } +} diff --git a/src/game/util/null_ascii_string/error.rs b/src/game/util/null_ascii_string/error.rs new file mode 100644 index 0000000..14c1922 --- /dev/null +++ b/src/game/util/null_ascii_string/error.rs @@ -0,0 +1,22 @@ +//! Errors + +/// Error type for [`read`](super::read) +#[derive(PartialEq, Eq, Clone, Copy, Debug, thiserror::Error)] +pub enum ReadError { + /// No null was found in the string + #[error("No null was found on the buffer")] + NoNull, + + /// The string was not ascii + #[error("The buffer did not contain valid Ascii")] + NotAscii(#[source] ascii::AsAsciiStrError), +} + +/// Error type for [`write`](super::read) +#[derive(PartialEq, Eq, Clone, Copy, Debug, thiserror::Error)] +#[allow(clippy::missing_docs_in_private_items)] +pub enum WriteError { + /// The input string was too large + #[error("Input string was too large for buffer. ({}+1 / {})", input_len, buffer_len)] + TooLarge { input_len: usize, buffer_len: usize }, +} diff --git a/src/lib.rs b/src/lib.rs index dc2b03b..0c86797 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,7 +79,7 @@ // Although we generally try to avoid this, this can happen due to our module organization. // In the future, this lint should be removed globally and only enabled for modules which // actually require the use of it. -#![allow(clippy::module_inception)] +#![allow(clippy::module_inception, clippy::module_name_repetitions)] // False positives: // TODO: Remove them in the future once they are no longer triggered. // We only slice arrays, which are verified at compile time. This