re: F2 -- no gain field in tables.pak, with the two unchecked sites named
The port has no gain anywhere and confirm sits 3 dB above the music. Asked the disc rather than choosing a number. Five audio-bearing objects in tables.pak, zero tokens matching VOL GAIN LEVEL DB ATTEN AMP MIX LOUD. Control passes: the same matcher finds 38 SE_UI hits, so "0 hits" is not a broken matcher. The negative is stronger than a name search usually is, and I expected it not to be. Dumping the schema shows the token stream is value-then-key pairs -- "40, LINE_PITCH", "0, Y_OFFSET_ANALOG_STICK" -- so NUMBERS ARE TOKENS AND THEY CARRY NAMES. A gain in this format would have a name, and the name search covers exactly the space where it would live. That turns "I did not find one" into "one is not there in this file". Reach, and it is why this is undecodable rather than decoded: I did not check the .slb bank headers, which is the other conventional home for a per-wave gain beside a wave index -- and is where the play-test's own framing points. No .slb exists as a loose file on the extracted disc, so it costs a pak extraction I did not have budget for; it is the first thing the next attempt should do. Nor did I check the executable, where a mix could be immediates in the sound-play path around sub_821C5580. So this is not "the mix is not on the disc". It is "the mix is not in the table where a cue's fields live". Pointer, not a finding: object #15 lists po_sound_scr.prt -> SOUND among GP_OPTIONS' screens, so a user-facing sound options screen exists and at least one volume is runtime state. Port keeps authoring nothing yet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jc4pciRArGHfxGGhEbwp5t
This commit is contained in:
65
crates/sylpheed-formats/examples/sound_cue_fields.rs
Normal file
65
crates/sylpheed-formats/examples/sound_cue_fields.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
//! F2 — is a per-cue or per-bus GAIN on the disc?
|
||||
//!
|
||||
//! The port has no gain value anywhere in its export: `confirm` peaks at
|
||||
//! −0.0 dBFS and sits 3 dB above the music. A cue record commonly carries a
|
||||
//! volume beside its wave index. This asks the disc directly rather than
|
||||
//! choosing a number.
|
||||
//!
|
||||
//! Method: dump every token of every `tables.pak` object whose tokens mention
|
||||
//! SOUND/BANK/SE/BGM, so a gain field would appear as a token if one exists.
|
||||
//! ⚠️ A NEGATIVE here is only as good as its coverage, so this prints the token
|
||||
//! count per object and does not filter — a field missed by a filter would read
|
||||
//! exactly like a field that is not there.
|
||||
//!
|
||||
//! cargo run --release -p sylpheed-formats --example sound_cue_fields -- $SYLPHEED_DISC
|
||||
use sylpheed_formats::{idxd::IdxdObject, pak::PakArchive};
|
||||
|
||||
fn main() {
|
||||
let a: Vec<String> = std::env::args().collect();
|
||||
let root = a.get(1).cloned().unwrap_or_else(|| std::env::var("SYLPHEED_DISC").unwrap());
|
||||
let arc = PakArchive::open(format!("{root}/dat/tables.pak")).unwrap();
|
||||
|
||||
// Anything a gain would plausibly be called, plus the audio nouns.
|
||||
const GAINY: &[&str] = &["VOL", "GAIN", "LEVEL", "DB", "ATTEN", "AMP", "MIX", "LOUD"];
|
||||
let mut audio_objs = 0usize;
|
||||
let mut gain_hits: Vec<(usize, String)> = Vec::new();
|
||||
|
||||
for (i, e) in arc.entries().iter().enumerate() {
|
||||
let Ok(b) = arc.read(e) else { continue };
|
||||
let Ok(o) = IdxdObject::parse(&b) else { continue };
|
||||
let t = o.tokens();
|
||||
let up: Vec<String> = t.iter().map(|s| s.to_uppercase()).collect();
|
||||
let is_audio = up.iter().any(|s| {
|
||||
s.contains("SOUND") || s.contains("BANK_") || s.starts_with("SE_") || s.starts_with("BGM_")
|
||||
});
|
||||
if !is_audio { continue }
|
||||
audio_objs += 1;
|
||||
println!("audio object #{i}: schema {:08x}, {} tokens", o.schema_hash, t.len());
|
||||
for (j, tok) in up.iter().enumerate() {
|
||||
if GAINY.iter().any(|g| tok.contains(g)) {
|
||||
gain_hits.push((i, t[j].clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("\naudio-bearing objects examined: {audio_objs}");
|
||||
println!("tokens matching {GAINY:?}: {}", gain_hits.len());
|
||||
for (i, tok) in &gain_hits {
|
||||
println!(" object #{i}: {tok}");
|
||||
}
|
||||
if gain_hits.is_empty() {
|
||||
println!("\n==> NO gain-like token in any audio object of tables.pak.");
|
||||
}
|
||||
|
||||
// CONTROL: the search must be able to FIND a token when one is present.
|
||||
// Without this, "no hits" is indistinguishable from a broken matcher.
|
||||
let mut ctrl = 0usize;
|
||||
for e in arc.entries() {
|
||||
let Ok(b) = arc.read(e) else { continue };
|
||||
let Ok(o) = IdxdObject::parse(&b) else { continue };
|
||||
ctrl += o.tokens().iter().filter(|s| s.to_uppercase().contains("SE_UI")).count();
|
||||
}
|
||||
println!("\nCONTROL — the same matcher looking for a token known to exist (\"SE_UI\"): {ctrl} hits");
|
||||
println!(" {}", if ctrl > 0 { "PASS: the matcher finds tokens that are there" }
|
||||
else { "FAIL: matcher is broken, the negative above means nothing" });
|
||||
}
|
||||
Reference in New Issue
Block a user