A profile-based Wine launcher and compatibility toolkit for Windows Steam games on Apple Silicon. This is not a universal compatibility guarantee: each profile records what was tested.
Repository: zigrok/play-it-here.
Original project code and documentation are available under the MIT License. Downloaded dependencies and games retain their own licenses and are not covered by this grant. See THIRD_PARTY.md for dependency sources and redistribution considerations.
./games list
./games steam
./games doctor chaos-child
./games play chaos-child
./games play anonymous-codeRun steam only when this Windows Steam instance is not running. Games still
require your Steam account, license, and installed files. Use games for all
commands, with an explicit profile ID for game-specific operations.
| Profile | Current evidence |
|---|---|
anonymous-code |
Movie memory-retention fix installed; full-game stability not certified |
chaos-child |
Rendered title screen on OpenGL after resetting stale controller configuration; gameplay/audio/save-load not certified |
games is the public entry point and delegates to the game-neutral lib/runner.sh. Shared Wine/Steam helpers live in lib/runtime.sh, download/build tooling in lib/dependencies.sh, and ANONYMOUS;CODE's guarded fixes in lib/compat/anonymous-code.sh. Profiles live under profiles/anonymous-code.sh and profiles/chaos-child.sh. The custom Media Foundation shim remains exclusive to ANONYMOUS;CODE, not a default fix for new games.
See PROFILES.md for adding games, paths, isolation limitations,
and CHAOS;CHILD setup recovery. Steam's own Play button does not apply these
per-process profile settings. Use ./games play GAME for the tested launch.
Repeatable setup and operation: RUNBOOK.md.
Use ./games help for commands. The examples in this section explicitly select
ANONYMOUS;CODE where a game profile is needed.
Dependencies are pinned in dependencies.lock.tsv, with
the optional GStreamer installer in host-dependencies.lock.tsv.
./games doctor anonymous-code
./games steam
./games play anonymous-codeRun steam only when Windows Steam is not already running. play requires it.
The Finder app launches Windows Steam using the same script.
./games play anonymous-code and ./games debug anonymous-code automatically
fit the current main display to the largest exact 16:9 client area. Use
--resolution select to choose interactively from the connected displays and
the 16:9 sizes that fit the selected display:
./games play anonymous-code --resolution select
./games play anonymous-code --resolution select --fullscreen on
./games play anonymous-code --fullscreen on./games configure anonymous-code enables Wine's game-only display capture so
fullscreen covers the display instead of remaining an ordinary borderless
window. Resolution selection does not move the game window: for fullscreen on
an extended display, make that display the macOS main display first. Mirrored
displays appear as one active desktop.
./games display-check anonymous-code reports actual client size and Wine
display data; see the runbook's monitor-change recovery procedure for stale
desktop geometry. Fullscreen launches now compare Wine's cached screen size
with the current macOS main display and stop with a specific recovery command
instead of opening at a stale size:
./games display-refresh anonymous-code
./games steamdisplay-refresh closes every process in this isolated Wine prefix, including
Windows Steam, then verifies that Wine sees the current main display.
Tested through 2026-09-11: macOS 26.6.2, Apple M4 Max.
- Windows Steam updated to client build 1788652215 using Wine 11.16. The initial window titled "Sign in to Steam" rendered black, so its title alone did not establish a usable login screen.
- The login form was subsequently verified by screenshot: account/password fields, Sign In button, and a generated QR code render after applying the in-process GPU helper wrapper below. The user subsequently authenticated.
- The game (Steam app ID 2291020) is installed. Its initial launch crashed at
game+0x22e40aafter failed D3D11 device creation. A game-specific graphics workaround now starts the game and renders its title screen and story scenes. Both DXVK D3D11 and Wine's game-specific Vulkan DXGI backend are required. - Loading a save initially triggered division by zero inside built-in XAudio2. Microsoft's game-local x86 XAudio2 replacement fixed the reported failure: the user confirmed that voices played correctly without crashing after reload.
- A later movie-transition crash exposed missing display-aperture metadata and mismatched NV12 buffer dimensions. A game-local Media Foundation reader shim now passes complete prologue (1,700 frames) and opening (3,885 frames) DXGI-backed decode/conversion tests. The opening movie subsequently played in-game, and the user confirmed correct audio/video with no crash after the movie audio-worker wait workaround described below.
- Longer normal and debug sessions later exhausted the 32-bit low address space after repeated movie readers. The largest failed reservations closely matched installed movie sizes, and a live failed process peaked at 5.1 GB of physical footprint despite low reported GPU use. The reader shim now moves each repaired in-memory movie to a temporary file-backed stream and releases the game's redundant vector and COM stream copies after reader creation. This directly removes the retained allocations from the reproduced failure path, but a full playthrough has not yet certified long-session stability.
- The packaged launch was separately verified with the downloaded local
MoltenVK 1.4.1 driver, without relying on
/usr/local/lib/libMoltenVK.dylib. The feature-level and frame-presentation diagnostics pass. Automated setup safety tests pass. Full-game completion, all movies, and new save creation have not been certified. - No CrossOver bottles, native Steam settings, or system Wine installation were modified. No GPTK components or Wine source patches were installed.
- Microsoft XAudio2 Redist 1.2.13 is installed beside the game as
XAudio2_9.dll, with a game-specific native override. GStreamer 1.26.7 is the tested host runtime.
Open Anonymous Code Wine.app in Finder to launch Windows Steam. Keep the app in this workspace: it locates the runtime relative to itself. The launcher does not need a terminal or a global Wine configuration.
- Runtime:
downloads/Wine Devel.app/Contents/Resources/wine/bin/wine - Active prefix:
~/Library/Application Support/AnonymousCode-Wine-11 - Windows Steam:
drive_c/Program Files (x86)/Steamwithin that prefix. - Current launch logs:
logs/game-<timestamp>-<pid>.logandlogs/steam-<timestamp>-<pid>.log - Historical launch log:
logs/wine-11.log - Steam diagnostics:
logsinside the Windows Steam directory. - Earlier test log:
logs/wine.log(includes initial Wine 11 bootstrap). - Earlier Wine 10 prefix:
~/Library/Application Support/AnonymousCode-Wine
The Wine 10 test stalled after a loader exception. Both stalled test instances
were stopped using their prefix-specific Wine servers. Wine 11 also encountered a
bootstrapper crash during the initial update; dismissing the crash report and
retrying allowed the update to finish and the current 64-bit client to start.
Initial client startup took approximately one minute. Steam's automatic GPU
fallback and explicit software-rendering switches alone still produced a black
login window. Passing -cef-in-process-gpu to Steam was ignored by this client.
The launcher uses -cef-disable-gpu -cef-disable-gpu-compositing for Steam's UI.
In addition, tools/steamwebhelper-wrapper.c
forwards Steam's helper arguments to the original executable and appends
--in-process-gpu directly. This combination made the login form visible.
This is separate from the game's graphics and audio fixes.
The original Valve helper is preserved beside the wrapper as
bin/cef/cef.win64/steamwebhelper.real.exe inside the isolated Steam directory.
The wrapper replaces only that directory's steamwebhelper.exe.
Homebrew mingw-w64 and its dependencies were installed to build it:
x86_64-w64-mingw32-gcc -std=c11 -Wall -Wextra -Werror -O2 -municode -mwindows \
tools/steamwebhelper-wrapper.c -o downloads/steamwebhelper-wrapper.exeTemporary update restriction: the launcher includes -noverifyfiles -nobootstrapupdate -norepairfiles to keep Steam from overwriting the test wrapper.
Do not treat a client with updates suppressed as a permanent setup. These flags
must be removed when restoring normal updates; a new client may require the
workaround to be reapplied and retested against its newly downloaded helper.
The wrapper does not disable CEF's sandbox.
To roll back, first exit this Windows Steam instance. From the workspace root:
./games helper restore
./games steam-updateThe steam-update command omits update-restriction flags. After updating, close
Windows Steam and run ./games helper install before the next ./games steam launch.
The black-window issue may return without the wrapper. The installer preserves
each original helper by hash and does not overwrite a running Steam instance.
- Run
./games doctor anonymous-code,./games probe anonymous-code, and./games smoke anonymous-codeafter setup or dependency changes. - Verify a rendered title screen, load a known save, and play several voiced lines.
- Test skipping, music/effects, movie playback, and save/load using an empty slot.
- Repeat after Steam, Wine, driver, or macOS updates. Never run different Wine versions concurrently against the same prefix.
The WINEDLLOVERRIDES=... %command% examples in Proton reports are Linux shell
syntax, not Windows Steam launch options. Use Wine's environment or per-app
registry settings here. ./games configure anonymous-code installs the verified game-only overrides.
The Committee of Zero patch includes an audio fix but also localization and asset changes. It is not installed; the narrow audio fix was sufficient for the reported voice crash.
tools/mfreadwrite-proxy.c wraps only the two source
reader factories used by this game. Wine's reader omits
MF_MT_MINIMUM_DISPLAY_APERTURE; the game ignores that lookup failure and can
allocate a zero-byte RGB output buffer. It also rounds converter input height
to 1,088 while decoded NV12 frames contain only 1,080 rows. Wine 11.16's color
converter logs these size errors but its fallback still attempts the copies.
The shim supplies the original display aperture and pads 1920x1080 NV12 frames
to 1920x1088, keeping luma/chroma offsets and real DXGI-backed buffers intact.
The game requires IMFDXGIBuffer; replacing it with plain memory caused a
separate null-interface crash during testing. The shim delegates decoding to
Wine, preserves sample timestamps, and leaves movie files unchanged. This is a
game-specific synchronous-reader workaround, not a general replacement for
Media Foundation or a fix for Wine's unchecked converter copies.
The 32-bit game normally keeps three copies/references to each repaired movie:
its source byte vector, an IStream, and an IMFByteStream retained alongside
the source reader. Installed movies are as large as 170 MB. Repeated readers
eventually left Wine unable to reserve contiguous 64-192 MB regions even though
DXVK reported less than 700 MB in use. For byte-stream readers, the shim now
copies the repaired stream to a temporary file, creates Wine's reader from that
file, and releases the redundant game-owned vector and stream objects. Reader
creation/destruction and active-reader counts are logged for further lifetime
validation. Temporary files are deleted when their readers are destroyed.
The release optimization is restricted to the inspected game.exe
(df373c4d6e1e050e8ad36bf1842cf7620818da5687a70f9d0bb4daa450a2f4ef)
and checks the relevant call site and allocator instructions in memory before
touching the game object. An updated executable keeps the file-backed reader
but skips the game-layout optimization unless those guards are deliberately
reviewed and updated.
The pinned DXVK allocator was also inspected and rebuilt from its tagged source.
Its 64-192 MB failures use dedicated host-visible allocations, not the pooled
chunk-size path, so reducing DXVK's chunk size would not address this crash.
No speculative DXVK replacement is installed. If the failure recurs after the
movie-storage fix, the next reversible experiment is
d3d11.maxDynamicImageBufferSize = 16384; it can reduce discard allocations
for large single-plane textures, but does not affect multi-plane NV12 textures
and therefore must be measured in-game before becoming a default.
The movie audio-feed worker also repeatedly faulted at wow64cpu+0x1135 while
entering NtYieldExecution. The shim redirects the game's SwitchToThread
import to Sleep(1) in memory; the inspected executable uses that import only
in this worker's idle loop. This avoided the observed failure. The exact runtime
cause remains unresolved: an isolated 800,000-yield test passed. The hook checks
the game worker's instructions and import target and refuses a changed layout.
The executable on disk and the native XAudio2 implementation are not modified.
With the game closed:
./games build
./games media anonymous-code install
./games doctor anonymous-code
./games media-probe anonymous-code 'C:\Program Files (x86)\Steam\steamapps\common\AC\windata\movie\op_en.mzv'The probe reads installed .mzv assets into memory, repairs their MP4 header
only in that copy, then releases that memory stream after the shim creates its
file-backed reader. It exercises the byte-stream reader and NV12-to-RGB converter.
It does not play audio or certify rendering, A/V synchronization, or all movies.
Its 60-second watchdog returns failure if decoding hangs. Successful tests on
this host still emit GStreamer Python-plugin/fixed-caps warnings.
./games media anonymous-code restore removes only the recognized shim and its game-specific
override, retaining the working graphics/audio fixes. Installation records the
DLL hash under the prefix's ac-setup-state; updates and removal refuse unknown
or modified DLLs. No on-disk game executable or asset patch is installed.
The crash at 0x0062e40a dereferences a null device after three unsuccessful
D3D11CreateDevice calls. The game requests feature levels 11.1, 11.0, 10.1,
and 10.0 with BGRA support. The 32-bit diagnostic
tools/d3d11-probe.c reproduces those calls: Wine's default
backend and its Vulkan backend returned 0x80004005 for all three driver types.
A separate lower-feature test succeeded only at 9.3 on the default backend.
Gcenx's macOS DXVK 1.10.3 repack successfully created feature-level 11.0 devices
in the exact-match probe. Its x86 d3d11.dll was copied beside AC/game.exe
(no preexisting DLL was present), with this per-application registry setting:
HKCU\Software\Wine\AppDefaults\game.exe\DllOverrides
d3d11 = native,builtin
xaudio2_9 = native,builtin
gameoverlayrenderer =
gameoverlayrenderer64 =
HKCU\Software\Wine\AppDefaults\game.exe\Direct3D
renderer = vulkan
No DXGI replacement or global graphics override is installed. The game and
diagnostics each have their own Vulkan renderer registry setting. To undo only
the recognized game fixes, exit the game and run ./games rollback anonymous-code. Unknown DLLs
or conflicting registry values cause a refusal rather than being overwritten.
i686-w64-mingw32-gcc -std=c11 -Wall -Wextra -Werror -O2 \
tools/d3d11-probe.c -ld3d11 -o downloads/d3d11-probe.exeDownload: DXVK macOS release.
Archive SHA-256 recorded locally (not compared to a published checksum):
acd1520ad105d8ef124a09c8e11a259a5dc8bdc565ad18e0e52693f9807b2477.
Installed x86 DLL SHA-256:
f79d417f675d00008375eb4ea2318af6fcd0b27971d604d3cf345a0dcfa998a0.
These results preceded the successful combination above. The later filtered
trace located the transition failure in OpenGL initialization inside Wine's
DXGI factory. Selecting renderer=vulkan for game.exe avoided that path.
The normal Steam launch briefly created the game window, then exited with
virtual_setup_exception nested exception on signal stack. A direct traced
launch confirmed native AC/d3d11.dll loading and a 1920x1080 DXVK swap chain,
but remained black after an access violation near wow64cpu+0x1135.
Steam's overlay was loaded in that run; causality is not established.
Diagnostic comparisons on this host:
| Configuration | Result |
|---|---|
| Overlay DLLs disabled, default Vulkan discovery | Black window; native exception handling visible in a thread sample |
| Wine-bundled MoltenVK manifest only | Exact D3D11 probe fails all three calls; game returns to game+0x22e40a |
Existing /usr/local/share/vulkan/icd.d/MoltenVK_icd.json only |
Probe succeeds at 11.0; game crashes at wow64cpu+0x1239, read address 0x2dc1 |
Default Vulkan discovery loaded both bundled and system MoltenVK libraries. Selecting one driver avoided that ambiguity but did not fix startup. No system driver files were removed or modified. Wine's automatic debugger attached to the last crash but did not produce a usable backtrace during inspection.
For current reproduction, run ./games debug anonymous-code. It uses the same local pinned
graphics/audio configuration as ./games play anonymous-code, with extra DLL/exception tracing
and a unique log. The app's legacy --debug-game argument delegates to it.
Evidence: logs/game-startup.log (appended across diagnostic runs),
logs/game_d3d11.log (latest DXVK output), and
logs/game-black-screen.sample.txt (overlay-disabled/default-driver hang).
Hung test game processes were terminated inside the isolated prefix; Steam and
other Wine setups were not intentionally stopped. These historical logs predate
the final XAudio2 fix.
- Valve Windows Steam installer
SHA-256:
7d3654531c32d941b8cae81c4137fc542172bfa9635f169cb392f245a0a12bcb - Wine 11.16 macOS release
Archive:
wine-devel-11.16-osx64.tar.xz. Published SHA-256, verified before extraction:6f9af818b7af6001aeed7818cb32bf0155598c5ea4e3b33380a03cf814e033cd. Requires Rosetta on Apple Silicon and the release's GStreamer prerequisite; this Mac has GStreamer 1.26.7 installed. Movie coverage remains incomplete. - Steam black-window discussion and maintainer corrections
Motivated direct
--in-process-gpuinjection. The thread's initial claim that a missing crypto API caused login failure was later retracted as unproven. - Microsoft XAudio2 package
Extracted
build/native/release/bin/x86/xaudio2_9redist.dllunderdownloads/xaudio2; archive integrity and PE32 architecture checked. - Voice workaround discussion
- Proton issue: audio and Media Foundation analysis
- Committee of Zero patch
The current verified scope is listed above; this is not a full-game certification. Do not upload Steam credentials, prefix contents, game files, or unredacted logs.