Windows port, part 4a: Epic, GOG, Ubisoft, EA and Heroic on Windows, and the dual-boot hints (W4 part 1) #35

Manually merged
SkyfaR merged 30 commits from windows-w4a into main 2026-10-06 07:43:09 +02:00
Owner

Windows port, phase W4, part 1: the other launchers on Windows, and the dual-boot side on both systems. Plan section 4 (launcher table) and section 10 (W4 row). It is based on main and independent of #33 and #34; whichever merges after the others gets small conflicts in the help texts and docs.

Launchers on Windows

Launcher Source Key Version for auto Busy (left alone)
Epic Games Launcher %ProgramData%\Epic\EpicGamesLauncher\Data\Manifests\*.item, or ModSdkMetadataDir epic:<AppName> AppVersionString, plus each DLC's id and version bIsIncompleteInstall not a plain false, a staging folder that holds files or cannot be listed, a DLC still installing
GOG Galaxy HKLM\SOFTWARE\WOW6432Node\GOG.com\Games\<id> gog:<id> buildId, plus DLC the game's !Temp folder holds files or cannot be listed
Heroic %APPDATA%\heroic\…, the Linux code with Windows paths and no prefixes as on Linux as on Linux as on Linux
Ubisoft Connect HKLM\…\Ubisoft\Launcher\Installs\<id> with the uninstall entry's name ubisoft:<id> DisplayVersion where written no signal; auto --include-new waits for a version
EA app Uninstall entries with Publisher "Electronic Arts" (the encrypted IS file is not read) ea:<…> DisplayVersion as Ubisoft
  • Records are validated (launchers/recorded.rs), the same way Steam's installdir is. Refused paths: not absolute, UNC or device paths, .., device names, colons, a trailing dot or space, and Linux-style paths. Such a record becomes an unreadable warning.
  • A game listed by two launchers shows once (overlap.rs). Steam, Epic or GOG win over Ubisoft and EA, and Ubisoft wins over EA. With an Epic library present, Epic wins over Heroic (Heroic's Epic sync); on Linux there is no Epic library, so Heroic is unchanged there.
  • ogc list (also --json), analyze, compress, auto and exclude work on all of them. Battle.net is the only launcher not looked for on Windows, and ogc list names it on stderr.
  • Help and empty-state texts name every launcher of the system and the epic:, gog:, heroic:, ubisoft: and ea: selectors, in 13 languages.

Dual boot (src/dualboot/)

  • Linux:
    • Detection: for a game library on NTFS it names the driver (ntfs3, the remade ntfs, or ntfs-3g/fuseblk) and finds games with files Windows compressed (WOF, and classic LZNT1).
    • The walk is bounded: 4,000 entries and 250 ms per game, 2 s per library, with name filters before any stat.
    • Probe: one file per driver and kind (a program where there is one) is read (64 KiB) and its first page is mapped read-only and then copy-on-write, as Wine maps program sections, then unmapped. Nothing is written. The copy-on-write map is skipped where ntfs3 would allocate a sparse file's holes.
    • Hints, one per outcome:
      • ntfs-3g reads these files only with the system-compression plugin;
      • the driver cannot read them;
      • the remade ntfs driver before its WOF support reads them as zeros (games do not start);
      • ntfs3 refuses the copy-on-write map with EOPNOTSUPP (games do not start under Proton);
      • ntfs3 decompresses files on write (Steam updates undo the compression).
    • Where it shows: in ogc list (code windows-compressed; JSON ntfs_driver, windows_compressed) and under the NTFS drive's banner in the app, beside the existing move-to-btrfs offer.
    • btrfs and ext4 libraries list exactly as before (one statfs), and the hourly timer never walks NTFS games.
  • Windows:
    • A Steam library whose steamapps\compatdata holds something (Proton made it from Linux) is "auch von Linux genutzt" (used-by-linux in ogc list).
    • auto and the background task skip its games, noting this once a day; ogc compress still compresses them, with a warning.

Tests

  • Linux: fmt, both clippy runs, and cargo test --all-features with Broadway and OGC_TEST_REQUIRE_BTRFS=1 (1516 passed, 0 failed).
  • Windows: check.sh and wine-test.sh in the container. The Wine smoke lists one game of each launcher (Epic with a forward-slash path, GOG with a DLC, Ubisoft pointing into Steam's Portal so Portal shows once, EA, Heroic), the Battle.net notice and the used-by-linux hint.
  • Fixture tests for every launcher, the record check, the overlap rule, DLC folding, the busy signals, driver detection, the probe (real maps on tmpfs and a fake that refuses), the bounded walk and the hint texts.
  • Each piece had a review with adversarial verification. The integration had a fix round and a final review; its two findings are fixed in the last commit.

Not verified yet

  • Real installs of Epic, GOG, Ubisoft Connect, the EA app and Heroic for Windows (plan §9.3 #4). The busy signals of Epic and GOG are inferred from public reports.
  • A WOF-compressed NTFS library on ntfs3, the remade ntfs driver and ntfs-3g (plan §9.3 #8). The behaviour was taken from the 7.2 kernel modules; OGC_TEST_NTFS_DIR runs the probe test on a real drive.
Windows port, phase W4, part 1: the other launchers on Windows, and the dual-boot side on both systems. Plan section 4 (launcher table) and section 10 (W4 row). It is based on main and independent of #33 and #34; whichever merges after the others gets small conflicts in the help texts and docs. ## Launchers on Windows | Launcher | Source | Key | Version for `auto` | Busy (left alone) | |---|---|---|---|---| | Epic Games Launcher | `%ProgramData%\Epic\EpicGamesLauncher\Data\Manifests\*.item`, or `ModSdkMetadataDir` | `epic:<AppName>` | `AppVersionString`, plus each DLC's id and version | `bIsIncompleteInstall` not a plain `false`, a staging folder that holds files or cannot be listed, a DLC still installing | | GOG Galaxy | `HKLM\SOFTWARE\WOW6432Node\GOG.com\Games\<id>` | `gog:<id>` | `buildId`, plus DLC | the game's `!Temp` folder holds files or cannot be listed | | Heroic | `%APPDATA%\heroic\…`, the Linux code with Windows paths and no prefixes | as on Linux | as on Linux | as on Linux | | Ubisoft Connect | `HKLM\…\Ubisoft\Launcher\Installs\<id>` with the uninstall entry's name | `ubisoft:<id>` | `DisplayVersion` where written | no signal; `auto --include-new` waits for a version | | EA app | Uninstall entries with Publisher "Electronic Arts" (the encrypted `IS` file is not read) | `ea:<…>` | `DisplayVersion` | as Ubisoft | - **Records are validated** (`launchers/recorded.rs`), the same way Steam's `installdir` is. Refused paths: not absolute, UNC or device paths, `..`, device names, colons, a trailing dot or space, and Linux-style paths. Such a record becomes an `unreadable` warning. - **A game listed by two launchers shows once** (`overlap.rs`). Steam, Epic or GOG win over Ubisoft and EA, and Ubisoft wins over EA. With an Epic library present, Epic wins over Heroic (Heroic's Epic sync); on Linux there is no Epic library, so Heroic is unchanged there. - **`ogc list`** (also `--json`), `analyze`, `compress`, `auto` and `exclude` work on all of them. Battle.net is the only launcher not looked for on Windows, and `ogc list` names it on stderr. - **Help and empty-state texts** name every launcher of the system and the `epic:`, `gog:`, `heroic:`, `ubisoft:` and `ea:` selectors, in 13 languages. ## Dual boot (`src/dualboot/`) - **Linux:** - **Detection:** for a game library on NTFS it names the driver (ntfs3, the remade `ntfs`, or ntfs-3g/fuseblk) and finds games with files Windows compressed (WOF, and classic LZNT1). - **The walk is bounded:** 4,000 entries and 250 ms per game, 2 s per library, with name filters before any stat. - **Probe:** one file per driver and kind (a program where there is one) is read (64 KiB) and its first page is mapped read-only and then copy-on-write, as Wine maps program sections, then unmapped. Nothing is written. The copy-on-write map is skipped where ntfs3 would allocate a sparse file's holes. - **Hints**, one per outcome: - ntfs-3g reads these files only with the system-compression plugin; - the driver cannot read them; - the remade `ntfs` driver before its WOF support reads them as zeros (games do not start); - ntfs3 refuses the copy-on-write map with `EOPNOTSUPP` (games do not start under Proton); - ntfs3 decompresses files on write (Steam updates undo the compression). - **Where it shows:** in `ogc list` (code `windows-compressed`; JSON `ntfs_driver`, `windows_compressed`) and under the NTFS drive's banner in the app, beside the existing move-to-btrfs offer. - **btrfs and ext4 libraries list exactly as before** (one `statfs`), and the hourly timer never walks NTFS games. - **Windows:** - A Steam library whose `steamapps\compatdata` holds something (Proton made it from Linux) is "auch von Linux genutzt" (`used-by-linux` in `ogc list`). - `auto` and the background task skip its games, noting this once a day; `ogc compress` still compresses them, with a warning. ## Tests - **Linux:** fmt, both clippy runs, and `cargo test --all-features` with Broadway and `OGC_TEST_REQUIRE_BTRFS=1` (1516 passed, 0 failed). - **Windows:** `check.sh` and `wine-test.sh` in the container. The Wine smoke lists one game of each launcher (Epic with a forward-slash path, GOG with a DLC, Ubisoft pointing into Steam's Portal so Portal shows once, EA, Heroic), the Battle.net notice and the `used-by-linux` hint. - **Fixture tests** for every launcher, the record check, the overlap rule, DLC folding, the busy signals, driver detection, the probe (real maps on tmpfs and a fake that refuses), the bounded walk and the hint texts. - Each piece had a review with adversarial verification. The integration had a fix round and a final review; its two findings are fixed in the last commit. ## Not verified yet - Real installs of Epic, GOG, Ubisoft Connect, the EA app and Heroic for Windows (plan §9.3 #4). The busy signals of Epic and GOG are inferred from public reports. - A WOF-compressed NTFS library on ntfs3, the remade `ntfs` driver and ntfs-3g (plan §9.3 #8). The behaviour was taken from the 7.2 kernel modules; `OGC_TEST_NTFS_DIR` runs the probe test on a real drive.
Ubisoft Connect records one key per installed game, and the EA app's
games are found among the uninstall entries, so the registry seam can
now list subkeys: RegEnumKeyExW in the 64-bit view on Windows, the
fake derives them from the keys its values were set in.
The same records as on Linux, in the Windows profile. Its games run
natively, so no Wine prefix is taken from GamesConfig or config.json.
The folder and id of each record are checked as Steam's installdir is
(launchers::recorded): a full path with no .., device or verbatim
prefix or part Windows reads otherwise, and an id usable as a file
name, as Heroic names its files of a game by it. A record that fails
is an unreadable warning. Linux' reading is unchanged.
Ubisoft: one game per Installs\<id> key below WOW6432Node, its folder
from InstallDir (forward slashes, lowercase: listed in its real case),
its name from the uninstall entry 'Uplay Install <id>', else the
folder. A key left behind by an uninstall (no folder, no entry) is
left out quietly. The id is Ubisoft's product number.

EA app: the uninstall entries (64-bit, 32-bit, the user's) whose
Publisher starts with Electronic Arts, folder from InstallLocation; the
encrypted IS file is not read. The app's and Origin's own entries, the
anti-cheat, updates and hidden entries are left out, and so are games
in a steamapps folder or listed by another launcher, for which EA's
installer writes the same entry. The id is the entry's key, local to
the PC.

Neither records a build: DisplayVersion stands for it where written;
otherwise auto compresses such a game again only when its folder
changes. Every folder and id is checked as Steam's installdir is.
On Windows load_others reads Heroic, Ubisoft and EA instead of the
Linux launchers. The new Launcher variants get no artwork or icon in
the app yet.
GOG records each installed game as a key of its own below one, which
the seam could not enumerate. Registry::subkeys does so: RegEnumKeyExW
in the 64-bit view on Windows, the keys that hold values in the fake,
none where there is no registry.
Epic's games come from its manifests (*.item in %ProgramData%, or the
folder HKCU\Software\Epic Games\EOS\ModSdkMetadataDir names), GOG's
from HKLM\SOFTWARE\WOW6432Node\GOG.com\Games\<id>, as GameFinder
reads them. Each becomes a library of the new launcher Epic or GOG, keyed
by AppName or the GOG id, with AppVersionString or buildId as the build
auto tells an update by. DLC is left out. An install Epic has not
finished (bIsIncompleteInstall), and a game whose staging folder (Epic's
.egstore\bps or StagingLocation, GOG Galaxy's !Temp) holds files, is
shown as updating and left alone.

The folder a record names is taken only where it names a folder on a
drive plainly (launchers::location): absolute, no UNC or device path, no
. or .., and no part Steam's installdir check refuses; otherwise the
record is reported as unreadable, as a bad Steam manifest is.

On Windows, load_others reads these two instead of Heroic, Lutris and
Faugus. The app shows no picture or launcher icon for them yet.
FINDS_OTHER_LAUNCHERS gives way to LAUNCHERS_NOT_LOOKED_FOR, the names
ogc list prints on stderr where the system has such launchers: Heroic,
Ubisoft Connect, the EA app and Battle.net on Windows, none on Linux.
The Windows help names Steam, Epic, GOG and the own folders, and the
epic:NAME and gog:NAME selectors, in all 13 languages.
The made-up installation gets an Epic manifest in %ProgramData%, written
with forward slashes as Epic does at times, and a GOG game with a DLC in
the registry. ogc list must show both games with Windows paths and no
DLC, and analyze and compress refuse their drive like Steam's.
Where their records are, what is left out, what tells an install or
update in progress, and how a recorded folder is checked.
FINDS_OTHER_LAUNCHERS is now true on Windows: ogc list and the app
show Heroic's, Ubisoft Connect's and the EA app's games, and no longer
say that other launchers are not looked for. The Wine smoke lists a
made-up game of each (Ubisoft's InstallDir in lowercase with forward
slashes, shown in its real case). ARCHITECTURE.md describes the
records, ids, versions and what is left out.
The ntfs driver that came back in Linux 7.1 reports 0x5346544e, which
ogc list showed as fs:0x5346544e. It stays FsKind::Other, now named ntfs,
and its magic is exported for the dual-boot checks.
dualboot::check gives a library hints as warnings with codes of their own.
On Linux it names the NTFS driver (ntfs3, the remade ntfs, ntfs-3g through
FUSE), finds the games with a WOF file (by the reparse tag where ntfs-3g
gives it, else by the reparse-point attribute, checked against ntfs3's
statx), and tries the first such file: a byte read and a page mapped, so
that the hint says whether the games start at all, may not start under
Proton, or lose their compression to Steam's updates under ntfs3. On
Windows a Steam library holding Proton's steamapps\compatdata is one Linux
uses too. Only reads; a library on btrfs or ext4 costs one statfs.
The hints follow a library's warnings in every format. A game Windows
compressed reads "compressed by Windows" where OpenGameCompressor did
not compress it, and the heading names the NTFS driver (ntfs-3g rather
than fuse). The JSON adds ntfs_driver and windows_compressed, left out
elsewhere, so btrfs and ext4 libraries list as before.
ogc auto skips them with the reason "auch von Linux genutzt", noted once
a day as DirectStorage games are, and --include-new does not take them
up; ogc compress still compresses them, with a warning. The Wine smoke's
second library gets a Proton prefix, and ogc list must show its hint.
Discovery adds the library's hint; the banner of the NTFS drive shows it
under its title, above the move to btrfs, which works as before. The
banner of reading problems leaves it out.
Add dualboot-reads-zeros and dualboot-no-write-copy in all 13 languages,
and say plainly that games whose files cannot be mapped do not start
under Proton.
ntfs3 maps a compressed file read-only but refuses the copy-on-write
mapping Wine uses for a program's sections (EOPNOTSUPP), so the probe
now maps the first page read-only and then PROT_READ|PROT_WRITE with
MAP_PRIVATE, and reports which one failed. On ntfs3 the writable
mapping is tried only for files statx calls compressed, as it would
allocate a sparse file's holes otherwise.

The ntfs driver before its WOF support shows a WOF file's empty sparse
stream: the probe reads the first 64 KiB and reports zeros where a
program does not start with MZ or no byte is non-zero. A program is
preferred as the file tried.

Files compressed with LZNT1 are found too and tried the same way; they
get a hint only where the driver fails with them.

The walk through a game is bounded by entries (folders included) and
time, per game and per library; folders and small text files are passed
over by name, and a file gets a stat only where its attributes say
reparse point or compressed.

The probe and the walk move to their own modules.
Both pieces added Registry::subkeys: one version is kept, with a default
for registries without keys and the real one opening the key for listing
only. One loader list reads the launchers of Windows (Epic, GOG, Heroic,
Ubisoft Connect, the EA app), so only Battle.net is not looked for there.
The Wine smoke lists one game of each.
The dual-boot hints: Linux tells the NTFS games Windows compressed, and Windows treats a Steam library with Proton's compatdata as also used by Linux.
Epic and GOG checked a recorded folder in location.rs, Heroic, Ubisoft
Connect and the EA app in recorded.rs. recorded.rs is kept: it reads the
text the way Windows does on every system, so its rules are tested on
Linux too, and Epic's ids go through the same id check as Heroic's.

The layout of the system picks the form a full path takes: on Windows a
Linux-style path such as /Games/X is refused, which Windows would read
below the current drive's top (it was taken as a full path before).
mod.rs had grown past 500 lines with the launchers of Windows. Found and library() go to found.rs, leave_out_containers and its test to overlap.rs.
The help of ogc, compress --all and the game selectors, ogc list with no
game found and the app's empty library now name Steam, Epic, GOG,
Heroic, Ubisoft Connect, the EA app and the own folders on Windows, and
the heroic:, ubisoft: and ea: selectors, in all 13 languages. The notice
of the launchers not looked for reads well with Battle.net alone.
A bIsIncompleteInstall that is not a plain false (a string, a number,
null) made the game ready, and so did a staging or !Temp folder that is
there but cannot be listed. Both now count as an install in progress,
so that auto leaves the game alone. Only a folder that is not there, or
a file in its place, still proves that nothing is staged.
A DLC lies in its game's folder, but its record was thrown away whole:
an Epic DLC still being installed left its game ready, and installing
or updating a DLC did not change the game's build, so ogc auto never
compressed the new files and ogc list kept the game as compressed.

Each DLC's id and version are now added to its game's version
(1.0+dlcA=1.2,dlcB=3), and an Epic DLC in progress makes its game not
ready. A game without DLC keeps its version as it was.
Ubisoft Connect records the Ubisoft games bought on Steam or Epic in the
folder that store installed them to, so such a game showed twice, as
Steam's and as Ubisoft's, and its size was counted twice. Only the EA
app's games were left out where another launcher lists the folder.

One rule now covers both: a game of Ubisoft Connect or the EA app whose
folder a launcher before them lists is that launcher's (one both name
is Ubisoft's), and a library of theirs left empty is dropped. The own
folders do not count. The Wine smoke records Portal for Ubisoft too and
requires it to show once.
Ubisoft Connect and the EA app record no download in progress, and
Ubisoft mostly writes no version. A run with --include-new that came
while a first download was paused compressed what was there, recorded
the game as compressed, and nothing ever told that the download went on:
the files written later stayed uncompressed while the game showed as
compressed.

Until there is a safe sign that such an install is finished, ogc auto
leaves a game of theirs without a version alone as a new one. Updates
of games compressed before, and ogc compress, work as before. No other
launcher is affected.
One table and one set of rules for Epic, GOG, Heroic, Ubisoft Connect
and the EA app: the one folder and id check, games another store's
launcher lists, DLC folded into its game, the doubtful busy signs, and
why auto takes up Ubisoft and EA games as new only with a version. The
README names every launcher Windows looks for.
Take a Heroic game the Epic Games Launcher also records as Epic's, and zeros as a sign only where the driver may show an empty stream
All checks were successful
CI / Windows, command line (tests under Wine) (pull_request) Successful in 1m59s
CI / Windows, command line (clippy with tests) (pull_request) Successful in 48s
CI / Format, lint and test (pull_request) Successful in 5m33s
CI / Bundle programs, command line and helper (pull_request) Successful in 2m13s
CI / Bundle programs, desktop app (pull_request) Successful in 3m2s
CI / Arch package (pull_request) Successful in 3m2s
CI / Bundle for any distribution (pull_request) Successful in 40s
CI / Release (pull_request) Has been skipped
VM test / Migrations in a VM (pull_request) Successful in 18m2s
2613b49251
Heroic for Windows can sync its Epic games into the Epic Games Launcher's
manifests, so the same game was listed twice. Where an Epic library is
read, Heroic now stands behind it; Linux, with no Epic library, is
unchanged.

A file whose first 64 KiB are zeros counted as unreadable on every
driver, though only the ntfs driver before its WOF support shows an
empty stream; ntfs3 and ntfs-3g give a file's real data. A program
without "MZ" still counts on every driver.
SkyfaR manually merged commit 741e85162d into main 2026-10-06 07:43:09 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
LevelXStudios/OpenGameCompressor!35
No description provided.