Files
BT411/context/build-and-run.md
T
arcattackandClaude Opus 5 7afbda900e the terminal that never closes: every launcher waited for ANY btl4.exe on the machine
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>
2026-07-27 16:06:39 -05:00

7.6 KiB

id, title, status, source_sections, related_topics, key_terms
id title status source_sections related_topics key_terms
build-and-run Build / Run / Debug — recipe, repo layout, env gates established PROGRESS_LOG.md §10a, §10a-bis; README.md
wintesla-port
decomp-reference
reconstruction-method
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.

# 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 SetCurrentDirectorys 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 cds 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 CreateProcesses 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.cmakebuild/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