From 77cd2c8fbff973f6c36386d56fc911f3f86cff9b Mon Sep 17 00:00:00 2001 From: SulfurNitride Date: Sat, 28 Mar 2026 16:42:33 -0500 Subject: Search all Steam library folders for game compatdata Steam can place a game's compatdata in a different library folder than the game itself (e.g. game on SD card, compatdata on internal storage). Previously we only looked in the same steamapps directory as the appmanifest, causing games to have no prefix_path and their Documents/My Games folders to not get symlinked during prefix setup. Co-Authored-By: Claude Opus 4.6 (1M context) --- libs/nak/src/game_finder/steam.rs | 40 ++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) (limited to 'libs') diff --git a/libs/nak/src/game_finder/steam.rs b/libs/nak/src/game_finder/steam.rs index b88a0b0..d403cdf 100644 --- a/libs/nak/src/game_finder/steam.rs +++ b/libs/nak/src/game_finder/steam.rs @@ -33,14 +33,17 @@ pub fn detect_steam_games() -> Vec { for steam_info in find_steam_installations(&home) { let libraries = get_library_folders(&steam_info.path); - for library_path in libraries { - let steamapps = library_path.join("steamapps"); - if !steamapps.exists() { - continue; - } - + // Collect all steamapps paths so we can search across them for compatdata. + // Steam can place compatdata in a different library folder than the game. + let all_steamapps: Vec = libraries + .iter() + .map(|l| l.join("steamapps")) + .filter(|s| s.exists()) + .collect(); + + for steamapps in &all_steamapps { // Scan for appmanifest_*.acf files - let Ok(entries) = fs::read_dir(&steamapps) else { + let Ok(entries) = fs::read_dir(steamapps) else { continue; }; @@ -51,7 +54,9 @@ pub fn detect_steam_games() -> Vec { }; if name.starts_with("appmanifest_") && name.ends_with(".acf") { - if let Some(game) = parse_appmanifest(&path, &steamapps, &steam_info) { + if let Some(game) = + parse_appmanifest(&path, steamapps, &all_steamapps, &steam_info) + { games.push(game); } } @@ -143,6 +148,7 @@ fn get_library_folders(steam_path: &Path) -> Vec { fn parse_appmanifest( manifest_path: &Path, steamapps_path: &Path, + all_steamapps: &[PathBuf], steam_info: &SteamInstallation, ) -> Option { let content = fs::read_to_string(manifest_path).ok()?; @@ -159,17 +165,13 @@ fn parse_appmanifest( return None; } - // Build the prefix path - let prefix_path = steamapps_path - .join("compatdata") - .join(&manifest.app_id) - .join("pfx"); - - let prefix_path = if prefix_path.exists() { - Some(prefix_path) - } else { - None - }; + // Search ALL library folders for the compatdata — Steam can place it in a + // different library than the game itself (e.g. game on SD card, compatdata + // on internal storage or vice-versa). + let prefix_path = all_steamapps + .iter() + .map(|sa| sa.join("compatdata").join(&manifest.app_id).join("pfx")) + .find(|p| p.exists()); // Look up known game info let known_game = find_by_steam_id(&manifest.app_id); -- cgit v1.3.1