diff --git a/docs/re/INDEX.md b/docs/re/INDEX.md index 13bf143..b64350c 100644 --- a/docs/re/INDEX.md +++ b/docs/re/INDEX.md @@ -20,7 +20,7 @@ Promote to a prose `structures/…md` file when a format needs behavioural notes | IXUD subtitle | 🟑/βœ… | `sylpheed-formats/src/ixud.rs` + [movie link](movie-subtitle-link.md) | timed cues. **The movie↔subtitle↔voice link is solved β€” statically**, from the movie config record in `tables.pak` (schema `0x067025b9`), not from the running game as this row previously assumed: [101 movies mapped](captures/movie-subtitle-voice-map.csv), 94 with subtitles, 83 with voice, 21 with a telop overlay. 93 of 94 subtitle refs resolve in the language paks; **`SUBTITLE_S12B.tbl` is missing from all six languages** β€” a dangling reference on the disc. Naming is `SUBTITLE_.tbl` / `VOICE_` with six documented exceptions. The record's ~104 **script ids** are ❔ β€” positional pairing drifts by three because the IDXD pool dedupes repeated values | | Fonts (ttf/otf/ttc) | βœ… | `sylpheed-formats/src/font.rs` | standard OpenType, parsed via ttf-parser | | XBG7 mesh | 🟑/❔ | `sylpheed-formats/src/mesh.rs` + `tests/mesh_disc.rs` ([xbg7](structures/xbg7-mesh.md)) | weapons/props: declaration-driven variable stride (36 models), GPU-confirmed. **Stage containers: 5662 sub-models across 22 stages** via content-anchored grouped pools (`stage_models`). **Declined set now measured**: 6 294 resources, **5 480 decode (87.1 %), 814 declined** in 31/166 containers β€” and it is *not* "a few quantized hero bodies" but 492 `e*`, 142 `f*`, 73 `n*`, 23 `eff*` plus `*_dead` variants. The descriptor does **not** declare decodability (word[2] is a sub-mesh count; decoded and declined appear at every value), so the gap is the anchor scan, not an unread format flag β€” see [xbg7](structures/xbg7-mesh.md). **Silent mis-decodes also exist and are now measurable**: a resource shared across containers must decode to the same bounds, and **125 of 681 shared resources fail that check** with identical vertex/triangle counts β€” the decoder picked a different buffer of the same size. A majority vote across containers would resolve 104 of them (14 are 50/50), but that is 🟑 unvalidated beyond the one case with a render and a capture behind it | -| Capital-ship part placement | 🟑 | `sylpheed-formats/src/ship.rs` (static) + [runtime capture](ship-placement-runtime-capture.md) | hull placement static-exact; external parts approximate statically. **Runtime capture** (Canary F10 β†’ VS-constant WorldView) gives ground truth β€” validated on `e106` destroyer; not yet baked into the viewer | +| Capital-ship part placement | βœ…/🟑 | `sylpheed-formats/src/ship.rs` (static) + [runtime capture](ship-placement-runtime-capture.md) | Placement itself is **sound**: hull static-exact, validated against the `e106` runtime capture, and cross-id mounting is genuinely narrow (**2 pairs across 335 ships**). **The user-reported "ships assemble wrong" is NOT a placement bug** β€” it is an XBG7 **mis-decode** that makes one shared turret 100Γ— too large in 3 of 14 containers ([backlog](BACKLOG.md), [xbg7](structures/xbg7-mesh.md)). Also open: `static_assembly_matches_runtime_capture` walks capture parts only, so **extra** static placements can never fail it | | Weapon fields defaulted on disc | βœ… | [runtime struct](structures/weapon-struct-runtime.md) Β· [DATA SHEET route](weapon-datasheet-runtime.md) | **Solved.** Canary maps guest RAM into `/dev/shm`, so the parsed `Weapon`/`Shell` objects are readable live; their layout is solved against disc ground truth (zero contradictions over 100+ records). All 126 weapons, exact numbers, no story progress needed β€” [4 393 values](captures/weapon-runtime-fields.csv) the disc does not carry. Supersedes the letter-bucket limit of the DATA SHEET route, which now serves as the independent cross-check | | Unit (craft/vessel) fields defaulted on disc | βœ…/🟑 | [runtime struct](structures/unit-struct-runtime.md) | The parsed `unit\UN_*.tbl` definition object, vtable `0x820af844`, β‰₯`0x380` bytes, one per unit β€” **discovered, not assumed** (`unit_discover.py`), and distinguished from the spawned-entity class `0x820af030` by being one-per-ID and byte-constant within a run. Across runs only pointer words move β€” `--crosscheck` proves **no reported field offset is run-dependent** (two words, `+0x2c8`/`+0x2d0`, are stage-dependent and remain unidentified). 27 fields βœ… (21 units, 7 runs); the `Maneuver` block is **schema declaration order, 4 bytes/field, base `0x9c` with a two-slot gap after `AA_Roll_Min`** (29 anchors, 0 conflicts), which also pins 5 fields *no* disc record ever values. Angles are **radians at runtime, degrees on disc**. Unlike weapons, unit definitions are instantiated **per stage**, so coverage (21/110) grows by visiting missions β€” but a defaulted field is **not** a global constant: `Size_Y` provably inherits `Size_X` (7 independent units, 6 distinct values), and three more sibling rules are recorded ❔, recovering 65 values in units never visited β€” [values](captures/unit-runtime-fields.csv) | | Arsenal develop economy | βœ…/❔ | [arsenal-develop-economy](arsenal-develop-economy.md) + [conditions](captures/arsenal-develop-conditions.csv) | The Arsenal reads `weapon.tbl` (item ids, in the 8-category display order) and `strings.tbl` (names, descriptions, and a **"Conditions to obtain"** block per item) out of `GP_HANGAR_ARSENAL.pak`. All **60** conditions are extracted: gates are stage completion, a predecessor item, or an **ace kill**; costs run 3 000–350 000 P and **20 items are free** once gated. `weapon.tbl`'s first record reproduces the in-game DATA SHEET exactly (Range D / Power E / Speed – / Weight 0.3 = Light / 4000 P) β€” later records are unreadable from the string pool alone because IDXD **dedupes repeated values**. Used to identify the save blob's index space, now **solved**: the blob follows **`strings.tbl`'s** order β€” the display order *plus* the cut items only the localisation file lists (`Adhesive Mine B2A`, `Ballista GSH`, …) β€” pinned by four hand-written probe saves (9 Stiletto, 21 Falcon, 39 Tomahawk, 48 Jamming System) and closing exactly at index 53. `weapon.tbl`'s id list is **not** the index space; that it is also 54 long is a coincidence, and the two agree only to index 32. The retail save's five unexplained owned entries are the cut items, shipped owned and never rendered | diff --git a/docs/re/SESSION-2026-08-11.md b/docs/re/SESSION-2026-08-11.md new file mode 100644 index 0000000..5cbc9fc --- /dev/null +++ b/docs/re/SESSION-2026-08-11.md @@ -0,0 +1,69 @@ +# Session log β€” 2026-08-11 (autonomous run) + +What changed today, in the order a reviewer would want it, with the one item +that needs **your decision** called out first. + +## Needs a decision from you + +**Editing save slot 01.** Save files can now be written back +([savegame_edit.py](../../tools/re-capture/savegame_edit.py) re-wraps a real save +**byte-identically**), and hand-written saves load. That unblocked several +questions by probing throwaway slots. But the **title-menu screens read the +auto-save (slot 01)**, so the remaining save-field questions (the difficulty +enum, `SHAB.b`) need slot 01 itself edited β€” the only save with real progress. +Not done. Recoverable (full content backup + slot 01's bytes archived), but it is +your call. Everything else below was done in throwaway slots. + +## Findings, by area + +### Save format +- **The develop differential**: buying one Arsenal weapon moves exactly three + fields β€” Points `+24` (which **separates it from `+28`**), clear ratio `+8` + (so the ratio counts *collection*, not stages), and two entries of the 54-byte + blob. Blob alphabet: `0` locked / `2` developable / `4` owned; only the `4`s + are stored, `2` is re-derived at load. +- **The blob's index space is solved** β€” it follows `strings.tbl`'s item order, + **cut items included**, pinned by four probe saves and closing exactly at 53. +- **`SHAB` is the per-stage record table**: record 0 = Stage 01, and `c` is its + best clear time in ms (`324773` = the `05:24.77` MISSION SELECT shows). +- **Refuted**: every GHAD scalar as stage/difficulty (16 elements probed), and + "stage = filled `SHAB` count + 1". + +### Formats +- **T8aD 96 % β†’ 100 %** of the disc: a surface is a list of **sub-rectangles** + (`dst X, dst Y, w, h` per rectangle), not a 256 grid. The "~15 % variants" were + a wrong model. +- **LSTA**: the count was never unreliable β€” it counts **sprites *and* `PRMD` + primitives** (64/64 exact). +- **RATC**: "one level deep" is not a limitation; nested records are **leaves + that reference siblings by name** (10 144/10 148 resolve). +- **UI screens are fully reconstructible from their bundle** β€” element list + + order (declaration table, with a **parent index**) and placement/animation + (keyframe region). **Validated against the running game to Β±2 px.** +- **Movie ↔ subtitle ↔ voice link solved statically** (101 movies) β€” `INDEX` had + it filed as needing the running game. +- **XPR2 channel order confirmed** against the game (hue 23.3Β° vs 9.3Β°). + +### Meshes β€” the user-reported viewer bug +- Root-caused, **after one wrong turn I corrected in place**: the giant slab on + `e106` is not a stray volume but an **XBG7 mis-decode** β€” the same resource is a + 49Γ—23Γ—42 turret in 11 containers and 1600Γ—2100Γ—4800 in 3. +- **New detector**: a resource shared across containers must decode to the same + bounds. **125 of 681** shared resources fail that check β€” a measured lower + bound on *silent* mis-decodes (distinct from the 814 honest declines). + +## Two disc defects found +`SUBTITLE_S12B.tbl` (referenced by the movie table, present in **no** language) +and `pmbase.t32` (placed by `pmbase.rat`, present **nowhere**). A reimplementation +should skip a missing asset rather than treat it as a decode failure. + +## Process notes worth keeping +- Several "the data is inconsistent" comments meant **our model was incomplete** + (T8aD variants, the LSTA count). Re-test such notes rather than routing around + them. +- A test that walks the *oracle's* items and looks each up in our output **can + only fail one way** β€” it never sees extras. That shape hid the ship bug and, + earlier, `include_external`. +- When a diagnosis has eliminated every hypothesis and the last step is *a + visual*, do the visual first. Three iterations went by before a 30-second + render showed it.