//! Every record reachable from an element — leaf **and** `focus_link` — plus a //! disc-wide census of how many elements have more than one. //! //! ⚠️ WHY. I claimed `ptbtn00f`'s peak alpha of 80 was undeclared, having read //! `ptbtn00.rat` (the leaf, flat 255) and stopped. The pulse is in //! `ptbtn00f.rat`, the focus record. `focus_link` was already parsed and //! `ui_layout.rs` already documented it: the format was known and I did not //! consult it. An absence claim is only as good as its enumeration, so this //! enumerates rather than asking the reader to remember. //! //! cargo run -p sylpheed-formats --example element_records -- GP_TITLE ptbtn00 //! cargo run -p sylpheed-formats --example element_records -- --census use std::path::PathBuf; use sylpheed_formats::{pak::PakArchive, ui_layout}; fn main() { let root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC")); let argv: Vec = std::env::args().skip(1).collect(); if argv.iter().any(|a| a == "--census") { let (mut els, mut linked, mut screens_with) = (0usize, 0usize, 0usize); let mut paks: Vec<_> = std::fs::read_dir(root.join("dat")) .expect("dat") .filter_map(|e| e.ok()) .map(|e| e.path()) .filter(|p| p.extension().map(|x| x == "pak").unwrap_or(false)) .collect(); paks.sort(); for p in &paks { let Ok(ar) = PakArchive::open(p) else { continue; }; for ent in ar.entries() { let Ok(by) = ar.read(ent) else { continue }; let Some(b) = ui_layout::parse_build(&by) else { continue; }; let n = b.elements.iter().filter(|e| e.focus_link.is_some()).count(); els += b.elements.len(); linked += n; if n > 0 { screens_with += 1 } } } println!("elements disc-wide : {els}"); println!( "with a focus_link record : {linked} ({:.1}%)", 100.0 * linked as f64 / els as f64 ); println!("builds containing at least 1: {screens_with}"); println!("\nEach of those carries a SECOND record whose keyframes are invisible"); println!("to anyone who looks up the leaf by name and stops."); return; } let pak = argv.first().cloned().unwrap_or_else(|| "GP_TITLE".into()); let want = argv.get(1).cloned(); let ar = PakArchive::open(root.join(format!("dat/{pak}.pak"))).expect("pak"); for (i, ent) in ar.entries().iter().enumerate() { let Ok(by) = ar.read(ent) else { continue }; let Some(b) = ui_layout::parse_build(&by) else { continue; }; for el in &b.elements { if let Some(w) = &want { if !el.name.starts_with(w.as_str()) { continue; } } if el.focus_link.is_none() && want.is_none() { continue; } println!("entry {i}: {}", el.name); let stem = el.name.trim_end_matches(".rat"); for (tag, rec) in [ ("leaf", format!("{stem}.rat")), ("focus", el.focus_link.clone().unwrap_or_default()), ] { if rec.is_empty() { continue; } match b.records.get(rec.as_str()) { Some(&(lo, ls)) => { let bytes = &by[lo..(lo + ls).min(by.len())]; let loop_u = ui_layout::loop_length_units(bytes); let kf: Vec = ui_layout::parse_build(bytes) .map(|lb| { lb.elements .iter() .map(|e| { format!( "{} [{} keys, peak a{}]", e.name, e.keyframes.len(), e.keyframes .iter() .map(|k| k.fade >> 24) .max() .unwrap_or(0) ) }) .collect() }) .unwrap_or_default(); println!(" {tag:<6} {rec:<20} loop {:?} {}", loop_u, kf.join(", ")); } None => println!(" {tag:<6} {rec:<20} (no such record)"), } } } } }