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>
8.6 KiB
I now have everything verified against the raw decomp, the engine headers, the CFG, and the sibling reconstructions. Here is the adversarial verdict.
ADVERSARIAL VERIFY — SectorDisplay: verdict APPROVE WITH CORRECTIONS
The spec is structurally sound and its layout corrections over the current (buggy) btl4gau3.hpp are right. I confirmed sizeof, every ctor offset, the base chain, the Make idiom, the methodDescription row set (against the live CFG), the dtor/BecameActive/Execute structure, and all five systemic-check verdicts. One substantive error (a wrong rounding claim with a fabricated justification) and two confidence downgrades (disassembly-only claims) must be fixed before implementing.
What I verified as CORRECT (keep as-is)
- Base =
GraphicGauge, base ctorFUN_00444818, sizeof0xC4— Make allocsFUN_00402298(0xc4)(part_014.c:2632). ✓ - Every ctor offset vs the raw
param_1[N]writes (part_014.c:2666–2704):gridImage@0x94,cellWidth@0x98=width/14,gridLeft@0x9C=cw*12,gridRight@0xA0=cw*13-1,gridHeight@0xA4,numericColor@0xA8,gridColor@0xAC, both500s @0xB0/@0xB4,numericA@0xBC (x=0),numericB@0xC0 (x=cw*4). ✓ - The header-layout fixes are real bugs in the current header — it omits
@0xA8and@0xB8, mislabels@0x90asenabledand@0xB0/@0xB4asbaseLine/dirty, and wrongly gives the ctor two image strings (the raw has ONE,param_9, used for both grid + font). Spec fixes all of these. ✓ @0x90=subjectEntity* set byLinkToEntity(slot 9), NOTenabled/SetEnable— well-founded:FUN_004ca068is a barethis[0x24]=paramsetter, base slot9 is the emptyLinkToEntity, and Execute gates on+0x90 != 0and reads a position from it. This is the correct reinterpretation of the current header. ✓- methodDescription 5-row set (
typeRate, typeModeMask, typeString, typeColor, typeColor) — matches the Make's DAT_ reads AND the live CFG (content/GAUGE/L4GAUGE.CFG:5146):sectorDisplay(K, ModeAlwaysActive, helv15.pcc, 0, 3)withoffset=(125,579)supplied separately. ✓ - Make/ctor base-chain idiom — identical to the registered sibling
PilotList(btl4gau3.cpp:226–238, 253):ParameterDescription *p = methodDescription.parameterList; … GraphicGauge(rate,mode,renderer,0,port,id). ✓ - dtor (implicit base, release grid, free name, delete numerics), BecameActive (dirty=1, ForceUpdate B-then-A, non-inactivating), Execute grid blit (
MoveToAbsolute(cw*3,0)+SetColor(gridColor@0xAC)+DrawBitMap(0,grid,gridLeft,0,gridRight,gridHeight)) — all match the raw byte-for-byte. ✓ - Systemic checks all correct: Execute (slot16) + non-inactivating BecameActive (slot3) both overridden → no
Gauge::ExecuteFail→abort; the two NumericDisplays are private members drawn directly, not registered children → no child-GuardedExecuterecursion; no shadow/alias/phantom fields;subject-NULL guarded with the viewpoint fallback; single cockpit gauge, not per-mech. ✓ renderer->warehousePointeris reachable in member methods/dtor — confirmed byPlayerStatus::Execute(btl4gau3.cpp:673). Registration line goes before&BTL4ChainToPrevious(btl4grnd.cpp:150). ✓
CHANGES I made (what was wrong)
① CRITICAL — FUN_004dcd94 is ROUND, not truncate. Confirmed at its definition (part_015.c:3769): local_10 = (undefined4)(longlong)ROUND(in_ST0); — round-to-nearest (x87 frndint, half-to-even). The codebase's own map agrees (btl4gau3.cpp:36 "FUN_004dcd94 Round"; CLAUDE.md). The spec asserts the opposite ("truncate toward zero … RC=chop or 0xc, fistp … (long) not round-to-nearest") — that justification is fabricated; there is no or 0xc/fistp in the function. Fix: the two numerics must round, not C-truncate. Use lrintf (default FE_TONEAREST == frndint) or the engine Round, never (int)(x).
② Downgrade the position data-binding to disassembly-sourced / MEDIUM confidence. The pseudocode Execute (FUN_004ca07c, part_014.c:2789–2794) shows only iVar2 = FUN_004dcd94(); iVar3 = FUN_004dcd94(); — the decompiler dropped the x87 operand loads. So the entire mapping (that iVar2 = -Z·0.01, iVar3 = +X·0.01, the fchs, and the 0.01 scale) is the reviewer's disassembly, which was provably wrong on FUN_004dcd94. The subject != 0 gate + the two +500 offsets + drawing two numerics are pseudocode-solid; the which-axis / sign / scale are not. Keep the mapping as the best structural reconstruction but mark it, and re-disassemble @0x4ca089–0x4ca0b9 to lock the axis/sign/scale before claiming fidelity. (The read itself — subject->localOrigin.linearPosition via the engine Entity type — is confirmed as the established radar pattern: btl4rdr.cpp:150 reads entity->localOrigin.linearPosition; ENTITY.h:140 Origin localOrigin; entity+0x100 == that per btl4rdr.cpp:622.)
③ Unify the FUN_00442aec naming to GetIfAlreadyExists (the codebase's established name, btl4gau3.cpp:128–129 "peek, no AddRef"). The spec inconsistently modeled the identical call as bitMapBin.Get (held) in the ctor but GetIfAlreadyExists (peek) in Make/Execute. Use GetIfAlreadyExists in all three; keep the dtor Release (FUN_00442c12, part_014.c:2717) balancing the ctor's held reference and the Make existence-check Release (part_014.c:2640) — that is binary-faithful.
④ Minor: remove the now-unused static DAT_0051a2bc/0300/0344 placeholders (btl4gau3.cpp:69–71) — the rewired Make reads methodDescription.parameterList (same as the PlayerStatus rewire, line 72–73). Drop the redundant (L4Warehouse*) cast in Make (warehousePointer is already typed, per line 128). Confirm NumericDisplay::unsignedFormat == 0 (raw format arg is literal 0); if unsure use (NumericDisplay::Format)0. The offsetX/offsetY names are misleading (both 500; A feeds the Z-numeric, B the X-numeric) — rename sectorBaseA/sectorBaseB or just comment "both = 500".
Corrected Execute (the only body that changes)
// @0x4ca07c -- Execute (slot 16). RAW: iVar2/iVar3 = FUN_004dcd94() = ROUND(ST0)
// (part_015.c:3769); the x87 operand loads were dropped by the decompiler, so the
// -Z / +X axis assignment, the fchs and the 0.01 scale are DISASSEMBLY-sourced
// (@0x4ca089-0x4ca0b9) and MEDIUM-confidence -- re-verify before claiming fidelity.
void SectorDisplay::Execute()
{
Entity *s = subject; // this+0x90
if (s == NULL && application != NULL) // PORT: LinkToEntity broadcast
s = (Entity *)application->GetViewpointEntity(); // may be unwired -> fall back
if (s == NULL)
return; // binary gates purely on subject!=0
const Point3D &pos = s->localOrigin.linearPosition; // engine type (NOT raw +0x100)
int vA = (int)lrintf(-pos.z * 0.01f) + sectorBaseA; // ROUND, not (int) truncate
int vB = (int)lrintf( pos.x * 0.01f) + sectorBaseB;
numericA->Draw(&localView, (Scalar)vA); // FUN_00470430
numericB->Draw(&localView, (Scalar)vB);
if (dirty) { // this+0xB8
dirty = 0;
BitMap *grid = ((L4Warehouse *)renderer->warehousePointer)
->bitMapBin.GetIfAlreadyExists(gridImage); // FUN_00442aec
if (grid != NULL) {
localView.MoveToAbsolute(cellWidth * 3, 0); // vtbl+0x24
localView.SetColor(gridColor); // vtbl+0x18 (this+0xAC)
localView.DrawBitMap(0, grid, gridLeft, 0, gridRight, gridHeight); // vtbl+0x54
}
}
}
Everything else in the spec (identity table, the 0xC4 overflow-lock, the methodDescription literal, the registration line, ctor/dtor/Make/BecameActive/TestInstance bodies) is correct as written — apply only ①–④.
FINAL CONFIDENCE
HIGH on structure, layout, registration, and all systemic-check verdicts (pseudocode-verified). MEDIUM on the numeric data-binding specifics: the value is a rounded ±position·0.01 + 500, but the exact axis→numeric assignment and the sign of Z are disassembly-only and the reviewer's disassembly was demonstrably unreliable (it inverted the rounding mode). Re-disassemble @0x4ca089–0x4ca0b9 to lock the axis/sign/scale; nothing else blocks implementation.
Remaining UNKNOWNS (non-blocking): exact axis/sign/scale of the position read (②); whether GaugeRenderer::LinkToEntity is broadcast in the port (the viewpoint fallback makes it LIVE either way); NumericDisplay::unsignedFormat's enum value (raw = 0).