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
//! Print the container structure of an `.atx` file without decoding it.
//!
//! ```text
//! cargo run --example inspect -- path/to/file.atx
//! ```

use std::env;
use std::fs;
use std::process::ExitCode;

use atx_reader::{AstcFootprint, AtxContainer};

fn main() -> ExitCode {
    let Some(input) = env::args().nth(1) else {
        eprintln!("usage: inspect <input.atx>");
        return ExitCode::from(2);
    };

    let bytes = match fs::read(&input) {
        Ok(b) => b,
        Err(e) => {
            eprintln!("read {input}: {e}");
            return ExitCode::FAILURE;
        }
    };

    let container = match AtxContainer::parse(&bytes) {
        Ok(c) => c,
        Err(e) => {
            eprintln!("parse: {e}");
            return ExitCode::FAILURE;
        }
    };

    println!("{} ({} bytes)", input, bytes.len());
    println!("AAPL container OK");
    println!("chunks:");
    for chunk in container.chunks() {
        println!(
            "  @0x{:06x}  '{}'  payload={} bytes",
            chunk.offset,
            chunk.tag_str(),
            chunk.payload.len()
        );
    }

    match container.header() {
        Ok(h) => {
            println!();
            println!("HEAD:");
            println!("  flags         = {}", h.flags);
            println!("  size          = {}x{}", h.width, h.height);
            println!("  depth         = {}", h.depth);
            println!("  array_layers  = {}", h.array_layers);
            println!("  mipmap_count  = {}", h.mipmap_count);
            println!("  uuid          = {}", hex(&h.uuid));
            println!(
                "  format_codes  = ({}, {})  -> {:?}",
                h.format_code_a,
                h.format_code_b,
                AstcFootprint::from_format_codes(h.format_code_a, h.format_code_b),
            );
        }
        Err(e) => eprintln!("HEAD parse error: {e}"),
    }

    match container.astc_payload() {
        Ok(p) => println!("astc payload: {} bytes ({} blocks)", p.len(), p.len() / 16),
        Err(e) => eprintln!("astc payload: {e}"),
    }

    ExitCode::SUCCESS
}

fn hex(bytes: &[u8]) -> String {
    let mut s = String::with_capacity(bytes.len() * 2);
    for b in bytes {
        use std::fmt::Write;
        let _ = write!(&mut s, "{b:02x}");
    }
    s
}