From ddfa0636af68fc7c6cd6156929f150792c762f5a Mon Sep 17 00:00:00 2001 From: Filipe Rodrigues Date: Fri, 1 May 2020 19:05:11 +0100 Subject: [PATCH] Added a `write_maybe_null_ascii_string` to `game::util`. Finished `Bytes` implementation for `game::deck::table::Table`. --- src/game/deck/deck.rs | 45 ++++++++++++++++++++++------- src/game/deck/table.rs | 64 ++++++++++++++++++++++++++++++++++++++---- src/game/util.rs | 32 +++++++++++++++++++++ 3 files changed, 125 insertions(+), 16 deletions(-) diff --git a/src/game/deck/deck.rs b/src/game/deck/deck.rs index 3ab9597..933c769 100644 --- a/src/game/deck/deck.rs +++ b/src/game/deck/deck.rs @@ -18,6 +18,9 @@ pub struct Deck { /// All of the card ids that make up this deck pub cards: [u16; 30], + + // Unknown + unknown: [u8; 0xc], } /// Error type for [`Bytes::from_bytes`] @@ -37,11 +40,11 @@ pub enum FromBytesError { pub enum ToBytesError { /// Unable to write the deck name #[display(fmt = "Unable to write the deck name")] - Name(#[error(source)] util::WriteNullAsciiStringError), + Name(#[error(source)] util::WriteMaybeNullAsciiStringError), /// Unable to write the deck owner #[display(fmt = "Unable to write the deck owner")] - Owner(#[error(source)] util::WriteNullAsciiStringError), + Owner(#[error(source)] util::WriteMaybeNullAsciiStringError), } impl Bytes for Deck { @@ -51,12 +54,11 @@ impl Bytes for Deck { fn from_bytes(bytes: &Self::ByteArray) -> Result { // Split the bytes - let bytes = util::array_split!(&bytes, - deck : [0x3c], - name : [0x13], - owner: [0x13], - - _unknown: [0xc], + let bytes = util::array_split!(bytes, + deck : [0x3c], + name : [0x13], + owner : [0x13], + unknown: [0xc], ); Ok(Self { @@ -77,10 +79,33 @@ impl Bytes for Deck { cards_buf }, + + unknown: *bytes.unknown, }) } - fn to_bytes(&self, _bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> { - todo!(); + fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> { + // Split the bytes + let bytes = util::array_split_mut!(bytes, + deck : [0x3c], + name : [0x13], + owner : [0x13], + unknown: [0xc], + ); + + // Nanme / 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)?; + + // Deck + for (card_id, card) in self.cards.iter().enumerate() { + LittleEndian::write_u16(&mut bytes.deck[0x0 + card_id * 2..0x2 + card_id * 2], *card); + } + + // Unknown + *bytes.unknown = self.unknown; + + // And return Ok + Ok(()) } } diff --git a/src/game/deck/table.rs b/src/game/deck/table.rs index a6a95b6..309f854 100644 --- a/src/game/deck/table.rs +++ b/src/game/deck/table.rs @@ -47,19 +47,48 @@ pub enum DeserializeError { err: std::io::Error, }, - /// Could not parse a deck entry - #[display(fmt = "Unable to parse deck entry with id {}", "id")] - ParseDeck { + /// Could not deserialize a deck entry + #[display(fmt = "Unable to serialize deck entry with id {}", "id")] + DeserializeDeck { id: usize, #[error(source)] err: deck::FromBytesError, }, } +/// Error type for [`Table::serialize`] +#[derive(Debug)] +#[derive(derive_more::Display, err_impl::Error)] +pub enum SerializeError { + /// Unable to seek game file + #[display(fmt = "Unable to seek game file to card table")] + Seek(#[error(source)] std::io::Error), + + /// Unable to read table header + #[display(fmt = "Unable to read table header")] + WriteHeader(#[error(source)] std::io::Error), + + /// Could not deserialize a deck entry + #[display(fmt = "Unable to deserialize deck entry with id {}", "id")] + SerializeDeck { + id: usize, + #[error(source)] + err: deck::ToBytesError, + }, + + /// Could not write a deck entry + #[display(fmt = "Unable to read deck entry with id {}", "id")] + WriteDeck { + id: usize, + #[error(source)] + err: std::io::Error, + }, +} + impl Table { - pub fn deserialize(game_file: &mut GameFile) -> Result + pub fn deserialize(game_file: &mut GameFile) -> Result where - F: Read + Write + Seek, + R: Read + Write + Seek, { // The deck array let mut decks = vec![]; @@ -76,7 +105,7 @@ impl Table { game_file.read_exact(&mut bytes).map_err(|err| DeserializeError::ReadDeck { id, err })?; // And try to serialize the deck - let deck = Deck::from_bytes(&bytes).map_err(|err| DeserializeError::ParseDeck { id, err })?; + let deck = Deck::from_bytes(&bytes).map_err(|err| DeserializeError::DeserializeDeck { id, err })?; // And add it decks.push(deck); @@ -85,4 +114,27 @@ impl Table { // And return the table Ok(Self { decks }) } + + pub fn serialize(&self, game_file: &mut GameFile) -> Result<(), SerializeError> + where + R: Read + Write + Seek, + { + // Seek to the beginning of the deck table + game_file + .seek(std::io::SeekFrom::Start(u64::from(Self::DECK_TABLE_START_ADDRESS))) + .map_err(SerializeError::Seek)?; + + // Then get each deck + for (id, deck) in self.decks.iter().enumerate() { + // Parse each deck into bytes + let mut bytes = [0; 0x6e]; + deck.to_bytes(&mut bytes).map_err(|err| SerializeError::SerializeDeck { id, err })?; + + // And write them to file + game_file.write(&bytes).map_err(|err| SerializeError::WriteDeck { id, err })?; + } + + // And return Ok + Ok(()) + } } diff --git a/src/game/util.rs b/src/game/util.rs index 053d30e..4c6a67b 100644 --- a/src/game/util.rs +++ b/src/game/util.rs @@ -183,3 +183,35 @@ pub fn write_null_ascii_string<'a>(input: &ascii::AsciiStr, buf: &'a mut [u8]) - // And return Ok with the buffer Ok(buf) } + +/// Error type for [`write_maybe_null_ascii_string`] +#[derive(Debug)] +#[derive(derive_more::Display, err_impl::Error)] +pub enum WriteMaybeNullAsciiStringError { + /// The input string was too large + #[display(fmt = "Input string was too large for buffer. ({} / {})", "input_len", "buffer_len")] + TooLarge { input_len: usize, buffer_len: usize }, +} + +/// Writes a possibly null-terminated ascii string to a buffer and returns it +pub fn write_maybe_null_ascii_string<'a>(input: &ascii::AsciiStr, buf: &'a mut [u8]) -> Result<&'a mut [u8], WriteMaybeNullAsciiStringError> { + // 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(), + buffer_len: buf.len(), + }); + } + + // Copy everything over to the slice + // Note: We leave all other bytes as they are, no need to set them to 0 + buf[0..input.len()].copy_from_slice(input.as_bytes()); + + // If there's a character left, write it to null + if let Some(null_byte) = buf.get_mut(input.len()) { + *null_byte = 0; + } + + // And return Ok with the buffer + Ok(buf) +}