1
0
Fork 0
mirror of synced 2024-06-02 18:54:41 +12:00
Rare/rare/utils/compat/utils.py

171 lines
6.1 KiB
Python
Raw Normal View History

2024-02-13 05:30:23 +13:00
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 proton
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