re(route-b): 13 defaulted Size_Y values read out of the running game

Live definition objects carry no name, so identity comes from the values:
unit_signatures.rs prints (HP, Size_X/Y/Z) for every disc unit and vessel, and a
live object is the record whose known fields it reproduces. 7 of 14 match a disc
record outright; the other 7 match nothing because their records default a field
-- 18 of 23 vessel records are missing at least one, nearly always Size_Y.

Matching each single-default record against the live object that reproduces its
remaining fields resolves 13 values (Destroyer 200, Cruiser 600/700, ASFrigate
80, ADAN Destroyer 300, Acropolis 400, ISCMissile 300, plus _Inv/_EX variants).
Every one equals that unit's Size_X, so the engine's own parsed definitions
confirm the statically-derived inheritance rule.

Limits stated: variants share one live object, and the ...Ratio/...Count family
is not resolved -- their offsets in the live object are still unknown.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-12 20:28:55 +00:00
parent a836f8e9ea
commit 3322202732
3 changed files with 196 additions and 5 deletions

View File

@@ -66,8 +66,51 @@ with them. It also gives independent support to the **`Size_Y` inherits `Size_X`
default rule — five of the fourteen have `+0x30 == +0x34` exactly
(300/300, 400/400, 200/200, 600/600, 700/700) while the rest differ.
**Not yet resolved: which unit each object is.** Nothing in the first 96 words
is an obvious name pointer, so these cannot be matched to their disc records yet
— and that match is what turns the snapshot into defaulted-field *values*. The
next step is to dump further into the object (and the entity that points at it)
looking for the id or name field, which is one more single-call run.
## Identity without a name: match on the values
Nothing in the first 96 words is a name pointer, so identity comes from the
numbers themselves. `examples/unit_signatures.rs` prints `(HP, Size_X, Size_Y,
Size_Z)` for every craft unit and vessel on the disc; a live object is then the
record whose *known* fields it reproduces exactly.
Seven of the fourteen match a disc record on all four values — the player's
Delta Saber (`10, 7, 29`, HP 1500 in its player form and 1000 otherwise), the
ArrowHead, an ADAN turret, an Attacker. **The other seven match nothing, and
that is the point:** their disc records leave a field defaulted, so there is
nothing to match against. **18 of the 23 vessel records on disc are missing at
least one of these four fields**, nearly always `Size_Y`.
## ✅ Route B: 13 defaulted fields read out of the running game
Matching each disc record that defaults exactly one field against the live object
that reproduces its remaining fields resolves the missing value:
| unit | field | runtime value | live object (sx, sy, sz, hp) |
|---|---|---|---|
| `UN_e201_ADAN_ISCMissile` | `Size_Y` | **300** | `0xbd3e0680` (300, 300, 2800, 3000) |
| `UN_f106_TCAF_Destroyer` | `Size_Y` | **200** | `0xbd3ee300` (200, 200, 2000, 10000) |
| `UN_f106_TCAF_Destroyer_Inv` | `Size_Y` | **200** | `0xbd3ee300` |
| `UN_e105_ADAN_Cruiser` | `Size_Y` | **600** | `0xbd3fd800` (600, 600, 3800, 30000) |
| `UN_e105_ADAN_CruiserEX` | `Size_Y` | **600** | `0xbd3fd800` |
| `UN_f105_TCAF_Cruiser` | `Size_Y` | **700** | `0xbd40e200` (700, 700, 3800, 30000) |
| `UN_f105_TCAF_Cruiser_EX5` | `Size_Y` | **700** | `0xbd40e200` |
| `UN_f105_TCAF_Cruiser_Inv` | `Size_Y` | **700** | `0xbd40e200` |
| `UN_e108_ADAN_ASFrigate` | `Size_Y` | **80** | `0xbd3d2d80` (80, 80, 350, 4000) |
| `UN_e108_ADAN_ASFrigateEX` | `Size_Y` | **80** | `0xbd3d2d80` |
| `UN_e106_ADAN_Destroyer` | `Size_Y` | **300** | `0xbd3e6f80` (300, 300, 2100, 10000) |
| `UN_e106_ADAN_DestroyerEX` | `Size_Y` | **300** | `0xbd3e6f80` |
| `UN_f101_TCAF_Acropolis` | `Size_Y` | **400** | `0xbd3e1f00` (400, 400, 1400, 25000) |
**Every resolved value equals that unit's `Size_X`** — so the running game
confirms the `Size_Y` inherits `Size_X` rule that was derived statically from
sibling records, this time from the engine's own parsed definitions.
Two honest limits. Variants share a live object (`_Inv`, `_EX5`, `EX` map to the
same definition as their base), so the snapshot cannot distinguish them — the
value is right for the class, and per-variant differences in *other* fields are
not addressed. And this run only reaches the units Stage 02 instantiates: the
`…Ratio` / `…Count` family (`HQRatio`, `PowerRatio`, `ShieldRatio`, `HatchCount`,
`NodeCount`, `SequencingCount`, …) is defaulted on 523 records each and is
**not** resolved here, because those fields' offsets in the live object are not
yet known. Finding them is the next run: dump deeper than 96 words and look for
the constants the disc *does* set on the records that set them.