2021-02-10 23:48:25 +13:00
|
|
|
import os
|
2021-06-21 07:55:31 +12:00
|
|
|
import platform
|
2021-03-09 08:36:42 +13:00
|
|
|
from logging import getLogger
|
2021-02-10 23:48:25 +13:00
|
|
|
|
2022-04-01 08:29:31 +13:00
|
|
|
from PyQt5.QtCore import pyqtSignal, QCoreApplication, QObject, QRunnable, QStandardPaths
|
2021-09-07 07:10:18 +12:00
|
|
|
from legendary.core import LegendaryCore
|
2022-07-02 03:42:41 +12:00
|
|
|
|
2022-07-11 04:32:42 +12:00
|
|
|
from rare.lgndr.api_arguments import LgndrVerifyGameArgs
|
|
|
|
from rare.lgndr.api_exception import LgndrException
|
2022-07-08 05:19:52 +12:00
|
|
|
from rare.shared import LegendaryCLISingleton, LegendaryCoreSingleton
|
2022-02-06 02:48:50 +13:00
|
|
|
from rare.utils import config_helper
|
2021-02-10 23:48:25 +13:00
|
|
|
|
2021-03-09 08:36:42 +13:00
|
|
|
logger = getLogger("Legendary Utils")
|
|
|
|
|
2021-02-10 23:48:25 +13:00
|
|
|
|
2021-05-01 21:17:18 +12:00
|
|
|
def uninstall(app_name: str, core: LegendaryCore, options=None):
|
2021-04-06 21:00:13 +12:00
|
|
|
if not options:
|
|
|
|
options = {"keep_files": False}
|
2021-03-09 08:36:42 +13:00
|
|
|
igame = core.get_installed_game(app_name)
|
2021-05-01 21:17:18 +12:00
|
|
|
|
|
|
|
# remove shortcuts link
|
2022-04-01 08:29:31 +13:00
|
|
|
desktop = QStandardPaths.writableLocation(QStandardPaths.DesktopLocation)
|
|
|
|
applications = QStandardPaths.writableLocation(QStandardPaths.ApplicationsLocation)
|
2021-06-21 07:55:31 +12:00
|
|
|
if platform.system() == "Linux":
|
2022-04-01 08:29:31 +13:00
|
|
|
desktop_shortcut = os.path.join(desktop, f"{igame.title}.desktop")
|
|
|
|
if os.path.exists(desktop_shortcut):
|
|
|
|
os.remove(desktop_shortcut)
|
|
|
|
|
|
|
|
applications_shortcut = os.path.join(applications, f"{igame.title}.desktop")
|
|
|
|
if os.path.exists(applications_shortcut):
|
|
|
|
os.remove(applications_shortcut)
|
2021-05-01 21:17:18 +12:00
|
|
|
|
2021-06-21 07:55:31 +12:00
|
|
|
elif platform.system() == "Windows":
|
2022-04-01 08:29:31 +13:00
|
|
|
game_title = igame.title.split(":")[0]
|
|
|
|
desktop_shortcut = os.path.join(desktop, f"{game_title}.lnk")
|
|
|
|
if os.path.exists(desktop_shortcut):
|
|
|
|
os.remove(desktop_shortcut)
|
|
|
|
|
|
|
|
start_menu_shortcut = os.path.join(applications, "..", f"{game_title}.lnk")
|
|
|
|
if os.path.exists(start_menu_shortcut):
|
|
|
|
os.remove(start_menu_shortcut)
|
2021-05-12 21:45:10 +12:00
|
|
|
|
2021-03-09 08:36:42 +13:00
|
|
|
try:
|
|
|
|
# Remove DLC first so directory is empty when game uninstall runs
|
|
|
|
dlcs = core.get_dlc_for_game(app_name)
|
|
|
|
for dlc in dlcs:
|
|
|
|
if (idlc := core.get_installed_game(dlc.app_name)) is not None:
|
|
|
|
logger.info(f'Uninstalling DLC "{dlc.app_name}"...')
|
2021-04-06 21:00:13 +12:00
|
|
|
core.uninstall_game(idlc, delete_files=not options["keep_files"])
|
2021-03-09 08:36:42 +13:00
|
|
|
|
|
|
|
logger.info(f'Removing "{igame.title}" from "{igame.install_path}"...')
|
2021-12-24 22:09:50 +13:00
|
|
|
core.uninstall_game(
|
|
|
|
igame, delete_files=not options["keep_files"], delete_root_directory=True
|
|
|
|
)
|
|
|
|
logger.info("Game has been uninstalled.")
|
2021-03-09 08:36:42 +13:00
|
|
|
|
|
|
|
except Exception as e:
|
2021-12-24 22:09:50 +13:00
|
|
|
logger.warning(
|
|
|
|
f"Removing game failed: {e!r}, please remove {igame.install_path} manually."
|
|
|
|
)
|
2022-07-02 03:42:41 +12:00
|
|
|
if not options["keep_files"]:
|
|
|
|
logger.info("Removing sections in config file")
|
|
|
|
config_helper.remove_section(app_name)
|
|
|
|
config_helper.remove_section(f"{app_name}.env")
|
2021-03-10 04:52:51 +13:00
|
|
|
|
2022-07-02 03:42:41 +12:00
|
|
|
config_helper.save_config()
|
2021-08-31 01:14:40 +12:00
|
|
|
|
2021-03-10 04:52:51 +13:00
|
|
|
|
2021-11-10 09:31:25 +13:00
|
|
|
def update_manifest(app_name: str, core: LegendaryCore):
|
|
|
|
game = core.get_game(app_name)
|
2022-02-02 10:29:34 +13:00
|
|
|
logger.info(f"Reloading game manifest of {game.app_title}")
|
2021-11-10 09:31:25 +13:00
|
|
|
new_manifest_data, base_urls = core.get_cdn_manifest(game)
|
|
|
|
# overwrite base urls in metadata with current ones to avoid using old/dead CDNs
|
|
|
|
game.base_urls = base_urls
|
|
|
|
# save base urls to game metadata
|
|
|
|
core.lgd.set_game_meta(game.app_name, game)
|
|
|
|
|
|
|
|
new_manifest = core.load_manifest(new_manifest_data)
|
2021-12-24 22:09:50 +13:00
|
|
|
logger.debug(f"Base urls: {base_urls}")
|
2021-11-10 09:31:25 +13:00
|
|
|
# save manifest with version name as well for testing/downgrading/etc.
|
2021-12-24 22:09:50 +13:00
|
|
|
core.lgd.save_manifest(
|
|
|
|
game.app_name, new_manifest_data, version=new_manifest.meta.build_version
|
|
|
|
)
|
2021-11-10 09:31:25 +13:00
|
|
|
|
|
|
|
|
2021-11-11 10:07:28 +13:00
|
|
|
class VerifyWorker(QRunnable):
|
2022-07-08 05:19:52 +12:00
|
|
|
class Signals(QObject):
|
2022-07-11 04:32:42 +12:00
|
|
|
status = pyqtSignal(str, int, int, float, float)
|
|
|
|
result = pyqtSignal(str, bool, int, int)
|
|
|
|
error = pyqtSignal(str, str)
|
2022-07-08 05:19:52 +12:00
|
|
|
|
2021-11-23 08:09:05 +13:00
|
|
|
num: int = 0
|
|
|
|
total: int = 1 # set default to 1 to avoid DivisionByZero before it is initialized
|
|
|
|
|
2022-01-16 04:40:31 +13:00
|
|
|
def __init__(self, app_name):
|
2021-11-11 10:07:28 +13:00
|
|
|
super(VerifyWorker, self).__init__()
|
2022-07-08 05:19:52 +12:00
|
|
|
self.signals = VerifyWorker.Signals()
|
2021-11-11 10:07:28 +13:00
|
|
|
self.setAutoDelete(True)
|
2022-07-08 05:19:52 +12:00
|
|
|
self.cli = LegendaryCLISingleton()
|
2022-02-26 06:43:27 +13:00
|
|
|
self.core = LegendaryCoreSingleton()
|
|
|
|
self.app_name = app_name
|
2021-03-10 04:52:51 +13:00
|
|
|
|
2022-07-08 05:19:52 +12:00
|
|
|
def status_callback(self, num: int, total: int, percentage: float, speed: float):
|
2022-07-11 04:32:42 +12:00
|
|
|
self.signals.status.emit(self.app_name, num, total, percentage, speed)
|
2022-07-08 05:19:52 +12:00
|
|
|
|
2021-03-10 04:52:51 +13:00
|
|
|
def run(self):
|
2022-07-11 04:32:42 +12:00
|
|
|
args = LgndrVerifyGameArgs(app_name=self.app_name,
|
|
|
|
verify_stdout=self.status_callback)
|
2022-07-08 05:19:52 +12:00
|
|
|
try:
|
|
|
|
# TODO: offer this as an alternative when manifest doesn't exist
|
|
|
|
# TODO: requires the client to be online. To do it this way, we need to
|
|
|
|
# TODO: somehow detect the error and offer a dialog in which case `verify_games` is
|
|
|
|
# TODO: re-run with `repair_mode` and `repair_online`
|
2022-07-11 04:32:42 +12:00
|
|
|
success, failed, missing = self.cli.verify_game(
|
|
|
|
args, print_command=False, repair_mode=True, repair_online=True)
|
|
|
|
# success, failed, missing = self.cli.verify_game(args, print_command=False)
|
|
|
|
self.signals.result.emit(self.app_name, success, failed, missing)
|
2022-07-08 05:19:52 +12:00
|
|
|
except LgndrException as ret:
|
2022-07-11 04:32:42 +12:00
|
|
|
self.signals.error.emit(self.app_name, ret.message)
|
2021-03-10 04:52:51 +13:00
|
|
|
|
|
|
|
|
2022-05-05 08:11:41 +12:00
|
|
|
# FIXME: lk: ah ef me sideways, we can't even import this thing properly
|
|
|
|
# FIXME: lk: so copy it here
|
|
|
|
def resolve_aliases(core: LegendaryCore, name):
|
|
|
|
# make sure aliases exist if not yet created
|
|
|
|
core.update_aliases(force=False)
|
|
|
|
name = name.strip()
|
|
|
|
# resolve alias (if any) to real app name
|
|
|
|
return core.lgd.config.get(
|
|
|
|
section='Legendary.aliases', option=name,
|
|
|
|
fallback=core.lgd.aliases.get(name.lower(), name)
|
|
|
|
)
|
|
|
|
|