diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-02-11 02:37:39 -0600 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-02-11 02:37:39 -0600 |
| commit | 7ee008e150bc5bcf76082d726f719ee0fdfda982 (patch) | |
| tree | 27fb39be241fdb5ac2734c574de678977d1856d0 /libs/basic_games/origin_utils.py | |
Fluorine Manager: full Linux port of Mod Organizer 2
Complete native Linux port with FUSE-based virtual filesystem,
Proton/umu-run integration, and Flatpak packaging.
Key features:
- FUSE VFS replacing Windows USVFS (in-process + standalone helper for Flatpak)
- Proton/GE-Proton/umu-run launcher with env var forwarding
- Flatpak support (sandbox-aware VFS, NXM handler, umu-run)
- Wine prefix management UI
- Case-insensitive path resolution for Linux filesystems
- QSettings-safe INI handling (avoids Bethesda INI corruption)
- Portable instance support with auto-generated launcher scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'libs/basic_games/origin_utils.py')
| -rw-r--r-- | libs/basic_games/origin_utils.py | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/libs/basic_games/origin_utils.py b/libs/basic_games/origin_utils.py new file mode 100644 index 0000000..da050e8 --- /dev/null +++ b/libs/basic_games/origin_utils.py @@ -0,0 +1,104 @@ +# -*- encoding: utf-8 -*- + +# Heavily influenced by https://github.com/erri120/GameFinder + +import os +import threading +import time +from collections.abc import Sequence +from pathlib import Path +from urllib import parse + +import psutil + + +class OriginWatcher: + """ + This is a class to control killing Origin when needed. This is used in + order to hook and unhook Origin to get around the Origin DRM. Support + for launching Origin is not included as it's intended for the game's + DRM to launch Origin as needed. + """ + + def __init__(self, executables: Sequence[str] = []): + self.executables = list(map(lambda s: s.lower(), executables)) + + def spawn_origin_watcher(self) -> bool: + self.kill_origin() + self.worker_alive = True + self.worker = threading.Thread(target=self._workerFunc) + self.worker.start() + return True + + def stop_origin_watcher(self) -> None: + self.worker_alive = False + self.worker.join(10.0) + + def kill_origin(self) -> None: + """ + Kills the Origin application + """ + for proc in psutil.process_iter(): + if proc.name().lower() == "origin.exe": + proc.kill() + + def _workerFunc(self) -> None: + gameAliveCount = 300 # Large number to allow Origin and the game to launch + while self.worker_alive: + gameAlive = False + # See if the game is still alive + for proc in psutil.process_iter(): + if proc.name().lower() in self.executables: + gameAlive = True + break + if gameAlive: + # Game is alive, sleep and keep monitoring at faster pace + gameAliveCount = 5 + else: + gameAliveCount -= 1 + if gameAliveCount <= 0: + self.kill_origin() + self.worker_alive = False + time.sleep(1) + + +def find_games() -> dict[str, Path]: + """ + Find the list of Origin games installed. + + Returns: + A mapping from Origin manifest IDs to install locations for available + Origin games. + """ + games: dict[str, Path] = {} + + program_data_path = os.path.expandvars("%PROGRAMDATA%") + local_content_path = Path(program_data_path).joinpath("Origin", "LocalContent") + for manifest in local_content_path.glob("**/*.mfst"): + # Skip any manifest file with '@steam' + if "@steam" in manifest.name.lower(): + continue + + # Read the file and look for &id= and &dipinstallpath= + with open(manifest, "r") as f: + manifest_query = f.read() + url = parse.urlparse(manifest_query) + query = parse.parse_qs(url.query) + if "id" not in query: + # If id is not present, we have no clue what to do. + continue + if "dipinstallpath" not in query: + # We could query the Origin server for the install location but... no? + continue + + for id_ in query["id"]: + for path_ in query["dipinstallpath"]: + games[id_] = Path(path_) + + return games + + +if __name__ == "__main__": + games = find_games() + for k, v in games.items(): + print("Found game with id {} at {}.".format(k, v)) |
