From 5c7e2bc9454492c392dbc2cf48443e07de68dc95 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Wed, 25 Oct 2023 19:25:12 -0400 Subject: [PATCH] Sprinkle os.path.expanduser all over Yes, it's ugly and inelegant. But we've got many users with ~ in the paths in their config files. This plops a bunch of expanduser calls in an effort to make games like that at least work. It's unlikely this covers everything, but hopefully it will handle the most important cases. --- lutris/game.py | 2 +- lutris/runners/dosbox.py | 7 +++++-- lutris/runners/easyrpg.py | 2 +- lutris/runners/linux.py | 6 ++++-- lutris/runners/runner.py | 24 +++++++++++++++--------- lutris/runners/wine.py | 4 +++- lutris/scanners/lutris.py | 12 ++++++++---- lutris/util/wine/prefix.py | 1 + 8 files changed, 38 insertions(+), 20 deletions(-) diff --git a/lutris/game.py b/lutris/game.py index 2b6269938..4176efe3a 100644 --- a/lutris/game.py +++ b/lutris/game.py @@ -306,7 +306,7 @@ class Game(GObject.Object): """Return the game's directory; if it is not known this will try to find it. This can still return an empty string if it can't do that.""" if self.directory: - return self.directory + return os.path.expanduser(self.directory) # expanduser just in case! if self.runner: return self.runner.resolve_game_path() return "" diff --git a/lutris/runners/dosbox.py b/lutris/runners/dosbox.py index 59c8cb147..8ea9e2cad 100644 --- a/lutris/runners/dosbox.py +++ b/lutris/runners/dosbox.py @@ -111,10 +111,13 @@ class dosbox(Runner): """Return a guaranteed absolute path""" if not path: return "" + path = os.path.expanduser(path) if os.path.isabs(path): return path - if self.game_data.get("directory"): - return os.path.join(self.game_data.get("directory"), path) + directory = self.game_data.get("directory") + if directory: + directory = os.path.expanduser(directory) + return os.path.join(directory, path) return "" @property diff --git a/lutris/runners/easyrpg.py b/lutris/runners/easyrpg.py index d8efbe4bb..60dd69425 100644 --- a/lutris/runners/easyrpg.py +++ b/lutris/runners/easyrpg.py @@ -422,7 +422,7 @@ class easyrpg(Runner): def game_path(self): game_path = self.game_data.get("directory") if game_path: - return game_path + return path.expanduser(game_path) # just in case # Default to the directory of the entry point entry_point = self.game_config.get(self.entry_point_option) diff --git a/lutris/runners/linux.py b/lutris/runners/linux.py index c047146ed..c190cef3a 100644 --- a/lutris/runners/linux.py +++ b/lutris/runners/linux.py @@ -80,6 +80,7 @@ class linux(Runner): exe = self.game_config.get("exe") if not exe: return None + exe = os.path.expanduser(exe) # just in case! if os.path.isabs(exe): return exe if self.game_path: @@ -130,10 +131,11 @@ class linux(Runner): else: command = [] - working_dir = launch_config.get("working_dir") or self.working_dir + working_dir = os.path.expanduser(launch_config.get("working_dir") or self.working_dir) if "exe" in launch_config: - command.append(self.get_relative_exe(launch_config["exe"], working_dir)) + config_exe = os.path.expanduser(launch_config["exe"] or '') + command.append(self.get_relative_exe(config_exe, working_dir)) elif len(command) == 0: raise GameConfigError(_("The runner could not find a command or exe to use for this configuration.")) diff --git a/lutris/runners/runner.py b/lutris/runners/runner.py index 52bb9e06f..b376dff5c 100644 --- a/lutris/runners/runner.py +++ b/lutris/runners/runner.py @@ -105,7 +105,7 @@ class Runner: # pylint: disable=too-many-public-methods """Return the directory where the game is installed.""" game_path = self.game_data.get("directory") if game_path: - return game_path + return os.path.expanduser(game_path) # expanduser just in case! if self.has_explicit_config: # Default to the directory where the entry point is located. @@ -297,14 +297,17 @@ class Runner: # pylint: disable=too-many-public-methods exe = launch_config.get("exe") config_working_dir = self.get_launch_config_working_dir(launch_config) - if exe and config_working_dir and not os.path.isabs(exe): - exe_from_config = self.resolve_config_path(exe, config_working_dir) - exe_from_game = self.resolve_config_path(exe) + if exe: + exe = os.path.expanduser(exe) # just in case! - if os.path.exists(exe_from_game) and not os.path.exists(exe_from_config): - relative = os.path.relpath(exe_from_game, start=config_working_dir) - if not relative.startswith("../"): - return relative + if config_working_dir and not os.path.isabs(exe): + exe_from_config = self.resolve_config_path(exe, config_working_dir) + exe_from_game = self.resolve_config_path(exe) + + if os.path.exists(exe_from_game) and not os.path.exists(exe_from_config): + relative = os.path.relpath(exe_from_game, start=config_working_dir) + if not relative.startswith("../"): + return relative return exe @@ -324,11 +327,14 @@ class Runner: # pylint: disable=too-many-public-methods def resolve_config_path(self, path, relative_to=None): """Interpret a path taken from the launch_config relative to - a working directory, using the game's working_dir if that is omitted. + a working directory, using the game's working_dir if that is omitted, + and expanding the '~' if we get one. This is provided as a method so the WINE runner can try to convert Windows-style paths to usable paths. """ + path = os.path.expanduser(path) + if not os.path.isabs(path): if not relative_to: relative_to = self.working_dir diff --git a/lutris/runners/wine.py b/lutris/runners/wine.py index 68a35b895..3b822430f 100644 --- a/lutris/runners/wine.py +++ b/lutris/runners/wine.py @@ -612,6 +612,7 @@ class wine(Runner): if not _prefix_path and self.game_config.get("exe"): # Find prefix from game if we have one _prefix_path = find_prefix(self.game_exe) + _prefix_path = os.path.expanduser(_prefix_path) # just in case! return _prefix_path @property @@ -622,6 +623,7 @@ class wine(Runner): if not exe: logger.error("The game doesn't have an executable") return None + exe = os.path.expanduser(exe) # just in case! if os.path.isabs(exe): return system.fix_path_case(exe) if not self.game_path: @@ -634,7 +636,7 @@ class wine(Runner): """Return the working directory to use when running the game.""" _working_dir = self._working_dir or self.game_config.get("working_dir") if _working_dir: - return _working_dir + return os.path.expanduser(_working_dir) if self.game_exe: game_dir = os.path.dirname(self.game_exe) if os.path.isdir(game_dir): diff --git a/lutris/scanners/lutris.py b/lutris/scanners/lutris.py index 0d6f107f2..ddb56546c 100644 --- a/lutris/scanners/lutris.py +++ b/lutris/scanners/lutris.py @@ -121,9 +121,13 @@ def get_path_from_config(game): path = game_config[key] if key == "files": path = path[0] - if not path.startswith("/"): - path = os.path.join(game.directory, path) - return path + + if path: + path = os.path.expanduser(path) + if not path.startswith("/"): + path = os.path.join(game.directory, path) + return path + logger.warning("No path found in %s", game.config) return "" @@ -210,4 +214,4 @@ def get_missing_game_ids(): def is_game_missing(game_id): cache = get_path_cache() path = cache.get(str(game_id)) - return path and not os.path.exists(path) + return path and not os.path.exists(os.path.expanduser(path)) diff --git a/lutris/util/wine/prefix.py b/lutris/util/wine/prefix.py index 5f03e9b25..3da2338a0 100644 --- a/lutris/util/wine/prefix.py +++ b/lutris/util/wine/prefix.py @@ -28,6 +28,7 @@ def find_prefix(path): if not dir_path: logger.info("No path given, unable to guess prefix location") return + dir_path = os.path.expanduser(dir_path) while dir_path != "/" and dir_path: dir_path = os.path.dirname(dir_path) if is_prefix(dir_path):