1677ea762c
Using `LegendaryCore.get_game_and_dlc_list` with platform `Windows` updated the assets only for the `Windows` builds of the games missing `Win32` and `MacOS` assets on clean installs. This caused Rare to not include MacOS install options on MacOS (duh!). This might also have been the cause that users were unable to launch games, since they where only offered the `Windows` build of the games (big duh!). To fix this, fetch the assets for `Win32` and `MacOS` games before getting the final list of games and dlcs based on the `Windows` platform. In this regard, also re-use the existing options for getting metadata to give the option to the user to include them when updating assets. Also add an option to include Unreal engine assets which until now were fetched unconditionally. * Include Unreal: When the user option is `true` or debugging. Defaults to `false` * Update Win32: When the user option is `true` or debugging. Defaults to `false` * Update MacOS: Force on MacOS, when the option is `true` or debugging on other platforms. Defaults to `true` on MacOS and is disabled, `false` on others Furthermore, respect legendary's `default_platform` config option and set it in the config on new configurations. The new method in our LegendaryCore monkey allows us to use that option in RareGame when doing version checks on not installed games, and not defaulting to `Windows`. Finally, set `install_platform_fallback` to false in a new config to avoid unwanted side-effects.
183 lines
8.1 KiB
Python
183 lines
8.1 KiB
Python
import functools
|
|
import json
|
|
import logging
|
|
import os
|
|
from multiprocessing import Queue
|
|
from sys import platform as sys_platform
|
|
from uuid import uuid4
|
|
|
|
# On Windows the monkeypatching of `run_real` below doesn't work like on Linux
|
|
# This has the side effect of emitting the UIUpdate in DownloadThread complaining with a TypeError
|
|
# So import `legendary.core` and monkeypatch its imported DLManager
|
|
import legendary.core
|
|
from legendary.core import LegendaryCore as LegendaryCoreReal
|
|
from legendary.lfs.utils import delete_folder
|
|
from legendary.models.downloading import AnalysisResult
|
|
from legendary.models.egl import EGLManifest
|
|
from legendary.models.game import Game, InstalledGame
|
|
from legendary.models.manifest import ManifestMeta
|
|
|
|
from rare.lgndr.downloader.mp.manager import DLManager
|
|
from rare.lgndr.glue.exception import LgndrException, LgndrLogHandler
|
|
from rare.lgndr.lfs.lgndry import LGDLFS
|
|
|
|
legendary.core.DLManager = DLManager
|
|
legendary.core.LGDLFS = LGDLFS
|
|
|
|
|
|
# fmt: off
|
|
class LegendaryCore(LegendaryCoreReal):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(LegendaryCore, self).__init__(*args, **kwargs)
|
|
self.log.info("Using Rare's LegendaryCore monkey")
|
|
self.handler = LgndrLogHandler(logging.CRITICAL)
|
|
self.log.addHandler(self.handler)
|
|
|
|
@staticmethod
|
|
def unlock_installed(func):
|
|
@functools.wraps(func)
|
|
def unlock(self, *args, **kwargs):
|
|
self.log.debug("%s: Using unlock decorator", func.__name__)
|
|
if not self.lgd.lock_installed():
|
|
self.log.info("Data is locked, trying to forcefully release it")
|
|
# self.lgd._installed_lock.release(force=True)
|
|
try:
|
|
ret = func(self, *args, **kwargs)
|
|
except Exception as e:
|
|
raise e
|
|
finally:
|
|
self.lgd.unlock_installed()
|
|
return ret
|
|
return unlock
|
|
|
|
@property
|
|
def default_platform(self) -> str:
|
|
os_default = "Mac" if sys_platform == "darwin" else "Windows"
|
|
return self.lgd.config.get("Legendary", "default_platform", fallback=os_default)
|
|
|
|
# skip_sync defaults to false but since Rare is persistent, skip by default
|
|
# def get_installed_game(self, app_name, skip_sync=True) -> InstalledGame:
|
|
# return super(LegendaryCore, self).get_installed_game(app_name, skip_sync)
|
|
|
|
def prepare_download(self, game: Game, base_game: Game = None, base_path: str = '',
|
|
status_q: Queue = None, max_shm: int = 0, max_workers: int = 0,
|
|
force: bool = False, disable_patching: bool = False,
|
|
game_folder: str = '', override_manifest: str = '',
|
|
override_old_manifest: str = '', override_base_url: str = '',
|
|
platform: str = 'Windows', file_prefix_filter: list = None,
|
|
file_exclude_filter: list = None, file_install_tag: list = None,
|
|
dl_optimizations: bool = False, dl_timeout: int = 10,
|
|
repair: bool = False, repair_use_latest: bool = False,
|
|
disable_delta: bool = False, override_delta_manifest: str = '',
|
|
egl_guid: str = '', preferred_cdn: str = None,
|
|
disable_https: bool = False, bind_ip: str = None) -> (DLManager, AnalysisResult, ManifestMeta):
|
|
dlm, analysis, igame = super(LegendaryCore, self).prepare_download(
|
|
game=game, base_game=base_game, base_path=base_path,
|
|
status_q=status_q, max_shm=max_shm, max_workers=max_workers,
|
|
force=force, disable_patching=disable_patching,
|
|
game_folder=game_folder, override_manifest=override_manifest,
|
|
override_old_manifest=override_old_manifest, override_base_url=override_base_url,
|
|
platform=platform, file_prefix_filter=file_prefix_filter,
|
|
file_exclude_filter=file_exclude_filter, file_install_tag=file_install_tag,
|
|
dl_optimizations=dl_optimizations, dl_timeout=dl_timeout,
|
|
repair=repair, repair_use_latest=repair_use_latest,
|
|
disable_delta=disable_delta, override_delta_manifest=override_delta_manifest,
|
|
egl_guid=egl_guid, preferred_cdn=preferred_cdn,
|
|
disable_https=disable_https, bind_ip=bind_ip,
|
|
)
|
|
# lk: monkeypatch run_real (the method that emits the stats) into DLManager
|
|
# pylint: disable=E1111
|
|
dlm.run_real = DLManager.run_real.__get__(dlm, DLManager)
|
|
# lk: set the queue for reporting statistics back the UI
|
|
dlm.status_queue = Queue()
|
|
# lk: set the queue to send control signals to the DLManager
|
|
# lk: this doesn't exist in the original class, but it is monkeypatched in
|
|
dlm.signals_queue = Queue()
|
|
return dlm, analysis, igame
|
|
|
|
def uninstall_game(self, installed_game: InstalledGame, delete_files=True, delete_root_directory=False):
|
|
try:
|
|
super(LegendaryCore, self).uninstall_game(installed_game, delete_files, delete_root_directory)
|
|
except Exception as e:
|
|
raise e
|
|
finally:
|
|
pass
|
|
|
|
@unlock_installed.__func__
|
|
def egl_import(self, app_name):
|
|
try:
|
|
super(LegendaryCore, self).egl_import(app_name)
|
|
except LgndrException as ret:
|
|
raise ret
|
|
finally:
|
|
pass
|
|
|
|
def egstore_write(self, app_name):
|
|
self.log.debug(f'Exporting ".egstore" for "{app_name}"')
|
|
# load igame/game
|
|
lgd_game = self.get_game(app_name)
|
|
lgd_igame = self._get_installed_game(app_name)
|
|
manifest_data, _ = self.get_installed_manifest(app_name)
|
|
if not manifest_data:
|
|
self.log.error(f'Game Manifest for "{app_name}" not found, cannot export!')
|
|
return
|
|
|
|
# create guid if it's not set already
|
|
if not lgd_igame.egl_guid:
|
|
lgd_igame.egl_guid = str(uuid4()).replace('-', '').upper()
|
|
_ = self._install_game(lgd_igame)
|
|
# convert to egl manifest
|
|
egl_game = EGLManifest.from_lgd_game(lgd_game, lgd_igame)
|
|
|
|
# make sure .egstore folder exists
|
|
egstore_folder = os.path.join(lgd_igame.install_path, '.egstore')
|
|
if not os.path.exists(egstore_folder):
|
|
os.makedirs(egstore_folder)
|
|
|
|
# copy manifest and create mancpn file in .egstore folder
|
|
with open(os.path.join(egstore_folder, f'{egl_game.installation_guid}.manifest', ), 'wb') as mf:
|
|
mf.write(manifest_data)
|
|
|
|
mancpn = dict(FormatVersion=0, AppName=app_name,
|
|
CatalogItemId=lgd_game.catalog_item_id,
|
|
CatalogNamespace=lgd_game.namespace)
|
|
with open(os.path.join(egstore_folder, f'{egl_game.installation_guid}.mancpn', ), 'w') as mcpnf:
|
|
json.dump(mancpn, mcpnf, indent=4, sort_keys=True)
|
|
|
|
def egstore_delete(self, igame: InstalledGame, delete_files=True):
|
|
self.log.debug(f'Removing ".egstore" for "{igame.app_name}"')
|
|
if delete_files:
|
|
delete_folder(os.path.join(igame.install_path, '.egstore'))
|
|
|
|
@unlock_installed.__func__
|
|
def egl_export(self, app_name):
|
|
try:
|
|
super(LegendaryCore, self).egl_export(app_name)
|
|
except LgndrException as ret:
|
|
raise ret
|
|
finally:
|
|
pass
|
|
|
|
@unlock_installed.__func__
|
|
def egl_sync(self, app_name=''):
|
|
try:
|
|
super(LegendaryCore, self).egl_sync(app_name)
|
|
except LgndrException as ret:
|
|
raise ret
|
|
finally:
|
|
pass
|
|
|
|
def prepare_overlay_install(self, path=None):
|
|
dlm, analysis_result, igame = super(LegendaryCore, self).prepare_overlay_install(path)
|
|
# lk: monkeypatch status_q (the queue for download stats)
|
|
# pylint: disable=E1111
|
|
dlm.run_real = DLManager.run_real.__get__(dlm, DLManager)
|
|
# lk: set the queue for reporting statistics back the UI
|
|
dlm.status_queue = Queue()
|
|
# lk: set the queue to send control signals to the DLManager
|
|
# lk: this doesn't exist in the original class, but it is monkeypatched in
|
|
dlm.signals_queue = Queue()
|
|
return dlm, analysis_result, igame
|
|
|
|
# fmt: on
|