Use libraryfolders.vdf to find addtl steam games

* Add `read_library_folders` method to read and parse the
`libraryfolders.vdf` file for additional Steam game libraries
* Add `get_library_config` method to the steam runner file
to execute the `read_library_folders()` method from the steam
config file.
* After parsing the `config.vdf` file for any `BaseInstallFolder_X`
keys, it will now also look for any `path` keys in the
parsed `libraryfolders.vdf` dictionary. This happens before the code
cleans up duplicate entries in the `dirs` array.
This commit is contained in:
David Hollinger III 2021-08-06 23:00:35 -05:00 committed by Mathieu Comandon
parent 2b7ef5dc01
commit 6a61aa570f
2 changed files with 44 additions and 1 deletions

View file

@ -10,7 +10,7 @@ from lutris.runners.runner import Runner
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 @@ class steam(Runner):
"""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 @@ class steam(Runner):
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:

View file

@ -141,6 +141,30 @@ def read_config(steam_data_dir):
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()