re: the twins' correct block validates -- distinctness is a real fix; eng_02_l is not

debug_try_anchor asks validate_block directly at the capture-proven offsets. Both
119-vert twin buffers are accepted by both twins (so the correct block lost the
first-match race, and a distinct assignment fixes it); the drawn 51-vert bridge
buffer is accepted by both bridge resources; eng_02_l's proven offset is rejected
outright, even with the pad widened to 64 -- a validator gap, not a selection one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-12 02:17:55 +00:00
parent 76a433f75c
commit 51723af59a
3 changed files with 107 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
//! Does the capture-proven offset validate for the resource that should own it?
use sylpheed_formats::mesh::debug_try_anchor;
fn main() {
let a: Vec<String> = std::env::args().collect();
let bytes = std::fs::read(&a[1]).unwrap();
// The production scan tries pads 0..=3; pass a bigger one to ask whether the
// block would validate at all with a wider index/vertex gap.
let max_pad: usize = std::env::var("MAX_PAD").ok().and_then(|v| v.parse().ok()).unwrap_or(3);
for pair in a[2..].iter() {
let (name, off) = pair.split_once('@').unwrap();
let off = usize::from_str_radix(off.trim_start_matches("0x"), 16).unwrap();
match debug_try_anchor(&bytes, name, off, max_pad) {
Some((v, i, pad)) => println!("{name:22} @ 0x{off:x} ACCEPTED v={v} idx={i} pad={pad}"),
None => println!("{name:22} @ 0x{off:x} rejected"),
}
}
}

View File

@@ -613,6 +613,66 @@ impl Xbg7Model {
}
}
/// Diagnostic: would the anchor scan accept `vb` as the vertex buffer of the
/// named resource's first sub-mesh? A runtime capture proves which offset the
/// engine drew from, so this answers whether the correct block is *acceptable*
/// and merely lost the first-match race, or is rejected outright by
/// `validate_block`. Returns `(vtx_count, index_count, accepted_pad)`.
pub fn debug_try_anchor(
bytes: &[u8],
name: &str,
vb: usize,
max_pad: usize,
) -> Option<(usize, usize, usize)> {
if bytes.len() < 16 || &bytes[..4] != b"XPR2" {
return None;
}
let mut cur = Cursor::new(bytes);
let header = Xpr2Header::read(&mut cur).ok()?;
const DIR_BASE: usize = 0x10;
for _ in 0..header.num_resources {
let Ok(e) = Xpr2ResourceEntry::read(&mut cur) else { break };
if &e.type_tag != b"XBG7" {
continue;
}
let desc = e.data_offset as usize + DIR_BASE;
let desc_end = (desc + e.descriptor_size as usize).min(bytes.len());
if desc >= bytes.len() || desc_end <= desc {
continue;
}
let rname = read_cstr(bytes, e.name_offset as usize + DIR_BASE)
.unwrap_or_else(|| "XBG7".to_string());
if rname != name {
continue;
}
let d = &bytes[desc..desc_end];
let markers = all_index_markers(d);
let decl = parse_vertex_decl(d)?;
let (vtx_count, index_count) = *markers.first()?;
let idx_bytes = index_count * 2;
for pad in 0..=max_pad {
if vb < idx_bytes + pad {
continue;
}
let mc = if pad == 0 { 0.0 } else { 0.85 };
if validate_block(
bytes,
vb - idx_bytes - pad,
vb,
vtx_count,
index_count,
&decl,
mc,
true,
) {
return Some((vtx_count, index_count, pad));
}
}
return None;
}
None
}
/// Diagnostic: the candidate vertex-buffer starts the stage anchor scan will
/// consider for a given `stride`, for one container. A runtime capture names the
/// offsets the engine really drew from (see `examples/shared_vbase_check.rs`), so