Files
Syplheed-Reborn/crates/sylpheed-formats/examples/live_offsets.rs
Claude (auto-RE) f4f3bcb20a re: a deeper window does not help -- the 0x820af844 object is not the definition record
Dumping 512 words instead of 96 finds exactly the same 4 of ~50 disc values, for
all five correlated units, so the rest of the record is not in this structure.
Also corrects the reason recorded last commit: get_f32 is NOT unreliable on
default-heavy records -- it resolves 46-57 numeric fields per unit, which is what
made the emptiness of the correlation measurable in the first place. The one
automated hit (YawDragFactor -> +0x0c) remains false: +0x0c holds the integer 2
as a denormal and collided with the float 2.0.

Next probe is a RAM-wide search for a unit-distinctive value, not a bigger window.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 20:49:19 +00:00

91 lines
3.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Where does each declared field sit inside the engine's live definition object?
//!
//! A live object carries no names, so the mapping has to be inferred: take a unit
//! whose disc record SETS a field, look for that value in the dumped words of the
//! matching live object, and keep the offsets that agree across several units.
//!
//! Run: live_offsets <disc-root> <live-dump.txt> <ID=0xVA> ...
//! e.g. … UN_f106_TCAF_Destroyer=0xbd3ee300 UN_f105_TCAF_Cruiser=0xbd40e200
use std::collections::BTreeMap;
use sylpheed_formats::idxd::IdxdObject;
use sylpheed_formats::pak::PakArchive;
fn main() {
let mut a = std::env::args().skip(1);
let disc = a.next().expect("disc root");
let dump = a.next().expect("live dump");
let pairs: Vec<(String, String)> = a
.filter_map(|s| s.split_once('=').map(|(i, v)| (i.to_string(), v.to_string())))
.collect();
// live: va -> offset -> f32
let mut live: BTreeMap<String, BTreeMap<usize, f32>> = BTreeMap::new();
let mut cur = String::new();
for line in std::fs::read_to_string(&dump).expect("dump").lines() {
if let Some(rest) = line.strip_prefix("=== ") {
cur = rest.trim().to_string();
continue;
}
let f: Vec<&str> = line.split_whitespace().collect();
if f.len() >= 4 && f[1].starts_with('+') {
if let (Ok(off), Ok(val)) =
(usize::from_str_radix(&f[1][1..], 16), f[3].parse::<f32>())
{
live.entry(cur.clone()).or_default().insert(off, val);
}
}
}
let pak = PakArchive::open(format!("{disc}/dat/GP_MAIN_GAME_E.pak")).expect("pak");
// key -> offset -> how many units agree
let mut votes: BTreeMap<String, BTreeMap<usize, usize>> = BTreeMap::new();
let mut units = 0usize;
for entry in pak.entries() {
let Ok(bytes) = pak.read(entry) else { continue };
let Ok(obj) = IdxdObject::parse(&bytes) else { continue };
let Some(id) = obj.get_raw("ID") else { continue };
let Some((_, va)) = pairs.iter().find(|(i, _)| i == id) else { continue };
let Some(words) = live.get(va) else { continue };
units += 1;
let mut numeric = 0usize;
let mut hit = 0usize;
for key in obj.tokens() {
let Some(v) = obj.get_f32(key) else { continue };
if !v.is_finite() || v == 0.0 {
continue; // zero matches everywhere and says nothing
}
numeric += 1;
let mut found = false;
for (off, w) in words {
if (*w - v).abs() <= v.abs() * 1e-6 {
*votes.entry(key.clone()).or_default().entry(*off).or_default() += 1;
found = true;
}
}
if found {
hit += 1;
}
}
eprintln!(" {id}: {numeric} numeric fields set on disc, {hit} found in the dumped window");
}
println!("{units} units correlated against their live objects\n");
println!("{:<28} {:>8} offsets agreeing (votes)", "field", "unique?");
for (key, offs) in &votes {
let best = offs.iter().max_by_key(|(_, n)| **n).unwrap();
if *best.1 < units.max(2) {
continue; // needs every correlated unit to agree
}
let list: Vec<String> = offs
.iter()
.filter(|(_, n)| **n == *best.1)
.map(|(o, n)| format!("+{o:03x}×{n}"))
.collect();
println!(
"{:<28} {:>8} {}",
key,
if list.len() == 1 { "unique" } else { "ambig" },
list.join(" ")
);
}
}