From d2c66b6316a13bb557f9a0066d8275fa0dc257db Mon Sep 17 00:00:00 2001 From: m4ko Date: Fri, 28 Aug 2026 20:14:35 +0200 Subject: [PATCH] Some minor changes to support arch distros. --- packaging/install-dependencies.sh | 21 ++-- ptlinux/__init__.py | 2 +- ptlinux/doctor.py | 14 +-- ptlinux/settings.py | 6 +- ptlinux/v21_texture_reclaim.py | 156 ------------------------------ 5 files changed, 23 insertions(+), 176 deletions(-) delete mode 100644 ptlinux/v21_texture_reclaim.py diff --git a/packaging/install-dependencies.sh b/packaging/install-dependencies.sh index 320744f..d89a57a 100755 --- a/packaging/install-dependencies.sh +++ b/packaging/install-dependencies.sh @@ -121,6 +121,9 @@ ptlinux_resolve_dependency_packages() arch:openssl) candidates=(openssl) ;; + arch:umu) + candidates=(umu-launcher) + ;; suse:python) candidates=(python3) ;; @@ -210,6 +213,7 @@ ptlinux_has_wine_stack() ptlinux_detect_missing_dependencies() { local python_bin="${PYTHON_BIN:-python3}" + local family="${1:-}" PTLINUX_MISSING_DEPENDENCIES=() if ! command -v "$python_bin" >/dev/null 2>&1 @@ -227,10 +231,6 @@ ptlinux_detect_missing_dependencies() then PTLINUX_MISSING_DEPENDENCIES+=(winetricks) fi - if ! command -v gamescope >/dev/null 2>&1 - then - PTLINUX_MISSING_DEPENDENCIES+=(gamescope) - fi if ! command -v cabextract >/dev/null 2>&1 then PTLINUX_MISSING_DEPENDENCIES+=(cabextract) @@ -239,6 +239,10 @@ ptlinux_detect_missing_dependencies() then PTLINUX_MISSING_DEPENDENCIES+=(openssl) fi + if [[ "$family" == "arch" ]] && ! command -v umu-run >/dev/null 2>&1 + then + PTLINUX_MISSING_DEPENDENCIES+=(umu) + fi } ptlinux_select_privilege_command() @@ -344,15 +348,14 @@ ptlinux_install_host_dependencies() local package_line local -a packages=() - ptlinux_detect_missing_dependencies + ptlinux_read_distribution + family="$(ptlinux_dependency_family "$PTLINUX_DISTRIBUTION_ID" "$PTLINUX_DISTRIBUTION_LIKE")" + ptlinux_detect_missing_dependencies "$family" if (( ${#PTLINUX_MISSING_DEPENDENCIES[@]} == 0 )) then printf '%s\n' "All required host dependencies are already available." return 0 fi - - ptlinux_read_distribution - family="$(ptlinux_dependency_family "$PTLINUX_DISTRIBUTION_ID" "$PTLINUX_DISTRIBUTION_LIKE")" if [[ "$family" == "unknown" ]] then printf 'Unsupported distribution: %s. Re-run with --skip-deps after installing: %s\n' \ @@ -374,7 +377,7 @@ ptlinux_install_host_dependencies() printf '%s\n' "Installing missing host dependencies (authentication may be requested)." ptlinux_install_resolved_packages "$family" "${packages[@]}" hash -r - ptlinux_detect_missing_dependencies + ptlinux_detect_missing_dependencies "$family" if (( ${#PTLINUX_MISSING_DEPENDENCIES[@]} != 0 )) then printf 'Dependencies remain unavailable after package installation: %s\n' \ diff --git a/ptlinux/__init__.py b/ptlinux/__init__.py index 6c48bce..23f87e1 100644 --- a/ptlinux/__init__.py +++ b/ptlinux/__init__.py @@ -4,4 +4,4 @@ from .paths import AppPaths __all__ = ["AppPaths", "__version__"] -__version__ = "0.5.3" +__version__ = "0.5.8" diff --git a/ptlinux/doctor.py b/ptlinux/doctor.py index edfc9f1..2c18dd5 100644 --- a/ptlinux/doctor.py +++ b/ptlinux/doctor.py @@ -70,8 +70,8 @@ def inspect_host(environment: Mapping[str, str] | None = None) -> DoctorReport: ToolStatus( "gamescope", shutil.which("gamescope"), - True, - "borderless X11/Wayland game container", + False, + "optional manual game-container diagnostics", ), ToolStatus( "cabextract", @@ -147,22 +147,22 @@ def _python_tk_path() -> str | None: def _package_hint(distribution: str) -> str | None: if distribution in {"ubuntu", "debian", "linuxmint", "pop"}: return ( - "Install Python/Tk, Wine (64- and 32-bit), Winetricks, Gamescope, " + "Install Python/Tk, Wine (64- and 32-bit), Winetricks, " "Cabextract, OpenSSL, and both 64- and 32-bit Vulkan drivers with APT." ) if distribution in {"fedora", "nobara"}: return ( - "Install python3-tkinter, wine, winetricks, gamescope, cabextract, openssl, " + "Install python3-tkinter, wine, winetricks, cabextract, openssl, " "and the matching 32-bit Vulkan driver with DNF." ) if distribution in {"arch", "cachyos", "endeavouros", "manjaro"}: return ( - "Install python, tk, wine, winetricks, gamescope, cabextract, openssl, and " - "the matching lib32 Vulkan driver with Pacman." + "Install python, tk, wine, winetricks, umu-launcher, cabextract, " + "openssl, and the matching lib32 Vulkan driver with Pacman." ) if distribution in {"opensuse-tumbleweed", "opensuse-leap", "suse"}: return ( - "Install Python/Tk, Wine, Winetricks, Gamescope, Cabextract, OpenSSL, and " + "Install Python/Tk, Wine, Winetricks, Cabextract, OpenSSL, and " "32-bit Vulkan support with Zypper." ) return None diff --git a/ptlinux/settings.py b/ptlinux/settings.py index ff7135b..20f1c45 100644 --- a/ptlinux/settings.py +++ b/ptlinux/settings.py @@ -92,10 +92,10 @@ def _validate(settings: Settings) -> None: if settings.refresh_rate < 24 or settings.refresh_rate > 1000: raise ValueError("refresh_rate must be between 24 and 1000") if settings.scaler not in {"auto", "integer", "fit", "fill", "stretch"}: - raise ValueError(f"unsupported Gamescope scaler: {settings.scaler}") + raise ValueError(f"unsupported legacy scaler setting: {settings.scaler}") if settings.filter not in {"linear", "nearest", "fsr", "nis", "pixel"}: - raise ValueError(f"unsupported Gamescope filter: {settings.filter}") - if settings.runtime not in {"auto", "proton7", "umu"}: + raise ValueError(f"unsupported legacy filter setting: {settings.filter}") + if settings.runtime not in {"auto", "proton7", "proton7-container", "umu"}: raise ValueError(f"unsupported runtime selection: {settings.runtime}") if settings.proton_sync not in {"auto", "fsync", "esync"}: raise ValueError(f"unsupported Proton synchronization mode: {settings.proton_sync}") diff --git a/ptlinux/v21_texture_reclaim.py b/ptlinux/v21_texture_reclaim.py deleted file mode 100644 index 39ea210..0000000 --- a/ptlinux/v21_texture_reclaim.py +++ /dev/null @@ -1,156 +0,0 @@ -"""Build the verified V21 texture-reclaim runtime DLL from a local game DLL.""" - -from __future__ import annotations - -from dataclasses import dataclass -from datetime import datetime -import hashlib -from pathlib import Path -import shutil -import struct - - -SOURCE_SHA256 = "4b4e5c28e2bfdaa8d36c8341fad27becfd03faf63aa8ea874f5d43c5b1961868" -PATCHED_SHA256 = "0be11b06ccf9be747e32b195c8d5980062f446d7d3a0a662ebfe0e602a01c538" -TEXT_VA = 0x10001000 -TEXT_FILE_OFFSET = 0x400 -ENTRY_VA = 0x107C0B40 -ENTRY_FILE_OFFSET = TEXT_FILE_OFFSET + (ENTRY_VA - TEXT_VA) -ENTRY_ORIGINAL = bytes.fromhex("55 8b ec 6a ff") -CAVE_VA = 0x11195CE0 -CAVE_FILE_OFFSET = TEXT_FILE_OFFSET + (CAVE_VA - TEXT_VA) -TEXT_RAW_SIZE = 0x1194E00 -MAX_HOOK_SIZE = TEXT_FILE_OFFSET + TEXT_RAW_SIZE - CAVE_FILE_OFFSET -HOOK = bytes.fromhex( - "9c60e8490136ff85c0744a89c38d70088dbe505d010039fe743b80be58030000" - "00742a8b068b4c242885c974083b81d41f000074188b4c242c85c974083b81d4" - "1f000074085089d9e8a32036ff81c650040000ebc1619d5589e56affe904ae62" - "ff" -) - - -class TextureReclaimPatchError(RuntimeError): - """Raised when the exact V21 binary profile cannot be patched safely.""" - - -@dataclass(frozen=True) -class TextureReclaimPatchResult: - source: str - destination: str - source_sha256: str - destination_sha256: str - entry_va: str - cave_va: str - hook_size: int - old_text_virtual_size: int - new_text_virtual_size: int - commands: tuple[tuple[str, ...], ...] - created_at: str - - -def build_texture_reclaim_patch( - source: Path, - destination: Path, -) -> TextureReclaimPatchResult: - """Create the V21 patch in a separate runtime cache, never in the game tree.""" - source = source.expanduser().resolve(strict=True) - if _sha256(source) != SOURCE_SHA256: - raise TextureReclaimPatchError( - "source game.dll does not match the verified V21 profile" - ) - - hook = HOOK - commands: list[tuple[str, ...]] = [] - if not hook or len(hook) > MAX_HOOK_SIZE: - raise TextureReclaimPatchError( - f"compiled hook size {len(hook)} exceeds the {MAX_HOOK_SIZE}-byte code cave" - ) - - image = bytearray(source.read_bytes()) - if bytes(image[ENTRY_FILE_OFFSET : ENTRY_FILE_OFFSET + len(ENTRY_ORIGINAL)]) != ENTRY_ORIGINAL: - raise TextureReclaimPatchError( - "V21 map-loader entry bytes do not match the verified profile" - ) - if any(image[CAVE_FILE_OFFSET : CAVE_FILE_OFFSET + len(hook)]): - raise TextureReclaimPatchError("V21 code cave is not empty") - - text_header = _find_pe_section(image, b".text") - old_virtual_size, text_rva, raw_size, raw_offset = struct.unpack_from( - " TEXT_RAW_SIZE: - raise TextureReclaimPatchError("compiled hook extends beyond the executable section") - if old_virtual_size > new_virtual_size: - raise TextureReclaimPatchError( - "verified executable section is already larger than the patch target" - ) - - entry_displacement = CAVE_VA - (ENTRY_VA + 5) - image[ENTRY_FILE_OFFSET : ENTRY_FILE_OFFSET + 5] = b"\xe9" + struct.pack( - " int: - if len(image) < 0x40: - raise TextureReclaimPatchError("source is too small to be a PE image") - pe_offset = struct.unpack_from(" str: - digest = hashlib.sha256() - with path.open("rb") as handle: - for chunk in iter(lambda: handle.read(1024 * 1024), b""): - digest.update(chunk) - return digest.hexdigest()