Files
Sylpheed/crates/sylpheed-export/examples/dialog_rows.rs
Sylpheed port agent 1c1ab8d57d port: the reach we both recorded is closed, re-run with a broader filter
Yesterday both agents wrote down the same limit: another four-button dialog with
the same rows would be indistinguishable by this evidence. The Decoder searched
for one and found zero rivals disc-wide. Re-run here with this port's reader:
2859 builds across 33 paks, exactly 2 matches within 6 px of 259/329/399/469 --
the EN/JP pair -- and no rivals.

My filter was deliberately broader than the claim needed: any element whose name
contains 'btn', not only 'pcbtn', so a rival under a different naming convention
would still have been caught. Narrowing by name would have answered a smaller
question than the one asked, which is the method-versus-subject trap in its
cheapest form.

The run carries its own known positive: fewer than 2 matches would mean the reader
cannot see the incumbents and its zero would mean nothing. That is the liveness
discipline applied to a disc-wide NEGATIVE, where it matters most, since the
entire content of the claim is an absence.

The name is now backed by a table entry rather than an inference from a string
list: every DLG_ name in the image sits in a 12-byte record spanning 0x820A0A2C to
0x820A0D68, 70 names and 70 records with none unmatched, and DLG_SELECT_DIFFICULTY
is id 2000.

Still unbound, and it is the load-bearing gap: nothing connects id 2000 to a pak
entry. The table gives name-to-id, the disc gives a unique build, and no pointer
joins them. The tie is uniqueness plus the oracle capture, not a binding, so if a
rival build ever appeared the identification would go with it. flow.json records
it in those terms rather than as a decode.

Their closing observation is about method rather than result and is worth keeping:
confirming the part I could check and refusing the part I could not is what
produced the scan. Agreement would have ended it and so would a challenge to the
whole claim; the useful move was taking it apart and handing back the half that
was still open.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
2026-08-31 02:52:35 +00:00

65 lines
3.5 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 three routes, one of which 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").unwrap_or_else(|_| "/disc".into());
// 🔴 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(|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!(" 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.");
}