From 19c3dcd5c4f6c97d4c069d0f202b1da75697d740 Mon Sep 17 00:00:00 2001 From: Eamonn Rea Date: Wed, 21 Feb 2024 18:52:02 +0000 Subject: [PATCH 1/6] util: Check if Steam configuration files exist before marking as available --- pupgui2/constants.py | 2 +- pupgui2/steamutil.py | 20 +++++++++++++++++++- pupgui2/util.py | 23 +++++++++++++++++++++-- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/pupgui2/constants.py b/pupgui2/constants.py index 97d77cdc..5580679e 100644 --- a/pupgui2/constants.py +++ b/pupgui2/constants.py @@ -26,7 +26,7 @@ # support different Steam root directories # valid install dir should have config.vdf and libraryfolders.vdf, to ensure it is not an unused folder with correct directory structure _POSSIBLE_STEAM_ROOTS = ['~/.local/share/Steam', '~/.steam/root', '~/.steam/steam', '~/.steam/debian-installation'] -_STEAM_ROOT = _POSSIBLE_STEAM_ROOTS[0] +_STEAM_ROOT = _POSSIBLE_STEAM_ROOTS[0] # Default to '~/.local/share/Steam', but this is not guaranteed to exist for steam_root in _POSSIBLE_STEAM_ROOTS: ct_dir = os.path.join(os.path.expanduser(steam_root), 'config') config_vdf = os.path.join(ct_dir, 'config.vdf') diff --git a/pupgui2/steamutil.py b/pupgui2/steamutil.py index 5c33cd43..5ee46c11 100644 --- a/pupgui2/steamutil.py +++ b/pupgui2/steamutil.py @@ -791,4 +791,22 @@ def determine_most_recent_steam_user(steam_users: List[SteamUser]) -> SteamUser: return steam_users[0] print('Warning: No Steam users found. Returning None') - return None \ No newline at end of file + return None + + +def is_valid_steam_install(steam_path) -> bool: + + """ + Return whether required Steam data files actually exist to determine if 'steam_path' is a valid Steam installation. + Return Type: bool + """ + + # Identical to the check we do in constants.py to determine the Steam root + ct_dir = os.path.join(os.path.expanduser(steam_path), 'config') + + config_vdf = os.path.join(ct_dir, 'config.vdf') + libraryfolders_vdf = os.path.join(ct_dir, 'libraryfolders.vdf') + + is_valid_steam_install = os.path.exists(config_vdf) and os.path.exists(libraryfolders_vdf) + + return is_valid_steam_install diff --git a/pupgui2/util.py b/pupgui2/util.py index 1b39bb75..ff5f6a67 100644 --- a/pupgui2/util.py +++ b/pupgui2/util.py @@ -23,7 +23,7 @@ from pupgui2.constants import AWACY_GAME_LIST_URL, LOCAL_AWACY_GAME_LIST from pupgui2.constants import GITHUB_API, GITLAB_API, GITLAB_API_RATELIMIT_TEXT from pupgui2.datastructures import BasicCompatTool, CTType, Launcher, SteamApp, LutrisGame, HeroicGame -from pupgui2.steamutil import remove_steamtinkerlaunch +from pupgui2.steamutil import remove_steamtinkerlaunch, is_valid_steam_install def create_msgbox( @@ -196,6 +196,25 @@ def create_compatibilitytools_folder() -> None: print(f'Error trying to create compatibility tools folder {str(install_dir)}: {str(e)}') +def is_valid_launcher_installation(loc) -> bool: + + """ + Check whether a launcher installation is actually valid based on per-launcher criteria + Return Type: bool + """ + + install_dir = os.path.expanduser(loc['install_dir']) + + # Right now we only check to make sure regular Steam (not Flatpak or Snap) has config.vdf and libraryfolders.vdf + # because Steam can leave behind its directory structure when uninstalled, but not these files. + # + # In future we could expand this to other Steam flavours and other launchers. + if loc['display_name'] == 'Steam': # This seems to get called many times, why? + return is_valid_steam_install(os.path.realpath(os.path.join(install_dir, '..'))) + + return os.path.exists(install_dir) # Default to path check for all other launchers + + def available_install_directories() -> List[str]: """ List available install directories @@ -204,7 +223,7 @@ def available_install_directories() -> List[str]: available_dirs = [] for loc in POSSIBLE_INSTALL_LOCATIONS: install_dir = os.path.expanduser(loc['install_dir']) - if os.path.exists(install_dir): + if is_valid_launcher_installation(loc): available_dirs.append(install_dir) install_dir = config_custom_install_location().get('install_dir') if install_dir and os.path.exists(install_dir): From d0e05ce40335b635127ba65560e46cdd31fb934f Mon Sep 17 00:00:00 2001 From: Eamonn Rea Date: Sat, 24 Feb 2024 19:41:37 +0000 Subject: [PATCH 2/6] constants: Move valid Steam install check to is_valid_launcher_installation --- pupgui2/constants.py | 33 +++++++++++++++++++++------------ pupgui2/util.py | 6 ++++-- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/pupgui2/constants.py b/pupgui2/constants.py index 5580679e..e19c495f 100644 --- a/pupgui2/constants.py +++ b/pupgui2/constants.py @@ -23,20 +23,29 @@ TEMP_DIR = os.path.join(os.getenv('XDG_CACHE_HOME'), 'tmp', 'pupgui2.a70200/') if os.path.exists(os.getenv('XDG_CACHE_HOME', '')) else '/tmp/pupgui2.a70200/' HOME_DIR = os.path.expanduser('~') -# support different Steam root directories -# valid install dir should have config.vdf and libraryfolders.vdf, to ensure it is not an unused folder with correct directory structure -_POSSIBLE_STEAM_ROOTS = ['~/.local/share/Steam', '~/.steam/root', '~/.steam/steam', '~/.steam/debian-installation'] -_STEAM_ROOT = _POSSIBLE_STEAM_ROOTS[0] # Default to '~/.local/share/Steam', but this is not guaranteed to exist -for steam_root in _POSSIBLE_STEAM_ROOTS: - ct_dir = os.path.join(os.path.expanduser(steam_root), 'config') - config_vdf = os.path.join(ct_dir, 'config.vdf') - libraryfolders_vdf = os.path.join(ct_dir, 'libraryfolders.vdf') - if os.path.exists(config_vdf) and os.path.exists(libraryfolders_vdf): - _STEAM_ROOT = steam_root - break +# support different Steam root directories, building paths relative to HOME_DIR (i.e. /home/gaben/.local/share/Steam) +_POSSIBLE_STEAM_ROOTS = [ + os.path.join(HOME_DIR, _STEAM_ROOT) for _STEAM_ROOT in ['.local/share/Steam', '.steam/root', '.steam/steam', '.steam/debian-installation'] +] +# Steam can be installled in any of the locations at '_POSSIBLE_STEAM_ROOTS' - usually only one, and the others (if they exist) are typically symlinks, +# i.e. '~/.steam/root' is usually a symlink to '~/.local/share/Steam' +# Use os.path.realpath to expand all _STEAM_ROOT paths and only add unique _STEAM_ROOT paths +# These paths may still not be valid installations however, as they could be leftother paths from an old Steam installation without the data files we need ('config.vdf' and 'libraryfolders.vdf') +# We catch this later on in util#is_valid_launcher_installation though POSSIBLE_INSTALL_LOCATIONS = [ - {'install_dir': f'{_STEAM_ROOT}/compatibilitytools.d/', 'display_name': 'Steam', 'launcher': 'steam', 'type': 'native', 'icon': 'steam', 'vdf_dir': f'{_STEAM_ROOT}/config'}, + { + 'install_dir': f'{os.path.realpath(_STEAM_ROOT)}/compatibilitytools.d/', + 'display_name': 'Steam', + 'launcher': 'steam', + 'type': 'native', + 'icon': 'steam', + 'vdf_dir': f'{os.path.realpath(_STEAM_ROOT)}/config' + } for _STEAM_ROOT in _POSSIBLE_STEAM_ROOTS if os.path.exists(os.path.realpath(_STEAM_ROOT)) +] + +# Possible install locations for all other launchers, ensuring Steam paths are at the top of the list +POSSIBLE_INSTALL_LOCATIONS += [ {'install_dir': '~/.var/app/com.valvesoftware.Steam/data/Steam/compatibilitytools.d/', 'display_name': 'Steam Flatpak', 'launcher': 'steam', 'type': 'flatpak', 'icon': 'steam', 'vdf_dir': '~/.var/app/com.valvesoftware.Steam/.local/share/Steam/config'}, {'install_dir': '~/snap/steam/common/.steam/root/compatibilitytools.d/', 'display_name': 'Steam Snap', 'launcher': 'steam', 'type': 'snap', 'icon': 'steam', 'vdf_dir': '~/snap/steam/common/.steam/root/config'}, {'install_dir': '~/.local/share/lutris/runners/wine/', 'display_name': 'Lutris', 'launcher': 'lutris', 'type': 'native', 'icon': 'lutris', 'config_dir': '~/.config/lutris'}, diff --git a/pupgui2/util.py b/pupgui2/util.py index ff5f6a67..17cbbf07 100644 --- a/pupgui2/util.py +++ b/pupgui2/util.py @@ -223,10 +223,12 @@ def available_install_directories() -> List[str]: available_dirs = [] for loc in POSSIBLE_INSTALL_LOCATIONS: install_dir = os.path.expanduser(loc['install_dir']) - if is_valid_launcher_installation(loc): + # POSSIBLE_INSTALL_LOCATIONS may contain duplicate paths, so only add unique paths to available_dirs + # This avoids adding symlinked Steam paths + if is_valid_launcher_installation(loc) and not install_dir in available_dirs: available_dirs.append(install_dir) install_dir = config_custom_install_location().get('install_dir') - if install_dir and os.path.exists(install_dir): + if install_dir and os.path.exists(install_dir) and not install_dir in available_dirs: available_dirs.append(install_dir) return available_dirs From 6423f52e48a982d79eefbb2552e86141435c4c6e Mon Sep 17 00:00:00 2001 From: Eamonn Rea Date: Sat, 24 Feb 2024 19:53:31 +0000 Subject: [PATCH 3/6] constants: Use os.path.join for building Steam install location paths --- pupgui2/constants.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pupgui2/constants.py b/pupgui2/constants.py index e19c495f..4481d21f 100644 --- a/pupgui2/constants.py +++ b/pupgui2/constants.py @@ -24,23 +24,26 @@ HOME_DIR = os.path.expanduser('~') # support different Steam root directories, building paths relative to HOME_DIR (i.e. /home/gaben/.local/share/Steam) +# Use os.path.realpath to expand all _STEAM_ROOT paths _POSSIBLE_STEAM_ROOTS = [ - os.path.join(HOME_DIR, _STEAM_ROOT) for _STEAM_ROOT in ['.local/share/Steam', '.steam/root', '.steam/steam', '.steam/debian-installation'] + os.path.realpath(os.path.join(HOME_DIR, _STEAM_ROOT)) for _STEAM_ROOT in ['.local/share/Steam', '.steam/root', '.steam/steam', '.steam/debian-installation'] ] +# Remove duplicate paths while preserving order, as os.path.realpath may expand some symlinks to the real Steam root +_POSSIBLE_STEAM_ROOTS = list(dict.fromkeys(_POSSIBLE_STEAM_ROOTS)) + # Steam can be installled in any of the locations at '_POSSIBLE_STEAM_ROOTS' - usually only one, and the others (if they exist) are typically symlinks, # i.e. '~/.steam/root' is usually a symlink to '~/.local/share/Steam' -# Use os.path.realpath to expand all _STEAM_ROOT paths and only add unique _STEAM_ROOT paths # These paths may still not be valid installations however, as they could be leftother paths from an old Steam installation without the data files we need ('config.vdf' and 'libraryfolders.vdf') # We catch this later on in util#is_valid_launcher_installation though POSSIBLE_INSTALL_LOCATIONS = [ { - 'install_dir': f'{os.path.realpath(_STEAM_ROOT)}/compatibilitytools.d/', + 'install_dir': f'{os.path.join(_STEAM_ROOT, "compatibility_tools.d")}', 'display_name': 'Steam', 'launcher': 'steam', 'type': 'native', 'icon': 'steam', - 'vdf_dir': f'{os.path.realpath(_STEAM_ROOT)}/config' + 'vdf_dir': f'{os.path.join(_STEAM_ROOT, "config")}' } for _STEAM_ROOT in _POSSIBLE_STEAM_ROOTS if os.path.exists(os.path.realpath(_STEAM_ROOT)) ] From af05168db3ec5b9bb0de8331abf940cf4c36f999 Mon Sep 17 00:00:00 2001 From: Eamonn Rea Date: Sat, 24 Feb 2024 20:46:05 +0000 Subject: [PATCH 4/6] constants: Don't use os.path.join when building Steam POSSIBLE_INSTALL_LOCATIONS Fixes a strange recusion depth crash. --- pupgui2/constants.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pupgui2/constants.py b/pupgui2/constants.py index 4481d21f..a4b2fbee 100644 --- a/pupgui2/constants.py +++ b/pupgui2/constants.py @@ -38,13 +38,13 @@ # We catch this later on in util#is_valid_launcher_installation though POSSIBLE_INSTALL_LOCATIONS = [ { - 'install_dir': f'{os.path.join(_STEAM_ROOT, "compatibility_tools.d")}', + 'install_dir': f'{_STEAM_ROOT}/compatibilitytools.d/', 'display_name': 'Steam', 'launcher': 'steam', 'type': 'native', 'icon': 'steam', - 'vdf_dir': f'{os.path.join(_STEAM_ROOT, "config")}' - } for _STEAM_ROOT in _POSSIBLE_STEAM_ROOTS if os.path.exists(os.path.realpath(_STEAM_ROOT)) + 'vdf_dir': f'{_STEAM_ROOT}/config' + } for _STEAM_ROOT in _POSSIBLE_STEAM_ROOTS if os.path.exists(_STEAM_ROOT) ] # Possible install locations for all other launchers, ensuring Steam paths are at the top of the list From ed709b9967406e417fc6f5f6129b764d2bfd72d2 Mon Sep 17 00:00:00 2001 From: Eamonn Rea Date: Fri, 1 Mar 2024 16:53:29 +0000 Subject: [PATCH 5/6] comment cleanup --- pupgui2/steamutil.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pupgui2/steamutil.py b/pupgui2/steamutil.py index 5ee46c11..8914f561 100644 --- a/pupgui2/steamutil.py +++ b/pupgui2/steamutil.py @@ -801,7 +801,6 @@ def is_valid_steam_install(steam_path) -> bool: Return Type: bool """ - # Identical to the check we do in constants.py to determine the Steam root ct_dir = os.path.join(os.path.expanduser(steam_path), 'config') config_vdf = os.path.join(ct_dir, 'config.vdf') From 64ec6d097c14eab14be48bcacc25d9a7dbe68b4c Mon Sep 17 00:00:00 2001 From: Eamonn Rea Date: Fri, 8 Mar 2024 17:52:21 +0000 Subject: [PATCH 6/6] comment cleanup 2 electric boogaloo --- pupgui2/util.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pupgui2/util.py b/pupgui2/util.py index 17cbbf07..e91991fa 100644 --- a/pupgui2/util.py +++ b/pupgui2/util.py @@ -223,8 +223,7 @@ def available_install_directories() -> List[str]: available_dirs = [] for loc in POSSIBLE_INSTALL_LOCATIONS: install_dir = os.path.expanduser(loc['install_dir']) - # POSSIBLE_INSTALL_LOCATIONS may contain duplicate paths, so only add unique paths to available_dirs - # This avoids adding symlinked Steam paths + # only add unique paths to available_dirs if is_valid_launcher_installation(loc) and not install_dir in available_dirs: available_dirs.append(install_dir) install_dir = config_custom_install_location().get('install_dir')