Merge branch 'ashemedai-ea-desktop-support' into qt6

# Conflicts:
#	README.md
#	basic_game.py
This commit is contained in:
Mikaël Capelle
2022-05-02 16:52:26 +02:00
5 changed files with 93 additions and 6 deletions
+8 -4
View File
@@ -158,6 +158,7 @@ class Witcher3Game(BasicGame):
| GameOriginManifestIds | Origin Manifest ID of the game (Optional) | `originManifestIds` | `List[str]` or `str` |
| GameOriginWatcherExecutables | Executables to watch for Origin DRM (Optional) | `originWatcherExecutables` | `List[str]` or `str` |
| GameEpicId | Epic ID (`AppName`) of the game (Optional) | `epicAPPId` | `List[str]` or `str` |
| GameEaDesktopId | EA Desktop ID of the game (Optional) | `eaDesktopContentId` | `List[str]` or `str` or `int` |
You can use the following variables for `str`:
@@ -169,9 +170,11 @@ You can use the following variables for `str`:
The meta-plugin provides some useful extra feature:
1. **Automatic Steam, GOG, Origin and Epic game detection:** If you provide Steam, GOG, Origin or Epic IDs for the game
(via `GameSteamId`, `GameGogId`, `GameOriginManifestIds`, or `GameEpicId`), the game will be listed in the list of
available games when creating a new MO2 instance (if the game is installed via Steam, GOG, Origin or Epic Games / Legendary).
1. **Automatic Steam, GOG, Origin, Epic Games and EA Desktop detection:** If you provide
Steam, GOG, Origin or Epic IDs for the game (via `GameSteamId`, `GameGogId`,
`GameOriginManifestIds`, `GameEpicId` or `GameEaDesktopId`), the game will be listed
in the list of available games when creating a new MO2 instance (if the game is
installed via Steam, GOG, Origin, Epic Games / Legendary or EA Desktop).
2. **Basic save game preview:** If you use the Python version, and if you can easily obtain a picture (file)
for any saves, you can provide basic save-game preview by using the `BasicGameSaveGameInfo`.
See [games/game_witcher3.py](games/game_witcher3.py) for more details.
@@ -184,5 +187,6 @@ Game IDs can be found here:
- For Epic Games (`AppName`) from:
- `C:\ProgramData\Epic\EpicGamesLauncher\Data\Manifests\` (.item files)
- or: `C:\ProgramData\Epic\EpicGamesLauncher\UnrealEngineLauncher\LauncherInstalled.dat`
- For Legendary (alt. Epic launcher) via command `legendary list-games`
- For Legendary (alt. Epic launcher) via command `legendary list-games`
or from: `%USERPROFILE%\.config\legendary\installed.json`
- For EA Desktop from `<EA Games install location>\<game title>\__Installer\installerdata.xml`
+26 -1
View File
@@ -210,6 +210,7 @@ class BasicGameMappings:
originManifestIds: BasicGameOptionsMapping[str]
originWatcherExecutables: BasicGameMapping[List[str]]
epicAPPId: BasicGameOptionsMapping[str]
eaDesktopContentId: BasicGameOptionsMapping[str]
@staticmethod
def _default_documents_directory(game):
@@ -328,6 +329,13 @@ class BasicGameMappings:
self.epicAPPId = BasicGameOptionsMapping(
game, "GameEpicId", "epicAPPId", default=lambda g: "", apply_fn=ids_apply
)
self.eaDesktopContentId = BasicGameOptionsMapping(
game,
"GameEaDesktopId",
"eaDesktopContentId",
default=lambda g: "",
apply_fn=ids_apply,
)
class BasicGame(mobase.IPluginGame):
@@ -341,9 +349,11 @@ class BasicGame(mobase.IPluginGame):
gog_games: Dict[str, Path]
origin_games: Dict[str, Path]
epic_games: Dict[str, Path]
eadesktop_games: Dict[str, Path]
@staticmethod
def setup():
from .eadesktop_utils import find_games as find_eadesktop_games
from .epic_utils import find_games as find_epic_games
from .gog_utils import find_games as find_gog_games
from .origin_utils import find_games as find_origin_games
@@ -353,6 +363,7 @@ class BasicGame(mobase.IPluginGame):
BasicGame.gog_games = find_gog_games()
BasicGame.origin_games = find_origin_games()
BasicGame.epic_games = find_epic_games()
BasicGame.eadesktop_games = find_eadesktop_games()
# File containing the plugin:
_fromName: str
@@ -390,6 +401,9 @@ class BasicGame(mobase.IPluginGame):
def is_epic(self) -> bool:
return self._mappings.epicAPPId.has_value()
def is_eadesktop(self) -> bool:
return self._mappings.eaDesktopContentId.has_value()
# IPlugin interface:
def init(self, organizer: mobase.IOrganizer) -> bool:
@@ -457,6 +471,11 @@ class BasicGame(mobase.IPluginGame):
self.setGamePath(BasicGame.epic_games[epic_id])
return
for eadesktop_content_id in self._mappings.eaDesktopContentId.get():
if eadesktop_content_id in BasicGame.eadesktop_games:
self.setGamePath(BasicGame.eadesktop_games[eadesktop_content_id])
return
def gameName(self) -> str:
return self._mappings.gameName.get()
@@ -489,6 +508,9 @@ class BasicGame(mobase.IPluginGame):
def epicAPPId(self) -> str:
return self._mappings.epicAPPId.current()
def eaDesktopContentId(self) -> str:
return self._mappings.eaDesktopContentId.current()
def binaryName(self) -> str:
return self._mappings.binaryName.get()
@@ -589,7 +611,7 @@ class BasicGame(mobase.IPluginGame):
path = Path(path)
# Check if we have a matching steam ID or GOG id and set the index accordingly:
# Check if we have a matching steam, GOG, Origin or EA Desktop id and set the index accordingly:
for steamid, steampath in BasicGame.steam_games.items():
if steampath == path:
self._mappings.steamAPPId.set_value(steamid)
@@ -602,6 +624,9 @@ class BasicGame(mobase.IPluginGame):
for epicid, epicpath in BasicGame.epic_games.items():
if epicpath == path:
self._mappings.epicAPPId.set_value(epicid)
for eadesktopid, eadesktoppath in BasicGame.eadesktop_games.items():
if eadesktoppath == path:
self._mappings.eaDesktopContentId.set_value(eadesktopid)
def documentsDirectory(self) -> QDir:
return self._mappings.documentsDirectory.get()
+56
View File
@@ -0,0 +1,56 @@
# -*- encoding: utf-8 -*-
import configparser
import os
import xml.etree.ElementTree as et
from pathlib import Path
from typing import Dict, List
def find_games() -> 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
user_ini, *_ = list(ea_desktop_settings_path.glob("user_*.ini"))
# 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()
config.read_string(ini_content)
install_path = Path(config.get("mod_organizer", "user.downloadinplacedir"))
for game_dir in install_path.iterdir():
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]")
game_id = content_id.text
games[game_id] = game_dir
return games
if __name__ == "__main__":
games = find_games()
for k, v in games.items():
print("Found game with id {} at {}.".format(k, v))
+1
View File
@@ -20,6 +20,7 @@ class DA2Game(BasicGame):
GameSteamId = 1238040
GameOriginManifestIds = ["OFB-EAST:59474", "DR:201797000"]
GameOriginWatcherExecutables = ("DragonAge2.exe",)
GameEaDesktopId = [70784, 1002980]
def version(self):
# Don't forget to import mobase!
+2 -1
View File
@@ -10,7 +10,7 @@ class DAOriginsGame(BasicGame):
Name = "Dragon Age Origins Support Plugin"
Author = "Patchier"
Version = "1.1.0"
Version = "1.1.1"
GameName = "Dragon Age: Origins"
GameShortName = "dragonage"
@@ -20,6 +20,7 @@ class DAOriginsGame(BasicGame):
GameSaveExtension = "das"
GameSteamId = [17450, 47810]
GameGogId = 1949616134
GameEaDesktopId = [70377, 70843]
def init(self, organizer: mobase.IOrganizer):
super().init(organizer)