re: exact anchor offsets (GameMesh.vbuf_offset) -- correcting yesterday's defect table

The first read of the capture-truth table located our resources by searching the
container for their leading vertices, which reads much worse than reality: the
same leading run occurs at several offsets in one container. GameMesh now carries
the offset the anchor scan actually chose, so the comparison is exact -- 4 of the
ship's drawn buffers are anchored correctly, 2 are the twin collapse, and 2 are
real mis-anchors of a size we do decode (brg 51 verts, eng_02_l 44).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-12 02:04:55 +00:00
parent 3245aca8cb
commit 26f1bdf6fd
5 changed files with 90 additions and 38 deletions

View File

@@ -117,15 +117,27 @@ fn main() {
// scan chose for each resource — a direct read-out of what we got wrong.
if let Some(a) = args.iter().find_map(|a| a.strip_prefix("--truth=")) {
let base = u32::from_str_radix(a.trim_start_matches("0x"), 16).expect("base");
// Where the anchor scan actually put each sub-mesh — exact, from the
// decoder, not inferred by searching for its leading vertices (the same
// leading run occurs at several offsets in a container, so a search
// cannot tell where a resource was anchored).
let mut ours: BTreeMap<usize, Vec<(String, usize)>> = BTreeMap::new();
for m in &models {
let pos: Vec<[f32; 3]> =
m.meshes.iter().flat_map(|s| s.positions.iter().copied()).take(8).collect();
let n: usize = m.meshes.iter().map(|s| s.positions.len()).sum();
for (o, _) in locate_run(&bytes, &pos) {
ours.entry(o).or_default().push((m.name.clone(), n));
for sub in &m.meshes {
if let Some(o) = sub.vbuf_offset {
ours.entry(o).or_default().push((m.name.clone(), sub.positions.len()));
}
}
}
if args.iter().any(|a| a == "--anchors") {
println!("{:<12} where our decode put each resource", "file offset");
for (o, v) in &ours {
for (n, c) in v {
println!("0x{o:<10x} {n} ({c} verts)");
}
}
return;
}
let mut drawn: BTreeMap<usize, u32> = BTreeMap::new();
for (_, draws) in &logs {
for d in draws {
@@ -135,7 +147,14 @@ fn main() {
}
}
}
println!("{:<12} {:>7} claimed by our decode", "file offset", "vcount");
// Which resources have the drawn vertex count, wherever we put them?
// Right size + wrong place is a different bug from never finding it.
let mut by_count: BTreeMap<usize, Vec<String>> = BTreeMap::new();
for m in &models {
let n: usize = m.meshes.iter().map(|s| s.positions.len()).sum();
by_count.entry(n).or_default().push(m.name.clone());
}
println!("{:<12} {:>7} {:<44} our resources with that vcount", "file offset", "vcount", "claimed by our decode");
for (off, vcount) in &drawn {
let who = ours
.get(off)
@@ -143,7 +162,18 @@ fn main() {
v.iter().map(|(n, c)| format!("{n}({c})")).collect::<Vec<_>>().join(", ")
})
.unwrap_or_else(|| "— NOBODY".into());
println!("0x{off:<10x} {vcount:>7} {who}");
let same = by_count
.get(&(*vcount as usize))
.map(|v| v.join(", "))
.unwrap_or_else(|| "— none".into());
// Nearest resource we anchored at or before this offset — the
// likely owner of a buffer nobody claims.
let near = ours
.range(..=*off)
.next_back()
.map(|(o, v)| format!("{} @ -0x{:x}", v[0].0, off - o))
.unwrap_or_default();
println!("0x{off:<10x} {vcount:>7} {who:<44} {same:<34} {near}");
}
return;
}

View File

@@ -115,6 +115,12 @@ pub struct GameMesh {
pub indices: Vec<u32>,
/// Sub-mesh / node name from the descriptor, when available.
pub name: Option<String>,
/// Byte offset of this sub-mesh's vertex buffer inside the container, when
/// the decode path knows it. The content-anchored stage path does — and a
/// runtime capture names the same offset (a draw's `vbase` is this plus the
/// container's load address), so this is what makes an anchor checkable
/// against ground truth. See `examples/shared_vbase_check.rs`.
pub vbuf_offset: Option<usize>,
}
/// A model = the set of sub-meshes recovered from one XPR2 container's first
@@ -376,6 +382,7 @@ impl Xbg7Model {
uvs,
indices,
name: None,
vbuf_offset: Some(vb),
});
off = align16(ve) - base;
}
@@ -976,6 +983,7 @@ fn read_pool_mesh(
uvs,
indices,
name: None,
vbuf_offset: Some(vb),
}
}

View File

@@ -3677,6 +3677,7 @@ fn exhaust_cone_mesh() -> sylpheed_formats::mesh::GameMesh {
uvs: Vec::new(),
indices,
name: Some("exhaust".to_string()),
vbuf_offset: None, // procedural, not read from a container
}
}