Files
BT412/scratchpad/wf_5_PrepEngr_verify.md
T
arcattackandClaude Opus 4.8 4a4ec6855c gauge-complete P4e: SectorDisplay reconstructed + registered -> radar SECTOR X/Z read-out LIVE
The "sectorDisplay" cockpit primitive (Secondary overlay, the radar SECTOR X/Z
coordinate read-out) was PROSE-ONLY in btl4gau3 (placeholder Make, no ctor/
methodDescription/registration) -> the config line was parse-SKIPPED and never built.

Reconstructed byte-verified from the disassembly (ctor @4c9e10, Execute @4ca07c,
methodDescription PE-parse): SectorDisplay : GraphicGauge, sizeof 0xC4. Its Execute
reads the linked mech's world position and shows two 100-unit sector numerics:
  numericA = Round(-localOrigin.z * 0.01) + 500
  numericB = Round( localOrigin.x * 0.01) + 500
(rounding = round-to-nearest == FUN_004dcd94, corrected from the reviewer's wrong
"truncate" claim; 0.01 const PE-verified; -Z/+X axis + fchs confirmed from asm).
Overridden slots: LinkToEntity(9) caches the subject, BecameActive(3, non-inactivating),
Execute(16) -> satisfies the container-Execute rule. Layout overflow-locked
(static_assert sizeof<=0xC4). Make/ctor/dtor mirror the registered PilotList sibling;
the config image name is copied (nameCopy) since Execute reads it per-frame.
Registered in BTL4MethodDescription[].

VERIFIED LIVE (BT_SECTOR_LOG): Make port=1 pos=(125,579) image=helv15.pcc gridCached=1;
Execute -Z=960.4 X=361.6 -> sectorA=510 sectorB=504 (Round(9.6)+500=510, Round(3.6)+500=504
-- authentic 100-unit sectors from live mech position). Gauge composite renders full,
no crash. The skip list is now exactly the two remaining widgets (prepEngr x12, messageBoard).

Also: a permanent BT_GAUGE_SKIP_LOG diagnostic (GAUGREND.cpp, gated) that logs each
unregistered gauge primitive the dev-parse skips -- the tool that pinned this down
(earlier "not built" runs were killed before the lazy gauge-renderer init).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 21:25:48 -05:00

8.0 KiB
Raw Blame History

I have now verified every load-bearing claim against the raw decomp, the vtable data, the BackgroundBitmap twin, and the established reconstruction templates. Here is my adversarial-verification verdict.


VERIFY VERDICT: PrepEngrScreen spec — CONFIRMED CORRECT (with 3 minor strengthenings)

The colleague's spec is sound and ready-to-implement. Its three headline corrections to the existing stub are all independently verified. I found no incorrect claim; the changes below are hardening, not fixes.

Core claims — all VERIFIED against the raw

(a) Base class / sizeof / ctor offsets — CORRECT

  • Base = GraphicGaugeBackground, confirmed: PrepEngr's ctor FUN_004c7bf0 calls base ctor FUN_004440d4 (part_014.c:1213), and FUN_004440d4 is the ctor called by BackgroundBitmap and its siblings (part_009.c:1633/1695/1758/1838/1956) — the L4GAUGE GraphicGaugeBackground leaves. 5-arg base ctor (mode, renderer, owner, port, id) matches GraphicGaugeBackground(ModeMask, GaugeRenderer*, unsigned, int, const char*) (GAUGE.h:117). The stub's public Gauge is wrong → change to GraphicGaugeBackground.
  • sizeof 0x90 = FUN_00402298(0x90) (part_014.c:1189). ✓
  • ctor field writes: param_1[0x1b]=param_7(screen)@0x6C, param_1[0x1c]=param_6(mech)@0x70 (part_014.c:1215-1216). Stub has these two swapped (btl4gau2.hpp:318-319) → spec's fix is correct.
  • ctor param order (mode, renderer, owner, port, mech, screen, img0..6, id) matches the Make→ctor call (part_014.c:1191). ✓

(b) methodDescription rows — CORRECT. CFG prepEngr(...) (L4GAUGE.CFG:4595) has exactly 9 args: ModeMFD1Eng1, 1, helv42.pcc, escnd.pcc, edmg.pcc, bteslev.pcc, bteunjm.pcc, espeed.pcc, btesrnge.pcctypeModeMask, typeInteger, 7×typeString. Global-DAT spacing confirms the parameterList mapping: sizeof(ParameterDescription)=0x44 (4 + maxStringLength=0x40, GAUGREND.h:242-262), and DAT_0051936c(screen)=parameterList[1].data, DAT_005193b0(img0)=parameterList[2].data.string, stride 0x44 — exactly the reconstruction's read p[i].data.{integer,string,modeMask}. ✓

(c) Container-Execute rule — CORRECTLY N/A. Execute first appears on Gauge (GAUGE.h:366, vtable slot ~14); a GraphicGaugeBackground vtable has only 13 slots and no Execute. The overridden slot is BecameActive, not Execute — proved three ways:

  1. PrepEngr vtable (vtables.tsv row 0051a040, ptr 0051a06c) = [4c7d14(dtor), 4173b8, 417938, 4c7e48, 444004…]byte-identical shape to BackgroundBitmap (004fb918, ptr 471d7c) = [471d7c(dtor), 4173b8, 417938, 471dec, 444004…].
  2. BackgroundBitmap is explicitly a GraphicGaugeBackground (L4GAUGE.cpp:2443) whose only non-dtor override is BecameActive() (L4GAUGE.cpp:2511) → its single override slot (3) = BecameActive → PrepEngr's slot 3 (4c7e48) = BecameActive.
  3. Slots 1/2 are Node virtuals: FUN_004173b8 = return 0 = Node::ReceiveNodeCommand (node.h:10 inline {return 0;}); FUN_00417938 = ReleaseLinkHandler — so slot 3 is the first GaugeBase virtual = BecameActive (GAUGE.h:52). FUN_004c7e48 is a paint-on-activation (walk roster → draw labels into localView), semantically a BecameActive, not a per-frame Execute. No container/child-chain exists (ctor builds none; dtor FUN_004c7d14 only frees 7 images + implicit localView/base dtor) → the abort-on-unoverridden-Execute hazard does not apply. ✓

