Myth: The Fallen Lords (Bungie, 1997) — demo and retail ISO

Status 2026-10-05 (NEW-GAME-MYTH-20261005): both the official Win32 demo and the retail CD reach in-level 3D gameplay headless with ordinary mouse and keyboard input. Evidence: scratch/runs/20261005-myth-tfl-retail-iso-gameplay/ (retail, reviewed, result.json) and scratch/runs/20261005-myth-tfl-demo-gameplay/ (demo; not a registered candidate, so no result.json), and the browser route scratch/runs/20261005-myth-tfl-retail-browser/ (reviewed). FPS not measured.

Registered app myth_tfl (localhost-only)

Build or rebuild the local tree with node tools/prepare-myth-tfl.js (runs the disc's own Setup.exe headless, ~5 min; --manifest-only rewrites just the cue and .wine-assembly-browser.json from an existing installed/). A fresh run reproduces the installed tree byte for byte.

lib/apps.js LOCAL_CANDIDATE_APPS; refused by tools/deploy-berrry.js. Local media under test/binaries/candidates/myth-the-fallen-lords/ (gitignored):

Browser (headless Chrome, web-input-probe.js --app=myth_tfl, served by tools/dev-server.js for Range support): menu at ~48 s, level ~110 s real time. In real time the enemy is already crossing the bridge by the time the level is on screen. Input verified there: A turns the camera, F8 key list, minimap click moves the view; Enter (Select All) reached the guest as WM_KEYDOWN 0x0D with no visible selection. Unit select/move was verified on the CLI.

Sources

What Where Checksum
Demo installer https://archive.org/download/MYTHDEMO/MYTHDEMO.EXE (32,647,564 B) md5 a38fe6ef950e22c1c7e7d231c29d0afc, sha256 d6b85468bed2714685e827311047083339f4123f03baa804ebe039a5e52b2771
Retail ISO candidate myth-the-fallen-lords (tools/fetch-candidate-corpus.js), 538,191,872 B md5 1edda1890e32e7bde09ce3bf563c0479

tucows_205995_Myth_-The_Fallen_Lords-Demo on archive.org is the Mac .sit; skip it. Myth II demo (MythIiSoulblighterDemo) was not needed.

Both installers are MindVision Installer VISE (vise32ex.dll); 7-Zip cannot open them, so they must run in the emulator. Demo: 52 MB install, payload is one tags/tags.gor (51,594,033 B). Retail "Small" install is 31 MB (myth_tfl.exe, tags/tags.gor, tags/scrap.gor, DLLs); artsound.gor (320 MB) and cutscene.gor (114 MB) are read from the CD (D:\TAGS\, label MYTH_TFL).

Modules and API profile

Emulator fixes this title needed

  1. _hwrite (api id 4093) — VISE installer writes with it; delegates to _lwrite.
  2. CreateFile on an existing directory must fail ERROR_ACCESS_DENIED (5). Myth's dir-exists probe (exe+0x412b40: CreateFile(dir, GENERIC_READ, OPEN_EXISTING); GetLastError()==5||==32 ⇒ exists) got 2, then CreateDirectory → 183 and the tag-file layer reported "error #2 while writing (#0,#1228)", ending in initialize_myth_for_new_map(NULL) failed. (string 0x4a0014) → exit(-1) with no visible message. Retail recursed on C:\TAGS\PRIVATE instead.
  3. uber.dll/mclient.dll app-local DLLs (above).
  4. Harness: --iso-exe now reports the disc path (D:\Setup.exe) from GetModuleFileName — VISE copies "from its own directory" and failed with "cannot open the source file 'C:\mclient.dll'". --save-vfs-prefix= added: an unfiltered --save-vfs after an ISO install exports every disc file Setup read and filled the host disk.

Named addresses (demo exe)

Reproduction

Retail (after a Small install into retail-installed/, ISO on D:):

node test/run.js --exe=<retail-installed>/myth_tfl.exe \
  --vfs-include='*.dll,tags/**,modules/**,*.ini' --iso=<myth-tfl.iso> \
  --quiet-api --stuck-after=0 --max-seconds=280 --max-batches=84000 --no-close \
  --input=12000:mousedown:320:240,12100:mouseup:320:240,16000:mousedown:320:240,16100:mouseup:320:240,20000:click:404:272,21900:mousemove:310:258,22000:mousedown:310:258,22100:mouseup:310:258,44000:mousedown:320:400,44100:mouseup:320:400,75000:png:/tmp/game.png

Intro ≈ batch 11900, menu ≈ 21800 (after dismissing "no network modules"), briefing ≈ 40000, Crow's Bridge in-level ≈ 75000. F8 = key list; W/S/Z/X move, A/D turn, Q/E orbit, C/V zoom.

Network modules notice (fixed 2026-10-05)

"Networking is unavailable because no network modules were found in the modules folder" came from UBER.DLL's DllMain (0x10002a7a orig): it chdirs to .\Modules, enumerates *.*, LoadLibraryAs each DLL and asks for NMGetModuleInfo, then restores the CWD. Three emulator faults stacked:

  1. CLI: run.js mounted the app's files after the static DllMains ran, so C:\Modules did not exist yet (the page loads files first). It now mounts the manifest before loadDlls.
  2. VFS: a directory that only exists because a mounted file lives under it was refused by SetCurrentDirectory/GetFileAttributes (_isDirectory).
  3. Both hosts: callDllMain runs an initializer synchronously and treated the LoadLibraryA yield (reason 5) as "resume", so LoadLibraryA returned a stale EAX (0x074ff7b0) and the load was serviced only after DllMain had given up. serviceLoadLibraryYieldSync (lib/process-boot.js) now maps the DLL in place when its bytes are resident; anything needing a fetch keeps the async path. (--trace-api still prints the stale EAX for that call: it records the return at yield time.)

After the fix "Multiplayer Game" is enabled in the main menu.

Performance and audio (browser, headless Chrome, 2026-10-05)

3dfx (Glide 2) renderer (2026-10-06)

Myth has its own 3dfx renderer (C:\myth\render\render_3dfx.c) and picks it by itself. 0x419a80 loads glide2x.dll (our built-in module) through 0x419db0, which resolves 33 entry points by name and returns failure on the first NULL (test eax,eax / jnz after each store, e.g. [0x4b63f0] = _grDrawPolygonVertexList@8); it then calls grSstQueryBoards and sets [0x4ab950]=1 ("3dfx available") when a board is reported. 0x46d1f0(type) is "renderer usable": 0 = software always, 1 = 3dfx if available and the memory tier [0x4ac13c] >= 1 (0x42a710: GlobalMemoryStatus.dwAvailPageFile - 8 MB, capped at 32 MB, against tiers at 0x4a0910: 7 MB, then 12 + 1.5 MB). The graphics prefs record (validated by 0x46d2e0) holds the renderer at +0x1c; the defaults routine 0x46d380 takes the highest usable renderer, so a fresh preference file uses 3dfx whenever Glide is complete.

What it needed from us:

Evidence: scratch/runs/20261006T003007Z-myth-3dfx-inlevel-final (CLI, --glide-renderer=software); compare the 8bpp software frame in scratch/runs/20261005-myth-tfl-retail-iso-gameplay/05-gameplay.png. Software stays selectable in Myth's own Preferences.

Choosing the renderer (user-facing). A fresh install starts on 3Dfx. To switch, open Preferences from the main menu, pick Graphics → Rendering → Software (or 3Dfx to switch back), and press OK. The next game uses it. Give the menu a few seconds after OK before clicking: Myth reinitialises its renderer there, and an immediate New Game click is ignored. The choice is stored in Myth's own preference file C:\TAGS\LOCAL\PREF\GAME. Whether it survives a page reload depends on the browser keeping the emulated C: drive; that was not tested, because every probe starts a fresh profile.

Browser verification (2026-10-06, headless Chrome, SwiftShader WebGL):

Threads crash (fixed b7acece2). With Threads on, Glide goes through lib/glide-render-worker.js, which accepted only a 20-byte open packet. 8cd80d5a's 24-byte grSstWinOpen packet (word 5 = nColBuffers) made it throw Invalid Glide open packet, trapping the guest at EIP 0x4602f6 (Myth's grSstWinOpen). Safari reports the page-side frame instead (lib/render-worker.js:54 _receive, which rejects with the worker's error). Regression: test/test-glide-webgl-fallback.js.

Bring-your-own ISO in the page (2026-10-06)

Evidence: scratch/runs/20261006-myth-tfl-byo-iso-run6/ (reviewed). Regression: test/test-myth-byo-iso-web.js (local-only, skips without the ISO; ~4 min). Both were validated on e7c970ef, i.e. on Myth's software renderer: from 8cd80d5a the game picks its 3dfx renderer, and in the browser that is the WebGL Glide backend, which crashed at the time of writing (claude:1863d2b5's open fix) -- rerun this test once it lands.

  1. Drop myth-tfl.iso on the desktop (window.wineMedia._fileInput): the insert dialog reads label MYTH_TFL, 538 MB, 312 files and offers 13 programs; the default is the autorun launcher Startup.exe, so pick D:\Setup.exe.
  2. "Insert and keep" copies the ISO into OPFS; Setup's C: writes go to that media's OPFS overlay journal ([overlay] checkpoint: persisted N change(s)). The VISE pages advance with Enter; the final "Install DirectX 5.0?" box is Tab, Enter (= No); then Enter for Close.
  3. VISE makes its Start Menu group only by Program Manager DDE ([CreateGroup("Myth The Fallen Lords")], [ShowGroup(...,1)], [ReplaceItem(...)], [AddItem(C:\Program Files\Myth_TFL\myth_tfl.exe, Myth The Fallen Lords, , 0, -1, -1, )], with wFmt = CF_TEXT). The virtual Progman used to ACK without acting (and refused CF_TEXT), so no shortcut and no icon; it now writes the .lnk (e7c970ef). The shell then shows a kept icon "Myth The Fallen Lords" (lnk:<media id>:c:\program files\myth_tfl\myth_tfl.exe).
  4. That icon launches the installed exe with D: = the ISO (DRIVE_CDROM, MYTH_TFL): intro, menu, New Game, Crow's Bridge, A turns the camera, F8, in-level sound (54 plays/20 s, PCM peak 0.16).

Driver pitfalls (not product bugs): the new icon is appended below an 800 px viewport -- scroll it into view; the desktop's open gesture is two click events within 500 ms (puppeteer clickCount: 2 did not launch it); a Read Me window may be open over the desktop.

Open