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.71.0Apple siliconmacOS 15+

Graphics routes at a glance

Checking a game? Start with its compatibility notes. These five cards map the public route groups; D3DMetal uses the managed GPTK 4 beta 2 payload and Wine 11.17, while internal diagnostic routes stay in the detailed source notes. Open a document below to read one chapter at a time; section counts and read estimates help you choose a starting point.

Guides

How to Use MetalSharp

Install, launch, diagnose, and update flow.

9 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-09-08

Install

  1. Download the latest MetalSharp DMG from GitHub Releases.
  2. Drag MetalSharp into /Applications. Optionally use the homebrew tap to install.
  3. Open it. If macOS blocks the unsigned app, go to System Settings → Privacy & Security and choose Open Anyway.
  4. Run setup from inside MetalSharp — it uses the tools bundled in the app to install the Wine runtime, MetalSharp-owned graphics/runtime assets, and redistributable source material used by bottle repair. Homebrew is not required.
  5. Start Wine Steam, sign in, and download a Windows game.

Steam Games

Click Play from the Library page. Use the launch mode dropdown when you want to force a route:

Mode Use
VKD3D D3D12/11/10/9 to Metal via MoltenVK
M11(32) D3D11 32Bit to Metal
M11 D3D11 to Metal
M10(32) D3D10 32Bit to Metal
M10 D3D10 to Metal
M9 D3D9 through the DXMT launch/cache family
Mono/FNA Windows XNA/FNA games through MetalSharp's native Mono runtime, staged FNA/XNA assemblies, native dylibs, FMOD/FAudio/FNA3D shims, and Steamworks shim support
D3DMetal Managed GPTK 4 beta 2 payload with MetalSharp Wine 11.17, using the shared Wine Steam prefix and game-local D3DMetal DLLs

Goldberg Steam Emulator

The Goldberg toggle enables offline play for supported games without Wine Steam running. Toggle it on from the game card — MetalSharp saves the original Steam DLLs as .orig and deploys the emulator with the correct appid. Toggle off to restore the originals. Goldberg is not a requirement of the D3DMetal route: normal D3DMetal Steam launches use the Steam-aware direct launcher. Offline requirements remain game-specific.

D3DMetal

Select D3DMetal in the game's bottle workspace and save the route. Saving a resolved game stages the matched DLLs; Play refreshes them again. The current UI shows a single D3DMetal readiness indicator, not the old Homebrew installation, Repair Redist, or Seed Prefix sequence. Start Wine Steam and sign in for normal Steam-backed play. See Wine Architecture for paths and troubleshooting.

Sharp Library

Sharp Library is for Windows apps, demos, launchers, installers, and non-Steam programs.

Use Install Windows Program to select an .exe or .msi. MetalSharp may import it directly, or create an installer bottle, classify the installer, apply a known launcher recipe when one matches, launch it with the right profile, then scan for installed app candidates.

Optionally you can manage / login / install / launch Epic / Gog / GameJolt Games here. As well as ps2/ps3/ps4/ps5 emulators.

Logs and Settings

Use Logs when something fails. The page has drawer sections for live logs, crash reports, and recent log files.

Use Settings to manage Steam API sync, backend restart, cache cleanup, and runtime maintenance.

Controller Input Shims

The sidebar has a Controller selector (Off / X / D) near the theme picker:

  • Off (default) — no input shims are deployed.
  • X — XInput shims (xinput1_1.dll … xinput1_4.dll, xinput9_1_0.dll) are copied into the game folder on launch and into the Steam prefix (system32 + syswow64).
  • D — DInput shims (dinput.dll, dinput8.dll) are deployed the same way.

