models_named pruned to the wanted set BEFORE distinct assignment, so collision resolution saw a different resource population and returned different offsets: 27 of 356 resources in Stage_S02 decoded differently when asked for alone, including real geometry (f001_bdy_30, f106_sld_02_l/m/d, f101_wep_01_l). Both the viewer and assemble_ship decode subsets, so both could disagree with the container's own answer. This was a regression from distinct assignment itself. Fixed by filtering the OUTPUT: the assignment always runs over the whole container. One-name, three-name and full decodes now agree exactly. Cost: a single-resource query on a 50MB container goes from near-instant to ~15s; per-container caching is the follow-up. Ten suites green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
54 lines
2.1 KiB
Rust
54 lines
2.1 KiB
Rust
//! Does a resource's DESCRIPTOR carry its bounding box?
|
|
//!
|
|
//! The last cross-container disagreements are 24-vertex bound boxes swapping
|
|
//! identities; no anchoring rule can pin them (see docs). If the descriptor
|
|
//! states the box, that is the missing information. This decodes the resource,
|
|
//! takes the box its geometry actually spans, and searches the descriptor for
|
|
//! those float values.
|
|
//!
|
|
//! Usage: bounds_in_descriptor <container.xpr> <resource>...
|
|
use sylpheed_formats::mesh::{xbg7_descriptor_range, Xbg7Model};
|
|
use std::collections::HashSet;
|
|
|
|
fn main() {
|
|
let a: Vec<String> = std::env::args().collect();
|
|
let bytes = std::fs::read(&a[1]).expect("container");
|
|
let want: HashSet<String> = a[2..].iter().cloned().collect();
|
|
for m in Xbg7Model::models_named(&bytes, &want, &|| false) {
|
|
let (mut lo, mut hi) = ([f32::MAX; 3], [f32::MIN; 3]);
|
|
for s in &m.meshes {
|
|
for q in &s.positions {
|
|
for k in 0..3 {
|
|
lo[k] = lo[k].min(q[k]);
|
|
hi[k] = hi[k].max(q[k]);
|
|
}
|
|
}
|
|
}
|
|
let Some((d0, d1)) = xbg7_descriptor_range(&bytes, &m.name) else { continue };
|
|
println!(
|
|
"{} descriptor 0x{d0:x}..0x{d1:x} ({} bytes), box lo{:?} hi{:?}",
|
|
m.name,
|
|
d1 - d0,
|
|
lo.map(|v| v.round()),
|
|
hi.map(|v| v.round())
|
|
);
|
|
// Where in the descriptor does each bound value appear (±0.01)?
|
|
let targets: Vec<(&str, f32)> = vec![
|
|
("lo.x", lo[0]), ("lo.y", lo[1]), ("lo.z", lo[2]),
|
|
("hi.x", hi[0]), ("hi.y", hi[1]), ("hi.z", hi[2]),
|
|
];
|
|
for (label, v) in targets {
|
|
let mut at: Vec<usize> = Vec::new();
|
|
let mut o = d0;
|
|
while o + 4 <= d1 {
|
|
let f = f32::from_be_bytes(bytes[o..o + 4].try_into().unwrap());
|
|
if (f - v).abs() <= 0.01 * (1.0 + v.abs()) {
|
|
at.push(o - d0);
|
|
}
|
|
o += 4;
|
|
}
|
|
println!(" {label:5} {v:10.3} at descriptor offsets {:x?}", &at[..at.len().min(6)]);
|
|
}
|
|
}
|
|
}
|