The Mech ctor fully constructs (33 subsystems / 7 weapons on real TEST.EGG data, trace-confirmed). The crash is the control-mapping resource binding in MakeViewpointEntity resolving subsystem attribute IDs via GetAttributePointer -- the subsystems publish none (reuse base AttributeIndex). Documented the precise phase-5 step-1 scope (per-subsystem AttributeIndex + the ID chain) in MECH.NOTES.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6.8 KiB
MECH.CPP / .HPP — reconstruction notes
Status: Mech::Make (factory) reconstructed; the Mech constructor and the
entire mech subsystem are the CURRENT FRONTIER — the largest remaining
milestone in the reconstruction.
What is reconstructed
Mech::Make(MakeMessage *)=return new Mech(creation_message);— the factory the registry /MakeAndLinkViewpointEntitycalls.- The static shared data (ClassDerivations / DefaultData) — from the earlier staging.
- A staged Mech ctor that chains the real
JointedMoverbase ctor (so the model skeleton/segments stream from BTL4.RES) and then Fails.
The spawn factory path is now validated live: BTPlayer::CreatePlayerVehicle
builds the Mech::MakeMessage, Application::MakeAndLinkViewpointEntity routes
it through the registry to Mech::Make -> new Mech -> the JointedMover base
ctor (real engine code, streams the mech model) -> the Mech ctor Fail. Boot
reaches mech.cpp:66.
Why the Mech ctor is a milestone, not a brick
Mech::Mech is @004a1674, 5690 bytes — the largest single function in the
game. It walks the model's segment table and instantiates the full subsystem
roster (power, heat, weapons, actuators, controls, tech, damage zones), and
primes ~150 members. Reconstructing it requires three things the tree does not
yet have:
- The complete 1995 Mech member layout as named fields. MECH.HPP currently
carries
int reserved[331](1324 bytes) as a placeholder. BT411's Ghidra reconstruction sidesteps the layout entirely by writing raw offset pokes (Wword(0x104),*(void**)((char*)this + 0x388),this[0x14a]) — a WinTesla-port technique that CANNOT compile into a fresh 1995 build, where BC4.52 assigns offsets from the header declarations. So the layout must be derived as real members first. - The subsystem class hierarchy. MechSubsystem + derived: sensor, generator (gnrator), gyro, torso, HUD, myomers, plus the weapon subsystems (mechweap, ppc, gauss, projweap, mislanch, ammobin...). Of these, only GAUSS, PPC, SENSOR survive as source in the 4.10 archive (CODE/BT/BT); GNRATOR is a partial .TCP. The rest are part of the measured "917 missing functions" and come only from BT411's decomp.
- The segment-table walk that reads the model resource and instantiates + wires each subsystem.
BT411's mech reconstruction is ~12,255 lines across mech.cpp / mech2 / mech3 / mech4 / mechsub alone, before the subsystem TUs — this is the bulk of the remaining game.
Reconstruction plan (for the dedicated mech milestone)
- Derive the Mech member layout. Turn
reserved[331]into named members. Sources: BT411's offset comments (this[0x14a]gyroSubsystem,+0x388target entity, etc.) give the binary offsets; the embedded member types (NameFilter, AlarmIndicator, Slot/Socket, Reticle, Filter, CString, StateIndicator) come from the surviving MUNGA headers. Cross-check total size against the binary'snew Mechallocation. - Reconstruct MechSubsystem base + the subsystem roster, backdating BT411 mechsub.cpp + the per-subsystem TUs; fold in the surviving 4.10 GAUSS/PPC/ SENSOR source where it exists (authoritative over the decomp).
- Reconstruct the Mech ctor against the named layout: embedded-member construction, the segment-table walk, subsystem instantiation + wiring.
- Then the per-frame path (mech2 animation SM, mech3, mech4 locomotion/ targeting) and the damage/weapon handlers.
Until then the ctor Fails cleanly at the frontier; everything below it (engine, app, network, mission, player, factory, jointed-mover base) runs real reconstructed code.
PHASE 4 DONE + PHASE 5 FRONTIER (2026-07-20)
The Mech constructor WORKS. Live boot: new Mech -> segment walk instantiates
the full subsystem roster into the base Entity subsystemArray (dispatch on the
real VDATA classIDs; unknown IDs -> base MechSubsystem so no NULL slots) ->
caches sensor/gyro/sinkSource/hud -> mech links as viewpoint -> SetMappingSubsystem
installs the MechControlsMapper in slot 0 -> mission startup. All structural.
Phase-5 frontier (the behavioral integration): boot now crashes (real NULL
deref, not a staged Fail) in Simulation::GetAttributePointer reached via
Simulation::PerformAndWatch (the per-frame execute + watcher update). Root:
the reconstructed subsystems reuse the base Subsystem::AttributeIndex, so they
publish NONE of their real attributes (Sensor RadarPercent/SelfTest/BadVoltage,
Generator OutputVoltage, Emitter ChargeLevel, HUD/Torso/Gyro readouts, ...). The
watcher/gauge binding + per-frame PerformAndWatch resolve those attribute IDs and
hit a bad/NULL activeAttributeIndex path.
Phase-5 work, in order:
- Per-subsystem AttributeIndex: define each subsystem's AttributePointers[] (ATTRIBUTE_ENTRY(class, Name, member)) + a real AttributeIndex chained to the parent, and pass it to that subsystem's DefaultData (instead of the base). The surviving CODE headers (SENSOR.HPP, PPC.HPP, GAUSS.HPP) list the attribute enums; the decomp AttributePointers[] tables give the rest.
- Watcher / capability-chain / plug wiring in the Mech ctor (controllable/ heatable/weaponRoster/damageable) so PerformAndWatch has valid targets.
- The per-frame simulation itself: mech2 (animation SM), mech3, mech4 (locomotion/targeting) -- ~9K lines, currently all staged Fail.
- Rendering integration (the DPL renderer bridge) + playable verification.
Everything through the Mech ctor + viewpoint link runs real reconstructed code.
PHASE-5 CRASH LOCALIZED (2026-07-20)
Trace-confirmed: the Mech ctor FULLY constructs on the real pod TEST.EGG mech --
subsystemCount=33 weaponCount=7 (BT_MECH_LOG). The crash is AFTER construction,
in BTL4Application::MakeViewpointEntity's "Load the control mappings" block
(btl4app.cpp ~420): it SearchLists the ControlMappingsListResourceType resource
and binds each streamed mapping to a subsystem attribute -- via an AttributeWatcher
-> Simulation::GetAttributePointer(attributeID). The reconstructed subsystems
publish NONE of their real attributes (they reuse the base Subsystem::AttributeIndex),
so the streamed attribute IDs don't resolve.
Phase-5 step 1 = per-subsystem AttributeIndex, and it's a precision task:
- BT411 has the AttributePointers[] tables (mechsub 1, heat/HeatSink 12, powersub 4, Generator, sensor 3, torso 13, weapons, ...) -- but ATTRIBUTE_ENTRY(class,Name,member) needs each member NAMED + accessible (several of ours sit in reserved dynamics pads -- Gyroscope/Torso/HUD -- and must be broken out).
- The attribute-ID enums chain Simulation::NextAttributeID -> Entity -> Mover -> ... -> subsystem, and MUST match the 4.10 binary IDs (the streamed control mappings reference them by number). Then: watcher/plug/capability-chain wiring, then the mech2/3/4 per-frame sim (~9K lines, staged), then rendering. This is the behavioral half.