zipatch-rs 1.5.0

Parser for FFXIV ZiPatch patch files
Documentation
use binrw::{BinRead, BinResult, Endian};
use std::io::Cursor;

/// `binrw` parse helper: read a `u32 BE` length prefix then that many bytes,
/// trim trailing NUL bytes, and decode as UTF-8.
///
/// Used by [`crate::chunk::adir::AddDirectory`] and
/// [`crate::chunk::ddir::DeleteDirectory`] for their directory name fields.
/// Returns a `binrw::Error::Custom` wrapping the underlying
/// [`std::string::FromUtf8Error`] if the bytes are not valid UTF-8.
pub(crate) fn read_null_trimmed_utf8<R: std::io::Read + std::io::Seek>(
    reader: &mut R,
    endian: Endian,
    (): (),
) -> BinResult<String> {
    let len = <u32 as BinRead>::read_options(reader, endian, ())?;
    let mut bytes = vec![0u8; len as usize];
    reader.read_exact(&mut bytes)?;
    String::from_utf8(bytes)
        .map(|s| s.trim_end_matches('\0').to_owned())
        .map_err(|e| binrw::Error::Custom {
            pos: 0,
            err: Box::new(e),
        })
}

/// Wrap `body` in a [`Cursor`] and deserialise a big-endian `T` via `binrw`.
///
/// Convenience wrapper so individual chunk parsers don't need to repeat the
/// `Cursor::new` + `read_be` + `?` pattern on every call site.
pub(crate) fn parse_be<T: for<'a> BinRead<Args<'a> = ()>>(body: &[u8]) -> crate::Result<T> {
    Ok(T::read_be(&mut Cursor::new(body))?)
}