Added a write_maybe_null_ascii_string to game::util.

Finished `Bytes` implementation for `game::deck::table::Table`.
This commit is contained in:
2020-05-01 19:05:11 +01:00
parent a1c9867ccb
commit ddfa0636af
3 changed files with 125 additions and 16 deletions

View File

@@ -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<Self, Self::FromError> {
// 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(())
}
}

View File

@@ -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<F>(game_file: &mut GameFile<F>) -> Result<Self, DeserializeError>
pub fn deserialize<R>(game_file: &mut GameFile<R>) -> Result<Self, DeserializeError>
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<R>(&self, game_file: &mut GameFile<R>) -> 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(())
}
}

View File

@@ -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)
}