Fix and simplify string property parsing, add test

I figured out what the actual layout of this property is (instead of
guessing, which created some bad results.) And added tests of course!

Also fixed a bug in read_string_with_length() that didn't handle the
case where the string length is zero.
This commit is contained in:
Joshua Goins 2025-02-23 15:52:21 -05:00
parent 6b7d4c66dd
commit 21ab9f2cb0
2 changed files with 62 additions and 8 deletions

View file

@ -1,3 +1,4 @@
use std::io::Seek;
use binrw::{BinRead, BinReaderExt, BinResult}; use binrw::{BinRead, BinReaderExt, BinResult};
pub(crate) fn read_bool_from<T: From<u8> + PartialEq>(x: T) -> bool { pub(crate) fn read_bool_from<T: From<u8> + PartialEq>(x: T) -> bool {
@ -6,8 +7,12 @@ pub(crate) fn read_bool_from<T: From<u8> + PartialEq>(x: T) -> bool {
#[binrw::parser(reader, endian)] #[binrw::parser(reader, endian)]
pub(crate) fn read_string_with_length() -> BinResult<String> { pub(crate) fn read_string_with_length() -> BinResult<String> {
let length = u32::read_le(reader)? as usize;
if length == 0 {
return Ok(String::default());;
}
// last byte is the null terminator which Rust ignores // last byte is the null terminator which Rust ignores
let length = u32::read_le(reader)? as usize - 1; let length = length - 1;
let mut bytes: Vec<u8> = vec![0u8; length]; let mut bytes: Vec<u8> = vec![0u8; length];
// TODO: there was to be way to read this all in one go? // TODO: there was to be way to read this all in one go?
for i in 0..length { for i in 0..length {

View file

@ -1,14 +1,63 @@
use binrw::binrw; use binrw::binrw;
use crate::common::read_string_with_length;
#[binrw] #[binrw]
#[derive(Debug)] #[derive(Debug)]
pub struct StrProperty { pub struct StrProperty {
#[br(temp)]
#[bw(ignore)]
#[br(pad_after = 5)] #[br(pad_after = 5)]
pub name_length: u32, pub size_in_bytes: u32,
#[br(count = name_length)]
#[bw(map = |x : &String | x.as_bytes())] #[br(parse_with = read_string_with_length)]
#[br(map = | x: Vec<u8> | String::from_utf8(x).unwrap().trim_matches(char::from(0)).to_string())] #[bw(ignore)]
pub name: String, pub value: String,
}
#[cfg(test)]
mod tests {
use super::*;
use binrw::BinRead;
use std::io::Cursor;
#[test]
fn empty_string() {
// From Slot.sav
let data = [
0x04, 0x00, 0x00,
0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
0x00, 0x0b, 0x00,
0x00, 0x00, 0x4c,
0x6f, 0x61, 0x64,
0x4f, 0x70, 0x74,
0x69, 0x6f, 0x6e,
0x00,
];
let mut cursor = Cursor::new(data);
let decoded = StrProperty::read_le(&mut cursor).unwrap();
assert_eq!(decoded.value, "");
}
#[test]
fn regular_string() {
// From Slot.sav
let data = [
0x1e, 0x00, 0x00,
0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
0x1a, 0x00, 0x00,
0x00, 0x41, 0x52,
0x30, 0x58, 0x4a,
0x47, 0x46, 0x57,
0x41, 0x36, 0x48,
0x4e, 0x49, 0x51,
0x31, 0x41, 0x41,
0x55, 0x4a, 0x39,
0x55, 0x52, 0x38,
0x32, 0x38, 0x00,
];
let mut cursor = Cursor::new(data);
let decoded = StrProperty::read_le(&mut cursor).unwrap();
assert_eq!(decoded.value, "AR0XJGFWA6HNIQ1AAUJ9UR828");
}
} }