Skip to content

Releases: MathiasKowoll/MacGameVideoFix

MacGameVideoFix 4.8.6 — the codec half of the catalogue crosses the seam

Choose a tag to compare

@MathiasKowoll MathiasKowoll released this 25 Aug 22:04

A launcher applying these fixes could not stage a codec, and could not know which titles needed one. Nine of them do; three need nothing else. That whole half of the catalogue stopped at the edge of the bundle.

Two reasons, both quiet. The requirement travelled as prose inside why"needs the staged Matroska demuxer too" — which no interface can act on. And stage-codecs.sh was not in the tarball at all: the generator stages what an installer names, and no installer names it. So applying the NINJA GAIDEN 4 or Persona 5 Strikers fix from the tarball handed the user back the exact symptom the fix was for.

  • stage-codecs.sh ships. It is a script the launcher runs, the same as the installers.
  • Each title carries codeclibgstlibav or libgstmatroska, from the dictionary in wiki/games.py that already had the answer per title. Empty means the engine decodes it unaided, which is a measured statement and not a gap.
  • Titles whose whole fix is the codec are in the manifest. Nothing is installed beside those games, so no installer declared them and they were absent entirely — a launcher had no way to know they exist.

One thing measured, two things said out loud

Devil May Cry 5 goes in with its executable read off a real install. The two RESIDENT EVIL folders on the machine this was built on are empty shells, so theirs has not been read and they stay out — and the generator says so on every run:

note: "RESIDENT EVIL 2" needs libgstlibav and no installer; its executable
      has not been read off a real install, so it is not in the manifest

An entry with no executable cannot be matched to a folder, and a silent absence looks exactly like a title that needs nothing. A gap that announces itself is a gap someone can close.

MacGameVideoFix 4.8.5 — nobody's bottle names but their own

Choose a tag to compare

@MathiasKowoll MathiasKowoll released this 25 Aug 22:00

Bottle names are made up by whoever makes the bottle. Ours are not a fact about anyone else's machine, and they are not ours to publish — and they had got into comments inside installers that travel in the fixes bundle. 4.8.4 shipped with them (it was downloaded by nobody before this replaced it).

Nothing ever depended on a name for its behaviour: the installers find a bottle by which Steam library it lists, not by what it is called. The comments simply named this machine's. They now describe the situation instead — one name held in two roots, two spellings differing only in case — which is the part that is true anywhere.

launch-and-capture.sh no longer defaults to a bottle called Steam. A default that happens to exist on somebody's machine captures the wrong one without saying so.

And a guard, because remembering the rule did not work

It had leaked twice: once into the wiki earlier, once into the bundle now. runtime/check-builds.sh reads the bottle names actually present on the machine it runs on and refuses to find them in anything shipped or published. It cannot go stale, because it does not carry a list — it asks.

A bottle simply called Steam is not checked and cannot be: the word belongs to the subject matter. Names that are merely the default are not the ones that identify somebody's setup anyway.

Everything else is 4.8.4: the manifest carrying backend, gptk and env per title, bottles.sh travelling beside the installers, and the diagnostic tools reaching whichever root holds a bottle.

MacGameVideoFix 4.8.4 — what a title needs configured, and fixes raised on Procyon

Choose a tag to compare

@MathiasKowoll MathiasKowoll released this 25 Aug 21:56

The manifest now says what to configure, not only what to install. Each of the sixteen titles carries the graphics backend it requires, the Game Porting Toolkit generation it is tied to, and a place for environment variables. Schema 3; schema 2 consumers are unaffected in shape, the fields are additions.

gptk is reported only where it is a requirement. "3.0 and 4.0b2" means both work and the field stays empty, because pinning a toolkit for a game that does not care is worse than leaving the choice alone. NINJA GAIDEN 4 reports 3; both Life is Strange titles report 4 — and that pair is the one that was quietly wrong, since a launcher defaulting to generation 3 had been handing them the one that crashes them.

None of it is a second copy. wiki/games.py measured these and emits them with --config-json, which the bundle generator reads.

