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:
17
crates/sylpheed-formats/examples/try_anchor.rs
Normal file
17
crates/sylpheed-formats/examples/try_anchor.rs
Normal 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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -674,6 +674,36 @@ An assignment that is distinct by construction — each candidate used at most o
|
||||
offsets actually validate for their resources is the next thing to test; if they
|
||||
do, distinctness alone is the fix.
|
||||
|
||||
**5. Do the proven offsets validate? Two of three — and that splits the fix.**
|
||||
`mesh::debug_try_anchor(bytes, name, vb, max_pad)` asks `validate_block` directly
|
||||
(`examples/try_anchor.rs`):
|
||||
|
||||
| resource | proven offset | verdict |
|
||||
|---|---|---|
|
||||
| `e106_bdy_01_l` / `e106_bdy_02_l` | `0x3b3ee8` **and** `0x3c55d8` | **accepted for both, at both** (v=119, idx=246, pad=0) |
|
||||
| `e106_brg_01_l` / `e106_brg_01_b_02` | `0x40e418` | **accepted for both** (v=51, idx=126, pad=0) |
|
||||
| `e106_eng_02_l` | `0x44a32c` | **rejected** — and still rejected with the pad widened to 64 |
|
||||
|
||||
So the twin case is exactly what it looked like: the correct block is perfectly
|
||||
acceptable and simply lost the first-match race, and a **distinct assignment**
|
||||
(each candidate buffer claimed by at most one resource) fixes it — the two
|
||||
resources have two accepted offsets between them. The bridge pair is weaker:
|
||||
`0x40e418` is accepted by both, our current `0x40de48` is accepted too, so
|
||||
distinctness would separate them but not choose correctly.
|
||||
|
||||
`eng_02_l` is a different failure: the offset the engine drew from is **not
|
||||
acceptable at all**, so no selection policy can reach it. That is the
|
||||
"residual" class this file describes above, now with one member pinned to a
|
||||
concrete offset for the first time.
|
||||
|
||||
❔ **An open discrepancy, recorded not explained.** The capture's `DRAW` lines
|
||||
carry an `indices=` field that does not agree with the descriptor's index count:
|
||||
the 119-vertex twin draws log `indices=21` where our marker says 246, and the
|
||||
44-vertex draw logs `indices=12`. Whether that field is an index *count* of a
|
||||
sub-range, a different unit, or a Xenia-side artifact is unknown — it may matter
|
||||
for `eng_02_l`, whose block validation is exactly what an index-count mismatch
|
||||
would break.
|
||||
|
||||
Not settled: `e106_brg_01_b_02` ≡ `e106_brg_01_l` (51 verts). A second 51-vertex
|
||||
`vbase` exists in the logs but is **not** from this container, and the container
|
||||
holds three near-identical 51-vertex runs, so the pair has no oracle yet.
|
||||
|
||||
Reference in New Issue
Block a user