Turns the differential finding into code. Each shrub body record is 6
varying bytes plus 2 constant ones; body_records() hands them back raw and
body_is_well_formed() checks the trailing pair.
ShrubHeader::region now takes an explicit span_end. It has to: without it
the data slice runs to the end of the lump, so the last class in each level
absorbs whatever follows shrub and a per-class check ends up measuring its
neighbours. That is the same unbounded-span mistake as before, caught this
time by a failing assertion rather than by reading the docs afterwards.
The disc test asserts well-formedness only for the 334 of 358 classes whose
span ends at the next class. The last class in each level has no successor,
so its span contains the 690 KB region after shrub and its body is not
wholly records -- that is the open question, and asserting either way would
be wrong. Shrub::span_end_is_a_successor records which case a span is.
Records are summed only across bounded bodies. Summing the last class too
reproduces the "3.5 million records" figure retracted two commits ago, to
within rounding -- which is a useful check on how easy that mistake is to
repeat by accident.
8,938 records across the 334 bounded bodies; 16 denormal reserved words;
358 headers naming their own class.
Extended the shrub header past the two self-identifying words. The first
four words are floats -- finite on all 358 spans -- though they do not
behave like a min/max bounding box, which orders correctly on only 146 of
358, so they are recorded as bounds rather than named. The word at +0x10
is positive everywhere, 3.0 to 20.0, exactly 5.0 on 281 of them.
The word at +0x14 is the interesting one. It is zero on 342 spans and the
bit pattern 04000000 on the other 16, which is a denormal of about 2.8e-45.
The Emotion Engine has no denormals, so hardware reads all 358 as zero.
That is only the second reading. My probe reported the field as 0.000
everywhere because it printed to three decimals, and the test caught it:
level 7 class 7 fails a strict zero check. The lesson is that "exact" in
a measurement means exact in the measured precision -- rounding in the
output made a real distinction invisible.
It also matches AGENTS.md on modelling the FPU rather than correcting it:
this field looks like noise on disc and is a constant to the machine that
reads it, and the parser accepts either rather than picking one.
The shrub body past +0x2c is still not decoded.
Shrub is the third class-geometry family and is not the moby one: all 358
spans on the disc have a leading word of 0 or one too large to index a
table into, so none contain a moby-style part table. The rule that holds
for 43,467 of 43,467 moby parts holds for zero of them.
Two header fields do self-identify, exactly, on all 358 spans: +0x24 equals
the owning class entry's own o_class, and +0x2c is a data offset landing
inside the span. Both are checked rather than merely reported -- a span
naming the wrong class is a different thing, and quietly returning the
mismatched value would hide that.
The body past +0x2c is not decoded. It tiles at no stride consistent with
the count at +0x28 (tails of 240, 240, 192 and 224 bytes against counts of
5, 6, 5 and 3), and the header's leading bytes are packed rather than
float even though +0x10 lands exactly on 5.0f on 281 of 358 spans.
Shrub class entries are 0x30 bytes rather than 0x20, because they carry
billboard texture fields the other two tables lack; the parser records that
so the stride is not re-derived.