The manifest also states the limit of what it describes, in a scopeWarning field: the toolkit is installed into the shared CrossOver application, so whichever game ran last leaves its generation in place for all of them. gptk is a requirement of the title, not an isolation guarantee.

Fixes are now raised against Procyon

Development moved onto the Procyon fork — the toolkit and the codecs can be swapped freely there, and its bottles live under their own root, so diagnosing a game touches nothing in a stock CrossOver install.

The diagnostic tools could not reach that environment: each named CrossOver's bottle directory outright, and the capture picked its engine from two hardcoded paths. They now look in every root, and ask which engine owns a bottle rather than guessing — three installs on the machine this was written on declare the same version and only one can open any given bottle.

One file, and one refusal

bottle_roots and crossover_for_bottle were byte-identical in two installers with a third copy about to appear, so they are now runtime/bottles.sh, which travels in the fixes bundle beside the installers that source it.

And looking a bottle up by name now refuses to guess. A machine can hold SteamARM under one root and SteamArm under another, and macOS filesystems are case-insensitive — so first-match-wins answered with the wrong bottle, silently. An absolute path is taken as given; an ambiguous name is an error that lists what it could have meant.

MacGameVideoFix 4.8.3 — the manifest carries the games, not just the scripts

Choose a tag to compare

@MathiasKowoll MathiasKowoll released this 25 Aug 20:23

For anyone building a launcher on top of this. The fixes bundle's manifest.json now describes titles, not scripts: sixteen entries rather than ten, each with the game's name, its shipping executable, the carrier DLL and what the original is renamed to, where that carrier sits relative to the game folder, whether a registry override is written, and a one-line reason. Schema goes to 2.

Before this, exe, carrier and keptAs were empty for every entry — which made per-game work impossible, since there is nothing else to match a folder on. There is no AppID anywhere in this project, and the folder name is Valve's to choose: Mortal Shell 2 installs into one called Sparta.

Two causes, and only one was a bug

The bug. Every extraction pattern was built as a qr// without /m and then interpolated into /$re/m. A qr carries its own flags, so the outer one never reached it, and ^ matched only the very start of the file. Every field came out empty, in a manifest whose whole purpose is to carry them.

The rest was not there to extract. Seven of the ten installers never named the executable — they had no reason to, checking for it inline or identifying the folder another way. The ten now declare what they serve, one line per title. Four of them serve more than one game, which the old shape could not express at all.

The copy is checked

Those declarations duplicate what the app already knows, and this repository has been bitten by second copies often enough today to know how that ends. runtime/check-builds.sh now compares them against the app's own table on every run and fails naming both sides. Verified the way these things should be: by introducing a wrong executable and watching it refuse.

And a reason, before rather than after

Each title carries a one-line why. NieR Replicant's installer already printed "the video is WMV2 with WMA v2 audio in ASF, and CrossOver demuxes ASF while decoding neither stream" — but only once it had finished. That belongs in the confirmation, before anything touches somebody's game folder.

MacGameVideoFix 4.8.2 — the engine that owns the bottle, and a release step that cannot forget

Choose a tag to compare

@MathiasKowoll MathiasKowoll released this 25 Aug 19:53

Replaces the 4.8.1 fixes bundle. That asset was built three corrections earlier, and one of them matters to anyone downloading it: the installers chose which CrossOver to drive by matching version numbers, and two engines on the same machine can declare the same version. wine --bottle takes a name, not a path, so it falls back to that engine's own bottle directory — where a bottle with the same name may exist and may already carry the key. The override went into the wrong bottle and the verification then passed against that same wrong bottle: a false success inside the check built to prevent false successes.

The engine is now chosen by which one owns the bottle's root, read from its CrossOver.conf, with version matching kept only as the fallback for bottles in the default directory.

