vehicleSubSystems: full reverse-engineering + reconstruction spec (blocked)
Reverse-engineered the vehicleSubSystems config primitive end to end (Ghidra headless, all ~28 functions). It is NOT a widget: it is the engineering-screen (MFD) subsystem-panel system. Its Make (FUN_004cbaf0) is a per-subsystem factory that builds a SubsystemCluster-family status panel onto one of 12 auxiliary MFD positions, dispatching on subsystem classID (HeatSink/Myomer/Energy/Ballistic clusters). The whole cluster family (base + 5 subclasses) + 4 btl4gau2 sub-gauges (CoolingLoop/PowerSource/ScalarBarGauge/ConfigMapGauge) are declared in btl4gau2.hpp but not reconstructed. BLOCKER: the Make reads base subsystem fields subsystem[0x1dc] (aux-screen position), [0x1e4]/[0x1e0]/[0x224] that our MechSubsystem reconstruction (ends 0x114) does not have or populate -- so the panels render nothing until the core subsystem layout is extended + populated from the resource parse, which touches the working combat/heat subsystem code (regression risk). Checkpointed at full spec pending go/no-go on the large core-touching implementation. - docs/VEHICLE_SUBSYSTEMS.md: complete reconstruction spec (dispatch table, geometry table, class family map, sub-gauge inventory, engine-primitive reuse, the blocker, the Phase-1/Phase-2 plan). - reference/ghidra_scripts/DecompVSS.java: headless address-list decompiler (reusable for any function the assert-anchored exporter skipped). - CLAUDE.md: record the finding in the gauge-widget notes. 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
099871ef4c
commit
a5fa9f1c79
@@ -0,0 +1,110 @@
|
||||
# `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.
|
||||
|
||||
## What it is
|
||||
The config primitive `vehicleSubSystems(qXXX0.pcc .. qXXX7.pcc)` (26 uses in `L4GAUGE.CFG`, one per
|
||||
vehicle init block after `MechInit;`) registers a **Make dispatcher** (`FUN_004cbaf0`,
|
||||
methodDescription @`0x51c080`, 8 `typeString` params = the 8 strip bitmaps). At cockpit-build time the
|
||||
Make walks the mech's subsystem roster (`entity+0x124` count / `+0x128` array, **skipping slot 0** = the
|
||||
controls mapper) and, for each subsystem, builds a **status cluster panel** onto one of 12 auxiliary MFD
|
||||
positions, dispatching on the subsystem's `classID` (`sub+4`):
|
||||
|
||||
| classID | subsystem | child cluster | ctor | new size | id string |
|
||||
|---|---|---|---|---|---|
|
||||
| 0xBC3 | Sensor | `HeatSinkCluster` (looks up the `"HUD"` subsystem via `FindSubsystem`, `FUN_0041f98c`) | `FUN_004c8a6c` | 0xf0 | "SensorCluster" |
|
||||
| 0xBC6 | Myomers | `MyomerCluster` | `FUN_004c8df4` | 0xd8 | "MyomerCluster" |
|
||||
| 0xBC8 / 0xBD4 | Emitter / PPC | `EnergyWeaponCluster` | `FUN_004c93b0` | 0xf0 | "EnergyWeaponCluster" |
|
||||
| 0xBCD / 0xBD0 | Projectile / Missile | `BallisticWeaponCluster` | `FUN_004c9558` | 0x11c | "ProjWeaponCluster" |
|
||||
| 0xBCE | Gauss | — (error "Gauss rifle not yet supported") | — | — | — |
|
||||
| all others | | skip | | | |
|
||||
|
||||
Type filter before dispatch: `FUN_0041a1a4(**(sub+0xc), "")` (an IsA/registry check) must pass.
|
||||
|
||||
## The per-group geometry table @`0x51bf34` (12 groups × 7 dwords, keyed by `sub[0x1dc]-1`)
|
||||
Each group = `{ X, Y, planeMask, subBit, mfdPortName, engPortName, planeIndex }`:
|
||||
```
|
||||
grp X Y planeMask subBit mfdPort engPort idx
|
||||
1 0 240 0x1 0x2 mfd1 eng1 0
|
||||
2 322 240 0x1 0x4 mfd1 eng1 0
|
||||
3 0 28 0x1 0x8 mfd1 eng1 0
|
||||
4 322 28 0x1 0x10 mfd1 eng1 1
|
||||
5 0 240 0x20 0x40 mfd2 eng2 1
|
||||
6 322 240 0x20 0x80 mfd2 eng2 1
|
||||
7 0 28 0x20 0x100 mfd2 eng2 1
|
||||
8 322 28 0x20 0x200 mfd2 eng2 2
|
||||
9 0 240 0x400 0x800 mfd3 eng3 2
|
||||
10 322 240 0x400 0x1000 mfd3 eng3 2
|
||||
11 0 28 0x400 0x2000 mfd3 eng3 2
|
||||
12 322 28 0x400 0x4000 mfd3 eng3 (self)
|
||||
```
|
||||
`mfdPort` → `renderer->GetGraphicsPort(name)` (the visible MFD surface); `engPort` (string) is the
|
||||
engineering-screen render port name passed to the cluster sub-gauges.
|
||||
|
||||
## Class family (all btl4gau2, all DECLARED in btl4gau2.hpp, NONE reconstructed pre-this-effort
|
||||
except `SeekVoltageGraph`)
|
||||
- **`SubsystemCluster`** base @`0x4c8140` (vt `0x51a020`) : `GraphicGauge` — builds ~11 sub-gauges
|
||||
(HorizTwoPartBar temp bar, CoolingLoop ×2, PowerSource ×2, BackgroundBitmap, OneOfSeveralInt modeLamp,
|
||||
GeneratorVoltage/ScalarBarGauge, OneOfSeveralStates stateLamp, VertTwoPartBar ×2, LeakGauge). dtor
|
||||
`0x4c87dc`, Execute `0x4c88e4`, draw-bg helper `0x4c89c4`, BecameActive-draw `0x4c8820`, TestInstance
|
||||
`0x4c8990`, ShowInstance `0x4c8a28`. Reads attributes InputVoltage/HeatSink/CurrentTemperature/
|
||||
DegradationTemperature/FailureTemperature/CoolantMassLeakRate via `FUN_0041bfc0` (=`GetAttributeIndex`
|
||||
by name, returns `owner + idx - 1` or 0).
|
||||
- **`HeatSinkCluster`** @`0x4c8a6c` (vt `0x519fd4`) : SubsystemCluster — adds 4 fail TwoStates
|
||||
(btehfail/btepfail/btesfail) + a NumericDisplayScalar. Execute `0x4c8db0` scales `this[0x35]` by
|
||||
`_DAT_004c8df0` (=100.0). dtor `0x4c8d18`.
|
||||
- **`MyomerCluster`** @`0x4c8df4` (vt `0x519f88`) : SubsystemCluster — adds a SeekVoltageGraph
|
||||
("EngrGraph", edestryd.pcc) + a seek-step OneOfSeveralInt (bteseek.pcc, `subsys+0x800`). Execute =
|
||||
base. dtor `0x4c8f54`.
|
||||
- **`WeaponCluster`** @`0x4c8fc4` (vt `0x519f38`) : SubsystemCluster — adds SegmentArc270 recharge dial
|
||||
(reads "PercentDone") + ConfigMapGauge. Make-time warns "WeaponCluster missing <name>". Execute
|
||||
`0x4c9290` toggles a warn lamp when `this[0x36] > _DAT_004c92e0`. DrawWarningLamp `0x4c932c`,
|
||||
BecameActive `0x4c9258`, dtor `0x4c91d4`, slot17 `0x4c92e4`.
|
||||
- **`EnergyWeaponCluster`** @`0x4c93b0` (vt `0x519ee8`) : WeaponCluster — adds SeekVoltageGraph
|
||||
(OutputVoltage) + seek-step OneOfSeveralInt (`subsys+0x3f0`). dtor `0x4c94dc`.
|
||||
- **`BallisticWeaponCluster`** @`0x4c9558` (vt `0x519e98`) : WeaponCluster — adds 2× NumericDisplayInteger
|
||||
(ammo, `link+0x180`), jam/fire TwoStates (btejam/btefire), NumericDisplayScalarTwoState, eject
|
||||
BitMapInverseWipeScalar (bteejtm, `subsys+0x3f8`), a GraphicsViewRecord erase tracker. Execute
|
||||
`0x4c9a38` tracks weaponAlarm==5 (Jammed, `subsys+0x364`) + the reload clock. dtor `0x4c9940`,
|
||||
BecameActive `0x4c99cc`, TestInstance `0x4c9adc`, slots `0x4c9b24`/`0x4c9b50`.
|
||||
|
||||
## Sub-gauge classes to reconstruct (btl4gau2, declared not defined)
|
||||
- **`AnimatedSubsystemLamp`** (CoolingLoop) @`0x4c70a4` (vt `0x51a174`) : OneOfSeveral + connection
|
||||
`FUN_004c3134` (vt `0x518ea8`, subsystem-state driven) at `+0x2b`. dtor `0x4c7134`.
|
||||
- **`AnimatedSourceLamp`** (PowerSource) @`0x4c7160` (vt `0x51a130`) : OneOfSeveral + connection
|
||||
`FUN_004c31ec` (vt `0x518e9c`) at `+0x2b`. dtor `0x4c71f0`.
|
||||
- **`ScalarBarGauge`** (GeneratorVoltage) @`0x4c721c` (vt `0x51a0e4`) : engine bar `FUN_00472ef0`
|
||||
(VertNormalSlider-ish) + connection `FUN_004c3288` (vt `0x518e90`) at `+0x2d`. 2nd ctor `0x4c72ac`
|
||||
(uses the engine `FUN_00474855` scalar-attr connection). dtor `0x4c733c`.
|
||||
- **`ConfigMapGauge`** @`0x4c6d80` (vt `0x51a1b8`) : GraphicGauge — 5 pcc names (btjoy/cm_off/cm_other/
|
||||
cm_only/cm_both) interned into the warehouse; Execute `0x4c6f1c` reads the control-mapper state from
|
||||
`app->GetModeManager()` (`DAT_004efc94+0x3c`, group `0x1c0 + i*0x20`) and blits the matching frame.
|
||||
SetColor `0x4c6ee0` (writes `this+0x94`). dtor `0x4c6e54`.
|
||||
|
||||
Engine primitives it calls (ALREADY in `munga_engine.lib`, callable — do NOT reconstruct): `TwoState`
|
||||
(`FUN_004739d8`, vt `0x4fb740`), `NumericDisplayScalar` (`0x470888`, vt `0x4fbb40`),
|
||||
`NumericDisplayInteger` (`0x470cfc`), `NumericDisplayScalarTwoState` (`0x473c94`), `BackgroundBitmap`
|
||||
(`0x471d00`), and the GaugeConnection ctors `FUN_00474855`/`FUN_004749de`. Already reconstructed:
|
||||
HorizTwoPartBar/VertTwoPartBar/OneOfSeveralInt/OneOfSeveralStates/BitMapInverseWipe(Scalar)/SegmentArc270/
|
||||
SeekVoltageGraph.
|
||||
|
||||
## ⚠ THE BLOCKER — core `MechSubsystem` layout (Phase 2)
|
||||
The Make reads `subsystem[0x1dc]` (auxiliary-screen position, INT 1..12; 0/miss → the panel is skipped
|
||||
with "Auxiliary screen position = zero"), `[0x1e4]` (display label), `[0x1e0]` (damage-zone index),
|
||||
`[0x224]` (a name). Our reconstructed `MechSubsystem` ends at **0x114** — these are **unreconstructed base
|
||||
fields not populated from the resource parse**. So the panels render NOTHING until the core subsystem
|
||||
layout is extended + populated (from `MechSubsystem::CreateStreamedSubsystem` / the resource ctor
|
||||
`FUN_004ac644`) — a change to the WORKING combat/heat subsystem code (regression risk per the §10 systemic
|
||||
rules). The bytes exist in the allocation (subclass layouts are locked to binary offsets, so there is
|
||||
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.
|
||||
Reference in New Issue
Block a user