diff --git a/crates/sylpheed-cli/src/main.rs b/crates/sylpheed-cli/src/main.rs index a8631472..ab68a845 100644 --- a/crates/sylpheed-cli/src/main.rs +++ b/crates/sylpheed-cli/src/main.rs @@ -655,7 +655,9 @@ fn cmd_screen_render( Some(t) } _ => { - println!("no settle window (fewer than two distinct keyframe times) — using rest()"); + println!( + "no settle window (fewer than two distinct keyframe times) — using rest()" + ); None } } diff --git a/crates/sylpheed-formats/examples/_all2.rs b/crates/sylpheed-formats/examples/_all2.rs index f559a97b..799fc8ee 100644 --- a/crates/sylpheed-formats/examples/_all2.rs +++ b/crates/sylpheed-formats/examples/_all2.rs @@ -1,35 +1,89 @@ use sylpheed_formats::{pak, ui_layout}; -fn main(){ - let mut a=std::env::args().skip(1); - let pk=a.next().unwrap(); let i:usize=a.next().unwrap().parse().unwrap(); - let ar=pak::PakArchive::open(pk).unwrap(); - let by=ar.read(&ar.entries()[i]).unwrap(); - let b=ui_layout::parse_build(&by).unwrap(); - println!("entry {i}: {} elements, {} records, {} sprites", - b.elements.len(), b.records.len(), b.sprites.len()); - let mut rk:Vec<&String>=b.records.keys().collect(); rk.sort(); +fn main() { + let mut a = std::env::args().skip(1); + let pk = a.next().unwrap(); + let i: usize = a.next().unwrap().parse().unwrap(); + let ar = pak::PakArchive::open(pk).unwrap(); + let by = ar.read(&ar.entries()[i]).unwrap(); + let b = ui_layout::parse_build(&by).unwrap(); + println!( + "entry {i}: {} elements, {} records, {} sprites", + b.elements.len(), + b.records.len(), + b.sprites.len() + ); + let mut rk: Vec<&String> = b.records.keys().collect(); + rk.sort(); println!(" records: {:?}", rk); - for (rn,&(o,sz)) in &b.records { - if o+sz>by.len() { continue } - let Some(lb)=ui_layout::parse_build(&by[o..o+sz]) else { continue }; + for (rn, &(o, sz)) in &b.records { + if o + sz > by.len() { + continue; + } + let Some(lb) = ui_layout::parse_build(&by[o..o + sz]) else { + continue; + }; println!(" RECORD {rn}: {} elements", lb.elements.len()); for le in &lb.elements { - let lts:Vec=le.keyframes.iter() - .map(|k|format!("t{}a{}",k.time.map(|v|v as i64).unwrap_or(-1),k.fade>>24)).collect(); - println!(" [{}] {:<22} kind=0x{:<6x} {}", le.index, le.name, le.kind, lts.join(" ")); + let lts: Vec = le + .keyframes + .iter() + .map(|k| { + format!( + "t{}a{}", + k.time.map(|v| v as i64).unwrap_or(-1), + k.fade >> 24 + ) + }) + .collect(); + println!( + " [{}] {:<22} kind=0x{:<6x} {}", + le.index, + le.name, + le.kind, + lts.join(" ") + ); } } for e in &b.elements { - let ts:Vec=e.keyframes.iter() - .map(|k|format!("t{}a{}",k.time.map(|v|v as i64).unwrap_or(-1),k.fade>>24)).collect(); - println!(" [{}] {:<24} kind=0x{:<6x} {}", e.index, e.name, e.kind, ts.join(" ")); - if let Some(&(o,s))=b.records.get(&e.name) { - if o+s<=by.len() { - if let Some(lb)=ui_layout::parse_build(&by[o..o+s]) { + let ts: Vec = e + .keyframes + .iter() + .map(|k| { + format!( + "t{}a{}", + k.time.map(|v| v as i64).unwrap_or(-1), + k.fade >> 24 + ) + }) + .collect(); + println!( + " [{}] {:<24} kind=0x{:<6x} {}", + e.index, + e.name, + e.kind, + ts.join(" ") + ); + if let Some(&(o, s)) = b.records.get(&e.name) { + if o + s <= by.len() { + if let Some(lb) = ui_layout::parse_build(&by[o..o + s]) { for le in &lb.elements { - let lts:Vec=le.keyframes.iter() - .map(|k|format!("t{}a{}",k.time.map(|v|v as i64).unwrap_or(-1),k.fade>>24)).collect(); - println!(" leaf {:<20} kind=0x{:<6x} {}", le.name, le.kind, lts.join(" ")); + let lts: Vec = le + .keyframes + .iter() + .map(|k| { + format!( + "t{}a{}", + k.time.map(|v| v as i64).unwrap_or(-1), + k.fade >> 24 + ) + }) + .collect(); + println!( + " leaf {:<20} kind=0x{:<6x} {}", + le.name, + le.kind, + lts.join(" ") + ); } } } diff --git a/crates/sylpheed-formats/examples/_keys.rs b/crates/sylpheed-formats/examples/_keys.rs index a92c7a2e..487522bf 100644 --- a/crates/sylpheed-formats/examples/_keys.rs +++ b/crates/sylpheed-formats/examples/_keys.rs @@ -1,21 +1,27 @@ use sylpheed_formats::{pak, ui_layout}; -fn main(){ - let mut a=std::env::args().skip(1); - let pk=a.next().unwrap(); - let ar=pak::PakArchive::open(pk).unwrap(); +fn main() { + let mut a = std::env::args().skip(1); + let pk = a.next().unwrap(); + let ar = pak::PakArchive::open(pk).unwrap(); for t in a { - let i:usize=t.parse().unwrap(); - let by=ar.read(&ar.entries()[i]).unwrap(); - let b=ui_layout::parse_build(&by).unwrap(); + let i: usize = t.parse().unwrap(); + let by = ar.read(&ar.entries()[i]).unwrap(); + let b = ui_layout::parse_build(&by).unwrap(); println!("=== entry {i} ==="); - let order=ui_layout::derived_paint_order(&b,&by); + let order = ui_layout::derived_paint_order(&b, &by); for e in &b.elements { - let k=ui_layout::sprite_layer_key(&b,&by,e); - let pos=order.iter().position(|&x|x==e.index); - println!(" [{}] {:<24} kind=0x{:<5x} sprite={:<24} key={:<12} paint#{:?}", - e.index, e.name, e.kind, - e.sprite.clone().unwrap_or_else(||"".into()), - k.map(|v|format!("0x{v:08x}")).unwrap_or_else(||"NONE".into()), pos); + let k = ui_layout::sprite_layer_key(&b, &by, e); + let pos = order.iter().position(|&x| x == e.index); + println!( + " [{}] {:<24} kind=0x{:<5x} sprite={:<24} key={:<12} paint#{:?}", + e.index, + e.name, + e.kind, + e.sprite.clone().unwrap_or_else(|| "".into()), + k.map(|v| format!("0x{v:08x}")) + .unwrap_or_else(|| "NONE".into()), + pos + ); } } } diff --git a/crates/sylpheed-formats/examples/_pbafc.rs b/crates/sylpheed-formats/examples/_pbafc.rs index f9d80f26..0222a668 100644 --- a/crates/sylpheed-formats/examples/_pbafc.rs +++ b/crates/sylpheed-formats/examples/_pbafc.rs @@ -1,36 +1,84 @@ use sylpheed_formats::{pak, ratc, ui_layout}; -fn main(){ - let root=std::env::var("SYLPHEED_DISC").unwrap_or_else(|_|"/disc".into()); - let ar=pak::PakArchive::open(format!("{root}/dat/GP_READY_ROOM.pak")).unwrap(); - for (i,e) in ar.entries().iter().enumerate() { - let Ok(by)=ar.read(e) else { continue }; - if !ratc::is_ratc(&by) { continue } - let Some(b)=ui_layout::parse_build(&by) else { continue }; - let Some(p)=b.elements.iter().find(|el|el.name=="pbafc.prm") else { continue }; +fn main() { + let root = std::env::var("SYLPHEED_DISC").unwrap_or_else(|_| "/disc".into()); + let ar = pak::PakArchive::open(format!("{root}/dat/GP_READY_ROOM.pak")).unwrap(); + for (i, e) in ar.entries().iter().enumerate() { + let Ok(by) = ar.read(e) else { continue }; + if !ratc::is_ratc(&by) { + continue; + } + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; + let Some(p) = b.elements.iter().find(|el| el.name == "pbafc.prm") else { + continue; + }; println!("=== entry {i}: {} elements ===", b.elements.len()); - println!(" pbafc.prm pivot=({},{}) -> {}x{}", p.pivot_x,p.pivot_y,p.pivot_x*2,p.pivot_y*2); + println!( + " pbafc.prm pivot=({},{}) -> {}x{}", + p.pivot_x, + p.pivot_y, + p.pivot_x * 2, + p.pivot_y * 2 + ); for k in &p.keyframes { - println!(" t={:<5} fade={:08x} a={:<4} xy=({},{}) s={}/{}", - k.time.map(|v|v as i64).unwrap_or(-1), k.fade, k.fade>>24, k.x,k.y,k.scale_x,k.scale_y); + println!( + " t={:<5} fade={:08x} a={:<4} xy=({},{}) s={}/{}", + k.time.map(|v| v as i64).unwrap_or(-1), + k.fade, + k.fade >> 24, + k.x, + k.y, + k.scale_x, + k.scale_y + ); } // what does it cover, and is anything visible while it is opaque? - let rest=p.rest().unwrap(); - let (px,py,pw,ph)=(rest.x, rest.y, (p.pivot_x*2) as i32, (p.pivot_y*2) as i32); + let rest = p.rest().unwrap(); + let (px, py, pw, ph) = ( + rest.x, + rest.y, + (p.pivot_x * 2) as i32, + (p.pivot_y * 2) as i32, + ); println!(" its rect at rest: ({px},{py}) {pw}x{ph}"); - let tmax=b.elements.iter().flat_map(|e|e.keyframes.iter().filter_map(|k|k.time)).max().unwrap_or(0); - let op:Vec=(0..=tmax).filter(|&t|p.pose_at(t).map(|k|k.fade>>24)==Some(255)).collect(); - println!(" opaque at {} instants (t={:?}..{:?}) of 0..{tmax}", op.len(), op.first(), op.last()); - let mut cov=0; let mut vis=0; + let tmax = b + .elements + .iter() + .flat_map(|e| e.keyframes.iter().filter_map(|k| k.time)) + .max() + .unwrap_or(0); + let op: Vec = (0..=tmax) + .filter(|&t| p.pose_at(t).map(|k| k.fade >> 24) == Some(255)) + .collect(); + println!( + " opaque at {} instants (t={:?}..{:?}) of 0..{tmax}", + op.len(), + op.first(), + op.last() + ); + let mut cov = 0; + let mut vis = 0; for o in &b.elements { - if o.index==p.index { continue } - let Some(ok)=o.rest() else { continue }; - let (ow,oh)=((o.pivot_x*2) as i32,(o.pivot_y*2) as i32); - let overlap=(px+pw).min(ok.x+ow)-px.max(ok.x)>0 && (py+ph).min(ok.y+oh)-py.max(ok.y)>0; - if !overlap { continue } - cov+=1; - if op.iter().any(|&t| o.pose_at(t).map(|k|k.fade>>24).unwrap_or(0)>0) { - vis+=1; - if vis<=6 { println!(" covered AND visible while opaque: {}", o.name); } + if o.index == p.index { + continue; + } + let Some(ok) = o.rest() else { continue }; + let (ow, oh) = ((o.pivot_x * 2) as i32, (o.pivot_y * 2) as i32); + let overlap = (px + pw).min(ok.x + ow) - px.max(ok.x) > 0 + && (py + ph).min(ok.y + oh) - py.max(ok.y) > 0; + if !overlap { + continue; + } + cov += 1; + if op + .iter() + .any(|&t| o.pose_at(t).map(|k| k.fade >> 24).unwrap_or(0) > 0) + { + vis += 1; + if vis <= 6 { + println!(" covered AND visible while opaque: {}", o.name); + } } } println!(" elements its rect covers: {cov}; visible while it is opaque: {vis}"); diff --git a/crates/sylpheed-formats/examples/_rechdr.rs b/crates/sylpheed-formats/examples/_rechdr.rs index 56493e7c..b925eeb1 100644 --- a/crates/sylpheed-formats/examples/_rechdr.rs +++ b/crates/sylpheed-formats/examples/_rechdr.rs @@ -1,24 +1,35 @@ use sylpheed_formats::{pak, ui_layout}; -fn main(){ - let mut a=std::env::args().skip(1); - let pk=a.next().unwrap(); - let ar=pak::PakArchive::open(pk).unwrap(); +fn main() { + let mut a = std::env::args().skip(1); + let pk = a.next().unwrap(); + let ar = pak::PakArchive::open(pk).unwrap(); for t in a { - let i:usize=t.parse().unwrap(); - let by=ar.read(&ar.entries()[i]).unwrap(); - let Some(b)=ui_layout::parse_build(&by) else { continue }; - let top=u32::from_be_bytes(by[8..12].try_into().unwrap()); + let i: usize = t.parse().unwrap(); + let by = ar.read(&ar.entries()[i]).unwrap(); + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; + let top = u32::from_be_bytes(by[8..12].try_into().unwrap()); println!("entry {i:2} TOP +08 = {top}"); - let mut ks:Vec<&String>=b.records.keys().collect(); ks.sort(); + let mut ks: Vec<&String> = b.records.keys().collect(); + ks.sort(); for rn in ks { - let &(o,s)=b.records.get(rn).unwrap(); - if o+16>by.len() { continue } - let magic=&by[o..o+4]; - let h4=u32::from_be_bytes(by[o+4..o+8].try_into().unwrap()); - let h8=u32::from_be_bytes(by[o+8..o+12].try_into().unwrap()); - let maxt=ui_layout::parse_build(&by[o..o+s]) - .map(|lb|lb.elements.iter().flat_map(|e|e.keyframes.iter() - .filter_map(|k|k.time)).max().unwrap_or(0)).unwrap_or(0); + let &(o, s) = b.records.get(rn).unwrap(); + if o + 16 > by.len() { + continue; + } + let magic = &by[o..o + 4]; + let h4 = u32::from_be_bytes(by[o + 4..o + 8].try_into().unwrap()); + let h8 = u32::from_be_bytes(by[o + 8..o + 12].try_into().unwrap()); + let maxt = ui_layout::parse_build(&by[o..o + s]) + .map(|lb| { + lb.elements + .iter() + .flat_map(|e| e.keyframes.iter().filter_map(|k| k.time)) + .max() + .unwrap_or(0) + }) + .unwrap_or(0); println!(" {rn:<24} magic={:?} +04={:08x}({:.1}) +08={h8:<6} max keyframe t={maxt} ratio={:.4}", String::from_utf8_lossy(magic), h4, h4 as f64/65536.0, if maxt>0 {h8 as f64/maxt as f64} else {0.0}); diff --git a/crates/sylpheed-formats/examples/additive_census.rs b/crates/sylpheed-formats/examples/additive_census.rs index 55d42346..4fd1c863 100644 --- a/crates/sylpheed-formats/examples/additive_census.rs +++ b/crates/sylpheed-formats/examples/additive_census.rs @@ -8,19 +8,34 @@ fn main() { println!("# Before that change EVERY row below was drawn alpha-over by our renderer,"); println!("# which is why `verify-screen` was structurally incapable on these screens."); for pak in ["GP_TITLE", "GP_OPTIONS"] { - let Ok(ar) = PakArchive::open(root.join(format!("dat/{pak}.pak"))) else { continue }; + let Ok(ar) = PakArchive::open(root.join(format!("dat/{pak}.pak"))) else { + continue; + }; let n = ar.entries().len(); for e in 0..n { - let Ok(by) = ar.read(&ar.entries()[e]) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; - let mut add: Vec<&String> = b.sprites.keys() + let Ok(by) = ar.read(&ar.entries()[e]) else { + continue; + }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; + let mut add: Vec<&String> = b + .sprites + .keys() .filter(|s| ui_layout::blend_additive_by_name(&b, &by, s) == Some(true)) .collect(); - if add.is_empty() { continue } + if add.is_empty() { + continue; + } add.sort(); - println!("\n{pak} entry {e} -- {} of {} sprites additive", - add.len(), b.sprites.len()); - for s in add { println!(" {s}"); } + println!( + "\n{pak} entry {e} -- {} of {} sprites additive", + add.len(), + b.sprites.len() + ); + for s in add { + println!(" {s}"); + } } } } diff --git a/crates/sylpheed-formats/examples/adv_region_extend.rs b/crates/sylpheed-formats/examples/adv_region_extend.rs index dc1e0760..a5d19941 100644 --- a/crates/sylpheed-formats/examples/adv_region_extend.rs +++ b/crates/sylpheed-formats/examples/adv_region_extend.rs @@ -28,7 +28,11 @@ fn main() { let (start, end) = media::resolve_movie_voice_region(&src, "ADV", VoiceLang::English).expect("region"); println!("resolver says {start}..{end} ({} B)", end - start); - println!("decoder wants {:?} = {} B payload\n", WANT, WANT.iter().sum::()); + println!( + "decoder wants {:?} = {} B payload\n", + WANT, + WANT.iter().sum::() + ); for back_packets in [0usize, 100, 200, 237, 238, 239, 300, 400] { let back = (back_packets * 2048) as u64; @@ -47,7 +51,11 @@ fn main() { "-{back_packets:4} packets (start {s}): {} chunk(s) {:?}{}", riffs.len(), sizes, - if hit { " <== MATCHES THE DECODER" } else { "" } + if hit { + " <== MATCHES THE DECODER" + } else { + "" + } ); } } diff --git a/crates/sylpheed-formats/examples/adv_voice_dump.rs b/crates/sylpheed-formats/examples/adv_voice_dump.rs index fd68936b..6637d324 100644 --- a/crates/sylpheed-formats/examples/adv_voice_dump.rs +++ b/crates/sylpheed-formats/examples/adv_voice_dump.rs @@ -21,8 +21,8 @@ fn main() { let src = DirectorySource::new(std::path::PathBuf::from(&disc)); std::fs::create_dir_all(&out).expect("outdir"); - let (start, end) = media::resolve_movie_voice_region(&src, &movie, VoiceLang::English) - .expect("voice region"); + let (start, end) = + media::resolve_movie_voice_region(&src, &movie, VoiceLang::English).expect("voice region"); let bytes = src .read_segment_range("dat/sound", start, (end - start) as usize) .expect("region"); @@ -35,7 +35,10 @@ fn main() { std::fs::write(&p, r).expect("write"); // the probe reports `byte_size` = RIFF total - 60; print both so the // dump can be tied to a specific XMA context by its own number - println!(" chunk {i}: {} B byte_size-equivalent {} -> {p}", - r.len(), r.len() as i64 - 60); + println!( + " chunk {i}: {} B byte_size-equivalent {} -> {p}", + r.len(), + r.len() as i64 - 60 + ); } } diff --git a/crates/sylpheed-formats/examples/bank_streams.rs b/crates/sylpheed-formats/examples/bank_streams.rs index 40d6c8e2..31df0465 100644 --- a/crates/sylpheed-formats/examples/bank_streams.rs +++ b/crates/sylpheed-formats/examples/bank_streams.rs @@ -30,7 +30,11 @@ fn main() { " stream {i}: payload {payload} B ({} packets) declared {rate} B/s \ => {:.3} s", payload / 2048, - if rate > 0 { payload as f64 / rate as f64 } else { 0.0 } + if rate > 0 { + payload as f64 / rate as f64 + } else { + 0.0 + } ); } } diff --git a/crates/sylpheed-formats/examples/bgm_dump.rs b/crates/sylpheed-formats/examples/bgm_dump.rs index c9724421..b1f85d14 100644 --- a/crates/sylpheed-formats/examples/bgm_dump.rs +++ b/crates/sylpheed-formats/examples/bgm_dump.rs @@ -6,7 +6,9 @@ use sylpheed_formats::media::{self, DirectorySource}; use sylpheed_formats::slb; fn main() { - let name = std::env::args().nth(1).unwrap_or_else(|| "BGM_103.slb".into()); + let name = std::env::args() + .nth(1) + .unwrap_or_else(|| "BGM_103.slb".into()); let out = std::env::args().nth(2).expect("OUTDIR"); let disc = std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC"); let src = DirectorySource::new(std::path::PathBuf::from(&disc)); @@ -17,6 +19,10 @@ fn main() { for (i, r) in slb::to_xma_riffs(&bytes).iter().enumerate() { let p = format!("{out}/{}_{i}.xma", name.trim_end_matches(".slb")); std::fs::write(&p, r).expect("write"); - println!(" wave {i}: {} B (byte_size {}) -> {p}", r.len(), r.len() - 60); + println!( + " wave {i}: {} B (byte_size {}) -> {p}", + r.len(), + r.len() - 60 + ); } } diff --git a/crates/sylpheed-formats/examples/black_backdrop_predicate.rs b/crates/sylpheed-formats/examples/black_backdrop_predicate.rs index f2813dbc..d085d372 100644 --- a/crates/sylpheed-formats/examples/black_backdrop_predicate.rs +++ b/crates/sylpheed-formats/examples/black_backdrop_predicate.rs @@ -17,17 +17,21 @@ //! bundles before its disc-wide numbers mean anything. //! //! cargo run -p sylpheed-formats --example black_backdrop_predicate -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::io::Write; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, ui_layout}; /// A screen declares its own backdrop if some `.prm` primitive holds /// `fade == 0xff000000` at t = 0: full alpha over black. fn has_black_backdrop(b: &ui_layout::UiBuild) -> Option { for el in &b.elements { - if !el.name.ends_with(".prm") { continue } + if !el.name.ends_with(".prm") { + continue; + } if let Some(k) = el.keyframes.iter().find(|k| k.time == Some(0)) { - if k.fade == 0xff00_0000 { return Some(el.name.clone()) } + if k.fade == 0xff00_0000 { + return Some(el.name.clone()); + } } } None @@ -40,39 +44,63 @@ fn main() { let ar = PakArchive::open(root.join("dat/GP_TITLE.pak")).expect("GP_TITLE.pak"); let (mut y, mut n) = (0, 0); for e in 0..16usize { - let Ok(by) = ar.read(&ar.entries()[e]) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; + let Ok(by) = ar.read(&ar.entries()[e]) else { + continue; + }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; match has_black_backdrop(&b) { - Some(nm) => { y += 1; println!(" entry {e:>2} YES {nm}") } - None => { n += 1; println!(" entry {e:>2} no") } + Some(nm) => { + y += 1; + println!(" entry {e:>2} YES {nm}") + } + None => { + n += 1; + println!(" entry {e:>2} no") + } } } println!(" -> {y} with, {n} without\n"); println!("== DISC-WIDE, over every screen build"); - let mut paks: Vec = std::fs::read_dir(root.join("dat")).expect("dat/") + 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().is_some_and(|x| x == "pak")).collect(); + .filter(|p| p.extension().is_some_and(|x| x == "pak")) + .collect(); paks.sort(); let (mut tot, mut with) = (0usize, 0usize); for pak in &paks { - let Ok(ar) = PakArchive::open(pak) else { continue }; + let Ok(ar) = PakArchive::open(pak) else { + continue; + }; let name = pak.file_name().unwrap().to_string_lossy().to_string(); let (mut t, mut w) = (0usize, 0usize); for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; - if !ui_layout::is_build(&by) { continue } - let Some(b) = ui_layout::parse_build(&by) else { continue }; + if !ui_layout::is_build(&by) { + continue; + } + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; t += 1; - if has_black_backdrop(&b).is_some() { w += 1 } + if has_black_backdrop(&b).is_some() { + w += 1 + } } if t > 0 { println!("{name:30} {w:4} / {t:<4} declare a black backdrop"); std::io::stdout().flush().ok(); } - tot += t; with += w; + tot += t; + with += w; } - println!("\n{with} of {tot} screen builds disc-wide declare an opaque-black backdrop \ - ({:.1} %)", 100.0 * with as f64 / tot as f64); + println!( + "\n{with} of {tot} screen builds disc-wide declare an opaque-black backdrop \ + ({:.1} %)", + 100.0 * with as f64 / tot as f64 + ); println!("--- END ---"); } diff --git a/crates/sylpheed-formats/examples/blend_api_check.rs b/crates/sylpheed-formats/examples/blend_api_check.rs index 55fbcf80..d59a47e9 100644 --- a/crates/sylpheed-formats/examples/blend_api_check.rs +++ b/crates/sylpheed-formats/examples/blend_api_check.rs @@ -14,51 +14,94 @@ use sylpheed_formats::{pak::PakArchive, ui_layout}; /// (build entry, sprite, measured additive?) — from `data/blend-bit-vs-oracle.txt`, /// every label an `RB_BLENDCONTROL0` value read out of the guest command stream. const MEASURED: &[(usize, &str, bool)] = &[ - (4, "ptbase2.t32", false), (4, "ptlogo1.t32", false), (4, "ptlogo2.t32", false), - (4, "ptlogo_tm.t32", false), (4, "ptcopyright.t32", false), - (4, "ptlogo_back2.t32", false), (4, "ptlogo_back2eff.t32", false), - (2, "ptbtn00.t32", false), (2, "ptbtn00f.t32", true), - (5, "ptbase.t32", false), (5, "ptmsg.t32", false), (5, "ptbtn01f.t32", false), - (5, "ptbtneff01.t32", false), (5, "pteff10.t32", true), (5, "pteff12.t32", true), - (6, "pteff21.t32", true), (6, "pteff22.t32", true), (6, "pteff23.t32", true), - (6, "ptframe3.t32", true), (6, "ptframe4.t32", true), - (6, "pteff03.t32", true), (6, "pteff03a.t32", true), + (4, "ptbase2.t32", false), + (4, "ptlogo1.t32", false), + (4, "ptlogo2.t32", false), + (4, "ptlogo_tm.t32", false), + (4, "ptcopyright.t32", false), + (4, "ptlogo_back2.t32", false), + (4, "ptlogo_back2eff.t32", false), + (2, "ptbtn00.t32", false), + (2, "ptbtn00f.t32", true), + (5, "ptbase.t32", false), + (5, "ptmsg.t32", false), + (5, "ptbtn01f.t32", false), + (5, "ptbtneff01.t32", false), + (5, "pteff10.t32", true), + (5, "pteff12.t32", true), + (6, "pteff21.t32", true), + (6, "pteff22.t32", true), + (6, "pteff23.t32", true), + (6, "ptframe3.t32", true), + (6, "ptframe4.t32", true), + (6, "pteff03.t32", true), + (6, "pteff03a.t32", true), ]; fn main() { let root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC")); let ar = PakArchive::open(root.join("dat/GP_TITLE.pak")).expect("GP_TITLE"); let (mut ok, mut bad, mut missing) = (0, 0, 0); - println!("{:<7} {:<22} {:<10} {:<10} {}", "entry", "sprite", "expected", "accessor", ""); + println!( + "{:<7} {:<22} {:<10} {:<10} {}", + "entry", "sprite", "expected", "accessor", "" + ); for e in [2usize, 4, 5, 6] { let by = ar.read(&ar.entries()[e]).expect("entry"); let b = ui_layout::parse_build(&by).expect("build"); for &(oe, name, additive) in MEASURED { - if oe != e { continue } + if oe != e { + continue; + } // Prefer the Element accessor; fall back to the by-name one for // focused variants, which are reached through `opt ` and carry no // top-level element of their own. - let got = b.elements.iter().find(|x| x.sprite.as_deref() == Some(name)) + let got = b + .elements + .iter() + .find(|x| x.sprite.as_deref() == Some(name)) .and_then(|el| ui_layout::sprite_blend_additive(&b, &by, el)) .or_else(|| ui_layout::blend_additive_by_name(&b, &by, name)); - if got.is_none() { println!("{e:<7} {name:<22} {additive:<10} {:<10} MISSING", "-"); missing += 1; continue } + if got.is_none() { + println!("{e:<7} {name:<22} {additive:<10} {:<10} MISSING", "-"); + missing += 1; + continue; + } match got { - Some(g) if g == additive => { ok += 1; - println!("{e:<7} {name:<22} {additive:<10} {g:<10} OK"); } - other => { bad += 1; - println!("{e:<7} {name:<22} {additive:<10} {other:?} MISMATCH"); } + Some(g) if g == additive => { + ok += 1; + println!("{e:<7} {name:<22} {additive:<10} {g:<10} OK"); + } + other => { + bad += 1; + println!("{e:<7} {name:<22} {additive:<10} {other:?} MISMATCH"); + } } } } - println!("\n{ok} agree, {bad} mismatched, {missing} not found (of {})", MEASURED.len()); + println!( + "\n{ok} agree, {bad} mismatched, {missing} not found (of {})", + MEASURED.len() + ); // The control that removes the test's own subject: the accessor must also // report a MIX. An accessor stuck at one value would pass every `false` row. let by = ar.read(&ar.entries()[6]).expect("entry"); let b = ui_layout::parse_build(&by).expect("build"); - let add = b.elements.iter().filter(|e| ui_layout::sprite_blend_additive(&b, &by, e) == Some(true)).count(); - let over = b.elements.iter().filter(|e| ui_layout::sprite_blend_additive(&b, &by, e) == Some(false)).count(); + let add = b + .elements + .iter() + .filter(|e| ui_layout::sprite_blend_additive(&b, &by, e) == Some(true)) + .count(); + let over = b + .elements + .iter() + .filter(|e| ui_layout::sprite_blend_additive(&b, &by, e) == Some(false)) + .count(); println!("control -- entry 6 must report BOTH values: additive={add} alpha-over={over}"); assert!(add > 0 && over > 0, "accessor is not discriminating"); - assert_eq!(bad, 0, "the public accessor disagrees with the committed oracle"); + assert_eq!( + bad, 0, + "the public accessor disagrees with the committed oracle" + ); println!("PASS"); } diff --git a/crates/sylpheed-formats/examples/blend_prediction_splash.rs b/crates/sylpheed-formats/examples/blend_prediction_splash.rs index 3afb4b1a..b4e6a1bb 100644 --- a/crates/sylpheed-formats/examples/blend_prediction_splash.rs +++ b/crates/sylpheed-formats/examples/blend_prediction_splash.rs @@ -15,31 +15,46 @@ //! //! cargo run -p sylpheed-formats --example blend_prediction_splash //! cargo run -p sylpheed-formats --example blend_prediction_splash -- GP_OPTIONS 0 1 2 -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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 args: Vec = std::env::args().skip(1).collect(); - let pak = args.iter().find(|a| a.parse::().is_err()) - .cloned().unwrap_or_else(|| "GP_TITLE".to_string()); + let pak = args + .iter() + .find(|a| a.parse::().is_err()) + .cloned() + .unwrap_or_else(|| "GP_TITLE".to_string()); let ar = PakArchive::open(root.join(format!("dat/{pak}.pak"))).expect("pak"); let builds: Vec = args.iter().filter_map(|a| a.parse().ok()).collect(); - let builds = if builds.is_empty() { (0..ar.entries().len()).collect() } else { builds }; + let builds = if builds.is_empty() { + (0..ar.entries().len()).collect() + } else { + builds + }; for e in builds { - let Ok(by) = ar.read(&ar.entries()[e]) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; + let Ok(by) = ar.read(&ar.entries()[e]) else { + continue; + }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; println!("=== {pak} entry {e} ==="); let mut names: Vec<&String> = b.sprites.keys().collect(); names.sort(); for n in names { let (off, size) = b.sprites[n]; let s = &by[off..(off + size).min(by.len())]; - if s.len() < 8 || &s[0..4] != b"T8aD" { continue } + if s.len() < 8 || &s[0..4] != b"T8aD" { + continue; + } let w = u32::from_be_bytes([s[4], s[5], s[6], s[7]]); - println!("{n:<26} +0x04 = {w:08X} bit 0x02 {} PREDICT {}", - if w & 2 != 0 { "SET " } else { "clear" }, - if w & 2 != 0 { "ADDITIVE" } else { "alpha-over" }); + println!( + "{n:<26} +0x04 = {w:08X} bit 0x02 {} PREDICT {}", + if w & 2 != 0 { "SET " } else { "clear" }, + if w & 2 != 0 { "ADDITIVE" } else { "alpha-over" } + ); } println!(); } diff --git a/crates/sylpheed-formats/examples/blend_vs_t8ad_bit.rs b/crates/sylpheed-formats/examples/blend_vs_t8ad_bit.rs index 3932ff15..4fd6d1e4 100644 --- a/crates/sylpheed-formats/examples/blend_vs_t8ad_bit.rs +++ b/crates/sylpheed-formats/examples/blend_vs_t8ad_bit.rs @@ -15,9 +15,9 @@ //! `data/ui-blend-extras-complete.txt`. //! //! cargo run -p sylpheed-formats --example blend_vs_t8ad_bit -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::collections::BTreeMap; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, ui_layout}; /// (build entry, sprite, measured additive?) — the oracle's verdicts, verbatim. const MEASURED: &[(usize, &str, bool)] = &[ @@ -40,7 +40,7 @@ const MEASURED: &[(usize, &str, bool)] = &[ (5, "pteff12.t32", true), (5, "ptframe1.t32", true), (5, "ptframe2.t32", true), - (5, "pteff03.t32", true), // the rotated sweep strips, via ptloop01/02 + (5, "pteff03.t32", true), // the rotated sweep strips, via ptloop01/02 (5, "pteff03a.t32", true), // --- entry 6, EXTRAS ------------------------------------------------------ (6, "ptbase.t32", false), @@ -62,7 +62,10 @@ const MEASURED: &[(usize, &str, bool)] = &[ ]; fn main() { - if std::env::args().any(|a| a == "decl") { decl_rivals(); return } + if std::env::args().any(|a| a == "decl") { + decl_rivals(); + return; + } let root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC")); let ar = PakArchive::open(root.join("dat/GP_TITLE.pak")).expect("GP_TITLE"); let mut hdr: BTreeMap<(usize, String), u32> = BTreeMap::new(); @@ -71,16 +74,25 @@ fn main() { let b = ui_layout::parse_build(&by).expect("build"); for (n, &(off, size)) in &b.sprites { let s = &by[off..(off + size).min(by.len())]; - if s.len() < 8 || &s[0..4] != b"T8aD" { continue } + if s.len() < 8 || &s[0..4] != b"T8aD" { + continue; + } hdr.insert((e, n.clone()), u32::from_be_bytes([s[4], s[5], s[6], s[7]])); } } - println!("{:<10} {:<22} {:<10} {:>10} {}", "entry", "sprite", "+0x04", "bit 0x02", "measured blend"); + println!( + "{:<10} {:<22} {:<10} {:>10} {}", + "entry", "sprite", "+0x04", "bit 0x02", "measured blend" + ); let (mut tp, mut tn, mut fp, mut fnn, mut missing) = (0, 0, 0, 0, 0); for &(e, n, additive) in MEASURED { let Some(&w) = hdr.get(&(e, n.to_string())) else { - println!("{e:<10} {n:<22} {:<10} {:>10} {}", "MISSING", "-", - if additive { "ADDITIVE" } else { "alpha-over" }); + println!( + "{e:<10} {n:<22} {:<10} {:>10} {}", + "MISSING", + "-", + if additive { "ADDITIVE" } else { "alpha-over" } + ); missing += 1; continue; }; @@ -91,20 +103,31 @@ fn main() { (true, false) => fp += 1, (false, true) => fnn += 1, } - println!("{e:<10} {n:<22} {:08X} {:>10} {}{}", w, bit, - if additive { "ADDITIVE" } else { "alpha-over" }, - if bit == additive { "" } else { " <== DISAGREES" }); + println!( + "{e:<10} {n:<22} {:08X} {:>10} {}{}", + w, + bit, + if additive { "ADDITIVE" } else { "alpha-over" }, + if bit == additive { + "" + } else { + " <== DISAGREES" + } + ); } println!("\nbit set & additive {tp}"); println!("bit clear & alpha-over {tn}"); println!("bit set & alpha-over {fp} <- false positives"); println!("bit clear & additive {fnn} <- false negatives"); println!("sprite not found {missing}"); - println!("\n{}", if fp == 0 && fnn == 0 && missing == 0 { - "PERFECT PARTITION on every element whose blend was measured." - } else { - "THE BIT DOES NOT PREDICT THE MEASURED BLEND." - }); + println!( + "\n{}", + if fp == 0 && fnn == 0 && missing == 0 { + "PERFECT PARTITION on every element whose blend was measured." + } else { + "THE BIT DOES NOT PREDICT THE MEASURED BLEND." + } + ); // ── THE CONTROL THAT MATTERS ──────────────────────────────────────────── // A perfect partition is worthless if half the header partitions equally @@ -119,10 +142,17 @@ fn main() { let b = ui_layout::parse_build(&by).expect("build"); for (n, &(off, size)) in &b.sprites { let s = &by[off..(off + size).min(by.len())]; - if s.len() < 48 || &s[0..4] != b"T8aD" { continue } - words.insert((e, n.clone()), (0..12) - .map(|k| u32::from_be_bytes([s[k*4], s[k*4+1], s[k*4+2], s[k*4+3]])) - .collect()); + if s.len() < 48 || &s[0..4] != b"T8aD" { + continue; + } + words.insert( + (e, n.clone()), + (0..12) + .map(|k| { + u32::from_be_bytes([s[k * 4], s[k * 4 + 1], s[k * 4 + 2], s[k * 4 + 3]]) + }) + .collect(), + ); } } for w in 0..12 { @@ -131,10 +161,20 @@ fn main() { let mut set_seen = false; let mut clear_seen = false; for &(e, n, additive) in MEASURED { - let Some(v) = words.get(&(e, n.to_string())) else { ok = false; break }; + let Some(v) = words.get(&(e, n.to_string())) else { + ok = false; + break; + }; let on = (v[w] >> bit) & 1 == 1; - if on { set_seen = true } else { clear_seen = true } - if on != additive { ok = false; break } + if on { + set_seen = true + } else { + clear_seen = true + } + if on != additive { + ok = false; + break; + } } // A constant bit trivially "agrees" with nothing; require both sides. if ok && set_seen && clear_seen { @@ -144,11 +184,16 @@ fn main() { } println!("\nRIVAL FIELDS — other bits of the first 12 header words that separate"); println!("the same 35 elements with zero errors: {}", rivals.len()); - for r in &rivals { println!(" {r}"); } + for r in &rivals { + println!(" {r}"); + } if rivals.len() == 1 { println!(" -> the sample singles out ONE field. Nothing else in the header does it."); } else { - println!(" -> the sample does NOT single out a field; {} candidates tie.", rivals.len()); + println!( + " -> the sample does NOT single out a field; {} candidates tie.", + rivals.len() + ); } } @@ -171,17 +216,33 @@ fn decl_rivals() { const STRIDE: usize = 60; let mut decl: BTreeMap<(usize, String), Vec> = BTreeMap::new(); for e in [2usize, 4, 5, 6] { - let Ok(by) = ar.read(&ar.entries()[e]) else { continue }; - if by.len() < 0x18 { continue } + let Ok(by) = ar.read(&ar.entries()[e]) else { + continue; + }; + if by.len() < 0x18 { + continue; + } let count = u32::from_be_bytes([by[0x14], by[0x15], by[0x16], by[0x17]]) as usize; for i in 0..count { let at = AT + i * STRIDE; - if at + STRIDE > by.len() { break } + if at + STRIDE > by.len() { + break; + } let end = by[at..at + 12].iter().position(|&c| c == 0).unwrap_or(12); let name = String::from_utf8_lossy(&by[at..at + end]).to_string(); - decl.insert((e, name), (0..15) - .map(|k| u32::from_be_bytes([by[at+k*4], by[at+k*4+1], by[at+k*4+2], by[at+k*4+3]])) - .collect()); + decl.insert( + (e, name), + (0..15) + .map(|k| { + u32::from_be_bytes([ + by[at + k * 4], + by[at + k * 4 + 1], + by[at + k * 4 + 2], + by[at + k * 4 + 3], + ]) + }) + .collect(), + ); } } let mut missing: Vec = Vec::new(); @@ -191,23 +252,37 @@ fn decl_rivals() { } } println!("\n=== DECLARATION-ENTRY RIVAL SWEEP ==="); - println!("measured elements with NO declaration entry of their own: {} of {}", - missing.len(), MEASURED.len()); - for m in &missing { println!(" {m}"); } + println!( + "measured elements with NO declaration entry of their own: {} of {}", + missing.len(), + MEASURED.len() + ); + for m in &missing { + println!(" {m}"); + } if !missing.is_empty() { println!(" -> no declaration field can select the blend for these, because they"); println!(" have no declaration entry. The header is the only per-sprite home."); } - let labelled: Vec<&(usize, &str, bool)> = MEASURED.iter() - .filter(|(e, n, _)| decl.contains_key(&(*e, n.to_string()))).collect(); + let labelled: Vec<&(usize, &str, bool)> = MEASURED + .iter() + .filter(|(e, n, _)| decl.contains_key(&(*e, n.to_string()))) + .collect(); let mut rivals = 0; for w in 0..15 { for bit in 0..32 { let (mut ok, mut s, mut c) = (true, false, false); for &&(e, n, additive) in &labelled { let on = (decl[&(e, n.to_string())][w] >> bit) & 1 == 1; - if on { s = true } else { c = true } - if on != additive { ok = false; break } + if on { + s = true + } else { + c = true + } + if on != additive { + ok = false; + break; + } } if ok && s && c { println!(" RIVAL: declaration +0x{:02X} bit {bit}", w * 4); @@ -215,6 +290,8 @@ fn decl_rivals() { } } } - println!("declaration bits that separate the {} labellable elements: {rivals}", - labelled.len()); + println!( + "declaration bits that separate the {} labellable elements: {rivals}", + labelled.len() + ); } diff --git a/crates/sylpheed-formats/examples/decl_entry_diff.rs b/crates/sylpheed-formats/examples/decl_entry_diff.rs index 93667f30..508b9d15 100644 --- a/crates/sylpheed-formats/examples/decl_entry_diff.rs +++ b/crates/sylpheed-formats/examples/decl_entry_diff.rs @@ -12,8 +12,8 @@ //! separates exactly those two is a candidate; one that does not, is not. //! //! cargo run -p sylpheed-formats --example decl_entry_diff -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, ui_layout}; const AT: usize = 0x20; const N: usize = 60; @@ -24,8 +24,12 @@ fn main() { let by = ar.read(&ar.entries()[5]).expect("entry 5"); let b = ui_layout::parse_build(&by).expect("build"); let names: Vec = b.elements.iter().map(|e| e.name.clone()).collect(); - let frames: Vec = names.iter().enumerate() - .filter(|(_, n)| n.starts_with("ptframe")).map(|(i, _)| i).collect(); + let frames: Vec = names + .iter() + .enumerate() + .filter(|(_, n)| n.starts_with("ptframe")) + .map(|(i, _)| i) + .collect(); println!("{} elements; frames at indices {:?}", names.len(), frames); let word = |i: usize, w: usize| -> u32 { @@ -36,13 +40,26 @@ fn main() { for w in 0..N / 4 { let fv: Vec = frames.iter().map(|&i| word(i, w)).collect(); let same_in_frames = fv.windows(2).all(|p| p[0] == p[1]); - let others: Vec = (0..names.len()).filter(|i| !frames.contains(i)) - .map(|i| word(i, w)).collect(); + let others: Vec = (0..names.len()) + .filter(|i| !frames.contains(i)) + .map(|i| word(i, w)) + .collect(); let unique = same_in_frames && !others.contains(&fv[0]); - let distinct = { let mut v: Vec = (0..names.len()).map(|i| word(i, w)).collect(); - v.sort_unstable(); v.dedup(); v.len() }; - println!(" +0x{:02X} frames {:?} distinct values {distinct:2}{}", - w * 4, fv.iter().map(|v| format!("{v:08X}")).collect::>(), - if unique { " <- SEPARATES THE FRAMES" } else { "" }); + let distinct = { + let mut v: Vec = (0..names.len()).map(|i| word(i, w)).collect(); + v.sort_unstable(); + v.dedup(); + v.len() + }; + println!( + " +0x{:02X} frames {:?} distinct values {distinct:2}{}", + w * 4, + fv.iter().map(|v| format!("{v:08X}")).collect::>(), + if unique { + " <- SEPARATES THE FRAMES" + } else { + "" + } + ); } } diff --git a/crates/sylpheed-formats/examples/decl_flag_words.rs b/crates/sylpheed-formats/examples/decl_flag_words.rs index 4e684b95..55433aa6 100644 --- a/crates/sylpheed-formats/examples/decl_flag_words.rs +++ b/crates/sylpheed-formats/examples/decl_flag_words.rs @@ -8,8 +8,8 @@ //! distinct values: +0x28 (3) and +0x2C (6). This prints who has what. //! //! cargo run -p sylpheed-formats --example decl_flag_words -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, ui_layout}; const AT: usize = 0x20; const N: usize = 60; @@ -23,10 +23,23 @@ fn main() { let o = AT + i * N + off; u32::from_be_bytes([by[o], by[o + 1], by[o + 2], by[o + 3]]) }; - println!("{:<22} {:>10} {:>10} {:>10} {:>10}", "element", "+0x28", "+0x2C", "+0x34", "kind"); + println!( + "{:<22} {:>10} {:>10} {:>10} {:>10}", + "element", "+0x28", "+0x2C", "+0x34", "kind" + ); for (i, e) in b.elements.iter().enumerate() { - let mark = if e.name.starts_with("ptframe") { " <- FRAME" } else { "" }; - println!("{:<22} {:>10} {:>10} {:>10} {:>#10x}{mark}", - e.name, w(i, 0x28), w(i, 0x2C) as i32, w(i, 0x34), e.kind); + let mark = if e.name.starts_with("ptframe") { + " <- FRAME" + } else { + "" + }; + println!( + "{:<22} {:>10} {:>10} {:>10} {:>#10x}{mark}", + e.name, + w(i, 0x28), + w(i, 0x2C) as i32, + w(i, 0x34), + e.kind + ); } } diff --git a/crates/sylpheed-formats/examples/design_size_fallback.rs b/crates/sylpheed-formats/examples/design_size_fallback.rs index 8675b3c4..1a31538d 100644 --- a/crates/sylpheed-formats/examples/design_size_fallback.rs +++ b/crates/sylpheed-formats/examples/design_size_fallback.rs @@ -10,44 +10,62 @@ //! This replicates the scan through the public RATC API and counts. //! //! cargo run -p sylpheed-formats --example design_size_fallback -use sylpheed_formats::{pak::PakArchive, ratc, ui_layout}; use std::io::Write; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, ratc, ui_layout}; fn be32(b: &[u8], o: usize) -> u32 { - if o + 4 > b.len() { return 0 } + 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 root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC")); - let mut paks: Vec = std::fs::read_dir(root.join("dat")).expect("dat/") + 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().is_some_and(|x| x == "pak")).collect(); + .filter(|p| p.extension().is_some_and(|x| x == "pak")) + .collect(); paks.sort(); let (mut read, mut fell_back, mut nonstd) = (0usize, 0usize, 0usize); for pak in &paks { - let Ok(ar) = PakArchive::open(pak) else { continue }; + let Ok(ar) = PakArchive::open(pak) else { + continue; + }; let name = pak.file_name().unwrap().to_string_lossy().to_string(); let (mut r, mut f) = (0usize, 0usize); for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; - if !ui_layout::is_build(&by) { continue } - let Some(kids) = ratc::parse(&by) else { continue }; + if !ui_layout::is_build(&by) { + continue; + } + let Some(kids) = ratc::parse(&by) else { + continue; + }; // the same predicate ui_layout uses, over the same records // ⚠️ A first version took EVERY RATC child and failed its control: // it reported all 965 builds stating a non-1280x720 size, where // `screen list` prints 1280x720 for every one. `records` in // ui_layout is the `.rat` children only; a T8aD sprite header read // at +0x18 is garbage that passes the range test. - let found = kids.iter().filter(|k| k.kind == "RATC" || k.name.ends_with(".rat")).find_map(|k| { - let rec = &by[k.offset..(k.offset + k.size).min(by.len())]; - let (w, h) = (be32(rec, 0x18), be32(rec, 0x1c)); - (w > 0 && h > 0 && w <= 8192 && h <= 8192).then_some((w, h)) - }); + let found = kids + .iter() + .filter(|k| k.kind == "RATC" || k.name.ends_with(".rat")) + .find_map(|k| { + let rec = &by[k.offset..(k.offset + k.size).min(by.len())]; + let (w, h) = (be32(rec, 0x18), be32(rec, 0x1c)); + (w > 0 && h > 0 && w <= 8192 && h <= 8192).then_some((w, h)) + }); match found { - Some((w, h)) => { r += 1; if (w, h) != (1280, 720) { nonstd += 1; - println!(" {name} : a build states a NON-standard design size {w}x{h}"); } } + Some((w, h)) => { + r += 1; + if (w, h) != (1280, 720) { + nonstd += 1; + println!(" {name} : a build states a NON-standard design size {w}x{h}"); + } + } None => f += 1, } } @@ -55,7 +73,8 @@ fn main() { println!("{name:30} {r:5} read {f:5} FABRICATED"); std::io::stdout().flush().ok(); } - read += r; fell_back += f; + read += r; + fell_back += f; } println!("\n{read} builds state a design size, {fell_back} get the 1280x720 FALLBACK"); println!("{nonstd} builds state something other than 1280x720"); diff --git a/crates/sylpheed-formats/examples/dialog_button_rows.rs b/crates/sylpheed-formats/examples/dialog_button_rows.rs index 65788f26..106edffb 100644 --- a/crates/sylpheed-formats/examples/dialog_button_rows.rs +++ b/crates/sylpheed-formats/examples/dialog_button_rows.rs @@ -10,12 +10,16 @@ //! is independently known to place them (162/242/322/401/482, spacing 80). //! //! cargo run -p sylpheed-formats --example dialog_button_rows -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, ui_layout}; fn rows(ar: &PakArchive, entry: usize, what: &str) { - let Ok(by) = ar.read(&ar.entries()[entry]) else { return }; - let Some(b) = ui_layout::parse_build(&by) else { return }; + let Ok(by) = ar.read(&ar.entries()[entry]) else { + return; + }; + let Some(b) = ui_layout::parse_build(&by) else { + return; + }; let mut v: Vec<(i32, String)> = b .elements .iter() @@ -23,7 +27,12 @@ fn rows(ar: &PakArchive, entry: usize, what: &str) { let n = &e.name; (n.starts_with("pcbtn") || n.starts_with("ptbtn")) && !n.contains('f') }) - .map(|e| (e.rest().map(|k| k.y).unwrap_or(e.pivot_y as i32), e.name.clone())) + .map(|e| { + ( + e.rest().map(|k| k.y).unwrap_or(e.pivot_y as i32), + e.name.clone(), + ) + }) .collect(); v.sort_by_key(|r| r.0); println!("\n{what} (entry {entry}):"); @@ -39,7 +48,11 @@ fn rows(ar: &PakArchive, entry: usize, what: &str) { fn main() { let root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC")); let t = PakArchive::open(root.join("dat/GP_TITLE.pak")).expect("GP_TITLE"); - rows(&t, 5, "CONTROL: GP_TITLE main menu (must be 162/242/322/401/482)"); + rows( + &t, + 5, + "CONTROL: GP_TITLE main menu (must be 162/242/322/401/482)", + ); let d = PakArchive::open(root.join("dat/GP_DIALOG.pak")).expect("GP_DIALOG"); rows(&d, 2, "GP_DIALOG candidate for DLG_SELECT_DIFFICULTY"); rows(&d, 3, "GP_DIALOG entry 3 (the pair)"); diff --git a/crates/sylpheed-formats/examples/dialog_pair_37.rs b/crates/sylpheed-formats/examples/dialog_pair_37.rs index cc821037..63da9f30 100644 --- a/crates/sylpheed-formats/examples/dialog_pair_37.rs +++ b/crates/sylpheed-formats/examples/dialog_pair_37.rs @@ -11,8 +11,8 @@ //! CONTROL: entries 2/3 (identical element sets) must come out as "buttons match". //! //! cargo run -p sylpheed-formats --example dialog_pair_37 -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, ui_layout}; fn sig(ar: &PakArchive, i: usize) -> Option<(Vec<(String, i32)>, usize)> { let by = ar.read(&ar.entries()[i]).ok()?; @@ -21,7 +21,12 @@ fn sig(ar: &PakArchive, i: usize) -> Option<(Vec<(String, i32)>, usize)> { .elements .iter() .filter(|e| e.name.contains("btn")) - .map(|e| (e.name.clone(), e.rest().map(|k| k.y).unwrap_or(e.pivot_y as i32))) + .map(|e| { + ( + e.name.clone(), + e.rest().map(|k| k.y).unwrap_or(e.pivot_y as i32), + ) + }) .collect(); v.sort(); let n = v.len(); @@ -35,13 +40,17 @@ fn main() { let (mut match_btn, mut differ_btn, mut ctrl) = (0, 0, false); let mut examples = 0; for k in (0..n).step_by(2) { - let (Some((a, na)), Some((b, nb))) = (sig(&ar, k), sig(&ar, k + 1)) else { continue }; + let (Some((a, na)), Some((b, nb))) = (sig(&ar, k), sig(&ar, k + 1)) else { + continue; + }; if na != nb { continue; // the 26 already settled } if a == b { match_btn += 1; - if k == 2 { ctrl = true } + if k == 2 { + ctrl = true + } } else { differ_btn += 1; if examples < 5 { @@ -54,5 +63,8 @@ fn main() { } println!("\nequal-button-count pairs whose button NAMES+ROWS match : {match_btn}"); println!("equal-button-count pairs whose buttons DIFFER : {differ_btn}"); - println!("control (entries 2/3 counted as matching): {}", if ctrl { "PASSED" } else { "FAILED" }); + println!( + "control (entries 2/3 counted as matching): {}", + if ctrl { "PASSED" } else { "FAILED" } + ); } diff --git a/crates/sylpheed-formats/examples/dialog_pair_button_counts.rs b/crates/sylpheed-formats/examples/dialog_pair_button_counts.rs index 7f15ce23..6330e947 100644 --- a/crates/sylpheed-formats/examples/dialog_pair_button_counts.rs +++ b/crates/sylpheed-formats/examples/dialog_pair_button_counts.rs @@ -7,8 +7,8 @@ //! re-derives that with my own reader before I record the refutation. //! //! cargo run -p sylpheed-formats --example dialog_pair_button_counts -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, ui_layout}; fn btns(ar: &PakArchive, i: usize) -> Option { let by = ar.read(&ar.entries()[i]).ok()?; diff --git a/crates/sylpheed-formats/examples/dialog_pair_diffs.rs b/crates/sylpheed-formats/examples/dialog_pair_diffs.rs index 10027fe8..d8e87709 100644 --- a/crates/sylpheed-formats/examples/dialog_pair_diffs.rs +++ b/crates/sylpheed-formats/examples/dialog_pair_diffs.rs @@ -6,9 +6,9 @@ //! language pair should differ in the SAME slots with locale-marked names. //! //! cargo run -p sylpheed-formats --example dialog_pair_diffs -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::collections::BTreeSet; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, ui_layout}; fn names(ar: &PakArchive, i: usize) -> Option> { let by = ar.read(&ar.entries()[i]).ok()?; @@ -22,7 +22,9 @@ fn main() { let n = ar.entries().len(); let mut shown = 0; for k in (0..n).step_by(2) { - let (Some(a), Some(b)) = (names(&ar, k), names(&ar, k + 1)) else { continue }; + let (Some(a), Some(b)) = (names(&ar, k), names(&ar, k + 1)) else { + continue; + }; if a == b { continue; } @@ -35,8 +37,14 @@ fn main() { continue; } if shown < 6 { - println!("entries {k:3}/{:<3} shared {} only-in-{k}: {:?} only-in-{}: {:?}", - k + 1, a.intersection(&b).count(), oa, k + 1, ob); + println!( + "entries {k:3}/{:<3} shared {} only-in-{k}: {:?} only-in-{}: {:?}", + k + 1, + a.intersection(&b).count(), + oa, + k + 1, + ob + ); shown += 1; } } diff --git a/crates/sylpheed-formats/examples/dialog_pair_identity.rs b/crates/sylpheed-formats/examples/dialog_pair_identity.rs index 71da3a29..bf21fdb0 100644 --- a/crates/sylpheed-formats/examples/dialog_pair_identity.rs +++ b/crates/sylpheed-formats/examples/dialog_pair_identity.rs @@ -14,8 +14,8 @@ //! separate two unrelated dialogs cannot judge two similar ones. //! //! cargo run -p sylpheed-formats --example dialog_pair_identity -use sylpheed_formats::pak::PakArchive; use std::path::PathBuf; +use sylpheed_formats::pak::PakArchive; fn cmp(ar: &PakArchive, a: usize, b: usize, what: &str) { let (Ok(x), Ok(y)) = (ar.read(&ar.entries()[a]), ar.read(&ar.entries()[b])) else { @@ -27,10 +27,16 @@ fn cmp(ar: &PakArchive, a: usize, b: usize, what: &str) { let diff = (0..n).filter(|&i| x[i] != y[i]).count(); let first = (0..n).find(|&i| x[i] != y[i]); println!("{what}"); - println!(" sizes {} / {} ({})", x.len(), y.len(), - if same_len { "equal" } else { "DIFFER" }); - println!(" differing bytes over the common prefix: {diff} / {n} ({:.2}%)", - 100.0 * diff as f64 / n as f64); + println!( + " sizes {} / {} ({})", + x.len(), + y.len(), + if same_len { "equal" } else { "DIFFER" } + ); + println!( + " differing bytes over the common prefix: {diff} / {n} ({:.2}%)", + 100.0 * diff as f64 / n as f64 + ); match first { None if same_len => println!(" => BYTE-IDENTICAL — a duplicate"), None => println!(" => one is a prefix of the other"), @@ -41,7 +47,12 @@ fn cmp(ar: &PakArchive, a: usize, b: usize, what: &str) { fn main() { let root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC")); let ar = PakArchive::open(root.join("dat/GP_DIALOG.pak")).expect("GP_DIALOG"); - cmp(&ar, 10, 11, "CONTROL: entries 10/11 — known two different dialogs"); + cmp( + &ar, + 10, + 11, + "CONTROL: entries 10/11 — known two different dialogs", + ); cmp(&ar, 0, 1, "entries 0/1"); cmp(&ar, 2, 3, "entries 2/3 — the DIFFICULTY build"); } diff --git a/crates/sylpheed-formats/examples/dialog_pairing.rs b/crates/sylpheed-formats/examples/dialog_pairing.rs index 6a44b687..4234a348 100644 --- a/crates/sylpheed-formats/examples/dialog_pairing.rs +++ b/crates/sylpheed-formats/examples/dialog_pairing.rs @@ -9,9 +9,9 @@ //! sets are the signature of a language pair. //! //! cargo run -p sylpheed-formats --example dialog_pairing -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::collections::BTreeSet; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, ui_layout}; fn names(ar: &PakArchive, i: usize) -> Option> { let by = ar.read(&ar.entries()[i]).ok()?; @@ -27,7 +27,12 @@ fn main() { for k in (0..n).step_by(2) { match (names(&ar, k), names(&ar, k + 1)) { (Some(a), Some(b)) => { - if a == b { adj_same += 1; println!(" identical pair: entries {k}/{}", k+1) } else { adj_diff += 1 } + if a == b { + adj_same += 1; + println!(" identical pair: entries {k}/{}", k + 1) + } else { + adj_diff += 1 + } } _ => skipped += 1, } @@ -38,10 +43,16 @@ fn main() { for k in 0..n / 2 { match (names(&ar, k), names(&ar, k + n / 2)) { (Some(a), Some(b)) => { - if a == b { h_same += 1 } else { h_diff += 1 } + if a == b { + h_same += 1 + } else { + h_diff += 1 + } } _ => h_skip += 1, } } - println!("HALVES pairing (i, i+70): identical {h_same} differing {h_diff} unreadable {h_skip}"); + println!( + "HALVES pairing (i, i+70): identical {h_same} differing {h_diff} unreadable {h_skip}" + ); } diff --git a/crates/sylpheed-formats/examples/dialog_pak_shape.rs b/crates/sylpheed-formats/examples/dialog_pak_shape.rs index 7e0e4cfd..2276ddc9 100644 --- a/crates/sylpheed-formats/examples/dialog_pak_shape.rs +++ b/crates/sylpheed-formats/examples/dialog_pak_shape.rs @@ -6,8 +6,8 @@ //! join would be positional — this checks. //! //! cargo run -p sylpheed-formats --example dialog_pak_shape -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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")); diff --git a/crates/sylpheed-formats/examples/element_records.rs b/crates/sylpheed-formats/examples/element_records.rs index fd452b71..9cfd42ad 100644 --- a/crates/sylpheed-formats/examples/element_records.rs +++ b/crates/sylpheed-formats/examples/element_records.rs @@ -10,8 +10,8 @@ //! //! cargo run -p sylpheed-formats --example element_records -- GP_TITLE ptbtn00 //! cargo run -p sylpheed-formats --example element_records -- --census -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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")); @@ -19,22 +19,35 @@ fn main() { 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(); + 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 }; + 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 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 } + 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!( + "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."); @@ -46,22 +59,49 @@ fn main() { 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 }; + 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 } + 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 } + 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()) + 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(", ")); } diff --git a/crates/sylpheed-formats/examples/extras_button_order.rs b/crates/sylpheed-formats/examples/extras_button_order.rs index 35b56f75..96f5eb5c 100644 --- a/crates/sylpheed-formats/examples/extras_button_order.rs +++ b/crates/sylpheed-formats/examples/extras_button_order.rs @@ -11,12 +11,16 @@ //! known screen it cannot be trusted on an unknown one. //! //! cargo run -p sylpheed-formats --example extras_button_order -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, ui_layout}; fn report(ar: &PakArchive, entry: usize, what: &str) { - let Ok(by) = ar.read(&ar.entries()[entry]) else { return }; - let Some(b) = ui_layout::parse_build(&by) else { return }; + let Ok(by) = ar.read(&ar.entries()[entry]) else { + return; + }; + let Some(b) = ui_layout::parse_build(&by) else { + return; + }; let mut rows: Vec<(i32, String, u32)> = b .elements .iter() diff --git a/crates/sylpheed-formats/examples/find_difficulty_build.rs b/crates/sylpheed-formats/examples/find_difficulty_build.rs index 5aa7664d..fa987a12 100644 --- a/crates/sylpheed-formats/examples/find_difficulty_build.rs +++ b/crates/sylpheed-formats/examples/find_difficulty_build.rs @@ -13,8 +13,8 @@ //! is independently known (entries 6/9, buttons ptbtn11/12/13). //! //! cargo run -p sylpheed-formats --example find_difficulty_build -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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")); @@ -28,10 +28,14 @@ fn main() { let mut found_extras = false; for p in &paks { - let Ok(ar) = PakArchive::open(p) else { continue }; + let Ok(ar) = PakArchive::open(p) else { + continue; + }; for (i, e) in ar.entries().iter().enumerate() { let Ok(by) = ar.read(e) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; let btns: Vec<&String> = b .records .keys() @@ -51,8 +55,11 @@ fn main() { if name != "GP_TITLE.pak" { let mut names: Vec = btns.iter().map(|s| (*s).clone()).collect(); names.sort(); - println!(" {name:28} entry {i:3} {} buttons {:?}", btns.len(), - &names[..names.len().min(8)]); + println!( + " {name:28} entry {i:3} {} buttons {:?}", + btns.len(), + &names[..names.len().min(8)] + ); } } } diff --git a/crates/sylpheed-formats/examples/find_difficulty_names.rs b/crates/sylpheed-formats/examples/find_difficulty_names.rs index caec503e..b4e325ce 100644 --- a/crates/sylpheed-formats/examples/find_difficulty_names.rs +++ b/crates/sylpheed-formats/examples/find_difficulty_names.rs @@ -14,8 +14,8 @@ //! proves nothing unless it can find something. //! //! cargo run -p sylpheed-formats --example find_difficulty_names -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, ui_layout}; const WANTED: &[&str] = &["easy", "normal", "hard", "diff", "level", "rank"]; @@ -31,17 +31,16 @@ fn main() { let mut control = false; let mut hits = 0usize; for p in &paks { - let Ok(ar) = PakArchive::open(p) else { continue }; + let Ok(ar) = PakArchive::open(p) else { + continue; + }; let pname = p.file_name().unwrap().to_string_lossy().to_string(); for (i, e) in ar.entries().iter().enumerate() { let Ok(by) = ar.read(e) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; - let all: Vec = b - .sprites - .keys() - .chain(b.records.keys()) - .cloned() - .collect(); + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; + let all: Vec = b.sprites.keys().chain(b.records.keys()).cloned().collect(); if pname == "GP_TITLE.pak" && all.iter().any(|n| n.contains("ptbtn11")) { control = true; } @@ -61,6 +60,9 @@ fn main() { } } } - println!("\ncontrol (found ptbtn11 in GP_TITLE): {}", if control { "PASSED" } else { "FAILED" }); + println!( + "\ncontrol (found ptbtn11 in GP_TITLE): {}", + if control { "PASSED" } else { "FAILED" } + ); println!("{hits} build(s) carried a difficulty-ish name"); } diff --git a/crates/sylpheed-formats/examples/find_stream_by_size.rs b/crates/sylpheed-formats/examples/find_stream_by_size.rs index 7a58ad89..4b7bdd71 100644 --- a/crates/sylpheed-formats/examples/find_stream_by_size.rs +++ b/crates/sylpheed-formats/examples/find_stream_by_size.rs @@ -37,7 +37,9 @@ fn all_descriptors(buf: &[u8]) -> Vec<(usize, u32)> { fn main() { let mut args = std::env::args().skip(1); - let disc = args.next().expect("usage: find_stream_by_size …"); + let disc = args + .next() + .expect("usage: find_stream_by_size …"); let wanted: Vec = args.filter_map(|a| a.parse().ok()).collect(); assert!(!wanted.is_empty(), "give at least one payload size"); // A `to_xma_riffs` chunk is the payload plus a 60-byte RIFF wrapper. diff --git a/crates/sylpheed-formats/examples/focus_alpha_census.rs b/crates/sylpheed-formats/examples/focus_alpha_census.rs index 22116108..18e164f4 100644 --- a/crates/sylpheed-formats/examples/focus_alpha_census.rs +++ b/crates/sylpheed-formats/examples/focus_alpha_census.rs @@ -12,9 +12,12 @@ use sylpheed_formats::{pak, ratc, ui_layout}; fn main() { let root = std::env::var("SYLPHEED_DISC").unwrap_or_else(|_| "/disc".into()); - let mut paks: Vec<_> = std::fs::read_dir(format!("{root}/dat")).expect("dat/") - .flatten().map(|e| e.path()) - .filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pak")).collect(); + let mut paks: Vec<_> = std::fs::read_dir(format!("{root}/dat")) + .expect("dat/") + .flatten() + .map(|e| e.path()) + .filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pak")) + .collect(); paks.sort(); let (mut n_rec, mut n_elem, mut varying) = (0usize, 0usize, 0usize); let (mut at_peak, mut mid_ramp) = (0usize, 0usize); @@ -22,31 +25,55 @@ fn main() { let mut hits: Vec = Vec::new(); for p in &paks { let pn = p.file_name().unwrap().to_string_lossy().to_string(); - let Ok(ar) = pak::PakArchive::open(p) else { continue }; + let Ok(ar) = pak::PakArchive::open(p) else { + continue; + }; for (ei, e) in ar.entries().iter().enumerate() { let Ok(by) = ar.read(e) else { continue }; - if !ratc::is_ratc(&by) { continue } - let Some(b) = ui_layout::parse_build(&by) else { continue }; + if !ratc::is_ratc(&by) { + continue; + } + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; for (rn, &(o, s)) in &b.records { // A focus record is one whose name is another record's plus `f`. - let Some(stem) = rn.strip_suffix("f.rat") else { continue }; - if !b.records.contains_key(&format!("{stem}.rat")) { continue } - if o + s > by.len() { continue } - let Some(lb) = ui_layout::parse_build(&by[o..o + s]) else { continue }; + let Some(stem) = rn.strip_suffix("f.rat") else { + continue; + }; + if !b.records.contains_key(&format!("{stem}.rat")) { + continue; + } + if o + s > by.len() { + continue; + } + let Some(lb) = ui_layout::parse_build(&by[o..o + s]) else { + continue; + }; n_rec += 1; for el in &lb.elements { - if el.keyframes.is_empty() { continue } + if el.keyframes.is_empty() { + continue; + } n_elem += 1; let a: Vec = el.keyframes.iter().map(|k| k.fade >> 24).collect(); let (lo, hi) = (*a.iter().min().unwrap(), *a.iter().max().unwrap()); - if lo == hi { continue } + if lo == hi { + continue; + } varying += 1; let rest = el.rest().map(|k| k.fade >> 24).unwrap_or(0); - if rest == hi { at_peak += 1 } else { mid_ramp += 1 } + if rest == hi { + at_peak += 1 + } else { + mid_ramp += 1 + } *by_pak.entry(pn.clone()).or_default() += 1; hits.push(format!( "{pn} [{ei}] {rn}::{} alpha {lo}..{hi} rest()={rest}{}", - el.name, if rest == hi { " 🔴 == PEAK" } else { "" })); + el.name, + if rest == hi { " 🔴 == PEAK" } else { "" } + )); } } } @@ -57,9 +84,14 @@ fn main() { println!(" of which rest() == the PEAK : {at_peak} <- burns bright forever"); println!(" of which rest() is MID-RAMP : {mid_ramp} <- neither extreme; looks plausible"); println!("\nby pak:"); - for (k, v) in &by_pak { println!(" {k:<34} {v}") } + for (k, v) in &by_pak { + println!(" {k:<34} {v}") + } println!("\nevery varying one:"); - hits.sort(); hits.dedup(); - for h in &hits { println!(" {h}") } + hits.sort(); + hits.dedup(); + for h in &hits { + println!(" {h}") + } println!("\n({} distinct)", hits.len()); } diff --git a/crates/sylpheed-formats/examples/forced_backdrop_ink_thresholds.rs b/crates/sylpheed-formats/examples/forced_backdrop_ink_thresholds.rs index c8a14263..321031bc 100644 --- a/crates/sylpheed-formats/examples/forced_backdrop_ink_thresholds.rs +++ b/crates/sylpheed-formats/examples/forced_backdrop_ink_thresholds.rs @@ -71,8 +71,13 @@ fn main() { let without = order_without_rule(&b, &by); let a = ui_layout::compose_with_order(&b, &by, opts.clone(), None, Some(&with)); let c = ui_layout::compose_with_order(&b, &by, opts, None, Some(&without)); - println!("\n== GP_TITLE entry {entry} — {label} ({}x{})", a.width, a.height); - println!(" threshold | RGB>t with rule | A>t with rule | RGB>t WITHOUT | A>t WITHOUT"); + println!( + "\n== GP_TITLE entry {entry} — {label} ({}x{})", + a.width, a.height + ); + println!( + " threshold | RGB>t with rule | A>t with rule | RGB>t WITHOUT | A>t WITHOUT" + ); for t in [0u8, 1, 2, 4, 8, 16] { let (r1, a1) = counts(&a.rgba, t); let (r0, a0) = counts(&c.rgba, t); diff --git a/crates/sylpheed-formats/examples/forced_backdrop_key_source.rs b/crates/sylpheed-formats/examples/forced_backdrop_key_source.rs index f8a5571a..fc3240da 100644 --- a/crates/sylpheed-formats/examples/forced_backdrop_key_source.rs +++ b/crates/sylpheed-formats/examples/forced_backdrop_key_source.rs @@ -29,7 +29,9 @@ fn main() { let (mut read, mut implied, mut none) = (0usize, 0usize, 0usize); println!("# archive entry element key_source key"); for pak in &paks { - let Ok(ar) = PakArchive::open(pak) else { continue }; + let Ok(ar) = PakArchive::open(pak) else { + continue; + }; let name = pak.file_name().unwrap().to_string_lossy().to_string(); for (i, e) in ar.entries().iter().enumerate() { let Ok(by) = ar.read(e) else { continue }; diff --git a/crates/sylpheed-formats/examples/forced_backdrop_necessity.rs b/crates/sylpheed-formats/examples/forced_backdrop_necessity.rs index 5907220d..cdec2df0 100644 --- a/crates/sylpheed-formats/examples/forced_backdrop_necessity.rs +++ b/crates/sylpheed-formats/examples/forced_backdrop_necessity.rs @@ -55,60 +55,63 @@ fn main() { } eprintln!("# scanning {} archive(s)", paks.len()); for path in &paks { - let ar = PakArchive::open(path).expect("pak"); - println!("# {}", path.display()); - println!("# entry forced decides elements note"); + let ar = PakArchive::open(path).expect("pak"); + println!("# {}", path.display()); + println!("# entry forced decides elements note"); - let mut decides = Vec::new(); - let mut agrees = Vec::new(); - #[allow(unused)] - let _ = (&decides, &agrees); - for (i, e) in ar.entries().iter().enumerate() { - let Ok(by) = ar.read(e) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { - continue; - }; - let with = ui_layout::derived_paint_order(&b, &by); - let without = order_without_rule(&b, &by); + let mut decides = Vec::new(); + let mut agrees = Vec::new(); + #[allow(unused)] + let _ = (&decides, &agrees); + for (i, e) in ar.entries().iter().enumerate() { + let Ok(by) = ar.read(e) else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; + let with = ui_layout::derived_paint_order(&b, &by); + let without = order_without_rule(&b, &by); - // Which elements does the rule fire on, and of those, which have no key - // of their own to fall back on? - let mut forced = Vec::new(); - let mut keyless = Vec::new(); - for el in &b.elements { - if !ui_layout::forced_backdrop(&b, el) { + // Which elements does the rule fire on, and of those, which have no key + // of their own to fall back on? + let mut forced = Vec::new(); + let mut keyless = Vec::new(); + for el in &b.elements { + if !ui_layout::forced_backdrop(&b, el) { + continue; + } + forced.push(el.name.clone()); + let own = ui_layout::sprite_layer_key(&b, &by, el) + .or_else(|| ui_layout::implied_layer_key(&el.name)); + if own.is_none() { + keyless.push(el.name.clone()); + } + } + if forced.is_empty() { continue; } - forced.push(el.name.clone()); - let own = ui_layout::sprite_layer_key(&b, &by, el) - .or_else(|| ui_layout::implied_layer_key(&el.name)); - if own.is_none() { - keyless.push(el.name.clone()); + let moved = with != without; + if moved { + decides.push(i); + } else { + agrees.push(i); } + println!( + " {i:>5} {:>6} {:>7} {:>8} forced=[{}] keyless=[{}]", + forced.len(), + if moved { "YES" } else { "no" }, + b.elements.len(), + forced.join(","), + keyless.join(","), + ); } - if forced.is_empty() { - continue; - } - let moved = with != without; - if moved { - decides.push(i); - } else { - agrees.push(i); - } - println!( - " {i:>5} {:>6} {:>7} {:>8} forced=[{}] keyless=[{}]", - forced.len(), - if moved { "YES" } else { "no" }, - b.elements.len(), - forced.join(","), - keyless.join(","), - ); + println!("# rule DECIDES the order on entries {decides:?}"); + println!("# rule merely AGREES on entries {agrees:?}\n"); + total_decides += decides.len(); + total_agrees += agrees.len(); } - println!("# rule DECIDES the order on entries {decides:?}"); - println!("# rule merely AGREES on entries {agrees:?}\n"); - total_decides += decides.len(); - total_agrees += agrees.len(); - } - println!("# TOTAL over {} archive(s): {total_decides} deciding entries, \ -{total_agrees} agreeing", paks.len()); + println!( + "# TOTAL over {} archive(s): {total_decides} deciding entries, \ +{total_agrees} agreeing", + paks.len() + ); } diff --git a/crates/sylpheed-formats/examples/forced_backdrop_pixel_cost.rs b/crates/sylpheed-formats/examples/forced_backdrop_pixel_cost.rs index 0b00a3b1..ffd850b1 100644 --- a/crates/sylpheed-formats/examples/forced_backdrop_pixel_cost.rs +++ b/crates/sylpheed-formats/examples/forced_backdrop_pixel_cost.rs @@ -64,7 +64,9 @@ fn main() { println!("# archive entry element changed_px total_px ink_px(control) pct"); let (mut decided, mut zero_cost, mut blind) = (0usize, 0usize, 0usize); for pak in &paks { - let Ok(ar) = PakArchive::open(pak) else { continue }; + let Ok(ar) = PakArchive::open(pak) else { + continue; + }; let name = pak.file_name().unwrap().to_string_lossy().to_string(); for (i, e) in ar.entries().iter().enumerate() { let Ok(by) = ar.read(e) else { continue }; diff --git a/crates/sylpheed-formats/examples/four_button_row_rivals.rs b/crates/sylpheed-formats/examples/four_button_row_rivals.rs index 1aff3697..d7461390 100644 --- a/crates/sylpheed-formats/examples/four_button_row_rivals.rs +++ b/crates/sylpheed-formats/examples/four_button_row_rivals.rs @@ -9,8 +9,8 @@ //! cannot find the incumbent proves nothing. //! //! cargo run -p sylpheed-formats --example four_button_row_rivals -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, ui_layout}; const WANT: [i32; 4] = [259, 329, 399, 469]; @@ -24,11 +24,15 @@ fn main() { paks.sort(); let (mut incumbent, mut rivals) = (0, 0); for p in &paks { - let Ok(ar) = PakArchive::open(p) else { continue }; + let Ok(ar) = PakArchive::open(p) else { + continue; + }; let pname = p.file_name().unwrap().to_string_lossy().to_string(); for (i, e) in ar.entries().iter().enumerate() { let Ok(by) = ar.read(e) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; let mut ys: Vec = b .elements .iter() @@ -43,13 +47,21 @@ fn main() { let close = ys.iter().zip(WANT.iter()).all(|(a, b)| (a - b).abs() <= 6); if close { let is_inc = pname == "GP_DIALOG.pak" && (i == 2 || i == 3); - if is_inc { incumbent += 1 } else { rivals += 1 } - println!(" {}{pname:24} entry {i:4} rows {ys:?}", - if is_inc { "INCUMBENT " } else { "RIVAL " }); + if is_inc { + incumbent += 1 + } else { + rivals += 1 + } + println!( + " {}{pname:24} entry {i:4} rows {ys:?}", + if is_inc { "INCUMBENT " } else { "RIVAL " } + ); } } } - println!("\ncontrol: found {incumbent} incumbent build(s) (want 2) — {}", - if incumbent == 2 { "PASSED" } else { "FAILED" }); + println!( + "\ncontrol: found {incumbent} incumbent build(s) (want 2) — {}", + if incumbent == 2 { "PASSED" } else { "FAILED" } + ); println!("{rivals} rival build(s) elsewhere on the disc"); } diff --git a/crates/sylpheed-formats/examples/frame_alpha_census.rs b/crates/sylpheed-formats/examples/frame_alpha_census.rs index d6188147..8f990d43 100644 --- a/crates/sylpheed-formats/examples/frame_alpha_census.rs +++ b/crates/sylpheed-formats/examples/frame_alpha_census.rs @@ -8,8 +8,8 @@ //! LOW in our decode. This example tests the second, which is on the disc. //! //! cargo run -p sylpheed-formats --example frame_alpha_census -use sylpheed_formats::{pak::PakArchive, t8ad, ui_layout}; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, t8ad, ui_layout}; fn census(name: &str, img: &t8ad::T8adImage) { let n = (img.width * img.height) as usize; @@ -22,16 +22,28 @@ fn census(name: &str, img: &t8ad::T8adImage) { let max = (0..256).rev().find(|&a| hist[a] > 0).unwrap_or(0); let nonzero = n - zero; // the top five alpha values that actually occur, by population - let mut top: Vec<(usize, usize)> = (1..256).map(|a| (hist[a], a)).filter(|&(c, _)| c > 0).collect(); + let mut top: Vec<(usize, usize)> = (1..256) + .map(|a| (hist[a], a)) + .filter(|&(c, _)| c > 0) + .collect(); top.sort_unstable_by(|a, b| b.0.cmp(&a.0)); - let top5: Vec = top.iter().take(5).map(|&(c, a)| format!("{a}x{c}")).collect(); + let top5: Vec = top + .iter() + .take(5) + .map(|&(c, a)| format!("{a}x{c}")) + .collect(); println!( "{name:<16} {}x{:<4} px={n:<8} a=0:{:5.1}% a=255:{:5.1}% max={max:<3} \ partial(1..254)/nonzero={:5.1}% top:[{}]", - img.width, img.height, + img.width, + img.height, 100.0 * zero as f64 / n as f64, 100.0 * full as f64 / n as f64, - if nonzero > 0 { 100.0 * (nonzero - full) as f64 / nonzero as f64 } else { 0.0 }, + if nonzero > 0 { + 100.0 * (nonzero - full) as f64 / nonzero as f64 + } else { + 0.0 + }, top5.join(" ") ); } @@ -39,16 +51,23 @@ fn census(name: &str, img: &t8ad::T8adImage) { fn main() { let root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC")); let argv: Vec = std::env::args().skip(1).collect(); - let pak = argv.iter().find(|a| a.parse::().is_err()) - .cloned().unwrap_or_else(|| "GP_TITLE".to_string()); + let pak = argv + .iter() + .find(|a| a.parse::().is_err()) + .cloned() + .unwrap_or_else(|| "GP_TITLE".to_string()); let ar = PakArchive::open(root.join(format!("dat/{pak}.pak"))).expect("pak"); // Builds default to the two the port ships and can be overridden, so the // same census serves the title (4) and the `PRESS (A)` plate (2). let args: Vec = argv.iter().filter_map(|a| a.parse().ok()).collect(); let builds: Vec = if args.is_empty() { vec![5, 6] } else { args }; for build in builds { - let Ok(by) = ar.read(&ar.entries()[build]) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; + let Ok(by) = ar.read(&ar.entries()[build]) else { + continue; + }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; println!("=== {pak} build {build} ==="); let mut names: Vec<&String> = b.sprites.keys().collect(); names.sort(); diff --git a/crates/sylpheed-formats/examples/frame_keyframe_unknowns.rs b/crates/sylpheed-formats/examples/frame_keyframe_unknowns.rs index 8e603af5..53d77fa1 100644 --- a/crates/sylpheed-formats/examples/frame_keyframe_unknowns.rs +++ b/crates/sylpheed-formats/examples/frame_keyframe_unknowns.rs @@ -8,14 +8,16 @@ //! per-element draw mode could live. //! //! cargo run -p sylpheed-formats --example frame_keyframe_unknowns -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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 ar = PakArchive::open(root.join("dat/GP_TITLE.pak")).expect("GP_TITLE"); - println!("{:<22} {:>5} {:>3} {:>10} {:>10} {:>8} {:>8} {:>8}", - "element", "build", "kf", "unknown_4", "unknown_8", "fade", "tint", "rot"); + println!( + "{:<22} {:>5} {:>3} {:>10} {:>10} {:>8} {:>8} {:>8}", + "element", "build", "kf", "unknown_4", "unknown_8", "fade", "tint", "rot" + ); let mut frame_sets: Vec<(String, i32, i32, u32, u32, i32)> = Vec::new(); let mut other_sets: Vec<(String, i32, i32, u32, u32, i32)> = Vec::new(); for build in [5usize, 6] { @@ -23,24 +25,55 @@ fn main() { let b = ui_layout::parse_build(&by).expect("build"); for e in &b.elements { for (i, k) in e.keyframes.iter().enumerate() { - println!("{:<22} {build:>5} {i:>3} {:>10} {:>10} {:08X} {:08X} {:>8}", - e.name, k.unknown_4, k.unknown_8, k.fade, k.tint, k.rotation_deg); - let row = (e.name.clone(), k.unknown_4, k.unknown_8, k.fade, k.tint, k.rotation_deg); - if e.name.contains("frame") { frame_sets.push(row) } else { other_sets.push(row) } + println!( + "{:<22} {build:>5} {i:>3} {:>10} {:>10} {:08X} {:08X} {:>8}", + e.name, k.unknown_4, k.unknown_8, k.fade, k.tint, k.rotation_deg + ); + let row = ( + e.name.clone(), + k.unknown_4, + k.unknown_8, + k.fade, + k.tint, + k.rotation_deg, + ); + if e.name.contains("frame") { + frame_sets.push(row) + } else { + other_sets.push(row) + } } } } - println!("\nframe keyframes: {} other keyframes: {}", frame_sets.len(), other_sets.len()); + println!( + "\nframe keyframes: {} other keyframes: {}", + frame_sets.len(), + other_sets.len() + ); for (label, get) in [ - ("unknown_4", 0usize), ("unknown_8", 1), ("fade", 2), ("tint", 3), ("rotation", 4), + ("unknown_4", 0usize), + ("unknown_8", 1), + ("fade", 2), + ("tint", 3), + ("rotation", 4), ] { let val = |r: &(String, i32, i32, u32, u32, i32)| -> i64 { - match get { 0 => r.1 as i64, 1 => r.2 as i64, 2 => r.3 as i64, 3 => r.4 as i64, _ => r.5 as i64 } + match get { + 0 => r.1 as i64, + 1 => r.2 as i64, + 2 => r.3 as i64, + 3 => r.4 as i64, + _ => r.5 as i64, + } }; let fv: std::collections::BTreeSet = frame_sets.iter().map(val).collect(); let ov: std::collections::BTreeSet = other_sets.iter().map(val).collect(); let only_frames: Vec<&i64> = fv.iter().filter(|v| !ov.contains(v)).collect(); - println!("{label:<10} frames take {:?} others take {} distinct values; frame-only values: {:?}", - fv, ov.len(), only_frames); + println!( + "{label:<10} frames take {:?} others take {} distinct values; frame-only values: {:?}", + fv, + ov.len(), + only_frames + ); } } diff --git a/crates/sylpheed-formats/examples/frame_vs_accurate_words.rs b/crates/sylpheed-formats/examples/frame_vs_accurate_words.rs index 395c3be2..347a5852 100644 --- a/crates/sylpheed-formats/examples/frame_vs_accurate_words.rs +++ b/crates/sylpheed-formats/examples/frame_vs_accurate_words.rs @@ -8,8 +8,8 @@ //! must put `pteff10` on the ACCURATE side. //! //! cargo run -p sylpheed-formats --example frame_vs_accurate_words -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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")); @@ -23,41 +23,74 @@ fn main() { for n in names { let (off, size) = b.sprites[n]; let s = &by[off..(off + size).min(by.len())]; - if s.len() < 48 || &s[0..4] != b"T8aD" { continue } + if s.len() < 48 || &s[0..4] != b"T8aD" { + continue; + } let ws: Vec = (0..12) - .map(|k| u32::from_be_bytes([s[k*4], s[k*4+1], s[k*4+2], s[k*4+3]])) + .map(|k| u32::from_be_bytes([s[k * 4], s[k * 4 + 1], s[k * 4 + 2], s[k * 4 + 3]])) .collect(); rows.push((n.clone(), build, ws)); } } let is_frame = |n: &str| n.starts_with("ptframe"); - println!("{:<16} {:>5} +0x04 +0x08 +0x1C +0x2C", "sprite", "build"); + println!( + "{:<16} {:>5} +0x04 +0x08 +0x1C +0x2C", + "sprite", "build" + ); for (n, b, w) in &rows { - println!("{n:<16} {b:>5} {:08X} {:08X} {:08X} {:08X}{}", - w[1], w[2], w[7], w[11], if is_frame(n) { " <- TOO DARK" } else { "" }); + println!( + "{n:<16} {b:>5} {:08X} {:08X} {:08X} {:08X}{}", + w[1], + w[2], + w[7], + w[11], + if is_frame(n) { " <- TOO DARK" } else { "" } + ); } println!("\nwords where every ptframe* agrees and NO other sprite takes that value:"); - let frames: Vec<&(String, usize, Vec)> = rows.iter().filter(|(n,_,_)| is_frame(n)).collect(); + let frames: Vec<&(String, usize, Vec)> = + rows.iter().filter(|(n, _, _)| is_frame(n)).collect(); let mut any = false; for k in 0..12 { let v = frames[0].2[k]; - if !frames.iter().all(|r| r.2[k] == v) { continue } - if rows.iter().any(|(n,_,w)| !is_frame(n) && w[k] == v) { continue } - println!(" word {k} (+0x{:02X}) = {v:08X}", k*4); any = true; + if !frames.iter().all(|r| r.2[k] == v) { + continue; + } + if rows.iter().any(|(n, _, w)| !is_frame(n) && w[k] == v) { + continue; + } + println!(" word {k} (+0x{:02X}) = {v:08X}", k * 4); + any = true; } - if !any { println!(" NONE — no header word separates the four frames from the rest"); } - println!("\nper-bit check on +0x04 and +0x08 (a bit that is 1 on all frames, 0 on all others):"); + if !any { + println!(" NONE — no header word separates the four frames from the rest"); + } + println!( + "\nper-bit check on +0x04 and +0x08 (a bit that is 1 on all frames, 0 on all others):" + ); let mut anyb = false; for &k in &[1usize, 2] { for bit in 0..32 { let on = |v: u32| (v >> bit) & 1 == 1; - if frames.iter().all(|r| on(r.2[k])) && rows.iter().all(|(n,_,w)| is_frame(n) || !on(w[k])) { - println!(" +0x{:02X} bit {bit} (0x{:X})", k*4, 1u32 << bit); anyb = true; + if frames.iter().all(|r| on(r.2[k])) + && rows.iter().all(|(n, _, w)| is_frame(n) || !on(w[k])) + { + println!(" +0x{:02X} bit {bit} (0x{:X})", k * 4, 1u32 << bit); + anyb = true; } - if frames.iter().all(|r| !on(r.2[k])) && rows.iter().all(|(n,_,w)| is_frame(n) || on(w[k])) { - println!(" +0x{:02X} bit {bit} (0x{:X}) INVERTED", k*4, 1u32 << bit); anyb = true; + if frames.iter().all(|r| !on(r.2[k])) + && rows.iter().all(|(n, _, w)| is_frame(n) || on(w[k])) + { + println!( + " +0x{:02X} bit {bit} (0x{:X}) INVERTED", + k * 4, + 1u32 << bit + ); + anyb = true; } } } - if !anyb { println!(" NONE"); } + if !anyb { + println!(" NONE"); + } } diff --git a/crates/sylpheed-formats/examples/gp_title_buttons.rs b/crates/sylpheed-formats/examples/gp_title_buttons.rs index e006d062..8d7b910a 100644 --- a/crates/sylpheed-formats/examples/gp_title_buttons.rs +++ b/crates/sylpheed-formats/examples/gp_title_buttons.rs @@ -6,15 +6,17 @@ //! GP_TITLE, then NEW GAME loads nothing external and that reading is wrong. //! //! cargo run -p sylpheed-formats --example gp_title_buttons -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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 ar = PakArchive::open(root.join("dat/GP_TITLE.pak")).expect("GP_TITLE.pak"); for (i, e) in ar.entries().iter().enumerate() { let Ok(by) = ar.read(e) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; let mut btns: Vec = b .records .keys() diff --git a/crates/sylpheed-formats/examples/gp_title_entry_names.rs b/crates/sylpheed-formats/examples/gp_title_entry_names.rs index 2aeb2505..a3bcdfc4 100644 --- a/crates/sylpheed-formats/examples/gp_title_entry_names.rs +++ b/crates/sylpheed-formats/examples/gp_title_entry_names.rs @@ -1,14 +1,24 @@ -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::path::PathBuf; -fn main(){ - let root=PathBuf::from(std::env::var("SYLPHEED_DISC").unwrap()); - let ar=PakArchive::open(root.join("dat/GP_TITLE.pak")).unwrap(); - for (i,e) in ar.entries().iter().enumerate(){ - let Ok(by)=ar.read(e) else { println!("{i:2} "); continue }; +use sylpheed_formats::{pak::PakArchive, ui_layout}; +fn main() { + let root = PathBuf::from(std::env::var("SYLPHEED_DISC").unwrap()); + let ar = PakArchive::open(root.join("dat/GP_TITLE.pak")).unwrap(); + for (i, e) in ar.entries().iter().enumerate() { + let Ok(by) = ar.read(e) else { + println!("{i:2} "); + continue; + }; let names: Vec = ui_layout::parse_build(&by) - .map(|b| b.sprites.keys().take(2).cloned().collect()).unwrap_or_default(); + .map(|b| b.sprites.keys().take(2).cloned().collect()) + .unwrap_or_default(); let rec: Vec = ui_layout::parse_build(&by) - .map(|b| b.records.keys().take(2).cloned().collect()).unwrap_or_default(); - println!("{i:2} {} B sprites {:?} records {:?}", by.len(), names, rec); + .map(|b| b.records.keys().take(2).cloned().collect()) + .unwrap_or_default(); + println!( + "{i:2} {} B sprites {:?} records {:?}", + by.len(), + names, + rec + ); } } diff --git a/crates/sylpheed-formats/examples/gp_title_pair_check.rs b/crates/sylpheed-formats/examples/gp_title_pair_check.rs index 66b853d3..282cbafd 100644 --- a/crates/sylpheed-formats/examples/gp_title_pair_check.rs +++ b/crates/sylpheed-formats/examples/gp_title_pair_check.rs @@ -10,12 +10,14 @@ //! (the PRESS Ⓐ plate) is byte-identical in size and is the control. //! //! cargo run -p sylpheed-formats --example gp_title_pair_check -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::collections::BTreeSet; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, ui_layout}; fn sprites(ar: &PakArchive, i: usize) -> BTreeSet { - let Ok(by) = ar.read(&ar.entries()[i]) else { return BTreeSet::new() }; + let Ok(by) = ar.read(&ar.entries()[i]) else { + return BTreeSet::new(); + }; ui_layout::parse_build(&by) .map(|b| b.sprites.keys().cloned().collect()) .unwrap_or_default() @@ -24,19 +26,32 @@ fn sprites(ar: &PakArchive, i: usize) -> BTreeSet { fn main() { let root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC")); let ar = PakArchive::open(root.join("dat/GP_TITLE.pak")).expect("GP_TITLE.pak"); - let pairs = [(0, 1, "loading plain"), (2, 3, "PRESS (A) plate [CONTROL]"), - (4, 7, "title art"), (5, 8, "main menu"), (6, 9, "EXTRAS"), - (10, 13, "publisher splash"), (11, 14, "developer splash"), - (12, 15, "loading dressed")]; + let pairs = [ + (0, 1, "loading plain"), + (2, 3, "PRESS (A) plate [CONTROL]"), + (4, 7, "title art"), + (5, 8, "main menu"), + (6, 9, "EXTRAS"), + (10, 13, "publisher splash"), + (11, 14, "developer splash"), + (12, 15, "loading dressed"), + ]; for (a, b, what) in pairs { let (sa, sb) = (sprites(&ar, a), sprites(&ar, b)); let only_a: Vec<_> = sa.difference(&sb).cloned().collect(); let only_b: Vec<_> = sb.difference(&sa).cloned().collect(); let shared = sa.intersection(&sb).count(); - let verdict = if only_a.is_empty() && only_b.is_empty() { "IDENTICAL SET" } - else { "DIFFERS" }; + let verdict = if only_a.is_empty() && only_b.is_empty() { + "IDENTICAL SET" + } else { + "DIFFERS" + }; println!("\n{a:2}/{b:<2} {what:26} {shared:3} shared {verdict}"); - if !only_a.is_empty() { println!(" only in {a}: {only_a:?}"); } - if !only_b.is_empty() { println!(" only in {b}: {only_b:?}"); } + if !only_a.is_empty() { + println!(" only in {a}: {only_a:?}"); + } + if !only_b.is_empty() { + println!(" only in {b}: {only_b:?}"); + } } } diff --git a/crates/sylpheed-formats/examples/inrange_fallback_count.rs b/crates/sylpheed-formats/examples/inrange_fallback_count.rs index 73e902af..4a3c1a4a 100644 --- a/crates/sylpheed-formats/examples/inrange_fallback_count.rs +++ b/crates/sylpheed-formats/examples/inrange_fallback_count.rs @@ -15,45 +15,62 @@ //! later by `if tmax == 0 { return false; }`, so 0 is handled, not assumed.) //! //! cargo run -p sylpheed-formats --example inrange_fallback_count -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::io::Write; 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 mut paks: Vec = std::fs::read_dir(root.join("dat")).expect("dat/") + 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().is_some_and(|x| x == "pak")).collect(); + .filter(|p| p.extension().is_some_and(|x| x == "pak")) + .collect(); paks.sort(); let (mut kf, mut untimed, mut builds) = (0u64, 0u64, 0u64); let (mut queries, mut none_at) = (0u64, 0u64); for pak in &paks { - let Ok(ar) = PakArchive::open(pak) else { continue }; + let Ok(ar) = PakArchive::open(pak) else { + continue; + }; for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; - if !ui_layout::is_build(&by) { continue } - let Some(b) = ui_layout::parse_build(&by) else { continue }; + if !ui_layout::is_build(&by) { + continue; + } + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; builds += 1; // (a) :1681 -- how many poses carry no time? for el in &b.elements { for k in &el.keyframes { kf += 1; - if k.time.is_none() { untimed += 1 } + if k.time.is_none() { + untimed += 1 + } } } // (b) :1010 -- ask every element for a pose at every time that any // element declares, which is the set the occlusion test draws from. - let mut times: Vec = b.elements.iter() - .flat_map(|e| e.keyframes.iter().filter_map(|k| k.time)).collect(); - times.sort_unstable(); times.dedup(); + let mut times: Vec = b + .elements + .iter() + .flat_map(|e| e.keyframes.iter().filter_map(|k| k.time)) + .collect(); + times.sort_unstable(); + times.dedup(); for el in &b.elements { for &t in × { queries += 1; - if el.pose_at(t).is_none() { none_at += 1 } + if el.pose_at(t).is_none() { + none_at += 1 + } } } } - print!("."); std::io::stdout().flush().ok(); + print!("."); + std::io::stdout().flush().ok(); } println!(); println!("{builds} builds, {kf} keyframes"); @@ -67,15 +84,23 @@ fn main() { let mut out_queries = 0u64; let mut none_out = 0u64; for pak in &paks { - let Ok(ar) = PakArchive::open(pak) else { continue }; + let Ok(ar) = PakArchive::open(pak) else { + continue; + }; for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; - if !ui_layout::is_build(&by) { continue } - let Some(b) = ui_layout::parse_build(&by) else { continue }; + if !ui_layout::is_build(&by) { + continue; + } + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; for el in &b.elements { for &t in &[u32::MAX, 1_000_000u32] { out_queries += 1; - if el.pose_at(t).is_none() { none_out += 1 } + if el.pose_at(t).is_none() { + none_out += 1 + } } } } diff --git a/crates/sylpheed-formats/examples/kf_flip_test.rs b/crates/sylpheed-formats/examples/kf_flip_test.rs index 02f07d85..d9cbef9d 100644 --- a/crates/sylpheed-formats/examples/kf_flip_test.rs +++ b/crates/sylpheed-formats/examples/kf_flip_test.rs @@ -10,14 +10,16 @@ //! //! CONTROL: the same search run on `+12`, which is decoded as a real rotation //! and should NOT show a 0/180 pairing pattern of the same strength. -use sylpheed_formats::{pak, ui_layout}; use std::collections::{BTreeMap, BTreeSet}; +use sylpheed_formats::{pak, ui_layout}; fn main() { let dir = std::env::args().nth(1).expect("/dat"); - let mut paks: Vec<_> = std::fs::read_dir(&dir).expect("dir") + 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(); + .filter(|p| p.extension().map(|x| x == "pak").unwrap_or(false)) + .collect(); paks.sort(); // field -> (pak, entry, sprite) -> set of values seen @@ -25,13 +27,20 @@ fn main() { [BTreeMap::new(), BTreeMap::new(), BTreeMap::new()]; for p in &paks { - let Ok(ar) = pak::PakArchive::open(p) else { continue }; + let Ok(ar) = pak::PakArchive::open(p) else { + continue; + }; let pn = 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 }; - let mut groups: Vec<(String, Vec)> = - b.elements.iter().map(|el| (el.name.clone(), el.keyframes.clone())).collect(); + let Some(b) = ui_layout::parse_build(&bytes) else { + continue; + }; + 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]) { @@ -54,15 +63,20 @@ fn main() { for (idx, label) in [(0, "+4"), (1, "+8"), (2, "+12 (rotation, CONTROL)")] { let m = &seen[idx]; - let mut pair_0_180 = 0usize; // a sprite seen at BOTH 0 and 180 in one build + let mut pair_0_180 = 0usize; // a sprite seen at BOTH 0 and 180 in one build let mut only_180 = 0usize; - let mut multi = 0usize; // more than two distinct values + let mut multi = 0usize; // more than two distinct values for v in m.values() { - if v.len() > 2 { multi += 1; } + if v.len() > 2 { + multi += 1; + } let has0 = v.contains(&0); let has180 = v.contains(&180) || v.contains(&-180); - if has0 && has180 { pair_0_180 += 1; } - else if has180 && !has0 { only_180 += 1; } + if has0 && has180 { + pair_0_180 += 1; + } else if has180 && !has0 { + only_180 += 1; + } } println!("{label}: {} sprite-instances", m.len()); println!(" both 0 and ±180 in one build : {pair_0_180}"); diff --git a/crates/sylpheed-formats/examples/kf_timeline.rs b/crates/sylpheed-formats/examples/kf_timeline.rs index 51f073c2..fbaa4be9 100644 --- a/crates/sylpheed-formats/examples/kf_timeline.rs +++ b/crates/sylpheed-formats/examples/kf_timeline.rs @@ -1,19 +1,37 @@ use sylpheed_formats::{pak, ui_layout}; -fn main(){ - let mut a=std::env::args().skip(1); - let pk=a.next().unwrap(); let bi:usize=a.next().unwrap().parse().unwrap(); - let ar=pak::PakArchive::open(pk).unwrap(); - let by=ar.read(&ar.entries()[bi]).unwrap(); - let b=ui_layout::parse_build(&by).unwrap(); +fn main() { + let mut a = std::env::args().skip(1); + let pk = a.next().unwrap(); + let bi: usize = a.next().unwrap().parse().unwrap(); + let ar = pak::PakArchive::open(pk).unwrap(); + let by = ar.read(&ar.entries()[bi]).unwrap(); + let b = ui_layout::parse_build(&by).unwrap(); for t in a { - let i:usize=t.parse().unwrap(); - let e=&b.elements[i]; - println!("[{i}] {} kind=0x{:x} parent={:?} pivot=({},{}) kfs={}", - e.name, e.kind, e.parent, e.pivot_x, e.pivot_y, e.keyframes.len()); + let i: usize = t.parse().unwrap(); + let e = &b.elements[i]; + println!( + "[{i}] {} kind=0x{:x} parent={:?} pivot=({},{}) kfs={}", + e.name, + e.kind, + e.parent, + e.pivot_x, + e.pivot_y, + e.keyframes.len() + ); for k in &e.keyframes { - println!(" t={:<5} a={:<4} xy=({},{}) s={}/{} rot={} u4={} u8={} tint={:08x}", - k.time.map(|v|v as i64).unwrap_or(-1), k.fade>>24, k.x,k.y, - k.scale_x,k.scale_y,k.rotation_deg,k.unknown_4,k.unknown_8,k.tint); + println!( + " t={:<5} a={:<4} xy=({},{}) s={}/{} rot={} u4={} u8={} tint={:08x}", + k.time.map(|v| v as i64).unwrap_or(-1), + k.fade >> 24, + k.x, + k.y, + k.scale_x, + k.scale_y, + k.rotation_deg, + k.unknown_4, + k.unknown_8, + k.tint + ); } } } diff --git a/crates/sylpheed-formats/examples/kf_unknown_census.rs b/crates/sylpheed-formats/examples/kf_unknown_census.rs index 0a79f6dc..c346672d 100644 --- a/crates/sylpheed-formats/examples/kf_unknown_census.rs +++ b/crates/sylpheed-formats/examples/kf_unknown_census.rs @@ -5,8 +5,8 @@ //! 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 sylpheed_formats::{pak, ui_layout}; use std::collections::BTreeMap; +use sylpheed_formats::{pak, ui_layout}; fn main() { let dir = std::env::args().nth(1).expect("usage: /dat"); @@ -24,20 +24,28 @@ fn main() { let (mut kfs, mut builds) = (0usize, 0usize); for p in &paks { - let Ok(ar) = pak::PakArchive::open(p) else { continue }; + 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 }; + 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(); + .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())); + groups + .push((format!("{}->{}", el.name, le.name), le.keyframes.clone())); } } } @@ -49,8 +57,10 @@ fn main() { *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)); + both_nz.push(format!( + "{name} e{i} {nm} +4={} +8={} +12={}", + k.unknown_4, k.unknown_8, k.rotation_deg + )); } } } @@ -59,11 +69,17 @@ fn main() { 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); + 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}"); } + 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 @@ -74,10 +90,14 @@ fn main() { *per.entry(pak).or_default() += 1; } println!(" by pak:"); - for (k, v) in &per { println!(" {k:<28} {v}"); } + 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}"); } + for l in both_nz.iter().filter(|l| l.starts_with(&f)) { + println!(" {l}"); + } } } diff --git a/crates/sylpheed-formats/examples/kind3002_names.rs b/crates/sylpheed-formats/examples/kind3002_names.rs index 1278c133..a7e091de 100644 --- a/crates/sylpheed-formats/examples/kind3002_names.rs +++ b/crates/sylpheed-formats/examples/kind3002_names.rs @@ -1,38 +1,61 @@ //! What do 0x3002/0x3003 elements actually look like disc-wide? //! The port's menu-item rule keys on this class; this asks whether the class is //! uniformly menu-row-shaped or whether it contains other things too. -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::collections::BTreeMap; 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 mut names: BTreeMap = BTreeMap::new(); - 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(); + 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 }; + 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 Ok(by) = ar.read(ent) else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; for el in &b.elements { - if el.kind==0x3002 || el.kind==0x3003 { + if el.kind == 0x3002 || el.kind == 0x3003 { let stem = el.name.split('.').next().unwrap_or(""); - let cls: String = stem.trim_end_matches(|c:char| c.is_ascii_digit()).to_string(); - *names.entry(cls).or_default()+=1; + let cls: String = stem + .trim_end_matches(|c: char| c.is_ascii_digit()) + .to_string(); + *names.entry(cls).or_default() += 1; } } } } let total: usize = names.values().sum(); - println!("0x3002/0x3003 elements disc-wide: {total}, {} name-stems", names.len()); + println!( + "0x3002/0x3003 elements disc-wide: {total}, {} name-stems", + names.len() + ); let mut v: Vec<_> = names.into_iter().collect(); - v.sort_by_key(|(_,n)| std::cmp::Reverse(*n)); - let btn: usize = v.iter().filter(|(k,_)| k.contains("btn")).map(|(_,n)| n).sum(); - println!("stems containing \"btn\": {btn} of {total} ({:.1}%)", 100.0*btn as f64/total as f64); + v.sort_by_key(|(_, n)| std::cmp::Reverse(*n)); + let btn: usize = v + .iter() + .filter(|(k, _)| k.contains("btn")) + .map(|(_, n)| n) + .sum(); + println!( + "stems containing \"btn\": {btn} of {total} ({:.1}%)", + 100.0 * btn as f64 / total as f64 + ); println!("\ntop stems:"); - for (k,n) in v.iter().take(14) { println!(" {n:5} {k}"); } + for (k, n) in v.iter().take(14) { + println!(" {n:5} {k}"); + } println!("\nNON-btn stems (the ones a menu-item rule would also claim):"); - for (k,n) in v.iter().filter(|(k,_)| !k.contains("btn")).take(12) { println!(" {n:5} {k}"); } + for (k, n) in v.iter().filter(|(k, _)| !k.contains("btn")).take(12) { + println!(" {n:5} {k}"); + } } diff --git a/crates/sylpheed-formats/examples/kind_bit0_census.rs b/crates/sylpheed-formats/examples/kind_bit0_census.rs index 89cb3c36..1b6134d9 100644 --- a/crates/sylpheed-formats/examples/kind_bit0_census.rs +++ b/crates/sylpheed-formats/examples/kind_bit0_census.rs @@ -6,9 +6,9 @@ //! disc, so it is tested here rather than argued from two screens. //! //! cargo run -p sylpheed-formats --example kind_bit0_census -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::collections::BTreeMap; 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")); @@ -18,35 +18,54 @@ fn main() { let mut kind_parent: BTreeMap<(u32, bool), usize> = BTreeMap::new(); let mut examples: Vec = Vec::new(); - 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(); + 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 }; + let Ok(ar) = PakArchive::open(p) else { + continue; + }; 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 }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; for el in &b.elements { let has_parent = el.parent.is_some(); let bit0 = el.kind & 1 == 1; *kinds.entry(el.kind).or_default() += 1; *kind_parent.entry((el.kind, has_parent)).or_default() += 1; - if bit0 == has_parent { agree += 1 } else { + if bit0 == has_parent { + agree += 1 + } else { disagree += 1; if examples.len() < 10 { - examples.push(format!("{} entry {i} {} kind={:#x} parent={:?}", - p.file_name().unwrap().to_string_lossy(), el.name, el.kind, el.parent)); + examples.push(format!( + "{} entry {i} {} kind={:#x} parent={:?}", + p.file_name().unwrap().to_string_lossy(), + el.name, + el.kind, + el.parent + )); } } } } } println!("kind&1 == has_parent : agree {agree} DISAGREE {disagree}"); - for e in &examples { println!(" counterexample: {e}"); } + for e in &examples { + println!(" counterexample: {e}"); + } println!("\nkind histogram (count, and how many of each have a parent):"); for (k, n) in &kinds { let wp = kind_parent.get(&(*k, true)).copied().unwrap_or(0); - println!(" {k:#06x} n={n:6} with parent {wp:6} without {:6}", n - wp); + println!( + " {k:#06x} n={n:6} with parent {wp:6} without {:6}", + n - wp + ); } } diff --git a/crates/sylpheed-formats/examples/kind_census_five_screens.rs b/crates/sylpheed-formats/examples/kind_census_five_screens.rs index 271bc0a2..df2e70c3 100644 --- a/crates/sylpheed-formats/examples/kind_census_five_screens.rs +++ b/crates/sylpheed-formats/examples/kind_census_five_screens.rs @@ -18,12 +18,15 @@ //! `GP_TITLE`, so the claim is disc-wide. //! //! cargo run -p sylpheed-formats --example kind_census_five_screens -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::collections::BTreeMap; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, ui_layout}; fn suffix(n: &str) -> &str { - match n.rfind('.') { Some(i) => &n[i..], None => "(none)" } + match n.rfind('.') { + Some(i) => &n[i..], + None => "(none)", + } } fn main() { @@ -36,11 +39,21 @@ fn main() { let mut btn_nonstd: Vec<(usize, String, u32)> = Vec::new(); let mut builds = 0usize; for e in 0..n { - let by = match ar.read(&ar.entries()[e]) { Ok(b) => b, Err(_) => continue }; - let b = match ui_layout::parse_build(&by) { Some(b) => b, None => continue }; + let by = match ar.read(&ar.entries()[e]) { + Ok(b) => b, + Err(_) => continue, + }; + let b = match ui_layout::parse_build(&by) { + Some(b) => b, + None => continue, + }; builds += 1; for el in &b.elements { - *table.entry(el.kind).or_default().entry(suffix(&el.name).to_string()).or_default() += 1; + *table + .entry(el.kind) + .or_default() + .entry(suffix(&el.name).to_string()) + .or_default() += 1; if el.name.ends_with(".t32") && !el.name.contains("btn") && el.kind != 0 { t32_nonzero.push((e, el.name.clone(), el.kind)); } @@ -55,10 +68,17 @@ fn main() { let s: Vec = m.iter().map(|(sfx, c)| format!("{sfx}x{c}")).collect(); println!("0x{k:<8X} {}", s.join(" ")); } - println!("\nNON-BUTTON .t32 elements with kind != 0: {}", t32_nonzero.len()); - for (e, nm, k) in t32_nonzero.iter().take(30) { println!(" entry {e:>2} {nm:<24} kind 0x{k:X}"); } + println!( + "\nNON-BUTTON .t32 elements with kind != 0: {}", + t32_nonzero.len() + ); + for (e, nm, k) in t32_nonzero.iter().take(30) { + println!(" entry {e:>2} {nm:<24} kind 0x{k:X}"); + } println!("\n*btn* elements with kind != 0x3002: {}", btn_nonstd.len()); - for (e, nm, k) in btn_nonstd.iter().take(30) { println!(" entry {e:>2} {nm:<24} kind 0x{k:X}"); } + for (e, nm, k) in btn_nonstd.iter().take(30) { + println!(" entry {e:>2} {nm:<24} kind 0x{k:X}"); + } // Is `kind` a bitfield, and does bit 0x2 mean "focusable"? The focus/nav // index at +0x2C is -1 on everything that cannot take the cursor, so the two @@ -71,65 +91,115 @@ fn main() { const STRIDE: usize = 60; let mut cross: BTreeMap<(u32, i32), usize> = BTreeMap::new(); for e in 0..n { - let by = match ar.read(&ar.entries()[e]) { Ok(b) => b, Err(_) => continue }; - if ui_layout::parse_build(&by).is_none() { continue } - if by.len() < 0x18 { continue } + let by = match ar.read(&ar.entries()[e]) { + Ok(b) => b, + Err(_) => continue, + }; + if ui_layout::parse_build(&by).is_none() { + continue; + } + if by.len() < 0x18 { + continue; + } let count = u32::from_be_bytes([by[0x14], by[0x15], by[0x16], by[0x17]]) as usize; for i in 0..count { let at = AT + i * STRIDE; - if at + STRIDE > by.len() { break } - let kind = u32::from_be_bytes([by[at+0x28], by[at+0x29], by[at+0x2A], by[at+0x2B]]); - let foc = i32::from_be_bytes([by[at+0x2C], by[at+0x2D], by[at+0x2E], by[at+0x2F]]); - *cross.entry((kind, if foc < 0 { -1 } else { 1 })).or_default() += 1; + if at + STRIDE > by.len() { + break; + } + let kind = + u32::from_be_bytes([by[at + 0x28], by[at + 0x29], by[at + 0x2A], by[at + 0x2B]]); + let foc = + i32::from_be_bytes([by[at + 0x2C], by[at + 0x2D], by[at + 0x2E], by[at + 0x2F]]); + *cross + .entry((kind, if foc < 0 { -1 } else { 1 })) + .or_default() += 1; } } for ((k, f), c) in &cross { - println!(" kind 0x{k:<6X} focus {:<10} {c:>4} elements", - if *f < 0 { "= -1" } else { ">= 0" }); + println!( + " kind 0x{k:<6X} focus {:<10} {c:>4} elements", + if *f < 0 { "= -1" } else { ">= 0" } + ); } // ── the same test, every UI pak on the disc ────────────────────────────── let mut all: BTreeMap<(u32, i32), usize> = BTreeMap::new(); let mut paks = 0usize; let mut violations: Vec = Vec::new(); - let mut dir: Vec<_> = std::fs::read_dir(root.join("dat")).expect("dat") - .filter_map(|d| d.ok()).map(|d| d.path()).collect(); + let mut dir: Vec<_> = std::fs::read_dir(root.join("dat")) + .expect("dat") + .filter_map(|d| d.ok()) + .map(|d| d.path()) + .collect(); dir.sort(); for path in dir { let name = path.file_name().unwrap().to_string_lossy().to_string(); - if !name.ends_with(".pak") { continue } - let ar = match PakArchive::open(&path) { Ok(a) => a, Err(_) => continue }; + if !name.ends_with(".pak") { + continue; + } + let ar = match PakArchive::open(&path) { + Ok(a) => a, + Err(_) => continue, + }; let mut used = false; for i in 0..ar.entries().len() { - let by = match ar.read(&ar.entries()[i]) { Ok(b) => b, Err(_) => continue }; - if ui_layout::parse_build(&by).is_none() { continue } - if by.len() < 0x18 { continue } + let by = match ar.read(&ar.entries()[i]) { + Ok(b) => b, + Err(_) => continue, + }; + if ui_layout::parse_build(&by).is_none() { + continue; + } + if by.len() < 0x18 { + continue; + } used = true; let count = u32::from_be_bytes([by[0x14], by[0x15], by[0x16], by[0x17]]) as usize; for e in 0..count { let at = AT + e * STRIDE; - if at + STRIDE > by.len() { break } - let kind = u32::from_be_bytes([by[at+0x28], by[at+0x29], by[at+0x2A], by[at+0x2B]]); - let foc = i32::from_be_bytes([by[at+0x2C], by[at+0x2D], by[at+0x2E], by[at+0x2F]]); + if at + STRIDE > by.len() { + break; + } + let kind = u32::from_be_bytes([ + by[at + 0x28], + by[at + 0x29], + by[at + 0x2A], + by[at + 0x2B], + ]); + let foc = i32::from_be_bytes([ + by[at + 0x2C], + by[at + 0x2D], + by[at + 0x2E], + by[at + 0x2F], + ]); let f = if foc < 0 { -1 } else { 1 }; *all.entry((kind, f)).or_default() += 1; if ((kind & 0x2) != 0) != (f > 0) { - violations.push(format!("{name} entry {i} elem {e}: kind 0x{kind:X} focus {foc}")); + violations.push(format!( + "{name} entry {i} elem {e}: kind 0x{kind:X} focus {foc}" + )); } } } - if used { paks += 1 } + if used { + paks += 1 + } } let total: usize = all.values().sum(); println!("\nDISC-WIDE — {paks} UI paks, {total} declaration entries"); println!("{:<12} {:>12} {:>12}", "kind", "focus = -1", "focus >= 0"); let kinds: std::collections::BTreeSet = all.keys().map(|(k, _)| *k).collect(); for k in kinds { - println!("0x{k:<10X} {:>12} {:>12}", - all.get(&(k, -1)).copied().unwrap_or(0), - all.get(&(k, 1)).copied().unwrap_or(0)); + println!( + "0x{k:<10X} {:>12} {:>12}", + all.get(&(k, -1)).copied().unwrap_or(0), + all.get(&(k, 1)).copied().unwrap_or(0) + ); } println!("\nHYPOTHESIS: bit 0x2 of kind == (focus index >= 0)"); println!("violations: {} of {total}", violations.len()); - for v in violations.iter().take(20) { println!(" {v}"); } + for v in violations.iter().take(20) { + println!(" {v}"); + } } diff --git a/crates/sylpheed-formats/examples/leaf_alpha_compose.rs b/crates/sylpheed-formats/examples/leaf_alpha_compose.rs index e7e1a830..b16030d1 100644 --- a/crates/sylpheed-formats/examples/leaf_alpha_compose.rs +++ b/crates/sylpheed-formats/examples/leaf_alpha_compose.rs @@ -28,8 +28,16 @@ fn main() { if let Some(el) = build.elements.iter().find(|x| x.name == name) { println!(" PARENT keyframes (t, alpha, scale, rot, x,y):"); for k in &el.keyframes { - println!(" t={:<5} a={:<4} scale=({},{}) rot={:<5} ({},{})", - k.time.map(|t| t as i64).unwrap_or(-1), alpha_of(k), k.scale_x, k.scale_y, k.rotation_deg, k.x, k.y); + println!( + " t={:<5} a={:<4} scale=({},{}) rot={:<5} ({},{})", + k.time.map(|t| t as i64).unwrap_or(-1), + alpha_of(k), + k.scale_x, + k.scale_y, + k.rotation_deg, + k.x, + k.y + ); } } match build.records.get(name) { @@ -40,9 +48,16 @@ fn main() { for le in &lb.elements { println!(" LEAF element {:?}:", le.name); for k in &le.keyframes { - println!(" t={:<5} a={:<4} scale=({},{}) rot={:<5} ({},{})", - k.time.map(|t| t as i64).unwrap_or(-1), alpha_of(k), k.scale_x, k.scale_y, - k.rotation_deg, k.x, k.y); + println!( + " t={:<5} a={:<4} scale=({},{}) rot={:<5} ({},{})", + k.time.map(|t| t as i64).unwrap_or(-1), + alpha_of(k), + k.scale_x, + k.scale_y, + k.rotation_deg, + k.x, + k.y + ); } } } diff --git a/crates/sylpheed-formats/examples/leaf_dump.rs b/crates/sylpheed-formats/examples/leaf_dump.rs index 40dff320..26ac782d 100644 --- a/crates/sylpheed-formats/examples/leaf_dump.rs +++ b/crates/sylpheed-formats/examples/leaf_dump.rs @@ -3,15 +3,31 @@ //! cargo run -p sylpheed-formats --example leaf_dump -- use sylpheed_formats::{pak, ui_layout}; -fn a(k: &ui_layout::Keyframe) -> u32 { k.fade >> 24 } -fn t(k: &ui_layout::Keyframe) -> i64 { k.time.map(|v| v as i64).unwrap_or(-1) } +fn a(k: &ui_layout::Keyframe) -> u32 { + k.fade >> 24 +} +fn t(k: &ui_layout::Keyframe) -> i64 { + k.time.map(|v| v as i64).unwrap_or(-1) +} fn show(tag: &str, els: &[ui_layout::Element]) { for e in els { - println!(" {tag} {:?} pivot=({},{}) kind={:#x}", e.name, e.pivot_x, e.pivot_y, e.kind); + println!( + " {tag} {:?} pivot=({},{}) kind={:#x}", + e.name, e.pivot_x, e.pivot_y, e.kind + ); for k in &e.keyframes { - println!(" t={:<5} a={:<4} scale=({},{}) rot={:<5} pos=({},{}) tint={:#010x}", - t(k), a(k), k.scale_x, k.scale_y, k.rotation_deg, k.x, k.y, k.tint); + println!( + " t={:<5} a={:<4} scale=({},{}) rot={:<5} pos=({},{}) tint={:#010x}", + t(k), + a(k), + k.scale_x, + k.scale_y, + k.rotation_deg, + k.x, + k.y, + k.tint + ); } } } @@ -24,8 +40,18 @@ fn main() { let ar = pak::PakArchive::open(&path).expect("open"); let bytes = ar.read(&ar.entries()[entry]).expect("read"); let b = ui_layout::parse_build(&bytes).expect("build"); - println!("entry {entry}: {} elements, design {}x{}", b.elements.len(), b.design_w, b.design_h); - let els: Vec<_> = b.elements.iter().filter(|e| e.name.contains(&want)).cloned().collect(); + println!( + "entry {entry}: {} elements, design {}x{}", + b.elements.len(), + b.design_w, + b.design_h + ); + let els: Vec<_> = b + .elements + .iter() + .filter(|e| e.name.contains(&want)) + .cloned() + .collect(); show("PARENT", &els); for e in &els { match b.records.get(&e.name) { diff --git a/crates/sylpheed-formats/examples/leaf_keyframes.rs b/crates/sylpheed-formats/examples/leaf_keyframes.rs index 99bce905..98403412 100644 --- a/crates/sylpheed-formats/examples/leaf_keyframes.rs +++ b/crates/sylpheed-formats/examples/leaf_keyframes.rs @@ -1,8 +1,8 @@ //! Dump the keyframes of any nested `.rat` leaf by name. //! //! cargo run -p sylpheed-formats --example leaf_keyframes -- GP_TITLE ptbtn00.rat 2 3 4 -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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")); @@ -12,19 +12,32 @@ fn main() { let builds: Vec = argv[2..].iter().filter_map(|a| a.parse().ok()).collect(); let ar = PakArchive::open(root.join(format!("dat/{pak}.pak"))).expect("pak"); for e in builds { - let Ok(by) = ar.read(&ar.entries()[e]) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; - let Some(&(lo, ls)) = b.records.get(rec.as_str()) else { continue }; + let Ok(by) = ar.read(&ar.entries()[e]) else { + continue; + }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; + let Some(&(lo, ls)) = b.records.get(rec.as_str()) else { + continue; + }; let leaf = &by[lo..(lo + ls).min(by.len())]; println!("=== {pak} entry {e} — {rec} (leaf {ls} bytes) ==="); - if let Some(u) = ui_layout::loop_length_units(leaf) { println!(" declared loop: {u} units"); } + if let Some(u) = ui_layout::loop_length_units(leaf) { + println!(" declared loop: {u} units"); + } if let Some(lb) = ui_layout::parse_build(leaf) { for el in &lb.elements { println!(" {} — {} keyframes", el.name, el.keyframes.len()); for (i, k) in el.keyframes.iter().enumerate() { - println!(" kf{i:<2} t={:<5} x={:<6} y={:<6} fade={:08X} (alpha {:3})", + println!( + " kf{i:<2} t={:<5} x={:<6} y={:<6} fade={:08X} (alpha {:3})", k.time.map(|t| t.to_string()).unwrap_or_else(|| "-".into()), - k.x, k.y, k.fade, k.fade >> 24); + k.x, + k.y, + k.fade, + k.fade >> 24 + ); } } } diff --git a/crates/sylpheed-formats/examples/leafdbg.rs b/crates/sylpheed-formats/examples/leafdbg.rs index 97f9efbf..d7faaac8 100644 --- a/crates/sylpheed-formats/examples/leafdbg.rs +++ b/crates/sylpheed-formats/examples/leafdbg.rs @@ -1,19 +1,32 @@ use sylpheed_formats::{pak, ui_layout}; -fn main(){ - let ar=pak::PakArchive::open(std::env::args().nth(1).unwrap()).unwrap(); - let by=ar.read(&ar.entries()[4]).unwrap(); - let b=ui_layout::parse_build(&by).unwrap(); - for n in ["ptloop01.rat","ptloop02.rat"] { - let el=b.elements.iter().find(|e| e.name==n).unwrap(); - println!("{n}: kind={:#x} animated={} rec={:?}", el.kind, el.animated, - b.records.get(n).map(|&(o,s)|(o,s))); - if let Some(&(o,s))=b.records.get(n) { - match ui_layout::parse_build(&by[o..o+s]) { - Some(lb)=>for le in &lb.elements { - println!(" leaf {:?} sprite={:?} in_sprites={}", le.name, le.sprite, - le.sprite.as_ref().map(|x| b.sprites.contains_key(x)).unwrap_or(false)); - }, - None=>println!(" leaf parse FAILED"), +fn main() { + let ar = pak::PakArchive::open(std::env::args().nth(1).unwrap()).unwrap(); + let by = ar.read(&ar.entries()[4]).unwrap(); + let b = ui_layout::parse_build(&by).unwrap(); + for n in ["ptloop01.rat", "ptloop02.rat"] { + let el = b.elements.iter().find(|e| e.name == n).unwrap(); + println!( + "{n}: kind={:#x} animated={} rec={:?}", + el.kind, + el.animated, + b.records.get(n).map(|&(o, s)| (o, s)) + ); + if let Some(&(o, s)) = b.records.get(n) { + match ui_layout::parse_build(&by[o..o + s]) { + Some(lb) => { + for le in &lb.elements { + println!( + " leaf {:?} sprite={:?} in_sprites={}", + le.name, + le.sprite, + le.sprite + .as_ref() + .map(|x| b.sprites.contains_key(x)) + .unwrap_or(false) + ); + } + } + None => println!(" leaf parse FAILED"), } } } diff --git a/crates/sylpheed-formats/examples/loo_band.rs b/crates/sylpheed-formats/examples/loo_band.rs index 4bdc4476..b1dbc24b 100644 --- a/crates/sylpheed-formats/examples/loo_band.rs +++ b/crates/sylpheed-formats/examples/loo_band.rs @@ -1,26 +1,36 @@ // Leave-one-out over the elements that touch a band, dumping raw RGBA each time. use sylpheed_formats::{pak, ui_layout}; -fn main(){ - let mut a=std::env::args().skip(1); - let pk=a.next().unwrap(); - let bi:usize=a.next().unwrap().parse().unwrap(); - let dir=a.next().unwrap(); - let ar=pak::PakArchive::open(pk).unwrap(); - let by=ar.read(&ar.entries()[bi]).unwrap(); - let b=ui_layout::parse_build(&by).unwrap(); - let o=ui_layout::ComposeOptions{ backdrop:[0,0,0,255], ..Default::default() }; - let n=b.elements.len(); - let base=ui_layout::compose(&b,&by,o.clone(),None); - std::fs::write(format!("{dir}/base.raw"),&base.rgba).unwrap(); +fn main() { + let mut a = std::env::args().skip(1); + let pk = a.next().unwrap(); + let bi: usize = a.next().unwrap().parse().unwrap(); + let dir = a.next().unwrap(); + let ar = pak::PakArchive::open(pk).unwrap(); + let by = ar.read(&ar.entries()[bi]).unwrap(); + let b = ui_layout::parse_build(&by).unwrap(); + let o = ui_layout::ComposeOptions { + backdrop: [0, 0, 0, 255], + ..Default::default() + }; + let n = b.elements.len(); + let base = ui_layout::compose(&b, &by, o.clone(), None); + std::fs::write(format!("{dir}/base.raw"), &base.rgba).unwrap(); println!("canvas {}x{} elements {n}", base.width, base.height); for i in 0..n { - let e=&b.elements[i]; - let Some(kf)=e.rest() else { continue }; + let e = &b.elements[i]; + let Some(kf) = e.rest() else { continue }; // only bother with elements whose rest pose can touch the band - let mut v=vec![true;n]; v[i]=false; - let c=ui_layout::compose(&b,&by,o.clone(),Some(&v)); - std::fs::write(format!("{dir}/wo-{i}.raw"),&c.rgba).unwrap(); - println!("{i}\t{}\t{}\t({},{})\ta={}", e.name, - e.sprite.clone().unwrap_or_default(), kf.x, kf.y, kf.fade>>24); + let mut v = vec![true; n]; + v[i] = false; + let c = ui_layout::compose(&b, &by, o.clone(), Some(&v)); + std::fs::write(format!("{dir}/wo-{i}.raw"), &c.rgba).unwrap(); + println!( + "{i}\t{}\t{}\t({},{})\ta={}", + e.name, + e.sprite.clone().unwrap_or_default(), + kf.x, + kf.y, + kf.fade >> 24 + ); } } diff --git a/crates/sylpheed-formats/examples/loop_length_offset_discriminates.rs b/crates/sylpheed-formats/examples/loop_length_offset_discriminates.rs index 4194cb10..eb9d0e1c 100644 --- a/crates/sylpheed-formats/examples/loop_length_offset_discriminates.rs +++ b/crates/sylpheed-formats/examples/loop_length_offset_discriminates.rs @@ -9,8 +9,8 @@ //! This checks that from my own reader before I correct the page. //! //! cargo run -p sylpheed-formats --example loop_length_offset_discriminates -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, ui_layout}; fn be32(b: &[u8], o: usize) -> Option { (b.len() >= o + 4).then(|| u32::from_be_bytes([b[o], b[o + 1], b[o + 2], b[o + 3]])) @@ -29,16 +29,22 @@ fn main() { let mut stat = [(0usize, 0usize, 0usize); 3]; let offsets = [0x04usize, 0x08, 0x0c]; for p in &paks { - let Ok(ar) = PakArchive::open(p) else { continue }; + let Ok(ar) = PakArchive::open(p) else { + continue; + }; for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; for (_, (off, size)) in &b.records { let rec = &by[*off..(*off + *size).min(by.len())]; if rec.len() < 0x10 || &rec[0..4] != b"RATC" { continue; } - let Some(leaf) = ui_layout::parse_build(rec) else { continue }; + let Some(leaf) = ui_layout::parse_build(rec) else { + continue; + }; let max_t = leaf .elements .iter() @@ -48,21 +54,33 @@ fn main() { // sylpheed-port's reconciliation: max_t == 0 makes "does the word // equal the largest keyframe time?" vacuous, and those records were // silently in my denominator. Filter them and the counts must meet. - if std::env::var("MEANINGFUL_ONLY").is_ok() && max_t == 0 { continue } + if std::env::var("MEANINGFUL_ONLY").is_ok() && max_t == 0 { + continue; + } for (i, o) in offsets.iter().enumerate() { if let Some(w) = be32(rec, *o) { stat[i].0 += 1; - if w < max_t { stat[i].1 += 1 } - if w == max_t { stat[i].2 += 1 } + if w < max_t { + stat[i].1 += 1 + } + if w == max_t { + stat[i].2 += 1 + } } } } } } - println!("{:>8} {:>9} {:>12} {:>14}", "offset", "records", "violations", "exact == max_t"); + println!( + "{:>8} {:>9} {:>12} {:>14}", + "offset", "records", "violations", "exact == max_t" + ); for (i, o) in offsets.iter().enumerate() { let (n, v, x) = stat[i]; - println!(" +0x{o:02X} {n:>9} {v:>12} ({:>5.1}%) {x:>8} ({:>5.1}%)", - 100.0 * v as f64 / n.max(1) as f64, 100.0 * x as f64 / n.max(1) as f64); + println!( + " +0x{o:02X} {n:>9} {v:>12} ({:>5.1}%) {x:>8} ({:>5.1}%)", + 100.0 * v as f64 / n.max(1) as f64, + 100.0 * x as f64 / n.max(1) as f64 + ); } } diff --git a/crates/sylpheed-formats/examples/main_menu_element_extents.rs b/crates/sylpheed-formats/examples/main_menu_element_extents.rs index c0521681..8b03aefd 100644 --- a/crates/sylpheed-formats/examples/main_menu_element_extents.rs +++ b/crates/sylpheed-formats/examples/main_menu_element_extents.rs @@ -9,22 +9,29 @@ //! readings are printed so the mapping is not assumed. //! //! cargo run -p sylpheed-formats --example main_menu_element_extents -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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 ar = PakArchive::open(root.join("dat/GP_TITLE.pak")).expect("GP_TITLE"); let by = ar.read(&ar.entries()[5]).expect("entry 5"); let b = ui_layout::parse_build(&by).expect("build"); - println!("{:<26} {:>6} {:>6} {:>7} {:>7} {}", "element", "x", "y", "pivot_x", "pivot_y", "sprite"); + println!( + "{:<26} {:>6} {:>6} {:>7} {:>7} {}", + "element", "x", "y", "pivot_x", "pivot_y", "sprite" + ); let mut rows: Vec<(i32, i32, String, String)> = b .elements .iter() .map(|e| { let k = e.rest(); - (k.map(|k| k.x).unwrap_or(0), k.map(|k| k.y).unwrap_or(0), - e.name.clone(), e.sprite.clone().unwrap_or_default()) + ( + k.map(|k| k.x).unwrap_or(0), + k.map(|k| k.y).unwrap_or(0), + e.name.clone(), + e.sprite.clone().unwrap_or_default(), + ) }) .collect(); rows.sort_by_key(|r| (r.1, r.0)); @@ -39,8 +46,18 @@ fn main() { (false, true) => " <- hot in CAPTURE space", _ => "", }; - println!("{n:<26} {x:>6} {y:>6} {:>7} {:>7} {s}{mark}", - b.elements.iter().find(|e| &e.name == n).map(|e| e.pivot_x).unwrap_or(0), - b.elements.iter().find(|e| &e.name == n).map(|e| e.pivot_y).unwrap_or(0)); + println!( + "{n:<26} {x:>6} {y:>6} {:>7} {:>7} {s}{mark}", + b.elements + .iter() + .find(|e| &e.name == n) + .map(|e| e.pivot_x) + .unwrap_or(0), + b.elements + .iter() + .find(|e| &e.name == n) + .map(|e| e.pivot_y) + .unwrap_or(0) + ); } } diff --git a/crates/sylpheed-formats/examples/name_from_hash.rs b/crates/sylpheed-formats/examples/name_from_hash.rs index 11168461..00937d03 100644 --- a/crates/sylpheed-formats/examples/name_from_hash.rs +++ b/crates/sylpheed-formats/examples/name_from_hash.rs @@ -14,7 +14,9 @@ fn main() { .collect(); assert!(!wanted.is_empty(), "give hashes in hex"); let langs = ["eng", "jpn", ""]; - let dirs = ["", "Movie", "etc", "Voice", "Sound", "BGM", "bgm", "se", "SE"]; + let dirs = [ + "", "Movie", "etc", "Voice", "Sound", "BGM", "bgm", "se", "SE", + ]; let mut tried = 0usize; let mut check = |name: String, tried: &mut usize| { *tried += 1; diff --git a/crates/sylpheed-formats/examples/ordinal_entry_map.rs b/crates/sylpheed-formats/examples/ordinal_entry_map.rs index 0694e496..493f9d40 100644 --- a/crates/sylpheed-formats/examples/ordinal_entry_map.rs +++ b/crates/sylpheed-formats/examples/ordinal_entry_map.rs @@ -11,9 +11,9 @@ //! are not necessarily the same object. //! //! cargo run -p sylpheed-formats --example ordinal_entry_map -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::io::Write; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, ui_layout}; /// One decompression pass per entry, both predicates applied to it: reading the /// archive twice doubled the cost on `GP_READY_ROOM` (902 entries) for nothing. @@ -21,37 +21,66 @@ fn maps(ar: &PakArchive) -> (Vec, Vec) { let (mut d, mut a) = (Vec::new(), Vec::new()); for (i, e) in ar.entries().iter().enumerate() { let Ok(by) = ar.read(e) else { continue }; - if ui_layout::is_build(&by) { d.push(i) } - if ui_layout::is_composable(&by) { a.push(i) } + if ui_layout::is_build(&by) { + d.push(i) + } + if ui_layout::is_composable(&by) { + a.push(i) + } } (d, a) } fn main() { let root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC")); - let mut paks: Vec = std::fs::read_dir(root.join("dat")).expect("dat/") + 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().is_some_and(|x| x == "pak")).collect(); + .filter(|p| p.extension().is_some_and(|x| x == "pak")) + .collect(); paks.sort(); let (mut clean, mut div, mut allshift) = (0usize, 0usize, 0usize); for pak in &paks { - let Ok(ar) = PakArchive::open(pak) else { continue }; + let Ok(ar) = PakArchive::open(pak) else { + continue; + }; let name = pak.file_name().unwrap().to_string_lossy().to_string(); let (d, a) = maps(&ar); - if d.is_empty() && a.is_empty() { continue } + if d.is_empty() && a.is_empty() { + continue; + } let bad = d.iter().enumerate().find(|(o, &e)| *o != e).map(|(o, _)| o); // does `--all` renumber? compare the entry each ordinal resolves to let shift = (0..d.len().min(a.len())).find(|&o| d[o] != a[o]); let tag = match bad { - None => { clean += 1; format!("{:4} builds ordinal == entry throughout", d.len()) } - Some(o) => { div += 1; - let t: Vec = d.iter().enumerate().skip(o).take(5) - .map(|(x, &y)| format!("[{x}]->{y}")).collect(); - format!("{:4} builds 🔴 diverges at ordinal {o}: {}", d.len(), t.join(" ")) } + None => { + clean += 1; + format!("{:4} builds ordinal == entry throughout", d.len()) + } + Some(o) => { + div += 1; + let t: Vec = d + .iter() + .enumerate() + .skip(o) + .take(5) + .map(|(x, &y)| format!("[{x}]->{y}")) + .collect(); + format!( + "{:4} builds 🔴 diverges at ordinal {o}: {}", + d.len(), + t.join(" ") + ) + } }; let s = match shift { - Some(o) => { allshift += 1; - format!(" ⚠️ --all renumbers from [{o}]: entry {} -> {}", d[o], a[o]) } + Some(o) => { + allshift += 1; + format!( + " ⚠️ --all renumbers from [{o}]: entry {} -> {}", + d[o], a[o] + ) + } None if a.len() != d.len() => format!(" (--all appends {} more)", a.len() - d.len()), None => String::new(), }; diff --git a/crates/sylpheed-formats/examples/palogo_eff_check.rs b/crates/sylpheed-formats/examples/palogo_eff_check.rs index 7d4c6c16..a0a74ac8 100644 --- a/crates/sylpheed-formats/examples/palogo_eff_check.rs +++ b/crates/sylpheed-formats/examples/palogo_eff_check.rs @@ -6,35 +6,60 @@ //! element genuinely HOLDS, and `rest_plateau()` returning it is correct. Only the //! fallback is the unsound path. //! cargo run -p sylpheed-formats --example palogo_eff_check -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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 ar = PakArchive::open(root.join("dat/GP_TITLE.pak")).expect("GP_TITLE"); for (i, e) in ar.entries().iter().enumerate() { let Ok(by) = ar.read(e) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; for el in &b.elements { - if !el.name.starts_with("palogo") || !el.name.contains("eff") { continue } + if !el.name.starts_with("palogo") || !el.name.contains("eff") { + continue; + } // A single-keyframe element has no gap to maximise, so neither path // applies and `rest()` trivially returns the only pose. Excluding it // here matches the census, which filters `len < 2`. - if el.keyframes.len() < 2 { continue } + if el.keyframes.len() < 2 { + continue; + } let plateau = el.keyframes.windows(2).position(|w| { - w[0].x == w[1].x && w[0].y == w[1].y && w[0].scale_x == w[1].scale_x - && w[0].scale_y == w[1].scale_y && w[0].fade == w[1].fade + w[0].x == w[1].x + && w[0].y == w[1].y + && w[0].scale_x == w[1].scale_x + && w[0].scale_y == w[1].scale_y + && w[0].fade == w[1].fade }); - let ks: Vec = el.keyframes.iter().map(|k| format!( - "{}:a{} {},{} {}%", - k.time.map(|v| v.to_string()).unwrap_or("-".into()), - (k.fade >> 24) & 0xff, k.x, k.y, k.scale_x)).collect(); + let ks: Vec = el + .keyframes + .iter() + .map(|k| { + format!( + "{}:a{} {},{} {}%", + k.time.map(|v| v.to_string()).unwrap_or("-".into()), + (k.fade >> 24) & 0xff, + k.x, + k.y, + k.scale_x + ) + }) + .collect(); let r = el.rest(); println!("e{i:<3} {:24} kf=[{}]", el.name, ks.join(" ")); - println!(" plateau at pair {:?} -> path: {} rest a={} t={:?}", - plateau, - if plateau.is_some() { "PLATEAU (sound: the pose is held)" } else { "DWELL FALLBACK (unsound)" }, - r.map(|k| (k.fade >> 24) & 0xff).unwrap_or(0), - r.and_then(|k| k.time)); + println!( + " plateau at pair {:?} -> path: {} rest a={} t={:?}", + plateau, + if plateau.is_some() { + "PLATEAU (sound: the pose is held)" + } else { + "DWELL FALLBACK (unsound)" + }, + r.map(|k| (k.fade >> 24) & 0xff).unwrap_or(0), + r.and_then(|k| k.time) + ); } } } diff --git a/crates/sylpheed-formats/examples/plateau_choice.rs b/crates/sylpheed-formats/examples/plateau_choice.rs index 3234fa2e..3dc5d050 100644 --- a/crates/sylpheed-formats/examples/plateau_choice.rs +++ b/crates/sylpheed-formats/examples/plateau_choice.rs @@ -11,80 +11,123 @@ //! explanation and something else is wrong. //! //! cargo run -p sylpheed-formats --example plateau_choice -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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 mut paks: Vec = std::fs::read_dir(root.join("dat")).expect("dat/") + 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().is_some_and(|x| x == "pak")).collect(); + .filter(|p| p.extension().is_some_and(|x| x == "pak")) + .collect(); paks.sort(); - let (mut one_cov, mut one_agree) = (0usize, 0usize); // control + let (mut one_cov, mut one_agree) = (0usize, 0usize); // control let (mut multi_cov, mut multi_agree, mut multi_wrongrun) = (0usize, 0usize, 0usize); for pak in &paks { - let Ok(ar) = PakArchive::open(pak) else { continue }; + let Ok(ar) = PakArchive::open(pak) else { + continue; + }; for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; - let Some((lo, hi)) = b.settle_window() else { continue }; - if hi - lo < 10 { continue } + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; + let Some((lo, hi)) = b.settle_window() else { + continue; + }; + if hi - lo < 10 { + continue; + } let st = lo + (hi - lo) / 2; for el in &b.elements { let k = &el.keyframes; - if k.len() < 2 { continue } + if k.len() < 2 { + continue; + } let same = |a: &ui_layout::Keyframe, c: &ui_layout::Keyframe| { - a.fade == c.fade && a.scale_x == c.scale_x && a.scale_y == c.scale_y - && a.tint == c.tint && a.x == c.x && a.y == c.y + a.fade == c.fade + && a.scale_x == c.scale_x + && a.scale_y == c.scale_y + && a.tint == c.tint + && a.x == c.x + && a.y == c.y }; // enumerate maximal runs of length >= 2, with their time spans let mut runs: Vec<(usize, usize)> = Vec::new(); let mut i = 0usize; while i < k.len() { let mut j = i; - while j + 1 < k.len() && same(&k[j], &k[j + 1]) { j += 1 } - if j - i + 1 >= 2 { runs.push((i, j)) } + while j + 1 < k.len() && same(&k[j], &k[j + 1]) { + j += 1 + } + if j - i + 1 >= 2 { + runs.push((i, j)) + } i = j + 1; } - if runs.is_empty() { continue } + if runs.is_empty() { + continue; + } let covers = |&(a, c): &(usize, usize)| match (k[a].time, k[c].time) { (Some(t0), Some(t1)) => t0 <= st && st <= t1, _ => false, }; let covering: Vec<_> = runs.iter().filter(|r| covers(r)).collect(); - if covering.is_empty() { continue } - let (Some(r), Some(s)) = (el.rest(), el.pose_at(st)) else { continue }; - let agree = r.fade == s.fade && r.x == s.x && r.y == s.y - && r.scale_x == s.scale_x && r.scale_y == s.scale_y; + if covering.is_empty() { + continue; + } + let (Some(r), Some(s)) = (el.rest(), el.pose_at(st)) else { + continue; + }; + let agree = r.fade == s.fade + && r.x == s.x + && r.y == s.y + && r.scale_x == s.scale_x + && r.scale_y == s.scale_y; if runs.len() == 1 { one_cov += 1; - if agree { one_agree += 1 } + if agree { + one_agree += 1 + } } else { multi_cov += 1; - if agree { multi_agree += 1 } - else { + if agree { + multi_agree += 1 + } else { // did rest() land on a run that does NOT cover settle? let on_covering = covering.iter().any(|&&(a, c)| { (a..=c).any(|idx| { let kk = &k[idx]; - kk.fade == r.fade && kk.x == r.x && kk.y == r.y - && kk.scale_x == r.scale_x && kk.scale_y == r.scale_y + kk.fade == r.fade + && kk.x == r.x + && kk.y == r.y + && kk.scale_x == r.scale_x + && kk.scale_y == r.scale_y }) }); - if !on_covering { multi_wrongrun += 1 } + if !on_covering { + multi_wrongrun += 1 + } } } } } } println!("CONTROL — exactly ONE plateau, and it covers the settle instant:"); - println!(" {one_cov} elements, rest() and pose_at(settle) agree on {one_agree} ({:.1} %)", - 100.0 * one_agree as f64 / one_cov.max(1) as f64); + println!( + " {one_cov} elements, rest() and pose_at(settle) agree on {one_agree} ({:.1} %)", + 100.0 * one_agree as f64 / one_cov.max(1) as f64 + ); println!("\nTEST — MORE THAN ONE plateau, at least one covering the settle instant:"); - println!(" {multi_cov} elements, agree on {multi_agree} ({:.1} %)", - 100.0 * multi_agree as f64 / multi_cov.max(1) as f64); - println!(" of the {} disagreements, rest() landed on a run that does NOT cover", - multi_cov - multi_agree); + println!( + " {multi_cov} elements, agree on {multi_agree} ({:.1} %)", + 100.0 * multi_agree as f64 / multi_cov.max(1) as f64 + ); + println!( + " of the {} disagreements, rest() landed on a run that does NOT cover", + multi_cov - multi_agree + ); println!(" the settle instant: {multi_wrongrun}"); println!("\n--- END (if this line is missing, the run did not finish) ---"); } diff --git a/crates/sylpheed-formats/examples/prm_alpha_census.rs b/crates/sylpheed-formats/examples/prm_alpha_census.rs index 21b26b02..aca2bbe1 100644 --- a/crates/sylpheed-formats/examples/prm_alpha_census.rs +++ b/crates/sylpheed-formats/examples/prm_alpha_census.rs @@ -11,27 +11,40 @@ //! ⚠️ The rule was invented AFTER seeing three of those answers, so it is fitted //! on them and only `pfbase.tbm` is out of sample. This prints all four plus a //! disc-wide census, so the fit and its reach are visible together. -use sylpheed_formats::{pak, ratc, ui_layout}; use std::collections::BTreeMap; +use sylpheed_formats::{pak, ratc, ui_layout}; fn main() { let root = std::env::var("SYLPHEED_DISC").unwrap_or_else(|_| "/disc".into()); - let mut paks: Vec<_> = std::fs::read_dir(format!("{root}/dat")).expect("dat/") - .flatten().map(|e| e.path()) - .filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pak")).collect(); + let mut paks: Vec<_> = std::fs::read_dir(format!("{root}/dat")) + .expect("dat/") + .flatten() + .map(|e| e.path()) + .filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pak")) + .collect(); paks.sort(); // name -> (count, set of t=0 alphas, set of "starts at max" flags) let mut byname: BTreeMap)> = BTreeMap::new(); for p in &paks { - let Ok(ar) = pak::PakArchive::open(p) else { continue }; + let Ok(ar) = pak::PakArchive::open(p) else { + continue; + }; for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; - if !ratc::is_ratc(&by) { continue } - let Some(b) = ui_layout::parse_build(&by) else { continue }; + if !ratc::is_ratc(&by) { + continue; + } + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; for el in &b.elements { // primitives and the keyless: anything with no layer key - if ui_layout::sprite_layer_key(&b, &by, el).is_some() { continue } - let Some(k0) = el.keyframes.first() else { continue }; + if ui_layout::sprite_layer_key(&b, &by, el).is_some() { + continue; + } + let Some(k0) = el.keyframes.first() else { + continue; + }; let a0 = k0.fade >> 24; let ent = byname.entry(el.name.clone()).or_default(); ent.0 += 1; @@ -39,11 +52,25 @@ fn main() { } } } - println!("{:>28} {:>7} alpha at t=0 (count)", "keyless element", "n"); - let known = ["palogo_eff0.prm", "pfbase.tbm", "pteff02.prm", "pteff00.prm"]; + println!( + "{:>28} {:>7} alpha at t=0 (count)", + "keyless element", "n" + ); + let known = [ + "palogo_eff0.prm", + "pfbase.tbm", + "pteff02.prm", + "pteff00.prm", + ]; for (n, (c, a)) in &byname { - let tag = if known.contains(&n.as_str()) { " <- IN THE MEASURED TABLE" } else { "" }; - if *c < 4 && tag.is_empty() { continue } + let tag = if known.contains(&n.as_str()) { + " <- IN THE MEASURED TABLE" + } else { + "" + }; + if *c < 4 && tag.is_empty() { + continue; + } let al: Vec = a.iter().map(|(k, v)| format!("{k}x{v}")).collect(); println!(" {n:>26} {c:>7} {}{tag}", al.join(" ")); } diff --git a/crates/sylpheed-formats/examples/prm_colour_census.rs b/crates/sylpheed-formats/examples/prm_colour_census.rs index 133b89d2..db984ad4 100644 --- a/crates/sylpheed-formats/examples/prm_colour_census.rs +++ b/crates/sylpheed-formats/examples/prm_colour_census.rs @@ -3,35 +3,50 @@ //! `ui-prm-primitives.md` leaves blend mode open, and `forced_backdrop` assumes //! straight alpha-over. A quad whose ARGB would tint the whole screen a colour no //! screen shows is evidence against alpha-over for that quad. -use sylpheed_formats::{pak, ratc, ui_layout}; use std::collections::BTreeMap; -fn main(){ - let root=std::env::var("SYLPHEED_DISC").unwrap_or_else(|_|"/disc".into()); - let mut paks:Vec<_>=std::fs::read_dir(format!("{root}/dat")).expect("dat/") - .flatten().map(|e|e.path()) - .filter(|p|p.extension().and_then(|s|s.to_str())==Some("pak")).collect(); +use sylpheed_formats::{pak, ratc, ui_layout}; +fn main() { + let root = std::env::var("SYLPHEED_DISC").unwrap_or_else(|_| "/disc".into()); + let mut paks: Vec<_> = std::fs::read_dir(format!("{root}/dat")) + .expect("dat/") + .flatten() + .map(|e| e.path()) + .filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pak")) + .collect(); paks.sort(); - let mut m:BTreeMap>=BTreeMap::new(); + let mut m: BTreeMap> = BTreeMap::new(); for p in &paks { - let Ok(ar)=pak::PakArchive::open(p) else { continue }; + let Ok(ar) = pak::PakArchive::open(p) else { + continue; + }; for e in ar.entries() { - let Ok(by)=ar.read(e) else { continue }; - if !ratc::is_ratc(&by) { continue } - let Some(b)=ui_layout::parse_build(&by) else { continue }; + let Ok(by) = ar.read(e) else { continue }; + if !ratc::is_ratc(&by) { + continue; + } + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; for el in &b.elements { - if el.sprite.is_some() { continue } + if el.sprite.is_some() { + continue; + } for k in &el.keyframes { - *m.entry(el.name.clone()).or_default() - .entry(format!("{:08x}", k.fade)).or_default()+=1; + *m.entry(el.name.clone()) + .or_default() + .entry(format!("{:08x}", k.fade)) + .or_default() += 1; } } } } println!("{:>26} fade ARGB values (count)", "keyless element"); - for (n,v) in &m { - let tot:usize=v.values().sum(); - if tot<4 { continue } - let s:Vec=v.iter().map(|(k,c)|format!("{k}x{c}")).collect(); + for (n, v) in &m { + let tot: usize = v.values().sum(); + if tot < 4 { + continue; + } + let s: Vec = v.iter().map(|(k, c)| format!("{k}x{c}")).collect(); println!(" {n:>24} {}", s.join(" ")); } } diff --git a/crates/sylpheed-formats/examples/prm_forced_first.rs b/crates/sylpheed-formats/examples/prm_forced_first.rs index 385e83c6..f84b2092 100644 --- a/crates/sylpheed-formats/examples/prm_forced_first.rs +++ b/crates/sylpheed-formats/examples/prm_forced_first.rs @@ -15,37 +15,72 @@ use sylpheed_formats::{pak, ratc, ui_layout}; fn main() { let root = std::env::var("SYLPHEED_DISC").unwrap_or_else(|_| "/disc".into()); - let mut paks: Vec<_> = std::fs::read_dir(format!("{root}/dat")).expect("dat/") - .flatten().map(|e| e.path()) - .filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pak")).collect(); + let mut paks: Vec<_> = std::fs::read_dir(format!("{root}/dat")) + .expect("dat/") + .flatten() + .map(|e| e.path()) + .filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pak")) + .collect(); paks.sort(); let (mut forced, mut partial, mut free) = (0usize, 0usize, 0usize); let mut names: std::collections::BTreeMap = Default::default(); for p in &paks { - let Ok(ar) = pak::PakArchive::open(p) else { continue }; + let Ok(ar) = pak::PakArchive::open(p) else { + continue; + }; for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; - if !ratc::is_ratc(&by) { continue } - let Some(b) = ui_layout::parse_build(&by) else { continue }; - let tmax = b.elements.iter().flat_map(|el| el.keyframes.iter().filter_map(|k| k.time)) - .max().unwrap_or(0); - if tmax == 0 { continue } + if !ratc::is_ratc(&by) { + continue; + } + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; + let tmax = b + .elements + .iter() + .flat_map(|el| el.keyframes.iter().filter_map(|k| k.time)) + .max() + .unwrap_or(0); + if tmax == 0 { + continue; + } for el in &b.elements { - if ui_layout::sprite_layer_key(&b, &by, el).is_some() { continue } + if ui_layout::sprite_layer_key(&b, &by, el).is_some() { + continue; + } // full-screen only: a quad that does not cover cannot occlude - if el.pivot_x * 2 < 1280 || el.pivot_y * 2 < 720 { continue } + if el.pivot_x * 2 < 1280 || el.pivot_y * 2 < 720 { + continue; + } let op: Vec = (0..=tmax) - .filter(|&t| el.pose_at(t).map(|k| k.fade >> 24) == Some(255)).collect(); - if op.is_empty() { continue } + .filter(|&t| el.pose_at(t).map(|k| k.fade >> 24) == Some(255)) + .collect(); + if op.is_empty() { + continue; + } let others: Vec<&ui_layout::Element> = b.elements.iter().filter(|o| o.index != el.index).collect(); - if others.is_empty() { continue } - let below = others.iter().filter(|o| - op.iter().any(|&t| o.pose_at(t).map(|k| k.fade >> 24).unwrap_or(0) > 0)).count(); + if others.is_empty() { + continue; + } + let below = others + .iter() + .filter(|o| { + op.iter() + .any(|&t| o.pose_at(t).map(|k| k.fade >> 24).unwrap_or(0) > 0) + }) + .count(); let ent = names.entry(el.name.clone()).or_default(); ent.1 += 1; - if below == others.len() { forced += 1; ent.0 += 1 } - else if below > 0 { partial += 1 } else { free += 1 } + if below == others.len() { + forced += 1; + ent.0 += 1 + } else if below > 0 { + partial += 1 + } else { + free += 1 + } } } } @@ -54,5 +89,7 @@ fn main() { println!(" forced below SOME but not all : {partial}"); println!(" occludes nothing : {free}"); println!("\nby name — instances forced first / total:"); - for (n, (f, t)) in &names { println!(" {n:>24} {f:>4} / {t}"); } + for (n, (f, t)) in &names { + println!(" {n:>24} {f:>4} / {t}"); + } } diff --git a/crates/sylpheed-formats/examples/prm_occlusion_check.rs b/crates/sylpheed-formats/examples/prm_occlusion_check.rs index cdbcf96d..8672ad60 100644 --- a/crates/sylpheed-formats/examples/prm_occlusion_check.rs +++ b/crates/sylpheed-formats/examples/prm_occlusion_check.rs @@ -18,55 +18,120 @@ fn opaque_span(el: &ui_layout::Element, thr: u32, tmax: u32) -> Vec<(f64, f64)> while t <= tmax as f64 { let a = el.pose_at(t as u32).map(|k| k.fade >> 24).unwrap_or(0); if a >= thr { - if cur.is_none() { cur = Some(t) } + if cur.is_none() { + cur = Some(t) + } } else if let Some(s) = cur.take() { out.push((s, t)); } t += 0.5; } - if let Some(s) = cur { out.push((s, tmax as f64)) } + if let Some(s) = cur { + out.push((s, tmax as f64)) + } out } fn main() { let root = std::env::var("SYLPHEED_DISC").unwrap_or_else(|_| "/disc".into()); - let mut paks: Vec<_> = std::fs::read_dir(format!("{root}/dat")).expect("dat/") - .flatten().map(|e| e.path()) - .filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pak")).collect(); + let mut paks: Vec<_> = std::fs::read_dir(format!("{root}/dat")) + .expect("dat/") + .flatten() + .map(|e| e.path()) + .filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pak")) + .collect(); paks.sort(); - let want = ["pteff00.prm", "pgloading_eff00.prm", "palogo_eff0.prm", - "pfbase.tbm", "pteff02.prm", "pzeff00.prm", "pceff00.prm", "pdeff00.prm"]; - println!("{:>22} {:>5} {:>16} {:>18} overlap", "primitive", "entry", "opaque while", "content visible"); + let want = [ + "pteff00.prm", + "pgloading_eff00.prm", + "palogo_eff0.prm", + "pfbase.tbm", + "pteff02.prm", + "pzeff00.prm", + "pceff00.prm", + "pdeff00.prm", + ]; + println!( + "{:>22} {:>5} {:>16} {:>18} overlap", + "primitive", "entry", "opaque while", "content visible" + ); for p in &paks { - let Ok(ar) = pak::PakArchive::open(p) else { continue }; + let Ok(ar) = pak::PakArchive::open(p) else { + continue; + }; for (ei, e) in ar.entries().iter().enumerate() { let Ok(by) = ar.read(e) else { continue }; - if !ratc::is_ratc(&by) { continue } - let Some(b) = ui_layout::parse_build(&by) else { continue }; - let tmax = b.elements.iter().flat_map(|el| el.keyframes.iter().filter_map(|k| k.time)) - .max().unwrap_or(0); - if tmax == 0 { continue } + if !ratc::is_ratc(&by) { + continue; + } + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; + let tmax = b + .elements + .iter() + .flat_map(|el| el.keyframes.iter().filter_map(|k| k.time)) + .max() + .unwrap_or(0); + if tmax == 0 { + continue; + } for el in &b.elements { - if !want.contains(&el.name.as_str()) { continue } - if ui_layout::sprite_layer_key(&b, &by, el).is_some() { continue } - let op = opaque_span(el, 255, tmax); - if op.is_empty() { continue } - // when is any OTHER element visible? - let mut cmin = f64::MAX; let mut cmax = f64::MIN; - for o in &b.elements { - if o.index == el.index { continue } - for (s, t) in opaque_span(o, 1, tmax) { cmin = cmin.min(s); cmax = cmax.max(t) } + if !want.contains(&el.name.as_str()) { + continue; + } + if ui_layout::sprite_layer_key(&b, &by, el).is_some() { + continue; + } + let op = opaque_span(el, 255, tmax); + if op.is_empty() { + continue; + } + // when is any OTHER element visible? + let mut cmin = f64::MAX; + let mut cmax = f64::MIN; + for o in &b.elements { + if o.index == el.index { + continue; + } + for (s, t) in opaque_span(o, 1, tmax) { + cmin = cmin.min(s); + cmax = cmax.max(t) + } + } + if cmin > cmax { + continue; } - if cmin > cmax { continue } // overlap of the primitive's opaque span with the content span - let ov: f64 = op.iter() - .map(|&(s, t)| (t.min(cmax) - s.max(cmin)).max(0.0)).sum(); - let opd: String = op.iter().map(|&(s,t)| format!("{s:.0}-{t:.0}")).collect::>().join(","); - let flag = if ov > 2.0 { " 🔴 CANNOT BE ON TOP" } else { "" }; - println!("{:>22} {:>5} {:>16} {:>18} {ov:6.1}{flag}", - el.name, format!("{}:{}", p.file_name().unwrap().to_string_lossy() - .trim_end_matches(".pak").trim_start_matches("GP_"), ei), - opd, format!("{cmin:.0}-{cmax:.0}")); + let ov: f64 = op + .iter() + .map(|&(s, t)| (t.min(cmax) - s.max(cmin)).max(0.0)) + .sum(); + let opd: String = op + .iter() + .map(|&(s, t)| format!("{s:.0}-{t:.0}")) + .collect::>() + .join(","); + let flag = if ov > 2.0 { + " 🔴 CANNOT BE ON TOP" + } else { + "" + }; + println!( + "{:>22} {:>5} {:>16} {:>18} {ov:6.1}{flag}", + el.name, + format!( + "{}:{}", + p.file_name() + .unwrap() + .to_string_lossy() + .trim_end_matches(".pak") + .trim_start_matches("GP_"), + ei + ), + opd, + format!("{cmin:.0}-{cmax:.0}") + ); } } } diff --git a/crates/sylpheed-formats/examples/prm_span_sensitivity.rs b/crates/sylpheed-formats/examples/prm_span_sensitivity.rs index b6e309fe..c9cae6f0 100644 --- a/crates/sylpheed-formats/examples/prm_span_sensitivity.rs +++ b/crates/sylpheed-formats/examples/prm_span_sensitivity.rs @@ -18,54 +18,105 @@ use sylpheed_formats::{pak, ratc, ui_layout}; fn last_t(el: &ui_layout::Element) -> u32 { - el.keyframes.iter().filter_map(|k| k.time).max().unwrap_or(0) + el.keyframes + .iter() + .filter_map(|k| k.time) + .max() + .unwrap_or(0) } fn forced(b: &ui_layout::UiBuild, el: &ui_layout::Element, tmax: u32, hold: bool) -> Option { - if el.sprite.is_some() { return None } - if (el.pivot_x * 2) < b.design_w as u32 || (el.pivot_y * 2) < b.design_h as u32 { return None } - if tmax == 0 { return None } + if el.sprite.is_some() { + return None; + } + if (el.pivot_x * 2) < b.design_w as u32 || (el.pivot_y * 2) < b.design_h as u32 { + return None; + } + if tmax == 0 { + return None; + } let alpha = |e: &ui_layout::Element, t: u32| -> u32 { - if !hold && t > last_t(e) { return 0 } + if !hold && t > last_t(e) { + return 0; + } e.pose_at(t).map(|k| k.fade >> 24).unwrap_or(0) }; let op: Vec = (0..=tmax).filter(|&t| alpha(el, t) == 255).collect(); - if op.is_empty() { return None } - let others: Vec<&ui_layout::Element> = b.elements.iter().filter(|o| o.index != el.index).collect(); - if others.is_empty() { return None } - let below = others.iter().filter(|o| op.iter().any(|&t| alpha(o, t) > 0)).count(); + if op.is_empty() { + return None; + } + let others: Vec<&ui_layout::Element> = + b.elements.iter().filter(|o| o.index != el.index).collect(); + if others.is_empty() { + return None; + } + let below = others + .iter() + .filter(|o| op.iter().any(|&t| alpha(o, t) > 0)) + .count(); Some(below == others.len()) } fn main() { let root = std::env::var("SYLPHEED_DISC").unwrap_or_else(|_| "/disc".into()); - let mut paks: Vec<_> = std::fs::read_dir(format!("{root}/dat")).expect("dat/") - .flatten().map(|e| e.path()) - .filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pak")).collect(); + let mut paks: Vec<_> = std::fs::read_dir(format!("{root}/dat")) + .expect("dat/") + .flatten() + .map(|e| e.path()) + .filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pak")) + .collect(); paks.sort(); let (mut n, mut a_true) = (0usize, 0usize); let mut flips = [0usize; 3]; let mut examples: Vec = Vec::new(); for p in &paks { - let Ok(ar) = pak::PakArchive::open(p) else { continue }; + let Ok(ar) = pak::PakArchive::open(p) else { + continue; + }; for (ei, e) in ar.entries().iter().enumerate() { let Ok(by) = ar.read(e) else { continue }; - if !ratc::is_ratc(&by) { continue } - let Some(b) = ui_layout::parse_build(&by) else { continue }; - let tall = b.elements.iter().flat_map(|el| el.keyframes.iter().filter_map(|k| k.time)) - .max().unwrap_or(0); - let hdr = if by.len() >= 12 { u32::from_be_bytes(by[8..12].try_into().unwrap()) } else { 0 }; + if !ratc::is_ratc(&by) { + continue; + } + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; + let tall = b + .elements + .iter() + .flat_map(|el| el.keyframes.iter().filter_map(|k| k.time)) + .max() + .unwrap_or(0); + let hdr = if by.len() >= 12 { + u32::from_be_bytes(by[8..12].try_into().unwrap()) + } else { + 0 + }; for el in &b.elements { - let Some(va) = forced(&b, el, tall, true) else { continue }; - n += 1; if va { a_true += 1 } - for (k, vb) in [forced(&b, el, last_t(el), true), - forced(&b, el, hdr, true), - forced(&b, el, tall, false)].into_iter().enumerate() { + let Some(va) = forced(&b, el, tall, true) else { + continue; + }; + n += 1; + if va { + a_true += 1 + } + for (k, vb) in [ + forced(&b, el, last_t(el), true), + forced(&b, el, hdr, true), + forced(&b, el, tall, false), + ] + .into_iter() + .enumerate() + { if vb != Some(va) { flips[k] += 1; if k == 2 && examples.len() < 6 { - examples.push(format!("{}:{} {} A={va} D={vb:?}", - p.file_name().unwrap().to_string_lossy(), ei, el.name)); + examples.push(format!( + "{}:{} {} A={va} D={vb:?}", + p.file_name().unwrap().to_string_lossy(), + ei, + el.name + )); } } } @@ -75,8 +126,19 @@ fn main() { println!("keyless full-screen primitives with an opaque interval: {n}"); println!(" convention A (span = all elements' max, hold) -> forced first: {a_true}\n"); println!(" verdicts that CHANGE under:"); - println!(" B span = the primitive's own last keyframe : {}", flips[0]); - println!(" C span = the header's declared length +0x08 : {}", flips[1]); - println!(" D elements GONE after their last keyframe : {}", flips[2]); - for e in &examples { println!(" {e}") } + println!( + " B span = the primitive's own last keyframe : {}", + flips[0] + ); + println!( + " C span = the header's declared length +0x08 : {}", + flips[1] + ); + println!( + " D elements GONE after their last keyframe : {}", + flips[2] + ); + for e in &examples { + println!(" {e}") + } } diff --git a/crates/sylpheed-formats/examples/ptloop_leaf_extent.rs b/crates/sylpheed-formats/examples/ptloop_leaf_extent.rs index 6ac4ca4b..ce246837 100644 --- a/crates/sylpheed-formats/examples/ptloop_leaf_extent.rs +++ b/crates/sylpheed-formats/examples/ptloop_leaf_extent.rs @@ -8,35 +8,53 @@ //! export; this checks that against the disc. //! //! cargo run -p sylpheed-formats --example ptloop_leaf_extent -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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 ar = PakArchive::open(root.join("dat/GP_TITLE.pak")).expect("GP_TITLE.pak"); for entry in [4usize, 5, 7] { - let Ok(by) = ar.read(&ar.entries()[entry]) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; + let Ok(by) = ar.read(&ar.entries()[entry]) else { + continue; + }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; println!("\n######## GP_TITLE entry {entry} ########"); for parent in ["ptloop01", "ptloop02"] { let Some(el) = b.elements.iter().find(|e| e.name.starts_with(parent)) else { - println!(" {parent}: not present in this build"); continue }; + println!(" {parent}: not present in this build"); + continue; + }; let Some(&(off, size)) = b.records.get(&el.name) else { - println!(" {}: no nested record", el.name); continue }; + println!(" {}: no nested record", el.name); + continue; + }; let span = u32::from_be_bytes(by[off + 8..off + 12].try_into().unwrap()); let Some(lb) = ui_layout::parse_build(&by[off..off + size]) else { - println!(" {}: leaf will not parse", el.name); continue }; - println!(" {} parent rest ({},{}) nested cycle span {span}", - el.name, el.keyframes.last().map(|k| k.x).unwrap_or(0), - el.keyframes.last().map(|k| k.y).unwrap_or(0)); + println!(" {}: leaf will not parse", el.name); + continue; + }; + println!( + " {} parent rest ({},{}) nested cycle span {span}", + el.name, + el.keyframes.last().map(|k| k.x).unwrap_or(0), + el.keyframes.last().map(|k| k.y).unwrap_or(0) + ); for le in &lb.elements { let (mut lo, mut hi) = (i64::MAX, i64::MIN); let (mut sxs, mut sys) = (Vec::new(), Vec::new()); for t in 0..=span { if let Some(k) = le.pose_at(t) { - lo = lo.min(k.x as i64); hi = hi.max(k.x as i64); - if !sxs.contains(&k.scale_x) { sxs.push(k.scale_x) } - if !sys.contains(&k.scale_y) { sys.push(k.scale_y) } + lo = lo.min(k.x as i64); + hi = hi.max(k.x as i64); + if !sxs.contains(&k.scale_x) { + sxs.push(k.scale_x) + } + if !sys.contains(&k.scale_y) { + sys.push(k.scale_y) + } } } // A CYCLE LENGTH IS NOT A MOTION DURATION. Find the last t at @@ -46,15 +64,24 @@ fn main() { let mut prev = None; for t in 0..=span { if let Some(k) = le.pose_at(t) { - if prev.map_or(false, |p| p != k.x) { last_move = t } + if prev.map_or(false, |p| p != k.x) { + last_move = t + } prev = Some(k.x); } } let w = (le.pivot_x * 2) as i64; - println!(" leaf {:<12} pivot {}x{} quad w={w} x track {lo} .. {hi} \ + println!( + " leaf {:<12} pivot {}x{} quad w={w} x track {lo} .. {hi} \ (centre {} .. {}) scale_x {:?} scale_y {:?}", - le.name, le.pivot_x, le.pivot_y, - lo + le.pivot_x as i64, hi + le.pivot_x as i64, sxs, sys); + le.name, + le.pivot_x, + le.pivot_y, + lo + le.pivot_x as i64, + hi + le.pivot_x as i64, + sxs, + sys + ); println!(" motion ends at t={last_move} of a {span}-unit cycle -> {:.3} px/unit over the MOVING span (vs {:.3} over the cycle)", (hi - lo) as f64 / last_move.max(1) as f64, (hi - lo) as f64 / span as f64); diff --git a/crates/sylpheed-formats/examples/ptloop_leaf_keyframes.rs b/crates/sylpheed-formats/examples/ptloop_leaf_keyframes.rs index b22a7e77..ddf25c1e 100644 --- a/crates/sylpheed-formats/examples/ptloop_leaf_keyframes.rs +++ b/crates/sylpheed-formats/examples/ptloop_leaf_keyframes.rs @@ -7,8 +7,8 @@ //! number from their export, so it is worth deriving independently. //! //! cargo run -p sylpheed-formats --example ptloop_leaf_keyframes -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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")); @@ -16,9 +16,15 @@ fn main() { let by = ar.read(&ar.entries()[4]).expect("entry 4"); let b = ui_layout::parse_build(&by).expect("parse"); for parent in ["ptloop01", "ptloop02"] { - let Some(el) = b.elements.iter().find(|e| e.name.starts_with(parent)) else { continue }; - let Some(&(off, size)) = b.records.get(&el.name) else { continue }; - let Some(lb) = ui_layout::parse_build(&by[off..off + size]) else { continue }; + let Some(el) = b.elements.iter().find(|e| e.name.starts_with(parent)) else { + continue; + }; + let Some(&(off, size)) = b.records.get(&el.name) else { + continue; + }; + let Some(lb) = ui_layout::parse_build(&by[off..off + size]) else { + continue; + }; for le in &lb.elements { println!("\n== {} -> leaf {}", el.name, le.name); let ks: Vec<_> = le.keyframes.iter().collect(); @@ -27,8 +33,14 @@ fn main() { match (a.time, c.time) { (Some(t0), Some(t1)) if t1 > t0 => println!( " t {t0:>4} -> {t1:<4} x {:>6} -> {:<6} = {:+.4} px/unit", - a.x, c.x, (c.x - a.x) as f64 / (t1 - t0) as f64), - _ => println!(" t {:?} -> {:?} x {} -> {} (no rate)", a.time, c.time, a.x, c.x), + a.x, + c.x, + (c.x - a.x) as f64 / (t1 - t0) as f64 + ), + _ => println!( + " t {:?} -> {:?} x {} -> {} (no rate)", + a.time, c.time, a.x, c.x + ), } } } diff --git a/crates/sylpheed-formats/examples/ptloop_leaf_sweep_at.rs b/crates/sylpheed-formats/examples/ptloop_leaf_sweep_at.rs index 26998ee9..80ec9669 100644 --- a/crates/sylpheed-formats/examples/ptloop_leaf_sweep_at.rs +++ b/crates/sylpheed-formats/examples/ptloop_leaf_sweep_at.rs @@ -41,7 +41,10 @@ fn main() { // CYCLE LENGTH, and its keyframes need not fill it. This is why two // captures of the same settled title do not share a sweep phase. let loop_len = u32::from_be_bytes(by[off + 8..off + 12].try_into().unwrap()); - println!("\n-- {} nested record: loop length (+0x08) = {loop_len}", el.name); + println!( + "\n-- {} nested record: loop length (+0x08) = {loop_len}", + el.name + ); let Some(lb) = ui_layout::parse_build(&by[off..off + size]) else { eprintln!("{}: leaf will not parse", el.name); continue; diff --git a/crates/sylpheed-formats/examples/ptloop_parent_keyframes.rs b/crates/sylpheed-formats/examples/ptloop_parent_keyframes.rs index f2e80466..498990e7 100644 --- a/crates/sylpheed-formats/examples/ptloop_parent_keyframes.rs +++ b/crates/sylpheed-formats/examples/ptloop_parent_keyframes.rs @@ -6,19 +6,30 @@ //! alpha, and this prints it. //! //! cargo run -p sylpheed-formats --example ptloop_parent_keyframes -- GP_TITLE 5 -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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(); - let pak = argv.iter().find(|a| a.parse::().is_err()) - .cloned().unwrap_or_else(|| "GP_TITLE".to_string()); + let pak = argv + .iter() + .find(|a| a.parse::().is_err()) + .cloned() + .unwrap_or_else(|| "GP_TITLE".to_string()); let ar = PakArchive::open(root.join(format!("dat/{pak}.pak"))).expect("pak"); let builds: Vec = argv.iter().filter_map(|a| a.parse().ok()).collect(); - for e in if builds.is_empty() { vec![5usize] } else { builds } { - let Ok(by) = ar.read(&ar.entries()[e]) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; + for e in if builds.is_empty() { + vec![5usize] + } else { + builds + } { + let Ok(by) = ar.read(&ar.entries()[e]) else { + continue; + }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; println!("=== {pak} entry {e}: {} elements ===", b.elements.len()); for el in &b.elements { for (i, k) in el.keyframes.iter().enumerate() { diff --git a/crates/sylpheed-formats/examples/record_loop_length.rs b/crates/sylpheed-formats/examples/record_loop_length.rs index 88a70934..3d9bcfa8 100644 --- a/crates/sylpheed-formats/examples/record_loop_length.rs +++ b/crates/sylpheed-formats/examples/record_loop_length.rs @@ -16,54 +16,98 @@ //! * The distribution must not be trivial: if every record had exactly //! `+0x08 == max t`, the field would carry nothing and "loop length" would be //! an unfalsifiable relabelling of the keyframes. -use sylpheed_formats::{pak, ratc, ui_layout}; use std::collections::BTreeMap; +use sylpheed_formats::{pak, ratc, ui_layout}; fn main() { let root = std::env::var("SYLPHEED_DISC").unwrap_or_else(|_| "/disc".into()); - let mut paks: Vec<_> = std::fs::read_dir(format!("{root}/dat")).expect("dat/") - .flatten().map(|e| e.path()) - .filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pak")).collect(); + let mut paks: Vec<_> = std::fs::read_dir(format!("{root}/dat")) + .expect("dat/") + .flatten() + .map(|e| e.path()) + .filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pak")) + .collect(); paks.sort(); let (mut total, mut exact, mut holds, mut violations) = (0usize, 0usize, 0usize, 0usize); let mut hold_hist: BTreeMap = BTreeMap::new(); let mut worst: Vec<(i64, String)> = Vec::new(); for p in &paks { - let Ok(ar) = pak::PakArchive::open(p) else { continue }; + let Ok(ar) = pak::PakArchive::open(p) else { + continue; + }; for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; - if !ratc::is_ratc(&by) { continue } - let Some(b) = ui_layout::parse_build(&by) else { continue }; + if !ratc::is_ratc(&by) { + continue; + } + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; for (rn, &(o, s)) in &b.records { - if o + 12 > by.len() || o + s > by.len() { continue } - if &by[o..o + 4] != b"RATC" { continue } + if o + 12 > by.len() || o + s > by.len() { + continue; + } + if &by[o..o + 4] != b"RATC" { + continue; + } let len = u32::from_be_bytes(by[o + 8..o + 12].try_into().unwrap()) as i64; - let Some(lb) = ui_layout::parse_build(&by[o..o + s]) else { continue }; - let maxt = lb.elements.iter() + let Some(lb) = ui_layout::parse_build(&by[o..o + s]) else { + continue; + }; + let maxt = lb + .elements + .iter() .flat_map(|el| el.keyframes.iter().filter_map(|k| k.time)) - .max().unwrap_or(0) as i64; - if maxt == 0 { continue } // a static record declares no cycle + .max() + .unwrap_or(0) as i64; + if maxt == 0 { + continue; + } // a static record declares no cycle total += 1; let slack = len - maxt; *hold_hist.entry(slack).or_default() += 1; - if slack == 0 { exact += 1 } else if slack > 0 { holds += 1 } else { + if slack == 0 { + exact += 1 + } else if slack > 0 { + holds += 1 + } else { violations += 1; if worst.len() < 12 { - worst.push((slack, format!("{}:{rn} len={len} maxt={maxt}", - p.file_name().unwrap().to_string_lossy()))); + worst.push(( + slack, + format!( + "{}:{rn} len={len} maxt={maxt}", + p.file_name().unwrap().to_string_lossy() + ), + )); } } } } } println!("nested records with timed keyframes : {total}"); - println!(" +08 == max keyframe time (exact) : {exact} ({:.1}%)", 100.0*exact as f64/total as f64); - println!(" +08 > max keyframe time (a hold) : {holds} ({:.1}%)", 100.0*holds as f64/total as f64); - println!(" +08 < max keyframe time 🔴 : {violations} ({:.2}%) <- the falsifier", - 100.0*violations as f64/total as f64); + println!( + " +08 == max keyframe time (exact) : {exact} ({:.1}%)", + 100.0 * exact as f64 / total as f64 + ); + println!( + " +08 > max keyframe time (a hold) : {holds} ({:.1}%)", + 100.0 * holds as f64 / total as f64 + ); + println!( + " +08 < max keyframe time 🔴 : {violations} ({:.2}%) <- the falsifier", + 100.0 * violations as f64 / total as f64 + ); println!("\nslack (+08 - max t) distribution, most common first:"); let mut h: Vec<_> = hold_hist.iter().collect(); h.sort_by_key(|&(_, n)| std::cmp::Reverse(*n)); - for (k, n) in h.iter().take(14) { println!(" slack {k:>6} : {n}"); } - if !worst.is_empty() { println!("\nviolations:"); for (s, w) in &worst { println!(" {s:>6} {w}"); } } + for (k, n) in h.iter().take(14) { + println!(" slack {k:>6} : {n}"); + } + if !worst.is_empty() { + println!("\nviolations:"); + for (s, w) in &worst { + println!(" {s:>6} {w}"); + } + } } diff --git a/crates/sylpheed-formats/examples/record_loop_length_api.rs b/crates/sylpheed-formats/examples/record_loop_length_api.rs index 56b5767d..0b648575 100644 --- a/crates/sylpheed-formats/examples/record_loop_length_api.rs +++ b/crates/sylpheed-formats/examples/record_loop_length_api.rs @@ -10,54 +10,83 @@ //! anything cannot be trusted to return the right one. //! //! cargo run -p sylpheed-formats --example record_loop_length_api -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, ui_layout}; fn main() { // ---- controls ------------------------------------------------------- - assert_eq!(ui_layout::loop_length_units(b"NOTR\x00\x00\x00\x00\x00\x00\x00\x78"), None, - "control FAILED: accepted a non-RATC slice"); - assert_eq!(ui_layout::loop_length_units(b"RATC\x00\x00"), None, - "control FAILED: accepted a slice too short for +0x08"); - assert_eq!(ui_layout::loop_length_units(b"RATC\x00\x00\x00\x00\x00\x00\x00\x78"), Some(120), - "control FAILED: did not read +0x08 big-endian"); + assert_eq!( + ui_layout::loop_length_units(b"NOTR\x00\x00\x00\x00\x00\x00\x00\x78"), + None, + "control FAILED: accepted a non-RATC slice" + ); + assert_eq!( + ui_layout::loop_length_units(b"RATC\x00\x00"), + None, + "control FAILED: accepted a slice too short for +0x08" + ); + assert_eq!( + ui_layout::loop_length_units(b"RATC\x00\x00\x00\x00\x00\x00\x00\x78"), + Some(120), + "control FAILED: did not read +0x08 big-endian" + ); println!("controls pass: rejects non-RATC, rejects short, reads BE at +0x08"); let root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC")); let ar = PakArchive::open(root.join("dat/GP_TITLE.pak")).expect("GP_TITLE.pak"); // The records the finding names, with their published values. - let expect: &[(&str, u32)] = &[("ptbtn00f.rat", 120), ("ptloop01.rat", 600), - ("ptloop02.rat", 720)]; + let expect: &[(&str, u32)] = &[ + ("ptbtn00f.rat", 120), + ("ptloop01.rat", 600), + ("ptloop02.rat", 720), + ]; let mut seen = 0usize; let (mut recs, mut viol) = (0usize, 0usize); for (ei, e) in ar.entries().iter().enumerate() { let Ok(by) = ar.read(e) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; for (name, (off, size)) in &b.records { let rec = &by[*off..(*off + *size).min(by.len())]; - let Some(len) = ui_layout::loop_length_units(rec) else { continue }; + let Some(len) = ui_layout::loop_length_units(rec) else { + continue; + }; recs += 1; // the disc-wide invariant the finding rests on let largest = ui_layout::parse_build(rec) - .map(|l| l.elements.iter() - .flat_map(|el| el.keyframes.iter().filter_map(|k| k.time)) - .max().unwrap_or(0)) + .map(|l| { + l.elements + .iter() + .flat_map(|el| el.keyframes.iter().filter_map(|k| k.time)) + .max() + .unwrap_or(0) + }) .unwrap_or(0); - if len < largest { viol += 1; } + if len < largest { + viol += 1; + } for (want_name, want) in expect { if name == want_name && seen < 16 { seen += 1; let ok = if len == *want { "OK" } else { "MISMATCH" }; - println!(" entry {ei:2} {name:16} +0x08 = {len:4} \ - (published {want}) largest kf {largest:4} {ok}"); + println!( + " entry {ei:2} {name:16} +0x08 = {len:4} \ + (published {want}) largest kf {largest:4} {ok}" + ); assert_eq!(len, *want, "{name} disagrees with the published value"); } } } } - println!("\n{recs} records read through the public fn; \ - {viol} violate +0x08 >= largest keyframe time"); - assert!(seen > 0, "found none of the named records — the check proved nothing"); + println!( + "\n{recs} records read through the public fn; \ + {viol} violate +0x08 >= largest keyframe time" + ); + assert!( + seen > 0, + "found none of the named records — the check proved nothing" + ); } diff --git a/crates/sylpheed-formats/examples/rest_fallback_audit.rs b/crates/sylpheed-formats/examples/rest_fallback_audit.rs index 0ad06138..f1e4d426 100644 --- a/crates/sylpheed-formats/examples/rest_fallback_audit.rs +++ b/crates/sylpheed-formats/examples/rest_fallback_audit.rs @@ -16,14 +16,16 @@ //! * last alpha == 0 -> it fades out; a visible rest is a transient's peak //! //! cargo run -p sylpheed-formats --example rest_fallback_audit -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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 mut paks: Vec = std::fs::read_dir(root.join("dat")).expect("dat/") + 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().is_some_and(|x| x == "pak")).collect(); + .filter(|p| p.extension().is_some_and(|x| x == "pak")) + .collect(); paks.sort(); let (mut fires, mut vis, mut ends_visible, mut ends_zero, mut at_peak) = (0, 0, 0, 0, 0); // ⚠️ The port agent's exit-ramp finding applies to THIS split too: if a @@ -32,26 +34,53 @@ fn main() { // elements before using it on the 1 697. let (mut all_el, mut all_end_zero) = (0usize, 0usize); for pak in &paks { - let Ok(ar) = PakArchive::open(pak) else { continue }; + let Ok(ar) = PakArchive::open(pak) else { + continue; + }; for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; for el in &b.elements { - if el.keyframes.len() < 2 { continue } + if el.keyframes.len() < 2 { + continue; + } all_el += 1; - if (el.keyframes.last().unwrap().fade >> 24) & 0xff == 0 { all_end_zero += 1 } - if el.keyframes.windows(2).any(|w| w[0].x == w[1].x && w[0].y == w[1].y - && w[0].scale_x == w[1].scale_x && w[0].scale_y == w[1].scale_y - && w[0].fade == w[1].fade) { continue } + if (el.keyframes.last().unwrap().fade >> 24) & 0xff == 0 { + all_end_zero += 1 + } + if el.keyframes.windows(2).any(|w| { + w[0].x == w[1].x + && w[0].y == w[1].y + && w[0].scale_x == w[1].scale_x + && w[0].scale_y == w[1].scale_y + && w[0].fade == w[1].fade + }) { + continue; + } fires += 1; let Some(r) = el.rest() else { continue }; let a = (r.fade >> 24) & 0xff; - if a == 0 { continue } + if a == 0 { + continue; + } vis += 1; let last = (el.keyframes.last().unwrap().fade >> 24) & 0xff; - if last > 0 { ends_visible += 1 } else { ends_zero += 1 } - let peak = el.keyframes.iter().map(|k| (k.fade >> 24) & 0xff).max().unwrap_or(0); - if a == peak { at_peak += 1 } + if last > 0 { + ends_visible += 1 + } else { + ends_zero += 1 + } + let peak = el + .keyframes + .iter() + .map(|k| (k.fade >> 24) & 0xff) + .max() + .unwrap_or(0); + if a == peak { + at_peak += 1 + } } } } @@ -62,7 +91,9 @@ fn main() { println!(" rest alpha == the element's MAX {at_peak}"); println!("\nCONTROL on the split itself — is 'ends at a=0' near-universal?"); println!(" all elements with >= 2 keyframes {all_el}"); - println!(" of those, last keyframe alpha = 0 {all_end_zero} ({:.1} %)", - 100.0 * all_end_zero as f64 / all_el as f64); + println!( + " of those, last keyframe alpha = 0 {all_end_zero} ({:.1} %)", + 100.0 * all_end_zero as f64 / all_el as f64 + ); println!("\n--- END (if this line is missing, the run did not finish) ---"); } diff --git a/crates/sylpheed-formats/examples/rest_fallback_census.rs b/crates/sylpheed-formats/examples/rest_fallback_census.rs index f5bd7caf..f3e10e36 100644 --- a/crates/sylpheed-formats/examples/rest_fallback_census.rs +++ b/crates/sylpheed-formats/examples/rest_fallback_census.rs @@ -14,8 +14,8 @@ //! //! cargo run -p sylpheed-formats --example rest_fallback_census -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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")); @@ -30,22 +30,33 @@ fn main() { let mut worst: Vec<(u32, String, String)> = Vec::new(); let mut per_pak: std::collections::BTreeMap = Default::default(); for pak in &paks { - let Ok(ar) = PakArchive::open(pak) else { continue }; + let Ok(ar) = PakArchive::open(pak) else { + continue; + }; let name = pak.file_name().unwrap().to_string_lossy().to_string(); for (i, e) in ar.entries().iter().enumerate() { let Ok(by) = ar.read(e) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; for el in &b.elements { - if el.keyframes.len() < 2 { continue } + if el.keyframes.len() < 2 { + continue; + } elements += 1; // a plateau is two ADJACENT poses that are equal — the same test // the plateau path makes before the fallback can run let has_plateau = el.keyframes.windows(2).any(|w| { - w[0].x == w[1].x && w[0].y == w[1].y - && w[0].scale_x == w[1].scale_x && w[0].scale_y == w[1].scale_y + w[0].x == w[1].x + && w[0].y == w[1].y + && w[0].scale_x == w[1].scale_x + && w[0].scale_y == w[1].scale_y && w[0].fade == w[1].fade }); - if has_plateau { plateau += 1; continue } + if has_plateau { + plateau += 1; + continue; + } fallback += 1; per_pak.entry(name.clone()).or_default().0 += 1; let Some(r) = el.rest() else { continue }; @@ -58,13 +69,16 @@ fn main() { } } } - println!("POPULATION: {elements} elements with >= 2 keyframes, over {} archives", paks.len()); + println!( + "POPULATION: {elements} elements with >= 2 keyframes, over {} archives", + paks.len() + ); println!("COVERAGE: {plateau} have a plateau (fallback never runs)"); println!(" {fallback} have NONE -> the dwell fallback decides"); println!(" {fb_visible} of those rest at alpha > 0 -- i.e. VISIBLE\n"); println!("PER ARCHIVE — fallback fires / of those, rests VISIBLE:"); let mut rows: Vec<_> = per_pak.into_iter().collect(); - rows.sort_by(|a, b| b.1.1.cmp(&a.1.1)); + rows.sort_by(|a, b| b.1 .1.cmp(&a.1 .1)); for (pak, (fires, vis)) in &rows { println!(" {pak:34} {fires:5} fires {vis:5} visible"); } diff --git a/crates/sylpheed-formats/examples/rest_fallback_title.rs b/crates/sylpheed-formats/examples/rest_fallback_title.rs index c526b78a..f34d0331 100644 --- a/crates/sylpheed-formats/examples/rest_fallback_title.rs +++ b/crates/sylpheed-formats/examples/rest_fallback_title.rs @@ -1,27 +1,49 @@ //! Which `GP_TITLE` elements does the resting-pose dwell fallback decide, and does //! it hand back a visible pose? The disc-wide census says 5 fires / 4 visible here. //! cargo run -p sylpheed-formats --example rest_fallback_title -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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 ar = PakArchive::open(root.join("dat/GP_TITLE.pak")).expect("GP_TITLE"); for (i, e) in ar.entries().iter().enumerate() { let Ok(by) = ar.read(e) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; for el in &b.elements { - if el.keyframes.len() < 2 { continue } - if el.keyframes.windows(2).any(|w| w[0].x == w[1].x && w[0].y == w[1].y - && w[0].scale_x == w[1].scale_x && w[0].scale_y == w[1].scale_y - && w[0].fade == w[1].fade) { continue } + if el.keyframes.len() < 2 { + continue; + } + if el.keyframes.windows(2).any(|w| { + w[0].x == w[1].x + && w[0].y == w[1].y + && w[0].scale_x == w[1].scale_x + && w[0].scale_y == w[1].scale_y + && w[0].fade == w[1].fade + }) { + continue; + } let Some(r) = el.rest() else { continue }; let a = (r.fade >> 24) & 0xff; - let ks: Vec = el.keyframes.iter() - .map(|k| format!("{}:a{}", k.time.map(|v| v.to_string()).unwrap_or("-".into()), (k.fade >> 24) & 0xff)) + let ks: Vec = el + .keyframes + .iter() + .map(|k| { + format!( + "{}:a{}", + k.time.map(|v| v.to_string()).unwrap_or("-".into()), + (k.fade >> 24) & 0xff + ) + }) .collect(); - println!("entry {i:2} {:24} rest a={a:3} t={:?} [{}]{}", - el.name, r.time, ks.join(" "), - if a > 0 { " <== VISIBLE" } else { "" }); + println!( + "entry {i:2} {:24} rest a={a:3} t={:?} [{}]{}", + el.name, + r.time, + ks.join(" "), + if a > 0 { " <== VISIBLE" } else { "" } + ); } } } diff --git a/crates/sylpheed-formats/examples/rest_scale_of.rs b/crates/sylpheed-formats/examples/rest_scale_of.rs index 2f4b7b18..522da616 100644 --- a/crates/sylpheed-formats/examples/rest_scale_of.rs +++ b/crates/sylpheed-formats/examples/rest_scale_of.rs @@ -6,29 +6,56 @@ //! nothing like its stored size. //! //! cargo run -p sylpheed-formats --example rest_scale_of -- 5 6 4 -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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(); - let pak = argv.iter().find(|a| a.parse::().is_err()) - .cloned().unwrap_or_else(|| "GP_TITLE".to_string()); + let pak = argv + .iter() + .find(|a| a.parse::().is_err()) + .cloned() + .unwrap_or_else(|| "GP_TITLE".to_string()); let ar = PakArchive::open(root.join(format!("dat/{pak}.pak"))).expect("pak"); let builds: Vec = argv.iter().filter_map(|a| a.parse().ok()).collect(); - for build in if builds.is_empty() { vec![5usize, 6] } else { builds } { - let Ok(by) = ar.read(&ar.entries()[build]) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; + for build in if builds.is_empty() { + vec![5usize, 6] + } else { + builds + } { + let Ok(by) = ar.read(&ar.entries()[build]) else { + continue; + }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; println!("=== {pak} entry {build} ==="); - println!("{:<22} {:>10} {:>7} {:>7} {:>12} {}", "element", "pivot(w,h)", "sx%", "sy%", "drawn px", "at 1x/2x of pivot*2"); + println!( + "{:<22} {:>10} {:>7} {:>7} {:>12} {}", + "element", "pivot(w,h)", "sx%", "sy%", "drawn px", "at 1x/2x of pivot*2" + ); for e in &b.elements { - let k = match e.rest() { Some(k) => k, None => continue }; + let k = match e.rest() { + Some(k) => k, + None => continue, + }; let w = e.pivot_x * 2; let h = e.pivot_y * 2; let dw = w as f64 * k.scale_x as f64 / 100.0; let dh = h as f64 * k.scale_y as f64 / 100.0; - println!("{:<22} {:>4},{:<5} {:>7} {:>7} {:>6.1}x{:<5.1} {:>6.1}x{:<5.1}", - e.name, w, h, k.scale_x, k.scale_y, dw, dh, dw * 2.0, dh * 2.0); + println!( + "{:<22} {:>4},{:<5} {:>7} {:>7} {:>6.1}x{:<5.1} {:>6.1}x{:<5.1}", + e.name, + w, + h, + k.scale_x, + k.scale_y, + dw, + dh, + dw * 2.0, + dh * 2.0 + ); } println!(); } diff --git a/crates/sylpheed-formats/examples/rest_vs_settle.rs b/crates/sylpheed-formats/examples/rest_vs_settle.rs index 3ab4817c..24f58002 100644 --- a/crates/sylpheed-formats/examples/rest_vs_settle.rs +++ b/crates/sylpheed-formats/examples/rest_vs_settle.rs @@ -20,14 +20,16 @@ //! averaged over. //! //! cargo run -p sylpheed-formats --example rest_vs_settle -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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 mut paks: Vec = std::fs::read_dir(root.join("dat")).expect("dat/") + 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().is_some_and(|x| x == "pak")).collect(); + .filter(|p| p.extension().is_some_and(|x| x == "pak")) + .collect(); paks.sort(); // control population (plateau) and test population (fallback), each split by // whether the bundle settles at all @@ -35,19 +37,36 @@ fn main() { let (mut fb_n, mut fb_rest_vis, mut fb_settle_vis) = (0usize, 0usize, 0usize); let mut narrow = 0usize; for pak in &paks { - let Ok(ar) = PakArchive::open(pak) else { continue }; + let Ok(ar) = PakArchive::open(pak) else { + continue; + }; for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; - let Some((lo, hi)) = b.settle_window() else { continue }; - if hi - lo < 10 { narrow += 1; continue } // this bundle never settles + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; + let Some((lo, hi)) = b.settle_window() else { + continue; + }; + if hi - lo < 10 { + narrow += 1; + continue; + } // this bundle never settles let st = lo + (hi - lo) / 2; for el in &b.elements { - if el.keyframes.len() < 2 { continue } - let plateau = el.keyframes.windows(2).any(|w| w[0].x == w[1].x - && w[0].y == w[1].y && w[0].scale_x == w[1].scale_x - && w[0].scale_y == w[1].scale_y && w[0].fade == w[1].fade); - let (Some(r), Some(s)) = (el.rest(), el.pose_at(st)) else { continue }; + if el.keyframes.len() < 2 { + continue; + } + let plateau = el.keyframes.windows(2).any(|w| { + w[0].x == w[1].x + && w[0].y == w[1].y + && w[0].scale_x == w[1].scale_x + && w[0].scale_y == w[1].scale_y + && w[0].fade == w[1].fade + }); + let (Some(r), Some(s)) = (el.rest(), el.pose_at(st)) else { + continue; + }; let (ra, sa) = ((r.fade >> 24) & 0xff, (s.fade >> 24) & 0xff); if plateau { // ⚠️ The first version of this control compared EVERY plateau @@ -60,9 +79,11 @@ fn main() { // held interval actually CONTAINS the settle instant. ctl_n += 1; let covers = el.keyframes.windows(2).any(|w| { - let held = w[0].x == w[1].x && w[0].y == w[1].y + let held = w[0].x == w[1].x + && w[0].y == w[1].y && w[0].scale_x == w[1].scale_x - && w[0].scale_y == w[1].scale_y && w[0].fade == w[1].fade; + && w[0].scale_y == w[1].scale_y + && w[0].fade == w[1].fade; match (w[0].time, w[1].time) { (Some(a), Some(bb)) => held && a <= st && st <= bb, _ => false, @@ -70,13 +91,23 @@ fn main() { }); if covers { ctl_cov += 1; - if ra == sa && r.x == s.x && r.y == s.y - && r.scale_x == s.scale_x && r.scale_y == s.scale_y { ctl_agree += 1 } + if ra == sa + && r.x == s.x + && r.y == s.y + && r.scale_x == s.scale_x + && r.scale_y == s.scale_y + { + ctl_agree += 1 + } } } else { fb_n += 1; - if ra > 0 { fb_rest_vis += 1 } - if sa > 0 { fb_settle_vis += 1 } + if ra > 0 { + fb_rest_vis += 1 + } + if sa > 0 { + fb_settle_vis += 1 + } } } } @@ -85,13 +116,19 @@ fn main() { println!("CONTROL — elements where rest() takes the SOUND plateau path:"); println!(" {ctl_n} plateau elements in settling bundles"); println!(" {ctl_cov} of them HOLD ACROSS the settle instant — the fair control"); - println!(" pose_at(settle) agrees with rest() on {ctl_agree} of those ({:.1} %)", - 100.0 * ctl_agree as f64 / ctl_cov.max(1) as f64); + println!( + " pose_at(settle) agrees with rest() on {ctl_agree} of those ({:.1} %)", + 100.0 * ctl_agree as f64 / ctl_cov.max(1) as f64 + ); println!("\nTEST — elements where the unsound dwell fallback decides:"); println!(" {fb_n} elements"); - println!(" rest() returns a VISIBLE pose on {fb_rest_vis} ({:.1} %)", - 100.0 * fb_rest_vis as f64 / fb_n.max(1) as f64); - println!(" pose_at(settle) returns a VISIBLE pose on {fb_settle_vis} ({:.1} %)", - 100.0 * fb_settle_vis as f64 / fb_n.max(1) as f64); + println!( + " rest() returns a VISIBLE pose on {fb_rest_vis} ({:.1} %)", + 100.0 * fb_rest_vis as f64 / fb_n.max(1) as f64 + ); + println!( + " pose_at(settle) returns a VISIBLE pose on {fb_settle_vis} ({:.1} %)", + 100.0 * fb_settle_vis as f64 / fb_n.max(1) as f64 + ); println!("\n--- END (if this line is missing, the run did not finish) ---"); } diff --git a/crates/sylpheed-formats/examples/scale_census.rs b/crates/sylpheed-formats/examples/scale_census.rs index e55a8f94..d074655a 100644 --- a/crates/sylpheed-formats/examples/scale_census.rs +++ b/crates/sylpheed-formats/examples/scale_census.rs @@ -3,8 +3,8 @@ //! `DECISIONS.md` records `ptlogo_eff2` at 125 % as "the single drawn element in //! the whole export at a scale that is not a whole multiple of 100 %". That //! census was over parents only -- leaves were never opened. This opens them. -use sylpheed_formats::{pak, ui_layout}; use std::collections::BTreeMap; +use sylpheed_formats::{pak, ui_layout}; fn main() { let path = std::env::args().nth(1).expect("pak"); @@ -13,7 +13,9 @@ fn main() { let mut leaves_opened = 0usize; 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 }; + let Some(b) = ui_layout::parse_build(&bytes) else { + continue; + }; for el in &b.elements { for k in &el.keyframes { hist.entry((k.scale_x, k.scale_y)) @@ -38,11 +40,21 @@ fn main() { println!("{:>12} {:>7} examples", "scale", "count"); for (k, v) in &hist { let mut ex: Vec<&String> = v.iter().collect(); - ex.sort(); ex.dedup(); + ex.sort(); + ex.dedup(); let odd = k.0 % 100 != 0 || k.1 % 100 != 0; - println!("{}{:>5},{:<5} {:>7} {}", if odd { "* " } else { " " }, - k.0, k.1, v.len(), - ex.iter().take(3).map(|s| s.as_str()).collect::>().join(", ")); + println!( + "{}{:>5},{:<5} {:>7} {}", + if odd { "* " } else { " " }, + k.0, + k.1, + v.len(), + ex.iter() + .take(3) + .map(|s| s.as_str()) + .collect::>() + .join(", ") + ); } println!("\n* = not a whole multiple of 100%"); } diff --git a/crates/sylpheed-formats/examples/settle_midramp_census.rs b/crates/sylpheed-formats/examples/settle_midramp_census.rs index eeb4ac15..a927b47b 100644 --- a/crates/sylpheed-formats/examples/settle_midramp_census.rs +++ b/crates/sylpheed-formats/examples/settle_midramp_census.rs @@ -15,14 +15,16 @@ //! whose two endpoint poses DIFFER — it is interpolating, not held. //! //! cargo run -p sylpheed-formats --example settle_midramp_census -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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 mut paks: Vec = std::fs::read_dir(root.join("dat")).expect("dat/") + 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().is_some_and(|x| x == "pak")).collect(); + .filter(|p| p.extension().is_some_and(|x| x == "pak")) + .collect(); paks.sort(); // buckets by settle-window width let edges = [0u32, 10, 20, 30, 60, u32::MAX]; @@ -31,18 +33,29 @@ fn main() { let mut mid = [0usize; 5]; let mut bundles = [0usize; 5]; for pak in &paks { - let Ok(ar) = PakArchive::open(pak) else { continue }; + let Ok(ar) = PakArchive::open(pak) else { + continue; + }; for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; - let Some((lo, hi)) = b.settle_window() else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; + let Some((lo, hi)) = b.settle_window() else { + continue; + }; let w = hi - lo; - let bi = edges.windows(2).position(|p| w >= p[0] && w < p[1]).unwrap_or(4); + let bi = edges + .windows(2) + .position(|p| w >= p[0] && w < p[1]) + .unwrap_or(4); bundles[bi] += 1; let st = lo + w / 2; for el in &b.elements { let k = &el.keyframes; - if k.len() < 2 { continue } + if k.len() < 2 { + continue; + } els[bi] += 1; // the interval containing the settle instant let mut interpolating = false; @@ -50,26 +63,49 @@ fn main() { if let (Some(t0), Some(t1)) = (pair[0].time, pair[1].time) { if t0 <= st && st <= t1 && t0 != t1 { let same = pair[0].fade == pair[1].fade - && pair[0].x == pair[1].x && pair[0].y == pair[1].y + && pair[0].x == pair[1].x + && pair[0].y == pair[1].y && pair[0].scale_x == pair[1].scale_x && pair[0].scale_y == pair[1].scale_y; - if !same && st != t0 && st != t1 { interpolating = true } + if !same && st != t0 && st != t1 { + interpolating = true + } break; } } } - if interpolating { mid[bi] += 1 } + if interpolating { + mid[bi] += 1 + } } } } - println!("{:8}{:>10}{:>10}{:>12}{:>10}", "window", "bundles", "elements", "mid-ramp", "share"); + println!( + "{:8}{:>10}{:>10}{:>12}{:>10}", + "window", "bundles", "elements", "mid-ramp", "share" + ); for i in 0..5 { - if els[i] == 0 { continue } - println!("{:8}{:>10}{:>10}{:>12}{:>9.1}%", names[i], bundles[i], els[i], mid[i], - 100.0 * mid[i] as f64 / els[i] as f64); + if els[i] == 0 { + continue; + } + println!( + "{:8}{:>10}{:>10}{:>12}{:>9.1}%", + names[i], + bundles[i], + els[i], + mid[i], + 100.0 * mid[i] as f64 / els[i] as f64 + ); } - let te: usize = els.iter().sum(); let tm: usize = mid.iter().sum(); - println!("{:8}{:>10}{:>10}{:>12}{:>9.1}%", "ALL", bundles.iter().sum::(), te, tm, - 100.0 * tm as f64 / te as f64); + let te: usize = els.iter().sum(); + let tm: usize = mid.iter().sum(); + println!( + "{:8}{:>10}{:>10}{:>12}{:>9.1}%", + "ALL", + bundles.iter().sum::(), + te, + tm, + 100.0 * tm as f64 / te as f64 + ); println!("\n--- END (if this line is missing, the run did not finish) ---"); } diff --git a/crates/sylpheed-formats/examples/settle_narrow_rate.rs b/crates/sylpheed-formats/examples/settle_narrow_rate.rs index 89322813..8ff68dee 100644 --- a/crates/sylpheed-formats/examples/settle_narrow_rate.rs +++ b/crates/sylpheed-formats/examples/settle_narrow_rate.rs @@ -8,42 +8,73 @@ //! different and much smaller population. This computes both. //! //! cargo run -p sylpheed-formats --example settle_narrow_rate -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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 mut paks: Vec = std::fs::read_dir(root.join("dat")).expect("dat/") + 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().is_some_and(|x| x == "pak")).collect(); + .filter(|p| p.extension().is_some_and(|x| x == "pak")) + .collect(); paks.sort(); // (with >=2 keyframe times, narrow) for each population - let (mut b2, mut bn) = (0usize, 0usize); // screen builds (is_build) - let (mut c2, mut cn) = (0usize, 0usize); // composable (is_composable) + let (mut b2, mut bn) = (0usize, 0usize); // screen builds (is_build) + let (mut c2, mut cn) = (0usize, 0usize); // composable (is_composable) for pak in &paks { - let Ok(ar) = PakArchive::open(pak) else { continue }; + let Ok(ar) = PakArchive::open(pak) else { + continue; + }; for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; let is_b = ui_layout::is_build(&by); let is_c = ui_layout::is_composable(&by); - if !is_b && !is_c { continue } - let Some(b) = ui_layout::parse_build(&by) else { continue }; - let mut ts: Vec = b.elements.iter() - .flat_map(|el| el.keyframes.iter().filter_map(|k| k.time)).collect(); - ts.sort_unstable(); ts.dedup(); - if ts.len() < 2 { continue } - let narrow = match b.settle_window() { Some((lo, hi)) => hi - lo < 10, None => true }; - if is_b { b2 += 1; if narrow { bn += 1 } } - if is_c { c2 += 1; if narrow { cn += 1 } } + if !is_b && !is_c { + continue; + } + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; + let mut ts: Vec = b + .elements + .iter() + .flat_map(|el| el.keyframes.iter().filter_map(|k| k.time)) + .collect(); + ts.sort_unstable(); + ts.dedup(); + if ts.len() < 2 { + continue; + } + let narrow = match b.settle_window() { + Some((lo, hi)) => hi - lo < 10, + None => true, + }; + if is_b { + b2 += 1; + if narrow { + bn += 1 + } + } + if is_c { + c2 += 1; + if narrow { + cn += 1 + } + } } } println!("population n narrow (<10 u) share"); println!("SCREEN BUILDS (is_build, what `screen render` renders by default)"); - println!(" {b2:5} {bn:9} {:.0} %", - 100.0 * bn as f64 / b2.max(1) as f64); + println!( + " {b2:5} {bn:9} {:.0} %", + 100.0 * bn as f64 / b2.max(1) as f64 + ); println!("COMPOSABLE bundles (is_composable, what --all admits)"); - println!(" {c2:5} {cn:9} {:.0} %", - 100.0 * cn as f64 / c2.max(1) as f64); + println!( + " {c2:5} {cn:9} {:.0} %", + 100.0 * cn as f64 / c2.max(1) as f64 + ); println!("\nui-settle-time.md quotes 731 / 1758 = 42 % over composable bundles."); println!("--- END ---"); } diff --git a/crates/sylpheed-formats/examples/settle_window.rs b/crates/sylpheed-formats/examples/settle_window.rs index 96a5b2c6..e3e4d186 100644 --- a/crates/sylpheed-formats/examples/settle_window.rs +++ b/crates/sylpheed-formats/examples/settle_window.rs @@ -6,24 +6,42 @@ // instead one INSTANT that every element is posed at, and the instant to pick is // inside the longest interval during which no element has a keyframe at all. use sylpheed_formats::{pak, ui_layout}; -fn main(){ - let mut a=std::env::args().skip(1); - let pk=a.next().unwrap(); - let ar=pak::PakArchive::open(&pk).unwrap(); - let only:Option=a.next().and_then(|s|s.parse().ok()); - for (i,e) in ar.entries().iter().enumerate() { - if let Some(o)=only { if o!=i { continue } } - let Ok(by)=ar.read(e) else { continue }; - let Some(b)=ui_layout::parse_build(&by) else { continue }; - if b.elements.len()<2 { continue } - let mut ts:Vec=b.elements.iter().flat_map(|el| - el.keyframes.iter().filter_map(|k|k.time)).collect(); - if ts.len()<2 { continue } - ts.sort_unstable(); ts.dedup(); +fn main() { + let mut a = std::env::args().skip(1); + let pk = a.next().unwrap(); + let ar = pak::PakArchive::open(&pk).unwrap(); + let only: Option = a.next().and_then(|s| s.parse().ok()); + for (i, e) in ar.entries().iter().enumerate() { + if let Some(o) = only { + if o != i { + continue; + } + } + let Ok(by) = ar.read(e) else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; + if b.elements.len() < 2 { + continue; + } + let mut ts: Vec = b + .elements + .iter() + .flat_map(|el| el.keyframes.iter().filter_map(|k| k.time)) + .collect(); + if ts.len() < 2 { + continue; + } + ts.sort_unstable(); + ts.dedup(); // longest gap between consecutive keyframe times - let (mut best,mut lo,mut hi)=(0u32,0u32,0u32); + let (mut best, mut lo, mut hi) = (0u32, 0u32, 0u32); for w in ts.windows(2) { - if w[1]-w[0] > best { best=w[1]-w[0]; lo=w[0]; hi=w[1]; } + if w[1] - w[0] > best { + best = w[1] - w[0]; + lo = w[0]; + hi = w[1]; + } } println!("{:>4} {:<28} times {:>3} span {:>4} settle window [{lo},{hi}] = {best} units ({:.2}s) -> t={}", i, format!("{:08x}",e.name_hash), ts.len(), ts.last().unwrap(), best as f64/60.0, lo+best/2); diff --git a/crates/sylpheed-formats/examples/settle_window_check.rs b/crates/sylpheed-formats/examples/settle_window_check.rs index f7d4a2bc..f7c91532 100644 --- a/crates/sylpheed-formats/examples/settle_window_check.rs +++ b/crates/sylpheed-formats/examples/settle_window_check.rs @@ -2,26 +2,38 @@ //! publisher splash's widest keyframe-free gap as 190 units; `--settle` reports 8. //! One of the two readings is wrong and the file settles it. //! cargo run -p sylpheed-formats --example settle_window_check -- -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::path::PathBuf; +use sylpheed_formats::{pak::PakArchive, ui_layout}; fn main() { - let b: usize = std::env::args().nth(1).unwrap_or("10".into()).parse().unwrap(); + let b: usize = std::env::args() + .nth(1) + .unwrap_or("10".into()) + .parse() + .unwrap(); let root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC")); let ar = PakArchive::open(root.join("dat/GP_TITLE.pak")).expect("GP_TITLE"); let by = ar.read(&ar.entries()[b]).expect("entry"); let build = ui_layout::parse_build(&by).expect("parse"); println!("entry {b}: {} elements", build.elements.len()); for el in &build.elements { - let ts: Vec = el.keyframes.iter() - .map(|k| k.time.map(|v| v.to_string()).unwrap_or("-".into())).collect(); + let ts: Vec = el + .keyframes + .iter() + .map(|k| k.time.map(|v| v.to_string()).unwrap_or("-".into())) + .collect(); println!(" {:26} [{}]", el.name, ts.join(" ")); } - let mut ts: Vec = build.elements.iter() - .flat_map(|e| e.keyframes.iter().filter_map(|k| k.time)).collect(); - ts.sort_unstable(); ts.dedup(); + let mut ts: Vec = build + .elements + .iter() + .flat_map(|e| e.keyframes.iter().filter_map(|k| k.time)) + .collect(); + ts.sort_unstable(); + ts.dedup(); println!("\nunion of all element keyframe times: {ts:?}"); - let gaps: Vec<(u32,u32,u32)> = ts.windows(2).map(|w| (w[1]-w[0], w[0], w[1])).collect(); - let mut g = gaps.clone(); g.sort_by(|a,b| b.0.cmp(&a.0)); + let gaps: Vec<(u32, u32, u32)> = ts.windows(2).map(|w| (w[1] - w[0], w[0], w[1])).collect(); + let mut g = gaps.clone(); + g.sort_by(|a, b| b.0.cmp(&a.0)); println!("widest gaps: {:?}", &g[..g.len().min(4)]); println!("settle_window() reports {:?}", build.settle_window()); } diff --git a/crates/sylpheed-formats/examples/sound_cue_fields.rs b/crates/sylpheed-formats/examples/sound_cue_fields.rs index a50cc8bc..a9c57366 100644 --- a/crates/sylpheed-formats/examples/sound_cue_fields.rs +++ b/crates/sylpheed-formats/examples/sound_cue_fields.rs @@ -16,7 +16,10 @@ use sylpheed_formats::{idxd::IdxdObject, pak::PakArchive}; fn main() { let a: Vec = std::env::args().collect(); - let root = a.get(1).cloned().unwrap_or_else(|| std::env::var("SYLPHEED_DISC").unwrap()); + let root = a + .get(1) + .cloned() + .unwrap_or_else(|| std::env::var("SYLPHEED_DISC").unwrap()); let arc = PakArchive::open(format!("{root}/dat/tables.pak")).unwrap(); // Anything a gain would plausibly be called, plus the audio nouns. @@ -26,15 +29,26 @@ fn main() { for (i, e) in arc.entries().iter().enumerate() { let Ok(b) = arc.read(e) else { continue }; - let Ok(o) = IdxdObject::parse(&b) else { continue }; + let Ok(o) = IdxdObject::parse(&b) else { + continue; + }; let t = o.tokens(); let up: Vec = t.iter().map(|s| s.to_uppercase()).collect(); let is_audio = up.iter().any(|s| { - s.contains("SOUND") || s.contains("BANK_") || s.starts_with("SE_") || s.starts_with("BGM_") + s.contains("SOUND") + || s.contains("BANK_") + || s.starts_with("SE_") + || s.starts_with("BGM_") }); - if !is_audio { continue } + if !is_audio { + continue; + } audio_objs += 1; - println!("audio object #{i}: schema {:08x}, {} tokens", o.schema_hash, t.len()); + println!( + "audio object #{i}: schema {:08x}, {} tokens", + o.schema_hash, + t.len() + ); for (j, tok) in up.iter().enumerate() { if GAINY.iter().any(|g| tok.contains(g)) { gain_hits.push((i, t[j].clone())); @@ -56,10 +70,24 @@ fn main() { let mut ctrl = 0usize; for e in arc.entries() { let Ok(b) = arc.read(e) else { continue }; - let Ok(o) = IdxdObject::parse(&b) else { continue }; - ctrl += o.tokens().iter().filter(|s| s.to_uppercase().contains("SE_UI")).count(); + let Ok(o) = IdxdObject::parse(&b) else { + continue; + }; + ctrl += o + .tokens() + .iter() + .filter(|s| s.to_uppercase().contains("SE_UI")) + .count(); } - println!("\nCONTROL — the same matcher looking for a token known to exist (\"SE_UI\"): {ctrl} hits"); - println!(" {}", if ctrl > 0 { "PASS: the matcher finds tokens that are there" } - else { "FAIL: matcher is broken, the negative above means nothing" }); + println!( + "\nCONTROL — the same matcher looking for a token known to exist (\"SE_UI\"): {ctrl} hits" + ); + println!( + " {}", + if ctrl > 0 { + "PASS: the matcher finds tokens that are there" + } else { + "FAIL: matcher is broken, the negative above means nothing" + } + ); } diff --git a/crates/sylpheed-formats/examples/splash_blend_check.rs b/crates/sylpheed-formats/examples/splash_blend_check.rs index 4b2acd6a..1ce07907 100644 --- a/crates/sylpheed-formats/examples/splash_blend_check.rs +++ b/crates/sylpheed-formats/examples/splash_blend_check.rs @@ -33,8 +33,11 @@ fn main() { println!("entry {entry}: not a build"); continue; }; - println!("\n## GP_TITLE entry {entry} -- {} elements, {} sprites", - b.elements.len(), b.sprites.len()); + println!( + "\n## GP_TITLE entry {entry} -- {} elements, {} sprites", + b.elements.len(), + b.sprites.len() + ); // Every sprite the bundle carries, not only those a top-level element // names: a focused variant is reached through `focus_link` and would @@ -43,13 +46,24 @@ fn main() { names.sort(); for n in names { match ui_layout::blend_additive_by_name(&b, &by, n) { - Some(true) => { additive += 1; - println!(" {n:<26} word04=0x{:08X} ADDITIVE", - ui_layout::header_word_04_by_name(&b, &by, n).unwrap()); } - Some(false) => { alpha_over += 1; - println!(" {n:<26} word04=0x{:08X} alpha-over", - ui_layout::header_word_04_by_name(&b, &by, n).unwrap()); } - None => { no_header += 1; println!(" {n:<26} (no T8aD header)"); } + Some(true) => { + additive += 1; + println!( + " {n:<26} word04=0x{:08X} ADDITIVE", + ui_layout::header_word_04_by_name(&b, &by, n).unwrap() + ); + } + Some(false) => { + alpha_over += 1; + println!( + " {n:<26} word04=0x{:08X} alpha-over", + ui_layout::header_word_04_by_name(&b, &by, n).unwrap() + ); + } + None => { + no_header += 1; + println!(" {n:<26} (no T8aD header)"); + } } } } @@ -59,8 +73,10 @@ fn main() { if additive == 0 { println!("PREDICTION HELD -- the bit agrees with the capture on both splashes"); } else { - println!("PREDICTION FAILED -- {additive} sprite(s) claim additive, \ - the capture submits none"); + println!( + "PREDICTION FAILED -- {additive} sprite(s) claim additive, \ + the capture submits none" + ); } // Control: this harness must be able to REPORT additive, or "additive=0" @@ -68,8 +84,14 @@ fn main() { // the oracle measures as mixed. let by6 = ar.read(&ar.entries()[6]).expect("entry 6"); let b6 = ui_layout::parse_build(&by6).expect("build 6"); - let c_add = b6.sprites.keys() - .filter(|n| ui_layout::blend_additive_by_name(&b6, &by6, n) == Some(true)).count(); - println!("control -- entry 6 through the SAME code path reports additive={c_add} \ - (must be > 0): {}", if c_add > 0 { "PASS" } else { "FAIL" }); + let c_add = b6 + .sprites + .keys() + .filter(|n| ui_layout::blend_additive_by_name(&b6, &by6, n) == Some(true)) + .count(); + println!( + "control -- entry 6 through the SAME code path reports additive={c_add} \ + (must be > 0): {}", + if c_add > 0 { "PASS" } else { "FAIL" } + ); } diff --git a/crates/sylpheed-formats/examples/splash_quad_names.rs b/crates/sylpheed-formats/examples/splash_quad_names.rs index 830ffca9..1a3520b4 100644 --- a/crates/sylpheed-formats/examples/splash_quad_names.rs +++ b/crates/sylpheed-formats/examples/splash_quad_names.rs @@ -26,13 +26,13 @@ use sylpheed_formats::{pak::PakArchive, t8ad, ui_layout}; const CAPTURED: &[(&str, f64, f64, f64, f64, usize)] = &[ // name, x0, x1, y0, y1, draws submitted in ("Q0", -0.520, 0.520, -0.100, 0.080, 111), - ("Q1", -0.390, 0.390, 0.350, 0.550, 87), - ("Q2", -0.190, 0.190, -0.120, 0.120, 87), + ("Q1", -0.390, 0.390, 0.350, 0.550, 87), + ("Q2", -0.190, 0.190, -0.120, 0.120, 87), ("Q3", -0.300, 0.300, -0.620, -0.250, 87), - ("Q4", -0.410, 0.410, 0.320, 0.570, 21), - ("Q5", -0.200, 0.210, -0.150, 0.150, 21), + ("Q4", -0.410, 0.410, 0.320, 0.570, 21), + ("Q5", -0.200, 0.210, -0.150, 0.150, 21), ("Q6", -0.320, 0.310, -0.650, -0.220, 21), - ("Q7", -0.530, 0.540, -0.130, 0.120, 8), + ("Q7", -0.530, 0.540, -0.130, 0.120, 8), ]; fn main() { @@ -45,12 +45,20 @@ fn main() { let by = ar.read(&ar.entries()[entry]).expect("entry"); let b = ui_layout::parse_build(&by).expect("build"); for el in &b.elements { - let Some(sprite) = el.sprite.as_ref() else { continue }; - let Some(&(off, size)) = b.sprites.get(sprite) else { continue }; - let Some(img) = t8ad::parse(&by[off..off + size]) else { continue }; + let Some(sprite) = el.sprite.as_ref() else { + continue; + }; + let Some(&(off, size)) = b.sprites.get(sprite) else { + continue; + }; + let Some(img) = t8ad::parse(&by[off..off + size]) else { + continue; + }; // Declared placement is constant across every keyframe on these // eight elements, so any keyframe gives the same rect; take the first. - let Some(kf) = el.keyframes.first() else { continue }; + let Some(kf) = el.keyframes.first() else { + continue; + }; let (x, y) = (kf.x as f64, kf.y as f64); let (w, h) = (img.width as f64, img.height as f64); predicted.push(( @@ -64,15 +72,20 @@ fn main() { } let dist = |p: &(String, f64, f64, f64, f64), c: &(&str, f64, f64, f64, f64, usize)| { - (p.1 - c.1).abs().max((p.2 - c.2).abs()) - .max((p.3 - c.3).abs()).max((p.4 - c.4).abs()) + (p.1 - c.1) + .abs() + .max((p.2 - c.2).abs()) + .max((p.3 - c.3).abs()) + .max((p.4 - c.4).abs()) }; // The capture rounds to 0.01, so a correct prediction must land inside half // a step plus the pixel grid: 0.01 NDC is 6.4 px in x, 3.6 px in y. const TOL: f64 = 0.010; - println!("{:<26} {:<4} {:>8} {:>9} {:>7} {}", - "sprite (disc)", "quad", "max|d|", "runner-up", "draws", "verdict"); + println!( + "{:<26} {:<4} {:>8} {:>9} {:>7} {}", + "sprite (disc)", "quad", "max|d|", "runner-up", "draws", "verdict" + ); let (mut ok, mut bad) = (0, 0); let mut used: Vec<&str> = Vec::new(); for p in &predicted { @@ -82,15 +95,31 @@ fn main() { let (d0, best) = (ds[0].0, ds[0].1); let d1 = ds[1].0; let good = d0 <= TOL && !used.contains(&best.0); - if good { ok += 1; used.push(best.0) } else { bad += 1 } - println!("{:<26} {:<4} {:>8.4} {:>9.4} {:>7} {}", - p.0, best.0, d0, d1, best.5, - if good { "OK" } else { "MISMATCH" }); + if good { + ok += 1; + used.push(best.0) + } else { + bad += 1 + } + println!( + "{:<26} {:<4} {:>8.4} {:>9.4} {:>7} {}", + p.0, + best.0, + d0, + d1, + best.5, + if good { "OK" } else { "MISMATCH" } + ); } - println!("\n{ok} named, {bad} unmatched, of {} predicted / {} captured", - predicted.len(), CAPTURED.len()); - println!("CONTROL: every runner-up above must be far outside the {TOL} tolerance; \ - if it is not, the rects are not separable and the naming is luck."); + println!( + "\n{ok} named, {bad} unmatched, of {} predicted / {} captured", + predicted.len(), + CAPTURED.len() + ); + println!( + "CONTROL: every runner-up above must be far outside the {TOL} tolerance; \ + if it is not, the rects are not separable and the naming is luck." + ); } /// The second question this data answers, appended as its own pass: what IS an @@ -103,39 +132,63 @@ fn eff_is_a_concentric_outset_not_a_scale() { let ar = PakArchive::open(root.join("dat/GP_TITLE.pak")).expect("GP_TITLE"); for (entry, pairs) in [ (10usize, &[("palogo_sqex", "palogo_sqex_eff")][..]), - (11, &[("palogo_gamearts", "palogo_gamearts_eff"), - ("palogo_seta", "palogo_seta_eff"), - ("palogo_anima", "palogo_anima_eff")][..]), + ( + 11, + &[ + ("palogo_gamearts", "palogo_gamearts_eff"), + ("palogo_seta", "palogo_seta_eff"), + ("palogo_anima", "palogo_anima_eff"), + ][..], + ), ] { let by = ar.read(&ar.entries()[entry]).expect("entry"); let b = ui_layout::parse_build(&by).expect("build"); for (logo, eff) in pairs { let g = |n: &str| { - let el = b.elements.iter() - .find(|e| e.sprite.as_deref() == Some(&format!("{n}.t32"))).unwrap(); + let el = b + .elements + .iter() + .find(|e| e.sprite.as_deref() == Some(&format!("{n}.t32"))) + .unwrap(); let &(off, size) = b.sprites.get(&format!("{n}.t32")).unwrap(); let img = t8ad::parse(&by[off..off + size]).unwrap(); let kf = el.keyframes.first().unwrap(); - (kf.x as f64, kf.y as f64, img.width as f64, img.height as f64) + ( + kf.x as f64, + kf.y as f64, + img.width as f64, + img.height as f64, + ) }; let (lx, ly, lw, lh) = g(logo); let (ex, ey, ew, eh) = g(eff); // concentric? let dcx = (ex + ew / 2.0) - (lx + lw / 2.0); let dcy = (ey + eh / 2.0) - (ly + lh / 2.0); - assert!(dcx.abs() <= 2.0 && dcy.abs() <= 2.0, - "{eff}: centres differ by ({dcx},{dcy}) -- not concentric"); + assert!( + dcx.abs() <= 2.0 && dcy.abs() <= 2.0, + "{eff}: centres differ by ({dcx},{dcy}) -- not concentric" + ); // uniform outset, NOT a uniform scale? let (ox, oy) = ((ew - lw) / 2.0, (eh - lh) / 2.0); - assert!((ox - oy).abs() <= 2.0, - "{eff}: outset ({ox},{oy}) is not uniform"); - assert!(ox >= 8.0 && ox <= 12.0, "{eff}: outset {ox} px outside 8..12"); + assert!( + (ox - oy).abs() <= 2.0, + "{eff}: outset ({ox},{oy}) is not uniform" + ); + assert!( + ox >= 8.0 && ox <= 12.0, + "{eff}: outset {ox} px outside 8..12" + ); // and the scale reading it displaces let (sx, sy) = (ew / lw, eh / lh); - assert!((sx - sy).abs() > 0.05, - "{eff}: scales {sx:.3}/{sy:.3} ARE uniform -- 'scaled larger' would stand"); - println!("{eff:<26} outset {ox:.0}x{oy:.0} px, centre off by \ - ({dcx:.1},{dcy:.1}), scale {sx:.3}/{sy:.3} (NOT uniform)"); + assert!( + (sx - sy).abs() > 0.05, + "{eff}: scales {sx:.3}/{sy:.3} ARE uniform -- 'scaled larger' would stand" + ); + println!( + "{eff:<26} outset {ox:.0}x{oy:.0} px, centre off by \ + ({dcx:.1},{dcy:.1}), scale {sx:.3}/{sy:.3} (NOT uniform)" + ); } } } diff --git a/crates/sylpheed-formats/examples/sprite_dims.rs b/crates/sylpheed-formats/examples/sprite_dims.rs index e7ade7d1..afecec0d 100644 --- a/crates/sylpheed-formats/examples/sprite_dims.rs +++ b/crates/sylpheed-formats/examples/sprite_dims.rs @@ -1,12 +1,21 @@ -use sylpheed_formats::{pak, ui_layout, t8ad}; -fn main(){ - let mut a=std::env::args().skip(1); - let pk=a.next().unwrap(); let i:usize=a.next().unwrap().parse().unwrap(); - let ar=pak::PakArchive::open(pk).unwrap(); - let by=ar.read(&ar.entries()[i]).unwrap(); - let b=ui_layout::parse_build(&by).unwrap(); - let mut v:Vec<(String,u32,u32)>=b.sprites.iter().filter_map(|(n,&(o,s))|{ - let im=t8ad::parse(&by[o..o+s])?; Some((n.clone(),im.width,im.height))}).collect(); +use sylpheed_formats::{pak, t8ad, ui_layout}; +fn main() { + let mut a = std::env::args().skip(1); + let pk = a.next().unwrap(); + let i: usize = a.next().unwrap().parse().unwrap(); + let ar = pak::PakArchive::open(pk).unwrap(); + let by = ar.read(&ar.entries()[i]).unwrap(); + let b = ui_layout::parse_build(&by).unwrap(); + let mut v: Vec<(String, u32, u32)> = b + .sprites + .iter() + .filter_map(|(n, &(o, s))| { + let im = t8ad::parse(&by[o..o + s])?; + Some((n.clone(), im.width, im.height)) + }) + .collect(); v.sort(); - for (n,w,h) in v { println!(" {w:>5} x {h:<5} {n}"); } + for (n, w, h) in v { + println!(" {w:>5} x {h:<5} {n}"); + } } diff --git a/crates/sylpheed-formats/examples/sprite_of.rs b/crates/sylpheed-formats/examples/sprite_of.rs index 4cc71730..f8d344fd 100644 --- a/crates/sylpheed-formats/examples/sprite_of.rs +++ b/crates/sylpheed-formats/examples/sprite_of.rs @@ -1,9 +1,17 @@ use sylpheed_formats::{pak, ui_layout}; -fn main(){ - let ar=pak::PakArchive::open(std::env::args().nth(1).unwrap()).unwrap(); - let b=ui_layout::parse_build(&ar.read(&ar.entries()[4]).unwrap()).unwrap(); - for el in b.elements.iter().filter(|e| e.name.contains("ptloop")||e.name.contains("ptbtn")) { - println!("{:<18} sprite={:?} has_leaf={}", el.name, el.sprite, - b.records.contains_key(&el.name)); +fn main() { + let ar = pak::PakArchive::open(std::env::args().nth(1).unwrap()).unwrap(); + let b = ui_layout::parse_build(&ar.read(&ar.entries()[4]).unwrap()).unwrap(); + for el in b + .elements + .iter() + .filter(|e| e.name.contains("ptloop") || e.name.contains("ptbtn")) + { + println!( + "{:<18} sprite={:?} has_leaf={}", + el.name, + el.sprite, + b.records.contains_key(&el.name) + ); } } diff --git a/crates/sylpheed-formats/examples/static_cycle_inert.rs b/crates/sylpheed-formats/examples/static_cycle_inert.rs index c74c44f8..ccf86976 100644 --- a/crates/sylpheed-formats/examples/static_cycle_inert.rs +++ b/crates/sylpheed-formats/examples/static_cycle_inert.rs @@ -10,8 +10,8 @@ //! holding something the disc says animates. //! //! cargo run -p sylpheed-formats --example static_cycle_inert -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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")); @@ -19,13 +19,17 @@ fn main() { let (mut total, mut static_cycle, mut multipose) = (0usize, 0usize, 0usize); for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; for (name, (off, size)) in &b.records { let rec = &by[*off..(*off + *size).min(by.len())]; if rec.len() < 0x10 || &rec[0..4] != b"RATC" { continue; } - let Some(leaf) = ui_layout::parse_build(rec) else { continue }; + let Some(leaf) = ui_layout::parse_build(rec) else { + continue; + }; total += 1; let times: Vec = leaf .elements @@ -37,10 +41,17 @@ fn main() { continue; } static_cycle += 1; - let worst = leaf.elements.iter().map(|el| el.keyframes.len()).max().unwrap_or(0); + let worst = leaf + .elements + .iter() + .map(|el| el.keyframes.len()) + .max() + .unwrap_or(0); if worst > 1 { multipose += 1; - println!(" 🔴 {name} declares {cycle} units and has an element with {worst} poses"); + println!( + " 🔴 {name} declares {cycle} units and has an element with {worst} poses" + ); } else if name.starts_with("ptbtn1") { println!(" {name:16} declares {cycle} units, max poses/element {worst}"); } diff --git a/crates/sylpheed-formats/examples/static_record_census.rs b/crates/sylpheed-formats/examples/static_record_census.rs index de76cf0c..94b16608 100644 --- a/crates/sylpheed-formats/examples/static_record_census.rs +++ b/crates/sylpheed-formats/examples/static_record_census.rs @@ -6,8 +6,8 @@ //! well-formed and "not exact" is a real answer. This checks it. //! //! cargo run -p sylpheed-formats --example static_record_census -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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")); @@ -19,16 +19,22 @@ fn main() { paks.sort(); let (mut untimed, mut all_zero, mut nonzero) = (0usize, 0usize, 0usize); for p in &paks { - let Ok(ar) = PakArchive::open(p) else { continue }; + let Ok(ar) = PakArchive::open(p) else { + continue; + }; for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; for (_, (off, size)) in &b.records { let rec = &by[*off..(*off + *size).min(by.len())]; if rec.len() < 0x10 || &rec[0..4] != b"RATC" { continue; } - let Some(leaf) = ui_layout::parse_build(rec) else { continue }; + let Some(leaf) = ui_layout::parse_build(rec) else { + continue; + }; let times: Vec = leaf .elements .iter() @@ -44,7 +50,10 @@ fn main() { } } } - println!("nested RATC records that parse: {}", untimed + all_zero + nonzero); + println!( + "nested RATC records that parse: {}", + untimed + all_zero + nonzero + ); println!(" NO timed keyframe at all : {untimed}"); println!(" timed, every pose at t == 0 : {all_zero}"); println!(" timed, largest t > 0 : {nonzero}"); diff --git a/crates/sylpheed-formats/examples/sweep_leaf_ramp.rs b/crates/sylpheed-formats/examples/sweep_leaf_ramp.rs index 0951bc5e..775c292e 100644 --- a/crates/sylpheed-formats/examples/sweep_leaf_ramp.rs +++ b/crates/sylpheed-formats/examples/sweep_leaf_ramp.rs @@ -8,21 +8,34 @@ //! whole answer. //! //! cargo run -p sylpheed-formats --example sweep_leaf_ramp -- GP_TITLE 5 6 4 -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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(); - let pak = argv.iter().find(|a| a.parse::().is_err()) - .cloned().unwrap_or_else(|| "GP_TITLE".to_string()); + let pak = argv + .iter() + .find(|a| a.parse::().is_err()) + .cloned() + .unwrap_or_else(|| "GP_TITLE".to_string()); let ar = PakArchive::open(root.join(format!("dat/{pak}.pak"))).expect("pak"); let builds: Vec = argv.iter().filter_map(|a| a.parse().ok()).collect(); - for e in if builds.is_empty() { vec![5usize, 6, 4] } else { builds } { - let Ok(by) = ar.read(&ar.entries()[e]) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; + for e in if builds.is_empty() { + vec![5usize, 6, 4] + } else { + builds + } { + let Ok(by) = ar.read(&ar.entries()[e]) else { + continue; + }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; for name in ["ptloop01.rat", "ptloop02.rat"] { - let Some(&(lo, ls)) = b.records.get(name) else { continue }; + let Some(&(lo, ls)) = b.records.get(name) else { + continue; + }; let leaf_bytes = &by[lo..(lo + ls).min(by.len())]; println!("=== {pak} entry {e} — {name} (leaf {ls} bytes) ==="); if let Some(loop_units) = ui_layout::loop_length_units(leaf_bytes) { diff --git a/crates/sylpheed-formats/examples/t8ad_header_compare.rs b/crates/sylpheed-formats/examples/t8ad_header_compare.rs index 3caa8c99..edea2947 100644 --- a/crates/sylpheed-formats/examples/t8ad_header_compare.rs +++ b/crates/sylpheed-formats/examples/t8ad_header_compare.rs @@ -8,8 +8,8 @@ //! sprites the port measures as ordinary. //! //! cargo run -p sylpheed-formats --example t8ad_header_compare -use sylpheed_formats::{pak::PakArchive, ui_layout}; 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")); @@ -23,26 +23,51 @@ fn main() { for n in names { let (off, size) = b.sprites[n]; let s = &by[off..(off + size).min(by.len())]; - if s.len() < 48 { continue } + if s.len() < 48 { + continue; + } let ws: Vec = (0..12) - .map(|k| u32::from_be_bytes([s[k*4], s[k*4+1], s[k*4+2], s[k*4+3]])) + .map(|k| u32::from_be_bytes([s[k * 4], s[k * 4 + 1], s[k * 4 + 2], s[k * 4 + 3]])) .collect(); - let mark = if n.starts_with("ptframe") { " <- FRAME" } else { "" }; - println!("{n:<20} {size:>8} {}{mark}", - ws.iter().map(|v| format!("{v:08X}")).collect::>().join(" ")); + let mark = if n.starts_with("ptframe") { + " <- FRAME" + } else { + "" + }; + println!( + "{n:<20} {size:>8} {}{mark}", + ws.iter() + .map(|v| format!("{v:08X}")) + .collect::>() + .join(" ") + ); rows.push((n.clone(), ws)); } // which words take a value the two frames share and nobody else does? - let fr: Vec<&(String, Vec)> = rows.iter().filter(|(n, _)| n.starts_with("ptframe")).collect(); + let fr: Vec<&(String, Vec)> = rows + .iter() + .filter(|(n, _)| n.starts_with("ptframe")) + .collect(); if fr.len() == 2 { println!("\nwords where BOTH frames agree and no other sprite has that value:"); let mut any = false; for w in 0..12 { - let a = fr[0].1[w]; let bb = fr[1].1[w]; - if a != bb { continue } - if rows.iter().any(|(n, v)| !n.starts_with("ptframe") && v[w] == a) { continue } - println!(" word {w} (+0x{:02X}) = {a:08X}", w * 4); any = true; + let a = fr[0].1[w]; + let bb = fr[1].1[w]; + if a != bb { + continue; + } + if rows + .iter() + .any(|(n, v)| !n.starts_with("ptframe") && v[w] == a) + { + continue; + } + println!(" word {w} (+0x{:02X}) = {a:08X}", w * 4); + any = true; + } + if !any { + println!(" NONE"); } - if !any { println!(" NONE"); } } } diff --git a/crates/sylpheed-formats/examples/t8ad_word8_census.rs b/crates/sylpheed-formats/examples/t8ad_word8_census.rs index 9bfdb466..2e68b464 100644 --- a/crates/sylpheed-formats/examples/t8ad_word8_census.rs +++ b/crates/sylpheed-formats/examples/t8ad_word8_census.rs @@ -6,9 +6,9 @@ //! is not frame-specific and not a mode. //! //! cargo run -p sylpheed-formats --example t8ad_word8_census -use sylpheed_formats::{pak::PakArchive, ui_layout}; use std::collections::BTreeMap; 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")); @@ -22,29 +22,51 @@ fn main() { let mut frames_like: BTreeMap = BTreeMap::new(); let mut examples: BTreeMap> = BTreeMap::new(); for p in &paks { - let Ok(ar) = PakArchive::open(p) else { continue }; + let Ok(ar) = PakArchive::open(p) else { + continue; + }; for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; for (n, (off, size)) in &b.sprites { let s = &by[*off..(*off + *size).min(by.len())]; - if s.len() < 48 { continue } + if s.len() < 48 { + continue; + } let w = u32::from_be_bytes([s[8], s[9], s[10], s[11]]); *hist.entry(w).or_default() += 1; - if n.contains("frame") { *frames_like.entry(w).or_default() += 1 } + if n.contains("frame") { + *frames_like.entry(w).or_default() += 1 + } let ex = examples.entry(w).or_default(); - if ex.len() < 3 && !ex.contains(n) { ex.push(n.clone()) } + if ex.len() < 3 && !ex.contains(n) { + ex.push(n.clone()) + } } } } let total: usize = hist.values().sum(); - println!("{total} sprites disc-wide; distinct +0x08 values: {}\n", hist.len()); - println!("{:>10} {:>8} {:>10} examples", "value", "count", "of which 'frame'"); + println!( + "{total} sprites disc-wide; distinct +0x08 values: {}\n", + hist.len() + ); + println!( + "{:>10} {:>8} {:>10} examples", + "value", "count", "of which 'frame'" + ); for (v, c) in hist.iter().filter(|(_, c)| **c >= 20) { - println!("{:>#10x} {c:>8} {:>10} {}", v, frames_like.get(v).copied().unwrap_or(0), - examples[v].join(", ")); + println!( + "{:>#10x} {c:>8} {:>10} {}", + v, + frames_like.get(v).copied().unwrap_or(0), + examples[v].join(", ") + ); } - println!("\n0x8050 specifically: {} sprites, {} of them named *frame*", - hist.get(&0x8050).copied().unwrap_or(0), - frames_like.get(&0x8050).copied().unwrap_or(0)); + println!( + "\n0x8050 specifically: {} sprites, {} of them named *frame*", + hist.get(&0x8050).copied().unwrap_or(0), + frames_like.get(&0x8050).copied().unwrap_or(0) + ); } diff --git a/crates/sylpheed-formats/examples/tie_break_pixel_cost.rs b/crates/sylpheed-formats/examples/tie_break_pixel_cost.rs index 4007960d..fc85c0f3 100644 --- a/crates/sylpheed-formats/examples/tie_break_pixel_cost.rs +++ b/crates/sylpheed-formats/examples/tie_break_pixel_cost.rs @@ -138,7 +138,11 @@ fn cases() -> Vec { ] } -fn tied_overlapping_pairs(build: &UiBuild, bytes: &[u8], at: Option) -> Vec<(usize, usize, u32)> { +fn tied_overlapping_pairs( + build: &UiBuild, + bytes: &[u8], + at: Option, +) -> Vec<(usize, usize, u32)> { let keys: Vec = build .elements .iter() diff --git a/crates/sylpheed-formats/examples/tie_cost_over_time.rs b/crates/sylpheed-formats/examples/tie_cost_over_time.rs index 78694e77..fc328ab4 100644 --- a/crates/sylpheed-formats/examples/tie_cost_over_time.rs +++ b/crates/sylpheed-formats/examples/tie_cost_over_time.rs @@ -19,7 +19,9 @@ fn rect(e: &ui_layout::Element, t: u32) -> Option<(i32, i32, i32, i32)> { return None; } let (w, h) = ((e.pivot_x * 2) as i32, (e.pivot_y * 2) as i32); - if w == 0 || h == 0 { return None; } + if w == 0 || h == 0 { + return None; + } Some((kf.x, kf.y, w, h)) } @@ -27,50 +29,92 @@ fn live_pairs(b: &UiBuild, bytes: &[u8], keys: &[u32], t: u32) -> usize { let mut n = 0; for a in 0..keys.len() { for c in (a + 1)..keys.len() { - if keys[a] != keys[c] || keys[a] == u32::MAX { continue } - let (Some(ra), Some(rb)) = (rect(&b.elements[a], t), rect(&b.elements[c], t)) else { continue }; + if keys[a] != keys[c] || keys[a] == u32::MAX { + continue; + } + let (Some(ra), Some(rb)) = (rect(&b.elements[a], t), rect(&b.elements[c], t)) else { + continue; + }; if (ra.0 + ra.2).min(rb.0 + rb.2) - ra.0.max(rb.0) > 0 - && (ra.1 + ra.3).min(rb.1 + rb.3) - ra.1.max(rb.1) > 0 { n += 1 } + && (ra.1 + ra.3).min(rb.1 + rb.3) - ra.1.max(rb.1) > 0 + { + n += 1 + } } } n } fn main() { - let path = std::env::args().nth(1).expect("usage: tie_cost_over_time "); + let path = std::env::args() + .nth(1) + .expect("usage: tie_cost_over_time "); let ar = pak::PakArchive::open(&path).expect("open pak"); println!("# tied pairs that could cost a pixel, over time — {path}\n"); for (i, e) in ar.entries().to_vec().iter().enumerate() { let Ok(by) = ar.read(e) else { continue }; - let Some(b) = ui_layout::parse_build(&by) else { continue }; - let keys: Vec = b.elements.iter() - .map(|el| ui_layout::sprite_layer_key(&b, &by, el).unwrap_or(u32::MAX)).collect(); - let mut ts: Vec = b.elements.iter() - .flat_map(|el| el.keyframes.iter().filter_map(|k| k.time)).collect(); - ts.sort_unstable(); ts.dedup(); - if ts.len() < 2 { continue } + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; + let keys: Vec = b + .elements + .iter() + .map(|el| ui_layout::sprite_layer_key(&b, &by, el).unwrap_or(u32::MAX)) + .collect(); + let mut ts: Vec = b + .elements + .iter() + .flat_map(|el| el.keyframes.iter().filter_map(|k| k.time)) + .collect(); + ts.sort_unstable(); + ts.dedup(); + if ts.len() < 2 { + continue; + } let last = *ts.last().unwrap(); // sample every keyframe time AND every midpoint between them let mut samples: Vec = ts.clone(); - for w in ts.windows(2) { samples.push(w[0] + (w[1] - w[0]) / 2); } - samples.sort_unstable(); samples.dedup(); - let counts: Vec<(u32, usize)> = - samples.iter().map(|&t| (t, live_pairs(&b, &by, &keys, t))).collect(); + for w in ts.windows(2) { + samples.push(w[0] + (w[1] - w[0]) / 2); + } + samples.sort_unstable(); + samples.dedup(); + let counts: Vec<(u32, usize)> = samples + .iter() + .map(|&t| (t, live_pairs(&b, &by, &keys, t))) + .collect(); let peak = counts.iter().map(|&(_, n)| n).max().unwrap_or(0); - if peak == 0 { continue } - let Some((lo, hi)) = b.settle_window() else { continue }; + if peak == 0 { + continue; + } + let Some((lo, hi)) = b.settle_window() else { + continue; + }; let st = b.settle_time().unwrap(); let at_settle = live_pairs(&b, &by, &keys, st); // the whole plateau, not just its midpoint - let plateau: Vec = - (lo..=hi).step_by(((hi - lo).max(1) / 8).max(1) as usize) - .map(|t| live_pairs(&b, &by, &keys, t)).collect(); + let plateau: Vec = (lo..=hi) + .step_by(((hi - lo).max(1) / 8).max(1) as usize) + .map(|t| live_pairs(&b, &by, &keys, t)) + .collect(); let pmax = plateau.iter().copied().max().unwrap_or(0); - println!("entry {i:2} peak {peak} live pair(s) over t=0..{last} \ -settle window [{lo},{hi}] at t={st}: {at_settle} ACROSS THE WHOLE WINDOW: max {pmax}"); - let busy: Vec = counts.iter().filter(|&&(_, n)| n > 0) - .map(|&(t, n)| format!("t{t}:{n}")).collect(); - if busy.len() <= 24 { println!(" live only at {}", busy.join(" ")); } - else { println!(" live at {} of {} sampled instants", busy.len(), counts.len()); } + println!( + "entry {i:2} peak {peak} live pair(s) over t=0..{last} \ +settle window [{lo},{hi}] at t={st}: {at_settle} ACROSS THE WHOLE WINDOW: max {pmax}" + ); + let busy: Vec = counts + .iter() + .filter(|&&(_, n)| n > 0) + .map(|&(t, n)| format!("t{t}:{n}")) + .collect(); + if busy.len() <= 24 { + println!(" live only at {}", busy.join(" ")); + } else { + println!( + " live at {} of {} sampled instants", + busy.len(), + counts.len() + ); + } } } diff --git a/crates/sylpheed-formats/examples/voice_region_cap_sweep.rs b/crates/sylpheed-formats/examples/voice_region_cap_sweep.rs index 930096fc..4730a0bb 100644 --- a/crates/sylpheed-formats/examples/voice_region_cap_sweep.rs +++ b/crates/sylpheed-formats/examples/voice_region_cap_sweep.rs @@ -23,13 +23,21 @@ fn main() { let src = DirectorySource::new(std::path::PathBuf::from(&disc)); let code = VoiceLang::English.code_pub(); let tpak = src.open_pak("dat/tables.pak").expect("tables.pak"); - let manifest = tpak.entries().iter() + let manifest = tpak + .entries() + .iter() .find_map(|e| tpak.read(e).ok().filter(|b| movie_manifest::is_manifest(b))) .expect("manifest"); let marker = format!("{code}\\Movie\\VOICE_ADV.slb"); - let registry = tpak.entries().iter().find_map(|e| { - tpak.read(e).ok().filter(|b| b.windows(marker.len()).any(|w| w == marker.as_bytes())) - }).expect("registry"); + let registry = tpak + .entries() + .iter() + .find_map(|e| { + tpak.read(e) + .ok() + .filter(|b| b.windows(marker.len()).any(|w| w == marker.as_bytes())) + }) + .expect("registry"); let ids = movie_voice::registry_voice_ids(®istry); let stoc = src.read_file("dat/sound.pak").expect("sound.pak"); let entries = PakArchive::parse_toc(&stoc).expect("toc"); @@ -37,23 +45,47 @@ fn main() { let (mut same, mut grew, mut extra, mut skip) = (0, 0, 0, 0); for m in movie_manifest::parse(&manifest) { let movie = m.movie; - let Some(token) = movie_manifest::voice_token(&manifest, &movie) else { skip += 1; continue }; - let Some(&id) = ids.get(&token) else { skip += 1; continue }; - let Some(anchor) = ["Movie","etc","Voice"].iter().find_map(|dir| { + let Some(token) = movie_manifest::voice_token(&manifest, &movie) else { + skip += 1; + continue; + }; + let Some(&id) = ids.get(&token) else { + skip += 1; + continue; + }; + let Some(anchor) = ["Movie", "etc", "Voice"].iter().find_map(|dir| { let h = name_hash(&format!("{code}\\{dir}\\{token}.slb")); - entries.binary_search_by_key(&h, |e| e.name_hash).ok().map(|i| entries[i].offset as u64) - }) else { skip += 1; continue }; - let win_start = anchor.saturating_sub(2*1024*1024) & !3; - let Ok(window) = src.read_segment_range("dat/sound", win_start, 8*1024*1024) else { skip += 1; continue }; - let Some(end_local) = movie_voice::find_descriptor(&window, id) else { skip += 1; continue }; + entries + .binary_search_by_key(&h, |e| e.name_hash) + .ok() + .map(|i| entries[i].offset as u64) + }) else { + skip += 1; + continue; + }; + let win_start = anchor.saturating_sub(2 * 1024 * 1024) & !3; + let Ok(window) = src.read_segment_range("dat/sound", win_start, 8 * 1024 * 1024) else { + skip += 1; + continue; + }; + let Some(end_local) = movie_voice::find_descriptor(&window, id) else { + skip += 1; + continue; + }; let end = win_start + end_local as u64; let cand = movie_voice::find_descriptor(&window, id.wrapping_sub(1)) .or_else(|| movie_voice::find_descriptor_before(&window, end_local)) .map(|o| win_start + o as u64) .filter(|&s| s < end); let today = cand.filter(|&s| end - s < 1_500_000).unwrap_or(anchor); - let Some(proposed) = cand else { skip += 1; continue }; - if today == proposed { same += 1; continue } + let Some(proposed) = cand else { + skip += 1; + continue; + }; + if today == proposed { + same += 1; + continue; + } let sizes = |s: u64| -> Vec { src.read_segment_range("dat/sound", s, (end - s) as usize) .map(|b| slb::to_xma_riffs(&b).iter().map(|r| r.len() - 60).collect()) @@ -62,9 +94,15 @@ fn main() { let (a, b) = (sizes(today), sizes(proposed)); let tail_same = a.len() == b.len() && a.iter().skip(1).eq(b.iter().skip(1)); let verdict = if a.len() == b.len() && tail_same && b[0] > a[0] { - grew += 1; "first chunk GREW, tail identical" - } else if b.len() > a.len() { extra += 1; "EXTRA leading chunks" } - else { extra += 1; "changed otherwise" }; + grew += 1; + "first chunk GREW, tail identical" + } else if b.len() > a.len() { + extra += 1; + "EXTRA leading chunks" + } else { + extra += 1; + "changed otherwise" + }; println!("{movie:10} today {a:?}\n{:10} prop {b:?} {verdict}", ""); } println!("\nunchanged {same} fixed-cleanly {grew} would-break {extra} skipped {skip}"); diff --git a/crates/sylpheed-formats/examples/voice_region_chunk_census.rs b/crates/sylpheed-formats/examples/voice_region_chunk_census.rs index 38733412..744ecb78 100644 --- a/crates/sylpheed-formats/examples/voice_region_chunk_census.rs +++ b/crates/sylpheed-formats/examples/voice_region_chunk_census.rs @@ -12,39 +12,60 @@ //! //! cargo run -p sylpheed-formats --example voice_region_chunk_census +use std::collections::BTreeMap; use sylpheed_formats::media::{self, DirectorySource, DiscSource}; use sylpheed_formats::movie_manifest; use sylpheed_formats::slb::{self, VoiceLang}; -use std::collections::BTreeMap; fn main() { let disc = std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC"); let src = DirectorySource::new(std::path::PathBuf::from(&disc)); let tpak = src.open_pak("dat/tables.pak").expect("tables.pak"); let manifest = tpak - .entries().iter() + .entries() + .iter() .find_map(|e| tpak.read(e).ok().filter(|b| movie_manifest::is_manifest(b))) .expect("manifest"); - let movies: Vec = movie_manifest::parse(&manifest).into_iter().map(|m| m.movie).collect(); + let movies: Vec = movie_manifest::parse(&manifest) + .into_iter() + .map(|m| m.movie) + .collect(); let total = movies.len(); let mut hist: BTreeMap> = BTreeMap::new(); let (mut resolved, mut unresolved, mut unreadable) = (0, 0, 0); for movie in movies { - let Some((start, end)) = media::resolve_movie_voice_region(&src, &movie, VoiceLang::English) - else { unresolved += 1; continue }; - let Ok(b) = src.read_segment_range("dat/sound", start, (end - start) as usize) - else { unreadable += 1; continue }; + let Some((start, end)) = + media::resolve_movie_voice_region(&src, &movie, VoiceLang::English) + else { + unresolved += 1; + continue; + }; + let Ok(b) = src.read_segment_range("dat/sound", start, (end - start) as usize) else { + unreadable += 1; + continue; + }; resolved += 1; - hist.entry(slb::to_xma_riffs(&b).len()).or_default().push(movie); + hist.entry(slb::to_xma_riffs(&b).len()) + .or_default() + .push(movie); } println!("POPULATION: {total} movies in the manifest"); println!("COVERAGE: {resolved} resolved and read, {unresolved} unresolved, {unreadable} unreadable"); - println!(" {} accounted for\n", resolved + unresolved + unreadable); + println!( + " {} accounted for\n", + resolved + unresolved + unreadable + ); for (n, ms) in &hist { - println!(" {n} chunk(s): {:>3} region(s) {}", ms.len(), - ms.iter().cloned().collect::>().join(" ")); + println!( + " {n} chunk(s): {:>3} region(s) {}", + ms.len(), + ms.iter().cloned().collect::>().join(" ") + ); } - println!("\nTHREE-CHUNK REGIONS: {}", hist.get(&3).map(|v| v.len()).unwrap_or(0)); + println!( + "\nTHREE-CHUNK REGIONS: {}", + hist.get(&3).map(|v| v.len()).unwrap_or(0) + ); println!("--- END OF CENSUS (if this line is missing, the run did not finish) ---"); } diff --git a/crates/sylpheed-formats/examples/voice_region_chunks.rs b/crates/sylpheed-formats/examples/voice_region_chunks.rs index 0d907922..05f65e22 100644 --- a/crates/sylpheed-formats/examples/voice_region_chunks.rs +++ b/crates/sylpheed-formats/examples/voice_region_chunks.rs @@ -66,7 +66,10 @@ fn main() { "no leading region".to_string() } else if hdr == Some(first_riff as usize) { with_header += 1; - format!("BANK HEADER ({first_riff} B = {} packets exactly)", first_riff / 2048) + format!( + "BANK HEADER ({first_riff} B = {} packets exactly)", + first_riff / 2048 + ) } else { with_leading += 1; *census.entry(first_riff as usize % 2048).or_default() += 1; diff --git a/crates/sylpheed-formats/examples/voice_region_fix_test.rs b/crates/sylpheed-formats/examples/voice_region_fix_test.rs index 7427e4e0..6c736b75 100644 --- a/crates/sylpheed-formats/examples/voice_region_fix_test.rs +++ b/crates/sylpheed-formats/examples/voice_region_fix_test.rs @@ -24,17 +24,27 @@ const WANT: [usize; 3] = [1_294_336, 1_118_208, 1_171_456]; fn main() { let disc = std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC"); let src = DirectorySource::new(std::path::PathBuf::from(&disc)); - for (label, start) in [("anchor (today)", ADV_ANCHOR), ("predecessor (proposed)", ADV_PRED)] { + for (label, start) in [ + ("anchor (today)", ADV_ANCHOR), + ("predecessor (proposed)", ADV_PRED), + ] { let bytes = src .read_segment_range("dat/sound", start, (ADV_END - start) as usize) .expect("region"); - let sizes: Vec = slb::to_xma_riffs(&bytes).iter().map(|r| r.len() - 60).collect(); + let sizes: Vec = slb::to_xma_riffs(&bytes) + .iter() + .map(|r| r.len() - 60) + .collect(); let hit = sizes.len() == 3 && sizes.iter().zip(WANT.iter()).all(|(a, b)| a == b); println!( "{label:24} start {start} span {:>9} -> {:?}{}", ADV_END - start, sizes, - if hit { " <== MATCHES THE DECODER" } else { "" } + if hit { + " <== MATCHES THE DECODER" + } else { + "" + } ); } } diff --git a/crates/sylpheed-formats/examples/voice_region_start_audit.rs b/crates/sylpheed-formats/examples/voice_region_start_audit.rs index 4633b220..31266c2f 100644 --- a/crates/sylpheed-formats/examples/voice_region_start_audit.rs +++ b/crates/sylpheed-formats/examples/voice_region_start_audit.rs @@ -30,33 +30,58 @@ fn main() { .map(|m| m.movie) .collect(); - println!("{:10} {:>8} {:>12} {:>10} {}", "movie", "chunks", "first chunk", "clip pkts", "verdict"); + println!( + "{:10} {:>8} {:>12} {:>10} {}", + "movie", "chunks", "first chunk", "clip pkts", "verdict" + ); let (mut clipped, mut clean, mut skipped) = (0, 0, 0); for movie in movies { - let Some((start, end)) = media::resolve_movie_voice_region(&src, &movie, VoiceLang::English) - else { skipped += 1; continue }; + let Some((start, end)) = + media::resolve_movie_voice_region(&src, &movie, VoiceLang::English) + else { + skipped += 1; + continue; + }; // ONE read of the region plus a lead-in, then slide inside it. Re-reading // several MB per step made this too slow to finish at all. const LEAD: u64 = 600 * 2048; let lead = LEAD.min(start); - let buf = match src.read_segment_range("dat/sound", start - lead, (end - (start - lead)) as usize) { - Ok(b) => b, Err(_) => { skipped += 1; continue } + let buf = match src.read_segment_range( + "dat/sound", + start - lead, + (end - (start - lead)) as usize, + ) { + Ok(b) => b, + Err(_) => { + skipped += 1; + continue; + } }; let at = |back: usize| -> Vec> { let off = lead as usize - back * 2048; slb::to_xma_riffs(&buf[off..]) }; let base_riffs = at(0); - if base_riffs.is_empty() { skipped += 1; continue } + if base_riffs.is_empty() { + skipped += 1; + continue; + } let n0 = base_riffs.len(); let first0 = base_riffs[0].len() - 60; let mut clip = 0usize; for k in 1..=(lead as usize / 2048) { - if at(k).len() != n0 { break } + if at(k).len() != n0 { + break; + } clip = k; } - let verdict = if clip == 0 { clean += 1; "starts at a boundary" } - else { clipped += 1; "STARTS MID-STREAM" }; + let verdict = if clip == 0 { + clean += 1; + "starts at a boundary" + } else { + clipped += 1; + "STARTS MID-STREAM" + }; println!("{movie:10} {n0:>8} {first0:>12} {clip:>10} {verdict}"); } println!("\nclipped {clipped} clean {clean} skipped {skipped}"); diff --git a/crates/sylpheed-formats/examples/voice_region_start_why.rs b/crates/sylpheed-formats/examples/voice_region_start_why.rs index 7195c2af..bc6c5f49 100644 --- a/crates/sylpheed-formats/examples/voice_region_start_why.rs +++ b/crates/sylpheed-formats/examples/voice_region_start_why.rs @@ -25,33 +25,55 @@ fn main() { let code = VoiceLang::English.code_pub(); let tpak = src.open_pak("dat/tables.pak").expect("tables.pak"); let manifest = tpak - .entries().iter() + .entries() + .iter() .find_map(|e| tpak.read(e).ok().filter(|b| movie_manifest::is_manifest(b))) .expect("manifest"); let marker = format!("{code}\\Movie\\VOICE_ADV.slb"); - let registry = tpak.entries().iter().find_map(|e| { - tpak.read(e).ok().filter(|b| b.windows(marker.len()).any(|w| w == marker.as_bytes())) - }).expect("registry"); + let registry = tpak + .entries() + .iter() + .find_map(|e| { + tpak.read(e) + .ok() + .filter(|b| b.windows(marker.len()).any(|w| w == marker.as_bytes())) + }) + .expect("registry"); let ids = movie_voice::registry_voice_ids(®istry); let stoc = src.read_file("dat/sound.pak").expect("sound.pak"); let entries = PakArchive::parse_toc(&stoc).expect("toc"); - println!("{:10} {:>6} {:>12} {:>12} {:>12} {:>10} {:>9} {}", - "movie","id","anchor","pred(id-1)","pred(before)","span","chosen","note"); + println!( + "{:10} {:>6} {:>12} {:>12} {:>12} {:>10} {:>9} {}", + "movie", "id", "anchor", "pred(id-1)", "pred(before)", "span", "chosen", "note" + ); for m in movie_manifest::parse(&manifest) { let movie = m.movie; - let Some(token) = movie_manifest::voice_token(&manifest, &movie) else { continue }; + let Some(token) = movie_manifest::voice_token(&manifest, &movie) else { + continue; + }; let Some(&id) = ids.get(&token) else { continue }; - let Some(anchor) = ["Movie","etc","Voice"].iter().find_map(|dir| { + let Some(anchor) = ["Movie", "etc", "Voice"].iter().find_map(|dir| { let h = name_hash(&format!("{code}\\{dir}\\{token}.slb")); - entries.binary_search_by_key(&h, |e| e.name_hash).ok().map(|i| entries[i].offset as u64) - }) else { continue }; - let win_start = anchor.saturating_sub(2*1024*1024) & !3; - let Ok(window) = src.read_segment_range("dat/sound", win_start, 8*1024*1024) else { continue }; - let Some(end_local) = movie_voice::find_descriptor(&window, id) else { continue }; + entries + .binary_search_by_key(&h, |e| e.name_hash) + .ok() + .map(|i| entries[i].offset as u64) + }) else { + continue; + }; + let win_start = anchor.saturating_sub(2 * 1024 * 1024) & !3; + let Ok(window) = src.read_segment_range("dat/sound", win_start, 8 * 1024 * 1024) else { + continue; + }; + let Some(end_local) = movie_voice::find_descriptor(&window, id) else { + continue; + }; let end = win_start + end_local as u64; - let p1 = movie_voice::find_descriptor(&window, id.wrapping_sub(1)).map(|o| win_start + o as u64); - let pb = movie_voice::find_descriptor_before(&window, end_local).map(|o| win_start + o as u64); + let p1 = + movie_voice::find_descriptor(&window, id.wrapping_sub(1)).map(|o| win_start + o as u64); + let pb = + movie_voice::find_descriptor_before(&window, end_local).map(|o| win_start + o as u64); let cand = p1.or(pb); // the resolver's own filter let kept = cand.filter(|&s| s < end && end - s < 1_500_000); @@ -62,9 +84,11 @@ fn main() { (Some(_), Some(_)) => "predecessor kept", (None, _) => "no predecessor found -> anchor", }; - println!("{movie:10} {id:>6} {anchor:>12} {:>12} {:>12} {span:>10} {:>9} {note}", - p1.map(|v| v.to_string()).unwrap_or("-".into()), - pb.map(|v| v.to_string()).unwrap_or("-".into()), - if chosen == anchor { "anchor" } else { "pred" }); + println!( + "{movie:10} {id:>6} {anchor:>12} {:>12} {:>12} {span:>10} {:>9} {note}", + p1.map(|v| v.to_string()).unwrap_or("-".into()), + pb.map(|v| v.to_string()).unwrap_or("-".into()), + if chosen == anchor { "anchor" } else { "pred" } + ); } } diff --git a/crates/sylpheed-formats/examples/voice_stream_cue_map.rs b/crates/sylpheed-formats/examples/voice_stream_cue_map.rs index b78a37f5..e27e03ac 100644 --- a/crates/sylpheed-formats/examples/voice_stream_cue_map.rs +++ b/crates/sylpheed-formats/examples/voice_stream_cue_map.rs @@ -66,7 +66,11 @@ fn main() { let ids = sylpheed_formats::movie_voice::registry_voice_ids(®istry); let name_of: std::collections::HashMap = ids.iter().map(|(n, &i)| (i, n.clone())).collect(); - println!("registry: {} cue names, {} distinct ids", ids.len(), name_of.len()); + println!( + "registry: {} cue names, {} distinct ids", + ids.len(), + name_of.len() + ); // The 17 regions that open with a headerless stream, from the manifest. let movies: Vec = { @@ -76,7 +80,10 @@ fn main() { .iter() .find_map(|e| tpak.read(e).ok().filter(|b| movie_manifest::is_manifest(b))) .expect("manifest"); - movie_manifest::parse(&manifest).into_iter().map(|m| m.movie).collect() + movie_manifest::parse(&manifest) + .into_iter() + .map(|m| m.movie) + .collect() }; let mut regions = Vec::new(); for m in &movies { @@ -89,38 +96,57 @@ fn main() { // Scan a window covering every region, with margin for cues either side. let win_start = lo.saturating_sub(8 * 1024 * 1024) & !3; let win_len = (hi - win_start + 8 * 1024 * 1024) as usize; - println!("scanning dat/sound {win_start}..{} ({:.1} MB)", win_start + win_len as u64, - win_len as f64 / 1e6); + println!( + "scanning dat/sound {win_start}..{} ({:.1} MB)", + win_start + win_len as u64, + win_len as f64 / 1e6 + ); let buf = src .read_segment_range("dat/sound", win_start, win_len) .expect("stream window"); let descs = all_descriptors(&buf); println!("{} trailer descriptors found\n", descs.len()); - let named = descs.iter().filter(|(_, id)| name_of.contains_key(id)).count(); - println!(" of those, {named} carry an id the registry names, {} do not\n", - descs.len() - named); + let named = descs + .iter() + .filter(|(_, id)| name_of.contains_key(id)) + .count(); + println!( + " of those, {named} carry an id the registry names, {} do not\n", + descs.len() - named + ); // For each stream-opening region, name the cue that OWNS the leading span: // the cue whose [prev_desc .. desc] interval contains it. println!("leading span -> owning cue\n"); let mut verdicts: std::collections::BTreeMap<&str, usize> = Default::default(); for (m, s, e) in ®ions { - let Ok(bytes) = src.read_segment_range("dat/sound", *s, (e - s) as usize) else { continue }; - let Some(fr) = bytes.windows(4).position(|w| w == b"RIFF") else { continue }; + let Ok(bytes) = src.read_segment_range("dat/sound", *s, (e - s) as usize) else { + continue; + }; + let Some(fr) = bytes.windows(4).position(|w| w == b"RIFF") else { + continue; + }; if fr == 0 || sylpheed_formats::slb::bank_header_len(&bytes) == Some(fr) { continue; // bank-header case, not ours } let (a, b) = (*s, *s + fr as u64); // the leading span, global offsets - // Descriptors bracketing the MIDDLE of the leading span. + // Descriptors bracketing the MIDDLE of the leading span. let mid = (a + b) / 2; let mid_local = (mid - win_start) as usize; - let before = descs.iter().rev().find(|(o, _)| (*o as u64) < mid_local as u64); + let before = descs + .iter() + .rev() + .find(|(o, _)| (*o as u64) < mid_local as u64); let after = descs.iter().find(|(o, _)| *o >= mid_local); let owner = after.map(|(_, id)| *id); let owner_name = owner .and_then(|id| name_of.get(&id).cloned()) - .unwrap_or_else(|| owner.map(|i| format!("")).unwrap_or("".into())); + .unwrap_or_else(|| { + owner + .map(|i| format!("")) + .unwrap_or("".into()) + }); let kind = if owner_name.starts_with("VOICE_D_") { "MISSION line" } else if owner_name.starts_with("VOICE_") { @@ -146,18 +172,33 @@ fn main() { println!("\ncue span vs the 1.5 MB guard, and what the region actually starts at:\n"); let (mut over, mut under, mut over_is_stream, mut under_is_stream) = (0, 0, 0, 0); for (m, s, e) in ®ions { - let Ok(bytes) = src.read_segment_range("dat/sound", *s, (e - s) as usize) else { continue }; + let Ok(bytes) = src.read_segment_range("dat/sound", *s, (e - s) as usize) else { + continue; + }; let fr = bytes.windows(4).position(|w| w == b"RIFF"); let is_stream = matches!(fr, Some(f) if f > 0 && sylpheed_formats::slb::bank_header_len(&bytes) != Some(f)); // The true predecessor trailer for this cue, from the full descriptor list. let end_local = (*e - win_start) as usize; - let prev = descs.iter().rev().find(|(o, _)| *o < end_local).map(|(o, _)| *o as u64 + win_start); + let prev = descs + .iter() + .rev() + .find(|(o, _)| *o < end_local) + .map(|(o, _)| *o as u64 + win_start); let Some(prev) = prev else { continue }; let span = e - prev; let guarded = span >= 1_500_000; - if guarded { over += 1; if is_stream { over_is_stream += 1 } } - else { under += 1; if is_stream { under_is_stream += 1 } } + if guarded { + over += 1; + if is_stream { + over_is_stream += 1 + } + } else { + under += 1; + if is_stream { + under_is_stream += 1 + } + } if is_stream { println!( " {m:8} true cue span {span:8} B (> guard: {guarded}) region starts at {s}, \ diff --git a/crates/sylpheed-formats/examples/voice_three_stream_sizes.rs b/crates/sylpheed-formats/examples/voice_three_stream_sizes.rs index ced72a43..75545ea3 100644 --- a/crates/sylpheed-formats/examples/voice_three_stream_sizes.rs +++ b/crates/sylpheed-formats/examples/voice_three_stream_sizes.rs @@ -74,8 +74,10 @@ fn main() { .expect("window"); let descs = all_descriptors(&buf); - println!("{:<14} {:>10} {:>10} {:>10} {:>7} {:>9} {:>9}", - "cue", "stream1", "stream2", "stream3", "s3/s2", "rate2", "rate3"); + println!( + "{:<14} {:>10} {:>10} {:>10} {:>7} {:>9} {:>9}", + "cue", "stream1", "stream2", "stream3", "s3/s2", "rate2", "rate3" + ); let mut ratios: Vec = Vec::new(); let mut tiny = 0; for w in descs.windows(2) { @@ -101,8 +103,12 @@ fn main() { println!( "{:<14} {s1:>10} {s2:>10} {s3:>10} {r:>7.4} {:>9} {:>9}", name.trim_start_matches("VOICE_"), - declared_rate(&riffs[1]).map(|v| v.to_string()).unwrap_or("-".into()), - declared_rate(&riffs[2]).map(|v| v.to_string()).unwrap_or("-".into()), + declared_rate(&riffs[1]) + .map(|v| v.to_string()) + .unwrap_or("-".into()), + declared_rate(&riffs[2]) + .map(|v| v.to_string()) + .unwrap_or("-".into()), ); } ratios.sort_by(|a, b| a.partial_cmp(b).unwrap()); @@ -113,7 +119,11 @@ fn main() { let var = ratios.iter().map(|r| (r - mean).powi(2)).sum::() / n as f64; println!( " stream3/stream2 ratio: min {:.4} median {:.4} max {:.4} mean {:.4} sd {:.4}", - ratios[0], ratios[n / 2], ratios[n - 1], mean, var.sqrt() + ratios[0], + ratios[n / 2], + ratios[n - 1], + mean, + var.sqrt() ); println!(" cues where stream 3 is less than HALF of stream 2: {tiny}"); let near = ratios.iter().filter(|r| (**r - 1.0).abs() < 0.15).count(); diff --git a/crates/sylpheed-formats/src/audio.rs b/crates/sylpheed-formats/src/audio.rs index 92bf54a9..66467f20 100644 --- a/crates/sylpheed-formats/src/audio.rs +++ b/crates/sylpheed-formats/src/audio.rs @@ -456,10 +456,16 @@ mod tests { let info = AudioInfo::probe(&v); assert_eq!(info.codec, AudioCodec::Xma); assert_eq!(info.channels, Some(2), "channels came from wBitsPerSample"); - assert_eq!(info.sample_rate, Some(48_000), "rate came from wEncodeOptions"); + assert_eq!( + info.sample_rate, + Some(48_000), + "rate came from wEncodeOptions" + ); assert_eq!(info.bits_per_sample, Some(16)); assert_eq!(info.avg_bytes_per_sec, Some(8142)); - let d = info.duration_secs.expect("duration from the declared byte rate"); + let d = info + .duration_secs + .expect("duration from the declared byte rate"); assert!( (d - 137.324).abs() < 0.05, "declared-rate duration {d} should match the decoded 137.324 s" diff --git a/crates/sylpheed-formats/src/ui_layout.rs b/crates/sylpheed-formats/src/ui_layout.rs index 937a1712..77ae9ddf 100644 --- a/crates/sylpheed-formats/src/ui_layout.rs +++ b/crates/sylpheed-formats/src/ui_layout.rs @@ -1432,7 +1432,11 @@ pub fn compose_with_order( lk.rotation_deg != 0 || lk.scale_x != kf.scale_x || lk.scale_y != kf.scale_y }) }); - if differs { Some(lb) } else { None } + if differs { + Some(lb) + } else { + None + } }); if let Some(lb) = leaf { let mut any = false; @@ -1447,8 +1451,12 @@ pub fn compose_with_order( // is parsed from its own slice. Its NAME is the sprite name, and // the sprite itself lives in the PARENT bundle's table. let lsp = le.sprite.clone().unwrap_or_else(|| le.name.clone()); - let Some(&(so, ss)) = build.sprites.get(&lsp) else { continue }; - let Some(limg) = t8ad::parse(&bundle[so..so + ss]) else { continue }; + let Some(&(so, ss)) = build.sprites.get(&lsp) else { + continue; + }; + let Some(limg) = t8ad::parse(&bundle[so..so + ss]) else { + continue; + }; // 🔴 Only count it as drawn if it CAN draw. `blit` returns early // on a zero scale — "collapsed to nothing", not "unset" — so // setting the flag unconditionally would let a scale-0 leaf @@ -1458,8 +1466,16 @@ pub fn compose_with_order( if lk.scale_x == 0 || lk.scale_y == 0 { continue; } - blit(&mut canvas, w, h, &limg, &lk, le.pivot_x, le.pivot_y, - limg.flags & 0x02 != 0); + blit( + &mut canvas, + w, + h, + &limg, + &lk, + le.pivot_x, + le.pivot_y, + limg.flags & 0x02 != 0, + ); any = true; } if any { @@ -1467,8 +1483,16 @@ pub fn compose_with_order( continue; } } - blit(&mut canvas, w, h, &img, kf, el.pivot_x, el.pivot_y, - img.flags & 0x02 != 0); + blit( + &mut canvas, + w, + h, + &img, + kf, + el.pivot_x, + el.pivot_y, + img.flags & 0x02 != 0, + ); drawn.push(el.index); } ComposedScreen { @@ -1757,12 +1781,26 @@ mod tests { /// A solid opaque rectangle sprite, for exercising `blit` geometry. fn solid(w: u32, h: u32) -> t8ad::T8adImage { - t8ad::T8adImage { width: w, height: h, rgba: vec![255u8; (w * h * 4) as usize], - flags: 0 } + t8ad::T8adImage { + width: w, + height: h, + rgba: vec![255u8; (w * h * 4) as usize], + flags: 0, + } } fn kf_at(x: i32, y: i32, rot: i32) -> Keyframe { - Keyframe { fade: 0xff_ff_ff_ff, rotation_deg: rot, unknown_4: 0, unknown_8: 0, - scale_x: 100, scale_y: 100, tint: 0xffff_ffff, x, y, time: Some(0) } + Keyframe { + fade: 0xff_ff_ff_ff, + rotation_deg: rot, + unknown_4: 0, + unknown_8: 0, + scale_x: 100, + scale_y: 100, + tint: 0xffff_ffff, + x, + y, + time: Some(0), + } } fn draw(img: &t8ad::T8adImage, kf: &Keyframe, px: u32, py: u32) -> Vec { let mut c = vec![0u8; 64 * 64 * 4]; @@ -1771,9 +1809,13 @@ mod tests { } fn covered(c: &[u8]) -> Vec<(i32, i32)> { let mut v = Vec::new(); - for y in 0..64 { for x in 0..64 { - if c[((y * 64 + x) * 4 + 3) as usize] != 0 { v.push((x as i32, y as i32)); } - }} + for y in 0..64 { + for x in 0..64 { + if c[((y * 64 + x) * 4 + 3) as usize] != 0 { + v.push((x as i32, y as i32)); + } + } + } v } @@ -1789,8 +1831,11 @@ mod tests { // 0° and 360° must be identical to the unrotated path, byte for byte: // the fast path must be exactly the old behaviour. - assert_eq!(base, draw(&img, &kf_at(20, 30, 360), px, py), - "360 degrees must equal the unrotated path exactly"); + assert_eq!( + base, + draw(&img, &kf_at(20, 30, 360), px, py), + "360 degrees must equal the unrotated path exactly" + ); // 90° must turn a 10x4 into a 4x10 about the same centre. let r90 = covered(&draw(&img, &kf_at(20, 30, 90), px, py)); @@ -1805,18 +1850,24 @@ mod tests { // The covered area must be conserved to a few percent -- a rotation that // loses or invents pixels is the forward-mapping bug this path avoids. let (a0, a90) = (b.len() as f64, r90.len() as f64); - assert!((a0 - a90).abs() / a0 < 0.15, - "area changed too much under rotation: {a0} -> {a90}"); + assert!( + (a0 - a90).abs() / a0 < 0.15, + "area changed too much under rotation: {a0} -> {a90}" + ); // And the centroid must stay on the pivot. let cen = |v: &Vec<(i32, i32)>| { let n = v.len() as f64; - (v.iter().map(|p| p.0 as f64).sum::() / n, - v.iter().map(|p| p.1 as f64).sum::() / n) + ( + v.iter().map(|p| p.0 as f64).sum::() / n, + v.iter().map(|p| p.1 as f64).sum::() / n, + ) }; let (c0, c9) = (cen(&b), cen(&r90)); - assert!((c0.0 - c9.0).abs() < 1.0 && (c0.1 - c9.1).abs() < 1.0, - "rotation moved the centroid: {c0:?} -> {c9:?}"); + assert!( + (c0.0 - c9.0).abs() < 1.0 && (c0.1 - c9.1).abs() < 1.0, + "rotation moved the centroid: {c0:?} -> {c9:?}" + ); } /// A synthetic build bundle: RATC magic, entry count at 0x14, a declaration @@ -2060,23 +2111,34 @@ mod tests { /// Adding zero changes nothing: a black additive source is the identity. #[test] fn additive_control_black_source_is_identity() { - let img = t8ad::T8adImage { width: 4, height: 4, - rgba: [[0u8, 0, 0, 255]; 16].concat(), flags: 0x02 }; + let img = t8ad::T8adImage { + width: 4, + height: 4, + rgba: [[0u8, 0, 0, 255]; 16].concat(), + flags: 0x02, + }; let kf = kf_at(10, 10, 0); let mut c = vec![77u8; 64 * 64 * 4]; let before = c.clone(); blit(&mut c, 64, 64, &img, &kf, 0, 0, true); // alpha is forced to 255 on any touched pixel, so compare colour only for i in (0..c.len()).filter(|i| i % 4 != 3) { - assert_eq!(c[i], before[i], "black additive source moved a pixel at {i}"); + assert_eq!( + c[i], before[i], + "black additive source moved a pixel at {i}" + ); } } /// A fully transparent source is the identity in either blend. #[test] fn additive_control_alpha_zero_is_identity() { - let img = t8ad::T8adImage { width: 4, height: 4, - rgba: [[200u8, 200, 200, 0]; 16].concat(), flags: 0x02 }; + let img = t8ad::T8adImage { + width: 4, + height: 4, + rgba: [[200u8, 200, 200, 0]; 16].concat(), + flags: 0x02, + }; let kf = kf_at(10, 10, 0); let mut c = vec![77u8; 64 * 64 * 4]; let before = c.clone(); @@ -2087,16 +2149,27 @@ mod tests { /// The sum is the sum, and it SATURATES rather than wrapping. #[test] fn additive_control_known_sums() { - for (dst, src, want) in [(40u8, 100u32, 140u8), (200, 100, 255), (0, 255, 255), - (250, 10, 255), (7, 8, 15)] { + for (dst, src, want) in [ + (40u8, 100u32, 140u8), + (200, 100, 255), + (0, 255, 255), + (250, 10, 255), + (7, 8, 15), + ] { let img = t8ad::T8adImage { - width: 2, height: 2, - rgba: [[src as u8, src as u8, src as u8, 255]; 4].concat(), flags: 0x02 }; + width: 2, + height: 2, + rgba: [[src as u8, src as u8, src as u8, 255]; 4].concat(), + flags: 0x02, + }; let kf = kf_at(10, 10, 0); let mut c = vec![dst; 64 * 64 * 4]; blit(&mut c, 64, 64, &img, &kf, 0, 0, true); let di = ((10 * 64 + 10) * 4) as usize; - assert_eq!(c[di], want, "additive {dst} + {src} should saturate to {want}"); + assert_eq!( + c[di], want, + "additive {dst} + {src} should saturate to {want}" + ); } } @@ -2106,8 +2179,12 @@ mod tests { /// always drew additive would pass every test above. #[test] fn additive_control_bit_actually_selects() { - let img = t8ad::T8adImage { width: 2, height: 2, - rgba: [[100u8, 100, 100, 128]; 4].concat(), flags: 0 }; + let img = t8ad::T8adImage { + width: 2, + height: 2, + rgba: [[100u8, 100, 100, 128]; 4].concat(), + flags: 0, + }; let kf = kf_at(10, 10, 0); let di = ((10 * 64 + 10) * 4) as usize; @@ -2120,6 +2197,9 @@ mod tests { assert_eq!(over[di], 90, "alpha-over arithmetic changed"); // additive: 80 + 100*128/255 = 80 + 50 = 130 assert_eq!(add[di], 130, "additive arithmetic changed"); - assert_ne!(over[di], add[di], "the blend flag selects nothing -- blit ignores it"); + assert_ne!( + over[di], add[di], + "the blend flag selects nothing -- blit ignores it" + ); } } diff --git a/crates/sylpheed-formats/tests/slb_leading_segment_disc.rs b/crates/sylpheed-formats/tests/slb_leading_segment_disc.rs index 420c1f6d..5f71ef8a 100644 --- a/crates/sylpheed-formats/tests/slb_leading_segment_disc.rs +++ b/crates/sylpheed-formats/tests/slb_leading_segment_disc.rs @@ -315,7 +315,9 @@ fn a_bank_that_states_its_own_header_has_no_leading_segment() { // decompressing 9 519 entries: `sound.pak` stores them uncompressed, and a // full read of all of them is several GB (it OOM-killed the test runner). for entry in snd.entries() { - let Some(head) = snd.data_at(entry.offset as usize, 0x38) else { continue }; + let Some(head) = snd.data_at(entry.offset as usize, 0x38) else { + continue; + }; match slb::bank_header_len(head) { Some(h) => { let b = snd.read(entry).expect("read a bank that states a header"); @@ -330,7 +332,10 @@ fn a_bank_that_states_its_own_header_has_no_leading_segment() { } // 28 music banks (ids 1001-1023, 1101-1105); the rest are mid-bank windows, // where the leading region IS real and must keep being emitted. - assert_eq!(with_header, 28, "banks stating their own header at offset 0"); + assert_eq!( + with_header, 28, + "banks stating their own header at offset 0" + ); assert!(mid_bank > 9000, "mid-bank windows, got {mid_bank}"); eprintln!("{with_header} banks state a header; {mid_bank} mid-bank windows"); } diff --git a/crates/sylpheed-formats/tests/ui_forced_backdrop_disc.rs b/crates/sylpheed-formats/tests/ui_forced_backdrop_disc.rs index ca4e9b36..d92404a7 100644 --- a/crates/sylpheed-formats/tests/ui_forced_backdrop_disc.rs +++ b/crates/sylpheed-formats/tests/ui_forced_backdrop_disc.rs @@ -104,7 +104,11 @@ fn the_hold_after_a_final_keyframe_is_required_by_a_measured_order() { for entry in [10usize, 11] { let (_, b) = build(&ar, entry); let prim = el(&b, "palogo_eff0.prm"); - assert_eq!(prim.keyframes.len(), 1, "entry {entry}: the case rests on it being static"); + assert_eq!( + prim.keyframes.len(), + 1, + "entry {entry}: the case rests on it being static" + ); // With the hold — what `pose_at` does, and what the game does. assert!( @@ -114,18 +118,36 @@ fn the_hold_after_a_final_keyframe_is_required_by_a_measured_order() { // Without it, spelled out here rather than imported, so the test states // the counterfactual it is pinning. - let tmax = b.elements.iter() - .flat_map(|e| e.keyframes.iter().filter_map(|k| k.time)).max().unwrap_or(0); - let last = |e: &ui_layout::Element| e.keyframes.iter().filter_map(|k| k.time).max().unwrap_or(0); + let tmax = b + .elements + .iter() + .flat_map(|e| e.keyframes.iter().filter_map(|k| k.time)) + .max() + .unwrap_or(0); + let last = + |e: &ui_layout::Element| e.keyframes.iter().filter_map(|k| k.time).max().unwrap_or(0); let alpha_no_hold = |e: &ui_layout::Element, t: u32| -> u32 { - if t > last(e) { 0 } else { e.pose_at(t).map(|k| k.fade >> 24).unwrap_or(0) } + if t > last(e) { + 0 + } else { + e.pose_at(t).map(|k| k.fade >> 24).unwrap_or(0) + } }; - let opaque: Vec = (0..=tmax).filter(|&t| alpha_no_hold(prim, t) == 255).collect(); - let others: Vec<_> = b.elements.iter().filter(|o| o.index != prim.index).collect(); - let below = others.iter() - .filter(|o| opaque.iter().any(|&t| alpha_no_hold(o, t) > 0)).count(); + let opaque: Vec = (0..=tmax) + .filter(|&t| alpha_no_hold(prim, t) == 255) + .collect(); + let others: Vec<_> = b + .elements + .iter() + .filter(|o| o.index != prim.index) + .collect(); + let below = others + .iter() + .filter(|o| opaque.iter().any(|&t| alpha_no_hold(o, t) > 0)) + .count(); assert_ne!( - below, others.len(), + below, + others.len(), "entry {entry}: without the hold this element would come out FREE — which is \ why the hold is load-bearing rather than incidental" ); @@ -149,20 +171,27 @@ fn forced_backdrops_are_full_screen_and_plentiful() { paks.sort(); let (mut forced, mut prm, mut tbm) = (0usize, 0usize, 0usize); for p in &paks { - let Ok(ar) = PakArchive::open(p) else { continue }; + let Ok(ar) = PakArchive::open(p) else { + continue; + }; for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; if !ratc::is_ratc(&by) { continue; } - let Some(b) = ui_layout::parse_build(&by) else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; for element in &b.elements { if !ui_layout::forced_backdrop(&b, element) { continue; } forced += 1; - if element.name.ends_with(".prm") { prm += 1 } - else if element.name.ends_with(".tbm") { tbm += 1 } + if element.name.ends_with(".prm") { + prm += 1 + } else if element.name.ends_with(".tbm") { + tbm += 1 + } // 🔴 Untextured only. This assertion caught the rule's real // limit: applied to `.t32` sprites it claimed 22 of them must // sort first, against their own layer keys — a sprite's element @@ -191,7 +220,9 @@ fn forced_backdrops_are_full_screen_and_plentiful() { // they are textured, and element alpha does not establish their coverage. // Their verdicts are kept because restricting to `.prm` would send eleven // screens' backgrounds back to last, which is the bug this rule fixed. - assert!(prm >= 40 && tbm >= 30, + assert!( + prm >= 40 && tbm >= 30, "expected roughly 42 .prm / 38 .tbm forced instances, got {prm} / {tbm} — \ -if this moved, re-read the self-refutation section of ui-forced-backdrop.md"); +if this moved, re-read the self-refutation section of ui-forced-backdrop.md" + ); } diff --git a/crates/sylpheed-formats/tests/ui_keyframe_record_disc.rs b/crates/sylpheed-formats/tests/ui_keyframe_record_disc.rs index fc22b1bc..bade337a 100644 --- a/crates/sylpheed-formats/tests/ui_keyframe_record_disc.rs +++ b/crates/sylpheed-formats/tests/ui_keyframe_record_disc.rs @@ -52,7 +52,9 @@ fn for_each_build(root: &Path, mut f: impl FnMut(&str, &[u8])) { paks.sort(); for p in &paks { let name = p.file_name().unwrap().to_string_lossy().to_string(); - let Ok(arc) = PakArchive::open(p) else { continue }; + let Ok(arc) = PakArchive::open(p) else { + continue; + }; for e in arc.entries() { let Ok(bytes) = arc.read(e) else { continue }; if ratc::is_ratc(&bytes) { @@ -147,7 +149,10 @@ fn every_pose_is_timed_and_the_times_are_ordered() { for e in &examples { eprintln!(" {e}"); } - assert!(groups > 10_000, "expected the whole disc, saw {groups} groups"); + assert!( + groups > 10_000, + "expected the whole disc, saw {groups} groups" + ); assert_eq!(untimed, 0, "every pose must carry a time"); assert_eq!(out_of_order, 0, "keyframe times must not descend"); } diff --git a/crates/sylpheed-formats/tests/ui_record_loop_length_disc.rs b/crates/sylpheed-formats/tests/ui_record_loop_length_disc.rs index eaeac8d1..71868eb8 100644 --- a/crates/sylpheed-formats/tests/ui_record_loop_length_disc.rs +++ b/crates/sylpheed-formats/tests/ui_record_loop_length_disc.rs @@ -59,15 +59,21 @@ fn a_records_declared_length_is_never_shorter_than_its_keyframes() { let (mut total, mut exact, mut holds) = (0usize, 0usize, 0usize); for p in &paks { - let Ok(ar) = PakArchive::open(p) else { continue }; + let Ok(ar) = PakArchive::open(p) else { + continue; + }; for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; if !ratc::is_ratc(&by) { continue; } - let Some(b) = ui_layout::parse_build(&by) else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; for (rn, &(o, s)) in &b.records { - let Some((len, maxt)) = record_len_and_maxt(&by, o, s) else { continue }; + let Some((len, maxt)) = record_len_and_maxt(&by, o, s) else { + continue; + }; total += 1; assert!( len >= maxt, @@ -75,11 +81,18 @@ fn a_records_declared_length_is_never_shorter_than_its_keyframes() { a loop cannot restart before its own last pose", p.file_name().unwrap().to_string_lossy() ); - if len == maxt { exact += 1 } else { holds += 1 } + if len == maxt { + exact += 1 + } else { + holds += 1 + } } } } - assert!(total > 1500, "expected >1500 timed nested records, got {total}"); + assert!( + total > 1500, + "expected >1500 timed nested records, got {total}" + ); // The field must carry information. If every record declared exactly its own // last keyframe time, "loop length" would be an unfalsifiable relabelling. assert!( @@ -97,13 +110,19 @@ fn the_press_a_plate_glow_holds_dark_for_fifteen_units() { return; }; let ar = PakArchive::open(root.join("dat/GP_TITLE.pak")).expect("GP_TITLE.pak"); - let by = ar.read(&ar.entries()[2]).expect("entry 2 — the PRESS A plate"); + let by = ar + .read(&ar.entries()[2]) + .expect("entry 2 — the PRESS A plate"); let b = ui_layout::parse_build(&by).expect("parse"); let &(o, s) = b.records.get("ptbtn00f.rat").expect("the focus record"); let (len, maxt) = record_len_and_maxt(&by, o, s).expect("a timed record"); assert_eq!(maxt, 105, "the glow's ramp ends at t=105"); assert_eq!(len, 120, "but the cycle is 120 units"); - assert_eq!(len - maxt, 15, "so it holds dark for 15 units between pulses"); + assert_eq!( + len - maxt, + 15, + "so it holds dark for 15 units between pulses" + ); // The five main-menu focus records fill their cycle exactly — the contrast // that shows the slack is a property of this record, not of the format. @@ -112,9 +131,15 @@ fn the_press_a_plate_glow_holds_dark_for_fifteen_units() { let mut checked = 0; for n in 1..=5 { let name = format!("ptbtn0{n}f.rat"); - let Some(&(mo, ms)) = mb.records.get(&name) else { continue }; + let Some(&(mo, ms)) = mb.records.get(&name) else { + continue; + }; let (l, m) = record_len_and_maxt(&menu, mo, ms).expect("timed"); - assert_eq!((l, m), (120, 120), "{name} should fill its 120-unit cycle exactly"); + assert_eq!( + (l, m), + (120, 120), + "{name} should fill its 120-unit cycle exactly" + ); checked += 1; } assert_eq!(checked, 5, "expected five main-menu focus records"); diff --git a/crates/sylpheed-formats/tests/ui_settle_time_disc.rs b/crates/sylpheed-formats/tests/ui_settle_time_disc.rs index dee31caf..cde311f5 100644 --- a/crates/sylpheed-formats/tests/ui_settle_time_disc.rs +++ b/crates/sylpheed-formats/tests/ui_settle_time_disc.rs @@ -48,13 +48,21 @@ fn a_flash_is_transparent_at_the_settle_time_and_opaque_at_rest() { let (lo, hi) = b.settle_window().expect("a settle window"); let t = b.settle_time().expect("a settle time"); - assert!(hi - lo >= 60, "title's settle window should be a second or more, got {lo}..{hi}"); - assert!(lo < t && t < hi, "settle time {t} must lie inside {lo}..{hi}"); + assert!( + hi - lo >= 60, + "title's settle window should be a second or more, got {lo}..{hi}" + ); + assert!( + lo < t && t < hi, + "settle time {t} must lie inside {lo}..{hi}" + ); let alpha = |k: &ui_layout::Keyframe| k.fade >> 24; let mut flashes = 0; for el in &b.elements { - let Some(name) = el.name.strip_prefix("ptlogo_back2eff") else { continue }; + let Some(name) = el.name.strip_prefix("ptlogo_back2eff") else { + continue; + }; // `ptlogo_back2eff.t32` itself holds; only the numbered ones flash. if !name.starts_with(|c: char| c.is_ascii_digit()) { continue; @@ -75,7 +83,10 @@ fn a_flash_is_transparent_at_the_settle_time_and_opaque_at_rest() { el.name ); } - assert_eq!(flashes, 5, "GP_TITLE build 4 has five numbered back2 flashes"); + assert_eq!( + flashes, 5, + "GP_TITLE build 4 has five numbered back2 flashes" + ); // …while the two that genuinely hold are still opaque there. for want in ["ptlogo_back2eff.t32", "ptlogo_back2.t32"] { @@ -112,8 +123,12 @@ fn settle_windows_are_self_consistent_disc_wide() { if !ratc::is_ratc(&by) { continue; } - let Some(b) = ui_layout::parse_build(&by) else { continue }; - let Some((lo, hi)) = b.settle_window() else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { + continue; + }; + let Some((lo, hi)) = b.settle_window() else { + continue; + }; with += 1; assert!(lo < hi, "an empty window is not a window: {lo}..{hi}"); let t = b.settle_time().expect("a window implies a time"); @@ -137,6 +152,9 @@ fn settle_windows_are_self_consistent_disc_wide() { } } } - assert!(with > 1000, "expected >1000 bundles with a settle window, got {with}"); + assert!( + with > 1000, + "expected >1000 bundles with a settle window, got {with}" + ); eprintln!("{with} bundles have a settle window; {wide} are at least 30 units wide"); }