context-system: bootstrap the progressive-context expert knowledge base (foundation)
Adapt the spark-lesson / expert-seed.md progressive-context pattern to the bt411 port:
a thin router + context/*.md knowledge graph so the 2236-line CLAUDE.md's deep knowledge
loads on-demand instead of every session.
NON-DESTRUCTIVE foundation pass (live CLAUDE.md unchanged; proposed router is a draft):
- reference/glossary.yaml -- ~45 terms (engine/formats/scene/reconstruction)
- context/decomp-reference.md -- the quantitative hub: resource types, ClassID map,
mech offsets, damage delivery, weapon constants, env gates, tools
- context/reconstruction-gotchas.md -- the 12 systemic bug classes (conventions/DO-NOT hub)
- context/bgf-format.md -- geometry format + CONN/PCONN + LOD-sqrt3 + ramp shading
- context/gauges-hud.md -- the gauge/MFD system (the just-completed wave)
- context/open-questions.md -- deferred systems + get-from-Nick
- context/_ROUTER-DRAFT.md -- the proposed slim CLAUDE.md (identity, protocols,
quick-lookup, evidence tiers T0-T4, conventions, structure)
- phases/phase-01-context-restructure.md -- design + migration plan + status
docs/*.md ledgers stay as the DETAILED logs; context/*.md are the curated digests that
route into them. Remaining: ~10 topic files (project-overview, subsystems, combat-damage,
rendering, locomotion, wintesla-port, build-and-run, ...) then the CLAUDE.md swap.
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
401b290949
commit
1cd57ade85
@@ -0,0 +1,153 @@
|
||||
# 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,64 @@
|
||||
---
|
||||
id: bgf-format
|
||||
title: "BGF Model Format (DIV-BIZ2 geometry) + the render gotchas"
|
||||
status: established
|
||||
source_sections: "docs/BGF_FORMAT.md (full spec); CLAUDE.md §5, §10 render-fidelity notes"
|
||||
related_topics: [asset-formats, rendering, decomp-reference]
|
||||
key_terms: [BGF, DIV-BIZ2, PMESH, CONN, PCONN, LOD, material-ramp, SV_SPECIAL]
|
||||
open_questions:
|
||||
- "Replacement-LOD selection for NON-additive multi-LOD models (hot-spot/reference semantics)"
|
||||
---
|
||||
|
||||
# BGF Model Format
|
||||
|
||||
`.BGF` = **`DIV-BIZ2`** little-endian nested Tag-Length-Value geometry. Full byte-level parsing
|
||||
spec (verified against all 1275 content `.BGF` files) lives in **`docs/BGF_FORMAT.md`** — this
|
||||
topic captures the structure + the hard-won gotchas that cost real debugging. Loader of record:
|
||||
`engine/MUNGA_L4/bgfload.cpp` (game-callable via `LoadBgfFile`); standalone reference reader
|
||||
`DivLoader/VGCDivLoader.cpp`. Tag names: `dsys/PFBIZTAG.H`.
|
||||
|
||||
## Structure
|
||||
`OBJECT → LOD → PATCH (geogroup) → PMESH`. Interleaved float32 verts; **QUAD faces (must
|
||||
triangulate `[a b c][a c d]`)**; materials referenced by `library:material` name (resolved
|
||||
against the map's `.BMF` libs). Damage/articulation are **name-driven via `SV_SPECIAL` (0x2037)
|
||||
`dz_*` tokens**, not stored geometry. [T1]
|
||||
|
||||
## Indices — the CONN/PCONN story (two corrections)
|
||||
A PMESH carries faces in `CONNECTION_LIST` (0x0047) and/or `PCONN_LIST`:
|
||||
- `CONNECTION_LIST` is a **FLAT TRIANGLE LIST** (3 indices/face) — NOT "one polygon, fan-triangulate"
|
||||
(the original doc was wrong; fan-triangulating turned buttec's 115 tris into 341 fan-garbage).
|
||||
Corpus-verified: ALL 3488 CONN chunks have n%3==0. [T2]
|
||||
- **CONN and PCONN COEXIST** — a pmesh can carry quads/hexes in PCONN PLUS loose triangles in
|
||||
CONN. **370 of 841 GEO models** had mixed pmeshes silently dropping their CONN triangles until
|
||||
both were processed always (the "turret base missing panels" bug). `ppf=6` hexagons exist. [T2]
|
||||
|
||||
## LODs — the √3 decode
|
||||
Each LOD chunk carries a `0x2046` header = **[near..far) viewing band**. The board selected the
|
||||
LOD whose band contains the camera distance.
|
||||
- **√3 range decode:** every stored band value is the authored euclidean distance ÷ √3 (92% of
|
||||
the corpus is exactly nice-number/√3; hand-conversion typos prove it). Multiply parsed bands by
|
||||
`√3` (`kLodRangeScale=1.7320508`). [T2]
|
||||
- **Metric = the instance-transformed OBJECT ORIGIN distance**, ONE shared `d` for all LODs of an
|
||||
object (a bounding-sphere-SURFACE metric desyncs sibling bands — wrong). [T2]
|
||||
- **ADDITIVE_LODS** (object-level `SV_SPECIAL` token, 135 pod BGFs — all arena structures): at eye
|
||||
distance d, EVERY LOD with `d < OutDist` draws (near detail ADDS onto coarser massing). Non-additive
|
||||
keep first-LOD-only (the 2007 engine's own behavior). [T2]
|
||||
- **PUNCH** (patch-level `SV_SPECIAL`): cutout geogroups — black texels = HOLES (alpha-test in the
|
||||
opaque pass). Coplanar shells inside one LOD z-fight → depth-bias by submission ordinal. [T2]
|
||||
|
||||
## Shading model (from libDPL DPLTYPES.H)
|
||||
Shading is selected PER-GEOMETRY by vertex type:
|
||||
- **No-normal geometry** (XYZ_UV / XYZ_RGBA_UV: mesas/ground/sky/buildings/mech, WHITE baked
|
||||
verts) is UNLIT, colorized by the material's 2-endpoint **RAMP** (see [[material-ramp]]).
|
||||
- **Normal-bearing geometry** (XYZ_N_UV: ~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]
|
||||
|
||||
## Skeleton assembly
|
||||
A mech is not one BGF — it's a `.SKL` skeleton (a DCS tree of joints) whose each joint's
|
||||
`Object=part.bgf` is loaded + posed, then animated by `.ANI` clips. See [[asset-formats]] /
|
||||
[[locomotion]] for skeleton + animation. [T1]
|
||||
|
||||
## Key Relationships
|
||||
- Detailed spec: `docs/BGF_FORMAT.md`.
|
||||
- Consumed by: [[rendering]] (the D3D9 draw path), [[locomotion]] (skeleton parts).
|
||||
- Related: [[asset-formats]] (BMF/BSL/SKL/ANI), [[decomp-reference]].
|
||||
@@ -0,0 +1,169 @@
|
||||
---
|
||||
id: decomp-reference
|
||||
title: "Decomp Reference — resource types, ClassIDs, offsets, addresses, env gates"
|
||||
status: established
|
||||
source_sections: "CLAUDE.md §10c; game/reconstructed/CLASSMAP.md; reference/decomp/all/part_*.c"
|
||||
related_topics: [subsystems, combat-damage, reconstruction-gotchas, build-and-run, gauges-hud]
|
||||
key_terms: [ClassID, resource-type, subsystem-roster, BTL4OPT, factory]
|
||||
open_questions:
|
||||
- "0xBCE weapon family fully mapped? (GaussRifle : Emitter, FireWeapon is a no-op in this build)"
|
||||
---
|
||||
|
||||
# Decomp Reference
|
||||
|
||||
The quantitative hub for the reconstruction: resource-type enum, ClassID map, mech struct
|
||||
offsets, key `FUN_` addresses, and env gates. Cite a section here rather than re-deriving.
|
||||
Everything is `[T1]` (decompiled + byte-verified) unless tagged otherwise. The living
|
||||
source-of-truth is `reference/decomp/all/part_*.c`; the class map is
|
||||
`game/reconstructed/CLASSMAP.md`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Resource lookup
|
||||
|
||||
`ResourceFile::FindResourceDescription(name, type, index=-1)` (`FUN_00406ff8` = find-by-type-and-name;
|
||||
`FUN_00407064` = `SearchList(resourceID, type)`). Lookup is **by NAME or by ID**. `Mech::ResolveAnimationClip(prefix,suffix)` builds a name and calls it.
|
||||
|
||||
⚠ A clip_id from `ResolveAnimationClip` is a **DIRECT resource ID** → `FindResourceDescription(clip_id)`,
|
||||
NOT `SearchList(clip_id, 16)` (which treats the arg as a LIST → walks garbage → AV). [T1]
|
||||
|
||||
### Resource type enum
|
||||
| Type | Value | Holds |
|
||||
|---|---|---|
|
||||
| SkeletonStream | — | `.SKL` skeletons |
|
||||
| DamageZoneStream | **0x14** | damage-zone records (Mech ctor reads count + array) |
|
||||
| ModelList | **18** | model/mech names (`bhk1`, `madcat`, …) |
|
||||
| AnimationResourceType | **16 (0x10)** | animation clips (lowercase: `blhrrl`=904, `blhrrr`=905, `blhwwli`=910, `blhrrli`=916) |
|
||||
| damage-zone segment list | 0x14 | per-zone segment list |
|
||||
| critical-subsystem segment | 0x1e | crit subsystem segments |
|
||||
|
||||
Animation clip names in `BTL4.RES` are **lowercase**. [T1]
|
||||
|
||||
---
|
||||
|
||||
## 2. ClassID map (subsystem factory)
|
||||
|
||||
Base 3000 = **0xBB8**. The factory switches on the resource ClassID; the `case <Name>ClassID`
|
||||
LABELS are **systematically mislabeled** — trust the `// FUN_004xxxxx` ctor-address comment
|
||||
reconciled via `CLASSMAP.md`, not the case label. [T1]
|
||||
|
||||
| ClassID | Real class | ctor @ | Notes |
|
||||
|---|---|---|---|
|
||||
| 0xBB9 | **Mech** | @004a2d48 (`Mech::Make`) | sizeof 0x854 (binary); reconstructed 0x638 |
|
||||
| 0xBBD | **Condenser** | @4ae568 | valve; `case`="?" |
|
||||
| 0xBBE | **AggregateHeatSink** (heat-sink BANK) | @4ae8d0 | GUID 0x50e590, sizeof 0x1E4; `case`=SensorClassID |
|
||||
| 0xBC0 | **Reservoir** | @4af408 | `case`=Condenser |
|
||||
| 0xBC1 | **Generator** | @4b… | power bus |
|
||||
| 0xBC2 | **PoweredSubsystem** | @004b0f74 | sizeof 0x31C |
|
||||
| 0xBC3 | **Sensor** | — | `case`=MyomersClassID |
|
||||
| 0xBC4 | **Gyroscope** | @004b3778 | DEFERRED (NaN integrator; reverted to stub) |
|
||||
| 0xBC5 | **Torso** | @004b6b0c | sizeof 0x280; `case`=SinkSource |
|
||||
| 0xBC6 | **Myomers** | @4b8fec | `case`=Actuator; INERT un-stub |
|
||||
| 0xBC8 | **Emitter** | @004b1d18 | sizeof 0x478; energy weapon |
|
||||
| 0xBCB | **AmmoBin** | — | `case`=JumpJet; sizeof 0x22C |
|
||||
| 0xBCD | **ProjectileWeapon** | @4bc3fc | sizeof 0x448 (`case`≠ this — see VDATA.h) |
|
||||
| 0xBCE | **GaussRifle** | @4bdcb4 | : Emitter; FireWeapon is a **no-op** in this build |
|
||||
| 0xBD0 | **MissileLauncher** | @4bcff0 | sizeof 0x44C |
|
||||
| 0xBD3 | **SubsystemMessageManager** | — | DEFERRED (WAVE 8; the valve/message control hub @mech+0x190) |
|
||||
| 0xBD4 | **PPC** (: Emitter) | — | same FireWeapon as Emitter; `factory calls it GaussRifle` |
|
||||
| 0xBD6 | **HUD** | — | alloc 0x2a4; `case`=MechDisplay-ish |
|
||||
| 0xBD8 | **Searchlight** | — | `case`=LegSubsystem |
|
||||
| 0xBDC | **MechTech** | — | alloc 0x104 |
|
||||
| 0xBDE | **ThermalSight** | — | `case`=MechDisplay |
|
||||
|
||||
Identify a class at runtime with `sub->GetClassID()` (== `sub+4`). [T1]
|
||||
|
||||
### Class hierarchy (the two branches that share only MechSubsystem)
|
||||
- **Heat leaf:** `PoweredSubsystem : HeatSink : HeatableSubsystem : MechSubsystem` (Emitter/PPC/Sensor/Myomers/weapons chain here). [T1]
|
||||
- **Watcher branch:** `Torso/Gyroscope/Searchlight/ThermalSight/HUD : PowerWatcher : HeatWatcher : MechSubsystem`; `AmmoBin : HeatWatcher`. Uses a 0xC connection + 0x54 alarm (NOT the 4/8-byte heat-leaf types). [T1]
|
||||
|
||||
---
|
||||
|
||||
## 3. Mech struct offsets (`Mech*`, binary layout)
|
||||
|
||||
Reconstructed access MUST use named members/accessors — these raw offsets are the BINARY's,
|
||||
valid for reading the decomp, NOT for our compiled layout (see [[reconstruction-gotchas]]). [T1]
|
||||
|
||||
| Offset | Field | Notes |
|
||||
|---|---|---|
|
||||
| +0x11c / +0x120 | `damageZoneCount` / `damageZones[]` | inherited Entity; Mech ctor populates the array |
|
||||
| +0x124 / +0x128 | `subsystemCount` / `subsystemArray[]` | the [[subsystem-roster]] (NOT the segment table) |
|
||||
| +0x190 | `messageManager` | the 0xBD3 SubsystemMessageManager (damage guard / valve+message route source) |
|
||||
| +0x260 / +0x26c | `localOrigin` = { Point3D linearPosition; Quaternion angularPosition } | ORIGIN.h:15; position@+0x100 in some subsystem views |
|
||||
| +0x300 | segment table | skeleton `EntitySegment`s (muzzle sites, joints) |
|
||||
| +0x37c | target world `Point3D` | aim source |
|
||||
| +0x388 | target `Entity*` | HasActiveTarget() gate |
|
||||
| +0x38c | targeted sub-zone (int) | -1 = whole |
|
||||
| +0x42c | `shadowJointNode` (Joint*) | ShadowJointName @model+0xB4 |
|
||||
| +0x438 | torso subsystem cache | HUD/mapper read it |
|
||||
| +0x5b4 | HUD subsystem cache | (factory case 0xBD6 writes param_1[0x16d]) |
|
||||
| +0x650 | `deathAnimationLatched` / crashed flag | |
|
||||
| +0x7cc | `damageableSubsystems` chain | the condenser chain (GUID 0x50e4fc) RecomputeCondenserValves iterates |
|
||||
|
||||
`mech+0x100` (a subsystem-view of localOrigin.linearPosition): `.x@+0x100, .z@+0x108`. [T1]
|
||||
|
||||
---
|
||||
|
||||
## 4. Damage delivery
|
||||
|
||||
- `Entity::TakeDamageMessage(id, size, inflictingEntityID, zone, Damage&)` → `target->Dispatch(&msg)`.
|
||||
- Base `Entity::TakeDamageMessageHandler` **IGNORES zone==-1** — send a VALID zone index (the Mech cylinder-lookup override for unaimed hits is not reconstructed). [T1]
|
||||
- `class Damage { damageType(enum Collision/Ballistic/Explosive/Laser/Energy), damageAmount, damageForce, surfaceNormal, impactPoint, burstCount }`. [T1]
|
||||
- Weapon effect id: **"explode" = 13** (`Explosion::Make`). [T2]
|
||||
- `DestroyEntityMessage(id,size)` removes an entity — but a killed mech STAYS (a WRECK); death = a STATE transition (`SetGraphicState(DestroyedGraphicState)`), NOT removal. Issuing removal-on-death is the P5 teardown bug (do not). [T2]
|
||||
|
||||
---
|
||||
|
||||
## 5. Weapon fire-rate constants (recovered)
|
||||
|
||||
From the weapon `.SUB` records + the charge-curve `.data` constants (PE-parsed as x87 float10). [T1]
|
||||
- `DischargeTime` = beam-on time. ER-medium laser (BLH) = **0.2 s**.
|
||||
- `RechargeRate` = recharge time in **seconds exactly** (the two ln5 factors cancel): ER-S 1.0 / ER-M 2.0 / ER-L 4.0 / PPC 5.0.
|
||||
- One BLH ER-M cycle = 0.2 + 2.0 = **2.2 s**; ~12 lasers staggered → aggregate ≈ 5/s.
|
||||
- `WeaponRange` 500 (ER laser), `DamageAmount` 3.5, `PipColor` 1.0 0.0 0.0 (RED).
|
||||
- Charge constants: `_DAT_004bb3c4`=1.0, `_DAT_004bb3b8`=1e-4, `_DAT_004bb3b4`=0.5; RatedVoltage (GENRATOR.SUB)=1e4; fire threshold `seekVoltage[rec]=0.8*RatedVoltage`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Env-var gates (runtime; default OFF unless noted)
|
||||
|
||||
The authentic stack is DEFAULT-ON (`BTEnvOn`, mechrecon.hpp): `BT_GAIT_CUTOVER`, `BT_GAIT_SM`,
|
||||
`BT_COLLISION`, `BT_REAL_CONTROLS` default ON — set `=0` to fall back. ⚠ Gates check
|
||||
`getenv(name) != NULL`, so `BT_X=""` (empty) still ENABLES; use `Remove-Item Env:\BT_X`.
|
||||
|
||||
| Var | Effect |
|
||||
|---|---|
|
||||
| `BT_FORCE_THROTTLE` | auto-walk forward (no key) |
|
||||
| `BT_SPAWN_ENEMY` | spawn a target mech 120u ahead along the spawn facing |
|
||||
| `BT_FORCE_FIRE` | auto-fire on cooldown |
|
||||
| `BT_ASSERT_TO_DEBUGGER` | route CRT asserts to the debugger, not a modal box |
|
||||
| `BT_HEAPCHECK` | whole-heap validation on every alloc/free (O(n²) at mission load — SLOW) |
|
||||
| `BT_PROBE_BGF=<name\|ALL>` | boot-time BGF loader probe, exits after |
|
||||
| `BT_LOG=<file>` | per-instance log file (in content/) |
|
||||
| `BT_GAUGE_ATTR_LOG` | `[attr] <binding> OK/NULL` per config binding |
|
||||
| `BT_GAUGE_SKIP_LOG` | `[gskip] <primitive>` per unregistered gauge widget the parse skips |
|
||||
| `BT_VALVE_LOG` | condenser valve flow distribution |
|
||||
| `BT_SECTOR_LOG` | SectorDisplay Make/Execute |
|
||||
| `BT_NET_TRACE` | `[net-tx]/[net-rx]/[net-upd]` MP tracing |
|
||||
| `BT_DEV_GAUGES` | render the 6 pod MFD surfaces in a separate dev window |
|
||||
|
||||
Full render/locomotion gates (BT_RAMP, BT_MATPRI, BT_CULL, BT_SHADOW_*, BT_LODSEL, BT_ADDLOD,
|
||||
BT_PUNCH, …) are catalogued in [[rendering]].
|
||||
|
||||
---
|
||||
|
||||
## 7. Tools
|
||||
|
||||
- `tools/disas2.py <VA> [len]` — capstone disassembly of BTL4OPT.EXE at a VA (recovers x87 math
|
||||
Ghidra drops; folds known call targets + float constants). THE tool for any `FUN_` the
|
||||
assert-anchored exporter didn't capture (e.g. `@0x4ae464` MoveValve, `@0x4ca07c` Execute).
|
||||
- `tools/disas2.py`-style PE reads: `scratchpad/rdva.py` (4-byte VA read), `rdtbyte.py` (80-bit float10).
|
||||
- `reference/ghidra_scripts/ExportBTSource.java` — the headless assert-anchored decompiler.
|
||||
- `reference/ghidra_scripts/DecompVSS.java` — headless address-list decompiler (any skipped fn).
|
||||
- `tools/btconsole.py` — the MP console emulator. `tools/mapscan.py`/`resscan.py` — content scanners.
|
||||
|
||||
---
|
||||
|
||||
## Key Relationships
|
||||
- Feeds: [[subsystems]] (factory + ClassIDs), [[combat-damage]] (damage delivery), [[gauges-hud]] (attribute binding).
|
||||
- Verified against: [[reconstruction-method]] (the decomp loop), [[reconstruction-gotchas]] (why raw offsets fail).
|
||||
@@ -0,0 +1,86 @@
|
||||
---
|
||||
id: gauges-hud
|
||||
title: "Cockpit Gauges / MFD HUD system"
|
||||
status: established
|
||||
source_sections: "docs/GAUGE_COMPOSITE.md (full map); CLAUDE.md §8"
|
||||
related_topics: [reconstruction-gotchas, decomp-reference, subsystems, pod-hardware]
|
||||
key_terms: [gauge, methodDescription, attribute-pointer, GaugeRenderer, MFD, PCC]
|
||||
open_questions:
|
||||
- "Valve/message CONTROL routes need the 0xBD3 SubsystemMessageManager (WAVE 8)"
|
||||
- "Status-message queue (StatusMessagePool) is a NULL stub -> MessageBoard is empty"
|
||||
---
|
||||
|
||||
# Cockpit Gauges / MFD HUD
|
||||
|
||||
The pod's cockpit instruments: the secondary MFD (radar/heat/comm), the 5 mono MFDs, and the
|
||||
engineering screens. Full map + per-widget history in **`docs/GAUGE_COMPOSITE.md`**. **The gauge
|
||||
system is COMPLETE** at the registration+binding level: all 50 config attribute bindings resolve
|
||||
(0 NULL) and every config gauge primitive is registered + built (0 parse-skips). [T2]
|
||||
|
||||
## Architecture (three layers)
|
||||
1. **The config** `content/GAUGE/l4gauge.cfg` — a text file the engine `GaugeInterpreter` parses
|
||||
(`BuildConfigurationFile → Initialize → GetProcedureBody → ParsePrimitive`). Each mech's gauge
|
||||
tree is built from the label `GetGameModel()+"Init"` (e.g. `bhk1Init`), which invokes shared
|
||||
blocks (`MechInit`, `Secondary1`) that `configure` the ports and call gauge primitives. [T1]
|
||||
2. **Widgets** — each `keyword(...)` in the config resolves to a `MethodDescription` in
|
||||
`BTL4MethodDescription[]` (btl4grnd.cpp). An UNREGISTERED keyword is **parse-skipped** (never
|
||||
built). Base primitives (`numeric`/`digitalClock`/`rankAndScore`/`vertBar`-base/`segmentArc`)
|
||||
are ENGINE (L4GAUGE.cpp); BT-specific ones (`vertBar`/`map`/`pilotList`/`GeneratorCluster`/
|
||||
`sectorDisplay`/`prepEngr`/`messageBoard`/`vehicleSubSystems`/the ColorMapper family) are
|
||||
reconstructed + registered. [T2]
|
||||
3. **Data binding** — gauges bind to game state by NAME via the engine [[attribute-pointer]]
|
||||
system: the config's `Subsystem/Attribute` (e.g. `HeatSink/CurrentTemperature`) resolves
|
||||
through `ParseAttribute → FindSubsystem (stricmp GetName()) → GetAttributePointer`. A class
|
||||
PUBLISHES an attribute via a `<Name>AttributeID` enum + `static AttributePointers[]`
|
||||
(`ATTRIBUTE_ENTRY`) + `GetAttributeIndex()` chained to its parent. ⚠ DENSE-TABLE HAZARD (see
|
||||
[[reconstruction-gotchas]] §11). [T2]
|
||||
|
||||
## Reconstructing a widget — the recipe
|
||||
`MethodDescription Class::methodDescription = { "keyword", Class::Make, { ParameterDescription
|
||||
rows } };` + a line in `BTL4MethodDescription[]` before `&BTL4ChainToPrevious`. The static
|
||||
`Make(int port, Vector2DOf<int> pos, Entity*, GaugeRenderer*)` reads `methodDescription.parameterList[]`,
|
||||
allocs (`operator new(binSize)` or plain `new`), placement-news the ctor. Then ctor/dtor/
|
||||
TestInstance/BecameActive/Execute. **Gotchas that bite EVERY widget:**
|
||||
- Container-Execute must override (§10 gotchas) — else `Gauge::Execute` aborts.
|
||||
- `/FORCE` trap — a prose-only slot AVs on first call.
|
||||
- Databinding trap — never raw-read owner offsets; use a bridge (`BTGetSubsystemAuxScreen`, …).
|
||||
- ReconStream no-op — use `DEBUG_STREAM` for logs, not `DebugStream`.
|
||||
- Lazy build — wait for the gauge window before concluding "not built" (`BT_GAUGE_SKIP_LOG`). [T2]
|
||||
|
||||
## The completed widgets (this project's gauge wave)
|
||||
- **Attributes published:** HeatSink table (Degradation/Failure/NormalizedPressure/CoolantMassLeakRate/
|
||||
ValveSetting/…), PoweredSubsystem (InputVoltage), MechWeapon (OutputVoltage), Mech (LinearSpeed,
|
||||
radar Position/Quaternion), Sensor (RadarPercent), **AggregateHeatSink (AmbientTemperature=300 —
|
||||
the LAST NULL)**. [T2]
|
||||
- **Widgets reconstructed + registered:** ColorMapper family (cmHeat/cmCrit/cmArmor/multiArmor),
|
||||
headingPointer, vertBar (VertTwoPartBar), segmentArcRatio, oneOfSeveralPixInt, map (radar),
|
||||
PlayerStatus, vehicleSubSystems (the engineering cluster panels), LeakGauge, vertNormalSlider,
|
||||
**pilotList** (Comm KILLS/DEATHS), **GeneratorCluster**, **SectorDisplay** (radar SECTOR X/Z
|
||||
read-out — live), **PrepEngrScreen** (12 engineering-screen label overlays), **MessageBoard**
|
||||
(comm ticker — deferred-empty). [T2]
|
||||
- **Condenser valve gauge:** ValveSetting→coolantFlowScale reads the authentic **1/N**
|
||||
(`RecomputeCondenserValves`, FUN_0049f788, was a no-op stub). [T2]
|
||||
|
||||
## Dev composite (off-pod)
|
||||
`BT_DEV_GAUGES` renders the 6 pod [[MFD]] surfaces in a separate 960×384 window (bit-plane masks
|
||||
over one shared `SVGA16` pixelBuffer; `SVGA16::DrawDevSurface`). On the POD they come from
|
||||
`SETENV.BAT`/`L4GAUGE` on the real multi-adapter hardware (`FindBestAdapterIndices`/`BuildWindows`,
|
||||
intact). The `overlay` port (SectorDisplay lives there) shares the `sec` physical surface via a
|
||||
different bit-plane (0x00C0). [T2]
|
||||
|
||||
## Authentically-static (do NOT "fix")
|
||||
`currentTemperature ~77` (huge thermalMass ~1.39e6 → a 3.5-heat shot rounds to zero);
|
||||
Degradation/Failure temps are fixed markers; AmbientTemperature 300; CoolantMassLeakRate 0 on a
|
||||
pristine mech (damage-gated); cmArmor/cmCrit all-green (undamaged solo player — BT is PvP-only);
|
||||
RANK 1 solo; MessageBoard empty (no status messages exist in bring-up). The Heat MFD is
|
||||
authentically NEAR-STATIC, not a live heat-accumulation display. [T2]
|
||||
|
||||
## Remaining = DATA FEEDS, not widgets (deferred systems)
|
||||
The valve/message CONTROL routes need the **0xBD3 SubsystemMessageManager** (WAVE 8); the
|
||||
status-message queue needs **StatusMessagePool** (a NULL stub); `SeekVoltageGraph`'s 4 Seek*
|
||||
attrs are a cluster-child (not config-called). See [[open-questions]]. [T2]
|
||||
|
||||
## Key Relationships
|
||||
- Full history: `docs/GAUGE_COMPOSITE.md`.
|
||||
- Uses: [[attribute-pointer]] + [[reconstruction-gotchas]]; reads [[subsystems]] state.
|
||||
- Renders on: [[pod-hardware]] MFD surfaces.
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
id: open-questions
|
||||
title: "Open Questions — deferred systems, unknowns, get-from-Nick"
|
||||
status: living
|
||||
source_sections: "CLAUDE.md §9; docs/HARD_PROBLEMS.md; per-subsystem deferral notes"
|
||||
related_topics: [source-completeness, subsystems, multiplayer, combat-damage, locomotion]
|
||||
---
|
||||
|
||||
# Open Questions
|
||||
|
||||
Known unknowns + deliberately-deferred systems. For each: what's known, what's missing, what
|
||||
would resolve it, status. Deferred ≠ broken — most are marked-and-guarded stand-ins with the
|
||||
authentic path scoped.
|
||||
|
||||
## From Nick (external — gating for some paths)
|
||||
- **[GATING] The BT game SOURCE CODE** — the missing implementation `.cpp` (mech, subsystems,
|
||||
mapper, HUD, app; see [[source-completeness]]). Without it, Route A (recompile original) is
|
||||
impossible; we reconstruct from the binary instead. Likely on a backup/dev drive. Status: OPEN.
|
||||
- Confirmation the PodPC image is the complete content master. (Low priority — the runtime
|
||||
`BTL4.RES` already has 8 maps + the full mech anim set.) Status: OPEN.
|
||||
- Pod specifics for Phase 8: the 7-monitor driver setup, the RIO cockpit I/O protocol, current
|
||||
Win10+wrapper pod config. Status: OPEN.
|
||||
|
||||
## Deferred subsystems / feeds (authentic path scoped, marked in code)
|
||||
- **0xBD3 SubsystemMessageManager (WAVE 8)** — the per-mech message/damage hub @mech+0x190. Gates:
|
||||
the condenser valve CONTROL route (`MoveValve` @0x4ae464 is reconstructed but dormant — no
|
||||
caller + its guard reads the messmgr); the MessageBoard source; the Myomers advanced-damage
|
||||
gate; gauge control-mode cycling. What resolves it: reconstruct 0xBD3 + wire the message routes.
|
||||
- **StatusMessagePool (NULL stub, btstubs.cpp:62)** — the per-player status-message queue.
|
||||
BTPlayer+0x1dc is never populated → MessageBoard is empty (authentic for bring-up). What
|
||||
resolves it: wire the pool + decode `AddStatusMessage @0042e580` + the `Player__StatusMessage`
|
||||
record ({messageId@+0xc, nameEntity@+0x10}).
|
||||
- **Gyroscope (0xBC4)** — layout + joint-I/O reconstructed, but the ctor field-init + the two
|
||||
integrators (IntegrateEyeJoint @004b2ec0 / IntegrateBody @004b30ec) are incomplete → writes NaN
|
||||
to the root joint. Reverted to a stub. A full subsystem-sim reconstruction (bigger than Torso).
|
||||
- **MechControlsMapper look/eyepoint commit** — reconstructed but its offsets collide with declared
|
||||
members; arbitrate before enabling (no port consumer yet). The fire-trigger-through-mapper is
|
||||
still the `gBTWeaponTrigger` bring-up.
|
||||
- **Myomers authentic coupling** — the structural un-stub is INERT (mover feed + heat-gen no-op).
|
||||
Real coupling needs the 0xBD3 advanced-damage gate + `MoverAttach` routing into the LIVE
|
||||
JointedMover (must be reconciled with the gait cutover first).
|
||||
- **SeekVoltageGraph** — 4 Seek* attrs unpublished (a cluster-child, not config-called; non-blocking).
|
||||
|
||||
## Locomotion / combat polish (non-gating)
|
||||
- Authentic per-mech TURN-RATE constant (currently a bring-up constant rate).
|
||||
- Body-callback gimp handlers (states 16-19, targets 0x70b2/0x7161 undecoded → fall back to stand);
|
||||
airborne callbacks (`FUN_004a6344`/`FUN_004a7970`).
|
||||
- Wall-block-vs-climb tuning; collision DAMAGE application (computed, not applied in bring-up).
|
||||
- Death: reconstruct `DeathShutdown` (RP `VTV::DeathShutdown` analog) + collapse anim + destroyed
|
||||
skin. The wreck-stays behavior we ship is faithful; do NOT issue DestroyEntityMessage on death.
|
||||
|
||||
## Multiplayer (Phase 7 / P6)
|
||||
- Cross-pod COMBAT (target a replicant + route damage to the owning master).
|
||||
- Replicant GAIT animation (replicants don't animate joints yet — derive from replicated velocity).
|
||||
- The pod-LAN config (real IPs, bare-IP pilot entries). The WinSock2 TCP stack (L4NET.CPP) is
|
||||
reconstructed + smoke-tested on one box (two instances share a world, movement replicates).
|
||||
|
||||
## Rendering follow-ups (non-blocking)
|
||||
- Replacement-LOD selection for NON-additive multi-LOD models (needs the board's hot-spot/
|
||||
reference-point semantics; leads: `s_dplobject.lod_ranges[16]/lod_hot_spot`, 0x2047/0x2048).
|
||||
- Day/night MATERIAL path priority (BMF/IMG) — infrastructure done, gated `BT_MATPRI` (over-applies
|
||||
to shared terrain mats); needs the terrain-material model understood.
|
||||
- RGBA4444 alpha (TREE.BSL cutout) decoded but not alpha-tested outside punch batches; VTX/TGA paths.
|
||||
|
||||
## Content build sub-project (low priority)
|
||||
- Lab/other-build maps (`des`/`burnt`/`frstrm`) are source-only (`.map` in CONTENT/BT/MAPS/); would
|
||||
need compiling into a RES via the DOS `btl4tool.exe`. The 8 RES maps cover testing.
|
||||
|
||||
## Key Relationships
|
||||
- Feeds from: every subsystem/render topic (their deferral notes collect here).
|
||||
- Gating master: [[source-completeness]] (the missing BT source).
|
||||
@@ -0,0 +1,166 @@
|
||||
---
|
||||
id: reconstruction-gotchas
|
||||
title: "Reconstruction Gotchas — the systemic bug classes (check these FIRST)"
|
||||
status: established
|
||||
source_sections: "CLAUDE.md §5a, §10c; docs/HARD_PROBLEMS.md; docs/RESOURCE_AUDIT.md; gauge-wave notes"
|
||||
related_topics: [reconstruction-method, decomp-reference, subsystems, combat-damage, gauges-hud]
|
||||
key_terms: [shadow-field, databinding-trap, Wword-trap, FORCE-trap, dtor-epilogue, bridge, attribute-pointer]
|
||||
open_questions:
|
||||
- "Which reconstructed classes still carry un-audited raw-offset reads?"
|
||||
---
|
||||
|
||||
# Reconstruction Gotchas
|
||||
|
||||
The reconstruction is a **layout + linkage** problem as much as a logic problem. Our compiled
|
||||
classes are NOT byte-identical to the 1995 binary, and the BT link uses `/FORCE`, so a whole
|
||||
family of bugs is **silent** — garbage that happens to be non-fatal, or a runtime AV with no
|
||||
link error. When a reconstructed class misbehaves, walk this checklist FIRST; the answer is
|
||||
usually here, not in the logic.
|
||||
|
||||
> **The core rule (RULE: no stand-ins):** the full game logic IS in the pseudocode — a "gap"
|
||||
> is a reconstruction stub not yet filled, never a hole in the original. Never write
|
||||
> 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) is clearly
|
||||
> marked and meant to be REPLACED, never to substitute for reading the decomp. [T2]
|
||||
|
||||
---
|
||||
|
||||
## 1. Shadow field — re-declaring an engine-base field (THE most common)
|
||||
|
||||
**Symptom:** a field reads `0xCDCDCDCD` (fresh-heap fill) even though the ctor "sets" it; or an
|
||||
object over-sizes past its factory alloc. **Cause:** the reconstruction re-declared a field the
|
||||
engine base already owns, at the *binary's* offset. Two failures: (a) the copy **shadows** the
|
||||
base — the engine ctor writes the base field, the reconstruction reads its own uninitialised
|
||||
copy; (b) it lands at a **different offset** than the binary assumed.
|
||||
|
||||
**Fix:** delete the re-declaration; use the inherited member/accessor. Examples: `Mech`
|
||||
`damageZoneCount`/`damageZones` (shadowed `Entity`'s → zones never built); `Mech__DamageZone`
|
||||
`structureLevel`→engine `damageLevel`; the whole `HeatableSubsystem` de-shadow
|
||||
(`statusFlags`→`simulationFlags`, `destroyed`→`simulationState`, `statusBits`→`ForceUpdate()`).
|
||||
[T2]
|
||||
|
||||
## 2. Wword(N) — a global scratch bank, NOT this+N
|
||||
|
||||
`mechrecon.hpp:192` defines `Wword(int i)` as `static BTVal bank[0x400]; return bank[i&0x3ff]`.
|
||||
Any `Wword(N)` used for OBJECT access reads/writes a shared global, not `this+i*4`. e.g.
|
||||
`(Mech*)Wword(3)` for a zone's owner → garbage; use `GetOwningSimulation()`. [T2]
|
||||
|
||||
## 3. Databinding trap — raw offsets read garbage
|
||||
|
||||
Our compiled layout != the 1995 binary, so `*(T*)(obj+0xNN)` reads garbage for ANY object we
|
||||
compile. This is WHY shadow fields fail and why raw subsystem reads (e.g. a gauge reading
|
||||
`owner+0x438`) return junk. **Fix:** use compiled named members/accessors; for a cross-TU raw
|
||||
op, use a **bridge** (§8). A `+0x128`-style owner offset in subsystem code is the
|
||||
[[subsystem-roster]] (`subsystemArray`), NOT the segment table — check every `GetSegment(int)`
|
||||
in a reconstructed subsystem ctor. [T2]
|
||||
|
||||
## 4. Resource-struct layout mismatch (sibling of the shadow bug)
|
||||
|
||||
**Symptom:** RESOURCE fields read garbage (silently — a `heatSinkIndex` reading `10.0f`).
|
||||
**Cause:** `*__SubsystemResource` structs overlay pre-built 256-byte records loaded VERBATIM at
|
||||
fixed offsets, so OUR struct must match the binary byte-for-byte. Breaks two ways: (a) wrong
|
||||
inheritance base (the resource must mirror the CLASS hierarchy — `HeatableSubsystem`'s resource
|
||||
inherits `MechSubsystem__SubsystemResource` 0xE4, not `Subsystem::SubsystemResource` 0x30); (b)
|
||||
under-sized fields (a 12-byte record field typed as a 4-byte `ResourceID`). **Diagnose:** log
|
||||
compiled offsets `(char*)&res->field - (char*)res` vs the binary's; dump raw record bytes as
|
||||
int+float. **Lock:** `static_assert(offsetof(...)==0xNN)` + `static_assert(sizeof(...)==0xNN)`.
|
||||
The 33-agent RESOURCE_AUDIT fixed 8 such bugs (docs/RESOURCE_AUDIT.md). [T2]
|
||||
|
||||
## 5. Alias / phantom / interior fields (object layout)
|
||||
|
||||
- **Alias field:** a subclass member re-declaring an inherited slot the ctor reuses under a new
|
||||
name (Condenser `refrigerationOutput`==inherited `massScale@0x160`; Emitter
|
||||
`outputVoltage`==`rechargeLevel@0x320`). → delete, use the inherited name.
|
||||
- **Alarm-interior field:** a value the binary reads at `alarm+0x14` modeled as a separate
|
||||
member (HeatSink `heatState@0x184` == `heatAlarm.GetLevel()`). → route to the accessor.
|
||||
- **Phantom field:** a member at an offset PAST the object (Generator `shortFlag@0x25C` is
|
||||
really `*(owner+0x190)+0x25c` the msg-manager). → remove; read the real source.
|
||||
`GaugeAlarm54` = 0x54 (the real `AlarmIndicator`; STATUS level at +0x14, so
|
||||
`subsystem+0x184 == heatAlarm+0x14 == GetLevel()`); `SubsystemConnection` = 0xC. [T1]
|
||||
|
||||
## 6. /FORCE trap — unresolved symbol → runtime AV, not a link error
|
||||
|
||||
`/FORCE` turns an unresolved external (or a prose-only vtable slot) into a **runtime AV near
|
||||
`__ImageBase`**, NOT a link error. **When a /FORCE build crashes with a garbage call target
|
||||
near the image base, grep the link log for "unresolved external" — the "successful" build is
|
||||
lying.** Corollary: a bridge fn / a `.data` fn-ptr callback MUST have a real (stub) definition.
|
||||
A `SetVideoPathPriority` defined in an anonymous namespace → internal linkage → unresolved in
|
||||
another TU → stubbed by /FORCE → AV in `LoadMissionImplementation`. [T2]
|
||||
|
||||
## 7. Dtor-epilogue rule — do not reconstruct compiler glue
|
||||
|
||||
In a decompiled DESTRUCTOR, the trailing member-dtor calls (`FUN_xxx(this+N, 2)`), the base-dtor
|
||||
call (`FUN_xxx(this, 0)`), and the `(flags&1) && operator delete(this)` tail are COMPILER GLUE.
|
||||
Reconstruct only the body ABOVE them; C++ re-emits member+base destruction at the closing brace.
|
||||
An explicit base-dtor call runs the whole `~JointedMover → ~Mover → ~Entity` chain **TWICE** =
|
||||
the P5 double-free (re-`delete[]`s `collisionLists`, re-runs `DeletePlugs` over the freed segment
|
||||
table). ONE bug = BOTH the death-row crash AND the app-exit crash. [T2]
|
||||
|
||||
## 8. Bridges — the databinding-safe escape hatch
|
||||
|
||||
When TU A needs a raw-offset-safe op but its local RECON stubs collide with the real class
|
||||
headers, put the op in a **bridge**: a free function in a complete-type TU, `extern`-declared in
|
||||
A. Examples: `BTResolveWeaponMuzzle` (mech4.cpp — a complete-Mech TU with the segment API),
|
||||
`BTRecomputeCondenserValves` (heatfamily_reslice.cpp — sees Condenser), `BTResolveMessageBoard`
|
||||
(btplayer.cpp — complete BTPlayer), `BTGetSubsystemAuxScreen` (powersub.cpp — casts through the
|
||||
real PoweredSubsystem). Keep the alloc SIZE + special-cache when swapping a factory case. [T2]
|
||||
|
||||
## 9. Message-handler chaining + entity validity
|
||||
|
||||
- A reconstructed class's `MessageHandlers` set must be built **chained to the parent's**
|
||||
(`Receiver::MessageHandlerSet(Entity::GetMessageHandlers())`). An empty default-ctor set has
|
||||
no parent chain → `Receiver::Receive` finds no handler → every inherited message (TakeDamage!)
|
||||
is **silently dropped**. [T2]
|
||||
- `Entity::Dispatch` delivers synchronously only for a VALID master; an invalid entity `Post`s a
|
||||
deferred event that never fires. A manually-spawned entity (no CheckLoad handshake) must call
|
||||
`SetValidFlag()` itself — else EVERY message defers forever (the replicant / spawned-dummy /
|
||||
MessageBoard-source class of bug). [T2]
|
||||
|
||||
## 10. Container-Execute must override (gauges)
|
||||
|
||||
The 2007 engine `Gauge::Execute` base is `Fail("not overridden")` → `abort()` (GAUGE.cpp:598);
|
||||
`GuardedExecute`'s SEH **cannot catch abort()**. So a container/parent gauge MUST override
|
||||
`Execute` (even as a no-op) AND override `BecameActive` with a **non-inactivating** body (the
|
||||
default `GaugeBase::BecameActive` inactivates). A `GraphicGaugeBackground`-derived widget
|
||||
(PrepEngrScreen/BackgroundBitmap) has NO Execute virtual → the hazard doesn't apply; there the
|
||||
overridden slot is `BecameActive`. [T2]
|
||||
|
||||
## 11. Dense-table hazard (attribute publishing)
|
||||
|
||||
`AttributeIndexSet::Build` leaves gap slots uninitialized and `Find` strcmps EVERY slot → a
|
||||
published attribute table MUST be a **dense prefix** from the parent's `NextAttributeID`; a gap
|
||||
AVs. Fill gaps with a shared read-only pad member. Same for a class's `<Name>AttributeID` enum.
|
||||
[T2]
|
||||
|
||||
## 12. Verification gotchas (don't fool yourself)
|
||||
|
||||
- **Lazy gauge build:** `GaugeRenderer::BuildConfigurationFile` runs LAZILY. A too-early process
|
||||
kill shows `[gskip]=0` / "not built" even though the widget is fine — **wait for the gauge
|
||||
window** before concluding. (Cost a long detour this session.) [T2]
|
||||
- **ReconStream is a no-op:** `btl4gau3.cpp`'s `DebugStream` is the `ReconStream` whose
|
||||
`operator<<` is `{ return *this; }` — it DISCARDS everything. Use the engine `DEBUG_STREAM`
|
||||
(what heat.cpp uses) for a log that reaches the BT_LOG file. [T2]
|
||||
- **Head-on repro hides intermittent bugs:** a straight ram gives 1 clean result; the bug shows
|
||||
on GLANCING/sliding/rough-terrain contact. Reproduce with an angled/terrain-crossing approach.
|
||||
[T2]
|
||||
- **`static_assert` not runtime `Check`:** a runtime `Check(sizeof<=alloc)` in a factory bridge
|
||||
does NOT fail the build (it's a runtime assert → heap overflow at construction). Use a
|
||||
compile-time `static_assert` sizeof lock. [T2]
|
||||
- **Engine-class new member:** a NEW member on a 2007 engine class (d3d_OBJECT, DPLRenderer) MUST
|
||||
be initialized in EVERY ctor init-list (debug heap fills 0xCDCDCDCD → an uninit flag reads
|
||||
TRUE); and any device state a special draw path sets must be save/restored exactly. Deleting
|
||||
stale `.obj`s fixes layout-mismatch corruption when a base class grows. [T2]
|
||||
|
||||
---
|
||||
|
||||
## Diagnostic recipe (the standard loop)
|
||||
1. Read the RAW decomp `reference/decomp/all/part_*.c` for the `FUN_xxxx`.
|
||||
2. Map `FUN_`/`DAT_`/`this+0xNN` to engine symbols via BT headers + WinTesla MUNGA source +
|
||||
`CLASSMAP.md` + RP's parallel code.
|
||||
3. Write the REAL reconstruction; `static_assert`-lock the layout.
|
||||
4. Build; run env-gated; read `btl4.log`; cdb on any crash (0xCDCDCDCD=uninit, 0xFEEEFEEE=freed).
|
||||
5. For exhaustive multi-function analysis: a read-only Workflow (understand), then implement hands-on.
|
||||
|
||||
## Key Relationships
|
||||
- Applies to: every topic that reconstructs a class ([[subsystems]], [[combat-damage]], [[gauges-hud]], [[locomotion]]).
|
||||
- Uses: [[decomp-reference]] (offsets/ClassIDs), [[reconstruction-method]] (the loop).
|
||||
@@ -0,0 +1,60 @@
|
||||
# Phase 01 — Progressive Context Restructure
|
||||
|
||||
**Goal:** adapt the `spark-lesson`/`expert-seed.md` progressive-context pattern to the bt411
|
||||
port, turning the 2236-line monolithic `CLAUDE.md` into a thin router + a `context/*.md`
|
||||
knowledge graph. Motivation: CLAUDE.md is loaded every session and is too large; the deep RE
|
||||
knowledge should load on-demand.
|
||||
|
||||
## Design decisions
|
||||
- **In-place** (context/ + reference/ at bt411 root, mirroring spark-lesson), NOT a separate repo.
|
||||
- The router serves DOUBLE duty: expert-agent knowledge routing AND the load-bearing WORK
|
||||
directives (build recipe, "keep current" mandate) — bt411's CLAUDE.md drives active work, unlike
|
||||
spark-lesson's pure knowledge base.
|
||||
- The existing **`docs/*.md` ledgers stay** as the DETAILED running logs (RECONCILE 1555 lines,
|
||||
GAUGE_COMPOSITE 627, HARD_PROBLEMS, SUBSYS_PLAN, P3_LOCOMOTION, …). The `context/*.md` files are
|
||||
the CURATED digests that route into them. (docs/ ≈ spark-lesson's phases/; context/ ≈ its context/.)
|
||||
- **Evidence tiers adapted for RE:** T0 engine/header truth · T1 decompiled+verified · T2
|
||||
reconstructed+runtime-verified · T3 best-effort/guarded · T4 unconfirmed hypothesis.
|
||||
- **Non-destructive:** the live `CLAUDE.md` is UNCHANGED until the migration is complete + approved.
|
||||
The proposed router is `context/_ROUTER-DRAFT.md`.
|
||||
|
||||
## Status
|
||||
|
||||
### ✅ Foundation built (this pass)
|
||||
- `reference/glossary.yaml` — ~45 terms (platform/engine, formats, scene/entity, reconstruction).
|
||||
- `context/decomp-reference.md` — the quantitative hub: resource-type enum, ClassID map, mech
|
||||
offsets, damage delivery, weapon constants, env gates, tools.
|
||||
- `context/reconstruction-gotchas.md` — the 12 systemic bug classes (the conventions/DO-NOT hub).
|
||||
- `context/bgf-format.md` — geometry format + CONN/PCONN + LOD √3 + ramp shading (frames docs/BGF_FORMAT.md).
|
||||
- `context/gauges-hud.md` — the gauge/MFD system (frames docs/GAUGE_COMPOSITE.md); the just-completed wave.
|
||||
- `context/open-questions.md` — deferred systems + get-from-Nick.
|
||||
- `context/_ROUTER-DRAFT.md` — the proposed slim CLAUDE.md (identity, protocols, quick-lookup,
|
||||
evidence tiers, conventions, structure).
|
||||
|
||||
### ☐ Remaining topic files (the next tranche — migrate from CLAUDE.md §/docs)
|
||||
| topic | source in CLAUDE.md / docs |
|
||||
|---|---|
|
||||
| `project-overview.md` | §1-3 |
|
||||
| `pod-hardware.md` | §3 |
|
||||
| `content-archives.md` | §4, §4a |
|
||||
| `asset-formats.md` | §5, docs/ASSET_PIPELINE.md |
|
||||
| `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)
|
||||
1. Move the current `CLAUDE.md` deep content into the topic files above.
|
||||
2. Replace `CLAUDE.md` with `context/_ROUTER-DRAFT.md` content (preserving the git history of the old).
|
||||
3. Verify: a fresh session reads the router → quick-lookup → topic files load on demand.
|
||||
|
||||
## Notes
|
||||
- The router keeps the build one-liners + the "keep current" mandate so active port work is
|
||||
uninterrupted. The detailed §10 combat-bring-up narrative migrates to `combat-damage.md` +
|
||||
`locomotion.md` + `rendering.md` + the docs/ ledgers it already references.
|
||||
- Cross-references use `[[topic-id]]`; the glossary uses `related_topics`.
|
||||
@@ -0,0 +1,343 @@
|
||||
# BattleTech Pod-Port — Glossary
|
||||
# Terms, acronyms, class/format names used across the reverse-engineering + reconstruction.
|
||||
# Each entry: definition, kind (concept|format|class|tool|file), where it's discussed, related terms.
|
||||
# Evidence tiers (see CLAUDE.md): T0 engine/header truth · T1 decompiled+verified ·
|
||||
# T2 reconstructed+runtime-verified · T3 best-effort/guarded · T4 unconfirmed hypothesis.
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Platform / engine
|
||||
# ---------------------------------------------------------------------------
|
||||
MUNGA:
|
||||
kind: concept
|
||||
definition: The proprietary VWE game engine shared by BattleTech (BT) and Red Planet (RP). Renderer, mover/animation, math, scene graph. Lives in engine/MUNGA/.
|
||||
related_terms: [MUNGA_L4, L4, BT, RP, WinTesla]
|
||||
related_topics: [project-overview, wintesla-port]
|
||||
|
||||
MUNGA_L4:
|
||||
kind: concept
|
||||
definition: The hardware-abstraction layer (HAL) for MUNGA — video-out, input, timing, file I/O, audio. "L4" is the HAL brick. On the port it targets Win32/Direct3D9. Lives in engine/MUNGA_L4/.
|
||||
related_terms: [MUNGA, L4, L4D3D, DPL]
|
||||
related_topics: [wintesla-port, rendering]
|
||||
|
||||
L4:
|
||||
kind: concept
|
||||
definition: Shorthand for the MUNGA_L4 HAL. Prefixes its files (L4VIDEO, L4NET, L4CTRL, L4GAUGE, L4RIO, L4SERIAL, ...).
|
||||
related_terms: [MUNGA_L4, RIO]
|
||||
|
||||
BT:
|
||||
kind: concept
|
||||
definition: BattleTech — the primary port target. VWE arcade pod game, Tesla platform release 4.10 (~1995-96). Game logic = CODE/BT (mostly MISSING as source; reconstructed from the binary).
|
||||
related_terms: [RP, MUNGA, BTL4OPT, source-completeness]
|
||||
related_topics: [project-overview, source-completeness]
|
||||
|
||||
RP:
|
||||
kind: concept
|
||||
definition: Red Planet — the sibling VWE pod game sharing the MUNGA engine. Its game source (VTV/VTVMPPR/WEAPSYS) SURVIVES in the WinTesla archive and is the live reference for BT's parallel systems (VTV≈mech, WEAPSYS≈weapons).
|
||||
related_terms: [BT, WinTesla, MUNGA]
|
||||
related_topics: [wintesla-port, source-completeness]
|
||||
|
||||
Tesla-platform:
|
||||
kind: concept
|
||||
definition: VWE's arcade pod hardware/software platform. Original BT ran 32-bit DOS under a DPMI32 extender (Borland C++ 5 + TASM32) on Novell DOS pods with a Division IG image-generator board.
|
||||
related_terms: [IG-board, DPL, pod]
|
||||
related_topics: [pod-hardware, project-overview]
|
||||
|
||||
pod:
|
||||
kind: concept
|
||||
definition: The physical arcade cockpit. 2 video cards -> 7 monitors (main 3D 800x600, radar 640x480, five mono MFDs as one 1280x480 span). Cockpit I/O ("RIO") over serial. Now runs Windows 10 + a Voodoo/Glide wrapper.
|
||||
related_terms: [RIO, MFD, IG-board]
|
||||
related_topics: [pod-hardware]
|
||||
|
||||
RIO:
|
||||
kind: concept
|
||||
definition: The pod cockpit I/O — joystick X/Y, throttle, pedals, buttons over serial COM (L4RIO, L4SERIAL). Remapped to keyboard/gamepad on a dev box.
|
||||
related_terms: [pod, L4, MechControlsMapper]
|
||||
related_topics: [pod-hardware, locomotion]
|
||||
|
||||
IG-board:
|
||||
kind: concept
|
||||
definition: Division "IG" image-generator board (dual EISA) — the original 3D renderer, driven by the closed-source libDPL.lib + VREND microcode. The port REPLACES it with a dpl_* -> Direct3D9 shim (done in WinTesla as L4D3D).
|
||||
related_terms: [DPL, L4D3D, WinTesla]
|
||||
related_topics: [rendering, wintesla-port]
|
||||
|
||||
DPL:
|
||||
kind: concept
|
||||
definition: libDPL / dpl_* — the closed Division rendering API MUNGA called for 3D (dpl_*) and 2D (dpl2d_*). Shipped binary-only. Replaced by L4D3D in WinTesla; a few unported dpl_* effects (weapon beams) are re-implemented in the port.
|
||||
related_terms: [IG-board, L4D3D, libDPL]
|
||||
related_topics: [rendering]
|
||||
|
||||
L4D3D:
|
||||
kind: file
|
||||
definition: MUNGA_L4/L4D3D.cpp — the WinTesla renderer that replaced the IG board / libDPL with Direct3D9. THE hardest infrastructure, already done in the WinTesla archive.
|
||||
related_terms: [DPL, WinTesla, rendering]
|
||||
related_topics: [wintesla-port, rendering]
|
||||
|
||||
WinTesla:
|
||||
kind: concept
|
||||
definition: The Windows port of the MUNGA engine found in the "Elsewhen RP411" archive (VS2005). Renderer bypass (L4D3D), audio (OpenAL+libsndfile), L4 HAL, and full Red Planet game logic all work. BT's game layer is ABSENT (must be reconstructed). The bt411 repo builds on this engine.
|
||||
related_terms: [MUNGA, RP, L4D3D, source-completeness]
|
||||
related_topics: [wintesla-port, source-completeness, build-and-run]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Binary / decompilation
|
||||
# ---------------------------------------------------------------------------
|
||||
BTL4OPT:
|
||||
kind: file
|
||||
definition: BTL4OPT.EXE — the real BT game binary (1.24 MB, uncompressed PE32, Phar Lap TNT). Unwrapped from btrel410.exe (a PKZIP self-extractor). The decompile ORACLE — retains class names, Class::Method strings, and source-file paths in asserts. In content/.
|
||||
related_terms: [decomp, btrel410, oracle, BTL4.RES]
|
||||
related_topics: [source-completeness, decomp-reference, reconstruction-method]
|
||||
|
||||
decomp:
|
||||
kind: concept
|
||||
definition: The raw Ghidra pseudo-C recovered from BTL4OPT.EXE, in reference/decomp/all/part_*.c. The source-of-truth every reconstruction is verified against ("there are no gaps, just work to be done").
|
||||
related_terms: [BTL4OPT, oracle, reconstruction-method]
|
||||
related_topics: [reconstruction-method, decomp-reference]
|
||||
|
||||
oracle:
|
||||
kind: concept
|
||||
definition: Using the original binary (BTL4OPT.EXE) as a behavioral reference — running it under cdb, or reading its decompiled logic — to verify a reconstruction is behavior-equivalent.
|
||||
related_terms: [decomp, BTL4OPT, cdb]
|
||||
related_topics: [reconstruction-method]
|
||||
|
||||
BTL4.RES:
|
||||
kind: file
|
||||
definition: The compiled BT resource archive (3.23 MB). Holds maps (8 loadable), animation clips (lowercase names, blhrrl=904), models, materials, skeletons, damage-zone/subsystem records. Resolved by name or ID via FindResourceDescription. In content/.
|
||||
related_terms: [BTL4OPT, EGG, resource-type]
|
||||
related_topics: [content-archives, decomp-reference]
|
||||
|
||||
cdb:
|
||||
kind: tool
|
||||
definition: The x86 Windows debugger (Debuggers/x86/cdb.exe). Used to trace faulting stacks. Debug CRT marks fresh heap 0xCDCDCDCD (uninitialised) and freed 0xFEEEFEEE — invaluable for "was this ever constructed?".
|
||||
related_terms: [BT_HEAPCHECK, decomp]
|
||||
related_topics: [build-and-run, reconstruction-method]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Asset formats
|
||||
# ---------------------------------------------------------------------------
|
||||
BGF:
|
||||
kind: format
|
||||
definition: DIV-BIZ2 little-endian nested-TLV geometry. OBJECT->LOD->PATCH(geogroup)->PMESH; interleaved float32 verts; QUAD faces (must triangulate); materials by library:material name; damage/articulation name-driven via SV_SPECIAL dz_* tokens.
|
||||
related_terms: [DIV-BIZ2, PMESH, CONN, PCONN, LOD, SV_SPECIAL, material-ramp]
|
||||
related_topics: [bgf-format, asset-formats]
|
||||
|
||||
DIV-BIZ2:
|
||||
kind: format
|
||||
definition: The Division nested Tag-Length-Value container magic used by BGF (geometry) and BMF (materials). Chunk tags catalogued in dsys/PFBIZTAG.H (0x2046=LOD_DISTANCE, 0x2037=SV_SPECIAL, 0x2047/0x2048 reference/transition).
|
||||
related_terms: [BGF, BMF, PFBIZTAG]
|
||||
related_topics: [bgf-format, asset-formats]
|
||||
|
||||
BMF:
|
||||
kind: format
|
||||
definition: DIV-BIZ2 (FILETYPE=1) material+texture LIBRARY (no pixels). Material -> DIFFUSE color + MATERIAL_TEXTURE -> texture -> TEXTURE_MAP image basename. Tag 0x18 BITSLICE selects a BSL sub-image; 0x28 ramp ref; 0x26 emissive.
|
||||
related_terms: [BGF, BSL, material-ramp, VMF]
|
||||
related_topics: [asset-formats, rendering]
|
||||
|
||||
BSL:
|
||||
kind: format
|
||||
definition: DIV-BSL2 BIT-SLICED texture container. w*h 32-bit texel words hold 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. Decoding it wrong = the "rainbow/graffiti mech" bug.
|
||||
related_terms: [BMF, VTX, TGA, material-ramp]
|
||||
related_topics: [asset-formats, rendering]
|
||||
|
||||
SKL:
|
||||
kind: format
|
||||
definition: 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.
|
||||
related_terms: [ANI, DCS, joint, MOD]
|
||||
related_topics: [asset-formats, locomotion]
|
||||
|
||||
ANI:
|
||||
kind: format
|
||||
definition: INI keyframe animation. [HEADER] framecount/framerate(30)/skeletonfile; [frameN] joint->rx ry rz radians; [RootTranslation] per-keyframe x y z where .z = forward root SPEED (units/s). Walk speed is animation-driven, not physics.
|
||||
related_terms: [SKL, RootTranslation, locomotion]
|
||||
related_topics: [asset-formats, locomotion]
|
||||
|
||||
MOD:
|
||||
kind: format
|
||||
definition: INI mech/object definition — skeleton, skins, collision, gauges, physics, animations, class. The .MOD physics (MoverMass/drag) governed thrust VEHICLES + collisions, NOT walk gait.
|
||||
related_terms: [SKL, ANI, SLD, DZM]
|
||||
related_topics: [asset-formats]
|
||||
|
||||
SLD:
|
||||
kind: format
|
||||
definition: Collision solids. Terrain is modeled AS BoxedSolid collision volumes (Block/YCyl/Cone/Ramp/Wedge). The ground model + object collision both query these via the engine BoundingBoxTree. BT solids ship ZERO tiles.
|
||||
related_terms: [MoveAndCollide, BoundingBoxTree, ground-model]
|
||||
related_topics: [combat-damage, locomotion]
|
||||
|
||||
DZM:
|
||||
kind: format
|
||||
definition: INI damage-zone -> material map (mech skin damage states).
|
||||
related_terms: [damage-zone, DZ]
|
||||
related_topics: [combat-damage]
|
||||
|
||||
GIM:
|
||||
kind: format
|
||||
definition: Vector MFD/radar line maps (+ .GAT color table). Cockpit HUD vector assets.
|
||||
related_terms: [GAT, PCC, gauge]
|
||||
related_topics: [gauges-hud]
|
||||
|
||||
PCC:
|
||||
kind: format
|
||||
definition: ZSoft PCX 8-bit gauge rasters — the cockpit HUD bitmap assets (fonts helv15/helv42, label strips, dials).
|
||||
related_terms: [GIM, gauge, PCX]
|
||||
related_topics: [gauges-hud]
|
||||
|
||||
VMF:
|
||||
kind: format
|
||||
definition: DIV-VIZ2 material SOURCE (VIDEO/MAT/.../BTFX.VMF etc.). Carries the SCROLL animation + MAP{} image binding for effects (beams, muzzle, smoke, sky). The .BMF only names the texture; the .VMF binds it + scroll speed. THE key effects asset.
|
||||
related_terms: [BMF, BSL, beam]
|
||||
related_topics: [rendering]
|
||||
|
||||
material-ramp:
|
||||
kind: concept
|
||||
definition: The IG board colours no-normal geometry (terrain/mesas/sky/buildings/mech) by a material's 2-endpoint RAMP (dpl_SetMaterialRamp) — texture luminance indexes a low->high gradient. Normal-bearing geometry (vehicles) is LIT instead. Applying the ramp to lit geometry = the "white blobs" bug.
|
||||
related_terms: [BMF, BSL, emissive, hasNormals]
|
||||
related_topics: [rendering]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Scene / entity model
|
||||
# ---------------------------------------------------------------------------
|
||||
DCS:
|
||||
kind: concept
|
||||
definition: Dynamic Coordinate System — a transform node in the scene graph. A skeleton builds a DCS tree; each joint is a DCS.
|
||||
related_terms: [SKL, joint, scene]
|
||||
related_topics: [asset-formats, locomotion]
|
||||
|
||||
joint:
|
||||
kind: class
|
||||
definition: Engine Joint node (JOINT.h). GetJointType (hinge 0..2 / ball 4..5 / balltranslate 5); SetRotation(Radian|EulerAngles), SetTranslation. Resolved by name via Mech::ResolveJoint -> GetSegment(name) -> JointSubsystem::GetJoint.
|
||||
related_terms: [SKL, DCS, EntitySegment, Torso]
|
||||
related_topics: [locomotion]
|
||||
|
||||
EntitySegment:
|
||||
kind: class
|
||||
definition: A skeleton segment (288 bytes) in the mech's segmentTable. Maps a joint + geometry. Muzzle SITE segments (siterugunport etc.) resolve to world muzzle positions via GetSegmentToEntity x localToWorld. NOT the subsystem roster.
|
||||
related_terms: [joint, subsystem-roster, GetSegment]
|
||||
related_topics: [locomotion, combat-damage]
|
||||
|
||||
subsystem-roster:
|
||||
kind: concept
|
||||
definition: subsystemArray @mech+0x128 (count @0x124) — the inherited Entity table of the mech's Subsystem* instances (heat/weapons/gyro/sensor/...). A +0x128-style owner offset in subsystem code is the ROSTER, NOT the segment table (a classic bug source).
|
||||
related_terms: [subsystem, factory, EntitySegment]
|
||||
related_topics: [subsystems, reconstruction-gotchas]
|
||||
|
||||
subsystem:
|
||||
kind: class
|
||||
definition: A mech component (Subsystem-derived). Built by the per-class factory (Mech ctor). Ticks a per-frame Performance/Simulation. Families: heat (HeatSink/Condenser/Reservoir/AggregateHeatSink), power (Generator/PoweredSubsystem), weapons (Emitter/PPC/ProjectileWeapon/MissileLauncher/GaussRifle), Sensor/Gyroscope/Torso/Myomers/Searchlight/ThermalSight/AmmoBin/MechTech/HUD.
|
||||
related_terms: [subsystem-roster, factory, ClassID, PoweredSubsystem]
|
||||
related_topics: [subsystems, decomp-reference]
|
||||
|
||||
factory:
|
||||
kind: concept
|
||||
definition: The Mech-ctor per-class subsystem factory (switch on resource ClassID). The case LABELS are systematically MISLABELED — the real class is the ctor-address comment reconciled via CLASSMAP.md, not the case name. Each real class wired via a Create<Class>Subsystem bridge.
|
||||
related_terms: [ClassID, subsystem, CLASSMAP]
|
||||
related_topics: [subsystems, decomp-reference]
|
||||
|
||||
ClassID:
|
||||
kind: concept
|
||||
definition: RegisteredClass::ClassID — the per-subsystem-class id (base 3000=0xBB8). e.g. 0xBB9 Mech, 0xBBD Condenser, 0xBBE AggregateHeatSink(bank), 0xBC8 Emitter, 0xBD4 PPC. Read via sub->GetClassID() (sub+4). Full map in decomp-reference / CLASSMAP.md.
|
||||
related_terms: [factory, subsystem, CLASSMAP]
|
||||
related_topics: [subsystems, decomp-reference]
|
||||
|
||||
master:
|
||||
kind: concept
|
||||
definition: A locally-simulated entity (the pod's own mech + spawned dummies). Ticks its full drive/subsystems and EMITS network update records. Contrast replicant.
|
||||
related_terms: [replicant, viewpoint-entity, multiplayer]
|
||||
related_topics: [multiplayer]
|
||||
|
||||
replicant:
|
||||
kind: concept
|
||||
definition: A remote entity's local proxy (a peer pod's mech). Does NOT drive itself — it DeadReckons toward the master's replicated position. Must SetValidFlag() at creation or every message defers forever.
|
||||
related_terms: [master, dead-reckon, multiplayer]
|
||||
related_topics: [multiplayer]
|
||||
|
||||
viewpoint-entity:
|
||||
kind: concept
|
||||
definition: application->GetViewpointEntity() — the local pod's own mech (the cockpit view + player controls). Gate player-only logic on `this == GetViewpointEntity()` (Mech::PerformAndWatch runs for EVERY mech).
|
||||
related_terms: [master, MakeViewpointEntity]
|
||||
related_topics: [combat-damage, locomotion]
|
||||
|
||||
EGG:
|
||||
kind: format
|
||||
definition: The mission INI (.EGG): [mission] map= scenario= time= weather=, [pilots] roster (pilot=ip[:port] + per-pilot vehicle=/dropzone). map= resolved from BTL4.RES; a missing name null-derefs. Default test egg = DEV.EGG (map=grass, time=day).
|
||||
related_terms: [BTL4.RES, mission, pilot]
|
||||
related_topics: [content-archives, multiplayer]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Reconstruction concepts (the systemic bug classes live in reconstruction-gotchas.md)
|
||||
# ---------------------------------------------------------------------------
|
||||
shadow-field:
|
||||
kind: concept
|
||||
definition: A reconstructed class re-declaring a field its engine base already owns -> shadows the base (reads uninitialised 0xCDCDCDCD) AND lands at a different offset. THE most common reconstruction bug. Fix: use the inherited member/accessor.
|
||||
related_terms: [databinding-trap, Wword-trap, alias-field, phantom-field]
|
||||
related_topics: [reconstruction-gotchas]
|
||||
|
||||
databinding-trap:
|
||||
kind: concept
|
||||
definition: Our compiled class layout != the 1995 binary, so a RAW offset read *(T*)(obj+0xNN) reads garbage. Use compiled named members / accessors / a bridge fn in a complete-type TU. The systemic reason raw-offset reads fail.
|
||||
related_terms: [shadow-field, bridge, Wword-trap]
|
||||
related_topics: [reconstruction-gotchas]
|
||||
|
||||
Wword-trap:
|
||||
kind: concept
|
||||
definition: mechrecon.hpp Wword(int i) is `static BTVal bank[0x400]; return bank[i&0x3ff]` — a GLOBAL scratch bank, NOT this+i*4. Any Wword(N) used for object access reads/writes garbage.
|
||||
related_terms: [databinding-trap, shadow-field]
|
||||
related_topics: [reconstruction-gotchas]
|
||||
|
||||
FORCE-trap:
|
||||
kind: concept
|
||||
definition: The BT link uses /FORCE, so an unresolved symbol (or a prose-only vtable slot) becomes a RUNTIME AV near __ImageBase, NOT a link error. When a /FORCE build crashes with a garbage call target near the image base, grep the link log for "unresolved external".
|
||||
related_terms: [bridge, dtor-epilogue]
|
||||
related_topics: [reconstruction-gotchas, build-and-run]
|
||||
|
||||
dtor-epilogue:
|
||||
kind: concept
|
||||
definition: In a decompiled DESTRUCTOR the trailing member-dtor calls, the base-dtor call FUN_xxx(this,0), and the (flags&1)&&operator delete(this) tail are COMPILER GLUE — reconstruct only the body ABOVE them. An explicit base-dtor call runs the chain TWICE (the P5 double-free).
|
||||
related_terms: [FORCE-trap, P5]
|
||||
related_topics: [reconstruction-gotchas, combat-damage]
|
||||
|
||||
bridge:
|
||||
kind: concept
|
||||
definition: A free function in a complete-type TU that does a raw-offset-safe operation the caller's TU can't (its local stubs collide). e.g. BTResolveWeaponMuzzle (mech4.cpp), BTRecomputeCondenserValves, BTResolveMessageBoard. The databinding-safe escape hatch.
|
||||
related_terms: [databinding-trap, FORCE-trap]
|
||||
related_topics: [reconstruction-gotchas, subsystems]
|
||||
|
||||
attribute-pointer:
|
||||
kind: concept
|
||||
definition: The engine system binding cockpit gauges to game state by NAME. A class publishes attributes via a <Name>AttributeID enum (dense prefix from parent NextAttributeID) + static AttributePointers[] (ATTRIBUTE_ENTRY) + GetAttributeIndex() chained to parent. ParseAttribute->FindSubsystem->GetAttributePointer resolves the config binding. DENSE-TABLE HAZARD: a gap AVs.
|
||||
related_terms: [gauge, methodDescription, dense-table]
|
||||
related_topics: [gauges-hud, reconstruction-gotchas]
|
||||
|
||||
methodDescription:
|
||||
kind: concept
|
||||
definition: A gauge widget's registration record: { "keyword", Class::Make, {ParameterDescription rows} }. Added to BTL4MethodDescription[] (btl4grnd.cpp). An UNREGISTERED keyword is parse-SKIPPED (never built). The Make factory allocs + placement-news the widget from config params.
|
||||
related_terms: [gauge, attribute-pointer, GaugeRenderer]
|
||||
related_topics: [gauges-hud]
|
||||
|
||||
GaugeRenderer:
|
||||
kind: class
|
||||
definition: The engine gauge system. BuildConfigurationFile parses gauge\l4gauge.cfg via GaugeInterpreter (Initialize->GetProcedureBody->ParsePrimitive). Builds LAZILY -> a too-early process kill shows a false "not built". Composites onto the pod's SVGA16 bit-plane surfaces.
|
||||
related_terms: [methodDescription, MFD, gauge, SVGA16]
|
||||
related_topics: [gauges-hud]
|
||||
|
||||
MFD:
|
||||
kind: concept
|
||||
definition: Multi-Function Display — the pod's mono cockpit screens (Heat/Comm/Mfd1/Mfd2/Mfd3), bit-plane MASKS over one shared SVGA16 pixelBuffer (Heat 0x4000, Comm 0x8000, Mfd1 0x0100, Mfd2 0x0400, Mfd3 0x1000, sec = palette low byte). Eng1-3 = engineering-mode alt planes.
|
||||
related_terms: [gauge, GaugeRenderer, SVGA16, pod]
|
||||
related_topics: [gauges-hud, pod-hardware]
|
||||
|
||||
damage-zone:
|
||||
kind: class
|
||||
definition: Mech__DamageZone (engine DamageZone base). Armor model: damageLevel += damageAmount*damageScale[type], accumulates to 1.0 (destroyed). Built by the Mech ctor from the DamageZoneStreamResourceType (0x14) resource. Aimed hits route via Entity::TakeDamageMessage.
|
||||
related_terms: [DZM, TakeDamage, DZ]
|
||||
related_topics: [combat-damage]
|
||||
|
||||
ground-model:
|
||||
kind: concept
|
||||
definition: The authentic 1995 vertical-position model (FUN_004a9b5c). NO GRAVITY — an ABSOLUTE per-frame ground SNAP via the zone BoundingBoxTree (FindBoundingBoxUnder), lift = 5% of volume X-width. Walls block by FULL FRAME REJECTION; crushable props pass with a crunch.
|
||||
related_terms: [SLD, BoundingBoxTree, MoveAndCollide]
|
||||
related_topics: [locomotion]
|
||||
|
||||
RootTranslation:
|
||||
kind: concept
|
||||
definition: The .ANI [RootTranslation] section — per-keyframe forward root SPEED (.z, units/s). Locomotion integrates movement += dt*rootTrans.z; feet plant by construction (no separate stride constant).
|
||||
related_terms: [ANI, locomotion, SequenceController]
|
||||
related_topics: [locomotion]
|
||||
Reference in New Issue
Block a user