aboutsummaryrefslogtreecommitdiff
path: root/libs/basic_games/origin_utils.py
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-11 02:37:39 -0600
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-11 02:37:39 -0600
commit7ee008e150bc5bcf76082d726f719ee0fdfda982 (patch)
tree27fb39be241fdb5ac2734c574de678977d1856d0 /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.py104
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))