gbfs 0.3.0

A crate for reading gameboy filesystem archives, a format commonly used in GBA homebrew games.
Documentation
const MAGIC: &[u8] = "PinEightGBFS\r\n\u{1a}\n".as_bytes();
pub(crate) const GBFS_HEADER_LENGTH: usize = 32;

use crate::GBFSError;

#[derive(Debug, Clone)]
pub(crate) struct GBFSHeader {
    pub(crate) total_len: u32,       /* total length of archive */
    pub(crate) dir_off: u16,         /* offset in bytes to directory */
    pub(crate) dir_num_members: u16, /* number of files */
}

impl GBFSHeader {
    pub(crate) const fn from_slice(
        data: &[u8; GBFS_HEADER_LENGTH],
    ) -> Result<GBFSHeader, GBFSError> {
        // I apologize for the ugly code below. It's needed due to const fn limitations.

        // Ensure magic is correct by checking char-for-char
        let mut i = 0;
        while i < 16 {
            if data[i] != MAGIC[i] {
                return Err(GBFSError::HeaderInvalid);
            }
            i += 1;
        }

        // Read total length of archive
        let total_len = u32::from_le_bytes([data[16], data[17], data[18], data[19]]);
        // Read offset in bytes to directory
        let dir_off = u16::from_le_bytes([data[20], data[21]]);
        // Read number of files in dir
        let dir_num_members = u16::from_le_bytes([data[22], data[23]]);

        // Ensure reserved bytes are unused; otherwise, we may be trying to interpret a newer, unknown version
        // of the format.
        // We can't use assert_eq! here because it's not permitted in const fn's.
        let mut i = 24;
        // Are reserved bytes 0 as expected?
        while i < 32 {
            if data[i] != 0 {
                return Err(GBFSError::HeaderInvalid);
            }
            i += 1;
        }
        return Ok(GBFSHeader {
            total_len,
            dir_off,
            dir_num_members,
        });
    }
}