Skip to content

Commit ab0fb57

Browse files
committed
Make library folder discovery case-insensitive
Steam will go out of its way to perform certain operations case-insensitively. For example, when a Steam library folder has been added, Steam will automatically discover it even if it is renamed to ALL CAPS. The configuration will not be updated to use the new name, however, so we have to perform this search ourselves as well. Adjust Steam library folder discovery to account for this. Refs #433
1 parent aa8f1b1 commit ab0fb57

File tree

3 files changed

+103
-0
lines changed

3 files changed

+103
-0
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

7+
## [Unreleased]
8+
### Fixed
9+
- Fix Steam library folder discovery by using case-insensitive directory name matching
10+
711
## [1.13.0] - 2025-08-11
812
### Added
913
- Improve compatibility by setting additional Proton related environment variables when applicable

src/protontricks/steam.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,6 +1016,54 @@ def parse_library_folders(data):
10161016
if is_library_folder_xdg_steam and is_flatpak_steam:
10171017
path = flatpak_steam_path
10181018

1019+
# Steam matches library folders case-insensitively.
1020+
candidates = [
1021+
candidate
1022+
for candidate in path.parent.iterdir()
1023+
if candidate.name.lower() == path.name.lower()
1024+
]
1025+
1026+
logger.debug(
1027+
"Following candidates found for the Steam library path %s: %s",
1028+
path, candidates
1029+
)
1030+
1031+
if not candidates:
1032+
logger.warning(
1033+
"Steam library folder %s in Steam configuration does not "
1034+
"exist",
1035+
path
1036+
)
1037+
continue
1038+
1039+
# In case of multiple matches, prioritize those that contain
1040+
# a 'steamapps' subdirectory
1041+
candidates.sort(
1042+
key=lambda path: any(
1043+
path.name.lower() == "steamapps"
1044+
for path in path.iterdir()
1045+
),
1046+
reverse=True
1047+
)
1048+
1049+
# In case multiple directories match, pick the first one,
1050+
# log a warning and hope for the best
1051+
if len(candidates) > 1:
1052+
logger.warning(
1053+
"Multiple Steam library folders with name %s were found. "
1054+
"Selecting %s.",
1055+
path, candidates[0]
1056+
)
1057+
1058+
if candidates[0].name != path.name:
1059+
logger.warning(
1060+
"Steam library folder %s in configuration differs from "
1061+
"found path %s. Was this directory renamed?",
1062+
path, candidates[0]
1063+
)
1064+
1065+
path = candidates[0]
1066+
10191067
logger.debug(
10201068
"Found Steam library folder %s. Is Flatpak path: %s, "
10211069
"Is XDG Steam path: %s.",

tests/test_steam.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,57 @@ def test_get_steam_lib_paths_adjust_flatpak_steam_path(
527527
assert len(library_paths) == 1
528528
assert str(library_paths[0]) == str(flatpak_steam_dir)
529529

530+
def test_get_steam_lib_paths_multiple_case_insensitive_matches(
531+
self, steam_dir, steam_library_factory, home_dir, caplog):
532+
"""
533+
Retrieve Steam library folders ensuring that when multiple
534+
case-insensitive matches are found for one configured path, the first
535+
path with 'steamapps' directory inside it is picked and a warning
536+
is printed
537+
"""
538+
library_dir = steam_library_factory("TestLibrary")
539+
shutil.copytree(library_dir, library_dir.parent / "TESTLIBRARY")
540+
shutil.copytree(library_dir, library_dir.parent / "testlibrary")
541+
542+
# Add 'steamapps' subdir to 'TESTLIBRARY' to ensure it's prioritized
543+
expected_path = library_dir.parent / "TESTLIBRARY"
544+
(expected_path / "steamapps").mkdir()
545+
546+
lib_paths = get_steam_lib_paths(steam_dir)
547+
548+
assert expected_path in lib_paths
549+
assert library_dir not in lib_paths
550+
551+
assert any(
552+
record for record in caplog.records
553+
if record.levelname == "WARNING"
554+
and f"Multiple Steam library folders with name {library_dir}"
555+
in record.getMessage()
556+
)
557+
558+
def test_get_steam_lib_paths_renamed_library(
559+
self, steam_dir, steam_library_factory, home_dir, caplog):
560+
"""
561+
Retrieve Steam library folders ensuring that a renamed library folder
562+
can be found if it still matches the old name case-insensitively
563+
564+
Regression test for #433
565+
"""
566+
library_dir = steam_library_factory("TestLibrary")
567+
library_dir.rename(library_dir.parent / "testlibrary")
568+
569+
lib_paths = get_steam_lib_paths(steam_dir)
570+
571+
assert library_dir not in lib_paths
572+
assert library_dir.parent / "testlibrary" in lib_paths
573+
574+
assert any(
575+
record for record in caplog.records
576+
if record.levelname == "WARNING"
577+
and f"Steam library folder {library_dir} in configuration"
578+
in record.getMessage()
579+
)
580+
530581

531582
class TestFindAppidProtonPrefix:
532583
def test_find_appid_proton_prefix_steamapps_case(

0 commit comments

Comments
 (0)