Also in this bundle

  • pe.pl implements --ordinals. The move off Python — macOS ships no python3 without the Command Line Tools — had left that mode unimplemented while three build scripts depend on it to generate forwarder tables. A .def built from the incomplete output would have come out as = X_real. @: an export table that is wrong in silence. Verified against pe.py across twelve DLLs in both modes.
  • build-app.sh ships the file the installers call. The installers had moved to pe.pl while the bundle still carried pe.py.
  • A script with no HOME refuses instead of answering. Run under env -i, bottle discovery died on an unbound variable and the script carried on to print broken — a state word for something it had not been able to look at.

And the release step itself

Four releases went out earlier today with no app attached. Each was correct, tagged and described at length, and useless to anyone who wanted to run it. app/release.sh is now that step, and it refuses to publish when the built app does not say the tag's version, when check-builds.sh reports drift, or when games.py --check fails. It refused on its first run, correctly.

All four of those releases have since been given the bundle built from their own tag.

MacGameVideoFix 4.8.1 — a bundle a launcher can download, and no python to run it

Choose a tag to compare

@MathiasKowoll MathiasKowoll released this 25 Aug 19:35

This release adds an asset: fixes-v4.8.1.tar.gz, everything needed to apply a per-game fix and nothing else. Ten installers, the carrier DLL each one names, the export reader, and a manifest saying which belongs to which game. 620 KB, flat, no external dependency.

It exists so a launcher can fetch the fixes without shipping them, and so a new title arrives by publishing a release rather than by rebuilding somebody else's application.

PE exports are read with perl now. /usr/bin/python3 is not Python: it is one of 78 hard links to the same xcrun dispatcher, the same inode as /usr/bin/git and /usr/bin/clang. On a Mac with developer tools it dispatches to a real interpreter; on a clean one it opens the install-developer-tools dialog and fails. A machine that has Xcode is exactly the machine that cannot notice this. /usr/bin/perl is real, and unpack is made for reading binary headers. Verified identical output on twelve DLLs, and verified to run under env -i.

A bottle is addressed by its path, not its name. wine --bottle takes a name and resolves it under that engine's own bottle directory, so a fix could not reach a bottle kept anywhere else. Worse than not reaching it: measured on a stock engine, --bottle SteamArm resolved to a same-named bottle in the default root — macOS does not distinguish the case — and the override would have landed in a bottle the user never plays in, silently. Every registry write now names the root explicitly.

The manifest records the commit it was built from, whether the tree was clean and whether that commit is a released tag, so an asset that cannot be rebuilt from its own release says so.

Verify with:

shasum -a 256 -c fixes-v4.8.1.tar.gz.sha256

MacGameVideoFix 4.8.0 — a fix that reports itself installed is not necessarily loaded

Choose a tag to compare

@MathiasKowoll MathiasKowoll released this 25 Aug 17:27

A minor rather than another patch, because two of the things that changed are not fixes to features — they are the repository learning to check itself.

Two titles

NieR Replicant runs on stable CrossOver 26.3. What had kept it in the Preview-only column was never the engine. Its bridge rides on dinput8, which Wine implements and prefers, so it depends on a registry override — and that override had gone missing, while --status answered installed from the two files on disk without ever asking the registry. Every measurement taken on 26.3 was of the game running with no fix at all.

Its video turned out to be WMV2 with WMA v2 audio, in ASF, sixteen bytes into the game's own .arc. The page had recorded the codec as unknowable; the earlier scan had looked one header too early. CrossOver opens the container and decodes neither stream, so the title needs the staged libgstlibav — and had been filed as needing nothing.

NINJA GAIDEN 4 answers its Media Foundation gate from whatever the engine has. It asks MFTEnumEx whether anything can decode VP9 and quits with its own dialog if the count is zero. Our answer re-asked for H.264 and handed back that count — real MFTs with real lifetimes, which was the point — but assumed H.264 was registered. On an engine offering none, the fallback had nothing to return. It now falls through to any video decoder the engine has. Measured on both 26.3 and a patched Preview 27; on 26.3 the original path still carries it, which is exactly what the change was designed to leave alone.

Both architectures

