147 lines
6.2 KiB
Python
147 lines
6.2 KiB
Python
import os
|
|
import platform
|
|
import time
|
|
from configparser import ConfigParser
|
|
from logging import getLogger
|
|
from typing import Union, Iterable, Mapping, List
|
|
|
|
from PyQt5.QtCore import pyqtSignal, QObject, QRunnable
|
|
|
|
from rare.lgndr.core import LegendaryCore
|
|
from rare.models.game import RareGame
|
|
from rare.models.pathspec import PathSpec
|
|
from rare.utils import config_helper as config
|
|
from rare.utils.misc import path_size, format_size
|
|
from .worker import Worker
|
|
|
|
if platform.system() == "Windows":
|
|
# noinspection PyUnresolvedReferences
|
|
import winreg # pylint: disable=E0401
|
|
from legendary.lfs import windows_helpers
|
|
else:
|
|
from rare.utils import runners
|
|
|
|
logger = getLogger("WineResolver")
|
|
|
|
|
|
class WinePathResolver(Worker):
|
|
class Signals(QObject):
|
|
result_ready = pyqtSignal(str, str)
|
|
|
|
def __init__(self, command: List[str], environ: Mapping, path: str):
|
|
super(WinePathResolver, self). __init__()
|
|
self.signals = WinePathResolver.Signals()
|
|
self.command = command
|
|
self.environ = environ
|
|
self.path = path
|
|
|
|
@staticmethod
|
|
def _resolve_unix_path(cmd, env, path: str) -> str:
|
|
if "waitforexitandrun" in cmd:
|
|
cmd[cmd.index("waitforexitandrun")] = "runinprefix"
|
|
logger.info("Resolving path '%s'", path)
|
|
wine_path = runners.resolve_path(cmd, env, path)
|
|
logger.debug("Resolved Wine path '%s'", path)
|
|
unix_path = runners.convert_to_unix_path(cmd, env, wine_path)
|
|
logger.debug("Resolved Unix path '%s'", unix_path)
|
|
return unix_path
|
|
|
|
def run_real(self):
|
|
path = self._resolve_unix_path(self.command, self.environ, self.path)
|
|
self.signals.result_ready.emit(path, "default")
|
|
return
|
|
|
|
|
|
class WineSavePathResolver(WinePathResolver):
|
|
|
|
def __init__(self, core: LegendaryCore, rgame: RareGame):
|
|
cmd = core.get_app_launch_command(rgame.app_name, disable_wine=config.get_boolean(rgame.app_name, "no_wine"))
|
|
env = core.get_app_environment(rgame.app_name, disable_wine=config.get_boolean(rgame.app_name, "no_wine"))
|
|
env = runners.get_environment(env, silent=True)
|
|
path = PathSpec(core, rgame.igame).resolve_egl_path_vars(rgame.raw_save_path)
|
|
if not (cmd and env and path):
|
|
raise RuntimeError(f"Cannot setup {type(self).__name__}, missing infomation")
|
|
super(WineSavePathResolver, self).__init__(cmd, env, path)
|
|
self.rgame = rgame
|
|
|
|
def run_real(self):
|
|
logger.info("Resolving save path for %s (%s)", self.rgame.app_title, self.rgame.app_name)
|
|
path = self._resolve_unix_path(self.command, self.environ, self.path)
|
|
# Clean wine output
|
|
# pylint: disable=E1136
|
|
if os.path.exists(path):
|
|
self.rgame.save_path = path
|
|
self.signals.result_ready.emit(path, self.rgame.app_name)
|
|
return
|
|
|
|
|
|
class OriginWineWorker(QRunnable):
|
|
def __init__(self, core: LegendaryCore, games: Union[Iterable[RareGame], RareGame]):
|
|
super(OriginWineWorker, self).__init__()
|
|
self.__cache: dict[str, ConfigParser] = {}
|
|
self.core = core
|
|
self.games = [games] if isinstance(games, RareGame) else games
|
|
|
|
def run(self) -> None:
|
|
t = time.time()
|
|
|
|
for rgame in self.games:
|
|
|
|
reg_path: str = rgame.game.metadata \
|
|
.get("customAttributes", {}) \
|
|
.get("RegistryPath", {}).get("value", None)
|
|
if not reg_path:
|
|
continue
|
|
|
|
reg_key: str = rgame.game.metadata \
|
|
.get("customAttributes", {}) \
|
|
.get("RegistryKey", {}).get("value", None)
|
|
if not reg_key:
|
|
continue
|
|
|
|
if platform.system() == "Windows":
|
|
install_dir = windows_helpers.query_registry_value(winreg.HKEY_LOCAL_MACHINE, reg_path, reg_key)
|
|
else:
|
|
command = self.core.get_app_launch_command(rgame.app_name)
|
|
environ = self.core.get_app_environment(rgame.app_name)
|
|
environ = runners.get_environment(environ, silent=True)
|
|
|
|
prefix = config.get_prefix(rgame.app_name)
|
|
if not prefix:
|
|
return
|
|
|
|
use_wine = False
|
|
if not use_wine:
|
|
# lk: this is the original way of getting the path by parsing "system.reg"
|
|
reg = self.__cache.get(prefix, None) or runners.read_registry("system.reg", prefix)
|
|
self.__cache[prefix] = reg
|
|
|
|
reg_path = reg_path.replace("SOFTWARE", "Software").replace("WOW6432Node", "Wow6432Node")
|
|
# lk: split and rejoin the registry path to avoid slash expansion
|
|
reg_path = "\\\\".join([x for x in reg_path.split("\\") if bool(x)])
|
|
|
|
install_dir = reg.get(reg_path, f'"{reg_key}"', fallback=None)
|
|
else:
|
|
# lk: this is the alternative way of getting the path by using wine itself
|
|
install_dir = runners.query_reg_key(command, environ, f"HKLM\\{reg_path}", reg_key)
|
|
|
|
if install_dir:
|
|
logger.debug("Found Wine install directory %s", install_dir)
|
|
install_dir = runners.convert_to_unix_path(command, environ, install_dir)
|
|
if install_dir:
|
|
logger.debug("Found Unix install directory %s", install_dir)
|
|
else:
|
|
logger.info("Could not find Unix install directory for %s", rgame.app_title)
|
|
else:
|
|
logger.info("Could not find Wine install directory for %s", rgame.app_title)
|
|
|
|
if install_dir:
|
|
if os.path.isdir(install_dir):
|
|
install_size = path_size(install_dir)
|
|
rgame.set_origin_attributes(install_dir, install_size)
|
|
logger.info("Origin game %s (%s, %s)", rgame.app_title, install_dir, format_size(install_size))
|
|
else:
|
|
logger.warning("Origin game %s (%s does not exist)", rgame.app_title, install_dir)
|
|
else:
|
|
logger.info("Origin game %s is not installed", rgame.app_title)
|
|
logger.info("Origin worker finished in %ss", time.time() - t)
|