Switching between X and D removes the previously deployed set before deploying the new one; switching to Off removes both. Files that already existed (for example a game's own xinput1_3.dll) are backed up and restored when the mode is switched off.

Uninstall

Settings includes a Danger Zone section at the bottom with an Uninstall MetalSharp button. This removes all Wine prefixes, bottles, Steam, runtime, caches, and settings, then moves the app to Trash.

Useful Docs

Guides

Install from Source

Build MetalSharp from source without the DMG.

7 sections · 2 min read

Updated: 2026-09-08

Build MetalSharp from source without using the DMG. Requires macOS 14+ on Apple Silicon.

Prerequisites

# Xcode CLI Tools
xcode-select --install

# Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
eval "$(/opt/homebrew/bin/brew shellenv)"

# Build dependencies
brew install cmake node zstd

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
mkdir -p build
cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON
cmake --build build --parallel $(sysctl -n hw.ncpu)

# C backend
make -C app/src-c

# Electron frontend
cd app && npm install && npm run build && cd ..

Fetch Runtime Bundles

Downloads MetalSharp-owned runtime assets from the GitHub release: Wine, DXMT/M12 graphics DLLs, Steam setup files, Mono/FNA support files, Goldberg assets, and other bundled runtime material.

The managed runtime includes Wine 11.17 and the GPTK 4 beta 2 D3DMetal payload. D3DMetal uses MetalSharp Wine and the shared Steam prefix; saving a bottle does not install a separate Homebrew GPTK app. See Wine Architecture.

Building the app does not compile Wine. To rebuild the patched Wine runtime itself, follow How to Build MetalSharp Wine; a prepared source tree and matching x86_64 dependencies are required.

./tools/dmg/create-bundles.sh

Run

cd app && npx electron .

Build a Signed App

For an ad-hoc signed .app (no Apple Developer account needed):

cd app && npx electron-builder --dir --mac --arm64
codesign --force --deep --sign - ../dist/electron/mac-arm64/MetalSharp.app
open ../dist/electron/mac-arm64/MetalSharp.app

For a distributable DMG with hardened runtime (requires Apple Developer certificate):

cd app && npm run dmg

Troubleshooting

  • cmake fails: Ensure Xcode CLI tools are installed (xcode-select -p should return a path)
  • npm install fails: Make sure Node 18+ is installed (brew install node)
  • Missing bundles: Run ./tools/dmg/create-bundles.sh — this downloads MetalSharp-owned runtime assets from GitHub. Use the matched managed D3DMetal payload rather than mixing DLLs with a separate GPTK installation.
  • App won't open: If you see a Gatekeeper warning, run xattr -cr /path/to/MetalSharp.app
Compatibility

Supported Games

Working/blocked game evidence and recommended public routes.

11 sections · 5 min read

Updated: 2026-09-10 (D3DMetal online-play status)

Tested and working games organized by pipeline. Only games confirmed playable are listed.

Sharp Library Sources

Source Tested game Launch path
GameJolt The Joy of Creation: Reborn GameJolt card Play action through its configured Windows bottle
GOG Fall of Porcupine: Prologue GOG card Play action through its selected bottle pipeline

PCSX2 is available as an isolated emulator provider, but no PlayStation 2 title is listed here until a user-owned dump has been manually confirmed playable. Runtime installation and synthetic homebrew/process fixtures do not count as a game compatibility claim.

Test System

Games were tested from an external 1TB M.2 SSD (~5000 MB/s over USB-C 3.1) on an M4 MacBook Air with 16GB RAM.

Pipelines

Pipeline Backend Use
D3DMetal Managed GPTK 4 beta 2 / Apple D3DMetal D3D11/D3D12 through MetalSharp Wine 11.17, the shared Steam prefix, and game-local route DLLs.
VKD3D Vulkan D3D12/D3D11/D3D10/D3D9 Vulkan
M11 DXMT D3D11 to Metal
M11 (32-bit) DXMT D3D11 to Metal, 32-bit prefix route
M10 DXMT D3D10 to Metal
M9 DXMT D3D9 to Metal
Mono/FNA MonoKickstart + FNA XNA/FNA/MonoGame via native Mono runtime

Internal routes (dxmt auto-detect, Wine Steam, macOS Steam, wine_bare) remain backend machinery and are not shown in bottle selectors.


D3DMetal

The current route uses the managed GPTK 4 beta 2 payload and MetalSharp Wine 11.17. Save stages the matched DLLs beside a resolved game executable; Play refreshes them and uses the Steam-aware direct launcher with ~/.metalsharp/prefix-steam. There is no route-wide Steam-emulator requirement.

The D3DMetal titles below now support online play except Elden Ring and ARMORED CORE VI FIRES OF RUBICON, which remain offline-only. This documentation update does not change the route or runtime.

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

VKD3D

Game AppID Notes
PEAK 3527290 Working
Hollow Knight: Silksong 1030300
Schedule I 3164500
Dark Deception 332950
Portal2 620 Steam-Emu Required

M11 — D3D11 to Metal

Game AppID Notes
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 M11 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
Dave The Diver 1868140
Besiege 346010
AmongUs 945360
Team Fortress 2 440
Amid Evil 673130

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

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

M10 — D3D10 to Metal

| Game | AppID | Notes | | Mind Scanners | 1389550 | |


M9 — D3D9 to Metal

Game AppID Notes
Mirror's Edge 17410 Sync-loading mitigation active.
Half-Life 2 220
Portal 2 620 Steam Emu supported.
Among Us 945360 Steam online play.
Fallout: New Vegas 22380 Direct Steam Launch.

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.

Notes

  • Game cards can be tested through the route dropdown in each game's bottle workspace.
  • Shader caches are per-appid and can be cleared from Settings.
  • Wine Steam remains the background Steam client for installed Windows Steam games.
  • Installed Wine Steam games create steam_<appid> bottle records for runtime asset/component preflight before launch.
  • Env-dependent Steam routes keep Wine Steam alive as the background client, then launch the game executable directly with the selected pipeline, bottle prefix, route env, and Steam identity variables.
  • D3DMetal uses the managed Wine runtime and shared Steam prefix too. Its graphics payload is separate from DXMT/VKD3D; see Wine Architecture.
Runtime

Runtime Bundles & Steam Routing

What ships with MetalSharp and how Steam starts games.

4 sections · 4 min read

Updated: 2026-09-08

This is the operational contract for bundle provenance and Wine Steam launch routing.

Bundle Provenance

Runtime assets are downloaded from the bundles GitHub release into app/bundles/ during app packaging and into ~/.metalsharp/cache/bundles/ during installer fallback downloads.

The manifest-tracked assets are listed in tools/bundles/asset-manifest.tsv. The verifier checks archive roots, required files, and lane-specific hash contracts. Release staging also verifies downloaded archive bytes against the published bundle manifest; replacing an asset requires updating that manifest, not bypassing checks.

Current split bundle roots:

Asset Why it is guarded
metalsharp-electron.tar.zst Contains electron/, the built Electron application payload.
metalsharp-graphics-dll.tar.zst Contains Graphics/dll/, the legacy DXMT D3D9/D3D10/D3D11 surface and the isolated M12 D3D12 surface.
metalsharp-runtime.tar.zst Contains runtime/, the patched Wine 11.17 runtime, host ABI, and managed runtime payloads including D3DMetal.
metalsharp-assets.tar.zst Contains assets/, Mono, Goldberg, EAC toggle, shims, and compatibility/runtime support assets.
metalsharp-scripts-tools.tar.zst Contains scripts/tools/, updater scripts, configs, native tools, and CEF helpers.
metalsharp-steam.tar.zst Contains steam/, the Steam installer and Steam CEF wrapper assets.
metalsharp-d3d12-developer-sdk.tar.zst Contains developer-sdk/d3d12/, the D3D12 contracts, probes, scripts, docs, staged developer Wine runtime, DXMT DLLs, Winemetal bridge files, and runtime provenance manifest.

Verification commands:

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

Installer Acceptance Rules

The installer consumes the split runtime tarballs by root name. metalsharp-graphics-dll.tar.zst is the only source for the active DXMT runtime payloads used by M9-M12.

The graphics bundle has two runtime surfaces:

Graphics/dll/dxmt/      -> DXMT v0.80 baseline and retained compatibility payloads
Graphics/dll/dxmt-m12/  -> isolated D3D12/DXGI/winemetal payload for M12

After install those surfaces live under:

~/.metalsharp/runtime/wine/lib/dxmt/
~/.metalsharp/runtime/wine/lib/dxmt_m12/

Installed DXMT runtime state is recorded in:

~/.metalsharp/runtime/wine/lib/dxmt/metalsharp-dxmt-runtime.json

Both installed lanes have metalsharp-dxmt-runtime.json metadata using schema metalsharp.dxmt-runtime.v2; for app 0.65.0 the baseline version is 0.65.0-dxmt-v0.80-baseline-v1. Migration must accept the same version that setup writes, not the retired M12 manifest suffix.

Do not trust Wine or DXMT version strings alone. Check required files, both lane manifests, and the M12/DXVK/VKD3D/MoltenVK hash contracts when diagnosing deployment drift. The bundled backend is packaged separately at Contents/Resources/runtime/metalsharp-backend.

D3DMetal uses ~/.metalsharp/runtime/d3dmetal-gptk4-beta2/ with the same Wine 11.17 host and Steam prefix, not a Homebrew-owned runtime. See Wine Architecture.

Steam Launch Route

The app launches Wine Steam through:

Renderer button -> POST /steam/launch -> steam::launch_wine_steam()

Game launches that need an explicit public route use M12/M11/M10/M9/VKD3D/D3DMetal/Mono-FNA route IDs. Raw dxmt remains an internal auto-router and legacy compatibility value.

Renderer Play -> POST /steam/launch-game {"launchMethod":"m12"} -> prepare_steam_pipeline_env() -> direct game launch with Wine Steam alive in the background

Wine Steam must be launched by the backend so it gets the managed Wine prefix, runtime library env, DLL overrides, and wrapper deployment. Launching Steam.exe directly from a shell is not equivalent to pressing the app button.

Steam-model titles use the real Steam DLLs instead of Goldberg. For those titles the launcher stages the real Steam API DLLs and the Steam client/overlay components next to the selected executable when they are available:

steam_api64.dll
steam_api.dll
steamclient64.dll
steamclient.dll
GameOverlayRenderer64.dll
GameOverlayRenderer.dll

This applies to Steam launch-model titles such as Party Animals and Source games without forcing -secure onto titles that only need -steam.

Steam Wrapper Rules

Before launching Steam, MetalSharp calls ensure_steam_launch_ready() and redeploys steamwebhelper.exe when Steam has overwritten it. Steam assets come from metalsharp-steam.tar.zst, and the C backend validates the staged wrapper before launch.

The expected deployed Steam CEF layout is:

~/.metalsharp/prefix-steam/drive_c/Program Files (x86)/Steam/bin/cef/cef.win64/
├── steamwebhelper.exe       # MetalSharp wrapper
├── steamwebhelper_real.exe  # Steam's original helper
└── .ms_wrapper_deployed

If Steam login stops rendering after bundle or wrapper work, verify the wrapper hash before changing launch args.

Runtime

Mono Runtime Lanes

When XNA, FNA, and MonoGame titles use native Mono.

4 sections · 3 min read

Updated: 2026-07-08

Status: June 2026 route hardening active

MetalSharp now treats Mono/FNA as a first-class public route, not a global dependency and not a synonym for Wine Mono. The visible selector shows one Mono/FNA option while the launcher chooses the appropriate native Mono lane and shim set per game.

Lanes

Lane Runtime Known version Scope Notes
Wine Mono Wine prefix component 11.3.0, bundled in the Wine runtime Windows CLR/bootstrapper apps inside a bottle Wine discovers the bundled support package from runtime/wine/share/mono/wine-mono-11.3.0 and installs it into a prefix on demand. No separate Settings or GOG download step is required.
Native Mono ARM64 ~/.metalsharp/runtime/mono-arm64/bin/mono 6.14.1 Terraria/FNA ARM64 style games This is the dinosaur path that made Terraria work: native macOS Mono, FNA/XNA dllmaps, native SDL/FNA3D/FAudio/shims, and no Wine prefix ownership.
Native Mono x86_64 ~/.metalsharp/runtime/mono-x86/bin/mono 6.12.0.122 Celeste/FNA legacy lane under Rosetta This lane exists because some older FNA/Mono dependencies were x86_64-only or behaved better with Mono 6.12 and explicit dllmaps.

Rules

  • Sharp Library installer bottles should use Wine Mono only when the target is actually a Windows CLR/bootstrapper app.
  • Steam games should request native Mono lanes only for known FNA/XNA targets or proof targets that need the old macOS Mono path.
  • Native Mono ARM64/x86_64 should not be injected into every Wine bottle.
  • Wine Mono failures should be logged separately from native Mono/FNA failures.
  • A hard case like Minecraft should remain in the Wine bottle lane until evidence shows it needs a native launcher bridge or a different Wine Mono strategy.
  • The public route label is Mono/FNA. Internal component names such as fna_arm64, fna_x86, mono-arm64, and mono-x86 remain implementation details.

Current Mono/FNA Route Contract

The route stages FNA/XNA assemblies, native FNA3D/FAudio/SDL/Steam libraries when available, FMOD/FMOD Studio stubs, steam_appid.txt, and Steamworks compatibility shims. It also deploys the macOS framework-backed MetalSharp shims expected by the dllmaps: libkernel32.dylib, libuser32.dylib, libCarbon.dylib, the Carbon interpose shim, and bundled CoreAudio/GameController shims such as xaudio2_9.dylib and xinput1_4.dylib when present. Celeste-style games select x86_64 Mono under Rosetta; Terraria-style games can use the Terraria runtime patch/stub path. Celeste and Terraria are now supported through this lane; future Mono/FNA titles should still be promoted only after per-game launch proof.

Phase 11 Minecraft Finding

Minecraft is a useful hard case because it exercises installer classification, bottle readiness, Wine Mono, embedded browser/runtime needs, and app detection. Current evidence shows:

  • The bottle classifies as java_launcher and launches from a stable installer bottle.
  • Fonts and Gecko can be repaired or detected correctly.
  • WebView2 is still not available as a local runtime asset.
  • M9, bare Wine, and WINEDLLOVERRIDES=mscompatdb=d all reproduce the same native Mono crash.

The next implementation path is to keep Minecraft as the proof target while adding better Wine Mono diagnostics and, if needed, a bottle-scoped alternate Mono strategy rather than changing global runtime behavior.

Runtime

Wine Architecture

How Wine prefixes and runtime wrappers are laid out.

8 sections · 6 min read

Updated: 2026-09-11

MetalSharp ships a self-contained Wine runtime at:

~/.metalsharp/runtime/wine/

The current baseline is Wine 11.17, with an x86_64 Unix host and i386 + x86_64 PE WoW64 support. Apple Silicon executes the host through Rosetta; 32-bit Windows support does not imply a separate 32-bit Unix host or Steam prefix.

It is used by M12, M11, M10, M9, VKD3D, and D3DMetal. Internal fallback/diagnostic routes such as M32, Steam handoff, and plain Wine also use this runtime. Mono/FNA does not use the Wine runtime.

Check the installed version without launching Steam or creating a prefix:

~/.metalsharp/runtime/wine/bin/wine --version

A matching version is not sufficient to identify the patched runtime: preserve its loader, bootstrap, graphics-bridge and synchronization integrations. See How to Build MetalSharp Wine.

Layout

~/.metalsharp/runtime/wine/
├── bin/
│   ├── metalsharp-wine
│   ├── wine
│   └── wineserver
├── lib/
│   ├── wine/
│   │   ├── x86_64-unix/
│   │   ├── x86_64-windows/
│   │   └── i386-windows/
│   ├── dxmt/
│   │   ├── x86_64-unix/
│   │   └── x86_64-windows/
│   ├── dxmt_m12/
│   └── moltenvk-vkmt/
└── etc/
    ├── dxmt.conf
    └── vulkan/icd.d/MoltenVK_icd.json

Other runtime pieces live beside it:

~/.metalsharp/runtime/
├── d3dmetal-gptk4-beta2/
│   ├── wine/x86_64-windows/
│   └── external/D3DMetal.framework/D3DMetal
├── redist/
└── wine/

User/runtime state lives beside the runtime root:

~/.metalsharp/
├── prefix-steam/
├── bottles/
├── sharp-library/
├── games/
├── shader-cache/
├── cache/
└── logs/

Route Use

Route Wine use
M12 Wine + DXMT D3D12/D3D11/DXGI
M11 Wine + DXMT D3D11/DXGI
M10 Wine + DXMT D3D10/D3D11/DXGI
M9 Wine + D3D9 Metal under the DXMT launch family
VKD3D Wine + VKD3D-Proton/DXVK + MoltenVK; separate from M12
D3DMetal Wine + managed GPTK 4 beta 2 D3DMetal payload

M32, Steam handoff, and plain Wine remain internal Wine-backed routes for diagnostics, bootstrap cases, legacy records, and installer/custom-app internals.

DLL Deployment

The backend copies graphics DLLs into the game directory before launch.

M11/M10:

d3d11.dll
dxgi.dll
d3d10core.dll
winemetal.dll

M10 deploys Wine's public d3d10.dll and d3d10_1.dll entrypoints for D3D10 imports, then uses DXMT's d3d10core.dll as the D3D10 handoff and shares the D3D11/DXGI/winemetal runtime with M11.

M12:

d3d12.dll
d3d11.dll
dxgi.dll
d3d10core.dll
winemetal.dll

M9:

d3d9.dll

D3DMetal

D3DMetal uses the managed GPTK 4 beta 2 payload at ~/.metalsharp/runtime/d3dmetal-gptk4-beta2/, not the Wine binary in a separate Homebrew GPTK application. For Steam games it uses the existing ~/.metalsharp/prefix-steam/, not the historical prefix-gptk workflow.

Saving a resolved D3DMetal bottle stages the matching route DLLs beside the selected game executable. Play refreshes those files, checks readiness, reconciles the selected controller input shims, and calls the Steam-aware direct launcher with SteamAppId and SteamGameId. Goldberg/offline mode is not required merely because D3DMetal is selected. The UI exposes a single D3DMetal readiness indicator instead of the old Repair Redist / Seed Prefix sequence.

The launch environment includes:

WINEPREFIX=<MetalSharp home>/prefix-steam
D3DMETAL_RUNTIME_DIR=<MetalSharp home>/runtime/d3dmetal-gptk4-beta2
D3DMETAL_FRAMEWORK_PATH=<MetalSharp home>/runtime/d3dmetal-gptk4-beta2/external/D3DMetal.framework/D3DMetal

D3DMETAL_FRAMEWORK_PATH must name the framework executable, not just the .framework directory. Keep the PE DLLs, Unix-side support, and framework from the same payload. Do not combine DLLs from another GPTK release.

When switching between D3DMetal, DXMT/M12, and VKD3D, graphics cleanup removes only files byte-matching known managed payloads, including D3DMetal's nvngx-on-metalfx.dll. Modified or game-provided files are not indiscriminately deleted. This graphics ownership check is separate from the existing controller-shim helper's backup/restore behavior.

Troubleshooting

  • Payload incomplete: repair the MetalSharp runtime installation; installing Homebrew GPTK is not the repair path for this route.
  • Executable missing or wrong: verify the game is installed and refresh its bottle. Selection must resolve the actual game executable rather than a launcher/service; persisted D3DMetal state re-evaluates the executable rules.
  • Save occurred before discovery finished: refresh or save the bottle after the game path resolves. Play must still pass readiness and staging checks.
  • Launch fails: inspect the bottle/runtime report and logs before altering DLLs or prefixes. Keep Wine Steam signed in for normal Steam-backed play.
  • External library issue: check the registered Steam library path and shared prefix drive mappings; do not delete the Steam prefix or repoint its z: drive as a generic graphics fix.

Prefixes

The shared Steam prefix is:

~/.metalsharp/prefix-steam/

Steam is installed inside that prefix. External Steam libraries may be mounted into the prefix by drive letter.

Sharp Library installer/app bottles use dedicated prefixes:

~/.metalsharp/bottles/<id>/prefix/

Steam game bottles are different: they are launch-authoritative readiness records, but their prefix_path currently points at ~/.metalsharp/prefix-steam/ so Runtime Doctor and repair actions affect the prefix Wine Steam actually uses. Wine Steam remains the live background client that stays connected for Steam games. Env-dependent Steam game launches run the game executable directly through the selected MTSP pipeline with this prefix, route env, cache paths, and SteamAppId/SteamGameId; client-only Steam handoff remains internal for diagnostics/bootstrap cases.

High Resolution (Retina) is enabled by default for this shared prefix. Before each managed launch, MetalSharp applies Wine's RetinaMode setting and sets Windows DPI to 192; disabling the setting restores 96 DPI. Restart Wine Steam after changing it. Because games share the prefix, disabling Retina mode can reduce render resolution and GPU load.

Important Environment

Variable Purpose
WINEPREFIX Prefix location
WINEDLLPATH Wine PE DLL lookup
DYLD_FALLBACK_LIBRARY_PATH Unix library lookup for Wine and DXMT
WINEDLLOVERRIDES Selects injected/native DLL behavior
DXMT_SHADER_CACHE_PATH DXMT shader cache
DXMT_CONFIG_FILE DXMT config file
SteamAppId / SteamGameId Steam identity for direct Steam-bottle game launches
D3DMETAL_RUNTIME_DIR Root of the matched D3DMetal payload
D3DMETAL_FRAMEWORK_PATH D3DMetal framework executable
WINEMSYNC MSYNC selection, derived from MetalSharp's msync setting

Wine 11.17 includes MSYNC client/server support. The Wine server retains its synchronization selection for its lifetime, so changing settings does not establish that an already-running server switched modes. Apply changes after a normal shutdown of managed Wine processes, not by deleting prefixes or killing unrelated Wine sessions.

Steam Wrapper

Wine Steam uses the bundled steamwebhelper.exe wrapper. Steam updates may replace it, so MetalSharp redeploys it when preparing or launching Steam.

Runtime

Launcher Runtime

How Sharp Library starts installers and Windows apps.

5 sections · 7 min read

Updated: 2026-08-25

Status: Phase 3 foundation

MetalSharp treats launcher installers as bottle-managed Windows programs, not as one-off EXEs. The goal is to let launchers keep their login/session state, install child games into the same bottle, and produce logs that explain why a launcher or child game failed.

Known Launcher Recipes

The installer classifier recognizes these launcher families before generic .NET, WebView, MSI, or PE import heuristics:

  • Minecraft Launcher -> Java Launcher profile
  • EA App / Origin -> WebView profile
  • Ubisoft Connect / Uplay -> WebView profile
  • Epic Games Launcher -> WebView profile
  • Rockstar Games Launcher / Social Club -> WebView profile
  • GOG Galaxy -> Launcher profile

Known launcher hints are stored in the classifier output as known_launcher:<id> and launcher_name:<display name>. These hints make installer bottles more predictable, especially when launcher bootstrapper binaries also contain generic .NET or WebView strings.

Known launchers default to the bare Wine pipeline during install/bootstrap. That keeps store launchers from inheriting game-specific graphics routes such as M9 before the actual child game executable exists. Once a launcher installs or starts a game, that child executable still gets its own bottle/runtime route.

Native Epic Library Path

The Sharp Library Epic tab is the supported game-download path when the Windows launcher reaches DP-06. It uses upstream Legendary 0.21.0 out of process rather than patching Wine, ADVAPI32, NTDLL, or Epic binaries.

  • MetalSharp downloads the official native arm64 release only after the user selects Install Epic Support.
  • The release URL, version, 64 MiB size ceiling, arm64 Mach-O identity, and SHA-256 28f5f7d0eb8c029679d4faaa483ec85888af17a9a75977ae9170c21d8ce3428b are backend-owned and verified before atomic activation at ~/.metalsharp/tools/legendary/legendary-0.21.0.
  • Epic authentication occurs in a sandboxed, context-isolated Electron window restricted to Epic, Legendary, and explicit identity-provider HTTPS hosts. The backend receives only the resulting authorization code. Legendary state is isolated under ~/.metalsharp/epic/legendary/.
  • Library sync requests Windows-installable account entries as JSON on login and manual Sync. The backend also starts a serialized background sync when it launches. Successful catalogs are atomically cached at ~/.metalsharp/epic/library.json, and normal Epic-tab loads read that cache immediately so navigation never clears the library while a network refresh is running or unavailable.
  • Downloads use Legendary's manifest/CDN pipeline. Each install prompts for an existing writable location constrained to the user's home or /Volumes; the configured root from ~/.metalsharp/launcher-games/epic/location.txt remains the backend fallback. Per-title progress and logs live under ~/.metalsharp/epic/processes/.
  • Installed games require an explicit Initialize Bottle action before first launch. Each title owns ~/.metalsharp/bottles/epic_<appName>/prefix plus a managed manifest recording its selected pipeline and mouse mode. No Recenter is the default; Mouse Auto restores Wine cursor warping for games that need relative capture.
  • Pipeline and mouse selectors remain beside Play/Uninstall like the GOG library. Launches inherit the selected MetalSharp graphics backend, and supervision waits on the prefix's real Wineserver. Stop, the card close action, and Cmd+Opt+Q terminate the isolated Epic Wineserver. Uninstall removes Legendary's registered game files and that title's bottle.
  • Epic account data, the cached catalog, configured game location, per-game bottle manifests, and each Epic bottle's registry/user settings are explicitly preserved and restored by runtime migration.
  • This download/authentication path uses native Legendary rather than the Windows Epic Launcher. The unsupported Windows Epic Launcher card and its legacy Epic-Games-Prefix have been removed.

Backend routes are GET /sharp-library/epic/status, GET /sharp-library/epic/games, and POST actions for install-tool, auth, logout, sync, install, progress, cancel, initialize, play, stop, stop-all, and uninstall.

Runtime Behavior

Install Windows Program routes launcher-like EXEs and MSI packages into installer bottles. The bottle records:

  • source installer path
  • installer kind
  • runtime profile
  • prefix path
  • launch log
  • launch pid/status
  • detected installed app candidates

Minecraft gets a Java Launcher profile even when the bootstrapper includes CLR metadata. Storefront launchers get WebView or Launcher profiles so their bottle dependency set matches the login and embedded-browser surface they actually need. The WebView profile includes Gecko, WebView2, .NET 4.8, VC runtime, and core fonts because EA-style WiX/MSI launchers can execute .NET custom actions after the visible install bar completes.

CEF Compatibility

Steam already uses a wrapped steamwebhelper.exe to force CEF onto a Wine-safe software GPU path. Sharp Library bottles now generalize that behavior for launcher apps that carry CEF or Chromium payloads.

When a bottle app looks like a launcher and its install directory contains CEF assets such as libcef.dll, chrome_*.pak, vk_swiftshader.dll, or app.asar, MetalSharp preserves the original executable as <name>_real.exe and replaces <name>.exe with a small architecture-matched wrapper. The wrapper relaunches the real executable with --in-process-gpu --disable-gpu and deploys a sibling metalsharp-cefchildhook.dll for launchers that spawn renderer, utility, or GPU children from the preserved executable.

The first proof target is Minecraft Launcher:

  • the Microsoft Store .exe bootstrapper is a 32-bit .NET/WPF package and can fall into Wine Mono or native .NET setup failure before Minecraft exists
  • the official Mojang MinecraftInstaller.msi installs cleanly into a java_launcher bottle
  • MinecraftLauncher.exe now receives the generic CEF wrapper and child hook, but the current proof still renders a blank surface after CEF initializes
  • local hook logs prove imports are patched, but Minecraft's embedded CEF child creation is not yet passing through the hooked CreateProcessA/W, GetProcAddress, or ShellExecute paths

EA App is the first Steam-adjacent storefront proof target:

  • the installer reaches the EA MSI apply step in bottle installer_16c2e7d7a6e2d5e7
  • the visible install bar completes, then the MSI fails with 0x80070643, which EA reports as INST-14-1603
  • extracted MSI custom-action metadata requests .NET v4.0, so the WebView profile now provisions dotnet48 before the launcher installer runs
  • known launchers now install through bare Wine first instead of falling back to M9 from the 32-bit bootstrapper PE header
  • fresh proof bottle relaunches now stay in the selected proof bottle instead of silently falling back to the stable source-path bottle
  • the latest EA proof has corefonts, dotnet48, gecko, vcrun2019, and webview2 installed
  • the direct MSI log files are still created as zero bytes, so the next EA pass needs deeper Wine MSI/service/elevation inspection around per-machine package cache writes
  • WebView2/Edge helper executables are runtime components, not apps; prefix app detection filters them so a runtime repair does not pollute the Sharp Library

Launcher evidence reports:

POST /launcher/evidence
{"family":"ea"}

POST /launcher/evidence
{"family":"ubisoft"}

EA currently reports ea_msi_1603: the bootstrapper reaches MSI apply and fails with 0x80070643 / INST-14-1603.

Ubisoft currently reports ubisoft_auto_started_then_crash_reporter: the installer staged UbisoftConnect.exe, auto-started Ubisoft Game Launcher 171.0.13174, then entered the crash-reporter path. A clean direct launch of UbisoftConnect.exe still needs to be captured after runtime repair.

Remaining Work

  • Finish the Minecraft CEF child-process path by either reaching the lower-level process creation call or mapping Minecraft's native CEF preference surface correctly.
  • Persist child game processes spawned by launchers as bottle app records.
  • Track launcher-owned game install folders separately from the launcher EXE.
  • Add launcher-specific repair controls for WebView, Gecko, VC runtime, and session data.
  • Add end-to-end smoke cases for at least three launchers once redistributable assets and test installers are available.
Runtime

Compatdata Architecture

Where Steam game prefixes live and who owns them.

4 sections · 3 min read

Updated: 2026-07-08

Status: Phase 2 foundation

MetalSharp compatdata records are the launch-authoritative runtime records for Steam games. They are inspired by Proton's per-app runtime discipline, but they keep Wine Steam as the account, download, and session provider.

Paths

Steam game compatdata records live at:

~/.metalsharp/compatdata/<appid>/metalsharp-compatdata.json
~/.metalsharp/compatdata/<appid>/logs/
~/.metalsharp/compatdata/<appid>/assets/

Existing Steam bottle manifests still live at:

~/.metalsharp/bottles/steam_<appid>/bottle.json

The current implementation records the shared Wine Steam prefix as the active prefix because Wine Steam must remain a stable, long-lived entity. The compatdata record is still authoritative for launch routing, dependency visibility, runtime assets, and diagnostics.

Runtime migrations preserve compatdata metadata alongside bottle settings, game metadata, Steam prefix settings, and Sharp Library records. They do not stage full Wine prefixes, Steam steamapps/ payloads, or downloaded game installations.

Record Contents

Each Steam compatdata record stores:

  • Steam appid and display name
  • linked bottle id
  • compatdata path
  • active prefix path
  • Wine Steam prefix path
  • game install path
  • runtime profile
  • launch pipeline
  • Steam identity mode
  • compatibility tool name and launch command template
  • log directory
  • detected runtime assets from the game install
  • required runtime components
  • last launch log path, pid, status, and finish time when known

API

POST /steam/compatdata

Request:

{
  "appid": 620,
  "pipeline": "m9"
}

The endpoint ensures the Steam game bottle exists, refreshes detected assets, writes the compatdata record, and returns the record.

Steam game launches also attach the current compatdata record to launch responses when available.

Wine-backed MTSP Steam launches write process output to:

~/.metalsharp/compatdata/<appid>/logs/launch-<timestamp>.log

Bottle diagnostics refresh the Steam compatdata record and check that both the compatdata manifest and log directory exist. That makes the Steam game bottle the repair surface while the compatdata record remains the authoritative launch ledger.

Why This Matters

Steam should not be torn down or replaced just to pass route-specific game runtime state. Steam remains responsible for login, ownership, downloads, cloud/session behavior, and staying alive while games run. MetalSharp compatdata owns the game launch contract so DLLs, redists, logs, runtime assets, and selected pipeline state can be repaired and inspected per game.

Future work can move individual game runtime state into compatdata/<appid>/pfx only where the Steam/game process model allows it cleanly.

Runtime

Host Runtime ABI

How native macOS shims connect to the runtime.

7 sections · 3 min read

Updated: 2026-07-08

Status: Phase 1 draft implementation

This document defines the first supported boundary between Windows-facing shims, Wine unixlib modules, and macOS host services. The goal is to turn today's scattered dylibs and per-game shims into a bottle-aware runtime contract.

What The ABI Owns

Host Runtime ABI services are low-level host capabilities that many launch routes need:

  • process, environment, and path identity for a bottle
  • runtime logging and diagnostics paths
  • Steam identity bridge configuration
  • managed runtime configuration for Mono/.NET launchers
  • graphics, audio, and input dispatch capability reporting

The ABI does not own Steam account/session lifecycle. Wine Steam remains the long-lived Steam entity. MetalSharp owns route authority and injects bottle-specific runtime state into the game process or installer process.

Contract Header

The C ABI lives in include/metalsharp/HostRuntimeABI.h. It provides:

  • METALSHARP_HOST_ABI_VERSION_MAJOR
  • METALSHARP_HOST_ABI_VERSION_MINOR
  • MetalSharpHostRuntimePaths
  • MetalSharpSteamBridgeConfig
  • MetalSharpManagedRuntimeConfig
  • MetalSharpHostCapabilities
  • metalsharp_host_get_abi_version()
  • metalsharp_host_query_capabilities()
  • metalsharp_host_self_test()

Every struct starts with struct_size so newer hosts can append fields without breaking older shims. Callers must reject incompatible major versions and tolerate larger struct sizes.

Phase 1 Runtime Changes

The initial implementation removes two brittle assumptions from the runtime:

  • src/wine/mscoree_unix.c no longer uses a machine-local absolute Mono path. It accepts METALSHARP_MONO_LIB, METALSHARP_MONO_ROOT, METALSHARP_MONO_ASSEMBLY_DIR, and METALSHARP_MONO_CONFIG_DIR, with portable METALSHARP_HOME/HOME fallbacks.
  • src/fna/shims/steam_shim.c accepts METALSHARP_STEAM_BRIDGE_PORT, and the C launch route reports/passes the same value.
  • The backend exposes GET /runtime/host-abi so the app can inspect the current ABI version, service list, bridge port, and managed runtime environment contract.

These are small changes, but they are the necessary shape: bottle manifests and installer runtime profiles can now configure shims through explicit runtime state instead of requiring patched binaries or one global machine assumption.

Bottle Manifest Mapping

Future bottle manifests should map directly to ABI structs:

Manifest field ABI target
id MetalSharpHostRuntimePaths.bottle_id
prefix_path MetalSharpHostRuntimePaths.bottle_prefix
game_install_path MetalSharpHostRuntimePaths.game_install_path
bottle log path MetalSharpHostRuntimePaths.log_path
Steam appid MetalSharpSteamBridgeConfig.appid
bridge port MetalSharpSteamBridgeConfig.port
Mono root/lib dirs MetalSharpManagedRuntimeConfig

Self-Test

tests/test_host_runtime_abi.cpp compiles the header and calls the runtime query/self-test surface from src/runtime/host/HostRuntimeABI.cpp. It validates version constants, struct sizing, capabilities, and default Steam bridge configuration. It is intentionally lightweight so CI can catch ABI breakage before runtime packaging work depends on it.

Packaging

The shared host runtime target is metalsharp_host_runtime. macOS builds produce libmetalsharp_host_runtime.dylib.

tools/package/create-host-runtime.sh stages the package into app/native/host/:

  • libmetalsharp_host_runtime.dylib or platform equivalent
  • HostRuntimeABI.h
  • manifest.json

Electron packages this directory as runtime/host/. This gives the app, updater, and future runtime installer a stable place to discover host ABI artifacts without scraping the native library root.

During setup, the backend copies packaged runtime/host/ assets into:

~/.metalsharp/runtime/host/

The setup dependency check treats the host runtime as required. Runtime migration schema 2 also treats a missing host ABI install as a repair condition so existing installs can be refreshed cleanly.

Next Phase Hooks

Phase 2 should use this ABI boundary to generate a per-bottle runtime env block from bottle manifests. That block should be applied to installer launches, direct game launches, and Steam game handoffs that create a child process outside the already-running Steam client.

Runtime

Redistributable Runtime

Where runtime dependencies come from and how repair works.

4 sections · 2 min read

Updated: 2026-07-08

Status: Phase 4 foundation

Redistributables are modeled as bottle components. MetalSharp looks for legal local assets in Steam Common Redistributables, Sharp Library installer bottles, and ~/.metalsharp/runtime/redist/, then records missing, installed, or repair-needed state in the bottle.

Component IDs

The current repairable component surface includes:

  • vcrun2019
  • directx_jun2010
  • dotnet48
  • webview2
  • openal
  • xna
  • physx
  • wine-mono
  • gecko
  • corefonts

Graphics route components such as d3d9, d3d10, d3d11, d3d12, and dxgi are verified from the MetalSharp runtime instead of downloaded as redistributables.

Asset Discovery

Steam game bottles scan game installs for _CommonRedist, CommonRedist, and installscript.vdf assets. Detected assets infer required bottle components:

  • VC redist payloads -> vcrun2019
  • DirectX payloads or install scripts -> directx_jun2010
  • .NET payloads -> dotnet48
  • WebView payloads -> webview2
  • OpenAL payloads -> openal
  • XNA payloads or install scripts -> xna
  • PhysX payloads or install scripts -> physx

The install script parser is intentionally conservative: it only infers known redistributable families from obvious names such as DXSETUP.exe, xnafx40_redist.msi, oalinst.exe, or PhysX installers.

Repair Sources

The repair resolver searches:

~/.metalsharp/prefix-steam/drive_c/Program Files (x86)/Steam/steamapps/common/Steamworks Shared/_CommonRedist/
~/.metalsharp/bottles/*/installers/
~/.metalsharp/runtime/redist/

MSI redistributables are launched through msiexec /i; EXE redistributables are launched directly with silent arguments where known. XNA Framework 4.0 repairs also reuse a matching Sharp Library installer-bottle payload when the user has already installed or staged xnafx40_redist.msi through Sharp Library. Every repair writes a per-bottle component log.

Remaining Work

  • Persist redist install receipts separately from heuristic file checks.
  • Parse more Steam install script actions and conditions.
  • Add legal asset download/install UX around the existing redist source guide.
  • Add end-to-end verification against Steamworks Common Redistributables on a real Wine Steam install.
Runtime

Steam Compatibility Tool Surface

The compatibility interface MetalSharp presents to Steam.

3 sections · 2 min read

Updated: 2026-07-08

Status: Phase 7 foundation

MetalSharp should behave like a compatibility runtime without pretending macOS Steam exposes Linux Proton's exact compatibilitytools.d contract. The current app-owned model stays authoritative: Steam owns account/session/download state, while MetalSharp owns the game process route, bottle, compatdata, logs, and runtime assets.

Current Surface

Steam game compatdata now records:

  • compat_tool_name
  • launch_command_template
  • launch_pipeline
  • steam_identity_mode
  • bottle_id
  • prefix_path
  • steam_prefix_path
  • runtime assets, components, and launch ledger state

The launch command template is intentionally backend-shaped:

POST /steam/launch-game {"appid":<appid>,"launchMethod":"<pipeline>"}

That keeps the contract honest. The current supported path is still MetalSharp launching the game process while Wine Steam remains alive in the background for Steamworks connectivity.

Why Not Fake Proton

Linux Proton is installed as a Steam compatibility tool under compatibilitytools.d. macOS Steam does not provide that same documented Proton tool surface. Wine Steam also needs to remain a normal Windows Steam client for login, downloads, and session state. For now, MetalSharp records a compatibility-tool-like contract in compatdata and uses its backend as the launcher.

Remaining Work

  • Verify whether any current macOS Steam or Wine Steam path honors compatibility tool metadata in a useful way.
  • Generate optional compatibilitytool.vdf scaffolding only for experiments, not as the default app path.
  • Add last-known-good runtime rollback per appid.
  • Add a visible per-game route template/debug view so users can see exactly what MetalSharp will launch.
Runtime

Vendor Trust Kit

Which vendor runtime files MetalSharp trusts.

3 sections · 2 min read

Updated: 2026-07-08

Status: Phase 9 foundation

The anti-cheat path is cooperative vendor trust, not bypass. A vendor or game developer should be able to inspect MetalSharp's runtime identity, launch model, logs, and non-evasion policy without reverse-engineering the repository.

Kit Contents

The vendor kit should include:

  • runtime identity and version
  • host ABI manifest
  • signed/notarized artifact evidence when available
  • Steam identity model
  • compatdata sample
  • launch log sample
  • process tree and environment handoff explanation
  • anti-cheat classification output
  • no-bypass/no-spoof policy
  • known unsupported boundaries
  • contact and reproduction instructions

Generator

tools/package/create-vendor-trust-kit.sh creates a local bundle under:

dist/vendor-trust-kit/

The generator copies the core policy/runtime docs and writes a manifest.json that records the current git commit, version files, and included documents. It does not claim a game is supported; it prepares the evidence package for a vendor conversation.

Required Before External Use

  • Replace placeholder signing/notarization state with real notarized artifact evidence.
  • Attach real launch logs from a target game.
  • Attach real anti-cheat classification JSON from Launch Doctor.
  • Add explicit vendor/game contact context.
  • Remove any stale "bypass" terminology from shipped UX and docs.
Runtime

Host Shim Inventory

A list of the native and Wine shims in use.

6 sections · 5 min read

Created: 2026-05-19

Purpose: Phase 0 inventory of existing C, C++, and Objective-C host shims that can seed a formal MetalSharp Host Runtime ABI.

Classification Key

  • stable: useful foundation that can move toward a supported ABI with limited redesign
  • prototype: useful experiment, but needs cleanup, tests, relocation, or broader semantics
  • game-specific: useful for a known game/runtime path but should not become a general ABI unchanged
  • legacy-risk: naming or behavior should be revisited before anti-cheat/vendor-facing work
  • obsolete: should be removed or replaced

Inventory

Area File Current role Classification Phase 1 action
Steam identity bridge src/fna/shims/steam_shim.c Exports Steam API symbols and talks to a localhost bridge on port 18733. prototype Generalize into Host Runtime ABI service for Steam identity/session calls. Make port/config dynamic and per-bottle aware.
FNA Steamworks offline shim src/fna/shims/csteamworks_shim.c, src/fna/shims/SteamworksOffline.cs Provides offline CSteamworks/Steamworks.NET success stubs by default, with optional native libsteam_api.dylib passthrough via METALSHARP_FNA_STEAM_PASSTHROUGH=1. game-specific Keep for FNA/Mono/XNA games; do not treat as complete Steamworks compatibility.
Win32 process/env/time shim src/fna/shims/kernel32_shim.c Provides POSIX/macOS-backed kernel32-style functions for console, env, timing, file, process, and thread basics. prototype Split reusable process/env/time services into Host Runtime ABI; leave FNA-specific exports behind.
Window/message shim src/fna/shims/user32_shim.c Provides lightweight user32 stubs for windows, messages, focus, key state, and basic UI calls. prototype Promote only explicit window/input services; document which calls are no-op compatibility stubs.
Carbon interpose src/fna/shims/carbon_interpose.c Intercepts dlopen for Carbon and redirects to METALSHARP_CARBON_SHIM. game-specific Keep as compatibility shim for legacy FNA/Mono/macOS paths. Do not make this a general injection pattern.
Carbon HIView shim src/fna/shims/carbon_hiview_shim.m Supplies Carbon/HIView compatibility symbols. game-specific Keep with FNA/legacy macOS shims; test only with known consumers.
FMOD stubs src/fna/shims/fmod_stub.c, src/fna/shims/fmodstudio_stub.c Stub FMOD libraries for FNA/game compatibility. game-specific Keep as per-game runtime assets, not ABI services.
Mono/.NET unixlib bridge src/wine/mscoree_unix.c Wine unixlib-style bridge that loads Mono via dlopen and executes managed assemblies. prototype Remove hardcoded paths, make bottle/runtime path configurable, add tests with known .NET installer/app cases.
Wine D3D11 unix bridge src/wine/metalsharp_unix.h, src/wine/metalsharp_unix.mm, src/wine/d3d11_pe.cpp, src/wine/metalsharp_d3d11_pe.cpp, src/wine/dxgi_pe.cpp PE-side D3D/DXGI shims dispatch to Objective-C++ Metal backend through Wine unixlib style calls. stable Use as the strongest model for Host Runtime ABI shape: PE-facing shim, host-side dispatch table, versioned structs.
Wine D3D9 unix bridge src/wine/d3d9_pe.cpp, src/wine/d3d9_unix.h, src/wine/d3d9_unix.mm PE-side D3D9 shim dispatches to Metal backend via unixlib calls. stable Align D3D9 and D3D11 dispatch/versioning patterns.
CoreAudio bridge src/audio/CoreAudioBackend.mm, src/audio/XAudio2Engine.cpp, src/audio/DirectSoundBackend.cpp Maps XAudio2/DirectSound-like behavior to CoreAudio. stable Expose audio capability and diagnostics through Host Runtime ABI.
GameController bridge src/input/GameControllerBridge.mm, src/input/XInputEngine.cpp Maps XInput-like behavior to Apple's GameController framework. stable Expose input capability and device state diagnostics through Host Runtime ABI.
PE loader shim registry src/loader/PELoader.cpp, src/loader/D3DShims.cpp Registers shim DLL exports and resolves imports for the native PE loader path. stable Keep as native-loader foundation; document boundary with Wine PE/unixlib path.
Win32 shim layer src/win32/kernel32/*.cpp, include/metalsharp/Win32Types.h, include/metalsharp/ExtraShims.h Larger native-loader Win32 shim set for kernel32/ntdll/network/extra APIs. stable Use selectively for ABI service mapping; avoid duplicating behavior between FNA C shims and C++ Win32 shims.
Anti-cheat database include/metalsharp/AntiCheatDB.h, src/runtime/DRMDetector.cpp Static detection tables now use evidence-backed support status strings instead of compatible/incompatible booleans. diagnostic Keep static statuses aligned with launch-recipe evidence and live protected-launch proof.
Runtime deploy glue app/src-c/runtime/steam_actions.c, app/src-c/runtime/setup.c Copies shims into runtime/game folders and assembles launch env. The Mono/FNA launcher has a native shim manifest for kernel32/user32/Carbon interpose plus bundled CoreAudio/GameController dylibs. prototype Extend the manifest pattern into versioned Host Runtime ABI asset selection and self-tests.

Strongest Existing Pattern

The D3D9/D3D11 Wine PE-to-unix split is the best model for Phase 1:

Windows PE side -> versioned structs -> dispatch id -> host Objective-C++ implementation -> Metal/CoreAudio/GameController/macOS APIs

That pattern is safer than ad hoc interposition because:

  • the boundary is explicit
  • structs can be versioned
  • host code can be tested independently
  • logs can name each dispatch
  • the ABI can be packaged as a known runtime artifact

Weakest Current Pattern

The weakest remaining pattern is per-game dylib/stub deployment outside the Mono/FNA native shim manifest:

  • hardcoded ports
  • hardcoded user paths
  • implicit @loader_path assumptions
  • no ABI version
  • no uniform self-test
  • unclear difference between no-op compatibility stubs and real host-backed services

Phase 1 should fix this by creating a versioned Host Runtime ABI and a manifest-driven shim selection layer.

Required Phase 1 Decisions

  1. Which services are ABI services? - process/env/path/time/logging: yes - Steam identity/session bridge: yes - graphics/audio/input dispatch: yes - per-game FMOD/FNA stubs: no, keep as runtime assets - Carbon interpose: no, keep as legacy compatibility asset

  2. Which runtime owns process launch? - The C backend owns high-level launch orchestration. - Host ABI should own low-level host service calls. - Wine Steam should own account/session/download state. - Game bottle/compatdata should own per-game runtime state.

  3. How are shims configured? - no hardcoded user paths - all paths derive from bottle/compatdata/runtime manifests - all local IPC ports or sockets are dynamic or manifest-configured - every shim can report version/capabilities

Phase 0 Conclusions

  • MetalSharp already has credible host shim foundations.
  • The D3D PE/unixlib bridge should become the architectural reference.
  • Several early shims are good experiments but must be made relocatable, tested, and versioned.
  • Anti-cheat-facing names and compatibility claims need cleanup before vendor-facing work.
Runtime

Darwin Sync Map

How macOS runtime files stay in sync.

4 sections · 2 min read

Updated: 2026-07-08

Status: Phase 6 foundation

Linux Proton can lean on Linux synchronization primitives and, where available, /dev/ntsync. macOS does not have Linux futexes or an ntsync device. MetalSharp has to map Windows synchronization behavior onto Darwin primitives without pretending macOS is Linux.

Source Surface

The first source-backed map lives in:

  • include/metalsharp/DarwinSyncMap.h
  • src/runtime/host/DarwinSyncMap.cpp
  • tests/test_darwin_sync_map.cpp

It classifies each primitive by strategy, whether a kernel/system component is required, and whether the mapping is safe to call shipping-ready today.

Current Classification

Primitive Strategy Shipping-ready Notes
Event pthread_mutex_t + pthread_cond_t yes Good for tracked in-process manual/auto reset events.
Semaphore dispatch/pthread counted semaphore yes Current use is covered; Mach semaphore benchmarking remains useful.
Mutex pthread_mutex_t yes Current in-process ownership semantics are covered.
CriticalSection pthread_mutex_t yes Already represented by existing kernel32/ntdll shims.
WaitAny condition-variable wakeups over tracked handles yes In-process only; cross-process semantics need more work.
WaitAll coordinated wait over tracked handles no Needs correctness tests for mixed event/semaphore/mutex waits.
Futex Darwin ulock candidate no Research candidate only; not Linux ABI compatible.
NtSyncDevice unsupported Linux-specific device no macOS has no /dev/ntsync equivalent.

Boundary

This map is not an anti-cheat bypass and not a kernel plan. It is a compatibility inventory. If a future game proves we need cross-process NT object semantics or lower-latency wait behavior, that evidence decides whether to prototype a user-space ulock strategy, a Mach-backed strategy, or an Apple-approved system extension investigation.

Remaining Work

  • Add mixed-object WaitAll correctness tests.
  • Benchmark pthread condition variables against Mach semaphores and ulock where legally usable.
  • Identify which real games stress synchronization enough to justify deeper work.
  • Document entitlement, signing, notarization, install, and user-consent requirements before any system extension prototype.
Architecture

Launch Architecture

How MetalSharp chooses a route and starts a game.

2 sections · 2 min read

Updated: 2026-09-08

MetalSharp launches games through the C backend and the current MTSP pipeline resolver.

Flow

Play clicked
  -> renderer calls backend
  -> backend resolves a pipeline
  -> backend syncs/preflights the runtime bottle when one applies
  -> backend builds a LaunchRecipe
  -> backend preflights runtime assets
  -> backend prepares DLLs/env/cache beside the selected executable
  -> selected MTSP route starts the game; internal Steam/Wine/macOS handoffs are used only when the backend selects them

Current Pipelines

Public route Backend Launch path
VKD3D Vulkan Direct Wine launch with dxvk/vkd3d-proton D3D12/11/10/9/DXGI Dll's with updated MoltenVK 1.4.3 dylib/icd
M12 - Hidden from UI DXMT Direct Wine launch with isolated dxmt-m12 D3D12/D3D11/DXGI/winemetal DLLs
M11(32) DXMT Direct Wine launch with i386 dxmt D3D11/DXGI DLLs
M11 DXMT Direct Wine launch with legacy dxmt D3D11/DXGI DLLs
M10(32) DXMT Direct Wine launch with i386 dxmt D3D10/D3D10core/DXGI DLLs
M10 DXMT Direct Wine launch with legacy dxmt D3D10/D3D10core/DXGI DLLs
M9 DXMT launch family Direct Wine launch with bundled d3d9.dll and DXMT-family cache/env
Mono/FNA Native Mono Native FNA/XNA/Mono runtime with FNA/XNA assemblies, native dylib staging, FMOD/FAudio/FNA3D shims, and Steamworks shim support
D3DMetal Managed GPTK 4 beta 2 Steam-aware direct launch through MetalSharp Wine 11.17, with game-local D3DMetal DLLs and prefix-steam

D3DMetal uses the same managed Wine runtime as the other Wine-backed Steam routes, not a separate Homebrew Wine installation. Its launcher supplies SteamAppId/SteamGameId, the payload root in D3DMETAL_RUNTIME_DIR, and the framework executable in D3DMETAL_FRAMEWORK_PATH. M12 remains a separate DXMT route, not an alias for D3DMetal or VKD3D. See Wine Architecture.

Architecture

D3D12 Pipeline Map

How D3D12 reaches Metal through DXMT.

7 sections · 7 min read

Updated: 2026-07-08

Last verified: 2026-06-13.

M12 is the D3D12 -> DXMT -> Metal path used by the game launcher. The current MetalSharp tree also contains a native metalsharp_d3d12 implementation and a Cocoa/CAMetalLayer viewer path, but those are not the same runtime path that M12 uses for Wine-launched games.

Runtime Ownership

Layer Current owner Evidence Status
Game detection app/src-c/runtime/steam_actions.c, mtsp.c D3D12 imports and rules select M12 for compatible 64-bit games. Present in current project
Pipeline definition app/src-c/runtime/mtsp.c, steam_actions.c M12 is named D3D12 -> Metal via DXMT, deploys isolated DXMT DLLs, and sets D3D12/DXGI/D3D11 overrides. Present in current project
Launcher handoff app/src-c/runtime/steam_actions.c The C launch route copies DLLs into the game directory and sets Wine/DYLD/cache env. Present in current project
Shader/cache routing app/src-c/runtime/steam_actions.c M12 uses isolated m12 shader and pipeline cache directories. Present in current project
M12 artifact surface ~/.metalsharp/runtime/wine/lib/dxmt_m12 M12 loads the updated D3D12/DXGI/winemetal payload from the isolated dxmt_m12 directory. Present in current project
Legacy DXMT surface ~/.metalsharp/runtime/wine/lib/dxmt M9/M10/M11 continue to use the known-good legacy DXMT payload. Present in current project
DXMT D3D12 implementation External DXMT source tree Conformance branch contains the real DXMT D3D12/DXIL/winemetal work used by M12 runtime DLLs. External source tree
Native D3D12 target include/metalsharp/D3D12Device.h, src/d3d/d3d12/* Builds build/d3d12.dylib and exposes D3D12CreateDevice. In-tree, smoke-tested
Cocoa surface src/win32/user32/WindowManager.mm, src/dxgi/DXGISwapChain.mm Creates NSWindow/CAMetalLayer for the native loader path. In-tree, not the Wine M12 surface
Wine M12 surface DXMT winemetal.so plus Wine/macOS windowing DXMT presents through Wine/winemetal, not through the native WindowManager path. External runtime path

M12 Launch Flow

  1. The PE scanner sees d3d12.dll and rules select M12.
  2. The launcher resolves the game directory and Wine prefix.
  3. M12 deploys DXMT PE DLLs from lib/dxmt_m12/x86_64-windows into the game directory: d3d12.dll, d3d11.dll, dxgi.dll, d3d10core.dll, and winemetal.dll.
  4. M12 sets WINEDLLOVERRIDES so Wine prefers the deployed native DXMT DLLs.
  5. M12 adds lib/dxmt_m12/x86_64-unix and Wine unix library paths to DYLD_FALLBACK_LIBRARY_PATH.
  6. M12 sets shader and pipeline cache paths under the MetalSharp cache root.
  7. Wine launches the executable without a forced DirectX command-line flag. dx12 and d3d12 are route aliases for selecting M12, not universal game args.
  8. DXMT handles D3D12/DXGI calls, compiles DXIL/MSL work, sends commands through winemetal, and presents through the Wine/macOS surface.

Current Verification

These checks were run from the repository root:

cmake --build build --target test_d3d12
cmake --build build --target test_d3d12_entrypoint test_d3d12
./build/tests/test_d3d12
./build/tests/test_d3d12_entrypoint
ctest --test-dir build -R "d3d12|d3d12_entrypoint|phase18|phase19" --output-on-failure
nm -gU build/d3d12.dylib | rg "D3D12CreateDevice|D3D12GetDebugInterface|D3D12SerializeRootSignature"
otool -L build/d3d12.dylib

Results:

  • test_d3d12 passed: 50 passed, 0 failed.
  • test_d3d12_entrypoint passed: 5 passed, 0 failed.
  • ctest passed d3d12, d3d12_entrypoint, phase18, and phase19.
  • build/d3d12.dylib exports D3D12CreateDevice.
  • build/d3d12.dylib links Metal, Foundation, QuartzCore, AppKit, and libmetalirconverter.

The external DXMT source tree also rebuilt successfully with:

ninja -C <dxmt-source>/build src/winemetal/unix/winemetal.so src/d3d12/d3d12.dll

The current release-hosted graphics bundle contains two DXMT surfaces:

  • Graphics/dll/dxmt: the 0.46.5 legacy surface used by M9/M10/M11.
  • Graphics/dll/dxmt-m12: the updated M12 surface used only by M12, including winemetal.so, libc++.1.dylib, libc++abi.1.dylib, and libunwind.1.dylib.

Completion State

Area State Notes
M12 app routing Primary/stable Current project maps D3D12 games to M12 before broad directory heuristics, uses M12 as the unresolved default, and invokes the backend launcher path.
M12 backend handoff Present The handoff copies isolated M12 DXMT DLLs, configures Wine/DYLD env, cache env, and launch args.
Subnautica-class M12 runtime Demonstrated by local use This validates the launcher/runtime path, not the native CMake D3D12 dylib.
Avery DXMT probes Strongest external proof tests/ROADMAP.md in dxmt-src marks probes 2-6 complete, including compute, triangle, indexed draw, depth, and texture sampling.
Deployed runtime parity Split surface M9/M10/M11 stay on the known-good dxmt surface while M12 uses the updated release-hosted dxmt-m12 surface.
Avery source cleanliness Needs cleanup dxmt-src has dirty debug/probe changes and notes that prior dirty changes broke Steam launching.
Native in-tree D3D12 Expanded coverage Smoke, C entrypoint, MSL compute PSO dispatch, and MSL indexed draw tests pass.
Native compute PSO Implemented for MSL/DXBC/DXIL paths The in-tree native CreateComputePipelineState now creates a real Metal compute pipeline when shader bytecode is available.
Native indexed draw Covered by offscreen test GPU virtual-address lookup now binds real Metal vertex/index buffers and the test executes an indexed draw.
Native raytracing/mesh Stubbed Advanced D3D12 calls return success or placeholders without full Metal execution.
Native Cocoa viewer Implemented separately The NSWindow/CAMetalLayer path exists for native-loader presentation, but M12 Wine games present through DXMT/winemetal.

Stability Gaps To Close

  1. Add a first-class M12 runtime verification command in this repo that launches a small D3D12 probe through the same C launch environment used by games. — Addressed (Phase 3): GET /diagnostics/m12/dry-run?appid=... and GET /diagnostics/pipeline/dry-run?appid=...&pipeline=m12 report the exact env pairs, artifact hashes, and unix sidecars M12 would load, using the same route path and cache builders as a real C backend launch, without launching Steam or the game. The existing POST /steam/d3d12-runtime-doctor runs the SDK mini-probe suite through that same environment.
  2. Add a native Cocoa viewer test target if the goal is to exercise the in-tree metalsharp_d3d12 implementation through CAMetalLayer rather than through Wine/winemetal.
  3. Expand native D3D12 tests beyond the current graphics/compute coverage: texture sampling, depth compare, and swapchain present.

Practical Conclusion

The current MetalSharp project treats M12 as the D3D12 DXMT route while keeping older DXMT routes isolated. D3D12 PE import detection selects M12, the backend handoff deploys the dxmt_m12 runtime, and M9/M10/M11 continue to use the legacy dxmt surface that is known to work for current Steam/Wine titles.

M12 Artifact and Launch Verification (Phase 3)

A reviewer can prove M12 loaded the intended artifacts without launching a full game using the read-only dry-run verifier. It runs through the same environment builders (set_route_paths and set_launch_cache_env) used by the C launch route, so the reported env pairs and artifact sources are exactly what a real M12 launch would use.

  • GET /diagnostics/m12/dry-run?appid=<appid> — M12-specific dry-run including the lib/dxmt_m12/x86_64-unix sidecars (winemetal.so, libc++.1.dylib, libc++abi.1.dylib, libunwind.1.dylib).
  • GET /diagnostics/pipeline/dry-run?appid=<appid>&pipeline=m12|m11|... — generic pipeline dry-run for comparing lanes.

The dry-run reports, per artifact: resolved source path, presence, sha256, and size; required artifacts that are missing produce a structured ok: false with a missing[] array rather than a silent fallback. Env keys verified present: WINEDLLOVERRIDES (winemetal overrides), DXMT_SHADER_CACHE_PATH (isolated m12 lane), DYLD_FALLBACK_LIBRARY_PATH/LD_LIBRARY_PATH, SteamAppId, and DXMT_WINEMETAL_UNIXLIB.

Contract guarantees covered by tests:

  • M12 deploys d3d12.dll, dxgi.dll, d3d11.dll, d3d10core.dll, winemetal.dll from lib/dxmt_m12/x86_64-windows.
  • M11 does not deploy d3d12.dll and points only at lib/dxmt, never lib/dxmt_m12.
  • M12 dry-run includes d3d12.dll; M11 dry-run does not.
Architecture

D3D12 Shader Engine

How D3D12 shaders become Metal pipelines.

7 sections · 6 min read

Updated: 2026-07-08

M12 treats shader translation as a defined engine, not as a single converter function. The engine boundary starts when a D3D12 game supplies DXBC or DXIL bytecode and ends when a Metal render or compute pipeline is created, cached, bound, and used by a draw, dispatch, or present pass.

The shader engine is also a deployment contract. Install and migration must put known-good shader-engine material on disk, launch must seed the selected M12 cache from that material, and diagnostics must write to logs instead of mixing proof output into shader or pipeline caches.

Engine Surfaces

  • DXIL intake: dxil_container, llvm_bitcode, and dxil_ir parse DXIL containers, LLVM bitcode, shader model, entrypoint, values, types, and resource handles.
  • MSL lowering: msl_lowering is the primary typed DXIL-to-MSL path. It owns typed value coercion, DX op lowering, vertex input handling, binding manifests, and targeted compatibility fallbacks. dxil_to_msl remains the fallback path and must follow the same resource-binding rules.
  • D3D12 shader compiler: d3d12_shader_compiler owns runtime compilation, cache lookup, converter selection, generated MSL sidecars, metallib creation, and compile diagnostics.
  • PSO build: d3d12_device and d3d12_pipeline_state map D3D12 graphics and compute PSO descriptors to Metal pipeline states.
  • Runtime binding: command list and command queue code bind descriptor tables, root constants, direct buffers, vertex inputs, draw args, and present resources before command encoding.
  • Cache and diagnostics: shader cache paths, MSL/metallib sidecars, root-signature reports, PSO manifests, and focused D3D12 trace output are part of the engine contract because stale or incomplete cache data can make a fixed shader look broken.
  • Installed corpus: checked-in shader corpora under tools/d3d12-metal-sdk/shader-corpus/ are packaged into the runtime and scripts/tools bundles. Install and migration validate the expected corpus proof file and runtime-safe material before M12 is considered ready.

Runtime Material

M12 shader-engine material can be sourced from:

~/.metalsharp/runtime/wine/share/d3d12-metal-sdk/shader-corpus/
~/.metalsharp/runtime/d3d12-metal-sdk/shader-corpus/
~/.metalsharp/scripts/tools/d3d12-metal-sdk/shader-corpus/

The install/migration readiness proof is the source-controlled corpus checksum file:

elden-ring-present-vb-pull-20260612/proof/SHA256SUMS

That proof must exist under at least one installed corpus source, and the source must also contain runtime-safe shader-engine material. This prevents a partial archive with one stray .metallib from passing M12 readiness.

When a game uses the M12 cache namespace, the C backend copies runtime-safe files into:

~/.metalsharp/shader-cache/m12/<appid>/

The copied file classes are:

.metallib
.air
.msl
.dxbc
.dxil
.cso
.json
.module.txt
.dxil_report.txt

The cache seeding step intentionally skips proof, result, log, and direct Metal error directories. Runtime caches should contain shader-engine inputs and sidecars, not the entire developer proof tree.

Logs are separate:

~/.metalsharp/logs/m12-pipeline/<appid>/

Pipeline caches are also separate:

~/.metalsharp/pipeline-cache/m12/<appid>/

This split matters. A shader fix is not proven by a stale .metallib, and a runtime log is not a shader cache input.

Invariants

  • A DXIL createHandle regression must be proven against generated MSL, not inferred from a game frame. The present-blend regression for Elden shader 6f0e7d2f3cfff83c proves the engine samples tex0 and tex1, not the old double-counted tex2 path.
  • Every typed MSL shader must emit metalsharp.binding_manifest.v1 so offline tooling can audit direct buffers, textures, samplers, and descriptor ranges.
  • Root-signature coverage, PSO manifests, and shader manifests are separate proof layers. Passing one layer does not imply the others are correct.
  • Live game captures are diagnostics only. Contract status comes from source contracts, converter tests, corpus replay, PSO audits, and SDK probes.
  • Shader cache invalidation is part of correctness. When lowering changes, stale .msl, .metallib, and pipeline-cache outputs must not be used as proof of current behavior.
  • A present or fullscreen fallback shader must preserve the draw shape it is replacing. For example, the procedural fallback distinguishes a 4-vertex triangle-strip fullscreen quad from a 3-vertex fullscreen triangle by reading the bound draw args instead of clamping every draw to three vertices.
  • A binding-completeness failure is not only a shader failure. Root signature, descriptor table, direct root buffer, vertex input, render-target, and PSO metadata must be checked together before blaming lowering.

Proof Layers

The engine has separate proof layers. They are cumulative, not interchangeable.

Layer Artifact Proves
DXIL intake .dxbc, .dxil, .module.txt, DXIL reports The bytecode was found, parsed, and classified.
Lowering .msl, binding manifests DXIL values, resources, semantics, and bindings lowered into MSL.
Metal compile .air, .metallib, compiler logs Generated MSL compiles and links under Apple's Metal toolchain.
Root signature root-signature reports Shader bindings can be matched to D3D12 root parameters and descriptor ranges.
PSO manifests pso-render-*.json, pso-compute-*.json Render/compute pipeline descriptors have enough format, topology, shader, and binding metadata.
Runtime binding focused m12.log diagnostics Command lists bind descriptors, buffers, draw args, render targets, and resources before encoding.
Developer probes tools/d3d12-metal-sdk/results/*.json The pipeline behavior is reproducible without a commercial game launch.

Required Gates

Run the structural engine check first:

python3 tools/d3d12-metal-sdk/scripts/validate-shader-engine.py \
  --json tools/d3d12-metal-sdk/results/shader-engine-audit-metalsharp.json

Then run the focused native converter gate against the relevant captured corpus:

ninja -C vendor/dxmt/build-metalsharp-x64-tests tests/test_dxil_converter
vendor/dxmt/build-metalsharp-x64-tests/tests/test_dxil_converter <shader-cache>

For generated MSL directories, run:

python3 tools/d3d12-metal-sdk/scripts/dxil-binding-manifest-audit.py \
  --msl-dir <generated-msl> \
  --markdown tools/d3d12-metal-sdk/results/binding-manifest-audit.md

For captured runtime PSO/root-signature corpora, run the root-signature, graphics PSO, and compute PSO audits before a game launch is used as evidence.

The full SDK render proof remains:

tools/ci/m12-check.sh

For release-bundle proof, also run:

tools/bundles/verify-bundles.sh --bundle-dir app/bundles --require mac
tools/bundles/verify-developer-sdk.sh app/bundles/metalsharp-d3d12-developer-sdk.tar.zst

Contract Authority

The machine-readable source of truth is tools/d3d12-metal-sdk/contracts/d3d12-shader-engine-contract.json. It names the source surfaces, required offline gates, runtime artifacts, and review evidence. validate-contracts.py includes this contract, while validate-shader-engine.py verifies the engine-specific source and gate shape.

Developer Stress Executables

Two Windows executables exist so M12 development does not depend on repeated commercial game launches:

  • m12_game.exe: the PR/CI cube proof built by tools/ci/m12-check.sh.
  • m12_stress_game.exe: the higher-pressure title-like scene built by tools/d3d12-metal-sdk/scripts/m12-dev.sh stress-game.

m12_stress_game.exe is deliberately broader than a unit probe. It starts with a splash/movie-style pass, then renders a beach scene with water, sun, boat, tree geometry, text, textures, shadows, unusual vertices, and repeated presents. It should be used when changing the shader engine, PSO creation, vertex binding, texture sampling, or present path and a game run would be too unstable.

Architecture

D3D10 Pipeline Map

How D3D10 reaches Metal through DXMT.

3 sections · 2 min read

Updated: 2026-07-08

M10 is the stable D3D10 engine path. It launches Windows D3D10 titles through Wine and DXMT, then hands rendering to Metal through DXMT's winemetal bridge.

Runtime Shape

D3D10 game
  -> Wine
  -> Wine d3d10.dll / d3d10_1.dll public entrypoints
  -> DXMT d3d10core.dll
  -> DXMT d3d11.dll + dxgi.dll
  -> winemetal.dll / winemetal.so
  -> Metal command buffers
  -> Apple GPU

M10 deploys Wine's public D3D10 entrypoint DLLs for games that import d3d10.dll or d3d10_1.dll, then routes the core handoff through DXMT's d3d10core.dll plus the same DXMT D3D11, DXGI, and winemetal runtime used by M11.

M10 deploys these public D3D10 entrypoints from ~/.metalsharp/runtime/wine/lib/wine/x86_64-windows/:

  • d3d10.dll
  • d3d10_1.dll

M10 deploys these DXMT handoff DLLs from ~/.metalsharp/runtime/wine/lib/dxmt/x86_64-windows/:

  • d3d11.dll
  • dxgi.dll
  • d3d10core.dll
  • winemetal.dll

M10 deliberately does not deploy d3d12.dll.

Engine Contract

Field Value
Pipeline M10
Backend dxmt
Launch args none by default; dx10/d3d10 select M10 as route aliases
Wine overrides d3d10,d3d10_1,dxgi,d3d11,d3d10core=n,b;gameoverlayrenderer,gameoverlayrenderer64=d
Shader cache subdir m10
Preset fallback family m10, then dxmt-metal

M10 uses the same DXMT Unix library search path as M11:

lib/wine/x86_64-unix
lib/dxmt/x86_64-unix

Selection Rules

The backend resolves M10 from 64-bit PE imports before broad directory heuristics. That keeps 64-bit D3D10 games from being demoted to M11 just because their folder also includes common engine or Steam markers. 32-bit D3D10 executables are not routed into M10 because this runtime contract deploys the x86_64 D3D10/DXMT payload.

Recognized D3D10 imports:

  • d3d10.dll
  • d3d10_1.dll
  • d3d10core.dll

If a game imports both D3D12 and D3D10 compatibility DLLs, D3D12 still wins and maps to M12 for 64-bit executables.

Architecture

Internal Route Detail

How the internal M12 machinery routes behind DXMT.

7 sections · 7 min read

Updated: 2026-07-08

Last verified: 2026-06-13.

M12 is the D3D12 -> DXMT -> Metal path used by the game launcher. The current MetalSharp tree also contains a native metalsharp_d3d12 implementation and a Cocoa/CAMetalLayer viewer path, but those are not the same runtime path that M12 uses for Wine-launched games.

Runtime Ownership

Layer Current owner Evidence Status
Game detection app/src-c/runtime/steam_actions.c, mtsp.c D3D12 imports and rules select M12 for compatible 64-bit games. Present in current project
Pipeline definition app/src-c/runtime/mtsp.c, steam_actions.c M12 is named D3D12 -> Metal via DXMT, deploys isolated DXMT DLLs, and sets D3D12/DXGI/D3D11 overrides. Present in current project
Launcher handoff app/src-c/runtime/steam_actions.c The C launch route copies DLLs into the game directory and sets Wine/DYLD/cache env. Present in current project
Shader/cache routing app/src-c/runtime/steam_actions.c M12 uses isolated m12 shader and pipeline cache directories. Present in current project
M12 artifact surface ~/.metalsharp/runtime/wine/lib/dxmt_m12 M12 loads the updated D3D12/DXGI/winemetal payload from the isolated dxmt_m12 directory. Present in current project
Legacy DXMT surface ~/.metalsharp/runtime/wine/lib/dxmt M9/M10/M11 continue to use the known-good legacy DXMT payload. Present in current project
DXMT D3D12 implementation External DXMT source tree Conformance branch contains the real DXMT D3D12/DXIL/winemetal work used by M12 runtime DLLs. External source tree
Native D3D12 target include/metalsharp/D3D12Device.h, src/d3d/d3d12/* Builds build/d3d12.dylib and exposes D3D12CreateDevice. In-tree, smoke-tested
Cocoa surface src/win32/user32/WindowManager.mm, src/dxgi/DXGISwapChain.mm Creates NSWindow/CAMetalLayer for the native loader path. In-tree, not the Wine M12 surface
Wine M12 surface DXMT winemetal.so plus Wine/macOS windowing DXMT presents through Wine/winemetal, not through the native WindowManager path. External runtime path

M12 Launch Flow

  1. The PE scanner sees d3d12.dll and rules select M12.
  2. The launcher resolves the game directory and Wine prefix.
  3. M12 deploys DXMT PE DLLs from lib/dxmt_m12/x86_64-windows into the game directory: d3d12.dll, d3d11.dll, dxgi.dll, d3d10core.dll, and winemetal.dll.
  4. M12 sets WINEDLLOVERRIDES so Wine prefers the deployed native DXMT DLLs.
  5. M12 adds lib/dxmt_m12/x86_64-unix and Wine unix library paths to DYLD_FALLBACK_LIBRARY_PATH.
  6. M12 sets shader and pipeline cache paths under the MetalSharp cache root.
  7. Wine launches the executable without a forced DirectX command-line flag. dx12 and d3d12 are route aliases for selecting M12, not universal game args.
  8. DXMT handles D3D12/DXGI calls, compiles DXIL/MSL work, sends commands through winemetal, and presents through the Wine/macOS surface.

Current Verification

These checks were run from the repository root:

cmake --build build --target test_d3d12
cmake --build build --target test_d3d12_entrypoint test_d3d12
./build/tests/test_d3d12
./build/tests/test_d3d12_entrypoint
ctest --test-dir build -R "d3d12|d3d12_entrypoint|phase18|phase19" --output-on-failure
nm -gU build/d3d12.dylib | rg "D3D12CreateDevice|D3D12GetDebugInterface|D3D12SerializeRootSignature"
otool -L build/d3d12.dylib

Results:

  • test_d3d12 passed: 50 passed, 0 failed.
  • test_d3d12_entrypoint passed: 5 passed, 0 failed.
  • ctest passed d3d12, d3d12_entrypoint, phase18, and phase19.
  • build/d3d12.dylib exports D3D12CreateDevice.
  • build/d3d12.dylib links Metal, Foundation, QuartzCore, AppKit, and libmetalirconverter.

The external DXMT source tree also rebuilt successfully with:

ninja -C <dxmt-source>/build src/winemetal/unix/winemetal.so src/d3d12/d3d12.dll

The current release-hosted graphics bundle contains two DXMT surfaces:

  • Graphics/dll/dxmt: the 0.46.5 legacy surface used by M9/M10/M11.
  • Graphics/dll/dxmt-m12: the updated M12 surface used only by M12, including winemetal.so, libc++.1.dylib, libc++abi.1.dylib, and libunwind.1.dylib.

Completion State

Area State Notes
M12 app routing Primary/stable Current project maps D3D12 games to M12 before broad directory heuristics, uses M12 as the unresolved default, and invokes the backend launcher path.
M12 backend handoff Present The handoff copies isolated M12 DXMT DLLs, configures Wine/DYLD env, cache env, and launch args.
Subnautica-class M12 runtime Demonstrated by local use This validates the launcher/runtime path, not the native CMake D3D12 dylib.
Avery DXMT probes Strongest external proof tests/ROADMAP.md in dxmt-src marks probes 2-6 complete, including compute, triangle, indexed draw, depth, and texture sampling.
Deployed runtime parity Split surface M9/M10/M11 stay on the known-good dxmt surface while M12 uses the updated release-hosted dxmt-m12 surface.
Avery source cleanliness Needs cleanup dxmt-src has dirty debug/probe changes and notes that prior dirty changes broke Steam launching.
Native in-tree D3D12 Expanded coverage Smoke, C entrypoint, MSL compute PSO dispatch, and MSL indexed draw tests pass.
Native compute PSO Implemented for MSL/DXBC/DXIL paths The in-tree native CreateComputePipelineState now creates a real Metal compute pipeline when shader bytecode is available.
Native indexed draw Covered by offscreen test GPU virtual-address lookup now binds real Metal vertex/index buffers and the test executes an indexed draw.
Native raytracing/mesh Stubbed Advanced D3D12 calls return success or placeholders without full Metal execution.
Native Cocoa viewer Implemented separately The NSWindow/CAMetalLayer path exists for native-loader presentation, but M12 Wine games present through DXMT/winemetal.

Stability Gaps To Close

  1. Add a first-class M12 runtime verification command in this repo that launches a small D3D12 probe through the same C launch environment used by games. — Addressed (Phase 3): GET /diagnostics/m12/dry-run?appid=... and GET /diagnostics/pipeline/dry-run?appid=...&pipeline=m12 report the exact env pairs, artifact hashes, and unix sidecars M12 would load, using the same route path and cache builders as a real C backend launch, without launching Steam or the game. The existing POST /steam/d3d12-runtime-doctor runs the SDK mini-probe suite through that same environment.
  2. Add a native Cocoa viewer test target if the goal is to exercise the in-tree metalsharp_d3d12 implementation through CAMetalLayer rather than through Wine/winemetal.
  3. Expand native D3D12 tests beyond the current graphics/compute coverage: texture sampling, depth compare, and swapchain present.

Practical Conclusion

The current MetalSharp project treats M12 as the D3D12 DXMT route while keeping older DXMT routes isolated. D3D12 PE import detection selects M12, the backend handoff deploys the dxmt_m12 runtime, and M9/M10/M11 continue to use the legacy dxmt surface that is known to work for current Steam/Wine titles.

M12 Artifact and Launch Verification (Phase 3)

A reviewer can prove M12 loaded the intended artifacts without launching a full game using the read-only dry-run verifier. It runs through the same environment builders (set_route_paths and set_launch_cache_env) used by the C launch route, so the reported env pairs and artifact sources are exactly what a real M12 launch would use.

  • GET /diagnostics/m12/dry-run?appid=<appid> — M12-specific dry-run including the lib/dxmt_m12/x86_64-unix sidecars (winemetal.so, libc++.1.dylib, libc++abi.1.dylib, libunwind.1.dylib).
  • GET /diagnostics/pipeline/dry-run?appid=<appid>&pipeline=m12|m11|... — generic pipeline dry-run for comparing lanes.

The dry-run reports, per artifact: resolved source path, presence, sha256, and size; required artifacts that are missing produce a structured ok: false with a missing[] array rather than a silent fallback. Env keys verified present: WINEDLLOVERRIDES (winemetal overrides), DXMT_SHADER_CACHE_PATH (isolated m12 lane), DYLD_FALLBACK_LIBRARY_PATH/LD_LIBRARY_PATH, SteamAppId, and DXMT_WINEMETAL_UNIXLIB.

Contract guarantees covered by tests:

  • M12 deploys d3d12.dll, dxgi.dll, d3d11.dll, d3d10core.dll, winemetal.dll from lib/dxmt_m12/x86_64-windows.
  • M11 does not deploy d3d12.dll and points only at lib/dxmt, never lib/dxmt_m12.
  • M12 dry-run includes d3d12.dll; M11 dry-run does not.
Architecture

DXMT & Vulkan Architecture

How DXMT routes and Vulkan fallbacks fit together.

3 sections · 2 min read

Updated: 2026-09-08

MetalSharp separates its graphics translation families:

  • DXMT launch family: M9/M10/M11/M12 to Metal
  • DXMT 32Bit Launch Family: M10(32)/M11(32) to Metal
  • DXVK + MoltenVK: VKD3D D3D12/11/10/9 Via MoltenVk -> Metal
  • D3DMetal: managed GPTK 4 beta 2 payload, using MetalSharp Wine 11.17 and the shared Steam prefix

Pipeline Map

Public route Translation
VKD3D D3D12/11/10/9 -> Moltenvk -> Metal
M12 D3D12 -> DXMT -> Metal
M11 D3D11 -> DXMT -> Metal
M10 D3D10 -> DXMT -> Metal
M9 D3D9 -> MetalSharp D3D9 -> DXMT launch family -> Metal
D3DMetal GPTK 4 beta 2 D3D12/11 -> Metal; separate from DXMT and VKD3D

DXMT

The DXMT launch family is used by M12, M11, M10, and M9

The managed baseline is DXMT v0.80. The isolated M12 payload remains separate; upstream v0.80 alone is not a substitute for MetalSharp's complete M12 runtime. See Runtime Bundles and Steam Routing.

DXMT-family DLLs:

DLL Used by
d3d12.dll M12 - Intentionally hidden from user facing routes
d3d11.dll (i386) M11(32)
d3d11.dll M11, M10
dxgi.dll M12, M11, M10
d3d10.dll(i386), d3d10core(i386) M10(32)
d3d10.dll, d3d10_1.dll d3d10core M10
winemetal.dll M12, M11, M10
d3d9.dll M9
winemetal.so Unix Metal bridge

Basic flow:

Game
  -> DXMT PE DLL
  -> Winemetal.so / Winemetal.dll
  -> Metal command buffers
  -> Apple GPU

DXMT uses per-game shader caches under:

~/.metalsharp/shader-cache/m9/<appid>/
~/.metalsharp/shader-cache/m10/<appid>/
~/.metalsharp/shader-cache/m11/<appid>/
~/.metalsharp/shader-cache/m12/<appid>/

VKD3D

The Vulkan Launch Family used by VKD3D

VKD3D-Family Dlls:

DLL Notes Used By
d3d12.dll,d3d12core.dll VKD3D-Proton Dll's VKD3D
d3d11.dll DXVK-MacOS Dll VKD3D
d3d10core.dll DXVK-MacOS Dll VKD3D
d3d9.dll DXVK-MacOS Dll VKD3D
dxgi.dll DXVK-MacOS Dll with d3d12 support VKD3D
MoltenVK.dylib, Moltenvk_icd.json Metal Renderer for VKD3D VKD3D
Emulators

PCSX2 Integration

How MetalSharp connects to PCSX2.

9 sections · 6 min read

MetalSharp provides an isolated, managed environment for the official stable PCSX2 macOS app.

What MetalSharp manages

  • Official stable release discovery and 12-hour metadata caching.
  • Exact asset size and SHA-256 verification.
  • Safe .tar.xz preflight and shell-free extraction with lsar and unar.
  • x86_64 architecture, macOS deployment target, contained dependencies, bundle identity, Developer ID team, hardened runtime, and notarization checks.
  • Versioned, read-only activation with pin, skip, repair, rollback, and configuration backup.
  • A private PCSX2 home at ~/.metalsharp/emulators/pcsx2/home.
  • User-owned BIOS validation and atomic import.
  • Exact indexing of individually selected disc images, plus bounded discovery in explicitly selected game folders.
  • Allowlisted PCSX2 controller-type and renderer settings written atomically to the isolated upstream configuration.
  • Direct, shell-free launch and restart-safe process supervision.
  • Preservation of BIOS, memory cards, saves, savestates, settings, controller profiles, covers, caches, screenshots, logs, and games during runtime updates and removal.

MetalSharp does not bundle PCSX2 and never downloads or uploads Sony BIOS files, games, disc images, licenses, or other console content.

Host requirements

  • macOS 11 or newer;
  • Intel x86-64 with SSE4.1, or Apple Silicon with Rosetta 2;
  • 8 GiB RAM recommended by upstream;
  • game-specific compatibility and performance vary.

The currently managed stable app is x86_64. Apple Silicon launches use Rosetta explicitly. PCSX2 does not use Wine, GPTK, D3DMetal, Steam bottles, RPCS3 state, or shadPS4 state.

Setup

  1. Open Sharp Library → PCSX2.
  2. Install the verified official stable runtime.
  3. Follow PCSX2's official BIOS dumping guide and import a BIOS dumped from a PlayStation 2 you own.
  4. Expand PCSX2 Setup in MetalSharp and select the virtual controller type for ports 1 and 2 and the renderer. These settings are saved directly to PCSX2 without opening its application.
  5. Add an owned disc image or a dedicated game folder. The official disc-dumping guide explains supported dumping methods.

Supported library files are ISO, BIN, IMG, MDF, GZ, CSO, ZSO, CHD, and homebrew ELF. CUE, TOC, and CDR sidecars are not launchable PCSX2 library entries.

Selecting one file indexes only that file; MetalSharp does not scan the file's parent directory. Selecting a folder opts that folder into bounded recursive discovery. Removing a location removes only its reference and never deletes or rewrites external content.

PCSX2 Setup exposes the exact upstream controller values DualShock 2, Guitar, JogCon, NeGcon, and Pop'n Music for both emulated ports. Renderer choices are Automatic, Metal, OpenGL, Vulkan, and Software—the methods present in the verified macOS runtime. Each change is allowlisted, written atomically, and reloaded whenever MetalSharp opens the page. Settings changes are blocked while PCSX2 or a runtime transaction is active to prevent configuration races. Advanced input mapping and other expert options remain available through Open PCSX2, but they are not required for these baseline choices.

Data layout

~/.metalsharp/emulators/pcsx2/
├── current -> versions/<tag>
├── previous -> versions/<tag>
├── versions/<tag>/
│   ├── PCSX2.app/
│   ├── LICENSE
│   ├── THIRD_PARTY_LICENSES.html
│   ├── source.json
│   └── capabilities.json
├── home/Library/Application Support/PCSX2/
├── downloads/
├── staging/
├── sessions/
├── logs/
├── backups/
├── environment.json
├── update-policy.json
└── library.json

Runtime removal deletes only downloaded runtime versions, activation pointers, downloads, and staging. The isolated home, backups, policies, library references, logs, sessions, and external games remain.

Updates and rollback

MetalSharp disables PCSX2's startup updater in the isolated configuration. This keeps runtime changes inside the verified transaction:

  1. download to a unique partial file;
  2. verify size and SHA-256;
  3. reject unsafe archive entries;
  4. extract into same-volume staging;
  5. verify app identity, every Mach-O, dependencies, signature, hardened runtime, notarization, and CLI;
  6. back up configuration and controller profiles;
  7. freeze and atomically commit the new version;
  8. activate it while retaining the previous version.

A failed transaction leaves the active version unchanged. Rollback switches only the runtime. It does not rewind memory cards or user data. PCSX2 savestates can be version-sensitive, so the UI warns before rollback.

BIOS privacy and validation

The contained picker accepts a .bin BIOS file. The provider also validates a regular file or bounded dump directory supplied through its API. The main ROM must be 4–8 MiB and pass PCSX2-compatible ROMDIR/ROMVER validation. Known companion files are accepted from a selected dump directory.

Imports are copied through private staging. The previous valid BIOS directory is restored if replacement fails. API responses expose only the detected description and region. BIOS contents, paths, and hashes are not uploaded or included in general diagnostics.

Game metadata and artwork

MetalSharp always provides a sanitized filename title. For uncompressed images it may read at most the first 32 MiB to locate a normal PS2 serial such as SLUS-12345. Compressed formats retain filename metadata rather than being decompressed during a scan.

PCSX2-local covers are used only when a size-limited regular image matches the serial or MetalSharp game ID. Baseline scanning performs no artwork scraping and makes no compatibility-site requests.

API

Read endpoints:

GET /emulators
GET /sharp-library/pcsx2/status
GET /sharp-library/pcsx2/games
GET /sharp-library/pcsx2/settings
GET /sharp-library/pcsx2/cover?id=<id>
GET /sharp-library/pcsx2/update/check
GET /sharp-library/pcsx2/update/progress

Mutation endpoints:

POST /sharp-library/pcsx2/initialize
POST /sharp-library/pcsx2/configure
POST /sharp-library/pcsx2/import-bios
POST /sharp-library/pcsx2/scan
POST /sharp-library/pcsx2/add-root
POST /sharp-library/pcsx2/remove-root
POST /sharp-library/pcsx2/launch
POST /sharp-library/pcsx2/stop
POST /sharp-library/pcsx2/open-ui
POST /sharp-library/pcsx2/open-setup
POST /sharp-library/pcsx2/update/refresh
POST /sharp-library/pcsx2/update/install
POST /sharp-library/pcsx2/update/rollback
POST /sharp-library/pcsx2/pin-current
POST /sharp-library/pcsx2/unpin
POST /sharp-library/pcsx2/skip-update
POST /sharp-library/pcsx2/clear-skip
POST /sharp-library/pcsx2/remove-runtime

No endpoint accepts an arbitrary executable, URL, command, shell fragment, PCSX2 configuration key, or file-open target. The configure endpoint accepts only the documented controller and renderer identifiers and maps them to audited upstream INI sections and values.

Trust boundary

Electron exposes only a .bin BIOS picker, a game-file-or-folder picker, contained path reveals, and exact allowlisted browser resources. Find Games opens https://archive.org/; Download Firmware opens https://www.retrostic.com/bios/pcsx2-playstation-2; the official PCSX2 BIOS and disc-dumping guides remain available under runtime support. No renderer-supplied URL is accepted. Every path reveal is resolved again in the main process and must remain inside the PCSX2 environment or a registered game location. Revealing a file selects it in Finder; it does not open or execute the content.

The complete release, host, CLI, isolation, BIOS, discovery, update, and process contract is in PCSX2-UPSTREAM-CONTRACT.md.

Emulators

PCSX2 Upstream Contract

What the PCSX2 integration expects upstream.

10 sections · 7 min read

Status: production contract

Validated on 2026-08-24 against:

  • PCSX2 source revision 3e29183a37e74cbc8c17bda8afb63c2d9bc6fd14;
  • PCSX2 documentation revision 24ba3e41793165062c1ddcb434460033471f3f8c;
  • official stable tag v2.6.3;
  • official asset pcsx2-v2.6.3-macos-Qt.tar.xz.

Release identity

MetalSharp reads https://api.github.com/repos/PCSX2/pcsx2/releases/latest and accepts one non-draft, non-prerelease release whose tag is exactly v<major>.<minor>.<patch>. It requires exactly one asset named:

pcsx2-<tag>-macos-Qt.tar.xz

The selected release must include a positive byte size and a sha256: digest. The inspected v2.6.3 asset is 28,960,388 bytes with SHA-256:

cb7b9e6330f1abf0cf92c94065f7eb983d0fa8affcfe6b0ccb9c2a4ebf067f1a

The archive contains exactly one top-level app, PCSX2-v2.6.3.app, and 221 inspected entries. It contains no links. MetalSharp nevertheless rejects links, hard links, devices, FIFOs, absolute/traversing/control/non-ASCII paths, case-folded duplicate paths, a second top-level entry, more than 20,000 entries, or more than 2 GiB of declared output.

macOS app contract

After extraction, the app must satisfy all of these checks:

  • CFBundleIdentifier: net.pcsx2.pcsx2;
  • CFBundleExecutable: PCSX2;
  • CFBundleShortVersionString: selected release version;
  • executable path: Contents/MacOS/PCSX2;
  • every detected Mach-O: x86_64 and not arm64;
  • deployment target: macOS 11.0 or newer, no newer than the host;
  • non-system dependencies use contained @rpath, @loader_path, or @executable_path references;
  • Developer ID team: PTMR35SWS3;
  • hardened runtime present;
  • codesign --verify --deep --strict succeeds;
  • Gatekeeper accepts the app as a notarized Developer ID application.

The absolute install ID embedded in upstream libshaderc_shared.1.dylib is accepted only as that dylib's own install-name record. It is not accepted as an executable dependency path.

MetalSharp preserves the upstream signature and notarization. It does not patch or ad-hoc sign PCSX2. The installed version directory is made read-only without changing protected app-bundle modes.

Host contract

The stable macOS app is x86_64-only.

  • Intel hosts require x86_64, SSE4.1, and macOS 11 or newer.
  • Apple Silicon hosts require macOS 11 or newer and a successful bounded /usr/bin/arch -x86_64 /usr/bin/true Rosetta probe.
  • Other architectures fail closed.
  • Less than 8 GiB RAM or fewer than four logical CPU threads produces an advisory, not a fabricated compatibility verdict.

The executable and actual Mach-O deployment target override prose when upstream changes.

CLI contract

The selected stable is probed without opening its GUI:

-version
-help
-testconfig

For v2.6.3, informational -version and -help print valid output and exit with status 1. -testconfig exits with status 0. MetalSharp accepts that exact observed informational behavior while still requiring the expected output.

Required stable options are:

-batch -nogui -logfile -testconfig -setupwizard --

v2.6.3 does not support -datapath. Upstream introduced it in commit bd486f172970bba3c3fd1b93ffd36b426129ce5f, first released in v2.7.296. MetalSharp detects it from the installed runtime's -help output and records the result in capabilities.json; it never infers support from current web documentation.

Stable game launches use fixed argv equivalent to:

PCSX2 -nogui -batch -fullscreen -logfile <contained-log> -- <indexed-game>

-fullscreen is omitted when disabled. On Apple Silicon the fixed argv is prefixed with /usr/bin/arch -x86_64. No shell is used.

Data and updater contract

v2.6.3 stores mutable data below:

$HOME/Library/Application Support/PCSX2/

MetalSharp supplies ~/.metalsharp/emulators/pcsx2/home as HOME and precreates Library/Application Support. The runtime probe creates the expected PCSX2 directories, including BIOS, cache, covers, INI, input-profile, memory-card, savestate, texture, video, and log locations.

MetalSharp never uses -portable, because portable mode mixes mutable user data into the signed version store. If and only if the capability manifest records dataPathFlag: true, MetalSharp supplies the isolated PCSX2 data directory through -datapath as an additional boundary.

MetalSharp atomically forces [AutoUpdater] CheckAtStartup = false while PCSX2 is stopped. PCSX2 runtime changes then occur only through MetalSharp's verified, versioned, rollback-capable transaction. Before probing an update against existing state, MetalSharp creates a bounded private backup of inis, gamesettings, and inputprofiles. It does not rewind memory cards, saves, or savestates during rollback.

PCSX2 string lists use repeated INI keys. MetalSharp adds and removes only exact managed entries under:

[GameList]
RecursivePaths = /canonical/user/root

Unknown sections and unrelated values are preserved. Configuration is never changed while a managed PCSX2 process is active.

The inspected v2.6.3 source and runtime define controller type under [Pad1] Type and [Pad2] Type. MetalSharp accepts only the upstream values DualShock2, Guitar, Jogcon, NeGcon, and Popn. The global renderer is [EmuCore/GS] Renderer; the verified macOS runtime contains Automatic (-1), Metal (17), OpenGL (12), Vulkan (14), and Software (13). Null rendering is intentionally not exposed. Baseline setup writes only these exact allowlisted values, [UI] SetupWizardIncomplete = false, and the disabled updater value in one atomic replacement. Advanced bindings and all other emulator settings remain PCSX2-owned.

BIOS contract

Upstream pcsx2/ps2/BiosTools.cpp identifies normal BIOS files between 4 MiB and 8 MiB and validates the ROM directory plus ROMVER. MetalSharp mirrors the bounded ROMDIR/ROMVER checks for import and requires a recognized region marker and valid version/date digits.

A BIOS must be dumped by the user from a PlayStation 2 console they own. MetalSharp:

  • never downloads, bundles, searches for, uploads, or diagnoses BIOS contents remotely;
  • accepts only regular non-symlink files or a bounded dump directory;
  • recognizes optional .rom1, .rom2, .erom, .nvm, and .mec companions;
  • copies through private staging and atomically replaces the isolated BIOS directory;
  • restores the prior valid set if replacement fails;
  • returns description/region only, not BIOS contents or paths.

Official instructions: https://pcsx2.net/docs/setup/bios/.

Game contract

The inspected stable source supports:

.iso .bin .img .mdf .gz .cso .zso .chd

PCSX2 also loads homebrew .elf files. MetalSharp verifies ELF magic and recognizes no other executable type. .cue, .toc, .cdr, GS dumps, block dumps, and savestates are not normal library entries.

Discovery is bounded to 32 registered locations, depth 8, 20,000 entries, and 512 displayed games. An individually selected disc image is indexed exactly and does not opt its parent directory into scanning; a selected directory enables bounded recursive discovery. MetalSharp never follows directory or file symlinks and never hashes complete multi-gigabyte images during scanning. A bounded 32 MiB read may recover a normalized PS2 serial from an uncompressed disc image; otherwise the sanitized filename is authoritative fallback metadata. Compressed images remain filename-based unless a later versioned upstream contract supplies safe metadata.

Official disc-dumping instructions: https://pcsx2.net/docs/setup/discs/.

Process contract

MetalSharp permits one managed PCSX2 process at a time, including the setup wizard and main UI. Every process has:

  • isolated environment and fixed working directory;
  • a dedicated process group;
  • private stdout/stderr and PCSX2 log path;
  • PID, start time, executable, runtime tag, content path, and start timestamp persisted atomically;
  • PID-reuse checks against process start time and executable command;
  • graceful group termination before forced termination;
  • restart-safe stale-session cleanup and exit records.

Launch revalidates the upstream signature and identity every time. Runtime updates, BIOS import, initialization, root mutation, rollback, and removal fail while PCSX2 is active. New launches fail while an update transaction is active.

Licensing and prohibited content

PCSX2 is GPL-3.0-or-later. Every installed runtime preserves:

  • upstream Contents/Resources/docs/GPL.html as LICENSE;
  • upstream ThirdPartyLicenses.html;
  • release tag, source repository, asset URL, size, digest, signing team, and signature-preservation record.

MetalSharp does not bundle PCSX2. It downloads the official app only after user confirmation. It does not acquire Sony BIOS files, games, disc images, licenses, keys, updates, DLC, console modules, fonts, or decryption material.

Contract tests

The active contract is exercised by:

  • app/src-c/tests/smoke.sh;
  • app/src-c/tests/pcsx2_update_test.py;
  • app/src-c/tests/pcsx2_release.json;
  • app/src-c/tests/pcsx2_bad_archive.tar.xz;
  • C transaction and smoke tests under app/src-c/tests/.

The transaction tests cover successful isolated initialization, updater disablement, read-only activation, repair, rollback/roll-forward, removal preservation, interrupted activation, size/digest failure, traversal, links, multiple top-level entries, wrong architecture, CLI drift, draft/prerelease releases, and duplicate matching assets. Synthetic fixtures contain no Sony code or game content.

Emulators

RPCS3 Integration

How MetalSharp connects to RPCS3.

7 sections · 4 min read

MetalSharp exposes RPCS3 as a supported, managed Sharp Library environment for PlayStation 3 games.

RPCS3 ownership and paths

MetalSharp keeps the emulator runtime separate from emulator state:

~/.metalsharp/emulators/rpcs3/
├── current -> versions/<release-tag>
├── previous -> versions/<release-tag>
├── versions/<release-tag>/RPCS3.app
├── home/Library/Application Support/rpcs3/
├── home/Library/Caches/rpcs3/
├── downloads/
├── staging/
├── sessions/
├── logs/
├── environment.json
└── library.json

RPCS3 is launched with the environment's home directory as HOME. Firmware, saves, trophies, configuration, shader caches, and installed PS3 content therefore remain isolated from a separately installed copy of RPCS3.

Removing the managed runtime only removes versions, current, previous, downloads, and update staging. It preserves the isolated home and every user-selected external game folder.

Official releases and updates

MetalSharp selects an official release repository based on the host architecture. Release metadata is cached for 12 hours; the tab's header-level Check RPCS3 action bypasses that cache. Users can pin the installed build, skip the current latest build, or clear either preference without modifying emulator state.

An update is installed as follows:

  1. Fetch official GitHub release metadata.
  2. Require an architecture-matching macOS .7z asset, byte size, and SHA-256 digest.
  3. Download into the managed downloads directory.
  4. Verify the exact byte count and SHA-256 digest.
  5. Extract with unar into an isolated staging directory.
  6. Reject escaping or broken symlinks and unexpected archives without RPCS3.app/Contents/MacOS/rpcs3.
  7. Verify the application with codesign --verify --deep --strict.
  8. Move the app into a versioned directory on the same volume.
  9. Wait for an active RPCS3 session to exit, if necessary.
  10. Atomically switch current, preserving the old target as previous for rollback.

A failed download, digest, extraction, signature, move, or activation leaves the existing runtime selected. RPCS3 user state is never part of the update transaction.

unar is required. MetalSharp does not fall back to shell-evaluated archive commands.

Firmware and owned content

MetalSharp does not download or bundle Sony firmware, games, keys, or licenses. Download Firmware opens Sony's exact PlayStation support URL in the user's browser, and Find Games opens https://archive.org/. Both links are fixed in the Electron main process; no renderer-supplied URL is accepted.

Users can select a legally acquired PS3UPDAT.PUP; MetalSharp invokes the managed emulator with --headless --installfw. User-selected PS3 packages are installed with --headless --installpkg. Games launch through the managed RPCS3 app with --no-gui, and fullscreen is enabled by default.

External game folders are references. Removing a folder from the tab only removes it from library.json; it never deletes the folder.

Game discovery

The provider scans:

  • the isolated RPCS3 dev_hdd0/game directory;
  • user-selected external roots.

It reads bounded PARAM.SFO metadata for title, title ID, version, and category. ICON0.PNG is served as local card artwork. Symlinked directories are not traversed during discovery.

Process supervision

Each launch receives its own process group and log. A session record stores its PID, provider executable, log path, and start time. Status and stop operations validate that the PID still belongs to RPCS3 before reporting or signaling it. Session records permit recovery after a MetalSharp backend restart.

Backend API

Read endpoints:

GET /emulators
GET /sharp-library/rpcs3/status
GET /sharp-library/rpcs3/games
GET /sharp-library/rpcs3/cover?id=<id>
GET /sharp-library/rpcs3/update/check
GET /sharp-library/rpcs3/update/progress

RPCS3 mutation endpoints:

POST /sharp-library/rpcs3/scan
POST /sharp-library/rpcs3/add-root
POST /sharp-library/rpcs3/remove-root
POST /sharp-library/rpcs3/launch
POST /sharp-library/rpcs3/stop
POST /sharp-library/rpcs3/open-ui
POST /sharp-library/rpcs3/install-firmware
POST /sharp-library/rpcs3/install-package
POST /sharp-library/rpcs3/remove-runtime
POST /sharp-library/rpcs3/update/refresh
POST /sharp-library/rpcs3/update/install
POST /sharp-library/rpcs3/update/rollback
POST /sharp-library/rpcs3/pin-current
POST /sharp-library/rpcs3/unpin
POST /sharp-library/rpcs3/skip-update
POST /sharp-library/rpcs3/clear-skip

Other emulator plans

Emulators

shadPS4 Integration

How MetalSharp connects to shadPS4.

8 sections · 6 min read

MetalSharp exposes shadPS4 as an experimental managed Sharp Library provider for PlayStation 4 games. shadPS4 remains early software; a game appearing in the library does not imply that it is playable.

MetalSharp is not affiliated with Sony Interactive Entertainment or the shadPS4 project.

Host readiness

The current official macOS stable core is an x86_64 executable designed for Rosetta translation on Apple Silicon. MetalSharp fails closed unless all of these conditions are true:

  • the host is Apple Silicon;
  • Rosetta 2 can execute x86_64 programs;
  • the host macOS version is at least the downloaded executable's LC_BUILD_VERSION deployment target;
  • the downloaded runtime passes its bounded CLI capability probe.

Intel Macs are not supported by the current upstream runtime. MetalSharp derives the final OS requirement from the verified executable because upstream prose can lag behind release artifacts.

Runtime and state ownership

~/.metalsharp/emulators/shadps4/
├── current -> versions/<release-tag>
├── previous -> versions/<release-tag>
├── versions/<release-tag>/
│   ├── shadps4
│   ├── libvulkan.dylib
│   ├── libvulkan_kosmickrisp.dylib
│   ├── kosmickrisp_mesa_icd.json
│   ├── LICENSE
│   ├── source.json
│   └── capabilities.json
├── home/Library/Application Support/shadPS4/
├── downloads/
├── staging/
├── sessions/
├── logs/
├── environment.json
└── library.json

The selected version directory is the process working directory so the upstream KosmicKrisp ICD resolves its local Vulkan driver. HOME points to the environment's isolated home directory. shadPS4 settings, saves, trophies, controller profiles, screenshots, patches, cheats, modules, fonts, shader caches, and other state therefore remain separate from a standalone shadPS4 installation.

Runtime removal deletes version pointers, version directories, downloads, and staging. It preserves the isolated home, logs, sessions, library manifest, and all external game folders.

Verified stable updates

MetalSharp uses official stable releases from https://github.com/shadps4-emu/shadPS4/releases. It does not use QtLauncher or nightly builds for the production channel.

The update transaction:

  1. Loads official release metadata, cached for 12 hours unless manually refreshed.
  2. Requires one macOS SDL ZIP with a positive byte size and GitHub-provided SHA-256.
  3. Downloads to a unique .part file over HTTPS.
  4. Verifies exact size and SHA-256 before extraction.
  5. Rejects absolute paths, traversal, duplicate entries, control characters, escaping links, and unsupported extracted file types.
  6. Requires the core, Vulkan loader, KosmicKrisp driver, and an ICD manifest that resolves only the local driver.
  7. Requires x86_64 Mach-O files and a deployment target supported by the host.
  8. Records the original verified asset digest and exact upstream source tag in source.json.
  9. Ad-hoc signs the verified Mach-O files locally because current upstream macOS assets are unsigned, then verifies each local signature.
  10. Saves the exact upstream GPL license beside the runtime.
  11. Executes shadps4 --help through Rosetta with an isolated HOME, bounded output, and a timeout, requiring the launch and configuration flags MetalSharp uses.
  12. Waits for active sessions, then atomically activates the new version while retaining the previous runtime for rollback.

Any failure removes staging and partial downloads and leaves the prior current pointer unchanged. Pin, skip, clear-skip, rollback, and runtime-removal controls never mutate emulator state.

Games and owned content

MetalSharp accepts external directories containing already dumped games owned by the user. A base game is indexed only when a bounded scan finds both:

CUSAxxxxx/eboot.bin
CUSAxxxxx/sce_sys/param.sfo

The bounded SFO parser reads title, CUSA ID, version, and category. sce_sys/icon0.png is served as local card artwork with backend size limits. Patch/update directories are not emitted as duplicate game cards. Directory symlinks are not traversed, and scans have depth, entry, metadata-size, and game-count limits.

Removing a root only removes its canonical path from library.json. MetalSharp never deletes external games.

MetalSharp does not extract PS4 packages. Games, updates, and DLC must be dumped and prepared by the user in layouts supported by upstream shadPS4.

Optional modules and fonts

Some games benefit from decrypted firmware modules and fonts dumped from a legally owned console. They are optional compatibility files, not a firmware-installation requirement.

  • Module import accepts only upstream-supported .sprx names with ELF magic, rejects links and oversized files, and atomically copies accepted files into isolated sys_modules.
  • Font import rejects links, devices, excessive depth, excessive file counts, excessive total size, and oversized individual files. It stages a complete replacement and restores the previous font tree if activation fails.
  • Source files are copied, never moved.

MetalSharp never downloads or extracts Sony update PUPs, modules, fonts, trophy keys, games, updates, DLC, licenses, keys, or decryption material. Trophy-key onboarding remains intentionally unavailable.

Launch supervision

MetalSharp launches the official core directly as an argv array without a shell. Every launch receives:

  • the selected runtime directory as its working directory;
  • isolated HOME and an absolute local Vulkan ICD path;
  • a dedicated process group;
  • a per-launch log;
  • an atomic session record with PID, executable identity, game path, runtime tag, log path, and start time.

Status, stop, and recovery validate that the PID still belongs to the recorded executable. Stop requests signal the process group, wait for graceful termination, and escalate only after a bounded interval. Exit status is retained separately from active session state, and the latest launch log remains available from the game card.

No Wine, GPTK, D3DMetal, Steam-bottle, or RPCS3 variables are injected into shadPS4.

Backend API

Read endpoints:

GET /emulators
GET /sharp-library/shadps4/status
GET /sharp-library/shadps4/games
GET /sharp-library/shadps4/cover?id=<id>
GET /sharp-library/shadps4/update/check
GET /sharp-library/shadps4/update/progress

Mutations:

POST /sharp-library/shadps4/scan
POST /sharp-library/shadps4/add-root
POST /sharp-library/shadps4/remove-root
POST /sharp-library/shadps4/import-modules
POST /sharp-library/shadps4/import-fonts
POST /sharp-library/shadps4/launch
POST /sharp-library/shadps4/stop
POST /sharp-library/shadps4/update/refresh
POST /sharp-library/shadps4/update/install
POST /sharp-library/shadps4/update/rollback
POST /sharp-library/shadps4/pin-current
POST /sharp-library/shadps4/unpin
POST /sharp-library/shadps4/skip-update
POST /sharp-library/shadps4/clear-skip
POST /sharp-library/shadps4/remove-runtime

Electron path-opening IPC independently permits only the isolated shadPS4 environment and canonical roots registered in library.json.

Verification

The C smoke and update-transaction suites cover provider registration, host rejection, SFO discovery, artwork, root preservation, module/font import, process launch/stop, active-session update handoff, rollback, runtime state preservation, wrong size/digest, traversal, duplicate ZIP entries, symlinks, missing runtime files, wrong Mach-O architecture, invalid ICD manifests, failed local signing, failed CLI probes, failed activation, and .part cleanup.

The packaged application must additionally pass C normal/ASAN tests, frontend checks, code-sign verification, installed-backend route checks, and manual normal/narrow UI inspection.

Emulators

shadPS4 Upstream Contract

What the shadPS4 integration expects upstream.

4 sections · 3 min read

Probe date: 2026-08-23

This document freezes the upstream facts used by MetalSharp's first production shadPS4 provider. Re-run the probe before changing release channels, asset matching, runtime layout, or CLI arguments.

Probed release

  • Repository: shadps4-emu/shadPS4
  • Stable tag: v.0.18.0
  • Asset: shadps4-macos-sdl-0.18.0.zip
  • Size: 38,342,169 bytes
  • GitHub API digest: sha256:3543e255e2c9bad792ff77000f251493c9af3b32fef7ce5dab3a40906b403fed
  • Locally calculated SHA-256: 3543e255e2c9bad792ff77000f251493c9af3b32fef7ce5dab3a40906b403fed

Required archive members:

shadps4
libvulkan.dylib
libvulkan_kosmickrisp.dylib
kosmickrisp_mesa_icd.json

All three Mach-O files are x86_64. The core declares LC_BUILD_VERSION minos 26.0 and SDK 26.5. The official asset is unsigned. Its ICD manifest resolves ./libvulkan_kosmickrisp.dylib relative to the working directory.

The core executes successfully through:

/usr/bin/arch -x86_64 ./shadps4 --help

Required CLI capabilities observed:

-g, --game
-f, --fullscreen
--config-global
--add-game-folder
--set-addon-folder
--override-root

--override-root is passed into the guest emulator filesystem and is not the host-side shadPS4 user-data root.

Data isolation probe

Upstream path initialization uses $HOME/Library/Application Support/shadPS4 on macOS unless a portable user directory exists in the process working directory.

MetalSharp therefore:

  1. uses the selected runtime directory as the working directory for the relative ICD;
  2. ensures that runtime versions do not contain a portable user directory;
  3. sets HOME to ~/.metalsharp/emulators/shadps4/home;
  4. sets the Vulkan ICD path explicitly;
  5. validates writes under the isolated home during the update-time CLI probe and launch tests.

The upstream user tree includes logs, screenshots, shader and pipeline cache, game data, temporary data, sys_modules, downloads, captures, cheats, patches, metadata, custom trophies, per-game configs, general cache, fonts, trophies, emulated home content, and custom modules. All are treated as persistent state.

Reproduction commands

curl -fsSL \
  https://api.github.com/repos/shadps4-emu/shadPS4/releases/latest \
  -o /tmp/shadps4-release.json

curl -fL \
  https://github.com/shadps4-emu/shadPS4/releases/download/v.0.18.0/shadps4-macos-sdl-0.18.0.zip \
  -o /tmp/shadps4-macos-sdl-0.18.0.zip

stat -f %z /tmp/shadps4-macos-sdl-0.18.0.zip
shasum -a 256 /tmp/shadps4-macos-sdl-0.18.0.zip
unzip -Z1 /tmp/shadps4-macos-sdl-0.18.0.zip
file shadps4 libvulkan.dylib libvulkan_kosmickrisp.dylib
otool -l shadps4
codesign -dv --verbose=4 shadps4
/usr/bin/arch -x86_64 ./shadps4 --help

Checked-in enforcement

  • app/src-c/tests/shadps4_release.json freezes trusted metadata parsing.
  • app/src-c/tests/shadps4_bad_archive.zip exercises corrupt-download cleanup.
  • app/src-c/tests/shadps4_update_test.py generates signed architecture-specific runtime fixtures and validates the full update transaction and failure matrix.
  • app/src-c/tests/smoke.sh validates provider routes, owned-content discovery/import, launch supervision, removal preservation, and corrupt-update failure.

A release is rejected if the API no longer provides a positive size and SHA-256, the macOS asset naming or layout changes, the required CLI flags disappear, the ICD escapes the runtime, the architecture is no longer supported by MetalSharp, or the deployment target exceeds the host.

Emulators

SharpEmu Integration

How MetalSharp connects to SharpEmu.

11 sections · 7 min read

MetalSharp exposes SharpEmu as an experimental PlayStation 5 research environment in the Sharp Library. Most games do not run. Windows remains SharpEmu's primary development target, and macOS support is experimental.

MetalSharp and SharpEmu are unaffiliated with Sony. MetalSharp does not download, bundle, import, decrypt, patch, upload, or provide acquisition instructions for Sony firmware, games, keys, licenses, modules, fonts, updates, DLC, or decryption material.

Managed layout

~/.metalsharp/emulators/sharpemu/
├── current -> versions/<release-tag>
├── previous -> versions/<release-tag>
├── versions/<release-tag>/
│   ├── SharpEmu
│   ├── libMoltenVK.dylib
│   ├── libvulkan.1.dylib
│   ├── plugins/
│   ├── licenses/
│   ├── LICENSE.txt
│   ├── source-manifest.json
│   ├── activation-manifest.json
│   └── capabilities.json
├── home/
├── state/
│   ├── saves/
│   ├── custom-configs/
│   └── roots.json
├── cache/
│   ├── dotnet-bundle/
│   ├── ampr-index/
│   └── vulkan/
├── writable/
├── downloads/
├── staging/
├── sessions/
├── logs/
├── environment.json
├── update-policy.json
└── library-cache.json

Runtime versions are separate from mutable state. Activated version trees are read-only. Updates, rollback, repair, and runtime removal preserve saves, settings, roots, caches, logs, sessions, and external games.

Host requirements

The current official macOS release is x86-64.

  • Apple Silicon requires Rosetta 2.
  • Intel Macs run x86-64 directly.
  • The effective full-payload minimum is macOS 26 because bundled FFmpeg dylibs declare minos 26.0.
  • lsar and unar are required.
  • Installation requires at least 1 GiB of available transaction space.
  • Vulkan uses the bundled MoltenVK; the experimental upstream native Metal backend is disabled.

Status reports architecture, macOS, Rosetta, archive tools, free disk, network containment, runtime integrity, and MoltenVK readiness separately.

Secure stable installation

MetalSharp downloads only the exact official stable macOS x64 asset from sharpemu/sharpemu after user confirmation.

  1. Fetch bounded GitHub release JSON over HTTPS.
  2. Require a non-draft, non-prerelease stable v... tag.
  3. Require exactly sharpemu-<version>-osx-x64.tar.gz.
  4. Bind release ID, asset ID, tag, URL, name, size, digest, and timestamps.
  5. Quarantine changed metadata for an already observed tag/asset.
  6. Download to a new .part file with HTTPS-only redirects.
  7. Verify exact bytes and SHA-256.
  8. Preflight with lsar; reject traversal, links, devices, sparse entries, duplicates, case collisions, and bounds violations.
  9. Extract with unar into same-volume staging.
  10. Require the executable, Vulkan loaders, plugins, and license payload.
  11. Inspect every Mach-O architecture, dependency, and deployment target.
  12. Record pre-sign hashes in source-manifest.json.
  13. Ad-hoc sign each native dependency and the main executable locally.
  14. Verify signatures, MoltenVK loading, and the no-window nonexistent-eboot CLI probe.
  15. Record post-sign hashes in activation-manifest.json.
  16. Make the version tree read-only.
  17. Atomically switch current, retaining previous for rollback.

The upstream macOS archive is not Developer ID signed or notarized. MetalSharp's local ad-hoc signature is not represented as upstream signing or Apple notarization.

Update policy and rollback

Users can:

  • check or refresh stable release metadata;
  • install/update;
  • pin the current version;
  • unpin;
  • skip the latest version;
  • clear a skipped version;
  • roll back to previous;
  • remove managed runtime versions.

Download may occur while a game runs, but activation waits for all SharpEmu sessions to exit. Rollback and runtime removal are rejected while SharpEmu runs. Runtime removal is also rejected during an update transaction.

Game discovery

Roots are selected with a native directory picker. MetalSharp rejects:

  • symlinked and missing roots;
  • /, system, library, applications, home, and MetalSharp-managed roots;
  • duplicate or overlapping ancestor/descendant roots;
  • more than 32 roots.

Scanning:

  • searches for exact regular eboot.bin files;
  • never follows symlinked directories;
  • stops after depth 8, 20,000 entries, or 512 games;
  • validates bounded ELF/fSELF leading structure;
  • reads at most 1 MiB of sce_sys/param.json or adjacent param.json;
  • reads title, PPSA title ID, content/master version, and localized title;
  • validates local PNG artwork and never fetches PlayStation Store images;
  • persists a launch index containing canonical path and executable size.

Launch reopens the indexed executable with no-follow semantics and compares current size to the saved scan identity. Replaced files fail with a “changed” error and require a rescan.

External roots are references. Removing a root changes only state/roots.json and library-cache.json; it never deletes external content.

CLI-only launch

MetalSharp never launches SharpEmu's no-argument GUI or updater. It launches the exact active SharpEmu executable with reviewed arguments:

--cpu-engine=native
--log-level=info
--log-file <isolated-log>
--window-mode=windowed
--scaling=fit
--vsync=on
<canonical-eboot.bin>

Fullscreen currently maps to SharpEmu's exclusive window mode but is not enabled by default in the UI.

The child environment redirects:

  • HOME;
  • .NET single-file extraction;
  • saves;
  • AMPR indexes;
  • Vulkan pipeline cache;
  • guest temporary/download/devlog/hostapp mounts;
  • TMPDIR;
  • SharpEmu logs.

MetalSharp removes inherited SharpEmu diagnostics, debugger/profiler, dynamic-loader, proxy, RenderDoc, native-Metal, writable-app0, and network-redirection variables before launch.

Guest networking

SharpEmu guest networking can create real host sockets.

Default behavior:

  • MetalSharp runs SharpEmu through sandbox-exec with all network operations denied.
  • Host readiness verifies that the sandbox starts and cannot connect to a MetalSharp-owned loopback listener.
  • If containment is unavailable, a default launch fails closed.

Explicit opt-in:

  • The Sharp Library has an “Allow unrestricted guest networking” checkbox.
  • Enabling it shows a persistent danger state.
  • Every network-enabled launch requires a second confirmation.
  • The launch runs without the network-denial profile.
  • The session record stores networkEnabled: true.

No mode uploads MetalSharp telemetry, diagnostics, game metadata, or compatibility reports automatically.

Process supervision

Every launch receives:

  • a stable session ID and game ID;
  • PID and process group;
  • exact executable and runtime tag;
  • canonical game path;
  • MetalSharp log path;
  • start timestamp;
  • network-policy value.

MetalSharp waits until the child has executed the exact managed executable before reporting success. Backend restart recovery validates command path and process start time before accepting a PID.

Stop behavior:

  1. SIGINT to the validated process group;
  2. bounded graceful wait;
  3. SIGTERM;
  4. final SIGKILL fallback.

Exit code/signal and the latest log remain visible on the game card. Logs remain local and may contain title IDs, game paths, module names, and crash details.

Backend API

Read endpoints:

GET /emulators
GET /sharp-library/sharpemu/status
GET /sharp-library/sharpemu/games
GET /sharp-library/sharpemu/cover?id=<stable-id>
GET /sharp-library/sharpemu/sessions
GET /sharp-library/sharpemu/update/check
GET /sharp-library/sharpemu/update/progress

Mutation endpoints:

POST /sharp-library/sharpemu/scan
POST /sharp-library/sharpemu/add-root
POST /sharp-library/sharpemu/remove-root
POST /sharp-library/sharpemu/launch
POST /sharp-library/sharpemu/stop
POST /sharp-library/sharpemu/update/refresh
POST /sharp-library/sharpemu/update/install
POST /sharp-library/sharpemu/update/rollback
POST /sharp-library/sharpemu/pin-current
POST /sharp-library/sharpemu/unpin
POST /sharp-library/sharpemu/skip-update
POST /sharp-library/sharpemu/clear-skip
POST /sharp-library/sharpemu/remove-runtime

There are no firmware, key, module, package, decryption, debugger, upstream-GUI, or compatibility-submission endpoints. Request objects reject unknown fields and wrong primitive types.

Electron boundary

The preload exposes only:

  • bounded backend requests;
  • a SharpEmu game-root picker;
  • path reveal restricted to the SharpEmu environment and registered roots;
  • exact official FAQ and compatibility URLs.

The renderer cannot open arbitrary SharpEmu paths or URLs. A per-game URL is created only from a validated PPSA plus five digits.

Testing and evidence

app/src-c/tests/sharpemu_update_test.py covers:

  • provider/status/update contracts;
  • synthetic real Mach-O transaction installation;
  • archive digest and path safety;
  • local signing and read-only activation;
  • source and activation manifests;
  • discovery, metadata, artwork, and PPSA parsing;
  • symlinked-root rejection;
  • request schema rejection;
  • denied-network and explicit-network session records;
  • process supervision, sessions, stop, and runtime-removal blocking;
  • changed launch target rejection;
  • replaced artwork symlink rejection;
  • mutable upstream asset quarantine;
  • state and external-game preservation;
  • malicious symlink archive rejection.

The test-only probe bypass works only when:

  • the backend path is under src-c/build/ or src-c/build-asan/;
  • both release and download fixtures are present;
  • the explicit test variable is set.

Packaged binaries reject that bypass.

See SHARPEMU-UPSTREAM-CONTRACT.md for frozen upstream evidence and the production contract.

Emulators

SharpEmu Upstream Contract

What the SharpEmu integration expects upstream.

11 sections · 7 min read

This document freezes the source-backed assumptions used by MetalSharp's managed SharpEmu provider. The packaged runtime backend is C.

Frozen research baseline

Contract Frozen value
Repository https://github.com/sharpemu/sharpemu
Researched main 600fcde63763a8109eb50e5052b3ebbeb4372dae
Stable release v0.0.3-release.3
Release source commit d9b599a1fdf105187156b9baad1b3737c093a46a
macOS asset sharpemu-0.0.3-release.3-osx-x64.tar.gz
Asset ID 518522588
Asset bytes 71,999,495
Asset SHA-256 cf54f8f50c4984c0b0a6f6723ed6fbb94eb15f5f318112d3bc371156d05b681a
Archive entries 31 regular files/directories
Extracted bytes 111,974,175
Main executable x86-64 bare Mach-O, ad-hoc signed
Effective minimum macOS 26.0 due to the bundled FFmpeg dylibs
Upstream notarization none; Gatekeeper rejects the archive executable
Runtime self-contained .NET 10 (net10.0, SDK 10.0.103)
License GPL-2.0-or-later

Representative extracted hashes:

  • SharpEmu: 54fec65c3e5ff9d87abc78c285a0cd33c3eb250906232a48a61c818a768c3466
  • libMoltenVK.dylib: a1bbbbbf683f61094adbfe1b5da7d1da63de032702337d47b093248869b63222
  • libvulkan.1.dylib: a1bbbbbf683f61094adbfe1b5da7d1da63de032702337d47b093248869b63222
  • plugins/libavcodec.61.19.101.dylib: 58b1d73526eb0a708387e8917d279f6383e3f7dd3de02808df6761bec6aa0dc2

GitHub reports the release asset as mutable. MetalSharp therefore binds the release ID, asset ID, tag, exact name, URL, size, digest, and timestamps. Changed metadata for an already observed tag/asset is quarantined rather than treated as an update.

Release-channel contract

The initial provider accepts only:

  • repository sharpemu/sharpemu;
  • the latest non-draft, non-prerelease GitHub release;
  • a safe tag beginning v and containing a dotted version;
  • no alpha, beta, or rc tag;
  • exactly one asset named sharpemu-<tag-without-v>-osx-x64.tar.gz;
  • an exact https://github.com/sharpemu/sharpemu/releases/download/<tag>/<asset> URL;
  • an asset with a positive ID, bounded byte size, and 64-digit SHA-256 digest.

Rolling osx-x64-main-*, branch, commit, nightly, and ambiguous assets are unsupported. The website Downloads page is informational only because it was behind GitHub during research.

Archive contract

The researched archive has a flat root containing:

SharpEmu
libMoltenVK.dylib
libvulkan.1.dylib
plugins/
licenses/
LICENSE.txt

plugins/ contains managed bridge assemblies and x86-64 native FFmpeg libraries. MetalSharp does not hard-code a permanent plugin count, but every release must satisfy:

  • no absolute/traversing/control-character paths;
  • no links, devices, sockets, FIFOs, sparse entries, duplicate paths, or case-fold collisions;
  • at most 512 entries;
  • at most 512 MiB extracted and 256 MiB per file;
  • required executable, Vulkan loaders, plugin directory, upstream license, and license directory;
  • only regular files and directories after extraction.

lsar performs bounded preflight and unar extracts into isolated same-volume staging. There is no shell or permissive archive fallback.

macOS runtime contract

The official macOS release is x86-64. Apple Silicon runs it through Rosetta 2. MetalSharp supports x86-64 Macs directly and Apple Silicon only after a bounded Rosetta probe.

Every Mach-O is inspected recursively. Required properties are:

  • an x86-64 slice;
  • dependencies limited to Apple system paths or local @rpath, @loader_path, and @executable_path references;
  • a deployment target no newer than the host;
  • a valid local ad-hoc signature after MetalSharp activation.

MetalSharp records hashes before signing in source-manifest.json, signs native leaves and the main executable, records post-sign hashes in activation-manifest.json, makes the version tree read-only, and verifies hashes and signatures when activating a runtime.

The upstream archive is not notarized. The provider and UI must never represent MetalSharp's local ad-hoc signatures as upstream Developer ID signing or Apple notarization.

CLI contract

No arguments launches the upstream Avalonia GUI. MetalSharp never uses that mode because the GUI writes beside the executable and exposes the upstream updater.

A game launch passes an eboot.bin plus only reviewed options:

--cpu-engine=native
--log-level=info
--log-file <isolated-log>
--window-mode=windowed|exclusive
--scaling=fit
--vsync=on
<canonical-eboot.bin>

Supported but unavailable in MetalSharp's first provider:

  • debug server;
  • import tracing and strict-import diagnostics;
  • manual display/resolution/refresh/HDR overrides;
  • RenderDoc and dump toggles;
  • native Metal backend;
  • compatibility environment hacks.

The installation probe invokes a nonexistent eboot path. The inspected CLI returns exit code 2 after reporting that the file does not exist, without creating a game window. This verifies x86-64 execution, Rosetta, .NET extraction, local dependency loading, CLI parsing, and isolated write paths.

Executable-content contract

SharpEmu accepts:

  • a decrypted ELF beginning 7f 45 4c 46; or
  • a recognized fake-signed SELF beginning PS4 4f 15 3d 1d or PS5 54 14 f5 ee with the known layout identifier bytes.

SharpEmu has no retail decryption keys. MetalSharp validates only bounded leading structure and never decrypts, fake-signs, repairs, patches, or converts content.

Discovery finds a regular file named exactly eboot.bin. Metadata is optional and comes from sce_sys/param.json or adjacent param.json:

  • titleId;
  • contentVersion;
  • masterVersion;
  • localizedParameters.defaultLanguage and localized titleName.

Valid PS5 compatibility IDs match PPSA plus five digits. Local artwork uses sce_sys/icon0.png, then pic0.png, then pic1.png, after PNG signature, size, and dimension checks.

Writable-state contract

Upstream GUI state defaults below AppContext.BaseDirectory/user. MetalSharp avoids the GUI and redirects reviewed mutable families:

Upstream contract MetalSharp location
HOME home/
.NET single-file extraction cache/dotnet-bundle/<runtime-tag>/
SHARPEMU_SAVEDATA_DIR state/saves/
SHARPEMU_AMPR_INDEX_CACHE cache/ampr-index/
SHARPEMU_VK_PIPELINE_CACHE_PATH cache/vulkan/<game-id>/pipeline.bin
SHARPEMU_TEMP0_DIR writable/temp0/<session-id>/
SHARPEMU_DOWNLOAD0_DIR writable/download0/<session-id>/
SHARPEMU_DEVLOG_APP_DIR writable/devlog/<session-id>/
SHARPEMU_HOSTAPP_DIR writable/hostapp/<session-id>/
TMPDIR writable/tmp/<session-id>/
log file logs/<game-id>-<timestamp>.log

Inherited SharpEmu, debugger, profiler, dynamic-loader, proxy, RenderDoc, and diagnostic environment variables are removed before reviewed values are set. SHARPEMU_GPU_BACKEND=metal, SHARPEMU_WRITABLE_APP0, debug-server, network-redirection, and dump toggles are never set.

Networking contract

The inspected sceNet implementation maps guest networking to real System.Net.Sockets.Socket operations, including DNS, connect, bind, listen, accept, send, and receive. Upstream has no complete network-off option.

MetalSharp policy:

  • guest networking is denied by default with a sandbox-exec profile containing (deny network*);
  • the provider verifies sandbox execution and denied loopback socket creation;
  • users may explicitly enable unrestricted networking in the Sharp Library and must confirm each network-enabled launch;
  • session records retain whether networking was enabled;
  • no network mode uploads MetalSharp diagnostics or compatibility data automatically.

This explicit opt-in is a product policy chosen for the integration; it is not an upstream containment feature.

Process and updater contract

MetalSharp owns updates and process supervision.

  • The upstream updater is never invoked.
  • The no-argument GUI is never launched.
  • Each game receives a process group, session record, isolated logs, runtime tag, executable identity, start time, and network-policy value.
  • Launch waits until the child has executed the exact managed SharpEmu path.
  • Recovery validates executable path and process start time before reporting or signaling a PID.
  • Stop sends SIGINT, then SIGTERM, then SIGKILL after bounded waits.
  • Runtime activation, rollback, and removal are blocked while a session is live.
  • Failed updates leave current unchanged; previous preserves one known-good rollback target.

Legal and privacy contract

SharpEmu is GPL-2.0-or-later and runs out of process. The official archive's LICENSE.txt and licenses/ directory remain beside each runtime. MetalSharp records the exact source tag, release URL, and corresponding source repository.

MetalSharp does not download, bundle, import, decrypt, patch, upload, or provide acquisition instructions for Sony firmware, games, keys, licenses, modules, fonts, updates, DLC, or decryption material. External games are path references and are never deleted by root or runtime removal.

Compatibility pages and the FAQ open only on explicit user action. Diagnostic logs remain local and can contain title IDs, game paths, module names, and crash details.

Maintenance procedure

Before allowlisting a new stable release:

  1. Freeze the tag and source commit.
  2. Record GitHub release/asset IDs, URL, size, digest, timestamps, and mutability.
  3. Audit every archive path and extracted byte bound.
  4. Record every Mach-O architecture, deployment target, install name, and dependency.
  5. Confirm CLI parsing and nonexistent-path exit behavior.
  6. Re-audit writable environment variables and all newly added diagnostic/network toggles.
  7. Re-audit guest networking and sandbox behavior.
  8. Run the malicious archive, transaction, mutation, discovery, path-race, supervision, state-preservation, and packaged-app suites.
  9. Update this contract, THIRD_PARTY_LICENSES, and the capability manifest.
  10. Keep the release unavailable if any contract changed without an implemented fail-closed adaptation.
Release

Release Signing

Signing, packaging, and release verification.

Reference note · 2 min read

Updated: 2026-07-08

MetalSharp DMG releases must be signed with a Developer ID Application certificate and notarized before upload. Without that, macOS Gatekeeper can show the "Apple could not verify this app is free of malware" prompt and force users through Security & Privacy.

Configure these GitHub Actions secrets to produce a signed and notarized DMG:

  • MACOS_CERTIFICATE_P12: base64-encoded Developer ID Application .p12
  • MACOS_CERTIFICATE_PASSWORD: password for the .p12

Configure one notarization credential set:

  • Apple ID credentials: APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD, APPLE_TEAM_ID
  • App Store Connect API key credentials: APPLE_API_KEY_P8_BASE64, APPLE_API_KEY_ID, APPLE_API_ISSUER

The release job imports the certificate into a temporary keychain, runs Electron Builder with Developer ID signing, notarizes through app/build/notarize.cjs, then validates the stapled app and DMG with tools/dmg/verify-notarization.sh. The notarization verifier requires a Developer ID Application identity on the app, a stapled ticket, accepted Gatekeeper assessment, and a structurally valid DMG image before upload.

If the Apple secrets are not configured yet, Release CI falls back to an unsigned/ad-hoc DMG instead of skipping the release entirely. The fallback disables Electron Builder certificate discovery, packages the DMG, verifies the embedded runtime assets, and uploads a DMG-SIGNING.txt marker beside the DMG. This keeps release artifacts available during credential setup, but the unsigned DMG can still trigger Gatekeeper warnings until the Developer ID and notarization secrets are configured.