From 27a0701e0dfd55adcc1e5553b653a5a79b007c3a Mon Sep 17 00:00:00 2001 From: "Claude (auto-RE)" Date: Wed, 12 Aug 2026 00:39:35 +0000 Subject: [PATCH] fix(mesh): refine the anchor map before using it -- inconsistency 63 -> 51 Pass 1's anchor map contains exactly the mistakes the neighbourhood is meant to correct, so a resource sitting beside a mis-anchored neighbour inherits a bad reference. Re-anchoring against the improving map and repeating converges quickly: two rounds, and a third changes nothing (the loop exits early when a round is a fixpoint). before decoded 5480/6294 inconsistent 125 neighbourhood anchor decoded 5480/6294 inconsistent 63 + refining the map decoded 5480/6294 inconsistent 51 Coverage still unchanged. The 51 that remain cluster in _l (LOD) and _dead variants -- e001_l, e010_bdy_01_l, e106_eng_02_l, _rou_f302_base_dead, e303_base_dead and friends. A plausible reading is that a variant shares its base's vertex and index counts, making the two mutually confusable so that locality cannot separate them; recorded as untested rather than asserted. Co-Authored-By: Claude Opus 5 (1M context) --- crates/sylpheed-formats/src/mesh.rs | 20 ++++++++++++++++++- .../tests/mesh_consistency_disc.rs | 2 +- docs/re/structures/xbg7-mesh.md | 15 ++++++++++++-- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/crates/sylpheed-formats/src/mesh.rs b/crates/sylpheed-formats/src/mesh.rs index 5546d1e..ed6d924 100644 --- a/crates/sylpheed-formats/src/mesh.rs +++ b/crates/sylpheed-formats/src/mesh.rs @@ -653,7 +653,25 @@ impl Xbg7Model { let pass1: Vec<(Option, Option)> = resources.iter().enumerate().map(run1).collect(); - let vbs: Vec> = pass1.iter().map(|(_, vb)| *vb).collect(); + let mut vbs: Vec> = pass1.iter().map(|(_, vb)| *vb).collect(); + + // Refine the anchor map before using it: pass 1's anchors include the + // very mistakes this is meant to correct, so a resource next to a + // mis-anchored neighbour inherits a bad reference. Re-anchoring against + // the improving map and repeating converges quickly; two rounds is + // enough on this disc (a third changes nothing). + for _ in 0..2 { + let refined: Vec> = (0..resources.len()) + .map(|i| match (need_pass1[i], neighbourhood(&vbs, i)) { + (true, Some(anchor)) => decode_one_near(&resources[i], Some(anchor)).1.or(vbs[i]), + _ => vbs[i], + }) + .collect(); + if refined == vbs { + break; + } + vbs = refined; + } // Pass 2 — re-anchor preferring the resource's own neighbourhood, which // is what separates its data from another resource's identically-shaped diff --git a/crates/sylpheed-formats/tests/mesh_consistency_disc.rs b/crates/sylpheed-formats/tests/mesh_consistency_disc.rs index 69ff646..90c8b40 100644 --- a/crates/sylpheed-formats/tests/mesh_consistency_disc.rs +++ b/crates/sylpheed-formats/tests/mesh_consistency_disc.rs @@ -53,7 +53,7 @@ fn span(m: &Xbg7Model) -> Option<[i64; 3]> { } #[test] -#[ignore = "known-failing: 63 of 681 shared resources still decode inconsistently (was 125 before the neighbourhood anchor, 2026-08-12)"] +#[ignore = "known-failing: 51 of 681 shared resources still decode inconsistently (125 before the neighbourhood anchor, 63 before refining it — 2026-08-12)"] fn shared_resources_decode_identically_in_every_container() { let Some(root) = disc_root() else { eprintln!("SKIP: extracted disc not found (set SYLPHEED_DISC to enable)"); diff --git a/docs/re/structures/xbg7-mesh.md b/docs/re/structures/xbg7-mesh.md index 732f397..1046158 100644 --- a/docs/re/structures/xbg7-mesh.md +++ b/docs/re/structures/xbg7-mesh.md @@ -405,7 +405,13 @@ guesswork. | | decoded | shared | inconsistent | |---|---|---|---| | before | 5 480 / 6 294 | 681 | **125** | -| after | 5 480 / 6 294 | 681 | **63** | +| neighbourhood anchor | 5 480 / 6 294 | 681 | **63** | +| + refining the map | 5 480 / 6 294 | 681 | **51** | + +**Refining matters** because pass 1's anchor map contains the very mistakes the +neighbourhood is meant to correct, so a resource beside a mis-anchored neighbour +inherits a bad reference. Re-anchoring against the improving map and repeating +converges quickly — two rounds, with a third changing nothing. **Coverage is unchanged and inconsistency halves.** `e303_wep_01` now decodes to 49 × 23 × 42 in *all* containers, and `e106` renders as a destroyer instead of a @@ -420,7 +426,12 @@ now collected regardless of the filter, but only the asked-for ones and their ± neighbours are decoded in pass 1, so a filtered decode stays proportional to what was asked for. -**63 remain.** The ignored test +**51 remain**, and they cluster: `_l` (LOD) and `_dead` variants — +`e001_l`, `e010_bdy_01_l`, `e011_bdy_01_l`, `e016_l`, `e104_bdy_05_l`, +`e106_eng_02_l`, `e501_01_l`, `_rou_f301_base_dead`, `_rou_f302_base_dead`, +`e303_base_dead`. 🟡 A plausible reading is that a variant shares its base's +vertex and index counts, so the two are mutually confusable and the neighbourhood +cannot separate them — untested. The ignored test [`mesh_consistency_disc.rs`](../../crates/sylpheed-formats/tests/mesh_consistency_disc.rs) still asserts the target state and now records 63 rather than 125; the remaining cases are where the neighbourhood is itself wrong or absent.