Finishes #16 in the three places its earlier remedies missed. `tests/`: the last four local `disc_root()` copies now use `tests/common`, and with them goes the one real hardcoded fallback — `ui_keyframe_record_disc.rs` fell back to an absolute path on one machine, which made `unset SYLPHEED_DISC` a no-op there. Control: with the corpus absent that suite now finishes in 0.00s instead of 57.55s, so it skips rather than finding a disc of its own. `examples/`: seventeen examples defaulted to `/disc`, the mount point inside the CI container. Redundant there — `docker/ci/run` sets `SYLPHEED_DISC=/disc` — and wrong everywhere else, where a missing corpus turned into a file-not-found against a path that has never existed on the host. They now name the variable to set, like the other hundred examples already did. `docker/ci/run`: mount `$SYLPHEED_RES3D` and `$SYLPHEED_ISO` alongside the disc. Only the disc was mounted, so an in-container run sat out the res3d and iso suites while looking like a full one — the defect this issue is about, in the runner itself. Measured in the container on this desktop with all three corpora present: 45 suites / 377 passed / 0 failed / 14 ignored, and `sylpheed-corpus-report.txt` now reports PRESENT for all three rather than for the disc alone. Refs #16. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
95 lines
4.3 KiB
Rust
95 lines
4.3 KiB
Rust
//! Independent check of "DIFFICULTY is a dialog: GP_DIALOG entries 2/3".
|
|
//!
|
|
//! The Decoder identified `DLG_SELECT_DIFFICULTY` as `GP_DIALOG.pak` entries 2/3
|
|
//! by TWO arguments, one of them compound — corrected from "three routes", which
|
|
//! was taking credit for the exclusion scan. The image leg names no entry, and
|
|
//! the disc and oracle legs are one argument, since the capture is compared
|
|
//! against the disc's rows. One of them is button count and geometry. That half is
|
|
//! readable from the disc with this port's own reader, so it is checked here
|
|
//! rather than taken on their word — the same form as re-deriving `ptbtn11`'s
|
|
//! row order from my export when they offered it.
|
|
//!
|
|
//! ⚠️ What this CANNOT check is their binding claim, and they flagged it first:
|
|
//! entries 2/3 are identified by button count and geometry, **not** by a binding
|
|
//! from the `DLG_` name to a pak entry. Another four-button dialog with the same
|
|
//! rows would be indistinguishable by this evidence. Reproducing the geometry
|
|
//! confirms the geometry; it does not name the screen.
|
|
use sylpheed_formats::{pak, ratc, ui_layout};
|
|
|
|
fn main() {
|
|
let root =
|
|
std::env::var("SYLPHEED_DISC").expect("set SYLPHEED_DISC to the extracted disc root");
|
|
// 🔴 WIDENED 2026-08-31 to every pak, to check the Decoder's rival search
|
|
// independently. They report zero four-button builds within 6 px of
|
|
// 259/329/399/469 anywhere on the disc, which turns "another dialog with
|
|
// these rows would be indistinguishable" from a standing reach into a
|
|
// bounded one. A disc-wide negative is exactly the claim worth re-running
|
|
// 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();
|
|
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_key(|r| r.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!(" 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");
|
|
println!(" cannot see the incumbents and its zero would mean nothing.");
|
|
}
|