mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-08 19:34:27 +00:00
Refactored game::item.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
})
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user