mirror of
https://github.com/ModOrganizer2/modorganizer-basic_games.git
synced 2026-07-27 14:07:29 -07:00
* Refactoring following uibase change for game features. * Switch to ruff for linting and formatting. * Bump stubs to 2.5.1a0.
105 lines
3.2 KiB
Python
105 lines
3.2 KiB
Python
# -*- 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))
|