//! Dump an XBG7 resource's descriptor around each index marker, to see whether a //! grouped pool carries a declaration PER sub-mesh (the 2026-08-13 mixed-stride //! finding says it must: n201's four sub-meshes use strides 24/24/24/28 and four //! different vertex shaders). //! Usage: desc_dump fn be32(b: &[u8], o: usize) -> u32 { u32::from_be_bytes(b[o..o + 4].try_into().unwrap()) } fn main() { let a: Vec = std::env::args().collect(); let bytes = std::fs::read(&a[1]).unwrap(); let want = &a[2]; // Walk the XPR2 directory by hand (the crate's reader is private). // 16-byte directory entries at 0x10: type tag, data offset, descriptor size, // name offset — all relative to 0x10 (see texture::Xpr2ResourceEntry). let num = be32(&bytes, 12) as usize; for i in 0..num { let e = &bytes[0x10 + i * 16..0x10 + i * 16 + 16]; let tag = &e[0..4]; let data_off = be32(e, 4) as usize + 0x10; let desc_size = be32(e, 8) as usize; let name_off = be32(e, 12) as usize + 0x10; if tag != b"XBG7" { continue; } let name = { let s = name_off.min(bytes.len()); let end = bytes[s..].iter().position(|&c| c == 0).unwrap_or(0) + s; String::from_utf8_lossy(&bytes[s..end]).to_string() }; if name != *want { continue; } let desc = &bytes[data_off..(data_off + desc_size).min(bytes.len())]; println!("{name}: descriptor at 0x{data_off:X}, {} bytes", desc.len()); // markers: a == c*2, c%3==0, vertex count 32 bytes earlier let mut rel = 0usize; while rel + 8 <= desc.len() { let (x, c) = (be32(desc, rel), be32(desc, rel + 4)); if c >= 3 && c % 3 == 0 && c < 400_000 && x == c * 2 && rel >= 32 { let vc = be32(desc, rel - 32); if (3..=200_000).contains(&vc) { println!("\n marker @0x{rel:X}: {vc} verts / {c} indices — declaration triples after it:"); let mut r = rel + 8; let mut stride = 0u32; for _ in 0..12 { if r + 12 > desc.len() { break; } let (o, code, usage) = (be32(desc, r), be32(desc, r + 4), be32(desc, r + 8) >> 16); if o == 0x00FF_0000 || code == 0xFFFF_FFFF || o > 0x1000 { println!(" end marker @0x{r:X}: off=0x{o:X} code=0x{code:X}"); break; } println!(" off {o:>3} code 0x{:06X} usage {usage}", code & 0xFF_FFFF); stride = stride.max(o + 4); r += 12; } println!(" (min stride from element offsets: {stride})"); rel += 8; continue; } } rel += 4; } return; } eprintln!("resource not found"); }