diff options
Diffstat (limited to 'libs/nak/src/game_finder/bottles.rs')
| -rw-r--r-- | libs/nak/src/game_finder/bottles.rs | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/libs/nak/src/game_finder/bottles.rs b/libs/nak/src/game_finder/bottles.rs new file mode 100644 index 0000000..7d974e8 --- /dev/null +++ b/libs/nak/src/game_finder/bottles.rs @@ -0,0 +1,147 @@ +//! Bottles prefix detection +//! +//! Detects games installed in Bottles prefixes by scanning registry files +//! for known game registry entries. + +use std::fs; +use std::path::{Path, PathBuf}; + +use super::known_games::KNOWN_GAMES; +use super::registry::{read_registry_value, wine_path_to_linux}; +use super::{Game, Launcher}; +use crate::logging::log_info; + +/// Possible Bottles data paths +const BOTTLES_PATHS: &[&str] = &[ + ".local/share/bottles/bottles", + ".var/app/com.usebottles.bottles/data/bottles/bottles", +]; + +/// Detect all games in Bottles prefixes +pub fn detect_bottles_games() -> Vec<Game> { + let mut games = Vec::new(); + let home = match std::env::var("HOME") { + Ok(h) => h, + Err(_) => return games, + }; + + for relative_path in BOTTLES_PATHS { + let bottles_path = PathBuf::from(&home).join(relative_path); + if !bottles_path.exists() { + continue; + } + + log_info(&format!("Found Bottles installation: {}", bottles_path.display())); + + // Scan each bottle + let Ok(entries) = fs::read_dir(&bottles_path) else { + continue; + }; + + for entry in entries.flatten() { + let bottle_path = entry.path(); + if !bottle_path.is_dir() { + continue; + } + + // Each bottle might have games + let bottle_games = scan_bottle(&bottle_path); + games.extend(bottle_games); + } + } + + log_info(&format!("Bottles: Found {} installed games", games.len())); + games +} + +/// Scan a single Bottles bottle for known games +fn scan_bottle(bottle_path: &Path) -> Vec<Game> { + let mut games = Vec::new(); + + // The prefix is directly in the bottle folder (not in pfx subfolder like Steam) + // Check for drive_c to confirm it's a valid Wine prefix + let drive_c = bottle_path.join("drive_c"); + if !drive_c.exists() { + return games; + } + + let bottle_name = bottle_path + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or("Unknown"); + + // Check for each known game by registry entry + for known_game in KNOWN_GAMES { + if let Some(install_path_wine) = + read_registry_value(bottle_path, known_game.registry_path, known_game.registry_value) + { + // Convert Wine path to Linux path + let install_path = match wine_path_to_linux(&install_path_wine) { + Some(p) => p, + None => { + // Try as a relative path within drive_c + if install_path_wine.starts_with("C:") || install_path_wine.starts_with("c:") { + let relative = install_path_wine[2..].replace('\\', "/"); + drive_c.join(relative.trim_start_matches('/')) + } else { + continue; + } + } + }; + + if !install_path.exists() { + continue; + } + + log_info(&format!( + "Found {} in Bottles bottle '{}'", + known_game.name, bottle_name + )); + + games.push(Game { + name: known_game.name.to_string(), + app_id: format!("bottles-{}", known_game.steam_app_id), + install_path, + prefix_path: Some(bottle_path.to_path_buf()), + launcher: Launcher::Bottles, + my_games_folder: known_game.my_games_folder.map(String::from), + appdata_local_folder: known_game.appdata_local_folder.map(String::from), + appdata_roaming_folder: known_game.appdata_roaming_folder.map(String::from), + registry_path: Some(known_game.registry_path.to_string()), + registry_value: Some(known_game.registry_value.to_string()), + }); + } + } + + games +} + +/// Find all Bottles prefixes (for manual prefix selection) +pub fn find_bottles_prefixes() -> Vec<PathBuf> { + let mut prefixes = Vec::new(); + let home = match std::env::var("HOME") { + Ok(h) => h, + Err(_) => return prefixes, + }; + + for relative_path in BOTTLES_PATHS { + let bottles_path = PathBuf::from(&home).join(relative_path); + if !bottles_path.exists() { + continue; + } + + let Ok(entries) = fs::read_dir(&bottles_path) else { + continue; + }; + + for entry in entries.flatten() { + let bottle_path = entry.path(); + // Verify it's a valid Wine prefix + if bottle_path.is_dir() && bottle_path.join("drive_c").exists() { + prefixes.push(bottle_path); + } + } + } + + prefixes +} |
