Alien Shooter (Reflexive)

Local fixture: test/binaries/candidates/reflexive-alien-shooter/game/AlienShooter.exe. The complete companion assets are required (--vfs-include='**/*'). Original installer/provenance and SHA-256 are recorded in reflexive.md and the local candidate manifest. No guest executable or asset patches were made.

DirectSound8 startup (2026-09-29)

Startup originally stopped at DSOUND.DLL#11, IAT 0x47a01c, caller 0x41cb65. This is DirectSoundCreate8, not a capture function; see Wine's export specification and Microsoft's factory contract.

Commit 17a29c31 adds the named factory and ordinal, an IDirectSound8 vtable with the inherited eleven methods and VerifyCertification, full IID matching, initialized speaker state, default playback GUID aliases, aggregation/device errors, and an explicit uncertified software-device result. The initial APIs 3756–3757 became 3886–3887 when the concurrent Glide branch was merged. The vtable registry also requires capacity 0x124 bytes and a regenerated region map. test-directsound-ordinals.js covers the factory, errors, ordinal, vtable slots, versioned QI/refcounts, certification state, and stdcall cleanup.

Native GPU probe and later corruption

Pinned isolated artifact from this investigation: build/alien.wasm (initial DS8), then build/alien-final.wasm (final factory validation and registry size).

node test/run.js \
  --exe=test/binaries/candidates/reflexive-alien-shooter/game/AlienShooter.exe \
  '--vfs-include=**/*' --no-build --wasm=build/alien-final.wasm \
  --headless-gl --no-close --quiet-api --input=50:dlg-click:1,500:keypress:13 \
  --max-batches=30000 --batch-size=200000 --max-seconds=150 \
  --png=/private/tmp/alien-long.png

The initial DS8 GPU probe renders the SIGMA TEAM splash. A longer run reaches a guest NULL call near batch 2597. --no-uop reproduces it. This is a later graphics/locking failure, not evidence against the ordinal fix.

--watch=0x43ff20 --watch-log identifies actual executable-memory overwrite: the RLE sprite writer at 0x417384 stores ARGB pixels into code. Watch stops at 0x417392, previous EIP 0x417322, destination EAX 0x43ff23, row base EDI 0x43fac7. The original first dword 0x5314ec83 becomes 0x53ffa5ae, then 0x08ffa5ae. Static 0x43ff20 is a shared object tick method reached through vtable 0x47a90c, slot 2, from caller 0x4056b6.

The destination comes from a cached locked surface pointer:

Logs: /private/tmp/alien-{long,no-uop,watch,lock}.log. The watch log includes original code/heap dumps at batch 2580 and the exact writer at batches 2607–2608. Native GPU startup also warns about two GL contexts; this can include the capability probe and is not by itself proof of the color-upload error's cause.

Software backend diagnostic

Replace --headless-gl with --d3d9-renderer=software. The first 528 observed LockRects return success, pitch 2560, pBits 0x501a2000; use --trace-at=0x416808 --trace-at-mem=ebp-0x3c:8 to inspect the actual return. An EAX 0x8876086c printed while parked inside the API thunk is only the handler's initial value while an asynchronous operation is pending, and must not be reported as the call's returned HRESULT.

A 6000-batch software run remained active without the native NULL fault but captured a black canvas.

Root cause and corrected rendering

The renderer split one guest device into two host devices. D3D8 CreateDevice creates the primary D3D9 object and changes its vtable to Device8. The internal d3d9_backbuffer_owner requested a Device9 interface wrapper for that same object, which allocated an auxiliary COM pointer. GPU descriptor IDs use the device pointer: Present therefore addressed the primary renderer while LockRect/UnlockRect addressed a second renderer.

The software backend exposed two independent targets: CPU pixels went to one, while the other kept presenting black. Native headless GL created two contexts and encountered GL_INVALID_VALUE (1281) directly from texSubImage2D despite matching 640×480 logical sizes. The failed unlock/next lock then caused the guest's unchecked blit into code.

The fix returns the device's primary COM wrapper from the internal owner lookup. No interface query is needed there. The regression in test-d3d9-color-surfaces.js changes the primary vtable to Device8, verifies a lock reads existing render contents, writes a pixel, unlocks/presents, checks the pixel, and asserts that the host device count never changes.

With that fix, both native GPU and software runs reach the main menu with real rendering and no corruption. Parent-observed native menu screenshot: /private/tmp/reflexive-probe/alien-native-observe.png; software menu: /private/tmp/reflexive-probe/alien-menu.png. Native final surface capture is /private/tmp/alien-native-fixed.png. The live control PNG is preferable to the runner's final surface chooser during loading (the latter can capture the old SIGMA splash while the composited screen shows LOADING).

The route is graphics setup OK, LOADING, SIGMA TEAM, Alien Shooter title, then menu; Enter skips the title. Mouse movement before clicking New Game reaches Select Player.

Mission 01 acceptance

A fresh software run with --tick-ms-per-batch=5 reached Mission 01 and verified character/camera movement and mouse aiming. The default 200ms clock produced extremely slow presentation/input progress while executing Vorbis LSP-to-curve decoding at 0x46b554; changing it mid-run did not immediately recover responsiveness. Start the interactive CLI run at 5ms.

node test/run.js --app=alien_shooter --no-close --quiet-api \
  --d3d9-renderer=software --tick-ms-per-batch=5 --batch-size=200000 \
  --input=50:dlg-click:1 --control=58118 --max-seconds=600

After each screen finishes loading, move before pressing/releasing the normal left mouse button: New Game (98,296), Campaign (320,334), then Continue (535,445) in the Mission 01 equipment screen. Held right arrow moves the character; mouse movement changes the aim. Firing was exercised but no clear projectile was captured, so combat is not certified by this acceptance.

Evidence in /private/tmp/reflexive-probe/: alien-lowclock-campaign2.png, alien-lowclock-gameplay.png, alien-lowclock-move-fire.png, and alien-lowclock-fresh.log. This was CLI software acceptance; browser gameplay has not been verified for Alien Shooter.

Validation limits

The DirectSound ordinal/interface regression passes on the merged source. The added D3D8 identity/pixel assertions pass in the synchronous software branch of test-d3d9-color-surfaces.js. Its later worker branch fails an existing rectangular UpdateSurface-to-backbuffer pixel assertion before reaching the new case. The same failure reproduces on pristine 79483e6a and the combined main source; it is not a passing full-suite result. Logs: /private/tmp/alien-owner-baseline.log, /private/tmp/reflexive-probe/merged-color-surfaces.log.

The merge-time combined source compiled and passed the remaining build gates when the previously documented baseline union-gate failure was excluded. A later standard build of the moving shared worktree stopped at new duplicate Glide grFinish/grFlush handlers (final-main-build.log in the same scratch directory). The canonical artifacts were refreshed with the compiler directly; this is not an entirely green standard-build result.