//! What are the keyframe block's `+4` and `+8`? //! //! `+12` is decoded as a screen-plane rotation in degrees. `+4` and `+8` sit //! immediately before it and are carried but unexplained; one standing 🟡 //! reading is that the three together are rotations about three axes, "not tied //! to an observed rotation". This censuses them across every UI pak on the disc //! so the reading can be argued with rather than assumed. use std::collections::BTreeMap; use sylpheed_formats::{pak, ui_layout}; fn main() { let dir = std::env::args().nth(1).expect("usage: /dat"); let mut paks: Vec<_> = std::fs::read_dir(&dir) .expect("dir") .filter_map(|e| e.ok().map(|e| e.path())) .filter(|p| p.extension().map(|x| x == "pak").unwrap_or(false)) .collect(); paks.sort(); let mut h4: BTreeMap = BTreeMap::new(); let mut h8: BTreeMap = BTreeMap::new(); let mut h12: BTreeMap = BTreeMap::new(); let mut both_nz: Vec = Vec::new(); let (mut kfs, mut builds) = (0usize, 0usize); for p in &paks { let Ok(ar) = pak::PakArchive::open(p) else { continue; }; let name = p.file_name().unwrap().to_string_lossy().to_string(); for (i, e) in ar.entries().to_vec().iter().enumerate() { let Ok(bytes) = ar.read(e) else { continue }; let Some(b) = ui_layout::parse_build(&bytes) else { continue; }; builds += 1; // parents and leaves alike let mut groups: Vec<(String, Vec)> = b .elements .iter() .map(|el| (el.name.clone(), el.keyframes.clone())) .collect(); for el in &b.elements { if let Some(&(off, size)) = b.records.get(&el.name) { if let Some(lb) = ui_layout::parse_build(&bytes[off..off + size]) { for le in &lb.elements { groups .push((format!("{}->{}", el.name, le.name), le.keyframes.clone())); } } } } for (nm, ks) in groups { for k in &ks { kfs += 1; *h4.entry(k.unknown_4).or_default() += 1; *h8.entry(k.unknown_8).or_default() += 1; *h12.entry(k.rotation_deg).or_default() += 1; if k.unknown_4 != 0 || k.unknown_8 != 0 { both_nz.push(format!( "{name} e{i} {nm} +4={} +8={} +12={}", k.unknown_4, k.unknown_8, k.rotation_deg )); } } } } } println!("{builds} builds, {kfs} keyframes (parents + leaves)\n"); for (nm, h) in [("+4", &h4), ("+8", &h8), ("+12 (rotation)", &h12)] { let nz: usize = h.iter().filter(|(k, _)| **k != 0).map(|(_, v)| *v).sum(); println!( "{nm}: {} distinct values, {} non-zero keyframes ({:.4}%)", h.len(), nz, 100.0 * nz as f64 / kfs as f64 ); let mut top: Vec<_> = h.iter().filter(|(k, _)| **k != 0).collect(); top.sort_by_key(|(_, v)| std::cmp::Reverse(**v)); for (k, v) in top.iter().take(6) { println!(" {k:>8} x{v}"); } } println!("\nkeyframes with a non-zero +4 or +8: {}", both_nz.len()); // Per-pak, so a reader can ask whether a pak they have a CAPTURE of is // among them -- which decides whether the field is testable at all. let mut per: BTreeMap = BTreeMap::new(); for l in &both_nz { let pak = l.split_whitespace().next().unwrap_or("?").to_string(); *per.entry(pak).or_default() += 1; } println!(" by pak:"); for (k, v) in &per { println!(" {k:<28} {v}"); } println!(" paks with NONE: (any UI pak not listed above)"); if let Ok(f) = std::env::var("KF_SHOW") { println!("\n all lines for {f}:"); for l in both_nz.iter().filter(|l| l.starts_with(&f)) { println!(" {l}"); } } }