Clean, self-contained extraction of the BattleTech-specific work from the
reverse-engineering workspace -- engine + game + content + build, with nothing
from Red Planet or the raw archive dumps. Builds green (Win32) and runs the
single-player drive->animate->target->fire->damage->destroy loop out of the box.
Layout:
engine/ MUNGA + MUNGA_L4 shared 2007 engine, carrying our BT render/loader
work (bgfload/L4D3D/L4VIDEO: BSL bit-slice decode, LOD/ground/shadow
models) + image codec; the minimal rp/ headers the audio HAL needs
game/ reconstructed BT logic + surviving-original BT source + fwd shims
+ WinMain launcher
content/ full runtime tree (BTL4.RES, VIDEO/, GAUGE/, AUDIO/, eggs, BTDPL.INI)
docs/ format specs + reconstruction ledgers
reference/ raw Ghidra pseudocode (recon source-of-truth) + decomp exporter
tools/ MP console emulator + map/resource scanners
One top-level CMake builds munga_engine lib + bt410_l4 game lib + btl4.exe.
All paths relativized (186 fwd shims + ~437 CMake abs paths -> repo-relative);
DXSDK is the one external, overridable via -DDXSDK. Verified: builds to a
byte-identical 2.27MB exe and runs combat (TARGET DESTROYED, 0 crashes) against
the bundled content.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
58 lines
5.2 KiB
Markdown
58 lines
5.2 KiB
Markdown
# BT Port — Resource-Layout Audit (resource-layout-audit workflow, 33 agents, adversarially confirmed)
|
|
|
|
> **✅ STATUS: ALL 8 BUGS FIXED & VERIFIED** (commits 802a7a6 A, 7de66ad B, 975a397 Gyro+Searchlight).
|
|
> Each layout locked with compile-time `static_assert(offsetof/sizeof)` guards; full build green; combat +
|
|
> heat-flow un-regressed. The plan below is retained as the record of what was found and done.
|
|
|
|
**Verdict:** 24 `*__SubsystemResource` structs audited → **8 confirmed bugs** (all high-severity,
|
|
review-upheld, none refuted), 16 clean. All 8 root to **2 broken base resources** (`HeatWatcher`,
|
|
`PowerWatcher`) + **2 struct-specific field defects** (`Gyroscope`, `Searchlight`).
|
|
|
|
Same class as the heat-resource bug fixed earlier (§10d): a resource struct that doesn't mirror its
|
|
class hierarchy reads every field from the wrong offset — SILENT garbage (non-fatal). These 6 Watcher-
|
|
family subsystems (AmmoBin, Gyroscope, Searchlight, ThermalSight, Torso, HUD) currently TICK on garbage
|
|
resource data. Combat is unaffected (mech/weapons/damage/heat don't depend on them) — it bites when
|
|
WAVE 4-8 consumes gyro/torso motion, sensor/searchlight/thermalsight readouts, and ammo.
|
|
|
|
## Raw-decomp verification (done this session — the fix is bigger than "one-liner")
|
|
- `HeatWatcher::CreateStreamedSubsystem` = `FUN_004aec54` chains to `FUN_004ac9ec` (the **MechSubsystem**
|
|
parse, fills 0x30..0xE4 — armorByFacing/videoObjectName/alarmModel/... verified) then adds only
|
|
`DegradationTemperature@0xE8` + `FailureTemperature@0xEC`. Record ends **0xF0** (MechSubsystem + 3).
|
|
- `HeatWatcher` ctor `@4aeb40` chains to `FUN_004ac644` (MechSubsystem-level base ctor, shared with the
|
|
HeatSink ctor) and reads only `res+0xe4/0xe8/0xec` — NO thermal-mass/heatSinkIndex reads. So HeatWatcher
|
|
is genuinely **MechSubsystem-based**, not a full HeatableSubsystem.
|
|
- `PowerWatcher` ctor chains to `FUN_004aeb40` = **HeatWatcher's ctor** → `PowerWatcher : HeatWatcher`
|
|
(powersub.hpp already comments "real base is HeatWatcher"). Adds `MinVoltagePercent@0xF0`, record ends **0xF4**.
|
|
- The reconstruction implemented BOTH classes as `: public HeatableSubsystem` (an approximation, with the
|
|
real base in `// FUN_...` comments) → the resource was made HeatableSubsystem-based (0xFC) → every
|
|
Watcher field slid +0x18 (HeatWatcher) / +0xC (PowerWatcher). Fixing the resource base alone will NOT
|
|
compile (the ctor passes the resource to the HeatableSubsystem base ctor) → class+ctor+parser must change together.
|
|
|
|
## Fix plan (bases before children; each verified by offset logging `(char*)&res->f - (char*)res`)
|
|
1. **Root cause A — HeatWatcher → MechSubsystem** (heatfamily_reslice.hpp/.cpp):
|
|
- class `HeatWatcher : public MechSubsystem` (was HeatableSubsystem)
|
|
- resource `HeatWatcher__SubsystemResource : public MechSubsystem__SubsystemResource` (was HeatableSubsystem::SubsystemResource); keep `int watchedSubsystem;`(0xE4) `Scalar degradationTemperature;`(0xE8) `Scalar failureTemperature;`(0xEC) → ends **0xF0**
|
|
- ctor chains `MechSubsystem(...)`; parser chains the MechSubsystem parse (FUN_004ac9ec)
|
|
- de-shadow any fields HeatWatcher inherited from HeatableSubsystem it no longer has
|
|
- **Fixes HeatWatcher + AmmoBin** (child; fields realign to 0xF0/0xF4/0xF8/0xFC/0x100, size 0x104). Target stamp: HeatWatcher **0xF0**, AmmoBin **0x104**.
|
|
2. **Root cause B — PowerWatcher → HeatWatcher** (powersub.hpp/.cpp; depends on A):
|
|
- class `PowerWatcher : public HeatWatcher`; resource `... : public HeatWatcher::SubsystemResource`, keep `Scalar minVoltagePercent;`(0xF0) → ends **0xF4** (do NOT pad — would break the 0xF4 stamp)
|
|
- powersub.hpp must `#include` the HeatWatcher resource (`heatfamily_reslice.hpp`)
|
|
- ctor chains `HeatWatcher(...)`; parser chains HeatWatcher parse (FUN_004aec54, WatchedSubsystem/Degradation/Failure keys)
|
|
- **Fixes PowerWatcher + HUD + ThermalSight + Torso** (children realign, no child edits). Target stamps: PowerWatcher **0xF4**, Torso **0x158**.
|
|
3. **Gyroscope** (gyro.hpp; after B): add `Scalar field_f4;`(0xF4, ctor `FUN_004b3778` reads it, init -1.0f — best-effort name) BEFORE `exageration`(0xF8). Then fields align through 0x21C.
|
|
4. **Searchlight** (searchlight.hpp:75 + searchlight.cpp:96; independent, after B): delete `int segmentDefault;`
|
|
(compiles OOB at sizeof(PowerWatcher)); the binary reads `resource+0x28` = inherited `segmentIndex` →
|
|
change searchlight.cpp:96 to `commandedOn = subsystem_resource->segmentIndex;`.
|
|
|
|
**Clean / no action (16):** Emitter, HeatableSubsystem (correctly 0xFC — do NOT touch; HeatSink/Generator/
|
|
PoweredSubsystem/Reservoir/Condenser depend on it), Condenser, Reservoir, MechSubsystem, MechTech,
|
|
MechWeapon, SubsystemMessageManager, MissileLauncher, MissileThruster, Myomers, PoweredSubsystem,
|
|
Generator, ProjectileWeapon, Seeker, Sensor.
|
|
|
|
**Rejected proposal:** aliasing HeatWatcher fields onto the 0xFC HeatableSubsystem base via accessors/union
|
|
— leaves sizeof=0xFC, contradicts the 0xF0 stamp, cascades the wrong size into PowerWatcher. Use the re-base.
|
|
|
|
**Verify after each step:** offset logging vs stamps (HeatWatcher 0xF0, PowerWatcher 0xF4, AmmoBin 0x104,
|
|
Torso 0x158, Gyroscope ends 0x21C); build green; combat/heat/torso un-regressed.
|