CrossOver 27 runs ARM Windows binaries as well as x86_64 ones, and a bottle records which it is. stage-codecs.sh now stages every architecture an engine ships in one pass, and each bottle gets the staging its own WineArch asks for. Staging only x86_64 had left an ARM bottle with no VC-1, WMV or WMA decoder at all.

Two guards

  • runtime/check-builds.sh rebuilds every shipped DLL from the source it names and compares export tables and strings. It is self-contained — carrier, source and export table all come from the binary itself, so no game needs to be installed. It immediately found a proxy several revisions behind its own source, with a third build again in the game folder.
  • wiki/games.py --check refuses hand-written counts of the title set outside the generated blocks, and the README's table is now generated from the same rows as the wiki's. It had eighteen and was missing NINJA GAIDEN 4 entirely.

And the defects that prompted them

All one shape — something claimed, nothing checking:

  • A fix reported as applied while the key that makes it load was gone, and an --install whose refusal to act was defined by the same predicate that called it fine.
  • A guard in front of a destructive mv that could never match, because it looked for a string the proxy does not contain.
  • An install that overwrote a dstorage.dll a Steam verification had just restored — the only copy of the original.
  • A bottle counted as written that could not be addressed, and a bottle judged as missing a key it could not be asked about.
  • Three environment levers that set a flag to the value it already had, one of them advertised in four rows of a published table.

MacGameVideoFix 4.7.6 — both architectures staged, and NINJA GAIDEN 4 measured on stable

Choose a tag to compare

@MathiasKowoll MathiasKowoll released this 25 Aug 17:25

NINJA GAIDEN 4, on the engine this project supports

The gate change shipped in 4.7.4 has now been run on CrossOver 26.3 with D3DMetal 3.0, and the log says something better than "it works": MFTEnumEx answers 1 for H.264 there, so the original path carried it and the new fallback never ran. That is what made the change safe to ship before it was measured — it only reaches for "any video decoder" where the old code was already returning zero — and it is now measured rather than reasoned. The source reader opens the .msd, and no ActivateObject line follows on either engine: the game counts what it is handed and never activates it.

Both architectures, in one pass

CrossOver 27 runs ARM Windows binaries as well as x86_64 ones, and a bottle records which it is — "WineArch" = "arm64" against "win64". The two do not share a plugin directory, and staging only x86_64 left an ARM bottle with no VC-1, WMV or WMA decoder at all. The GStreamer framework is universal, so the material had been there the whole time; the gap was the script's.

stage-codecs.sh now stages every architecture an engine ships, in one pass, and each bottle gets the staging its own WineArch asks for.

Two things that had to be got right rather than assumed:

  • lib64 is the old single-architecture layout, and it holds x86_64. The fallback to it happens only when staging x86_64; taking it for aarch64 would have filled an ARM directory with x86_64 libraries that resolve while staging and fail on load.
  • An ARM bottle recorded against an engine with no ARM GStreamer is refused, with the reason. It used to be handed a path built from the engine's name without anyone going to look — and a key that is set, on a bottle that reads as configured, with nothing behind it, is worse than one that is wrong.

Two ways of not claiming what we cannot check

Both found by a status that disagreed with a working game:

  • A bottle outside CrossOver's own directory cannot be written to. wine --bottle takes a name, not a path, so two bottles called "Steam" in two roots collapse onto one. They are found — that is useful — and skipped out loud rather than counted as done.
  • A bottle that cannot answer is not a bottle that says no. An ARM bottle on a CrossOver with no ARM support has its keys on disk and cannot run reg.exe to confirm them. Each bottle is now asked something that must exist before its answer is trusted.

MacGameVideoFix 4.7.5 — the Strikers proxy catches up with its own source

Choose a tag to compare

@MathiasKowoll MathiasKowoll released this 25 Aug 17:03

4.7.4 shipped runtime/amd_ags_x64.dll knowing it lagged its own source, and said so rather than rebuilding it blind: the newer source hooks IDirect3D9Ex::CreateDeviceEx where the shipped binary hooked CreateDevice, and that is a change in behaviour, not a tidy-up.

