Reworked util module.

Changed all functions that read/write ascii strings to a trait.
This commit is contained in:
2020-07-14 18:48:22 +01:00
parent f5ebcd1cf5
commit 36c429372e
13 changed files with 361 additions and 316 deletions

View File

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

View File

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

View File

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

View File

@@ -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<Effect> {
// `bytes` should include the `exists` byte
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError> {
let bytes = util::array_split!(bytes,
let bytes = array_split!(bytes,
exists : 0x1,
effect : [0xf],
);
@@ -479,7 +480,7 @@ impl Bytes for Option<Effect> {
}
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],
);

View File

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

View File

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

View File

@@ -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()],

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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