1
0
Fork 0
mirror of synced 2024-06-02 10:44:40 +12:00

WineResolver: Move common wine operations to rare/utils/wine

`rare.utils.misc.get_raw_save_path` has been removed in favor of the
equivalent `RareGame` property.
This commit is contained in:
loathingKernel 2023-03-10 12:27:13 +02:00
parent 3adabda1ba
commit f6189772d0
No known key found for this signature in database
GPG key ID: CE0C72D0B53821FD
3 changed files with 128 additions and 92 deletions

View file

@ -1,6 +1,5 @@
import os
import platform
import subprocess
import time
from argparse import Namespace
from configparser import ConfigParser
@ -9,10 +8,11 @@ from typing import Union, Iterator
from PyQt5.QtCore import pyqtSignal, QObject
import rare.utils.wine as wine
from rare.lgndr.core import LegendaryCore
from rare.models.game import RareGame
from rare.models.pathspec import PathSpec
from rare.utils.misc import read_registry, path_size, format_size
from rare.utils.misc import path_size, format_size
from .fetch import FetchWorker
from .worker import Worker
@ -31,17 +31,8 @@ class WineResolver(Worker):
def __init__(self, core: LegendaryCore, path: str, app_name: str):
super(WineResolver, self).__init__()
self.signals = WineResolver.Signals()
self.wine_env = os.environ.copy()
self.wine_env.update(core.get_app_environment(app_name))
self.wine_env["WINEDLLOVERRIDES"] = "winemenubuilder=d;mscoree=d;mshtml=d;"
self.wine_env["DISPLAY"] = ""
self.wine_binary = core.lgd.config.get(
app_name, "wine_executable", fallback=core.lgd.config.get(
"default", "wine_executable", fallback="wine"
)
)
self.winepath_binary = os.path.join(os.path.dirname(self.wine_binary), "winepath")
self.wine_env = wine.environ(core, app_name)
self.wine_exec = wine.wine(core, app_name)
self.path = PathSpec(core, app_name).cook(path)
def run_real(self):
@ -49,36 +40,13 @@ class WineResolver(Worker):
# pylint: disable=E1136
self.signals.result_ready[str].emit("")
return
if not os.path.exists(self.wine_binary) or not os.path.exists(self.winepath_binary):
if not os.path.exists(self.wine_exec) or not os.path.exists(wine.winepath(self.wine_exec)):
# pylint: disable=E1136
self.signals.result_ready[str].emit("")
return
path = self.path.strip().replace("/", "\\")
# lk: if path does not exist form
cmd = [self.wine_binary, "cmd", "/c", "echo", path]
# lk: if path exists and needs a case-sensitive interpretation form
# cmd = [self.wine_binary, 'cmd', '/c', f'cd {path} & cd']
proc = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=self.wine_env,
shell=False,
text=True,
)
out, err = proc.communicate()
path = wine.resolve_path(self.wine_exec, self.wine_env, self.path)
# Clean wine output
out = out.strip().strip('"')
proc = subprocess.Popen(
[self.winepath_binary, "-u", out],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=self.wine_env,
shell=False,
text=True,
)
out, err = proc.communicate()
real_path = os.path.realpath(out.strip())
real_path = wine.convert_to_unix_path(self.wine_exec, self.wine_env, path)
# pylint: disable=E1136
self.signals.result_ready[str].emit(real_path)
return
@ -104,46 +72,35 @@ class OriginWineWorker(FetchWorker):
if not reg_path:
continue
reg_key: str = rgame.game.metadata \
.get("customAttributes", {}) \
.get("RegistryKey", {}).get("value", None)
if platform.system() == "Windows":
install_dir = windows_helpers.query_registry_value(winreg.HKEY_LOCAL_MACHINE, reg_path, "Install Dir")
install_dir = windows_helpers.query_registry_value(winreg.HKEY_LOCAL_MACHINE, reg_path, reg_key)
else:
wine_prefix = self.core.lgd.config.get(
rgame.app_name, "wine_prefix", fallback=self.core.lgd.config.get(
"default", "wine_prefix", fallback=os.path.expanduser("~/.wine")
)
)
reg = self.__cache.get(wine_prefix, None) or read_registry("system.reg", wine_prefix)
wine_env = wine.environ(self.core, rgame.app_name)
wine_exec = wine.wine(self.core, rgame.app_name)
# lk: this is the original way of gettijng the path by parsing "system.reg"
wine_prefix = wine.prefix(self.core, rgame.app_name)
reg = self.__cache.get(wine_prefix, None) or wine.read_registry("system.reg", wine_prefix)
self.__cache[wine_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, '"Install Dir"', fallback=None)
install_dir = reg.get(reg_path, f'"{reg_key}"', fallback=None)
# lk: this is the alternative way of getting the path by using wine itself
# install_dir = wine.query_reg_key(wine_exec, wine_env, f"HKLM\\{reg_path}", reg_key)
if install_dir:
wine = self.core.lgd.config.get(
rgame.app_name, "wine_executable", fallback=self.core.lgd.config.get(
"default", "wine_executable", fallback="wine"
)
)
winepath = os.path.join(os.path.dirname(wine), "winepath")
wine_env = os.environ.copy()
wine_env.update(self.core.get_app_environment(rgame.app_name))
wine_env["WINEDLLOVERRIDES"] = "winemenubuilder=d;mscoree=d;mshtml=d;"
wine_env["DISPLAY"] = ""
install_dir = install_dir.strip().strip('"')
proc = subprocess.Popen(
[winepath, "-u", install_dir],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=wine_env,
shell=False,
text=True,
)
out, err = proc.communicate()
install_dir = os.path.realpath(out.strip())
install_dir = wine.convert_to_unix_path(wine_exec, wine_env, install_dir)
if install_dir:
if os.path.exists(install_dir):
if os.path.isdir(install_dir):
size = path_size(install_dir)
rgame.set_origin_attributes(install_dir, size)
logger.debug(f"Found Origin game {rgame.title} ({install_dir}, {format_size(size)})")