(d) shadow/alias/phantom / databinding — CORRECTLY handled. No shadow/alias/phantom fields. Roster read uses the databinding-safe Entity::GetSubsystemCount()/GetSubsystem(i) (ENTITY.h:167-177, reads the reconstructed subsystemArray/subsystemCount) + BTGetSubsystemAuxScreen — the exact VehicleSubSystems::Make pattern (btl4gau2.cpp:1621-1636). NumericDisplay/DrawPortBackground operate on the object's own localView/warehouse. The two raw-offset reads (hs+0x1D4, sub+0x224) are correctly guarded fail-soft and flagged (§6).

(e) every-mech / abort / NULL — no crash risk. mech null-checked; GetSubsystem(i) bounded by i<count (its internal Verify(index<subsystemCount) can't trip); ResolveLink("HeatSink") null-guarded before the +0x1D4 read; the 0x50f4bc filter + auxScreen check gate every deref. Not an every-mech path (gauge is bound to the viewpoint mech). ✓

Bonus confirmations (spec was even more right than it claimed)

  • NumericDisplay usage is EXACT, not just "structural." L4GAUGE.h:22-63 defines class NumericDisplay with NumericDisplay(L4Warehouse*, int x, int y, const char* font, int digits, NumericFormat, int bg, int fg) and void Draw(GraphicsView*, Scalar). The spec's NumericDisplay num(warehouse,0xe9,0x176,font,2,unsignedFormat,0,0xff); num.Draw(&localView,(Scalar)screenNumber) maps 1:1 to the raw FUN_004700bc(…,0xe9,0x176,font,2,0,0,0xff) + FUN_00470430(…) + dtor FUN_0047018c (part_014.c:1310-1313). Draw (not DrawAt) is the correct method name.
  • DrawPortBackground(GraphicsView*, L4Warehouse*, int x, int y, const char*) = FUN_004c2ec4 (btl4gau2.cpp:923) — signature matches the spec's calls; the raw's (x,y,view,warehouse,name) arg order is correctly reordered by the existing helper.

Changes I made (3, all hardening)

  1. Make allocation — switch to plain new PrepEngrScreen(...) (drop operator new(0x90)+placement-new). The reconstructed GraphicsView localView is not byte-exact to the 1995 0x24..0x6B window, so sizeof(PrepEngrScreen) may exceed 0x90 — a fixed operator new(0x90) would then heap-overflow. new PrepEngrScreen(...) allocates the true size and matches BackgroundBitmap::Make (L4GAUGE.cpp:2379); no external code reads this object at raw offsets, so the binary alloc size is not load-bearing. Keep static_assert(sizeof(PrepEngrScreen) <= 0x90) only as a compile-time canary. Optionally add Register_Object(g); after construction for parity with BackgroundBitmap (debug leak-tracking only).

  2. §6 #4 UPGRADED from "unknown" to CONFIRMED FAITHFUL. BTGetSubsystemAuxScreen (powersub.cpp:1333) applies BTIsPoweredSubsystem = FUN_0041a1a4(…,0x50f4bc) — the exact filter the raw uses (part_014.c:1308) — and returns auxScreenNumber via the real PoweredSubsystem type (+0x104 = binary sub+0x1DC). So folding the filter + auxScreen==screenNumber gate into the bridge is byte-faithful; the qualifying-subsystem set is identical.

  3. Acknowledge the dropped erase in the label block. The raw does 3 unresolved localView vtbl calls (+0x18/+0x24/+0x48, args not recovered by Ghidra) before the sub+0x224 label draw (part_014.c:1323-1325) — a cell-erase/setup. The spec drops them and substitutes SetColor(0xff) + DrawPortBackground(label64). Cosmetic only; add to the §6 best-effort list next to #1.

Residual UNKNOWNS (unchanged from spec, all cosmetic, none block registration)

  • sub+0x224 drawn-label ≠ auxScreenLabel (sub+0x1E4, what the bridge returns) — the label bitmap name may differ; needs a subsystem-ctor field-map pass (disas2.py). Guarded/flagged.
  • resolvedHeatSink+0x1D4 (1-digit sink index) is past byte-exact HeatSink (0x1D0) → resolved link must be a larger subclass; guarded raw read, best-effort.
  • DrawStatusCells C-NULL branch vtbl+0x4c(99,10) — unresolved GraphicsView slot; plain fill substituted.

FINAL CONFIDENCE: HIGH

Identity, base class, the BecameActive slot, layout, ctor/dtor/Make/BecameActive bodies, the 9-param methodDescription, and the BTL4MethodDescription[] registration line are all verified against part_014.c, vtables.tsv, L4GAUGE.cpp/h, GAUGE.h, ENTITY.h, and powersub.cpp. Registering &PrepEngrScreen::methodDescription (btl4grnd.cpp, before &BTL4ChainToPrevious) + the corrected header (public GraphicGaugeBackground, void BecameActive(), screenNumber@0x6C/mech@0x70) + the bodies as written (with change #1) will build and stop the 12 prepEngr(...) lines from parse-skipping. The only behavioral caveats are the two flagged cosmetic label reads.