mirror of
https://github.com/ModOrganizer2/modorganizer-basic_games.git
synced 2026-07-27 14:07:29 -07:00
* Catch and log errors for failed EA games list parsing * Extend error logging for Epic Games * Add error message box for errors loading game list * Fix lint error in game_sims4.py
86 lines
2.6 KiB
Python
86 lines
2.6 KiB
Python
# -*- 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))
|