game_manager_lib/utils/
launcher.rs1use crate::models::{Game, Platform};
2use std::env;
3use std::path::PathBuf;
4use tracing::warn;
5
6#[derive(Debug, Clone)]
7pub enum LaunchResolution {
8 Protocol(String), Executable(String), Launcher(String), Store(String), Unavailable,
13}
14
15pub struct PlatformFallback {
16 pub launcher_candidates: &'static [&'static str],
17 pub store_url: &'static str,
18}
19
20pub fn platform_fallback(platform: &Platform) -> PlatformFallback {
21 match platform {
22 Platform::Amazon => PlatformFallback {
23 launcher_candidates: &[r"%LOCALAPPDATA%\Amazon Games\App\Amazon Games.exe"],
24 store_url: "",
25 },
26 Platform::BattleNet => PlatformFallback {
27 launcher_candidates: &[
28 r"C:\Program Files (x86)\Battle.net\Battle.net.exe",
29 r"C:\Program Files (x86)\Battle.net\Battle.net Launcher.exe",
30 ],
31 store_url: "https://battle.net/",
32 },
33 Platform::EA => PlatformFallback {
34 launcher_candidates: &[
35 r"C:\Program Files\Electronic Arts\EA Desktop\EA Desktop\EADesktop.exe",
36 r"C:\Program Files\Electronic Arts\EA Desktop\EA Desktop\EALauncher.exe",
37 ],
38 store_url: "https://www.ea.com/",
39 },
40 Platform::Epic => PlatformFallback {
41 launcher_candidates: &[
42 r"C:\Program Files (x86)\Epic Games\Launcher\Portal\Binaries\Win32\EpicGamesLauncher.exe",
43 r"C:\Program Files (x86)\Epic Games\Launcher\Portal\Binaries\Win64\EpicGamesLauncher.exe",
44 ],
45 store_url: "https://store.epicgames.com/",
46 },
47 Platform::GOG => PlatformFallback {
48 launcher_candidates: &[r"C:\Program Files (x86)\GOG Galaxy\GalaxyClient.exe"],
49 store_url: "https://www.gog.com/",
50 },
51 Platform::Heroic => PlatformFallback {
52 launcher_candidates: &[
53 r"C:\Program Files\Heroic\Heroic.exe",
54 r"C:\Users\%USERNAME%\AppData\Local\Programs\heroic\Heroic.exe",
55 ],
56 store_url: "",
57 },
58 Platform::Indiegala => PlatformFallback {
59 launcher_candidates: &[r"C:\Program Files (x86)\IGClient\IGClient.exe"],
60 store_url: "https://www.indiegala.com/store",
61 },
62 Platform::Itch => PlatformFallback {
63 launcher_candidates: &[
64 r"C:\Users\%USERNAME%\AppData\Local\itch\itch.exe",
65 r"C:\Program Files (x86)\itch\itch.exe",
66 ],
67 store_url: "https://itch.io/",
68 },
69 Platform::LegacyGames => PlatformFallback {
70 launcher_candidates: &[
71 r"C:\Program Files (x86)\Legacy Games\Legacy Games Launcher\Legacy Games Launcher.exe",
72 ],
73 store_url: "https://legacygames.com/",
74 },
75 Platform::Steam => PlatformFallback {
76 launcher_candidates: &[r"C:\Program Files (x86)\Steam\Steam.exe"],
77 store_url: "https://store.steampowered.com/",
78 },
79 Platform::Ubisoft => PlatformFallback {
80 launcher_candidates: &[
81 r"C:\Program Files (x86)\Ubisoft\Ubisoft Game Launcher\UbisoftConnect.exe",
82 r"C:\Program Files (x86)\Ubisoft\Ubisoft Game Launcher\UbisoftGameLauncher.exe",
83 ],
84 store_url: "https://store.ubisoft.com/",
85 },
86 _ => PlatformFallback {
88 launcher_candidates: &[],
89 store_url: "",
90 },
91 }
92}
93
94pub fn resolve_launcher_path(platform: &Platform, override_path: Option<&str>) -> Option<PathBuf> {
97 if let Some(custom) = override_path.filter(|s| !s.trim().is_empty()) {
99 let path = PathBuf::from(custom);
100 if path.exists() {
101 return Some(path);
102 }
103 warn!("Launcher path configurado para {platform:?} não existe mais: {path:?}");
104 }
105
106 platform_fallback(platform)
108 .launcher_candidates
109 .iter()
110 .filter_map(|template| expand_path_template(template))
111 .find(|p| p.exists())
112}
113
114fn is_launcher_only(platform: &Platform) -> bool {
118 matches!(platform, Platform::EA | Platform::Ubisoft)
119}
120
121fn protocol_handles_install(platform: &Platform) -> bool {
125 matches!(platform, Platform::Steam)
126}
127
128fn protocol_url_for(game: &Game) -> Option<String> {
130 match game.platform {
131 Platform::Steam => Some(format!("steam://rungameid/{}", game.platform_game_id)),
132 Platform::BattleNet => Some(format!("battlenet://{}", game.platform_game_id)),
133 _ => None,
134 }
135}
136
137pub fn resolve_launch(game: &Game, launcher_path_override: Option<&str>) -> LaunchResolution {
143 let protocol_bypasses_install_check =
144 protocol_handles_install(&game.platform) && protocol_url_for(game).is_some();
145
146 if (game.installed || protocol_bypasses_install_check) && !is_launcher_only(&game.platform) {
147 if let Some(url) = protocol_url_for(game) {
148 return LaunchResolution::Protocol(url);
149 }
150 if let Some(exec) = &game.executable_path {
151 return LaunchResolution::Executable(exec.clone());
152 }
153 }
154
155 if let Some(launcher_path) = resolve_launcher_path(&game.platform, launcher_path_override) {
156 return LaunchResolution::Launcher(launcher_path.to_string_lossy().to_string());
157 }
158
159 let store_url = platform_fallback(&game.platform).store_url;
160 if store_url.is_empty() {
161 return LaunchResolution::Unavailable;
162 }
163
164 LaunchResolution::Store(store_url.to_string())
165}
166
167fn expand_path_template(template: &str) -> Option<PathBuf> {
169 let mut resolved = template.to_string();
170
171 if resolved.contains("%LOCALAPPDATA%") {
172 let local_app_data = env::var("LOCALAPPDATA").ok()?;
173 resolved = resolved.replace("%LOCALAPPDATA%", &local_app_data);
174 }
175
176 if resolved.contains("%USERNAME%") {
177 let username = env::var("USERNAME").ok()?;
178 resolved = resolved.replace("%USERNAME%", &username);
179 }
180
181 Some(PathBuf::from(resolved))
182}