sots-re/findings/objects
alex a4aba6a9fb lane D2: the ship-design catalogue -- how designs persist, hull size, and the 0x400 flag
Blocker #4 on lane Y's path to a byte-matching turn.

HOW DESIGNS PERSIST, correcting a published finding. Game::ShipDesign::Write is
0x008325e0 and makes four stream calls. The recorded address 0x008747a0 is in
NO vftable at all (lane V2's inversion), so "ShipDesign::Write makes no stream
call" was a misattribution, not a fact about the class. Game::ShipDesign
derives from Game::ShipDesignDef and inherits IStreamable second, so its writer
is reached through an adjustor thunk -- which is what the slot-indexed
serializer sweep found instead. A design persists as two serializers, base and
derived: ShipDesignDef::Write 0x00827390 emits FAIDes/DHide/DWep/DName then
exactly three DSec frames (+0x4c command, +0x24 mission, +0x74 engine),
ShipDesign::Write appends Dtc, the Dwgv flag and a conditional Dwg frame.

THREE sections, not five. The campaign's "slots 3-4 reserved and always empty"
was save_reader.py's trailing Rest("sections") sweeping Dtc and Dwgv into the
section list, and stock_designs.py decoding them as two empty sections -- rule 8
in its exact form, reader and consumer agreeing with each other and both wrong.
Two independent enumerations say three: the writer's straight-line body, and the
ctor's eh_vector_constructor_iterator(this+0x24, 0x28, 3) closing at
0x9c = sizeof(ShipDesignDef).

DWep and Dwgv are BOOLs, not ints -- byte-indistinguishable from ints at a
four-character tag, the same class of defect as ObservedTech.odet.

THE 0x400 FLAG IS `defence_platform`, read off the .shipsection parser's own bit
setter at 0x005749b7. NOT lane B5's 0x400: that one is a fleet flag, on the wire
as FtFlg. The full role-flag table is in the finding. HULL SIZE is section_class
through a three-name stricmp table (Destroyer/Cruiser/Dreadnought -> 0/1/2),
absent or unrecognised meaning 0 with a log line rather than an error. Both
words are recomputed from the data files by ShipDesign::UpdateDerivedStats
0x0087e7c0 and neither is on the wire. Corroborated by the default hull-health
table the same bit picks: 500/3000/15000 without it, 100/500/1000 with.

MEASURED: the census rebuilt from each save's own state matches the record the
game archived, 480 leaves / 0 mismatched over 11 saves and 503 designs, computed
independently in Python and in C++. COVERAGE REPORTED AS LOUDLY: only 32 of the
480 leaves are nonzero, and three of the six census leaves (both cruiser rows,
dreadnought platforms) are unexercised by every save in the corpus.

Closed 0 / regressed 0 against the standalone's divergence list, reported
separately: the census leaves live in src/app's turn record, which lane A2 holds
this cycle, so this lane evaluated and reported rather than writing.

Oracles fixed openly (rule 12): save_reader.py's Des shape, 49/49 with three
corrected tests and one added that pins "exactly three DSec" against real saves;
stock_designs.json regenerated, whose diff is only raw_slots 5->3 and dWep
int->bool across all 127 designs with every other field identical;
test_design_rules.py still 32/32 with the same ground truth.

19 addresses in ghidra/addresses.d/lane-d2.json, no collision; the generated
header was validated to a scratch path, never written in place.
2026-09-08 12:45:36 -04:00
..
.keep.md scaffold RE campaign: board, backlog, findings template, verify tree 2026-09-07 14:40:44 -04:00
00-inventory.md map: RTTI class inventory (1924 types, engine=Mars); reseed board with real targets 2026-09-07 14:48:43 -04:00
aiagent-block.md A: the AIAgent CD blocks - derivation, addresses, and what stays a hypothesis 2026-09-08 07:33:33 -04:00
classes-game.txt map: RTTI class inventory (1924 types, engine=Mars); reseed board with real targets 2026-09-07 14:48:43 -04:00
classes-mars.txt map: RTTI class inventory (1924 types, engine=Mars); reseed board with real targets 2026-09-07 14:48:43 -04:00
ghidra-recon.md recon: RTTI hierarchy + vftables, affinity pin has no topology branch, save-struct reference 2026-09-07 15:01:50 -04:00
rtti-raw.txt map: RTTI class inventory (1924 types, engine=Mars); reseed board with real targets 2026-09-07 14:48:43 -04:00
save-editor-structs.md events: recover the player event-posting API (lane E) 2026-09-08 02:47:11 -04:00
schema-gaps-resolved.md objects: save-schema gaps resolved from serializers; StrategyServer complete 2026-09-07 16:24:54 -04:00
serializable-types.txt map: RTTI class inventory (1924 types, engine=Mars); reseed board with real targets 2026-09-07 14:48:43 -04:00
serializer-struct-recovery.md lane D: automated struct recovery from the IStreamable serializers 2026-09-08 05:51:37 -04:00
ship-design-catalogue.md lane D2: the ship-design catalogue -- how designs persist, hull size, and the 0x400 flag 2026-09-08 12:45:36 -04:00
struct-recovery.md lane S: std::string is 0x1c binary-wide; ObservedTech element fully mapped 2026-09-08 05:09:33 -04:00
svsctob-variants.md lane W: SvSctOb variant factories; fix the four save_reader.py defects openly 2026-09-08 07:05:09 -04:00
turncommands-block.md lane Q: TurnCommands_v5 reconciled and typed; SAVE_FORMAT section 11 corrected 2026-09-08 09:03:26 -04:00
wire-schema-channel.md lane G: record that save_reader.py still carries the four defects, and why it was left alone 2026-09-08 06:33:53 -04:00
wire-schema-closeout.md lane W: SvSctOb variant factories; fix the four save_reader.py defects openly 2026-09-08 07:05:09 -04:00