-
Notifications
You must be signed in to change notification settings - Fork 0
Diagnosing a new game
The tools in diagnostics/, each answering a different question. The numbered
ones below run in this order — every one narrows what the next has to look at.
A few more are listed at the end; they did decisive work on titles here without
belonging to a fixed step in the sequence.
diagnostics/survey-games.sh "/path/to/steamapps/common/<Game>"
One row: engine, how many videos, in what container, the codec of a sample, and which media DLLs the main executable is linked against. Usually enough to say which failure mode you are looking at, or that the game is not in this category at all.
Read the codec and the container as two separate answers. The codec says
whether anything can decode the file; the container says whether anything can
open it, and those come apart. On stable CrossOver 26.3 the same VP9 plays in
an .mp4 and does not in a .webm, because that build ships no matroska
plugin — 355 .webm files and 61 VP9 .mp4 files are the same codec and
different outcomes. Which build opens what is set out in
Games.
Given a whole library instead of one game it surveys everything under it, which helps when hunting for a title worth working on. The wiki still only covers games we deliberately took on.
runtime/pe.py imports "/path/to/Game-Win64-Shipping.exe" --dlls
That answers the cheaper half: whether the game reaches Media Foundation at
all. pe.py has four modes — exports, exports --ordinals, imports,
imports --dlls — and prints nothing else.
The half that settles the crash is a scan for Electra's D3D12 version check. The pattern is
cmp dword [rbp+disp], 12000 ; 81 7D ?? E0 2E 00 00
jl <cpu path> ; 7C ?? or 0F 8C ?? ?? ?? ??
No static scanner for it ships here yet. The code that knows the pattern is the runtime patch itself, which finds and rewrites the sites inside the running process and reports how many it found, so the count arrives once the fix is installed rather than before.
Mortal Shell 2 has four. A UE4 title has none, because the buffer pool that crashes does not exist in that engine version. A count of zero means this particular bug is not present, whatever else may be wrong.
For the silent failure — cutscene reached, screen black, no crash, no log.
diagnostics/build-probe.sh "/path/to/game/<carrier>.dll"
mf-probe replaces entries in the game's import address table and logs each
call with its result: MFStartup, MFCreateFile, MFTEnumEx,
MFCreateSourceReaderFromByteStream, CoCreateInstance failures, and
CreateFileW on video paths. Every hook calls through and returns the real
result unchanged — it watches, it does not intervene.
Hooking the import table beats scanning for byte patterns here: the IAT is a documented structure, so nothing depends on which compiler built the game or on things moving between updates.
Picking a carrier. The game has no plugin hook, so the probe rides in on a DLL the game already loads. It must be imported directly, load early, and have nothing to do with rendering. A shipped third-party library is ideal. The three in use here:
-
libogg_64.dll— any Unreal title. -
libxess.dll— DYNASTY WARRIORS: ORIGINS. -
amd_ags_x64.dll— Persona 5 Strikers. A vendor library the game imports and barely calls under CrossOver, which is the shape to look for when a game ships neither of the other two.
Never steam_api64.dll.
build-probe.sh reads the carrier's export table and generates PE forwarders
for every symbol, so the real library still answers every call. Install by
renaming the game's copy to <stem>_real.dll and dropping the probe in its
place. The log goes to the bottle's C:\mf-probe.log.
Read an empty log carefully. Two things produce one that has nothing to do with what the game asked for.
Imports by name only. If the probe hooked the import table and not
GetProcAddress, a call resolved at runtime went somewhere nobody was
watching.
Imports by ordinal. An import with no name is skipped outright by a
name-walking hook, so the hook installs, reports itself installed, and never
fires. DYNASTY WARRIORS brings in D3D12CreateDevice from d3d12.dll as
ordinal 101 with no name, which is not unusual, and this has cost a wasted run
more than once here. hook_import_ordinal in diagnostics/mf-probe.c exists
for exactly that.
An empty log means "the game did not ask" only once both are ruled out.
diagnostics/capture-mf-trace.sh <bottle> 'Z:\path\to\game.exe'
Launches through Wine with the Media Foundation channels on and reduces the trace to the media types and handlers that were refused. Slower, far noisier, and needs the game launched by hand — but it needs nothing installed into the game folder.
diagnostics/find-callsites.py <file.exe> <symbol> [symbol...]
Finds where a PE calls an imported function without running it, by resolving
every call qword ptr [rip+disp32] against the import table. On DYNASTY
WARRIORS it found in one second what four launches of guesswork had not — two
MFStartup call sites, only one of them the video player.
diagnostics/launch-and-capture.sh <bottle> [engine]
Repository only — this one is not in any release. It keeps the stderr a Steam or CrossOver-interface launch throws away. Wine, D3DMetal and DXMT write their diagnostics there, and for a game that hangs on a black screen with no crash report that is often the only statement of what is wrong. One trap worth knowing before trusting a quiet capture: launching Steam captures nothing, because Steam forks and returns, so the command finishes before the game starts and the game's output belongs to another process.
There is also diagnostics/registry/apply-webm-handler.sh, which adds or
removes the .webm byte-stream handler mapping in a bottle. It was written to
test one question — whether that single mapping is the whole of what winevideo
provides for a WebM/VP9 game on a build that already carries the demuxer.
Hooking and patching a running process is what anti-cheat exists to stop. Do not point any of this at a game with anti-cheat. Everything here is for single-player titles whose cutscenes do not play.
Back to the games table · Findings, what the six have in common