atx_reader 0.1.0

Parser and decoder for Apple .atx texture archives (AAPL container with ASTC payload), as produced by tools like Cellebrite UFED iOS exports.
Documentation
/// All ASTC 2D block footprints, mirroring `astc-decode::Footprint`.
///
/// The HEAD chunk carries two u32 discriminator words at payload offsets
/// `0x4C` and `0x50`. Their exact mapping to a footprint is proprietary and
/// only verified for the (3, 5) → 4×4 sample currently in hand. Callers that
/// know the footprint up-front should specify it explicitly via
/// `DecodeOptions::footprint`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AstcFootprint {
    Astc4x4,
    Astc5x4,
    Astc5x5,
    Astc6x5,
    Astc6x6,
    Astc8x5,
    Astc8x6,
    Astc8x8,
    Astc10x5,
    Astc10x6,
    Astc10x8,
    Astc10x10,
    Astc12x10,
    Astc12x12,
}

impl AstcFootprint {
    pub const fn block_width(self) -> u32 {
        match self {
            Self::Astc4x4 => 4,
            Self::Astc5x4 | Self::Astc5x5 => 5,
            Self::Astc6x5 | Self::Astc6x6 => 6,
            Self::Astc8x5 | Self::Astc8x6 | Self::Astc8x8 => 8,
            Self::Astc10x5 | Self::Astc10x6 | Self::Astc10x8 | Self::Astc10x10 => 10,
            Self::Astc12x10 | Self::Astc12x12 => 12,
        }
    }

    pub const fn block_height(self) -> u32 {
        match self {
            Self::Astc4x4 | Self::Astc5x4 => 4,
            Self::Astc5x5 | Self::Astc6x5 | Self::Astc8x5 | Self::Astc10x5 => 5,
            Self::Astc6x6 | Self::Astc8x6 | Self::Astc10x6 => 6,
            Self::Astc8x8 | Self::Astc10x8 => 8,
            Self::Astc10x10 | Self::Astc12x10 => 10,
            Self::Astc12x12 => 12,
        }
    }

    /// Best-effort lookup from the `(code_a, code_b)` pair at HEAD payload
    /// offsets `0x4C` / `0x50`. Returns `None` for unknown combinations.
    ///
    /// Confirmed mappings (all observed samples are ASTC 4×4):
    ///
    /// - `(3, 5)` — `astc` chunk variant (iOS lock-screen / snapshot)
    /// - `(3, 1)` — `astc` chunk variant (snapshot switcher floating layer)
    /// - `(1, 1)` — `LZFS` chunk variant (camera thumbnail)
    ///
    /// Returns `None` for unknown combinations. Add new mappings here as
    /// fresh samples come in.
    pub const fn from_format_codes(code_a: u32, code_b: u32) -> Option<Self> {
        match (code_a, code_b) {
            (3, 5) | (3, 1) | (1, 1) => Some(Self::Astc4x4),
            _ => None,
        }
    }

    #[cfg(feature = "decode")]
    pub(crate) fn to_astc_decode(self) -> astc_decode::Footprint {
        use astc_decode::Footprint as F;
        match self {
            Self::Astc4x4 => F::ASTC_4X4,
            Self::Astc5x4 => F::ASTC_5X4,
            Self::Astc5x5 => F::ASTC_5X5,
            Self::Astc6x5 => F::ASTC_6X5,
            Self::Astc6x6 => F::ASTC_6X6,
            Self::Astc8x5 => F::ASTC_8X5,
            Self::Astc8x6 => F::ASTC_8X6,
            Self::Astc8x8 => F::ASTC_8X8,
            Self::Astc10x5 => F::ASTC_10X5,
            Self::Astc10x6 => F::ASTC_10X6,
            Self::Astc10x8 => F::ASTC_10X8,
            Self::Astc10x10 => F::ASTC_10X10,
            Self::Astc12x10 => F::ASTC_12X10,
            Self::Astc12x12 => F::ASTC_12X12,
        }
    }
}