Skip to main content

game_manager_lib/commands/platforms/
legacy.rs

1//! Legacy - Importa jogos de Legacy Games Launcher
2
3use crate::commands::platforms::core::{spawn_import_custom, ImportOutcome, NewlyImportedGame};
4use crate::database::AppState;
5use crate::errors::AppError;
6use crate::sources::legacy::LegacySource;
7use crate::utils::status_logic;
8use chrono::Utc;
9use rusqlite::params;
10use tauri::{AppHandle, Manager};
11use uuid::Uuid;
12
13/// Persiste jogos da Legacy Games nas tabelas `games` e `game_details`.
14///
15/// Difere de `persist_source_games` por também gravar `cover_url` e
16/// `description_raw` em `game_details`, que não fazem parte do `SourceGame` padrão.
17async fn persist_legacy_games(
18    state: &AppState,
19    games: Vec<crate::sources::legacy::LegacyGame>,
20) -> Result<(u32, u32, Vec<NewlyImportedGame>), AppError> {
21    let mut newly_imported = Vec::new();
22    let mut conn = state.games_db.lock().map_err(|_| AppError::MutexError)?;
23    let tx = conn
24        .transaction()
25        .map_err(|e| AppError::DatabaseError(e.to_string()))?;
26
27    let mut inserted = 0u32;
28    let mut updated = 0u32;
29    let now = Utc::now().to_rfc3339();
30
31    for legacy_game in games {
32        let game = &legacy_game.source;
33
34        let exists: bool = tx
35            .query_row(
36                "SELECT EXISTS(SELECT 1 FROM games WHERE platform = ?1 AND platform_game_id = ?2)",
37                params![&game.platform, &game.platform_game_id],
38                |row| row.get(0),
39            )
40            .unwrap_or(false);
41
42        let status = status_logic::calculate_status(game.playtime_minutes.unwrap_or(0) as i32);
43
44        if !exists {
45            let new_id = Uuid::new_v4().to_string();
46            let display_name = game.name.clone().unwrap_or_else(|| "Unknown".to_string());
47            let slug = crate::utils::text::slugify(&display_name);
48
49            tx.execute(
50                "INSERT INTO games (
51                        id, name, slug, cover_url, platform, platform_game_id,
52                        installed, status, playtime, playtime_source, last_played, added_at,
53                        favorite, user_rating, install_path, executable_path
54                    ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, NULL, NULL, ?10, 0, NULL, ?11, ?12)",
55                params![
56                        new_id,
57                        display_name,
58                        slug,
59                        legacy_game.cover_url,
60                        game.platform,
61                        game.platform_game_id,
62                        game.installed,
63                        status,
64                        game.playtime_minutes.unwrap_or(0),
65                        now,
66                        game.install_path,
67                        game.executable_path,
68                ],
69            )
70                .map_err(|e| AppError::DatabaseError(e.to_string()))?;
71
72            // Insere metadados na tabela game_details
73            if legacy_game.description_raw.is_some() {
74                tx.execute(
75                    "INSERT OR IGNORE INTO game_details (game_id, description_raw)
76                     VALUES (?1, ?2)",
77                    params![new_id, legacy_game.description_raw],
78                )
79                    .map_err(|e| AppError::DatabaseError(e.to_string()))?;
80            }
81
82            newly_imported.push(NewlyImportedGame {
83                game_id: new_id,
84                name: display_name,
85                platform: game.platform.clone(),
86                platform_game_id: game.platform_game_id.clone(),
87            });
88
89            inserted += 1;
90        } else {
91            tx.execute(
92                "UPDATE games SET
93                    installed   = ?1,
94                    status      = ?2,
95                    install_path     = COALESCE(?3, install_path),
96                    executable_path  = COALESCE(?4, executable_path)
97                 WHERE platform = ?5 AND platform_game_id = ?6",
98                params![
99                    game.installed,
100                    status,
101                    game.install_path,
102                    game.executable_path,
103                    game.platform,
104                    game.platform_game_id,
105                ],
106            )
107                .map_err(|e| AppError::DatabaseError(e.to_string()))?;
108
109            updated += 1;
110        }
111    }
112
113    tx.commit()
114        .map_err(|e| AppError::DatabaseError(e.to_string()))?;
115
116    Ok((inserted, updated, newly_imported))
117}
118
119/// Importa a biblioteca de jogos da Legacy Games.
120///
121/// Lê o arquivo `app-state-bck.json` do launcher da Legacy Games,
122/// cruza os jogos adquiridos com o catálogo embutido e persiste os dados
123/// nas tabelas `games` e `game_details`.
124///
125/// `app_state_path` — (opcional) caminho customizado para o `app-state-bck.json`.
126/// Se omitido, usa o caminho padrão do sistema operacional.
127/// `wine_prefix` — (Linux) caminho do Wine prefix onde o Legacy Games Launcher está instalado.
128/// No Windows o parâmetro é ignorado.
129#[tauri::command]
130pub async fn import_legacy_games(
131    app: AppHandle,
132    app_state_path: Option<String>,
133    wine_prefix: Option<String>,
134) -> Result<(), AppError> {
135    let path = app_state_path
136        .filter(|s| !s.trim().is_empty())
137        .map(std::path::PathBuf::from);
138    let prefix = wine_prefix
139        .filter(|s| !s.trim().is_empty())
140        .map(std::path::PathBuf::from);
141
142    spawn_import_custom(app, "LegacyGames", |app| async move {
143        let state: tauri::State<crate::database::AppState> = app.state();
144        let source = LegacySource::new_with_wine(path, prefix);
145        let games = source.fetch_games_detailed().await?;
146
147        if games.is_empty() {
148            return Ok(ImportOutcome::Empty);
149        }
150
151        let (inserted, updated, newly_imported) = persist_legacy_games(&state, games).await?;
152        Ok(ImportOutcome::Persisted {
153            inserted,
154            updated,
155            newly_imported,
156        })
157    });
158
159    Ok(())
160}