2023-11-30 23:52:24 +13:00
|
|
|
import functools
|
2023-03-16 04:59:33 +13:00
|
|
|
import json
|
2023-12-11 00:47:07 +13:00
|
|
|
import logging
|
2023-03-16 04:59:33 +13:00
|
|
|
import os
|
2022-04-19 02:51:51 +12:00
|
|
|
from multiprocessing import Queue
|
2023-12-16 03:57:32 +13:00
|
|
|
from sys import platform as sys_platform
|
2023-03-16 04:59:33 +13:00
|
|
|
from uuid import uuid4
|
2022-04-19 02:51:51 +12:00
|
|
|
|
2022-08-30 11:33:08 +12:00
|
|
|
# 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
|
2022-07-21 08:33:44 +12:00
|
|
|
from legendary.core import LegendaryCore as LegendaryCoreReal
|
2023-03-16 04:59:33 +13:00
|
|
|
from legendary.lfs.utils import delete_folder
|
2022-07-08 05:19:52 +12:00
|
|
|
from legendary.models.downloading import AnalysisResult
|
2023-03-16 04:59:33 +13:00
|
|
|
from legendary.models.egl import EGLManifest
|
2022-07-16 06:16:12 +12:00
|
|
|
from legendary.models.game import Game, InstalledGame
|
2022-07-08 05:19:52 +12:00
|
|
|
from legendary.models.manifest import ManifestMeta
|
2022-06-26 10:27:18 +12:00
|
|
|
|
2022-10-27 14:00:48 +13:00
|
|
|
from rare.lgndr.downloader.mp.manager import DLManager
|
2023-12-11 00:47:07 +13:00
|
|
|
from rare.lgndr.glue.exception import LgndrException, LgndrLogHandler
|
2023-12-16 03:57:32 +13:00
|
|
|
from rare.lgndr.lfs.lgndry import LGDLFS
|
2022-07-08 05:19:52 +12:00
|
|
|
|
2022-08-30 11:19:11 +12:00
|
|
|
legendary.core.DLManager = DLManager
|
2023-12-14 02:00:36 +13:00
|
|
|
legendary.core.LGDLFS = LGDLFS
|
2022-07-11 20:41:22 +12:00
|
|
|
|
2022-08-03 11:33:50 +12:00
|
|
|
|
|
|
|
# fmt: off
|
2022-07-21 08:33:44 +12:00
|
|
|
class LegendaryCore(LegendaryCoreReal):
|
2022-04-19 02:51:51 +12:00
|
|
|
|
2023-12-14 02:00:36 +13:00
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super(LegendaryCore, self).__init__(*args, **kwargs)
|
|
|
|
self.log.info("Using Rare's LegendaryCore monkey")
|
2023-12-23 07:58:36 +13:00
|
|
|
self.log.info("Using config in %s", self.lgd.path)
|
2023-12-11 00:47:07 +13:00
|
|
|
self.handler = LgndrLogHandler(logging.CRITICAL)
|
2022-07-08 05:19:52 +12:00
|
|
|
self.log.addHandler(self.handler)
|
|
|
|
|
2023-11-30 23:52:24 +13:00
|
|
|
@staticmethod
|
|
|
|
def unlock_installed(func):
|
|
|
|
@functools.wraps(func)
|
|
|
|
def unlock(self, *args, **kwargs):
|
2023-12-14 02:53:17 +13:00
|
|
|
self.log.debug("%s: Using unlock decorator", func.__name__)
|
2023-12-11 09:46:02 +13:00
|
|
|
if not self.lgd.lock_installed():
|
2023-12-14 02:53:17 +13:00
|
|
|
self.log.info("Data is locked, trying to forcefully release it")
|
2023-12-11 09:46:02 +13:00
|
|
|
# self.lgd._installed_lock.release(force=True)
|
2023-12-11 00:47:07 +13:00
|
|
|
try:
|
|
|
|
ret = func(self, *args, **kwargs)
|
|
|
|
except Exception as e:
|
|
|
|
raise e
|
|
|
|
finally:
|
2023-12-14 02:00:36 +13:00
|
|
|
self.lgd.unlock_installed()
|
2023-11-30 23:52:24 +13:00
|
|
|
return ret
|
|
|
|
return unlock
|
|
|
|
|
2023-12-16 03:57:32 +13:00
|
|
|
@property
|
|
|
|
def default_platform(self) -> str:
|
|
|
|
os_default = "Mac" if sys_platform == "darwin" else "Windows"
|
2023-12-17 02:36:38 +13:00
|
|
|
usr_platform = self.lgd.config.get("Legendary", "default_platform", fallback=os_default)
|
|
|
|
return usr_platform if usr_platform in ("Windows", "Win32", "Mac") else os_default
|
2023-12-16 03:57:32 +13:00
|
|
|
|
2023-12-17 03:23:58 +13:00
|
|
|
def update_check_enabled(self):
|
2023-12-19 22:46:13 +13:00
|
|
|
return True
|
2023-12-17 03:23:58 +13:00
|
|
|
|
|
|
|
def update_notice_enabled(self):
|
|
|
|
return False
|
|
|
|
|
2022-07-30 20:19:06 +12:00
|
|
|
# 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)
|
|
|
|
|
2022-07-08 05:19:52 +12:00
|
|
|
def prepare_download(self, game: Game, base_game: Game = None, base_path: str = '',
|
2022-04-19 02:51:51 +12:00
|
|
|
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,
|
2023-12-10 23:09:26 +13:00
|
|
|
disable_https: bool = False, bind_ip: str = None) -> (DLManager, AnalysisResult, ManifestMeta):
|
2022-07-21 08:33:44 +12:00
|
|
|
dlm, analysis, igame = super(LegendaryCore, self).prepare_download(
|
2022-07-11 20:41:22 +12:00
|
|
|
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,
|
2023-12-10 23:09:26 +13:00
|
|
|
disable_https=disable_https, bind_ip=bind_ip,
|
2022-07-11 20:41:22 +12:00
|
|
|
)
|
2022-07-21 08:33:44 +12:00
|
|
|
# lk: monkeypatch run_real (the method that emits the stats) into DLManager
|
2022-08-30 11:33:08 +12:00
|
|
|
# pylint: disable=E1111
|
2022-07-21 08:33:44 +12:00
|
|
|
dlm.run_real = DLManager.run_real.__get__(dlm, DLManager)
|
2022-08-03 11:33:50 +12:00
|
|
|
# lk: set the queue for reporting statistics back the UI
|
2022-07-24 10:06:35 +12:00
|
|
|
dlm.status_queue = Queue()
|
2022-08-03 11:33:50 +12:00
|
|
|
# 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()
|
2022-07-21 08:33:44 +12:00
|
|
|
return dlm, analysis, igame
|
2022-07-16 06:16:12 +12:00
|
|
|
|
|
|
|
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
|
2022-06-26 06:51:32 +12:00
|
|
|
|
2023-12-14 02:04:22 +13:00
|
|
|
@unlock_installed.__func__
|
2022-06-26 06:51:32 +12:00
|
|
|
def egl_import(self, app_name):
|
2022-06-26 23:30:17 +12:00
|
|
|
try:
|
|
|
|
super(LegendaryCore, self).egl_import(app_name)
|
|
|
|
except LgndrException as ret:
|
|
|
|
raise ret
|
|
|
|
finally:
|
2022-07-08 05:19:52 +12:00
|
|
|
pass
|
2022-06-26 06:51:32 +12:00
|
|
|
|
2023-03-16 04:59:33 +13:00
|
|
|
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'))
|
|
|
|
|
2023-12-14 02:04:22 +13:00
|
|
|
@unlock_installed.__func__
|
2022-06-26 06:51:32 +12:00
|
|
|
def egl_export(self, app_name):
|
2022-06-26 23:30:17 +12:00
|
|
|
try:
|
|
|
|
super(LegendaryCore, self).egl_export(app_name)
|
|
|
|
except LgndrException as ret:
|
|
|
|
raise ret
|
|
|
|
finally:
|
2022-07-08 05:19:52 +12:00
|
|
|
pass
|
2022-06-26 06:51:32 +12:00
|
|
|
|
2023-12-14 02:04:22 +13:00
|
|
|
@unlock_installed.__func__
|
2023-12-14 02:00:36 +13:00
|
|
|
def egl_sync(self, app_name=''):
|
|
|
|
try:
|
|
|
|
super(LegendaryCore, self).egl_sync(app_name)
|
|
|
|
except LgndrException as ret:
|
|
|
|
raise ret
|
|
|
|
finally:
|
|
|
|
pass
|
|
|
|
|
2022-07-24 10:06:35 +12:00
|
|
|
def prepare_overlay_install(self, path=None):
|
2022-07-11 20:41:22 +12:00
|
|
|
dlm, analysis_result, igame = super(LegendaryCore, self).prepare_overlay_install(path)
|
|
|
|
# lk: monkeypatch status_q (the queue for download stats)
|
2022-08-30 11:33:08 +12:00
|
|
|
# pylint: disable=E1111
|
2022-07-21 08:33:44 +12:00
|
|
|
dlm.run_real = DLManager.run_real.__get__(dlm, DLManager)
|
2022-08-03 11:33:50 +12:00
|
|
|
# lk: set the queue for reporting statistics back the UI
|
2022-07-24 10:06:35 +12:00
|
|
|
dlm.status_queue = Queue()
|
2022-08-03 11:33:50 +12:00
|
|
|
# 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()
|
2022-07-11 20:41:22 +12:00
|
|
|
return dlm, analysis_result, igame
|
2022-07-16 06:16:12 +12:00
|
|
|
|
2022-08-03 11:33:50 +12:00
|
|
|
# fmt: on
|