MetalSharp / Reference

Documentation
with a clear path.

Start with the install path, choose a route with evidence, or go deep on the runtime. This index is generated from the current MetalSharp source docs.

0.78.0Apple siliconmacOS 15+
Using MetalSharp

User Guide

Install, play, Sharp Library, controllers, streaming, and logs.

6 sections · 4 min read

Quick startDownload the DMG, open MetalSharp, start Wine Steam, install a game, then choose Play. Each game’s managed setup is called a bottle.

Updated: 2026-10-05

Install

  1. Download the DMG from Releases and drag MetalSharp into /Applications, or run brew install --cask metalsharp/tap/metalsharp.
  2. Open it. If macOS blocks it, go to System Settings → Privacy & Security → Open Anyway.
  3. Let the setup wizard install the runtime (Wine, graphics libraries, redistributables). Homebrew is not required.
  4. Start Wine Steam, sign in, and install a Windows game.

Steam Games

Click Play on the Library page. MetalSharp picks a graphics route automatically; if a game doesn't run, pick another from the launch mode dropdown. The routes are listed in the README, and tested picks are in Supported Games.

Steam-Emu (Goldberg) lets supported games, mostly older ones, run offline without Wine Steam. Turn it on from the game card. MetalSharp backs up the original Steam DLLs as .orig and restores them when you turn it off.

Choose EXE: For Steam and Ubisoft games, open the game's Settings popover in the Library hero. For GOG and Epic games, use the gear on the game card. Pick an .exe inside the game's install folder. The choice is saved for that game.

Other Stores and Programs

The Sharp Library holds Windows apps, launchers, installers, and non-Steam games. It has tabs for Epic, GOG, Game Jolt, and emulators. For Ubisoft Connect, see the Ubisoft Connect guide.

  • Install Windows Program takes an .exe or .msi. MetalSharp imports it directly, or runs the installer in its own bottle and picks up the installed app.
  • Rename an app with the pencil next to its title, and change its cover with Change Image (JPEG, PNG, or WebP).
  • Tools → Open Folder opens the app's bottle in Finder. Install runtime components through the bottle tools; don't copy them in by hand.

Controller Input

The Controller selector in the sidebar sets which input shims MetalSharp deploys to the game folder and Steam prefix:

  • Off (default): none.
  • X: XInput (xinput1_1–xinput1_4.dll, xinput9_1_0.dll).
  • D: DirectInput (dinput.dll, dinput8.dll).

Switching modes removes the previous set. If the game shipped its own copy of a shim, MetalSharp backs it up and restores it.

Game Streaming

MetalSharp can stream games to a phone or tablet using Sunshine on the Mac and Moonlight on the device.

  1. Click Stream in the footer, then Install Sunshine, then Start Streaming Host.
  2. Install Moonlight (iOS, Android) and put the device on the same Wi-Fi network as the Mac.
  3. In Moonlight, tap your Mac. Enter the PIN it shows into MetalSharp and click Pair Device.

Sunshine on macOS is experimental, and gamepads don't work yet, so use Moonlight's touch controls. Approve Screen Recording for Sunshine on first use, or the stream will be black. Sunshine uses ports 47984–48010 (TCP and UDP). If you change the login in Sunshine's web UI (https://localhost:47990), delete ~/.metalsharp/streaming-creds.json and restart streaming. PINs work once; if pairing fails, tap the Mac again in Moonlight to get a new one.

Logs, Settings, and Uninstall

  • Logs has live logs, crash reports, and recent log files.
  • Settings covers Steam API sync, backend restart, cache cleanup, runtime maintenance, and updates. Updates download, verify, then ask before closing MetalSharp and stopping Steam/Wine.
  • Settings → Danger Zone → Uninstall MetalSharp removes all prefixes, bottles, Steam, the runtime, caches, and settings, then moves the app to the Trash.
Using MetalSharp

Ubisoft Connect

Install Ubisoft Connect and play its games.

Reference note · 2 min read

Updated: 2026-10-05

  1. Install. Open the Start Steam dropdown in the library header and choose to install Ubisoft Connect. MetalSharp creates a Ubisoft prefix and runs the installer. Keep the default install location so MetalSharp can find it.

  2. Start and stop. Once installed, the dropdown shows Launch Ubisoft. Use the same dropdown to stop it.

Launch Ubisoft in the dropdown Stop Ubisoft in the dropdown

  1. Install games. Sign in and install a game. External drives show up in Ubisoft Connect as drive Y:. To install to one, click Change Location, choose the drive shown as /, go to Volumes/<drive name>/, and create a Ubisoft folder.

Change Location Select the drive Create the Ubisoft folder

  1. Play. Installed games appear in the main library. Pick a launch route and press Play. Ubisoft Connect starts on its own if it isn't already running. Stop a game with Stop or ⌘⌥Q.

Ubisoft game in the library

Using MetalSharp

Emulators

PCSX2, RPCS3, shadPS4, and SharpEmu in the Sharp Library.

5 sections · 5 min read

Updated: 2026-10-05

MetalSharp installs and runs four console emulators from the Sharp Library. Each one runs out of process in its own managed environment, separate from any standalone install of the same emulator.

Emulator Console Status
PCSX2 PlayStation 2 Stable
RPCS3 PlayStation 3 Stable
shadPS4 PlayStation 4 Experimental — a listed game may not be playable
SharpEmu PlayStation 5 Experimental research — most games do not run

BIOS, firmware, and games must come from hardware and content you own. MetalSharp ships none of them.

Managed Environment

Every emulator uses the same layout under ~/.metalsharp/emulators/<name>/:

