Repository: lutris/lutris

Issue:
Beta 0.5.9 - Steam doesn't search non-default library folders
It looks like Steam stores the non-default library paths in a file called `libraryfolders.vdf` in the Steam configuration directory. It doesn't appear as though the `get_steamapps_dir()` function in `lutris/runners/steam.py` looks for this file.

Example of the file:

```bash
"libraryfolders"
{
	"contentstatsid"		"74416093390690613"
	"1"
	{
		"path"		"/home/username/extras/SteamLibrary"
		"label"		""
		"contentid"		"2862334064878865260"
		"totalsize"		"491182006272"
		"apps"
		{ <redacted> }
	}
}
```

Also, looking at my current installation of steam, I can't seem to find the `BaseInstallFolder_<int>` directives in `config.vdf`. Looks like the client is changing that soon, if not already.


Discussion:
After some additional digging, it appears as though using `BaseInstallFolder_<int>` in `config.vdf` is still valid, the client itself will put the details into the `libraryfolders.vdf` file by default, regardless if the user manually adds the entry to `config.vdf`.

Resolving patch:
diff --git a/lutris/runners/steam.py b/lutris/runners/steam.py
--- a/lutris/runners/steam.py
+++ b/lutris/runners/steam.py
@@ -10,7 +10,7 @@
 from lutris.util import linux, system
 from lutris.util.log import logger
 from lutris.util.steam.appmanifest import get_appmanifest_from_appid, get_path_from_appmanifest
-from lutris.util.steam.config import STEAM_DATA_DIRS, get_default_acf, get_steam_dir, read_config
+from lutris.util.steam.config import STEAM_DATA_DIRS, get_default_acf, get_steam_dir, read_config, read_library_folders
 from lutris.util.steam.vdf import to_vdf
 from lutris.util.strings import split_arguments
 
@@ -144,6 +144,10 @@ def get_steam_config(self):
         """Return the "Steam" part of Steam's config.vdf as a dict."""
         return read_config(self.steam_data_dir)
 
+    def get_library_config(self):
+        """Return the "libraryfolders" part of Steam's libraryfolders.vdf as a dict """
+        return read_library_folders(self.steam_data_dir)
+
     @property
     def game_path(self):
         if not self.appid:
@@ -235,6 +239,21 @@ def get_steamapps_dirs(self):
                     dirs.append(path)
                 i += 1
 
+        # New Custom dirs
+        if library_config := self.get_library_config():
+            paths = []
+            for entry in library_config.values():
+                if "mounted" in entry:
+                    if entry.get("path") and entry.get("mounted") == "1":
+                        path = system.fix_path_case(entry.get("path") + "/steamapps")
+                        paths.append(path)
+                else:
+                    path = system.fix_path_case(entry.get("path") + "/steamapps")
+                    paths.append(path)
+            for path in paths:
+                if path and os.path.isdir(path):
+                    dirs.append(path)
+
         # Deduplicate directories with the same Device.Inode
         unique_dirs = {}
         for _dir in dirs:
diff --git a/lutris/util/steam/config.py b/lutris/util/steam/config.py
--- a/lutris/util/steam/config.py
+++ b/lutris/util/steam/config.py
@@ -141,6 +141,30 @@ def get_entry_case_insensitive(config_dict, path):
         logger.error("Steam config %s is empty: %s", config_filename, ex)
 
 
+def read_library_folders(steam_data_dir):
+    """Read the Steam Library Folders config and return it as an object"""
+    def get_entry_case_insensitive(library_dict, path):
+        for key, value in library_dict.items():
+            if key.lower() == path[0].lower():
+                if len(path) <= 1:
+                    return value
+                return get_entry_case_insensitive(library_dict[key], path[1:])
+            raise KeyError(path[0])
+    if not steam_data_dir:
+        return None
+    library_filename = os.path.join(steam_data_dir, "config/libraryfolders.vdf")
+    if not system.path_exists(library_filename):
+        return None
+    with open(library_filename, "r") as steam_library_file:
+        library = vdf_parse(steam_library_file, {})
+        # The contentstatsid key is unused and causes problems when looking for library paths.
+        library["libraryfolders"].pop("contentstatsid")
+    try:
+        return get_entry_case_insensitive(library, ["libraryfolders"])
+    except KeyError as ex:
+        logger.error("Steam libraryfolders %s is empty: %s", library_filename, ex)
+
+
 def get_steamapps_paths():
     from lutris.runners import steam  # pylint: disable=import-outside-toplevel
     return steam.steam().get_steamapps_dirs()


Test patch: