re: the break-composite alarm is false -- the container stores 3-6 copies of each buffer

locate_draw counts copies of a captured buffer. The f105 parts _rou_f105_break
claims exist 3-6 times over (the 2336-vert one: 6 direct AND 6 mirrored), and
every live LOD checked is anchored on a direct, byte-identical copy -- so the
composite taking the drawn copy costs nothing. This also calibrates the oracle:
'exact' (anchored at the drawn offset) is stricter than correct, so 45/46 is a
lower bound. Open: a resource landing on a MIRRORED copy would be a real defect
invisible to every count-based metric.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-12 03:32:58 +00:00
parent 7f1ac41563
commit 55e1fe3783
2 changed files with 84 additions and 8 deletions

View File

@@ -0,0 +1,53 @@
//! How many copies of a captured buffer does a container hold?
//!
//! The twins showed that two resources decoding to one buffer can mean the
//! container really holds two (mirrored) copies and our scan found only one.
//! This asks that question for any draw: give it a `vbase`, and it reports every
//! offset whose leading vertices match the draw's dumped positions — directly, and
//! X-mirrored.
//!
//! Usage: locate_draw <container.xpr> <capture.log> <vbase-hex>...
use sylpheed_formats::ship_capture::{parse_capture, parse_drawlog};
fn main() {
let a: Vec<String> = std::env::args().collect();
let bytes = std::fs::read(&a[1]).expect("container");
let text = std::fs::read_to_string(&a[2]).expect("log");
let mut draws = parse_capture(&text);
if draws.is_empty() {
draws = parse_drawlog(&text);
}
let be = |at: usize| f32::from_be_bytes(bytes[at..at + 4].try_into().unwrap());
for want in &a[3..] {
let vb = u32::from_str_radix(want.trim_start_matches("0x"), 16).expect("hex vbase");
let Some(d) = draws.iter().find(|d| d.vbase == vb) else {
println!("vbase 0x{vb:08X}: not in this log");
continue;
};
let n = d.pos.len().min(8);
let mut direct = Vec::new();
let mut mirror = Vec::new();
for o in (0..bytes.len().saturating_sub(12 + 64 * 24)).step_by(4) {
for (flip, out) in [(1.0f32, &mut direct), (-1.0f32, &mut mirror)] {
let hit = (0..n).all(|k| {
let at = o + k * 24;
(be(at) - flip * d.pos[k][0]).abs() <= 1e-4
&& (be(at + 4) - d.pos[k][1]).abs() <= 1e-4
&& (be(at + 8) - d.pos[k][2]).abs() <= 1e-4
});
if hit {
out.push(o);
}
}
}
println!(
"vbase 0x{vb:08X} vcount={}: {} direct copy/copies {:x?}, {} mirrored {:x?}",
d.vcount,
direct.len(),
&direct[..],
mirror.len(),
&mirror[..]
);
}
}

View File

@@ -898,14 +898,37 @@ systematic mis-anchor is invisible to it because it is consistent. Where the two
disagree, the capture wins. Full suite green including the disc- and ISO-gated disagree, the capture wins. Full suite green including the disc- and ISO-gated
ship tests. ship tests.
**Still open — grouped composites.** `_rou_f105_break` (the f105 destruction **Closed — the grouped composite is a false alarm.** `_rou_f105_break` (the
model) claims **twelve consecutive** drawn buffers, each matching one of its f105 destruction model) claims **twelve consecutive** drawn buffers while
sub-mesh vertex counts exactly, while `f105_bdy_01_m`, `f105_bdy_03_m`, `f105_bdy_01_m`, `f105_bdy_03_m` and `f105_eng_01_m` sit elsewhere, which looked
`f105_eng_01_m` — resources of exactly those sizes — are anchored elsewhere. like the twins' bug in the grouped-pool path. It is not.
Distinct assignment does not touch this: the grouped-pool path is excluded. The `examples/locate_draw.rs` counts how many copies of a captured buffer a container
question is the twins' question again — does the container hold two copies of holds, directly and X-mirrored:
each of those buffers, or one that both names should share? The same
mirror/duplicate test would answer it. | drawn buffer | vcount | direct copies | mirrored copies |
|---|---|---|---|
| `0x1bf596c` | 2446 | **3** | 0 |
| `0x1c0fe2c` | 2336 | **6** | **6** |
| `0x1c211cc` | 1402 | **4** | 0 |
| `0x1bf40f4` | 261 | 1 | 0 |
The container stores these parts several times over, and every live LOD checked
(`f105_bdy_01_m` at `0x1ecb4c0`, `f105_bdy_03_m` at `0x1f902a4`, `f105_eng_01_m`
at `0x1ffb884`) is anchored on a **direct** copy — byte-identical geometry to the
one the engine drew. So the composite taking "the drawn" copy costs nothing: the
decode is the same vertices either way. (The draws use the ordinary ship shader
`0xEEA84C59D7F95371`, the same one as the validated e106 hull draws, so these are
intact-ship draws, not debris.)
⚠️ **This also calibrates the oracle metric.** "Exact" in the tables above means
*anchored at the offset the engine drew from*, which is stricter than correct: a
resource anchored on an identical copy is equally right. The 45/46 figure stands
as a lower bound, and an "unclaimed" row is not automatically a defect.
❔ What the copy counts *do* leave open: with six direct and six mirrored copies
of one buffer, a resource landing on a **mirrored** copy would be a real defect
and would look identical to a correct decode in every count-based metric. Only a
capture (or the twin-pair invariant) can catch it.
Not settled: `e106_brg_01_b_02``e106_brg_01_l` (51 verts). A second 51-vertex 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 `vbase` exists in the logs but is **not** from this container, and the container