re: Size_Y is genuinely defaulted, and the loader leaves it at 0.0

pool_window.rs shows the Destroyer's raw tokens: "200.0" "Size_X" "Size_Y"
"2000.0" "Size_Z" -- Size_Y is a bare key, so the 13 runtime values recovered
earlier are real defaults, not a reader artefact.

The filler reads each size field through sub_822FC5A8, which loads f31 from
0x8209fd28 = 0.0 at entry and returns it on a pool miss, then stores to +52.
Nothing else in the 15876-byte filler writes +52, so the definition leaves a
defaulted Size_Y at 0.0 and the runtime 200 is written later.

Two corrections: the +48/+52/+56 comparison block builds a size-class bitmask
against 1000.0 (constant 0x8209fd20), not a has-value mask; and both earlier
Size_Y derivation candidates are refuted (one is a conditional pick, the other
multiplies into a different struct).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-12 22:47:43 +00:00
parent be42e998bc
commit 2ed475868b
2 changed files with 72 additions and 0 deletions

View File

@@ -293,3 +293,46 @@ 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.**
## Confirming the field really is defaulted, and where the loader leaves it
Before chasing the derivation any further it was worth checking the premise.
`examples/pool_window.rs` prints the raw token sequence around a key, and for the
Destroyer it reads:
```
[14] "200.0" [15] "Size_X" [16] "Size_Y" [17] "2000.0" [18] "Size_Z"
```
`Size_X` takes the value before it; **`Size_Y` is a bare key** — genuinely
defaulted, exactly as the value-before-key rule predicts. So the 13 values
recovered from the running game are real defaults, not a reader artefact.
The loader's own answer for a missing field is now pinned too. Each size field is
filled by the same three-call sequence — build the key string, call the float
accessor **`sub_822FC5A8`**, store the result:
```
0x82341d94 addi r4, r30, -14004 ; "Size_Y"
0x82341da0 bl 0x8217FA08 ; make key
0x82341dac bl 0x822FC5A8 ; read float
0x82341db0 stfs f1, 52(r29) ; -> Size_Y
```
and `sub_822FC5A8` loads `f31` from `0x8209fd28` = **0.0** at entry and returns it
when the pool lookup misses. **So the definition leaves a defaulted `Size_Y` at
0.0**, and nothing else in the 15 876-byte filler writes `+52`.
Two readings corrected on the way:
- The block at `0x82341e4c``0x82341ebc` that loads `+48`/`+52`/`+56` is **not** a
"was this field set" mask. It compares each against the constant at
`0x8209fd20` = **1000.0** and ORs flags into `+108` — a **size-class bitmask**
for large objects.
- Both earlier `Size_Y` candidates are refuted: `0x82217e18` conditionally picks
between two registers rather than copying, and `0x823081e8` *multiplies* `+48`
by a table constant into `+52` of a different structure.
So the runtime `Size_Y = Size_X` must be written **after** the definition is
loaded — by whatever instantiates a unit from it, or a post-load pass over the
table. `+108` itself is too generic to chase (1 129 loads image-wide).