├── current -> versions/<tag>     # active build
├── previous -> versions/<tag>    # rollback target
├── versions/<tag>/               # read-only emulator build
├── home/                         # isolated HOME: config, saves, caches
├── downloads/  staging/  sessions/  logs/
├── environment.json  library.json
  • Isolation: the emulator runs with home/ as HOME, so its settings, saves, and caches never touch a standalone install.
  • Updates: MetalSharp downloads only official stable releases. It verifies size and SHA-256, rejects unsafe archives, checks the binary, and switches current atomically. If any step fails, the active version stays put. Activation waits until running games exit.
  • Controls: check for updates, install, pin the current build, skip a release, roll back to previous, or remove the runtime.
  • Removal: removing the runtime deletes only the version trees and downloads. Home data, logs, and your game folders stay.
  • Game folders: added folders are references. Removing one only edits library.json; nothing on disk is deleted. Symlinked directories are skipped.
  • Processes: each launch gets its own process group, log, and session record. Stop and status check that the PID still belongs to the emulator, and records survive backend restarts.

The backend API is GET /emulators plus /sharp-library/<name>/... routes for status, games, scan, launch, stop, and update actions.

PCSX2 (PS2)

Uses the official signed and notarized macOS app, which runs under Rosetta on Apple Silicon. The upstream signature is checked again at every launch.

  1. Open Sharp Library → PCSX2 and install the runtime.
  2. Import a BIOS dumped from your own PS2 (guide). Only .bin files from 4 to 8 MiB with a recognized region are accepted.
  3. In PCSX2 Setup, choose the controller for ports 1 and 2 and the renderer (Automatic, Metal, OpenGL, Vulkan, or Software).
  4. Add a disc image or a game folder (dumping guide).

Files: ISO, BIN, IMG, MDF, GZ, CSO, ZSO, CHD, and homebrew ELF. Picking a single file indexes only that file; picking a folder scans it recursively.

Differences:

  • PCSX2's own updater is turned off.
  • A configuration backup is taken before each update.
  • Rolling back warns first because savestates can depend on the version.
  • Titles come from the disc serial when the image is uncompressed, and from the filename otherwise.

RPCS3 (PS3)

  1. Open Sharp Library → RPCS3 and install the runtime. Apple Silicon and Intel Macs each get their matching official build.
  2. Download Firmware opens Sony's PS3 update page. Install the downloaded PS3UPDAT.PUP from MetalSharp.
  3. Install PS3 packages (.pkg) or add folders of game dumps.

Differences:

  • Games are discovered in the isolated dev_hdd0/game folder and in any folders you add. Title and ID come from PARAM.SFO, and ICON0.PNG is used as artwork.
  • Games launch fullscreen with no RPCS3 GUI.
  • Release metadata is cached for 12 hours. Check RPCS3 skips the cache.

shadPS4 (PS4) — experimental

Requires Apple Silicon with Rosetta 2. The official macOS build is x86_64.

  1. Open Sharp Library → shadPS4 and install the runtime.
  2. Add folders containing your own game dumps. A game is found when its folder contains CUSAxxxxx/eboot.bin and sce_sys/param.sfo. Patch folders are de-duplicated.
  3. Optionally, import decrypted firmware modules (.sprx) and fonts from a console you own.

Differences:

  • Upstream macOS builds are unsigned, so MetalSharp ad-hoc signs them locally after verifying them.
  • The emulator runs from its version folder so its bundled Vulkan driver (KosmicKrisp) resolves correctly.

SharpEmu (PS5) — experimental

Requires macOS 26+, Rosetta 2 on Apple Silicon, lsar/unar, and at least 1 GiB of free space. Vulkan runs through the bundled MoltenVK.

  1. Open Sharp Library → SharpEmu and confirm the install.
  2. Add game folders. A game is found when its folder contains eboot.bin (a decrypted ELF or fake-signed SELF) with a param.json, and its title ID has the form PPSA#####.

Differences:

  • The layout adds state/ (saves, configs, folders), cache/ (.NET, AMPR, Vulkan), and writable/. All of them survive updates and removal.
  • The upstream archive is unsigned and its release asset can change in place. MetalSharp pins the exact release and asset IDs, quarantines changed metadata, and ad-hoc signs the build locally.
  • Networking is off by default. Games run under sandbox-exec with all network access denied, and launch fails if the sandbox can't be confirmed. Turning on Allow unrestricted guest networking asks for a second confirmation at every launch.
  • A game whose eboot.bin changed since the last scan must be rescanned before it can launch.
Using MetalSharp

Supported Games

Tested titles and the route each one uses.

7 sections · 4 min read

Updated: 2026-10-06

Games confirmed playable by users or the developer, grouped by launch route. Many more likely work. Tested on an M4 MacBook Air (16 GB) from an external USB-C SSD. Routes are described in the README.

Sharp Library Sources

Source Tested game Route
GameJolt The Joy of Creation: Reborn DXMT
GOG Fall of Porcupine: Prologue DXMT

D3DMetal - D3D12, D3D11, D3D10 through Apple's D3DMetal Framework

Game AppID Notes
Elden Ring 1245620 Offline Play
ARMORED CORE VI FIRES OF RUBICON 1888160 Offline Play
High On Life 1583230 Online Play
Cyberpunk 2077 1091500 Online Play
Ghostrunner 1139900 Online Play
Star Wars Jedi: Fallen Order 1172830 Online Play
Control: Ultimate Edition 870780 Online Play
BeamNG.drive 284160 Online Play
MECCHA CHAMELEON 4704690 Online Play
Sons Of The Forest 1326470 Online Play
Subnautica 2 1962700 Online Play
Overwatch 2 2357570 Online Play
Sekiro: Shadows Die Twice 814380 Online Play
Sonic Frontiers 1237320 Online Play
Black Myth: Wukong 2358720 Online Play
Borderlands 3 397540 Online Play
The Witcher 3: Wild Hunt 292030 Online Play
Yu-Gi-Oh! Master Duel 1449850 Online Play
Palworld 1623730 Online Play
Hogwarts Legacy 990080 Online Play
HELLDIVERS 2 553850 Online Play, Medium Settings
Assassin's Creed Odyssey 812140
Eve Online 8500 Launches through steam with Specific Flags that enable the launcher to load
Far Cry 5 Ubisoft
Far Cry 6 Ubisoft
TrackMania Ubisoft
Marvel Rivals 2767030 Launches through Steam with -Windowed to avoid a mouse bug
Baldur's Gate 3 1086940
Watch Dogs Legions Ubisoft
Red Dead Redemption 2 1174180 Starts the Rockstar Games Launcher directly with WFDXCompat; also runs on VKD3D (Vulkan)
Grand Theft Auto V Enhanced 3240220 Story mode. Starts the Rockstar Games Launcher directly; BattlEye launcher swapped for the game exe; Agility SDK D3D12 loading through WFDXCompat; also runs on VKD3D

