2022-12-24 09:49:27 +13:00
|
|
|
import os
|
2023-02-02 09:40:46 +13:00
|
|
|
import platform
|
2023-02-02 01:17:51 +13:00
|
|
|
import time
|
|
|
|
from configparser import ConfigParser
|
2023-01-26 00:04:21 +13:00
|
|
|
from logging import getLogger
|
2024-02-13 05:30:23 +13:00
|
|
|
from typing import Union, Iterable, List, Tuple, Dict
|
2022-12-24 09:49:27 +13:00
|
|
|
|
2024-02-13 05:30:23 +13:00
|
|
|
from PyQt5.QtCore import pyqtSignal, QObject
|
2022-12-24 09:49:27 +13:00
|
|
|
|
2022-12-27 04:26:33 +13:00
|
|
|
from rare.lgndr.core import LegendaryCore
|
2023-02-02 01:17:51 +13:00
|
|
|
from rare.models.game import RareGame
|
2022-12-24 09:49:27 +13:00
|
|
|
from rare.models.pathspec import PathSpec
|
2024-02-13 05:30:23 +13:00
|
|
|
from rare.shared.wrappers import Wrappers
|
2023-12-24 09:21:13 +13:00
|
|
|
from rare.utils import config_helper as config
|
2023-03-10 23:27:13 +13:00
|
|
|
from rare.utils.misc import path_size, format_size
|
2023-02-01 00:59:22 +13:00
|
|
|
from .worker import Worker
|
2022-12-24 09:49:27 +13:00
|
|
|
|
2023-02-02 09:40:46 +13:00
|
|
|
if platform.system() == "Windows":
|
|
|
|
# noinspection PyUnresolvedReferences
|
2023-09-20 10:39:15 +12:00
|
|
|
import winreg # pylint: disable=E0401
|
2023-02-02 09:40:46 +13:00
|
|
|
from legendary.lfs import windows_helpers
|
2023-12-24 09:21:13 +13:00
|
|
|
else:
|
2024-02-18 23:44:04 +13:00
|
|
|
from rare.utils.compat import utils as compat_utils, steam
|
2023-02-02 09:40:46 +13:00
|
|
|
|
2023-01-26 00:04:21 +13:00
|
|
|
logger = getLogger("WineResolver")
|
|
|
|
|
2022-12-24 09:49:27 +13:00
|
|
|
|
2023-09-20 10:39:15 +12:00
|
|
|
class WinePathResolver(Worker):
|
2022-12-24 09:49:27 +13:00
|
|
|
class Signals(QObject):
|
2023-09-20 10:39:15 +12:00
|
|
|
result_ready = pyqtSignal(str, str)
|
|
|
|
|
2024-02-13 05:30:23 +13:00
|
|
|
def __init__(self, core: LegendaryCore, app_name: str, path: str):
|
2023-09-20 10:39:15 +12:00
|
|
|
super(WinePathResolver, self). __init__()
|
|
|
|
self.signals = WinePathResolver.Signals()
|
2024-02-13 05:30:23 +13:00
|
|
|
self.core = core
|
|
|
|
self.app_name = app_name
|
2023-09-20 10:39:15 +12:00
|
|
|
self.path = path
|
|
|
|
|
2024-02-13 05:30:23 +13:00
|
|
|
@staticmethod
|
|
|
|
def _configure_process(core: LegendaryCore, app_name: str) -> Tuple[List, Dict]:
|
2024-02-18 23:44:04 +13:00
|
|
|
tool: steam.CompatibilityTool = None
|
2024-02-13 05:30:23 +13:00
|
|
|
|
|
|
|
if config.get_boolean(app_name, "no_wine"):
|
|
|
|
wrappers = Wrappers()
|
|
|
|
for w in wrappers.get_game_wrapper_list(app_name):
|
|
|
|
if w.is_compat_tool:
|
2024-02-18 23:44:04 +13:00
|
|
|
for t in steam.find_tools():
|
2024-02-13 05:30:23 +13:00
|
|
|
if t.checksum == w.checksum:
|
|
|
|
tool = t
|
|
|
|
break
|
|
|
|
|
|
|
|
cmd = core.get_app_launch_command(
|
|
|
|
app_name,
|
2024-02-18 23:44:04 +13:00
|
|
|
wrapper=tool.as_str(steam.SteamVerb.RUN_IN_PREFIX) if tool is not None else None,
|
2024-02-13 05:30:23 +13:00
|
|
|
disable_wine=config.get_boolean(app_name, "no_wine")
|
|
|
|
)
|
|
|
|
env = core.get_app_environment(app_name, disable_wine=config.get_boolean(app_name, "no_wine"))
|
|
|
|
env = compat_utils.get_host_environment(env, silent=True)
|
|
|
|
|
|
|
|
return cmd, env
|
|
|
|
|
2023-09-20 10:39:15 +12:00
|
|
|
@staticmethod
|
|
|
|
def _resolve_unix_path(cmd, env, path: str) -> str:
|
|
|
|
logger.info("Resolving path '%s'", path)
|
2024-02-13 05:30:23 +13:00
|
|
|
wine_path = compat_utils.resolve_path(cmd, env, path)
|
|
|
|
logger.info("Resolved Wine path '%s'", wine_path)
|
|
|
|
unix_path = compat_utils.convert_to_unix_path(cmd, env, wine_path)
|
|
|
|
logger.info("Resolved Unix path '%s'", unix_path)
|
2023-09-20 10:39:15 +12:00
|
|
|
return unix_path
|
|
|
|
|
|
|
|
def run_real(self):
|
2024-02-13 05:30:23 +13:00
|
|
|
command, environ = self._configure_process(self.core, self.app_name)
|
|
|
|
if not (command and environ):
|
|
|
|
logger.error("Cannot setup %s, missing infomation", {type(self).__name__})
|
|
|
|
self.signals.result_ready.emit("", self.app_name)
|
|
|
|
|
|
|
|
path = self._resolve_unix_path(command, environ, self.path)
|
|
|
|
self.signals.result_ready.emit(path, self.app_name)
|
2023-09-20 10:39:15 +12:00
|
|
|
return
|
2022-12-24 09:49:27 +13:00
|
|
|
|
2023-09-20 10:39:15 +12:00
|
|
|
|
|
|
|
class WineSavePathResolver(WinePathResolver):
|
|
|
|
|
|
|
|
def __init__(self, core: LegendaryCore, rgame: RareGame):
|
|
|
|
path = PathSpec(core, rgame.igame).resolve_egl_path_vars(rgame.raw_save_path)
|
2024-02-13 05:30:23 +13:00
|
|
|
super(WineSavePathResolver, self).__init__(rgame.core, rgame.app_name, str(path))
|
2023-09-20 10:39:15 +12:00
|
|
|
self.rgame = rgame
|
2022-12-24 09:49:27 +13:00
|
|
|
|
2023-02-01 00:59:22 +13:00
|
|
|
def run_real(self):
|
2023-09-20 10:39:15 +12:00
|
|
|
logger.info("Resolving save path for %s (%s)", self.rgame.app_title, self.rgame.app_name)
|
2024-02-13 05:30:23 +13:00
|
|
|
command, environ = self._configure_process(self.core, self.rgame.app_name)
|
|
|
|
if not (command and environ):
|
|
|
|
logger.error("Cannot setup %s, missing infomation", {type(self).__name__})
|
|
|
|
self.signals.result_ready.emit("", self.rgame.app_name)
|
|
|
|
|
|
|
|
path = self._resolve_unix_path(command, environ, self.path)
|
2022-12-24 09:49:27 +13:00
|
|
|
# Clean wine output
|
|
|
|
# pylint: disable=E1136
|
2023-09-20 10:39:15 +12:00
|
|
|
if os.path.exists(path):
|
|
|
|
self.rgame.save_path = path
|
|
|
|
self.signals.result_ready.emit(path, self.rgame.app_name)
|
2022-12-24 09:49:27 +13:00
|
|
|
return
|
2023-02-02 01:17:51 +13:00
|
|
|
|
|
|
|
|
2024-02-13 05:30:23 +13:00
|
|
|
class OriginWineWorker(WinePathResolver):
|
2023-03-13 05:59:58 +13:00
|
|
|
def __init__(self, core: LegendaryCore, games: Union[Iterable[RareGame], RareGame]):
|
2024-02-13 05:30:23 +13:00
|
|
|
super(OriginWineWorker, self).__init__(core, "", "")
|
2023-02-02 01:17:51 +13:00
|
|
|
self.__cache: dict[str, ConfigParser] = {}
|
2023-03-12 12:09:51 +13:00
|
|
|
self.core = core
|
2023-09-20 10:39:15 +12:00
|
|
|
self.games = [games] if isinstance(games, RareGame) else games
|
2023-02-02 01:17:51 +13:00
|
|
|
|
2024-02-13 05:30:23 +13:00
|
|
|
def run_real(self) -> None:
|
2023-02-02 01:17:51 +13:00
|
|
|
t = time.time()
|
2023-03-11 08:28:37 +13:00
|
|
|
|
2023-02-02 01:17:51 +13:00
|
|
|
for rgame in self.games:
|
|
|
|
|
|
|
|
reg_path: str = rgame.game.metadata \
|
|
|
|
.get("customAttributes", {}) \
|
|
|
|
.get("RegistryPath", {}).get("value", None)
|
|
|
|
if not reg_path:
|
|
|
|
continue
|
|
|
|
|
2023-03-10 23:27:13 +13:00
|
|
|
reg_key: str = rgame.game.metadata \
|
|
|
|
.get("customAttributes", {}) \
|
|
|
|
.get("RegistryKey", {}).get("value", None)
|
2023-03-12 12:09:51 +13:00
|
|
|
if not reg_key:
|
|
|
|
continue
|
2023-03-10 23:27:13 +13:00
|
|
|
|
2023-02-02 09:40:46 +13:00
|
|
|
if platform.system() == "Windows":
|
2023-03-10 23:27:13 +13:00
|
|
|
install_dir = windows_helpers.query_registry_value(winreg.HKEY_LOCAL_MACHINE, reg_path, reg_key)
|
2023-02-02 09:40:46 +13:00
|
|
|
else:
|
2024-02-13 05:30:23 +13:00
|
|
|
command, environ = self._configure_process(self.core, rgame.app_name)
|
2023-09-20 10:39:15 +12:00
|
|
|
|
|
|
|
prefix = config.get_prefix(rgame.app_name)
|
|
|
|
if not prefix:
|
|
|
|
return
|
2023-03-10 23:27:13 +13:00
|
|
|
|
2024-02-13 05:30:23 +13:00
|
|
|
use_wine = True
|
2023-09-03 10:38:42 +12:00
|
|
|
if not use_wine:
|
2023-12-08 21:46:07 +13:00
|
|
|
# lk: this is the original way of getting the path by parsing "system.reg"
|
2024-02-13 05:30:23 +13:00
|
|
|
reg = self.__cache.get(prefix, None) or compat_utils.read_registry("system.reg", prefix)
|
2023-09-20 10:39:15 +12:00
|
|
|
self.__cache[prefix] = reg
|
2023-02-02 01:17:51 +13:00
|
|
|
|
2023-09-03 10:38:42 +12:00
|
|
|
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)])
|
2023-02-02 01:17:51 +13:00
|
|
|
|
2023-09-03 10:38:42 +12:00
|
|
|
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
|
2024-02-13 05:30:23 +13:00
|
|
|
install_dir = compat_utils.query_reg_key(command, environ, f"HKLM\\{reg_path}", reg_key)
|
2023-03-10 23:27:13 +13:00
|
|
|
|
2023-03-08 09:53:18 +13:00
|
|
|
if install_dir:
|
2023-09-03 23:47:44 +12:00
|
|
|
logger.debug("Found Wine install directory %s", install_dir)
|
2024-02-13 05:30:23 +13:00
|
|
|
install_dir = compat_utils.convert_to_unix_path(command, environ, install_dir)
|
2023-09-03 23:47:44 +12:00
|
|
|
if install_dir:
|
|
|
|
logger.debug("Found Unix install directory %s", install_dir)
|
|
|
|
else:
|
2023-12-15 10:08:08 +13:00
|
|
|
logger.info("Could not find Unix install directory for %s", rgame.app_title)
|
2023-09-03 23:47:44 +12:00
|
|
|
else:
|
2023-12-15 10:08:08 +13:00
|
|
|
logger.info("Could not find Wine install directory for %s", rgame.app_title)
|
2023-03-10 23:27:13 +13:00
|
|
|
|
2023-02-02 01:17:51 +13:00
|
|
|
if install_dir:
|
2023-03-10 23:27:13 +13:00
|
|
|
if os.path.isdir(install_dir):
|
2023-03-11 08:28:37 +13:00
|
|
|
install_size = path_size(install_dir)
|
2023-03-12 12:09:51 +13:00
|
|
|
rgame.set_origin_attributes(install_dir, install_size)
|
2023-12-15 10:08:08 +13:00
|
|
|
logger.info("Origin game %s (%s, %s)", rgame.app_title, install_dir, format_size(install_size))
|
2023-02-02 11:02:46 +13:00
|
|
|
else:
|
2023-12-15 10:08:08 +13:00
|
|
|
logger.warning("Origin game %s (%s does not exist)", rgame.app_title, install_dir)
|
2023-09-03 23:47:44 +12:00
|
|
|
else:
|
2023-12-15 10:08:08 +13:00
|
|
|
logger.info("Origin game %s is not installed", rgame.app_title)
|
2023-09-03 23:47:44 +12:00
|
|
|
logger.info("Origin worker finished in %ss", time.time() - t)
|