Files
Sylpheed/crates/sylpheed-formats/examples/dialog_pair_identity.rs
sylph-decoder 7626f9e646 re: GP_DIALOG 0/1 is a byte-identical duplicate; 2/3 is a structural pair
The two adjacent pairs with identical element sets split. 0/1 are the same 59 810
bytes stored twice -- a duplicate, not a language pair. 2/3, the DIFFICULTY build,
differ in size and in 2.77 % of bytes from offset 0x1BB while sharing every element
name, which is what a language pair looks like.

Control: entries 10/11, known to be two different dialogs, differ in 54.90 % of the
common prefix, so the comparator separates unrelated dialogs.

Supported, not proven, with the untested step named: I have not captured DIFFICULTY
in ja. That the two are ENGLISH and JAPANESE rests on the disc's convention, not on
a capture of this screen. This partially restores a claim I withdrew, at lower
strength than the original phrasing.

Also records a refutation attempt on the port's BGM_103 exclusion: it survives and
is tighter than they stated -- of 32 census rows, exactly one bank carries EITHER
wave size, not merely both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-31 03:12:59 +00:00

48 lines
2.1 KiB
Rust

//! Are `GP_DIALOG` entries 0/1 and 2/3 a LANGUAGE PAIR or a DUPLICATE?
//!
//! They are the only two adjacent pairs in that archive with identical element
//! sets; every other adjacent pair is two unrelated dialogs. Left open as
//! "untested" — identical element names are equally consistent with a language
//! pair (same layout, different glyphs baked into the textures) and with a
//! byte-for-byte duplicate.
//!
//! The bytes decide it: identical entries are a duplicate; entries that share
//! every element name but differ in payload are a language pair.
//!
//! CONTROL: entries 10/11, known to be two DIFFERENT dialogs (stage 10 vs stage
//! 02), must come out as differing — and by a lot. A comparator that cannot
//! 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;
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 {
println!("{what}: unreadable");
return;
};
let same_len = x.len() == y.len();
let n = x.len().min(y.len());
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);
match first {
None if same_len => println!(" => BYTE-IDENTICAL — a duplicate"),
None => println!(" => one is a prefix of the other"),
Some(o) => println!(" => first difference at offset 0x{o:X}"),
}
}
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, 0, 1, "entries 0/1");
cmp(&ar, 2, 3, "entries 2/3 — the DIFFICULTY build");
}