Anachronox demo (Ion Storm / Eidos 2001)
Registry id anachronox_demo. Role-playing game on a heavily extended
Quake II engine: anox.exe + gamex86.dll + anoxgfx.dll, renderer
ref_gl.dll (OpenGL only -- there is no ref_soft), Miles audio (mss32.dll
plus every .m3d provider in ANOXDATA\MILES), and game logic in
ANOXDATA\PLUGINS\*.dll (loaded in dllorder.txt order; ui.dll is a debug
build). Fixture: test/binaries/win98-games-a-d/Anachronox-Demo-SW-OpenGL
(InstallShield 6 cabinets); the unpacked tree is
Anachronox-Demo-SW-OpenGL-installed/anoxdemo (the setup's default
C:\AnoxDemo, 99 MB).
Install
InstallShield 6 runs its engine (ikernel.exe) as an out-of-process COM
server, which the emulator does not provide ("Setup failed to launch
installation engine"). The cabinets were unpacked instead:
node tools/is-cab.js <fixture> --extract=OUT (114 files, every one
MD5-verified). The installer's own support files (ctor.dll, objectps.dll,
iuser.dll, iscript.dll, isrt.dll, _IsRes.dll, setup.*,
value.shl, default.pal, corecomp.ini) were left out. The setup would
also write HKLM\SOFTWARE\Eidos Interactive\Anachronox Demo; the game runs
without it.
Running it
- Working directory must be
c:\anoxdemo: the engine opensanoxdata\*.datrelative to it (withC:\every pack fails to open). anoxgfx.dll,gamex86.dllandMSVCRT.DLLload as real PEs (the entry'sdlls:); without them_PH_Get_Argc_Argv@12falls to a stub.- Args are
anox_640gl.bat's (fullscreen 640x480 GL). The windowedanox_window.batargs give a 646x507 framed window that a 640x480 capture crops. - Headless needs
--gl-renderer=software. Without a GL context (--headless-glwith no native deps)wglCreateContextfails and the game says "Couldn't load renderer!". - GL entry points it needed beyond the measured set:
glColor3ub(GL op 111, HUD/font colours). - Mouse is a pointer-lock/
GetCursorPos+SetCursorPoscentring loop (no DirectInput): drive menus withrelmousemove:DX:DY(1:1 pixels; the cursor starts at about (10,0));mousedown:X:Y/mouseupat the cursor's position clicks. Esc skips the title logo.
Route (CLI, default tick): "One Moment" -> 3D title logo -> Esc at ~205k batches -> main menu; New Universe at (265,140) -> "Democratus: Town of Whitendon" loading screen for ~150k batches -> the opening scene in Fatima's room with her dialogue "Hi there." (~437k). Batches cost ~0.75 s each in the world (software GL at 640x480 plus the debug-build UI), against ~1 ms in the menu.
Evidence: scratch/runs/20261006T1720Z-anachronox_demo-menu-to-world.
Dialogue: click with the game's cursor over the box (solved 2026-10-06)
The opening dialogue was never stuck; the harness clicked in a way the game
cannot see. Measured on boat bx_yg8qxb66 with --count slots armed live from
ctl.js eval (exports.set_count on the main and every thread instance):
- While a dialogue window is open the game context stack is
64,1(gamex86GAME_GetContext, stack at[0x10029724]; ui.dll pushes 0x40 when it opens a window at0x10006d43and pops it in0x10006377).selectforward(ui0x100045b1, Space) only acts in context 1, so Space and Enter are inert during dialogue by design. - A line advances through the click handler
0x1000553f(100 ms debounce onATime_GetTotalMsec, then0x1000535d), but only while a dialogue window has focus:[0x100e8eb8](window id) and[0x100e8eb4](index) are set by the picker0x10005e90, which hit-tests the game's virtual cursor ([0x100f2104]+0x140,[0x100f2100]+0xf0) against the window rectangle. - The mouse is a recentring loop, so
run.js mousedown:X:Yat any point but the centre reads as a jump: after the first click the cursor sat at (0,0) or (162,0), outside the box, focus dropped to -1 and later clicks were ignored.
Route through it: one relmousemove to put the cursor inside the box
(e.g. relmousemove:200:60 from the top-left), then for every line
mousedown:320:240, ~40 batches, mouseup:320:240, ~500 batches. Clicking at
the recentre point adds no motion (the cursor drifts 1-2 px per click). That
walks Fatima's intro to "Cue the snow."; the context then becomes 256 for the
snow/mansion cinematic, which plays by itself in ~70k batches; John's lines in
town take ~9 more clicks; then the context returns to 1 and Boots is under
player control: holding Up (+forward) walks him up the street, while 1500
idle batches leave him beside John.
Evidence: scratch/runs/20261006T1940Z-anachronox_demo-gameplay-w6.
Earlier investigation: the opening dialogue "never advances" (parked 2026-10-06)
Kept for the measurements; the cause is the cursor, above.
The opening scene in Fatima's room shows her line "Hi there." and stays on it. Measured on two runs (one ctl session to 434k, one scripted to 440k):
- Batches in the scene are fast (~300 per few seconds); the ~0.75 s/batch seen on the first visit did not recur.
- The in-world cursor follows
relmousemove(input reaches the game). - Nothing advances the line: Space (bound to
selectforwardinANOXDATA\CONFIGS\default.cfg), left clicks on the box and on Fatima, Enter, F1 (fatstat goals) -- over ~10k batches.
Ruled out, with how:
RSX 3D provider timer. Its 11 ms
timeSetEventcallback (mssrsx+0x22d0cda0) fires once because RSX itself callstimeKillEvent(1): Miles opens each 3D provider, probes it and closes it. The callback returned normally (--counton its unlock landing).A wedged Miles mixer. The DirectSound mixer timer (mss32
0x211136e0, 10 ms, timer table at runtime0x021325cc) runs: a--watch-logon itslock inc/lock decre-entrancy counter ([0x2114f8cc], runtime0x1e338cc) shows ~23k clean 0->1->0 passes on thread T1 in 30k batches. Single memory dumps catch it at 1 because they land mid-pass -- do not read a dump of that counter as "stuck".Silent audio. Sound mixes: thread T1 makes ~314k
IDirectSoundBuffer_GetCurrentPositionand ~119kLockcalls per 60k batches. An earlier "zero DirectSound calls in the scene" was a tracing hole, not a finding: a guest thread's COM calls were logged as<ord>(and dropped entirely under--quiet-api), so no--trace-api=NAMESfilter matched them. Fixed in the same commit as this note; a trace run before it says nothing about guest threads.
Next lead: read how ui.dll's dialogue code decides a line is finished
(UI_StartTalk, ui_skipscene, A3SV_Playback_Skip; scripted playback is
A3SV_Playback_* in planet.dll). anoxsnd.dll polls AIL_sample_status /
AIL_sample_ms_position, so a voice sample that plays but never reports done
is still possible.
Named addresses
ui.dll(base 0x10000000)0x1000de20: sweeps the 8192-entry widget table at0x100e8f60(then0x10055de0) freeing every entry owned by its argument; the hottest code on the Whitendon loading screen.