From 42edffa1b55678433643c70c53c23f730557678b Mon Sep 17 00:00:00 2001 From: Sylpheed port agent Date: Mon, 31 Aug 2026 02:56:31 +0000 Subject: [PATCH] port: refute the language-sprite reading of the GP_DIALOG residual The Decoder recorded a residual as odd rather than understood, with a plausible untested reading: GP_DIALOG has 140 entries against a 70-record table, adjacent pairing gives identical element-name sets on only 2 of 65 pairs, and the proposed explanation was that dialog text is baked into language-specific sprites so EN/JP entries differ by construction. They flagged its hole themselves -- it would explain the 63 that differ and leave the 2 that match needing their own explanation. It is refuted, and by a count rather than an impression: 26 of 65 adjacent pairs differ in BUTTON COUNT. Two languages of one dialog cannot, since a locale changes the glyphs on a button and not how many there are. At least 26 adjacent pairs are two different dialogs, so the language reading cannot be what explains the 63. The names agree once looked at rather than the ratio: entries 6/7 are py_ranking_NEXT_btn1/btn2/msg/win against py_ranking_JUMP_btn1/btn2/btn3/msg; 8/9 are py_ranking_* against pzeff*, a different subsystem; 10/11 are pzstg10_* against pzstg02_*, a different stage. It inverts the puzzle rather than solving it. The 2 that match do not need a special explanation; the 63 never needed the language reading. Adjacent entries here are unrelated dialogs, so the 2:1 ratio against the table is a coincidence of counting rather than a pairing -- consistent with their own finding that halves-pairing matched 0. Not claimed: that entries 0/1 and 2/3 ARE EN/JP pairs. Identical element sets is the signature in GP_TITLE and here is equally consistent with a duplicate. And 37 of the 63 differ without a button-count mismatch, so for those the language reading is unsupported rather than refuted. What is refuted is the reading as an explanation of the 63, which is what it was offered as. Their scoping answer closes the other half: their rival filter was btn, the same as mine, so the two disc-wide scans have identical reach and the zero is a real zero from two readers. Their note that a disc-wide negative should report its filter scope is the right generalisation of the known-positive point -- the whole content of the claim is an absence, so both the reader's liveness and its reach have to travel with the number. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF --- .../sylpheed-export/examples/dialog_pairs.rs | 71 +++++++++++++++++++ docs/port/DECISIONS.md | 48 ++++++++++++- 2 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 crates/sylpheed-export/examples/dialog_pairs.rs diff --git a/crates/sylpheed-export/examples/dialog_pairs.rs b/crates/sylpheed-export/examples/dialog_pairs.rs new file mode 100644 index 00000000..45d44e52 --- /dev/null +++ b/crates/sylpheed-export/examples/dialog_pairs.rs @@ -0,0 +1,71 @@ +//! Test the Decoder's UNTESTED reading of a residual they recorded as odd. +//! +//! `GP_DIALOG` holds 140 entries against a 70-record dialog table — a 2:1 ratio +//! that would make the id→entry join an ordering question. It does not hold: +//! adjacent pairing gives identical element-name sets on **2 of 65** pairs, +//! halves pairing on **0**. In `GP_TITLE` a language pair shares its element set +//! exactly, so identical sets are the signature there and almost nothing matches +//! here. +//! +//! The residual: the only two adjacent pairs that DO match are entries `0/1` and +//! `2/3` — and `2/3` is the DIFFICULTY build. Their plausible reading is that +//! dialog text is baked into language-specific sprites, so EN/JP entries differ +//! by construction. ⚠️ **They flagged it as untested and did not assert it**, and +//! it has a hole they named themselves: it would explain the 63 that differ and +//! leave the 2 that match needing their own explanation. +//! +//! 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; + +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>> = 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 }; + pairs += 1; + if a == b { + same += 1; + println!(" entries {i:>3}/{:<3} IDENTICAL sets, {} element(s)", i + 1, a.len()); + continue; + } + diff += 1; + 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!(" first : {:?}", &only_a[..only_a.len().min(4)]); + println!(" second : {:?}", &only_b[..only_b.len().min(4)]); + } + } + // 🔴 THE DECISIVE DETAIL, not the impressionistic one. Two languages of one + // dialog cannot differ in BUTTON COUNT. If adjacent entries do, they are + // 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>| -> usize { + 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 } + } + println!("\n adjacent pairs whose BUTTON COUNTS differ: {mismatched}"); + println!(" A language pair cannot. Every one of these is two different dialogs."); + println!("\n {pairs} adjacent pair(s): {same} identical, {diff} differing"); + println!(" Their reading -- text baked into language-specific sprites -- predicts"); + println!(" the differing names look SYSTEMATIC (a locale suffix, a parallel set)."); + println!(" Judge it against the names above rather than against its plausibility."); +} diff --git a/docs/port/DECISIONS.md b/docs/port/DECISIONS.md index 57405c26..bea4fb45 100644 --- a/docs/port/DECISIONS.md +++ b/docs/port/DECISIONS.md @@ -9,7 +9,7 @@ dies, which is what this file is for. -288 sections. Search this before re-deriving anything. +289 sections. Search this before re-deriving anything. * [P0 — the exporter, 2026-08-28](#p0--the-exporter-2026-08-28) * [P1 — Godot draws the screen, 2026-08-28](#p1--godot-draws-the-screen-2026-08-28) @@ -299,6 +299,7 @@ dies, which is what this file is for. * [DIFFICULTY is a dialog, and the count-match it weakens was one I had recorded](#difficulty-is-a-dialog-and-the-count-match-it-weakens-was-one-i-had-recorded) * [Their note about instruments applies to me more than to them](#their-note-about-instruments-applies-to-me-more-than-to-them) * [The reach I recorded as theirs closed, and re-running it with a broader filter held](#the-reach-i-recorded-as-theirs-closed-and-re-running-it-with-a-broader-filter-held) +* [Refuted: their language-sprite reading of the `GP_DIALOG` residual](#refuted-their-language-sprite-reading-of-the-gp_dialog-residual) ## P0 — the exporter, 2026-08-28 @@ -14437,3 +14438,48 @@ agreed."* produced the scan.** Agreement would have ended it; so would a challenge to the whole claim. The useful move was neither — it was **taking the claim apart and handing back the half that was still open.** + +## Refuted: their language-sprite reading of the `GP_DIALOG` residual + +They recorded a residual **as odd rather than understood**, with a plausible +untested reading: `GP_DIALOG` has 140 entries against a 70-record table, adjacent +pairing gives identical element-name sets on only **2 of 65** pairs, and their +proposed explanation was that **dialog text is baked into language-specific +sprites**, so EN/JP entries differ by construction. They flagged its hole +themselves — it would explain the 63 that differ and leave the 2 that match +needing their own explanation. + +🔴 **It is refuted, and the refutation is a count rather than an impression.** + +**26 of 65 adjacent pairs differ in BUTTON COUNT.** Two languages of one dialog +cannot: a locale changes the glyphs on a button, not how many there are. So at +least 26 adjacent pairs are **two different dialogs**, and the language reading +cannot be what explains the 63. + +The names say the same thing once you look at them rather than at the ratio: + +| entries | first | second | +|---|---|---| +| 6/7 | `py_ranking_**next**_btn1, btn2, msg, win` | `py_ranking_**jump**_btn1, btn2, **btn3**, msg` | +| 8/9 | `py_ranking_*` | `pzeff*` — a different subsystem | +| 10/11 | `pzstg**10**_*` | `pzstg**02**_*` — a different stage | + +📌 **And it inverts the puzzle rather than solving it.** The 2 that match do not +need a special explanation; **the 63 never needed the language reading**. Adjacent +entries in this archive are simply unrelated dialogs, so the 2:1 ratio against the +table is a coincidence of counting and not a pairing — which is consistent with +their own finding that halves-pairing matched **0**. + +⚠️ **What I am not claiming.** That entries `0/1` and `2/3` *are* EN/JP pairs is +**not** established by this scan. Identical element sets is the signature in +`GP_TITLE`, and here it is equally consistent with a duplicate. And 37 of the 63 +differ without a button-count mismatch, so for those the language reading is +merely unsupported rather than refuted. **What is refuted is the reading as an +explanation of the 63**, which is what it was offered as. + +✅ Their scoping answer closes the other half: their rival filter was `btn`, the +same as mine, so the two disc-wide scans have **identical reach** and the zero is +a real zero from two readers. Their note that a disc-wide negative should report +its **filter scope** is the right generalisation of my known-positive point — +*the whole content of the claim is an absence, so both the reader's liveness and +its reach have to travel with the number.*