mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-07 01:58:58 +00:00
Revised dcb::card::Table.
This commit is contained in:
parent
0ebd141373
commit
2408cece65
@ -1,20 +1,18 @@
|
||||
The table of all digimon in the game
|
||||
|
||||
# Details
|
||||
The card table begins at address [0x216d000](Table::START_ADDRESS) of the game file,
|
||||
containing a header with the number of each card, and then a contiguous array of card entries.
|
||||
The card table contains a small header detailing how many of each cards exist, following by a contiguous
|
||||
array of card entries.
|
||||
|
||||
# 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 | 0x8 | u32 | Header | The [table header](TableHeader) |
|
||||
| 0x0 | 0x8 | u32 | Header | The [table header](Header) |
|
||||
| 0x8 | variable | [`CardEntry`](#card-entry-layout) | Card Entries | A contiguous array of [Card Entry](#card-entry-layout) |
|
||||
|
||||
# Card Entry Layout
|
||||
Each card entry consists of a header of the card
|
||||
Each card entry consists of a header of the card, the card itself and a null terminator.
|
||||
|
||||
| Offset | Size | Type | Name | Details |
|
||||
| ------ | -------- | -------------- | --------------- | -------------------------------------------- |
|
||||
|
||||
@ -5,12 +5,15 @@ pub mod error;
|
||||
pub mod header;
|
||||
|
||||
// Exports
|
||||
use std::{convert::TryInto, io};
|
||||
|
||||
use dcb_bytes::Bytes;
|
||||
pub use error::{DeserializeError, SerializeError};
|
||||
pub use header::Header;
|
||||
|
||||
// Imports
|
||||
use super::CardHeader;
|
||||
use crate::card::{self, property::CardType, Digimon, Digivolve, Item};
|
||||
use dcb_io::GameFile;
|
||||
|
||||
/// Table storing all cards.
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
@ -29,13 +32,8 @@ pub struct Table {
|
||||
|
||||
// Constants
|
||||
impl Table {
|
||||
/*
|
||||
/// The max size of the card table
|
||||
// TODO: Check the theoretical max, which is currently thought to be `0x14ff5`
|
||||
pub const MAX_BYTE_SIZE: usize = 0x14970;
|
||||
/// The start address of the card table
|
||||
pub const START_ADDRESS: Data = Data::from_u64(0x216d000);
|
||||
*/
|
||||
/// The file of the card table
|
||||
pub const PATH: &'static str = "B:\\CARD2.CDD";
|
||||
}
|
||||
|
||||
// Utils
|
||||
@ -45,30 +43,16 @@ impl Table {
|
||||
pub fn card_count(&self) -> usize {
|
||||
self.digimons.len() + self.items.len() + self.digivolves.len()
|
||||
}
|
||||
|
||||
/// Returns the byte size of all cards in this table
|
||||
#[must_use]
|
||||
#[rustfmt::skip]
|
||||
pub fn cards_byte_size(&self) -> usize {
|
||||
self.digimons .len() * (0x3 + CardType::Digimon .byte_size() + 0x1) +
|
||||
self.items .len() * (0x3 + CardType::Item .byte_size() + 0x1) +
|
||||
self.digivolves.len() * (0x3 + CardType::Digivolve.byte_size() + 0x1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Table {
|
||||
/// Deserializes the card table from a game file
|
||||
pub fn deserialize(_file: &mut GameFile) -> Result<Self, DeserializeError> {
|
||||
todo!();
|
||||
/*
|
||||
// Seek to the table
|
||||
file.seek(std::io::SeekFrom::Start(Self::START_ADDRESS.as_u64()))
|
||||
.map_err(DeserializeError::Seek)?;
|
||||
|
||||
/// Deserializes the card table from it's file
|
||||
#[allow(clippy::similar_names)] // Reader and Header are different.
|
||||
pub fn deserialize<R: io::Read>(mut reader: R) -> Result<Self, DeserializeError> {
|
||||
// Read header
|
||||
let mut header_bytes = <Header as Bytes>::ByteArray::default();
|
||||
file.read_exact(&mut header_bytes).map_err(DeserializeError::ReadHeader)?;
|
||||
let header = Header::from_bytes(&header_bytes).map_err(DeserializeError::Header)?;
|
||||
reader.read_exact(&mut header_bytes).map_err(DeserializeError::ReadHeader)?;
|
||||
let header = Header::from_bytes(&header_bytes).map_err(DeserializeError::ParseHeader)?;
|
||||
|
||||
// Then check the number of each card
|
||||
let digimon_cards: usize = header.digimons_len.into();
|
||||
@ -79,18 +63,6 @@ impl Table {
|
||||
// And calculate the number of cards
|
||||
let cards_len = digimon_cards + item_cards + digivolve_cards;
|
||||
|
||||
// If there are too many cards, return Err
|
||||
let table_size = digimon_cards * (0x3 + CardType::Digimon.byte_size() + 0x1) +
|
||||
item_cards * (0x3 + CardType::Item.byte_size() + 0x1) +
|
||||
digivolve_cards * (0x3 + CardType::Digivolve.byte_size() + 0x1);
|
||||
if table_size > Self::MAX_BYTE_SIZE {
|
||||
return Err(DeserializeError::TooManyCards {
|
||||
digimon_cards,
|
||||
item_cards,
|
||||
digivolve_cards,
|
||||
});
|
||||
}
|
||||
|
||||
// Create the arrays with capacity
|
||||
let mut digimons = Vec::with_capacity(digimon_cards);
|
||||
let mut items = Vec::with_capacity(item_cards);
|
||||
@ -100,7 +72,8 @@ impl Table {
|
||||
for card_id in 0..cards_len {
|
||||
// Read card header bytes
|
||||
let mut card_header_bytes = [0u8; 0x3];
|
||||
file.read_exact(&mut card_header_bytes)
|
||||
reader
|
||||
.read_exact(&mut card_header_bytes)
|
||||
.map_err(|err| DeserializeError::ReadCardHeader { id: card_id, err })?;
|
||||
|
||||
// Read the header
|
||||
@ -116,39 +89,41 @@ impl Table {
|
||||
match card_header.ty {
|
||||
CardType::Digimon => {
|
||||
let mut digimon_bytes = [0; std::mem::size_of::<<Digimon as Bytes>::ByteArray>()];
|
||||
file.read_exact(&mut digimon_bytes).map_err(|err| DeserializeError::ReadCard {
|
||||
reader.read_exact(&mut digimon_bytes).map_err(|err| DeserializeError::ReadCard {
|
||||
id: card_id,
|
||||
card_type: card_header.ty,
|
||||
err,
|
||||
})?;
|
||||
let digimon = Digimon::from_bytes(&digimon_bytes).map_err(|err| DeserializeError::DigimonCard { id: card_id, err })?;
|
||||
let digimon = Digimon::from_bytes(&digimon_bytes).map_err(|err| DeserializeError::ParseDigimonCard { id: card_id, err })?;
|
||||
digimons.push(digimon);
|
||||
},
|
||||
CardType::Item => {
|
||||
let mut item_bytes = [0; std::mem::size_of::<<Item as Bytes>::ByteArray>()];
|
||||
file.read_exact(&mut item_bytes).map_err(|err| DeserializeError::ReadCard {
|
||||
reader.read_exact(&mut item_bytes).map_err(|err| DeserializeError::ReadCard {
|
||||
id: card_id,
|
||||
card_type: card_header.ty,
|
||||
err,
|
||||
})?;
|
||||
let item = Item::from_bytes(&item_bytes).map_err(|err| DeserializeError::ItemCard { id: card_id, err })?;
|
||||
let item = Item::from_bytes(&item_bytes).map_err(|err| DeserializeError::ParseItemCard { id: card_id, err })?;
|
||||
items.push(item);
|
||||
},
|
||||
CardType::Digivolve => {
|
||||
let mut digivolve_bytes = [0; std::mem::size_of::<<Digivolve as Bytes>::ByteArray>()];
|
||||
file.read_exact(&mut digivolve_bytes).map_err(|err| DeserializeError::ReadCard {
|
||||
reader.read_exact(&mut digivolve_bytes).map_err(|err| DeserializeError::ReadCard {
|
||||
id: card_id,
|
||||
card_type: card_header.ty,
|
||||
err,
|
||||
})?;
|
||||
let digivolve = Digivolve::from_bytes(&digivolve_bytes).map_err(|err| DeserializeError::DigivolveCard { id: card_id, err })?;
|
||||
let digivolve =
|
||||
Digivolve::from_bytes(&digivolve_bytes).map_err(|err| DeserializeError::ParseDigivolveCard { id: card_id, err })?;
|
||||
digivolves.push(digivolve);
|
||||
},
|
||||
}
|
||||
|
||||
// Skip null terminator
|
||||
let mut null_terminator = [0; 1];
|
||||
file.read_exact(&mut null_terminator)
|
||||
reader
|
||||
.read_exact(&mut null_terminator)
|
||||
.map_err(|err| DeserializeError::ReadCardFooter { id: card_id, err })?;
|
||||
if null_terminator[0] != 0 {
|
||||
log::warn!("Card with id {}'s null terminator was {} instead of 0", card_id, null_terminator[0]);
|
||||
@ -157,52 +132,42 @@ impl Table {
|
||||
|
||||
// Return the table
|
||||
Ok(Self { digimons, items, digivolves })
|
||||
*/
|
||||
}
|
||||
|
||||
/// Serializes this card table to `file`.
|
||||
pub fn serialize(&self, _file: &mut GameFile) -> Result<(), SerializeError> {
|
||||
let _ = self;
|
||||
todo!()
|
||||
/*
|
||||
// Get the final table size
|
||||
let table_size = self.cards_byte_size();
|
||||
|
||||
// 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(),
|
||||
digivolve_cards: self.digivolves.len(),
|
||||
});
|
||||
}
|
||||
|
||||
// Seek to the beginning of the card table
|
||||
file.seek(std::io::SeekFrom::Start(Self::START_ADDRESS.as_u64()))
|
||||
.map_err(SerializeError::Seek)?;
|
||||
|
||||
/// Serializes this card table to a file
|
||||
pub fn serialize<R: io::Write>(&self, mut writer: R) -> Result<(), SerializeError> {
|
||||
// Write header
|
||||
let mut header_bytes = [0u8; 0x8];
|
||||
let header = Header {
|
||||
digimons_len: self.digimons.len().try_into().expect("Number of digimon cards exceeded `u16`"),
|
||||
items_len: self.items.len().try_into().expect("Number of item cards exceeded `u8`"),
|
||||
digivolves_len: self.digivolves.len().try_into().expect("Number of digivolve cards exceeded `u8`"),
|
||||
digimons_len: self
|
||||
.digimons
|
||||
.len()
|
||||
.try_into()
|
||||
.map_err(|_err| SerializeError::TooManyDigimon(self.digimons.len()))?,
|
||||
items_len: self
|
||||
.items
|
||||
.len()
|
||||
.try_into()
|
||||
.map_err(|_err| SerializeError::TooManyItems(self.items.len()))?,
|
||||
digivolves_len: self
|
||||
.digivolves
|
||||
.len()
|
||||
.try_into()
|
||||
.map_err(|_err| SerializeError::TooManyDigivolves(self.digivolves.len()))?,
|
||||
};
|
||||
let mut header_bytes = [0u8; 0x8];
|
||||
header.to_bytes(&mut header_bytes).into_ok();
|
||||
|
||||
// And write the header
|
||||
file.write_all(&header_bytes).map_err(SerializeError::WriteHeader)?;
|
||||
writer.write_all(&header_bytes).map_err(SerializeError::WriteHeader)?;
|
||||
|
||||
// Macro to help write all cards to file
|
||||
macro_rules! write_card {
|
||||
($cards:expr, $prev_ids:expr, $card_type:ident, $($on_err:tt)*) => {
|
||||
($cards:expr, $prev_ids:expr, $card_type:ident, $($on_err:tt)*) => {{
|
||||
for (rel_id, card) in $cards.iter().enumerate() {
|
||||
// Current id through the whole table
|
||||
let cur_id = $prev_ids + rel_id;
|
||||
|
||||
// Card bytes
|
||||
let mut card_bytes = [0; 0x3 + CardType::$card_type.byte_size() + 0x1];
|
||||
let bytes = array_split_mut!(&mut card_bytes,
|
||||
let bytes = dcb_util::array_split_mut!(&mut card_bytes,
|
||||
header : [0x3],
|
||||
card : [CardType::$card_type.byte_size()],
|
||||
footer : 1,
|
||||
@ -215,7 +180,7 @@ impl Table {
|
||||
card_header.to_bytes(bytes.header).into_ok();
|
||||
|
||||
// Write the card
|
||||
#[allow(unreachable_code)] // FIXME: Remove this
|
||||
#[allow(unreachable_code)] // Might be `!`
|
||||
card
|
||||
.to_bytes(bytes.card)
|
||||
.map_err(|err| {$($on_err)*}(err, cur_id))?;
|
||||
@ -224,32 +189,28 @@ impl Table {
|
||||
*bytes.footer = 0;
|
||||
|
||||
log::trace!("#{}: Writing {}", cur_id, CardType::$card_type);
|
||||
file.write_all(&card_bytes)
|
||||
writer.write_all(&card_bytes)
|
||||
.map_err(|err| SerializeError::WriteCard {
|
||||
id: cur_id,
|
||||
card_type: CardType::$card_type,
|
||||
err
|
||||
})?;
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
// Write all cards
|
||||
{
|
||||
// Buffer, Offset, Type, Error closure
|
||||
write_card! { self.digimons , 0 , Digimon ,
|
||||
|err, cur_id| SerializeError::SerializeDigimonCard { id: cur_id, err }
|
||||
}
|
||||
write_card! { self.items , self.digimons.len() , Item ,
|
||||
|err, cur_id| SerializeError::SerializeItemCard { id: cur_id, err }
|
||||
}
|
||||
write_card! { self.digivolves, self.digimons.len() + self.items.len(), Digivolve,
|
||||
|err, _| err
|
||||
}
|
||||
write_card! { self.digimons, 0, Digimon,
|
||||
|err, cur_id| SerializeError::SerializeDigimonCard { id: cur_id, err }
|
||||
}
|
||||
write_card! { self.items, self.digimons.len(), Item,
|
||||
|err, cur_id| SerializeError::SerializeItemCard { id: cur_id, err }
|
||||
}
|
||||
write_card! { self.digivolves, self.digimons.len() + self.items.len(), Digivolve,
|
||||
|err, _| err
|
||||
}
|
||||
|
||||
// And return Ok
|
||||
Ok(())
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,40 +3,16 @@
|
||||
// Imports
|
||||
use super::{card, header, CardType};
|
||||
|
||||
/// Error type for [`Table::deserialize`]
|
||||
/// Error type for [`Table::deserialize`](super::Table::deserialize)
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum DeserializeError {
|
||||
/// Unable to seek game file
|
||||
#[error("Unable to seek game file to card table")]
|
||||
Seek(#[source] std::io::Error),
|
||||
|
||||
/// Unable to read table header
|
||||
#[error("Unable to read table header")]
|
||||
ReadHeader(#[source] std::io::Error),
|
||||
|
||||
/// Unable to parse table header
|
||||
#[error("Unable to parse table header")]
|
||||
Header(#[source] header::FromBytesError),
|
||||
|
||||
/// There were too many cards
|
||||
#[error(
|
||||
"Too many cards in table ({digimon_cards} digimon, {item_cards} item, {digivolve_cards} digivolve, {} / {} bytes max)",
|
||||
digimon_cards * (0x3 + CardType::Digimon .byte_size() + 0x1) +
|
||||
item_cards * (0x3 + CardType::Item .byte_size() + 0x1) +
|
||||
digivolve_cards * (0x3 + CardType::Digivolve.byte_size() + 0x1),
|
||||
0
|
||||
//Table::MAX_BYTE_SIZE
|
||||
)]
|
||||
TooManyCards {
|
||||
/// Number of digimon cards
|
||||
digimon_cards: usize,
|
||||
|
||||
/// Number of item cards
|
||||
item_cards: usize,
|
||||
|
||||
/// Number of digivolve cards
|
||||
digivolve_cards: usize,
|
||||
},
|
||||
ParseHeader(#[source] header::FromBytesError),
|
||||
|
||||
/// Unable to read card header
|
||||
#[error("Unable to read card header for card id {}", id)]
|
||||
@ -76,7 +52,7 @@ pub enum DeserializeError {
|
||||
|
||||
/// Unable to deserialize a digimon card
|
||||
#[error("Unable to deserialize digimon card with id {}", id)]
|
||||
DigimonCard {
|
||||
ParseDigimonCard {
|
||||
/// Id of card
|
||||
id: usize,
|
||||
|
||||
@ -87,7 +63,7 @@ pub enum DeserializeError {
|
||||
|
||||
/// Unable to deserialize an item card
|
||||
#[error("Unable to deserialize item card with id {}", id)]
|
||||
ItemCard {
|
||||
ParseItemCard {
|
||||
/// Id of card
|
||||
id: usize,
|
||||
|
||||
@ -98,7 +74,7 @@ pub enum DeserializeError {
|
||||
|
||||
/// Unable to deserialize a digivolve card
|
||||
#[error("Unable to deserialize digivolve card with id {}", id)]
|
||||
DigivolveCard {
|
||||
ParseDigivolveCard {
|
||||
/// Id of card
|
||||
id: usize,
|
||||
|
||||
@ -119,32 +95,20 @@ pub enum DeserializeError {
|
||||
},
|
||||
}
|
||||
|
||||
/// Error type for [`Table::serialize`]
|
||||
/// Error type for [`Table::serialize`](super::Table::serialize)
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum SerializeError {
|
||||
/// There were too many cards
|
||||
#[error(
|
||||
"Too many cards in table ({digimon_cards} digimon, {item_cards} item, {digivolve_cards} digivolve, {} / {} bytes max)",
|
||||
digimon_cards * (0x3 + CardType::Digimon .byte_size() + 0x1) +
|
||||
item_cards * (0x3 + CardType::Item .byte_size() + 0x1) +
|
||||
digivolve_cards * (0x3 + CardType::Digivolve.byte_size() + 0x1),
|
||||
0
|
||||
//Table::MAX_BYTE_SIZE
|
||||
)]
|
||||
TooManyCards {
|
||||
/// Number of digimon cards
|
||||
digimon_cards: usize,
|
||||
/// Number of digimons must fit within a `u16`
|
||||
#[error("Number of digimons must fit within a `u16` (was {_0})")]
|
||||
TooManyDigimon(usize),
|
||||
|
||||
/// Number of item cards
|
||||
item_cards: usize,
|
||||
/// Number of items must fit within a `u8`
|
||||
#[error("Number of items must fit within a `u8` (was {_0})")]
|
||||
TooManyItems(usize),
|
||||
|
||||
/// Number of digivolve cards
|
||||
digivolve_cards: usize,
|
||||
},
|
||||
|
||||
/// Unable to seek game file
|
||||
#[error("Unable to seek game file to card table")]
|
||||
Seek(#[source] std::io::Error),
|
||||
/// Number of digivolves must fit within a `u8`
|
||||
#[error("Number of digivolves must fit within a `u8` (was {_0})")]
|
||||
TooManyDigivolves(usize),
|
||||
|
||||
/// Unable to write table header
|
||||
#[error("Unable to write table header")]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user