//! Does any unread declaration word POINT at the element's T8aD child? //! //! `pteff05.t32`/`pteff04.t32` resolve to no sprite because the `T8aD` they want //! is registered under the name `8AX`. Elimination says `8AX` is the one they //! mean -- one unresolved element, one unclaimed non-focus-state child, in 6 of //! 6 title-side builds. Elimination is not a pointer, so: the 60-byte //! declaration reads name[0..28], parent@32, kind@40, pivot@48/52. The words at //! +28, +36, +44 and +56 are unread. If one of them indexes the RATC child //! table, the RESOLVED elements are the control -- their child index is known, //! so a candidate field must reproduce it for them before it may be believed for //! the unresolved one. //! //! cargo run -p sylpheed-formats --example decl_word_probe -- [entry] use sylpheed_formats::{pak, ratc, ui_layout}; const OFFS: [usize; 4] = [28, 36, 44, 56]; fn be32(b: &[u8], o: usize) -> u32 { if o + 4 > b.len() { return 0; } u32::from_be_bytes([b[o], b[o + 1], b[o + 2], b[o + 3]]) } fn main() { let path = std::env::args() .nth(1) .expect("usage: decl_word_probe [entry]"); let want: Option = std::env::args().nth(2).and_then(|s| s.parse().ok()); let ar = pak::PakArchive::open(&path).expect("open pak"); let entries: Vec<_> = ar.entries().to_vec(); // Per candidate offset, across every build: control hits / control total. let mut hit = [0usize; 4]; let mut tot = 0usize; for (i, e) in entries.iter().enumerate() { if want.is_some_and(|w| w != i) { continue; } let Ok(bytes) = ar.read(e) else { continue }; let Some(build) = ui_layout::parse_build(&bytes) else { continue; }; let Some(kids) = ratc::parse(&bytes) else { continue; }; // Index space to test against: the T8aD children, in child order. let t8: Vec<&ratc::RatcChild> = kids.iter().filter(|c| c.kind == "T8aD").collect(); if build .elements .iter() .all(|el| el.sprite.is_some() || el.kind & 0x10 != 0) { continue; } println!( "== entry {i} ({} elements, {} T8aD children)", build.elements.len(), t8.len() ); for (n, c) in t8.iter().enumerate() { println!(" child[{n:2}] {}", c.name); } for el in &build.elements { if el.kind & 0x10 != 0 { continue; } let d = &bytes[0x20 + el.index * 60..0x20 + (el.index + 1) * 60]; let words: Vec = OFFS.iter().map(|&o| be32(d, o)).collect(); // The control: for a RESOLVED element, which T8aD child is it? let truth = el .sprite .as_ref() .and_then(|s| t8.iter().position(|c| &c.name == s)); if let Some(t) = truth { tot += 1; for (k, w) in words.iter().enumerate() { if *w as usize == t { hit[k] += 1; } } } println!( " [{:2}] {:26} sprite={:?} child={:?} +28={} +36={} +44={} +56={}", el.index, el.name, el.sprite, truth, words[0] as i32, words[1] as i32, words[2] as i32, words[3] as i32 ); } } println!("\nCONTROL: resolved elements whose child index a word reproduces, of {tot}:"); for (k, o) in OFFS.iter().enumerate() { println!(" +{o:<3} {:3}/{tot}", hit[k]); } }