P7: byte-exact re-base of the CORE heat leaf (HeatSink/Condenser/Reservoir/Generator/Myomers)

The reconstruction modeled the binary's shared alarm/connection types with undersized
stand-ins, sliding every field above them low (the 72-byte auxScreenNumber gap). Fix the
foundational heat-leaf classes byte-exact + static_assert-lock them, from the ctor decomp:

Shared types corrected:
  * SubsystemConnection 4 -> 0xC  (binary link node FUN_004af9cf; FUN_00417ab4 derefs +8)
  * GaugeAlarm54 = 0x54           (real AlarmIndicator FUN_0041b9ec; STATUS level at +0x14,
                                   so subsystem+0x184 == heatAlarm+0x14 == GetLevel())
    WatcherGaugeAlarm now typedefs GaugeAlarm54 (Watcher branch locks stay valid).

Byte-exact + locked (ctor-verified):
  * HeatSink   heatEnergy@0x158 linkedSinks@0x164 heatAlarm@0x170 resource@0x1C4
               pendingHeat@0x1C8, sizeof 0x1D0  (@004adda0)
  * Condenser  valveState@0x1D0 condenserAlarm@0x1DC  (@004ae568)
  * Reservoir  reservoirAlarm@0x1D0 ... squirtEfficiency@0x22C, sizeof 0x230  (@4aef78)
  * Generator  stateAlarm@0x1FC, sizeof 0x250  (@004b225c)
  * Myomers    phantom moverConnection tail removed (fits 0x358)

Three systemic bug classes fixed (added to the checklist in CLAUDE.md / HARD_PROBLEMS.md):
  * alias field    - a subclass member re-declaring an inherited slot the ctor reuses
                     (Condenser refrigerationOutput==massScale@0x160; Reservoir
                     coolantCapacity==thermalCapacity@0x128) -> use the inherited name
  * alarm-interior - a value read at alarm+0x14 modeled as a separate member
                     (HeatSink heatState@0x184, Reservoir injectActive@0x1e4)
                     -> route to alarm.GetLevel()
  * phantom field  - a member past the object end (Generator shortFlag@0x25C is really
                     *(owner+0x190)+0x25c the msg-manager, @004b0efc; Myomers
                     moverConnection@0x110 a write-only base slot) -> remove it

Heat conduction now reads the REAL heatEnergy=1.34e7 (not garbage); combat DESTROYED-in-8,
0 crashes, heapcheck-clean through construction.

REMAINING (measured; a distinct larger task): making PoweredSubsystem byte-exact grows it
+0x98 and cascades into MechWeapon/Emitter/PPC/Sensor/Myomers -- all model the 0x54
AlarmIndicator with 4-byte ReconAlarm / 8-byte HeatAlarm stand-ins and are short +
phantom-tailed; retyping without byte-exacting them overflows the Emitter alloc (heap
corruption). PoweredSubsystem kept on HeatAlarm(8) stand-ins (marked) pending a
subsystem-tree ALARM UNIFICATION. See docs/HARD_PROBLEMS.md P7.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-07 09:47:37 -05:00
co-authored by Claude Opus 4.8
parent 16ad741282
commit 1356870e56
11 changed files with 251 additions and 78 deletions
+10 -5
View File
@@ -143,9 +143,8 @@ Myomers::Myomers(
// (Subsystem flags @0x28 |= 8; optional graphic-state hook -- see header note)
// (WAVE 6 de-shim) the owner*/damageStructure shim backing fields are gone --
// their accessors return neutral defaults directly. Only moverConnection
// (used by ConnectToMover) remains a member.
moverConnection = -1; // @0x110 (detached)
// their accessors return neutral defaults directly. The mover handle lives at
// base+0x110 (not a Myomers own field); the coupling is inert, so no init here.
// Register the per-tick Performance. The binary ctor @004b8fec stores the
// MyomersSimulation pointer-to-member into activePerformance (this[7..9] =
@@ -481,9 +480,9 @@ void Myomers::ConnectToMover(SubsystemMessage &message)
int index = abs(message.value) - 1; // message+0xC
if (message.value < 1) {
MoverDetach(index); // owner mover vtable +0x3C
this->moverConnection /*@0x110*/ = -1;
/* base+0x110 = -1 (detached) -- inert, no base accessor yet */
} else {
this->moverConnection /*@0x110*/ = 0;
/* base+0x110 = 0 (attached) -- inert, no base accessor yet */
MoverAttach(index, &speedEffect); // owner mover vtable +0x38, feed @0x31C
// (original args: index,&speedEffect,this,10,9,0)
}
@@ -528,6 +527,12 @@ void Myomers::ToggleSeekVoltage()
//===========================================================================//
struct MyomersLayoutCheck
{
// OVERFLOW lock only: the PoweredSubsystem base is NOT byte-exact (its 0x54 alarms
// are modeled as HeatAlarm(8) pending the subsystem-tree alarm unification -- see
// powersub.hpp), so Myomers own fields don't land at their binary offsets yet. The
// phantom moverConnection tail was removed (it was a write-only base+0x110 shim), so
// the object fits the 0x358 factory alloc. Byte-exact offsets return with the P7
// weapon/power-subtree re-base.
static_assert(sizeof(Myomers) <= 0x358, "sizeof(Myomers) must fit the factory Memory::Allocate(0x358)");
};