diff options
Diffstat (limited to 'libs/nak/src/game_finder/mod.rs')
| -rw-r--r-- | libs/nak/src/game_finder/mod.rs | 218 |
1 files changed, 0 insertions, 218 deletions
diff --git a/libs/nak/src/game_finder/mod.rs b/libs/nak/src/game_finder/mod.rs deleted file mode 100644 index 600751a..0000000 --- a/libs/nak/src/game_finder/mod.rs +++ /dev/null @@ -1,218 +0,0 @@ -//! Game detection module -//! -//! Provides unified game detection across multiple launchers: -//! - Steam (native, Flatpak, Snap) -//! - Heroic (GOG, Epic) -//! - Bottles - -// Allow unused items - this is a public API module -#![allow(dead_code)] -#![allow(unused_imports)] - -mod bottles; -mod heroic; -pub mod known_games; -mod registry; -mod steam; -mod vdf; - -use std::path::PathBuf; - -pub use bottles::detect_bottles_games; -pub use heroic::detect_heroic_games; -pub use known_games::{ - find_by_epic_id, find_by_gog_id, find_by_name, find_by_steam_id, find_by_title, KnownGame, - KNOWN_GAMES, -}; -pub use registry::{read_registry_value, wine_path_to_linux}; -pub use steam::{detect_steam_games, find_game_install_path, find_game_prefix_path, get_known_game}; - -// ============================================================================ -// Core Types -// ============================================================================ - -/// The launcher/store a game was installed from -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Launcher { - Steam { is_flatpak: bool, is_snap: bool }, - Heroic { store: HeroicStore }, - Bottles, -} - -impl Launcher { - pub fn display_name(&self) -> &'static str { - match self { - Launcher::Steam { is_flatpak: true, .. } => "Steam (Flatpak)", - Launcher::Steam { is_snap: true, .. } => "Steam (Snap)", - Launcher::Steam { .. } => "Steam", - Launcher::Heroic { store: HeroicStore::GOG } => "Heroic (GOG)", - Launcher::Heroic { store: HeroicStore::Epic } => "Heroic (Epic)", - Launcher::Bottles => "Bottles", - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum HeroicStore { - GOG, - Epic, -} - -/// A detected game installation -#[derive(Debug, Clone)] -pub struct Game { - pub name: String, - pub app_id: String, - pub install_path: PathBuf, - pub prefix_path: Option<PathBuf>, - pub launcher: Launcher, - pub my_games_folder: Option<String>, - pub appdata_local_folder: Option<String>, - pub appdata_roaming_folder: Option<String>, - pub registry_path: Option<String>, - pub registry_value: Option<String>, -} - -impl Game { - pub fn has_prefix(&self) -> bool { - self.prefix_path.is_some() - } - - pub fn get_prefix_user_path(&self) -> Option<PathBuf> { - let prefix = self.prefix_path.as_ref()?; - let users_dir = prefix.join("drive_c/users"); - - if let Ok(entries) = std::fs::read_dir(&users_dir) { - for entry in entries.flatten() { - let name = entry.file_name().to_string_lossy().to_string(); - if name != "Public" && name != "root" { - return Some(users_dir.join(name)); - } - } - } - - Some(users_dir.join("steamuser")) - } - - pub fn get_prefix_documents_path(&self) -> Option<PathBuf> { - self.get_prefix_user_path().map(|p| p.join("Documents")) - } - - pub fn get_prefix_my_games_path(&self) -> Option<PathBuf> { - let docs = self.get_prefix_documents_path()?; - let folder = self.my_games_folder.as_ref()?; - Some(docs.join("My Games").join(folder)) - } - - pub fn get_prefix_appdata_local_path(&self) -> Option<PathBuf> { - let user = self.get_prefix_user_path()?; - let folder = self.appdata_local_folder.as_ref()?; - Some(user.join("AppData/Local").join(folder)) - } - - pub fn get_prefix_appdata_roaming_path(&self) -> Option<PathBuf> { - let user = self.get_prefix_user_path()?; - let folder = self.appdata_roaming_folder.as_ref()?; - Some(user.join("AppData/Roaming").join(folder)) - } -} - -// ============================================================================ -// Scan Results -// ============================================================================ - -#[derive(Debug, Default)] -pub struct GameScanResult { - pub games: Vec<Game>, - pub steam_count: usize, - pub heroic_count: usize, - pub bottles_count: usize, -} - -impl GameScanResult { - pub fn games_with_prefixes(&self) -> impl Iterator<Item = &Game> { - self.games.iter().filter(|g| g.has_prefix()) - } - - pub fn games_by_launcher(&self, launcher_type: &str) -> Vec<&Game> { - self.games - .iter() - .filter(|g| { - matches!( - (&g.launcher, launcher_type), - (Launcher::Steam { .. }, "steam") - | (Launcher::Heroic { .. }, "heroic") - | (Launcher::Bottles, "bottles") - ) - }) - .collect() - } - - pub fn find_by_name(&self, name: &str) -> Option<&Game> { - let name_lower = name.to_lowercase(); - self.games - .iter() - .find(|g| g.name.to_lowercase() == name_lower) - } - - pub fn find_by_app_id(&self, app_id: &str) -> Option<&Game> { - self.games.iter().find(|g| g.app_id == app_id) - } -} - -// ============================================================================ -// Public API -// ============================================================================ - -/// Detect all installed games from all supported launchers. -/// -/// Games are detected in priority order: Steam -> Heroic (GOG -> Epic) -> Bottles. -/// When the same game is found from multiple launchers, the higher-priority -/// detection is kept and duplicates are skipped. This ensures registry entries -/// (which are shared across storefronts) prefer Steam paths, then GOG, then Epic. -pub fn detect_all_games() -> GameScanResult { - let mut result = GameScanResult::default(); - - // Detect in priority order: Steam first, then GOG (via Heroic), then Epic, then Bottles. - let steam_games = detect_steam_games(); - result.steam_count = steam_games.len(); - result.games.extend(steam_games); - - let heroic_games = detect_heroic_games(); - result.heroic_count = heroic_games.len(); - result.games.extend(heroic_games); - - let bottles_games = detect_bottles_games(); - result.bottles_count = bottles_games.len(); - result.games.extend(bottles_games); - - // Deduplicate: keep the first occurrence of each game (by registry_path), - // which respects the detection order (Steam > GOG > Epic > Bottles). - deduplicate_games(&mut result); - - result -} - -/// Remove duplicate game detections, keeping the first (highest-priority) entry. -/// Two games are considered duplicates if they have the same registry_path. -fn deduplicate_games(result: &mut GameScanResult) { - let mut seen_registry_paths = std::collections::HashSet::new(); - result.games.retain(|game| { - if let Some(ref reg_path) = game.registry_path { - seen_registry_paths.insert(reg_path.clone()) - } else { - // Games without registry paths are always kept (no risk of conflict) - true - } - }); -} - -/// Detect only Steam games -pub fn detect_steam_only() -> GameScanResult { - let steam_games = detect_steam_games(); - GameScanResult { - steam_count: steam_games.len(), - games: steam_games, - ..Default::default() - } -} |
