1use crate::database::{current_schema_version, AppState};
6use crate::errors::AppError;
7use crate::models::{
8 Game, GameDataPath, GameDetails, GameExtras, Platform, SystemRequirements, WishlistGame,
9};
10use rusqlite::Connection;
11use tauri::State;
12
13pub type BackupDataTuple = (
15 Vec<Game>,
16 Vec<GameDetails>,
17 Vec<WishlistGame>,
18 Vec<GameExtras>,
19 Vec<SystemRequirements>,
20 Vec<GameDataPath>,
21 u32,
22);
23
24pub fn fetch_backup_data(state: &State<AppState>) -> Result<BackupDataTuple, AppError> {
26 let conn = state.games_db.lock()?;
27
28 conn.execute("BEGIN TRANSACTION", [])?;
30
31 let games = fetch_games(&conn)?;
32 let game_details = fetch_game_details(&conn)?;
33 let wishlist_game = fetch_wishlist(&conn)?;
34 let game_extras = fetch_game_extras(&conn)?;
35 let system_requirements = fetch_system_requirements(&conn)?;
36 let game_data_paths = fetch_game_data_paths(&conn)?;
37 let schema_version = current_schema_version(&conn)?;
38
39 conn.execute("COMMIT", [])?;
40
41 Ok((
42 games,
43 game_details,
44 wishlist_game,
45 game_extras,
46 system_requirements,
47 game_data_paths,
48 schema_version,
49 ))
50}
51
52fn fetch_games(conn: &Connection) -> Result<Vec<Game>, AppError> {
54 let mut stmt = conn.prepare(
55 "SELECT id, name, slug, cover_url, platform, platform_game_id, installed, import_confidence, install_path, executable_path, launch_args, user_rating, favorite, status, playtime, playtime_source, last_played, added_at, alternative_names FROM games"
56 )?;
57
58 let game_iter = stmt.query_map([], |row| {
59 let alt_names_json: Option<String> = row.get(18)?;
60 let alternative_names = alt_names_json.and_then(|s| serde_json::from_str(&s).ok());
61
62 Ok(Game {
63 id: row.get(0)?,
64 name: row.get(1)?,
65 slug: row.get(2)?,
66 cover_url: row.get(3)?,
67 genres: None,
68 developer: None,
69 platform: row.get::<_, String>(4)?.parse().unwrap_or(Platform::Outra),
70 platform_game_id: row.get(5)?,
71 alternative_names,
72 installed: row.get(6)?,
73 import_confidence: row
74 .get::<_, Option<String>>(7)?
75 .and_then(|s| s.parse().ok()),
76 install_path: row.get(8)?,
77 executable_path: row.get(9)?,
78 launch_args: row.get(10)?,
79 user_rating: row.get(11)?,
80 favorite: row.get(12)?,
81 status: row.get(13)?,
82 playtime: row.get(14)?,
83 playtime_source: row
84 .get::<_, Option<String>>(15)?
85 .and_then(|s| s.parse().ok()),
86 last_played: row.get(16)?,
87 added_at: row.get(17)?,
88 is_adult: false,
89 })
90 })?;
91
92 Ok(game_iter.collect::<Result<Vec<_>, _>>()?)
93}
94
95fn fetch_game_details(conn: &Connection) -> Result<Vec<GameDetails>, AppError> {
97 let mut stmt = conn.prepare(
98 "SELECT
99 game_id, steam_app_id, developer, publisher, release_date, genres, tags, series,
100 description_raw, description_ptbr, background_image, critic_score,
101 steam_review_label, steam_review_count, steam_review_score, steam_review_updated_at,
102 esrb_rating, is_adult, adult_tags, external_links, median_playtime,
103 estimated_playtime, updated_at
104 FROM game_details",
105 )?;
106
107 let details_iter = stmt.query_map([], |row| {
108 let links_json: Option<String> = row.get(19)?;
109 let external_links = links_json.and_then(|s| serde_json::from_str(&s).ok());
110
111 let tags_json: Option<String> = row.get(6)?;
112 let tags = tags_json.map(|s| crate::database::deserialize_tags(&s));
113
114 Ok(GameDetails {
115 game_id: row.get(0)?,
116 steam_app_id: row.get(1)?,
117 developer: row.get(2)?,
118 publisher: row.get(3)?,
119 release_date: row.get(4)?,
120 genres: row.get(5)?,
121 tags,
122 series: row.get(7)?,
123 description_raw: row.get(8)?,
124 description_ptbr: row.get(9)?,
125 background_image: row.get(10)?,
126 critic_score: row.get(11)?,
127 steam_review_label: row.get(12)?,
128 steam_review_count: row.get(13)?,
129 steam_review_score: row.get(14)?,
130 steam_review_updated_at: row.get(15)?,
131 esrb_rating: row.get(16)?,
132 is_adult: row.get(17).unwrap_or(false),
133 adult_tags: row.get(18)?,
134 external_links,
135 median_playtime: row.get(20)?,
136 estimated_playtime: row.get(21)?,
137 updated_at: row.get(22)?,
138 })
139 })?;
140
141 Ok(details_iter.collect::<Result<Vec<_>, _>>()?)
142}
143
144fn fetch_wishlist(conn: &Connection) -> Result<Vec<WishlistGame>, AppError> {
146 let mut stmt = conn.prepare(
147 "SELECT id, name, cover_url, store_url, store_platform, itad_id, current_price, normal_price, lowest_price, currency, on_sale, voucher, added_at FROM wishlist"
148 )?;
149
150 let wishlist_iter = stmt.query_map([], |row| {
151 Ok(WishlistGame {
152 id: row.get(0)?,
153 name: row.get(1)?,
154 cover_url: row.get(2)?,
155 store_url: row.get(3)?,
156 store_platform: row.get(4)?,
157 itad_id: row.get(5)?,
158 current_price: row.get(6)?,
159 normal_price: row.get(7)?,
160 lowest_price: row.get(8)?,
161 currency: row.get(9)?,
162 on_sale: row.get(10)?,
163 voucher: row.get(11)?,
164 added_at: row.get(12)?,
165 })
166 })?;
167
168 Ok(wishlist_iter.collect::<Result<Vec<_>, _>>()?)
169}
170
171fn fetch_game_extras(conn: &Connection) -> Result<Vec<GameExtras>, AppError> {
173 let mut stmt = conn.prepare(
174 "SELECT
175 steam_app_id, pcgw_page_id, pcgw_page_name, engine,
176 available_on,
177 dx_versions, vulkan_versions, opengl_versions,
178 win64, linux64, macos_arm, macos_intel64,
179 ray_tracing, upscaling, frame_gen,
180 ultrawidescreen, four_k_support, hdr, high_fps, fov, borderless_windowed, color_blind,
181 controller_support, full_controller, playstation_controllers, xinput_controllers,
182 surround_sound, subtitles, closed_captions,
183 has_save_data, has_config_data,
184 languages_interface, languages_audio, languages_subtitles,
185 fetched_at
186 FROM game_extras
187 WHERE fetched_at IS NOT NULL",
188 )?;
189
190 let parse_json_vec = |s: Option<String>| -> Option<Vec<String>> {
191 s.and_then(|v| serde_json::from_str(&v).ok())
192 };
193
194 let iter = stmt.query_map([], |row| {
195 Ok(GameExtras {
196 steam_app_id: row.get(0)?,
197 pcgw_page_id: row.get(1)?,
198 pcgw_page_name: row.get(2)?,
199 engine: row.get(3)?,
200 available_on: row.get(4)?,
201 dx_versions: row.get(5)?,
202 vulkan_versions: row.get(6)?,
203 opengl_versions: row.get(7)?,
204 win64: row.get(8)?,
205 linux64: row.get(9)?,
206 macos_arm: row.get(10)?,
207 macos_intel64: row.get(11)?,
208 ray_tracing: row.get(12)?,
209 upscaling: row.get(13)?,
210 frame_gen: row.get(14)?,
211 ultrawidescreen: row.get(15)?,
212 four_k_support: row.get(16)?,
213 hdr: row.get(17)?,
214 high_fps: row.get(18)?,
215 fov: row.get(19)?,
216 borderless_windowed: row.get(20)?,
217 color_blind: row.get(21)?,
218 controller_support: row.get(22)?,
219 full_controller: row.get(23)?,
220 playstation_controllers: row.get(24)?,
221 xinput_controllers: row.get(25)?,
222 surround_sound: row.get(26)?,
223 subtitles: row.get(27)?,
224 closed_captions: row.get(28)?,
225 has_save_data: row.get(29)?,
226 has_config_data: row.get(30)?,
227 languages_interface: parse_json_vec(row.get(31)?),
228 languages_audio: parse_json_vec(row.get(32)?),
229 languages_subtitles: parse_json_vec(row.get(33)?),
230 fetched_at: row.get(34)?,
231 })
232 })?;
233
234 Ok(iter.collect::<Result<Vec<_>, _>>()?)
235}
236
237fn fetch_system_requirements(conn: &Connection) -> Result<Vec<SystemRequirements>, AppError> {
239 let mut stmt = conn.prepare(
240 "SELECT
241 steam_app_id, os_family, tier_title, target,
242 min_os, min_cpu, min_cpu2, min_ram, min_gpu, min_gpu2, min_vram, min_dx, min_storage,
243 rec_os, rec_cpu, rec_cpu2, rec_ram, rec_gpu, rec_gpu2, rec_vram, rec_dx, rec_storage
244 FROM system_requirements
245 ORDER BY steam_app_id, id ASC",
246 )?;
247
248 let iter = stmt.query_map([], |row| {
249 Ok(SystemRequirements {
250 steam_app_id: row.get(0)?,
251 os_family: row.get(1)?,
252 tier_title: row.get(2)?,
253 target: row.get(3)?,
254 min_os: row.get(4)?,
255 min_cpu: row.get(5)?,
256 min_cpu2: row.get(6)?,
257 min_ram: row.get(7)?,
258 min_gpu: row.get(8)?,
259 min_gpu2: row.get(9)?,
260 min_vram: row.get(10)?,
261 min_dx: row.get(11)?,
262 min_storage: row.get(12)?,
263 rec_os: row.get(13)?,
264 rec_cpu: row.get(14)?,
265 rec_cpu2: row.get(15)?,
266 rec_ram: row.get(16)?,
267 rec_gpu: row.get(17)?,
268 rec_gpu2: row.get(18)?,
269 rec_vram: row.get(19)?,
270 rec_dx: row.get(20)?,
271 rec_storage: row.get(21)?,
272 })
273 })?;
274
275 Ok(iter.collect::<Result<Vec<_>, _>>()?)
276}
277
278fn fetch_game_data_paths(conn: &Connection) -> Result<Vec<GameDataPath>, AppError> {
280 let mut stmt = conn.prepare(
281 "SELECT steam_app_id, kind, os, raw_path
282 FROM game_data_paths
283 ORDER BY steam_app_id, id ASC",
284 )?;
285
286 let iter = stmt.query_map([], |row| {
287 Ok(GameDataPath {
288 steam_app_id: row.get(0)?,
289 kind: row.get(1)?,
290 os: row.get(2)?,
291 raw_path: row.get(3)?,
292 expanded_path: None,
293 })
294 })?;
295
296 Ok(iter.collect::<Result<Vec<_>, _>>()?)
297}