Files
BT411/scratchpad/wf_4_SectorDisplay_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.6 KiB
Raw Blame History

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 ctor FUN_00444818, sizeof 0xC4 — Make allocs FUN_00402298(0xc4) (part_014.c:2632). ✓
  • Every ctor offset vs the raw param_1[N] writes (part_014.c:26662704): gridImage@0x94, cellWidth@0x98=width/14, gridLeft@0x9C=cw*12, gridRight@0xA0=cw*13-1, gridHeight@0xA4, numericColor@0xA8, gridColor@0xAC, both 500s @0xB0/@0xB4, numericA@0xBC (x=0), numericB@0xC0 (x=cw*4). ✓
  • The header-layout fixes are real bugs in the current header — it omits @0xA8 and @0xB8, mislabels @0x90 as enabled and @0xB0/@0xB4 as baseLine/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 = subject Entity* set by LinkToEntity (slot 9), NOT enabled/SetEnable — well-founded: FUN_004ca068 is a bare this[0x24]=param setter, base slot9 is the empty LinkToEntity, and Execute gates on +0x90 != 0 and 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) with offset=(125,579) supplied separately. ✓
  • Make/ctor base-chain idiom — identical to the registered sibling PilotList (btl4gau3.cpp:226238, 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::Execute Failabort; the two NumericDisplays are private members drawn directly, not registered children → no child-GuardedExecute recursion; no shadow/alias/phantom fields; subject-NULL guarded with the viewpoint fallback; single cockpit gauge, not per-mech. ✓
  • renderer->warehousePointer is reachable in member methods/dtor — confirmed by PlayerStatus::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:27892794) 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 @0x4ca0890x4ca0b9 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:128129 "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:6971) — the rewired Make reads methodDescription.parameterList (same as the PlayerStatus rewire, line 7273). 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 @0x4ca0890x4ca0b9 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).