Changed Bytes interface to provide more safety with arrays.

This commit is contained in:
Filipe Rodrigues 2020-04-21 19:13:05 +01:00
parent e426d229f9
commit 888ace2017
12 changed files with 252 additions and 248 deletions

View File

@ -13,6 +13,7 @@ log = "0.4"
byteorder = "1.3"
arrayvec = { version = "0.5", features = ["serde"] }
ascii = { version = "1.0", features = ["serde"] }
arrayref = "0.3"
# Serde
serde = { version = "1.0", features = ["derive"] }

View File

@ -5,18 +5,18 @@ pub trait Bytes
where
Self: Sized
{
/// The size of the structure in bytes
const BUF_BYTE_SIZE: usize;
/// The type of array required by this structure
type ByteArray: Sized;
/// The error type used for the operation
type FromError: std::fmt::Debug + std::error::Error;
/// Reads `bytes` and returns a result with `Self`
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::FromError>;
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError>;
/// The error type used for the operation
type ToError: std::fmt::Debug + std::error::Error;
/// Writes bytes into `bytes` from self
fn to_bytes(&self, bytes: &mut [u8]) -> Result<(), Self::ToError>;
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError>;
}

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 | `moves.circle` | |
//! | 0x39 | 0x1c | [`Move`] | Triangle move | `moves.triangle` | |
//! | 0x55 | 0x1c | [`Move`] | Cross move | `moves.cross` | |
//! | 0x71 | 0x20 | [`SupportCondition`] | First condition | `effects.conditions.first` | |
//! | 0x91 | 0x20 | [`SupportCondition`] | Second condition | `effects.conditions.second` | |
//! | 0xb1 | 0x10 | [`SupportEffect`] | First effect | `support.effects.first` | |
//! | 0xc1 | 0x10 | [`SupportEffect`] | Second effect | `support.effects.second` | |
//! | 0xd1 | 0x10 | [`SupportEffect`] | Third effect | `support.effects.third` | |
//! | 0xe1 | 0x1 | [`CrossMoveEffect`] | Cross move effect | `support.cross_move` | |
//! | 0xe2 | 0x1 | `u8` | Unknown | `support.unknown` | |
//! | 0xe3 | 0x1 | [`ArrowColor`] | Effect arrow color | `effects.arrow_color` | |
//! | 0xe4 | 0x54 | `[[char; 0x15]; 4]` | Effect description lines | `effects.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 | [`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 |
// byteorder
use byteorder::{ByteOrder, LittleEndian};
@ -87,13 +87,13 @@ pub struct Digimon
pub unknown_e2: u8,
/// The digimon's circle move
pub circle_move: Move,
pub move_circle: Move,
/// The digimon's triangle move
pub triangle_move: Move,
pub move_triangle: Move,
/// The digimon's cross move
pub cross_move: Move,
pub move_cross: Move,
/// The digimon's cross move effect, if any
#[serde(default)]
@ -123,12 +123,6 @@ pub struct Digimon
#[derive(derive_more::Display, err_impl::Error)]
pub enum FromBytesError
{
/// The given slice was not big enough
#[display(fmt = "Given slice was too small ({} / {})", "slice_len", "Digimon::BUF_BYTE_SIZE")]
SliceTooSmall {
slice_len: usize,
},
/// Unable to read the digimon name
#[display(fmt = "Unable to read the digimon name")]
Name( #[error(source)] util::ReadNullAsciiStringError ),
@ -203,12 +197,6 @@ pub enum FromBytesError
#[derive(derive_more::Display, err_impl::Error)]
pub enum ToBytesError
{
/// The given slice was not big enough
#[display(fmt = "Given slice was too small ({} / {})", "slice_len", "Digimon::BUF_BYTE_SIZE")]
SliceTooSmall {
slice_len: usize,
},
/// Unable to write a move
#[display(fmt = "Unable to write the {} move", name)]
Move {
@ -220,21 +208,25 @@ pub enum ToBytesError
impl Bytes for Digimon
{
const BUF_BYTE_SIZE : usize = 0x138;
type ByteArray = [u8; 0x138];
type FromError = FromBytesError;
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::FromError>
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError>
{
// Make sure `bytes` is an array big enough, else return Err
// SAFETY: We confirm `bytes` has at least `Self::BUF_BYTE_SIZE` elements.
if bytes.len() < Self::BUF_BYTE_SIZE {
return Err( FromBytesError::SliceTooSmall { slice_len: bytes.len() } );
}
let bytes: &[u8; Self::BUF_BYTE_SIZE] = unsafe {
#[allow(clippy::as_conversions)]
&*( bytes.as_ptr() as *const [u8; Self::BUF_BYTE_SIZE] )
};
// Get all byte arrays we need
util::array_split!(bytes,
0x00..0x1d => _,
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..0x138 => _,
);
// Return the struct after building it
Ok( Self {
@ -245,10 +237,10 @@ impl Bytes for Digimon
unknown_15: LittleEndian::read_u16( &bytes[0x15..0x17] ),
speciality: Speciality::from_bytes( &[(bytes[0x17] & 0xF0) >> 4] )
speciality: Speciality::from_bytes( &( (bytes[0x17] & 0xF0) >> 4 ) )
.map_err(FromBytesError::Speciality)?,
level: Level::from_bytes( &[(bytes[0x17] & 0x0F) >> 0] )
level: Level::from_bytes( &( (bytes[0x17] & 0x0F) >> 0 ) )
.map_err(FromBytesError::Level)?,
dp_cost : bytes[0x18],
@ -258,52 +250,52 @@ impl Bytes for Digimon
hp: LittleEndian::read_u16( &bytes[0x1b..0x1d] ),
// 0x1d - 0x71
circle_move: Move::from_bytes( &bytes[0x1d..0x39] )
move_circle: Move::from_bytes( move_circle )
.map_err(FromBytesError::MoveCircle)?,
triangle_move: Move::from_bytes( &bytes[0x39..0x55] )
move_triangle: Move::from_bytes( move_triangle )
.map_err(FromBytesError::MoveTriangle)?,
cross_move: Move::from_bytes( &bytes[0x55..0x71] )
move_cross: Move::from_bytes( move_cross )
.map_err(FromBytesError::MoveCross)?,
// 0x71 - 0x138
effect_conditions: [
(bytes[0x73] != 0)
.then(|| SupportCondition::from_bytes( &bytes[0x71..0x91] ) )
.then(|| SupportCondition::from_bytes( condition_first ) )
.transpose()
.map_err(FromBytesError::EffectConditionFirst)?,
(bytes[0x93] != 0)
.then(|| SupportCondition::from_bytes( &bytes[0x91..0xb1] ) )
.then(|| SupportCondition::from_bytes( condition_second ) )
.transpose()
.map_err(FromBytesError::EffectConditionSecond)?,
],
effects: [
(bytes[0xb1] != 0)
.then(|| SupportEffect::from_bytes( &bytes[0xb1..0xc1] ) )
.then(|| SupportEffect::from_bytes( effect_first ) )
.transpose()
.map_err(FromBytesError::EffectFirst)?,
(bytes[0xc1] != 0)
.then(|| SupportEffect::from_bytes( &bytes[0xc1..0xd1] ) )
.then(|| SupportEffect::from_bytes( effect_second ) )
.transpose()
.map_err(FromBytesError::EffectSecond)?,
(bytes[0xd1] != 0)
.then(|| SupportEffect::from_bytes( &bytes[0xd1..0xe1] ) )
.then(|| SupportEffect::from_bytes( effect_third ) )
.transpose()
.map_err(FromBytesError::EffectThird)?,
],
cross_move_effect: (bytes[0xe1] != 0)
.then(|| CrossMoveEffect::from_bytes( &bytes[0xe1..0xe2] ) )
.then(|| CrossMoveEffect::from_bytes( &bytes[0xe1] ) )
.transpose()
.map_err(FromBytesError::CrossMoveEffect)?,
unknown_e2: bytes[0xe2],
effect_arrow_color: (bytes[0xe3] != 0)
.then(|| ArrowColor::from_bytes( &bytes[0xe3..0xe4] ) )
.then(|| ArrowColor::from_bytes( &bytes[0xe3] ) )
.transpose()
.map_err(FromBytesError::ArrowColor)?,
@ -325,99 +317,106 @@ impl Bytes for Digimon
}
type ToError = ToBytesError;
fn to_bytes(&self, bytes: &mut [u8]) -> Result<(), Self::ToError>
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError>
{
// Make sure `bytes` is an array big enough, else return Err
// SAFETY: We confirm `bytes` has at least `Self::BUF_BYTE_SIZE` elements.
if bytes.len() < Self::BUF_BYTE_SIZE {
return Err( ToBytesError::SliceTooSmall { slice_len: bytes.len() } );
}
let bytes: &mut [u8; Self::BUF_BYTE_SIZE] = unsafe {
#[allow(clippy::as_conversions)]
&mut *( bytes.as_mut_ptr() as *mut [u8; Self::BUF_BYTE_SIZE] )
};
// Get all byte arrays we need
util::array_split_mut!(bytes,
0x00..0x15 => name,
0x15..0x17 => unknown_15,
0x17..0x18 => speciality_level,
0x18..0x19 => dp_cost,
0x19..0x1a => dp_give,
0x1a..0x1b => 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
bytes[0x0..0x15].copy_from_slice(
// name
name.copy_from_slice(
// Note: `self.name` is at most [char; 20], this cannot fail
util::write_null_ascii_string(self.name.as_ref().as_ref(), &mut [0u8; 21])
.expect("Name was too large for output buffer")
);
// Basic
//--------------------------------------------------------------------------------------------------
// unknown_15
LittleEndian::write_u16(unknown_15, self.unknown_15);
// Speciality / Level
{
let (mut speciality_byte, mut level_byte) = ( 0u8, 0u8 );
// Note: Buffers have 1 byte, so this can't fail
self.speciality.to_bytes(&mut speciality_byte)?;
self.level.to_bytes(&mut level_byte)?;
// Unknown 1
LittleEndian::write_u16(&mut bytes[0x15..0x17], self.unknown_15);
// Speciality / Level
{
let (mut speciality_byte, mut level_byte) = ( [0u8], [0u8] );
// Note: Buffers have 1 byte, so this can't fail
self.speciality.to_bytes(&mut speciality_byte)
.expect("Could not convert speciality to bytes");
self.level.to_bytes(&mut level_byte)
.expect("Could not convert level to bytes");
// Merge them
bytes[0x17] = (speciality_byte[0] << 4) | level_byte[0];
}
// DP / +P
bytes[0x18] = self.dp_cost;
bytes[0x19] = self.dp_give;
// Unknown
bytes[0x1a] = self.unknown_1a;
// Health
LittleEndian::write_u16(&mut bytes[0x1b..0x1d], self.hp);
//--------------------------------------------------------------------------------------------------
// Merge them
speciality_level[0] = (speciality_byte << 4) | level_byte;
}
// DP / +P
dp_cost[0] = self.dp_cost;
dp_give[0] = self.dp_give;
// Unknown
unknown_1a[0] = self.unknown_1a;
// Health
LittleEndian::write_u16(hp, self.hp);
// Moves
self. circle_move.to_bytes(&mut bytes[0x1d..0x39]).map_err(|err| ToBytesError::Move{ name: "circle" , err })?;
self.triangle_move.to_bytes(&mut bytes[0x39..0x55]).map_err(|err| ToBytesError::Move{ name: "triangle", err })?;
self. cross_move.to_bytes(&mut bytes[0x55..0x71]).map_err(|err| ToBytesError::Move{ name: "cross" , err })?;
// Support
self. move_circle.to_bytes( move_circle ).map_err(|err| ToBytesError::Move{ name: "circle" , err })?;
self.move_triangle.to_bytes( move_triangle ).map_err(|err| ToBytesError::Move{ name: "triangle", err })?;
self. move_cross.to_bytes( move_cross ).map_err(|err| ToBytesError::Move{ name: "cross" , err })?;
// 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.
//--------------------------------------------------------------------------------------------------
// Support conditions
if let Some(support_condition) = &self.effect_conditions[0] { support_condition.to_bytes(&mut bytes[0x71..0x91])?; }
if let Some(support_condition) = &self.effect_conditions[1] { support_condition.to_bytes(&mut bytes[0x91..0xb1])?; }
// Support effects
if let Some(support_effect) = &self.effects[0] { support_effect.to_bytes(&mut bytes[0xb1..0xc1])?; }
if let Some(support_effect) = &self.effects[1] { support_effect.to_bytes(&mut bytes[0xc1..0xd1])?; }
if let Some(support_effect) = &self.effects[2] { support_effect.to_bytes(&mut bytes[0xd1..0xe1])?; }
// Cross move
if let Some(cross_move) = self.cross_move_effect { cross_move.to_bytes(&mut bytes[0xe1..0xe2])
.expect("Unable to convert cross move effect to bytes")
};
// Unknown
bytes[0xe2] = self.unknown_e2;
// Support arrow color
if let Some(arrow_color) = self.effect_arrow_color { arrow_color.to_bytes( &mut bytes[0xe3..0xe4] )
.expect("Unable to convert arrow color to bytes");
}
// Write the support effects
for (index, line) in self.effect_description.iter().enumerate()
{
bytes[0x0e4 + (0x15 * index) .. 0x0f9 + (0x15 * index)].copy_from_slice(
// Note: `line` is at most [char; 20], this cannot fail
util::write_null_ascii_string(line.as_ref().as_ref(), &mut [0u8; 21])
.expect("Effect description was too large for output buffer")
);
}
//--------------------------------------------------------------------------------------------------
if let Some(support_condition) = &self.effect_conditions[0] { support_condition.to_bytes( condition_first )?; }
if let Some(support_condition) = &self.effect_conditions[1] { support_condition.to_bytes( condition_second )?; }
// Support effects
if let Some(support_effect) = &self.effects[0] { support_effect.to_bytes( effect_first )?; }
if let Some(support_effect) = &self.effects[1] { support_effect.to_bytes( effect_second )?; }
if let Some(support_effect) = &self.effects[2] { support_effect.to_bytes( effect_third )?; }
// Cross move
if let Some(move_cross) = self.cross_move_effect { move_cross.to_bytes( &mut cross_move_effect[0] )
.expect("Unable to convert cross move effect to bytes")
};
// Unknown
unknown_e2[0] = self.unknown_e2;
// Support arrow color
if let Some(arrow_color) = self.effect_arrow_color { arrow_color.to_bytes( &mut effect_arrow_color[1] )
.expect("Unable to convert arrow color to bytes");
}
// effect_description
// Note: Each string is at most [char; 20], this cannot fail
effect_description[0x00..0x15].copy_from_slice( util::write_null_ascii_string(self.effect_description[0].as_ref().as_ref(), &mut [0u8; 21])
.expect("First line of effect description was too large for output buffer")
);
effect_description[0x15..0x2a].copy_from_slice( util::write_null_ascii_string(self.effect_description[1].as_ref().as_ref(), &mut [0u8; 21])
.expect("Second line of effect description was too large for output buffer")
);
effect_description[0x2a..0x3f].copy_from_slice( util::write_null_ascii_string(self.effect_description[2].as_ref().as_ref(), &mut [0u8; 21])
.expect("Third line of effect description was too large for output buffer")
);
effect_description[0x3f..0x54].copy_from_slice( util::write_null_ascii_string(self.effect_description[3].as_ref().as_ref(), &mut [0u8; 21])
.expect("Fourth line of effect description was too large for output buffer")
);
// Return Ok
Ok(())

View File

@ -149,11 +149,10 @@ use serde::Deserialize;
// Bytes
impl Bytes for Digivolve
{
const BUF_BYTE_SIZE : usize = 0x6c;
type ByteArray = [u8; 0x6c];
type FromError = FromBytesError;
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::FromError>
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError>
{
Ok( Self {
basic: Basic {
@ -176,8 +175,7 @@ use serde::Deserialize;
}
type ToError = ToBytesError;
fn to_bytes(&self, bytes: &mut [u8]) -> Result<(), Self::ToError>
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError>
{
// Basic
//--------------------------------------------------------------------------------------------------

View File

@ -10,6 +10,9 @@
use crate::game::card::property::ArrowColor;
//--------------------------------------------------------------------------------------------------
// Array-ref
use arrayref::{array_ref, array_mut_ref};
// byteorder
use byteorder::ByteOrder;
use byteorder::LittleEndian;
@ -144,10 +147,10 @@ use serde::Deserialize;
// Bytes
impl Bytes for Item
{
const BUF_BYTE_SIZE : usize = 0xde;
type ByteArray = [u8; 0xde];
type FromError = FromBytesError;
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::FromError>
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError>
{
// Assert some fields are 0
//assert_eq!(bytes[0x1a], 0);
@ -169,30 +172,30 @@ use serde::Deserialize;
],
arrow_color: if bytes[0x89] != 0 {
Some( ArrowColor::from_bytes( &bytes[0x89..0x8a] ).map_err(FromBytesError::EffectArrowColor)? )
Some( ArrowColor::from_bytes( &bytes[0x89] ).map_err(FromBytesError::EffectArrowColor)? )
} else { None },
conditions: SupportConditions {
first: if bytes[0x19] != 0 { Some(
SupportCondition::from_bytes( &bytes[0x19..0x39] ).map_err(|err| FromBytesError::SupportCondition{ rank: "1st", item_pos: 0x19, err })?
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( &bytes[0x39..0x59] ).map_err(|err| FromBytesError::SupportCondition{ rank: "2nd", item_pos: 0x39, err })?
SupportCondition::from_bytes( array_ref!(bytes, 0x39, 0x20) ).map_err(|err| FromBytesError::SupportCondition{ rank: "2nd", item_pos: 0x39, err })?
)} else { None },
},
effects: SupportEffects {
first: if bytes[0x59] != 0 { Some(
SupportEffect::from_bytes( &bytes[0x59..0x69] ).map_err(|err| FromBytesError::SupportEffect{ rank: "1st", err })?
SupportEffect::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( &bytes[0x69..0x79] ).map_err(|err| FromBytesError::SupportEffect{ rank: "2nd", err })?
SupportEffect::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( &bytes[0x79..0x89] ).map_err(|err| FromBytesError::SupportEffect{ rank: "3rd", err })?
SupportEffect::from_bytes( array_ref!(bytes, 0x79, 0x10) ).map_err(|err| FromBytesError::SupportEffect{ rank: "3rd", err })?
)} else { None },
},
},
@ -200,7 +203,7 @@ use serde::Deserialize;
}
type ToError = ToBytesError;
fn to_bytes(&self, bytes: &mut [u8]) -> Result<(), Self::ToError>
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError>
{
// Basic
//--------------------------------------------------------------------------------------------------
@ -243,17 +246,17 @@ use serde::Deserialize;
});
}
if let Some(arrow_color) = self.effects.arrow_color { arrow_color.to_bytes( &mut bytes[0x89..0x8a] ).expect("Unable to convert arrow color to bytes"); }
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(&mut bytes[0x19..0x39])?; }
if let Some(support_condition) = &self.effects.conditions.second { support_condition.to_bytes(&mut bytes[0x39..0x59])?; }
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) )?; }
// If they are None, 0 is a valid value for the effects
if let Some(support_effect) = &self.effects.effects.first { support_effect.to_bytes(&mut bytes[0x59..0x69])?; }
if let Some(support_effect) = &self.effects.effects.second { support_effect.to_bytes(&mut bytes[0x69..0x79])?; }
if let Some(support_effect) = &self.effects.effects.third { support_effect.to_bytes(&mut bytes[0x79..0x89])?; }
if let Some(support_effect) = &self.effects.effects.first { support_effect.to_bytes( array_mut_ref!(bytes, 0x59, 0x10) )?; }
if let Some(support_effect) = &self.effects.effects.second { support_effect.to_bytes( array_mut_ref!(bytes, 0x69, 0x10) )?; }
if let Some(support_effect) = &self.effects.effects.third { support_effect.to_bytes( array_mut_ref!(bytes, 0x79, 0x10) )?; }
//--------------------------------------------------------------------------------------------------
// Return the bytes

View File

@ -73,61 +73,31 @@ macro_rules! generate_enum_property_mod
#[derive(::derive_more::Display, ::err_impl::Error)]
$mod_vis enum FromBytesError {
/// The given slice was not big enough
#[display(fmt = "Given slice was too small ({} / 1)", "slice_len")]
SliceTooSmall {
slice_len: usize,
},
/// Unknown value
#[display(fmt = $error_unknown_value_display, "byte")]
UnknownValue {
byte: u8,
}
}
/// Error type for [`Bytes::to_bytes`]
#[derive(Debug)]
#[derive(::derive_more::Display, ::err_impl::Error)]
$mod_vis enum ToBytesError {
/// The given slice was not big enough
#[display(fmt = "Given slice was too small ({} / 1)", "slice_len")]
SliceTooSmall {
slice_len: usize,
},
}
// Bytes
impl $crate::game::Bytes for $enum_name
{
const BUF_BYTE_SIZE: usize = 1;
type ByteArray = u8;
type FromError = FromBytesError;
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::FromError>
fn from_bytes(byte: &Self::ByteArray) -> Result<Self, Self::FromError>
{
use ::std::convert::TryInto;
let bytes: &[u8; 1] = bytes
.try_into()
.map_err(|_| Self::FromError::SliceTooSmall { slice_len: bytes.len() })?;
match bytes[0] {
match byte {
$( $enum_variant_value => Ok( <$enum_name>::$enum_variant_name ), )*
_ => Err( Self::FromError::UnknownValue{ byte: bytes[0] } ),
_ => Err( Self::FromError::UnknownValue{ byte: *byte } ),
}
}
type ToError = ToBytesError;
fn to_bytes(&self, bytes: &mut [u8]) -> Result<(), Self::ToError>
type ToError = !;
fn to_bytes(&self, byte: &mut Self::ByteArray) -> Result<(), Self::ToError>
{
use ::std::convert::TryInto;
let slice_len = bytes.len();
let bytes: &mut [u8; 1] = bytes
.try_into()
.map_err(|_| Self::ToError::SliceTooSmall { slice_len })?;
bytes[0] = match self {
*byte = match self {
$( <$enum_name>::$enum_variant_name => $enum_variant_value, )*
};
@ -197,15 +167,15 @@ macro_rules! generate_enum_property_mod
{
/// Returns the byte size of the corresponding card
#[must_use]
pub fn card_byte_size(self) -> usize
pub fn byte_size(self) -> usize
{
use crate::game::Bytes;
match self
{
Self::Digimon => <crate::game::card::Digimon as Bytes>::BUF_BYTE_SIZE,
Self::Item => <crate::game::card::Item as Bytes>::BUF_BYTE_SIZE,
Self::Digivolve => <crate::game::card::Digivolve as Bytes>::BUF_BYTE_SIZE,
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 >(),
}
}
}

View File

@ -50,10 +50,10 @@ use byteorder::LittleEndian;
// Bytes
impl Bytes for Move
{
const BUF_BYTE_SIZE : usize = 0x1c;
type ByteArray = [u8; 0x1c];
type FromError = FromBytesError;
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::FromError>
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError>
{
// And return the move
Ok( Self {
@ -64,7 +64,7 @@ use byteorder::LittleEndian;
}
type ToError = ToBytesError;
fn to_bytes(&self, bytes: &mut [u8]) -> Result<(), Self::ToError>
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError>
{
// Write the name
bytes[0x6..0x1c].copy_from_slice( &{

View File

@ -72,13 +72,13 @@ use byteorder::{ByteOrder, LittleEndian};
// Bytes
impl Bytes for SupportCondition
{
const BUF_BYTE_SIZE : usize = 0x20;
type ByteArray = [u8; 0x20];
type FromError = FromBytesError;
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::FromError>
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError>
{
// Get the condition
let cond = DigimonProperty::from_bytes( &bytes[0x2..0x3] ).map_err(FromBytesError::Condition)?;
let cond = DigimonProperty::from_bytes( &bytes[0x2] ).map_err(FromBytesError::Condition)?;
// And return the move
Ok( Self {
@ -86,12 +86,12 @@ use byteorder::{ByteOrder, LittleEndian};
cond,
type_arg: if bytes[0x8] != 0 { Some(
DigimonProperty::from_bytes( &[bytes[0x8]] ).map_err(FromBytesError::PropertyArgument)?
DigimonProperty::from_bytes( &bytes[0x8] ).map_err(FromBytesError::PropertyArgument)?
)} else { None },
num_arg: LittleEndian::read_u16( &bytes[0x14..0x16] ),
operation: SupportConditionOperation::from_bytes( &bytes[0x1a..0x1b] ).map_err(FromBytesError::Operation)?,
operation: SupportConditionOperation::from_bytes( &bytes[0x1a] ).map_err(FromBytesError::Operation)?,
unknown: [
bytes[0x3], bytes[0x4], bytes[0x5], bytes[0x6], bytes[0x7],
@ -103,7 +103,7 @@ use byteorder::{ByteOrder, LittleEndian};
}
type ToError = !;
fn to_bytes(&self, bytes: &mut [u8]) -> Result<(), Self::ToError>
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError>
{
// 0x0 - Misfire
bytes[0x0] = if self.misfire { 1 } else { 0 };
@ -113,7 +113,7 @@ use byteorder::{ByteOrder, LittleEndian};
// 0x2 - Condition
#[allow(clippy::diverging_sub_expression)] { // False positive
self.cond.to_bytes(&mut bytes[0x2..0x3])
self.cond.to_bytes(&mut bytes[0x2])
.expect("Unable to convert condition to bytes");
}
@ -122,20 +122,20 @@ use byteorder::{ByteOrder, LittleEndian};
// 0x8 - Type arg / 0 if None
if let Some(type_arg) = self.type_arg {
type_arg.to_bytes(&mut bytes[0x8..0x9])
type_arg.to_bytes(&mut bytes[0x8])
.expect("Unable to convert type argument to bytes")
}
else { bytes[0x8] = 0; }
// 0x9..0x14 - Unknown[5..16]
bytes[0x9..0x14].copy_from_slice( &self.unknown[5..16] );
// 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
#[allow(clippy::diverging_sub_expression)] { // False positive
self.operation.to_bytes(&mut bytes[0x1a..0x1b])
self.operation.to_bytes(&mut bytes[0x1a])
.expect("Unable to convert operation to bytes");
}

View File

@ -197,12 +197,11 @@ use crate::{
//--------------------------------------------------------------------------------------------------
impl Bytes for SupportEffect
{
const BUF_BYTE_SIZE : usize = 0x10;
type ByteArray = [u8; 0x10];
type FromError = FromBytesError;
/// `bytes` should include the `exists` byte
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::FromError>
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError>
{
// Assert that we do exist
assert_ne!(bytes[0x0], 0);
@ -212,15 +211,15 @@ use crate::{
// The properties
let a = if bytes[0x2] != 0 {
Some( DigimonProperty::from_bytes( &bytes[0x2..0x3] ) .map_err(|err| FromBytesError::PropertyArgument{ rank: "1st", err })? )
Some( DigimonProperty::from_bytes( &bytes[0x2] ) .map_err(|err| FromBytesError::PropertyArgument{ rank: "1st", err })? )
} else { None };
let b = if bytes[0x4] != 0 {
Some( DigimonProperty::from_bytes( &bytes[0x4..0x5] ) .map_err(|err| FromBytesError::PropertyArgument{ rank: "2nd", err })? )
Some( DigimonProperty::from_bytes( &bytes[0x4] ) .map_err(|err| FromBytesError::PropertyArgument{ rank: "2nd", err })? )
} else { None };
let c = if bytes[0x6] != 0 {
Some( DigimonProperty::from_bytes( &bytes[0x6..0x7] ) .map_err(|err| FromBytesError::PropertyArgument{ rank: "3rd", err })? )
Some( DigimonProperty::from_bytes( &bytes[0x6] ) .map_err(|err| FromBytesError::PropertyArgument{ rank: "3rd", err })? )
} else { None };
// The numbers
@ -228,7 +227,7 @@ use crate::{
let y = LittleEndian::read_u16( &bytes[0xc..0xe] );
// The operation
let op = SupportEffectOperation::from_bytes( &bytes[0xf..0x10] ) .map_err(FromBytesError::Operation)?;
let op = SupportEffectOperation::from_bytes( &bytes[0xf] ) .map_err(FromBytesError::Operation)?;
// Check what the effect type is
match effect_type_byte
@ -236,15 +235,15 @@ use crate::{
0..=13 => {
Ok( Self::ChangeProperty {
// Note: unwrapping is fine here because we know that `effect_type_byte+1` is between 1 and 14 inclusive
property: DigimonProperty::from_bytes( &[ effect_type_byte+1 ] )
property: DigimonProperty::from_bytes( &(effect_type_byte+1) )
.expect("Unable to get digimon property from bytes"),
a, b, c, x, y, op,
})
},
// Take lower byte from `x` for these
16 => { Ok( Self::UseAttack{ player: PlayerType::Player , attack: AttackType::from_bytes( &[x.to_le_bytes()[0]] ) .map_err(FromBytesError::AttackType)? } ) },
17 => { Ok( Self::UseAttack{ player: PlayerType::Opponent, attack: AttackType::from_bytes( &[x.to_le_bytes()[0]] ) .map_err(FromBytesError::AttackType)? } ) },
16 => { Ok( Self::UseAttack{ player: PlayerType::Player , attack: AttackType::from_bytes( &x.to_le_bytes()[0] ) .map_err(FromBytesError::AttackType)? } ) },
17 => { Ok( Self::UseAttack{ player: PlayerType::Opponent, attack: AttackType::from_bytes( &x.to_le_bytes()[0] ) .map_err(FromBytesError::AttackType)? } ) },
25 => { Ok( Self::SetTempSlot{ a, b, c, op } ) },
@ -290,7 +289,7 @@ use crate::{
}
type ToError = !;
fn to_bytes(&self, _bytes: &mut [u8]) -> Result<(), Self::ToError>
fn to_bytes(&self, _bytes: &mut Self::ByteArray) -> Result<(), Self::ToError>
{
// Match which effect we are
todo!()

View File

@ -111,9 +111,9 @@ pub enum DeserializeError {
"digimon_cards",
"item_cards",
"digivolve_cards",
" digimon_cards * (0x3 + Digimon ::BUF_BYTE_SIZE + 0x1) +
item_cards * (0x3 + Item ::BUF_BYTE_SIZE + 0x1) +
digivolve_cards * (0x3 + Digivolve::BUF_BYTE_SIZE + 0x1)",
" digimon_cards * (0x3 + CardType::Digimon .byte_size() + 0x1) +
item_cards * (0x3 + CardType::Item .byte_size() + 0x1) +
digivolve_cards * (0x3 + CardType::Digivolve.byte_size() + 0x1)",
Table::MAX_BYTE_SIZE
)]
TooManyCards {
@ -175,9 +175,9 @@ pub enum SerializeError {
"digimon_cards",
"item_cards",
"digivolve_cards",
" digimon_cards * (0x3 + Digimon ::BUF_BYTE_SIZE + 0x1) +
item_cards * (0x3 + Item ::BUF_BYTE_SIZE + 0x1) +
digivolve_cards * (0x3 + Digivolve::BUF_BYTE_SIZE + 0x1)",
" digimon_cards * (0x3 + CardType::Digimon .byte_size() + 0x1) +
item_cards * (0x3 + CardType::Item .byte_size() + 0x1) +
digivolve_cards * (0x3 + CardType::Digivolve.byte_size() + 0x1)",
Table::MAX_BYTE_SIZE
)]
TooManyCards {
@ -240,10 +240,12 @@ impl Table {
// And calculate the number of cards
let cards_len = digimon_cards + item_cards + digivolve_cards;
// If there are too many cards, return Err
let table_size = digimon_cards * (0x3 + Digimon ::BUF_BYTE_SIZE + 0x1) +
item_cards * (0x3 + Item ::BUF_BYTE_SIZE + 0x1) +
digivolve_cards * (0x3 + Digivolve::BUF_BYTE_SIZE + 0x1);
let table_size = digimon_cards * (0x3 + CardType::Digimon .byte_size() + 0x1) +
item_cards * (0x3 + CardType::Item .byte_size() + 0x1) +
digivolve_cards * (0x3 + CardType::Digivolve.byte_size() + 0x1);
log::debug!("[Table Header] {} total bytes of cards", table_size);
if table_size > Self::MAX_BYTE_SIZE { return Err( DeserializeError::TooManyCards {
digimon_cards,
@ -266,7 +268,7 @@ impl Table {
// Read the header
let card_id = LittleEndian::read_u16( &card_header_bytes[0x0..0x2] );
let card_type = CardType::from_bytes( &card_header_bytes[0x2..0x3] )
let card_type = CardType::from_bytes( &card_header_bytes[0x2] )
.map_err(|err| DeserializeError::UnknownCardType{ id: cur_id, err } )?;
log::debug!("[Card Header] Found {} with id {}", card_type, card_id);
@ -279,7 +281,7 @@ impl Table {
match card_type
{
CardType::Digimon => {
let mut digimon_bytes = [0; Digimon::BUF_BYTE_SIZE];
let mut digimon_bytes = [0; std::mem::size_of::< <Digimon as Bytes>::ByteArray>()];
file.read_exact(&mut digimon_bytes)
.expect("Unable to read digimon bytes");
let digimon = Digimon::from_bytes(&digimon_bytes)
@ -287,7 +289,7 @@ impl Table {
digimons.push(digimon);
},
CardType::Item => {
let mut item_bytes = [0; Item::BUF_BYTE_SIZE];
let mut item_bytes = [0; std::mem::size_of::< <Item as Bytes>::ByteArray>()];
file.read_exact(&mut item_bytes)
.expect("Unable to read item bytes");
let item = Item::from_bytes(&item_bytes)
@ -295,7 +297,7 @@ impl Table {
items.push(item);
},
CardType::Digivolve => {
let mut digivolve_bytes = [0; Digivolve::BUF_BYTE_SIZE];
let mut digivolve_bytes = [0; std::mem::size_of::< <Digivolve as Bytes>::ByteArray>()];
file.read_exact(&mut digivolve_bytes)
.expect("Unable to read digivolve bytes");
let digivolve = Digivolve::from_bytes(&digivolve_bytes)
@ -323,9 +325,9 @@ impl Table {
pub fn serialize<R: Read + Write + Seek>(&self, file: &mut GameFile<R>) -> Result<(), SerializeError> {
// Get the final table size
let table_size = self.digimons .len() * (0x3 + Digimon ::BUF_BYTE_SIZE + 0x1) +
self.items .len() * (0x3 + Item ::BUF_BYTE_SIZE + 0x1) +
self.digivolves.len() * (0x3 + Digivolve::BUF_BYTE_SIZE + 0x1);
let table_size = self. digimons.len() * (0x3 + CardType::Digimon .byte_size() + 0x1) +
self. items.len() * (0x3 + CardType::Item .byte_size() + 0x1) +
self.digivolves.len() * (0x3 + CardType::Digivolve.byte_size() + 0x1);
// If the total table size is bigger than the max, return Err
if table_size > Self::MAX_BYTE_SIZE { return Err( SerializeError::TooManyCards {

View File

@ -6,7 +6,39 @@
//! All items in this module will eventually be depracated and moved
//! somewhere else, but this change might take some time.
// Macros
/// Splits an array into it's various element arrays
pub macro array_split {
(
$arr:ident,
$( $start:literal..$end:literal => $name:tt),* $(,)?
) => {
let (
$(
$name,
)*
) = ::arrayref::array_refs!(
$arr,
$( $end - $start ),*
);
}
}
/// Splits an array mutable into it's various element arrays
pub macro array_split_mut {
(
$arr:ident,
$( $start:literal..$end:literal => $name:tt),* $(,)?
) => {
let (
$(
$name,
)*
) = ::arrayref::mut_array_refs!(
$arr,
$( $end - $start ),*
);
}
}
// Types
//--------------------------------------------------------------------------------------------------

View File

@ -28,6 +28,7 @@
trait_alias,
unsized_locals,
bool_to_option,
decl_macro,
)]
// Lints
@ -47,6 +48,7 @@
clippy::multiple_inherent_impl, // We prefer to separate certain methods by type and insert error types in between methods
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
// TODO: Deal with casts eventually
clippy::cast_possible_wrap,
@ -56,9 +58,7 @@
// TODO: Remove these once all modules are ported
clippy::missing_docs_in_private_items,
clippy::as_conversions,
clippy::panic,
clippy::indexing_slicing,
clippy::unseparated_literal_suffix,
clippy::integer_arithmetic,
clippy::unreachable,
clippy::todo,