Three bodies, one reason: the corpus grew from 22 saves to 43 and started
carrying content the shapes did not name.
ProjectName read `usnc` as a count of ONE item, on the strength of a comment
saying "usnc is 0 in every save available". Game::SpecialProjectNameGen::Write
(0x008147e0) says otherwise: each 88-byte record opens with a 32-byte table of
per-suffix use counts, `usnc` is the number of NON-ZERO slots, and the loop then
writes, for every non-zero slot, its INDEX as `usp` and its count byte -- movzx
widened -- as `usc`. Both go through WriteInt, so both are i32 on the wire.
Twelve corpus saves have one such slot and were round-tripping exactly 12 bytes
short: one i32 item, 4 length + 3 tag + 4 value + 1 pad.
`Sprj` is a polymorphic frame and both halves of its mapping are now measured.
ServerPlayer::Write emits `SprjT` from the plain member at project+0x3c, and
ServerPlayer::Read feeds that value to the factory at 0x008610a0, whose table at
0x008611e8 is 0 BackEngProject / 1 MonitorProject / 2 JewelsProject / 3
TechOfferProject -- and each of those constructors stores its own index back at
+0x3c. Only SprjT 0 is exercised by any save; the other three arms are typed
from the recovered schema and labelled as the hypotheses they are, and an
unknown SprjT falls to rest() so it shows up as opaque coverage rather than
being mis-read in silence.
FieldTemplate carried its points because no save had ever put one on the wire.
Six now do, so Game::FieldTemplate::Point is typed and bound. FTPPosX/PosY/Sqd
read 0 in every observed point, so their i32 disk type is still the schema's
word and not the corpus's, and the comment says so.
All 43 saves now round-trip byte-identically; the wire-schema conformance test
binds six new shapes with 0 MISMATCH and every one matching item for item.