Drove the retail game headless (Canary + lavapipe, vgamepad) to the Ready Room Arsenal and read the Gallery-Mode DATA SHEET for every weapon the 5%-progress save reveals. Two UI->field mappings are CONFIRMED against weapons whose values ARE on disc: Ammo Capacity == LoadingCount (9/9 exact matches) Max. Lock Ons == TriggerShotCount (FALCON 9AM 12 == wep_02; BUZZARD 22 == wep_04) That makes the panel an oracle for fields the disc leaves defaulted, and it is shown for undeveloped weapons too, so no points need spending. Recovered TriggerShotCount for wep_05 and wep_60 (both 4, on-disc absent), and bracketed the defaulted Power / MaximumRange via the Range/Damage letter classes. Ruled out first: the defaults are not in hidden/DefTables.pak (no WEAPON-schema objects there) nor in the localized GP_MAIN_GAME_* duplicates. Also adds the two analysis examples that produce the shopping list of defaulted fields, the screenshot harness used to drive the menus, and the evidence PNGs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
51 lines
1.8 KiB
Rust
51 lines
1.8 KiB
Rust
//! Scratch analysis: for one IDXD key, list every object that DECLARES it and
|
|
//! whether it carries a value on disc or is left at the title-code default.
|
|
//!
|
|
//! Run: cargo run -p sylpheed-formats --example default_owners -- <KEY> [KEY...]
|
|
|
|
use sylpheed_formats::idxd::IdxdObject;
|
|
use sylpheed_formats::pak::PakArchive;
|
|
|
|
fn is_number(s: &str) -> bool {
|
|
let s = s.strip_suffix(['f', 'F']).unwrap_or(s);
|
|
let t = s.strip_prefix(['-', '+']).unwrap_or(s);
|
|
!t.is_empty() && t.chars().all(|c| c.is_ascii_digit() || c == '.')
|
|
}
|
|
fn is_key(s: &str) -> bool {
|
|
!s.is_empty()
|
|
&& s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
|
|
&& s.chars().next().is_some_and(|c| c.is_ascii_alphabetic() || c == '_')
|
|
&& !is_number(s)
|
|
}
|
|
fn is_value(s: &str) -> bool {
|
|
is_number(s) || !is_key(s)
|
|
}
|
|
|
|
fn main() {
|
|
let root = std::env::var("SYLPHEED_DISC")
|
|
.unwrap_or_else(|_| "/home/fabi/RE - Project Sylpheed/sylph_extract".into());
|
|
let wanted: Vec<String> = std::env::args().skip(1).collect();
|
|
let arc = PakArchive::open(std::path::Path::new(&root).join("dat/GP_MAIN_GAME_E.pak")).unwrap();
|
|
|
|
for e in arc.entries() {
|
|
let Ok(bytes) = arc.read(e) else { continue };
|
|
let Ok(obj) = IdxdObject::parse(&bytes) else { continue };
|
|
let toks = obj.tokens().to_vec();
|
|
let mut hits: Vec<String> = vec![];
|
|
for (i, t) in toks.iter().enumerate() {
|
|
if !wanted.iter().any(|w| w == t) {
|
|
continue;
|
|
}
|
|
let valued = i > 0 && is_value(&toks[i - 1]);
|
|
hits.push(if valued {
|
|
format!("{t}={}", toks[i - 1])
|
|
} else {
|
|
format!("{t}=<DEFAULT>")
|
|
});
|
|
}
|
|
if !hits.is_empty() {
|
|
println!("0x{:08x} {:<44} {}", obj.schema_hash, obj.identity(), hits.join(" "));
|
|
}
|
|
}
|
|
}
|