1
0
Fork 0
mirror of synced 2024-05-19 12:02:54 +12:00
Rare/rare/utils/compat/utils.py
2024-02-18 12:50:35 +02:00

171 lines
6.1 KiB
Python

import os
import platform
import subprocess
from configparser import ConfigParser
from logging import getLogger
from typing import Mapping, Dict, List, Tuple
from PyQt5.QtCore import QProcess, QProcessEnvironment
from rare.utils import config_helper as config
if platform.system() != "Windows":
from . import wine
if platform.system() != "Darwin":
from . import steam
logger = getLogger("CompatUtils")
# this is a copied function from legendary.utils.wine_helpers, but registry file can be specified
def read_registry(registry: str, prefix: 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(prefix, 'system.reg'))
return reg
def get_configured_qprocess(command: List[str], environment: Mapping) -> QProcess:
logger.debug("Executing command: %s", command)
proc = QProcess()
proc.setProcessChannelMode(QProcess.SeparateChannels)
penv = QProcessEnvironment()
for ek, ev in environment.items():
penv.insert(ek, ev)
proc.setProcessEnvironment(penv)
proc.setProgram(command[0])
proc.setArguments(command[1:])
return proc
def get_configured_subprocess(command: List[str], environment: Mapping) -> subprocess.Popen:
logger.debug("Executing command: %s", command)
return subprocess.Popen(
command,
stdin=None,
stdout=subprocess.PIPE,
stderr=None,
env=environment,
shell=False,
text=False,
)
def execute_subprocess(command: List[str], arguments: List[str], environment: Mapping) -> Tuple[str, str]:
proc = get_configured_subprocess(command + arguments, environment)
print(proc.args)
out, err = proc.communicate()
out, err = out.decode("utf-8", "ignore") if out else "", err.decode("utf-8", "ignore") if err else ""
# lk: the following is a work-around for wineserver sometimes hanging around after
proc = get_configured_subprocess(command + ["wineboot", "-k"], environment)
_, _ = proc.communicate()
return out, err
def execute_qprocess(command: List[str], arguments: List[str], environment: Mapping) -> Tuple[str, str]:
proc = get_configured_qprocess(command + arguments, environment)
proc.start()
proc.waitForFinished(-1)
out, err = (
proc.readAllStandardOutput().data().decode("utf-8", "ignore"),
proc.readAllStandardError().data().decode("utf-8", "ignore")
)
proc.deleteLater()
# lk: the following is a work-around for wineserver sometimes hanging around after
proc = get_configured_qprocess(command + ["wineboot", "-k"], environment)
proc.start()
proc.waitForFinished(-1)
proc.deleteLater()
return out, err
def execute(command: List[str], arguments: List[str], environment: Mapping) -> Tuple[str, str]:
# Use the current environment if we are in flatpak or our own if we are on host
# In flatpak our environment is passed through `flatpak-spawn` arguments
if os.environ.get("container") == "flatpak":
flatpak_command = ["flatpak-spawn", "--host"]
for name, value in environment.items():
flatpak_command.append(f"--env={name}={value}")
_command = flatpak_command + command
_environment = os.environ.copy()
else:
_command = command
_environment = environment
try:
out, err = execute_qprocess(_command, arguments, _environment)
except Exception as e:
out, err = "", str(e)
return out, err
def resolve_path(command: List[str], environment: Mapping, path: str) -> str:
path = path.strip().replace("/", "\\")
# lk: if path does not exist form
arguments = ["cmd.exe", "/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(command, arguments, environment)
out, err = out.strip(), err.strip()
if not out:
logger.error("Failed to resolve wine path due to \"%s\"", err)
return out
return out.strip('"')
def query_reg_path(wine_exec: str, wine_env: Mapping, reg_path: str):
raise NotImplementedError
def query_reg_key(command: List[str], environment: Mapping, reg_path: str, reg_key) -> str:
arguments = ["reg.exe", "query", reg_path, "/v", reg_key]
out, err = execute(command, arguments, environment)
out, err = out.strip(), err.strip()
if not out:
logger.error("Failed to query registry key due to \"%s\"", err)
return out
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(command: List[str], environment: Mapping, path: str) -> str:
path = path.strip().strip('"')
arguments = ["winepath.exe", "-u", path]
out, err = execute(command, arguments, environment)
out, err = out.strip(), err.strip()
if not out:
logger.error("Failed to convert to unix path due to \"%s\"", err)
return os.path.realpath(out) if (out := out.strip()) else out
def get_host_environment(app_environment: Dict, silent: bool = True) -> Dict:
# Get a clean environment if we are in flatpak, this environment will be passed
# to `flatpak-spawn`, otherwise use the system's.
_environ = {} if os.environ.get("container") == "flatpak" else os.environ.copy()
_environ.update(app_environment)
if silent:
_environ["WINEESYNC"] = "0"
_environ["WINEFSYNC"] = "0"
_environ["WINE_DISABLE_FAST_SYNC"] = "1"
_environ["WINEDEBUG"] = "-all"
_environ["WINEDLLOVERRIDES"] = "winemenubuilder=d;mscoree=d;mshtml=d;"
# lk: pressure-vessel complains about this but it doesn't fail due to it
_environ["DISPLAY"] = ""
return _environ