Operator report: "X-closing the game leaves the terminal open and leaves orphaned processes." Investigated on the rig against 4.11.600. The terminal half is real and is THIS: :btwait polled `tasklist /FI "IMAGENAME eq btl4.exe"`, which is machine-wide, so a bat that launched nothing at all keeps spinning while an unrelated instance lives -- proved with btwait_probe.ps1. A second client, the operator's own pod, or an orphan from a crash therefore hangs every join window, which reads as "the game never exited" and invites people to start killing processes. All four launchers carried the identical block. Fix: snapshot the btl4 PIDs alive BEFORE the launch; wait only on PIDs absent from that snapshot. The `if /I "%%P"=="btl4.exe"` guard is deliberately kept -- tokens=2 alone parses tasklist's "INFO: No tasks are running" line as a PID and spins forever with nothing running, which would be worse than the bug. Verified with the text lifted verbatim from the shipped play_solo.bat: nothing running -> signs off (the regression guard); someone else's instance -> signs off; our own generation -> keeps waiting; decoy gone -> signs off. The patched bat still launches (pid + launch_report.txt). NOT verified: the full handoff E2E, because the bat blocks on the FE menu waiting for a human. The orphan half did NOT reproduce on 600: closing the MAIN window exits cleanly in ~1s during solo model-load, in the relay join wait, and after a real console launch, with the relay logging the seat freed. The orphans the playtesters saw match 584 and earlier, where every close relaunched. Full write-up, including the aux windows that hide instead of closing and the WM_QUIT that BTLoadPump swallows, in phases/phase-12-orphan-processes.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
110 lines
7.6 KiB
Markdown
110 lines
7.6 KiB
Markdown
---
|
|
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]
|
|
**Since 2026-07-26 the exe enforces this itself** (`BTEnsureContentDirectory`, btl4main.cpp): if
|
|
cwd has no `BTL4.RES` it probes from the exe's own directory (`..\..\content` for the shipped
|
|
layout, plus flattened / in-content cases) and `SetCurrentDirectory`s there, before the log file
|
|
opens. The boot line says so when it had to look. ⚠ **The landmine it defuses** (field report):
|
|
a bare `btl4.exe` launch from `build\Release` found no resources ("Resource file btl4.res
|
|
v1.0.0.0 is obsolete!"), wrote a stray `bindings.txt`/`environ.ini`/`btl4.log` NEXT TO THE EXE —
|
|
so the player's real ones in `content\` looked like they were never created — and killed the
|
|
mission generation the menu launched (the child inherits the parent's cwd). Invisible for years
|
|
because every launcher `cd`s to `content\` first; it became reachable once glass became the
|
|
desktop default and a zero-arg launch started opening the menu.
|
|
- **The launcher's handoff wait must track ITS OWN generation [T2, 2026-07-27].** The front end
|
|
does not stay resident — it `CreateProcess`es the mission generation and exits — so every
|
|
launcher ends in a `:btwait` loop that waits for the handed-off process before printing its
|
|
sign-off. That loop used to poll `tasklist /FI "IMAGENAME eq btl4.exe"`, which is **machine-wide**:
|
|
a second client, the operator's own pod, or an orphan from a crash kept the window spinning
|
|
forever, which is the field report *"closing the game leaves the terminal open"*. Fixed by
|
|
snapshotting the PIDs alive before the launch and waiting only on PIDs absent from that snapshot.
|
|
⚠ **Keep the `if /I "%%P"=="btl4.exe"` guard** — parsing tasklist with `tokens=2` alone reads its
|
|
*"INFO: No tasks are running"* line as a PID and spins forever with nothing running. Full
|
|
investigation, including which windows exit vs merely hide: `phases/phase-12-orphan-processes.md`.
|
|
- **Why this is a BT411-only hazard [T1].** The 1995 pod shipped ONE folder — `BTL4OPT.EXE` sits
|
|
next to `BTL4.RES`/`VIDEO\`/`GAUGE\`/`AUDIO\` (it is still there in `content\`) — and RP411/RP412
|
|
keep that shape (`pack-dist.ps1` copies the exe and every asset dir into one dist root). BT411's
|
|
two-folder split (`build\Release\btl4.exe` + `content\`) is not a design decision: `mkdist.py`
|
|
zips TRACKED REPO PATHS verbatim, so the dist inherited the repo's developer layout. In the
|
|
authentic single-folder shape cwd is right by construction and a double-click just works, which
|
|
is why the other games never hit this. **A proposal to ship the authentic shape is written up
|
|
in `docs/DIST_LAYOUT_PLAN.md` — PROPOSED, not implemented, awaiting a decision between the
|
|
authors.** The cwd guard stays either way (it protects the developer tree, where the split is
|
|
real and permanent).
|
|
- Interactive: **WASD** drive; weapon groups (keyboard, task #43) **1/Space** = lasers, **2** =
|
|
PPCs, **3/Ctrl** = missiles; **X** all-stop; **V** cockpit/chase view. Default egg = `DEV.EGG`
|
|
(map=grass, time=day). Swap mech via the egg's `vehicle=` — **ALL 18 ModelList names
|
|
CERTIFIED playable (2026-07-18 vehicle sweep)**: the canonical 8 (avatar, bhk1/blkhawk,
|
|
loki, madcat, owens, sunder, thor, vulture) + the short variants (ava1, lok1/lok2,
|
|
mad1/mad2, own1, snd1, thr1, vul1); each boots a solo mission, spawns, animates, no
|
|
crash. The code path is mech-agnostic. [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.
|
|
|
|
## Versioning (2026-07-18)
|
|
`4.10` = the 1995 arcade release; `4.11` = this win32 reconstruction; dev builds =
|
|
**`4.11.<git commit count>` + short hash**, `+` suffix = built from an uncommitted tree
|
|
(e.g. `4.11.311 (980c9cd+)`). Stamped every build by `tools/btversion.cmake` →
|
|
`build/btversion.h` (`BT_VERSION_*` macros); shown in the boot banner (btl4.log head) and
|
|
the window title. Ask a tester for their title bar / btl4.log head to identify a build.
|
|
|
|
## Key Relationships
|
|
- Base: [[wintesla-port]] (the engine build recipe).
|
|
- Verify loop: [[reconstruction-method]]; env gates: [[decomp-reference]] §6.
|