It has now been run. Persona 5 Strikers plays its videos with the rebuilt proxy — CreateDeviceEx exercised fourteen times, the ASF header check seven, and source luma [420]: average 80, range 16..240 << has picture four hundred frames in.

runtime/check-builds.sh reports no drift at all: every shipped DLL rebuilds from the source it names, and every copy in the app bundle matches runtime/.

MacGameVideoFix 4.7.4 — answer the gate from what the engine has

Choose a tag to compare

@MathiasKowoll MathiasKowoll released this 25 Aug 16:52

NINJA GAIDEN 4 asks Media Foundation a question and quits if it does not like the answer. It calls MFTEnumEx for VP9, and if the count is zero it shows its own dialog — "Windows is missing required components… Media Foundation and the VP9 Codec" — and exits, before it has opened a single video file. It is the only title here that fails on the answer to a question rather than on decoding.

Our answer re-asked for H.264 and handed back that count. That was deliberate: those are real MFTs with real lifetimes, not an array we fabricated for the game to free. But it assumed H.264 was registered, and on a CrossOver that offers none, the fallback had nothing to return — so the game still quit, on an engine where every other part of the fix was working.

It now falls through to any video decoder the engine has, whatever it happens to register. Same reason, no dependence on a format being present by name. Measured: the gate passes, and MFCreateSourceReaderFromURL opens the .msd.

Two guards

A day of this turned up the same shape five times — something claimed, nothing checking.

  • runtime/check-builds.sh rebuilds every shipped DLL from the source it names and compares export tables and strings. It is self-contained: the carrier comes from the forwarders the DLL exports, the source from the log file it names, the export table from the binary itself, so no game needs to be installed. It immediately found runtime/amd_ags_x64.dll several revisions behind its own source, with a third build again in the game folder — three versions of one proxy, and nothing anywhere said so.
  • wiki/games.py --check now refuses hand-written counts of the title set outside the generated blocks. "Three titles need a codec" was seven. "Six of the eighteen run on stable" was seventeen of nineteen. "Four sources" was five. A count of something local — executables in one package, modes in one list — stays, marked <!-- count-ok -->.

The README's table is now generated from the same rows as the wiki's. It had eighteen and was missing NINJA GAIDEN 4 entirely.

Installers

  • --install and --status now agree. Install called a run a success on one bottle out of five while status required all of them, so a mixed run printed installed and answered broken for ever after — with the app re-offering a repair that could not change the answer.
  • Bottles outside CrossOver's own directory are skipped out loud. wine --bottle takes a name, not a path, so two bottles called "Steam" in two roots collapse onto one. Claiming to have written to the other took a correctly installed game to broken and kept it there.
  • NINJA GAIDEN 4's install no longer overwrites a dstorage.dll that a Steam verification has just restored. It asked whether the saved copy existed rather than which of the two was ours, and the only copy of the original was the one it replaced. --restore now checks the file is ours before removing it, which its own comment had promised for a long time.
  • install-node-guard learned half — the one script that modifies a bundle every bottle shares, and the only one with no way back out of that state.
  • A dinput8 that is not ours is refused rather than overwritten. A mod or an input wrapper is not in Steam's manifest, so "verify the game files" does not bring it back.

In the app

half is repairable where the carrier is copied from the bottle rather than shipped by the game — true for NieR Replicant and Kingdom Hearts, false for the rest. The header used to declare all of them unfixable, on rows a single click would have completed.

Also

Three dead levers removed or made real. P5S_REAL_FRAMES set a flag to the value it already had, so the compiler dropped the branch entirely and four rows of the table advertised a control that moved nothing.

Known, and stated rather than hidden: runtime/amd_ags_x64.dll still lags its source. Rebuilding it changes which D3D9 entry point is hooked, so it waits for a run of Persona 5 Strikers rather than shipping unmeasured. check-builds.sh reports it, which is the point of having it.