diff --git a/basic_game.py b/basic_game.py index 42457d2..a4fc442 100644 --- a/basic_game.py +++ b/basic_game.py @@ -202,6 +202,7 @@ class BasicGameMappings: savegameExtension: BasicGameMapping[str] steamAPPId: BasicGameOptionsMapping[str] gogAPPId: BasicGameOptionsMapping[str] + originManifestIds: BasicGameOptionsMapping[str] @staticmethod def _default_documents_directory(game): @@ -299,6 +300,9 @@ class BasicGameMappings: self.gogAPPId = BasicGameOptionsMapping( game, "GameGogId", "gogAPPId", default=lambda g: "", apply_fn=ids_apply ) + self.originManifestIds = BasicGameOptionsMapping( + game, "GameOriginManifestIds", "originManifestIds", default=lambda g: "", apply_fn=ids_apply + ) class BasicGame(mobase.IPluginGame): @@ -307,17 +311,20 @@ class BasicGame(mobase.IPluginGame): to make it easier to create game plugins without having to implement all the methods of mobase.IPluginGame.""" - # List of steam and GOG games: + # List of steam, GOG, and origin games: steam_games: Dict[str, Path] gog_games: Dict[str, Path] + origin_games: Dict[str, Path] @staticmethod def setup(): from .gog_utils import find_games as find_gog_games from .steam_utils import find_games as find_steam_games + from .origin_utils import find_games as find_origin_games BasicGame.steam_games = find_steam_games() BasicGame.gog_games = find_gog_games() + BasicGame.origin_games = find_origin_games() # File containing the plugin: _fromName: str @@ -349,6 +356,9 @@ class BasicGame(mobase.IPluginGame): def is_gog(self) -> bool: return self._mappings.gogAPPId.has_value() + def is_origin(self) -> bool: + return self._mappings.originManifestIds.has_value() + # IPlugin interface: def init(self, organizer: mobase.IOrganizer) -> bool: @@ -390,6 +400,11 @@ class BasicGame(mobase.IPluginGame): self.setGamePath(BasicGame.gog_games[gog_id]) return + for origin_manifest_id in self._mappings.originManifestIds.get(): + if origin_manifest_id in BasicGame.origin_games: + self.setGamePath(BasicGame.origin_games[origin_manifest_id]) + return + def gameName(self) -> str: return self._mappings.gameName.get() @@ -523,6 +538,9 @@ class BasicGame(mobase.IPluginGame): for gogid, gogpath in BasicGame.gog_games.items(): if gogpath == path: self._mappings.steamAPPId.set_value(gogid) + for originid, originpath in BasicGame.origin_games.items(): + if originpath == path: + self._mappings.originManifestIds.set_value(originid) def documentsDirectory(self) -> QDir: return self._mappings.documentsDirectory.get() diff --git a/origin_utils.py b/origin_utils.py new file mode 100644 index 0000000..3f7a306 --- /dev/null +++ b/origin_utils.py @@ -0,0 +1,51 @@ +# -*- encoding: utf-8 -*- + +# Heavily influenced by https://github.com/erri120/GameFinder + +import os + +from pathlib import Path +from typing import Dict +from urllib import parse + +from PyQt5.QtCore import QDir, QFileInfo, QStandardPaths + + +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] = {} + + local_content_path = Path(os.path.expandvars("%PROGRAMDATA%")).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)) \ No newline at end of file