Files
firestorm/CLAUDE.md
T

41 KiB
Raw Blame History

BattleTech: FireStorm — Project Notes (CLAUDE.md)

Working notes for Claude Code. Updated as work progresses.

What this project is

A total-conversion / expansion ("FireStorm") built on the MechWarrior 4 game engine (the Gameleap engine, v5.03), using Microsoft's GameOS / GOS technology (same lineage as MechWarrior 4 and MechCommander 2). ~60k files mixing C++ engine source, an asset/content pipeline, and game-design spreadsheets. Toolchain is late-90s/early-2000s Visual C++ 6.0 — no git, no modern build system.

Repo layout (top level)

2026-06-23 cleanup: unused/duplicate trees archived to _UNUSED\ to reduce newcomer confusion (Gameleap{Archive,Drivers,EditorDocs,Notes,batch,utilities}; GameleapCode5_03{Content,hsh}; resource_fullbak). Build paths untouched. See README.md for the orienting layout map. Build/deploy uses only: Gameleap\mw4 (data), Gameleap\code (code+binaries), build-env (toolchain).

  • Gameleap\code/ — THE BUILD TARGET. Engine + game source + content. (confirmed by user) (Its Content\ + hsh\ were stale 2005 duplicates → moved to _UNUSED\; live data is Gameleap\mw4.)
  • Gameleap/ — only mw4/ is used (game-data source for resource packing). Former siblings (editor docs, drivers, batch tools, runtime env) were historical/utility → moved to _UNUSED\.
  • BTFrstrm/ — FireStorm design data: mech stat workbooks (MechInfo_5.04.xls, scriptaddmech.xls).
  • Finished HUDS from J&J/ — per-mech HUD art (MFD + Radar) for ~15 'Mechs.

Inside Gameleap\code/

  • CoreTech/ — reusable engine layer (GameOS, gosFX, GOSScript, MLR renderer, Network, Stuff, Language, blade) + Tools.
  • mw4/Code/ — the game itself. mw4/Code/MW4/ = ~590 files; AI subsystem dominates (~65 AI_* files).
  • mw4/Libraries/ — mw4-local libs (Adept, Compost, ElementRenderer, gosfx, ImageLib, MLR, server, stuff, stlport).
  • Content/ — game content: ABLScripts/ (ABL scripting language), Defines, Subsystems, Tables, .build files.
  • rel.bin/ dbg.bin/ pro.bin/build OUTPUT folders (libs/exe/dll land here). Generated, not source.

STEP 1: Build process reconstruction — COMPLETE

