I now have the complete picture, verified against disassembly, the PE method-description table, the CFG, and the sibling reconstructions. Here is the full implementation spec. --- # SectorDisplay — Reconstruction Spec Cockpit **radar SECTOR X/Z read-out** (the Secondary-overlay grid + two 3-digit coordinate numerics). Config keyword `sectorDisplay`, currently **prose-only** in `game/reconstructed/btl4gau3.cpp` (a placeholder `Make`, no ctor/`methodDescription`/registration) → the CFG line is **parse-skipped**. Everything below is decoded from `BTL4OPT.EXE` (Execute/ctor/vtable disassembled; methodDescription PE-parsed) and cross-checked against `part_014.c` and the already-reconstructed siblings. --- ## 1. Identity | Field | Value | Evidence | |---|---|---| | Class | `SectorDisplay` | id string `s_SectorDisplay_0051bb34` `part_014.c:2635`; `"sectorDisplay"` keyword `@0x51bc82` | | Base | `GraphicGauge` (→`GraphicGaugeBackground`→`GaugeBase`); base ctor `FUN_00444818`, base vtable `@0x4ee088` | ctor `@0x4c9e38` calls `0x444818`; `btl4gau3.hpp:97` | | Config keyword | `sectorDisplay` (1 use, `L4GAUGE.CFG:5146`) | methodDescription name ptr `@0x51a2b0` → `@0x51bc82` | | **Make** | `@0x4c9d44` (returns `Logical`; alloc `operator new(0xc4)`) | `part_014.c:2625`; disasm | | ctor | `@0x4c9e10` (sets vtable `0x51beec`, sizeof `0xc4`) | `part_014.c:2654`; disasm | | dtor | `@0x4c9f94` (slot 0) | `part_014.c:2712` | | BecameActive | `@0x4ca038` (slot 3, +0x0c) | vtdump `0x51beec` | | **LinkToEntity(Entity\*)** | `@0x4ca068` (slot 9, +0x24) — *the subject setter* | vtdump; base slot9 `0x444088` = empty `LinkToEntity` (`GAUGE.h:62`) | | Execute | `@0x4ca07c` (slot 16, +0x40) | vtdump | | TestInstance | `@0x4ca020` → `FUN_004448ac`→`FUN_004443a4` (base test); `@0x4ca030` = empty no-op (`TestClass`/dead) | `part_014.c:2736,2747` | | **methodDescription** | `@0x51a2b0` → keyword `@0x51bc82`, Make `0x4c9d44`, 5 param rows | PE dump | | vtable | `PTR_FUN_0051beec` (17 slots; overrides 0/3/9/16) | vtdump | | sizeof | **0xC4** (== Make alloc `FUN_00402298(0xc4)`, `@0x4c9d54`) | disasm | | GUID / RTTI | none | vtable has no RTTI slot | **Overridden vtable slots** (vs base `0x4ee088`): slot0 dtor `0x4c9f94`; slot3 `BecameActive` `0x4ca038`; **slot9 `LinkToEntity` `0x4ca068`** (base `0x444088` is an empty `ret`); slot16 `Execute` `0x4ca07c`. All others inherited. --- ## 2. What it displays & the data binding — **DATA-LIVE** Two `NumericDisplay`s show the linked mech's **world sector coordinates**, plus a one-shot grid-cell background blit. The subject is an **`Entity*`** cached at `+0x90` by the overridden **`LinkToEntity(Entity*)`** (slot 9). The engine `GaugeRenderer::LinkToEntity` (`GAUGREND.cpp:3008`, == binary broadcast `FUN_00447b70` `part_006.c:5295` which calls slot9 on all three gauge lists) pushes the viewpoint entity to every gauge each frame. Execute reads the subject's position (disasm `@0x4ca096`–`@0x4ca0b9`): ``` numericA value = (int)(-subject->localOrigin.linearPosition.z * 0.01f) + 500 // -Z/100 + 500 numericB value = (int)( subject->localOrigin.linearPosition.x * 0.01f) + 500 // X/100 + 500 ``` - Scale const `@0x4ca194` is the **80-bit extended `0.01`** (= ÷100 → 100-unit sectors); decoded exact. - `-Z` uses `fchs` `@0x4ca09c`; both truncate toward zero (`FUN_004dcd94` sets FPU RC=chop `or 0xc`, `fistp` — it is `(long)`, **not** round-to-nearest). - `entity+0x100` == `Entity::localOrigin.linearPosition` is **already established** by the reconstructed radar (`btl4rdr.cpp:150,622` `FUN_00408440(this+0x310, owner+0x100)`); `.x@+0x100, .z@+0x108`. `Origin localOrigin` is a base-`Entity` member (`ENTITY.h:140`, `ORIGIN.h:15`), read via the **engine type** (compiled-consistent) — **not** a raw offset, so no databinding-trap garbage. - Heading is **not** read (the task's "heading" hint is inaccurate — only X/Z position feed the two numerics). **LIVE now**, with one caveat: the radar notes the renderer's linked-entity socket "may be unwired on WinTesla" (`btl4rdr.cpp:1029`). Mitigation (faithful to intent, same as `ResolveOperatorEntity`/`HeadingPointer::Execute`): when `subject==NULL`, fall back to `application->GetViewpointEntity()`. The grid background blits once (dirty-gated) from cell 12 of the `helv15.pcc` strip; if that image isn't cached the blit is skipped and Make warns `"SectorDisplay: Missing image …"` (`@0x4c9e91` peek). --- ## 3. Class layout (byte-exact) + locks ⚠ The **current `btl4gau3.hpp` layout is wrong** (mislabels `+0x90` as `enabled`, omits `+0xA8` and the `+0xB8` dirty flag, mislabels the two `500` offsets as `baseLine/dirty`). Corrected from ctor/Execute disasm: ```cpp class SectorDisplay : public GraphicGauge { public: static MethodDescription methodDescription; // register "sectorDisplay" static Logical Make(int, Vector2DOf, Entity *, GaugeRenderer *); // @0x4c9d44 SectorDisplay(GaugeRate, ModeMask, L4GaugeRenderer *, int graphics_port_number, int x, int y, const char *image, int color, int ok_color, const char *identification_string); // @0x4c9e10 (10 args; owner folded to 0) ~SectorDisplay(); // @0x4c9f94 Logical TestInstance() const; // @0x4ca020 void LinkToEntity(Entity *entity); // @0x4ca068 (slot 9 override) void BecameActive(); // @0x4ca038 (slot 3) void Execute(); // @0x4ca07c (slot 16) protected: Entity *subject; // @0x90 this[0x24] LinkToEntity target (linked mech); ctor=0 char *gridImage; // @0x94 this[0x25] interned image name (helv15.pcc) int cellWidth; // @0x98 this[0x26] imageWidth / 14 int gridLeft; // @0x9C this[0x27] cellWidth*12 int gridRight; // @0xA0 this[0x28] cellWidth*13 - 1 int gridHeight; // @0xA4 this[0x29] imageHeight int numericColor; // @0xA8 this[0x2A] param_10 (numeric color) int gridColor; // @0xAC this[0x2B] param_11 (grid blit color + numeric okColor) int offsetX; // @0xB0 this[0x2C] = 500 (added to numericA) int offsetY; // @0xB4 this[0x2D] = 500 (added to numericB) int dirty; // @0xB8 this[0x2E] redraw flag (BecameActive=1, Execute clears) NumericDisplay *numericA; // @0xBC this[0x2F] -Z sector readout NumericDisplay *numericB; // @0xC0 this[0x30] X sector readout // sizeof == 0xC4 }; ``` **Locks** — use the **overflow lock only** (matching the sibling widgets: the 2007 `GraphicGauge` base is not byte-identical to the 1995 base, so `subject@0x90`/`sizeof==0xC4` byte-exact asserts would fail; every read here is via named members / the engine `Entity` type, so byte-exactness is not required): ```cpp // namespace scope (sizeof needs no friend); the class reads NO raw this-offsets, so this // overflow lock against the Make alloc is the load-bearing guarantee (like Sensor/heat-leaf): static_assert(sizeof(SectorDisplay) <= 0xC4, "SectorDisplay must fit its 0xC4 Make alloc"); ``` (If a future reader wants the offset asserts anyway, they require a `friend struct SectorDisplayLayoutCheck` and will only pass if the engine `GraphicGauge` happens to end at 0x90 — do **not** gate the build on that.) --- ## 4. Full method bodies ```cpp //======================= btl4gau3.cpp (replace the prose-only SectorDisplay block) ======================= // @0x51a2b0 -- registered so the interpreter builds "sectorDisplay(K,ModeAlwaysActive,helv15.pcc,0,3)". // CFG shape (L4GAUGE.CFG:5146, port=overlay, offset=(125,579)): // sectorDisplay( K, ModeAlwaysActive, helv15.pcc, 0, 3 ) // | | | | +-- p[4] color=3 -> gridColor / numeric okColor // | | | +----- p[3] color=0 -> numericColor // | | +----------------- p[2] string -> grid/font image // | +----------------------------------- p[1] modeMask -> ModeAlwaysActive // +-------------------------------------- p[0] rate -> 'K' // (position (125,579) and the overlay port index arrive via Make's args, not config tokens.) MethodDescription SectorDisplay::methodDescription = { "sectorDisplay", SectorDisplay::Make, { { ParameterDescription::typeRate, NULL }, // p[0] rate [binary row0 type=1 @0x51a2b8] { ParameterDescription::typeModeMask, NULL }, // p[1] modeMask [row1 type=2 @0x51a2fc] { ParameterDescription::typeString, NULL }, // p[2] image [row2 type=9 @0x51a340] { ParameterDescription::typeColor, NULL }, // p[3] color [row3 type=4 @0x51a384] { ParameterDescription::typeColor, NULL }, // p[4] okColor [row4 type=4 @0x51a3c8] PARAMETER_DESCRIPTION_END // [row5 type=0 @0x51a40c] } }; // @0x4c9d44 -- Make. Alloc 0xc4 + construct; then verify the grid image exists // (warn "SectorDisplay: Missing image "). Returns True iff the image is cached // (binary-faithful: the object self-registers via the base ctor regardless of the return). Logical SectorDisplay::Make( int display_port_index, Vector2DOf position, Entity * /*entity -- unused*/, GaugeRenderer *gauge_renderer) { ParameterDescription *p = methodDescription.parameterList; SectorDisplay *gauge = (SectorDisplay *)operator new(0xc4); // FUN_00402298(0xc4) if (gauge != NULL) new (gauge) SectorDisplay( p[0].data.rate, p[1].data.modeMask, (L4GaugeRenderer *)gauge_renderer, display_port_index, // graphics_port_number (FIX: was 0) position.x, position.y, // SetOrigin (FIX: was 0,0) p[2].data.string, // grid/font image (helv15.pcc) p[3].data.color, // numericColor p[4].data.color, // gridColor / numeric okColor "SectorDisplay"); BitMap *grid = ((L4Warehouse *)gauge_renderer->warehousePointer) // renderer+0x4c ->bitMapBin.GetIfAlreadyExists(p[2].data.string); // FUN_00442aec (peek) if (grid == NULL) DebugStream << "SectorDisplay: Missing image " << p[2].data.string << "\n"; // FUN_004dbb24 x3 return (grid != NULL); } // @0x4c9e10 -- ctor (vtable 0x51beec). GraphicGauge base (owner=0); intern the image; // cellWidth=imgW/14, gridLeft=cellWidth*12, gridRight=cellWidth*13-1, gridHeight=imgH; // SetOrigin(x,y); build the two 3-digit NumericDisplays (unsignedFormat). SectorDisplay::SectorDisplay( GaugeRate rate, ModeMask mode_mask, L4GaugeRenderer *renderer_in, int graphics_port_number, int x, int y, const char *image, int color, int ok_color, const char *identification_string) : GraphicGauge(rate, mode_mask, renderer_in, 0, // FUN_00444818, owner=0 graphics_port_number, identification_string) { gridImage = InternName(image); // FUN_004700ac (dtor FreeName's it) numericColor = color; // this[0x2A] (param_10) gridColor = ok_color; // this[0x2B] (param_11) subject = NULL; // this[0x24] offsetY = 500; // this[0x2D] (@0x4c9e6d 0x1f4) offsetX = 500; // this[0x2C] (@0x4c9e77 0x1f4) L4Warehouse *wh = (L4Warehouse *)renderer_in->warehousePointer; // renderer+0x4c BitMap *img = wh->bitMapBin.Get(gridImage); // FUN_00442aec (held; dtor Releases) if (img == NULL) { cellWidth = gridLeft = gridRight = gridHeight = 0; } else { cellWidth = img->Data.Size.x / 14; // img+0xc = width (@0x4c9ec1) gridLeft = cellWidth * 12; // @0x4c9ed4 (shl 2, *3) gridRight = cellWidth * 12 + cellWidth - 1; // @0x4c9ee6 gridHeight = img->Data.Size.y; // img+0x10 = height (@0x4c9ef5) } localView.SetOrigin(x, y); // this+0x48 vtbl+0x10 (@0x4c9f0d) // NumericDisplay(warehouse, x, y, font, fieldWidth=3, format=unsignedFormat(0), color, okColor) numericA = new NumericDisplay(wh, 0, 0, image, 3, NumericDisplay::unsignedFormat, color, ok_color); // this[0x2F] numericB = new NumericDisplay(wh, cellWidth * 4, 0, image, 3, // @0x4c9f6f shl 2 NumericDisplay::unsignedFormat, color, ok_color); // this[0x30] dirty = 1; // binary leaves this[0x2E] uninit (BecameActive sets it before Execute); init for safety } // @0x4c9f94 -- dtor. Release grid, free name, delete both numerics. The base // GraphicGauge::~GraphicGauge (FUN_00444870) runs IMPLICITLY -- do NOT call it. SectorDisplay::~SectorDisplay() { ((L4Warehouse *)renderer->warehousePointer)->bitMapBin.Release(gridImage); // FUN_00442c12 FreeName(gridImage); gridImage = NULL; // FUN_004022e8 delete numericA; numericA = NULL; // FUN_0047018c delete numericB; numericB = NULL; } // @0x4ca020 -- TestInstance: out-of-line forward to the base (so it isn't /FORCE-stubbed). Logical SectorDisplay::TestInstance() const { return GraphicGauge::TestInstance(); } // @0x4ca068 -- LinkToEntity (slot 9): cache the renderer's linked entity as the subject. void SectorDisplay::LinkToEntity(Entity *entity) { subject = entity; } // @0x4ca038 -- BecameActive (slot 3): mark dirty + reset both numerics. NON-inactivating // (does NOT call the base) -> satisfies the container-Execute rule. void SectorDisplay::BecameActive() { dirty = 1; // this[0x2E] numericB->ForceUpdate(); // FUN_004703f4 (order B then A, @0x4ca03e/0x4ca04f) numericA->ForceUpdate(); } // @0x4ca07c -- Execute (slot 16): draw the two sector numerics from the linked mech's // world X/Z; on the first active frame blit the grid-cell background. void SectorDisplay::Execute() { Entity *s = subject; // PORT accommodation (marked): the LinkToEntity broadcast may be unwired on WinTesla -> // fall back to the viewpoint mech (same pattern as btl4rdr ResolveOperatorEntity). // The binary gates purely on `subject != 0` (@0x4ca08e). if (s == NULL && application != NULL) s = (Entity *)application->GetViewpointEntity(); if (s == NULL) return; const Point3D &pos = s->localOrigin.linearPosition; // s+0x100 (x@+0x100, z@+0x108) int vA = (int)(-pos.z * 0.01f) + offsetX; // @0x4ca096 ftol(chop) int vB = (int)( pos.x * 0.01f) + offsetY; // @0x4ca0ad numericA->Draw(&localView, (Scalar)vA); // FUN_00470430 numericB->Draw(&localView, (Scalar)vB); if (dirty) { // @0x4ca10f dirty = 0; BitMap *grid = ((L4Warehouse *)renderer->warehousePointer) ->bitMapBin.GetIfAlreadyExists(gridImage); // FUN_00442aec if (grid != NULL) { localView.MoveToAbsolute(cellWidth * 3, 0); // vtbl+0x24 @0x4ca152 localView.SetColor(gridColor); // vtbl+0x18 @0x4ca163 localView.DrawBitMap(0 /*rotation*/, grid, // vtbl+0x54 @0x4ca187 gridLeft, 0, gridRight, gridHeight); // source rect } } } ``` **Registration** — `game/reconstructed/btl4grnd.cpp`, in `BTL4MethodDescription[]` immediately before `&BTL4ChainToPrevious` (`btl4grnd.cpp:149`): ```cpp &SectorDisplay::methodDescription, // "sectorDisplay" -- radar SECTOR X/Z read-out (Secondary overlay) ``` `SectorDisplay` is already reachable there (`btl4grnd.cpp:71` includes `btl4gau3.hpp`). --- ## 5. Systemic-check verdicts - **Container-Execute override:** ✅ satisfied. `Execute` (slot 16) and `BecameActive` (slot 3, non-inactivating) are both overridden, so the base `Gauge::Execute` `Fail`→`abort()` is never reached. The two `NumericDisplay`s are **private sub-objects drawn directly** (not registered gauges), so there's no child-`Execute`/`GuardedExecute` recursion. - **Shadow fields:** none vs the engine base — `subject@0x90` is the first subclass field (base `GraphicGauge` ends before 0x90). The failures found were **internal layout bugs in the current header** (`enabled`↔`subject`, missing `+0xA8`/`+0xB8`, `baseLine/dirty`↔`offsetX/offsetY`), fixed in §3. - **Alias / phantom:** none. `numericColor@0xA8` is a real stored field (consumed only by the child ctors, never re-read — faithful, not phantom); `offsetX/offsetY` are two independent `500`s; all members lie within `sizeof 0xC4`. - **Databinding NULL risks:** all guarded — `subject==NULL` early-returns (+viewpoint fallback); missing grid image → `GetIfAlreadyExists` NULL → blit skipped + Make warns; `renderer->warehousePointer` always set by the base. Add a `numericA/numericB != NULL` guard before `Draw` if you keep the binary's `new`-can-fail branch (`@0x4c9f20`). - **Every-mech crash risk:** ✅ none. This is a **single cockpit gauge** built once for the local pod; it reads the **linked/viewpoint** entity's `localOrigin.linearPosition` through the **engine `Entity` type** (compiled-consistent), never a raw `*(T*)(entity+0xNN)`. It does not iterate mechs and is not per-mech. --- ## 6. Confidence & unknowns **Confidence: HIGH.** Full function set disassembled (Execute/ctor/dtor/BecameActive/LinkToEntity/vtable), `methodDescription` PE-parsed exact (keyword + 5 typed rows), the `0.01`/`÷100` const decoded, the CFG shape confirmed, and the position binding (`localOrigin.linearPosition`) is the exact mapping the reconstructed radar already uses. **Unknowns / follow-ups (none blocking):** 1. Whether `GaugeRenderer::LinkToEntity` is broadcast with a real viewpoint in the port. The `application->GetViewpointEntity()` fallback makes the gauge LIVE either way; if you confirm the broadcast fires, the fallback is inert. 2. `InternName`/`FreeName` (`FUN_004700ac`/`FUN_004022e8`) exact engine symbols — if `NumericDisplay`'s ctor copies the font name internally (siblings pass `p[2].data.string` uncopied), you may store `p[2].data.string` directly and drop the intern/free pair. Cosmetic. 3. `bitMapBin.Get`(ctor, held) vs `GetIfAlreadyExists`(peek) ref-counting — both compile; the ctor `Get` is balanced by the dtor `Release` (binary-faithful). If `Get`/`Release` aren't symmetric on the WinTesla `L4Warehouse`, use `GetIfAlreadyExists` in the ctor and drop the dtor `Release`. 4. `FUN_004ca030` (empty `ret`) is an unreferenced `TestClass`/dead stub — not needed for function. 5. Visual semantics of the blitted cell-12 sub-rect of `helv15.pcc` (`gridLeft=cellWidth*12`) are replicated exactly but not eyeballed; verify against pod footage when the Secondary overlay is composited (`BT_DEV_GAUGES`).