diff --git a/docs/re/live-unit-definitions.md b/docs/re/live-unit-definitions.md index 9f5df47..5b3e26d 100644 --- a/docs/re/live-unit-definitions.md +++ b/docs/re/live-unit-definitions.md @@ -256,3 +256,40 @@ confirmed: the first is a *conditional* assignment (it compares `+0x30` against register and picks between two values before storing to `+0x34`), not a plain copy, and offsets 48/52 are generic enough that both could belong to unrelated structures. Recorded as leads, not findings. + +## The filler, and a constant that turned out not to be a default + +The call graph completes: `sub_82341048` allocates **880 bytes** +(`addi r3, r0, 880` → `bl 0x8230C160`), calls the zero-filling constructor +`sub_8233FAF8`, and then hands the object to **`sub_82341A20`** — 15 876 bytes, +**3 969 instructions, 157 `stfs`, 892 calls**. That is the per-field filler for a +unit definition, and it is where any default has to be applied. + +Inside it, one pattern repeats: `lfs f, -15176(r30)` followed by `stfs f, +N(r29)` +for N = 336, 340, 344, 348, 352, 356, 360, 428, 512, 516 (and 124…160 of a +sub-object). `r30` resolves to `0x82090000 − 28780 = 0x82088f94` (from +`addis r11, r0, 0x8209` / `addi r30, r11, -28780`), so the constant sits at +**`0x820854cc`**, which the executable image gives as **`0x3f800000` = 1.0**. + +**That looks exactly like "write the default 1.0 into ten fields" — and it is +wrong.** Checking those offsets in the live objects first: + +| offset | `f001` fighter | `e007` turret | most others | +|---|---|---|---| +| `+336` | 0.261799 (15°) | 0 | 0 | +| `+344` | 0.174533 (10°) | 0 | 0 | +| `+356` | 0.698132 (40°) | 0 | 0 | +| `+428` | 0 | 0.785398 (45°) | 0 | +| `+512` | 0 | 0.523599 (30°) | 0 | + +Those are **radians** — 10°, 15°, 16°, 30°, 40°, 45°, 60° — i.e. per-unit angle +limits converted from the degrees the disc stores (`MaximumBank_Normal 60` → +1.0472). So the `1.0` is part of a formula or a conditional path in that +conversion, **not** a blanket default, and the fields themselves are 0 for units +whose records omit them. + +Which leaves the picture consistent with everything measured so far: **the ordinary +default is 0**, straight from the zero-filling constructor, and the interesting +non-zero defaults (`Size_Y` ← `Size_X`) are specific derivations to be found +individually in this filler. Worth noting for the reimplementation regardless: +**angle fields are stored in radians at runtime and in degrees on disc.**