Phantom Blade Zero
Never opens a window under Rosetta: its Denuvo checks the CPU for instructions Rosetta does not have. The Wine modules' ntdll shows them to this executable alone, it loads Microsoft's Visual C++ runtime first, and an H.264 decoder that offers NV12 brings back the black menu video.
What you see
Under Rosetta one core runs at 100% and no window ever opens. Once it starts, the menu video is black.
Why it happens
Its Denuvo checks the CPU for CLFLUSHOPT, CLWB and RDPID, which Rosetta does not have, and expects x86 answers for debug registers and non-canonical addresses. The game also quits on Wine's builtin Visual C++ runtime. Its menu video goes through Unreal's Electra, which takes NV12 only, and CrossOver's H.264 decoder offers no NV12 on macOS while saying it is D3D aware.
What the fix changes
The Wine modules' ntdll.so (engine patches 0017 and 0018) shows those bits to a process that asks and emulates the instructions; this fix asks for PBZ.exe in the prefix's registry, and loads its Visual C++ runtime native first. A decoder DLL of ours, loaded for PBZ.exe from the prefix's system32, serves Microsoft's H.264 decoder class over CrossOver's and offers NV12 without D3D, as winevideo's patch 0006 does inside Wine. With DLSS available, D3DMetal's NVAPI offers NVIDIA opacity micromaps it cannot build, and the game closes as it loads a level, whatever upscaler is chosen. TSR is recommended, as it ran better than DLSS in play (not measured): turn DLSS off for the game (D3DM_ENABLE_METALFX=0 in NotProton's launch options, or CrossOver's DLSS switch for the bottle) and choose TSR in the game. For DLSS, set D3DM_SUPPORT_DXR=0 (NotProton: the game's launch options; a CrossOver bottle: [EnvironmentVariables] in its cxbottle.conf).
The code
Ten values, for PBZ.exe only: the CPUID bits the Wine modules' ntdll adds when asked, Microsoft's Visual C++ runtime loaded before Wine's, and the H.264 decoder below loaded native.
fixes/install-pbz-fix.sh, lines 171–177values() {
printf '%s|%s|%s\n' "$ENV_KEY" "$CPUID_NAME" "$CPUID_DATA"
local d
for d in $DLLS; do printf '%s|*%s|native,builtin\n' "$DLL_KEY" "$d"; done
# native,builtin: should the prefix lose our file, CrossOver's decoder loads.
printf '%s|*msmpeg2vdec|native,builtin\n' "$DLL_KEY"
}NV12 offered first: CrossOver's list on macOS skips it, and the YV12 type renamed is the same frame size, stride and image size.
fixes/h264-software-decoder.c, lines 214–243static HRESULT WINAPI decoder_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index, IMFMediaType **type)
{
struct decoder *decoder = impl(iface);
IMFMediaType *first, *nv12 = NULL;
HRESULT hr;
if (index)
return IMFTransform_GetOutputAvailableType(decoder->inner, id, index, type);
if (FAILED(hr = IMFTransform_GetOutputAvailableType(decoder->inner, id, 0, &first)))
return hr;
if (is_subtype(first, &MFVideoFormat_NV12) || !is_subtype(first, &MFVideoFormat_YV12))
{
*type = first; /* NV12 already offered, or a list this was not written for */
return S_OK;
}
if (SUCCEEDED(hr = MFCreateMediaType(&nv12))
&& SUCCEEDED(hr = IMFMediaType_CopyAllItems(first, (IMFAttributes *)nv12))
&& SUCCEEDED(hr = IMFMediaType_SetGUID(nv12, &MF_MT_SUBTYPE, &MFVideoFormat_NV12)))
{
*type = nv12;
if (!InterlockedExchange(&decoder->offered_nv12, 1))
say("decoder %p: offering NV12 first", decoder);
}
else if (nv12)
IMFMediaType_Release(nv12);
IMFMediaType_Release(first);
return hr;
}A D3D manager refused, as a decoder that is not D3D aware does, so Electra decodes into system memory.
fixes/h264-software-decoder.c, lines 291–301static HRESULT WINAPI decoder_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param)
{
struct decoder *decoder = impl(iface);
if (message == MFT_MESSAGE_SET_D3D_MANAGER && param)
{
say("decoder %p: refused a D3D manager", decoder);
return E_NOTIMPL;
}
return IMFTransform_ProcessMessage(decoder->inner, message, param);
}Thanks to
- HravaWrote the ntdll patch that makes Rosetta's CPU look like the one Denuvo checks for (engine patch 0017), handed to us in a package for this game with the D3DMetal fallback (0018).
- winevideoIts winegstreamer patch 0006 is what the decoder DLL answers at the COM boundary: NV12 offered, no D3D awareness, a D3D manager refused.
Details
- Needs
- D3DMetal 4 on the Intel side (Rosetta): CrossOver Preview 27, or 26.3 with D3DMetal 4.
- Installer
- install-pbz-fix.sh
- Executable
- PBZ.exe
- Built from
- h264-software-decoder.c
- Verified on
- CrossOver Preview 27 (27.0.0.41073), NotProton runner, 2026-11-01