re(xbg7): characterise the residual 47 misses — 99.25% of resources decode

examples/why_missed.rs reports the furthest gate per undecoded resource. Disc-wide
that is 47 rows / 43 distinct names, and they are mostly not ship geometry:

  30  e_rou_/_rou_ pose & proxy composites (24-vertex marker boxes, extent 0.010)
   6  .DAT particle composites in ptc_pack
   8  damage/LOD variants (e101_bdy_02_d, e901_wing_05_*_m, ...)
   3  props/other (g005 extent 0.196, _rou_f001_wep_05, e_rou_e005)

By gate: extent 32, coverage 8, winding 7. The extent bucket is almost entirely the
pose-proxy boxes, and lowering that floor was measured and refuted earlier. The one
genuinely interesting residual is e901_wing_05_L/R at winding 0.587/0.570 against the
0.70 floor — the signature of a thin double-sided sheet, unproven without a capture
with the boss on screen (flying stage 16 puts the container in memory but the unit
never appeared).

So: 6 247 / 6 294 resources decode (99.25 %), and every real mesh drawn in three
captured missions decodes at the GPU's own offsets with index runs matching byte for
byte (93/93 and 128/128).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NptfmpjdpNCKEez6d2xvA9
This commit is contained in:
2026-08-13 12:50:19 +00:00
parent 3b35c48fce
commit d1b50dc1e6
3 changed files with 47 additions and 1 deletions

View File

@@ -0,0 +1,19 @@
//! Why does a named resource never decode? Reports the furthest gate its best
//! candidate reached. Usage: why_missed <container.xpr> [name-substring]
use sylpheed_formats::mesh::{debug_best_rejection, xbg7_resource_names, Xbg7Model};
use std::collections::HashSet;
fn main() {
let a: Vec<String> = std::env::args().collect();
let bytes = std::fs::read(&a[1]).unwrap();
let filter = a.get(2).cloned().unwrap_or_default();
let decoded: HashSet<String> = Xbg7Model::stage_models(&bytes).into_iter().map(|m| m.name).collect();
for n in xbg7_resource_names(&bytes) {
if decoded.contains(&n) || (!filter.is_empty() && !n.contains(&filter)) {
continue;
}
match debug_best_rejection(&bytes, &n) {
Some((rank, why)) => println!("{n:<28} rank {rank} {why}"),
None => println!("{n:<28} (no candidate reached any gate / skipped before anchoring)"),
}
}
}