game_manager_lib/sources/
ea.rs1use crate::errors::AppError;
21use crate::sources::providers::SourceGame;
22use crate::utils::executable_heuristics::guess_main_executable;
23use std::fs;
24use std::path::{Path, PathBuf};
25
26#[cfg(target_os = "windows")]
27const EA_INSTALL_DATA_DIR_WINDOWS: &str = r"C:\ProgramData\EA Desktop\InstallData";
28
29pub struct EaSource {
31 install_dir: Option<PathBuf>,
33}
34
35impl EaSource {
36 pub fn new(install_dir: Option<PathBuf>) -> Self {
37 Self { install_dir }
38 }
39
40 fn resolve_install_data_dir(&self) -> Option<PathBuf> {
42 #[cfg(target_os = "windows")]
43 {
44 let path = PathBuf::from(EA_INSTALL_DATA_DIR_WINDOWS);
45 if path.exists() {
46 return Some(path);
47 }
48 }
49 None
50 }
51
52 fn load_known_names(install_data_dir: &Path) -> Vec<String> {
54 let Ok(entries) = fs::read_dir(install_data_dir) else {
55 log::warn!("Não foi possível ler InstallData: {install_data_dir:?}");
56 return Vec::new();
57 };
58
59 entries
60 .flatten()
61 .filter(|e| e.path().is_dir())
62 .filter_map(|e| {
63 let name = e.file_name().to_string_lossy().trim().to_string();
64 (!name.is_empty()).then_some(name)
65 })
66 .collect()
67 }
68
69 pub async fn import_installed(&self) -> Result<Vec<SourceGame>, AppError> {
71 let known_names = self
72 .resolve_install_data_dir()
73 .map(|dir| Self::load_known_names(&dir))
74 .unwrap_or_default();
75
76 let mut games = Vec::new();
77 let mut matched_known: std::collections::HashSet<String> = std::collections::HashSet::new();
78
79 if let Some(install_dir) = &self.install_dir {
81 if install_dir.exists() && install_dir.is_dir() {
82 for entry in fs::read_dir(install_dir)?.flatten() {
83 let path = entry.path();
84 if !path.is_dir() {
85 continue;
86 }
87
88 let folder_name = entry.file_name().to_string_lossy().trim().to_string();
89 if folder_name.is_empty() {
90 continue;
91 }
92
93 let matched = known_names.iter().find(|known| {
94 let known_lower = known.to_lowercase();
95 let folder_lower = folder_name.to_lowercase();
96 folder_lower.starts_with(&known_lower)
97 || known_lower.starts_with(&folder_lower)
98 });
99
100 let display_name = matched.cloned().unwrap_or_else(|| folder_name.clone());
101 if let Some(m) = matched {
102 matched_known.insert(m.clone());
103 }
104
105 let executable_path =
106 guess_main_executable(&path).map(|p| p.to_string_lossy().to_string());
107
108 games.push(SourceGame {
109 platform: "EA".to_string(),
110 platform_game_id: normalize_id(&display_name),
111 name: Some(display_name),
112 installed: true,
113 executable_path,
114 install_path: Some(path.to_string_lossy().to_string()),
115 playtime_minutes: None,
116 last_played: None,
117 });
118 }
119 } else if install_dir.exists() {
120 log::warn!("Caminho de instalação EA configurado não é uma pasta: {install_dir:?}");
121 }
122 }
123
124 for known in &known_names {
127 if matched_known.contains(known) {
128 continue;
129 }
130
131 games.push(SourceGame {
132 platform: "EA".to_string(),
133 platform_game_id: normalize_id(known),
134 name: Some(known.clone()),
135 installed: false,
136 executable_path: None,
137 install_path: None,
138 playtime_minutes: None,
139 last_played: None,
140 });
141 }
142
143 Ok(games)
144 }
145}
146
147fn normalize_id(name: &str) -> String {
150 name.to_lowercase().replace(' ', "_")
151}