- decomp-reference.md: the binary ATTRIBUTE-TABLE section -- 16-byte
{id,name,off+1,0} row format (alignment warning), the walker technique,
and the three recovered tables (Mech ids 21-56 incl the corrected
EyepointRotation@0x360 / RearFiring@0x410 / DistanceToMissile@0x400
labels; HeatSink 3-12; Torso 3-15; the weapon RearFiring 'b' marker).
- combat-damage.md: the REAR-FIRE + look-view system (roster survey: the
Blackhawk's ERMLaser_2/3 are the game's only rear weapons; every missile
rack forward), the missile mount-frame launch truth, and the BANKED
full Missile flight-model decode (three performances, proximity fuse).
- wintesla-port.md: post-Phase-4 closures (instability model live, F14
static filter baked, the footstep warm-up bug + its trace-cap lesson);
deferred list trimmed to F21/HRTF.
- AUDIO_FIDELITY.md: status block -- F14 FIXED, warm-up bug noted.
- open-questions.md: "which mechs used rear fire" answered from data;
remaining look-view key bindings noted.
checkctx: CLEAN.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
159 lines
13 KiB
Markdown
159 lines
13 KiB
Markdown
---
|
||
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. (The early from-scratch D3D9 viewer was retired and removed.) [T1]
|
||
- **Audio: BACKEND now REAL + soundbank loaded; only game-triggering remains** [T2, updated
|
||
2026-07-15]. OpenAL replaces the HMI "SOS" engine and the renderer→device→buffer→source→play chain
|
||
IS all implemented (`L4AUDRND`/`L4AUDRES`/`L4AUDLVL`; `PatchLevelOfDetail::PlayNote` really calls
|
||
`alSourcePlay`). The "no sound, ever" root cause was DEEPER than a gate — **both audio backend DLLs
|
||
shipped in `engine/lib/` were no-op STUBS**: (a) `libsndfile-1.dll` exports 15 funcs BY ORDINAL
|
||
ONLY (no names), `sf_open()` always returns NULL; (b) `OpenAL32.dll` (72KB) imported **only
|
||
KERNEL32** — no dsound/wasapi/winmm — so it returned fake handles (`ctx=0x00000001`,
|
||
`alGenSources→0`) and never touched the hardware. So the chain ran clean and silent. FIXES (commit
|
||
5b46655): **OpenAL32.dll → real OpenAL Soft 1.25.2 Win32** (drop-in: the exe imports the 25 AL funcs
|
||
by NAME; `alSourcePlay` now reaches `AL_PLAYING`). **libsndfile DROPPED** — replaced by `LoadWavPCM()`
|
||
in `L4AUDRES` (a tiny in-tree RIFF/WAVE fmt+data reader, no external dep). Gates fixed earlier: the
|
||
`AWE_FRONT`/`AWE_REAR` env-var gate in `MakeAudioRenderer` was **relaxed to default-on** (`BT_NO_AUDIO=1`
|
||
disables). **Soundbank recovered**: 241 samples cracked from `AUDIO1/2.RES` (SF2 v1.0) by
|
||
`tools/sf2extract.py` → `content/AUDIO/*.wav` + the `allPresets[2][128]` table (`audiopresets.cpp`,
|
||
replacing the zero-init btstubs stub); all 241 load (`alErr=0`). **In-game triggering now WORKS** (commit 7b1e469): enabling audio unlocked
|
||
`L4AudioRenderer::CreateEntityAudioObjects`, which binds `AudioStateWatcher`s to entity/subsystem
|
||
state attrs BY NAME (the **attribute-pointer** databinding, same as gauges) and calls
|
||
`AddAudioWatcher` on the resolved `StateIndicator`. The reconstruction had pointed the Mech's state
|
||
attrs at the scalar read-pad `attrPad` (fine for a gauge that READs a value, fatal for a state watcher
|
||
that calls a METHOD → AV in `SChainOf::Add` on 0xCDCDCDCD). FIXED: the Mech's `AnimationState`/
|
||
`ReplicantAnimationState` (driven from `SetBodyAnimation`) and `CollisionState` (driven from
|
||
`ProcessCollision` via `collisionTemporaryState`) are now real `StateIndicator` members — **verified:
|
||
walking fires `SetupPatch`+`PlayNote`/`alSourcePlay` (footstep/gait patches 78/80/116)**. So mech
|
||
movement/animation/collision audio is live. **Subsystem STATE audio DONE** (commits 5ba541e/0e05a22/dcdd7dd):
|
||
the audio watcher TYPE per attribute was recovered via a `MakeObjectImplementation` ClassID trace
|
||
(class 28 = `AudioStateTrigger`/StateIndicator). KEY: the binary's 0x54-byte subsystem alarm
|
||
(`FUN_0041b9ec`, reconstructed as `GaugeAlarm54`) **IS a StateIndicator** — its "three sub-indicators"
|
||
@0x18/0x2c/0x40 are the audio/video/gauge watcher SChains, `level@0x14` = currentState. The
|
||
reconstruction had modeled that tail as an opaque `_tail` (never constructed → AddAudioWatcher AV'd on
|
||
0xCDCDCDCD). FIX: `GaugeAlarm54` now has three REAL constructed sockets + `AddAudioWatcher`; `SetLevel`
|
||
fires them on change (empty sockets = no-op, so other alarms/weapons unaffected; `levelB`/`LevelCountB`
|
||
untouched; sizeof stays 0x54). Then registered each state attr → its subsystem's own alarm (own
|
||
`AttributePointers[]` chained to the parent): `Generator.GeneratorState`→stateAlarm,
|
||
`Condenser.CondenserState`→condenserAlarm, `Reservoir.ReservoirState`→reservoirAlarm,
|
||
`AmmoBin.AmmoState`→ammoAlarm (+ chained AmmoBin's bare index so `SimulationState` resolves too).
|
||
`GeneratorOn`→generatorOn. **audiostate skips 85 → 0** — every StateIndicator audio attribute across
|
||
the mech + all subsystems binds to a real indicator, and the alarms are already `SetLevel`'d by the
|
||
sims, so generator/condenser/reservoir/ammo state sounds fire on transition. **Still inert** (read 0,
|
||
silent, non-crashing — no modeled backing member in the size-locked layouts, a polish follow-up):
|
||
`ReportLeak` (Logical), `Torso.MotionState` (Enum), `Torso.SpeedOfTorsoHorizontal` (Scalar).
|
||
NOW REAL (fidelity Phase 1, commit fc7f311): `ConfigureActivePress` published at the **MechSubsystem
|
||
BASE** (binary attr id 2, descriptor @0x50de5c → member @+0x110 — the field previously misnamed
|
||
`vitalSubsystemIndex`; idle −1, driven 0/−1 by `MechWeapon::ConfigureMappablesMessageHandler`),
|
||
`AmmoBin.FireCountdownStarted` → `cookOffArmed`@0x18C, `L4MechControlsMapper.TargetRangeExponent` →
|
||
`targetRangeExponent`@0x1a4. The **bring-up guards**
|
||
[T3, self-clearing] stay: a NULL subsystem attr → inert pad (was a fatal `Fail`); an
|
||
`AudioStateWatcher` on an unconstructed indicator (chain@+0x18 null/0xCDCDCDCD) → skip. Both pass
|
||
automatically once a subsystem is built. Diagnostics: `BT_AUDIO_LOG` (chain + skips/redirects), `BT_ATTRBIND_LOG` (every
|
||
attr bind), `BT_AUDIO_TEST` (proof-of-life buffer0).
|
||
- **Audio FIDELITY pass (2026-07-16, commits fc7f311 + 691e885; the full audit ledger is
|
||
`docs/AUDIO_FIDELITY.md`, 37 findings).** Settled facts [T1 unless noted]:
|
||
- **Patch numbering = SF2 `wPreset`.** Any tooling must key presets on `wPreset`, never on phdr
|
||
record order. `AUDIO1/2.RES` are SF2 **v1.0** (16-byte shdr, NO name/rate/pitch field); pitch
|
||
metadata rides generators: 55 = SBK samplePitch (`root*100+cents`, on every zone), 58 =
|
||
overridingRootKey, 51/52 = coarse/fineTune. **EMU8000 v1 base rate = 44100 Hz** → a zone's true
|
||
data rate is baked into its WAV: `rate = round(44100·2^(((6000−rootCents)+tune)/1200))`
|
||
(`tools/sf2extract.py`); the engine's note-60 pitch model (`BTNotePitchFactor`) then reproduces
|
||
original playback at every note. SBK `initialAttenuation`(48) is **INVERTED** (127 = full
|
||
volume; 0.375 dB/step [T3 exact curve], baked into the PCM).
|
||
- **Full-zone table**: 603 zones / 241 presets in `audiopresets.cpp`; the authored MIDI note
|
||
SELECTS zones by keyRange in `SetupPatch/PlayNote` (key-splits: Loaded/Jam family low-36
|
||
clunk vs high-84 blip; Warnings01 8-way klaxon split), layers attach together,
|
||
`MAX_PRESET_SAMPLES=25` (AllExplosion). Authored loop REGIONS apply via `AL_SOFT_loop_points`;
|
||
authored `releaseVolEnv` (1.1–3.9 s on ~20 looping presets) fades on Stop
|
||
(`PRESET_serviceReleaseFades`, serviced from `AudioHead::Execute`).
|
||
- **Distance/volume/doppler**: OpenAL distance model is `AL_NONE` — the AUTHORED
|
||
`GetDistanceVolumeScale()` curve multiplies in `CalculateSourceVolumeScale` (Dynamic3D +
|
||
Static3D); per-source `AL_GAIN = volume_scale²` (GM CC7 squared law); `alDopplerFactor(0)` —
|
||
the authored `GetDopplerCents()` (AUDIO.INI model, consumer decomp part_008.c:7466) adds into
|
||
the Dynamic3D pitch chain.
|
||
- **Audio clock**: `FUN_0044e19c` is `ApplicationManager::GetFrameRate` — the original audio
|
||
"frames" were SYSTEM CLOCK TICKS; `MakeAudioRenderer` calibrates at
|
||
`SystemClock::GetTicksPerSecond()` (fallback 1000), never the old garbage
|
||
`divisionParameters+0x10` read.
|
||
- **Published `localVelocity` smoothing is authentic** [T1]: `Mech::Execute` (FUN_004ab430) runs
|
||
the localVelocity components through `AverageOf` running-average filters
|
||
(part_012.c:15169-15179); the port's ~0.25 s exponential filter in `mech4.cpp` reconstructs it.
|
||
- **Phases 3+4 SHIPPED (2026-07-16, commits 0ca7d26..0e2401f).** Phase 3 EFX: `L4AUDEFX`
|
||
bridge — per-source lowpass (Dyn3D = highFreqCutoffScale×brightness UNGATED, Static/Direct =
|
||
brightness; AWE 100–8000 Hz → 5 kHz-ref gainhf [T3 curve]), one EAXReverb slot at
|
||
global_reverb_scale 0.3 (3D wet / cockpit Direct dry), Direct position-enum placement.
|
||
Phase 4 attributes (all [T1] from binary attribute tables, walked as 16-byte
|
||
{id,name,off+1,pad} rows): **HeatSink table @0x50e438 (ids 3..12)** confirms every heat
|
||
binding + adds ReportLeak id 12 @0x138 = coolantActive (serves all 19 leak watchers via
|
||
PoweredSubsystem⊂HeatSink); **Mech table @0x50c0xx (ids 21..56)**: IncomingLock@0x3fc /
|
||
DistanceToMissile@0x400 (real members, Missile::MoveAndCollide reports, PerformAndWatch
|
||
latches; old `maxSpeed@0x400` was a misread of the FLT_MAX far default),
|
||
CollisionSpeed@0x4B4 (=|impact velocity| at contact-arm), ReduceButton@0x340,
|
||
FootStep@0x394, RearFiring@0x410 (old `stateFlags` misread); **Torso table (ids 3..15)**:
|
||
full publish onto existing members (SpeedOfTorsoHorizontal=twistVelocity@0x1E8 abs'd,
|
||
MotionState=statusFlags@0x20C, ==2 = limit-hit frame). **F5 footStep = CONTACT LEVEL**:
|
||
(jointlocal.y <= *ANI hdr[2] threshold) per frame, states 0/1 retain (SelectSequence now
|
||
captures hdr[2]; the 150 ms pulse is retired). **F19**: the authored footstep volume =
|
||
LocalAcceleration |lin| [0,10]→ctl100 + LocalVelocity |lin| [0,0.6]→ctl101 (ALL motion
|
||
watchers extract |linearMotion|, motionValue=3); localAcceleration.linear = d(published
|
||
velocity)/dt (binary part_012.c:15186-15195); the mech2.cpp step broadcast is REMOVED.
|
||
**attrnull count = 0** — every authored audio attribute binds a real member.
|
||
- **Post-Phase-4 closures (2026-07-16):** UnstablePercentage is LIVE (the instability model
|
||
@0x3F0 byte-decoded + reconstructed — accel-sway² + gun-the-engine + turn-in-place terms,
|
||
feeds gyro swayBias + the audio alarm; commit c51d64a). The F14 per-zone static resonant
|
||
low-pass is BAKED (RBJ biquad at the AWE 100–8000 Hz curve, float pipeline + peak
|
||
normalization; 232 zones incl the LaserLoaded charge hum fc=57 Q=87; c581553). The
|
||
**footstep 10–20 s warm-up bug** is fixed (a11a697): an idle source's watcher chain executes
|
||
only at Start attempts and each hop is audio-frame-gated, so the authored fill-0 N=30
|
||
smoothers starved behind the 0.3 transient drop gate — fixes: AudioScaleOf sends EVERY poll
|
||
(the bitwise change-gate froze on our deterministic gait floats), recursive gate-free
|
||
Component::PrimeWatchers(30) on every transient Start request (AUDREND), and
|
||
localAcceleration via the binary's exact 15-sample ring buffers. LESSON: trace caps
|
||
saturate early and fake "per-stride" rates — always timestamp before inferring cadence.
|
||
- Still deferred: F21 attack velocity/time (all captured events were default 1), F22 HRTF
|
||
(user has 2 speakers; enable via alsoft.ini `hrtf=true` for headphones).
|
||
- **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 ~3.5k-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]].
|