From fd8b058287c6f239a9e07315f6d2cf2c945a06c1 Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Wed, 9 Oct 2024 04:16:59 +0200 Subject: [PATCH 01/21] Improved type hints --- fix.py | 5 +++-- util.py | 27 +++++++++++++++------------ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/fix.py b/fix.py index f00835dc..d3d48ea2 100644 --- a/fix.py +++ b/fix.py @@ -6,6 +6,7 @@ import csv from functools import lru_cache from importlib import import_module +from typing import Optional try: from . import config @@ -88,7 +89,7 @@ def get_game_name() -> str: return 'UNKNOWN' -def get_store_name(store: str) -> str: +def get_store_name(store: str) -> Optional[str]: """Mapping for store identifier to store name""" return { 'amazon': 'Amazon', @@ -110,7 +111,7 @@ def get_module_name(game_id: str, default: bool = False, local: bool = False) -> if game_id.isnumeric(): store = 'steam' elif os.environ.get('STORE'): - store = os.environ.get('STORE').lower() + store = os.environ.get('STORE', '').lower() if store != 'steam': log.info(f'Non-steam game {get_game_name()} ({game_id})') diff --git a/util.py b/util.py index c7a5fac0..fd8c9769 100644 --- a/util.py +++ b/util.py @@ -13,7 +13,7 @@ import urllib.request import functools from socket import socket, AF_INET, SOCK_DGRAM -from typing import Literal, Any, Callable, Union +from typing import Literal, Any, Callable, Union, Optional from collections.abc import Mapping, Generator try: @@ -28,8 +28,11 @@ except ImportError: log.warn('Unable to hook into Proton main script environment') +# TypeAliases +BasePathType = Literal['user', 'game'] -def which(appname: str) -> Union[str, None]: + +def which(appname: str) -> Optional[str]: """Returns the full path of an executable in $PATH""" for path in os.environ['PATH'].split(os.pathsep): fullpath = os.path.join(path, appname) @@ -50,7 +53,7 @@ def protonprefix() -> str: return os.path.join(os.environ['STEAM_COMPAT_DATA_PATH'], 'pfx/') -def protonnameversion() -> Union[str, None]: +def protonnameversion() -> Optional[str]: """Returns the version of proton from sys.argv[0]""" version = re.search('Proton ([0-9]*\\.[0-9]*)', sys.argv[0]) if version: @@ -73,7 +76,7 @@ def protontimeversion() -> int: return 0 -def protonversion(timestamp: bool = False) -> Union[str, None, int]: +def protonversion(timestamp: bool = False) -> Optional[Union[str, int]]: """Returns the version of proton""" if timestamp: return protontimeversion() @@ -81,8 +84,8 @@ def protonversion(timestamp: bool = False) -> Union[str, None, int]: def once( - func: Union[Callable, None] = None, retry: bool = False -) -> Union[None, Callable[..., Any]]: + func: Optional[Callable] = None, retry: bool = False +) -> Callable[..., Any]: """Decorator to use on functions which should only run once in a prefix. Error handling: @@ -292,9 +295,9 @@ def protontricks(verb: str) -> bool: def regedit_add( folder: str, - name: Union[str, None] = None, - typ: Union[str, None] = None, - value: Union[str, None] = None, + name: Optional[str] = None, + typ: Optional[str] = None, + value: Optional[str] = None, arch: bool = False, ) -> None: """Add regedit keys""" @@ -643,7 +646,7 @@ def _get_case_insensitive_name(path: str) -> str: return root -def _get_config_full_path(cfile: str, base_path: str) -> Union[str, None]: +def _get_config_full_path(cfile: str, base_path: BasePathType) -> Optional[str]: """Find game's config file""" # Start from 'user'/'game' directories or absolute path if base_path == 'user': @@ -674,7 +677,7 @@ def create_backup_config(cfg_path: str) -> None: def set_ini_options( - ini_opts: str, cfile: str, encoding: str, base_path: str = 'user' + ini_opts: str, cfile: str, encoding: str, base_path: BasePathType = 'user' ) -> bool: """Edit game's INI config file""" cfg_path = _get_config_full_path(cfile, base_path) @@ -700,7 +703,7 @@ def set_ini_options( def set_xml_options( - base_attibutte: str, xml_line: str, cfile: str, base_path: str = 'user' + base_attibutte: str, xml_line: str, cfile: str, base_path: BasePathType = 'user' ) -> bool: """Edit game's XML config file""" xml_path = _get_config_full_path(cfile, base_path) From ec2292f6c27e64f3128c57311ee289d3fa6c957a Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Wed, 9 Oct 2024 04:18:06 +0200 Subject: [PATCH 02/21] Enforce correct literal type usage --- gamefixes-steam/105000.py | 2 +- gamefixes-steam/206480.py | 5 ++--- gamefixes-steam/212500.py | 2 +- gamefixes-steam/230820.py | 2 +- gamefixes-steam/243200.py | 2 +- gamefixes-steam/593600.py | 2 +- gamefixes-steam/968370.py | 2 +- gamefixes-umu/umu-starcitizen.py | 2 +- 8 files changed, 9 insertions(+), 10 deletions(-) diff --git a/gamefixes-steam/105000.py b/gamefixes-steam/105000.py index c9e51c8e..22c72509 100755 --- a/gamefixes-steam/105000.py +++ b/gamefixes-steam/105000.py @@ -7,4 +7,4 @@ def main() -> None: - util.winedll_override('xaudio2_7', 'd') + util.winedll_override('xaudio2_7', '') diff --git a/gamefixes-steam/206480.py b/gamefixes-steam/206480.py index 62362ff2..ff0e22dd 100755 --- a/gamefixes-steam/206480.py +++ b/gamefixes-steam/206480.py @@ -1,11 +1,10 @@ """Game fix Dungeons & Dragons Online""" -# from protonfixes import util def main() -> None: """Disable libglesv2""" - # gpu acelleration on wibed3d https://bugs.winehq.org/show_bug.cgi?id=44985 + # gpu acceleration on wined3d https://bugs.winehq.org/show_bug.cgi?id=44985 # Make the store work. - util.winedll_override('libglesv2', 'd') + util.winedll_override('libglesv2', '') diff --git a/gamefixes-steam/212500.py b/gamefixes-steam/212500.py index c5113da9..3986f5fa 100755 --- a/gamefixes-steam/212500.py +++ b/gamefixes-steam/212500.py @@ -8,4 +8,4 @@ def main() -> None: """Disable libglesv2""" ## gpu acelleration on wined3d https://bugs.winehq.org/show_bug.cgi?id=44985 # Make the store work. - util.winedll_override('libglesv2', 'd') + util.winedll_override('libglesv2', '') diff --git a/gamefixes-steam/230820.py b/gamefixes-steam/230820.py index 45fbb809..82a31900 100755 --- a/gamefixes-steam/230820.py +++ b/gamefixes-steam/230820.py @@ -7,4 +7,4 @@ def main() -> None: - util.winedll_override('xaudio2_7', 'd') + util.winedll_override('xaudio2_7', '') diff --git a/gamefixes-steam/243200.py b/gamefixes-steam/243200.py index ee5eddf5..4bf161f2 100755 --- a/gamefixes-steam/243200.py +++ b/gamefixes-steam/243200.py @@ -7,4 +7,4 @@ def main() -> None: - util.winedll_override('xaudio2_7', 'd') + util.winedll_override('xaudio2_7', '') diff --git a/gamefixes-steam/593600.py b/gamefixes-steam/593600.py index fbfe8b78..95d0a134 100755 --- a/gamefixes-steam/593600.py +++ b/gamefixes-steam/593600.py @@ -5,4 +5,4 @@ def main() -> None: """Overrides the mprapi.dll to native.""" - util.winedll_override('mprapi', 'x') + util.winedll_override('mprapi', 'n') diff --git a/gamefixes-steam/968370.py b/gamefixes-steam/968370.py index 5b0f06c0..c1e442ef 100755 --- a/gamefixes-steam/968370.py +++ b/gamefixes-steam/968370.py @@ -6,5 +6,5 @@ def main() -> None: - util.winedll_override('d3d9', 'd') + util.winedll_override('d3d9', '') util.protontricks('segoe_script') diff --git a/gamefixes-umu/umu-starcitizen.py b/gamefixes-umu/umu-starcitizen.py index aabeea34..64ea0b30 100644 --- a/gamefixes-umu/umu-starcitizen.py +++ b/gamefixes-umu/umu-starcitizen.py @@ -12,4 +12,4 @@ def main() -> None: util.protontricks('powershell') # RSI Launcher animation - util.winedll_override("libglesv2", "builtin") + util.winedll_override('libglesv2', 'b') From cd147fe59c26dd401ec411f7dc238027b6672837 Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Wed, 9 Oct 2024 04:21:16 +0200 Subject: [PATCH 03/21] Exit if proton main script cant be hooked and handle it as an error. This prevents subsequent errors and fixes errors thrown by static code analysis. --- fix.py | 3 ++- util.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/fix.py b/fix.py index d3d48ea2..bf3759be 100644 --- a/fix.py +++ b/fix.py @@ -20,7 +20,8 @@ try: import __main__ as protonmain except ImportError: - log.warn('Unable to hook into Proton main script environment') + log.crit('Unable to hook into Proton main script environment') + exit() @lru_cache diff --git a/util.py b/util.py index fd8c9769..eb87872e 100644 --- a/util.py +++ b/util.py @@ -26,7 +26,8 @@ try: import __main__ as protonmain except ImportError: - log.warn('Unable to hook into Proton main script environment') + log.crit('Unable to hook into Proton main script environment') + exit() # TypeAliases BasePathType = Literal['user', 'game'] From 6d7dc326a312ab5a725100d5570736f389aa73b4 Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Wed, 9 Oct 2024 04:24:24 +0200 Subject: [PATCH 04/21] Type alias for Wine dll override types --- util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util.py b/util.py index eb87872e..f429eebb 100644 --- a/util.py +++ b/util.py @@ -31,7 +31,7 @@ # TypeAliases BasePathType = Literal['user', 'game'] - +OverrideTypes = Literal['n', 'b', 'n,b', 'b,n', ''] def which(appname: str) -> Optional[str]: """Returns the full path of an executable in $PATH""" @@ -414,7 +414,7 @@ def get_game_install_path() -> str: return install_path -def winedll_override(dll: str, dtype: Literal['n', 'b', 'n,b', 'b,n', '']) -> None: +def winedll_override(dll: str, dtype: OverrideTypes) -> None: """Add WINE dll override""" log.info(f'Overriding {dll}.dll = {dtype}') setting = f'{dll}={dtype}' From e3771ff0877c367aa0b0817d2ca3c308d4a89005 Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Wed, 9 Oct 2024 04:36:25 +0200 Subject: [PATCH 05/21] Import util relative in fixes --- gamefixes-egs/umu-1248080.py | 2 +- gamefixes-egs/umu-990080.py | 2 +- gamefixes-gog/umu-1141086411.py | 2 +- gamefixes-gog/umu-1209310984.py | 4 ++-- gamefixes-gog/umu-1228964594.py | 2 +- gamefixes-gog/umu-1564851593.py | 4 ++-- gamefixes-gog/umu-1580232252.py | 2 +- gamefixes-gog/umu-1584652180.py | 2 +- gamefixes-gog/umu-1771973390.py | 2 +- gamefixes-gog/umu-2069117974.py | 3 +-- gamefixes-gog/umu-22610.py | 2 +- gamefixes-gog/umu-22650.py | 2 +- gamefixes-gog/umu-22670.py | 2 +- gamefixes-steam/1017900.py | 2 +- gamefixes-steam/10220.py | 2 +- gamefixes-steam/1030830.py | 2 +- gamefixes-steam/105000.py | 2 +- gamefixes-steam/105400.py | 4 ++-- gamefixes-steam/105450.py | 2 +- gamefixes-steam/1056640.py | 2 +- gamefixes-steam/1062040.py | 2 +- gamefixes-steam/1063730.py | 2 +- gamefixes-steam/108710.py | 2 +- gamefixes-steam/1097150.py | 2 +- gamefixes-steam/1097880.py | 2 +- gamefixes-steam/1105510.py | 2 +- gamefixes-steam/110800.py | 2 +- gamefixes-steam/1113000.py | 2 +- gamefixes-steam/1121560.py | 2 +- gamefixes-steam/1151640.py | 2 +- gamefixes-steam/1158850.py | 2 +- gamefixes-steam/1174180.py | 2 +- gamefixes-steam/1175730.py | 2 +- gamefixes-steam/12200.py | 2 +- gamefixes-steam/12210.py | 2 +- gamefixes-steam/1222370.py | 2 +- gamefixes-steam/1222690.py | 2 +- gamefixes-steam/1230140.py | 2 +- gamefixes-steam/1237970.py | 2 +- gamefixes-steam/1239520.py | 2 +- gamefixes-steam/1240440.py | 2 +- gamefixes-steam/1245620.py | 2 +- gamefixes-steam/1250410.py | 2 +- gamefixes-steam/1257290.py | 2 +- gamefixes-steam/1259970.py | 2 +- gamefixes-steam/1272580.py | 2 +- gamefixes-steam/1277510.py | 2 +- gamefixes-steam/1277930.py | 2 +- gamefixes-steam/12810.py | 2 +- gamefixes-steam/12840.py | 2 +- gamefixes-steam/1284210.py | 2 +- gamefixes-steam/1284410.py | 2 +- gamefixes-steam/1286880.py | 2 +- gamefixes-steam/1293820.py | 2 +- gamefixes-steam/1293830.py | 2 +- gamefixes-steam/1361510.py | 2 +- gamefixes-steam/1382330.py | 2 +- gamefixes-steam/1413480.py | 2 +- gamefixes-steam/1434950.py | 3 +-- gamefixes-steam/1449280.py | 4 ++-- gamefixes-steam/1500540.py | 2 +- gamefixes-steam/15130.py | 2 +- gamefixes-steam/1532190.py | 2 +- gamefixes-steam/1544020.py | 2 +- gamefixes-steam/1557480.py | 2 +- gamefixes-steam/15700.py | 2 +- gamefixes-steam/15740.py | 2 +- gamefixes-steam/15750.py | 2 +- gamefixes-steam/1613450.py | 2 +- gamefixes-steam/1659420.py | 2 +- gamefixes-steam/1664350.py | 2 +- gamefixes-steam/16700.py | 2 +- gamefixes-steam/16810.py | 2 +- gamefixes-steam/1681970.py | 2 +- gamefixes-steam/1695791.py | 2 +- gamefixes-steam/1695793.py | 2 +- gamefixes-steam/1695794.py | 2 +- gamefixes-steam/1711950.py | 2 +- gamefixes-steam/1715130.py | 2 +- gamefixes-steam/1795390.py | 2 +- gamefixes-steam/1829980.py | 2 +- gamefixes-steam/1873170.py | 4 ++-- gamefixes-steam/1930.py | 2 +- gamefixes-steam/1999770.py | 2 +- gamefixes-steam/200490.py | 2 +- gamefixes-steam/200940.py | 2 +- gamefixes-steam/201480.py | 2 +- gamefixes-steam/204450.py | 2 +- gamefixes-steam/206480.py | 2 +- gamefixes-steam/206500.py | 2 +- gamefixes-steam/207350.py | 2 +- gamefixes-steam/208650.py | 2 +- gamefixes-steam/211420.py | 2 +- gamefixes-steam/212500.py | 3 +-- gamefixes-steam/213330.py | 2 +- gamefixes-steam/2138090.py | 2 +- gamefixes-steam/214510.py | 2 +- gamefixes-steam/214950.py | 2 +- gamefixes-steam/215280.py | 2 +- gamefixes-steam/21680.py | 2 +- gamefixes-steam/2183070.py | 2 +- gamefixes-steam/219030.py | 2 +- gamefixes-steam/219990.py | 2 +- gamefixes-steam/220240.py | 2 +- gamefixes-steam/2229850.py | 4 ++-- gamefixes-steam/22330.py | 2 +- gamefixes-steam/223750.py | 2 +- gamefixes-steam/224960.py | 2 +- gamefixes-steam/225640.py | 2 +- gamefixes-steam/227320.py | 2 +- gamefixes-steam/230820.py | 2 +- gamefixes-steam/231990.py | 3 +-- gamefixes-steam/2322010.py | 2 +- gamefixes-steam/233270.py | 2 +- gamefixes-steam/23460.py | 2 +- gamefixes-steam/237890.py | 2 +- gamefixes-steam/2399220.py | 2 +- gamefixes-steam/240600.py | 2 +- gamefixes-steam/242760.py | 2 +- gamefixes-steam/243200.py | 2 +- gamefixes-steam/244210.py | 2 +- gamefixes-steam/244850.py | 2 +- gamefixes-steam/2458530.py | 2 +- gamefixes-steam/2475980.py | 2 +- gamefixes-steam/2507620.py | 2 +- gamefixes-steam/251150.py | 2 +- gamefixes-steam/251290.py | 2 +- gamefixes-steam/252430.py | 2 +- gamefixes-steam/2552430.py | 2 +- gamefixes-steam/2561580.py | 2 +- gamefixes-steam/256330.py | 2 +- gamefixes-steam/260130.py | 2 +- gamefixes-steam/261510.py | 2 +- gamefixes-steam/2620.py | 2 +- gamefixes-steam/266840.py | 2 +- gamefixes-steam/2677660.py | 2 +- gamefixes-steam/268050.py | 2 +- gamefixes-steam/271590.py | 2 +- gamefixes-steam/282900.py | 2 +- gamefixes-steam/284160.py | 2 +- gamefixes-steam/286360.py | 2 +- gamefixes-steam/287310.py | 2 +- gamefixes-steam/287450.py | 2 +- gamefixes-steam/289130.py | 2 +- gamefixes-steam/292410.py | 2 +- gamefixes-steam/294700.py | 2 +- gamefixes-steam/298110.py | 2 +- gamefixes-steam/302370.py | 2 +- gamefixes-steam/307780.py | 2 +- gamefixes-steam/311210.py | 2 +- gamefixes-steam/311730.py | 2 +- gamefixes-steam/312060.py | 2 +- gamefixes-steam/312450.py | 2 +- gamefixes-steam/312670.py | 2 +- gamefixes-steam/312790.py | 2 +- gamefixes-steam/321040.py | 2 +- gamefixes-steam/328500.py | 2 +- gamefixes-steam/329380.py | 4 ++-- gamefixes-steam/331370.py | 2 +- gamefixes-steam/33460.py | 2 +- gamefixes-steam/33990.py | 2 +- gamefixes-steam/34330.py | 2 +- gamefixes-steam/348550.py | 2 +- gamefixes-steam/35000.py | 2 +- gamefixes-steam/35140.py | 2 +- gamefixes-steam/351710.py | 2 +- gamefixes-steam/356190.py | 2 +- gamefixes-steam/356500.py | 2 +- gamefixes-steam/3590.py | 2 +- gamefixes-steam/359550.py | 2 +- gamefixes-steam/359870.py | 2 +- gamefixes-steam/366250.py | 2 +- gamefixes-steam/371660.py | 2 +- gamefixes-steam/372000.py | 2 +- gamefixes-steam/377840.py | 2 +- gamefixes-steam/378630.py | 2 +- gamefixes-steam/379720.py | 2 +- gamefixes-steam/386360.py | 2 +- gamefixes-steam/388750.py | 2 +- gamefixes-steam/390710.py | 2 +- gamefixes-steam/39190.py | 2 +- gamefixes-steam/39200.py | 2 +- gamefixes-steam/39210.py | 2 +- gamefixes-steam/39500.py | 2 +- gamefixes-steam/39690.py | 2 +- gamefixes-steam/397540.py | 2 +- gamefixes-steam/40800.py | 3 +-- gamefixes-steam/409090.py | 2 +- gamefixes-steam/40950.py | 2 +- gamefixes-steam/40970.py | 2 +- gamefixes-steam/409720.py | 2 +- gamefixes-steam/410900.py | 2 +- gamefixes-steam/424840.py | 2 +- gamefixes-steam/428660.py | 2 +- gamefixes-steam/429720.py | 2 +- gamefixes-steam/43110.py | 2 +- gamefixes-steam/434570.py | 2 +- gamefixes-steam/436670.py | 2 +- gamefixes-steam/440900.py | 2 +- gamefixes-steam/44690.py | 2 +- gamefixes-steam/447040.py | 2 +- gamefixes-steam/452440.py | 4 ++-- gamefixes-steam/45750.py | 2 +- gamefixes-steam/460120.py | 3 +-- gamefixes-steam/465280.py | 2 +- gamefixes-steam/465840.py | 2 +- gamefixes-steam/4730.py | 2 +- gamefixes-steam/48190.py | 2 +- gamefixes-steam/49520.py | 2 +- gamefixes-steam/495420.py | 2 +- gamefixes-steam/497360.py | 2 +- gamefixes-steam/49900.py | 2 +- gamefixes-steam/508980.py | 2 +- gamefixes-steam/518790.py | 2 +- gamefixes-steam/550340.py | 2 +- gamefixes-steam/559620.py | 3 +-- gamefixes-steam/570940.py | 2 +- gamefixes-steam/582660.py | 2 +- gamefixes-steam/586140.py | 2 +- gamefixes-steam/593600.py | 2 +- gamefixes-steam/601510.py | 2 +- gamefixes-steam/61500.py | 2 +- gamefixes-steam/6270.py | 2 +- gamefixes-steam/627270.py | 2 +- gamefixes-steam/63110.py | 2 +- gamefixes-steam/633230.py | 2 +- gamefixes-steam/63700.py | 2 +- gamefixes-steam/63710.py | 2 +- gamefixes-steam/638160.py | 2 +- gamefixes-steam/638970.py | 2 +- gamefixes-steam/644930.py | 2 +- gamefixes-steam/65540.py | 2 +- gamefixes-steam/65600.py | 2 +- gamefixes-steam/65610.py | 2 +- gamefixes-steam/658150.py | 2 +- gamefixes-steam/658260.py | 2 +- gamefixes-steam/65930.py | 2 +- gamefixes-steam/678950.py | 2 +- gamefixes-steam/700600.py | 2 +- gamefixes-steam/702050.py | 2 +- gamefixes-steam/70400.py | 2 +- gamefixes-steam/70420.py | 2 +- gamefixes-steam/70650.py | 2 +- gamefixes-steam/729040.py | 2 +- gamefixes-steam/730830.py | 2 +- gamefixes-steam/73170.py | 2 +- gamefixes-steam/740550.py | 2 +- gamefixes-steam/750920.py | 2 +- gamefixes-steam/773370.py | 2 +- gamefixes-steam/78000.py | 2 +- gamefixes-steam/782330.py | 2 +- gamefixes-steam/7850.py | 2 +- gamefixes-steam/812140.py | 2 +- gamefixes-steam/813780.py | 2 +- gamefixes-steam/816020.py | 2 +- gamefixes-steam/8190.py | 2 +- gamefixes-steam/834530.py | 2 +- gamefixes-steam/888790.py | 2 +- gamefixes-steam/893180.py | 2 +- gamefixes-steam/895870.py | 2 +- gamefixes-steam/906510.py | 2 +- gamefixes-steam/910830.py | 2 +- gamefixes-steam/913740.py | 2 +- gamefixes-steam/9350.py | 2 +- gamefixes-steam/936160.py | 2 +- gamefixes-steam/950670.py | 4 ++-- gamefixes-steam/955050.py | 2 +- gamefixes-steam/963930.py | 2 +- gamefixes-steam/968370.py | 2 +- gamefixes-steam/976310.py | 2 +- gamefixes-steam/976730.py | 2 +- gamefixes-steam/9900.py | 2 +- gamefixes-steam/997070.py | 2 +- gamefixes-steam/default.py | 2 +- gamefixes-umu/umu-2016590.py | 2 +- gamefixes-umu/umu-39190.py | 2 +- gamefixes-umu/umu-model2.py | 3 +-- gamefixes-umu/umu-silenthill3.py | 2 +- gamefixes-umu/umu-starcitizen.py | 2 +- gamefixes-umu/umu-zenlesszonezero.py | 3 +-- gamefixes-umu/winetricks-gui.py | 2 +- gamefixes-zoomplatform/umu-240200.py | 2 +- .../umu-4bff76f4-566a-4714-b481-95d3343afe22.py | 2 +- 283 files changed, 292 insertions(+), 301 deletions(-) diff --git a/gamefixes-egs/umu-1248080.py b/gamefixes-egs/umu-1248080.py index a5565d2f..e93d4c54 100644 --- a/gamefixes-egs/umu-1248080.py +++ b/gamefixes-egs/umu-1248080.py @@ -1,6 +1,6 @@ """Game fix for CYGNI: All Guns Blazing""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-egs/umu-990080.py b/gamefixes-egs/umu-990080.py index 304c3b7b..f1c19e28 100644 --- a/gamefixes-egs/umu-990080.py +++ b/gamefixes-egs/umu-990080.py @@ -1,6 +1,6 @@ """Game fix Hogwarts Legacy""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-gog/umu-1141086411.py b/gamefixes-gog/umu-1141086411.py index a67f8bd8..b166d65a 100644 --- a/gamefixes-gog/umu-1141086411.py +++ b/gamefixes-gog/umu-1141086411.py @@ -1,6 +1,6 @@ """Silent Hill 4: The Room""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-gog/umu-1209310984.py b/gamefixes-gog/umu-1209310984.py index 786be6b0..750760d4 100644 --- a/gamefixes-gog/umu-1209310984.py +++ b/gamefixes-gog/umu-1209310984.py @@ -11,8 +11,8 @@ from tempfile import mkdtemp from urllib.request import urlopen -from protonfixes import util -from protonfixes.logger import log +from .. import util +from ..logger import log def main() -> None: diff --git a/gamefixes-gog/umu-1228964594.py b/gamefixes-gog/umu-1228964594.py index c7781362..ed76ceaf 100644 --- a/gamefixes-gog/umu-1228964594.py +++ b/gamefixes-gog/umu-1228964594.py @@ -1,6 +1,6 @@ """Game fix for Soldier of Fortune II: Double Helix - Gold Edition""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-gog/umu-1564851593.py b/gamefixes-gog/umu-1564851593.py index d3af0bfb..fc62c8f1 100644 --- a/gamefixes-gog/umu-1564851593.py +++ b/gamefixes-gog/umu-1564851593.py @@ -10,8 +10,8 @@ from urllib.request import urlopen from zipfile import ZipFile, is_zipfile -from protonfixes import util -from protonfixes.logger import log +from .. import util +from ..logger import log # Archive containing the text injecting framework arc = 'https://github.com/user-attachments/files/16136393/d3d9-2206220222.zip' diff --git a/gamefixes-gog/umu-1580232252.py b/gamefixes-gog/umu-1580232252.py index 73a807af..26a842e4 100644 --- a/gamefixes-gog/umu-1580232252.py +++ b/gamefixes-gog/umu-1580232252.py @@ -1,6 +1,6 @@ """Resident Evil (1997)""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-gog/umu-1584652180.py b/gamefixes-gog/umu-1584652180.py index 92bb09e6..fc731bac 100644 --- a/gamefixes-gog/umu-1584652180.py +++ b/gamefixes-gog/umu-1584652180.py @@ -1,6 +1,6 @@ """The Wheel of Time""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-gog/umu-1771973390.py b/gamefixes-gog/umu-1771973390.py index 35a3b092..4ac9a621 100644 --- a/gamefixes-gog/umu-1771973390.py +++ b/gamefixes-gog/umu-1771973390.py @@ -1,6 +1,6 @@ """METAL GEAR SOLID""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-gog/umu-2069117974.py b/gamefixes-gog/umu-2069117974.py index 80b0d12b..75d40cb6 100644 --- a/gamefixes-gog/umu-2069117974.py +++ b/gamefixes-gog/umu-2069117974.py @@ -1,8 +1,7 @@ """METAL GEAR SOLID 2 SUBSTANCE""" # GOG-ID 2069117974 -from protonfixes import util - +from .. import util def main() -> None: util.protontricks('dsound') diff --git a/gamefixes-gog/umu-22610.py b/gamefixes-gog/umu-22610.py index 89eab7b9..c7389b31 100755 --- a/gamefixes-gog/umu-22610.py +++ b/gamefixes-gog/umu-22610.py @@ -1,6 +1,6 @@ """Alien Breed: Impact""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-gog/umu-22650.py b/gamefixes-gog/umu-22650.py index 6a74adbc..f073f909 100755 --- a/gamefixes-gog/umu-22650.py +++ b/gamefixes-gog/umu-22650.py @@ -1,6 +1,6 @@ """Alien Breed 2: Assault""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-gog/umu-22670.py b/gamefixes-gog/umu-22670.py index 550226ed..60948f58 100755 --- a/gamefixes-gog/umu-22670.py +++ b/gamefixes-gog/umu-22670.py @@ -1,6 +1,6 @@ """Alien Breed 3: Descent""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1017900.py b/gamefixes-steam/1017900.py index 96769711..a565f7ef 100755 --- a/gamefixes-steam/1017900.py +++ b/gamefixes-steam/1017900.py @@ -1,6 +1,6 @@ """Game fix for Age of Empires: DE""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/10220.py b/gamefixes-steam/10220.py index d03180b9..40af1991 100644 --- a/gamefixes-steam/10220.py +++ b/gamefixes-steam/10220.py @@ -1,6 +1,6 @@ """Postal III""" -from protonfixes import util +from .. import util # Missing fonts for console and various UI diff --git a/gamefixes-steam/1030830.py b/gamefixes-steam/1030830.py index 92b20929..4c733b3b 100755 --- a/gamefixes-steam/1030830.py +++ b/gamefixes-steam/1030830.py @@ -1,6 +1,6 @@ """Game fix for Mafia II Definitive Edition""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/105000.py b/gamefixes-steam/105000.py index 22c72509..c4a3d341 100755 --- a/gamefixes-steam/105000.py +++ b/gamefixes-steam/105000.py @@ -3,7 +3,7 @@ No cutscene audio in Daedalic Games (Memoria, The Night of the Rabbit, A New Beginning - Final Cut) (105000 230820 243200) #1412 """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/105400.py b/gamefixes-steam/105400.py index 04e41679..d81e334c 100755 --- a/gamefixes-steam/105400.py +++ b/gamefixes-steam/105400.py @@ -3,8 +3,8 @@ import os import shutil -from protonfixes import util -from protonfixes.logger import log +from .. import util +from ..logger import log def main() -> None: diff --git a/gamefixes-steam/105450.py b/gamefixes-steam/105450.py index 107582c4..a1a40416 100755 --- a/gamefixes-steam/105450.py +++ b/gamefixes-steam/105450.py @@ -1,6 +1,6 @@ """Game fix for Age Of Empire 3: Complete Collection""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1056640.py b/gamefixes-steam/1056640.py index c5b48384..33db60b8 100755 --- a/gamefixes-steam/1056640.py +++ b/gamefixes-steam/1056640.py @@ -1,6 +1,6 @@ """Game fix for Phantasy Star Online 2""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1062040.py b/gamefixes-steam/1062040.py index c15695cb..b3607c66 100755 --- a/gamefixes-steam/1062040.py +++ b/gamefixes-steam/1062040.py @@ -1,6 +1,6 @@ """Dragon Star Varnir""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1063730.py b/gamefixes-steam/1063730.py index 489f41c1..057fba95 100755 --- a/gamefixes-steam/1063730.py +++ b/gamefixes-steam/1063730.py @@ -1,6 +1,6 @@ """Game fix for New World""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/108710.py b/gamefixes-steam/108710.py index 6f03704b..5ce494f5 100755 --- a/gamefixes-steam/108710.py +++ b/gamefixes-steam/108710.py @@ -1,6 +1,6 @@ """Alan Wake""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1097150.py b/gamefixes-steam/1097150.py index e955bafd..0e464658 100755 --- a/gamefixes-steam/1097150.py +++ b/gamefixes-steam/1097150.py @@ -1,6 +1,6 @@ """Game fix for Fall Guys""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1097880.py b/gamefixes-steam/1097880.py index e64f9b87..5427231e 100755 --- a/gamefixes-steam/1097880.py +++ b/gamefixes-steam/1097880.py @@ -1,6 +1,6 @@ """Game fix for Super Naughty Maid 2""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1105510.py b/gamefixes-steam/1105510.py index a7f790ce..48e0ab3d 100755 --- a/gamefixes-steam/1105510.py +++ b/gamefixes-steam/1105510.py @@ -1,6 +1,6 @@ """Game fix for Yakuza 5""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/110800.py b/gamefixes-steam/110800.py index f3cbdf61..6893a931 100755 --- a/gamefixes-steam/110800.py +++ b/gamefixes-steam/110800.py @@ -1,6 +1,6 @@ """Game fix for L.A. Noire""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1113000.py b/gamefixes-steam/1113000.py index c83f2ca1..69661d5e 100644 --- a/gamefixes-steam/1113000.py +++ b/gamefixes-steam/1113000.py @@ -1,6 +1,6 @@ """Game fix for Persona 4 Golden""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1121560.py b/gamefixes-steam/1121560.py index 5c690b97..991c68ea 100755 --- a/gamefixes-steam/1121560.py +++ b/gamefixes-steam/1121560.py @@ -3,7 +3,7 @@ Requires disabling the gstreamer protonaudioconverterbin plugin to get full audio in cutscenes """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1151640.py b/gamefixes-steam/1151640.py index fe4a2007..6987e5a3 100755 --- a/gamefixes-steam/1151640.py +++ b/gamefixes-steam/1151640.py @@ -1,6 +1,6 @@ """Game fix for Horizon Zero Dawn""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1158850.py b/gamefixes-steam/1158850.py index d3e2e28f..fe91148c 100755 --- a/gamefixes-steam/1158850.py +++ b/gamefixes-steam/1158850.py @@ -3,7 +3,7 @@ Requires disabling the gstreamer protonaudioconverterbin to get full audio """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1174180.py b/gamefixes-steam/1174180.py index 365c4f92..2951ae06 100755 --- a/gamefixes-steam/1174180.py +++ b/gamefixes-steam/1174180.py @@ -1,6 +1,6 @@ """Game fix for Red Dead Redemption 2""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1175730.py b/gamefixes-steam/1175730.py index 7654828a..9746b41f 100755 --- a/gamefixes-steam/1175730.py +++ b/gamefixes-steam/1175730.py @@ -1,6 +1,6 @@ """Game fix Tree Of Savior""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/12200.py b/gamefixes-steam/12200.py index 0b5a6b9a..f27b4b9d 100644 --- a/gamefixes-steam/12200.py +++ b/gamefixes-steam/12200.py @@ -1,6 +1,6 @@ """Game fix for Bully: Scholarship Edition""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/12210.py b/gamefixes-steam/12210.py index 4092da88..1a923142 100755 --- a/gamefixes-steam/12210.py +++ b/gamefixes-steam/12210.py @@ -1,6 +1,6 @@ """Game fix for GTA IV""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1222370.py b/gamefixes-steam/1222370.py index 6f34efc9..1fda8e19 100755 --- a/gamefixes-steam/1222370.py +++ b/gamefixes-steam/1222370.py @@ -1,6 +1,6 @@ """Necromunda: Hired Gun""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1222690.py b/gamefixes-steam/1222690.py index a2c2a7c5..a4c4d29e 100755 --- a/gamefixes-steam/1222690.py +++ b/gamefixes-steam/1222690.py @@ -1,6 +1,6 @@ """Game fix for Dragon Age Inquisition""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1230140.py b/gamefixes-steam/1230140.py index c63450c9..b4c5e60b 100755 --- a/gamefixes-steam/1230140.py +++ b/gamefixes-steam/1230140.py @@ -1,6 +1,6 @@ """Game fix for ATRI -My Dear Moments-""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1237970.py b/gamefixes-steam/1237970.py index e089e70c..767ffee3 100755 --- a/gamefixes-steam/1237970.py +++ b/gamefixes-steam/1237970.py @@ -3,7 +3,7 @@ import os import subprocess import glob -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1239520.py b/gamefixes-steam/1239520.py index 385c1f42..bda1e037 100755 --- a/gamefixes-steam/1239520.py +++ b/gamefixes-steam/1239520.py @@ -1,6 +1,6 @@ """Madden NFL 21 needs vcrun2019 for online mode to work""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1240440.py b/gamefixes-steam/1240440.py index 05d57484..e020b2c4 100755 --- a/gamefixes-steam/1240440.py +++ b/gamefixes-steam/1240440.py @@ -1,6 +1,6 @@ """Halo Infinite needs vcrun2019""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1245620.py b/gamefixes-steam/1245620.py index bbc1aaac..51f82380 100644 --- a/gamefixes-steam/1245620.py +++ b/gamefixes-steam/1245620.py @@ -1,7 +1,7 @@ """Game fix for Elden Ring: Create the `DLC.bdt` and `DLC.bhd` files to work around the "Inappropriate activity detected" error for players that don't own the DLC""" from pathlib import Path -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1250410.py b/gamefixes-steam/1250410.py index 1661befe..b9a5b97e 100755 --- a/gamefixes-steam/1250410.py +++ b/gamefixes-steam/1250410.py @@ -1,6 +1,6 @@ """Game fix for Flight Simulator 2020""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1257290.py b/gamefixes-steam/1257290.py index 3ed9215a..60c55fe0 100755 --- a/gamefixes-steam/1257290.py +++ b/gamefixes-steam/1257290.py @@ -3,7 +3,7 @@ Requires disabling the gstreamer protonaudioconverterbin plugin to get full audio in cutscenes """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1259970.py b/gamefixes-steam/1259970.py index 2a5f98d2..4493a119 100755 --- a/gamefixes-steam/1259970.py +++ b/gamefixes-steam/1259970.py @@ -1,6 +1,6 @@ """Game fix for Pes 2021""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1272580.py b/gamefixes-steam/1272580.py index 8aa3bdf4..172e784f 100644 --- a/gamefixes-steam/1272580.py +++ b/gamefixes-steam/1272580.py @@ -2,7 +2,7 @@ No music """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1277510.py b/gamefixes-steam/1277510.py index 9ee0abd9..80be9777 100644 --- a/gamefixes-steam/1277510.py +++ b/gamefixes-steam/1277510.py @@ -1,6 +1,6 @@ """Game fix for Re:ZERO -Starting Life in Another World- The Prophecy of the Throne""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1277930.py b/gamefixes-steam/1277930.py index ef21840f..e30f6119 100644 --- a/gamefixes-steam/1277930.py +++ b/gamefixes-steam/1277930.py @@ -1,6 +1,6 @@ """Game fix for Riddle Joker""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/12810.py b/gamefixes-steam/12810.py index b56db721..8bae3ea9 100644 --- a/gamefixes-steam/12810.py +++ b/gamefixes-steam/12810.py @@ -1,6 +1,6 @@ """Overlord II""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/12840.py b/gamefixes-steam/12840.py index b039db84..4076ed3b 100755 --- a/gamefixes-steam/12840.py +++ b/gamefixes-steam/12840.py @@ -1,6 +1,6 @@ """Game fix for Dirt 2""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1284210.py b/gamefixes-steam/1284210.py index 8368bb2f..5a12ff1d 100755 --- a/gamefixes-steam/1284210.py +++ b/gamefixes-steam/1284210.py @@ -1,7 +1,7 @@ """Game fix for Guild Wars 2""" import os -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1284410.py b/gamefixes-steam/1284410.py index f8acc85e..99b13961 100755 --- a/gamefixes-steam/1284410.py +++ b/gamefixes-steam/1284410.py @@ -1,6 +1,6 @@ """GWENT: The Witcher Card Game""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1286880.py b/gamefixes-steam/1286880.py index e46ac5cf..6238cb3f 100755 --- a/gamefixes-steam/1286880.py +++ b/gamefixes-steam/1286880.py @@ -1,6 +1,6 @@ """Ship Graveyard Simulator""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1293820.py b/gamefixes-steam/1293820.py index ebf830c7..55358532 100755 --- a/gamefixes-steam/1293820.py +++ b/gamefixes-steam/1293820.py @@ -1,6 +1,6 @@ """Game fix for YOU and ME and HER: A Love Story""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1293830.py b/gamefixes-steam/1293830.py index b63afbac..55ac652b 100755 --- a/gamefixes-steam/1293830.py +++ b/gamefixes-steam/1293830.py @@ -1,6 +1,6 @@ """Forza Horizon 4""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1361510.py b/gamefixes-steam/1361510.py index dc6221cb..b3610645 100644 --- a/gamefixes-steam/1361510.py +++ b/gamefixes-steam/1361510.py @@ -1,6 +1,6 @@ """Teenage Mutant Ninja Turtles Shredders Revenge""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1382330.py b/gamefixes-steam/1382330.py index 98166b77..cfbed801 100755 --- a/gamefixes-steam/1382330.py +++ b/gamefixes-steam/1382330.py @@ -4,7 +4,7 @@ fixed by Swish in Protondb """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1413480.py b/gamefixes-steam/1413480.py index d5215806..52e8bfc3 100755 --- a/gamefixes-steam/1413480.py +++ b/gamefixes-steam/1413480.py @@ -4,7 +4,7 @@ fixed Persona 5 Strikers by Swish in Protondb """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1434950.py b/gamefixes-steam/1434950.py index 887f6b33..43fc34bf 100755 --- a/gamefixes-steam/1434950.py +++ b/gamefixes-steam/1434950.py @@ -1,7 +1,6 @@ """Game fix HighFleet""" -# -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1449280.py b/gamefixes-steam/1449280.py index 681287f3..34470cd4 100755 --- a/gamefixes-steam/1449280.py +++ b/gamefixes-steam/1449280.py @@ -1,8 +1,8 @@ """Game fix for Ghostbusters: The Video Game Remastered (2019)""" from pathlib import Path -from protonfixes import util -from protonfixes.logger import log +from .. import util +from ..logger import log def main() -> None: diff --git a/gamefixes-steam/1500540.py b/gamefixes-steam/1500540.py index 1b308c2e..3ede4b23 100644 --- a/gamefixes-steam/1500540.py +++ b/gamefixes-steam/1500540.py @@ -1,6 +1,6 @@ """Hardwar""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/15130.py b/gamefixes-steam/15130.py index 91546023..a190b04d 100755 --- a/gamefixes-steam/15130.py +++ b/gamefixes-steam/15130.py @@ -1,6 +1,6 @@ """Game fix for Beyond Good and Evil""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1532190.py b/gamefixes-steam/1532190.py index 80389b95..7b86733b 100755 --- a/gamefixes-steam/1532190.py +++ b/gamefixes-steam/1532190.py @@ -1,6 +1,6 @@ """Game fix for Halo CE mod tools""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1544020.py b/gamefixes-steam/1544020.py index 48947278..680a2e6b 100644 --- a/gamefixes-steam/1544020.py +++ b/gamefixes-steam/1544020.py @@ -1,6 +1,6 @@ """The Callisto Protocol""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1557480.py b/gamefixes-steam/1557480.py index a8d3d746..af5efdaa 100755 --- a/gamefixes-steam/1557480.py +++ b/gamefixes-steam/1557480.py @@ -1,6 +1,6 @@ """Project MIKHAIL: A Muv-Luv War Story""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/15700.py b/gamefixes-steam/15700.py index fb14d390..36581324 100644 --- a/gamefixes-steam/15700.py +++ b/gamefixes-steam/15700.py @@ -1,6 +1,6 @@ """Oddworld: Abe's Oddysee""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/15740.py b/gamefixes-steam/15740.py index 80e5169d..06250f46 100755 --- a/gamefixes-steam/15740.py +++ b/gamefixes-steam/15740.py @@ -1,6 +1,6 @@ """Game fix for Oddworld: Munch's Oddysee""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/15750.py b/gamefixes-steam/15750.py index c98fb118..be8f0db7 100644 --- a/gamefixes-steam/15750.py +++ b/gamefixes-steam/15750.py @@ -1,6 +1,6 @@ """Oddworld: Stranger's Wrath HD""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1613450.py b/gamefixes-steam/1613450.py index 1e5c63d5..567f1b40 100755 --- a/gamefixes-steam/1613450.py +++ b/gamefixes-steam/1613450.py @@ -1,6 +1,6 @@ """Game fix for Halo 2 mod tools""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1659420.py b/gamefixes-steam/1659420.py index e77f07b6..4b74a524 100755 --- a/gamefixes-steam/1659420.py +++ b/gamefixes-steam/1659420.py @@ -1,6 +1,6 @@ """UNCHARTED: Legacy of Thieves Collection""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1664350.py b/gamefixes-steam/1664350.py index 3564490c..8b466387 100755 --- a/gamefixes-steam/1664350.py +++ b/gamefixes-steam/1664350.py @@ -1,6 +1,6 @@ """Ship Graveyard Simulator Prologue""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/16700.py b/gamefixes-steam/16700.py index 4bccc52c..166299d7 100755 --- a/gamefixes-steam/16700.py +++ b/gamefixes-steam/16700.py @@ -2,7 +2,7 @@ Fixes Multiplayer """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/16810.py b/gamefixes-steam/16810.py index 44534d66..83dc036a 100755 --- a/gamefixes-steam/16810.py +++ b/gamefixes-steam/16810.py @@ -1,6 +1,6 @@ """Civilization IV: Colonization""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1681970.py b/gamefixes-steam/1681970.py index 6c767655..f8c1724c 100755 --- a/gamefixes-steam/1681970.py +++ b/gamefixes-steam/1681970.py @@ -1,6 +1,6 @@ """神都不良探 Underdog Detective""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1695791.py b/gamefixes-steam/1695791.py index ab2bd90e..90d44c2d 100755 --- a/gamefixes-steam/1695791.py +++ b/gamefixes-steam/1695791.py @@ -1,6 +1,6 @@ """Game fix for Halo 3 mod tools""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1695793.py b/gamefixes-steam/1695793.py index b90867d2..5a776281 100755 --- a/gamefixes-steam/1695793.py +++ b/gamefixes-steam/1695793.py @@ -3,7 +3,7 @@ - Oro, @orowith2os """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1695794.py b/gamefixes-steam/1695794.py index 5504496e..a18f0aaa 100755 --- a/gamefixes-steam/1695794.py +++ b/gamefixes-steam/1695794.py @@ -1,6 +1,6 @@ """Game fix Halo 3: ODST mod tools""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1711950.py b/gamefixes-steam/1711950.py index 2baa86eb..2860ab0d 100755 --- a/gamefixes-steam/1711950.py +++ b/gamefixes-steam/1711950.py @@ -1,6 +1,6 @@ """GWENT: Rogue Mage""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1715130.py b/gamefixes-steam/1715130.py index cc774847..1ddd9b8d 100755 --- a/gamefixes-steam/1715130.py +++ b/gamefixes-steam/1715130.py @@ -1,6 +1,6 @@ """Crysis Remastered""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1795390.py b/gamefixes-steam/1795390.py index 3c7a0031..f7a2d418 100755 --- a/gamefixes-steam/1795390.py +++ b/gamefixes-steam/1795390.py @@ -2,7 +2,7 @@ Proton issue: https://github.com/ValveSoftware/Proton/issues/6645 """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1829980.py b/gamefixes-steam/1829980.py index e1cecb5e..d71290c6 100644 --- a/gamefixes-steam/1829980.py +++ b/gamefixes-steam/1829980.py @@ -1,6 +1,6 @@ """Game fix for Café Stella and the Reaper's Butterflies""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1873170.py b/gamefixes-steam/1873170.py index f8762e5c..66913271 100644 --- a/gamefixes-steam/1873170.py +++ b/gamefixes-steam/1873170.py @@ -11,8 +11,8 @@ import zipfile import subprocess import hashlib -from protonfixes import util -from protonfixes.logger import log +from .. import util +from ..logger import log def main() -> None: diff --git a/gamefixes-steam/1930.py b/gamefixes-steam/1930.py index de53ab8a..38c0782b 100755 --- a/gamefixes-steam/1930.py +++ b/gamefixes-steam/1930.py @@ -2,7 +2,7 @@ https://www.protondb.com/app/1930 """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/1999770.py b/gamefixes-steam/1999770.py index d8798751..c5c11b46 100755 --- a/gamefixes-steam/1999770.py +++ b/gamefixes-steam/1999770.py @@ -3,7 +3,7 @@ Requires disabling the gstreamer protonaudioconverterbin plugin to get full audio in cutscenes """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/200490.py b/gamefixes-steam/200490.py index b203ce0d..22b69394 100755 --- a/gamefixes-steam/200490.py +++ b/gamefixes-steam/200490.py @@ -3,7 +3,7 @@ hangs on logo without override """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/200940.py b/gamefixes-steam/200940.py index 345e1f1d..84b0219a 100755 --- a/gamefixes-steam/200940.py +++ b/gamefixes-steam/200940.py @@ -1,6 +1,6 @@ """Game fix for Sonic CD""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/201480.py b/gamefixes-steam/201480.py index 52def908..0376bf8f 100755 --- a/gamefixes-steam/201480.py +++ b/gamefixes-steam/201480.py @@ -1,6 +1,6 @@ """Game fix for Serious Sam: The Random Encounter""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/204450.py b/gamefixes-steam/204450.py index cdc702e6..32bf62e5 100644 --- a/gamefixes-steam/204450.py +++ b/gamefixes-steam/204450.py @@ -1,6 +1,6 @@ """Game fix for Call of Juarez: Gunslinger""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/206480.py b/gamefixes-steam/206480.py index ff0e22dd..1539e29c 100755 --- a/gamefixes-steam/206480.py +++ b/gamefixes-steam/206480.py @@ -1,6 +1,6 @@ """Game fix Dungeons & Dragons Online""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/206500.py b/gamefixes-steam/206500.py index d4bc70be..6584cdec 100755 --- a/gamefixes-steam/206500.py +++ b/gamefixes-steam/206500.py @@ -1,6 +1,6 @@ """Game fix for AirMech Strike""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/207350.py b/gamefixes-steam/207350.py index dadcf6d5..e2d44a72 100755 --- a/gamefixes-steam/207350.py +++ b/gamefixes-steam/207350.py @@ -1,6 +1,6 @@ """Game fix for Ys Origin""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/208650.py b/gamefixes-steam/208650.py index 21d4ca9c..514bb169 100755 --- a/gamefixes-steam/208650.py +++ b/gamefixes-steam/208650.py @@ -1,6 +1,6 @@ """Game fix for Batman Arkham Knight""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/211420.py b/gamefixes-steam/211420.py index 168e145b..21e7e53f 100755 --- a/gamefixes-steam/211420.py +++ b/gamefixes-steam/211420.py @@ -1,6 +1,6 @@ """Game fix Dark Souls Prepare To Die Edition""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/212500.py b/gamefixes-steam/212500.py index 3986f5fa..a226ee3c 100755 --- a/gamefixes-steam/212500.py +++ b/gamefixes-steam/212500.py @@ -1,7 +1,6 @@ """Game fix The Lord of the Rings Online""" -# -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/213330.py b/gamefixes-steam/213330.py index 6c6dc351..af74ba72 100755 --- a/gamefixes-steam/213330.py +++ b/gamefixes-steam/213330.py @@ -1,6 +1,6 @@ """Game fix for LEGO Batman 2: DC Super Heroes""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/2138090.py b/gamefixes-steam/2138090.py index 387cd09b..e2c878be 100755 --- a/gamefixes-steam/2138090.py +++ b/gamefixes-steam/2138090.py @@ -5,7 +5,7 @@ further stolen from marianoag by bitwolf """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/214510.py b/gamefixes-steam/214510.py index 576ac0f7..aa3d92d9 100755 --- a/gamefixes-steam/214510.py +++ b/gamefixes-steam/214510.py @@ -1,6 +1,6 @@ """Game fix for LEGO The Lord of the Rings""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/214950.py b/gamefixes-steam/214950.py index d0540253..d60cef30 100755 --- a/gamefixes-steam/214950.py +++ b/gamefixes-steam/214950.py @@ -1,6 +1,6 @@ """Game fix for Total War: Rome II""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/215280.py b/gamefixes-steam/215280.py index 5f4cf243..fa99acc7 100755 --- a/gamefixes-steam/215280.py +++ b/gamefixes-steam/215280.py @@ -1,6 +1,6 @@ """Game fix for Secret World Legends""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/21680.py b/gamefixes-steam/21680.py index 3aa950ac..ced7db29 100755 --- a/gamefixes-steam/21680.py +++ b/gamefixes-steam/21680.py @@ -1,6 +1,6 @@ """Bionic Commander Rearmed""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/2183070.py b/gamefixes-steam/2183070.py index a5d415ec..798a0380 100755 --- a/gamefixes-steam/2183070.py +++ b/gamefixes-steam/2183070.py @@ -1,6 +1,6 @@ """Game fix for Tokyo Necro""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/219030.py b/gamefixes-steam/219030.py index 13de6d29..17ace812 100755 --- a/gamefixes-steam/219030.py +++ b/gamefixes-steam/219030.py @@ -1,6 +1,6 @@ """Game fix for Ys Origin Demo""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/219990.py b/gamefixes-steam/219990.py index c19aad2f..cb24f483 100755 --- a/gamefixes-steam/219990.py +++ b/gamefixes-steam/219990.py @@ -1,6 +1,6 @@ """Game fix for Grim Dawn""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/220240.py b/gamefixes-steam/220240.py index d52f90ce..2f8af2c5 100755 --- a/gamefixes-steam/220240.py +++ b/gamefixes-steam/220240.py @@ -1,6 +1,6 @@ """FarCry 3""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/2229850.py b/gamefixes-steam/2229850.py index a821709c..46953516 100644 --- a/gamefixes-steam/2229850.py +++ b/gamefixes-steam/2229850.py @@ -2,8 +2,8 @@ import os -from protonfixes import util -from protonfixes.logger import log +from .. import util +from ..logger import log def main() -> None: diff --git a/gamefixes-steam/22330.py b/gamefixes-steam/22330.py index 9781f7bb..16f7b9ea 100755 --- a/gamefixes-steam/22330.py +++ b/gamefixes-steam/22330.py @@ -12,7 +12,7 @@ import os from dataclasses import dataclass -from protonfixes import util +from .. import util def main_with_id(game_id: str) -> None: """Enable modding and fixes""" diff --git a/gamefixes-steam/223750.py b/gamefixes-steam/223750.py index 126d16b1..b9f894c5 100755 --- a/gamefixes-steam/223750.py +++ b/gamefixes-steam/223750.py @@ -1,6 +1,6 @@ """Fixes for DCS World Steam Edition""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/224960.py b/gamefixes-steam/224960.py index bee2d8fc..5a1bd235 100755 --- a/gamefixes-steam/224960.py +++ b/gamefixes-steam/224960.py @@ -1,6 +1,6 @@ """Game fix for Tomb Raider I""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/225640.py b/gamefixes-steam/225640.py index d33e3569..8bb5d032 100755 --- a/gamefixes-steam/225640.py +++ b/gamefixes-steam/225640.py @@ -1,6 +1,6 @@ """Game fix for Sacred 2 Gold""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/227320.py b/gamefixes-steam/227320.py index b5d1b9e9..e09d20e7 100755 --- a/gamefixes-steam/227320.py +++ b/gamefixes-steam/227320.py @@ -1,6 +1,6 @@ """Game fix for You Need a Budget 4""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/230820.py b/gamefixes-steam/230820.py index 82a31900..4eb03d32 100755 --- a/gamefixes-steam/230820.py +++ b/gamefixes-steam/230820.py @@ -3,7 +3,7 @@ No cutscene audio in Daedalic Games (Memoria, The Night of the Rabbit, A New Beginning - Final Cut) (105000 230820 243200) #1412 """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/231990.py b/gamefixes-steam/231990.py index cd5214f0..b1f92712 100755 --- a/gamefixes-steam/231990.py +++ b/gamefixes-steam/231990.py @@ -1,7 +1,6 @@ """Game fix for Spider-Man: Shattered Dimensions""" -# -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/2322010.py b/gamefixes-steam/2322010.py index 21e03ca3..cef7ff6f 100644 --- a/gamefixes-steam/2322010.py +++ b/gamefixes-steam/2322010.py @@ -2,7 +2,7 @@ Will not launch without SteamDeck=1 """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/233270.py b/gamefixes-steam/233270.py index 07563a6c..43e86bd6 100755 --- a/gamefixes-steam/233270.py +++ b/gamefixes-steam/233270.py @@ -1,6 +1,6 @@ """Far Cry Blood Dragon""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/23460.py b/gamefixes-steam/23460.py index 48c410e7..b58bdaff 100644 --- a/gamefixes-steam/23460.py +++ b/gamefixes-steam/23460.py @@ -5,7 +5,7 @@ import os import subprocess -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/237890.py b/gamefixes-steam/237890.py index 4621d5e0..ccd42e24 100755 --- a/gamefixes-steam/237890.py +++ b/gamefixes-steam/237890.py @@ -1,6 +1,6 @@ """Game fix for Agarest: Generations of War""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/2399220.py b/gamefixes-steam/2399220.py index b3dad3d1..34809cd7 100755 --- a/gamefixes-steam/2399220.py +++ b/gamefixes-steam/2399220.py @@ -1,6 +1,6 @@ """Game fix for NUKITASHI""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/240600.py b/gamefixes-steam/240600.py index fb57950e..ad9a312f 100755 --- a/gamefixes-steam/240600.py +++ b/gamefixes-steam/240600.py @@ -1,6 +1,6 @@ """Game fix for MotorGP""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/242760.py b/gamefixes-steam/242760.py index 20e852f4..064c24ea 100755 --- a/gamefixes-steam/242760.py +++ b/gamefixes-steam/242760.py @@ -1,6 +1,6 @@ """Game fix for The Forest""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/243200.py b/gamefixes-steam/243200.py index 4bf161f2..c9d0940a 100755 --- a/gamefixes-steam/243200.py +++ b/gamefixes-steam/243200.py @@ -3,7 +3,7 @@ No cutscene audio in Daedalic Games (Memoria, The Night of the Rabbit, A New Beginning - Final Cut) (105000 230820 243200) #1412 """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/244210.py b/gamefixes-steam/244210.py index 826d8204..9d4034a0 100755 --- a/gamefixes-steam/244210.py +++ b/gamefixes-steam/244210.py @@ -1,6 +1,6 @@ """Game fix for Assetto Corsa""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/244850.py b/gamefixes-steam/244850.py index 5ed72f78..768b1480 100755 --- a/gamefixes-steam/244850.py +++ b/gamefixes-steam/244850.py @@ -1,6 +1,6 @@ """Game fix for Space Engineers""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/2458530.py b/gamefixes-steam/2458530.py index 8480a8fa..13868bfd 100644 --- a/gamefixes-steam/2458530.py +++ b/gamefixes-steam/2458530.py @@ -1,6 +1,6 @@ """Game fix for Sanoba Witch FHD Edition""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/2475980.py b/gamefixes-steam/2475980.py index 70a8f7bb..74c70f48 100644 --- a/gamefixes-steam/2475980.py +++ b/gamefixes-steam/2475980.py @@ -6,7 +6,7 @@ import sys import subprocess import glob -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/2507620.py b/gamefixes-steam/2507620.py index 2eba45b5..820dce49 100644 --- a/gamefixes-steam/2507620.py +++ b/gamefixes-steam/2507620.py @@ -1,6 +1,6 @@ """Game fix for The Quintessential Quintuplets - Five Memories Spent With You""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/251150.py b/gamefixes-steam/251150.py index cdd9de9c..2ffbf843 100644 --- a/gamefixes-steam/251150.py +++ b/gamefixes-steam/251150.py @@ -1,6 +1,6 @@ """Game fix for The Legend of Heroes: Trails in the Sky""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/251290.py b/gamefixes-steam/251290.py index 28d7bf49..7ae56ce1 100644 --- a/gamefixes-steam/251290.py +++ b/gamefixes-steam/251290.py @@ -1,6 +1,6 @@ """Game fix for The Legend of Heroes: Trails in the Sky SC""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/252430.py b/gamefixes-steam/252430.py index f388f80a..f35ab7d2 100755 --- a/gamefixes-steam/252430.py +++ b/gamefixes-steam/252430.py @@ -1,6 +1,6 @@ """Game fix for Dusty Revenge: Co-Op Edition""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/2552430.py b/gamefixes-steam/2552430.py index 1e601ae6..cbbe982a 100644 --- a/gamefixes-steam/2552430.py +++ b/gamefixes-steam/2552430.py @@ -1,6 +1,6 @@ """Game fix for KINGDOM HEARTS -HD 1.5+2.5 ReMIX-""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/2561580.py b/gamefixes-steam/2561580.py index f66e4e95..a3bb339c 100644 --- a/gamefixes-steam/2561580.py +++ b/gamefixes-steam/2561580.py @@ -2,7 +2,7 @@ from sys import argv from os import environ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/256330.py b/gamefixes-steam/256330.py index e80a2a71..18abcd1c 100644 --- a/gamefixes-steam/256330.py +++ b/gamefixes-steam/256330.py @@ -1,6 +1,6 @@ """WRC 4""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/260130.py b/gamefixes-steam/260130.py index 5aa67f4e..1eb9dea2 100755 --- a/gamefixes-steam/260130.py +++ b/gamefixes-steam/260130.py @@ -1,6 +1,6 @@ """Game fix for Agarest Zero""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/261510.py b/gamefixes-steam/261510.py index 2818bb21..3303ae90 100755 --- a/gamefixes-steam/261510.py +++ b/gamefixes-steam/261510.py @@ -1,6 +1,6 @@ """Game fix for Tesla Effect""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/2620.py b/gamefixes-steam/2620.py index bf2337d3..b3fcef9e 100755 --- a/gamefixes-steam/2620.py +++ b/gamefixes-steam/2620.py @@ -1,6 +1,6 @@ """Game fix for Call of Duty (2003)""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/266840.py b/gamefixes-steam/266840.py index ac79854d..4a485c61 100755 --- a/gamefixes-steam/266840.py +++ b/gamefixes-steam/266840.py @@ -2,7 +2,7 @@ Source: https://github.com/JamesHealdUK/protonfixes/blob/master/fixes/266840.sh """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/2677660.py b/gamefixes-steam/2677660.py index d7d603b0..02851add 100644 --- a/gamefixes-steam/2677660.py +++ b/gamefixes-steam/2677660.py @@ -1,6 +1,6 @@ """Game fix for Indiana Jones and the Great Circle""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/268050.py b/gamefixes-steam/268050.py index e308efa2..9f460e78 100755 --- a/gamefixes-steam/268050.py +++ b/gamefixes-steam/268050.py @@ -1,6 +1,6 @@ """Game fix for The Evil Within(268050)""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/271590.py b/gamefixes-steam/271590.py index db0e775e..87b2ffd2 100755 --- a/gamefixes-steam/271590.py +++ b/gamefixes-steam/271590.py @@ -1,7 +1,7 @@ """Game fix for GTAV""" import os -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/282900.py b/gamefixes-steam/282900.py index 2def67f6..f507834e 100755 --- a/gamefixes-steam/282900.py +++ b/gamefixes-steam/282900.py @@ -2,7 +2,7 @@ Poor performance on some AMD hardware """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/284160.py b/gamefixes-steam/284160.py index c64a2cd4..10ee651c 100755 --- a/gamefixes-steam/284160.py +++ b/gamefixes-steam/284160.py @@ -1,6 +1,6 @@ """Game fix for BeamNG.drive""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/286360.py b/gamefixes-steam/286360.py index f6ce2312..2866e663 100755 --- a/gamefixes-steam/286360.py +++ b/gamefixes-steam/286360.py @@ -3,7 +3,7 @@ """ import os -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/287310.py b/gamefixes-steam/287310.py index 4aa90e9a..03c3e995 100755 --- a/gamefixes-steam/287310.py +++ b/gamefixes-steam/287310.py @@ -1,6 +1,6 @@ """Game fix for Re-Volt (287310)""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/287450.py b/gamefixes-steam/287450.py index adad905d..bf3035f5 100755 --- a/gamefixes-steam/287450.py +++ b/gamefixes-steam/287450.py @@ -2,7 +2,7 @@ Source: https://github.com/simons-public/protonfixes/issues/24#issue-372384148 """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/289130.py b/gamefixes-steam/289130.py index b53032e4..90f71d83 100755 --- a/gamefixes-steam/289130.py +++ b/gamefixes-steam/289130.py @@ -1,6 +1,6 @@ """Game fix for Endless Legend""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/292410.py b/gamefixes-steam/292410.py index 53333230..e5d70ea2 100644 --- a/gamefixes-steam/292410.py +++ b/gamefixes-steam/292410.py @@ -1,6 +1,6 @@ """Street Racing Syndicate""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/294700.py b/gamefixes-steam/294700.py index c5593333..15bbf59e 100755 --- a/gamefixes-steam/294700.py +++ b/gamefixes-steam/294700.py @@ -1,7 +1,7 @@ """Game fix for Putt-Putt: Pep's Birthday Surprise""" import os -from protonfixes import util +from .. import util # Putt-Putt: PBS doesn't run unless there is a CD-ROM drive attached. diff --git a/gamefixes-steam/298110.py b/gamefixes-steam/298110.py index 3c746d5d..c3d656db 100755 --- a/gamefixes-steam/298110.py +++ b/gamefixes-steam/298110.py @@ -1,6 +1,6 @@ """FarCry 4""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/302370.py b/gamefixes-steam/302370.py index 725e7574..be68b46c 100755 --- a/gamefixes-steam/302370.py +++ b/gamefixes-steam/302370.py @@ -6,7 +6,7 @@ import os import subprocess -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/307780.py b/gamefixes-steam/307780.py index d70dd0e8..f1495460 100755 --- a/gamefixes-steam/307780.py +++ b/gamefixes-steam/307780.py @@ -1,6 +1,6 @@ """Game fix for Mortal Kombat X""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/311210.py b/gamefixes-steam/311210.py index 37d7fceb..c4069c34 100755 --- a/gamefixes-steam/311210.py +++ b/gamefixes-steam/311210.py @@ -1,6 +1,6 @@ """Game fix for Black Ops III""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/311730.py b/gamefixes-steam/311730.py index 1a573364..15cf49d1 100755 --- a/gamefixes-steam/311730.py +++ b/gamefixes-steam/311730.py @@ -1,6 +1,6 @@ """Game fix for DEAD OR ALIVE 5 Last Round""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/312060.py b/gamefixes-steam/312060.py index 6f64b536..b450ebdb 100755 --- a/gamefixes-steam/312060.py +++ b/gamefixes-steam/312060.py @@ -1,7 +1,7 @@ """Game fix for FFXIV""" import os -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/312450.py b/gamefixes-steam/312450.py index e4d6275d..4a2d8a0c 100755 --- a/gamefixes-steam/312450.py +++ b/gamefixes-steam/312450.py @@ -2,7 +2,7 @@ Still missing intro video codecs """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/312670.py b/gamefixes-steam/312670.py index 86aeda2c..59293cc2 100755 --- a/gamefixes-steam/312670.py +++ b/gamefixes-steam/312670.py @@ -1,6 +1,6 @@ """Game fix for Strange Brigade""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/312790.py b/gamefixes-steam/312790.py index e74bc2f3..1dc82d3c 100755 --- a/gamefixes-steam/312790.py +++ b/gamefixes-steam/312790.py @@ -1,6 +1,6 @@ """Game fix for Agarest: Generations of War 2""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/321040.py b/gamefixes-steam/321040.py index 6f93e4c2..47c93b17 100755 --- a/gamefixes-steam/321040.py +++ b/gamefixes-steam/321040.py @@ -1,6 +1,6 @@ """Game fix for Dirt 3 Complete Edition""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/328500.py b/gamefixes-steam/328500.py index 4ce9fe38..ce1673e6 100755 --- a/gamefixes-steam/328500.py +++ b/gamefixes-steam/328500.py @@ -1,7 +1,7 @@ """Game fix for Potatoman Seeks the Troof""" import os -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/329380.py b/gamefixes-steam/329380.py index efd82106..3f76a275 100755 --- a/gamefixes-steam/329380.py +++ b/gamefixes-steam/329380.py @@ -1,7 +1,7 @@ """Game fix Stealth Inc 2: A Game of Clones""" -from protonfixes import util -from protonfixes.logger import log +from .. import util +from ..logger import log def main() -> None: diff --git a/gamefixes-steam/331370.py b/gamefixes-steam/331370.py index a4038220..8b6a20da 100644 --- a/gamefixes-steam/331370.py +++ b/gamefixes-steam/331370.py @@ -1,6 +1,6 @@ """Game fix for Dauntless""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/33460.py b/gamefixes-steam/33460.py index 8d899e4d..c73be812 100644 --- a/gamefixes-steam/33460.py +++ b/gamefixes-steam/33460.py @@ -1,6 +1,6 @@ """Game fix for From Dust""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/33990.py b/gamefixes-steam/33990.py index 5e31d1bd..3bf9d5bd 100755 --- a/gamefixes-steam/33990.py +++ b/gamefixes-steam/33990.py @@ -3,7 +3,7 @@ hangs on logo without override """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/34330.py b/gamefixes-steam/34330.py index 465f0453..5cd3bc3f 100755 --- a/gamefixes-steam/34330.py +++ b/gamefixes-steam/34330.py @@ -1,6 +1,6 @@ """Total War: Shogun 2""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/348550.py b/gamefixes-steam/348550.py index f780ab4a..cbc2bcf1 100755 --- a/gamefixes-steam/348550.py +++ b/gamefixes-steam/348550.py @@ -1,6 +1,6 @@ """Game fix for Guilty Gear Accent Core Plus R""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/35000.py b/gamefixes-steam/35000.py index 3659533d..334866fa 100644 --- a/gamefixes-steam/35000.py +++ b/gamefixes-steam/35000.py @@ -1,6 +1,6 @@ """Game fix for Mini Ninjas""" -from protonfixes import util +from .. import util def main() -> None: """Game needs OpenAL library for audio to work, but the game doesn't include it by default, leading to missing audio in-game, even on Windows.""" diff --git a/gamefixes-steam/35140.py b/gamefixes-steam/35140.py index 5bdf0e00..213d1d37 100755 --- a/gamefixes-steam/35140.py +++ b/gamefixes-steam/35140.py @@ -2,7 +2,7 @@ (Currently no contollers) """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/351710.py b/gamefixes-steam/351710.py index b18fb4dd..4f714fc5 100755 --- a/gamefixes-steam/351710.py +++ b/gamefixes-steam/351710.py @@ -2,7 +2,7 @@ Poor performance on some AMD hardware """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/356190.py b/gamefixes-steam/356190.py index 67a7391e..3f870671 100755 --- a/gamefixes-steam/356190.py +++ b/gamefixes-steam/356190.py @@ -1,6 +1,6 @@ """Game fix Shadow of War""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/356500.py b/gamefixes-steam/356500.py index 1fd45d8f..e7286a40 100755 --- a/gamefixes-steam/356500.py +++ b/gamefixes-steam/356500.py @@ -1,6 +1,6 @@ """Game fix for STAR WARS Galactic Battlegrounds Saga""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/3590.py b/gamefixes-steam/3590.py index 9b3fde62..0ead77bc 100755 --- a/gamefixes-steam/3590.py +++ b/gamefixes-steam/3590.py @@ -2,7 +2,7 @@ Source: https://github.com/JamesHealdUK/protonfixes/blob/master/fixes/3590.sh """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/359550.py b/gamefixes-steam/359550.py index e7160a30..c74237cc 100755 --- a/gamefixes-steam/359550.py +++ b/gamefixes-steam/359550.py @@ -1,6 +1,6 @@ """Rainbow Six Siege""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/359870.py b/gamefixes-steam/359870.py index 1245f505..1b8986e5 100755 --- a/gamefixes-steam/359870.py +++ b/gamefixes-steam/359870.py @@ -1,7 +1,7 @@ """Game fix for FFX/X-2 HD Remaster""" import os -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/366250.py b/gamefixes-steam/366250.py index bb8ea0d9..efabe0e9 100755 --- a/gamefixes-steam/366250.py +++ b/gamefixes-steam/366250.py @@ -1,6 +1,6 @@ """Metal Slug""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/371660.py b/gamefixes-steam/371660.py index 71ac4558..227892fd 100755 --- a/gamefixes-steam/371660.py +++ b/gamefixes-steam/371660.py @@ -1,6 +1,6 @@ """Far Cry Primal""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/372000.py b/gamefixes-steam/372000.py index 7654828a..9746b41f 100755 --- a/gamefixes-steam/372000.py +++ b/gamefixes-steam/372000.py @@ -1,6 +1,6 @@ """Game fix Tree Of Savior""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/377840.py b/gamefixes-steam/377840.py index a2b93251..3a523034 100755 --- a/gamefixes-steam/377840.py +++ b/gamefixes-steam/377840.py @@ -1,6 +1,6 @@ """Game fix for FINAL FANTASY IX""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/378630.py b/gamefixes-steam/378630.py index 6e6a6732..46ae3b68 100755 --- a/gamefixes-steam/378630.py +++ b/gamefixes-steam/378630.py @@ -3,7 +3,7 @@ """ import os -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/379720.py b/gamefixes-steam/379720.py index d31cd39f..60edd2c7 100755 --- a/gamefixes-steam/379720.py +++ b/gamefixes-steam/379720.py @@ -5,7 +5,7 @@ import urllib.request import zipfile -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/386360.py b/gamefixes-steam/386360.py index 868bc435..f93e51a5 100755 --- a/gamefixes-steam/386360.py +++ b/gamefixes-steam/386360.py @@ -3,7 +3,7 @@ import glob import os import subprocess -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/388750.py b/gamefixes-steam/388750.py index b81050ca..d9e431d6 100755 --- a/gamefixes-steam/388750.py +++ b/gamefixes-steam/388750.py @@ -1,6 +1,6 @@ """Game fix for Chronophantasma Extend""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/390710.py b/gamefixes-steam/390710.py index 4cb5f164..2d725e62 100755 --- a/gamefixes-steam/390710.py +++ b/gamefixes-steam/390710.py @@ -1,6 +1,6 @@ """Game fix for SUGURI 2""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/39190.py b/gamefixes-steam/39190.py index 232d7a86..e59f596b 100644 --- a/gamefixes-steam/39190.py +++ b/gamefixes-steam/39190.py @@ -1,6 +1,6 @@ """Game fix for Dungeon Siege""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/39200.py b/gamefixes-steam/39200.py index 68509473..133044aa 100644 --- a/gamefixes-steam/39200.py +++ b/gamefixes-steam/39200.py @@ -1,6 +1,6 @@ """Game fix for Dungeon Siege II""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/39210.py b/gamefixes-steam/39210.py index 6f64b536..b450ebdb 100755 --- a/gamefixes-steam/39210.py +++ b/gamefixes-steam/39210.py @@ -1,7 +1,7 @@ """Game fix for FFXIV""" import os -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/39500.py b/gamefixes-steam/39500.py index be94ed27..efeb884a 100755 --- a/gamefixes-steam/39500.py +++ b/gamefixes-steam/39500.py @@ -1,7 +1,7 @@ """Game fix for Gothic 3""" import os -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/39690.py b/gamefixes-steam/39690.py index d70ddbd3..c92b2b56 100755 --- a/gamefixes-steam/39690.py +++ b/gamefixes-steam/39690.py @@ -1,6 +1,6 @@ """Game fix for Arkania""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/397540.py b/gamefixes-steam/397540.py index 68490f61..39b89b55 100755 --- a/gamefixes-steam/397540.py +++ b/gamefixes-steam/397540.py @@ -1,6 +1,6 @@ """Game fix for Borderlands 3""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/40800.py b/gamefixes-steam/40800.py index c3ad29ec..1a801c82 100755 --- a/gamefixes-steam/40800.py +++ b/gamefixes-steam/40800.py @@ -1,7 +1,6 @@ """Game fix for Super Meat Boy""" -# -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/409090.py b/gamefixes-steam/409090.py index b629685d..8e2e350e 100755 --- a/gamefixes-steam/409090.py +++ b/gamefixes-steam/409090.py @@ -8,7 +8,7 @@ import os import subprocess import shutil -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/40950.py b/gamefixes-steam/40950.py index df5a4416..6c1833c2 100755 --- a/gamefixes-steam/40950.py +++ b/gamefixes-steam/40950.py @@ -2,7 +2,7 @@ Fixes Multiplayer """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/40970.py b/gamefixes-steam/40970.py index 6bd49bbb..c5329e49 100755 --- a/gamefixes-steam/40970.py +++ b/gamefixes-steam/40970.py @@ -2,7 +2,7 @@ Fixes Multiplayer """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/409720.py b/gamefixes-steam/409720.py index 1cf3c929..e6579efc 100755 --- a/gamefixes-steam/409720.py +++ b/gamefixes-steam/409720.py @@ -1,6 +1,6 @@ """Game fix for BioShock 2 Remastered""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/410900.py b/gamefixes-steam/410900.py index 37cec684..44e37988 100755 --- a/gamefixes-steam/410900.py +++ b/gamefixes-steam/410900.py @@ -1,6 +1,6 @@ """Game fix for Forts""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/424840.py b/gamefixes-steam/424840.py index ea624bc6..e8c1167c 100755 --- a/gamefixes-steam/424840.py +++ b/gamefixes-steam/424840.py @@ -1,6 +1,6 @@ """Game fix for Little Nightmares""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/428660.py b/gamefixes-steam/428660.py index f562699f..818d6d7e 100755 --- a/gamefixes-steam/428660.py +++ b/gamefixes-steam/428660.py @@ -1,6 +1,6 @@ """Deliver us the Moon fix""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/429720.py b/gamefixes-steam/429720.py index 01b39cd6..f7fa21dd 100755 --- a/gamefixes-steam/429720.py +++ b/gamefixes-steam/429720.py @@ -2,7 +2,7 @@ import os import getpass -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/43110.py b/gamefixes-steam/43110.py index ea5e5da1..e9641a66 100755 --- a/gamefixes-steam/43110.py +++ b/gamefixes-steam/43110.py @@ -1,6 +1,6 @@ """Game fix for Metro 2033""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/434570.py b/gamefixes-steam/434570.py index 8f04d388..b7e259d0 100755 --- a/gamefixes-steam/434570.py +++ b/gamefixes-steam/434570.py @@ -1,6 +1,6 @@ """Game fix for Blood and Bacon""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/436670.py b/gamefixes-steam/436670.py index 31da2d38..9fdca7be 100644 --- a/gamefixes-steam/436670.py +++ b/gamefixes-steam/436670.py @@ -1,6 +1,6 @@ """Game fix for The Legend of Heroes: Trails in the Sky the 3rd""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/440900.py b/gamefixes-steam/440900.py index 5c272184..ac884b4c 100755 --- a/gamefixes-steam/440900.py +++ b/gamefixes-steam/440900.py @@ -1,6 +1,6 @@ """Game fix for Conan Exiles""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/44690.py b/gamefixes-steam/44690.py index 13583845..52e70b2a 100755 --- a/gamefixes-steam/44690.py +++ b/gamefixes-steam/44690.py @@ -1,6 +1,6 @@ """Game fix for GT Legends""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/447040.py b/gamefixes-steam/447040.py index f8fc893e..b06f23af 100755 --- a/gamefixes-steam/447040.py +++ b/gamefixes-steam/447040.py @@ -1,6 +1,6 @@ """Game fix for Watch_Dogs 2""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/452440.py b/gamefixes-steam/452440.py index 722d8f70..dd4814f6 100644 --- a/gamefixes-steam/452440.py +++ b/gamefixes-steam/452440.py @@ -11,8 +11,8 @@ from subprocess import run import __main__ as protonmain -from protonfixes import util -from protonfixes.logger import log +from .. import util +from ..logger import log def main() -> None: diff --git a/gamefixes-steam/45750.py b/gamefixes-steam/45750.py index a0292dcd..21365dc6 100755 --- a/gamefixes-steam/45750.py +++ b/gamefixes-steam/45750.py @@ -4,7 +4,7 @@ 2. No more than 12 CPU cores (on PCGamingWiki is described as 6, but on my personal test I was able to set until 12 of 16) [source: https://www.pcgamingwiki.com/wiki/Lost_Planet_2#Alternate_solution_for_high_core_CPUs] """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/460120.py b/gamefixes-steam/460120.py index ab7b2f52..c2df5aa2 100755 --- a/gamefixes-steam/460120.py +++ b/gamefixes-steam/460120.py @@ -1,7 +1,6 @@ """Game fix for Megadimension Neptunia VII""" -# -from protonfixes import util +from .. import util # Fixes cinematics not showing or spawning in a different window diff --git a/gamefixes-steam/465280.py b/gamefixes-steam/465280.py index d1bd601b..7277a84d 100755 --- a/gamefixes-steam/465280.py +++ b/gamefixes-steam/465280.py @@ -1,6 +1,6 @@ """Game fix for Yesterday Origins""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/465840.py b/gamefixes-steam/465840.py index e4af6334..90978fd1 100644 --- a/gamefixes-steam/465840.py +++ b/gamefixes-steam/465840.py @@ -1,6 +1,6 @@ """The Last Blade""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/4730.py b/gamefixes-steam/4730.py index 0db01f48..647a7f0b 100644 --- a/gamefixes-steam/4730.py +++ b/gamefixes-steam/4730.py @@ -1,6 +1,6 @@ """Outrun 2006: Coast 2 Coast""" -from protonfixes import util +from .. import util # Fix water rendering as black diff --git a/gamefixes-steam/48190.py b/gamefixes-steam/48190.py index 38e80a64..356b9db1 100755 --- a/gamefixes-steam/48190.py +++ b/gamefixes-steam/48190.py @@ -3,7 +3,7 @@ Game uses an old customized Ubisoft launcher that's currently not working with Proton. """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/49520.py b/gamefixes-steam/49520.py index 8eee00d7..548a750a 100755 --- a/gamefixes-steam/49520.py +++ b/gamefixes-steam/49520.py @@ -1,6 +1,6 @@ """Game fix for Borderlands 2""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/495420.py b/gamefixes-steam/495420.py index 6042b406..626d0442 100755 --- a/gamefixes-steam/495420.py +++ b/gamefixes-steam/495420.py @@ -1,6 +1,6 @@ """Game fix for State of Decay 2""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/497360.py b/gamefixes-steam/497360.py index 4ed5fe3f..d8db19e7 100644 --- a/gamefixes-steam/497360.py +++ b/gamefixes-steam/497360.py @@ -8,7 +8,7 @@ import os import subprocess -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/49900.py b/gamefixes-steam/49900.py index 72898383..c49375e8 100755 --- a/gamefixes-steam/49900.py +++ b/gamefixes-steam/49900.py @@ -1,6 +1,6 @@ """Game fix for Plain Sight""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/508980.py b/gamefixes-steam/508980.py index f5de81a9..671f2ca5 100755 --- a/gamefixes-steam/508980.py +++ b/gamefixes-steam/508980.py @@ -1,7 +1,7 @@ """Game fix for Crashday Redline Edition""" import json -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/518790.py b/gamefixes-steam/518790.py index 76a6f0a5..203961bf 100755 --- a/gamefixes-steam/518790.py +++ b/gamefixes-steam/518790.py @@ -1,6 +1,6 @@ """The Hunter: Call of the Wild""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/550340.py b/gamefixes-steam/550340.py index 535e277b..a35d23d8 100755 --- a/gamefixes-steam/550340.py +++ b/gamefixes-steam/550340.py @@ -1,6 +1,6 @@ """Ougon Musoukyoku""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/559620.py b/gamefixes-steam/559620.py index cb707692..8add8978 100755 --- a/gamefixes-steam/559620.py +++ b/gamefixes-steam/559620.py @@ -1,7 +1,6 @@ """Game fix for Outlaws + A Handful of Missions""" -# -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/570940.py b/gamefixes-steam/570940.py index f3f6844f..1c6caea3 100755 --- a/gamefixes-steam/570940.py +++ b/gamefixes-steam/570940.py @@ -1,6 +1,6 @@ """Game fix Dark Souls Remastered""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/582660.py b/gamefixes-steam/582660.py index 3f976770..0784ac32 100755 --- a/gamefixes-steam/582660.py +++ b/gamefixes-steam/582660.py @@ -1,7 +1,7 @@ """Game fix for Black Desert Online""" import os -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/586140.py b/gamefixes-steam/586140.py index 3e946797..2ec5bbba 100755 --- a/gamefixes-steam/586140.py +++ b/gamefixes-steam/586140.py @@ -3,7 +3,7 @@ Requires disabling the gstreamer protonaudioconverterbin plugin to get full audio in cutscenes """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/593600.py b/gamefixes-steam/593600.py index 95d0a134..7ba0d38d 100755 --- a/gamefixes-steam/593600.py +++ b/gamefixes-steam/593600.py @@ -1,6 +1,6 @@ """Game fix for PixARK""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/601510.py b/gamefixes-steam/601510.py index 3f9c179c..3753568c 100755 --- a/gamefixes-steam/601510.py +++ b/gamefixes-steam/601510.py @@ -1,6 +1,6 @@ """Yu-Gi-Oh Duel Links needs vcrun2019""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/61500.py b/gamefixes-steam/61500.py index 2ecabc91..e851aa22 100755 --- a/gamefixes-steam/61500.py +++ b/gamefixes-steam/61500.py @@ -1,6 +1,6 @@ """Game fix for Age of Wonders""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/6270.py b/gamefixes-steam/6270.py index b6f7da54..f44fa879 100644 --- a/gamefixes-steam/6270.py +++ b/gamefixes-steam/6270.py @@ -1,6 +1,6 @@ """Ducati World Championship""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/627270.py b/gamefixes-steam/627270.py index f009d018..c465b848 100755 --- a/gamefixes-steam/627270.py +++ b/gamefixes-steam/627270.py @@ -1,6 +1,6 @@ """Game fix Injustice 2""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/63110.py b/gamefixes-steam/63110.py index 5059238b..b4253274 100755 --- a/gamefixes-steam/63110.py +++ b/gamefixes-steam/63110.py @@ -2,7 +2,7 @@ Launcher crashes immediately without displaying any windows """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/633230.py b/gamefixes-steam/633230.py index 7c21c952..149a9be3 100755 --- a/gamefixes-steam/633230.py +++ b/gamefixes-steam/633230.py @@ -1,6 +1,6 @@ """Game fix for Naruto To Boruto""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/63700.py b/gamefixes-steam/63700.py index b2e3c0fc..879365e0 100755 --- a/gamefixes-steam/63700.py +++ b/gamefixes-steam/63700.py @@ -1,6 +1,6 @@ """Game fix for BIT.TRIP BEAT""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/63710.py b/gamefixes-steam/63710.py index df0813ba..466c884d 100755 --- a/gamefixes-steam/63710.py +++ b/gamefixes-steam/63710.py @@ -1,6 +1,6 @@ """Game fix for BIT.TRIP RUNNER""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/638160.py b/gamefixes-steam/638160.py index c1873bb0..e93b148e 100755 --- a/gamefixes-steam/638160.py +++ b/gamefixes-steam/638160.py @@ -1,6 +1,6 @@ """Game fix for Moero Chronicle""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/638970.py b/gamefixes-steam/638970.py index 2e58afbe..0cc7fbb1 100755 --- a/gamefixes-steam/638970.py +++ b/gamefixes-steam/638970.py @@ -1,6 +1,6 @@ """Game fix for Yakuza 0""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/644930.py b/gamefixes-steam/644930.py index 07175b99..3c009492 100644 --- a/gamefixes-steam/644930.py +++ b/gamefixes-steam/644930.py @@ -1,6 +1,6 @@ """They Are Billions""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/65540.py b/gamefixes-steam/65540.py index ab7a5626..b324c103 100755 --- a/gamefixes-steam/65540.py +++ b/gamefixes-steam/65540.py @@ -2,7 +2,7 @@ Game fix for Gothic II: Gold Classic """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/65600.py b/gamefixes-steam/65600.py index edd3b888..67937ded 100755 --- a/gamefixes-steam/65600.py +++ b/gamefixes-steam/65600.py @@ -1,7 +1,7 @@ """Game fix for Gothic 3 Forsaken Gods Enhanced Edition""" import os -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/65610.py b/gamefixes-steam/65610.py index d70ddbd3..c92b2b56 100755 --- a/gamefixes-steam/65610.py +++ b/gamefixes-steam/65610.py @@ -1,6 +1,6 @@ """Game fix for Arkania""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/658150.py b/gamefixes-steam/658150.py index e617e7d3..f90d7435 100755 --- a/gamefixes-steam/658150.py +++ b/gamefixes-steam/658150.py @@ -1,6 +1,6 @@ """Skeleton Boomerang""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/658260.py b/gamefixes-steam/658260.py index 75a90e69..c61b5734 100644 --- a/gamefixes-steam/658260.py +++ b/gamefixes-steam/658260.py @@ -2,7 +2,7 @@ Missing voices/sounds in cutscenes """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/65930.py b/gamefixes-steam/65930.py index 2fd9d81f..0eeda447 100755 --- a/gamefixes-steam/65930.py +++ b/gamefixes-steam/65930.py @@ -1,6 +1,6 @@ """Game fix for The Bureau: XCOM Declassified""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/678950.py b/gamefixes-steam/678950.py index bd76153b..6e576681 100755 --- a/gamefixes-steam/678950.py +++ b/gamefixes-steam/678950.py @@ -1,6 +1,6 @@ """Game fix for DRAGON BALL FighterZ""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/700600.py b/gamefixes-steam/700600.py index f0bca178..00437935 100755 --- a/gamefixes-steam/700600.py +++ b/gamefixes-steam/700600.py @@ -1,6 +1,6 @@ """Game fix for Evil Genius 2""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/702050.py b/gamefixes-steam/702050.py index 30f2abbd..81d4ed14 100755 --- a/gamefixes-steam/702050.py +++ b/gamefixes-steam/702050.py @@ -1,6 +1,6 @@ """Game fix for The Song of Saya""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/70400.py b/gamefixes-steam/70400.py index 23bde81a..dba5f4a9 100755 --- a/gamefixes-steam/70400.py +++ b/gamefixes-steam/70400.py @@ -1,6 +1,6 @@ """Game fix for Recettear: An Item Shop's Tale""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/70420.py b/gamefixes-steam/70420.py index 4be9e8fd..3fe708ad 100755 --- a/gamefixes-steam/70420.py +++ b/gamefixes-steam/70420.py @@ -1,6 +1,6 @@ """Game fix for Chantelise - A Tale of Two Sisters""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/70650.py b/gamefixes-steam/70650.py index a91f43ab..6ebdfb07 100755 --- a/gamefixes-steam/70650.py +++ b/gamefixes-steam/70650.py @@ -1,6 +1,6 @@ """Game fix for Worms: Blast""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/729040.py b/gamefixes-steam/729040.py index 93efeef5..a5569cba 100755 --- a/gamefixes-steam/729040.py +++ b/gamefixes-steam/729040.py @@ -1,6 +1,6 @@ """Borderlands GOTY""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/730830.py b/gamefixes-steam/730830.py index 834603f5..5100daca 100755 --- a/gamefixes-steam/730830.py +++ b/gamefixes-steam/730830.py @@ -2,7 +2,7 @@ dgvoodoo2 to force anti-aliasing and higher resolution """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/73170.py b/gamefixes-steam/73170.py index daaecd3a..dd5e6e93 100755 --- a/gamefixes-steam/73170.py +++ b/gamefixes-steam/73170.py @@ -1,6 +1,6 @@ """Game fix for Darkest Hour: A Hearts of Iron Game""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/740550.py b/gamefixes-steam/740550.py index acf79ac0..6422bb3a 100755 --- a/gamefixes-steam/740550.py +++ b/gamefixes-steam/740550.py @@ -1,6 +1,6 @@ """Game fix for Record of Agarest War Mariage""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/750920.py b/gamefixes-steam/750920.py index c75b6665..f487b990 100755 --- a/gamefixes-steam/750920.py +++ b/gamefixes-steam/750920.py @@ -1,6 +1,6 @@ """Shadow of the Tomb Raider""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/773370.py b/gamefixes-steam/773370.py index e276cc19..cfb1cff5 100755 --- a/gamefixes-steam/773370.py +++ b/gamefixes-steam/773370.py @@ -1,6 +1,6 @@ """Exo One""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/78000.py b/gamefixes-steam/78000.py index 4de6d698..3a4ca497 100755 --- a/gamefixes-steam/78000.py +++ b/gamefixes-steam/78000.py @@ -1,6 +1,6 @@ """Game fix for Bejeweled 3""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/782330.py b/gamefixes-steam/782330.py index b29e1eb6..7ea35ffe 100755 --- a/gamefixes-steam/782330.py +++ b/gamefixes-steam/782330.py @@ -1,6 +1,6 @@ """DOOM Eternal""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/7850.py b/gamefixes-steam/7850.py index f7aed4f7..6f50a27c 100755 --- a/gamefixes-steam/7850.py +++ b/gamefixes-steam/7850.py @@ -1,6 +1,6 @@ """Game fix for Cryostasis""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/812140.py b/gamefixes-steam/812140.py index 8603cdeb..0baf2703 100755 --- a/gamefixes-steam/812140.py +++ b/gamefixes-steam/812140.py @@ -1,6 +1,6 @@ """Assassin's Creed: Odyssey""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/813780.py b/gamefixes-steam/813780.py index 69fa6976..5b50c5af 100755 --- a/gamefixes-steam/813780.py +++ b/gamefixes-steam/813780.py @@ -1,6 +1,6 @@ """Game fix Age of Empires II: DE""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/816020.py b/gamefixes-steam/816020.py index cfa473ac..2b6244c9 100755 --- a/gamefixes-steam/816020.py +++ b/gamefixes-steam/816020.py @@ -1,6 +1,6 @@ """Game fix for JUMP FORCE""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/8190.py b/gamefixes-steam/8190.py index 1faf89a7..583ff940 100755 --- a/gamefixes-steam/8190.py +++ b/gamefixes-steam/8190.py @@ -1,6 +1,6 @@ """Just Cause 2""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/834530.py b/gamefixes-steam/834530.py index d6e9d33b..5372f063 100755 --- a/gamefixes-steam/834530.py +++ b/gamefixes-steam/834530.py @@ -1,6 +1,6 @@ """Game fix for Yakuza Kiwami""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/888790.py b/gamefixes-steam/888790.py index 862d74ca..a466832a 100644 --- a/gamefixes-steam/888790.py +++ b/gamefixes-steam/888790.py @@ -1,6 +1,6 @@ """Game fix for Sabbat of the Witch""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/893180.py b/gamefixes-steam/893180.py index 02c34e38..6e15bb58 100755 --- a/gamefixes-steam/893180.py +++ b/gamefixes-steam/893180.py @@ -1,6 +1,6 @@ """Game fix Catherine Classic""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/895870.py b/gamefixes-steam/895870.py index 282cab9e..6472e125 100755 --- a/gamefixes-steam/895870.py +++ b/gamefixes-steam/895870.py @@ -1,6 +1,6 @@ """Project Wingman""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/906510.py b/gamefixes-steam/906510.py index 849292fa..0939000e 100755 --- a/gamefixes-steam/906510.py +++ b/gamefixes-steam/906510.py @@ -1,6 +1,6 @@ """Game fix for Conception PLUS: Maidens of the Twelve Stars""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/910830.py b/gamefixes-steam/910830.py index 971483a0..1fff8ca1 100755 --- a/gamefixes-steam/910830.py +++ b/gamefixes-steam/910830.py @@ -1,6 +1,6 @@ """Game fix for Rebel Galaxy Outlaw""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/913740.py b/gamefixes-steam/913740.py index e43d05ae..09f40bfb 100755 --- a/gamefixes-steam/913740.py +++ b/gamefixes-steam/913740.py @@ -1,6 +1,6 @@ """Game fix for WORLD OF HORROR""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/9350.py b/gamefixes-steam/9350.py index f690990b..b57fd0c2 100644 --- a/gamefixes-steam/9350.py +++ b/gamefixes-steam/9350.py @@ -1,6 +1,6 @@ """Game fix for Supreme Commander""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/936160.py b/gamefixes-steam/936160.py index 7ba8e801..89599bc9 100644 --- a/gamefixes-steam/936160.py +++ b/gamefixes-steam/936160.py @@ -5,7 +5,7 @@ further stolen from marianoag by bitwolf """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/950670.py b/gamefixes-steam/950670.py index c131069b..aedb11ef 100644 --- a/gamefixes-steam/950670.py +++ b/gamefixes-steam/950670.py @@ -1,8 +1,8 @@ """Game fix for Gothic Playable Teaser""" from pathlib import Path -from protonfixes import util -from protonfixes.logger import log +from .. import util +from ..logger import log def main() -> None: diff --git a/gamefixes-steam/955050.py b/gamefixes-steam/955050.py index acf345d6..1c066987 100755 --- a/gamefixes-steam/955050.py +++ b/gamefixes-steam/955050.py @@ -1,6 +1,6 @@ """Bright Memory""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/963930.py b/gamefixes-steam/963930.py index 132c167f..bd1c0e37 100644 --- a/gamefixes-steam/963930.py +++ b/gamefixes-steam/963930.py @@ -1,6 +1,6 @@ """Contractors VR""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/968370.py b/gamefixes-steam/968370.py index c1e442ef..cce242d3 100755 --- a/gamefixes-steam/968370.py +++ b/gamefixes-steam/968370.py @@ -2,7 +2,7 @@ garbled fonts & No cursive font (Segoe Script) """ -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/976310.py b/gamefixes-steam/976310.py index aa3415f6..3bd39544 100755 --- a/gamefixes-steam/976310.py +++ b/gamefixes-steam/976310.py @@ -1,6 +1,6 @@ """Game fix Mortal Kombat 11""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/976730.py b/gamefixes-steam/976730.py index bed84fd6..7ef19edd 100755 --- a/gamefixes-steam/976730.py +++ b/gamefixes-steam/976730.py @@ -1,6 +1,6 @@ """Game fix Halo:MCC""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/9900.py b/gamefixes-steam/9900.py index 01b157f2..e31fa912 100755 --- a/gamefixes-steam/9900.py +++ b/gamefixes-steam/9900.py @@ -1,6 +1,6 @@ """Game fix for Star Trek Online launcher""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/997070.py b/gamefixes-steam/997070.py index 089a93ea..d0e0a2bf 100755 --- a/gamefixes-steam/997070.py +++ b/gamefixes-steam/997070.py @@ -1,6 +1,6 @@ """Game fix Marvel's Avengers""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-steam/default.py b/gamefixes-steam/default.py index cabf10ae..d470b0d6 100755 --- a/gamefixes-steam/default.py +++ b/gamefixes-steam/default.py @@ -4,7 +4,7 @@ """ import sys -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-umu/umu-2016590.py b/gamefixes-umu/umu-2016590.py index dc3369c2..f487b600 100644 --- a/gamefixes-umu/umu-2016590.py +++ b/gamefixes-umu/umu-2016590.py @@ -1,6 +1,6 @@ """Game fix for Dark and Darker""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-umu/umu-39190.py b/gamefixes-umu/umu-39190.py index 341a9795..b7018e03 100644 --- a/gamefixes-umu/umu-39190.py +++ b/gamefixes-umu/umu-39190.py @@ -1,6 +1,6 @@ """Game fix for Dungeon Siege""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-umu/umu-model2.py b/gamefixes-umu/umu-model2.py index 32b7e490..6760189e 100755 --- a/gamefixes-umu/umu-model2.py +++ b/gamefixes-umu/umu-model2.py @@ -1,7 +1,6 @@ """Application fix Model 2 emulator""" -# -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-umu/umu-silenthill3.py b/gamefixes-umu/umu-silenthill3.py index 5c294a59..9edce6cf 100644 --- a/gamefixes-umu/umu-silenthill3.py +++ b/gamefixes-umu/umu-silenthill3.py @@ -1,6 +1,6 @@ """Game fix for Silent Hill 3""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-umu/umu-starcitizen.py b/gamefixes-umu/umu-starcitizen.py index 64ea0b30..00867399 100644 --- a/gamefixes-umu/umu-starcitizen.py +++ b/gamefixes-umu/umu-starcitizen.py @@ -1,6 +1,6 @@ """Game fix for Star Citizen""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-umu/umu-zenlesszonezero.py b/gamefixes-umu/umu-zenlesszonezero.py index b25d31b4..63c3e0ab 100644 --- a/gamefixes-umu/umu-zenlesszonezero.py +++ b/gamefixes-umu/umu-zenlesszonezero.py @@ -1,9 +1,8 @@ """Game fix for Zenless Zone Zero""" -from protonfixes import util +from .. import util def main() -> None: """Needs gamedrive fix to detect proper install space""" util.set_game_drive(True) - diff --git a/gamefixes-umu/winetricks-gui.py b/gamefixes-umu/winetricks-gui.py index aa308f3e..e866e372 100755 --- a/gamefixes-umu/winetricks-gui.py +++ b/gamefixes-umu/winetricks-gui.py @@ -1,6 +1,6 @@ """Call Winetricks GUI""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-zoomplatform/umu-240200.py b/gamefixes-zoomplatform/umu-240200.py index af4740bf..738e9da3 100644 --- a/gamefixes-zoomplatform/umu-240200.py +++ b/gamefixes-zoomplatform/umu-240200.py @@ -1,6 +1,6 @@ """Duke Nukem: Manhattan Project - Enhanced Edition""" -from protonfixes import util +from .. import util def main() -> None: diff --git a/gamefixes-zoomplatform/umu-4bff76f4-566a-4714-b481-95d3343afe22.py b/gamefixes-zoomplatform/umu-4bff76f4-566a-4714-b481-95d3343afe22.py index 0972f586..3f4e5344 100644 --- a/gamefixes-zoomplatform/umu-4bff76f4-566a-4714-b481-95d3343afe22.py +++ b/gamefixes-zoomplatform/umu-4bff76f4-566a-4714-b481-95d3343afe22.py @@ -1,6 +1,6 @@ """Incoming Trilogy""" -from protonfixes import util +from .. import util def main() -> None: From ec4a37d925389c5eb04c3e1e4e4d8c9c6c1283da Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Wed, 9 Oct 2024 14:10:20 +0200 Subject: [PATCH 06/21] Improve get_game_install_path() logic --- util.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/util.py b/util.py index f429eebb..05b65379 100644 --- a/util.py +++ b/util.py @@ -406,11 +406,8 @@ def del_environment(envvar: str) -> None: def get_game_install_path() -> str: """Game installation path""" - install_path = os.environ['PWD'] - if 'STEAM_COMPAT_INSTALL_PATH' in os.environ: - install_path = os.environ['STEAM_COMPAT_INSTALL_PATH'] - log.debug('Detected path to game: ' + install_path) - # only for `waitforexitandrun` command + install_path = os.environ.get('STEAM_COMPAT_INSTALL_PATH', os.getcwd()) + log.debug(f'Detected path to game: {install_path}') return install_path From a51a512addf8b03b273e0344484fbb5b4fa3a3ef Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Thu, 10 Oct 2024 21:50:20 +0200 Subject: [PATCH 07/21] Fixed several type errors - Correctly override optionxform - String can not be None - Fixed import of urllib - Exit if game id is not present - Allow paths to be str or Path --- .github/scripts/check_verbs.py | 6 +++--- engine.py | 8 ++++---- fix.py | 2 +- util.py | 31 ++++++++++++++++++------------- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/.github/scripts/check_verbs.py b/.github/scripts/check_verbs.py index c72c6c5e..0bb815cd 100644 --- a/.github/scripts/check_verbs.py +++ b/.github/scripts/check_verbs.py @@ -4,7 +4,7 @@ import re import subprocess -from glob import iglob +from typing import Generator from pathlib import Path from tempfile import mkdtemp @@ -13,7 +13,7 @@ whitelist_verbs = {'gui', 'vd=1280x720'} -def extract_verbs_from_glob(path_glob: iglob) -> set[str]: +def extract_verbs_from_glob(path_glob: Generator[Path, None, None]) -> set[str]: """Simply strip the extension from all found files.""" return {file.stem for file in path_glob} @@ -48,7 +48,7 @@ def find_valid_verbs(root: Path) -> set[str]: # Setup environment variables env = os.environ.copy() - env['TMPDIR'] = tmp_dir + env['TMPDIR'] = str(tmp_dir) env['WINETRICKS_LATEST_VERSION_CHECK'] = 'disabled' # Execute winetricks, suppress output diff --git a/engine.py b/engine.py index e19a777d..fdbbbca4 100644 --- a/engine.py +++ b/engine.py @@ -9,7 +9,7 @@ class Engine: """Game engines""" def __init__(self) -> None: # noqa: D107 - self.engine_name = None + self.engine_name = '' self.supported = { 'Dunia 2': 'https://pcgamingwiki.com/wiki/Engine:Dunia_2', 'Unity': 'https://pcgamingwiki.com/wiki/Engine:Unity', @@ -33,7 +33,7 @@ def __init__(self) -> None: # noqa: D107 else: log.info('Engine: unknown Game engine') - if self.engine_name is not None: + if self.engine_name: log.info('Engine: ' + self.engine_name) log.info('Engine: ' + self.supported[self.engine_name]) @@ -101,7 +101,7 @@ def name(self) -> str: """Report Engine name""" return self.engine_name - def set(self, _engine: str = None) -> bool: + def set(self, _engine: str) -> bool: """Force engine""" if _engine in self.supported: self.engine_name = _engine @@ -167,7 +167,7 @@ def windowed(self) -> bool: return False return True - def resolution(self, res: str = None) -> bool: + def resolution(self, res: str) -> bool: """Force screen resolution""" if not isinstance(res, str): self._log('resolution', 'not provided') diff --git a/fix.py b/fix.py index bf3759be..4e85cdfe 100644 --- a/fix.py +++ b/fix.py @@ -37,7 +37,7 @@ def get_game_id() -> str: return re.findall(r'\d+', os.environ['STEAM_COMPAT_DATA_PATH'])[-1] log.crit('Game ID not found in environment variables') - return None + exit() @lru_cache diff --git a/util.py b/util.py index 05b65379..de9aca96 100644 --- a/util.py +++ b/util.py @@ -1,7 +1,6 @@ """Utilities to make gamefixes easier""" import configparser -from io import TextIOWrapper import os import sys import re @@ -12,6 +11,8 @@ import subprocess import urllib.request import functools +from io import TextIOWrapper +from pathlib import Path from socket import socket, AF_INET, SOCK_DGRAM from typing import Literal, Any, Callable, Union, Optional from collections.abc import Mapping, Generator @@ -30,6 +31,7 @@ exit() # TypeAliases +StrPath = Union[str, Path] BasePathType = Literal['user', 'game'] OverrideTypes = Literal['n', 'b', 'n,b', 'b,n', ''] @@ -586,7 +588,7 @@ def disable_uplay_overlay() -> bool: def create_dosbox_conf( - conf_file: str, conf_dict: Mapping[str, Mapping[str, Any]] + conf_file: StrPath, conf_dict: Mapping[str, Mapping[str, Any]] ) -> None: """Create DOSBox configuration file. @@ -644,7 +646,7 @@ def _get_case_insensitive_name(path: str) -> str: return root -def _get_config_full_path(cfile: str, base_path: BasePathType) -> Optional[str]: +def _get_config_full_path(cfile: StrPath, base_path: BasePathType) -> Optional[str]: """Find game's config file""" # Start from 'user'/'game' directories or absolute path if base_path == 'user': @@ -656,7 +658,7 @@ def _get_config_full_path(cfile: str, base_path: BasePathType) -> Optional[str]: cfg_path = os.path.join(get_game_install_path(), cfile) else: cfg_path = cfile - cfg_path = _get_case_insensitive_name(cfg_path) + cfg_path = _get_case_insensitive_name(str(cfg_path)) if os.path.exists(cfg_path) and os.access(cfg_path, os.F_OK): log.debug('Found config file: ' + cfg_path) @@ -675,7 +677,7 @@ def create_backup_config(cfg_path: str) -> None: def set_ini_options( - ini_opts: str, cfile: str, encoding: str, base_path: BasePathType = 'user' + ini_opts: str, cfile: StrPath, encoding: str, base_path: BasePathType = 'user' ) -> bool: """Edit game's INI config file""" cfg_path = _get_config_full_path(cfile, base_path) @@ -688,7 +690,8 @@ def set_ini_options( conf = configparser.ConfigParser( empty_lines_in_values=True, allow_no_value=True, strict=False ) - conf.optionxform = str + # Preserve option name case (default converts to lower case) + conf.optionxform = lambda optionstr: optionstr conf.read(cfg_path, encoding) @@ -701,7 +704,7 @@ def set_ini_options( def set_xml_options( - base_attibutte: str, xml_line: str, cfile: str, base_path: BasePathType = 'user' + base_attibutte: str, xml_line: str, cfile: StrPath, base_path: BasePathType = 'user' ) -> bool: """Edit game's XML config file""" xml_path = _get_config_full_path(cfile, base_path) @@ -762,14 +765,15 @@ def read_dxvk_conf(cfp: TextIOWrapper) -> Generator[str, None, None]: def set_dxvk_option( - opt: str, val: str, cfile: str = '/tmp/protonfixes_dxvk.conf' + opt: str, val: str, cfile: StrPath = '/tmp/protonfixes_dxvk.conf' ) -> None: """Create custom DXVK config file See https://github.com/doitsujin/dxvk/wiki/Configuration for details """ conf = configparser.ConfigParser() - conf.optionxform = str + # Preserve option name case (default converts to lower case) + conf.optionxform = lambda optionstr: optionstr section = conf.default_section dxvk_conf = os.path.join(os.environ['PWD'], 'dxvk.conf') @@ -780,10 +784,11 @@ def set_dxvk_option( or conf.getint(section, 'session') != os.getpid() ): log.info('Creating new DXVK config') - set_environment('DXVK_CONFIG_FILE', cfile) + set_environment('DXVK_CONFIG_FILE', str(cfile)) conf = configparser.ConfigParser() - conf.optionxform = str + # Preserve option name case (default converts to lower case) + conf.optionxform = lambda optionstr: optionstr conf.set(section, 'session', str(os.getpid())) if os.access(dxvk_conf, os.F_OK): @@ -809,7 +814,7 @@ def install_battleye_runtime() -> None: install_app('1161040') -def install_all_from_tgz(url: str, path: str = os.getcwd()) -> None: +def install_all_from_tgz(url: str, path: StrPath = os.getcwd()) -> None: """Install all files from a downloaded tar.gz""" cache_dir = os.path.expanduser('~/.cache/protonfixes') os.makedirs(cache_dir, exist_ok=True) @@ -825,7 +830,7 @@ def install_all_from_tgz(url: str, path: str = os.getcwd()) -> None: tgz_obj.extractall(path) -def install_from_zip(url: str, filename: str, path: str = os.getcwd()) -> None: +def install_from_zip(url: str, filename: str, path: StrPath = os.getcwd()) -> None: """Install a file from a downloaded zip""" if filename in os.listdir(path): log.info(f'File {filename} found in {path}') From 02a02136571176c6d4000ea9989ced153aa4cf86 Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Sun, 13 Oct 2024 22:37:43 +0200 Subject: [PATCH 08/21] Rewrite config - Typed config - Separate sections in object - Easy and clean interface - Reusable --- config.py | 60 +++++++----------------- config_base.py | 123 +++++++++++++++++++++++++++++++++++++++++++++++++ fix.py | 10 ++-- 3 files changed, 144 insertions(+), 49 deletions(-) create mode 100644 config_base.py diff --git a/config.py b/config.py index 0743be36..7365499b 100644 --- a/config.py +++ b/config.py @@ -1,46 +1,18 @@ """Load configuration settings for protonfixes""" -import os -from configparser import ConfigParser - -try: - from .logger import log -except ImportError: - from logger import log - - -CONF_FILE = '~/.config/protonfixes/config.ini' -DEFAULT_CONF = """ -[main] -enable_checks = true -enable_splash = false -enable_global_fixes = true - - -[path] -cache_dir = ~/.cache/protonfixes -""" - -CONF = ConfigParser() -CONF.read_string(DEFAULT_CONF) - -try: - CONF.read(os.path.expanduser(CONF_FILE)) - -except Exception: - log.debug('Unable to read config file ' + CONF_FILE) - - -def opt_bool(opt: str) -> bool: - """Convert bool ini strings to actual boolean values""" - return opt.lower() in ['yes', 'y', 'true', '1'] - - -locals().update({x: opt_bool(y) for x, y in CONF['main'].items() if 'enable' in x}) - -locals().update({x: os.path.expanduser(y) for x, y in CONF['path'].items()}) - -try: - [os.makedirs(os.path.expanduser(d)) for n, d in CONF['path'].items()] -except OSError: - pass +from config_base import ConfigBase +from dataclasses import dataclass +from pathlib import Path + +class Config(ConfigBase): + @dataclass + class MainSection: + enable_checks: bool = True + enable_splash: bool = False + enable_global_fixes: bool = True + + @dataclass + class PathSection: + cache_dir: Path = Path.home() / '.cache/protonfixes' + +config = Config(Path.home() / '.config/protonfixes/config.ini') diff --git a/config_base.py b/config_base.py new file mode 100644 index 00000000..4caf39c0 --- /dev/null +++ b/config_base.py @@ -0,0 +1,123 @@ +"""Load configuration settings for protonfixes""" + +import re + +from configparser import ConfigParser +from dataclasses import dataclass, is_dataclass +from pathlib import Path + +from logger import log, LogLevelType + +class ConfigBase: + __CAMEL_CASE_PATTERN: re.Pattern = re.compile('((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))') + + @classmethod + def snake_case(cls, input: str) -> str: + # Convert CamelCase to snake_case + return cls.__CAMEL_CASE_PATTERN.sub(r'_\1', input).lower() + + + @staticmethod + def __log(message: str, level: LogLevelType = 'INFO') -> None: + log.log(f'[CONFIG]: {message}', level) + + + def __init__(self, path: Path) -> None: + assert path + if path.is_file(): + self.parse_config_file(path) + elif not path.exists(): + self.init_sections() + self.write_config_file(path) + else: + raise IsADirectoryError(f'Given path "{path.absolute()}" exists, but is not a file.') + + + def init_sections(self, force: bool = False) -> None: + for (member_name, member) in self.__class__.__dict__.items(): + # Find non private section definitions + if not member_name.endswith('Section') or member_name.startswith('_'): + continue + if not is_dataclass(member): + continue + + # Convert section definition class name to variable name (MyCfgSection -> my_cfg) + section_name = member_name.removesuffix('Section') + section_name = self.snake_case(section_name) + + # Do not override existing members by default + if hasattr(self, section_name) and not force: + continue + + # Initialize section class as a member + setattr(self, section_name, member()) + + + def parse_config_file(self, file: Path) -> bool: + # Initialize / reset sections to defaults + self.init_sections(True) + + # Only precede if the config file exists + if not file.is_file(): + return False + + try: + parser = ConfigParser() + parser.read(file) + + # Iterate over local config section objects + for (section_name, section) in self.__dict__.items(): + if not parser.has_section(section_name): + continue + + parser_items = parser[section_name] + + # Iterate over the option objects in this section + for (option_name, option_item) in section.__dict__.items(): + # Match type of local object + match type(option_item).__name__: + case 'int': + setattr(section, option_name, parser_items.getint(option_name)) + case 'float': + setattr(section, option_name, parser_items.getfloat(option_name)) + case 'bool': + setattr(section, option_name, parser_items.getboolean(option_name)) + case 'Path': + setattr(section, option_name, Path(parser_items.get(option_name))) + case 'PosixPath': + setattr(section, option_name, Path(parser_items.get(option_name))) + case 'str': + setattr(section, option_name, parser_items.get(option_name)) + case _: + setattr(section, option_name, parser_items.get(option_name)) + self.__log(f'[CONFIG]: Type mismatch') + except Exception as ex: + self.__log(f'Failed to parse config file "{file}". Exception: "{ex}"', 'CRIT') + return False + return True + + + def write_config_file(self, file: Path) -> bool: + # Only precede if the parent directory exists + if not file.parent.is_dir(): + self.__log(f'Parent directory "{file.parent}" does not exist. Abort.', 'WARN') + return False + + # Create and populate ConfigParser + try: + parser = ConfigParser() + # Iterate over local config section objects + for (section_name, section_item) in self.__dict__.items(): + if not parser.has_section(section_name): + parser.add_section(section_name) + + for (option_name, option_item) in section_item.__dict__.items(): + parser.set(section_name, option_name, str(option_item)) + + # Write config file + with file.open(mode='w') as stream: + parser.write(stream) + except Exception as ex: + self.__log(f'Failed to create config file "{file}". Exception: "{ex}"', 'CRIT') + return False + return True diff --git a/fix.py b/fix.py index 4e85cdfe..605f017c 100644 --- a/fix.py +++ b/fix.py @@ -9,11 +9,11 @@ from typing import Optional try: - from . import config + from .config import config from .checks import run_checks from .logger import log except ImportError: - import config + from config import config from checks import run_checks from logger import log @@ -174,15 +174,15 @@ def run_fix(game_id: str) -> None: if game_id is None: return - if config.enable_checks: + if config.main.enable_checks: run_checks() # execute default.py (local) - if not _run_fix_local(game_id, True) and config.enable_global_fixes: + if not _run_fix_local(game_id, True) and config.main.enable_global_fixes: _run_fix(game_id, True) # global # execute .py (local) - if not _run_fix_local(game_id, False) and config.enable_global_fixes: + if not _run_fix_local(game_id, False) and config.main.enable_global_fixes: _run_fix(game_id, False) # global From 19f61d016bf5292209164618fbc70424ce2fbc0f Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Sun, 13 Oct 2024 22:38:54 +0200 Subject: [PATCH 09/21] Refactor: match is not supported in Python 3.9 --- config_base.py | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/config_base.py b/config_base.py index 4caf39c0..232c26b9 100644 --- a/config_base.py +++ b/config_base.py @@ -6,6 +6,9 @@ from dataclasses import dataclass, is_dataclass from pathlib import Path +from typing import Any +from collections.abc import Callable + from logger import log, LogLevelType class ConfigBase: @@ -50,7 +53,7 @@ def init_sections(self, force: bool = False) -> None: continue # Initialize section class as a member - setattr(self, section_name, member()) + setattr(self, section_name, member()) # pyright: ignore [reportCallIssue] def parse_config_file(self, file: Path) -> bool: @@ -72,25 +75,29 @@ def parse_config_file(self, file: Path) -> bool: parser_items = parser[section_name] + # FIXME: match is not supported in Python 3.9 + def _get_parse_function(type_name: str) -> Callable[[str], Any]: + # Mapping of type_name to according value get function + value = { + 'int': parser_items.getint, + 'float': parser_items.getfloat, + 'bool': parser_items.getboolean, + 'Path': lambda option: Path(parser_items.get(option, '')), + 'PosixPath': lambda option: Path(parser_items.get(option, '')), + 'str': parser_items.get + }.get(type_name, None) + if not value: + value = parser_items.get + self.__log(f'Unknown type "{type_name}", falling back to "str".') + return value + # Iterate over the option objects in this section for (option_name, option_item) in section.__dict__.items(): - # Match type of local object - match type(option_item).__name__: - case 'int': - setattr(section, option_name, parser_items.getint(option_name)) - case 'float': - setattr(section, option_name, parser_items.getfloat(option_name)) - case 'bool': - setattr(section, option_name, parser_items.getboolean(option_name)) - case 'Path': - setattr(section, option_name, Path(parser_items.get(option_name))) - case 'PosixPath': - setattr(section, option_name, Path(parser_items.get(option_name))) - case 'str': - setattr(section, option_name, parser_items.get(option_name)) - case _: - setattr(section, option_name, parser_items.get(option_name)) - self.__log(f'[CONFIG]: Type mismatch') + # Get values from config and set it on object + type_name = type(option_item).__name__ + func = _get_parse_function(type_name) + value = func(option_name) + setattr(section, option_name, value) except Exception as ex: self.__log(f'Failed to parse config file "{file}". Exception: "{ex}"', 'CRIT') return False From f49e7fa0beda1037781d6ecba37c24587f9fb6f7 Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Sun, 13 Oct 2024 22:45:19 +0200 Subject: [PATCH 10/21] Logger rewrite - use print functions instead of manual - class methods - caching - format string --- engine.py | 22 +++++++++---------- logger.py | 66 +++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 52 insertions(+), 36 deletions(-) diff --git a/engine.py b/engine.py index fdbbbca4..8975dceb 100644 --- a/engine.py +++ b/engine.py @@ -2,7 +2,7 @@ import os import sys -from .logger import log +from .logger import log, LogLevelType class Engine: @@ -88,14 +88,12 @@ def _is_ue4(self) -> bool: """Detect Unreal Engine 4""" return False - def _log(self, ctx: str, msg: str, warn: bool = False) -> None: + def _log(self, ctx: str, msg: str, level: LogLevelType = 'INFO') -> None: """Log wrapper""" if self.engine_name is None: log.warn(ctx + ': Engine not defined') return - - log_func = log.warn if warn else log.info - log_func(f'{self.engine_name}: {ctx}: {msg}') + log.log(f'{self.engine_name}: {ctx}: {msg}', level) def name(self) -> str: """Report Engine name""" @@ -117,7 +115,7 @@ def nosplash(self) -> bool: self._add_argument('-nosplash') self._log('nosplash', 'splash screen disabled') else: - self._log('nosplash', 'not supported', True) + self._log('nosplash', 'not supported', 'WARN') return False return True @@ -127,7 +125,7 @@ def info(self) -> bool: self._add_argument('-help') self._log('info', 'command line arguments') else: - self._log('info', 'not supported', True) + self._log('info', 'not supported', 'WARN') return False return True @@ -140,7 +138,7 @@ def nointro(self) -> bool: self._add_argument('-skipintro') self._log('nointro', 'intro videos disabled') else: - self._log('nointro', 'not supported', True) + self._log('nointro', 'not supported', 'WARN') return False return True @@ -150,7 +148,7 @@ def launcher(self) -> bool: self._add_argument('-show-screen-selector') self._log('launcher', 'forced') else: - self._log('launcher', 'not supported', True) + self._log('launcher', 'not supported', 'WARN') return False return True @@ -163,14 +161,14 @@ def windowed(self) -> bool: self._add_argument('-windowed') self._log('windowed', 'window') else: - self._log('windowed', 'not supported', True) + self._log('windowed', 'not supported', 'WARN') return False return True def resolution(self, res: str) -> bool: """Force screen resolution""" if not isinstance(res, str): - self._log('resolution', 'not provided') + self._log('resolution', 'not provided', 'WARN') return False res_wh = res.split('x') @@ -184,7 +182,7 @@ def resolution(self, res: str) -> bool: self._add_argument('-width ' + res_wh[0] + ' -height ' + res_wh[1]) self._log('resolution', res) else: - self._log('resolution', 'not supported', True) + self._log('resolution', 'not supported', 'WARN') return False return True diff --git a/logger.py b/logger.py index 5905d6c6..90c4c1da 100644 --- a/logger.py +++ b/logger.py @@ -3,52 +3,70 @@ import os import sys +from typing import Literal +from functools import lru_cache + +# TypeAliases +LogLevelType = Literal['INFO', 'WARN', 'CRIT', 'DEBUG'] class Log: """Log to stderr for steam dumps""" - def __init__(self) -> None: # noqa: D107 - self.pfx = 'ProtonFixes[' + str(os.getpid()) + '] ' - self.colors = { + pfx = f'ProtonFixes[{os.getpid()}]' + + @staticmethod + @lru_cache + def __get_color(level: LogLevelType) -> str: + return { 'RESET': '\u001b[0m', 'INFO': '\u001b[34m', 'WARN': '\u001b[33m', 'CRIT': '\u001b[31m', 'DEBUG': '\u001b[35m', - } + }.get(level, '') - def __call__(self, msg: str) -> None: + @classmethod + def __colorize(cls, msg: str, level: LogLevelType) -> str: + color = cls.__get_color(level) + reset = cls.__get_color('RESET') + return f'{color}{msg}{reset}' + + @classmethod + def __call__(cls, msg: str) -> None: """Allows the Log instance to be called directly""" - self.log(msg) + cls.log(msg) - def log(self, msg: str = '', level: str = 'INFO') -> None: + @classmethod + def log(cls, msg: str = '', level: LogLevelType = 'INFO') -> None: """Prints the log message to stdout the same way as Proton""" - pfx = self.pfx + level + ': ' - color = self.colors[level] - reset = self.colors['RESET'] - logtext = pfx + str(msg) + os.linesep - fulltext = color + pfx + str(msg) + reset + os.linesep - sys.stderr.write(fulltext) - sys.stderr.flush() + # To terminal + print(cls.__colorize(f'{cls.pfx} {level}: {msg}', level), file=sys.stderr, flush=True) + + # To log file with open('/tmp/test', 'a', 1, encoding='utf-8') as testfile: - testfile.write(logtext) + print(f'{cls.pfx} {level}: {msg}', file=testfile) - def info(self, msg: str) -> None: + @classmethod + def info(cls, msg: str) -> None: """Wrapper for printing info messages""" - self.log(msg, 'INFO') + cls.log(msg, 'INFO') - def warn(self, msg: str) -> None: + @classmethod + def warn(cls, msg: str) -> None: """Wrapper for printing warning messages""" - self.log(msg, 'WARN') + cls.log(msg, 'WARN') - def crit(self, msg: str) -> None: + @classmethod + def crit(cls, msg: str) -> None: """Wrapper for printing critical messages""" - self.log(msg, 'CRIT') + cls.log(msg, 'CRIT') - def debug(self, msg: str) -> None: + @classmethod + def debug(cls, msg: str) -> None: """Wrapper for printing debug messages""" - if 'DEBUG' in os.environ: - self.log(msg, 'DEBUG') + if 'DEBUG' not in os.environ: + return + cls.log(msg, 'DEBUG') log = Log() From d738146d196dd249b21dd9262dd6ff319c408620 Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Sun, 13 Oct 2024 23:14:48 +0200 Subject: [PATCH 11/21] Added config sections explicitly to enable static type checking --- config.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config.py b/config.py index 7365499b..0154ca37 100644 --- a/config.py +++ b/config.py @@ -15,4 +15,7 @@ class MainSection: class PathSection: cache_dir: Path = Path.home() / '.cache/protonfixes' + main: MainSection + path: PathSection + config = Config(Path.home() / '.config/protonfixes/config.ini') From ad470d5fbc0a3299d2d5d97ef39e67ae10f0f547 Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Mon, 14 Oct 2024 23:06:02 +0200 Subject: [PATCH 12/21] Added docstrings, linter and type fixes - Changed ruff's docstring style to "google" - Removed ignored ruff rules, that are already satisfied - Ignore missing cls annotations (ANN102) - Added docstrings to Config and ConfigBase (+Engine) --- .github/scripts/check_verbs.py | 2 +- config.py | 18 +++++++++++ config_base.py | 57 ++++++++++++++++++++++++++++++++-- engine.py | 3 +- ruff.toml | 22 ++++++------- util.py | 4 +-- 6 files changed, 86 insertions(+), 20 deletions(-) diff --git a/.github/scripts/check_verbs.py b/.github/scripts/check_verbs.py index 0bb815cd..8bb218f4 100644 --- a/.github/scripts/check_verbs.py +++ b/.github/scripts/check_verbs.py @@ -4,9 +4,9 @@ import re import subprocess -from typing import Generator from pathlib import Path from tempfile import mkdtemp +from collections.abc import Generator # 'gui' is a virtual verb for opening the Winetricks GUI # 'vd=1280x720' is a setting for the virtual desktop and valid diff --git a/config.py b/config.py index 0154ca37..d0e779c5 100644 --- a/config.py +++ b/config.py @@ -5,14 +5,32 @@ from pathlib import Path class Config(ConfigBase): + """Configuration for umu-protonfix""" + @dataclass class MainSection: + """General parameters + + Attributes: + enable_checks (bool): Run checks (`checks.py`) before the fix is executed. + enable_splash (bool): Enables splash screen, that is shown while a fix is executed. + enable_global_fixes (bool): Enables included fixes. If deactivated, only local fixes (`~/.config/protonfixes/localfixes`) are executed. + + """ + enable_checks: bool = True enable_splash: bool = False enable_global_fixes: bool = True @dataclass class PathSection: + """Path parameters + + Attributes: + cache_dir (Path): The path that should be used to create temporary and cached files. + + """ + cache_dir: Path = Path.home() / '.cache/protonfixes' main: MainSection diff --git a/config_base.py b/config_base.py index 232c26b9..64774eb2 100644 --- a/config_base.py +++ b/config_base.py @@ -3,7 +3,7 @@ import re from configparser import ConfigParser -from dataclasses import dataclass, is_dataclass +from dataclasses import is_dataclass from pathlib import Path from typing import Any @@ -12,11 +12,25 @@ from logger import log, LogLevelType class ConfigBase: + """Base class for configuration objects. + + This reflects a given config file and populates the object with it's values. + It also injects attributes from the sub classes, this isn't compatible with static type checking though. + You can define the attributes accordingly to satisfy type checkers. + """ + __CAMEL_CASE_PATTERN: re.Pattern = re.compile('((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))') @classmethod def snake_case(cls, input: str) -> str: - # Convert CamelCase to snake_case + """Converts CamelCase to snake_case. + + Args: + input (str): The string to convert. + + Returns: + str: The converted string. + """ return cls.__CAMEL_CASE_PATTERN.sub(r'_\1', input).lower() @@ -26,6 +40,17 @@ def __log(message: str, level: LogLevelType = 'INFO') -> None: def __init__(self, path: Path) -> None: + """Initialize the instance from a given config file. + + Defaults will be used if the file doesn't exist. + The file will also be created in this case. + + Args: + path (Path): The reflected config file's path. + + Raises: + IsADirectoryError: If the path exists, but isn't a file. + """ assert path if path.is_file(): self.parse_config_file(path) @@ -37,6 +62,14 @@ def __init__(self, path: Path) -> None: def init_sections(self, force: bool = False) -> None: + """Find sub-classes and initialize them as attributes. + + Sub-classes are initialized and injected as attributes. + Example: `MainSection` will be injected as `main` to the config (this) object. + + Args: + force (bool, optional): Force initialization? This results in a reset. Defaults to False. + """ for (member_name, member) in self.__class__.__dict__.items(): # Find non private section definitions if not member_name.endswith('Section') or member_name.startswith('_'): @@ -57,6 +90,16 @@ def init_sections(self, force: bool = False) -> None: def parse_config_file(self, file: Path) -> bool: + """Parse a config file. + + This resets the data in the sections, regardless if the file exists or is loaded. + + Args: + file (Path): The reflected config file's path. + + Returns: + bool: True, if the config file was successfully loaded. + """ # Initialize / reset sections to defaults self.init_sections(True) @@ -88,7 +131,7 @@ def _get_parse_function(type_name: str) -> Callable[[str], Any]: }.get(type_name, None) if not value: value = parser_items.get - self.__log(f'Unknown type "{type_name}", falling back to "str".') + self.__log(f'Unknown type "{type_name}", falling back to "str".', 'WARN') return value # Iterate over the option objects in this section @@ -105,6 +148,14 @@ def _get_parse_function(type_name: str) -> Callable[[str], Any]: def write_config_file(self, file: Path) -> bool: + """Write the current config to a file. + + Args: + file (Path): The file path to write to. + + Returns: + bool: True, if the file was successfully written. + """ # Only precede if the parent directory exists if not file.parent.is_dir(): self.__log(f'Parent directory "{file.parent}" does not exist. Abort.', 'WARN') diff --git a/engine.py b/engine.py index 8975dceb..a3850874 100644 --- a/engine.py +++ b/engine.py @@ -8,7 +8,8 @@ class Engine: """Game engines""" - def __init__(self) -> None: # noqa: D107 + def __init__(self) -> None: + """Trys to figure out which engine is used in the current game""" self.engine_name = '' self.supported = { 'Dunia 2': 'https://pcgamingwiki.com/wiki/Engine:Dunia_2', diff --git a/ruff.toml b/ruff.toml index 100a5f39..55b1226c 100644 --- a/ruff.toml +++ b/ruff.toml @@ -57,20 +57,8 @@ select = [ "D" ] ignore = [ - # Requiring a type for self is deprecated - "ANN101", - # Ignore starting with 'This' - "D404", - # Ignore imperative mood - "D401", # Ignore period endings - "D400", - # Ignore punctuation "D415", - # Ignore '1 blank line required before class docstring' - "D203", - # Ignore 'Multi-line docstring summary should start at the second line' - "D213", # Ignore 'Missing docstring in public package' "D104", ] @@ -82,9 +70,17 @@ unfixable = [] # Allow unused variables when underscore-prefixed. dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" +[lint.pydocstyle] +convention = "google" + [lint.per-file-ignores] # Relax docstring-related lint rules for gamefixes -"gamefixes-*" = ["D103", "D205"] +"gamefixes-*" = [ + # Ignore 'Missing docstring in public function' + "D103", + # Ignore '1 blank line required between summary line and description' + "D205" +] [format] # Like Black, use double quotes for strings. diff --git a/util.py b/util.py index de9aca96..048bf750 100644 --- a/util.py +++ b/util.py @@ -14,8 +14,8 @@ from io import TextIOWrapper from pathlib import Path from socket import socket, AF_INET, SOCK_DGRAM -from typing import Literal, Any, Callable, Union, Optional -from collections.abc import Mapping, Generator +from typing import Literal, Any, Union, Optional +from collections.abc import Mapping, Generator, Callable try: from .logger import log From 5c9191ecfc81c6ccf866f34cbb77b84f42986399 Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Mon, 14 Oct 2024 23:26:52 +0200 Subject: [PATCH 13/21] CI: Static typechecking and error annotations - enable static type checking via pyright-action - added pyproject.toml with basic meta data and pyright configuration - use action-shellcheck instead of installing it manually - use ruff-action instead of installing it manually - Cache pip dependencies --- .github/workflows/ci.yml | 30 +++++++++++++++++++++--------- pyproject.toml | 21 +++++++++++++++++++++ 2 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 pyproject.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 37432194..496ffe20 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,28 +17,40 @@ jobs: with: submodules: recursive fetch-depth: 0 + - name: Set up Python uses: actions/setup-python@v5 with: # The Steam Runtime platform (sniper) uses Python 3.9 python-version: "3.9" - - name: Install dependencies + cache: 'pip' # caching pip dependencies + + - name: Install Python dependencies run: | - sudo apt-get install shellcheck - python3 -m pip install --upgrade pip - pip install ruff pip install ijson pip install "git+https://github.com/njbooher/steam.git@wsproto#egg=steam[client]" - - name: Lint with Shellcheck - run: | - shellcheck winetricks + + # FIXME: problem matcher is currently disabled upstream, using a fork for the moment + # https://github.com/ludeeus/action-shellcheck/pull/103 + - name: Run ShellCheck + uses: Root-Core/action-shellcheck@master + with: + format: gcc # gcc is used for the problem matcher + additional_files: winetricks # by default, only files with according extensions are checked + + # Ruff uses ruff.toml for it's configuration - name: Lint with Ruff - run: | - ruff check . + uses: astral-sh/ruff-action@v1 + + # Pyright uses pyproject.toml for it's configuration + - name: Static type checking with Pyright + uses: jakebailey/pyright-action@v2 + - name: Validate gamefix modules run: | python3 .github/scripts/check_gamefixes.py python3 .github/scripts/check_verbs.py + - name: Test with unittest run: | python3 protonfixes_test.py diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..98d4ce78 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,21 @@ +[project] +name = "umu-protonfixes" +description = "Stop gap solution to fix issues in games that run under Wine." +keywords = ["proton", "protontricks", "wine", "winetricks", "linux", "gaming"] +license = {file = "LICENSE"} +requires-python = "3.9" + + +[project.urls] +Repository = "https://github.com/Open-Wine-Components/umu-protonfixes.git" +Issues = "https://github.com/Open-Wine-Components/umu-protonfixes/issues" + + +[tool.pyright] +exclude = [ + "**/__pycache__", + "subprojects", + "verbs" +] +pythonVersion = "3.9" +pythonPlatform = "Linux" From 611641eb5b2424dbef9f1b18b6e524bc2559f90d Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Tue, 15 Oct 2024 11:18:54 +0200 Subject: [PATCH 14/21] Use pathlib (and config) for cache dir usage --- config.py | 2 -- util.py | 31 +++++++++++++++---------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/config.py b/config.py index d0e779c5..c83e195d 100644 --- a/config.py +++ b/config.py @@ -13,13 +13,11 @@ class MainSection: Attributes: enable_checks (bool): Run checks (`checks.py`) before the fix is executed. - enable_splash (bool): Enables splash screen, that is shown while a fix is executed. enable_global_fixes (bool): Enables included fixes. If deactivated, only local fixes (`~/.config/protonfixes/localfixes`) are executed. """ enable_checks: bool = True - enable_splash: bool = False enable_global_fixes: bool = True @dataclass diff --git a/util.py b/util.py index 048bf750..6d92c58b 100644 --- a/util.py +++ b/util.py @@ -19,9 +19,11 @@ try: from .logger import log + from .config import config from .steamhelper import install_app except ImportError: from logger import log + from config import config from steamhelper import install_app try: @@ -431,8 +433,7 @@ def patch_libcuda() -> bool: Returns true if the library was patched correctly. Otherwise returns false """ - cache_dir = os.path.expanduser('~/.cache/protonfixes') - os.makedirs(cache_dir, exist_ok=True) + config.path.cache_dir.mkdir(parents=True, exist_ok=True) try: # Use shutil.which to find ldconfig binary @@ -475,10 +476,9 @@ def patch_libcuda() -> bool: log.info(f'Found 64-bit libcuda.so at: {libcuda_path}') - patched_library = os.path.join(cache_dir, 'libcuda.patched.so') + patched_library = config.path.cache_dir / 'libcuda.patched.so' try: - with open(libcuda_path, 'rb') as f: - binary_data = f.read() + binary_data = patched_library.read_bytes() except OSError as e: log.crit(f'Unable to read libcuda.so: {e}') return False @@ -499,11 +499,10 @@ def patch_libcuda() -> bool: patched_binary_data = bytes.fromhex(hex_data) try: - with open(patched_library, 'wb') as f: - f.write(patched_binary_data) + patched_library.write_bytes(patched_binary_data) # Set permissions to rwxr-xr-x (755) - os.chmod(patched_library, 0o755) + patched_library.chmod(0o755) log.debug(f'Permissions set to rwxr-xr-x for {patched_library}') except OSError as e: log.crit(f'Unable to write patched libcuda.so to {patched_library}: {e}') @@ -816,12 +815,12 @@ def install_battleye_runtime() -> None: def install_all_from_tgz(url: str, path: StrPath = os.getcwd()) -> None: """Install all files from a downloaded tar.gz""" - cache_dir = os.path.expanduser('~/.cache/protonfixes') - os.makedirs(cache_dir, exist_ok=True) + config.path.cache_dir.mkdir(parents=True, exist_ok=True) + tgz_file_name = os.path.basename(url) - tgz_file_path = os.path.join(cache_dir, tgz_file_name) + tgz_file_path = config.path.cache_dir / tgz_file_name - if tgz_file_name not in os.listdir(cache_dir): + if not tgz_file_path.is_file(): log.info('Downloading ' + tgz_file_name) urllib.request.urlretrieve(url, tgz_file_path) @@ -836,12 +835,12 @@ def install_from_zip(url: str, filename: str, path: StrPath = os.getcwd()) -> No log.info(f'File {filename} found in {path}') return - cache_dir = os.path.expanduser('~/.cache/protonfixes') - os.makedirs(cache_dir, exist_ok=True) + config.path.cache_dir.mkdir(parents=True, exist_ok=True) + zip_file_name = os.path.basename(url) - zip_file_path = os.path.join(cache_dir, zip_file_name) + zip_file_path = config.path.cache_dir / zip_file_name - if zip_file_name not in os.listdir(cache_dir): + if not zip_file_path.is_file(): log.info(f'Downloading {filename} to {zip_file_path}') urllib.request.urlretrieve(url, zip_file_path) From 1ce9d45e729e3cb4e4544033f9159c97cc4882a7 Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Tue, 15 Oct 2024 11:34:04 +0200 Subject: [PATCH 15/21] Remove unused util.which() --- util.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/util.py b/util.py index 6d92c58b..9f6a512e 100644 --- a/util.py +++ b/util.py @@ -37,15 +37,6 @@ BasePathType = Literal['user', 'game'] OverrideTypes = Literal['n', 'b', 'n,b', 'b,n', ''] -def which(appname: str) -> Optional[str]: - """Returns the full path of an executable in $PATH""" - for path in os.environ['PATH'].split(os.pathsep): - fullpath = os.path.join(path, appname) - if os.path.exists(fullpath) and os.access(fullpath, os.X_OK): - return fullpath - log.warn(str(appname) + 'not found in $PATH') - return None - def protondir() -> str: """Returns the path to proton""" From 965a50eea3aa578443d18077693ca45537954cda Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Wed, 16 Oct 2024 20:29:08 +0200 Subject: [PATCH 16/21] Refactored util.py - Use pathlib if possible - Return Path() object is possible - Parse ProtonVersion correctly - Reimplemented get_resolution() with RegEx - Reimplemented _get_case_insensitive_name() - Reimplemented _killhanging() - Reimplemented _checkinstalled() - Improved some logging - Some caching --- fix.py | 5 +- util.py | 411 +++++++++++++++++++++++++------------------------------- 2 files changed, 190 insertions(+), 226 deletions(-) diff --git a/fix.py b/fix.py index 605f017c..f5f70c21 100644 --- a/fix.py +++ b/fix.py @@ -10,10 +10,12 @@ try: from .config import config + from .util import proton_version from .checks import run_checks from .logger import log except ImportError: from config import config + from util import proton_version from checks import run_checks from logger import log @@ -199,5 +201,6 @@ def main() -> None: log.debug('Not running protonfixes for setup runs') return - log.info('Running protonfixes') + version = proton_version() + log.info(f'Running protonfixes on "{version.version_name}", build at {version.build_date}.') run_fix(get_game_id()) diff --git a/util.py b/util.py index 9f6a512e..91d18578 100644 --- a/util.py +++ b/util.py @@ -11,11 +11,11 @@ import subprocess import urllib.request import functools -from io import TextIOWrapper from pathlib import Path +from datetime import datetime, timezone from socket import socket, AF_INET, SOCK_DGRAM from typing import Literal, Any, Union, Optional -from collections.abc import Mapping, Generator, Callable +from collections.abc import Mapping, Callable try: from .logger import log @@ -37,46 +37,42 @@ BasePathType = Literal['user', 'game'] OverrideTypes = Literal['n', 'b', 'n,b', 'b,n', ''] +class ProtonVersion: + """Parses the proton version and build timestamp""" -def protondir() -> str: - """Returns the path to proton""" - proton_dir = os.path.dirname(sys.argv[0]) - return proton_dir + def __init__(self, version_string: str) -> None: + """Initialize from a given version string""" + # Example string '1722141596 GE-Proton9-10-18-g3763cd3a\n' + parts = version_string.split() + if len(parts) != 2 or not parts[0].isnumeric(): + log.crit(f'Version string "{version_string}" is invalid!') + return + self.build_date: datetime = datetime.fromtimestamp(int(parts[0]), tz=timezone.utc) + self.version_name: str = parts[1] -def protonprefix() -> str: - """Returns the wineprefix used by proton""" - return os.path.join(os.environ['STEAM_COMPAT_DATA_PATH'], 'pfx/') +@functools.lru_cache +def protondir() -> Path: + """Returns the path to proton""" + return Path(sys.argv[0]).parent -def protonnameversion() -> Optional[str]: - """Returns the version of proton from sys.argv[0]""" - version = re.search('Proton ([0-9]*\\.[0-9]*)', sys.argv[0]) - if version: - return version.group(1) - log.warn('Proton version not parsed from command line') - return None +@functools.lru_cache +def protonprefix() -> Path: + """Returns the wineprefix used by proton""" + return Path(os.environ['STEAM_COMPAT_DATA_PATH']) / 'pfx' -def protontimeversion() -> int: - """Returns the version timestamp of proton from the `version` file""" - fullpath = os.path.join(protondir(), 'version') +@functools.lru_cache +def proton_version() -> ProtonVersion: + """Returns the version of proton""" + fullpath = protondir() / 'version' try: - with open(fullpath, encoding='ascii') as version: - for timestamp in version.readlines(): - return int(timestamp.strip()) + version_string = fullpath.read_text(encoding='ascii') + return ProtonVersion(version_string) except OSError: - log.warn('Proton version file not found in: ' + fullpath) - return 0 - log.warn('Proton version not parsed from file: ' + fullpath) - return 0 - - -def protonversion(timestamp: bool = False) -> Optional[Union[str, int]]: - """Returns the version of proton""" - if timestamp: - return protontimeversion() - return protonnameversion() + log.warn(f'Proton version file not found in: {fullpath}') + return ProtonVersion('0 Unknown') def once( @@ -99,12 +95,11 @@ def once( def wrapper(*args, **kwargs) -> None: # noqa: ANN002, ANN003 func_id = f'{func.__module__}.{func.__name__}' - prefix = protonprefix() - directory = os.path.join(prefix, 'drive_c/protonfixes/run/') - file = os.path.join(directory, func_id) - if not os.path.exists(directory): - os.makedirs(directory) - if os.path.exists(file): + directory = protonprefix() / 'drive_c/protonfixes/run/' + file = directory / func_id + if not directory.is_dir(): + directory.mkdir(parents=True) + if file.is_file(): return exception = None @@ -115,8 +110,7 @@ def wrapper(*args, **kwargs) -> None: # noqa: ANN002, ANN003 raise exc exception = exc - with open(file, 'a', encoding='ascii') as tmp: - tmp.close() + file.touch() if exception: raise exception @@ -130,24 +124,28 @@ def _killhanging() -> None: """Kills processes that hang when installing winetricks""" # avoiding an external library as proc should be available on linux log.debug('Killing hanging wine processes') - pids = [pid for pid in os.listdir('/proc') if pid.isdigit()] - badexes = ['mscorsvw.exe'] - for pid in pids: + + black_list = ['mscorsvw.exe'] + for path in Path('/proc').iterdir(): try: - with open(os.path.join('/proc', pid, 'cmdline'), 'rb') as proc_cmd: - cmdline = proc_cmd.read() - for exe in badexes: - if exe in cmdline.decode(): - os.kill(int(pid), signal.SIGKILL) - except OSError: + cmd = path / 'cmdline' + if not cmd.is_file(): + continue + + cmdline = cmd.read_text(encoding='ascii') + if any([i in cmdline for i in black_list]): + log.debug(f'Killing process #{path.stem}') + os.kill(int(path.stem), signal.SIGKILL) + except OSError as ex: + log.debug(f'Failed to read cmd lines: {ex}') continue def _forceinstalled(verb: str) -> None: """Records verb into the winetricks.log.forced file""" - forced_log = os.path.join(protonprefix(), 'winetricks.log.forced') - with open(forced_log, 'a', encoding='ascii') as forcedlog: - forcedlog.write(verb + '\n') + forced_log = protonprefix() / 'winetricks.log.forced' + with forced_log.open('a') as file: + file.write(f'{verb}\n') def _checkinstalled(verb: str, logfile: str = 'winetricks.log') -> bool: @@ -155,29 +153,14 @@ def _checkinstalled(verb: str, logfile: str = 'winetricks.log') -> bool: if not isinstance(verb, str): return False - winetricks_log = os.path.join(protonprefix(), logfile) - - # Check for 'verb=param' verb types - if len(verb.split('=')) > 1: - wt_verb = verb.split('=')[0] + '=' - wt_verb_param = verb.split('=')[1] - wt_is_set = False - try: - with open(winetricks_log, encoding='ascii') as tricklog: - for xline in tricklog.readlines(): - if re.findall(r'^' + wt_verb, xline.strip()): - wt_is_set = bool(xline.strip() == wt_verb + wt_verb_param) - return wt_is_set - except OSError: - return False - # Check for regular verbs + winetricks_log = protonprefix() / logfile try: - with open(winetricks_log, encoding='ascii') as tricklog: - if verb in reversed([x.strip() for x in tricklog.readlines()]): - return True + lines = winetricks_log.read_text(encoding='ascii').splitlines() + lines = [line.strip() for line in lines] + return verb in lines except OSError: + log.warn(f'Can not check installed verbs for "{verb}" in file "{winetricks_log}".') return False - return False def checkinstalled(verb: str) -> bool: @@ -185,13 +168,13 @@ def checkinstalled(verb: str) -> bool: if verb == 'gui': return False - log.info(f'Checking if winetricks {verb} is installed') + log.info(f'Checking if winetricks "{verb}" is installed') if _checkinstalled(verb, 'winetricks.log.forced'): return True return _checkinstalled(verb) -def is_custom_verb(verb: str) -> Union[bool, str]: +def is_custom_verb(verb: str) -> Union[bool, Path]: """Returns path to custom winetricks verb, if found""" if verb == 'gui': return False @@ -200,16 +183,18 @@ def is_custom_verb(verb: str) -> Union[bool, str]: verb_dir = 'verbs' # check local custom verbs - verbpath = os.path.expanduser('~/.config/protonfixes/localfixes/' + verb_dir) - if os.path.isfile(os.path.join(verbpath, verb_name)): - log.debug('Using local custom winetricks verb from: ' + verbpath) - return os.path.join(verbpath, verb_name) + verb_path = (Path.home() / '.config/protonfixes/localfixes/') / verb_dir + verb_file = verb_path / verb_name + if verb_file.is_file(): + log.debug(f'Using local custom winetricks verb from: {verb_path}') + return verb_file # check custom verbs - verbpath = os.path.join(os.path.dirname(__file__), verb_dir) - if os.path.isfile(os.path.join(verbpath, verb_name)): - log.debug('Using custom winetricks verb from: ' + verbpath) - return os.path.join(verbpath, verb_name) + verb_path = Path(__file__).parent / verb_dir + verb_file = verb_path / verb_name + if verb_file.is_file(): + log.debug(f'Using custom winetricks verb from: {verb_path}') + return verb_file return False @@ -237,14 +222,14 @@ def protontricks(verb: str) -> bool: log.info('Installing winetricks ' + verb) env = dict(protonmain.g_session.env) - env['WINEPREFIX'] = protonprefix() + env['WINEPREFIX'] = str(protonprefix()) env['WINE'] = protonmain.g_proton.wine_bin env['WINELOADER'] = protonmain.g_proton.wine_bin env['WINESERVER'] = protonmain.g_proton.wineserver_bin env['WINETRICKS_LATEST_VERSION_CHECK'] = 'disabled' env['LD_PRELOAD'] = '' - winetricks_bin = os.path.abspath(__file__).replace('util.py', 'winetricks') + winetricks_bin = Path(__file__).with_name('winetricks') winetricks_cmd = [winetricks_bin, '--unattended'] + verb.split(' ') if verb == 'gui': winetricks_cmd = [winetricks_bin, '--unattended'] @@ -258,7 +243,7 @@ def protontricks(verb: str) -> bool: log.warn('No winetricks was found in $PATH') if winetricks_bin is not None: - log.debug('Using winetricks command: ' + str(winetricks_cmd)) + log.debug(f'Using winetricks command: {winetricks_cmd}') # make sure proton waits for winetricks to finish for idx, arg in enumerate(sys.argv): @@ -266,7 +251,7 @@ def protontricks(verb: str) -> bool: sys.argv[idx] = arg.replace('run', 'waitforexitandrun') log.debug(str(sys.argv)) - log.info('Using winetricks verb ' + verb) + log.info(f'Using winetricks verb "{verb}"') subprocess.call([env['WINESERVER'], '-w'], env=env) with subprocess.Popen(winetricks_cmd, env=env) as process: process.wait() @@ -298,7 +283,7 @@ def regedit_add( ) -> None: """Add regedit keys""" env = dict(protonmain.g_session.env) - env['WINEPREFIX'] = protonprefix() + env['WINEPREFIX'] = str(protonprefix()) env['WINE'] = protonmain.g_proton.wine_bin env['WINELOADER'] = protonmain.g_proton.wine_bin env['WINESERVER'] = protonmain.g_proton.wineserver_bin @@ -399,9 +384,10 @@ def del_environment(envvar: str) -> None: del protonmain.g_session.env[envvar] -def get_game_install_path() -> str: +def get_game_install_path() -> Path: """Game installation path""" - install_path = os.environ.get('STEAM_COMPAT_INSTALL_PATH', os.getcwd()) + path = os.environ.get('STEAM_COMPAT_INSTALL_PATH') + install_path = Path(path) if path else Path.cwd() log.debug(f'Detected path to game: {install_path}') return install_path @@ -456,9 +442,9 @@ def patch_libcuda() -> bool: # Parse the line to extract the path parts = line.strip().split(' => ') if len(parts) == 2: - path = parts[1].strip() - if os.path.exists(path): - libcuda_path = os.path.abspath(path) + path = Path(parts[1].strip()) + if path.is_file(): + libcuda_path = path break if not libcuda_path: @@ -500,7 +486,7 @@ def patch_libcuda() -> bool: return False log.info(f'Patched libcuda.so saved to: {patched_library}') - set_environment('LD_PRELOAD', patched_library) + set_environment('LD_PRELOAD', str(patched_library)) return True except Exception as e: @@ -549,13 +535,10 @@ def disable_uplay_overlay() -> bool: UPlay will overwrite settings.yml on launch, but keep this setting. """ - config_dir = os.path.join( - protonprefix(), - 'drive_c/users/steamuser/Local Settings/Application Data/Ubisoft Game Launcher/', - ) - config_file = os.path.join(config_dir, 'settings.yml') + config_dir = protonprefix() / 'drive_c/users/steamuser/Local Settings/Application Data/Ubisoft Game Launcher/' + config_file = config_dir / 'settings.yml' - os.makedirs(config_dir, exist_ok=True) + config_dir.mkdir(parents=True, exist_ok=True) try: data = ( @@ -567,12 +550,12 @@ def disable_uplay_overlay() -> bool: 'user:\n' ' closebehavior: CloseBehavior_Close' ) - with open(config_file, 'a+', encoding='ascii') as file: + with config_file.open('a', encoding='ascii') as file: file.write(data) log.info('Disabled UPlay overlay') return True except OSError as err: - log.warn('Could not disable UPlay overlay: ' + err.strerror) + log.warn(f'Could not disable UPlay overlay: {err.strerror}') return False @@ -586,84 +569,77 @@ def create_dosbox_conf( option;, each subsequent one overwrites settings defined in previous files. """ - if os.access(conf_file, os.F_OK): + conf_file = Path(conf_file) + if conf_file.is_file(): return conf = configparser.ConfigParser() conf.read_dict(conf_dict) - with open(conf_file, 'w', encoding='ascii') as file: + with conf_file.open('w', encoding='ascii') as file: conf.write(file) -def _get_case_insensitive_name(path: str) -> str: +def _get_case_insensitive_name(path: Path) -> Path: """Find potentially differently-cased location e.g /path/to/game/system/gothic.ini -> /path/to/game/System/GOTHIC.INI """ - if os.path.exists(path): + # FIXME: Function could be replaced by glob with arg `case_sensitve=False` in Python 3.12 + if path.exists(): return path - root = path - # Find first existing directory in the tree - while not os.path.exists(root): - root = os.path.split(root)[0] - - if root[len(root) - 1] not in ['/', '\\']: - root = root + os.sep - # Separate missing path from existing root - s_working_dir = path.replace(root, '').split(os.sep) - paths_to_find = len(s_working_dir) - # Keep track of paths we found so far - paths_found = 0 - # Walk through missing paths - for directory in s_working_dir: - if not os.path.exists(root): - break - dir_list = os.listdir(root) - found = False - for existing_dir in dir_list: - # Find matching filename on drive - if existing_dir.lower() == directory.lower(): - root = os.path.join(root, existing_dir) - paths_found += 1 - found = True - # If path was not found append case that we were looking for - if not found: - root = os.path.join(root, directory) - paths_found += 1 - - # Append rest of the path if we were unable to find directory at any level - if paths_to_find != paths_found: - root = os.path.join(root, os.sep.join(s_working_dir[paths_found:])) - return root - - -def _get_config_full_path(cfile: StrPath, base_path: BasePathType) -> Optional[str]: + + # Parents are from nearest to farthest, we need to reverse them + # The parents do not include the Path object itself.. obviously + paths = list(reversed(path.parents)) + [path] + resolved = paths[0] + + for i, part in enumerate(path.parts): + current = resolved / part + if current.exists(): + resolved = current + continue + + # Mapping casefold file name in folder to it's Path() + files = {file.name.casefold(): file for file in current.parent.iterdir()} + cf_part = part.casefold() + if cf_part in files: + resolved = files[cf_part] + else: + unresolved = list(path.parts[i:]) + log.warn(f'Can not resolve case sensitive path "{path}", stopped after "{resolved}"') + log.info(f'Returning resolved path, with non resolvable parts {unresolved} attached') + return resolved / str.join('/', unresolved) + + log.debug(f'Resolved case sensitive path "{path}" -> "{resolved}"') + return resolved + + +def _get_config_full_path(cfile: StrPath, base_path: BasePathType) -> Optional[Path]: """Find game's config file""" # Start from 'user'/'game' directories or absolute path if base_path == 'user': - cfg_path = os.path.join( - protonprefix(), 'drive_c/users/steamuser/My Documents', cfile - ) + cfg_path = protonprefix() / 'drive_c/users/steamuser/My Documents' / cfile + elif base_path == 'game': + cfg_path = get_game_install_path() / cfile else: - if base_path == 'game': - cfg_path = os.path.join(get_game_install_path(), cfile) - else: - cfg_path = cfile - cfg_path = _get_case_insensitive_name(str(cfg_path)) + cfg_path = Path(cfile) + cfg_path = _get_case_insensitive_name(cfg_path) - if os.path.exists(cfg_path) and os.access(cfg_path, os.F_OK): - log.debug('Found config file: ' + cfg_path) + if cfg_path.is_file(): + log.debug(f'Found config file: {cfg_path}') return cfg_path - log.warn('Config file not found: ' + cfg_path) + log.warn(f'Config file not found: {cfg_path}') return None -def create_backup_config(cfg_path: str) -> None: +def create_backup_config(cfg_path: Path) -> bool: """Create backup config file""" - # Backup - if not os.path.exists(cfg_path + '.protonfixes.bak'): - log.info('Creating backup for config file') - shutil.copyfile(cfg_path, cfg_path + '.protonfixes.bak') + backup_path = cfg_path.with_name(cfg_path.name + '.protonfixes.bak') + if not backup_path.is_file(): + log.info(f'Creating backup for config file "{cfg_path}" -> "{backup_path}"') + shutil.copyfile(cfg_path, backup_path) + return True + return False def set_ini_options( @@ -685,10 +661,10 @@ def set_ini_options( conf.read(cfg_path, encoding) - log.info(f'Addinging INI options into {cfile}:\n{str(ini_opts)}') + log.info(f'Addinging INI options into "{cfile}":\n{ini_opts}') conf.read_string(ini_opts) - with open(cfg_path, 'w', encoding=encoding) as configfile: + with cfg_path.open('w', encoding=encoding) as configfile: conf.write(configfile) return True @@ -701,29 +677,22 @@ def set_xml_options( if not xml_path: return False - create_backup_config(xml_path) - - # set options - - base_size = os.path.getsize(xml_path) - backup_size = os.path.getsize(xml_path + '.protonfixes.bak') - - if base_size != backup_size: + # Check if backup already exists + if not create_backup_config(xml_path): return False - with open(xml_path, encoding='utf-8') as file: - contents = file.readlines() - i = 0 - for line in contents: - i += 1 - if base_attibutte in line: - log.info(f'Adding XML options into {cfile}, line {i}:\n{xml_line}') - contents.insert(i, xml_line + '\n') - - with open(xml_path, 'w', encoding='utf-8') as file: - for eachitem in contents: - file.write(eachitem) + # set options + i = 0 + contents = xml_path.read_text(encoding='utf-8').splitlines() + for line in contents: + i += 1 + if base_attibutte not in line: + continue + log.info(f'Adding XML options into "{cfile}", line {i}:\n{xml_line}') + contents.insert(i, xml_line + '\n') + data = str.join('\n', contents) + xml_path.write_text(data, encoding='utf-8') log.info('XML config patch applied') return True @@ -731,31 +700,22 @@ def set_xml_options( def get_resolution() -> tuple[int, int]: """Returns screen res width, height using xrandr""" # Execute xrandr command and capture its output - xrandr_bin = os.path.abspath(__file__).replace('util.py', 'xrandr') - xrandr_output = subprocess.check_output([xrandr_bin, '--current']).decode('utf-8') - - # Find the line that starts with 'Screen 0:' and extract the resolution - for line in xrandr_output.splitlines(): - if 'primary' in line: - resolution = line.split()[3] - width_height = resolution.split('x') - offset_values = width_height[1].split('+') - clean_resolution = width_height[0] + 'x' + offset_values[0] - screenx, screeny = clean_resolution.split('x') - return (int(screenx), int(screeny)) - - # If no resolution is found, return default values or raise an exception - return (0, 0) # or raise Exception('Resolution not found') + xrandr_bin = Path(__file__).with_name('xrandr') + xrandr_output = subprocess.check_output([xrandr_bin, '--current'], text=True) - -def read_dxvk_conf(cfp: TextIOWrapper) -> Generator[str, None, None]: - """Add fake [DEFAULT] section to dxvk.conf""" - yield f'[{configparser.ConfigParser().default_section}]' - yield from cfp + # Example line: "DP-1 connected primary 5120x1440+0+0 (normal left inverted right x axis y axis) 1193mm x 336mm" + res_match = re.search(r'connected primary (?P\d{3,5})x(?P\d{3,5})', xrandr_output) + if not res_match: + log.warn('Can not extract resolution from xrandr') + return (0, 0) # or raise Exception('Resolution not found') + + x = res_match.group('x') + y = res_match.group('y') + return (int(x), int(y)) def set_dxvk_option( - opt: str, val: str, cfile: StrPath = '/tmp/protonfixes_dxvk.conf' + opt: str, val: str, cfile: Path = Path('/tmp/protonfixes_dxvk.conf') ) -> None: """Create custom DXVK config file @@ -764,33 +724,34 @@ def set_dxvk_option( conf = configparser.ConfigParser() # Preserve option name case (default converts to lower case) conf.optionxform = lambda optionstr: optionstr + conf.read(cfile) + + # FIXME: Python 3.13 implements `allow_unnamed_section=True` section = conf.default_section - dxvk_conf = os.path.join(os.environ['PWD'], 'dxvk.conf') + if conf.has_option(section, 'session') and conf.getint(section, 'session') == os.getpid(): + return - conf.read(cfile) + log.info('Creating new DXVK config') + set_environment('DXVK_CONFIG_FILE', str(cfile)) - if ( - not conf.has_option(section, 'session') - or conf.getint(section, 'session') != os.getpid() - ): - log.info('Creating new DXVK config') - set_environment('DXVK_CONFIG_FILE', str(cfile)) + conf = configparser.ConfigParser() + # Preserve option name case (default converts to lower case) + conf.optionxform = lambda optionstr: optionstr + conf.set(section, 'session', str(os.getpid())) - conf = configparser.ConfigParser() - # Preserve option name case (default converts to lower case) - conf.optionxform = lambda optionstr: optionstr - conf.set(section, 'session', str(os.getpid())) + # Add configuration from game's directory + dxvk_conf = get_game_install_path() / 'dxvk.conf' + if dxvk_conf.is_file(): + text = dxvk_conf.read_text(encoding='ascii') + conf.read_string(f'[{section}]\n{text}') - if os.access(dxvk_conf, os.F_OK): - with open(dxvk_conf, encoding='ascii') as dxvk: - conf.read_file(read_dxvk_conf(dxvk)) - log.debug(f'{conf.items(section)}') + log.debug(f'DXVK config:\n{conf.items(section)}') # set option - log.info('Addinging DXVK option: ' + str(opt) + ' = ' + str(val)) + log.info(f'Addinging DXVK option: "{opt}" = "{val}"') conf.set(section, opt, str(val)) - with open(cfile, 'w', encoding='ascii') as configfile: + with cfile.open('w', encoding='ascii') as configfile: conf.write(configfile) @@ -804,7 +765,7 @@ def install_battleye_runtime() -> None: install_app('1161040') -def install_all_from_tgz(url: str, path: StrPath = os.getcwd()) -> None: +def install_all_from_tgz(url: str, path: Path = get_game_install_path()) -> None: """Install all files from a downloaded tar.gz""" config.path.cache_dir.mkdir(parents=True, exist_ok=True) @@ -820,10 +781,10 @@ def install_all_from_tgz(url: str, path: StrPath = os.getcwd()) -> None: tgz_obj.extractall(path) -def install_from_zip(url: str, filename: str, path: StrPath = os.getcwd()) -> None: +def install_from_zip(url: str, filename: str, path: Path = get_game_install_path()) -> None: """Install a file from a downloaded zip""" - if filename in os.listdir(path): - log.info(f'File {filename} found in {path}') + if (path / filename).is_file(): + log.info(f'File "{filename}" found in "{path}"') return config.path.cache_dir.mkdir(parents=True, exist_ok=True) @@ -832,11 +793,11 @@ def install_from_zip(url: str, filename: str, path: StrPath = os.getcwd()) -> No zip_file_path = config.path.cache_dir / zip_file_name if not zip_file_path.is_file(): - log.info(f'Downloading {filename} to {zip_file_path}') + log.info(f'Downloading "{filename}" to "{zip_file_path}"') urllib.request.urlretrieve(url, zip_file_path) with zipfile.ZipFile(zip_file_path, 'r') as zip_obj: - log.info(f'Extracting {filename} to {path}') + log.info(f'Extracting "{filename}" to "{path}"') zip_obj.extract(filename, path=path) From 12457bb5fd9965df8a985a32e1e8886d2250904c Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Thu, 17 Oct 2024 20:04:16 +0200 Subject: [PATCH 17/21] Some refactoring util.py: - Added create_dos_device() - Added patch_conf_value() - Added patch_voodoo_conf() - Added get_path_syswow64() - Moved class ReplaceType from Bethesda mod support (class Redirect) Other: - Refactored gamefixes to use pathlib - Simplified / reimplemented some fixes - Some docstrings - Linked Gobliiins 5 demo to main game - Unified Gothic 3 and Gothic 3 Forsaken Gods Enhanced Edition - Fixes after rebase --- .github/scripts/steam_client.py | 34 ++++--- gamefixes-gog/umu-1209310984.py | 13 ++- gamefixes-gog/umu-1564851593.py | 29 +++--- gamefixes-steam/105400.py | 19 ++-- gamefixes-steam/1237970.py | 11 +-- gamefixes-steam/1245620.py | 3 +- gamefixes-steam/1449280.py | 11 ++- gamefixes-steam/1873170.py | 61 ++++++++----- gamefixes-steam/22330.py | 17 +--- gamefixes-steam/23460.py | 11 +-- gamefixes-steam/2475980.py | 48 +++++----- gamefixes-steam/2505910.py | 1 + gamefixes-steam/257420.py | 18 ++-- gamefixes-steam/294700.py | 17 ++-- gamefixes-steam/302370.py | 40 ++++----- gamefixes-steam/328500.py | 31 +++---- gamefixes-steam/359870.py | 29 +++--- gamefixes-steam/379720.py | 34 +++---- gamefixes-steam/386360.py | 34 +++---- gamefixes-steam/39500.py | 5 +- gamefixes-steam/409090.py | 16 +--- gamefixes-steam/429720.py | 21 ++--- gamefixes-steam/452440.py | 31 +++---- gamefixes-steam/46500.py | 12 +-- gamefixes-steam/497360.py | 154 +++++++++++++++----------------- gamefixes-steam/508980.py | 20 +++-- gamefixes-steam/65600.py | 16 +--- util.py | 83 ++++++++++++++--- 28 files changed, 429 insertions(+), 390 deletions(-) create mode 120000 gamefixes-steam/2505910.py mode change 100755 => 120000 gamefixes-steam/65600.py diff --git a/.github/scripts/steam_client.py b/.github/scripts/steam_client.py index c1095b6e..310f898d 100644 --- a/.github/scripts/steam_client.py +++ b/.github/scripts/steam_client.py @@ -8,46 +8,60 @@ from steam.utils.proto import proto_to_dict from steam.core.connection import WebsocketConnection -class Steam: # noqa: D101 - def __init__(self) -> None: # noqa: D107 +class Steam: + """Minimal implementation of the SteamClient package that allows app id validation""" + + def __init__(self) -> None: + """Setup SteamClient and it's events + + Raises: + ValueError: When the SteamClient fires it's "error" event + """ self.logged_on_once = False self.steam = client = SteamClient() client.connection = WebsocketConnection() - @client.on('error') + # FIXME: pyright outputs 'error: Object of type "None" cannot be called (reportOptionalCall)' + @client.on(SteamClient.EVENT_ERROR) # pyright: ignore (reportOptionalCall) def handle_error(result: EResult) -> None: raise ValueError(f'Steam error: {repr(result)}') - @client.on('connected') + @client.on(SteamClient.EVENT_CONNECTED) # pyright: ignore (reportOptionalCall) def handle_connected() -> None: print(f'Connected to {client.current_server_addr}', file=sys.stderr) - @client.on('channel_secured') + @client.on(SteamClient.EVENT_CHANNEL_SECURED) # pyright: ignore (reportOptionalCall) def send_login() -> None: if self.logged_on_once and self.steam.relogin_available: self.steam.relogin() - @client.on('disconnected') + @client.on(SteamClient.EVENT_DISCONNECTED) # pyright: ignore (reportOptionalCall) def handle_disconnect() -> None: print('Steam disconnected', file=sys.stderr) if self.logged_on_once: print('Reconnecting...', file=sys.stderr) client.reconnect(maxdelay=30) - @client.on('logged_on') + @client.on(SteamClient.EVENT_LOGGED_ON) # pyright: ignore (reportOptionalCall) def handle_after_logon() -> None: self.logged_on_once = True client.anonymous_login() + def get_valid_appids(self, appids: set[int]) -> set[int]: """Queries Steam for the specified appids. - If an appid doesn't exist, it won't be in the response. + Args: + appids (set[int]): The app ids that should be validated + + Raises: + ValueError: When the response is empty / unexpected - Raises a ValueError if Steam returns unexpected data - """ + Returns: + set[int]: Only valid app ids will be returned + """ # https://github.com/SteamRE/SteamKit/blob/master/SteamKit2/SteamKit2/Base/Generated/SteamMsgClientServerAppInfo.cs#L331 resp = self.steam.send_job_and_wait( message = MsgProto(EMsg.ClientPICSProductInfoRequest), diff --git a/gamefixes-gog/umu-1209310984.py b/gamefixes-gog/umu-1209310984.py index 750760d4..6384c8be 100644 --- a/gamefixes-gog/umu-1209310984.py +++ b/gamefixes-gog/umu-1209310984.py @@ -5,7 +5,6 @@ directory and renamed to xaudio2_8.dll. """ -import os from gzip import open as gzip_open from hashlib import sha256 from tempfile import mkdtemp @@ -18,15 +17,14 @@ def main() -> None: arc = 'https://github.com/user-attachments/files/16788423/xaudio2_8.dll.gz' hashsum_file = '173cac0a7931989d66338e0d7779e451f2f01b2377903df7954d86c07c1bc8fb' + tmp = f'{mkdtemp()}/xaudio2_8.dll.gz' hashsum = sha256() - path_dll = f'{util.get_game_install_path()}/xaudio2_8.dll' + path_dll = util.get_game_install_path() / 'xaudio2_8.dll' # Full Metal Daemon from gog will not have the xaudio2_8.dll - if os.path.exists(path_dll): - log.info( - f"xaudio2_8.dll exists in '{util.get_game_install_path()}', skipping..." - ) + if path_dll.is_file(): + log.info(f"xaudio2_8.dll exists in '{path_dll.parent}', skipping...") return # Download the archive @@ -54,5 +52,4 @@ def main() -> None: # NOTE: The file is actually xaudio2_9.dll from winetricks but renamed log.info("Applying fix for 'Full Metal Daemon Muramasa'...") with gzip_open(tmp, 'rb') as reader: - with open(path_dll, 'wb') as writer: - writer.write(reader.read()) + path_dll.write_bytes(reader.read()) diff --git a/gamefixes-gog/umu-1564851593.py b/gamefixes-gog/umu-1564851593.py index fc62c8f1..690571cd 100644 --- a/gamefixes-gog/umu-1564851593.py +++ b/gamefixes-gog/umu-1564851593.py @@ -5,10 +5,12 @@ """ import os + from hashlib import sha256 from tempfile import mkdtemp from urllib.request import urlopen from zipfile import ZipFile, is_zipfile +from pathlib import Path from .. import util from ..logger import log @@ -23,44 +25,36 @@ def main() -> None: - tmp = f'{mkdtemp()}/d3d9-2206220222.zip' + tmp = Path(mkdtemp()) / 'd3d9-2206220222.zip' install_dir = util.get_game_install_path() - path_config = f'{install_dir}/config.json' - path_dll = f'{install_dir}/d3d9.dll' + path_config = install_dir / 'config.json' + path_dll = install_dir / 'd3d9.dll' hashsum = sha256() # Ensure that the text injection files do not already exist before opening - if not os.path.isfile(path_config) or not os.path.isfile(path_dll): + if not path_config.is_file() or not path_dll.is_dir(): log.warn( f"File 'config.json' or 'd3d9.dll' not found in '{install_dir}', skipping..." ) return - config = open(path_config, mode='rb') - dll = open(path_dll, mode='rb') - # Check if the text injection framework files have already been replaced if ( - sha256(config.read()).hexdigest() == hashsum_config - and sha256(dll.read()).hexdigest() == hashsum_d3d9 + sha256(path_config.read_bytes()).hexdigest() == hashsum_config + and sha256(path_dll.read_bytes()).hexdigest() == hashsum_d3d9 ): log.info( "Fix for 'Flowers - Le Volume Sur Hiver' has already been applied, skipping..." ) - config.close() - dll.close() return - config.close() - dll.close() - # Download the archive with urlopen(arc, timeout=30) as resp: if resp.status != 200: log.warn(f'github returned the status code: {resp.status}') return - with open(tmp, mode='wb', buffering=0) as file: + with tmp.open('wb', 0) as file: chunk_size = 64 * 1024 # 64 KB buffer = bytearray(chunk_size) view = memoryview(buffer) @@ -81,9 +75,10 @@ def main() -> None: # Rename the old files and apply the fix randstr = os.urandom(16).hex() log.info(f"Renaming 'config.json' -> '.{randstr}.config.json.bak'") + path_config.rename(install_dir / f'.{randstr}.config.json.bak') + log.info(f"Renaming 'd3d9.dll' -> '.{randstr}.d3d9.dll.bak'") - os.rename(path_config, f'{install_dir}/.{randstr}.config.json.bak') - os.rename(path_dll, f'{install_dir}/.{randstr}.d3d9.dll.bak') + path_dll.rename(install_dir / f'.{randstr}.d3d9.dll.bak') with ZipFile(tmp, mode='r') as zipfile: log.info("Fixing in-game font for 'Flowers - Le Volume Sur Hiver'...") diff --git a/gamefixes-steam/105400.py b/gamefixes-steam/105400.py index d81e334c..8b87f8da 100755 --- a/gamefixes-steam/105400.py +++ b/gamefixes-steam/105400.py @@ -1,6 +1,5 @@ """Game fix for Fable III""" -import os import shutil from .. import util @@ -12,15 +11,15 @@ def main() -> None: util.protontricks('xliveless') # Remove Windows Live folder - dirpath = os.path.join( - util.protonprefix(), - 'drive_c', - 'Program Files', - 'Common Files', - 'Microsoft Shared', - 'Windows Live', + dirpath = ( + util.protonprefix() / + 'drive_c' + 'Program Files' + 'Common Files' + 'Microsoft Shared' + 'Windows Live' ) - if os.path.exists(dirpath): + if dirpath.is_dir(): shutil.rmtree(dirpath) else: - log(f"Path '{dirpath}' could not be found") + log.info(f"Path '{dirpath}' could not be found") diff --git a/gamefixes-steam/1237970.py b/gamefixes-steam/1237970.py index 767ffee3..cc9262ab 100755 --- a/gamefixes-steam/1237970.py +++ b/gamefixes-steam/1237970.py @@ -1,16 +1,13 @@ """Game fix for Titanfall 2""" -import os -import subprocess -import glob from .. import util def main() -> None: """Allow -northstar option to work""" - # Define game directory - install_dir = glob.escape(util.get_game_install_path()) + # Path of backup file + backup_file = util.get_game_install_path() / 'Titanfall2.exe.bak' # Restore original titanfall2.exe if NorthstarLauncher.exe was previously symlinked - if os.path.isfile(install_dir + '/Titanfall2.exe.bak'): - subprocess.run(['mv', 'Titanfall2.exe.bak', 'Titanfall2.exe'], check=False) + if backup_file.is_file(): + backup_file.rename(backup_file.with_suffix('')) diff --git a/gamefixes-steam/1245620.py b/gamefixes-steam/1245620.py index 51f82380..45f52bab 100644 --- a/gamefixes-steam/1245620.py +++ b/gamefixes-steam/1245620.py @@ -1,11 +1,10 @@ """Game fix for Elden Ring: Create the `DLC.bdt` and `DLC.bhd` files to work around the "Inappropriate activity detected" error for players that don't own the DLC""" -from pathlib import Path from .. import util def main() -> None: - game_dir = Path(util.get_game_install_path()) / 'Game' + game_dir = util.get_game_install_path() / 'Game' # Create the DLC.bdt file if it doesn't already exist, which is known to fix Easy AntiCheat not working for players that don't own the DLC # A blank file is enough to get multiplayer working (game_dir / 'DLC.bdt').touch(exist_ok=True) diff --git a/gamefixes-steam/1449280.py b/gamefixes-steam/1449280.py index 34470cd4..bcec6a92 100755 --- a/gamefixes-steam/1449280.py +++ b/gamefixes-steam/1449280.py @@ -1,6 +1,5 @@ """Game fix for Ghostbusters: The Video Game Remastered (2019)""" -from pathlib import Path from .. import util from ..logger import log @@ -8,9 +7,13 @@ def main() -> None: # This directory is required to make the game settings persistent # [source: https://www.pcgamingwiki.com/wiki/Ghostbusters:_The_Video_Game_Remastered#Game_settings_do_not_save] - save_dir = f'{util.protonprefix()}/drive_c/users/steamuser/Local Settings/Application Data/GHOSTBUSTERS' + save_dir = ( + util.protonprefix() + / 'drive_c/users/steamuser/Local Settings/' + 'Application Data/GHOSTBUSTERS' + ) try: - Path(save_dir).mkdir(parents=True, exist_ok=True) + save_dir.mkdir(parents=True, exist_ok=True) except OSError as e: - log(f"Not able to make the settings directory at '{save_dir}': {e}") + log.warn(f"Not able to make the settings directory at '{save_dir}': {e}") diff --git a/gamefixes-steam/1873170.py b/gamefixes-steam/1873170.py index 66913271..11eae689 100644 --- a/gamefixes-steam/1873170.py +++ b/gamefixes-steam/1873170.py @@ -1,39 +1,52 @@ """Cease to Breathe -Replace included nwjs(0.71) wich doesn't work with 0.86 +Replace included nwjs (0.71) - which doesn't work - with 0.86 Fix cursor hitbox (set frame=false in package.json) Updated from 0.85 that didn't display custom cursors. """ -import os -import glob import shutil import urllib.request import zipfile -import subprocess import hashlib + from .. import util from ..logger import log +from pathlib import Path def main() -> None: util.replace_command('CTB.exe', 'nw.exe') - install_dir = glob.escape(util.get_game_install_path()) - if not os.path.isfile(os.path.join(install_dir, 'nw.exe')): - url = 'https://dl.nwjs.io/v0.86.0/nwjs-v0.86.0-win-x64.zip' - hashsum = 'ed2681847162e0fa457dd55e293b6f331ccd58acedd934a98e7fe1406c26dd4f' - nwjs = os.path.basename(url) - urllib.request.urlretrieve(url, nwjs) - with open(nwjs, 'rb') as f: - nwjs_sum = hashlib.sha256(f.read()).hexdigest() - if hashsum == nwjs_sum: - with zipfile.ZipFile(nwjs, 'r') as zip_ref: - zip_ref.extractall(install_dir) - nwjs = os.path.join(install_dir, nwjs.rsplit('.', 1)[0]) - shutil.copytree(nwjs, install_dir, dirs_exist_ok=True) - shutil.rmtree(nwjs) - else: - log(f"{nwjs} checksum doesn't match, fix not applied.") - subprocess.call( - [f'sed -i \'s/"frame": true/"frame": false/\' "{install_dir}/package.json"'], - shell=True, - ) + install_dir = util.get_game_install_path() + + install_nwjs(install_dir) + patch_package_json(install_dir) + + +def install_nwjs(install_dir: Path) -> None: + if (install_dir / 'nw.exe').is_file(): + return + + url = 'https://dl.nwjs.io/v0.86.0/nwjs-v0.86.0-win-x64.zip' + hashsum = 'ed2681847162e0fa457dd55e293b6f331ccd58acedd934a98e7fe1406c26dd4f' + nwjs = Path(Path(url).name) + urllib.request.urlretrieve(url, nwjs) + + # Check digest + nwjs_sum = hashlib.sha256(nwjs.read_bytes()).hexdigest() + if hashsum != nwjs_sum: + log.warn(f"{nwjs} checksum doesn't match, fix not applied.") + return + + # Install + with zipfile.ZipFile(nwjs, 'r') as zip_ref: + zip_ref.extractall(install_dir) + nwjs_dir = install_dir / nwjs.with_suffix('') + shutil.copytree(nwjs_dir, install_dir, dirs_exist_ok=True) + shutil.rmtree(nwjs_dir) + + +def patch_package_json(install_dir: Path) -> None: + json_file = install_dir / 'package.json' + json = json_file.read_text() + json = json.replace('"frame": true', '"frame": false', 1) + json_file.write_text(json) diff --git a/gamefixes-steam/22330.py b/gamefixes-steam/22330.py index 16f7b9ea..98145087 100755 --- a/gamefixes-steam/22330.py +++ b/gamefixes-steam/22330.py @@ -11,7 +11,6 @@ import os -from dataclasses import dataclass from .. import util def main_with_id(game_id: str) -> None: @@ -22,19 +21,11 @@ def main_with_id(game_id: str) -> None: # Run script extender if it exists. mapping = get_redirect_name(game_id) - if os.path.isfile(mapping.to_name): - util.replace_command(mapping.from_name, mapping.to_name) + if os.path.isfile(mapping.to_value): + util.replace_command(mapping.from_value, mapping.to_value) -@dataclass -class Redirect: - """Used for replacements""" - - from_name: str - to_name: str - - -def get_redirect_name(game_id: str) -> Redirect: +def get_redirect_name(game_id: str) -> util.ReplaceType: """Mapping for SteamID -> script extender replacements""" mapping = { '22380': ('FalloutNV.exe', 'nvse_loader.exe'), # Fallout New Vegas @@ -45,4 +36,4 @@ def get_redirect_name(game_id: str) -> Redirect: '489830': ('SkyrimSELauncher.exe', 'skse64_loader.exe'), # Skyrim SE '1716740': ('Starfield.exe', 'sfse_loader.exe') # Starfield }.get(game_id, ('', '')) - return Redirect(*mapping) + return util.ReplaceType(*mapping) diff --git a/gamefixes-steam/23460.py b/gamefixes-steam/23460.py index b58bdaff..c3310cd7 100644 --- a/gamefixes-steam/23460.py +++ b/gamefixes-steam/23460.py @@ -3,16 +3,17 @@ Videos still don't work. """ -import os -import subprocess from .. import util +from pathlib import Path def main() -> None: util.protontricks('dotnet35sp1') + util.winedll_override('libvkd3d-1', 'n') + # Videos play and audio works but screen is black. # util.protontricks('quartz') # util.protontricks('klite') - if os.path.isdir('./data/shared/videos'): - subprocess.call(['mv', './data/shared/videos', './data/shared/_videos']) - util.winedll_override('libvkd3d-1', 'n') + video_path = Path('data/shared/videos') + if video_path.is_dir(): + video_path.rename(video_path.with_name('_videos')) diff --git a/gamefixes-steam/2475980.py b/gamefixes-steam/2475980.py index 74c70f48..bb46523b 100644 --- a/gamefixes-steam/2475980.py +++ b/gamefixes-steam/2475980.py @@ -1,33 +1,29 @@ -"""Gobliiins 5 +"""Gobliiins 5 (and Demo - 2505910) Setup doesn't work and language is default to french """ -import os -import sys -import subprocess -import glob from .. import util -def main() -> None: - if sys.argv[2].find('winsetup') != -1: - os.chdir(sys.argv[2][-29:-13]) +def main_with_id(game_id: str) -> None: + """The game consists of 4 parts, that are located in their own folder + They each have a separate config, that needs to be patched - install_dir = glob.escape(util.get_game_install_path()) - with open( - os.path.join(install_dir, 'Gobliiins5-Part4/acsetup.cfg'), encoding='utf-8' - ) as f: - if 'Linear' not in f.read(): - for i in range(1, 5): - subprocess.call( - [ - f"sed -i 's/filter=stdscale/filter=Linear/' {install_dir}/Gobliiins5-Part{i}/acsetup.cfg" - ], - shell=True, - ) - subprocess.call( - [ - f"sed -i 's/translation.*/translation=English/' {install_dir}/Gobliiins5-Part{i}/acsetup.cfg" - ], - shell=True, - ) + The demo launches from it's install directory and doesn't need a subfolder + """ + cfg_str = """ + [language] + translation=English + + [graphics] + filter=Linear + """ + + # Demo + if game_id == '2505910': + util.set_ini_options(cfg_str, 'acsetup.cfg', 'utf-8', 'game')# + return + + # Full + for i in range(1, 5): + util.set_ini_options(cfg_str, f'Gobliiins5-Part{i}/acsetup.cfg', 'utf-8', 'game') diff --git a/gamefixes-steam/2505910.py b/gamefixes-steam/2505910.py new file mode 120000 index 00000000..26b16761 --- /dev/null +++ b/gamefixes-steam/2505910.py @@ -0,0 +1 @@ +2475980.py \ No newline at end of file diff --git a/gamefixes-steam/257420.py b/gamefixes-steam/257420.py index 92fb0b0d..5671bca0 100755 --- a/gamefixes-steam/257420.py +++ b/gamefixes-steam/257420.py @@ -1,14 +1,18 @@ """Game fix for Serious Sam 4""" -import os -import subprocess +from pathlib import Path def main() -> None: """Graphics API workaround""" - if not os.path.isfile('UserCfg.lua.bak'): - subprocess.call(['cp', 'UserCfg.lua', 'UserCfg.lua.bak']) + lua_file = Path('UserCfg.lua') + bak_file = lua_file.with_suffix('.lua.bak') - # Assume UTF-8 - with open('UserCfg.lua', 'a+', encoding='utf-8') as f: - f.write('sfx_strAPI = "OpenAL";') + if bak_file.is_file(): + return + + text = lua_file.read_text('utf-8') + text += '\nsfx_strAPI = "OpenAL";' + + lua_file.rename(bak_file) + lua_file.write_text(text, 'utf-8') diff --git a/gamefixes-steam/294700.py b/gamefixes-steam/294700.py index 15bbf59e..478e1b78 100755 --- a/gamefixes-steam/294700.py +++ b/gamefixes-steam/294700.py @@ -1,27 +1,24 @@ """Game fix for Putt-Putt: Pep's Birthday Surprise""" -import os from .. import util -# Putt-Putt: PBS doesn't run unless there is a CD-ROM drive attached. def main() -> None: - dosdevice = os.path.join(util.protonprefix(), 'dosdevices/r:') - if not os.path.exists(dosdevice): - os.symlink('/tmp', dosdevice) # create symlink for dosdevices + """The game doesn't run unless there is a CD-ROM drive attached.""" + util.create_dos_device() + # sets up ID? exported from regedit util.regedit_add( 'HKLM\\System\\MountedDevices', '\\??\\Volume{00000000-0000-0000-0000-000000000052}', 'REG_BINARY', '2f746d7000', - ) # sets up ID? exported from regedit + ) + + # sets up dosdevice? exported from regedit util.regedit_add( 'HKLM\\System\\MountedDevices', '\\DosDevices\\R:', 'REG_BINARY', '5c005c002e005c0064003a000000', - ) # sets up dosdevice? exported from regedit - util.regedit_add( - 'HKLM\\Software\\Wine\\Drives', 'r:', 'REG_SZ', 'cdrom', True - ) # designate drive as CD-ROM, requires 64-bit access + ) diff --git a/gamefixes-steam/302370.py b/gamefixes-steam/302370.py index be68b46c..aa898e40 100755 --- a/gamefixes-steam/302370.py +++ b/gamefixes-steam/302370.py @@ -4,31 +4,29 @@ edit registry to avoid ffdshow compatibility manager popup """ -import os -import subprocess from .. import util def main() -> None: util.protontricks('rsx3d') + if util.protontricks('lavfilters'): - util.regedit_add( - 'HKEY_CURRENT_USER\\Software\\GNU\\ffdshow', - 'blacklist', - 'REG_SZ', - 'OVERSEER.EXE', - ) - util.regedit_add( - 'HKEY_CURRENT_USER\\Software\\GNU\\ffdshow_audio', - 'blacklist', - 'REG_SZ', - 'OVERSEER.EXE', - ) + patch_lavfilter_registry() + if util.protontricks('dgvoodoo2'): - syswow64 = os.path.join( - util.protonprefix(), 'drive_c/windows/syswow64', 'dgvoodoo.conf' - ) - subprocess.call( - [f"sed -i '/[DirectX]/ {{/Resolution/s/max/unforced/}}' {syswow64}"], - shell=True, - ) + util.patch_voodoo_conf() + + +def patch_lavfilter_registry() -> None: + util.regedit_add( + 'HKEY_CURRENT_USER\\Software\\GNU\\ffdshow', + 'blacklist', + 'REG_SZ', + 'OVERSEER.EXE', + ) + util.regedit_add( + 'HKEY_CURRENT_USER\\Software\\GNU\\ffdshow_audio', + 'blacklist', + 'REG_SZ', + 'OVERSEER.EXE', + ) diff --git a/gamefixes-steam/328500.py b/gamefixes-steam/328500.py index ce1673e6..ddf2459c 100755 --- a/gamefixes-steam/328500.py +++ b/gamefixes-steam/328500.py @@ -1,23 +1,20 @@ """Game fix for Potatoman Seeks the Troof""" -import os from .. import util def main() -> None: - """The file mms.cfg must have the string OverrideGPUValidation=1 written""" - fix_installed = False - prefix = util.protonprefix() - macro_path = 'drive_c/windows/syswow64/Macromed/Flash' - flash_path = os.path.join(prefix, macro_path) - mms_path = os.path.join(flash_path, 'mms.cfg') - os.makedirs(flash_path, exist_ok=True) - if os.path.isfile(mms_path): - with open(mms_path, encoding='utf-8') as f: - for line in f: - if 'OverrideGPUValidation' in line: - fix_installed = True - if not fix_installed: - with open(mms_path, 'a', encoding='utf-8') as f: - f.write('\n') - f.write('OverrideGPUValidation=1') + """The file mms.cfg must contain the string `OverrideGPUValidation=1`""" + flash_path = util.get_path_syswow64() / 'Macromed/Flash' + flash_path.mkdir(parents=True, exist_ok=True) + + mms_path = flash_path / 'mms.cfg' + if not mms_path.is_file(): + return + + mms = mms_path.read_text(encoding='utf-8') + if 'OverrideGPUValidation' in mms: + return + + mms += '\nOverrideGPUValidation=1' + mms_path.write_text(mms, encoding='utf-8') diff --git a/gamefixes-steam/359870.py b/gamefixes-steam/359870.py index 1b8986e5..6c967a73 100755 --- a/gamefixes-steam/359870.py +++ b/gamefixes-steam/359870.py @@ -1,18 +1,27 @@ """Game fix for FFX/X-2 HD Remaster""" -import os from .. import util def main() -> None: # Game defaults to Japanese language, set this to English instead - configpath = os.path.join( - util.protonprefix(), - 'drive_c/users/steamuser/My Documents/SQUARE ENIX/FINAL FANTASY X&X-2 HD Remaster', + config_path = ( + util.protonprefix() + / 'drive_c/users/steamuser/My Documents/' + 'SQUARE ENIX/FINAL FANTASY X&X-2 HD Remaster' ) - if not os.path.exists(configpath): - os.makedirs(configpath) - configgame = os.path.join(configpath, 'GameSetting.ini') - if not os.path.isfile(configgame): - with open(configgame, 'w+', encoding='utf-8') as f: - f.write('Language=en') + + # Ensure path exists + config_path.mkdir(parents=True, exist_ok=True) + + # Find / create config file + config_file = config_path / 'GameSetting.ini' + config_file.touch() + + # Apply patch + config = config_file.read_text(encoding='utf-8') + if 'Language' in config: + return + + config += '\nLanguage=en' + config_file.write_text(config, encoding='utf-8') diff --git a/gamefixes-steam/379720.py b/gamefixes-steam/379720.py index 60edd2c7..465820b0 100755 --- a/gamefixes-steam/379720.py +++ b/gamefixes-steam/379720.py @@ -1,7 +1,5 @@ """Game fix for Doom 2016""" -import os -import shutil import urllib.request import zipfile @@ -9,20 +7,24 @@ def main() -> None: - """Enable preload options""" # Enable preload options util.append_argument('+r_renderAPI 1') + install_ccel() - installpath = os.path.abspath(os.getcwd()) - url = ( - 'https://github.com/Riesi/CChromaEditor/files/2277158/CChromaEditorLibrary.zip' - ) - - if not os.path.isfile(os.path.join(installpath, 'CChromaEditorLibrary.dll.bak')): - urllib.request.urlretrieve(url, 'CChromaEditorLibrary.zip') - shutil.move( - os.path.join(installpath, 'CChromaEditorLibrary.dll'), - os.path.join(installpath, 'CChromaEditorLibrary.dll.bak'), - ) - with zipfile.ZipFile('CChromaEditorLibrary.zip', 'r') as zip_ref: - zip_ref.extractall(installpath) + +def install_ccel() -> None: + install_path = util.get_game_install_path() + cchroma_file = install_path / 'CChromaEditorLibrary.dll' + cchroma_copy = cchroma_file.with_suffix('.dll.bak') + + if cchroma_copy.is_file(): + return + + # Download and backup + url = 'https://github.com/Riesi/CChromaEditor/files/2277158/CChromaEditorLibrary.zip' + urllib.request.urlretrieve(url, 'CChromaEditorLibrary.zip') + cchroma_file.rename(cchroma_copy) + + # Extract + with zipfile.ZipFile('CChromaEditorLibrary.zip', 'r') as zip_ref: + zip_ref.extractall(install_path) diff --git a/gamefixes-steam/386360.py b/gamefixes-steam/386360.py index f93e51a5..38a9035e 100755 --- a/gamefixes-steam/386360.py +++ b/gamefixes-steam/386360.py @@ -1,32 +1,18 @@ """Game fix for Smite""" -import glob -import os -import subprocess from .. import util def main() -> None: - """Fix EAC location in smite""" - install_dir = glob.escape(util.get_game_install_path()) + """Fix incorrect EAC locations in smite""" + install_dir = util.get_game_install_path() + eac_file_x64 = install_dir / 'Win64/EasyAntiCheat/easyanticheat_x64.so' + eac_file_x86 = install_dir / 'Win32/EasyAntiCheat/easyanticheat_x86.so' - # Fix EAC incorrect location: - if not os.path.exists(install_dir + '/Win64/EasyAntiCheat/easyanticheat_x64.so'): - subprocess.call( - [ - 'ln', - '-s', - install_dir + '/EasyAntiCheat/easyanticheat_x64.so', - install_dir + '/Win64/EasyAntiCheat/', - ] - ) + # x64 + if not eac_file_x64.exists(): + eac_file_x64.symlink_to(install_dir / 'EasyAntiCheat/easyanticheat_x64.so') - if not os.path.exists(install_dir + '/Win32/EasyAntiCheat/easyanticheat_x86.so'): - subprocess.call( - [ - 'ln', - '-s', - install_dir + '/EasyAntiCheat/easyanticheat_x86.so', - install_dir + '/Win32/EasyAntiCheat/', - ] - ) + # x86 + if not eac_file_x86.exists(): + eac_file_x86.symlink_to(install_dir / 'EasyAntiCheat/easyanticheat_x86.so') diff --git a/gamefixes-steam/39500.py b/gamefixes-steam/39500.py index efeb884a..75f95012 100755 --- a/gamefixes-steam/39500.py +++ b/gamefixes-steam/39500.py @@ -1,6 +1,5 @@ -"""Game fix for Gothic 3""" +"""Game fix for Gothic 3 (and Forsaken Gods Enhanced Edition)""" -import os from .. import util @@ -12,4 +11,4 @@ def main() -> None: FpS.Max=0 """ - util.set_ini_options(game_opts, os.path.join('Ini', 'ge3.ini'), 'cp1251', 'game') + util.set_ini_options(game_opts, 'Ini/ge3.ini', 'cp1251', 'game') diff --git a/gamefixes-steam/409090.py b/gamefixes-steam/409090.py index 8e2e350e..86db91f6 100755 --- a/gamefixes-steam/409090.py +++ b/gamefixes-steam/409090.py @@ -5,21 +5,13 @@ copy dgvoodoo2 d3d9.dll every time otherwise it gets overwritten """ -import os -import subprocess import shutil from .. import util def main() -> None: - syswow64 = os.path.join(util.protonprefix(), 'drive_c/windows/syswow64') if util.protontricks('dgvoodoo2'): - subprocess.call( - [ - f"sed -i '/[DirectX]/ {{/Resolution/s/max/unforced/}}' {syswow64}/dgvoodoo.conf" - ], - shell=True, - ) - shutil.copy( - os.path.join(syswow64, 'dgd3d9.dll'), os.path.join(syswow64, 'd3d9.dll') - ) + util.patch_voodoo_conf() + + wow64_path = util.get_path_syswow64() + shutil.copy(wow64_path / 'dgd3d9.dll', wow64_path / 'd3d9.dll') diff --git a/gamefixes-steam/429720.py b/gamefixes-steam/429720.py index f7fa21dd..c3b6d47b 100755 --- a/gamefixes-steam/429720.py +++ b/gamefixes-steam/429720.py @@ -1,19 +1,20 @@ """Game fix for IMSCARED""" -import os -import getpass +import shutil from .. import util +from pathlib import Path def main() -> None: # IMSCARED relies on a folder on the user's Desktop being accessible # The problem is that all of the folders in Proton are sandboxed # So this protonfix works around that - desktoppath = os.path.join(util.protonprefix(), 'drive_c/users/steamuser/Desktop') - if os.path.exists(desktoppath): - if os.path.islink(desktoppath): - os.unlink(desktoppath) - else: - os.rmdir(desktoppath) - dst = '/home/' + getpass.getuser() + '/Desktop/' - os.symlink(dst, desktoppath) + desktop_path = util.protonprefix() / 'drive_c/users/steamuser/Desktop' + + if desktop_path.is_symlink(): + desktop_path.unlink() + elif desktop_path.is_dir(): + shutil.rmtree(desktop_path) + + target = Path.home() / 'Desktop' + desktop_path.symlink_to(target) diff --git a/gamefixes-steam/452440.py b/gamefixes-steam/452440.py index dd4814f6..7b481dfe 100644 --- a/gamefixes-steam/452440.py +++ b/gamefixes-steam/452440.py @@ -6,7 +6,6 @@ outside its text box area. """ -import os from hashlib import sha256 from subprocess import run @@ -17,35 +16,37 @@ def main() -> None: env = protonmain.g_session.env.copy() - wine = f'{util.protondir()}/files/bin/wine64' + wine = util.protondir() / 'files/bin/wine64' install_dir = util.get_game_install_path() # Font installer inside the `fonts` subdir - font_installer = 'overlock_mod_font_installer.exe' + font_installer = install_dir / 'font/overlock_mod_font_installer.exe' # Digest of the font installer hashsum = 'd3bd48162d91322c3d2861cdccc538955336eff7f0fe50eeafee1b7551a52152' - if os.path.isfile(f'{util.protonprefix()}/drive_c/windows/Fonts/Overlock-Mod.ttf'): + if (util.protonprefix() / 'drive_c/windows/Fonts/Overlock-Mod.ttf').is_file(): log.info("Font 'Overlock-Mod.ttf' already installed in prefix, skipping...") return - if not os.path.isfile(f'{install_dir}/font/{font_installer}'): - log.warn(f"Could not find '{font_installer}' in '{install_dir}', skipping...") + if not font_installer.is_file(): + log.warn(f"Could not find '{font_installer}', skipping...") return - with open(f'{install_dir}/font/{font_installer}', mode='rb') as file: - if sha256(file.read()).hexdigest() != hashsum: - log.warn(f'Digest mismatched: {font_installer}') - log.warn(f"Expected '{hashsum}', skipping...") - return + # Verify file + font_bytes = font_installer.read_bytes() + font_digest = sha256(font_bytes).hexdigest() + if font_digest != hashsum: + log.warn(f'Digest mismatched: {font_installer}') + log.warn(f"Expected '{hashsum}', got '{font_digest}', skipping...") + return log.info("Installing font 'Overlock-Mod.ttf' in prefix...") - retc = run( - [wine, 'start', '/unix', f'{install_dir}/font/{font_installer}', '/silent'], + ret = run( + [wine, 'start', '/unix', font_installer, '/silent'], check=False, env=env, ).returncode - if retc: - log.warn(f"Running '{font_installer}' exited with the status code: {retc}") + if ret: + log.warn(f"Running '{font_installer}' exited with the status code: {ret}") diff --git a/gamefixes-steam/46500.py b/gamefixes-steam/46500.py index 750bb4b0..3a3ed80e 100755 --- a/gamefixes-steam/46500.py +++ b/gamefixes-steam/46500.py @@ -1,12 +1,12 @@ """Game fix for Syberia""" -import os -import subprocess +from pathlib import Path def main() -> None: """Needs player.ini to prevent black screen on load""" - if not os.path.isfile('player.ini'): - subprocess.call(['touch', 'player.ini']) - with open('player.ini', 'w+', encoding='utf-8') as f: - f.write('800 600 32 0 BaseCMO.cmo') + ini_file = Path('player.ini') + if ini_file.is_file(): + return + + ini_file.write_text('800 600 32 0 BaseCMO.cmo', 'utf-8') diff --git a/gamefixes-steam/497360.py b/gamefixes-steam/497360.py index d8db19e7..ce80d676 100644 --- a/gamefixes-steam/497360.py +++ b/gamefixes-steam/497360.py @@ -6,93 +6,87 @@ Widescreen supported (16:9/21:9, 32:9 not tested) """ -import os -import subprocess from .. import util def main() -> None: - dosdevice = os.path.join(util.protonprefix(), 'dosdevices/r:') - if not os.path.exists(dosdevice): - os.symlink('/tmp', dosdevice) # create symlink for dosdevices - util.regedit_add( - 'HKLM\\Software\\Wine\\Drives', 'r:', 'REG_SZ', 'cdrom', True - ) # designate drive as CD-ROM, requires 64-bit access + # Create a symlink in dosdevices + util.create_dos_device() + util.protontricks('quartz') util.protontricks('amstream') + # No errors but doesn't show videos on SYDNEY # util.protontricks('lavfilters') # Show videos but green background is visible util.protontricks('klite') - syswow64 = os.path.join(util.protonprefix(), 'drive_c/windows/syswow64') - if util.protontricks('dgvoodoo2'): - screen_width, screen_height = util.get_resolution() - width = int(screen_width / screen_height * 768 // 1) - subprocess.call( - [ - f"sed -i '/[DirectX]/ {{/Resolution/s/max/{width}x768/}}' {syswow64}/dgvoodoo.conf" - ], - shell=True, - ) - subprocess.call( - [ - f"sed -i '/[DirectXExt]/ {{/ExtraEnumeratedResolutions/s/= /= {width}x768,/}}' {syswow64}/dgvoodoo.conf" - ], - shell=True, - ) - util.regedit_add('HKCU\\Software\\Sierra On-Line') - util.regedit_add('HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3') - util.regedit_add( - 'HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3\\App', - 'Run Count', - 'REG_DWORD', - '0x1', - ) - util.regedit_add( - 'HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3\\Engine', - 'Full Screen', - 'REG_DWORD', - '0x1', - ) - util.regedit_add( - 'HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3\\Engine', - 'Monitor', - 'REG_DWORD', - '0x0', - ) - util.regedit_add( - 'HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3\\Engine', - 'Rasterizer', - 'REG_SZ', - 'detect', - ) - util.regedit_add( - 'HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3\\Engine', - 'Rasterizer GUID', - 'REG_SZ', - '{00000000-0000-0000-0000-000000000000}', - ) - util.regedit_add( - 'HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3\\Engine', - 'Screen Height', - 'REG_DWORD', - '0x300', - ) - util.regedit_add( - 'HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3\\Engine', - 'Screen Width', - 'REG_DWORD', - hex(width), - ) - util.regedit_add( - 'HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3\\Engine\\Hardware', - 'Gamma', - 'REG_SZ', - '1.5', - ) - util.regedit_add( - 'HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3\\Engine\\Hardware', - 'Surface Quality', - 'REG_SZ', - 'High', - ) + + # Everything after this call should only be executed once + if not util.protontricks('dgvoodoo2'): + return + + # Get width of resolution + screen_width, screen_height = util.get_resolution() + width = int(screen_width / screen_height * 768 // 1) + + # dgvoodoo2 config patches + util.patch_voodoo_conf(value=util.ReplaceType('max', f'{width}x768/')) + util.patch_voodoo_conf(value=util.ReplaceType('', f'{width}x768/,'), key='ExtraEnumeratedResolutions') + + # Registry + util.regedit_add('HKCU\\Software\\Sierra On-Line') + util.regedit_add('HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3') + util.regedit_add( + 'HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3\\App', + 'Run Count', + 'REG_DWORD', + '0x1', + ) + util.regedit_add( + 'HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3\\Engine', + 'Full Screen', + 'REG_DWORD', + '0x1', + ) + util.regedit_add( + 'HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3\\Engine', + 'Monitor', + 'REG_DWORD', + '0x0', + ) + util.regedit_add( + 'HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3\\Engine', + 'Rasterizer', + 'REG_SZ', + 'detect', + ) + util.regedit_add( + 'HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3\\Engine', + 'Rasterizer GUID', + 'REG_SZ', + '{00000000-0000-0000-0000-000000000000}', + ) + util.regedit_add( + 'HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3\\Engine', + 'Screen Height', + 'REG_DWORD', + '0x300', + ) + util.regedit_add( + 'HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3\\Engine', + 'Screen Width', + 'REG_DWORD', + hex(width), + ) + util.regedit_add( + 'HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3\\Engine\\Hardware', + 'Gamma', + 'REG_SZ', + '1.5', + ) + util.regedit_add( + 'HKCU\\Software\\Sierra On-Line\\Gabriel Knight 3\\Engine\\Hardware', + 'Surface Quality', + 'REG_SZ', + 'High', + ) diff --git a/gamefixes-steam/508980.py b/gamefixes-steam/508980.py index 671f2ca5..6ff4bbc7 100755 --- a/gamefixes-steam/508980.py +++ b/gamefixes-steam/508980.py @@ -8,14 +8,20 @@ def main() -> None: """Change setting FSAA to 0 in graphics.config""" config = ( util.protonprefix() - + 'drive_c/users/steamuser/Local Settings/' - + 'Application Data/Crashday/config/graphics.config' + / 'drive_c/users/steamuser/Local Settings/' + 'Application Data/Crashday/config/graphics.config' ) # https://stackoverflow.com/a/45435707 - with open(config, encoding='utf-8') as file: - json_data = json.load(file) - if 'FSAA' in json_data: - json_data['FSAA'] = 0 - with open(config, 'w', encoding='utf-8') as file: + with config.open(encoding='utf-8') as file: + json_data: dict = json.load(file) + + # Attempt to change FSAA parameter + if json_data.get('FSAA') != 0: + json_data['FSAA'] = 0 + else: + return + + # Only write, if config was alternated + with config.open('w', encoding='utf-8') as file: json.dump(json_data, file, indent=4) diff --git a/gamefixes-steam/65600.py b/gamefixes-steam/65600.py deleted file mode 100755 index 67937ded..00000000 --- a/gamefixes-steam/65600.py +++ /dev/null @@ -1,15 +0,0 @@ -"""Game fix for Gothic 3 Forsaken Gods Enhanced Edition""" - -import os -from .. import util - - -def main() -> None: - """Modify ge3.ini""" - game_opts = """ - [Engine.Setup] - Timer.ThreadSafe=false - FpS.Max=0 - """ - - util.set_ini_options(game_opts, os.path.join('Ini', 'ge3.ini'), 'cp1251', 'game') diff --git a/gamefixes-steam/65600.py b/gamefixes-steam/65600.py new file mode 120000 index 00000000..e56349da --- /dev/null +++ b/gamefixes-steam/65600.py @@ -0,0 +1 @@ +39500.py \ No newline at end of file diff --git a/util.py b/util.py index 91d18578..35ecb21b 100644 --- a/util.py +++ b/util.py @@ -12,6 +12,7 @@ import urllib.request import functools from pathlib import Path +from dataclasses import dataclass from datetime import datetime, timezone from socket import socket, AF_INET, SOCK_DGRAM from typing import Literal, Any, Union, Optional @@ -36,6 +37,13 @@ StrPath = Union[str, Path] BasePathType = Literal['user', 'game'] OverrideTypes = Literal['n', 'b', 'n,b', 'b,n', ''] +DosDeviceTypes = Literal['hd', 'network', 'floppy', 'cdrom'] +@dataclass +class ReplaceType: + """Used for replacements""" + + from_value: str + to_value: str class ProtonVersion: """Parses the proton version and build timestamp""" @@ -59,10 +67,16 @@ def protondir() -> Path: @functools.lru_cache def protonprefix() -> Path: - """Returns the wineprefix used by proton""" + """Returns wineprefix's path used by proton""" return Path(os.environ['STEAM_COMPAT_DATA_PATH']) / 'pfx' +@functools.lru_cache +def get_path_syswow64() -> Path: + """Returns the syswow64's path in the prefix""" + return protonprefix() / 'drive_c/windows/syswow64' + + @functools.lru_cache def proton_version() -> ProtonVersion: """Returns the version of proton""" @@ -911,18 +925,65 @@ def set_game_drive(enabled: bool) -> None: This function modifies the `compat_config` to include or exclude the "gamedrive" option based on the `enabled` parameter. - Parameters - ---------- - enabled : bool - If True, add "gamedrive" to `compat_config`. - If False, remove "gamedrive" from `compat_config`. - - Returns - ------- - None - + Args: + enabled (bool): + If True, add "gamedrive" to `compat_config`. + If False, remove "gamedrive" from `compat_config`. """ if enabled: protonmain.g_session.compat_config.add("gamedrive") else: protonmain.g_session.compat_config.discard("gamedrive") + + +def create_dos_device(letter: str = 'r', dev_type: DosDeviceTypes = 'cdrom') -> bool: + """Create a symlink to '/tmp' in the dosdevices folder of the prefix and register it + + Args: + letter (str, optional): Letter that the device gets assigned to, must be len = 1 + dev_type (DosDeviceTypes, optional): The device's type which will be registered to wine + + Returns: + bool: True, if device was created + """ + assert len(letter) == 1 + + dosdevice = protonprefix() / f'dosdevices/{letter}:' + if dosdevice.exists(): + return False + + # Create a symlink in dosdevices + dosdevice.symlink_to('/tmp', True) + + # designate device as CD-ROM, requires 64-bit access + regedit_add('HKLM\\Software\\Wine\\Drives', f'{letter}:', 'REG_SZ', dev_type, True) + return True + + +def patch_conf_value(file: Path, key: str, value: ReplaceType) -> None: + """Patches a single value in the given config file + + Args: + file (Path): Path to the config file to patch + key (str): The key of the value to patch + value (ReplaceType): The value that should be replaced + """ + if not file.is_file(): + log.warn(f'File "{file}" can not be opened to patch config value.') + return + + conf = file.read_text() + regex = rf"^\s*(?P{key}\s*=\s*)(?P{value.from_value})\s*$" + conf = re.sub(regex, rf'\g{value.to_value}', conf, flags=re.MULTILINE) + file.write_text(conf) + + +def patch_voodoo_conf(file: Path = get_path_syswow64() / 'dgvoodoo.conf', key: str = 'Resolution', value: ReplaceType = ReplaceType('unforced', 'max')) -> None: + """Patches the dgVoodoo2 config file. By default `Resolution` will be set from `unforced` to `max`. + + Args: + file (Path, optional): Path to the config file to patch + key (str, optional): The key of the value to patch + value (ReplaceType, optional): The value that should be replaced + """ + patch_conf_value(file, key, value) From c69b32e342d8db8e731b24e261bfc425277b0e10 Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Fri, 18 Oct 2024 16:32:42 +0200 Subject: [PATCH 18/21] Fixed symlinks in action-shellcheck --- .github/workflows/ci.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 496ffe20..ff60907e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,11 +32,13 @@ jobs: # FIXME: problem matcher is currently disabled upstream, using a fork for the moment # https://github.com/ludeeus/action-shellcheck/pull/103 + # FIXME: symlinks don't work upstream + # https://github.com/ludeeus/action-shellcheck/pull/104 - name: Run ShellCheck - uses: Root-Core/action-shellcheck@master + uses: Root-Core/action-shellcheck@fork with: - format: gcc # gcc is used for the problem matcher - additional_files: winetricks # by default, only files with according extensions are checked + ignore_paths: subprojects # prevent ShellCheck from checking unrelated files + ignore_symlinks: false # winetricks is symlinked # Ruff uses ruff.toml for it's configuration - name: Lint with Ruff From 5142e5cfeb4322f5122e112cc1cb0a65870fd5cd Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Fri, 18 Oct 2024 18:05:43 +0200 Subject: [PATCH 19/21] Fixed unit test --- protonfixes_test.py | 4 ++-- util.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/protonfixes_test.py b/protonfixes_test.py index 83ce91b9..e8d4b71a 100644 --- a/protonfixes_test.py +++ b/protonfixes_test.py @@ -192,8 +192,8 @@ def testGetGameNone(self): self.assertTrue('SteamGameId' not in os.environ, 'SteamGameId is set') self.assertTrue('UMU_ID' not in os.environ, 'UMU_ID is set') self.assertTrue('SteamAppId' not in os.environ, 'SteamAppId is set') - result = func() - self.assertFalse(result, 'None was not returned') + with self.assertRaises(SystemExit): + func() def testGetStoreNameZoom(self): """Pass zoomplatform as store name diff --git a/util.py b/util.py index 35ecb21b..00c28302 100644 --- a/util.py +++ b/util.py @@ -68,7 +68,7 @@ def protondir() -> Path: @functools.lru_cache def protonprefix() -> Path: """Returns wineprefix's path used by proton""" - return Path(os.environ['STEAM_COMPAT_DATA_PATH']) / 'pfx' + return Path(os.environ.get('STEAM_COMPAT_DATA_PATH', '')) / 'pfx' @functools.lru_cache From 54c18c266bc39066a946eae6d746daa345b8d132 Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Sat, 19 Oct 2024 04:11:30 +0200 Subject: [PATCH 20/21] Changed LiteralTypes to Enum classes - Logger: use Enum values for colors - Use default parameters, if argument is equal --- config_base.py | 12 +- engine.py | 18 +-- gamefixes-gog/umu-1141086411.py | 9 +- gamefixes-gog/umu-1580232252.py | 4 +- gamefixes-gog/umu-1584652180.py | 2 +- gamefixes-gog/umu-1771973390.py | 4 +- gamefixes-steam/105000.py | 2 +- gamefixes-steam/1062040.py | 2 +- gamefixes-steam/1286880.py | 2 +- gamefixes-steam/1500540.py | 4 +- gamefixes-steam/1664350.py | 2 +- gamefixes-steam/1681970.py | 2 +- gamefixes-steam/200490.py | 2 +- gamefixes-steam/201480.py | 2 +- gamefixes-steam/206480.py | 2 +- gamefixes-steam/212500.py | 2 +- gamefixes-steam/223750.py | 2 +- gamefixes-steam/230820.py | 2 +- gamefixes-steam/23460.py | 2 +- gamefixes-steam/237890.py | 2 +- gamefixes-steam/243200.py | 2 +- gamefixes-steam/244210.py | 2 +- gamefixes-steam/244850.py | 2 +- gamefixes-steam/2475980.py | 4 +- gamefixes-steam/287310.py | 4 +- gamefixes-steam/292410.py | 9 +- gamefixes-steam/33990.py | 2 +- gamefixes-steam/39500.py | 2 +- gamefixes-steam/559620.py | 4 +- gamefixes-steam/593600.py | 2 +- gamefixes-steam/63710.py | 2 +- gamefixes-steam/65540.py | 8 +- gamefixes-steam/70400.py | 2 +- gamefixes-steam/70420.py | 2 +- gamefixes-steam/950670.py | 2 +- gamefixes-steam/968370.py | 2 +- gamefixes-umu/umu-silenthill3.py | 2 +- gamefixes-umu/umu-starcitizen.py | 2 +- gamefixes-zoomplatform/umu-240200.py | 2 +- ...mu-4bff76f4-566a-4714-b481-95d3343afe22.py | 6 +- logger.py | 44 +++---- util.py | 108 ++++++++++++++---- 42 files changed, 176 insertions(+), 118 deletions(-) diff --git a/config_base.py b/config_base.py index 64774eb2..2263d4a0 100644 --- a/config_base.py +++ b/config_base.py @@ -9,7 +9,7 @@ from typing import Any from collections.abc import Callable -from logger import log, LogLevelType +from logger import log, LogLevel class ConfigBase: """Base class for configuration objects. @@ -35,7 +35,7 @@ def snake_case(cls, input: str) -> str: @staticmethod - def __log(message: str, level: LogLevelType = 'INFO') -> None: + def __log(message: str, level: LogLevel = LogLevel.INFO) -> None: log.log(f'[CONFIG]: {message}', level) @@ -131,7 +131,7 @@ def _get_parse_function(type_name: str) -> Callable[[str], Any]: }.get(type_name, None) if not value: value = parser_items.get - self.__log(f'Unknown type "{type_name}", falling back to "str".', 'WARN') + self.__log(f'Unknown type "{type_name}", falling back to "str".', LogLevel.WARN) return value # Iterate over the option objects in this section @@ -142,7 +142,7 @@ def _get_parse_function(type_name: str) -> Callable[[str], Any]: value = func(option_name) setattr(section, option_name, value) except Exception as ex: - self.__log(f'Failed to parse config file "{file}". Exception: "{ex}"', 'CRIT') + self.__log(f'Failed to parse config file "{file}". Exception: "{ex}"', LogLevel.CRIT) return False return True @@ -158,7 +158,7 @@ def write_config_file(self, file: Path) -> bool: """ # Only precede if the parent directory exists if not file.parent.is_dir(): - self.__log(f'Parent directory "{file.parent}" does not exist. Abort.', 'WARN') + self.__log(f'Parent directory "{file.parent}" does not exist. Abort.', LogLevel.WARN) return False # Create and populate ConfigParser @@ -176,6 +176,6 @@ def write_config_file(self, file: Path) -> bool: with file.open(mode='w') as stream: parser.write(stream) except Exception as ex: - self.__log(f'Failed to create config file "{file}". Exception: "{ex}"', 'CRIT') + self.__log(f'Failed to create config file "{file}". Exception: "{ex}"', LogLevel.CRIT) return False return True diff --git a/engine.py b/engine.py index a3850874..ae0fbc1a 100644 --- a/engine.py +++ b/engine.py @@ -2,7 +2,7 @@ import os import sys -from .logger import log, LogLevelType +from .logger import log, LogLevel class Engine: @@ -89,7 +89,7 @@ def _is_ue4(self) -> bool: """Detect Unreal Engine 4""" return False - def _log(self, ctx: str, msg: str, level: LogLevelType = 'INFO') -> None: + def _log(self, ctx: str, msg: str, level: LogLevel = LogLevel.INFO) -> None: """Log wrapper""" if self.engine_name is None: log.warn(ctx + ': Engine not defined') @@ -116,7 +116,7 @@ def nosplash(self) -> bool: self._add_argument('-nosplash') self._log('nosplash', 'splash screen disabled') else: - self._log('nosplash', 'not supported', 'WARN') + self._log('nosplash', 'not supported', LogLevel.WARN) return False return True @@ -126,7 +126,7 @@ def info(self) -> bool: self._add_argument('-help') self._log('info', 'command line arguments') else: - self._log('info', 'not supported', 'WARN') + self._log('info', 'not supported', LogLevel.WARN) return False return True @@ -139,7 +139,7 @@ def nointro(self) -> bool: self._add_argument('-skipintro') self._log('nointro', 'intro videos disabled') else: - self._log('nointro', 'not supported', 'WARN') + self._log('nointro', 'not supported', LogLevel.WARN) return False return True @@ -149,7 +149,7 @@ def launcher(self) -> bool: self._add_argument('-show-screen-selector') self._log('launcher', 'forced') else: - self._log('launcher', 'not supported', 'WARN') + self._log('launcher', 'not supported', LogLevel.WARN) return False return True @@ -162,14 +162,14 @@ def windowed(self) -> bool: self._add_argument('-windowed') self._log('windowed', 'window') else: - self._log('windowed', 'not supported', 'WARN') + self._log('windowed', 'not supported', LogLevel.WARN) return False return True def resolution(self, res: str) -> bool: """Force screen resolution""" if not isinstance(res, str): - self._log('resolution', 'not provided', 'WARN') + self._log('resolution', 'not provided', LogLevel.WARN) return False res_wh = res.split('x') @@ -183,7 +183,7 @@ def resolution(self, res: str) -> bool: self._add_argument('-width ' + res_wh[0] + ' -height ' + res_wh[1]) self._log('resolution', res) else: - self._log('resolution', 'not supported', 'WARN') + self._log('resolution', 'not supported', LogLevel.WARN) return False return True diff --git a/gamefixes-gog/umu-1141086411.py b/gamefixes-gog/umu-1141086411.py index b166d65a..a8099a7d 100644 --- a/gamefixes-gog/umu-1141086411.py +++ b/gamefixes-gog/umu-1141086411.py @@ -4,7 +4,8 @@ def main() -> None: - util.winedll_override('d3d8', 'n,b') # GOG's dxcfg / Steam006 fixes - util.winedll_override( - 'dsound', 'n,b' - ) # Ultimate ASI Loader / Silent Hill 4 Randomizer + # GOG's dxcfg / Steam006 fixes + util.winedll_override('d3d8', util.DllOverride.NATIVE_BUILTIN) + + # Ultimate ASI Loader / Silent Hill 4 Randomizer + util.winedll_override('dsound', util.DllOverride.NATIVE_BUILTIN) diff --git a/gamefixes-gog/umu-1580232252.py b/gamefixes-gog/umu-1580232252.py index 26a842e4..51830f7f 100644 --- a/gamefixes-gog/umu-1580232252.py +++ b/gamefixes-gog/umu-1580232252.py @@ -4,5 +4,5 @@ def main() -> None: - util.winedll_override('ddraw', 'n,b') - util.winedll_override('dinput', 'n,b') + util.winedll_override('ddraw', util.DllOverride.NATIVE_BUILTIN) + util.winedll_override('dinput', util.DllOverride.NATIVE_BUILTIN) diff --git a/gamefixes-gog/umu-1584652180.py b/gamefixes-gog/umu-1584652180.py index fc731bac..5ca937b4 100644 --- a/gamefixes-gog/umu-1584652180.py +++ b/gamefixes-gog/umu-1584652180.py @@ -4,4 +4,4 @@ def main() -> None: - util.winedll_override('ddraw', 'n,b') # GOG's dxcfg + util.winedll_override('ddraw', util.DllOverride.NATIVE_BUILTIN) # GOG's dxcfg diff --git a/gamefixes-gog/umu-1771973390.py b/gamefixes-gog/umu-1771973390.py index 4ac9a621..0be404a5 100644 --- a/gamefixes-gog/umu-1771973390.py +++ b/gamefixes-gog/umu-1771973390.py @@ -5,5 +5,5 @@ def main() -> None: """Override for wrapper shipped with the game""" - util.winedll_override('ddraw', 'n,b') - util.winedll_override('dinput', 'n,b') + util.winedll_override('ddraw', util.DllOverride.NATIVE_BUILTIN) + util.winedll_override('dinput', util.DllOverride.NATIVE_BUILTIN) diff --git a/gamefixes-steam/105000.py b/gamefixes-steam/105000.py index c4a3d341..246c934c 100755 --- a/gamefixes-steam/105000.py +++ b/gamefixes-steam/105000.py @@ -7,4 +7,4 @@ def main() -> None: - util.winedll_override('xaudio2_7', '') + util.winedll_override('xaudio2_7', util.DllOverride.DISABLED) diff --git a/gamefixes-steam/1062040.py b/gamefixes-steam/1062040.py index b3607c66..e5b70cdb 100755 --- a/gamefixes-steam/1062040.py +++ b/gamefixes-steam/1062040.py @@ -6,4 +6,4 @@ def main() -> None: """Dragon Star Varnir fix""" # Fixes the startup process. - util.winedll_override('xactengine3_7', 'n') + util.winedll_override('xactengine3_7', util.DllOverride.NATIVE) diff --git a/gamefixes-steam/1286880.py b/gamefixes-steam/1286880.py index 6238cb3f..6cb8cb34 100755 --- a/gamefixes-steam/1286880.py +++ b/gamefixes-steam/1286880.py @@ -5,4 +5,4 @@ def main() -> None: """Needs builtin vulkan-1""" - util.winedll_override('vulkan-1', 'b') + util.winedll_override('vulkan-1', util.DllOverride.BUILTIN) diff --git a/gamefixes-steam/1500540.py b/gamefixes-steam/1500540.py index 3ede4b23..a6f98a73 100644 --- a/gamefixes-steam/1500540.py +++ b/gamefixes-steam/1500540.py @@ -4,5 +4,5 @@ def main() -> None: - util.winedll_override('dinput', 'n,b') # DxWrapper component - util.winedll_override('winmm', 'n,b') # Music playback + util.winedll_override('dinput', util.DllOverride.NATIVE_BUILTIN) # DxWrapper component + util.winedll_override('winmm', util.DllOverride.NATIVE_BUILTIN) # Music playback diff --git a/gamefixes-steam/1664350.py b/gamefixes-steam/1664350.py index 8b466387..298544c4 100755 --- a/gamefixes-steam/1664350.py +++ b/gamefixes-steam/1664350.py @@ -5,4 +5,4 @@ def main() -> None: """Needs builtin vulkan-1""" - util.winedll_override('vulkan-1', 'b') + util.winedll_override('vulkan-1', util.DllOverride.BUILTIN) diff --git a/gamefixes-steam/1681970.py b/gamefixes-steam/1681970.py index f8c1724c..91c922eb 100755 --- a/gamefixes-steam/1681970.py +++ b/gamefixes-steam/1681970.py @@ -5,6 +5,6 @@ def main() -> None: util.protontricks('klite') - util.winedll_override('winegstreamer', '') + util.winedll_override('winegstreamer', util.DllOverride.DISABLED) # it uses quartz instead of mfplat on win7 util.protontricks('win7') diff --git a/gamefixes-steam/200490.py b/gamefixes-steam/200490.py index 22b69394..703fea6c 100755 --- a/gamefixes-steam/200490.py +++ b/gamefixes-steam/200490.py @@ -7,5 +7,5 @@ def main() -> None: - util.winedll_override('libvkd3d-1', 'n') + util.winedll_override('libvkd3d-1', util.DllOverride.NATIVE) util.protontricks('wmp11') diff --git a/gamefixes-steam/201480.py b/gamefixes-steam/201480.py index 0376bf8f..c11eda01 100755 --- a/gamefixes-steam/201480.py +++ b/gamefixes-steam/201480.py @@ -14,4 +14,4 @@ def main() -> None: util.protontricks('dsound') util.protontricks('dswave') util.protontricks('directplay') - util.winedll_override('streamci', 'n') + util.winedll_override('streamci', util.DllOverride.NATIVE) diff --git a/gamefixes-steam/206480.py b/gamefixes-steam/206480.py index 1539e29c..b63e41dd 100755 --- a/gamefixes-steam/206480.py +++ b/gamefixes-steam/206480.py @@ -7,4 +7,4 @@ def main() -> None: """Disable libglesv2""" # gpu acceleration on wined3d https://bugs.winehq.org/show_bug.cgi?id=44985 # Make the store work. - util.winedll_override('libglesv2', '') + util.winedll_override('libglesv2', util.DllOverride.DISABLED) diff --git a/gamefixes-steam/212500.py b/gamefixes-steam/212500.py index a226ee3c..fa1a2a5a 100755 --- a/gamefixes-steam/212500.py +++ b/gamefixes-steam/212500.py @@ -7,4 +7,4 @@ def main() -> None: """Disable libglesv2""" ## gpu acelleration on wined3d https://bugs.winehq.org/show_bug.cgi?id=44985 # Make the store work. - util.winedll_override('libglesv2', '') + util.winedll_override('libglesv2', util.DllOverride.DISABLED) diff --git a/gamefixes-steam/223750.py b/gamefixes-steam/223750.py index b9f894c5..ef351cab 100755 --- a/gamefixes-steam/223750.py +++ b/gamefixes-steam/223750.py @@ -8,4 +8,4 @@ def main() -> None: util.protontricks('d3dx11_43') util.protontricks('d3dcompiler_43') util.protontricks('d3dcompiler_47') - util.winedll_override('wbemprox', 'n') # doesn't seem to be strictly needed + util.winedll_override('wbemprox', util.DllOverride.NATIVE) # doesn't seem to be strictly needed diff --git a/gamefixes-steam/230820.py b/gamefixes-steam/230820.py index 4eb03d32..7d3364f0 100755 --- a/gamefixes-steam/230820.py +++ b/gamefixes-steam/230820.py @@ -7,4 +7,4 @@ def main() -> None: - util.winedll_override('xaudio2_7', '') + util.winedll_override('xaudio2_7', util.DllOverride.DISABLED) diff --git a/gamefixes-steam/23460.py b/gamefixes-steam/23460.py index c3310cd7..ca1fa182 100644 --- a/gamefixes-steam/23460.py +++ b/gamefixes-steam/23460.py @@ -9,7 +9,7 @@ def main() -> None: util.protontricks('dotnet35sp1') - util.winedll_override('libvkd3d-1', 'n') + util.winedll_override('libvkd3d-1', util.DllOverride.NATIVE) # Videos play and audio works but screen is black. # util.protontricks('quartz') diff --git a/gamefixes-steam/237890.py b/gamefixes-steam/237890.py index ccd42e24..60c7a4e8 100755 --- a/gamefixes-steam/237890.py +++ b/gamefixes-steam/237890.py @@ -5,4 +5,4 @@ def main() -> None: util.protontricks('wmp9') - util.winedll_override('winegstreamer', '') + util.winedll_override('winegstreamer', util.DllOverride.DISABLED) diff --git a/gamefixes-steam/243200.py b/gamefixes-steam/243200.py index c9d0940a..a2b8e5ae 100755 --- a/gamefixes-steam/243200.py +++ b/gamefixes-steam/243200.py @@ -7,4 +7,4 @@ def main() -> None: - util.winedll_override('xaudio2_7', '') + util.winedll_override('xaudio2_7', util.DllOverride.DISABLED) diff --git a/gamefixes-steam/244210.py b/gamefixes-steam/244210.py index 9d4034a0..e288857b 100755 --- a/gamefixes-steam/244210.py +++ b/gamefixes-steam/244210.py @@ -9,6 +9,6 @@ def main() -> None: # Fixes Content Manager (black windows) util.protontricks('d3dx11_43') util.protontricks('d3dcompiler_47') - util.winedll_override('dwrite', 'n,b') + util.winedll_override('dwrite', util.DllOverride.NATIVE_BUILTIN) util.protontricks('win10') util.set_environment('PULSE_LATENCY_MSEC', '60') diff --git a/gamefixes-steam/244850.py b/gamefixes-steam/244850.py index 768b1480..d016f925 100755 --- a/gamefixes-steam/244850.py +++ b/gamefixes-steam/244850.py @@ -11,6 +11,6 @@ def main() -> None: """ - util.set_xml_options(base_attibutte, game_opts, 'SpaceEngineers.exe.config', 'game') + util.set_xml_options(base_attibutte, game_opts, 'SpaceEngineers.exe.config') util.append_argument('-skipintro') diff --git a/gamefixes-steam/2475980.py b/gamefixes-steam/2475980.py index bb46523b..ebb0750d 100644 --- a/gamefixes-steam/2475980.py +++ b/gamefixes-steam/2475980.py @@ -21,9 +21,9 @@ def main_with_id(game_id: str) -> None: # Demo if game_id == '2505910': - util.set_ini_options(cfg_str, 'acsetup.cfg', 'utf-8', 'game')# + util.set_ini_options(cfg_str, 'acsetup.cfg') return # Full for i in range(1, 5): - util.set_ini_options(cfg_str, f'Gobliiins5-Part{i}/acsetup.cfg', 'utf-8', 'game') + util.set_ini_options(cfg_str, f'Gobliiins5-Part{i}/acsetup.cfg') diff --git a/gamefixes-steam/287310.py b/gamefixes-steam/287310.py index 03c3e995..fae3a08b 100755 --- a/gamefixes-steam/287310.py +++ b/gamefixes-steam/287310.py @@ -6,5 +6,5 @@ def main() -> None: """Sets the necessary dll overrides for the wrappers that are shipped with the game""" # Set overrides - util.winedll_override('ddraw', 'n') - util.winedll_override('dinput', 'n') + util.winedll_override('ddraw', util.DllOverride.NATIVE) + util.winedll_override('dinput', util.DllOverride.NATIVE) diff --git a/gamefixes-steam/292410.py b/gamefixes-steam/292410.py index e5d70ea2..4eec166f 100644 --- a/gamefixes-steam/292410.py +++ b/gamefixes-steam/292410.py @@ -4,7 +4,8 @@ def main() -> None: - util.protontricks('lavfilters') # fix videos - util.winedll_override( - 'd3d9', 'n,b' - ) # in case user uses the ThirteenAG widescreen fix + # fix videos + util.protontricks('lavfilters') + + # in case user uses the ThirteenAG widescreen fix + util.winedll_override('d3d9', util.DllOverride.NATIVE_BUILTIN) diff --git a/gamefixes-steam/33990.py b/gamefixes-steam/33990.py index 3bf9d5bd..a0f8d448 100755 --- a/gamefixes-steam/33990.py +++ b/gamefixes-steam/33990.py @@ -7,5 +7,5 @@ def main() -> None: - util.winedll_override('libvkd3d-1', 'n') + util.winedll_override('libvkd3d-1', util.DllOverride.NATIVE) util.protontricks('wmp11') diff --git a/gamefixes-steam/39500.py b/gamefixes-steam/39500.py index 75f95012..cb01cc9b 100755 --- a/gamefixes-steam/39500.py +++ b/gamefixes-steam/39500.py @@ -11,4 +11,4 @@ def main() -> None: FpS.Max=0 """ - util.set_ini_options(game_opts, 'Ini/ge3.ini', 'cp1251', 'game') + util.set_ini_options(game_opts, 'Ini/ge3.ini', 'cp1251') diff --git a/gamefixes-steam/559620.py b/gamefixes-steam/559620.py index 8add8978..f6eb8b72 100755 --- a/gamefixes-steam/559620.py +++ b/gamefixes-steam/559620.py @@ -5,5 +5,5 @@ def main() -> None: # Override ddraw (cutscenes+menu perf) and WinMM (Music) - util.winedll_override('ddraw', 'n,b') - util.winedll_override('winmm', 'n,b') + util.winedll_override('ddraw', util.DllOverride.NATIVE_BUILTIN) + util.winedll_override('winmm', util.DllOverride.NATIVE_BUILTIN) diff --git a/gamefixes-steam/593600.py b/gamefixes-steam/593600.py index 7ba0d38d..31111498 100755 --- a/gamefixes-steam/593600.py +++ b/gamefixes-steam/593600.py @@ -5,4 +5,4 @@ def main() -> None: """Overrides the mprapi.dll to native.""" - util.winedll_override('mprapi', 'n') + util.winedll_override('mprapi', util.DllOverride.NATIVE) diff --git a/gamefixes-steam/63710.py b/gamefixes-steam/63710.py index 466c884d..a0ed4129 100755 --- a/gamefixes-steam/63710.py +++ b/gamefixes-steam/63710.py @@ -7,4 +7,4 @@ def main() -> None: """From: https://www.protondb.com/app/63710""" util.protontricks('d3dcompiler_43') util.protontricks('d3dx9_43') - util.winedll_override('openal32', 'b') + util.winedll_override('openal32', util.DllOverride.BUILTIN) diff --git a/gamefixes-steam/65540.py b/gamefixes-steam/65540.py index b324c103..2462c2be 100755 --- a/gamefixes-steam/65540.py +++ b/gamefixes-steam/65540.py @@ -11,7 +11,7 @@ def main() -> None: # Fix background music / Gothic 2 startup util.protontricks('directmusic') - util.winedll_override('*dsound', 'b') # Override registry entry + util.winedll_override('*dsound', util.DllOverride.BUILTIN) # Override registry entry # Fix crackling audio util.set_environment('PULSE_LATENCY_MSEC', '90') @@ -21,12 +21,12 @@ def main() -> None: # Gothic 2: https://steamcommunity.com/sharedfiles/filedetails/?id=2787015529 # # This might also be necessary for the GOG release - util.winedll_override('ddraw', 'n,b') + util.winedll_override('ddraw', util.DllOverride.NATIVE_BUILTIN) # Fix extreme mouse stutter and allow additional use of 'GRawInput (mouse fix)' from workshop # Gothic 1: https://steamcommunity.com/sharedfiles/filedetails/?id=3054112346 # Gothic 2: https://steamcommunity.com/sharedfiles/filedetails/?id=3054078559 - util.winedll_override('dinput', 'n,b') + util.winedll_override('dinput', util.DllOverride.NATIVE_BUILTIN) def set_resolution() -> None: @@ -51,4 +51,4 @@ def set_resolution() -> None: """ ) - util.set_ini_options(game_opts, 'system/Gothic.ini', 'cp1251', 'game') + util.set_ini_options(game_opts, 'system/Gothic.ini', 'cp1251') diff --git a/gamefixes-steam/70400.py b/gamefixes-steam/70400.py index dba5f4a9..bc4fafa2 100755 --- a/gamefixes-steam/70400.py +++ b/gamefixes-steam/70400.py @@ -11,7 +11,7 @@ def main() -> None: util.protontricks('dmusic') util.protontricks('dsound') util.protontricks('dswave') - util.winedll_override('streamci', 'n') + util.winedll_override('streamci', util.DllOverride.NATIVE) util.protontricks('sound=alsa') """ Fix for audio stutter/desync diff --git a/gamefixes-steam/70420.py b/gamefixes-steam/70420.py index 3fe708ad..ae5ebaf9 100755 --- a/gamefixes-steam/70420.py +++ b/gamefixes-steam/70420.py @@ -11,7 +11,7 @@ def main() -> None: util.protontricks('dmusic') util.protontricks('dsound') util.protontricks('dswave') - util.winedll_override('streamci', 'n') + util.winedll_override('streamci', util.DllOverride.NATIVE) util.protontricks('sound=alsa') """ Fix for audio stutter/desync diff --git a/gamefixes-steam/950670.py b/gamefixes-steam/950670.py index aedb11ef..9ef4ca35 100644 --- a/gamefixes-steam/950670.py +++ b/gamefixes-steam/950670.py @@ -39,7 +39,7 @@ def modify_settings() -> None: r.DepthOfFieldQuality=0 r.LensFlareQuality=0 """ - util.set_ini_options(game_opts, path, 'utf-8', 'game') + util.set_ini_options(game_opts, path) def clear_logs() -> None: diff --git a/gamefixes-steam/968370.py b/gamefixes-steam/968370.py index cce242d3..3644fca0 100755 --- a/gamefixes-steam/968370.py +++ b/gamefixes-steam/968370.py @@ -6,5 +6,5 @@ def main() -> None: - util.winedll_override('d3d9', '') + util.winedll_override('d3d9', util.DllOverride.DISABLED) util.protontricks('segoe_script') diff --git a/gamefixes-umu/umu-silenthill3.py b/gamefixes-umu/umu-silenthill3.py index 9edce6cf..cefed283 100644 --- a/gamefixes-umu/umu-silenthill3.py +++ b/gamefixes-umu/umu-silenthill3.py @@ -7,4 +7,4 @@ def main() -> None: # Needs directmusic for some cutscenes util.protontricks('directmusic') - util.winedll_override("dsound", "builtin") + util.winedll_override('dsound', util.DllOverride.NATIVE_BUILTIN) diff --git a/gamefixes-umu/umu-starcitizen.py b/gamefixes-umu/umu-starcitizen.py index 00867399..7952c5c5 100644 --- a/gamefixes-umu/umu-starcitizen.py +++ b/gamefixes-umu/umu-starcitizen.py @@ -12,4 +12,4 @@ def main() -> None: util.protontricks('powershell') # RSI Launcher animation - util.winedll_override('libglesv2', 'b') + util.winedll_override('libglesv2', util.DllOverride.BUILTIN) diff --git a/gamefixes-zoomplatform/umu-240200.py b/gamefixes-zoomplatform/umu-240200.py index 738e9da3..e4576893 100644 --- a/gamefixes-zoomplatform/umu-240200.py +++ b/gamefixes-zoomplatform/umu-240200.py @@ -4,5 +4,5 @@ def main() -> None: - util.winedll_override('d3d8', 'n,b') + util.winedll_override('d3d8', util.DllOverride.NATIVE_BUILTIN) util.protontricks('vcrun2019') diff --git a/gamefixes-zoomplatform/umu-4bff76f4-566a-4714-b481-95d3343afe22.py b/gamefixes-zoomplatform/umu-4bff76f4-566a-4714-b481-95d3343afe22.py index 3f4e5344..ee716213 100644 --- a/gamefixes-zoomplatform/umu-4bff76f4-566a-4714-b481-95d3343afe22.py +++ b/gamefixes-zoomplatform/umu-4bff76f4-566a-4714-b481-95d3343afe22.py @@ -4,6 +4,6 @@ def main() -> None: - util.winedll_override('d3d8', 'n,b') - util.winedll_override('ddraw', 'b') - util.winedll_override('winmm', 'n,b') + util.winedll_override('d3d8', util.DllOverride.NATIVE_BUILTIN) + util.winedll_override('ddraw', util.DllOverride.BUILTIN) + util.winedll_override('winmm', util.DllOverride.NATIVE_BUILTIN) diff --git a/logger.py b/logger.py index 90c4c1da..94813235 100644 --- a/logger.py +++ b/logger.py @@ -3,32 +3,26 @@ import os import sys -from typing import Literal -from functools import lru_cache +from enum import Enum -# TypeAliases -LogLevelType = Literal['INFO', 'WARN', 'CRIT', 'DEBUG'] +# Enums +class LogLevel(Enum): + """Enum and mapping (level -> color) for log levels""" + RESET = '\u001b[0m' + INFO = '\u001b[34m' + WARN = '\u001b[33m' + CRIT = '\u001b[31m' + DEBUG = '\u001b[35m' class Log: """Log to stderr for steam dumps""" pfx = f'ProtonFixes[{os.getpid()}]' - @staticmethod - @lru_cache - def __get_color(level: LogLevelType) -> str: - return { - 'RESET': '\u001b[0m', - 'INFO': '\u001b[34m', - 'WARN': '\u001b[33m', - 'CRIT': '\u001b[31m', - 'DEBUG': '\u001b[35m', - }.get(level, '') - @classmethod - def __colorize(cls, msg: str, level: LogLevelType) -> str: - color = cls.__get_color(level) - reset = cls.__get_color('RESET') + def __colorize(cls, msg: str, level: LogLevel) -> str: + color = level.value + reset = LogLevel.RESET.value return f'{color}{msg}{reset}' @classmethod @@ -37,36 +31,36 @@ def __call__(cls, msg: str) -> None: cls.log(msg) @classmethod - def log(cls, msg: str = '', level: LogLevelType = 'INFO') -> None: + def log(cls, msg: str = '', level: LogLevel = LogLevel.INFO) -> None: """Prints the log message to stdout the same way as Proton""" # To terminal - print(cls.__colorize(f'{cls.pfx} {level}: {msg}', level), file=sys.stderr, flush=True) + print(cls.__colorize(f'{cls.pfx} {level.name}: {msg}', level), file=sys.stderr, flush=True) # To log file with open('/tmp/test', 'a', 1, encoding='utf-8') as testfile: - print(f'{cls.pfx} {level}: {msg}', file=testfile) + print(f'{cls.pfx} {level.name}: {msg}', file=testfile) @classmethod def info(cls, msg: str) -> None: """Wrapper for printing info messages""" - cls.log(msg, 'INFO') + cls.log(msg, LogLevel.INFO) @classmethod def warn(cls, msg: str) -> None: """Wrapper for printing warning messages""" - cls.log(msg, 'WARN') + cls.log(msg, LogLevel.WARN) @classmethod def crit(cls, msg: str) -> None: """Wrapper for printing critical messages""" - cls.log(msg, 'CRIT') + cls.log(msg, LogLevel.CRIT) @classmethod def debug(cls, msg: str) -> None: """Wrapper for printing debug messages""" if 'DEBUG' not in os.environ: return - cls.log(msg, 'DEBUG') + cls.log(msg, LogLevel.DEBUG) log = Log() diff --git a/util.py b/util.py index 00c28302..6e3eec0c 100644 --- a/util.py +++ b/util.py @@ -11,11 +11,13 @@ import subprocess import urllib.request import functools + +from enum import Enum from pathlib import Path from dataclasses import dataclass from datetime import datetime, timezone from socket import socket, AF_INET, SOCK_DGRAM -from typing import Literal, Any, Union, Optional +from typing import Any, Union, Optional from collections.abc import Mapping, Callable try: @@ -33,11 +35,69 @@ log.crit('Unable to hook into Proton main script environment') exit() + # TypeAliases StrPath = Union[str, Path] -BasePathType = Literal['user', 'game'] -OverrideTypes = Literal['n', 'b', 'n,b', 'b,n', ''] -DosDeviceTypes = Literal['hd', 'network', 'floppy', 'cdrom'] + + +# Enums +class BasePath(Enum): + """Enum for base paths + + Attributes: + USER: User's "My Documents" folder in the current prefix + GAME: Game's install folder + """ + USER = 'user' + GAME = 'game' + + +class DosDevice(Enum): + """Enum for dos device types (mounted at 'prefix/dosdevices/') + + Attributes: + NETWORK: A network device (UNC) + FLOPPY: A floppy drive + CD_ROM: A CD ROM drive + HD: A hard disk drive + """ + NETWORK = 'network' + FLOPPY = 'floppy' + CD_ROM = 'cdrom' + HD = 'hd' + + +class DllOverride(Enum): + """Enum for Wine dll override order + + Builtin means dlls that are provided by Wine. + Native means dlls that are provided by the user or application. + + https://gitlab.winehq.org/wine/wine/-/wikis/Wine-User%27s-Guide#dll-overrides + + DLLs usually get resolved in the following order: + + 1. The directory the program was started from. + 2. The current directory. + 3. The Windows system directory. + 4. The Windows directory. + 5. The PATH variable directories. + + Attributes: + DISABLED: Disable dll + NATIVE: Load native dll + BUILTIN: Load builtin dll + NATIVE_BUILTIN: Load native dll first, builtin second + BUILTIN_NATIVE: Load builtin dll first, native second + """ + DISABLED = '' + NATIVE = 'n' + BUILTIN = 'b' + NATIVE_BUILTIN = 'n,b' + BUILTIN_NATIVE = 'b,n' + + +# Helper classes @dataclass class ReplaceType: """Used for replacements""" @@ -45,6 +105,7 @@ class ReplaceType: from_value: str to_value: str + class ProtonVersion: """Parses the proton version and build timestamp""" @@ -59,6 +120,7 @@ def __init__(self, version_string: str) -> None: self.version_name: str = parts[1] +# Functions @functools.lru_cache def protondir() -> Path: """Returns the path to proton""" @@ -406,10 +468,10 @@ def get_game_install_path() -> Path: return install_path -def winedll_override(dll: str, dtype: OverrideTypes) -> None: +def winedll_override(dll: str, dtype: DllOverride) -> None: """Add WINE dll override""" - log.info(f'Overriding {dll}.dll = {dtype}') - setting = f'{dll}={dtype}' + log.info(f'Overriding {dll}.dll = {dtype.value}') + setting = f'{dll}={dtype.value}' protonmain.append_to_env_str( protonmain.g_session.env, 'WINEDLLOVERRIDES', setting, ';' ) @@ -511,12 +573,12 @@ def patch_libcuda() -> bool: def disable_nvapi() -> None: """Disable WINE nv* dlls""" log.info('Disabling NvAPI') - winedll_override('nvapi', '') - winedll_override('nvapi64', '') - winedll_override('nvcuda', '') - winedll_override('nvcuvid', '') - winedll_override('nvencodeapi', '') - winedll_override('nvencodeapi64', '') + winedll_override('nvapi', DllOverride.DISABLED) + winedll_override('nvapi64', DllOverride.DISABLED) + winedll_override('nvcuda', DllOverride.DISABLED) + winedll_override('nvcuvid', DllOverride.DISABLED) + winedll_override('nvencodeapi', DllOverride.DISABLED) + winedll_override('nvencodeapi64', DllOverride.DISABLED) def disable_esync() -> None: @@ -627,12 +689,12 @@ def _get_case_insensitive_name(path: Path) -> Path: return resolved -def _get_config_full_path(cfile: StrPath, base_path: BasePathType) -> Optional[Path]: +def _get_config_full_path(cfile: StrPath, base_path: BasePath) -> Optional[Path]: """Find game's config file""" # Start from 'user'/'game' directories or absolute path - if base_path == 'user': + if base_path == BasePath.USER: cfg_path = protonprefix() / 'drive_c/users/steamuser/My Documents' / cfile - elif base_path == 'game': + elif base_path == BasePath.GAME: cfg_path = get_game_install_path() / cfile else: cfg_path = Path(cfile) @@ -657,7 +719,7 @@ def create_backup_config(cfg_path: Path) -> bool: def set_ini_options( - ini_opts: str, cfile: StrPath, encoding: str, base_path: BasePathType = 'user' + ini_opts: str, cfile: StrPath, encoding: str = 'utf-8', base_path: BasePath = BasePath.GAME ) -> bool: """Edit game's INI config file""" cfg_path = _get_config_full_path(cfile, base_path) @@ -684,7 +746,7 @@ def set_ini_options( def set_xml_options( - base_attibutte: str, xml_line: str, cfile: StrPath, base_path: BasePathType = 'user' + base_attibutte: str, xml_line: str, cfile: StrPath, encoding: str = 'utf-8', base_path: BasePath = BasePath.GAME ) -> bool: """Edit game's XML config file""" xml_path = _get_config_full_path(cfile, base_path) @@ -697,7 +759,7 @@ def set_xml_options( # set options i = 0 - contents = xml_path.read_text(encoding='utf-8').splitlines() + contents = xml_path.read_text(encoding).splitlines() for line in contents: i += 1 if base_attibutte not in line: @@ -706,7 +768,7 @@ def set_xml_options( contents.insert(i, xml_line + '\n') data = str.join('\n', contents) - xml_path.write_text(data, encoding='utf-8') + xml_path.write_text(data, encoding) log.info('XML config patch applied') return True @@ -936,12 +998,12 @@ def set_game_drive(enabled: bool) -> None: protonmain.g_session.compat_config.discard("gamedrive") -def create_dos_device(letter: str = 'r', dev_type: DosDeviceTypes = 'cdrom') -> bool: +def create_dos_device(letter: str = 'r', dev_type: DosDevice = DosDevice.CD_ROM) -> bool: """Create a symlink to '/tmp' in the dosdevices folder of the prefix and register it Args: letter (str, optional): Letter that the device gets assigned to, must be len = 1 - dev_type (DosDeviceTypes, optional): The device's type which will be registered to wine + dev_type (DosDevice, optional): The device's type which will be registered to wine Returns: bool: True, if device was created @@ -956,7 +1018,7 @@ def create_dos_device(letter: str = 'r', dev_type: DosDeviceTypes = 'cdrom') -> dosdevice.symlink_to('/tmp', True) # designate device as CD-ROM, requires 64-bit access - regedit_add('HKLM\\Software\\Wine\\Drives', f'{letter}:', 'REG_SZ', dev_type, True) + regedit_add('HKLM\\Software\\Wine\\Drives', f'{letter}:', 'REG_SZ', dev_type.value, True) return True From 71eb0da8e21177c9a08491e4617875633b8f5a6a Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Wed, 18 Dec 2024 00:17:52 +0100 Subject: [PATCH 21/21] Fixed unit tests - Fixed relative imports - Removed import handling via ImportError exception - Fixed CI by running via module, instead of file - Added / improved handling in __init__.py for unit tests --- .github/workflows/ci.yml | 6 +++++- __init__.py | 40 +++++++++++++++++++++++++++++++--------- checks.py | 5 +---- config.py | 4 +++- config_base.py | 4 ++-- engine.py | 1 + fix.py | 15 +++++---------- protonfixes_test.py | 16 +++++++++------- util.py | 11 +++-------- 9 files changed, 60 insertions(+), 42 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff60907e..59bf272a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,6 +53,10 @@ jobs: python3 .github/scripts/check_gamefixes.py python3 .github/scripts/check_verbs.py + # We need a known parent package in the unit tests, so we need to change to the parent dir + # As "umu-protonfixes" is not a valid package name, we create a symbolic link to "protonfixes" - name: Test with unittest run: | - python3 protonfixes_test.py + cd .. + ln -s umu-protonfixes protonfixes + python3 -m protonfixes.protonfixes_test diff --git a/__init__.py b/__init__.py index 6605138c..aa329ea2 100644 --- a/__init__.py +++ b/__init__.py @@ -1,18 +1,40 @@ -"""Starts the protonfix module""" +"""Starts the protonfix module and runs fixes after pre-flight-checks""" import os import sys +import traceback -RUN_CONDITIONS = [ - 'STEAM_COMPAT_DATA_PATH' in os.environ, - 'PROTONFIXES_DISABLE' not in os.environ, - 'waitforexitandrun' in sys.argv[1], -] +from . import fix +from .logger import log -if all(RUN_CONDITIONS): - import traceback - from . import fix +def check_conditions() -> bool: + """Determine, if the actual game was executed and protonfixes isn't deactivated. + + Returns: + bool: True, if the fix should be executed. + """ + return len(sys.argv) >= 1 and \ + 'STEAM_COMPAT_DATA_PATH' in os.environ and \ + 'PROTONFIXES_DISABLE' not in os.environ and \ + 'waitforexitandrun' in sys.argv[1] + + +def check_iscriptevaluator() -> bool: + """Determine, if we were invoked while running "iscriptevaluator.exe". + + Returns: + bool: True, if we were invoked while running "iscriptevaluator.exe". + """ + return len(sys.argv) >= 2 and \ + 'iscriptevaluator.exe' in sys.argv[2] + + +if check_iscriptevaluator(): + log.debug('Skipping fix execution. We are running "iscriptevaluator.exe".') +elif not check_conditions(): + log.warn('Skipping fix execution. We are probably running an unit test.') +else: try: fix.main() diff --git a/checks.py b/checks.py index 064e0651..b560056d 100644 --- a/checks.py +++ b/checks.py @@ -1,9 +1,6 @@ """Run some tests and generate warnings for proton configuration issues""" -try: - from .logger import log -except ImportError: - from logger import log +from .logger import log def esync_file_limits() -> bool: diff --git a/config.py b/config.py index c83e195d..e6719468 100644 --- a/config.py +++ b/config.py @@ -1,9 +1,11 @@ """Load configuration settings for protonfixes""" -from config_base import ConfigBase from dataclasses import dataclass from pathlib import Path +from .config_base import ConfigBase + + class Config(ConfigBase): """Configuration for umu-protonfix""" diff --git a/config_base.py b/config_base.py index 2263d4a0..a66e6dc1 100644 --- a/config_base.py +++ b/config_base.py @@ -5,11 +5,11 @@ from configparser import ConfigParser from dataclasses import is_dataclass from pathlib import Path - from typing import Any from collections.abc import Callable -from logger import log, LogLevel +from .logger import log, LogLevel + class ConfigBase: """Base class for configuration objects. diff --git a/engine.py b/engine.py index ae0fbc1a..2719fe30 100644 --- a/engine.py +++ b/engine.py @@ -2,6 +2,7 @@ import os import sys + from .logger import log, LogLevel diff --git a/fix.py b/fix.py index f5f70c21..df4aaabc 100644 --- a/fix.py +++ b/fix.py @@ -4,20 +4,15 @@ import re import sys import csv + from functools import lru_cache from importlib import import_module from typing import Optional -try: - from .config import config - from .util import proton_version - from .checks import run_checks - from .logger import log -except ImportError: - from config import config - from util import proton_version - from checks import run_checks - from logger import log +from .config import config +from .util import proton_version +from .checks import run_checks +from .logger import log try: import __main__ as protonmain diff --git a/protonfixes_test.py b/protonfixes_test.py index e8d4b71a..f63f8d4b 100644 --- a/protonfixes_test.py +++ b/protonfixes_test.py @@ -1,11 +1,13 @@ -import unittest +import io import os import tempfile +import unittest +import urllib.request + from pathlib import Path from unittest.mock import patch, mock_open -import io -import urllib.request -import fix + +from . import fix class TestProtonfixes(unittest.TestCase): @@ -315,7 +317,7 @@ def testGetGameNameDBFileNotFound(self): with patch('builtins.open', mock_open()) as mocked_open: mocked_open.side_effect = FileNotFoundError - with patch('fix.log') as mocked_log: # Mock the logger separately + with patch('protonfixes.fix.log') as mocked_log: # Mock the logger separately func = fix.get_game_name.__wrapped__ # Do not reference the cache result = func() self.assertEqual(result, 'UNKNOWN') @@ -329,7 +331,7 @@ def testGetGameNameDbOS(self): with patch('builtins.open', mock_open()) as mocked_open: mocked_open.side_effect = OSError - with patch('fix.log') as mocked_log: # Mock the logger separately + with patch('protonfixes.fix.log') as mocked_log: # Mock the logger separately func = fix.get_game_name.__wrapped__ # Do not reference the cache result = func() self.assertEqual(result, 'UNKNOWN') @@ -357,7 +359,7 @@ def testGetGameNameDbUnicode(self): with patch('builtins.open', mock_open()) as mocked_open: mocked_open.side_effect = UnicodeDecodeError('utf-8', b'', 0, 1, '') - with patch('fix.log') as mocked_log: # Mock the logger separately + with patch('protonfixes.fix.log') as mocked_log: # Mock the logger separately func = fix.get_game_name.__wrapped__ # Do not reference the cache result = func() self.assertEqual(result, 'UNKNOWN') diff --git a/util.py b/util.py index 6e3eec0c..a6796ddf 100644 --- a/util.py +++ b/util.py @@ -20,14 +20,9 @@ from typing import Any, Union, Optional from collections.abc import Mapping, Callable -try: - from .logger import log - from .config import config - from .steamhelper import install_app -except ImportError: - from logger import log - from config import config - from steamhelper import install_app +from .logger import log +from .config import config +from .steamhelper import install_app try: import __main__ as protonmain