Toolchain

  • Compiler/IDE: Visual C++ 6.0 / MSDev98 (all .dsw/.dsp are "Format Version 6.00").
  • STL: bundled STLport at mw4/Libraries/stlport/ (custom, build it first).
  • External SDKs (NOT bundled) — exact reqs from CoreTech/Libraries/GameOS/pch.hpp:
    • DirectX 7.0 SDK — headers must be FIRST in include order (#if DIRECTDRAW_VERSION != 0x0700 → #error; uses ddraw7, dinput7, d3d.h immediate mode, dplay).
    • DirectX Media 6.0 SDK — DirectShow video (amstream.h, __IDDrawExclModeVideo).
    • EAX SDK (Creative eax.h) — audio.
    • DirectX 8 link libs (d3d8.lib d3dx8.lib dinput8.lib dxguid.lib) — used by standalone tools (Gos2Light/Text, HighTide, etc.).
    • NOTE: SDK paths are NOT in the .dsp files — they come from the IDE's global Tools → Options → Directories, which is why DX7-first ordering matters.
  • Compiler flags of note: /G6 /Zp4 (struct packing 4 — must be consistent across all libs), /MD runtime (Release), /MDd (Debug). Mismatched packing/runtime = link/crash.

Locating VC6 + SDKs on the ORIGINAL DISK IMAGE

  • VC6 / MSDev98: C:\Program Files\Microsoft Visual Studio\
    • VC98\Bin\ (cl.exe, link.exe, VCVARS32.BAT), VC98\Include, VC98\Lib, VC98\MFC
    • Common\MSDev98\Bin\MSDEV.EXE (IDE + command-line builder).
  • DirectX/Media/EAX SDKs — check these era-typical roots:
    • DX7 SDK: C:\DXSDK\, C:\mssdk\, or C:\DX7SDK\
    • DX8 SDK: C:\DXSDK\ or C:\Program Files\Microsoft DirectX 8*SDK\
    • DX Media 6: C:\DXMedia\ or C:\DXMEDIASDK\
    • EAX SDK: under C:\ or the Creative/SB SDK folder.
  • Most valuable artifact — the IDE directory ordering (reproduces the env exactly). Load the build user's NTUSER.DAT and read: HKCU\Software\Microsoft\DevStudio\6.0\Build System\Components\Platforms\Win32 (x86)\Directories → values "Include Dirs", "Library Dirs", "Path Dirs" (ordered; copy verbatim). Also: HKLM\SOFTWARE\Microsoft\VisualStudio\6.0, HKLM\SOFTWARE\Microsoft\DirectX (records DX version).
  • Signature-file search on a mounted image: MSDEV.EXE/VCVARS32.BAT → VC6; ddraw.h/d3d.h/dinput.h → DX7 includes (verify DIRECTDRAW_VERSION 0x0700); amstream.h → DX Media 6; d3dx8.lib/eax.h → DX8 / EAX SDK.

Master workspace & final target

  • Workspace: Gameleap\code/mw4/Code/MechWarrior4.dsw (the modern/current one).
    • Self-contained: all 24 sub-projects verified present on disk, spanning both mw4/Libraries and CoreTech/Libraries.
  • MechWarrior4Build.dsw = LEGACY/alternate (uses old MW4Gos, Network, GamePlatform, MW4GameEd v1). Do not use; superseded by MechWarrior4.dsw.
  • Final game target: project MW4ApplicationMW4.exe.
  • Editor target: project MW4GameEd2 → editor exe (separate).

Build configurations → output paths (from MW4Application.dsp)

Config Defines Output
Debug _DEBUG _ARMOR /MDd dbg.bin/MW4.exe
Armor NDEBUG _ARMOR /Zi arm.bin/MW4.exe
Profile NDEBUG profiling rel.bin/MW4pro.exe
Release RELEASE NDEBUG rel.bin/MW4.exe
icecap _ICECAP ice.bin/

A prior successful Release build exists in rel.bin/ (MW4.exe, MW4pro.exe, all .libs, Launcher.exe, ctcls.dll, MissionLang.dll, ScriptStrings.dll, autoconfig.exe, mw4print.exe). arm.bin/ and ice.bin/ are absent (those configs were never built here).

Build dependency order (topological, from MechWarrior4.dsw)

  1. stlport — foundation, no deps. Build first.
  2. Leaf libs (depend only on stlport / nothing): GameOS, Stuff, MLR, gosFX, ImageLib, Compost, GOSScript, ElementRenderer, Adept, server, DLLPlatform, MFCPlatform, GamePlatformNoMain, ctcls.
  3. MW4 — the game library.
  4. Helper exes/dlls: Launcher, autoconfig, mw4print, MissionLang, ScriptStrings, MW4DedicatedUI.
  5. MW4Application — links everything into MW4.exe. (Set as Active Project.)

VC6 honors these dependencies automatically via the .dsw "Project Dependency" records, so a single "Build" of MW4Application pulls the whole chain in order.

How to build

  • IDE: Open mw4/Code/MechWarrior4.dsw in VC6 → set MW4Application as Active Project → pick config (Win32 Debug / Release) → Build.
  • Command line (VC6): msdev MechWarrior4.dsw /MAKE "MW4Application - Win32 Debug" (or "... - Win32 Release"). Add /REBUILD for a clean build.

How to RUN (the mw4/*.bat scripts are LAUNCH, not compile)

  • game_dbg.bat etc. just run the exe: binaries\mw4\MW4_dbg -armorlevel 3 -nobuild.
  • -build = (re)build game resource .erfs from source assets at startup; -nobuild = skip.
  • bldresources_game_dbg.bat = run with -build to regenerate resources.
  • editor_dbg.bat = launch the editor (MW4Ed2_d).

⚠️ Open discrepancies to resolve before a real build

  1. Output path vs. launch path mismatch: .dsp files emit to dbg.bin/MW4.exe / rel.bin/MW4.exe, but the game_*.bat launchers point at binaries\mw4\MW4_dbg.exe. There is a missing copy/deploy step (or the bats target a different deployed layout). mw4/Binaries/ currently only holds 3DSPlug-ins/. TODO: find/recreate the deploy step.
  2. FullExport.batch references dead studio network paths (\\aas1\..., d:\ContentExport\). That's the art export pipeline, irrelevant to the code build.

STEP 2: Toolchain extracted from original disk image (E:) — COMPLETE

Original build machine image mounted at E:\. Build user = jeff. Everything needed is now copied into build-env/ (self-contained):

  • build-env/VisualStudio6/E:\Program Files\Microsoft Visual Studio (VC6: CL, LINK, NMAKE, MSDEV).
  • build-env/dx7asdk/E:\Code\dx7asdk (DirectX 7.0a SDK; ddraw.h = DIRECTDRAW_VERSION 0x0700 ✓).
  • build-env/DXMedia/E:\Code\DXMedia (DirectX Media 6.0 SDK; amstream/control/strmif + libs).
  • build-env/setup-mw4-build.bat + README.md (generated; build instructions).

Resolutions to prior open items:

  • DirectX version pinned: DX 7.0a SDK (headers) + DX Media 6.0 SDK. NOT DX8 for the game.
  • VC6 located & copied: VC++ 6.0 at build-env/VisualStudio6/.
  • EAX: bundled in repo (GameOS/Eax.h); no static eax.lib needed (runtime-resolved).
  • DX8 SDK: absent from image; only blocks 3 standalone tools (Gos2Light/Text, HighTide), not the game/editor.
  • Critical ordering: DX7 includes MUST precede VC98\INCLUDE (engine #error guard); enforced in setup .bat + README.

Exact registry directory order RECOVERED (from jeff's NTUSER.DAT)

Key: HKCU\...\DevStudio\6.0\Build System\Components\Platforms\Win32 (x86)\Directories

  • Include: DXMedia\include → dx7asdk\include → VC98\INCLUDE → VC98\MFC\INCLUDE → VC98\ATL\INCLUDE
  • Library: VC98\LIB → VC98\MFC\LIB only. Two verified facts that make this work:
  1. DXMedia\include has NO core DX headers (DirectShow only) → ddraw.h still resolves from dx7asdk before VC98's old one (satisfies the pch.hpp 0x0700 guard).
  2. The DX/DShow .libs were copied into VC98\Lib on the original machine (verified present in our copy: ddraw/dinput/dsound/dxguid/strmiids/quartz/strmbase/amstrmid); pulled in via #pragma comment(lib,...) in GameOS\guids.cpp, not the link line. Captured as build-env/vc6-directories.reg (rebased to local copies; HKCU import, no admin) and reflected exactly in build-env/setup-mw4-build.bat.

HKLM VC6 install registration extracted & rebased

Exported jeff's offline software hive trees (Microsoft\VisualStudio\6.0, Microsoft\DevStudio\6.0) and transformed into build-env/vc6-hklm-registration.reg:

  • Key root redirected oldswHKLM\SOFTWARE\Wow6432Node\... (32-bit msdev on 64-bit Windows reads HKLM\SOFTWARE redirected to Wow6432Node; HKCU\Software is NOT redirected).
  • All paths rebased: C:\Program Files\Microsoft Visual Studiobuild-env\VisualStudio6; C:\Program Files\Common Files\Microsoft Sharedbuild-env\CommonFiles-MSShared (copied VS98 + MSDesigners98 so those refs resolve).
  • Verified: 0 leftover oldsw, 0 un-rebased paths, 202 keys, ProductDir/InstallDir correct.
  • Raw exports kept as build-env/_raw_*.reg (provenance).
  • Import order (ELEVATED): vc6-hklm-registration.reg then vc6-directories.reg.

STEP 3: Clean build from source — SUCCESS (game target)

Registry imported (HKLM vc6-hklm-registration.reg + HKCU vc6-directories.reg), msdev runs from the copy, and a full clean rebuild of MW4Application - Win32 Release succeeds:

  • 22 projects compiled in dependency order (stlport → GameOS/Stuff/MLR/gosFX/… → MW4 → helper exes/dlls → MW4Application). MW4.exe — 0 errors, 384 warnings (normal for VC6).
  • Output: rel.bin\MW4.exe (~3.64 MB), plus fresh *.lib, Launcher.exe, autoconfig.exe, mw4print.exe, MissionLang.dll, ScriptStrings.dll, ctcls.dll.
  • Build is reproducible head-lessly via: msdev MechWarrior4.dsw /MAKE "MW4Application - Win32 Release" /REBUILD /OUT <log> (run via Start-Process -Wait; msdev is a GUI-subsystem app so the shell won't block otherwise).
  • Build logs kept in build-env\build_*.log.

stlnative dependency relocated INTO the working tree

mw4/Libraries/stlport/stl_inc_find.hpp hardcoded a native-headers path <c:\stlnative/##x> (3 macro defs, the only references in the codebase). STLport wraps a 27 MB snapshot of the VC98 + Platform SDK headers (974 files) restored from the image. Originally placed at C:\stlnative; now moved into build-env\stlnative\ and the 3 macros repointed to <c:\VWE\firestorm\build-env\stlnative/##x>. Verified: with C:\stlnative removed, a full clean rebuild still succeeds (MW4.exe, 0 errors). The build is now self-contained under c:\VWE\firestorm — no external C:\stlnative needed.


STEP 4: Runnable deployment assembled at C:\VWE\firestorm\MW4 — DONE

Rules-based deploy (no image/E: dependency) via build-env\deploy-mw4.ps1:

  • Result: C:\VWE\firestorm\MW4 — 845 files, 895 MB (target was ~900 MB), with our freshly built MW4.exe (3,637,248 bytes) + all runtime DLLs + prebuilt resource\*.mw4.
  • Sources (all from working dir): runtime base = Gameleap\mw4; binaries = rel.bin.
  • The old game_*.bat assumed a binaries\mw4\MW4_dbg.exe layout that the real game never used — the runnable game has MW4.exe at the deployment root. Launch via C:\VWE\firestorm\MW4\run-mw4.bat (resources prebuilt, no -build needed).

Deployment selection rules (the dev tree Gameleap\mw4 is a ~6 GB bloated superset)

  • KEEP whole: Resource\ (767 MB .mw4 packages), hsh\ (102 MB), Assets\, Stats\.
  • KEEP subset: Content\shellscripts\ MINUS Graphics\ (119 MB source .tga) and Original\ (57 MB backups) -> ~9 MB of compiled scripts/.gaf/.d3f.
  • KEEP root: runtime *.dll/*.ini/*.options/*.txt/*.rtf/*.ico/*.016/*.256/... + clokspl.exe.
  • DROP: Content\* (4.6 GB source except shellscripts), Movies\ (352 MB), Notes\, fonts\, all tool exes (ERF Deluxe, EffectEdit, MapCreator, ResourceBrowser, MW4Ed2, ...).
  • OVERLAY: fresh rel.bin binaries (MW4.exe, MW4pro.exe, Launcher, autoconfig, mw4print, MissionLang.dll, ScriptStrings.dll, ctcls.dll).

STEP 5: Runtime brought up at C:\VWE\firestorm\MW4 — RUNS (fullscreen + windowed)

Reference for "working" = C:\MW4knowngood (NOT E:\gameleap, NOT D:\5.0.8.2_console — those are unrelated). Two independent issues had to be fixed; both are now in deploy-mw4.ps1 so a fresh deploy is runnable out of the box:

  1. DirectDraw 16-bit compat shim (Win11). MW4 runs bitdepth=16; modern Windows fails its DirectDraw hardware-surface creation with DDERR_NODIRECTDRAWHW unless the DWM8And16BitMitigation AppCompat layer is applied to the exe path. Our path had a stray HIGHDPIAWARE-only layer that suppressed the auto-shim. Fix: set HKCU layer DWM8And16BitMitigation HIGHDPIAWARE for C:\VWE\firestorm\MW4\MW4.exe (deploy script does this; matching all-users HKLM $ DWM8And16BitMitigation is in build-env\mw4-compat-hklm.reg). Note: AppCompat is keyed on exe PATH, so a copy at a new path needs the layer re-applied.
  2. Dev-only DLLs shadowing system DLLs. Our rules copied every root *.dll, which pulled in dbghelp.dll/imagehlp.dll (2003 copies that shadow Windows' system DLLs and break DirectDraw init) plus tool DLLs ijl10.dll, ctcl.dll, Language.oeg.dll, Resources.dll. known-good has none of these. Fix: deploy script now excludes those 6 by name. Removing them was what made it run in BOTH fullscreen and windowed.

Diagnostic note: swapping known-good binaries into our deploy still failed before the shim was applied — that proved our rebuilt binaries are NOT the cause (the shim + DLL excludes were). Final deployment: C:\VWE\firestorm\MW4 = 894 MB, 839 files, DLL set identical to known-good, running our freshly built MW4.exe.


Resource (.mw4) build process — documented & VERIFIED

Full writeup in build-env\RESOURCE-BUILD.md. Tested end-to-end 2026-06-11 with our freshly built MW4pro.exe (Profile/LAB_ONLY): minimal Content\MechWarrior4.build -> resource\restest.mw4 + .dep (exit 0); #VBD magic + payload confirmed; .dep tracks source; incremental skip/rebuild both verified; Release exe confirmed to ignore -build. Key facts:

  • The resource compiler is inside the game exe, triggered by the -build flag, which is #ifdef LAB_ONLY — so only Debug/Armor/Profile (MW4_dbg/MW4_arm/MW4pro.exe) can build resources; Release MW4.exe cannot. (That's why bldresources_game_*.bat use the LAB_ONLY exes.)
  • Entry point: MW4Application.cpp -> Tool::Instance->BuildResources("Content\MechWarrior4.build", VER_CONTENTVERSION=63, deps); recursive driver Adept::Tool::BuildResources / ParseBuildFile; per-type packers in Adept\Tool.cpp + MW4\MWTool.cpp.
  • Input: Content\MechWarrior4.build (master tree) -> per-package .build manifests ([resource\X.mw4] + data=/instance=/file=/notation=/campaign=/... entries) -> source assets under Content\. Output: resource\*.mw4 + *.dep (incremental via .dep timestamps + content version). Needs the full ~4.6 GB Content\ source tree.

STEP 6: Editor (MW4GameEd2) builds — DONE (with a 1-line source fix)

  • Target MW4GameEd2 - Win32 Release -> rel.bin\MW4Ed2.exe (3,973,120 bytes, 0 errors). MFC app (Use_MFC 6); needs mfc42.dll (present in deployment) at runtime. Profile config also exists (-> pro.bin\MW4Ed2.exe).
  • Source fix required (editor source had drifted; shipped MW4Ed2.exe is from 2003 and was never rebuilt against the 2009 code): enum MPGameTypes in mw4\Code\MW4GameEd2\ObjectManager.h was missing GT_StockCampaign, which ObjectManager.cpp references (string map @1013, int map case 14). Added the enum member between GT_StockSiegeAssault and GT_CustomDestruction. (This is the only repo source edit beyond the stlnative relocation; both are marked with [...] comments in-place.)
  • Built incrementally but it was a full compile of the editor's own sources (none existed prior); shared libs reused from the Release game build.

⚠️ STEP 7: Editor builds + launches + draws UI, then HANGS (viewport spin)

Set up via build-env\deploy-editor.ps1; launches to window "MechWarrior 4 Mission Editor - 05.07.00.00" and creates its MDI children (Game View, Object Manager, Overview Window) — but then spins one CPU core at ~100% with zero memory growth and a stalled message pump ("not responding"; 282s CPU / 283s uptime). It's a busy-loop hang in the editor's real-time 3D viewport/idle loop — a runtime compat issue on modern hardware, NOT a build issue (it compiled, launched, and drew its UI). The game runs fine (different render path: fullscreen exclusive vs the editor's windowed live viewport).

ROOT CAUSE FOUND: incomplete dev content + modal-STOP-over-windowed-DDraw

The editor loads/builds the full content tree at startup and STOPs (fatal) on missing/ inconsistent dev content, one item at a time:

  • skies\reduex_sky\reduex_night.erf — misnamed (Reduex_sky_night.erf); FIXED by copy.
  • Texture skyfbb1 (referenced by Content\Skies\fact01_sky\fact01_sky.hint) — genuinely absent from the dev tree (no file of that name anywhere). The FireStorm dev Content\ is an incomplete WIP with real asset gaps. GameOS's STOP pops a modal dialog. Build-specific behavior on that dialog:
  • Profile (LAB_ONLY, lighter opt): shows the STOP dialog cleanly; dismissing it exits.
  • Release (/Ox /Og, no LAB_ONLY): trying to show the modal STOP over the windowed DirectDraw viewport hangs (100% spin) or crashes (AV) — a dialog/DDraw interaction fragility under full optimization. So the earlier "hang/crash" was this same content STOP, handled badly by the Release build (NOT a separate memory bug as first guessed). So: editor compiles + launches correctly; the blocker is incomplete dev content (a content-curation task on the project's own unfinished assets, distinct from build/toolchain).

Option 2 attempted (make content STOPs non-fatal) -> uncovered the REAL bug: heap corruption

  • Patched mw4\Libraries\Adept\ResourceImagePool.cpp:385 (texture-not-found): first tried continue (skip) -> downstream access violation (missing texture deref'd later); then a placeholder-substitute (file_name="content\\textures\\01AACA1.tga") -> got PAST the content STOPs. Both are tagged [editor degraded-mode] in-source.
  • With content handled, the editor returns to the original symptom: main UI thread spins at 100% CPU, not responding. EIP sampling (Wow64GetThreadContext) located the spin in a tight loop inside SysWOW64\ntdll.dll heap code (~0x76F73E04) = walking a corrupted heap free-list. ROOT CAUSE = heap corruption (buffer overrun / double-free / use-after-free) in the editor; it manifests non-deterministically as the ntdll heap-loop HANG or an AV, and shifts with optimization (Release vs Profile) -> exactly the observed behavior.
  • To actually fix: enable PageHeap for MW4Ed2.exe (registry Image File Execution Options\MW4Ed2.exe GlobalFlag=0x02000000; needs admin) to trap the corrupting write at its source with the Profile build's symbols, then fix the overrun. This is a deep memory-bug hunt in old code (the 2003 editor never hit it on XP / with complete content).
  • NOTE: ResourceImagePool.cpp placeholder patch is a degraded-mode hack; revert if not wanted.

PageHeap hunt (in progress) — corruption confirmed, but a Heisenbug to pin down

  • Enabled full PageHeap via build-env\mw4ed2-pageheap.reg (IFEO GlobalFlag=0x02000000, PageHeapFlags=0x3; off-switch in mw4ed2-pageheap-off.reg). Confirmed active (editor mem 45->94 MB). Built pro.bin\MW4Ed2.map (added /map to the Profile link in MW4GameEd2.dsp, preferred base 0x00400000) for address->function resolution.
  • With PageHeap + NO debugger: editor reliably AVs (0xC0000005) at ~1 s = the overrun caught at the corrupting write (the goal). But under my PowerShell debug loop it will NOT reproduce (many attempts, even with _NO_DEBUG_HEAP=1): attaching perturbs heap/timing so the editor takes a different path and exits without faulting -> classic heap-corruption Heisenbug.
  • Conclusion: pinning the exact corrupting instruction needs a REAL debugger that holds heap/ timing steady. Cleanest: install Debugging Tools for Windows (cdb) and run cdb -g -G -c "kb;q" -o MW4Ed2.exe with PageHeap on -> one-shot symbolicated faulting stack. Alt: WER LocalDumps (HKLM) -> parse the minidump's exception stream for the faulting EIP -> map via MW4Ed2.map (uncertain: GameOS's UEF may pre-empt WER). The corruption is in the content-load path (~1 s, texture/sky loading).
  • Working dir = proven runtime DLLs from the game deploy (NO imagehlp/dbghelp) + editor tool DLLs (ctcl/ijl10/Resources/Language.oeg) + MFC + our MW4Ed2.exe; the bulk (Content\, resource\, hsh\) are directory junctions to the dev tree Gameleap\mw4 (no 5.5 GB copy). Same DWM8And16BitMitigation compat shim as the game.
  • Content data fix needed to launch: Content\skies.build:109 declares File=Skies\reduex_sky\reduex_night.erf, but the night-sky erf was misnamed Reduex_sky_night.erf. The editor (LAB_ONLY, asserts on missing files) STOPped on it. Fixed by adding reduex_night.erf (copy of Reduex_sky_night.erf) in Gameleap\mw4\Content\Skies\Reduex_sky\. (A dev-content naming inconsistency, not a build/toolchain problem — the editor itself builds+runs correctly.)

STEP 8: Editor RUNS — root cause was DirectInput, NOT heap corruption (2026-06-22)

Installed Debugging Tools for Windows (cdb at C:\Program Files (x86)\Windows Kits\10\ Debuggers\x86\cdb.exe) and got the first fully-symbolicated faulting stack. This overturned the STEP 7 hypothesis: the crash is NOT content-load heap corruption — it is a modern-Windows DirectInput init cascade during CMInstall (GameOS), reproducible and deterministic:

  1. Joystick enumeration heap-corrupts in modern dinput.dll. CMInstall -> CMCreateJoysticks -> wEnumDevices(DIEDFL_ATTACHEDONLY) -> CMCreateJoystick7 (ControlManager.cpp). On Win11 the legacy IDirectInput7 path, when it Release()s a phantom/virtual HID "joystick" (0-axis "misreported device" at line ~602), corrupts the heap inside dinput.dll. Surfaces as RtlFreeHeap AV (normal heap) or, under full PageHeap, as a guard-page over-read in dinput!DIWdm_InitJoyId ([esi+0x44]). The PageHeap "AV at ~1s" from STEP 7 was THIS, mis-attributed to content loading. The game survives by heap-layout luck.

    • FIX (source): CoreTech\Libraries\mfcplatform\MFCPlatform.cpp InitGameOS now sets gDisableJoystick=1 (editor/Platform_MFC only; the game uses a different platform path and is unaffected). Chosen over the /gosnojoystick switch because in LAB_ONLY builds WinMain.cpp:~398 shows a help dialog and exits if any leftover /gos token remains on the command line — fragile (e.g. cdb perturbs the cmdline -> trips it). Tagged [editor modern-Windows fix].
  2. Mouse SetCooperativeLevel(NULL, FOREGROUND|NONEXCLUSIVE) fails 0x80070006. CMCreateMouse (Mouse.cpp) resolves the coop window from hWindow, which is always NULL in the editor at CMInstall time: the editor passes pFrame->GameWnd->m_wndView.m_hWnd, but InitSecondaryWindows (MainFrm.cpp:173) only news CGameFrame and never Create()s it, so CGameFrame::OnCreate (which builds m_wndView) hasn't run. XP tolerated a NULL coop window; modern DirectInput rejects it -> fatal PAUSE. (Keyboard has no coop-level call, so it survived.)

    • FIX (source): Mouse.cpp CMCreateMouse falls back to GetForegroundWindow()/ GetDesktopWindow() with BACKGROUND|NONEXCLUSIVE when the resolved window is NULL; the game path (valid hWindow) is unchanged. Tagged [editor modern-Windows fix].

Rebuilt MW4GameEd2 - Win32 Profile (recompiles GameOS + MFCPlatform, relinks; 0 errors) -> deployed pro.bin\MW4Ed2.exe+.pdb to C:\VWE\firestorm\MW4Editor. With both fixes + the content fixes below, the editor launches to a live, responsive, idle (0% CPU) main window ("MechWarrior 4 Mission Editor - 05.07.00.00", ~70 MB) — the old "100% spin / not responding" is GONE. Launch with just -armorlevel 3 (no /gos flag needed).

Content-curation fixes (FireStorm WIP dev tree, in Gameleap\mw4\Content)

The editor enumerates+loads EVERY mission/map at startup (ObjMan->BuildResources()) and STOPs fatally on the first defect. Fixed (all additive/non-destructive unless noted):

  • _backup dev-copy dirs (files named after the original, not the dir): created the missing <dir>.build/<dir>.instance (page [<dir>], unique resource targets): Maps\Colsm01_backup, Maps\StormCanyonSiege_Backup, Missions\Coliseum_backup. (Editor compiled these into resource\maps\colsm01_backup.mw4 etc. — confirms they're valid.)
  • Dangling map refs: Missions\EditorTemplate\EditorTemplate.instance Map= arctic01 (exists nowhere) -> arctic04; Missions\s1s2\s1s2.instance Map= lunar02 -> lunar01.
  • Double-nested user mission: Missions\PhoenixPalaceSTB\PhoenixPalaceSTB\* moved up one level (the files already referenced the top-level path); then its .instance Model= pointed at dropzones\hill.contents (page [droppoints]) -> fixed to phoenixpalacestb.data (page [gamedata]).
  • Scans confirmed scope is small: 0 remaining mission page-name mismatches; 1 dangling map ref (fixed); arctic01/lunar02 are genuine content gaps absent from ALL installs (dev Resource\Maps has 46 .mw4 — MORE complete than game-deploy/knowngood's 26 — so launch folder/junctions are correct).

3D viewport FIXED via DDrawCompat (2026-06-22)

Root cause pinned with temporary diagnostics in SetupMode/DirectDrawCreateAllBuffers (DXRasterizer.cpp, since reverted): every surface (front/back/Z), the clipper, and the IDirect3D7 object create fine with a valid hWindow, but IDirect3D7::CreateDevice fails with 0x88760082 = DDERR_INVALIDOBJECT for BOTH the hardware HAL (IID_IDirect3DTnLHalDevice -> IID_IDirect3DHALDevice) and the Blade software device — i.e. Win11's legacy DirectDraw does not support creating a windowed Direct3D7 device on an offscreen backbuffer (the game avoids this by using a fullscreen flip-chain). Not a code-flag issue. Fix: dropped DDrawCompat (narzoul, v0.7.1, ddraw.dll) into C:\VWE\firestorm\MW4Editor\ — a drop-in ddraw/d3d7 reimplementation. With it, CreateDevice succeeds and the Game View/Overview render (user-confirmed). Source kept at build-env\ddrawcompat\ddraw.dll; deploy-editor.ps1 now copies it. The standalone game (C:\VWE\firestorm\MW4, fullscreen) is unaffected and does NOT use it. The editor is now fully working: launches, edits, and renders the 3D viewport.

Open-Mission dialog now lists ALL source missions (2026-06-22)

Symptom: editor's File->Open showed only 5 missions. Cause: COpenMissionDlg::OnInitDialog (OpenMissionDlg.cpp) listed Resource\UserMissions\*.mw4 (compiled/published user-mission packages) — only 5 exist in the dev tree (and 0 in the game deploy C:\VWE\firestorm\MW4, so co-locating the editor with the game would have shown ZERO, not more). But CMainFrame::LoadMission loads from Content\Missions\<name>\<name>.Instance. Fix: changed the dialog to enumerate Content\Missions\* subdirectories that contain a matching <name>.instance -> now lists all 51 source missions. Tagged [editor data fix]. (Note: INVALID_FILE_ATTRIBUTES isn't defined in the VC6 SDK; used the literal 0xFFFFFFFF.) This made the requested "move editor into the game-binaries folder" unnecessary — the editor already junctions the dev tree (Gameleap\mw4), the most complete data (51 missions, 46 maps); the game deploy C:\VWE\firestorm\MW4 actually has 0 user missions, so it would have shown FEWER.

Second half of the fix — actually LOADING the listed missions (EditorApplication.cpp LoadMissionResources): listing wasn't enough. Opening any non-user mission STOPped with Can't open <garbage>. Cause: LoadMissionResources hardcoded the resource path as "Resource\\User" + (name after first \), i.e. only Resource\UserMissions\<name>.mw4 (the 5 user missions), and produced a garbage path (bad strchr+1 pointer) for other name forms. But stock missions compile to Resource\Missions\<short>.mw4 with abbreviated lowercase names (CentralPark->cpark.mw4, Frostbite->fbite.mw4) that aren't derivable by transform — the short name is declared in each mission's .build ([resource\missions\X.mw4]). Fix: extract the bare mission name, prefer Resource\UserMissions\<name>.mw4, else read the resource path straight out of Content\Missions\<name>\<name>.build. Verified headlessly via -report (which also calls LoadMissionResources): Coliseum -> 248 entities, CentralPark (cpark.mw4) -> 1709 entities, no STOP. All 51 missions are now openable; the 5 user missions still work. Tagged [editor data fix].

(historical) Editor INTERACTIVE; the (now-fixed) windowed 3D viewport blocker

Verified via window enumeration: at startup the editor creates ALL its MDI children (Game View, Object Manager, Overview Window) and presents a modal "Open Mission" dialog (normal startup; main frame correctly disabled behind it) — the user opens a map from there and the UI is usable. (Headless test runs "exit code 1" simply because nothing clicks that modal "Open Mission" dialog — NOT a crash. Earlier "idle/Responding" readings were the process sitting on that dialog; Responding=True+0% CPU cannot distinguish a working UI from a modal dialog — enumerate windows to tell them apart.) The only real failure is live 3D rendering: selecting Game View (and the Overview render once a mission draws) STOPs: No software rasterizer available (DXRasterizer.cpp:2488). Path: DirectDrawCreateAllBuffers -> SetupMode tries windowed hardware D3D7 (primary surface + clipper on hWindow + VRAM 3D backbuffer) which fails on Win11's legacy DirectDraw, then falls back to Blade software (555 system-mem surface, SetupMode line ~1816/1835) which also fails -> STOP. This is the windowed legacy DirectDraw/ D3D7-on-Win11 problem — the SAME core incompatibility the game avoids by running fullscreen- exclusive (+ DWM8And16BitMitigation shim). It is a distinct, deeper rendering-compat task from everything above (which was input + content). To pursue: capture the exact failing HRESULT by reproducing interactively (open map -> Game View) under cdb; candidate directions = a DDraw compat layer (e.g. DDrawCompat), forcing a supported windowed surface format, or hosting the viewport via a different present path. NOT yet solved.

Deployment finalized (2026-06-22)

  • PageHeap REMOVED (debug aid only): ran reg import C:\VWE\firestorm\build-env\mw4ed2-pageheap-off.reg ELEVATED (UAC) -> HKLM IFEO MW4Ed2.exe key gone. The real-name MW4Ed2.exe now runs directly (no longer AVs at ~2s). The MW4Ed2_nph.exe copy remains as a page-heap-immune fallback.
  • deploy-editor.ps1 updated: sources the Profile build (pro.bin, the working config) + .pdb; writes run-editor.bat (MW4Ed2.exe -armorlevel 3, no /gos); sets the DDraw shim; warns if PageHeap IFEO is present. A fresh deploy is runnable to the editor UI.
  • Launch: C:\VWE\firestorm\MW4Editor\run-editor.bat (or MW4Ed2.exe -armorlevel 3). Pick a mission in the startup "Open Mission" dialog. Everything except the live 3D render works.
  • Expect more WIP content STOPs only if exercising missions/maps not loaded at startup.
  • (Optional) Build Debug/Armor configs (need dbg.bin/arm.bin output dirs).

STEP 9: Editor runs IN PLACE in the data tree — no separate MW4Editor dir (2026-06-23)

The editor's only job is editing the source content/resources in Gameleap\mw4, so the separate C:\VWE\firestorm\MW4Editor deploy (which just junctioned back to Gameleap\mw4) was removed. deploy-editor.ps1 now installs the editor into Gameleap\mw4: copies MW4Ed2.exe(+pdb) + DDrawCompat ddraw.dll, renames the two DDraw-breaking DLLs (dbghelp.dll/imagehlp.dll.disabled), sets the DWM shim on Gameleap\mw4\MW4Ed2.exe, and writes Gameleap\mw4\run-editor.bat. Verified: editor launches in place with the full UI + DDrawCompat viewport (Game View/Overview/Object Manager/Resources).

  • Conflict handled: DDrawCompat ddraw.dll is fatal to MW4pro.exe's fullscreen build init ([DDrawCompat] Fatal Error). Since the resource builder runs from the same Gameleap\mw4, build-resources.ps1 now moves ddraw.dll aside (.buildaside) for the build and restores it in a finally (verified: build exit 0, ddraw restored).
  • Game deploy unaffected: deploy-mw4.ps1 adds ddraw.dll to its skip list, so DDrawCompat + the editor exe/pdb/launcher/.disabled DLLs never leak into C:\VWE\firestorm\MW4 (verified: 682 files / 868 MB, 58 packages, fullscreen native DDraw). Old launch path C:\VWE\firestorm\MW4Editor\... is superseded by C:\VWE\firestorm\Gameleap\mw4\run-editor.bat.

📋 Reference: raising the MP player cap 16 → 24 (investigated 2026-06-24; NOT planned)

Captured for the future; we are not tackling this in the foreseeable future. Question was whether ConLobby.script's MAX_ROSTER_COUNT 16 could be 24. Findings:

  • No engine wall at 16 — the real ceiling is 255. Adept::Maximum_Players = 255 (mw4\Libraries\Adept\Application.hpp:139); every per-player fixed array (servedConnectionData[], scoreboards, net-stats, mission-review logs, pdmg_given/rcvd) is sized to 255. connectionID is a BYTE (0255). Environment.NetworkMaxPlayers is passed straight to DirectPlay's dwMaxPlayers (CoreTech\...\GameOS\Net_Main.cpp:2142) — no GameOS ceiling; m_maxPlayers has no hard upper clamp (MW4Shell.cpp:1803 only clamps bots down to the player count).
  • 16 is enforced in THREE layers — a script edit alone is insufficient/misleading:
    1. UI: Content\ShellScripts\ConLobby.script #define MAX_ROSTER_COUNT 16 (already 8→16; gated by USE_O_MORE_PODS/g_nMechPodNum; also clamps vs MAXTESLA_P = cafe Tesla count, ≤8).
    2. Compiled defaults (the real connect gate): MW4Shell.cpp:13319-13321 non-coop sets m_maxPlayers=16; Environment.NetworkMaxPlayers=16; m_maxBots=16 (coop branch caps at 9). Editing only the .script shows 24 slots but the session still rejects the 17th player.
    3. Lobby pods / scoreboard (hudscore) / radar UI are laid out for ≤16.
  • To actually reach 24: raise MAX_ROSTER_COUNT and the m_maxPlayers/NetworkMaxPlayers/ m_maxBots defaults (→ rebuild MW4.exe), rework lobby/scoreboard/radar layouts, and ensure maps define ≥24 drop zones / start points (many MP maps only have ~16 — a per-map content limit).
  • Why the cap exists (the practical blocker): replication is ~O(n²) — 24 vs 16 ≈ (24/16)² ≈ 2.25× host outbound + CPU; the dictionary/culling netcode was tuned for ≤16. Bandwidth, not the data structures, is the ceiling.

📐 PLAN: raise MP cap 16 → 32 (drafted 2026-06-24; NOT scheduled)

Concrete plan extending the reference above to 32 players. Phased; each phase is independently testable. 32 is safe vs the engine ceiling (255 arrays, BYTE connectionID) and has no 32-bit player-mask boundary risk — verified the only 0x1<<id masks are per-mech weapon bits (MWEntityManager.cpp:1332, NetWeapon.cpp), not player/connection indices.

Phase 1 — Code defaults + lobby-param path (rebuild MW4.exe, Release + Profile).

  • MW4Shell.cpp:13319-13321 (non-coop game setup): m_maxPlayers, Environment.NetworkMaxPlayers, m_maxBots 16 → 32. Coop branch 13309/13315-13316 (=16/=9/=8) — leave or bump per design.
  • MW4Shell.cpp:1803-1831 (lobby-driven path): m_maxPlayers=INTPARM(1), m_maxBots=INTPARM(1), Environment.NetworkMaxPlayers = params->m_playerLimit. No hard upper clamp exists (1808 Max_Clamp(m_maxBots, m_maxPlayers) only clamps bots down) — but confirm the lobby "player limit" control feeds a value up to 32 (param PLAYER_LIMIT_PARAMETER, NetParams.h:53).
  • Grep-audit for any other implicit ≤16 assumptions before rebuild (none found so far; arrays=255).

Phase 2 — Lobby + in-game UI (repack props.mw4). [LOW PRIORITY] Cosmetic/usability, not a functional blocker: with only Phases 1+3 done, 32 players can still connect, spawn, and play — the lobby pods/roster and scoreboard/radar just overflow or clip past 16 (and the host can still set the limit via the param path even if the slider UI caps lower). Defer this polish until 32p is otherwise proven; do the minimum to make the lobby usable, leave full pod/scoreboard re-layout for later.

  • Content\ShellScripts\ConLobby.script: MAX_ROSTER_COUNT 16→32, MAX_TEAMMATE_COUNT 8→16 (both under USE_O_MORE_PODS/g_nMechPodNum); raise the player-limit control's max to 32. Note the MAXTESLA_P clamp is cafe-only (CTCL Tesla count) and won't block non-cafe play.
  • Biggest UI lift — the mech-selection "pods": the lobby draws a fixed grid of player/mech pods; 32 needs a reworked layout (grid size, scrolling, or smaller pods). Same for the team-assignment panel (TEAM_MAX_PLAYERS, team_max_plyrs @ ConLobby:328).
  • Multiplayer lobby scripts (Content\ShellScripts\Multiplayer\* incl. lobby_listbox.script, HostLobbyMission.script): roster list sizing + player-limit slider range.
  • In-game scoreboard (hudscore.cpp) and radar (GUIRadarManager.cpp): data is fine (255 arrays) but the on-screen score table / legend is laid out for ≤16 → rework for 32 (scroll or two columns). MP_Review.script top-down review screen likewise.

Phase 3 — Map content (per-map; the gating gameplay task).

  • Every MP map used at 32p must define ≥32 drop zones / start points. Most stock maps have ≤16; players 17-32 silently fail to spawn otherwise. Audit + author extra drop points in the editor (now runnable in place from Gameleap\mw4\run-editor.bat). This is per-map, independent of code.

Phase 4 — Netcode / performance. [LIKELY FINE on modern hardware/networks]

  • Replication is ~O(n²): 32 vs 16 ≈ (32/16)² = 4× host outbound + CPU. This was the limiting factor in 2002 (dial-up / early broadband), but modern broadband upstream + CPU absorb 4× easily — raw bandwidth/CPU is no longer expected to block 32. Demote from "real ceiling" to a verification item.
  • Residual (non-bandwidth) thing to check when implementing: any fixed-size netcode buffers / per- frame packet or replication caps hardcoded for ≤16 (distinct from bandwidth; e.g. packet-count or buffer limits). Audit during Phase 1; bounded by the 255-array design, so likely none. Still do a live load test to confirm, but don't expect tuning to be needed.

Verification: (1) rebuild → headless -report/launch sanity; (2) host a game, confirm 32 slots in lobby + 17th32nd players connect (Environment.NetworkMaxPlayers/DirectPlay dwMaxPlayers); (3) confirm spawns on a 32-drop-zone map; (4) load/bandwidth test; (5) check scoreboard/radar/review UI legibility. Primary remaining work: per-map drop-zone authoring (Phase 3) is now the main gating task. UI (Phase 2) is low-priority non-blocking polish; bandwidth/CPU (Phase 4) is expected fine on modern hardware — just verify fixed-size netcode buffers and run one live load test.

Next steps (proposed)

  • Windowed 3D viewport on Win11 — DONE via DDrawCompat (see STEP 8 viewport section).
  • (Optional) Curate remaining WIP content as features are exercised (editor loads dev Content\).
  • (Optional) Build Debug/Armor configs (need dbg.bin/arm.bin output dirs).
  • (Parked, NOT scheduled) Raise MP cap to 24 or 32 — see "Reference"/"PLAN" sections above.