VKD3D - D3D12, D3D11, D3D10, D3D9 -> Vulkan -> Metal

Game AppID Notes
PEAK 3527290 Working
Hollow Knight: Silksong 1030300
Schedule I 3164500
Dark Deception 332950
Portal2 620 Steam-Emu Required
Mirror's Edge 17410 Sync-loading mitigation active.
Half-Life 2 220
Among Us 945360 Steam online play.
Dwarf Fortress 975370
Caves Of Qud 333640
Skyrim: Special Edition 489830

DXMT — D3D11, D3D10 to Metal

Game AppID Notes
Dark Souls 3 374320
Repo 3241660
Cult of the Lamb 1313140
The Wilds 1028590
The Long Dark 305620 Ultra settings verified.
Subnautica 264710
Subnautica: Below Zero 848450
Rain World 312520
Hollow Knight 367520
Party Animals 1260320 Save bottle, launch direct with Steam.
Dave the Diver 1868140
Totally Accurate Battle Simulator 508440
Skul: The Hero Slayer 1147560
Crab Game 1782210
SkyIsland 2302640
Lethal Company 1966720
Insurgency 222880 Launch with -steam -secure flags.
Graveyard Keeper 599140
Brawlhalla 291550
PlateUp! 1599600
Nine Sols 1809540
Besiege 346010
AmongUs 945360
Team Fortress 2 440
Amid Evil 673130
Octopath Traveler II 1971650
Mind Scanners 1389550
Dredge 1562430
Blasphemous 2 2114740

DXMT (32-bit) — D3D11, D3D10 to Metal, 32-bit prefix route

Game AppID Notes
Hades 1145360
The Binding Of Isaac: Rebirth 250900
Ori and the Blind Forest: Definitive Edition 387290
Nidhogg 2 535520
Balatro 2379780

D3D9 - Wine i386 D3D9 W/ x87 Acceleration

Game AppID Notes
Portal 2 620 Steam-Emu Required
Half Life 2 220
Undertale 391540

Mono/FNA — XNA/FNA/MonoGame

Game AppID Notes
Celeste 504230 FNA/XNA assets, FMOD shims, Steamworks shim. x86_64 Mono. Install wizard fallback paths for steam_api detection.
Terraria 105600 TerrariaLauncher/patcher support, x86_64 Mono, XNA/FNA assemblies.
Working on MetalSharp

Architecture

Launch routes, Wine runtime, bottles, and bundles.

12 sections · 11 min read

Updated: 2026-10-06

How MetalSharp launches games, what it installs, and where state lives. Build instructions are in development.md and building-wine.md; emulator providers are in emulators.md.

Launch Flow

The C backend (127.0.0.1:9274) owns every launch. Route selection is data-driven: adding a game means editing configs/mtsp-rules.toml, not backend code.

Play clicked
  -> app calls the backend (e.g. POST /steam/launch-game {"appid":<appid>,"launchMethod":"<pipeline>"})
  -> backend resolves the route: TOML rule -> managed/FNA eligibility -> PE imports -> directory heuristics -> fallback
  -> backend syncs and preflights the bottle, builds a LaunchRecipe
  -> backend copies route DLLs beside the game executable and sets Wine/DYLD/cache env
  -> Wine (or native Mono) starts the game; Wine Steam stays alive in the background for Steamworks

Pipeline endpoints: GET /mtsp/pipelines, POST /mtsp/prepare.

Graphics Routes

Route APIs Translation chain DLLs staged beside the game
D3DMetal D3D12 / 11 / 10 DirectX -> D3DMetal -> Metal Managed GPTK payload DLLs
DXMT D3D11 / 10 (D3D12 lane) DirectX -> DXMT -> winemetal -> Metal d3d11.dll, dxgi.dll, d3d10core.dll, winemetal.dll; D3D12 adds d3d12.dll from lib/dxmt_m12
DXMT (32) 32-bit D3D11 / 10 DirectX -> DXMT i386 -> Metal i386 DXMT payload
D3D9 D3D9 Wine i386 D3D9 with x87 acceleration (DXMT launch/cache family) lib/dxmt
VKD3D D3D12 / 11 / 10 / 9 DirectX -> vkd3d-proton / DXVK -> MoltenVK -> Metal d3d12.dll, d3d12core.dll (vkd3d-proton); d3d11.dll, d3d10core.dll, d3d9.dll, dxgi.dll (DXVK); MoltenVK.dylib, MoltenVK_icd.json
Mono/FNA XNA / FNA / MonoGame Managed code -> native Mono + FNA -> Metal (no Wine) See Mono/FNA below

Internal route and lane IDs used in code, configs, and cache paths:

