style: rustfmt sweep -- 774 hunks across 154 files -> 0
`cargo fmt --all -- --check` has failed on every run in this repository's history, identically on `main` and on every branch. This is #12. Mechanical: `cargo fmt --all`, nothing else. 154 files, all `.rs`, no other extension touched. `cargo check --workspace` exits 0 afterwards, so nothing changed semantically. ON THE ORDERING, WHICH WAS THE REAL QUESTION. HANDOFF-2026-09-06 section 7 warns this is the expensive fix: a whole-tree reformat before #7 and #8 return "would put a conflict in every file of 861 commits and make the reviews those items exist to enable unreadable". That is measurably too pessimistic, and it had been reasoned rather than tested. Measured here by three-way merging a rustfmt'd `main` against both unmerged branches, file by file: file/branch pairs tested 32 merges CLEAN 28 merges CONFLICTING 4 (8 conflict hunks total) sylpheed-cli/src/main.rs 1 hunk sylpheed-export/src/check.rs 1 sylpheed-export/src/screen.rs 4 sylpheed-export/src/video.rs 2 All four are against `auto/frame-blend-draw-path` only; `auto/port-p6-audio` does not conflict anywhere. The earlier framing -- 154 dirty files, 133 that cannot collide, 21 that can, the collision set carrying 147 of 774 hunks (19%) -- reproduces exactly. What it did not say is that most of the 21 still merge cleanly, because rustfmt's edits and the branches' edits rarely land on the same lines. So the cost of sweeping now is 4 files and 8 hunks for one branch, against a check that is otherwise red forever. Deliberately NOT folded into the WASM PR: 154 reformatted files would make that one unreviewable. Closes #12
This commit is contained in:
@@ -19,22 +19,42 @@ fn main() {
|
||||
let w = std::env::temp_dir().join(format!("bk_{i}.wav"));
|
||||
let _ = Command::new("ffmpeg")
|
||||
.args(["-hide_banner", "-loglevel", "error", "-y", "-i"])
|
||||
.arg(&p).arg(&w).output();
|
||||
.arg(&p)
|
||||
.arg(&w)
|
||||
.output();
|
||||
let out = Command::new("ffmpeg")
|
||||
.args(["-hide_banner", "-v", "info", "-i"])
|
||||
.arg(&w)
|
||||
.args(["-af", "astats=measure_perchannel=none", "-f", "null", "-"])
|
||||
.output().unwrap();
|
||||
.output()
|
||||
.unwrap();
|
||||
let t = String::from_utf8_lossy(&out.stderr).into_owned();
|
||||
let get = |k: &str| t.lines().find_map(|l| l.split_once(k).map(|x| x.1.trim().to_string()))
|
||||
.unwrap_or_else(|| "?".into());
|
||||
let get = |k: &str| {
|
||||
t.lines()
|
||||
.find_map(|l| l.split_once(k).map(|x| x.1.trim().to_string()))
|
||||
.unwrap_or_else(|| "?".into())
|
||||
};
|
||||
let dur = Command::new("ffprobe")
|
||||
.args(["-v","error","-show_entries","format=duration","-of","csv=p=0"])
|
||||
.arg(&w).output().ok()
|
||||
.args([
|
||||
"-v",
|
||||
"error",
|
||||
"-show_entries",
|
||||
"format=duration",
|
||||
"-of",
|
||||
"csv=p=0",
|
||||
])
|
||||
.arg(&w)
|
||||
.output()
|
||||
.ok()
|
||||
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
|
||||
.unwrap_or_default();
|
||||
println!(" sub-wave {i}: {:>9} B -> {:>10} s peak {:>10} rms {}",
|
||||
r.len(), dur, get("Peak level dB:"), get("RMS level dB:"));
|
||||
println!(
|
||||
" sub-wave {i}: {:>9} B -> {:>10} s peak {:>10} rms {}",
|
||||
r.len(),
|
||||
dur,
|
||||
get("Peak level dB:"),
|
||||
get("RMS level dB:")
|
||||
);
|
||||
let _ = std::fs::remove_file(&p);
|
||||
let _ = std::fs::remove_file(&w);
|
||||
}
|
||||
|
||||
@@ -24,13 +24,19 @@ fn main() {
|
||||
let (mut found, mut matches) = (0usize, Vec::new());
|
||||
for n in 0..=199u32 {
|
||||
let name = format!("BGM_{n:03}.slb");
|
||||
let Ok(riffs) = media::sound_bank_riffs(&src, &name) else { continue };
|
||||
if riffs.is_empty() { continue }
|
||||
let Ok(riffs) = media::sound_bank_riffs(&src, &name) else {
|
||||
continue;
|
||||
};
|
||||
if riffs.is_empty() {
|
||||
continue;
|
||||
}
|
||||
found += 1;
|
||||
let sizes: Vec<usize> = riffs.iter().map(|r| r.len()).collect();
|
||||
// Compare on the DATA payload the port sums, not on the RIFF wrapper:
|
||||
// a wrapper differs by header bytes and would hide a real collision.
|
||||
let near = sizes.iter().any(|s| WANT.iter().any(|w| s.abs_diff(*w) < 4096));
|
||||
let near = sizes
|
||||
.iter()
|
||||
.any(|s| WANT.iter().any(|w| s.abs_diff(*w) < 4096));
|
||||
if near {
|
||||
matches.push((name.clone(), sizes.clone()));
|
||||
}
|
||||
@@ -39,7 +45,10 @@ fn main() {
|
||||
for (n, s) in &matches {
|
||||
println!(" {n:<14} wave sizes {s:?}");
|
||||
}
|
||||
println!("\n {} bank(s) carry a wave within 4 KiB of {WANT:?}", matches.len());
|
||||
println!(
|
||||
"\n {} bank(s) carry a wave within 4 KiB of {WANT:?}",
|
||||
matches.len()
|
||||
);
|
||||
println!(" Exactly 1 means the census EXCLUDES alternatives and is a real third");
|
||||
println!(" leg. More than 1 means the byte match does not distinguish BGM_103,");
|
||||
println!(" and \"three legs\" is two. Zero means this reader cannot see the");
|
||||
|
||||
@@ -16,27 +16,39 @@
|
||||
//!
|
||||
//! This prints what the differences actually look like, so the reading is judged
|
||||
//! against the names rather than accepted as plausible.
|
||||
use sylpheed_formats::{pak, ratc, ui_layout};
|
||||
use std::collections::BTreeSet;
|
||||
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_DIALOG.pak")).expect("GP_DIALOG.pak");
|
||||
let sets: Vec<Option<BTreeSet<String>>> = ar.entries().iter().map(|e| {
|
||||
let by = ar.read(e).ok()?;
|
||||
if !ratc::is_ratc(&by) { return None }
|
||||
let b = ui_layout::parse_build(&by)?;
|
||||
Some(b.elements.iter().map(|el| el.name.clone()).collect())
|
||||
}).collect();
|
||||
let sets: Vec<Option<BTreeSet<String>>> = ar
|
||||
.entries()
|
||||
.iter()
|
||||
.map(|e| {
|
||||
let by = ar.read(e).ok()?;
|
||||
if !ratc::is_ratc(&by) {
|
||||
return None;
|
||||
}
|
||||
let b = ui_layout::parse_build(&by)?;
|
||||
Some(b.elements.iter().map(|el| el.name.clone()).collect())
|
||||
})
|
||||
.collect();
|
||||
|
||||
let (mut same, mut diff, mut pairs) = (0usize, 0usize, 0usize);
|
||||
let mut shown = 0;
|
||||
for i in (0..sets.len().saturating_sub(1)).step_by(2) {
|
||||
let (Some(a), Some(b)) = (&sets[i], &sets[i + 1]) else { continue };
|
||||
let (Some(a), Some(b)) = (&sets[i], &sets[i + 1]) else {
|
||||
continue;
|
||||
};
|
||||
pairs += 1;
|
||||
if a == b {
|
||||
same += 1;
|
||||
println!(" entries {i:>3}/{:<3} IDENTICAL sets, {} element(s)", i + 1, a.len());
|
||||
println!(
|
||||
" entries {i:>3}/{:<3} IDENTICAL sets, {} element(s)",
|
||||
i + 1,
|
||||
a.len()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
diff += 1;
|
||||
@@ -49,19 +61,37 @@ fn main() {
|
||||
if (10..=15).contains(&i) {
|
||||
let sp = |x: &BTreeSet<String>| x.iter().filter(|n| n.ends_with(".t32")).count();
|
||||
let stage = |x: &BTreeSet<String>| -> Vec<String> {
|
||||
let mut v: Vec<String> = x.iter().filter_map(|n| n.strip_prefix("pzstg")
|
||||
.and_then(|r| r.get(..2)).map(|s| s.to_string())).collect();
|
||||
v.sort(); v.dedup(); v
|
||||
let mut v: Vec<String> = x
|
||||
.iter()
|
||||
.filter_map(|n| {
|
||||
n.strip_prefix("pzstg")
|
||||
.and_then(|r| r.get(..2))
|
||||
.map(|s| s.to_string())
|
||||
})
|
||||
.collect();
|
||||
v.sort();
|
||||
v.dedup();
|
||||
v
|
||||
};
|
||||
println!(" entries {i:>3}/{:<3} stages {:?} vs {:?} sprites {} vs {}",
|
||||
i + 1, stage(a), stage(b), sp(a), sp(b));
|
||||
println!(
|
||||
" entries {i:>3}/{:<3} stages {:?} vs {:?} sprites {} vs {}",
|
||||
i + 1,
|
||||
stage(a),
|
||||
stage(b),
|
||||
sp(a),
|
||||
sp(b)
|
||||
);
|
||||
}
|
||||
if shown < 3 {
|
||||
shown += 1;
|
||||
let only_a: Vec<_> = a.difference(b).cloned().collect();
|
||||
let only_b: Vec<_> = b.difference(a).cloned().collect();
|
||||
println!(" entries {i:>3}/{:<3} differ: {} only-in-first, {} only-in-second",
|
||||
i + 1, only_a.len(), only_b.len());
|
||||
println!(
|
||||
" entries {i:>3}/{:<3} differ: {} only-in-first, {} only-in-second",
|
||||
i + 1,
|
||||
only_a.len(),
|
||||
only_b.len()
|
||||
);
|
||||
println!(" first : {:?}", &only_a[..only_a.len().min(4)]);
|
||||
println!(" second : {:?}", &only_b[..only_b.len().min(4)]);
|
||||
}
|
||||
@@ -71,12 +101,17 @@ fn main() {
|
||||
// different dialogs and the whole adjacent-pairing premise is wrong -- which
|
||||
// is a stronger statement than "the language reading is untested".
|
||||
let btns = |s: &Option<BTreeSet<String>>| -> usize {
|
||||
s.as_ref().map_or(0, |x| x.iter().filter(|n| n.contains("btn")).count())
|
||||
s.as_ref()
|
||||
.map_or(0, |x| x.iter().filter(|n| n.contains("btn")).count())
|
||||
};
|
||||
let mut mismatched = 0;
|
||||
for i in (0..sets.len().saturating_sub(1)).step_by(2) {
|
||||
if sets[i].is_none() || sets[i + 1].is_none() { continue }
|
||||
if btns(&sets[i]) != btns(&sets[i + 1]) { mismatched += 1 }
|
||||
if sets[i].is_none() || sets[i + 1].is_none() {
|
||||
continue;
|
||||
}
|
||||
if btns(&sets[i]) != btns(&sets[i + 1]) {
|
||||
mismatched += 1
|
||||
}
|
||||
}
|
||||
println!("\n adjacent pairs whose BUTTON COUNTS differ: {mismatched}");
|
||||
println!(" A language pair cannot. Every one of these is two different dialogs.");
|
||||
|
||||
@@ -26,40 +26,66 @@ fn main() {
|
||||
// with a different reader, because its whole content is an absence.
|
||||
const WANT: [i32; 4] = [259, 329, 399, 469];
|
||||
const TOL: i32 = 6;
|
||||
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 hits, mut scanned) = (0usize, 0usize);
|
||||
for path in &paks {
|
||||
let Ok(ar) = pak::PakArchive::open(path) else { continue };
|
||||
let arch = path.file_name().unwrap().to_string_lossy().to_string();
|
||||
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 };
|
||||
scanned += 1;
|
||||
// Any button-shaped record, not just `pcbtn`: a rival need not share the
|
||||
// naming convention, and restricting by name would answer a narrower
|
||||
// question than the one asked.
|
||||
let mut rows: Vec<(String, i32)> = b.elements.iter()
|
||||
.filter(|el| el.name.contains("btn"))
|
||||
.filter_map(|el| el.rest().map(|r| (el.name.clone(), r.y)))
|
||||
.collect();
|
||||
if rows.is_empty() { continue }
|
||||
rows.sort_by(|a, b| a.1.cmp(&b.1));
|
||||
let ys: Vec<i32> = rows.iter().map(|r| r.1).collect();
|
||||
let gaps: Vec<i32> = ys.windows(2).map(|w| w[1] - w[0]).collect();
|
||||
if rows.len() == 4 && ys.iter().zip(WANT.iter()).all(|(a, b)| (a - b).abs() <= TOL) {
|
||||
hits += 1;
|
||||
println!(" {arch} entry {i:>2} {} record(s): {}", rows.len(),
|
||||
rows.iter().map(|r| r.0.as_str()).collect::<Vec<_>>().join(" "));
|
||||
println!(" rows {ys:?} gaps {gaps:?}");
|
||||
let Ok(ar) = pak::PakArchive::open(path) else {
|
||||
continue;
|
||||
};
|
||||
let arch = path.file_name().unwrap().to_string_lossy().to_string();
|
||||
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;
|
||||
};
|
||||
scanned += 1;
|
||||
// Any button-shaped record, not just `pcbtn`: a rival need not share the
|
||||
// naming convention, and restricting by name would answer a narrower
|
||||
// question than the one asked.
|
||||
let mut rows: Vec<(String, i32)> = b
|
||||
.elements
|
||||
.iter()
|
||||
.filter(|el| el.name.contains("btn"))
|
||||
.filter_map(|el| el.rest().map(|r| (el.name.clone(), r.y)))
|
||||
.collect();
|
||||
if rows.is_empty() {
|
||||
continue;
|
||||
}
|
||||
rows.sort_by(|a, b| a.1.cmp(&b.1));
|
||||
let ys: Vec<i32> = rows.iter().map(|r| r.1).collect();
|
||||
let gaps: Vec<i32> = ys.windows(2).map(|w| w[1] - w[0]).collect();
|
||||
if rows.len() == 4
|
||||
&& ys
|
||||
.iter()
|
||||
.zip(WANT.iter())
|
||||
.all(|(a, b)| (a - b).abs() <= TOL)
|
||||
{
|
||||
hits += 1;
|
||||
println!(
|
||||
" {arch} entry {i:>2} {} record(s): {}",
|
||||
rows.len(),
|
||||
rows.iter()
|
||||
.map(|r| r.0.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
);
|
||||
println!(" rows {ys:?} gaps {gaps:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("\n {scanned} build(s) scanned across {} pak(s); {hits} match the",
|
||||
paks.len());
|
||||
println!(
|
||||
"\n {scanned} build(s) scanned across {} pak(s); {hits} match the",
|
||||
paks.len()
|
||||
);
|
||||
println!(" DIFFICULTY row signature within +/-{TOL} px.");
|
||||
println!(" Expected: exactly 2 -- the EN/JP pair. More means a RIVAL exists and");
|
||||
println!(" the geometric identification is not unique; fewer means this reader");
|
||||
|
||||
@@ -13,26 +13,43 @@ fn main() {
|
||||
let e = &ar.entries()[4]; // entry 4 = the English title
|
||||
let bundle = ar.read(e).expect("read");
|
||||
let b = ui_layout::parse_build(&bundle).expect("parse");
|
||||
println!("build has {} elements, {} records", b.elements.len(), b.records.len());
|
||||
println!(
|
||||
"build has {} elements, {} records",
|
||||
b.elements.len(),
|
||||
b.records.len()
|
||||
);
|
||||
let mut names: Vec<&String> = b.records.keys().collect();
|
||||
names.sort();
|
||||
println!("records: {names:?}");
|
||||
for el in &b.elements {
|
||||
if !el.name.starts_with("ptloop") { continue; }
|
||||
if !el.name.starts_with("ptloop") {
|
||||
continue;
|
||||
}
|
||||
let r = el.rest();
|
||||
println!("\nPARENT {} sprite={:?} -> rest scale {:?} rot {:?}", el.name, el.sprite,
|
||||
r.map(|r| (r.scale_x, r.scale_y)), r.map(|r| r.rotation_deg));
|
||||
println!(
|
||||
"\nPARENT {} sprite={:?} -> rest scale {:?} rot {:?}",
|
||||
el.name,
|
||||
el.sprite,
|
||||
r.map(|r| (r.scale_x, r.scale_y)),
|
||||
r.map(|r| r.rotation_deg)
|
||||
);
|
||||
if let Some(&(off, size)) = b.records.get(&el.name) {
|
||||
match ui_layout::parse_build(&bundle[off..off + size]) {
|
||||
Some(leaf) => {
|
||||
println!(" LEAF {} parses: {} element(s)", el.name, leaf.elements.len());
|
||||
println!(
|
||||
" LEAF {} parses: {} element(s)",
|
||||
el.name,
|
||||
leaf.elements.len()
|
||||
);
|
||||
for le in &leaf.elements {
|
||||
let lr = le.rest();
|
||||
println!(" {:<20} rest scale {:?} rot {:?} pos {:?}",
|
||||
println!(
|
||||
" {:<20} rest scale {:?} rot {:?} pos {:?}",
|
||||
le.name,
|
||||
lr.map(|r| (r.scale_x, r.scale_y)),
|
||||
lr.map(|r| r.rotation_deg),
|
||||
lr.map(|r| (r.x, r.y)));
|
||||
lr.map(|r| (r.x, r.y))
|
||||
);
|
||||
for k in &le.keyframes {
|
||||
println!(" t={:?} scale=({},{}) rot={} pos=({},{}) fade={:#010x} u4={} u8={}",
|
||||
k.time, k.scale_x, k.scale_y, k.rotation_deg, k.x, k.y,
|
||||
|
||||
@@ -26,15 +26,14 @@
|
||||
//! and adds the one they could not run: the same two, restricted to the records
|
||||
//! **this port actually animates**. A disc-wide 0.00 % violation rate says
|
||||
//! nothing about my six screens if all six sit in the exceptional tail.
|
||||
use sylpheed_formats::{pak, ratc, ui_layout};
|
||||
use std::collections::BTreeMap;
|
||||
use sylpheed_formats::{pak, ratc, ui_layout};
|
||||
|
||||
/// The records the port animates: the plate glow, the five menu focus records,
|
||||
/// and the title's two sweeps. Named rather than pattern-matched, because the
|
||||
/// point is to check the ones that are shipped, not the ones that match a glob.
|
||||
const SHIPPED: &[&str] = &[
|
||||
"ptbtn00f", "ptbtn01f", "ptbtn02f", "ptbtn03f", "ptbtn04f", "ptbtn05f",
|
||||
"ptloop01", "ptloop02",
|
||||
"ptbtn00f", "ptbtn01f", "ptbtn02f", "ptbtn03f", "ptbtn04f", "ptbtn05f", "ptloop01", "ptloop02",
|
||||
];
|
||||
|
||||
/// Which header word to read as the loop length. `0x08` is the decoded one;
|
||||
@@ -44,14 +43,18 @@ const SHIPPED: &[&str] = &[
|
||||
static mut OFFSET: usize = 8;
|
||||
|
||||
fn main() {
|
||||
let off: usize = std::env::args().find_map(|a| a.strip_prefix("--offset=")
|
||||
.and_then(|v| v.parse().ok())).unwrap_or(8);
|
||||
let off: usize = std::env::args()
|
||||
.find_map(|a| a.strip_prefix("--offset=").and_then(|v| v.parse().ok()))
|
||||
.unwrap_or(8);
|
||||
unsafe { OFFSET = off };
|
||||
println!(" reading the loop length at header +0x{off:02x}");
|
||||
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);
|
||||
@@ -59,14 +62,24 @@ fn main() {
|
||||
let mut shipped: BTreeMap<String, (i64, i64)> = 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 (rn, &(o, s)) in &b.records {
|
||||
if o + off + 4 > by.len() || o + s > by.len() { continue }
|
||||
if &by[o..o + 4] != b"RATC" { continue }
|
||||
if o + off + 4 > by.len() || o + s > by.len() {
|
||||
continue;
|
||||
}
|
||||
if &by[o..o + 4] != b"RATC" {
|
||||
continue;
|
||||
}
|
||||
// 🔴 THE FALSIFIER IS RUN AT NEIGHBOURING OFFSETS TOO. The
|
||||
// Decoder's struct-layout control showed that a homogeneous
|
||||
// repeated table type-checks at every field boundary, so an
|
||||
@@ -77,15 +90,28 @@ fn main() {
|
||||
// "confirmation" without asking whether it discriminates the
|
||||
// OFFSET or merely the file.
|
||||
let len = u32::from_be_bytes(by[o + off..o + off + 4].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 } // static: declares no cycle at all
|
||||
.max()
|
||||
.unwrap_or(0) as i64;
|
||||
if maxt == 0 {
|
||||
continue;
|
||||
} // static: declares no cycle at all
|
||||
total += 1;
|
||||
let slack = len - maxt;
|
||||
*slack_hist.entry(slack).or_default() += 1;
|
||||
if slack == 0 { exact += 1 } else if slack > 0 { holds += 1 } else { violations += 1 }
|
||||
if slack == 0 {
|
||||
exact += 1
|
||||
} else if slack > 0 {
|
||||
holds += 1
|
||||
} else {
|
||||
violations += 1
|
||||
}
|
||||
let stem = rn.trim_end_matches(".rat");
|
||||
if SHIPPED.contains(&stem) {
|
||||
shipped.entry(stem.to_string()).or_insert((len, maxt));
|
||||
@@ -95,36 +121,68 @@ fn main() {
|
||||
}
|
||||
|
||||
println!("disc-wide, records with timed keyframes: {total}");
|
||||
println!(" +08 == max t (exact) : {exact:5} {:5.1} %", pc(exact, total));
|
||||
println!(" +08 > max t (a hold) : {holds:5} {:5.1} %", pc(holds, total));
|
||||
println!(" +08 < max t <- FALSIFIER : {violations:5} {:5.2} %", pc(violations, total));
|
||||
println!(
|
||||
" +08 == max t (exact) : {exact:5} {:5.1} %",
|
||||
pc(exact, total)
|
||||
);
|
||||
println!(
|
||||
" +08 > max t (a hold) : {holds:5} {:5.1} %",
|
||||
pc(holds, total)
|
||||
);
|
||||
println!(
|
||||
" +08 < max t <- FALSIFIER : {violations:5} {:5.2} %",
|
||||
pc(violations, total)
|
||||
);
|
||||
println!("\nslack distribution, most common first:");
|
||||
let mut h: Vec<_> = slack_hist.iter().collect();
|
||||
h.sort_by_key(|&(_, n)| std::cmp::Reverse(*n));
|
||||
for (k, n) in h.iter().take(8) { println!(" slack {k:>6} : {n}"); }
|
||||
for (k, n) in h.iter().take(8) {
|
||||
println!(" slack {k:>6} : {n}");
|
||||
}
|
||||
|
||||
println!("\nthe records THIS PORT animates:");
|
||||
println!(" {:<12} {:>6} {:>7} {:>7}", "record", "+0x08", "max t", "slack");
|
||||
println!(
|
||||
" {:<12} {:>6} {:>7} {:>7}",
|
||||
"record", "+0x08", "max t", "slack"
|
||||
);
|
||||
let (mut ship_exact, mut ship_hold, mut ship_bad) = (0, 0, 0);
|
||||
for (n, (len, maxt)) in &shipped {
|
||||
let slack = len - maxt;
|
||||
match slack { 0 => ship_exact += 1, s if s > 0 => ship_hold += 1, _ => ship_bad += 1 }
|
||||
println!(" {n:<12} {len:>6} {maxt:>7} {slack:>7}{}",
|
||||
if slack < 0 { " 🔴 FALSIFIED" } else { "" });
|
||||
match slack {
|
||||
0 => ship_exact += 1,
|
||||
s if s > 0 => ship_hold += 1,
|
||||
_ => ship_bad += 1,
|
||||
}
|
||||
println!(
|
||||
" {n:<12} {len:>6} {maxt:>7} {slack:>7}{}",
|
||||
if slack < 0 { " 🔴 FALSIFIED" } else { "" }
|
||||
);
|
||||
}
|
||||
println!("\n shipped: {ship_exact} exact, {ship_hold} hold, {ship_bad} falsified");
|
||||
if shipped.len() < SHIPPED.len() {
|
||||
let missing: Vec<_> = SHIPPED.iter().filter(|s| !shipped.contains_key(**s)).collect();
|
||||
let missing: Vec<_> = SHIPPED
|
||||
.iter()
|
||||
.filter(|s| !shipped.contains_key(**s))
|
||||
.collect();
|
||||
println!(" ⚠️ not found on the disc: {missing:?} -- a name the port ships and");
|
||||
println!(" this control never checked is worse than a violation it found.");
|
||||
}
|
||||
println!("\n verdict: {}", if ship_bad > 0 {
|
||||
"🔴 the reading fails on a record the port animates -- do NOT adopt"
|
||||
} else if ship_hold == 0 {
|
||||
"⚠️ every shipped record is exact, so this port cannot tell loop length\n from max keyframe time -- adopting 120 would change nothing here"
|
||||
} else {
|
||||
"✅ falsifier clean and the field is non-trivial ON THE SHIPPED SET"
|
||||
});
|
||||
println!(
|
||||
"\n verdict: {}",
|
||||
if ship_bad > 0 {
|
||||
"🔴 the reading fails on a record the port animates -- do NOT adopt"
|
||||
} else if ship_hold == 0 {
|
||||
"⚠️ every shipped record is exact, so this port cannot tell loop length\n from max keyframe time -- adopting 120 would change nothing here"
|
||||
} else {
|
||||
"✅ falsifier clean and the field is non-trivial ON THE SHIPPED SET"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
fn pc(n: usize, d: usize) -> f64 { if d == 0 { 0.0 } else { 100.0 * n as f64 / d as f64 } }
|
||||
fn pc(n: usize, d: usize) -> f64 {
|
||||
if d == 0 {
|
||||
0.0
|
||||
} else {
|
||||
100.0 * n as f64 / d as f64
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,29 +12,47 @@ 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 records, mut in_bounds, mut magic, mut parsed, mut timed) = (0, 0, 0, 0, 0);
|
||||
let (mut untimed, mut all_at_zero) = (0usize, 0usize);
|
||||
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 (_, &(o, s)) in &b.records {
|
||||
records += 1;
|
||||
if o + 12 > by.len() || o + s > by.len() { continue }
|
||||
if o + 12 > by.len() || o + s > by.len() {
|
||||
continue;
|
||||
}
|
||||
in_bounds += 1;
|
||||
if &by[o..o + 4] != b"RATC" { continue }
|
||||
if &by[o..o + 4] != b"RATC" {
|
||||
continue;
|
||||
}
|
||||
magic += 1;
|
||||
let Some(lb) = ui_layout::parse_build(&by[o..o + s]) else { continue };
|
||||
let Some(lb) = ui_layout::parse_build(&by[o..o + s]) else {
|
||||
continue;
|
||||
};
|
||||
parsed += 1;
|
||||
let maxt = lb.elements.iter()
|
||||
let maxt = lb
|
||||
.elements
|
||||
.iter()
|
||||
.flat_map(|el| el.keyframes.iter().filter_map(|k| k.time))
|
||||
.max().unwrap_or(0);
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
// 🔴 `maxt == 0` merges two different populations, and the
|
||||
// Decoder's cause -- `.max()` returning `Some(0)` -- is only one
|
||||
// of them. A record with NO timed keyframe has no largest
|
||||
@@ -43,10 +61,16 @@ fn main() {
|
||||
// content. Both of us called all 1 530 "the question has no
|
||||
// meaning"; that is true of one group and an assumption about
|
||||
// the other.
|
||||
let any_timed = lb.elements.iter()
|
||||
let any_timed = lb
|
||||
.elements
|
||||
.iter()
|
||||
.any(|el| el.keyframes.iter().any(|k| k.time.is_some()));
|
||||
if maxt == 0 {
|
||||
if any_timed { all_at_zero += 1 } else { untimed += 1 }
|
||||
if any_timed {
|
||||
all_at_zero += 1
|
||||
} else {
|
||||
untimed += 1
|
||||
}
|
||||
continue;
|
||||
}
|
||||
timed += 1;
|
||||
@@ -55,8 +79,10 @@ fn main() {
|
||||
}
|
||||
println!(" records declared by parse_build : {records}");
|
||||
println!(" within the entry's bounds : {in_bounds}");
|
||||
println!(" carrying the RATC magic : {magic} <- {} dropped here",
|
||||
in_bounds - magic);
|
||||
println!(
|
||||
" carrying the RATC magic : {magic} <- {} dropped here",
|
||||
in_bounds - magic
|
||||
);
|
||||
println!(" parsing as a nested build : {parsed}");
|
||||
println!(" with a largest keyframe time > 0: {timed}");
|
||||
println!(" of the {} excluded:", untimed + all_at_zero);
|
||||
|
||||
@@ -16,13 +16,25 @@ fn main() {
|
||||
let (mut total, mut hits, mut multipose) = (0usize, 0usize, 0usize);
|
||||
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 };
|
||||
if !ratc::is_ratc(&by) {
|
||||
continue;
|
||||
}
|
||||
let Some(b) = ui_layout::parse_build(&by) else {
|
||||
continue;
|
||||
};
|
||||
for (name, &(o, s)) in &b.records {
|
||||
if o + 12 > by.len() || o + s > by.len() || &by[o..o + 4] != b"RATC" { continue }
|
||||
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);
|
||||
if o + 12 > by.len() || o + s > by.len() || &by[o..o + 4] != b"RATC" {
|
||||
continue;
|
||||
}
|
||||
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);
|
||||
let len = ui_layout::loop_length_units(&by[o..o + s]).unwrap_or(0);
|
||||
total += 1;
|
||||
if maxt == 0 && len > 0 {
|
||||
@@ -31,10 +43,19 @@ fn main() {
|
||||
// to move between. All-at-t=0 with a single keyframe per element
|
||||
// is visually inert however it is played.
|
||||
let kf: usize = lb.elements.iter().map(|el| el.keyframes.len()).sum();
|
||||
let multi = lb.elements.iter().filter(|el| el.keyframes.len() > 1).count();
|
||||
if multi > 0 { multipose += 1 }
|
||||
println!(" entry {i:>2} {name:<16} {len}-unit cycle, {kf} keyframe(s) \
|
||||
across {} element(s), {multi} with >1 pose", lb.elements.len());
|
||||
let multi = lb
|
||||
.elements
|
||||
.iter()
|
||||
.filter(|el| el.keyframes.len() > 1)
|
||||
.count();
|
||||
if multi > 0 {
|
||||
multipose += 1
|
||||
}
|
||||
println!(
|
||||
" entry {i:>2} {name:<16} {len}-unit cycle, {kf} keyframe(s) \
|
||||
across {} element(s), {multi} with >1 pose",
|
||||
lb.elements.len()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,11 @@ fn main() {
|
||||
continue;
|
||||
};
|
||||
let riffs = media::voice_region_riffs(&src, s, e).expect("riffs");
|
||||
println!("{movie}: region [{s}, {e}) = {} bytes, {} chunk(s)", e - s, riffs.len());
|
||||
println!(
|
||||
"{movie}: region [{s}, {e}) = {} bytes, {} chunk(s)",
|
||||
e - s,
|
||||
riffs.len()
|
||||
);
|
||||
for (i, r) in riffs.iter().enumerate() {
|
||||
let p = std::env::temp_dir().join(format!("vc_{movie}_{i}.xma.wav"));
|
||||
std::fs::write(&p, r).unwrap();
|
||||
@@ -28,7 +32,14 @@ fn main() {
|
||||
.arg(&w)
|
||||
.output();
|
||||
let out = Command::new("ffprobe")
|
||||
.args(["-v", "error", "-show_entries", "format=duration", "-of", "csv=p=0"])
|
||||
.args([
|
||||
"-v",
|
||||
"error",
|
||||
"-show_entries",
|
||||
"format=duration",
|
||||
"-of",
|
||||
"csv=p=0",
|
||||
])
|
||||
.arg(&w)
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user