I spent the last few weeks building cellar, an open source Mac game launcher for Apple Silicon. Along the way I hit problems nobody had documented cleanly. This is the write-up I wish existed.
The problem
Whisky died. CrossOver costs money. Heroic doesn't handle repacks. Nothing on Mac handles the full loop: inspect the archive, set up the Wine bottle correctly for the game's engine, launch with the right D3D backend, make a clickable app out of it.
So I built it.
What actually runs Windows games on Apple Silicon
The short version: Wine alone doesn't work. You need the full stack:
Wine 11.x
→ Apple GPTK (D3DMetal 3.0)
→ Rosetta 2
→ Metal + M4 GPU
D3DMetal is Apple's D3D9/10/11/12 → Metal translator. Without it, D3D9 games NULL-deref on startup and modern Unity titles deadlock at 100% CPU. This isn't optional.
The vertex glitch that took hours to crack
CarX Street was running at 54fps but every car looked broken dark, corrupted metallic surfaces. Not a shader setting issue. Not anti-aliasing. Not Burst.
After ruling everything out, I tried swapping Whisky's bundled D3DMetal 2.0 framework for D3DMetal 3.0 from CrossOver 26.
Vertex glitch: gone.
Root cause confirmed: D3DMetal 2.0's DXBC → Metal AIR shader translator mistranslates min16float (half-precision) types in Unity URP Lit BRDF math. PBR metallic surfaces hit the half-precision path. Matte surfaces don't. D3DMetal 3.0 ships a rewritten translator that handles half types correctly.
Every "fast-math toggle" env var suggested online is hallucinated. There's no runtime knob. The only fix is D3DMetal 3.0.
The Unity 2022 IL2CPP wall — and how to climb it
Modern Unity titles call RoGetActivationFactory for Windows.System.DispatcherQueue. Wine 11.x doesn't implement it. The call is unconditional — you can't trick it with a Windows version flag.
The fix: stage Proton's WinRT DLL family (coremessaging.dll, wintypes.dll, twinapi.appcore.dll, the full windows.* set) into the bottle and register the activation classes pointing at coremessaging.dll. One script handles it:
bashscripts/install-proton-winrt.sh ~/.cellar/bottles/my-game/prefix
The FitGirl IPC wall honest about the limits
FitGirl's compression plugins (cls-lollypop.dll) use Windows shared-memory IPC to talk to a worker process. Wine on Apple Silicon cannot deliver that IPC. I spent days confirming this with full callback tracing before documenting it honestly.
The pure-Rust FreeArc reader I built (freearc-native) can inspect any FitGirl archive natively and extract files using open codecs (lzma, zstd). The lollypop codec chain stays blocked until Wine 12 fixes the shared-memory implementation.
The engine-family profile system
Rather than a script per game, cellar uses a profiles.json that encodes the full recipe per engine family DLL overrides, winetricks set, launch args, runtime prereqs. 10 engine families, 60+ games:
bash# match any game to its profile
./scripts/find-profile.sh "Elden Ring"
Best match: unreal-engine-4-5
proactive bottle setup
./scripts/cellar-install.sh unreal-engine-4-5 "Elden Ring"
clickable .app
./scripts/make-cellar-app.sh unreal-engine-4-5 "Elden Ring"
Frostbite, RAGE, UE4/5, RE Engine, Ubisoft AnvilNext, Bethesda Creation, ForzaTech, Fox Engine adding a new game in any of these families is a profile match, not new code.
What's blocked no false promises
Kernel-mode anti-cheat (Vanguard, EAAC, Hyperion): no Wine support, period.
FitGirl lollypop repacks: IPC deadlock, documented above.
GTA Online / RDR Online: Take-Two bans Wine sessions.
cellar ships a cellar-doctor.sh health check and an analyze-log.sh that matches launch logs against 15 known failure signatures and prints a diagnosis. When something breaks you get an actual answer, not a blank terminal.
The repo
github.com/joemunene-by/cellar MIT, Tauri 2 + Rust + React, macOS 14+ Apple Silicon.
Two games verified working today. The docs are honest about everything else. Issues and PRs welcome.