aboutsummaryrefslogtreecommitdiff
path: root/libs/basic_games/eadesktop_utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'libs/basic_games/eadesktop_utils.py')
-rw-r--r--libs/basic_games/eadesktop_utils.py85
1 files changed, 85 insertions, 0 deletions
diff --git a/libs/basic_games/eadesktop_utils.py b/libs/basic_games/eadesktop_utils.py
new file mode 100644
index 0000000..5720556
--- /dev/null
+++ b/libs/basic_games/eadesktop_utils.py
@@ -0,0 +1,85 @@
+# -*- encoding: utf-8 -*-
+
+import configparser
+import os
+import sys
+import xml.etree.ElementTree as et
+from configparser import NoOptionError
+from pathlib import Path
+from typing import Dict
+
+
+def find_games(errors: list[tuple[str, Exception]] | None = None) -> Dict[str, Path]:
+ """
+ Find the list of EA Desktop games installed.
+
+ Returns:
+ A mapping from EA Desktop content IDs to install locations for available
+ EA Desktop games.
+ """
+ games: Dict[str, Path] = {}
+
+ local_app_data_path = os.path.expandvars("%LocalAppData%")
+ ea_desktop_settings_path = Path(local_app_data_path).joinpath(
+ "Electronic Arts", "EA Desktop"
+ )
+
+ if not ea_desktop_settings_path.exists():
+ return games
+
+ try:
+ user_ini, *_ = list(ea_desktop_settings_path.glob("user_*.ini"))
+ except ValueError:
+ return games
+
+ # The INI file in its current form has no section headers.
+ # So we wrangle the input to add it all under a fake section.
+ with open(user_ini) as f:
+ ini_content = "[mod_organizer]\n" + f.read()
+
+ config = configparser.ConfigParser()
+ try:
+ config.read_string(ini_content)
+ except configparser.ParsingError as e:
+ error_message = (
+ f'Failed to parse EA Desktop games list file "{user_ini}",\n'
+ " Try to run the launcher to recreate it."
+ )
+ print(error_message, e, file=sys.stderr)
+ if errors is not None:
+ errors.append((error_message, e))
+ return games
+
+ try:
+ install_path = Path(config.get("mod_organizer", "user.downloadinplacedir"))
+ except NoOptionError:
+ install_path = Path(os.environ["ProgramW6432"]) / "EA Games"
+ config.set("mod_organizer", "user.downloadinplacedir", install_path.__str__())
+
+ if not install_path.exists():
+ return games
+
+ for game_dir in install_path.iterdir():
+ try:
+ installer_file = game_dir.joinpath("__Installer", "installerdata.xml")
+ xml_tree = et.parse(installer_file)
+ root = xml_tree.getroot()
+
+ # For all manifest files the following XPath expression returns the
+ # numeric ID. There are, in some cases, also name IDs but we do not
+ # consider these.
+ content_id = root.find(".//contentIDs/contentID[1]")
+
+ if content_id is not None and content_id.text:
+ game_id = content_id.text
+ games[game_id] = game_dir
+ except FileNotFoundError:
+ pass
+
+ return games
+
+
+if __name__ == "__main__":
+ games = find_games()
+ for k, v in games.items():
+ print("Found game with id {} at {}.".format(k, v))