From 2e0c8a06812db7fbeae4a7aef557b8ac459e5a65 Mon Sep 17 00:00:00 2001 From: Cyd Date: Fri, 24 Jul 2026 17:39:16 -0500 Subject: [PATCH] BT410: the gauge-block worksheet -- per-class re-hosting dossiers banked Three-dossier workflow output (65KB, verbatim): every class across the six missing gauge TUs audited -- engine base, the L4GAUGE.CFG keyword + the attribute each widget binds (and which reconstructed subsystem publishes it), the port-shim -> 1995-native mapping, BC++4.52 hazards, and the binary @ADDR anchors. This is the implementing session's hot-start worksheet for the first rendered milestone (see RENDER-ROADMAP.NOTES.md). Co-Authored-By: Claude Fable 5 --- .../source410/BT_L4/GAUGE-BLOCK.NOTES.md | 403 ++++++++++++++++++ 1 file changed, 403 insertions(+) create mode 100644 restoration/source410/BT_L4/GAUGE-BLOCK.NOTES.md diff --git a/restoration/source410/BT_L4/GAUGE-BLOCK.NOTES.md b/restoration/source410/BT_L4/GAUGE-BLOCK.NOTES.md new file mode 100644 index 00000000..51968b2c --- /dev/null +++ b/restoration/source410/BT_L4/GAUGE-BLOCK.NOTES.md @@ -0,0 +1,403 @@ +# The gauge block worksheet — re-hosting the 6 BT gauge TUs (prepped 2026-07-24) + +Three-dossier workflow output, banked VERBATIM for the implementing session. +Read RENDER-ROADMAP.NOTES.md first for the big picture. Donors: +C:\VWE\BT411\game econstructedtl4gaug/gau2/gau3/rdr/grnd/galm.*; +targets: the authentic surviving BTL4RDR/GRND/GALM.HPP interfaces + fresh +staged headers for gaug/gau2/gau3. Build order (authentic BTL4.MAK): +btl4rdr btl4gaug btl4gau2 btl4gau3 btl4grnd btl4galm. Verification oracle: +the SHIPPED exe's gauge output on the same GAUGE content (L4GAUGE=640x480x16, +HEAPSIZE=15000000, GAUGE INI granularityInKB=64, AWE32 on). + + + +============================================================================= +# btl4gau2.cpp/.hpp + btl4gau3.cpp/.hpp — per-class decode survey + +Files: C:\VWE\BT411\game\reconstructed\btl4gau2.hpp (585 ln) / btl4gau2.cpp (2265 ln) / btl4gau3.hpp (304 ln) / btl4gau3.cpp (1060 ln). TU extents: gau2 = @004c6798..@004c9b50 (+ VehicleSubSystems::Make @004cbaf0), gau3 = @004c9bd0..@004cbea0. Link order btl4gaug -> btl4gau2 -> btl4gau3 -> btl4grnd. + +## BTL4GAU2 — composite panel clusters + +**SeekVoltageGraph** : GraphicGauge (vtbl 0051a1fc). ctor @004c6798 / dtor @004c68ac / BecameActive @004c6920 / Execute @004c6934 / ClearGraph @004c6be4 / DrawCursorBox @004c6c30 / DrawTicks @004c6c6c. Binds 4 attribute POINTERS via AttributePointerOf (the FUN_0041bfc0 shim over Simulation::GetAttributePointer, miss normalised NullAttribute->NULL): "CurrentSeekVoltageIndex"/"MinSeekVoltageIndex"/"MaxSeekVoltageIndex"/"SeekVoltage" — ALL PUBLISHED by our recon (emitter.hpp: seekVoltage[5]@0x1C4, Min@0x3F8/Max@0x3FC; myomers.hpp: seekVoltage@0x198 + the full Myomers attr list). Live cursor derefs liveVoltage (@0xA4) = OutputVoltage attr ptr (energy path) or MyomerCluster::seekValue cache. Response sampler = subsystem vtbl slot 15 via BTSeekVoltageSample bridge (Emitter @004bb42c sqrt(seekPower/2e8), Myomers @004b8f94 sqrt(speed*3.6/350)); destroyed test = BTSubsystemDestroyed (simulationState@0x40==1) bridge. Plot constants x87-recovered: 230/187 scale, 1200 step, 12000 limit, 1/12000 ld80 norm, 9999 redraw sentinel. Port shims: the two bridges + ctor-seeded previousVoltage/cursor/tick fields (binary leaves uninit) + BT_SEEK_LOG probes. Its Execute's full-box CLEAR is the shared Eng bit-plane's top-box eraser (Gitea #10 finding A). + +**ConfigMapGauge** : GraphicGauge (vtbl 0051a1b8). @004c6d80 ctor / @004c6e54 dtor / @004c6ee0 LinkToEntity / @004c6ef4 BecameActive / @004c6f1c Execute. NOT attribute-bound: reads LBE4ControlsManager::buttonGroup[btn].GetMapState(dest, receiver, msgID, 0x10000) with the feed from BTSubsystemControlFeed (subsys+0xE8 &fireImpulse / +0xEC messageID). 4-slot table PE-recovered: {0x0d,0x45 Pinky}{0x25,0x46 ThumbLow}{0x3d,0x40 Trigger}{0x55,0x47 ThumbHigh}, blit x=0xc, state 0..3 -> cm_off/cm_other/cm_only/cm_both.pcc over btjoy.pcc. Key correction on record: @004c6ee0 is the GaugeBase::LinkToEntity vtbl-slot-9 OVERRIDE (armed by the viewpoint-bind broadcast, APP.cpp:1277 -> GAUGREND.cpp:3011), not an uncalled SetColor — the old "authentically dormant" claim is retired. Port fix in Execute: issue #42 doubled-joystick — MoveToAbsolute(0,0) restored before the base blit. + +**AnimatedSubsystemLamp / AnimatedSourceLamp** (best-effort names) : OneOfSeveral. @004c70a4 / @004c7160 ctors, shared-shape dtors @004c7134/@004c71f0. Drive inherited `selected`@0xAC via file-private connections: CoolingLoopConnection (@004c3134 ctor/@004c31a0 sampler) -> BTCoolingLoopFrame (coolantAvailable@0x134 gate, linkedSinks "HeatSink" resolve, condenserNumber@0x1d4); PowerSourceConnection (@004c31ec/@004c3258) -> BTPowerSourceFrame (Generator::generatorNumber@0x1E0 via named voltageSource). Two applied port fixes documented: SHADOW-FIELD (redeclared `selected` shadowed the base -> lamps frozen at frame 0) and COLOR (ctor dropped bg=0xff/fg=0 -> black-on-black). Both connections are DATABINDING-RULE rewrites: the binary's ResolveLink(AttributePointerOf("InputVoltage"))/attr-id-3 walks died when BT_DEV_GAUGES audio attribute rows shifted the chained ids, so the shims pass the SUBSYSTEM and read named members in powersub.cpp/heat.cpp. + +**ScalarBarGauge** : BarGraphBitMapScalar (WipeGaugeScalar : GraphicGauge; engine bar @00472ef0), vtbl 0051a0e4. Two ctors: @004c721c "slotOf" (GeneratorVoltageConnection @004c3288/@004c32f4 -> BTGeneratorVoltage = ResolveVoltageSource()->MeasuredVoltage(), Generator::outputVoltage@0x1DC) and @004c72ac plain-Scalar (GaugeConnectionDirectOf @00474855 from a direct Scalar*, the GeneratorCluster path). Shared dtor @004c733c. Deliberately declares NO value field — writes inherited currentValue@0xB4; a redeclaration would shadow/mis-align (stated in hpp:237-239). Overloads disambiguated Scalar* (exact) vs void* (std-conv) — passing a literal 0/NULL is ambiguous under both BC4.52 and modern resolution (BC4.52 hazard). + +**GeneratorCluster** : GraphicGauge (vtbl 0051a0a0). Make @004c7368 / ctor @004c746c / dtor @004c7778. Registered config primitive "GeneratorCluster" (15 params; NO leading rate token — panel rate hard-wired 0xFFFF); resolves GeneratorA..D by FindSubsystem, warns "Subsystem not found". Binds by name: CurrentTemperature/DegradationTemperature/FailureTemperature (published, heat.hpp +0xE4/+0xE8/+0xEC family), CoolantMassLeakRate, OutputVoltage (Generator states published in powersub). Raw-offset residue: leak "third" = *(subsys+0x150), lamp source = subsys+0x1D4 (generatorOn) — both 1995-binary offsets on non-byte-exact compiled objects. 5 children: VertTwoPartBar, ScalarBarGauge(@004c72ac), BitMapInverseWipe, 2x TwoState. Port shims: BecameActive (paints background; the engine-base default self-inactivates) + no-op Execute (2007 engine base Gauge::Execute is Fail->abort, GAUGE.cpp:598 — the 1995 base was a no-op; "only the dtor overridden" decode corrected). Children NOT deleted in dtor (renderer owns; binary-faithful). + +**PrepEngrScreen** : GraphicGaugeBackground (vtbl 0051a06c; paint-on-activation, no Execute). Make @004c7b30 / ctor @004c7bf0 / dtor @004c7d14 / BecameActive @004c7e48. Config "prepEngr" (mode, screen 1..12, font + 6 label .pcc's). Was a MISLABELED stub (mech/screenNumber SWAPPED, no methodDescription -> 12 CFG lines parse-skipped). Binds: mech roster walk (skip slot 0) + BTGetSubsystemAuxScreen bridge (auxScreenNumber/placement/label — replaces raw sub+0x1dc/0x1e0/0x1e4 which read garbage on our layout) + ClassID dispatch (0xBC3 Sensor / 0xBC6 Myomers / 0xBC8 Emitter / 0xBD4 PPC / 0xBCD Projectile / 0xBD0 Missile / 0xBCE Gauss unsupported). BEST-EFFORT residue: heat-sink number = resolved HeatSink link +0x1d4 (guarded), label bitmap via bridge label64 instead of sub+0x224. DrawStatusCells (FUN_004c79c8 shape) carries the binary's absolute cell rects; the C-cell compound blank is a marked plain-fill substitute. + +**SubsystemCluster** (base of the family) : GraphicGauge (vtbl 0051a020). ctor @004c8140 / dtor @004c87dc / Execute @004c88e4 / GetDrawState @004c8990 / DrawPanelFrame @004c89c4 / ReleaseChildren @004c8820 / SetChildrenEnable @004c8a28. Base ctor hard-wires rate/mode 0xFFFF/0xFFFFFFFF; mfd_mode/eng_mode are the two bit-plane child masks. Binds by name: InputVoltage/HeatSink/CurrentTemperature/DegradationTemperature/FailureTemperature/CoolantMassLeakRate (temps published). 11 children incl. HorizTwoPartBar, 2x cooling lamps, 2x bus lamps (gated on InputVoltage presence), ScalarBarGauge evolt 0..12000, OneOfSeveralInt btecmode (RAW subsys+0x134, best-effort), OneOfSeveralStates btemode (RAW +0x2b8, generator-only via BTIsPoweredSubsystem shim for IsDerivedFrom 0x50f4bc), 2x VertTwoPartBar (second off the ResolveLink'd HeatSink), LeakGauge (RAW +0x150 third). POLARITY CORRECTION (PPC-dial fix take 3): *(subsys+0x40)==1 means FAILED; failedState now derived from BTSubsystemDamageLevelOf(subsystem) >= 1.0 (truth source DistributeCriticalHit, mechdmg.cpp). GetDrawState: 1 = dead-panel; the binary's secondary *(subsystem+0x278)!=4 condition is UNIDENTIFIED [T4] and dropped. BecameActive is a port addition (previousDrawState=-1 force repaint). + +**HeatSinkCluster** : SubsystemCluster (vtbl 00519fd4). ctor @004c8a6c / dtor @004c8d18 / Execute @004c8db0. Adds 4 TwoStates + NumericDisplayScalar (helv18) reading heatLoadScaled = heatLoad * 100.0 (_DAT_004c8df0). Bindings: heatFailLamp <- own failedState; hsFailLamp <- failFlag = (failSubsystem("HUD")+0x40 == 1) RAW best-effort; powerFail RAW subsys+0x324; structFail RAW subsys+0x320; heatLoad connection RAW subsys+0x31c — all four raw offsets are 1995 layout on compiled objects (unbridged; hazard class identical to the fixed CoolingLoop/AFC100 bugs). + +**MyomerCluster** : SubsystemCluster (vtbl 00519f88). ctor @004c8df4 / dtor @004c8f54, Execute = base. Adds SeekVoltageGraph ("EngrGraph", edestryd.pcc, draw_cursor=False) fed by a GeneratorVoltageConnection into seekValue@0xCC, plus seek-step OneOfSeveralInt bteseek — the raw +0x800 was OOB for a 0x358 Myomers; re-pointed at Myomers::currentSeekVoltageIndex@0x320 (INFERRED — @004c8df4 not in the assert-anchored export). + +**WeaponCluster** : SubsystemCluster (vtbl 00519f38). ctor @004c8fc4 / dtor @004c91d4 / BecameActive @004c9258 / Execute @004c9290 / virtual DrawWarningLamp @004c932c. Binds "PercentDone" — PUBLISHED: mechweap.hpp PercentDoneAttributeID = 0x12 @0x320 rechargeLevel (TriggerState 0x13 @0x31C also published, feeding ConfigMapGauge's fireImpulse path). Children: SegmentArc270 recharge dial 0..360 + ConfigMapGauge; warning lamp toggles at percentDone > 0.99 (_DAT_004c92e0), gated to draw-state 0 (alive). Port shims: PPC-dial repaint fix (base black-fill wipes the arc's incremental draw memory -> re-BecameActive arc/configMap/lamp on draw-state flips), diagWeaponName diagnostic member (APPENDED field — offset-layout deviation, plain-new only), BT_PANEL_LOG 61-prime probe (60 aliases on 6-panel Avatars). + +**EnergyWeaponCluster** : WeaponCluster (vtbl 00519ee8). ctor @004c93b0 / dtor @004c94dc. Adds SeekVoltageGraph (draw_cursor=True) on the "OutputVoltage" attr ptr — note the port-extra 0x1D "OutputVoltage" MechWeapon alias is RETIRED (mechweap.hpp:208-212); Emitter publishes its authentic 0x1D-0x25 chain (currentLevel@0x414 raw volts). Seek-step OneOfSeveralInt RAW subsys+0x3f0 (Emitter::seekVoltageIndex, best-effort). + +**BallisticWeaponCluster** : WeaponCluster (vtbl 00519e98). ctor @004c9558 / dtor @004c9940 / BecameActive @004c99cc / Execute @004c9a38 / TestInstance @004c9adc / DrawWarningLamp @004c9b50 (override: base dot + ammo-numeric SetColors swap for the stencil look — the port originally had only the base). Bindings: ammo count via BTWeaponAmmoBin + BTAmmoBinCountPtr bridges (Streak/AFC100 fixes; old raw bin+0x180 was garbage), feed state via BTAmmoBinFeeding (was raw bin+0x18c). REMAINING RAW HAZARDS: Execute reads jammed = *(subsys+0x364)==5 "weaponAlarm Jammed", but our recon PUBLISHES WeaponState 0x1C @0x350 weaponAlarm — offset disagreement AND an unbridged raw read; TestInstance still walks raw ResolveLink(subsys+0x43c) + *(ammoBin+0x180) despite the ctor's bridge fixes. Bring-up NULLs: ammoErase (GraphicsViewRecord @0044ad78 engine-internal) and ejectWipe (BitMapInverseWipeScalar @004c61c8 not yet reconstructed in btl4gaug); reload elapsed-seconds left at base (frame clock DAT_0052140c unwired). + +**VehicleSubSystems** (no instance) : static Make @004cbaf0, config primitive "vehicleSubSystems" (8 typeString strip bitmaps). Walks the roster (skip slot 0), filters/reads aux screen via BTGetSubsystemAuxScreen, dispatches on ClassID: 0xBC3 Sensor->HeatSinkCluster(+"HUD" subsystem), 0xBC6->MyomerCluster, 0xBC8/0xBD4->EnergyWeaponCluster (cluster image picked by RAW *(sub+0x334)==0 ? qcircle : qcircr), 0xBCD/0xBD0->BallisticWeaponCluster, 0xBCE Gauss warned-unsupported (authentic). kAuxGeom = the 12 PE-recovered geometry rows (@0x51bf34 in the binary). SubsystemDisplayFilter is a permissive True stub for FUN_0041a1a4. + +## BTL4GAU3 — multiplayer / tactical HUD + +**PlayerStatusMappingGroup** : NOT a Gauge — plain 28-element group of ColorMapperArmor (engine ctor @004c3b98, 0x70 alloc each). build @004c9bd0 / destroy @004c9ca8 / Execute-all @004c9cf4 / SetColor-all @004c9d18. Zone table = the 28 dz_* names (PTR_s_dz_door_0051a240), resolved per-mech by GetDamageZoneIndex (FUN_0042076c, -1 => inert); palettes adpal.pcc/adpal2.pcc; base colour index increments per zone. + +**SectorDisplay** : GraphicGauge (vtbl 0051beec, sizeof 0xC4 = Make alloc, static_assert overflow-lock only). Make @004c9d44 / ctor @004c9e10 / dtor @004c9f94 / BecameActive @004ca038 / LinkToEntity @004ca068 (slot 9) / Execute @004ca07c. Config "sectorDisplay" (rate, mode, image, color, okColor); was PROSE-ONLY/parse-skipped, now byte-verified. Binds the LinkToEntity'd subject's localOrigin.linearPosition: sectorA = Round(-Z*0.01)+500, sectorB = Round(X*0.01)+500 into two 3-digit NumericDisplays; one-shot grid-cell blit (cellWidth = imageW/14, cells 12..13). Port shims: durable gridImage name copy (config string is transient), viewpoint-entity fallback when the LinkToEntity broadcast is unwired (marked; binary gates purely on subject!=0), BT_SECTOR_LOG. + +**TeamStatusDisplay** : GraphicGauge (vtbl 0051bea8) — STILL PROSE-ONLY in the .cpp (header decl + @ADDR comment block @004ca208 ctor / @004ca39c dtor / @004ca424 SetEnable / @004ca438 BecameActive / @004ca478 Execute; no bodies, no methodDescription). Documented bindings: "Players" node (FindByName @00403ad0), slot by entity+0x1cc player index, kills from entity+0x1c8 (our recon publishes killCount in btplayer), rank/team entity+0x204 via FUN_0044fc10, spectator bit +0x29&0x40, local player via App+0x6c mech ->+0x190 ->+0x1cc. 8 rows of NumericDisplay pairs. + +**PilotList** : GraphicGauge (vtbl 0051be64, sizeof 0x178). Make @004ca90c / ctor @004ca958 / dtor @004cab10 / BecameActive @004caba4 / Execute @004cabd0 / DrawMechIcon @004cad70 (DrawAmmoCount @004cae20 documented in hpp). Config "pilotList" (rate, mode, font) — the Comm KILLS/DEATHS roster; layout from the PE-parsed DAT_0051af88 8x(x,y,mode) table (Make position UNUSED). One slot per frame round-robin. Bindings ALL via compiled-member bridges (the gauge scoring wave databinding fix): BTResolveRosterPilot (mechmppr.cpp, ControlsMapper roster), BTPilotKills = killCount, BTPilotDeaths = Player::deathCount (NOT the binary's dead pad_0x280), BTPilotIsSelected (replaces the raw local+0x284/tgt+0x190 walk that AV'd under the SEH guard = the silent empty scoreboard). Residue: DrawMechIcon still derefs RAW pilot+0x1e0 nameId (harmless — feeds the always-NULL LookupPlayerNameBitmap stub, so the tinted-box branch runs). Byte-faithful quirk kept: e.resolvedMech never latched on the occupied path -> icon redraws every occupied frame. + +**PlayerStatus** : GraphicGauge (vtbl 0051be20, 200-byte alloc). Make @004cae90 / ctor @004cb1a8 / dtor @004cb28c / BecameActive @004cb310 / Execute @004cb358. Config "PlayerStatus" (rate, mode, playerNumber 1..8, font, color, okColor, aliveColor, deadColor); Make rewired to parameterList + port/x/y bug fixed (binary-side Make passed entity/renderer as x/y). Bindings: player resolve via GetMissionPlayer (playerIndex==0 ONLY — players 2..8 never resolve in the port; binary walked the App+0x24 "Players" dictionary), then RAW offsets on the player: +0x1FC vehicle, +0x1C8 score (read as Scalar), +0x1C4 alive/dead. Our recon publishes currentScore/killCount in btplayer — these raw reads are the same unbridged hazard class PilotList already fixed (mitigated only by the BT_DEV_GAUGES guarded Execute). CreateMutantPixelmap8 @004cafac is a marked BRING-UP STUB (recoloredMech stays NULL -> outline blit skipped; DynamicMemoryStream read API + pixel copy unmapped; ReadStreamString @004caf50 deferred with it); nameImage cache (App+0xC8) unwired -> filled-box branch; mappingGroup rebuilt per player change (dtor deletes it — fixes a binary leak); disabled-state refresh hardcoded 0 (Mech::IsDisabled needs a Mech* cast, mech.hpp collides in this TU). + +**MessageBoard** : GraphicGauge (vtbl 0051bddc, sizeof 0xA4 = Make alloc; layout-check friend struct). Make @004cb678 / ctor @004cb704 / dtor @004cb788 / BecameActive @004cb7fc / SetSource @004cb818 (no recovered caller) / Execute @004cb82c. Config "messageBoard" (rate, mode, btsmsgs.pcx, color). Strip cell math: cellX=(id&3)<<7, cellY=(id>>2)<<5, 0x80x0x20 cells, sender name cell at x=128, colour 7 (FUN_004c2f88). Field-order FIXes recorded: trackedMech@0x90 (was "int enabled"), previousNameId@0x9C=-1 / previousMessageId@0xA0=-2 (were swapped). Port deviations: FEED LIVE lazy viewpoint bind (binary's SetSource never called -> board authentically empty), message resolve via BTResolveMessageBoard bridge — currently a NULL stub in btplayer.cpp:1557 (StatusMessagePool is a NULL stub, BTPlayer+0x1dc never populated), so the board stays empty; previousNameId tracks the resolved BITMAP pointer instead of the binary's entity pointer (same change granularity). + +## Published-attribute cross-map (gauge reads -> what the recon already publishes) +- PercentDone 0x12 @0x320 rechargeLevel, TriggerState 0x13 @0x31C fireImpulse, WeaponState 0x1C @0x350 weaponAlarm — mechweap.hpp:196-212; 0x1D is NextAttributeID (the old port-extra "OutputVoltage" 0x1D alias RETIRED; Emitter chains authentic 0x1D-0x25 incl. currentLevel@0x414). WeaponCluster's PercentDone and ConfigMapGauge's control feed land on published members; BallisticWeaponCluster's jam test does NOT (raw +0x364 vs published weaponAlarm@0x350). +- Seek family (CurrentSeekVoltageIndex/Min/Max/SeekVoltage/OutputVoltage) — published on Emitter (emitter.hpp) and Myomers (myomers.hpp); SeekVoltageGraph binds all of them by name. +- HeatSink temps (CurrentTemperature/DegradationTemperature@0xE8/FailureTemperature@0xEC, "HeatSink" link) — published in heat.hpp/heat.cpp; BTCoolingLoopFrame reads named linkedSinks/condenserNumber. +- Generator states (outputVoltage/generatorNumber via ResolveVoltageSource/MeasuredVoltage) — powersub.cpp bridges BTGeneratorVoltage/BTPowerSourceFrame/BTIsPoweredSubsystem/BTGetSubsystemAuxScreen. +- killCount/deathCount/currentScore — btplayer.cpp bridges BTPilotKills:1479/BTPilotDeaths:1493; PilotList uses them; PlayerStatus and (prose) TeamStatusDisplay still read raw +0x1C8. + +## BC4.52 hazards (things that will not survive the authentic Borland 4.52 build) +1. static_assert (C++11) — btl4gau3.hpp:134 (SectorDisplay) and btl4gau3.cpp MessageBoardLayoutCheck: must become compile-time array tricks or be dropped (they are 2007-base-relative anyway). +2. `bool` keyword — btl4gau2.cpp:152/167 extern decls (BTGetSubsystemAuxScreen, BTIsPoweredSubsystem): BC4.52 predates bool; the surrounding code correctly uses Logical/True/False. +3. std::flush / namespaced iostream in every env-gated probe (BT_SEEK_LOG/BT_TEMPBAR_LOG/BT_PANEL_LOG/BT_VSS_LOG/BT_SECTOR_LOG/BT_SCORE_LOG/BT_PS_LOG) — port-only diagnostics; strip for a 4.52 build. +4. ScalarBarGauge ctor overload pair (void* vs Scalar* 15th arg): a literal 0/NULL argument is ambiguous; all current call sites pass typed pointers (safe). +5. Placement new in all the Make()s + PlayerStatusMappingGroup — needs under BC4.52 (supported). +6. Old for-scope: no same-name redeclaration collisions present (checked every multi-loop function; variables differ or live in separate functions). +7. WeaponCluster::diagWeaponName is an APPENDED member (port diagnostic) — changes sizeof vs the binary's 0xE8-family layouts; benign only because clusters are plain-new (no operator new(size) alloc locks), flagged in the header. +8. (int)(size_t) pointer truncation in MessageBoard::Execute / PilotList — fine on 32-bit BC4.52 (size_t via stddef.h), flag for any 64-bit target. + +### Load-bearing quotes + +``` +btl4gau2.cpp:2048 — jammed = (*(int *)((char *)subsystem + 0x364) == 5); // BEST-EFFORT raw (weaponAlarm==Jammed) [conflicts with mechweap.hpp:207 'WeaponStateAttributeID, // 0x1C @0x350 weaponAlarm (binary table END)'] +``` + +``` +mechweap.hpp:196-197 — PercentDoneAttributeID = 0x12, // @0x320 rechargeLevel / TriggerStateAttributeID, // 0x13 @0x31C (PINNED -- the fire-button binding) +``` + +``` +mechweap.hpp:208 — issue #11: the 0x1D \"OutputVoltage\" PORT-EXTRA alias is RETIRED -- +``` + +``` +btl4gau2.cpp:1580-1582 — the binary's secondary condition (*(subsystem+0x278) != 4 -> 1) remains unidentified [T4] -- a healthy subsystem reads ALIVE here until that field is decoded (open-questions). +``` + +``` +btl4gau2.hpp:237-239 — Its value slot @0xB4 (this[0x2D]) IS the inherited WipeGaugeScalar::currentValue -- the connection writes it, so NO own field is declared (declaring one would shadow / mis-align). +``` + +``` +btl4gau2.cpp:2093 — if (ammoBin == NULL || *(int *)((char *)ammoBin + 0x180) == 0) [TestInstance still raw despite the ctor's BTAmmoBinCountPtr bridge fix] +``` + +``` +btl4gau3.cpp:801 — scoreDisplay->Draw(&localView, *(Scalar *)((char *)player + 0x1C8)); // FUN_00470430 [raw player offset; btplayer publishes currentScore/killCount] +``` + +``` +btl4gau3.hpp:134 — static_assert(sizeof(SectorDisplay) <= 0xC4, \"SectorDisplay must fit its 0xC4 Make alloc\"); [C++11, not BC4.52] +``` + +``` +btl4gau2.cpp:152 — extern bool BTGetSubsystemAuxScreen(Subsystem *sub, int *screen, int *placement, char *label64); [bool keyword = BC4.52 hazard] +``` + +``` +btl4gau2.hpp:165-170 — @004c6ee0 -- the GaugeBase::LinkToEntity OVERRIDE (vtbl slot 9, +0x24), NOT a \"SetColor\" ... (The old \"SetColor, no callers -> authentically dormant\" claim was a mislabel.) +``` + +``` +btl4gau2.hpp:304-306 — the engine base Gauge::Execute is Fail(\"not overridden\")->abort (GAUGE.cpp:598) ... (The decode's \"only the dtor is overridden\" was wrong: the 1995 base Gauge::Execute was a no-op, the 2007 engine's aborts.) +``` + +``` +btl4gau3.cpp:614-618 — CreateMutantPixelmap8 BRING-UP STUB: 'With this stub recoloredMech stays NULL -> Execute skips the outline blit; the score, name box, and alive/dead status box still render.' +``` + +``` +btl4gau3.cpp:998-999 — trackedMech stays NULL and Execute is a safe no-op == the DEFERRED empty board [BTResolveMessageBoard = NULL stub at btplayer.cpp:1557] +``` + +``` +btl4gau2.cpp:1751-1755 — 0x800 was OOB for a 0x358 Myomers (garbage) -> the seek gear step is Myomers::currentSeekVoltageIndex@0x320 ... INFERRED (the MyomerCluster ctor @004c8df4 isn't in the assert-anchored export). +``` + +### Open questions +- SubsystemCluster::GetDrawState [T4]: the binary's secondary condition *(subsystem+0x278) != 4 is unidentified and currently dropped (failedState alone decides) — decode the +0x278 field (btl4gau2.cpp:1574-1583). +- BallisticWeaponCluster jam test reads raw *(subsys+0x364)==5 while mechweap.hpp publishes weaponAlarm as WeaponState 0x1C @0x350 — reconcile the offset (ProjectileWeapon-specific alarm vs base?) and route through the published attribute or a bridge; same class of layout bug as the fixed AFC100/Streak counters. +- BallisticWeaponCluster::TestInstance and Execute still walk raw ResolveLink(subsys+0x43c) and *(ammoBin+0x180) despite the ctor's BTWeaponAmmoBin/BTAmmoBinCountPtr bridge fixes — unify on the bridges? +- HeatSinkCluster raw offsets unbridged: heatLoad subsys+0x31c, powerFail +0x324, structFail +0x320, failSubsystem(+HUD)+0x40 — all 1995-layout reads on compiled objects (BEST-EFFORT flagged); candidates for the next databinding wave. +- PlayerStatus raw player offsets (+0x1FC vehicle, +0x1C8 score-as-Scalar, +0x1C4 alive/dead) vs the published btplayer members (currentScore/killCount) — PilotList already migrated to bridges; PlayerStatus has not. Also players 2..8 never resolve (GetMissionPlayer only serves playerIndex 0) — is the multi-player name-tag wanted in the port? +- TeamStatusDisplay is still prose-only (no ctor bodies, no methodDescription, no config keyword registered) — build it or record it as intentionally deferred? +- MyomerCluster seek-step source Myomers::currentSeekVoltageIndex@0x320 is INFERRED, not assert-anchored (@004c8df4 missing from the export) — verify against a live myomer eng page. +- Bring-up NULLs still open: BallisticWeaponCluster ammoErase (GraphicsViewRecord @0044ad78) and ejectWipe (BitMapInverseWipeScalar @004c61c8 not yet in btl4gaug); reload elapsed-seconds frame clock (DAT_0052140c) unwired; CreateMutantPixelmap8 + App+0xC8 name-bitmap cache (PlayerStatus outline/name and PilotList name icons draw the fallback boxes); MessageBoard feed dead until StatusMessagePool (btstubs.cpp:62) is real. +- ConfigMapGauge GetMapState mode constant 0x10000 ('ModeNonMapping -- the persistent groups') — stated but not marked verified against the LBE4 controls manager; confirm on a live trigger-config check. +- SubsystemCluster modeLamp (subsys+0x134) and stateLamp (subsys+0x2b8) remain BEST-EFFORT raw offsets on non-byte-exact objects — same bridge treatment as CoolingLoop/PowerSource pending. + + +============================================================================= +## 1. Donor -> authentic-header method mapping + +The donor headers in C:\VWE\BT411\game\reconstructed are already verbatim copies of the surviving TeslaRel410 headers, so re-hosting is near-1:1; the deltas are listed per TU. + +### btl4rdr (MapDisplay radar) — donor C:\VWE\BT411\game\reconstructed\btl4rdr.cpp vs authentic C:\VWE\TeslaRel410\CODE\BT\BT_L4\BTL4RDR.HPP +Every class method in the donor matches the authentic header signature EXACTLY (donor btl4rdr.hpp == authentic BTL4RDR.HPP byte-for-byte in declarations; member offsets confirmed against the shipped object, offsetPosition@0x2D0..shadowTable@0x3C4): +- MapName::ExtractFromEntity(Entity*, AffineMatrix&, Entity* exclude=NULL, Entity* hotbox=NULL) @004c19fc +- MapName::Draw(GraphicsView*, int hotbox_color) @004c1ab0 +- MapDisplay::methodDescription ("map") + static Make(int, Vector2DOf, Entity*, GaugeRenderer*) — NOT in decomp (ctor had no exported caller); reconstructed from L4GAUGE.CFG:4923 invocation +- MapDisplay::MapDisplay(GaugeRate, ModeMask, L4GaugeRenderer*, unsigned int owner_ID, int graphics_port_number, int left,bottom,right,top, OffsetPosition, int view_width_in_degrees, int bg/static/hotbox color, Scalar maximum_range, Entity*, Scalar* scale, Point3D** position, Quaternion** angle, Scalar* capabilities, Logical allow_rock_and_roll=False) @004c1c24, vtable 0051422c +- ~MapDisplay @004c1ea8; TestInstance @004c1f30; ShowInstance @004c1f40 (prints "ThreatIndicator:"); BecameActive @004c1fc0; Execute @004c1fd0 (4-phase amortized); CalculateBounds @004c2178 (assert path "d:\tesla\bt\bt_l4\BTL4RDR.CPP" line 0x224 CONFIRMS the TU); EraseDisplay @004c2294; DrawDisplay @004c22c4; BuildRadarShadow @004c228c (EMPTY in shipped binary — shadowTable allocated but never populated); DrawViewWedge @004c2484; DrawStatic @004c25b4; DrawMoving @004c2788; DrawNames @004c28c4 (assert line 0x408); helper MapBlipColor @004c2f88 (returns literal 7). + +Donor-only material with NO header impact (file-static adaptation layer): Pip/PipTable facades over L4GaugeImage / L4Warehouse::gaugeImageBin (pip Draw = FUN_0046f0c0, bin Get = FUN_004688ce, keyed by Entity::resourceID +0x1BC, resource type 0x12), EntityPosition/EntityTransform, ResolveOperatorEntity (FUN_00417ab4, renderer+0x40, falls back to application viewpoint), GetPipTable (renderer+0x4C warehousePointer), LookUpNameBitmap (Mission::GetSmallNameBitmap via playerBitmapIndex +0x1E0), GetNameID/IsLabelledEntity (Landmark landmarkID +0x21C — dormant, no shipped content), GetTarget (BTPlayer::objectiveMech +0x284 bridge), FindSubObject (FUN_0041f98c roster walk for "Torso"), GetHorizontalRotation (Torso::currentTwist @+0x1D8 via BTGetTorsoTwistAddr bridge). + +Donor PORT additions to strip or #ifdef when re-hosting to the authentic header/1995 semantics: renderer->RebuildEntityGrid() call in Execute phase 0 (RebuildEntityGrid itself is a port-added GaugeRenderer method — NOT in authentic GAUGREND.HPP); the three "VISIBLE-RADIUS cull" blocks in DrawStatic/DrawMoving/DrawNames; the fallback mech cross-blip branch in DrawMoving (classID 0xBB9, hand-rolled yaw projection); the YawPitchRoll-instead-of-EulerAngles heading fix (YawPitchRoll DOES exist in the authentic engine — restoration\source410\MUNGA\ROTATION.HPP:162 with operator=(const Quaternion&) at :190 — so this fix survives re-hosting); all getenv("BT_MAP_LOG"/"BT_RADAR_LOG") + GetTickCount debug blocks; the two transcription fixes (rotation-only assignment then translation row LAST; worldToView.Invert(view) is a true affine inverse FUN_0040b244) are CORRECTNESS-load-bearing and must stay. + +### btl4grnd (BTL4GaugeRenderer) — donor btl4grnd.cpp/.hpp vs authentic C:\VWE\TeslaRel410\CODE\BT\BT_L4\BTL4GRND.HPP +Authentic header declares exactly 5 members: ctor, dtor, TestInstance() const, NotifyOfNewInterestingEntity(Entity*), NotifyOfBecomingUninterestingEntity(Entity*). No data members (all state inherited: warehousePointer this[0x13]/@0x4C, lampManager this[0x7033], movingEntities @0x94, staticEntities @0xB0). Donor matches all 5: +- ctor @004cbea0 (vtable 0051cebc): lamp manager (BT lamp-name vtable 0051ceb0), L4Warehouse (FUN_00468778), BuildConfigurationFile("gauge\\l4gauge.cfg" @0051ce9e, BTL4MethodDescription @0051c910) +- dtor @004cbf40: Remove(0) all gauges BEFORE deleting warehouse; TestInstance @004cbf90; NotifyOfNewInterestingEntity @004cbfa0 (vtbl slot 9); NotifyOfBecomingUninterestingEntity @004cc028 (slot 10). +DELTA 1: donor adds TearDownForViewpointRestream() (port fix Gitea #12) — NOT in the authentic header; re-hosting onto the exact 1995 header requires dropping it or carrying it as an explicit annotated header delta. +DELTA 2: donor base-ctor call `L4GaugeRenderer(false, NULL, NULL, NULL)` and `new L4LampManager(controls_manager)` are modernized-engine signatures; the shipped binary called both no-arg (FUN_0046f97c(this) / FUN_00448c54(obj)). Authentic re-host = no-arg ctors. + +### btl4galm (BTL4GaugeAlarmManager) — donor btl4galm.cpp/.hpp vs authentic C:\VWE\TeslaRel410\CODE\BT\BT_L4\BTL4GALM.HPP +Signatures match exactly: CreateGaugeAlarmStreamItem(MemoryStream*, const char* alarm_name, const char* alarm_data) -> Logical; ReadGaugeAlarmStreamItem(GaugeAlarm*, Entity*, Subsystem*, Enumeration, MemoryStream*) -> void. CRITICAL provenance: NO distinct override body exists in BTL4OPT.EXE — the only GaugeAlarmManager code is the MUNGA base (trap virtuals CreateGaugeAlarmStreamItem @00448ab8 / ReadGaugeAlarmStreamItem @00448ad4, both assert "not overridden!"; drivers CreateGaugeAlarmStream @00448af0 / BuildAlarmsForModel @00448d00; GaugeAlarm ctor @004489e8, 0x28 bytes, vtable PTR_FUN_004eed7c). No subclass vtable in vtables.tsv, no constructor caller (base ctors @00448ebd/@00448edc/@00448efb externally uncalled), and the linker region reserved for btl4galm.obj (@004cc40c..@004cdac0) holds a PNAME1..8.bgf HUD-model builder instead. The donor bodies (alarm-type names "threshold"/"range"/"state"/"flash") are BEST-EFFORT, low confidence. gaugeAlarmManager is NULL'd in GaugeRenderer ctor (GAUGREND.CPP:2853) and nothing in the BT boot sets it — the alarm subsystem was DORMANT in shipped BT 4.10; StartEntityAlarmImplementation (GAUGREND.CPP:3214) would Check-fail if ever activated. + +## 2. GaugeEntityList / GAUGMAP radar query API (C:\VWE\TeslaRel410\restoration\source410\MUNGA\GAUGMAP.CPP + C:\VWE\BT411\engine\MUNGA\GAUGMAP.h) + +Two collections: +- GaugeEntityList (Node-derived, wraps ChainOf instanceList): GaugeEntityList(); ~(); TestInstance(); Add(Entity*); Remove(Entity*); Clear(); NumberOfItems(); CopyEntities(GaugeEntityList* dest, Derivation* = NULL); CopyEntitiesWithinBounds(dest, Scalar min_x,min_y,min_z,max_x,max_y,max_z, Derivation* = NULL); ChainOf* const GetInstanceList() — the accessor MapDisplay iterates with ChainIteratorOf. +- GaugeEntityArray (the spatial index): 64x64 grid of GaugeEntityList (gaugeMapArraySize=64) over FULL_EXTENT 32767 m; ArrayOffset(n) = int(64*(n/16383.5)+.5)+32, clamped; Add/Remove hash on entity->localOrigin.linearPosition (x,z); tracks min/max extents; CopyEntities / CopyEntitiesWithinBounds(dest, 6 bounds, Derivation*) / GetBounds / PrintStatistics. Cell search range pads by LARGEST_RADIUS=500.0 ("HACK - this is a guess"). + +AUTHENTIC QUIRKS the MapDisplay must (and does) compensate for: +(a) GaugeEntityList::CopyEntitiesWithinBounds's per-entity box test is COMMENTED OUT in the authentic source (GAUGMAP.CPP:323-337) — it accepts EVERY entity in the cell; min_y/max_y parameter names are commented out (/*min_y*/). Real filtering is grid-cell-granular only. +(b) Both grid scan loops are EXCLUSIVE (z L4GaugeRenderer::LoadMissionImplementation (C:\VWE\BT411\engine\MUNGA_L4\L4GREND.cpp:284 — just chains to base) -> GaugeRenderer::LoadMissionImplementation (restoration\source410\MUNGA\GAUGREND.CPP:3256 — EMPTY, a Tell() stub with commented-out list statistics). Mission load populates NOTHING in the radar lists. + +The actual feed is interesting-entity notification: +- BTL4GaugeRenderer::NotifyOfNewInterestingEntity @004cbfa0: SearchList(entity->GetResourceID(), ResourceDescription::GaugeImageStreamResourceType 0x12) — if the entity has NO type-0x12 gauge-image (pip) resource, discard; else IsDerivedFrom(Mover 0x4e4518) -> movingEntities.Add (FUN_004434f4, +0x94), else IsDerivedFrom(Terrain 0x4e6ef8) -> staticEntities.Add (FUN_00443864, +0xB0); ALWAYS chains to L4GaugeRenderer base (FUN_0046fd6c), which reaches GaugeRenderer::NotifyOfNewInterestingEntity (GAUGREND.CPP:3086) — that base forwards to every gauge in newList/activeList/inactiveList (when not suspended) and documents that list maintenance "has been moved up to the game-specific level... all descendents MUST chain back". +- NotifyOfBecomingUninterestingEntity @004cc028 is the exact mirror (Remove FUN_00443510/FUN_00443930). +PORT NOTE: the WinTesla port dropped ExecuteImplementation's InterestingEntity feed, so the port added GaugeRenderer::RebuildEntityGrid (GAUGREND.CPP:3623, decl GAUGREND.HPP:584) which repopulates both lists per frame from HostManager Dynamic Master + Replicant iterators, sorting by Mover derivation; MapDisplay::Execute phase 0 calls it. On a faithful re-host to the authentic notification feed, both the method and the call site go away. + +## 4. Gauge boot sequence + interpreter opcode->class registry + +Boot order (mixed authentic/port evidence): +1. BTL4Application::MakeGaugeRenderer -> `new BTL4GaugeRenderer` (C:\VWE\BT411\game\reconstructed\btl4app.cpp:385; alloc FUN_00402298(0x1c53c) -> ctor FUN_004cbea0). +2. GaugeRenderer base ctor (GAUGREND.CPP:2797): registers movingEntities/staticEntities, creates the GaugeInterpreter (:2848), gaugeAlarmManager = NULL (:2853), warehousePointer = NULL. +3. BTL4GaugeRenderer ctor @004cbea0: new L4LampManager (BT lamp-name vtable 0051ceb0, this[0x7033]) -> new L4Warehouse (this[0x13]) -> BuildConfigurationFile("gauge\\l4gauge.cfg", BTL4MethodDescription). +4. GaugeRenderer::BuildConfigurationFile (GAUGREND.CPP:4288): interpreter->Initialize(file, method_list, warehousePointer), then Configure("Initialization", NULL) — the cfg's `Initialization { }` procedure runs immediately (ports/global gauges), with entity==NULL. +5. GaugeInterpreter::Initialize (GAUGREND.CPP:1086): flattens the CHAINED MethodDescription lists (a name==NULL entry's parameterList[0].data.nextMethodList jumps to the next table — METHOD_DESCRIPTION_CHAIN macro, GAUGREND.HPP:352) into primitiveTable[primitiveCount]; parses the ENTIRE cfg to bytecode: each `