From a1c9867ccb1b5c1e089c170d4646926340e0bacd Mon Sep 17 00:00:00 2001 From: Filipe Rodrigues Date: Fri, 1 May 2020 14:08:13 +0100 Subject: [PATCH] Added a `read_maybe_null_ascii_string` to `game::util`. Added information to `Decks`. All decks are now read with `game::deck::Table`. --- src/game/deck/deck.rs | 78 ++++++++++++++++++++++++++++++++++++++++++ src/game/deck/table.rs | 54 +++++++++++++---------------- src/game/util.rs | 22 ++++++++++++ 3 files changed, 124 insertions(+), 30 deletions(-) diff --git a/src/game/deck/deck.rs b/src/game/deck/deck.rs index ecd6b99..3ab9597 100644 --- a/src/game/deck/deck.rs +++ b/src/game/deck/deck.rs @@ -1,8 +1,86 @@ //! Decks +// byteorder +use byteorder::{ByteOrder, LittleEndian}; + +// Crate +use crate::game::{util, Bytes}; + /// A deck #[derive(Debug)] #[derive(::serde::Serialize, ::serde::Deserialize)] pub struct Deck { + /// Name of this deck + pub name: ascii::AsciiString, + + /// Digimon who plays this deck + pub owner: ascii::AsciiString, + + /// All of the card ids that make up this deck pub cards: [u16; 30], } + +/// Error type for [`Bytes::from_bytes`] +#[derive(Debug, derive_more::Display, err_impl::Error)] +pub enum FromBytesError { + /// Unable to read the deck name + #[display(fmt = "Unable to read the deck name")] + Name(#[error(source)] util::ReadMaybeNullAsciiStringError), + + /// Unable to read the deck owner + #[display(fmt = "Unable to read the deck owner")] + Owner(#[error(source)] util::ReadMaybeNullAsciiStringError), +} + +/// Error type for [`Bytes::to_bytes`] +#[derive(Debug, derive_more::Display, err_impl::Error)] +pub enum ToBytesError { + /// Unable to write the deck name + #[display(fmt = "Unable to write the deck name")] + Name(#[error(source)] util::WriteNullAsciiStringError), + + /// Unable to write the deck owner + #[display(fmt = "Unable to write the deck owner")] + Owner(#[error(source)] util::WriteNullAsciiStringError), +} + +impl Bytes for Deck { + type ByteArray = [u8; 0x6e]; + type FromError = FromBytesError; + type ToError = ToBytesError; + + fn from_bytes(bytes: &Self::ByteArray) -> Result { + // Split the bytes + let bytes = util::array_split!(&bytes, + deck : [0x3c], + name : [0x13], + owner: [0x13], + + _unknown: [0xc], + ); + + Ok(Self { + name: util::read_maybe_null_ascii_string(bytes.name) + .map_err(FromBytesError::Name)? + .to_ascii_string(), + + owner: util::read_maybe_null_ascii_string(bytes.owner) + .map_err(FromBytesError::Owner)? + .to_ascii_string(), + + cards: { + let mut cards_buf = [0; 0x1e]; + + for (card_id, card) in cards_buf.iter_mut().enumerate() { + *card = LittleEndian::read_u16(&bytes.deck[0x0 + card_id * 2..0x2 + card_id * 2]); + } + + cards_buf + }, + }) + } + + fn to_bytes(&self, _bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> { + todo!(); + } +} diff --git a/src/game/deck/table.rs b/src/game/deck/table.rs index 0ca6b7c..a6a95b6 100644 --- a/src/game/deck/table.rs +++ b/src/game/deck/table.rs @@ -3,12 +3,9 @@ // Std use std::io::{Read, Seek, Write}; -// byteorder -use byteorder::{ByteOrder, LittleEndian}; - // Crate use crate::{ - game::Deck, + game::{deck::deck, Bytes, Deck}, io::{address::Data, GameFile}, }; @@ -41,14 +38,22 @@ pub enum DeserializeError { /// Unable to read table header #[display(fmt = "Unable to read table header")] ReadHeader(#[error(source)] std::io::Error), - /* - /// The magic of the table was wrong - #[display(fmt = "Found wrong table header magic (expected {:x}, found {:x})", Table::HEADER_MAGIC, "magic")] - HeaderMagic { magic: u32 }, - */ - /// Could not read a deck entry from the deck table - #[display(fmt = "Unable to fully read a deck entry (The file was too small)")] - DeckEntry(#[error(source)] std::io::Error), + + /// Could not read a deck entry + #[display(fmt = "Unable to read deck entry with id {}", "id")] + ReadDeck { + id: usize, + #[error(source)] + err: std::io::Error, + }, + + /// Could not parse a deck entry + #[display(fmt = "Unable to parse deck entry with id {}", "id")] + ParseDeck { + id: usize, + #[error(source)] + err: deck::FromBytesError, + }, } impl Table { @@ -64,25 +69,14 @@ impl Table { .seek(std::io::SeekFrom::Start(u64::from(Self::DECK_TABLE_START_ADDRESS))) .map_err(DeserializeError::Seek)?; - // Then loop until we're at the end of the table - //'table_loop: loop - for _ in 0..100 { - // Read the deck - let mut buf = [0u8; 110]; - game_file.read_exact(&mut buf).map_err(DeserializeError::DeckEntry)?; + // Then get each deck + for id in 0..159 { + // Read all bytes of the deck + let mut bytes = [0; 0x6e]; + game_file.read_exact(&mut bytes).map_err(|err| DeserializeError::ReadDeck { id, err })?; - // And construct the deck - let deck = Deck { - cards: { - let mut cards_buf = [0u16; 30]; - - for card_id in 0..30 { - cards_buf[card_id] = LittleEndian::read_u16(&buf[0x0 + card_id * 2..0x2 + card_id * 2]); - } - - cards_buf - }, - }; + // And try to serialize the deck + let deck = Deck::from_bytes(&bytes).map_err(|err| DeserializeError::ParseDeck { id, err })?; // And add it decks.push(deck); diff --git a/src/game/util.rs b/src/game/util.rs index eb74703..053d30e 100644 --- a/src/game/util.rs +++ b/src/game/util.rs @@ -134,6 +134,28 @@ pub fn read_null_ascii_string(buf: &impl AsRef<[u8]>) -> Result<&ascii::AsciiStr ascii::AsciiStr::from_ascii(buf).map_err(ReadNullAsciiStringError::NotAscii) } +/// Error type for [`read_maybe_null_ascii_string`] +#[derive(Debug)] +#[derive(derive_more::Display, err_impl::Error)] +pub enum ReadMaybeNullAsciiStringError { + /// The string was not ascii + #[display(fmt = "The buffer did not contain valid Ascii")] + NotAscii(#[error(source)] ascii::AsAsciiStrError), +} + +/// Reads a possibly null-terminated ascii string from a buffer. +pub fn read_maybe_null_ascii_string(buf: &impl AsRef<[u8]>) -> Result<&ascii::AsciiStr, ReadMaybeNullAsciiStringError> { + // Find the first null and trim the buffer until it + let buf = buf.as_ref(); + let buf = match buf.iter().position(|&b| b == 0) { + Some(null_idx) => &buf[0..null_idx], + None => buf, + }; + + // Then convert it from Ascii + ascii::AsciiStr::from_ascii(buf).map_err(ReadMaybeNullAsciiStringError::NotAscii) +} + /// Error type for [`write_null_ascii_string`] #[derive(Debug)] #[derive(derive_more::Display, err_impl::Error)]