From 63d312e8454555b840a4e09eb01af1f2742e2b82 Mon Sep 17 00:00:00 2001 From: Filipe Rodrigues Date: Wed, 13 May 2020 03:03:46 +0100 Subject: [PATCH] Added various rustfmt configurations. Revised documentation of the library and lint opt-out reasons. Moved some module documentation to it's own file due to overflowing the line limit. Fixed several spelling errors. --- rustfmt.toml | 39 ++++++++++ src/game/bytes.rs | 2 +- src/game/card/digimon.md | 28 +++++++ src/game/card/digimon.rs | 33 +------- src/game/card/digivolve.md | 12 +++ src/game/card/digivolve.rs | 13 +--- src/game/card/item.md | 18 +++++ src/game/card/item.rs | 19 +---- src/game/card/property.rs | 4 +- src/game/card/property/effect.md | 12 +++ src/game/card/property/effect.rs | 31 +++----- src/game/card/property/effect_condition.md | 19 +++++ src/game/card/property/effect_condition.rs | 36 +++------ src/game/card/property/moves.rs | 4 +- src/game/card/table.md | 34 ++++++++ src/game/card/table.rs | 78 +++++-------------- src/game/deck/deck.rs | 2 +- src/game/deck/table.rs | 8 +- src/game/util.rs | 6 +- src/io.rs | 2 +- src/io/address.rs | 11 +-- src/io/address/real.rs | 1 + src/io/game_file.rs | 12 +-- src/lib.rs | 91 +++++++++++++++------- 24 files changed, 295 insertions(+), 220 deletions(-) create mode 100644 src/game/card/digimon.md create mode 100644 src/game/card/digivolve.md create mode 100644 src/game/card/item.md create mode 100644 src/game/card/property/effect.md create mode 100644 src/game/card/property/effect_condition.md create mode 100644 src/game/card/table.md diff --git a/rustfmt.toml b/rustfmt.toml index 844da63..f0c05cf 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -16,3 +16,42 @@ fn_args_layout = "Compressed" merge_derives = false merge_imports = true max_width = 150 +use_small_heuristics = "Default" +indent_style = "Block" +wrap_comments = false +format_code_in_doc_comments = false +comment_width = 150 +normalize_comments = false +normalize_doc_attributes = false +license_template_path = "" +format_strings = true +format_macro_matchers = true +format_macro_bodies = true +empty_item_single_line = true +struct_lit_single_line = true +fn_single_line = false +where_single_line = false +imports_indent = "Block" +imports_layout = "Mixed" +reorder_imports = true +reorder_modules = true +type_punctuation_density = "Wide" +space_before_colon = false +space_after_colon = true +spaces_around_ranges = false +remove_nested_parens = true +combine_control_expr = true +struct_field_align_threshold = 20 +match_arm_blocks = true +force_multiline_blocks = false +brace_style = "SameLineWhere" +control_brace_style = "AlwaysSameLine" +trailing_semicolon = true +trailing_comma = "Vertical" +blank_lines_lower_bound = 0 +blank_lines_upper_bound = 2 +inline_attribute_width = 0 +use_try_shorthand = true +force_explicit_abi = true +error_on_line_overflow = true +error_on_unformatted = true diff --git a/src/game/bytes.rs b/src/game/bytes.rs index 2c98192..3351d62 100644 --- a/src/game/bytes.rs +++ b/src/game/bytes.rs @@ -1,6 +1,6 @@ //! Interface for converting various structures to and from bytes -/// Convertions to and from bytes for the game file +/// Conversions to and from bytes for the game file pub trait Bytes where Self: Sized, diff --git a/src/game/card/digimon.md b/src/game/card/digimon.md new file mode 100644 index 0000000..1afa7dd --- /dev/null +++ b/src/game/card/digimon.md @@ -0,0 +1,28 @@ +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: + +| Offset | Size | Type | Name | Location | Details | +| ------ | ---- | ------------------- | ------------------------ | ---------------------- | ----------------------------------------------------------------------------------- | +| 0x0 | 0x15 | `[char; 0x15]` | Name | `name` | Null-terminated | +| 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` | | +| 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 | [`EffectCondition`] | First condition | `effect_conditions[0]` | | +| 0x91 | 0x20 | [`EffectCondition`] | Second condition | `effect_conditions[1]` | | +| 0xb1 | 0x10 | [`Effect`] | First effect | `effects[0]` | | +| 0xc1 | 0x10 | [`Effect`] | Second effect | `effects[1]` | | +| 0xd1 | 0x10 | [`Effect`] | 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 | diff --git a/src/game/card/digimon.rs b/src/game/card/digimon.rs index d002bbd..3e97efb 100644 --- a/src/game/card/digimon.rs +++ b/src/game/card/digimon.rs @@ -1,31 +1,4 @@ -//! 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: -//! -//! | Offset | Size | Type | Name | Location | Details | -//! |--------|------|---------------------|---------------------------|------------------------|-------------------------------------------------------------------------------------| -//! | 0x0 | 0x15 | `[char; 0x15]` | Name | `name` | Null-terminated | -//! | 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` | | -//! | 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 | [`EffectCondition`] | First condition | `effect_conditions[0]` | | -//! | 0x91 | 0x20 | [`EffectCondition`] | Second condition | `effect_conditions[1]` | | -//! | 0xb1 | 0x10 | [`Effect`] | First effect | `effects[0]` | | -//! | 0xc1 | 0x10 | [`Effect`] | Second effect | `effects[1]` | | -//! | 0xd1 | 0x10 | [`Effect`] | 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 | +#![doc(include = "digimon.md")] // byteorder use byteorder::{ByteOrder, LittleEndian}; @@ -275,9 +248,9 @@ impl Bytes for Digimon { hp: LittleEndian::read_u16(bytes.hp), // Moves - move_circle: Move::from_bytes(bytes.move_circle).map_err(FromBytesError::MoveCircle)?, + move_circle: Move::from_bytes(bytes.move_circle).map_err(FromBytesError::MoveCircle)?, move_triangle: Move::from_bytes(bytes.move_triangle).map_err(FromBytesError::MoveTriangle)?, - move_cross: Move::from_bytes(bytes.move_cross).map_err(FromBytesError::MoveCross)?, + move_cross: Move::from_bytes(bytes.move_cross).map_err(FromBytesError::MoveCross)?, // Effects effect_conditions: [ diff --git a/src/game/card/digivolve.md b/src/game/card/digivolve.md new file mode 100644 index 0000000..6a893fc --- /dev/null +++ b/src/game/card/digivolve.md @@ -0,0 +1,12 @@ +A digivolve card + +This module contains the [`Digivolve`] struct, which describes a digivolve card. + +# Layout +The digivolve card has a size of `0x6c` bytes, and it's layout is the following: + +| Offset | Size | Type | Name | Location | Details | +| ------ | ---- | ------------------- | ------------------------ | -------------------- | ------------------------------------------------------------------- | +| 0x0 | 0x15 | `[char; 0x15]` | Name | `name` | Null-terminated | +| 0x15 | 0x3 | `[u8; 3]` | Unknown | `unknown_15` | Probably contains the card effect | +| 0x8a | 0x54 | `[[char; 0x15]; 4]` | Effect description lines | `effect_description` | Each line is` 0x15` bytes, split over 4 lines, each null terminated | diff --git a/src/game/card/digivolve.rs b/src/game/card/digivolve.rs index 5d9cf46..3a82623 100644 --- a/src/game/card/digivolve.rs +++ b/src/game/card/digivolve.rs @@ -1,15 +1,4 @@ -//! A digivolve card -//! -//! This module contains the [`Digivolve`] struct, which describes a digivolve card. -//! -//! # Layout -//! The digivolve card has a size of `0x6c` bytes, and it's layout is the following: -//! -//! | Offset | Size | Type | Name | Location | Details | -//! |--------|------|---------------------|---------------------------|------------------------|---------------------------------------------------------------------| -//! | 0x0 | 0x15 | `[char; 0x15]` | Name | `name` | Null-terminated | -//! | 0x15 | 0x3 | `[u8; 3]` | Unknown | `unknown_15` | Probably contains the card effect | -//! | 0x8a | 0x54 | `[[char; 0x15]; 4]` | Effect description lines | `effect_description` | Each line is` 0x15` bytes, split over 4 lines, each null terminated | +#![doc(include = "digivolve.md")] // Crate use crate::game::{util, Bytes}; diff --git a/src/game/card/item.md b/src/game/card/item.md new file mode 100644 index 0000000..637fe8a --- /dev/null +++ b/src/game/card/item.md @@ -0,0 +1,18 @@ +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 | diff --git a/src/game/card/item.rs b/src/game/card/item.rs index bc80f21..6044eed 100644 --- a/src/game/card/item.rs +++ b/src/game/card/item.rs @@ -1,21 +1,4 @@ -//! 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 | +#![doc(include = "item.md")] // byteorder use byteorder::{ByteOrder, LittleEndian}; diff --git a/src/game/card/property.rs b/src/game/card/property.rs index 02ebac2..8304b5a 100644 --- a/src/game/card/property.rs +++ b/src/game/card/property.rs @@ -27,7 +27,7 @@ macro_rules! generate_enum_property_mod // Note: Must have no data $enum_variant_name:ident - // `Display` convertion name + // `Display` conversion name ($enum_variant_rename:literal) => @@ -121,7 +121,7 @@ macro_rules! generate_enum_property_mod /// Implements [`Bytes`](crate::game::Bytes) for `Option` where `E` /// is the first argument of this macro and an enum. /// -/// This is done by suppling a sentinel value which is read/written as `None`. +/// This is done by supplying a sentinel value which is read/written as `None`. macro generate_enum_property_option { ( $( $enum_name:ty => $sentinel_value:literal ),* $(,)? diff --git a/src/game/card/property/effect.md b/src/game/card/property/effect.md new file mode 100644 index 0000000..7efe50d --- /dev/null +++ b/src/game/card/property/effect.md @@ -0,0 +1,12 @@ +A digimon's support effect + +This module contains the [`Effect`] struct, which describes a support effect. + +# Layout +Each support effect has a size of `0x10` bytes, and it's general layout is the following: + +| Offset | Size | Type | Name | Location | Details | +| ------ | ---- | ------ | ----------- | -------- | ------------------------------------------------------ | +| 0x0 | 0x1 | `bool` | Exists | N/A | If `0`, the effect does not exist | +| 0x1 | 0x1 | N/A | Effect Type | N/A | Determines which [`Effect`] variant is used. | +| 0x2 | 0xe | N/A | Arguments | N/A | The arguments used for the current [`Effect`] variant. | diff --git a/src/game/card/property/effect.rs b/src/game/card/property/effect.rs index b20ef20..eb1f386 100644 --- a/src/game/card/property/effect.rs +++ b/src/game/card/property/effect.rs @@ -1,15 +1,4 @@ -//! A digimon's support effect -//! -//! This module contains the [`Effect`] struct, which describes a support effect. -//! -//! # Layout -//! Each support effect has a size of `0x10` bytes, and it's general layout is the following: -//! -//! | Offset | Size | Type | Name | Location | Details | -//! |--------|------|----------------------|---------------------------|------------------------|--------------------------------------------------------| -//! | 0x0 | 0x1 | `bool` | Exists | N/A | If `0`, the effect does not exist | -//! | 0x1 | 0x1 | N/A | Effect Type | N/A | Determines which [`Effect`] variant is used. | -//! | 0x2 | 0xe | N/A | Arguments | N/A | The arguments used for the current [`Effect`] variant. | +#![doc(include = "effect.md")] // byteorder use byteorder::{ByteOrder, LittleEndian}; @@ -23,13 +12,13 @@ use crate::game::{ /// A digimon's support effects /// /// As this type is wildly volatile in which arguments it uses and from where, -/// it is an `enum` with struct variants instead of a struct. This simplifices argument +/// it is an `enum` with struct variants instead of a struct. This simplifies argument /// verification and, from a language perspective, makes more sense as an implementation. #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] #[derive(serde::Serialize, serde::Deserialize)] #[serde(tag = "type")] // TODO: Move this `allow` to the variant once clippy allows -#[allow(clippy::pub_enum_variant_names)] // `Effect` on `VoidOpponentSupportEffect` isn't refering to the enum +#[allow(clippy::pub_enum_variant_names)] // `Effect` on `VoidOpponentSupportEffect` isn't referring to the enum pub enum Effect { /// Changes a property of either digimon /// @@ -75,10 +64,9 @@ pub enum Effect { /// ` = + ( )` #[serde(rename = "Set temp slot")] SetTempSlot { - a: Option, - b: Option, - c: Option, - + a: Option, + b: Option, + c: Option, op: EffectOperation, }, @@ -93,11 +81,10 @@ pub enum Effect { /// - `Dp` -> `Offline` #[serde(rename = "Move cards")] MoveCards { - player: PlayerType, - source: Slot, + player: PlayerType, + source: Slot, destination: Slot, - - count: u16, + count: u16, }, /// Shuffles a player's online deck diff --git a/src/game/card/property/effect_condition.md b/src/game/card/property/effect_condition.md new file mode 100644 index 0000000..f5fcce7 --- /dev/null +++ b/src/game/card/property/effect_condition.md @@ -0,0 +1,19 @@ +A digimon's effect condition + +This module contains the [`EffectCondition`] struct, which describes a condition for an effect. + +# Layout +Each support condition has a size of `0x20` bytes, and it's layout is the following: + +| Offset | Size | Type | Name | Location | Details | +| ------ | ---- | ---------------------------- | ----------------- | -------------- | ---------------------------------------------------------------------------------- | +| 0x0 | 0x1 | `bool` | Misfire | `misfire` | If the condition throws a misfire when false | +| 0x1 | 0x1 | `u8` | | `unknown_1` | Always zero | +| 0x2 | 0x1 | [`DigimonProperty`] | Property compare | `property_cmp` | The property to compare to for the condition (or 0 if the condition doesn't exist) | +| 0x3 | 0x5 | `[u8; 0x5]` | | `unknown_3` | Unknown | +| 0x8 | 0x1 | `DigimonProperty` | Property argument | `arg_property` | Property argument for the comparison | +| 0x9 | 0xb | `[u8; 0xb]` | | `unknown_9` | Unknown | +| 0x14 | 0x2 | `u16` | Number argument | `arg_num` | Number argument for the comparison | +| 0x16 | 0x4 | `[u8; 0x4]` | | `unknown_16` | Unknown | +| 0x1a | 0x1 | [`EffectConditionOperation`] | Operation | `operation` | Operation to use for the comparison | +| 0x1b | 0x5 | `[u8; 0x5]` | | `unknown_1b` | Unknown | diff --git a/src/game/card/property/effect_condition.rs b/src/game/card/property/effect_condition.rs index fc8f782..78c7d24 100644 --- a/src/game/card/property/effect_condition.rs +++ b/src/game/card/property/effect_condition.rs @@ -1,22 +1,4 @@ -//! A digimon's effect condition -//! -//! This module contains the [`EffectCondition`] struct, which describes a condition for an effect. -//! -//! # Layout -//! Each support condition has a size of `0x20` bytes, and it's layout is the following: -//! -//! | Offset | Size | Type | Name | Location | Details | -//! |--------|------|------------------------------|---------------------------|--------------- |------------------------------------------------------------------------------------| -//! | 0x0 | 0x1 | `bool` | Misfire | `misfire` | If the condition throws a misfire when false | -//! | 0x1 | 0x1 | `u8` | | `unknown_1` | Always zero | -//! | 0x2 | 0x1 | [`DigimonProperty`] | Property compare | `property_cmp` | The property to compare to for the condition (or 0 if the condition doesn't exist) | -//! | 0x3 | 0x5 | `[u8; 0x5]` | | `unknown_3` | Unknown | -//! | 0x8 | 0x1 | `DigimonProperty` | Property argument | `arg_property` | Property argument for the comparation | -//! | 0x9 | 0xb | `[u8; 0xb]` | | `unknown_9` | Unknown | -//! | 0x14 | 0x2 | `u16` | Number argument | `arg_num` | Number argument for the comparation | -//! | 0x16 | 0x4 | `[u8; 0x4]` | | `unknown_16` | Unknown | -//! | 0x1a | 0x1 | [`EffectConditionOperation`] | Operation | `operation` | Operation to use for the comparation | -//! | 0x1b | 0x5 | `[u8; 0x5]` | | `unknown_1b` | Unknown | +#![doc(include = "effect_condition.md")] // byteorder use byteorder::{ByteOrder, LittleEndian}; @@ -47,9 +29,9 @@ pub struct EffectCondition { operation: EffectConditionOperation, // Unknown - unknown_1: u8, - unknown_3: [u8; 0x5], - unknown_9: [u8; 0xb], + unknown_1: u8, + unknown_3: [u8; 0x5], + unknown_9: [u8; 0xb], unknown_16: [u8; 0x4], unknown_1b: [u8; 0x5], } @@ -91,7 +73,7 @@ impl Bytes for EffectCondition { ); Ok(Self { - misfire: (*bytes.misfire != 0), + misfire: (*bytes.misfire != 0), property_cmp: DigimonProperty::from_bytes(bytes.property_cmp).map_err(FromBytesError::Condition)?, arg_property: Option::::from_bytes(bytes.arg_property).map_err(FromBytesError::PropertyArgument)?, @@ -100,9 +82,9 @@ impl Bytes for EffectCondition { operation: EffectConditionOperation::from_bytes(bytes.operation).map_err(FromBytesError::Operation)?, - unknown_1: *bytes.unknown_1, - unknown_3: *bytes.unknown_3, - unknown_9: *bytes.unknown_9, + unknown_1: *bytes.unknown_1, + unknown_3: *bytes.unknown_3, + unknown_9: *bytes.unknown_9, unknown_16: *bytes.unknown_16, unknown_1b: *bytes.unknown_1b, }) @@ -151,7 +133,7 @@ impl Bytes for Option { type ToError = ::ToError; fn from_bytes(bytes: &Self::ByteArray) -> Result { - // If we have no property comparation, return None + // If we have no property comparison, return None if bytes[0x2] == 0 { return Ok(None); } diff --git a/src/game/card/property/moves.rs b/src/game/card/property/moves.rs index 9ba2f76..9ca9024 100644 --- a/src/game/card/property/moves.rs +++ b/src/game/card/property/moves.rs @@ -63,8 +63,8 @@ impl Bytes for Move { // Return the move Ok(Self { - name: util::read_null_ascii_string(bytes.name).map_err(FromBytesError::Name)?.to_ascii_string(), - power: LittleEndian::read_u16(bytes.power), + name: util::read_null_ascii_string(bytes.name).map_err(FromBytesError::Name)?.to_ascii_string(), + power: LittleEndian::read_u16(bytes.power), unknown: LittleEndian::read_u32(bytes.unknown), }) } diff --git a/src/game/card/table.md b/src/game/card/table.md new file mode 100644 index 0000000..09ab022 --- /dev/null +++ b/src/game/card/table.md @@ -0,0 +1,34 @@ +The table of all digimon in the game + +# Details +At address [0x216d000](Table::START_ADDRESS) of the game file, the card table begins +with a small header of `0xb` and then the table itself. + +# Table Layout +The digimon table has a max size of [0x14950](Table::MAX_BYTE_SIZE), but does not +necessary use all of this space, but it does follow this layout: + +| Offset | Size | Type | Name | Details | +| ------ | -------- | --------------- | -------------------- | ----------------------------------------------------------------------- | +| 0x0 | 0x4 | u32 | Magic | Always contains the string "0ACD" (= [0x44434130](Table::HEADER_MAGIC)) | +| 0x4 | 0x2 | u16 | Number of digimon | | +| 0x6 | 0x1 | u8 | Number of items | | +| 0x7 | 0x1 | u8 | Number of digivolves | | +| 0x8 | variable | \[`CardEntry`\] | Card Entries | A contiguous array of [Card Entry](#card-entry-layout) | + +# Card Entry Layout +Each card entry consists of a header of the card + +| Offset | Size | Type | Name | Details | +| ------ | -------- | ------------------------------------ | --------------- | -------------------------------------------- | +| 0x0 | 0x3 | [`Card Header`](#card-header-layout) | Card Header | The card's header | +| 0x3 | variable | | Card | Either a [Digimon], [Item] or [Digivolve] | +| ... | 0x1 | u8 | Null terminator | A null terminator for the card (must be `0`) | + +# Card Header Layout +The card header determines which type of card this card entry has. + +| Offset | Size | Type | Name | Details | +| ------ | ---- | ------------ | --------- | ------------------------------------------------ | +| 0x0 | 0x2 | u16 | Card id | This card's ID | +| 0x2 | 0x1 | [`CardType`] | Card type | The card type ([Digimon], [Item] or [Digivolve]) | diff --git a/src/game/card/table.rs b/src/game/card/table.rs index ce4eb62..9613c10 100644 --- a/src/game/card/table.rs +++ b/src/game/card/table.rs @@ -1,42 +1,4 @@ -//! The table of all digimon in the game -//! -//! # Details -//! At address [0x216d000](Table::START_ADDRESS) of the game file, the card table begins -//! with a small header of `0xb` and then the table itself. -//! -//! # Table Layout -//! The digimon table has a max size of [0x14950](Table::MAX_BYTE_SIZE), but does not -//! necessary use all of this space, but it does follow this layout: -//! -//! | Offset | Size | Type | Name | Details | -//! |--------|----------|-----------------|----------------------|-------------------------------------------------------------------------| -//! | 0x0 | 0x4 | u32 | Magic | Always contains the string "0ACD" (= [0x44434130](Table::HEADER_MAGIC)) | -//! | 0x4 | 0x2 | u16 | Number of digimon | | -//! | 0x6 | 0x1 | u8 | Number of items | | -//! | 0x7 | 0x1 | u8 | Number of digivolves | | -//! | 0x8 | variable | \[`CardEntry`\] | Card Entries | A contigous array of [Card Entry](#card-entry-layout) | -//! -//! # Card Entry Layout -//! Each card entry consists of a header of the card -//! -//! | Offset | Size | Type | Name | Details | -//! |--------|----------|--------------------------------------|-----------------|----------------------------------------------| -//! | 0x0 | 0x3 | [`Card Header`](#card-header-layout) | Card Header | The card's header | -//! | 0x3 | variable | | Card | Either a [Digimon], [Item] or [Digivolve] | -//! | ... | 0x1 | u8 | Null terminator | A null terminator for the card (must be `0`) | -//! -//! # Card Header Layout -//! The card header determines which type of card this card entry has. -//! -//! | Offset | Size | Type | Name | Details | -//! |--------|------|--------------|-----------|--------------------------------------------------| -//! | 0x0 | 0x2 | u16 | Card id | This card's ID | -//! | 0x2 | 0x1 | [`CardType`] | Card type | The card type ([Digimon], [Item] or [Digivolve]) | - -// Lints -// False positive, we don't use any `unsafe` in impls -// TODO: Remove this lint once it no longer triggers. -#![allow(clippy::unsafe_derive_deserialize)] +#![doc(include = "table.md")] // Std use std::io::{Read, Seek, Write}; @@ -121,15 +83,15 @@ pub enum DeserializeError { Table::MAX_BYTE_SIZE )] TooManyCards { - digimon_cards: usize, - item_cards: usize, + digimon_cards: usize, + item_cards: usize, digivolve_cards: usize, }, /// Unable to read card header #[display(fmt = "Unable to read card header for card id {}", id)] ReadCardHeader { - id: usize, + id: usize, #[error(source)] err: std::io::Error, }, @@ -137,7 +99,7 @@ pub enum DeserializeError { /// An unknown card type was found #[display(fmt = "Unknown card type for card id {}", id)] UnknownCardType { - id: usize, + id: usize, #[error(source)] err: property::card_type::FromBytesError, }, @@ -145,7 +107,7 @@ pub enum DeserializeError { /// Unable to read a card #[display(fmt = "Unable to read {} with id {}", card_type, id)] ReadCard { - id: usize, + id: usize, card_type: CardType, #[error(source)] @@ -155,7 +117,7 @@ pub enum DeserializeError { /// Unable to deserialize a digimon card #[display(fmt = "Unable to deserialize digimon card with id {}", id)] DigimonCard { - id: usize, + id: usize, #[error(source)] err: card::digimon::FromBytesError, }, @@ -163,7 +125,7 @@ pub enum DeserializeError { /// Unable to deserialize an item card #[display(fmt = "Unable to deserialize item card with id {}", id)] ItemCard { - id: usize, + id: usize, #[error(source)] err: card::item::FromBytesError, }, @@ -171,7 +133,7 @@ pub enum DeserializeError { /// Unable to deserialize a digivolve card #[display(fmt = "Unable to deserialize digivolve card with id {}", id)] DigivolveCard { - id: usize, + id: usize, #[error(source)] err: card::digivolve::FromBytesError, }, @@ -179,7 +141,7 @@ pub enum DeserializeError { /// Unable to read card footer #[display(fmt = "Unable to read card footer for card id {}", id)] ReadCardFooter { - id: usize, + id: usize, #[error(source)] err: std::io::Error, }, @@ -209,15 +171,15 @@ pub enum SerializeError { Table::MAX_BYTE_SIZE )] TooManyCards { - digimon_cards: usize, - item_cards: usize, + digimon_cards: usize, + item_cards: usize, digivolve_cards: usize, }, /// Unable to write a digimon card #[display(fmt = "Unable to write digimon card with id {}", id)] WriteDigimonCard { - id: usize, + id: usize, #[error(source)] err: std::io::Error, }, @@ -225,7 +187,7 @@ pub enum SerializeError { /// Unable to write an item card #[display(fmt = "Unable to write item card with id {}", id)] WriteItemCard { - id: usize, + id: usize, #[error(source)] err: std::io::Error, }, @@ -233,7 +195,7 @@ pub enum SerializeError { /// Unable to write a digivolve card #[display(fmt = "Unable to write digivolve card with id {}", id)] WriteDigivolveCard { - id: usize, + id: usize, #[error(source)] err: std::io::Error, }, @@ -241,7 +203,7 @@ pub enum SerializeError { /// Unable to parse a digimon card #[display(fmt = "Unable to parse digimon card with id {}", id)] ParseDigimonCard { - id: usize, + id: usize, #[error(source)] err: card::digimon::ToBytesError, }, @@ -249,7 +211,7 @@ pub enum SerializeError { /// Unable to parse an item card #[display(fmt = "Unable to parse item card with id {}", id)] ParseItemCard { - id: usize, + id: usize, #[error(source)] err: card::item::ToBytesError, }, @@ -257,7 +219,7 @@ pub enum SerializeError { /// Unable to parse a digivolve card #[display(fmt = "Unable to parse digivolve card with id {}", id)] ParseDigivolveCard { - id: usize, + id: usize, #[error(source)] err: card::digivolve::ToBytesError, }, @@ -373,8 +335,8 @@ impl Table { // If the total table size is bigger than the max, return Err if table_size > Self::MAX_BYTE_SIZE { return Err(SerializeError::TooManyCards { - digimon_cards: self.digimons.len(), - item_cards: self.items.len(), + digimon_cards: self.digimons.len(), + item_cards: self.items.len(), digivolve_cards: self.digivolves.len(), }); } diff --git a/src/game/deck/deck.rs b/src/game/deck/deck.rs index 933c769..6a14508 100644 --- a/src/game/deck/deck.rs +++ b/src/game/deck/deck.rs @@ -93,7 +93,7 @@ impl Bytes for Deck { unknown: [0xc], ); - // Nanme / Owner + // 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)?; diff --git a/src/game/deck/table.rs b/src/game/deck/table.rs index 309f854..ead0b05 100644 --- a/src/game/deck/table.rs +++ b/src/game/deck/table.rs @@ -42,7 +42,7 @@ pub enum DeserializeError { /// Could not read a deck entry #[display(fmt = "Unable to read deck entry with id {}", "id")] ReadDeck { - id: usize, + id: usize, #[error(source)] err: std::io::Error, }, @@ -50,7 +50,7 @@ pub enum DeserializeError { /// Could not deserialize a deck entry #[display(fmt = "Unable to serialize deck entry with id {}", "id")] DeserializeDeck { - id: usize, + id: usize, #[error(source)] err: deck::FromBytesError, }, @@ -71,7 +71,7 @@ pub enum SerializeError { /// Could not deserialize a deck entry #[display(fmt = "Unable to deserialize deck entry with id {}", "id")] SerializeDeck { - id: usize, + id: usize, #[error(source)] err: deck::ToBytesError, }, @@ -79,7 +79,7 @@ pub enum SerializeError { /// Could not write a deck entry #[display(fmt = "Unable to read deck entry with id {}", "id")] WriteDeck { - id: usize, + id: usize, #[error(source)] err: std::io::Error, }, diff --git a/src/game/util.rs b/src/game/util.rs index 4c6a67b..7e92438 100644 --- a/src/game/util.rs +++ b/src/game/util.rs @@ -3,7 +3,7 @@ //! This modules is used for miscellaneous macros, functions that have //! not been moved to a more permanent location. //! -//! All items in this module will eventually be depracated and moved +//! All items in this module will eventually be deprecated and moved //! somewhere else, but this change might take some time. pub macro array_split { @@ -170,7 +170,7 @@ pub fn write_null_ascii_string<'a>(input: &ascii::AsciiStr, buf: &'a mut [u8]) - // 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(), + input_len: input.len(), buffer_len: buf.len(), }); } @@ -198,7 +198,7 @@ pub fn write_maybe_null_ascii_string<'a>(input: &ascii::AsciiStr, buf: &'a mut [ // 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(), + input_len: input.len(), buffer_len: buf.len(), }); } diff --git a/src/io.rs b/src/io.rs index b86303f..945cc40 100644 --- a/src/io.rs +++ b/src/io.rs @@ -2,7 +2,7 @@ //! //! The Io module takes care of interacting with the game file itself, such //! as ensuring that only the data sections in the game file are written to. -//! As well as making convertions between coordinates in data to real file +//! As well as making conversions between coordinates in data to real file //! coordinates. (For more details, visit the [`address`] module) // Modules diff --git a/src/io/address.rs b/src/io/address.rs index 4933001..7e7d04d 100644 --- a/src/io/address.rs +++ b/src/io/address.rs @@ -39,10 +39,10 @@ impl std::convert::TryFrom for Data { // The data address is just converting the real_sector // to a data_sector and subtracting the header from the // real offset to get the data offset + #[rustfmt::skip] Ok(Self::from( - Real::SECTOR_BYTE_SIZE * real_sector + // Base of data sector - real_sector_offset - - Real::HEADER_BYTE_SIZE, // Data offset + Real::SECTOR_BYTE_SIZE * real_sector + // Base of data sector + real_sector_offset - Real::HEADER_BYTE_SIZE, // Data offset (skipping header) )) } } @@ -54,12 +54,13 @@ impl From for Real { let data_sector = data_address.sector(); let data_sector_offset = data_address.offset(); - // Then the real address is just convering the data_sector + // Then the real address is just converting the data_sector // to a real_sector and adding the header plus the offset + #[rustfmt::skip] Self::from( Self::SECTOR_BYTE_SIZE * data_sector + // Base of real sector Self::HEADER_BYTE_SIZE + // Skip header - data_sector_offset, // Offset inside data sector + data_sector_offset, // Offset inside data sector ) } } diff --git a/src/io/address/real.rs b/src/io/address/real.rs index 6164fdc..875d5c2 100644 --- a/src/io/address/real.rs +++ b/src/io/address/real.rs @@ -47,6 +47,7 @@ impl Real { let real_sector = self.sector(); // The end of the real data section is after the header and data sections + #[rustfmt::skip] Self::from( Self::SECTOR_BYTE_SIZE * real_sector + // Beginning of sector Self::HEADER_BYTE_SIZE + // Skip Header diff --git a/src/io/game_file.rs b/src/io/game_file.rs index 5c34c3b..4f7a477 100644 --- a/src/io/game_file.rs +++ b/src/io/game_file.rs @@ -69,7 +69,7 @@ impl Read for GameFile { // While the buffer isn't empty while !buf.is_empty() { // Get the current real address we're at in the reader - // Note: If we can't get the position, we return immediatly + // Note: If we can't get the position, we return immediately let cur_real_address = RealAddress::from(self.reader.stream_position()?); // Get the data section end @@ -77,7 +77,7 @@ impl Read for GameFile { // If we're at the end of the data section, seek to the next data section if cur_real_address == data_section_end { - // Seek ahead by skiping the footer and next header + // Seek ahead by skipping the footer and next header self.reader.seek(std::io::SeekFrom::Current( (RealAddress::FOOTER_BYTE_SIZE + RealAddress::HEADER_BYTE_SIZE) as i64, ))?; @@ -106,7 +106,7 @@ impl Read for GameFile { } // Read either until the end of the data section or until buffer is full - // Note: If any fail, we immediatly return Err + // Note: If any fail, we immediately return Err let bytes_read = self.reader.read(&mut buf[0..bytes_to_read])?; // If 0 bytes were read, EOF was reached, so return with however many we've read @@ -137,7 +137,7 @@ impl Write for GameFile { // While the buffer isn't empty while !buf.is_empty() { // Get the current real address we're at in the reader - // Note: If we can't get the position, we return immediatly + // Note: If we can't get the position, we return immediately let cur_real_address = RealAddress::from(self.reader.stream_position()?); // Get the data section end @@ -145,7 +145,7 @@ impl Write for GameFile { // If we're at the end of the data section, seek to the next data section if cur_real_address == data_section_end { - // Seek ahead by skiping the footer and next header + // Seek ahead by skipping the footer and next header self.reader.seek(std::io::SeekFrom::Current( (RealAddress::FOOTER_BYTE_SIZE + RealAddress::HEADER_BYTE_SIZE) as i64, ))?; @@ -174,7 +174,7 @@ impl Write for GameFile { } // Write either until the end of the data section or until buffer runs out - // Note: If this fails, we immediatly return Err + // Note: If this fails, we immediately return Err let bytes_written = self.reader.write(&buf[0..bytes_to_write])?; // If 0 bytes were written, EOF was reached, so return with however many we've read diff --git a/src/lib.rs b/src/lib.rs index 052d038..b93c46a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,16 @@ -//! `dcb` is a library for interacting with the game file of `Digimon Digital Card Battle`, -//! a PSX game. +//! `dcb` is a library for interacting with the game file of `Digimon Digital Card Battle`. //! //! # Modules -//! `dcb` splits itself into 2 main modules, [`io`], which interacts with the game file -//! as well as general input / output operations and [`game`], where most of the game's -//! data types are defined. +//! `dcb` is split across 2 main modules, [`io`] and [`game`]. +//! +//! ## Io +//! The Io module is responsible for interacting with the game file. In the future it may be responsible +//! for also interacting with the game extracted database, once work on that is complete. +//! +//! ## Game +//! The game module is responsible for representing in-game structures such as cards, sprites, text, and +//! others. The trait has various interfaces to be able to deserialize these structures from both the game +//! file, database or even other sources, depending on the structure. //! //! # Example //! @@ -32,36 +38,65 @@ stmt_expr_attributes, unwrap_infallible, const_if_match, - exclusive_range_pattern + exclusive_range_pattern, + external_doc )] // Lints #![warn(clippy::restriction, clippy::pedantic, clippy::nursery)] +// Necessary items may be inlined using `LTO`, so we don't need to mark them as inline +#![allow(clippy::missing_inline_in_public_items)] +// We prefer tail returns where possible, as they help with code readability in most cases. +#![allow(clippy::implicit_return)] +// Very useful for arguments such as `arg: impl Into`, then used +// with `let arg = arg.into()`. As well as just going from `Option` +// to `T` without needing to change their names. +#![allow(clippy::shadow_reuse, clippy::shadow_same)] +// We use `.expect("...")` when we either know we cannot panic or it +// is the safest alternative, as proceeding would corrupt the program state. +#![allow(clippy::result_expect_used, clippy::option_expect_used)] +// Like-wise with `.expect()`, we use `unreachable!` when we know a branch +// if unreachable, and if it ever does get reached, panicking would be the +// safest option +#![allow(clippy::unreachable)] +// We find it more important to be able to copy paste literals such as `0xabcd1234` than +// being able to read them, which does not provide many benefits +#![allow(clippy::unreadable_literal, clippy::unseparated_literal_suffix)] +// We separate implementations per their functionality usually, such as constructors, getters, setters, and others. +#![allow(clippy::multiple_inherent_impl)] +// Many operations we need to repeat, and to keep symmetry +#![allow(clippy::identity_op)] +// We only introduce items before their first usage, which sometimes is half-way through the code +#![allow(clippy::items_after_statements)] +// Useful for when they either change a lot with new variants / data, +// or for symmetry purposes +#![allow(clippy::match_same_arms)] +// In this library we have very grain-level error types, each function +// will have it's own error type ideally, so any errors are explicit +// by the type, without needing a section for them +#![allow(clippy::missing_errors_doc)] +// Incomplete code should be tagged as `todo`. In future versions of the library, +// this lint may be removed, as incomplete code should not lie on a master branch. +#![allow(clippy::todo)] +// 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)] +// 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 +// lint currently triggers for `&[T; N]`, which we pass around a lot. +#![allow(clippy::indexing_slicing)] +// We don't have any `unsafe` impl for types that derive `Deserialize`. +#![allow(clippy::unsafe_derive_deserialize)] +// Banning arithmetic is too strict for this project +#![allow(clippy::integer_arithmetic)] +// TODO: Remove once fixed #![allow( - clippy::missing_inline_in_public_items, // Dubious lint - clippy::implicit_return, // We prefer tail returns where possible - clippy::shadow_reuse, // Very useful for arguments `arg: impl Into; let arg = arg.into()` - clippy::if_not_else, // Sometimes it's easier to read with a negation - clippy::result_expect_used, - clippy::option_expect_used, // We use `.expect` when there is no safe alternative and the program is corrupt - clippy::unreadable_literal, // More important to be able to copy the number with no formatting than it being readable - 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 - clippy::match_same_arms, // Sometimes we separate them for clarify and order - clippy::missing_errors_doc, // We provide documentation on errors on the error type itself - clippy::todo, // Code that is incomplete should be tagged as such. - clippy::unreachable, // Some code should be unreachable and panic when reached. - clippy::integer_arithmetic, // Come on now, we need to use numbers to program - clippy::shadow_same, // Useful when taking arguments such as `value: impl AsRef` / `let value = value.as_ref();` - clippy::module_inception, // Sometimes module organization causes this - clippy::missing_docs_in_private_items, // Not all private items are documented on purpose - clippy::indexing_slicing, // False-positives on arrays - // TODO: Deal with casts eventually + clippy::missing_docs_in_private_items, clippy::as_conversions, clippy::cast_possible_wrap, clippy::cast_sign_loss, - clippy::cast_possible_truncation, + clippy::cast_possible_truncation )] // Modules