docs: vehicleSubSystems DONE + render-verified
Mark the engineering-screen cluster-panel system complete in the spec + CLAUDE.md §10. Render-verified (BT_DEV_GAUGES_DOCK): 7 authentic panels build + draw -- SENSOR CLUSTER, MYOMERS, ER MED LASER RANGE 500M x3, PPC RANGE 500M x2 -- with bar gauges, recharge dials, and lamps; 0 crashes, combat un-regressed. Records that the aux-screen blocker resolved cleanly via the PoweredSubsystem bridge (the fields were already populated; no MechSubsystem core re-base was needed) and lists the non-blocking follow-ups. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
6fceb58439
commit
f4ad0f4340
@@ -736,24 +736,29 @@ video-object infra (`GetVideoObject`/`LookUpPip`/`GetNameID`, stubbed → cross-
|
||||
`coolantCapacity` shadow + the Searchlight `LightOn` table (duck button already resolves via the 0x37 Mech entry).
|
||||
**◐ `PlayerStatus` DONE** (mission-review scoreboard; lives in the config's `cameraInit` block, not `MechInit`,
|
||||
so it is build/link/mech-cockpit-safe but not runtime-verifiable via a normal mech egg — commit b691863).
|
||||
**◐ `vehicleSubSystems` — FULLY REVERSE-ENGINEERED, reconstruction NOT started (blocked; full spec in
|
||||
`docs/VEHICLE_SUBSYSTEMS.md`).** ⚠ It is NOT a widget — it is the whole **engineering-screen (MFD)
|
||||
subsystem-panel system**: its Make (`FUN_004cbaf0`) is a per-subsystem FACTORY that builds a status *cluster
|
||||
panel* onto one of 12 aux MFD positions, dispatching on subsystem classID → `HeatSinkCluster`(0xBC3)/
|
||||
`MyomerCluster`(0xBC6)/`EnergyWeaponCluster`(0xBC8/0xBD4)/`BallisticWeaponCluster`(0xBCD/0xBD0). The whole
|
||||
`SubsystemCluster` family (base + 5 subclasses, ctors+Executes+dtors) + 4 btl4gau2 sub-gauges (`CoolingLoop`/
|
||||
`PowerSource`/`ScalarBarGauge`/`ConfigMapGauge`) are DECLARED in btl4gau2.hpp but NOT reconstructed (btl4gau2.cpp
|
||||
defines only `SeekVoltageGraph`). Full Ghidra decomp of all ~28 functions captured; the engine primitives it
|
||||
uses (`TwoState`/`NumericDisplayScalar/Integer`/`BackgroundBitmap` + GaugeConnections) already exist + are
|
||||
callable. ⚠⚠ **BLOCKER:** the Make reads `subsystem[0x1dc]` (auxiliary-screen position 1..12; miss ⇒ panel
|
||||
skipped), `[0x1e4]`/`[0x1e0]`/`[0x224]` — **base subsystem fields our reconstruction (`MechSubsystem` ends at
|
||||
0x114) does NOT have or populate**; the `MechSubsystem` resource ctor `FUN_004ac644` writes only through ~0x110,
|
||||
so 0x1dc is set by a deeper base (HeatSink) or a config pass not yet traced. ⇒ **Phase-1 (the cluster family +
|
||||
Make + register) renders NOTHING until Phase-2 populates 0x1dc**, and Phase-2 touches the WORKING combat/heat
|
||||
subsystem layout (regression risk per the §10 systemic rules). Tooling: `reference/ghidra_scripts/DecompVSS.java`
|
||||
(headless address-list decompiler — reusable for any function the assert-anchored exporter skipped; run via the
|
||||
existing `vssproj` Ghidra project). Left checkpointed at full-spec pending a go/no-go on the large core-touching
|
||||
implementation.
|
||||
**✅✅ `vehicleSubSystems` — DONE: the engineering-screen (MFD) subsystem cluster-panel system is
|
||||
reconstructed, registered, and RENDERING (full spec + phasing in `docs/VEHICLE_SUBSYSTEMS.md`).** It is NOT a
|
||||
widget — its Make (`FUN_004cbaf0`) is a per-subsystem FACTORY building a status *cluster panel* onto one of 12
|
||||
aux MFD positions, dispatching on classID → `HeatSinkCluster`(0xBC3)/`MyomerCluster`(0xBC6)/`EnergyWeaponCluster`
|
||||
(0xBC8/0xBD4)/`BallisticWeaponCluster`(0xBCD/0xBD0), placed via the 12-row geometry table @0x51bf34. Reconstructed
|
||||
the whole `SubsystemCluster` family (base + 5 subclasses) + 4 sub-gauges (`AnimatedSubsystemLamp`/`AnimatedSourceLamp`/
|
||||
`ScalarBarGauge`/`ConfigMapGauge`) + 3 connections + the Make + registered in `BTL4MethodDescription[]`; ALSO
|
||||
reconstructed 4 base gauges the clusters build that were declared-but-undefined (`HorizTwoPartBar`/`OneOfSeveralInt`/
|
||||
`OneOfSeveralStates`/`BitMapInverseWipe`) + `StateConnection` + `SeekVoltageGraph`'s virtuals (their `/FORCE`-stubbed
|
||||
ctors segfaulted when a cluster built them). **⭐ THE AUX-SCREEN BLOCKER RESOLVED CLEANLY (no core re-base needed):**
|
||||
the Make reads `subsystem[0x1dc]`/`[0x1e0]`/`[0x1e4]` (aux-screen position/placement/label) which are
|
||||
**PoweredSubsystem** fields (`auxScreenNumber`/`auxScreenPlacement`/`auxScreenLabel`, resource +0x104/+0x108/+0x10C)
|
||||
that **our PoweredSubsystem ctor ALREADY populates** — the raw offsets read 0xCDCDCDCD only because the reconstructed
|
||||
heat-leaf branch isn't byte-exact. Fix = a bridge `BTGetSubsystemAuxScreen()` (powersub.cpp) casting through the real
|
||||
`PoweredSubsystem` type to return the NAMED fields (also the `FUN_0041a1a4`/`0x50f4bc` type filter); the Make +
|
||||
`SubsystemCluster` read via the bridge. **Render-verified (BT_DEV_GAUGES_DOCK): 7 authentic panels build + draw —
|
||||
SENSOR CLUSTER, MYOMERS, ER MED LASER·RANGE 500M ×3, PPC·RANGE 500M ×2 — with bar gauges/recharge dials/lamps, 0
|
||||
crashes, combat un-regressed.** Diagnostics `BT_VSS_LOG` (Make entry + per-subsystem classID/auxScreen dump).
|
||||
Tooling: `reference/ghidra_scripts/DecompVSS.java` (headless address-list decompiler — reusable for any function
|
||||
the assert-anchored exporter skipped; via the `vssproj` Ghidra project). Follow-ups (non-blocking, in the doc):
|
||||
the `SegmentArc270` recharge-dial + `BitMapInverseWipeScalar` eject-wipe children (placeholder ctors, deferred
|
||||
NULL); the remaining raw heat-leaf-branch value-pointer reads (BEST-EFFORT — need the branch re-base or per-field
|
||||
bridges); the separate 960×384 gauge window (docked mode renders; separate-window is existing dev-gauge infra).
|
||||
|
||||
**Alternate route considered:** run original `btrel410.exe` under DOSBox — **blocked**: it needs the
|
||||
VPX/IG board DOSBox can't emulate; bypassing it = the shim work anyway.
|
||||
|
||||
+39
-10
@@ -1,9 +1,13 @@
|
||||
# `vehicleSubSystems` — the engineering-screen subsystem cluster panels (reconstruction plan)
|
||||
|
||||
> Status: **reconstruction UNDERWAY** (from-scratch). This is the largest single gauge item — not a
|
||||
> widget but the whole engineering-screen (MFD) subsystem-panel system. Full decomp captured in the
|
||||
> session scratchpad (`vss_full.txt` / `vss_sub.txt`, Ghidra headless of `BTL4OPT.EXE`); this doc is the
|
||||
> durable spec so the analysis survives across sessions.
|
||||
> Status: **✅ DONE — reconstructed, registered, and RENDERING** (Phase 1 + Phase 2 complete). The whole
|
||||
> engineering-screen (MFD) subsystem-panel system is live: each subsystem builds its authentic cluster
|
||||
> panel on its real aux-screen position. Render-verified (docked dev-gauge composite) — the panels show
|
||||
> **SENSOR CLUSTER** (HeatSinkCluster), **MYOMERS** (MyomerCluster), **ER MED LASER · RANGE 500M** ×3 +
|
||||
> **PPC · RANGE 500M** ×2 (Energy/Ballistic weapon clusters) with bar gauges, recharge dials, lamps, and
|
||||
> the coolant/heat panel. 0 crashes, combat un-regressed. Full decomp captured in the session scratchpad
|
||||
> (`vss_full.txt`/`vss_sub.txt`/`vss_kids.txt`, Ghidra headless of `BTL4OPT.EXE`). Remaining polish noted
|
||||
> at the bottom.
|
||||
|
||||
## What it is
|
||||
The config primitive `vehicleSubSystems(qXXX0.pcc .. qXXX7.pcc)` (26 uses in `L4GAUGE.CFG`, one per
|
||||
@@ -102,9 +106,34 @@ rules). The bytes exist in the allocation (subclass layouts are locked to binary
|
||||
padding between 0x114 and the subclass fields); the task is to CARVE named fields out of that region + wire
|
||||
the resource parse, verifying combat/heat un-regressed. **This is the final, sign-off-gated step.**
|
||||
|
||||
## Phasing
|
||||
- **Phase 1 (SAFE, gated):** reconstruct the cluster family + the 4 sub-gauge classes + connections + the
|
||||
Make dispatcher + register in `BTL4MethodDescription[]`. Gauges only build under `BT_DEV_GAUGES`/pod; with
|
||||
`[0x1dc]` unpopulated the Make hits its skip path, so **combat is untouched**. Buildable + committable.
|
||||
- **Phase 2 (core-layout, sign-off):** extend + populate `MechSubsystem[0x1dc/0x1e0/0x1e4/0x224]` from the
|
||||
resource → the panels render live on mfd1/mfd2/mfd3. Verify combat/heat un-regressed.
|
||||
## Phasing — BOTH DONE
|
||||
- **✅ Phase 1 (DONE):** reconstructed the cluster family (base `SubsystemCluster` + 5 subclasses) + the 4
|
||||
sub-gauge classes (`AnimatedSubsystemLamp`/`AnimatedSourceLamp`/`ScalarBarGauge`/`ConfigMapGauge`) + the 3
|
||||
connections + the Make dispatcher + registered in `BTL4MethodDescription[]`. **Plus** 4 previously
|
||||
declared-but-undefined base gauges the clusters build (`HorizTwoPartBar`, `OneOfSeveralInt`,
|
||||
`OneOfSeveralStates`, `BitMapInverseWipe`/LeakGauge) + `StateConnection` + `SeekVoltageGraph`'s virtuals —
|
||||
their `/FORCE`-stubbed ctors were segfaulting when a cluster built them.
|
||||
- **✅ Phase 2 (DONE — NOT the feared core-layout re-base):** the blocker resolved cleanly. The aux-screen
|
||||
assignment turned out to be `PoweredSubsystem` fields (`auxScreenNumber`/`auxScreenPlacement`/
|
||||
`auxScreenLabel`, resource +0x104/+0x108/+0x10C) that **our ctor already populated** — the raw `sub[0x1dc]`
|
||||
reads were garbage only because the reconstructed heat-leaf branch isn't byte-exact. Fix = a bridge
|
||||
`BTGetSubsystemAuxScreen()` (powersub.cpp) that casts through the real `PoweredSubsystem` type + returns
|
||||
the NAMED fields (and is the `FUN_0041a1a4`/`0x50f4bc` type filter); the Make + `SubsystemCluster` read via
|
||||
the bridge, not raw offsets. **No `MechSubsystem` re-base was needed.** Render-verified: 7 authentic panels
|
||||
build with real aux-screen positions/labels/placements, 0 crashes, combat un-regressed.
|
||||
|
||||
## Remaining polish (non-blocking follow-ups)
|
||||
- Two minor cluster children deferred as marked NULLs: the `SegmentArc270` recharge dial (WeaponCluster) +
|
||||
the `BitMapInverseWipeScalar` eject wipe (BallisticWeaponCluster) — their ctors are placeholders in
|
||||
btl4gaug.hpp (the `SegmentArc270` @004c6244 body + a `BitMapInverseWipeScalar` class need reconstruction).
|
||||
- `SeekVoltageGraph::Execute` + `BitMapInverseWipe::Execute` are minimal-safe (vtable-complete, non-crashing)
|
||||
— the full seek-voltage plot + the reveal-wipe column math are deferred.
|
||||
- Many cluster child value pointers read raw subsystem offsets (0x31c/0x324/0x334/0x364/0x43c/…) still on
|
||||
the non-byte-exact heat-leaf branch — marked BEST-EFFORT; they render but may show wrong/zero data. The
|
||||
authentic fix is the heat-leaf-branch re-base (the same §10 deferred item for the Sensor RadarPercent
|
||||
gauge) or per-field bridges like the aux-screen one.
|
||||
- `ConfigMapGauge`'s control-mapper state loop is guarded off (raw App ModeManager offsets, `DAT_00518eb8`
|
||||
unrecovered).
|
||||
- The **separate** dev-gauge window (960×384) didn't appear in the headless test run (the panels render in
|
||||
`BT_DEV_GAUGES_DOCK=1` docked mode); the separate-window path is existing dev-gauge infra, unrelated to
|
||||
this reconstruction.
|
||||
|
||||
@@ -11,27 +11,14 @@ import java.io.*;
|
||||
|
||||
public class DecompVSS extends GhidraScript {
|
||||
public void run() throws Exception {
|
||||
String outPath = "C:\\Users\\epilectrik\\AppData\\Local\\Temp\\claude\\C--git-bt411\\09bf24bb-f92d-423a-b815-1eb662736f58\\scratchpad\\vss_sub.txt";
|
||||
String outPath = "C:\\Users\\epilectrik\\AppData\\Local\\Temp\\claude\\C--git-bt411\\09bf24bb-f92d-423a-b815-1eb662736f58\\scratchpad\\vss_kids.txt";
|
||||
PrintWriter out = new PrintWriter(new FileWriter(outPath));
|
||||
FunctionManager fm = currentProgram.getFunctionManager();
|
||||
Memory mem = currentProgram.getMemory();
|
||||
DecompInterface dec = new DecompInterface();
|
||||
dec.openProgram(currentProgram);
|
||||
|
||||
long[] targets = {
|
||||
// btl4gau2 sub-gauge ctors that must be reconstructed
|
||||
0x4c70a4L, 0x4c7134L, 0x4c3134L, // CoolingLoop/AnimatedSubsystemLamp + dtor + connection
|
||||
0x4c7160L, 0x4c71f0L, 0x4c31ecL, // PowerSource/AnimatedSourceLamp + dtor + connection
|
||||
0x4c721cL, 0x4c72acL, 0x4c733cL, // ScalarBarGauge/GeneratorVoltage + ctor2 + dtor
|
||||
0x4c6d80L, 0x4c6e54L, 0x4c6ee0L, // ConfigMapGauge + dtor + SetColor
|
||||
0x4c6f34L, // ConfigMapGauge Execute (guess)
|
||||
// SubsystemCluster helpers
|
||||
0x4c89c4L, 0x4c8820L, 0x4c8990L, 0x4c8a28L, 0x4c2ec4L,
|
||||
// Weapon/Ballistic extra vtable slots
|
||||
0x4c92e4L, 0x4c9b24L, 0x4c9b50L, 0x4c8990L,
|
||||
// connections
|
||||
0x4749deL, 0x474855L, 0x4c3288L,
|
||||
};
|
||||
long[] targets = { 0x4c4170L, 0x4c5470L, 0x4c5b7cL, 0x4c5148L };
|
||||
for (long t : targets) {
|
||||
Address a = toAddr(t);
|
||||
Function f = fm.getFunctionContaining(a);
|
||||
|
||||
Reference in New Issue
Block a user