Refactored game::item.

This commit is contained in:
2020-05-01 07:45:02 +01:00
parent 757fd2ed1e
commit c10ea99f7c
5 changed files with 286 additions and 321 deletions

View File

@@ -15,7 +15,7 @@
//#![allow(clippy::missing_docs_in_private_items)] // A lot of our private items are simple digimon types, so they don't need documentation
// Modules
#[macro_use] pub mod util;
mod util;
pub mod bytes;
pub mod card;

View File

@@ -1,6 +1,6 @@
//! A digimon card
//!
//! This module stores the [`Digimon`] struct, which describes a digimon card.
//! This module contains the [`Digimon`] struct, which describes a digimon card.
//!
//! # Layout
//! The digimon card has a size of `0x138` bytes, and it's layout is the following:
@@ -8,7 +8,7 @@
//! | 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 |
//! | 0x15 | 0x2 | `u16` | Unknown | `unknown_15` | |
//! | 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` | |
@@ -81,11 +81,6 @@ pub struct Digimon
/// `+P` in the game.
pub dp_give: u8,
// Unknown fields
pub unknown_1a: u8,
pub unknown_15: u16,
pub unknown_e2: u8,
/// The digimon's circle move
pub move_circle: Move,
@@ -116,6 +111,11 @@ pub struct Digimon
/// The effects themselves
#[serde(default)]
pub effects: [Option<Effect>; 3],
// Unknown fields
pub unknown_1a: u8,
pub unknown_15: u16,
pub unknown_e2: u8,
}
/// Error type for [`Bytes::from_bytes`]
@@ -249,7 +249,7 @@ impl Bytes for Digimon
type FromError = FromBytesError;
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError>
{
// Get all byte arrays we need
// Split bytes
let bytes = util::array_split!(bytes,
name : [0x15],
unknown_15 : [0x2],
@@ -277,13 +277,10 @@ impl Bytes for Digimon
// Return the struct after building it
Ok( Self {
// 0x0 - 0x1d
name: util::read_null_ascii_string(bytes.name)
.map_err(FromBytesError::Name)?
.chars().collect(),
unknown_15: LittleEndian::read_u16(bytes.unknown_15),
speciality: Speciality::from_bytes( &( (bytes.speciality_level & 0xF0) >> 4 ) )
.map_err(FromBytesError::Speciality)?,
@@ -292,11 +289,10 @@ impl Bytes for Digimon
dp_cost : *bytes.dp_cost,
dp_give : *bytes.dp_give,
unknown_1a: *bytes.unknown_1a,
hp: LittleEndian::read_u16( bytes.hp ),
// 0x1d - 0x71
// Moves
move_circle: Move::from_bytes( bytes.move_circle )
.map_err(FromBytesError::MoveCircle)?,
move_triangle: Move::from_bytes( bytes.move_triangle )
@@ -304,7 +300,7 @@ impl Bytes for Digimon
move_cross: Move::from_bytes( bytes.move_cross )
.map_err(FromBytesError::MoveCross)?,
// 0x71 - 0x138
// Effects
effect_conditions: [
Option::<EffectCondition>::from_bytes( bytes.condition_first )
.map_err(FromBytesError::EffectConditionFirst)?,
@@ -327,8 +323,6 @@ impl Bytes for Digimon
cross_move_effect: Option::<CrossMoveEffect>::from_bytes(bytes.cross_move_effect)
.map_err(FromBytesError::CrossMoveEffect)?,
unknown_e2: *bytes.unknown_e2,
effect_arrow_color: Option::<ArrowColor>::from_bytes(bytes.effect_arrow_color)
.map_err(FromBytesError::ArrowColor)?,
@@ -346,13 +340,18 @@ impl Bytes for Digimon
.map_err(FromBytesError::EffectDescriptionFourth)?
.chars().collect(),
],
// Unknown
unknown_15: LittleEndian::read_u16(bytes.unknown_15),
unknown_1a: *bytes.unknown_1a,
unknown_e2: *bytes.unknown_e2,
})
}
type ToError = ToBytesError;
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError>
{
// Get all byte arrays we need
// Split bytes
let bytes = util::array_split_mut!(bytes,
name : [0x15],
unknown_15 : [0x2],
@@ -378,13 +377,10 @@ impl Bytes for Digimon
effect_description_3: [0x15],
);
// name
// Name
util::write_null_ascii_string(self.name.as_ref(), bytes.name)
.map_err(ToBytesError::Name)?;
// unknown_15
LittleEndian::write_u16(bytes.unknown_15, self.unknown_15);
// Speciality / Level
{
let (mut speciality_byte, mut level_byte) = ( 0u8, 0u8 );
@@ -401,9 +397,6 @@ impl Bytes for Digimon
*bytes.dp_cost = self.dp_cost;
*bytes.dp_give = self.dp_give;
// Unknown
*bytes.unknown_1a = self.unknown_1a;
// Health
LittleEndian::write_u16(bytes.hp, self.hp);
@@ -424,9 +417,6 @@ impl Bytes for Digimon
// Cross move
Option::<CrossMoveEffect>::to_bytes(&self.cross_move_effect, bytes.cross_move_effect).into_ok();
// Unknown
*bytes.unknown_e2 = self.unknown_e2;
// Support arrow color
Option::<ArrowColor>::to_bytes(&self.effect_arrow_color, bytes.effect_arrow_color).into_ok();
@@ -440,6 +430,11 @@ impl Bytes for Digimon
util::write_null_ascii_string(self.effect_description[3].as_ref(), bytes.effect_description_3)
.map_err(ToBytesError::EffectDescriptionFourth)?;
// Unknown
LittleEndian::write_u16(bytes.unknown_15, self.unknown_15);
*bytes.unknown_1a = self.unknown_1a;
*bytes.unknown_e2 = self.unknown_e2;
// Return Ok
Ok(())
}

View File

@@ -1,258 +1,279 @@
//! Item
// Crate
//--------------------------------------------------------------------------------------------------
// Game
use crate::game::util;
use crate::game::Bytes;
use crate::game::card::property::EffectCondition;
use crate::game::card::property::Effect;
use crate::game::card::property::ArrowColor;
//--------------------------------------------------------------------------------------------------
// Array-ref
use arrayref::{array_ref, array_mut_ref};
//! An item card
//!
//! This module contains the [`Item`] struct, which describes an item card.
//!
//! # Layout
//! The item card has a size of `0xde` bytes, and it's layout is the following:
//!
//! | Offset | Size | Type | Name | Location | Details |
//! |--------|------|---------------------|---------------------------|------------------------|-------------------------------------------------------------------------------------|
//! | 0x0 | 0x15 | `[char; 0x15]` | Name | `name` | Null-terminated |
//! | 0x15 | 0x4 | `u32` | Unknown | `unknown_15` | |
//! | 0x19 | 0x20 | [`EffectCondition`] | First condition | `effect_conditions[0]` | |
//! | 0x39 | 0x20 | [`EffectCondition`] | Second condition | `effect_conditions[1]` | |
//! | 0x59 | 0x10 | [`Effect`] | First effect | `effects[0]` | |
//! | 0x69 | 0x10 | [`Effect`] | Second effect | `effects[1]` | |
//! | 0x79 | 0x10 | [`Effect`] | Third effect | `effects[2]` | |
//! | 0x89 | 0x1 | [`ArrowColor`] | Effect arrow color | `effect_arrow_color` | |
//! | 0x8a | 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;
use byteorder::LittleEndian;
use byteorder::{ByteOrder, LittleEndian};
// Macros
use serde::Serialize;
use serde::Deserialize;
// Crate
use crate::game::{
util,
Bytes,
card::property::{
self,
EffectCondition,
Effect,
ArrowColor,
}
};
// Types
//--------------------------------------------------------------------------------------------------
/// A item card
#[derive(Debug, Serialize, Deserialize)]
pub struct Item
{
/// The basic info of the item
pub basic: Basic,
/// The effects
effects: Effects,
}
/// A item card
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Item
{
/// The digimon's name
///
/// An ascii string with 20 characters at most
pub name: ascii::AsciiString,
/// The basic properties of a item
#[derive(Debug, Serialize, Deserialize)]
pub struct Basic
{
pub name: String,
pub unknown: u16,
}
/// The digimon's effect description.
///
/// The description is split along 4 lines, each
/// being an ascii string with 20 characters at most.
pub effect_description: [ascii::AsciiString; 4],
#[derive(Debug, Serialize, Deserialize)]
struct Effects
{
description: [String; 4],
arrow_color: Option<ArrowColor>,
conditions: SupportConditions,
effects : SupportEffects,
}
/// The effect arrow color
#[serde(default)]
pub effect_arrow_color: Option<ArrowColor>,
#[derive(Debug, Serialize, Deserialize)]
struct SupportEffects
{
first : Option<Effect>,
second: Option<Effect>,
third : Option<Effect>,
}
/// The effect conditions
#[serde(default)]
pub effect_conditions: [Option<EffectCondition>; 2],
#[derive(Debug, Serialize, Deserialize)]
struct SupportConditions
{
first : Option<EffectCondition>,
second: Option<EffectCondition>,
}
/// The effects themselves
#[serde(default)]
pub effects: [Option<Effect>; 3],
/// The error type thrown by `FromBytes`
#[derive(Debug, derive_more::Display)]
pub enum FromBytesError
{
/// Unable to convert name to a string
#[display(fmt = "Unable to convert name to a string")]
NameToString( util::ReadNullTerminatedStringError ),
/// Unable to read the effect arrow color
#[display(fmt = "Unable to read the effect arrow color")]
EffectArrowColor( crate::game::card::property::arrow_color::FromBytesError ),
/// Unable to convert one of the support effect descriptions to a string
#[display(fmt = "Unable to convert the {} support effect description to a string", rank)]
SupportEffectDescriptionToString {
rank: &'static str,
err: util::ReadNullTerminatedStringError,
},
/// Unable to read a support effect condition
#[display(fmt = "Unable to read the {0} support effect condition [item:0x{1:x}]", rank, item_pos)]
SupportCondition {
rank: &'static str,
item_pos: u64,
err: crate::game::card::property::effect_condition::FromBytesError,
},
/// Unable to read a support effect
#[display(fmt = "Unable to read the {} support effect", rank)]
SupportEffect {
rank: &'static str,
err: crate::game::card::property::effect::FromBytesError,
},
}
impl std::error::Error for FromBytesError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::NameToString(err) |
Self::SupportEffectDescriptionToString{err, ..} => Some(err),
Self::EffectArrowColor(err) => Some(err),
Self::SupportCondition{err, ..} => Some(err),
Self::SupportEffect{err, ..} => Some(err),
}
}
}
/// The error type thrown by `ToBytes`
#[derive(Debug, derive_more::Display)]
pub enum ToBytesError
{
/// The name was too big to be written to file
#[display(fmt = "The name \"{}\" is too long to be written to file (max is 20)", _0)]
NameTooLong( String ),
/// The name was too big to be written to file
#[display(fmt = "The {0} support effect description \"{1}\" is too long to be written to file (max is 21)", rank, string)]
SupportEffectDescriptionTooLong {
rank: &'static str,
string: String,
},
}
impl std::error::Error for ToBytesError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::NameTooLong(..) |
Self::SupportEffectDescriptionTooLong{ .. } => None,
}
}
}
//--------------------------------------------------------------------------------------------------
// Unknown fields
pub unknown_15: u32,
}
// Impl
//--------------------------------------------------------------------------------------------------
// Bytes
impl Bytes for Item
/// Error type for [`Bytes::from_bytes`]
#[derive(Debug)]
#[derive(derive_more::Display, err_impl::Error)]
pub enum FromBytesError
{
/// Unable to read the digimon name
#[display(fmt = "Unable to read the digimon name")]
Name( #[error(source)] util::ReadNullAsciiStringError ),
/// Unable to read the first support effect description
#[display(fmt = "Unable to read the first line of the effect description")]
EffectDescriptionFirst( #[error(source)] util::ReadNullAsciiStringError ),
/// Unable to read the second support effect description
#[display(fmt = "Unable to read the second line of the effect description")]
EffectDescriptionSecond( #[error(source)] util::ReadNullAsciiStringError ),
/// Unable to read the third support effect description
#[display(fmt = "Unable to read the third line of the effect description")]
EffectDescriptionThird( #[error(source)] util::ReadNullAsciiStringError ),
/// Unable to read the fourth support effect description
#[display(fmt = "Unable to read the fourth line of the effect description")]
EffectDescriptionFourth( #[error(source)] util::ReadNullAsciiStringError ),
/// An unknown effect arrow color was found
#[display(fmt = "Unknown effect arrow color found")]
ArrowColor( #[error(source)] property::arrow_color::FromBytesError ),
/// Unable to read the first effect condition
#[display(fmt = "Unable to read the first effect condition")]
EffectConditionFirst( #[error(source)] property::effect_condition::FromBytesError ),
/// Unable to read the second effect condition
#[display(fmt = "Unable to read the second effect condition")]
EffectConditionSecond( #[error(source)] property::effect_condition::FromBytesError ),
/// Unable to read the first effect
#[display(fmt = "Unable to read the first effect")]
EffectFirst( #[error(source)] property::effect::FromBytesError ),
/// Unable to read the second effect
#[display(fmt = "Unable to read the second effect")]
EffectSecond( #[error(source)] property::effect::FromBytesError ),
/// Unable to read the third effect
#[display(fmt = "Unable to read the third effect")]
EffectThird( #[error(source)] property::effect::FromBytesError ),
}
/// Error type for [`Bytes::to_bytes`]
#[derive(Debug)]
#[derive(derive_more::Display, err_impl::Error)]
pub enum ToBytesError
{
/// Unable to write the digimon name
#[display(fmt = "Unable to write the digimon name")]
Name( #[error(source)] util::WriteNullAsciiStringError ),
/// Unable to write the first support effect description
#[display(fmt = "Unable to write the first line of the effect description")]
EffectDescriptionFirst( #[error(source)] util::WriteNullAsciiStringError ),
/// Unable to write the second support effect description
#[display(fmt = "Unable to write the second line of the effect description")]
EffectDescriptionSecond( #[error(source)] util::WriteNullAsciiStringError ),
/// Unable to write the third support effect description
#[display(fmt = "Unable to write the third line of the effect description")]
EffectDescriptionThird( #[error(source)] util::WriteNullAsciiStringError ),
/// Unable to write the fourth support effect description
#[display(fmt = "Unable to write the fourth line of the effect description")]
EffectDescriptionFourth( #[error(source)] util::WriteNullAsciiStringError ),
/// Unable to write the first effect
#[display(fmt = "Unable to write the first effect")]
EffectFirst( #[error(source)] property::effect::ToBytesError ),
/// Unable to write the second effect
#[display(fmt = "Unable to write the second effect")]
EffectSecond( #[error(source)] property::effect::ToBytesError ),
/// Unable to write the third effect
#[display(fmt = "Unable to write the third effect")]
EffectThird( #[error(source)] property::effect::ToBytesError ),
}
impl Bytes for Item
{
type ByteArray = [u8; 0xde];
type FromError = FromBytesError;
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError>
{
type ByteArray = [u8; 0xde];
// Split bytes
let bytes = util::array_split!(bytes,
name : [0x15],
unknown_15 : [0x4],
condition_first : [0x20],
condition_second : [0x20],
effect_first : [0x10],
effect_second : [0x10],
effect_third : [0x10],
effect_arrow_color : 1,
effect_description_0: [0x15],
effect_description_1: [0x15],
effect_description_2: [0x15],
effect_description_3: [0x15],
);
type FromError = FromBytesError;
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError>
{
// Assert some fields are 0
//assert_eq!(bytes[0x1a], 0);
// And return the struct
Ok( Self {
basic: Basic {
name: util::read_null_terminated_string( &bytes[0x0..0x15] ).map_err(FromBytesError::NameToString)?.to_string(),
unknown: LittleEndian::read_u16( &bytes[0x15..0x17] ),
},
effects: Effects {
description: [
util::read_null_terminated_string( &bytes[0x8a..0x9f] ).map_err(|err| FromBytesError::SupportEffectDescriptionToString{ rank: "1st", err })?.to_string(),
util::read_null_terminated_string( &bytes[0x9f..0xb4] ).map_err(|err| FromBytesError::SupportEffectDescriptionToString{ rank: "2nd", err })?.to_string(),
util::read_null_terminated_string( &bytes[0xb4..0xc9] ).map_err(|err| FromBytesError::SupportEffectDescriptionToString{ rank: "3rd", err })?.to_string(),
util::read_null_terminated_string( &bytes[0xc9..0xde] ).map_err(|err| FromBytesError::SupportEffectDescriptionToString{ rank: "4th", err })?.to_string(),
],
arrow_color: if bytes[0x89] != 0 {
Some( ArrowColor::from_bytes( &bytes[0x89] ).map_err(FromBytesError::EffectArrowColor)? )
} else { None },
conditions: SupportConditions {
first: Option::<EffectCondition>::from_bytes( array_ref!(bytes, 0x19, 0x20) )
.map_err(|err| FromBytesError::SupportCondition{ rank: "1st", item_pos: 0x19, err })?,
second: Option::<EffectCondition>::from_bytes( array_ref!(bytes, 0x39, 0x20) )
.map_err(|err| FromBytesError::SupportCondition{ rank: "2nd", item_pos: 0x39, err })?,
},
effects: SupportEffects {
first: Option::<Effect>::from_bytes( array_ref!(bytes, 0x59, 0x10) )
.map_err(|err| FromBytesError::SupportEffect{ rank: "1st", err })?,
second: Option::<Effect>::from_bytes( array_ref!(bytes, 0x69, 0x10) )
.map_err(|err| FromBytesError::SupportEffect{ rank: "2nd", err })?,
third: Option::<Effect>::from_bytes( array_ref!(bytes, 0x79, 0x10) )
.map_err(|err| FromBytesError::SupportEffect{ rank: "3rd", err })?,
},
},
})
}
type ToError = ToBytesError;
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError>
{
// Basic
//--------------------------------------------------------------------------------------------------
// Write the name
bytes[0x0..0x15].copy_from_slice( &{
// Check if our name is too big
if self.basic.name.len() >= 0x15 { return Err( ToBytesError::NameTooLong( self.basic.name.clone() ) ); }
// Else make the buffer and copy everything over
let mut buf = [0u8; 0x15];
buf[ 0..self.basic.name.len() ].copy_from_slice( self.basic.name.as_bytes() );
buf
});
LittleEndian::write_u16(&mut bytes[0x15..0x17], self.basic.unknown);
//--------------------------------------------------------------------------------------------------
// And return the struct
Ok( Self {
name: util::read_null_ascii_string(bytes.name)
.map_err(FromBytesError::Name)?
.chars().collect(),
// Effects
//--------------------------------------------------------------------------------------------------
// Write the support effects
for (index, line) in self.effects.description.iter().enumerate()
{
bytes[0x8a + (0x15 * index) .. 0x9f + (0x15 * index)].copy_from_slice( &{
// If the line is too big, return Err
if line.len() >= 0x15 {
return Err( ToBytesError::SupportEffectDescriptionTooLong {
rank: match index {
0 => "1st", 1 => "2nd",
2 => "3rd", 3 => "4th",
_ => unreachable!(),
},
string: line.clone()
});
}
let mut buf = [0u8; 0x15];
buf[ 0..line.len() ].copy_from_slice( line.as_bytes() );
buf
});
}
effect_conditions: [
Option::<EffectCondition>::from_bytes( bytes.condition_first )
.map_err(FromBytesError::EffectConditionFirst)?,
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
self.effects.conditions.first .to_bytes( array_mut_ref!(bytes, 0x19, 0x20) )?;
self.effects.conditions.second.to_bytes( array_mut_ref!(bytes, 0x39, 0x20) )?;
// If they are None, 0 is a valid value for the effects
self.effects.effects.first .to_bytes( array_mut_ref!(bytes, 0x59, 0x10) ).expect("TODO");
self.effects.effects.second.to_bytes( array_mut_ref!(bytes, 0x69, 0x10) ).expect("TODO");
self.effects.effects.third .to_bytes( array_mut_ref!(bytes, 0x79, 0x10) ).expect("TODO");
//--------------------------------------------------------------------------------------------------
Option::<EffectCondition>::from_bytes( bytes.condition_second )
.map_err(FromBytesError::EffectConditionSecond)?,
],
// Return the bytes
Ok(())
}
effects: [
Option::<Effect>::from_bytes( bytes.effect_first )
.map_err(FromBytesError::EffectFirst)?,
Option::<Effect>::from_bytes( bytes.effect_second )
.map_err(FromBytesError::EffectSecond)?,
Option::<Effect>::from_bytes( bytes.effect_third )
.map_err(FromBytesError::EffectThird)?,
],
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 )
.map_err(FromBytesError::EffectDescriptionFirst)?
.chars().collect(),
util::read_null_ascii_string( bytes.effect_description_1 )
.map_err(FromBytesError::EffectDescriptionSecond)?
.chars().collect(),
util::read_null_ascii_string( bytes.effect_description_2 )
.map_err(FromBytesError::EffectDescriptionThird)?
.chars().collect(),
util::read_null_ascii_string( bytes.effect_description_3 )
.map_err(FromBytesError::EffectDescriptionFourth)?
.chars().collect(),
],
// Unknown
unknown_15: LittleEndian::read_u32(bytes.unknown_15),
})
}
//--------------------------------------------------------------------------------------------------
type ToError = ToBytesError;
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError>
{
// Split bytes
let bytes = util::array_split_mut!(bytes,
name : [0x15],
unknown_15 : [0x4],
condition_first : [0x20],
condition_second : [0x20],
effect_first : [0x10],
effect_second : [0x10],
effect_third : [0x10],
effect_arrow_color : 1,
effect_description_0: [0x15],
effect_description_1: [0x15],
effect_description_2: [0x15],
effect_description_3: [0x15],
);
// Name
util::write_null_ascii_string(self.name.as_ref(), bytes.name)
.map_err(ToBytesError::Name)?;
// Effect conditions
self.effect_conditions[0].to_bytes( bytes.condition_first ).into_ok();
self.effect_conditions[1].to_bytes( bytes.condition_second ).into_ok();
// Effects
self.effects[0].to_bytes( bytes.effect_first ).map_err(ToBytesError::EffectFirst )?;
self.effects[1].to_bytes( bytes.effect_second ).map_err(ToBytesError::EffectSecond)?;
self.effects[2].to_bytes( bytes.effect_third ).map_err(ToBytesError::EffectThird )?;
// Support arrow color
Option::<ArrowColor>::to_bytes(&self.effect_arrow_color, bytes.effect_arrow_color).into_ok();
// effect_description
util::write_null_ascii_string(self.effect_description[0].as_ref(), bytes.effect_description_0)
.map_err(ToBytesError::EffectDescriptionFirst)?;
util::write_null_ascii_string(self.effect_description[1].as_ref(), bytes.effect_description_1)
.map_err(ToBytesError::EffectDescriptionSecond)?;
util::write_null_ascii_string(self.effect_description[2].as_ref(), bytes.effect_description_2)
.map_err(ToBytesError::EffectDescriptionThird)?;
util::write_null_ascii_string(self.effect_description[3].as_ref(), bytes.effect_description_3)
.map_err(ToBytesError::EffectDescriptionFourth)?;
// Unknown
LittleEndian::write_u32(bytes.unknown_15, self.unknown_15);
// Return Ok
Ok(())
}
}

View File

@@ -411,7 +411,7 @@ impl Bytes for Effect
(Player , DpSlot, OfflineDeck) => 36,
(Opponent, DpSlot, OfflineDeck) => 37,
(_, &source, &destination) => return Err(ToBytesError::InvalidMoveCards { source, destination }),
(_, &source, &destination) => return Err( ToBytesError::InvalidMoveCards { source, destination } ),
};
LittleEndian::write_u16(bytes.y, *count);
}

View File

@@ -126,21 +126,6 @@ pub macro array_split_mut {
}
}
}
/// Error type for `write_null_terminated_string`
#[derive(Debug, derive_more::Display)]
#[display(fmt = "The string was too long to write to the buffer ({0} / {1})", string_size, buf_size)]
pub struct WriteNullTerminatedStringError
{
/// The string size
string_size: usize,
/// The buffer size
buf_size: usize,
}
// No source
impl std::error::Error for WriteNullTerminatedStringError { }
//--------------------------------------------------------------------------------------------------
// Impl
@@ -167,42 +152,6 @@ pub macro array_split_mut {
// Else try to conver the buffer into a utf8 str.
Ok( std::str::from_utf8( buf ).map_err(ReadNullTerminatedStringError::Utf8)? )
}
/// Writes a string to a buffer with a null terminator and returns it
///
/// # Details
/// Will reserve the last byte after the string for a null, none of
/// the bytes after it will be touched.
///
/// # Errors
/// - `TooLong`: If the string is too long for the buffer
pub fn write_null_terminated_string<'a>(string: &'_ str, buf: &'a mut [u8]) -> Result<&'a mut [u8], WriteNullTerminatedStringError>
{
// Check if the string is too big
// Note: This also catches the case where they're both 0
if string.len() >= buf.len() { return Err( WriteNullTerminatedStringError{ string_size: string.len(), buf_size: buf.len() } ); }
// Else copy everything over
buf[ 0..string.len() ].copy_from_slice( string.as_bytes() );
// Set the last byte of the string as null
buf[ string.len() ] = 0;
// And return the buffer
Ok( buf )
}
/// Returns an ordinal string from a u64
#[must_use]
pub fn as_ordinal(num: u64) -> String
{
format!("{0}{1}", num, match num % 10 {
1 => "st",
2 => "nd",
3 => "rd",
_ => "th",
})
}
//--------------------------------------------------------------------------------------------------