View file

@ -1,5 +1,4 @@
import os
from configparser import ConfigParser
from logging import getLogger
from typing import List, Union, Type
@ -18,7 +17,6 @@ from PyQt5.QtGui import QPalette, QColor, QFontMetrics
from PyQt5.QtWidgets import qApp, QStyleFactory, QLabel
from PyQt5.sip import wrappertype
from legendary.core import LegendaryCore
from legendary.models.game import Game
from requests.exceptions import HTTPError
from rare.utils.paths import resources_path
@ -162,7 +160,7 @@ def path_size(path: Union[str, os.PathLike]) -> int:
return sum(
os.stat(os.path.join(dp, f)).st_size
for dp, dn, filenames in os.walk(path)
for f in filenames
for f in filenames if os.path.isfile(f)
)
@ -173,18 +171,6 @@ def format_size(b: Union[int, float]) -> str:
b /= 1024
# this is a copied function from legendary.utils.wine_helpers, but registry file can be specified
def read_registry(registry: str, wine_pfx: str) -> ConfigParser:
accepted = ["system.reg", "user.reg"]
if registry not in accepted:
raise RuntimeError(f'Unknown target "{registry}" not in {accepted}')
reg = ConfigParser(comment_prefixes=(';', '#', '/', 'WINE'), allow_no_value=True,
strict=False)
reg.optionxform = str
reg.read(os.path.join(wine_pfx, 'system.reg'))
return reg
class CloudWorker(QRunnable):
class Signals(QObject):
# List[SaveGameFile]
@ -218,14 +204,6 @@ class CloudWorker(QRunnable):
self.signals.result_ready.emit(latest_saves)
def get_raw_save_path(game: Game):
if game.supports_cloud_saves:
return (
game.metadata.get("customAttributes", {})
.get("CloudSaveFolder", {})
.get("value")
)
def icon(icn_str: str, fallback: str = None, **kwargs):
try:

101
rare/utils/wine.py Normal file
View file

@ -0,0 +1,101 @@
import os
import shutil
import subprocess
from configparser import ConfigParser
from typing import Mapping, Dict, List, Tuple
from rare.lgndr.core import LegendaryCore
# this is a copied function from legendary.utils.wine_helpers, but registry file can be specified
def read_registry(registry: str, wine_pfx: str) -> ConfigParser:
accepted = ["system.reg", "user.reg"]
if registry not in accepted:
raise RuntimeError(f'Unknown target "{registry}" not in {accepted}')
reg = ConfigParser(comment_prefixes=(';', '#', '/', 'WINE'), allow_no_value=True,
strict=False)
reg.optionxform = str
reg.read(os.path.join(wine_pfx, 'system.reg'))
return reg
def execute(cmd: List, wine_env: Mapping) -> Tuple[str, str]:
proc = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=wine_env,
shell=False,
text=True,
)
return proc.communicate()
def resolve_path(wine_exec: str, wine_env: Mapping, path: str) -> str:
path = path.strip().replace("/", "\\")
# lk: if path does not exist form
cmd = [wine_exec, "cmd", "/c", "echo", path]
# lk: if path exists and needs a case-sensitive interpretation form
# cmd = [wine_cmd, 'cmd', '/c', f'cd {path} & cd']
out, err = execute(cmd, wine_env)
return out.strip().strip('"')
def query_reg_path(wine_exec: str, wine_env: Mapping, reg_path: str):
raise NotImplementedError
def query_reg_key(wine_exec: str, wine_env: Mapping, reg_path: str, reg_key) -> str:
cmd = [wine_exec, "reg", "query", reg_path, "/v", reg_key]
out, err = execute(cmd, wine_env)
lines = out.split("\n")
keys: Dict = {}
for line in lines:
if line.startswith(" "*4):
key = [x for x in line.split(" "*4, 3) if bool(x)]
keys.update({key[0]: key[2]})
return keys.get(reg_key, "")
def convert_to_windows_path(wine_exec: str, wine_env: Mapping, path: str) -> str:
raise NotImplementedError
def convert_to_unix_path(wine_exec: str, wine_env: Mapping, path: str) -> str:
path = path.strip().strip('"')
cmd = [winepath(wine_exec), "-u", path]
out, err = execute(cmd, wine_env)
return os.path.realpath(out.strip())
def winepath(wine_exec: str) -> str:
_winepath = os.path.join(os.path.dirname(wine_exec), "winepath")
if not os.path.isfile(_winepath):
return ""
return _winepath
def wine(core: LegendaryCore, app_name: str = "default") -> str:
_wine = core.lgd.config.get(
app_name, "wine_executable", fallback=core.lgd.config.get(
"default", "wine_executable", fallback=shutil.which("wine")
)
)
return _wine
def environ(core: LegendaryCore, app_name: str = "default") -> Dict:
_environ = os.environ.copy()
_environ.update(core.get_app_environment(app_name))
_environ["WINEDLLOVERRIDES"] = "winemenubuilder=d;mscoree=d;mshtml=d;"
_environ["DISPLAY"] = ""
return _environ
def prefix(core: LegendaryCore, app_name: str = "default") -> str:
_prefix = core.lgd.config.get(
app_name, "wine_prefix", fallback=core.lgd.config.get(
"default", "wine_prefix", fallback=os.path.expanduser("~/.wine")
)
)
return _prefix if os.path.isdir(_prefix) else ""