ID Meaning Payload Shader cache
m12 D3D12 via DXMT lib/dxmt_m12 shader-cache/m12/<appid>/
m11 D3D11 via DXMT lib/dxmt shader-cache/m11/<appid>/
m10 D3D10 via DXMT lib/dxmt shader-cache/m10/<appid>/
m9 D3D9 (auto-selected) lib/dxmt shader-cache/m9/<appid>/
DXMT Auto-router selecting D3D12/11/10/9 from rules and PE evidence
M32 32-bit Wine fallback for diagnostics/legacy records
Steam, MacOS Steam Windows / native Steam client handoff for bootstrap and diagnostics
WineBare Plain Wine for installers and custom apps

Route switching: graphics cleanup removes only files that byte-match a managed payload (including D3DMetal's nvngx-on-metalfx.dll); modified or game-provided files stay. If a payload is incomplete, repair the runtime install.

D3DMetal uses ~/.metalsharp/runtime/d3dmetal-gptk4-beta2/ with the shared Steam prefix. Saving the route stages its DLLs; Play refreshes them, reconciles controller shims, and launches with SteamAppId/SteamGameId. D3DMETAL_FRAMEWORK_PATH names the framework executable; keep PE DLLs, Unix files, and framework from the same payload. Wine Steam started with Launch Steam inherits the D3DMetal environment; a running client is reused, not restarted.

Shader presets: shader-presets/dxmt-metal/ (M11) and shader-presets/dxmt-metal12/ (M12) hold pre-compiled caches named <appid>.db (SQLite cache_18 table of metallib blobs, air64-apple-macosx15.0.0, MSL 3.2). On launch deploy_preset_cache() copies a preset if no user cache exists, otherwise merges with INSERT OR IGNORE. To add one, copy ~/.metalsharp/shader-cache/<engine>/<appid>/shaders_320.db into the matching folder as <appid>.db.

Wine Runtime

Wine 11.17, x86_64 Unix host (Rosetta) with i386 + x86_64 PE WoW64. Check with ~/.metalsharp/runtime/wine/bin/wine --version; the version string alone doesn't identify the patched build, so see building-wine.md.

  • MSYNC: WINEMSYNC follows the msync setting; the wineserver keeps its choice for its lifetime, so shut down managed Wine before changing it.
  • GSBASE: one canonical ntdll.so. DXMT enables the TEB/TSD swap; D3DMetal and bare Wine keep legacy behavior unless WINE_MACOS_GSBASE_SWAP=on.
  • Retina: on by default for the shared prefix (RetinaMode, 192 DPI; off restores 96). Restart Wine Steam after changing it.
  • metalsharp-wine wrapper preserves the backend's route-specific WINEDLLPATH, DYLD_FALLBACK_LIBRARY_PATH, and Vulkan ICD settings.

WineMetalGL (OpenGL) is built into the runtime from WineMetalGL v0.1.0 (WineMetalGL-0.1.0.tar.zst, SHA-256 1796266dc1fe43bb15050851d2a3f43d53c1ae961c7596b193cc9b47a465a553). The sidecar metalsharp-opengl.dylib is rebuilt as x86_64 and loaded by winemac.so; guest opengl32.dll ships for x86_64 and i386. Compatibility GL is the default; VKMT_OPENGL_METAL_EXPERIMENTAL=1 enables the GLSL/SPIR-V/Metal path. Build with tools/bundles/build-winemetalgl-x86.sh OUTPUT_DIR; artifact hashes are in tools/bundles/wine-runtime-hashes.tsv.

Launch Environment

Variable Purpose
WINEPREFIX Prefix location
WINEDLLPATH, WINEDLLOVERRIDES PE DLL lookup and native/builtin overrides
DYLD_FALLBACK_LIBRARY_PATH Unix library lookup for Wine and DXMT
ROSETTA_ADVERTISE_AVX Rosetta reports translated AVX/AVX2 (no emulation added)
DXMT_SHADER_CACHE_PATH, DXMT_CONFIG_FILE DXMT cache and config
D3DMETAL_RUNTIME_DIR, D3DMETAL_FRAMEWORK_PATH D3DMetal payload root and framework executable
SteamAppId, SteamGameId Steam identity for direct launches
WINEMSYNC MSYNC selection

Filesystem Layout

~/.metalsharp/
├── runtime/
│   ├── wine/
│   │   ├── bin/                  metalsharp-wine, wine, wineserver
│   │   ├── lib/wine/             x86_64-unix, x86_64-windows, i386-windows
│   │   ├── lib/dxmt/             DXMT baseline (x86_64-unix, x86_64-windows)
│   │   ├── lib/dxmt_m12/         isolated D3D12/DXGI/winemetal
│   │   ├── lib/moltenvk-vkmt/
│   │   └── etc/                  dxmt.conf, vulkan/icd.d/MoltenVK_icd.json
│   ├── d3dmetal-gptk4-beta2/     wine/x86_64-windows, external/D3DMetal.framework
│   ├── mono-arm64/, mono-x86/    native Mono lanes
│   ├── host/                     host runtime ABI dylib + manifest
│   └── redist/                   redistributable sources
├── prefix-steam/                 shared Wine Steam prefix (external libraries mount by drive letter)
├── prefix-ubisoft/               Ubisoft Connect prefix
├── bottles/
│   ├── steam_<appid>/bottle.json readiness record pointing at prefix-steam
│   ├── epic_<appName>/prefix     per-title Epic prefix + manifest
│   ├── gog-prefix/prefix         shared GOG prefix
│   └── <id>/prefix               Sharp Library installer/app bottles
├── compatdata/<appid>/           metalsharp-compatdata.json, logs/launch-<ts>.log, assets/
├── epic/                         legendary/ state, library.json, artwork/, processes/
├── tools/legendary/legendary-0.21.0
├── sharp-library/  games/  launcher-games/epic/location.txt
├── shader-cache/<lane>/<appid>/
├── cache/                        bundles/, ubisoft-connect/artwork/, thegamesdb_config.json
└── logs/

Runtime Bundles

Assets come from the bundles release, into app/bundles/ when packaging and ~/.metalsharp/cache/bundles/ for installer fallback. Hashes are tracked in tools/bundles/asset-manifest.tsv.

Asset Contents
metalsharp-graphics-dll.tar.zst Graphics/dll/dxmt/ (DXMT v0.80 baseline, the only source for DXMT payloads), dxvk/, vkd3d-proton/, d3dmetal/, wfdxcompat/ (the WFDXCompat launcher companion, staged to ~/.metalsharp/runtime/wfdxcompat/), and wfdxcompat-agility/ (WFDXCompat with its D3D12 frontend for Agility SDK titles, staged to ~/.metalsharp/runtime/wfdxcompat-agility/)
metalsharp-runtime.tar.zst Patched Wine 11.17, host ABI, managed payloads including D3DMetal
metalsharp-assets.tar.zst Mono, Goldberg, EAC toggle, shims
metalsharp-scripts-tools.tar.zst Updater scripts, configs, native tools, CEF helpers
metalsharp-steam.tar.zst Steam installer and CEF wrapper

Both DXMT lanes record state in metalsharp-dxmt-runtime.json (schema metalsharp.dxmt-runtime.v2, baseline 0.65.0-dxmt-v0.80-baseline-v1). Diagnose drift with hashes, not version strings:

tools/bundles/verify-bundles.sh --require mac
tools/bundles/verify-bundles.sh --release
tools/bundles/verify-developer-sdk.sh dist/developer-sdk/metalsharp-d3d12-developer-sdk.tar.zst

Steam

Wine Steam owns account, downloads, and sessions; MetalSharp owns the game process, bottle, compatdata, logs, and runtime assets. macOS Steam has no Proton-style compatibilitytools.d, so MetalSharp launches the game itself.

  • Launch: POST /steam/launch -> steam::launch_wine_steam(); games go through POST /steam/launch-game -> prepare_steam_pipeline_env().
  • Steam API staging: steam_api64.dll, steam_api.dll, steamclient64.dll, steamclient.dll, GameOverlayRenderer64.dll, GameOverlayRenderer.dll beside the executable when available.
  • CEF wrapper: ensure_steam_launch_ready() redeploys steamwebhelper.exe (original kept as steamwebhelper_real.exe, marker .ms_wrapper_deployed) in prefix-steam/.../Steam/bin/cef/cef.win64/ whenever Steam overwrites it. If login stops rendering, verify the wrapper hash before changing launch args.
  • Native macOS games: installs containing a .app are labeled Native macOS, skip the bottle/route selector, and launch via open.

Compatdata is the per-game launch ledger; the Steam game bottle is the repair surface. POST /steam/compatdata {"appid":620,"pipeline":"vkd3d"} ensures the bottle, refreshes assets, and returns the record. Fields: appid, name, bottle_id, prefix_path, steam_prefix_path, install path, runtime profile, launch_pipeline, steam_identity_mode, compat_tool_name, launch_command_template, log dir, detected assets/components, and last launch log/pid/status.

Launchers and Stores

The installer classifier detects known launchers before generic .NET/WebView/MSI/PE heuristics, records known_launcher:<id> and launcher_name:<name> hints, and installs them through bare Wine so launchers never inherit game routes. Child games get their own bottle and route.

Launcher Profile
Minecraft Launcher Java Launcher
EA App / Origin, Ubisoft Connect, Epic Games Launcher, Rockstar / Social Club WebView (Gecko, WebView2, .NET 4.8, VC runtime, core fonts)
GOG Galaxy Launcher

CEF launchers (libcef.dll, chrome_*.pak, vk_swiftshader.dll, app.asar): the original becomes <name>_real.exe, <name>.exe is a wrapper relaunching with --in-process-gpu --disable-gpu, and metalsharp-cefchildhook.dll covers child processes. Evidence: POST /launcher/evidence {"family":"ea"|"ubisoft"}.

Store How it works Backend routes
Ubisoft Connect Installer from https://ubi.li/4vxt9 into prefix-ubisoft with D3DMetal + Steam's CEF args; a writable external volume maps to Y:. Games are found from the prefix registry; artwork falls back Ubisoft -> SteamGridDB -> extracted icon. Per-game routes; Mono/FNA and Steam Emu hidden. GET /ubisoft/status, /ubisoft/library; POST /ubisoft/launch, /stop, /launch-game, /save-pipeline
Epic Legendary 0.21.0 (arm64, size/hash-verified) out of process. Each title needs Initialize Bottle; mouse mode No Recenter (default) or Mouse Auto. Optional TheGamesDB key supplies missing artwork. GET /sharp-library/epic/{status,games,running,thegamesdb-api-key}; POST install-tool, auth, thegamesdb-api-key, logout, sync, install, progress, cancel, initialize, play, stop, stop-all, uninstall
GOG Shared bottles/gog-prefix; Stop runs wineserver -k on it, so it stops every GOG game.
Game Jolt Isolated process groups; routes D3DMetal, VKD3D, DXMT, DXMT (32), D3D9.

Sharp Library apps run in isolated process groups tracked by app ID; Cmd+Opt+Q sends stop-all for every store. Runtime migration preserves the Ubisoft, Epic, and GOG prefixes and data, then runs wineboot -u and verifies the dosdevices/c: and z: mappings (failure is an explicit error).

Redistributables

Bottle components: vcrun2019, directx_jun2010, dotnet48, webview2, openal, xna, physx, wine-mono, gecko, corefonts. Graphics components (d3d9, d3d10, d3d11, d3d12, dxgi) verify from the runtime.

Steam bottles infer needs from _CommonRedist, CommonRedist, and installscript.vdf (e.g. DXSETUP.exe, xnafx40_redist.msi, oalinst.exe). Repair sources, in order: Steamworks Shared _CommonRedist in prefix-steam, bottles/*/installers/, runtime/redist/. MSIs run via msiexec /i, EXEs with known silent args; each repair writes a per-bottle log.

Mono/FNA

Lane Runtime Version Used for
Wine Mono runtime/wine/share/mono/wine-mono-11.3.0 11.3.0 CLR/bootstrapper apps inside Sharp Library bottles
Native Mono ARM64 runtime/mono-arm64/bin/mono 6.14.1 Terraria-style FNA games
Native Mono x86_64 runtime/mono-x86/bin/mono 6.12.0 Celeste-style legacy FNA games (Rosetta)

The route stages FNA/XNA assemblies, native FNA3D/FAudio/SDL/Steam libraries, FMOD stubs, steam_appid.txt, Steamworks shims, and dllmap shims (libkernel32.dylib, libuser32.dylib, libCarbon.dylib, Carbon interpose, xaudio2_9.dylib, xinput1_4.dylib). Component names fna_arm64, fna_x86, mono-arm64, mono-x86 stay internal.

Shim sources live in src/fna/shims/:

  • csteamworks_shim.c bridges Steamworks.NET v10 CSteamworks.dll calls to modern libsteam_api.dylib (RestartAppIfNecessary -> false, Init wraps SteamAPI_InitFlat). Build: ./build_csteamworks.sh.
  • fmod_stub.c, fmodstudio_stub.c are silent FMOD Studio 1.10 stubs (no arm64 build exists). Build: ./build_fmod_stubs.sh.

Host Runtime ABI

include/metalsharp/HostRuntimeABI.h is the boundary between Windows-facing shims, Wine unixlibs, and macOS services (process/path identity, logging, Steam bridge, managed runtime config, capability reporting). Every struct starts with struct_size; callers reject a different major version and tolerate larger structs.

METALSHARP_HOST_ABI_VERSION_MAJOR / _MINOR
MetalSharpHostRuntimePaths       bottle_id, bottle_prefix, game_install_path, log_path
MetalSharpSteamBridgeConfig      appid, port (default 18733)
MetalSharpManagedRuntimeConfig   Mono root/lib dirs
MetalSharpHostCapabilities
metalsharp_host_get_abi_version() / _query_capabilities() / _self_test()

Shims read METALSHARP_MONO_LIB, METALSHARP_MONO_ROOT, METALSHARP_MONO_ASSEMBLY_DIR, METALSHARP_MONO_CONFIG_DIR (falling back to METALSHARP_HOME/HOME), and METALSHARP_STEAM_BRIDGE_PORT. Inspect with GET /runtime/host-abi. Built as libmetalsharp_host_runtime.dylib, staged by tools/package/create-host-runtime.sh, installed to runtime/host/; tested by tests/test_host_runtime_abi.cpp.

Area Source Status
D3D11/DXGI and D3D9 Wine unix bridges src/wine/*_pe.cpp, metalsharp_unix.mm, d3d9_unix.mm stable (the model for the ABI)
CoreAudio (XAudio2/DirectSound) src/audio/ stable
GameController (XInput) src/input/ stable
PE loader and Win32 shims src/loader/, src/win32/kernel32/ stable
Steam bridge, kernel32/user32 shims src/fna/shims/steam_shim.c, kernel32_shim.c, user32_shim.c prototype
Mono unixlib bridge src/wine/mscoree_unix.c prototype
Runtime deploy glue app/src-c/runtime/steam_actions.c, setup.c prototype
Steamworks offline, Carbon, FMOD shims src/fna/shims/ (METALSHARP_FNA_STEAM_PASSTHROUGH=1 for native passthrough) game-specific
Anti-cheat database AntiCheatDB.h, src/runtime/DRMDetector.cpp diagnostic

Darwin sync map (src/runtime/host/DarwinSyncMap.cpp): Event, Semaphore, Mutex, CriticalSection, and in-process WaitAny map to pthread/dispatch primitives and ship. WaitAll, a ulock futex, and NtSync (no macOS /dev/ntsync) are not ready.

Vendor Trust Kit

Anti-cheat support is cooperative, never bypass or spoofing. tools/package/create-vendor-trust-kit.sh writes dist/vendor-trust-kit/ with this doc, policy docs, and a manifest.json (commit, version, included files) so a vendor can review the runtime identity, launch model, and logs. Before external use, add real notarization evidence, launch logs, Launch Doctor anti-cheat JSON, and vendor contact context.

Working on MetalSharp

Development

Build, run, test, package, and sign MetalSharp.

9 sections · 4 min read

Updated: 2026-10-05

Build, run, test, and package MetalSharp from source. Requires Apple Silicon; native code targets macOS 14.0 (CMAKE_OSX_DEPLOYMENT_TARGET in CMakeLists.txt).

Prerequisites

xcode-select --install
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install cmake zstd
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Packaging the .app also needs full Xcode (actool compiles the Liquid Glass icon).

Clone

git clone --recurse-submodules https://github.com/metalsharp/MetalSharp.git
cd MetalSharp

Build

# Native engine (C++ D3D/Metal layer), x86_64 for Rosetta 2 PE translation
cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON
cmake --build build --parallel $(sysctl -n hw.ncpu)

# C backend -> app/src-c/build/metalsharp-backend
make -C app/src-c

# GPUI app (generate tilted dock artwork once first)
(cd app-gpui && swift generate-dock-art.swift)
cargo build --release --manifest-path app-gpui/Cargo.toml

Download the runtime bundles (Wine 11.17, D3DMetal, DXMT, Mono/FNA, Goldberg, Steam setup files) from the bundles release:

./tools/dmg/create-bundles.sh

Building the app does not compile Wine; see Building MetalSharp Wine.

Run

cargo run --locked --manifest-path app-gpui/Cargo.toml                             # production: :9274, ~/.metalsharp
cargo run --locked --manifest-path app-gpui/Cargo.toml -- --connected-validation  # isolated home on :9276
cargo run --locked --manifest-path app-gpui/Cargo.toml -- --preview               # offline sample UI

Production mode behaves like the installed app: it terminates any stale metalsharp-backend on 9274, starts a fresh one against ~/.metalsharp, and stops it on quit. Quit any other MetalSharp first.

Validation mode reads METALSHARP_GPUI_PORT (default 9276), METALSHARP_GPUI_HOME (absolute, dedicated directory) and optionally METALSHARP_GPUI_BACKEND, and protects its backend with a per-launch session token. It refuses the production port and home.

The backend alone honors METALSHARP_PORT (default 127.0.0.1:9274) and METALSHARP_HOME (default ~/.metalsharp).

Test

make -C app/src-c test        # HTTP smoke, JSON, migration, SIGPIPE, emulator update transactions
make -C app/src-c asan-test   # same under sanitizers
cargo test --manifest-path app-gpui/Cargo.toml

A build-only browser harness is available with --features browser-fixture -- --browser-fixture (offline, fixed HTML) or -- --browser-steam-test (live Steam Store; only when explicitly needed).

Package

Ad-hoc signed app (no Apple Developer account needed):

ACTOOL=/Applications/Xcode.app/Contents/Developer/usr/bin/actool \
CARGO_TARGET_DIR=/tmp/metalsharp-target \
  app-gpui/package-app.sh
open app-gpui/target/MetalSharp.app

Keep CARGO_TARGET_DIR on the internal disk; release builds can fail on an external volume. package-app.sh bundles the C backend, host runtime, tools, updater, configs, and the runtime archives from app/bundles under bundle id com.metalsharp.app.

DMG (MetalSharp-<version>-arm64.dmg):

tools/dmg/package-gpui-dmg.sh app-gpui/target/MetalSharp.app dist/gpui

package-local-preview.sh and package-local-testing-app.sh / package-local-testing-dmg.sh build the same app under separate bundle ids for side-by-side testing (they still use ~/.metalsharp and port 9274).

Release Signing

Releases must be Developer ID signed and notarized, or Gatekeeper blocks them. Set these GitHub Actions secrets:

  • MACOS_CERTIFICATE_P12 (base64 .p12) and MACOS_CERTIFICATE_PASSWORD
  • One notarization set: APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD, APPLE_TEAM_ID or APPLE_API_KEY_P8_BASE64, APPLE_API_KEY_ID, APPLE_API_ISSUER

The release job runs tools/dmg/prepare-apple-signing.sh → package-app.sh with METALSHARP_GPUI_SIGN_IDENTITY (hardened runtime, tools/dmg/entitlements.mac.plist) → package-gpui-dmg.sh → sign-notarize-dmg.sh → verify-notarization.sh. Without the secrets, CI ships an ad-hoc DMG with a DMG-SIGNING.txt marker.

The in-app updater only accepts a DMG named MetalSharp-<version>-arm64.dmg containing com.metalsharp.app at that version, passing codesign --verify --deep --strict, and signed by the same Developer ID team as the installed app. Ad-hoc releases cannot be installed through the updater.

Pre-commit Hook

Opt in once per clone:

ln -s ../../.github/hooks/pre-commit .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
Staged files Check If tool missing
*.c, *.cpp, *.h, *.hpp, *.m, *.mm clang-format --dry-run --Werror fail
app-gpui/**/*.rs cargo fmt --check fail
configs/mtsp-rules.toml python3 tools/ci/validate-rules-toml.py fail
*.sh shellcheck warn
*.md python3 tools/ci/check-doc-freshness.py warn

Skip with git commit --no-verify; CI runs the same checks.

Troubleshooting

  • cmake fails: check xcode-select -p returns a path.
  • can't find crate in release builds: move CARGO_TARGET_DIR to the internal disk.
  • Missing bundles: rerun ./tools/dmg/create-bundles.sh.
  • App won't open: xattr -cr /path/to/MetalSharp.app.
Working on MetalSharp

Building MetalSharp Wine

Build the patched Wine 11.17 runtime.

6 sections · 4 min read

Updated: 2026-10-05

Builds the Wine 11.17 runtime: x86_64 Unix host (runs under Rosetta) with i386 + x86_64 PE (WoW64). Build into a separate directory, never over ~/.metalsharp/runtime or a live Steam prefix.

Source

You need the prepared MetalSharp-patched Wine 11.17 tree (get it from the maintainer; no public URL yet). Stock Wine lacks MetalSharp's D3DMetal/DXMT loaders, macOS presentation, MSYNC (client and server must come from the same build), WoW64 bootstrap, launcher, and x87 changes. Apply tools/bundles/wine/0001-x87sidecar-cooperative.patch once before configuring.

D3DMetal, DXMT (v0.80), DXVK/VKD3D-Proton, and MoltenVK payloads are separate; make install does not provide them. D3DMetal launches need D3DMETAL_RUNTIME_DIR (payload root) and D3DMETAL_FRAMEWORK_PATH (external/D3DMetal.framework/D3DMetal). Preserve upstream copyright and license notices (see LICENSES/WINEFORGE-NOTICES.md).

Tools

xcode-select --install
softwareupdate --install-rosetta --agree-to-license
brew install autoconf automake bison flex pkgconf llvm mingw-w64 python
command -v i686-w64-mingw32-gcc x86_64-w64-mingw32-gcc

x86_64 Dependencies

Every library linked into Wine's Unix side needs an x86_64 slice; arm64 Homebrew libraries don't count. Provide a prefix $DEPS (include/, lib/, bin/, lib/pkgconfig/) with: FreeType, GnuTLS, FFmpeg, GStreamer + GLib, SDL2, Samba NetAPI (netapi.h, libnetapi.dylib, ntlm_auth), Vulkan loader + MoltenVK, OpenCL headers, and gettext. Check with lipo -archs "$DEPS/lib/libgnutls.dylib".

Configure and Build

Use absolute paths without spaces.

export ROOT="/Volumes/BuildSSD/MetalSharpWine"
export SRC="$ROOT/sources/wine-11.17-metalsharp"
export DEPS="$ROOT/deps/x86_64"
export BUILD="$ROOT/build/wine-x86_64"
export PREFIX="$ROOT/install/wine"
export X87_WINE_PATCH="/path/to/MetalSharp/tools/bundles/wine/0001-x87sidecar-cooperative.patch"
export SDKROOT="$(xcrun --sdk macosx --show-sdk-path)"

export PATH="$(brew --prefix bison)/bin:$(brew --prefix flex)/bin:$(brew --prefix llvm)/bin:$DEPS/bin:$PATH"
export CC="clang -arch x86_64" CXX="clang++ -arch x86_64" CPP="clang -arch x86_64 -E" OBJC="clang -arch x86_64"
export CFLAGS="-O2 -isysroot $SDKROOT" CXXFLAGS="-O2 -isysroot $SDKROOT" OBJCFLAGS="-O2 -isysroot $SDKROOT"
export CPPFLAGS="-I$DEPS/include"
export LDFLAGS="-arch x86_64 -isysroot $SDKROOT -L$DEPS/lib -Wl,-rpath,$DEPS/lib"
export PKG_CONFIG_LIBDIR="$DEPS/lib/pkgconfig:$DEPS/share/pkgconfig"; unset PKG_CONFIG_PATH
export i386_CC="$(command -v i686-w64-mingw32-gcc)" x86_64_CC="$(command -v x86_64-w64-mingw32-gcc)"
export NETAPI_CFLAGS="-I$DEPS/include" NETAPI_LIBS="-L$DEPS/lib -lnetapi"
export OPENCL_CFLAGS="-I$DEPS/include" OPENCL_LIBS="-framework OpenCL"

mkdir -p "$BUILD" "$PREFIX" "$ROOT/logs"
git -C "$SRC" apply --check "$X87_WINE_PATCH" && git -C "$SRC" apply "$X87_WINE_PATCH"
(cd "$SRC" && autoreconf -fiv)
cd "$BUILD"
set -o pipefail
"$SRC/configure" \
  --build=x86_64-apple-darwin --enable-archs=i386,x86_64 --prefix="$PREFIX" \
  --disable-tests --disable-winemenubuilder \
  --with-coreaudio --with-cups --with-ffmpeg --with-freetype \
  --with-gettext --with-gnutls --with-gstreamer --with-mingw \
  --with-netapi --with-opencl --with-opengl --with-pcap \
  --with-pthread --with-sdl --with-vulkan \
  --without-alsa --without-capi --without-dbus --without-gphoto \
  --without-inotify --without-krb5 --without-oss --without-pulse \
  --without-sane --without-udev --without-usb --without-v4l2 \
  --without-wayland --without-x \
  2>&1 | tee "$ROOT/logs/configure.log"
make -j"$(sysctl -n hw.ncpu)" 2>&1 | tee "$ROOT/logs/build.log"
make install 2>&1 | tee "$ROOT/logs/install.log"

Use --enable-archs=i386,x86_64, not --enable-win64. If configure can't find a dependency, set its *_CFLAGS/*_LIBS to the x86_64 prefix; don't force cache results. Use a fresh build directory when changing the source, arch, or dependencies.

x87 Sidecar

export X87SIDECAR_VERSION="v1.7.0"
export X87SIDECAR_SHA256="b768336e0ad556807156cecd533423285c8ec654f4fdb9c0623124d8b12c0865"
ARCHIVE="$ROOT/downloads/x87sidecar-$X87SIDECAR_VERSION.tar.xz"
OUT="$ROOT/downloads/x87sidecar-$X87SIDECAR_VERSION"
mkdir -p "$OUT"
curl --fail --location --proto '=https' --tlsv1.2 \
  "https://github.com/athei/x87sidecar/releases/download/$X87SIDECAR_VERSION/x87sidecar.tar.xz" -o "$ARCHIVE"
printf '%s  %s\n' "$X87SIDECAR_SHA256" "$ARCHIVE" | shasum -a 256 -c -
tar -xJf "$ARCHIVE" -C "$OUT"
xattr -d com.apple.quarantine "$OUT/x87sidecar" 2>/dev/null || true
install -m 0755 "$OUT/x87sidecar" "$PREFIX/bin/x87sidecar"
"$PREFIX/bin/x87sidecar" --probe

The sidecar is used only for i386 images when the launcher sets ROSETTA_X87_PATH. Keep it at the same version as the patch.

Validate

export DYLD_FALLBACK_LIBRARY_PATH="$DEPS/lib:$PREFIX/lib:$PREFIX/lib/wine/x86_64-unix"
"$PREFIX/bin/wine" --version                       # wine-11.17
file "$PREFIX/lib/wine/x86_64-windows/ntdll.dll" "$PREFIX/lib/wine/i386-windows/ntdll.dll"

export WINEPREFIX="$(mktemp -d "$ROOT/smoke-prefix.XXXXXX")"   # never ~/.metalsharp/prefix-steam
export WINEARCH=wow64 WINESERVER="$PREFIX/bin/wineserver" WINEMSYNC=1
"$PREFIX/bin/wine" wineboot -u
"$PREFIX/bin/wine" cmd /c ver
"$PREFIX/bin/wineserver" -w

Never run wineserver -k against a live Steam prefix. make install output is a development install, not a relocatable bundle: audit load paths, include the host libraries it needs, and re-sign any modified Mach-O before shipping.