context-system: complete migration -> CLAUDE.md is now a 160-line router (zero context lost)
Full migration of the 2236-line monolithic CLAUDE.md into the progressive-context knowledge graph (per spark-lesson / expert-seed.md), so the deep RE knowledge loads on-demand instead of every session. ZERO CONTEXT LOST: - docs/PROGRESS_LOG.md = the complete old CLAUDE.md, VERBATIM (byte-identical) -- the lossless safety net + the "full detail" quick-lookup fallback. - 18 context/*.md topic files (1343 lines) digest every section (§1-3 -> project-overview, §4 -> content-archives, §5 -> asset-formats/bgf-format, §5a -> source-completeness, §5b/§8 -> wintesla-port, §7/§10 -> locomotion, §10a -> build-and-run, §10b -> reconstruction-method, §10c -> combat-damage + reconstruction-gotchas, §10d -> subsystems, render notes -> rendering, gauges -> gauges-hud, MP -> multiplayer, §9 -> open-questions). - reference/glossary.yaml (53 terms). decomp-reference.md = the offsets/ClassIDs/addresses hub. CLAUDE.md (160 lines) = router: identity, answer/reason protocols, quick-lookup table, evidence tiers (T0 engine-truth / T1 decompiled+verified / T2 reconstructed+runtime / T3 guarded / T4 hypothesis), conventions + DO-NOT (the systemic bug classes), structure. Retains the load-bearing work directives (build recipe pointer, "keep current" mandate). Knowledge graph validates CLEAN (scratchpad/checkctx.py -- all [[links]] + quick-lookup + docs refs resolve; [[name]] -> topic file or glossary term). docs/*.md ledgers stay as the detailed logs; context/*.md are the curated digests that route into them. 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
1cd57ade85
commit
f914fc040a
@@ -1,153 +0,0 @@
|
|||||||
# BattleTech Pod-Port — Reverse-Engineering Expert Knowledge Base
|
|
||||||
|
|
||||||
> ⚠️ **DRAFT of the proposed slim `CLAUDE.md`.** This is the router that would REPLACE the
|
|
||||||
> 2236-line `CLAUDE.md`, moving its deep knowledge into `context/*.md`. It is a draft for review
|
|
||||||
> — the live `CLAUDE.md` is unchanged until this is approved + the remaining topic files are
|
|
||||||
> migrated. See `phases/phase-01-context-restructure.md` for the migration plan + status.
|
|
||||||
|
|
||||||
**Project:** Port VWE's 1995 arcade **BattleTech** (BT) pod game (MUNGA engine, Tesla platform
|
|
||||||
4.10) to modern Windows + the pod hardware, by reconstructing the missing BT game logic from the
|
|
||||||
`BTL4OPT.EXE` binary on top of the working WinTesla engine.
|
|
||||||
**Repo of record:** the top-level `CMakeLists.txt` + `README.md` build `btl4.exe`. Layout:
|
|
||||||
`engine/ game/ content/ docs/ reference/ tools/ context/`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## YOU ARE THE BATTLETECH PORT EXPERT
|
|
||||||
|
|
||||||
**You (Claude) are the expert on this port** — the 1995 MUNGA/BT engine, its asset formats, the
|
|
||||||
decompilation, and the reconstruction state. The `context/*.md` files, `reference/glossary.yaml`,
|
|
||||||
`reference/decomp/` (the raw pseudocode), and the `docs/*.md` ledgers are YOUR knowledge base.
|
|
||||||
|
|
||||||
**ALWAYS consult the context system before answering a question about the engine, formats, a
|
|
||||||
subsystem, or the reconstruction state — and before writing reconstruction code.** Do not rely on
|
|
||||||
training data; this is a specific, reverse-engineered 1995 codebase. The knowledge here is more
|
|
||||||
precise than anything you can infer.
|
|
||||||
|
|
||||||
### How to answer a question
|
|
||||||
1. **Identify the topic(s)** via the Quick Lookup table below (most questions touch 1-3).
|
|
||||||
2. **Read the full topic file** — don't guess from the filename.
|
|
||||||
3. **Follow `[[wiki-links]]`** and `related_topics` frontmatter to adjacent topics.
|
|
||||||
4. **Check `context/decomp-reference.md`** for any offset, ClassID, resource type, address, or env gate.
|
|
||||||
5. **Check `reference/glossary.yaml`** for a term's meaning.
|
|
||||||
6. **Check `context/open-questions.md`** for known unknowns / deferred systems.
|
|
||||||
7. **Cite** the file + section (and the `docs/*.md` ledger or `reference/decomp` address when relevant).
|
|
||||||
|
|
||||||
### How to reason about / reconstruct something novel
|
|
||||||
1. **Ground it in the decomp.** Read the raw `FUN_xxxx` in `reference/decomp/all/part_*.c`; map
|
|
||||||
`FUN_`/`DAT_`/`this+0xNN` to engine symbols via BT headers + the WinTesla MUNGA source +
|
|
||||||
`game/reconstructed/CLASSMAP.md` + RP's parallel code.
|
|
||||||
2. **Check the gotchas.** `context/reconstruction-gotchas.md` — layout (shadow/alias/phantom
|
|
||||||
fields, resource mismatch), linkage (`/FORCE`), dtor-epilogue, databinding trap. Most bugs are here.
|
|
||||||
3. **RULE: no stand-ins.** The full logic IS in the pseudocode; a "gap" is an unfilled stub, not a
|
|
||||||
hole. Never write placeholder logic — read the decomp. Bring-up scaffolding is marked + temporary.
|
|
||||||
4. **Verify honestly.** `static_assert`-lock layouts; run env-gated; read `btl4.log`; cdb on crashes.
|
|
||||||
Tag claims with the evidence tier; flag T3/T4.
|
|
||||||
5. **Persist insights.** A genuinely new finding → add it to the right `context/*.md` (+ the `docs/`
|
|
||||||
ledger for detail), with an evidence tier. Keep the knowledge base current — this is a mandate,
|
|
||||||
not an afterthought.
|
|
||||||
|
|
||||||
### Quick Lookup
|
|
||||||
| User asks about... | Read this file |
|
|
||||||
|---|---|
|
|
||||||
| What the project is, engine, platform, goal | `context/project-overview.md` |
|
|
||||||
| Missing BT source, decompilation strategy | `context/source-completeness.md` |
|
|
||||||
| The WinTesla Windows port (renderer/audio/HAL) | `context/wintesla-port.md` |
|
|
||||||
| Build / run / debug / repo layout / env gates | `context/build-and-run.md` + `context/decomp-reference.md` §6 |
|
|
||||||
| Asset formats (MOD/SKL/ANI/BMF/BSL/SLD/…) | `context/asset-formats.md` |
|
|
||||||
| BGF geometry, LODs, CONN/PCONN, ramps | `context/bgf-format.md` |
|
|
||||||
| A layout/linkage/databinding BUG | `context/reconstruction-gotchas.md` |
|
|
||||||
| The reconstruction method / workflow | `context/reconstruction-method.md` |
|
|
||||||
| Walking, gait, ground model, collision | `context/locomotion.md` |
|
|
||||||
| Subsystems, the factory, heat/weapons/power | `context/subsystems.md` |
|
|
||||||
| Damage zones, targeting, firing, death | `context/combat-damage.md` |
|
|
||||||
| Rendering, LODs, materials, sky, shadows, beams | `context/rendering.md` |
|
|
||||||
| Cockpit gauges / MFD HUD | `context/gauges-hud.md` |
|
|
||||||
| Multiplayer, replication, netcode | `context/multiplayer.md` |
|
|
||||||
| Pod hardware, monitors, RIO, MFD surfaces | `context/pod-hardware.md` |
|
|
||||||
| Content archives, maps, where data lives | `context/content-archives.md` |
|
|
||||||
| Offsets, ClassIDs, addresses, resource types, env vars | `context/decomp-reference.md` |
|
|
||||||
| What we don't know / deferred systems | `context/open-questions.md` |
|
|
||||||
| Term / acronym definitions | `reference/glossary.yaml` |
|
|
||||||
| Detailed running ledgers | `docs/RECONCILE.md`, `docs/GAUGE_COMPOSITE.md`, `docs/HARD_PROBLEMS.md`, `docs/SUBSYS_PLAN.md`, `docs/P3_LOCOMOTION.md` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Evidence Tiers (adapted for reverse-engineering)
|
|
||||||
|
|
||||||
Tag individual claims (not sections) inline: `[T1]`, `[T2]`, … A claim inherits the **highest
|
|
||||||
(least certain)** tier of its inputs. Flag T3/T4 to the user.
|
|
||||||
|
|
||||||
| Tier | Label | Meaning |
|
|
||||||
|---|---|---|
|
|
||||||
| **T0** | Engine/header truth | A fact from the WinTesla MUNGA/L4 source or a BT header — the actual code we compile against. Authoritative. |
|
|
||||||
| **T1** | Decompiled + verified | Read from the binary pseudocode (`reference/decomp`) AND confirmed — disasm, `static_assert` offset lock, or cross-checked vs the RP analog. |
|
|
||||||
| **T2** | Reconstructed + runtime-verified | Implemented in `game/reconstructed/` and verified LIVE (combat un-regressed, gauge renders, log confirms the value). |
|
|
||||||
| **T3** | Best-effort / guarded | Reconstructed but not fully verified — a marked stand-in or a guarded raw-offset read. Runs, fidelity uncertain. |
|
|
||||||
| **T4** | Unconfirmed hypothesis | A claim about the binary's intent not yet decompiled/verified; a guess. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Conventions (CRITICAL — full detail in `context/reconstruction-gotchas.md`)
|
|
||||||
- **Never raw-read a compiled object's offsets** (`*(T*)(obj+0xNN)`) — our layout ≠ the 1995 binary.
|
|
||||||
Use named members / accessors / a **bridge** fn in a complete-type TU. (databinding trap)
|
|
||||||
- **Never re-declare an engine-base field** — it shadows the base (reads `0xCDCDCDCD`) + mis-offsets.
|
|
||||||
- **`/FORCE` hides unresolved symbols** — an unresolved external → runtime AV near `__ImageBase`, not
|
|
||||||
a link error. Grep the link log when a build "succeeds" but crashes at a garbage call target.
|
|
||||||
- **Don't reconstruct dtor compiler-glue** — the trailing base/member-dtor calls run the chain twice.
|
|
||||||
- **`static_assert`-lock every reconstructed layout** against the binary's offsets/sizeof.
|
|
||||||
- **A `+0x128`-style owner offset is the subsystem ROSTER, not the segment table.**
|
|
||||||
- **RULE: no stand-ins** — read the decomp; a gap is an unfilled stub.
|
|
||||||
- **Keep the knowledge base current** — new durable findings → `context/*.md` (+ `docs/` for detail).
|
|
||||||
|
|
||||||
## DO NOT
|
|
||||||
- Present T3/T4 claims as established without flagging the tier.
|
|
||||||
- Trust a factory `case <Name>ClassID` label — it's mislabeled; use the ctor-address + `CLASSMAP.md`.
|
|
||||||
- Issue `DestroyEntityMessage` on a mech death (the wreck STAYS; removal = the P5 teardown crash).
|
|
||||||
- Conclude "gauge not built" from an early process kill — the gauge renderer builds LAZILY.
|
|
||||||
- Use `DebugStream` (ReconStream, a no-op) for a log — use `DEBUG_STREAM`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Project Structure
|
|
||||||
```
|
|
||||||
bt411/
|
|
||||||
├── CLAUDE.md # this router (was the 2236-line monolith)
|
|
||||||
├── README.md # build of record
|
|
||||||
├── context/ # topic files (curated knowledge graph)
|
|
||||||
│ ├── decomp-reference.md # offsets/ClassIDs/addresses/env gates (the "equations" hub)
|
|
||||||
│ ├── reconstruction-gotchas.md # the systemic bug classes (conventions hub)
|
|
||||||
│ ├── open-questions.md # deferred systems + get-from-Nick
|
|
||||||
│ └── <topic>.md # one concept each
|
|
||||||
├── reference/
|
|
||||||
│ ├── glossary.yaml # terms/acronyms
|
|
||||||
│ ├── decomp/ # raw Ghidra pseudocode (the source-of-truth)
|
|
||||||
│ └── ghidra_scripts/ # the exporters
|
|
||||||
├── docs/ # detailed running ledgers (RECONCILE, GAUGE_COMPOSITE, HARD_PROBLEMS, …)
|
|
||||||
├── phases/ # restructuring / investigation logs
|
|
||||||
├── engine/ game/ content/ tools/ # the actual port (see README.md)
|
|
||||||
```
|
|
||||||
|
|
||||||
### How to add content
|
|
||||||
- **New finding on an existing topic** → edit the `context/*.md` (+ the `docs/` ledger for full detail).
|
|
||||||
- **New topic** → new `context/*.md` with YAML frontmatter; add to the Quick Lookup table + cross-refs.
|
|
||||||
- **Split** a topic file that exceeds ~25k tokens into finer subtopics.
|
|
||||||
- **New investigation** → a `phases/*.md` log.
|
|
||||||
- The `docs/*.md` ledgers stay as the DETAILED logs; `context/*.md` are the curated digests that route to them.
|
|
||||||
|
|
||||||
## Topic file format
|
|
||||||
```markdown
|
|
||||||
---
|
|
||||||
id: topic-id
|
|
||||||
title: "Title"
|
|
||||||
status: established | provisional | deferred | living
|
|
||||||
source_sections: "CLAUDE.md §X; docs/FILE.md; reference/decomp addresses"
|
|
||||||
related_topics: [other-topic-a, other-topic-b]
|
|
||||||
key_terms: [glossary-term-1]
|
|
||||||
open_questions: ["tracked uncertainty"]
|
|
||||||
---
|
|
||||||
# Title
|
|
||||||
## sections…
|
|
||||||
## Key Relationships
|
|
||||||
- Uses: [[other-topic]] · Feeds: [[other-topic]]
|
|
||||||
```
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
id: asset-formats
|
||||||
|
title: "Asset Formats — MOD/SKL/ANI/BMF/BSL/textures/gauges/collision"
|
||||||
|
status: established
|
||||||
|
source_sections: "PROGRESS_LOG.md §5; docs/ASSET_PIPELINE.md; docs/BGF_FORMAT.md"
|
||||||
|
related_topics: [bgf-format, rendering, locomotion, combat-damage, gauges-hud]
|
||||||
|
key_terms: [MOD, SKL, ANI, BMF, BSL, VTX, SLD, DZM, PCC, GIM, material-ramp]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Asset Formats
|
||||||
|
|
||||||
|
All reverse-engineered. Geometry (BGF) has its own topic ([[bgf-format]]); the full pixel/material
|
||||||
|
spec is `docs/ASSET_PIPELINE.md`. Full list: `docs/PROGRESS_LOG.md §5`.
|
||||||
|
|
||||||
|
## Definition / skeleton / animation
|
||||||
|
- **`.MOD`** — INI mech/object definition: skeleton, skins, collision, gauges, physics, animations,
|
||||||
|
class. The `.MOD` physics (MoverMass/drag/MaxAcceleration) governed thrust VEHICLES + collisions,
|
||||||
|
NOT walk gait ([[locomotion]]). [T1]
|
||||||
|
- **`.SKL`** — INI joint skeleton. `[joint] parent=, Type=(hingex/y/z|ball|balltranslate),
|
||||||
|
Object=part.bgf, dzone=, tranx/y/z, pitch/yaw/roll`. Build a **DCS tree**; neutral pose ≈
|
||||||
|
translations (pyr≈0). [T1]
|
||||||
|
- **`.ANI`** — INI keyframe animation. `[HEADER] framecount/framerate(30)/skeletonfile`; `[frameN]`
|
||||||
|
joint→`rx ry rz` radians (balltranslate→translate); `[RootTranslation]` per-keyframe `x y z`
|
||||||
|
where **`.z` = forward root SPEED** (units/s). Walk anims have no baked root translation — forward
|
||||||
|
motion is animation-driven from `.z` ([[locomotion]]). [T1]
|
||||||
|
|
||||||
|
## Materials / textures
|
||||||
|
- **`.BMF`** — `DIV-BIZ2` (FILETYPE=1) material+texture LIBRARY (no pixels). Material → DIFFUSE color
|
||||||
|
+ `MATERIAL_TEXTURE`→texture→`TEXTURE_MAP` image basename. Tags: `0x18 BITSLICE` (BSL slice select),
|
||||||
|
`0x28` ramp ref, `0x26` emissive, `0x30/0x31` ramp def. [T1]
|
||||||
|
- **Texture pixels:** `.tga` (Truevision), `.vtx` (`DIV-VTX2`, trailing RGB), and **`.bsl`**
|
||||||
|
(`DIV-BSL2` — a BIT-SLICED container: w*h 32-bit texel words holding SIX independent 4-bit
|
||||||
|
GRAYSCALE sub-images (nibble pair-swapped, byte 0 pad) OR RGB444/RGBA4444 (sliceType 7/8). The BMF
|
||||||
|
`0x18 BITSLICE` tag picks the slice, absent=0). Decoding BSL as RGB = the "rainbow/graffiti mech"
|
||||||
|
bug. Reference reader: `DivLoader/VGCDivLoader.cpp:323-410`. [T2]
|
||||||
|
- **`.VMF`** — `DIV-VIZ2` material SOURCE. Carries the `SCROLL` animation + `MAP{}` image binding for
|
||||||
|
effects (beams/muzzle/smoke/sky). THE key effects asset ([[rendering]]). [T2]
|
||||||
|
- **[[material-ramp]]** — the IG board colours no-normal geometry by a 2-endpoint RAMP; details in
|
||||||
|
[[bgf-format]] + [[rendering]]. [T2]
|
||||||
|
|
||||||
|
## Collision / damage / subsystems
|
||||||
|
- **`.SLD`** collision solids (Block/YCyl/Cone/Ramp/Wedge). Terrain is modeled AS collision volumes;
|
||||||
|
see [[locomotion]] ground model + [[combat-damage]]. [T1]
|
||||||
|
- **`.DZM`** = INI damage-zone→material map (mech skin damage states). **`.DMG`/`.TBL`** damage
|
||||||
|
zones/tables, **`.SUB`** subsystems (weapon params — DischargeTime/RechargeRate/WeaponRange),
|
||||||
|
**`.CTL`** control maps. [T1]
|
||||||
|
|
||||||
|
## Cockpit HUD assets ([[gauges-hud]])
|
||||||
|
- **`.GIM`** vector MFD/radar line maps + **`.GAT`** color table; **`.PCC`** ZSoft PCX 8-bit gauge
|
||||||
|
rasters (fonts helv15/helv42, label strips, dials). [T1]
|
||||||
|
|
||||||
|
## Key Relationships
|
||||||
|
- Geometry detail: [[bgf-format]]; pixel/material spec: `docs/ASSET_PIPELINE.md`.
|
||||||
|
- Consumed by: [[rendering]], [[locomotion]] (SKL/ANI), [[combat-damage]] (SLD/DZM/SUB), [[gauges-hud]] (GIM/PCC).
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
---
|
||||||
|
id: build-and-run
|
||||||
|
title: "Build / Run / Debug — recipe, repo layout, env gates"
|
||||||
|
status: established
|
||||||
|
source_sections: "PROGRESS_LOG.md §10a, §10a-bis; README.md"
|
||||||
|
related_topics: [wintesla-port, decomp-reference, reconstruction-method]
|
||||||
|
key_terms: [BTL4OPT, cdb, BTL4.RES, EGG, WinTesla]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Build / Run / Debug
|
||||||
|
|
||||||
|
The one top-level `CMakeLists.txt` builds `munga_engine` (engine) + `bt410_l4` (reconstructed BT
|
||||||
|
game lib) + `btl4.exe`. Full recipe + repo-layout map: `docs/PROGRESS_LOG.md §10a / §10a-bis` +
|
||||||
|
`README.md`. Env-gate table: [[decomp-reference]] §6.
|
||||||
|
|
||||||
|
## Build (Win32 / VS2019 BuildTools — the DXSDK link libs are Lib/x86)
|
||||||
|
```
|
||||||
|
# configure once:
|
||||||
|
cmake -S C:\git\bt411 -B C:\git\bt411\build -G "Visual Studio 16 2019" -A Win32 \
|
||||||
|
-DCMAKE_GENERATOR_INSTANCE="C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools"
|
||||||
|
# build:
|
||||||
|
cmake --build C:\git\bt411\build --config Debug
|
||||||
|
```
|
||||||
|
- Links DXSDK d3d9/d3dx9/dinput8 + OpenAL/libsndfile (`engine/lib/`). DXSDK June 2010 at
|
||||||
|
`C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\` (overridable `-DDXSDK`). [T2]
|
||||||
|
- Linker uses **`/FORCE`** — tolerates header-defined globals + the **dead offline-factory
|
||||||
|
unresolved externals in mech3.cpp** (`Mech::CreateSubsystemStream` references `void*`-signature
|
||||||
|
`CreateStreamedSubsystem` for every class; never called at runtime). ⚠ `/FORCE` also HIDES real
|
||||||
|
unresolved symbols as runtime AVs — see [[reconstruction-gotchas]] §6. [T2]
|
||||||
|
- Editing an `engine/` file rebuilds the engine lib automatically (one project). A NEW member on a
|
||||||
|
`DPLRenderer`/`d3d_OBJECT` class needs the game objs that embed its layout recompiled — delete
|
||||||
|
stale objs if layout-mismatch corruption appears. [T2]
|
||||||
|
- ⚠ Kill the running exe before rebuilding (`taskkill //F //IM btl4.exe`) or you get LNK1104.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
```
|
||||||
|
run\run.cmd [EGG] # default DEV.EGG; cd's to content\ and runs btl4.exe -egg <EGG>
|
||||||
|
```
|
||||||
|
- **cwd MUST be `content\`** — the engine resolves `BTL4.RES`, `VIDEO\`, `BTDPL.INI`, eggs relative
|
||||||
|
to cwd (the `loadTables` gotcha: `L4VIDEO.cpp:849` `fopen("VIDEO\\REPLACEMATS.tbl")` is relative +
|
||||||
|
unchecked → fread on NULL if cwd is wrong). Logs to `btl4.log` in `content\` (or `BT_LOG=<file>`). [T2]
|
||||||
|
- Interactive: **WASD** drive, **Space/Ctrl** fire, **X** all-stop. Default egg = `DEV.EGG`
|
||||||
|
(map=grass, time=day). Swap mech via the egg's `vehicle=` (bhk1/madcat/…). The code path is
|
||||||
|
mech-agnostic (verified — Mad Cat booted + fought with zero code changes). [T2]
|
||||||
|
|
||||||
|
## Debug (cdb x86)
|
||||||
|
`"C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\cdb.exe"`. Pattern for a faulting stack (cwd
|
||||||
|
= content\): `-g -c ".lines;sxe av;g;kp 24;q"` with `BT_ASSERT_TO_DEBUGGER=1`. Debug CRT fills fresh
|
||||||
|
heap **0xCDCDCDCD** (uninit) + freed **0xFEEEFEEE** — invaluable for "was this ever constructed?".
|
||||||
|
`BT_HEAPCHECK=1` = whole-heap validation every alloc/free (O(n²) at mission load — SLOW). To attach
|
||||||
|
to a frozen abort dialog: `cdb -p <pid> -c ".lines;~*kp 30;q"`. [T2]
|
||||||
|
|
||||||
|
## Repo layout (bt411)
|
||||||
|
- **`engine/MUNGA/` + `engine/MUNGA_L4/`** — the shared 2007 MUNGA engine + Win32/D3D9 HAL (carries
|
||||||
|
our BT render/loader work: bgfload/L4D3D/L4VIDEO + the image codec). `engine/shim/` (ATL),
|
||||||
|
`engine/lib/` (OpenAL/libsndfile), `engine/rp/` (RP *headers* the audio HAL includes).
|
||||||
|
- **`game/reconstructed/`** — the reconstructed BT source (the bulk).
|
||||||
|
- **`game/original/BT/` + `BT_L4/`** — surviving original BT `.cpp` + **all BT headers** (the include path).
|
||||||
|
- **`game/fwd/`** — ~186 forwarding shims (`#include <EXPLODE.hpp>` → `../../engine/MUNGA/<NAME>.h`).
|
||||||
|
- **`game/btl4main.cpp`** — the WinMain launcher.
|
||||||
|
- **`content/`** — the runtime tree (BTL4.RES, VIDEO/, GAUGE/, AUDIO/, eggs, BTDPL.INI). Run cwd.
|
||||||
|
- **`reference/decomp/`** — the raw Ghidra pseudocode (`all/part_*.c`) — the source-of-truth.
|
||||||
|
- **`docs/`** — the detailed ledgers (incl. `PROGRESS_LOG.md`, the full pre-restructure CLAUDE.md).
|
||||||
|
- **`tools/`** — btconsole.py, disas2.py, map/res scanners. **`context/`** — this knowledge base.
|
||||||
|
|
||||||
|
## Key Relationships
|
||||||
|
- Base: [[wintesla-port]] (the engine build recipe).
|
||||||
|
- Verify loop: [[reconstruction-method]]; env gates: [[decomp-reference]] §6.
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
---
|
||||||
|
id: combat-damage
|
||||||
|
title: "Combat & Damage — targeting, firing, damage zones, death"
|
||||||
|
status: established
|
||||||
|
source_sections: "PROGRESS_LOG.md §10c; docs/HARD_PROBLEMS.md (P5)"
|
||||||
|
related_topics: [subsystems, decomp-reference, reconstruction-gotchas, locomotion, rendering]
|
||||||
|
key_terms: [damage-zone, TakeDamage, viewpoint-entity, master, replicant]
|
||||||
|
open_questions:
|
||||||
|
- "Per-impact aim (cylinder lookup); collision damage application; DeathShutdown + collapse anim"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Combat & Damage
|
||||||
|
|
||||||
|
The drive→target→fire→damage→destroy loop. Full detail: `docs/PROGRESS_LOG.md §10c`; the death/
|
||||||
|
teardown forensics: `docs/HARD_PROBLEMS.md` (P5).
|
||||||
|
|
||||||
|
## No AI — BT is PvP-only (verified, not a recovery gap)
|
||||||
|
The mission system is NETWORKED and BT never had NPCs/AI. Every mech is driven by a `BTPlayer` (or a
|
||||||
|
`BTCameraDirector` spectator). The BT registry knows only Mech/Projectile/Missile/BTPlayer/BTTeam; no
|
||||||
|
Bot/Brain/AI class exists; `BTL4OPT.EXE` has no AI strings. `Crusher/Blocker/Runner` are RED PLANET
|
||||||
|
classes, NOT BT. ⇒ a solo mission has no enemy by design; `BT_SPAWN_ENEMY` is a TEST DUMMY
|
||||||
|
(uncontrolled Mech). The authentic opponent path is [[multiplayer]]. [T1]
|
||||||
|
|
||||||
|
## Entity spawn + the per-mech gate
|
||||||
|
`Mech::Make(MakeMessage*)` (`FUN_004a2d48`) allocs `sizeof(Mech)` + runs the ctor; the base Entity
|
||||||
|
ctor self-registers it (renders + ticks). `Mech::PerformAndWatch` runs for EVERY mech → gate
|
||||||
|
player-only logic on `this == application->GetViewpointEntity()` (else a spawned enemy is driven by
|
||||||
|
player input). [T1]
|
||||||
|
|
||||||
|
## Targeting (mech offsets, [[decomp-reference]] §3)
|
||||||
|
`mech+0x37c` = target world Point3D; `mech+0x388` = target `Entity*` (the `HasActiveTarget()` gate);
|
||||||
|
`mech+0x38c` = targeted sub-zone (−1=whole). Weapons cache hasTarget/targetPoint/muzzlePoint,
|
||||||
|
refreshed each frame. [T1]
|
||||||
|
|
||||||
|
## Firing arc + muzzles
|
||||||
|
There is NO firing-arc field in the binary (`Emitter::FireWeapon` fires whenever HasActiveTarget) —
|
||||||
|
the 1995 game got away with it because it was **cockpit-only** (a beam behind you is off-camera). The
|
||||||
|
port's external camera exposes it, so the arc is gated on the **torso twist range**
|
||||||
|
(`Torso::GetHorizontalReach`, 0 for the twist-disabled Blackhawk) + a base aim tolerance
|
||||||
|
(`BT_FIRE_ARC`, a labeled PORT presentation param). Beams fire from the mech's real gun-port SITE
|
||||||
|
segments (siterugunport etc.) resolved via `GetSegment(name)→segmentToEntity×localToWorld`. [T2]
|
||||||
|
|
||||||
|
## Weapon fire is a PORT reconstruction (the dpl_* beam layer is unported)
|
||||||
|
`Emitter::FireWeapon` builds a muzzle→target beam but the `dpl_*` beam renderable was never ported →
|
||||||
|
the beam is drawn by `BTPushBeam`/`BTDrawBeams` (L4VIDEO — an additive quad / the real `ermlaser.bgf`
|
||||||
|
tube with the scrolling `bexp` grit). Fire rate is decoded-exact (DischargeTime 0.2 + RechargeRate 2.2
|
||||||
|
per ER-M laser; [[decomp-reference]] §5). Flying projectiles (LRM/autocannon) are also a PORT
|
||||||
|
reconstruction (`BTPushProjectile` — the 2007 Entity is too small for the binary's raw integrator
|
||||||
|
offsets). [T2]
|
||||||
|
|
||||||
|
## Damage delivery + the real damage model
|
||||||
|
`Entity::TakeDamageMessage(id, size, inflictingEntityID, zone, Damage&)` → `target->Dispatch`.
|
||||||
|
**Base handler IGNORES zone==−1** (the Mech cylinder-lookup override for unaimed hits is not
|
||||||
|
reconstructed — send a VALID zone). `Mech__DamageZone::TakeDamage` → engine armor model (`damageLevel
|
||||||
|
+= damageAmount*damageScale[type]`) → 1.0 = zone destroyed → death. The Mech ctor populates the
|
||||||
|
inherited `damageZones[]` from the DamageZoneStream (type 0x14) resource. `class Damage{ damageType
|
||||||
|
(Collision/Ballistic/Explosive/Laser/Energy), damageAmount, damageForce, surfaceNormal, impactPoint,
|
||||||
|
burstCount }`. Verified: `structure` climbs, vital zone destroyed → `*** TARGET DESTROYED ***`. [T2]
|
||||||
|
Two non-field fixes were needed: **message-handler chaining** (an unchained set drops TakeDamage
|
||||||
|
silently) + **entity validity** (`SetValidFlag()` on a manually-spawned entity) — see
|
||||||
|
[[reconstruction-gotchas]] §9. [T2]
|
||||||
|
|
||||||
|
## Death — the wreck STAYS (P5 CLOSED)
|
||||||
|
A killed mech does NOT disappear — BT death is a STATE transition
|
||||||
|
(`SetGraphicState(DestroyedGraphicState)` + death anim + effect/splash), the mech becomes a persistent
|
||||||
|
WRECK (RP analog: `VTV::DeathShutdown`). **NEVER issue `DestroyEntityMessage` on death** — the
|
||||||
|
teardown crash was an artifact of forcing a removal the original never does. The actual bug was ONE
|
||||||
|
thing: an EXPLICIT `JointedMover::~JointedMover()` in the reconstructed `~Mech` ran the whole base-dtor
|
||||||
|
chain TWICE (the dtor-epilogue rule, [[reconstruction-gotchas]] §7) → a double-free of `collisionLists`
|
||||||
|
+ the segment table. Removing it fixed BOTH the death-row crash AND app-exit crashes. [T2]
|
||||||
|
|
||||||
|
## Key Relationships
|
||||||
|
- Weapons/roster: [[subsystems]]. Aim source: [[locomotion]] (drive/facing). Effects: [[rendering]].
|
||||||
|
- P5 forensics: `docs/HARD_PROBLEMS.md`. Data: [[decomp-reference]] §4-5.
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
id: content-archives
|
||||||
|
title: "Content Archives — where the game data lives + loadable maps"
|
||||||
|
status: established
|
||||||
|
source_sections: "PROGRESS_LOG.md §4, §4a"
|
||||||
|
related_topics: [asset-formats, build-and-run, decomp-reference]
|
||||||
|
key_terms: [BTL4.RES, EGG, WinTesla, BT]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Content Archives
|
||||||
|
|
||||||
|
The deployed game data was **never in source control** — it lived on the pod machines, spread
|
||||||
|
across archives. The bt411 `content/` dir is the assembled runtime tree. Full detail:
|
||||||
|
`docs/PROGRESS_LOG.md §4, §4a`.
|
||||||
|
|
||||||
|
## The archives (historical provenance)
|
||||||
|
| Archive | What | Has |
|
||||||
|
|---|---|---|
|
||||||
|
| `410srczipped.zip` | Source code (36MB) | all `.cpp/.hpp`, geometry `.bgf`, materials `.bmf`, **animations `.ani`** (428). NO per-mech skins/skeletons. |
|
||||||
|
| `Tesla4NovellTechPC-*.zip` | pod technician/test PC (77MB) | `btrel410.exe`, Division VPX board diagnostics, shared generic textures, Novell DOS. |
|
||||||
|
| `Tesla4PodPCNovell-*.zip` | the pod GAME machine (92MB) | **the full deployed tree** — per-mech skeletons (`AVA.SKL`, 124), skins (`AVATAR.BSL`), 1143 models, 1624 material libs, textures. |
|
||||||
|
| `Elsewhen RP411 Source-*.zip` ⭐ | the **WinTesla Windows port** | the modernized engine (see [[wintesla-port]]). |
|
||||||
|
|
||||||
|
**Content root for the port** = the pod tree (skeletons/skins/geometry); **animations** come from
|
||||||
|
the source tree's `CONTENT/BT/ANIMS/`. All assembled into bt411 `content/`. [T2]
|
||||||
|
|
||||||
|
## The runtime archive
|
||||||
|
`content/BTL4.RES` (3.23 MB, byte-identical to `reference/decomp/BTL4.RES`) contains the built
|
||||||
|
subset: models, materials, skeletons, animation clips, damage-zone/subsystem records, and **8
|
||||||
|
loadable maps**. Resolved by name/ID via `FindResourceDescription` (a missing map name null-derefs
|
||||||
|
at `MISSION.cpp:355`). [T1]
|
||||||
|
|
||||||
|
## Loadable maps (the 8 in the RES)
|
||||||
|
`cavern` (default night cave, enclosed), `grass` (grassland), `rav` (ravine), `polar3`/`polar4`
|
||||||
|
(snow), `arena1`/`arena2`, `dbase` (desert). **Switch via a copied `.EGG`'s `map=` field** (+
|
||||||
|
`time=day` for outdoor). ⚠ Names carry the suffix (`arena1`/`polar3`/`rav`), NOT bare
|
||||||
|
`arena`/`polar`. `des`/`burnt`/`frstrm` are NOT in this RES (lab/other-build maps — `.map` source
|
||||||
|
exists in `CONTENT/BT/MAPS/`, would need compiling via the DOS `btl4tool.exe`). All 8 boot + render
|
||||||
|
+ fight clean (map sweep complete). [T2]
|
||||||
|
|
||||||
|
## The full content SOURCE tree
|
||||||
|
`CONTENT/BT/` (in `410srczipped`) is comprehensive: 17 `.map`, 636 `MODELS/`, 219 `SOLIDS/`, 331
|
||||||
|
`ANIMS/`, 1958 `VIDEO/` textures, 207 `AUDIO/`, 404 `GAUGE/`. The assets to build ANY map exist; the
|
||||||
|
runtime RES is the already-built subset. [T1]
|
||||||
|
|
||||||
|
## Key Relationships
|
||||||
|
- Formats: [[asset-formats]], [[bgf-format]].
|
||||||
|
- Loaded by: [[build-and-run]] (run cwd = content/), [[rendering]].
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
---
|
||||||
|
id: locomotion
|
||||||
|
title: "Locomotion — gait, the ground model, collision, shadow"
|
||||||
|
status: established
|
||||||
|
source_sections: "PROGRESS_LOG.md §7, §10 (gait/ground/collision/shadow); docs/P3_LOCOMOTION.md"
|
||||||
|
related_topics: [asset-formats, decomp-reference, reconstruction-gotchas, combat-damage, rendering]
|
||||||
|
key_terms: [RootTranslation, ground-model, SequenceController, MechControlsMapper, SLD, joint]
|
||||||
|
open_questions:
|
||||||
|
- "Authentic per-mech turn-rate constant (currently bring-up constant)"
|
||||||
|
- "Body-callback gimp handlers (states 16-19); airborne callbacks"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Locomotion
|
||||||
|
|
||||||
|
Walking, the gait controller, the ground/collision model, and the shadow. All DEFAULT-ON now
|
||||||
|
(`BT_GAIT_CUTOVER`, `BT_GAIT_SM`, `BT_COLLISION`, `BT_REAL_CONTROLS` — set `=0` to fall back). Full
|
||||||
|
detail: `docs/P3_LOCOMOTION.md` + `docs/PROGRESS_LOG.md §7, §10`.
|
||||||
|
|
||||||
|
## Walk speed is animation-driven (not physics)
|
||||||
|
Each `.ANI` has a `[RootTranslation]` where **`.z` = forward root SPEED** (world units/s). The
|
||||||
|
engine `AnimationInstance::Animate(dt, move_joints)` integrates `movement += Δt·rootTrans.z` and
|
||||||
|
moves the body that far → feet plant by construction (no stride constant; k=1). Idle anims have
|
||||||
|
z=0. The `.MOD` physics governed thrust VEHICLES, NOT walk gait. [T1]
|
||||||
|
|
||||||
|
## The two-channel gait (real controls)
|
||||||
|
The BT `SequenceController` (`seqctl.cpp`, from `SelectSequence@004277a8`+`Advance@0042790c`) drives
|
||||||
|
locomotion. Under `BT_REAL_CONTROLS` two channels run:
|
||||||
|
- **LEG channel** writes the skeleton JOINTS from the live demand (a local sim; its joint writes are
|
||||||
|
overwritten by the body).
|
||||||
|
- **BODY channel** is the DISPLAYED motion — travel comes from the SAME Advance that writes the
|
||||||
|
displayed joints (`move_joints=1`), so display == travel BY CONSTRUCTION (v4 fix — killed the
|
||||||
|
desync class). [T2]
|
||||||
|
The gait STATE MACHINE (`AdvanceBodyAnimation`, mech2.cpp) slews `bodyCycleSpeed` toward the demand
|
||||||
|
through walk/run caps (`LoadLocomotionClips`, mech3.cpp:326 — measures `walkStride`/`standSpeed` via
|
||||||
|
`legAnimation.SelectSequence`). Transition callbacks (`Mech::BodyClipFinished @0x4a6d8c`,
|
||||||
|
`LegClipFinished @0x4a6928` — PE-parsed jump tables) alternate walk 6↔7 / run 12↔13 + walk→run. [T2]
|
||||||
|
⚠ **Aliasing bug class (namedClip):** `namedClip[]`@0x5e0 == `animationClips[]`@0x5cc+0x14 (ONE array);
|
||||||
|
`gimpBaseClip`@0x64c == `animationClips[0x20]`; `bodyAnimationState`@0x728 == `bodyStateAlarm.level`.
|
||||||
|
Declared-separate members don't see each other's writes — alias them.
|
||||||
|
|
||||||
|
## The AUTHENTIC 1995 ground model (default-on, `BT_GROUND_REAL`)
|
||||||
|
Decoded by the 10-agent `ground-model-decode` workflow (binary `FUN_004a9b5c` = the master perf).
|
||||||
|
**NO GRAVITY anywhere** — accelerations zeroed every frame. Vertical position = an ABSOLUTE per-frame
|
||||||
|
ground SNAP:
|
||||||
|
1. ctor lifts `collisionTemplate->minY` by 5% of volume X-width (the probe height + wall-vs-floor separation).
|
||||||
|
2. per frame `MoveCollisionVolume()` re-finds the containing node in the zone's **BoundingBoxTree**
|
||||||
|
(BOXTREE.cpp — NOT a heightfield).
|
||||||
|
3. probe `q = origin + (0, lift, 0)` → `FindBoundingBoxUnder(q, &h)` (h = distance down to the highest
|
||||||
|
solid top; −1 = miss).
|
||||||
|
4. snap gate `h > 1e-4`: `origin.y -= (h − lift)` ⇒ y = surfaceY exactly. **MISS ⇒ NOTHING** (y holds,
|
||||||
|
no runaway). Up-slope bounded by the lift window (implicit step allowance); walk-offs drop instantly.
|
||||||
|
5. `GetCurrentCollisions` → `ProcessCollisionList` → `Mech::ProcessCollision` (@0x4abb40, vtbl+0x3c):
|
||||||
|
BoxedSolid resolver → StaticBounce → owner classification. Crushable icon (flags&0x8000) = damage
|
||||||
|
0.00123f sentinel = move stands; damage>0 = **FULL FRAME REJECTION** (walls block, never slide);
|
||||||
|
impactVel²>40 = crash-anim (SetLegAnimation(0x20)). [T2]
|
||||||
|
KEY: `FindSmallestNodeContainingColumn`/`FindBoundingBoxUnder` (`FUN_0040e36c`/`0040e5f0`, BOXTREE.CPP)
|
||||||
|
are ALIVE in the 2007 engine — the "heightfield" labels were wrong (caused two reverted attempts). A
|
||||||
|
master mech needs a **CollisionAssistant** (GetCurrentCollisions iterates it unchecked). BT solids
|
||||||
|
ship ZERO tiles (h never negative). [T2]
|
||||||
|
|
||||||
|
## Knockdown / crash + the desync/stutter fixes
|
||||||
|
Wall impacts iv²>40 (~6.3 u/s) bind the `bmp` stagger clip. Two documented bug fixes: (a) the
|
||||||
|
knockdown must stagger BOTH channels (`SetBodyAnimation(0x20)` + `SetLegAnimation(0x20)`) or display
|
||||||
|
+ travel split permanently (foot-slip); (b) a contact-hysteresis gate (0.4 s `gBlockCooldown`)
|
||||||
|
stops the knockdown re-firing on sustained/glancing contact. `localVelocity` is zeroed while crashed
|
||||||
|
(spec-faithful) so a knocked mech can't keep advancing. [T2]
|
||||||
|
|
||||||
|
## Controls (`BT_REAL_CONTROLS`, default-on)
|
||||||
|
`MechControlsMapper` (mechmppr.cpp @004afbe0; btl4mppr.cpp mappers) interprets input → `speedDemand`
|
||||||
|
/ `turnDemand`. ⚠ **WndProc NEVER receives WM_KEYUP** (the engine's per-frame reader `GetMessage`s
|
||||||
|
them out) → poll `GetAsyncKeyState` per frame, gated on foreground. Virtual controls: W/S sweep a
|
||||||
|
persistent throttle LEVER with a zero detent; A/D a momentary auto-centering stick. [T2]
|
||||||
|
|
||||||
|
## Shadow (default-on, `BT_SHADOW_TILT`)
|
||||||
|
BT 4.10's shadow is the flat `*_tshd.bgf` proxy drawn as blend-pass geometry, posed by `jointshadow`
|
||||||
|
(terrain tilt) + `jointtshadow` (torso twist). Terrain-tilt samples the collision surface gradient;
|
||||||
|
a depth-bias (`D3DRS_DEPTHBIAS`, decal-style) stops it z-fighting/vanishing on inclines. Polar maps
|
||||||
|
have STUB `*_tshd` (shadow authored-off on snow). Also the visual-ground conform (`btvisgnd.cpp`,
|
||||||
|
render-only lift to the visible terrain triangles — presentation only, never touches localOrigin). [T2]
|
||||||
|
|
||||||
|
## Key Relationships
|
||||||
|
- Detail: `docs/P3_LOCOMOTION.md`. Uses: [[asset-formats]] (SKL/ANI), [[decomp-reference]] (offsets).
|
||||||
|
- Feeds: [[combat-damage]] (collision→damage), [[rendering]] (shadow/visual-conform).
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
---
|
||||||
|
id: multiplayer
|
||||||
|
title: "Multiplayer — replication, netcode, the console"
|
||||||
|
status: provisional
|
||||||
|
source_sections: "PROGRESS_LOG.md §7 (Phase 7), §8 (P6); docs/HARD_PROBLEMS.md (P6)"
|
||||||
|
related_topics: [wintesla-port, combat-damage, locomotion, decomp-reference]
|
||||||
|
key_terms: [master, replicant, EGG, dead-reckon]
|
||||||
|
open_questions:
|
||||||
|
- "Cross-pod combat (damage to a replicant's master); replicant gait; pod-LAN config"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Multiplayer
|
||||||
|
|
||||||
|
Pod-to-pod networked play. The hard infrastructure exists (WinTesla replaced DOS `NETNUB` with a
|
||||||
|
WinSock2 TCP stack); the work is integrating + smoke-testing it for BT. Full detail:
|
||||||
|
`docs/PROGRESS_LOG.md §7, §8`; the problem writeup: `docs/HARD_PROBLEMS.md` (P6).
|
||||||
|
|
||||||
|
## The stack (already reconstructed)
|
||||||
|
- WinTesla replaced `NETNUB` with a **3446-line WinSock2 TCP** reimplementation (`MUNGA_L4/L4NET.CPP`,
|
||||||
|
`SOCK_STREAM`/ReliableMode, zero SOCK_DGRAM); the master/replicant distributed-sim core
|
||||||
|
(InterestManager/Entity/HostManager) is complete. So MP = integrate + smoke-test that stack (gated
|
||||||
|
on P3 locomotion + P5 entity lifecycle + subsystem WAVEs). The rumored `L4NET.CPP:1853` send-size
|
||||||
|
bug is DEAD CODE (`#if MESSAGE_BUFFERING`==0). [T2]
|
||||||
|
|
||||||
|
## Master / replicant model
|
||||||
|
- **[[master]]** = a locally-simulated entity (your mech + dummies); drives itself + EMITS update records.
|
||||||
|
- **[[replicant]]** = a peer's mech, a local proxy; DeadReckons toward the master's replicated position.
|
||||||
|
MUST `SetValidFlag()` at creation or every message defers forever ([[reconstruction-gotchas]] §9).
|
||||||
|
|
||||||
|
## How a session forms
|
||||||
|
`-net <port>` = networked mode (the pod listens on <port> for a CONSOLE, boots ConsoleOnly; game mesh
|
||||||
|
= <port>+1). Solo (no `-net`) never touches WinSock. The mission egg's `[pilots]` section IS the
|
||||||
|
roster (`pilot=ip[:port]` + a per-address page); each pod self-identifies by local-IP+game-port,
|
||||||
|
connects to earlier pilots, listens for later ones — a deterministic full mesh; the mission loads when
|
||||||
|
all connect. The **CONSOLE** (operator station — ABSENT from every archive) delivers the egg as chunked
|
||||||
|
`ReceiveEggFileMessage` packets + the LAUNCH `RunMissionMessage`; **`tools/btconsole.py` is our console
|
||||||
|
emulator** (⚠ `NotationFile::ReadText` expects NUL-SEPARATED lines). [T2]
|
||||||
|
|
||||||
|
## Verified milestones (one box, two instances)
|
||||||
|
- **P6 smoke test:** two instances share a world — console egg → TCP mesh → synchronized start →
|
||||||
|
bidirectional entity replication (each renders its own mech + the peer's REPLICANT), 0 crashes.
|
||||||
|
- **Movement replication:** A's mech WALKS on B's screen (the replicant tracks the master to ~1.5u via
|
||||||
|
dead-reckoning). The authentic mission-start ladder runs (CreatingMission→…→RunningMission via the
|
||||||
|
console LAUNCH). SIX bugs fixed to get here (dead-reckoner install, replicant-motion DeadReckon,
|
||||||
|
master emission threshold, emission gated on RunningMission, the console-must-LAUNCH fact, replicant
|
||||||
|
validity). [T2]
|
||||||
|
- **Wire-format bug class found+fixed:** MakeMessages replicate RAW over TCP, so string payload must be
|
||||||
|
INLINE (`char[N]` at the binary offsets), not a `const char*` pointer (garbage cross-pod). Check
|
||||||
|
EVERY MakeMessage for pointer payloads. [T2]
|
||||||
|
|
||||||
|
## Debug tooling (`BT_NET_TRACE`, permanent)
|
||||||
|
`[net-tx]/[net-rx]` (L4NET), `[net-upd]` (update lookup), `[upd-repl]`, `[ent-exec]` (state ladder).
|
||||||
|
Per-instance: `BT_LOG=<file>` + `BT_AFFINITY=<mask>` (CPU pin). Update stream ≈ 60 Hz × 144-byte
|
||||||
|
records per moving master. [T2]
|
||||||
|
|
||||||
|
## Remaining (P6 phase 4 / Phase 7)
|
||||||
|
Cross-pod COMBAT (target a replicant + route damage to the owning master — Entity::Dispatch already
|
||||||
|
reroutes); interactive 2-window driving; replicant GAIT animation (derive from replicated velocity);
|
||||||
|
the pod-LAN config (real IPs, bare-IP pilot entries). See [[open-questions]]. [T3]
|
||||||
|
|
||||||
|
## Key Relationships
|
||||||
|
- Base: [[wintesla-port]] (L4NET). Depends on: [[locomotion]] (update writer), [[combat-damage]]
|
||||||
|
(entity lifecycle). Detail: `docs/HARD_PROBLEMS.md` (P6).
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
id: pod-hardware
|
||||||
|
title: "Pod Hardware — the fixed target (7 monitors, D3D9, RIO)"
|
||||||
|
status: established
|
||||||
|
source_sections: "PROGRESS_LOG.md §3; the PLATFORM PROFILE + GAUGE DEV-COMPOSITE notes"
|
||||||
|
related_topics: [gauges-hud, rendering, project-overview]
|
||||||
|
key_terms: [pod, RIO, MFD, IG-board, SVGA16]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Pod Hardware (the fixed target)
|
||||||
|
|
||||||
|
The port must run on (a) a dev box and (b) the fixed arcade pod. The pod is a hard constraint that
|
||||||
|
bounds the graphics API. Full detail: `docs/PROGRESS_LOG.md §3`.
|
||||||
|
|
||||||
|
## Displays
|
||||||
|
- **2 video cards → 7 monitors:** main 3D view **800×600**; radar **640×480**; five monochrome MFDs
|
||||||
|
driven as one **1280×480** horizontally-spanned surface. [T1]
|
||||||
|
- Requires **old NVIDIA drivers** for the MFD horizontal spanning — a hard constraint that bounds
|
||||||
|
the graphics API → **Target Direct3D 9** (lowest common denominator that runs on old-driver pods
|
||||||
|
AND modern cards; one codepath). NOT Vulkan/D3D11+. CUDA is irrelevant (compute, not display). [T1]
|
||||||
|
|
||||||
|
## Cockpit I/O (RIO)
|
||||||
|
Joystick X/Y, throttle, pedals, buttons over **serial COM** (`L4RIO`, `L4SERIAL`). Must be remapped
|
||||||
|
to keyboard/gamepad on a dev box; real wiring is a pod bring-up task (Phase 8). The engine is a PUSH
|
||||||
|
model — `LBE4ControlsManager` groups are fed by all devices (RIO on the pod, DirectInput on dev);
|
||||||
|
the `MechControlsMapper` interprets them ([[locomotion]]). [T2]
|
||||||
|
|
||||||
|
## Multi-surface gauge path (intact, pod-only by default)
|
||||||
|
The pod multi-surface path EXISTS and is intact: `DPLRenderer::FindBestAdapterIndices` (multi-
|
||||||
|
adapter selector, honors PRIMGAUGE/SECGAUGE/MFDGAUGE/SPANDISABLE), `SVGA16::BuildWindows` (a
|
||||||
|
fullscreen D3D device + window per gauge/MFD surface, MFD span = width×2), `L4GaugeRenderer` (gated
|
||||||
|
on `L4GAUGE`). `content/SETENV.BAT` is the authentic pod env preset (L4CONTROLS=RIO,KEYBOARD;
|
||||||
|
L4GAUGE=640x480x16; L4PLASMA=com2). [T2]
|
||||||
|
|
||||||
|
## Platform profile switch
|
||||||
|
`-platform pod|dev` (or `BT_PLATFORM` env; default DEV) selects a runtime env preset WITHOUT forking
|
||||||
|
the codepath. **DEV** = keyboard + single 800×600 window. **POD** = RIO input (keyboard fallback off
|
||||||
|
serial) + multi-surface gauges (from SETENV.BAT/L4GAUGE on real hardware). `-platform pod` does NOT
|
||||||
|
auto-enable L4GAUGE (each surface needs its own fullscreen device on the pod's 2 cards). Off-pod, the
|
||||||
|
6 MFD surfaces render in a dev window via `BT_DEV_GAUGES` ([[gauges-hud]]). [T2]
|
||||||
|
|
||||||
|
## MFD surface model
|
||||||
|
All cockpit surfaces are bit-plane MASKS over ONE shared `SVGA16` pixelBuffer: `sec`=palette low
|
||||||
|
byte; `Heat`=0x4000, `Mfd2`=0x0400, `Comm`=0x8000, `Mfd1`=0x0100, `Mfd3`=0x1000; `Eng1-3` =
|
||||||
|
engineering-mode alt planes; `overlay`=0x00C0 (shares the sec surface). See [[gauges-hud]]. [T2]
|
||||||
|
|
||||||
|
## Key Relationships
|
||||||
|
- Renders: [[gauges-hud]] (the MFD surfaces), [[rendering]] (the main 3D view).
|
||||||
|
- Input: [[locomotion]] (the mapper).
|
||||||
|
- Bring-up: Phase 8 (get pod specifics from Nick — [[open-questions]]).
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
id: project-overview
|
||||||
|
title: "Project Overview — what this is, people, goals"
|
||||||
|
status: established
|
||||||
|
source_sections: "PROGRESS_LOG.md §1-3; README.md"
|
||||||
|
related_topics: [pod-hardware, wintesla-port, source-completeness, build-and-run]
|
||||||
|
key_terms: [BT, RP, MUNGA, Tesla-platform, WinTesla, pod]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Project Overview
|
||||||
|
|
||||||
|
Port VWE's arcade **pod** games (Tesla platform, release 4.10, ~1995-96) to modern Windows and,
|
||||||
|
ultimately, the actual arcade pod hardware. Primary target: **BattleTech (BT)**; **Red Planet
|
||||||
|
(RP)** shares the engine. Full detail: `docs/PROGRESS_LOG.md §1-3`.
|
||||||
|
|
||||||
|
## The stack
|
||||||
|
- Two games, one engine: BT + RP. Engine = **MUNGA**; HAL = **L4** (`MUNGA_L4`). Game logic =
|
||||||
|
`CODE/BT`, `CODE/RP`. [T1]
|
||||||
|
- Original platform: **32-bit DOS** under a **DPMI32** extender, Borland C++ 5 + TASM32, on Novell
|
||||||
|
DOS pods. 3D by a proprietary Division **IG image-generator board** (dual EISA), driven by the
|
||||||
|
closed **`libDPL.lib`** (binary + VREND microcode). [T1]
|
||||||
|
- **The port replaces the IG board/libDPL** with a `dpl_*` → Direct3D9 shim (done in WinTesla as
|
||||||
|
L4D3D). Pods now run Windows 10 + a Voodoo/Glide wrapper. BT was abandoned (MechWarrior 4 runs on
|
||||||
|
the current pods); this project revives BT. [T1]
|
||||||
|
|
||||||
|
## People / goals
|
||||||
|
- **arcattack** (user): doing the port. NVIDIA 3060 + A6000 dev GPUs.
|
||||||
|
- **Nick:** owns the pods + the software license (porting is authorized). Runs the pod hardware. The
|
||||||
|
gating dependency — the missing BT source likely lives on his backup drives (see [[source-completeness]]).
|
||||||
|
- **Goal:** run on (a) a modern dev box and (b) the fixed pod hardware. Multiplayer (pod-to-pod) wanted.
|
||||||
|
|
||||||
|
## Repo orientation (bt411)
|
||||||
|
This is the **clean standalone bt411 repo**. `README.md` + the top-level `CMakeLists.txt` are the
|
||||||
|
build of record. Tree: `engine/ game/ content/ docs/ reference/ tools/ context/`. Historical
|
||||||
|
sections in `docs/PROGRESS_LOG.md` cite old `C:/git/nick-games/...` paths — treat as provenance;
|
||||||
|
they map into this repo (reconstructed BT → `game/reconstructed/`, engine → `engine/MUNGA{,_L4}/`,
|
||||||
|
content → `content/`, raw decomp → `reference/decomp/`). [T2]
|
||||||
|
|
||||||
|
## Current state (2026-07)
|
||||||
|
`btl4.exe` boots, renders the world + a skinned mech, and runs a **drive → animate → target → fire
|
||||||
|
→ damage → destroy** single-player loop. The engine/renderer/HAL/audio are done (WinTesla); the
|
||||||
|
active work is reconstructing each BT subsystem's authentic behavior from the binary. The **gauge
|
||||||
|
system is complete** ([[gauges-hud]]). [T2]
|
||||||
|
|
||||||
|
## Key Relationships
|
||||||
|
- Full detail: `docs/PROGRESS_LOG.md`.
|
||||||
|
- Enables: everything — see [[wintesla-port]] (the base), [[source-completeness]] (the gap),
|
||||||
|
[[build-and-run]] (how to build).
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
---
|
||||||
|
id: reconstruction-method
|
||||||
|
title: "Reconstruction Method — the loop, the no-stand-ins rule, decomp technique"
|
||||||
|
status: established
|
||||||
|
source_sections: "PROGRESS_LOG.md §10b, §5a (decompilation), §10c"
|
||||||
|
related_topics: [reconstruction-gotchas, decomp-reference, source-completeness]
|
||||||
|
key_terms: [decomp, oracle, bridge, BTL4OPT, WinTesla]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Reconstruction Method
|
||||||
|
|
||||||
|
How the missing BT game logic is rebuilt from the binary. The governing rule: **no stand-ins.**
|
||||||
|
Full detail: `docs/PROGRESS_LOG.md §10b`; the systemic bug classes are [[reconstruction-gotchas]].
|
||||||
|
|
||||||
|
## The loop (per feature)
|
||||||
|
1. Read the RAW decomp `reference/decomp/all/part_*.c` for the relevant `FUN_xxxx`.
|
||||||
|
2. Map `FUN_`/`DAT_`/`this+0xNN` to engine symbols using: the BT headers + the WinTesla MUNGA
|
||||||
|
source + `game/reconstructed/CLASSMAP.md` + **RP's parallel code** (VTV≈mech, WEAPSYS≈weapons).
|
||||||
|
3. Write the **real** reconstruction into `game/reconstructed/*.cpp`.
|
||||||
|
4. Build; run env-gated; read `btl4.log` (grep `[anim]/[drive]/[target]/[fire]/[damage]` markers).
|
||||||
|
5. cdb on any crash. `static_assert`-lock the layout against the binary's offsets. [T2]
|
||||||
|
|
||||||
|
## RULE: no stand-ins
|
||||||
|
The full game logic IS in the pseudocode (the binary ran the game); a "gap" is a reconstruction
|
||||||
|
stub not yet filled, not a hole in the original. **Never write stand-in/placeholder logic for an
|
||||||
|
apparent gap — read the decomp.** (User: "there are no gaps, just work to be done.") Bring-up
|
||||||
|
scaffolding (env-var paths, explosion-for-beam, a player-gated drive) is clearly MARKED and meant
|
||||||
|
to be REPLACED by the real reconstructed system, never to substitute for reading the decomp. [T2]
|
||||||
|
|
||||||
|
## Decompilation stack (Plan B — in progress)
|
||||||
|
- **Ghidra 12.1.2 + JDK21** installed under `tools/` (portable). `ExportBTSource.java` run headless
|
||||||
|
on `BTL4OPT.EXE` → real decompiled C in `reference/decomp/recovered/`. The pass is
|
||||||
|
**assert-anchored** (few funcs/file — anchored on the asserts that carry source paths). [T2]
|
||||||
|
- For a `FUN_` the assert-anchored exporter skipped: **`tools/disas2.py <VA> [len]`** (capstone
|
||||||
|
disassembly of `BTL4OPT.EXE` — recovers x87 math Ghidra drops, folds known calls + float
|
||||||
|
constants). Or `DecompVSS.java` (headless address-list decompiler). [T2]
|
||||||
|
- **Resolving a `.data` fn-pointer** (a `PTR_LAB_xxxx` callback/vtable slot the decomp didn't
|
||||||
|
export): PE-parse the DWORD at its VA, then capstone-disassemble the target. Used for the gait
|
||||||
|
callbacks (`@0x4a6d8c`), the valve handler (`@0x4ae464`), gauge widget slots. [T2]
|
||||||
|
- **`.data` float constants** are the biggest "couldn't recover" bucket (tuning values); read them
|
||||||
|
as the x87 80-bit `float10` the decomp uses, not 32-bit float (`scratchpad/rdtbyte.py`). [T2]
|
||||||
|
|
||||||
|
## Effort model (honest)
|
||||||
|
~0.5-1.5 hr/module → ~30-50 agent-hours + human review for ~40 modules, with a long tail (modules
|
||||||
|
with no surviving `.HPP` fragment, or split across decomp windows, cost more). Process upgrades:
|
||||||
|
per-class function lists (not address clusters), complete vtable rows, recover `.data` constants. [T3]
|
||||||
|
|
||||||
|
## Workflows (for scale)
|
||||||
|
For exhaustive multi-function decomp analysis, fan out a **read-only Workflow** (understanding
|
||||||
|
phase — one agent per function/class produces an offset map + findings, + an adversarial verify),
|
||||||
|
THEN implement hands-on so each change is visible + consistent. This is the §10c pattern; used for
|
||||||
|
the ground-model decode (10 agents), the alarm-unification (8), the gauge-widget decode (6). [T2]
|
||||||
|
|
||||||
|
## Key Relationships
|
||||||
|
- Bug classes: [[reconstruction-gotchas]] (check FIRST). Reference data: [[decomp-reference]].
|
||||||
|
- Why it's needed: [[source-completeness]].
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
---
|
||||||
|
id: rendering
|
||||||
|
title: "Rendering — the D3D9 pipeline, materials, sky, beams, performance"
|
||||||
|
status: established
|
||||||
|
source_sections: "PROGRESS_LOG.md §10 (render-fidelity notes); docs/BGF_FORMAT.md"
|
||||||
|
related_topics: [bgf-format, asset-formats, wintesla-port, locomotion, pod-hardware]
|
||||||
|
key_terms: [L4D3D, material-ramp, BSL, LOD, DPL, beam]
|
||||||
|
open_questions:
|
||||||
|
- "Replacement-LOD for NON-additive multi-LOD models; day/night material path priority (BT_MATPRI)"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Rendering
|
||||||
|
|
||||||
|
The port renders via the WinTesla `L4D3D`/`L4VIDEO` Direct3D9 path ([[wintesla-port]]), carrying our
|
||||||
|
BT loader (`bgfload.cpp`) + fidelity work. The goal: match the authentic pod footage. Geometry/LOD
|
||||||
|
mechanics are in [[bgf-format]]; this topic covers the D3D9 draw path + the fidelity fixes. Full
|
||||||
|
detail: `docs/PROGRESS_LOG.md §10`.
|
||||||
|
|
||||||
|
## The IG-board shading model (from libDPL DPLTYPES.H)
|
||||||
|
Shading is selected PER-GEOMETRY by vertex type: **no-normal geometry** (terrain/mesas/sky/buildings/
|
||||||
|
mech — WHITE baked verts) is UNLIT, colorized by the material's 2-endpoint **[[material-ramp]]**
|
||||||
|
(`dpl_SetMaterialRamp` — texture luminance indexes a low→high gradient); **normal-bearing geometry**
|
||||||
|
(~150 vehicle/missile files) is LIT by the map light. Applying the ramp to lit geometry = the "dusty
|
||||||
|
white blobs" bug (gate on `hasNormals`). [T2]
|
||||||
|
|
||||||
|
## The big render-fidelity fixes (dbase/desert matched to pod footage)
|
||||||
|
- **Geometry:** CONN is a flat trilist (not fan); CONN+PCONN coexist — see [[bgf-format]]. Malformed
|
||||||
|
fan-garbage was the "faceted mountains" bug. [T2]
|
||||||
|
- **RAMP colorize** (default-on `BT_RAMP`) + **day/night material priority** (default-on `BT_MATPRI` for
|
||||||
|
the ramps): the `mat\day` libs carry the warm ramps; the generic GEO libs are gray. Terrain/mesas
|
||||||
|
render warm tan; the sky's `dsky_mtl` ramp (0→0.99) gives bright clouds. `hasNormals` gate keeps
|
||||||
|
vehicles LIT (colored trucks). Ramp tint rule: explicit diffuse + NEUTRAL ramp ⇒ tint; coloured ramp
|
||||||
|
⇒ white. [T2]
|
||||||
|
- **Sky:** a `*sky.bgf` dome/plane drawn in a dedicated fullbright **PASS_SKY** (LIGHTING off, extended
|
||||||
|
far=9000 + world fog); tagged `drawAsSky` at load (the `.x` sidecar path doesn't exist). [T2]
|
||||||
|
- **The env pipeline** (`DPLReadEnvironment`→`DPLReadINIPage`, reads **`content/BTDPL.INI`** via
|
||||||
|
`L4DPLCFG`): resolves map+time → clip/fog/ambient + a directional sun. Bring-up stubs
|
||||||
|
(EnsureValidProjection / per-frame ambient / uninit cloud colours) had clobbered it — now they
|
||||||
|
RE-ASSERT the env values. Debug: copy BTDPL.INI → *DBG.INI with `debug=True`. [T2]
|
||||||
|
|
||||||
|
## Backface culling (default CW, `BT_CULL`)
|
||||||
|
The bring-up `D3DCULL_NONE` drew interior/back faces (the "dark wedge" shapes = mesh insides). CW is
|
||||||
|
correct for the BGF quad triangulation `[a b c][a c d]`. [T2]
|
||||||
|
|
||||||
|
## LODs (default: shipping-engine first-LOD + ADDITIVE_LODS; `BT_ADDLOD`/`BT_LOD_SCALE`/`BT_PUNCH`)
|
||||||
|
See [[bgf-format]] for the √3 range decode, the ADDITIVE_LODS cumulative rule, PUNCH cutouts, and the
|
||||||
|
coplanar-shell submission-order depth bias. Replacement LOD selection for NON-additive multi-LOD
|
||||||
|
models is deferred (needs the board's hot-spot semantics). [T2]
|
||||||
|
|
||||||
|
## Mech skins (BSL bit-slice — the rainbow-mech fix)
|
||||||
|
`.BSL` is a bit-sliced container (6 grayscale sub-images per file); the BMF `0x18 BITSLICE` tag picks
|
||||||
|
the slice. Reading it as RGB overlaid 2-3 gray sheets = the "rainbow/graffiti" bug. Fixed:
|
||||||
|
`image.cpp decodeBSL` slice-decodes; `bgfload` threads the 0x18 channel; a cross-library RAMP registry
|
||||||
|
resolves the mech-skin `softer` ramp (defined in OTHER libs). Every mech renders authentic gray-metal
|
||||||
|
paint now. See [[asset-formats]]. [T2]
|
||||||
|
|
||||||
|
## Weapon beams (the unported dpl_* layer)
|
||||||
|
The `dpl_*` beam renderable was never ported → beams are drawn by `BTPushBeam`/`BTDrawBeams`
|
||||||
|
(L4VIDEO). The real look is the `ermlaser.bgf` TUBE (native Z 0..−2000, UVs tiled) with the scrolling
|
||||||
|
`bexp` grit texture (from the `.VMF` `SCROLL`). ER lasers red, PPC blue, standard yellow. The beam
|
||||||
|
origin re-anchors to the live muzzle every frame (can't run out from under your own beam). See
|
||||||
|
[[combat-damage]] for fire-rate/muzzle. [T2]
|
||||||
|
|
||||||
|
## Performance — the 10fps→60fps fix (~50-80× render speedup)
|
||||||
|
The 10fps baseline had THREE stacked causes, all fixed (engine tree): (1) **`D3DCREATE_SOFTWARE_
|
||||||
|
VERTEXPROCESSING`** — every vertex transformed+lit on the CPU → HARDWARE first; (2) D3DX `DrawSubset`
|
||||||
|
attribute-table scan (our double-sided tris broke OptimizeInplace) → explicit `SetAttributeTable` +
|
||||||
|
direct `DrawIndexedPrimitive`; (3) no frustum culling → bounding-sphere culling per frame. ⚠ LESSON:
|
||||||
|
the user plays the DEBUG build, but the 10fps was NOT the debug build (RelWithDebInfo identical) —
|
||||||
|
split draw-vs-present + check the DEVICE flags before blaming the compiler. Diagnostics kept:
|
||||||
|
`[spike]/[loadobj]/[rstat]`. [T2]
|
||||||
|
|
||||||
|
## Key Relationships
|
||||||
|
- Geometry/LOD: [[bgf-format]]. Base: [[wintesla-port]] (L4D3D). Shadow/visual-conform: [[locomotion]].
|
||||||
|
- Renders on: [[pod-hardware]] (main 3D view).
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
---
|
||||||
|
id: source-completeness
|
||||||
|
title: "Source Completeness — the gating blocker + the decompilation route"
|
||||||
|
status: established
|
||||||
|
source_sections: "PROGRESS_LOG.md §5a; docs/BT_SOURCE_STATUS.md"
|
||||||
|
related_topics: [wintesla-port, reconstruction-method, decomp-reference, open-questions]
|
||||||
|
key_terms: [BTL4OPT, decomp, oracle, WinTesla, BT]
|
||||||
|
open_questions:
|
||||||
|
- "Get the missing BT .cpp from Nick (gating for Route A)"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Source Completeness — the gating blocker
|
||||||
|
|
||||||
|
The archive has the **engine and all headers, but NOT the BT game's implementation `.cpp`.**
|
||||||
|
This is THE gating fact that shapes the whole project. Full map: `docs/BT_SOURCE_STATUS.md`.
|
||||||
|
|
||||||
|
## What's present vs missing (verified across all 3 zips + the WinTesla archive)
|
||||||
|
- **MUNGA engine** (shared): ~183 `.cpp` — largely complete (renderer, MOVER/JMOVER, math, scene). [T1]
|
||||||
|
- **All BT game `.hpp` headers** present — every class/interface defined (the reconstruction's "answer key"). [T1]
|
||||||
|
- **BT game logic `.cpp`: MISSING** — `mech`, `mech2/3/4`, `mechsub`, `mechmppr`, `mechtech`, `heat`,
|
||||||
|
`sensor`, `gyro`, `torso`, `myomers`, `hud`, `mechweap`, `powersub`, `btplayer`, and the main app
|
||||||
|
`btl4` are all absent. Only ~10 BT `.cpp` survive (BTCNSL/BTMSSN/BTREG/BTSCNRL/BTTEAM/BTTOOL +
|
||||||
|
GAUSS/PPC + BTL4ARND/BTL4MODE). [T1]
|
||||||
|
- **RP can't backfill BT** — RP's game source is gutted the same way. The per-game source for BOTH
|
||||||
|
titles is missing; only shared *engine* game-logic survives. A complete repo likely exists
|
||||||
|
elsewhere (Nick). [T1]
|
||||||
|
|
||||||
|
## Consequence — routes forward
|
||||||
|
Route A (recompile original source) is **impossible as-is** — the code that IS the game isn't here.
|
||||||
|
Paths: (1) **get the missing `.cpp` from Nick** [gating, top priority]; (2) run `btrel410.exe` under
|
||||||
|
DOSBox by emulating the VPX/IG board (blocked — needs board emulation); (3) **decompile
|
||||||
|
`BTL4OPT.EXE` + reconstruct from headers** — the chosen route (in progress). [T2]
|
||||||
|
|
||||||
|
## The binary is a near-ideal decompile target
|
||||||
|
`btrel410.exe` is a PKZIP self-extractor; inside is **`BTL4OPT.EXE`** (1.24 MB, clean uncompressed
|
||||||
|
PE32, Phar Lap TNT). It retains **class names, `Class::Method` strings, and source-file paths in
|
||||||
|
asserts** (`d:\tesla_bt\bt\mech.cpp`) — confirming the missing modules ARE in the binary. No COFF
|
||||||
|
symbol table (naming comes from those strings). Extracted to `reference/decomp/`. [T1]
|
||||||
|
|
||||||
|
## Why reconstruction is viable (THREE anchors)
|
||||||
|
1. **All BT headers** = exact class layouts, vtable order, signatures (the answer key).
|
||||||
|
2. **The working WinTesla engine** to compile against ([[wintesla-port]]).
|
||||||
|
3. **Red Planet's parallel implementations** as a live reference (`VTV`≈mech, `VTVMPPR`≈mechmppr,
|
||||||
|
`WEAPSYS`≈mech weapons, `RP_L4` app≈BT app).
|
||||||
|
Plus `BTL4OPT.EXE` as a behavioral **oracle** (run under cdb / read the decomp). Result =
|
||||||
|
behavior-equivalent **reconstruction**, NOT original source. [T2]
|
||||||
|
|
||||||
|
## Key Relationships
|
||||||
|
- Feeds: [[reconstruction-method]] (the loop that fills the gap), [[decomp-reference]].
|
||||||
|
- Gated on: [[open-questions]] (get the source from Nick).
|
||||||
|
- Built atop: [[wintesla-port]].
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
---
|
||||||
|
id: subsystems
|
||||||
|
title: "Subsystems — the factory + the reconstruction waves"
|
||||||
|
status: established
|
||||||
|
source_sections: "PROGRESS_LOG.md §10d; docs/SUBSYS_PLAN.md; docs/VEHICLE_SUBSYSTEMS.md; docs/RESOURCE_AUDIT.md"
|
||||||
|
related_topics: [decomp-reference, reconstruction-gotchas, combat-damage, gauges-hud]
|
||||||
|
key_terms: [subsystem, factory, ClassID, subsystem-roster, PoweredSubsystem, bridge]
|
||||||
|
open_questions:
|
||||||
|
- "Gyroscope (0xBC4) integrator; Myomers authentic coupling; 0xBD3 message manager (WAVE 8)"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Subsystems
|
||||||
|
|
||||||
|
The mech's components (heat/power/weapons/sensor/gyro/torso/myomers/…), built by the Mech-ctor
|
||||||
|
factory. Full per-family plan: `docs/SUBSYS_PLAN.md`; the engineering-cluster panels:
|
||||||
|
`docs/VEHICLE_SUBSYSTEMS.md`; the layout audit: `docs/RESOURCE_AUDIT.md`. ClassID map:
|
||||||
|
[[decomp-reference]] §2.
|
||||||
|
|
||||||
|
## The factory
|
||||||
|
The Mech ctor switches on the resource ClassID to build each subsystem into the [[subsystem-roster]]
|
||||||
|
(`subsystemArray@0x128`). **The `case <Name>ClassID` LABELS are systematically MISLABELED** — the
|
||||||
|
real class is the `// FUN_004xxxxx` ctor-address comment reconciled via `CLASSMAP.md`, not the case
|
||||||
|
name (e.g. 0xBBE case="Sensor" is actually AggregateHeatSink). Each real class is wired via a
|
||||||
|
`Create<Class>Subsystem(Mech*,int,void*)` **bridge** in the class's own `.cpp` (do NOT `#include` the
|
||||||
|
real subsystem headers into mech.cpp — the local stubs collide). Keep the alloc SIZE + special-cache.
|
||||||
|
[T2]
|
||||||
|
|
||||||
|
## Class hierarchy (two branches, shared only at MechSubsystem)
|
||||||
|
- **Heat leaf:** `PoweredSubsystem : HeatSink : HeatableSubsystem : MechSubsystem`. Emitter/PPC/
|
||||||
|
Sensor/Myomers/ProjectileWeapon/MissileLauncher/GaussRifle chain here. Uses 0xC `SubsystemConnection`
|
||||||
|
+ 0x54 `GaugeAlarm54` (alarm level at +0x14). [T1]
|
||||||
|
- **Watcher branch:** `Torso/Gyroscope/Searchlight/ThermalSight/HUD : PowerWatcher : HeatWatcher :
|
||||||
|
MechSubsystem`; `AmmoBin : HeatWatcher`. Uses a 0xC `WatchedConnection` + 0x54 `WatcherGaugeAlarm`. [T1]
|
||||||
|
Making a base byte-exact GROWS every subclass — they must be re-based TOGETHER in one build, each
|
||||||
|
`static_assert`-locked (the P7 alarm-unification: HeatSink 0x1D0 → PoweredSubsystem 0x31C → MechWeapon
|
||||||
|
0x3F0 → Emitter 0x478). [T2]
|
||||||
|
|
||||||
|
## Reconstruction waves (state: ~18-19 of 20 factory cases wired to real classes)
|
||||||
|
- **WAVE 1** — HeatableSubsystem re-based onto MechSubsystem (de-shadow cascade).
|
||||||
|
- **WAVE 2** — heat family (Condenser/AggregateHeatSink/Reservoir) + HUD + MechTech (un-swap).
|
||||||
|
- **WAVE 3** — power bus (Generator/PoweredSubsystem) + Emitter/PPC fire-path (end-to-end fire; heat
|
||||||
|
conducts to the central sink via the linked-sink roster).
|
||||||
|
- **WAVE 4** — standalone readouts: Sensor/Searchlight/ThermalSight/AmmoBin (de-shim, gate fixes).
|
||||||
|
- **WAVE 5** — Torso (aim twist/elevation; joint-I/O reconstructed; the BLH record disables torso →
|
||||||
|
faithfully no visible twist).
|
||||||
|
- **WAVE 6** — Myomers (mover-coupled; structural un-stub is INERT — no live mover/heat coupling).
|
||||||
|
- **WAVE 7** — projectile/missile weapons (byte-exact; flying projectiles are a PORT reconstruction —
|
||||||
|
the 2007 Entity is 0x1BC vs the binary's 0x300, so raw base-offset reads fail).
|
||||||
|
- **DEFERRED** — Gyroscope 0xBC4 (NaN integrator), SubsystemMessageManager 0xBD3 (WAVE 8 — the valve/
|
||||||
|
message/advanced-damage hub @mech+0x190). [T2]
|
||||||
|
|
||||||
|
## The four systemic checks (every subsystem)
|
||||||
|
See [[reconstruction-gotchas]]: (1) shadowing (re-declared engine-base fields), (2) the `Wword` trap,
|
||||||
|
(3) message-handler chaining, (4) entity validity. Plus resource-struct layout (must mirror the class
|
||||||
|
hierarchy, byte-exact — the RESOURCE_AUDIT found 8 bugs) and object-layout (alias/phantom/interior
|
||||||
|
fields). A `+0x128`-style owner offset is the ROSTER, not the segment table (the heat-link type
|
||||||
|
confusion that caused a heap-corruption via OOB `ConductHeat` writes). [T2]
|
||||||
|
|
||||||
|
## Engineering cluster panels (`vehicleSubSystems`)
|
||||||
|
Not a widget — a per-subsystem FACTORY (`FUN_004cbaf0`) building a status CLUSTER onto one of 12 aux
|
||||||
|
MFD positions, dispatching on classID → HeatSinkCluster/MyomerCluster/EnergyWeaponCluster/
|
||||||
|
BallisticWeaponCluster. Reads `PoweredSubsystem::auxScreenNumber/Placement/Label` (res +0x104/8/C) via
|
||||||
|
the `BTGetSubsystemAuxScreen` bridge. See `docs/VEHICLE_SUBSYSTEMS.md` + [[gauges-hud]]. [T2]
|
||||||
|
|
||||||
|
## Key Relationships
|
||||||
|
- Data: [[decomp-reference]] (ClassIDs/hierarchy). Bugs: [[reconstruction-gotchas]].
|
||||||
|
- Feeds: [[combat-damage]] (weapons/damage), [[gauges-hud]] (attribute state).
|
||||||
|
- Plan: `docs/SUBSYS_PLAN.md`.
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
id: wintesla-port
|
||||||
|
title: "The WinTesla Windows Port — the base everything builds on"
|
||||||
|
status: established
|
||||||
|
source_sections: "PROGRESS_LOG.md §5b, §8"
|
||||||
|
related_topics: [source-completeness, rendering, build-and-run, multiplayer]
|
||||||
|
key_terms: [WinTesla, L4D3D, MUNGA, RP, DPL]
|
||||||
|
---
|
||||||
|
|
||||||
|
# The WinTesla Windows Port
|
||||||
|
|
||||||
|
The `Elsewhen RP411` archive is a **working Windows port of the MUNGA engine** (VS2005,
|
||||||
|
`WinTesla.sln`). It means the hardest infrastructure is **already solved** — the bt411 build sits
|
||||||
|
on top of it. Full detail: `docs/PROGRESS_LOG.md §5b, §8`.
|
||||||
|
|
||||||
|
## What WinTesla already did (do NOT rebuild)
|
||||||
|
- **Renderer bypass: DONE** — `MUNGA_L4/L4D3D.cpp` + `DXUtils` replace libDPL / the IG board with
|
||||||
|
Direct3D9. No `libdpl.lib` in the tree. (Our from-scratch `port/` D3D9 viewer is now a
|
||||||
|
reference/sandbox, not the path.) [T1]
|
||||||
|
- **Audio: DONE** — OpenAL + libsndfile replacing the HMI "SOS" engine. [T1]
|
||||||
|
- **L4 HAL on Windows + the VS build system: DONE.** [T1]
|
||||||
|
- **Red Planet game logic: COMPLETE & buildable** — `VTV/VTVMPPR/WEAPSYS/...`. RP is at/near playable. [T1]
|
||||||
|
- **Multiplayer: the DOS `NETNUB` driver is already replaced by a 3446-line WinSock2 TCP
|
||||||
|
reimplementation** (`MUNGA_L4/L4NET.CPP`) + the master/replicant distributed-sim core. See
|
||||||
|
[[multiplayer]]. [T1]
|
||||||
|
|
||||||
|
## What BT still needs (the only remaining work below the game layer is NONE)
|
||||||
|
Everything below the game layer — engine, renderer, audio, HAL, build — exists and is shared. The
|
||||||
|
ONLY thing BT needs is its **game-logic source**, which is gone (see [[source-completeness]]) → it
|
||||||
|
must be **reconstructed**, with the three anchors (BT headers + the WinTesla engine to compile
|
||||||
|
against + RP's parallel code + the binary oracle). [T2]
|
||||||
|
|
||||||
|
## Build implications
|
||||||
|
- `WinTesla.sln` is **VS2005** and hard-depends on the **legacy DirectX SDK (June 2010)** (`d3dx9`,
|
||||||
|
`dinput`, `dxerr` — all removed from the modern Windows SDK). The DXSDK is installed at
|
||||||
|
`C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\`. [T2]
|
||||||
|
- The shared engine builds GREEN to `munga_engine.lib` via CMake/MSVC Win32 (see [[build-and-run]]).
|
||||||
|
Recipe gotchas (include-path shadowing, the CAMMGR/time.h fixes, the ATL shim) are in
|
||||||
|
`docs/PROGRESS_LOG.md §8`. [T2]
|
||||||
|
|
||||||
|
## Roadmap position
|
||||||
|
Old plan ("build a libDPL shim + port the engine") is **superseded** — WinTesla did it. New plan:
|
||||||
|
reuse the WinTesla engine (shared) + reconstruct the BT game library (`BT410_L4` + game logic). The
|
||||||
|
from-scratch libDPL shim is no longer needed. Phases 4-8 (BT game layer + pod bring-up) still apply.
|
||||||
|
[T2]
|
||||||
|
|
||||||
|
## Key Relationships
|
||||||
|
- Solves: the infrastructure that [[source-completeness]] would otherwise require.
|
||||||
|
- Feeds: [[build-and-run]], [[rendering]], [[multiplayer]].
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -18,9 +18,14 @@ knowledge should load on-demand.
|
|||||||
- **Non-destructive:** the live `CLAUDE.md` is UNCHANGED until the migration is complete + approved.
|
- **Non-destructive:** the live `CLAUDE.md` is UNCHANGED until the migration is complete + approved.
|
||||||
The proposed router is `context/_ROUTER-DRAFT.md`.
|
The proposed router is `context/_ROUTER-DRAFT.md`.
|
||||||
|
|
||||||
## Status
|
## Status — ✅ COMPLETE (migration done, CLAUDE.md swapped)
|
||||||
|
|
||||||
### ✅ Foundation built (this pass)
|
The full migration landed: `CLAUDE.md` is now a **160-line router**; the complete 2236-line
|
||||||
|
monolith is preserved verbatim as **`docs/PROGRESS_LOG.md`** (byte-identical — zero context lost);
|
||||||
|
**18 context topic files** (1343 lines) digest every CLAUDE.md section; the knowledge graph
|
||||||
|
validates CLEAN (all `[[links]]` + quick-lookup + docs refs resolve; `scratchpad/checkctx.py`).
|
||||||
|
|
||||||
|
### ✅ Foundation built (first pass)
|
||||||
- `reference/glossary.yaml` — ~45 terms (platform/engine, formats, scene/entity, reconstruction).
|
- `reference/glossary.yaml` — ~45 terms (platform/engine, formats, scene/entity, reconstruction).
|
||||||
- `context/decomp-reference.md` — the quantitative hub: resource-type enum, ClassID map, mech
|
- `context/decomp-reference.md` — the quantitative hub: resource-type enum, ClassID map, mech
|
||||||
offsets, damage delivery, weapon constants, env gates, tools.
|
offsets, damage delivery, weapon constants, env gates, tools.
|
||||||
@@ -31,27 +36,24 @@ knowledge should load on-demand.
|
|||||||
- `context/_ROUTER-DRAFT.md` — the proposed slim CLAUDE.md (identity, protocols, quick-lookup,
|
- `context/_ROUTER-DRAFT.md` — the proposed slim CLAUDE.md (identity, protocols, quick-lookup,
|
||||||
evidence tiers, conventions, structure).
|
evidence tiers, conventions, structure).
|
||||||
|
|
||||||
### ☐ Remaining topic files (the next tranche — migrate from CLAUDE.md §/docs)
|
### ✅ Second pass — all topics migrated (§→topic)
|
||||||
| topic | source in CLAUDE.md / docs |
|
project-overview (§1-3), pod-hardware (§3), content-archives (§4/4a), asset-formats (§5),
|
||||||
|---|---|
|
source-completeness (§5a), wintesla-port (§5b/§8), build-and-run (§10a/10a-bis), reconstruction-method
|
||||||
| `project-overview.md` | §1-3 |
|
(§10b), locomotion (§7/§10), subsystems (§10d), combat-damage (§10c), rendering (§10 fidelity),
|
||||||
| `pod-hardware.md` | §3 |
|
multiplayer (§7/§8). Plus the first-pass hubs (decomp-reference, reconstruction-gotchas, bgf-format,
|
||||||
| `content-archives.md` | §4, §4a |
|
gauges-hud, open-questions). §6 (the standalone port/ D3D9 viewer sandbox) is noted in wintesla-port;
|
||||||
| `asset-formats.md` | §5, docs/ASSET_PIPELINE.md |
|
full detail in PROGRESS_LOG.
|
||||||
| `source-completeness.md` | §5a, docs/BT_SOURCE_STATUS.md |
|
|
||||||
| `wintesla-port.md` | §5b, §8 |
|
|
||||||
| `build-and-run.md` | §10a, §10a-bis |
|
|
||||||
| `reconstruction-method.md` | §10b |
|
|
||||||
| `locomotion.md` | §7, docs/P3_LOCOMOTION.md |
|
|
||||||
| `subsystems.md` | §10d, docs/SUBSYS_PLAN.md, docs/VEHICLE_SUBSYSTEMS.md |
|
|
||||||
| `combat-damage.md` | §10c, docs/HARD_PROBLEMS.md |
|
|
||||||
| `rendering.md` | §10 render-fidelity notes (large) |
|
|
||||||
| `multiplayer.md` | §7 Phase 7, §8 P6 |
|
|
||||||
|
|
||||||
### ☐ Swap (after review + full migration)
|
### ✅ Swap done
|
||||||
1. Move the current `CLAUDE.md` deep content into the topic files above.
|
1. `docs/PROGRESS_LOG.md` = the verbatim old CLAUDE.md (lossless safety net + the "full detail" quick-lookup row).
|
||||||
2. Replace `CLAUDE.md` with `context/_ROUTER-DRAFT.md` content (preserving the git history of the old).
|
2. `CLAUDE.md` = the router (git history preserves the old monolith too).
|
||||||
3. Verify: a fresh session reads the router → quick-lookup → topic files load on demand.
|
3. Verified: knowledge graph clean; a fresh session reads the router → quick-lookup → topics on demand.
|
||||||
|
|
||||||
|
### Maintenance going forward
|
||||||
|
New durable finding → the relevant `context/*.md` (+ the `docs/` ledger for exhaustive detail), with
|
||||||
|
an evidence tier. New topic → a `context/*.md` + a quick-lookup row + cross-refs. The `docs/*.md`
|
||||||
|
ledgers (incl. PROGRESS_LOG) remain the detailed logs; `context/*.md` are the curated digests.
|
||||||
|
Re-run `scratchpad/checkctx.py` to validate links after edits.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
- The router keeps the build one-liners + the "keep current" mandate so active port work is
|
- The router keeps the build one-liners + the "keep current" mandate so active port work is
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import re, glob, os, yaml
|
||||||
|
sys_ids = set(os.path.basename(f)[:-3] for f in glob.glob('context/*.md'))
|
||||||
|
# glossary top-level keys = valid link targets too
|
||||||
|
try:
|
||||||
|
gl = yaml.safe_load(open('reference/glossary.yaml',encoding='utf-8'))
|
||||||
|
gloss_keys = set(gl.keys()) if isinstance(gl, dict) else set()
|
||||||
|
except Exception as e:
|
||||||
|
gloss_keys = set(re.findall(r'^([A-Za-z][A-Za-z0-9_-]*):', open('reference/glossary.yaml',encoding='utf-8').read(), re.M))
|
||||||
|
valid = sys_ids | gloss_keys
|
||||||
|
# template placeholders that are documentation, not real links:
|
||||||
|
IGNORE = {'other-topic','other-topic-a','other-topic-b','topic-id','name'}
|
||||||
|
print("topic ids:", len(sys_ids), "| glossary keys:", len(gloss_keys))
|
||||||
|
bad=0
|
||||||
|
for f in glob.glob('context/*.md') + ['CLAUDE.md']:
|
||||||
|
txt=open(f,encoding='utf-8',errors='replace').read()
|
||||||
|
for m in re.findall(r'\[\[([a-z0-9-]+)\]\]', txt):
|
||||||
|
if m not in valid and m not in IGNORE:
|
||||||
|
print(f" UNRESOLVED [[{m}]] in {f}"); bad+=1
|
||||||
|
cl=open('CLAUDE.md',encoding='utf-8',errors='replace').read()
|
||||||
|
for m in re.findall(r'`(context/[a-z0-9-]+\.md)`', cl):
|
||||||
|
if not os.path.exists(m): print(f" MISSING quick-lookup {m}"); bad+=1
|
||||||
|
for m in re.findall(r'`(docs/[A-Z0-9_]+\.md)`', cl):
|
||||||
|
if not os.path.exists(m): print(f" MISSING docs ref {m}"); bad+=1
|
||||||
|
print("RESULT:", "CLEAN" if bad==0 else f"{bad} unresolved")
|
||||||
Reference in New Issue
Block a user