diff --git a/src/defaultconfig.toml b/src/defaultconfig.toml index 889f903..fd754db 100644 --- a/src/defaultconfig.toml +++ b/src/defaultconfig.toml @@ -3,6 +3,9 @@ debug= false [origin] enabled = false +[gog] +enabled = false + [epic_games] enabled = false diff --git a/src/gog/gog_config.rs b/src/gog/gog_config.rs new file mode 100644 index 0000000..9879a32 --- /dev/null +++ b/src/gog/gog_config.rs @@ -0,0 +1,7 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub(crate) struct GogConfig { + #[serde(alias = "installationPaths")] + pub installation_paths: Vec, +} diff --git a/src/gog/gog_game.rs b/src/gog/gog_game.rs new file mode 100644 index 0000000..5ad08a8 --- /dev/null +++ b/src/gog/gog_game.rs @@ -0,0 +1,63 @@ +use std::path::Path; + +use serde::{Deserialize, Serialize}; +use steam_shortcuts_util::{shortcut::ShortcutOwned, Shortcut}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub(crate) struct GogGame { + pub name: String, + #[serde(alias = "gameId")] + pub game_id: String, + #[serde(alias = "playTasks")] + pub play_tasks: Option>, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub(crate) struct PlayTask { + pub category: Option, + #[serde(alias = "isPrimary")] + pub is_primary: Option, + pub name: Option, + pub path: Option, + #[serde(alias = "type")] + pub task_type: String, + #[serde(alias = "workingDir")] + pub working_dir: Option, + +} + +pub(crate) struct GogShortcut { + pub name: String, + pub game_folder: String, + pub path: String, + pub working_dir:String, + pub game_id: String, +} + +impl From for ShortcutOwned { + fn from(gogs: GogShortcut) -> Self { + let exe = Path::new(&gogs.game_folder).join(gogs.path); + let icon_file = format!("goggame-{}.ico", gogs.game_id); + let icon_path = Path::new(&gogs.game_folder).join(&icon_file); + let icon = if icon_path.exists() { + icon_path.to_str().unwrap().to_string() + } else { + exe.to_str().unwrap_or("").to_string() + }; + let shortcut = Shortcut::new( + 0, + gogs.name.as_str(), + exe.to_str().unwrap(), + gogs.working_dir.as_str(), + icon.as_str(), + "", + "", + ); + let mut owned_shortcut = shortcut.to_owned(); + owned_shortcut.tags.push("Gog".to_owned()); + owned_shortcut.tags.push("Ready TO Play".to_owned()); + owned_shortcut.tags.push("Installed".to_owned()); + + owned_shortcut + } +} diff --git a/src/gog/gog_platform.rs b/src/gog/gog_platform.rs new file mode 100644 index 0000000..cc8c70a --- /dev/null +++ b/src/gog/gog_platform.rs @@ -0,0 +1,173 @@ +use failure::*; +use std::path::{Path, PathBuf}; + +use crate::{gog::gog_config::GogConfig, platform::Platform}; + +use super::{ + gog_game::{GogGame, GogShortcut}, + GogSettings, +}; + +pub struct GogPlatform { + pub settings: GogSettings, +} + +impl Platform for GogPlatform { + fn enabled(&self) -> bool { + self.settings.enabled + } + + fn name(&self) -> &str { + "Gog" + } + + fn get_shortcuts(&self) -> Result, GogErrors> { + let gog_location = self + .settings + .location + .as_ref() + .map(|location| Path::new(&location).to_path_buf()) + .unwrap_or_else(default_location); + if !gog_location.exists() { + return Err(GogErrors::PathNotFound { path: gog_location }); + } + let config_path = gog_location.join("config.json"); + if !config_path.exists() { + return Err(GogErrors::ConfigFileNotFound { path: config_path }); + } + let install_locations = get_install_locations(config_path)?; + dbg!(&install_locations); + let mut game_folders = vec![]; + for install_location in install_locations { + let path = Path::new(&install_location); + if path.exists() { + let dirs = path.read_dir(); + if let Ok(dirs) = dirs { + for dir in dirs { + if let Ok(dir) = dir { + if let Ok(file_type) = dir.file_type() { + if file_type.is_dir() { + game_folders.push(dir.path()); + } + } + } + } + } + } + } + let mut games = vec![]; + for game_folder in &game_folders { + if let Ok(files) = game_folder.read_dir() { + for file in files { + if let Ok(file) = file { + if let Some(file_name) = file.file_name().to_str() { + if file_name.starts_with("goggame-") { + if let Some(extension) = file.path().extension() { + if let Some(extension) = extension.to_str() { + if extension == "info" { + // Finally we know we can parse this as a game + if let Ok(content) = + std::fs::read_to_string(file.path()) + { + if let Ok(gog_game) = + serde_json::from_str::(&content) + { + games.push((gog_game, game_folder)); + } + } + } + } + } + } + } + } + } + } + } + + let mut shortcuts = vec![]; + for (game, game_folder) in games { + if let Some(folder_path) = game_folder.to_str() { + if let Some(tasks) = &game.play_tasks { + if let Some(primary_task) = tasks.iter().find(|t| { + t.is_primary.unwrap_or_default() + && t.task_type == "FileTask" + && t.category.as_ref().unwrap_or(&String::from("")) == "game" + }) { + if let Some(task_path) = &primary_task.path { + let full_path = game_folder.join(&task_path); + if let Some(full_path) = full_path.to_str() { + let folder_path = folder_path.to_string(); + + let working_dir = match &primary_task.working_dir { + Some(working_dir) => game_folder + .join(working_dir) + .to_str() + .unwrap_or(folder_path.as_str()) + .to_string(), + None => folder_path.to_string(), + }; + let shortcut = GogShortcut { + name: game.name, + game_folder: folder_path, + working_dir, + game_id: game.game_id, + path: full_path.to_string(), + }; + shortcuts.push(shortcut); + } + } + } + } + } + } + + Ok(shortcuts) + } +} + +fn get_install_locations(path: PathBuf) -> Result, GogErrors> { + let data_res = + std::fs::read_to_string(&path).map_err(|e| GogErrors::ConfigFileCouldNotBeRead { + path: path.clone(), + error: format!("{}", e), + })?; + let config: GogConfig = + serde_json::from_str(&data_res).map_err(|e| GogErrors::ConfigFileCouldNotBeRead { + path, + error: format!("{}", e), + })?; + Ok(config.installation_paths) +} + +pub fn default_location() -> PathBuf { + #[cfg(target_os = "windows")] + { + let key = "PROGRAMDATA"; + let program_data = std::env::var(key).expect("Expected a APPDATA variable to be defined"); + Path::new(&program_data).join("GOG.com").join("Galaxy") + } + #[cfg(target_os = "linux")] + { + let home = std::env::var("HOME").expect("Expected a home variable to be defined"); + Path::new(&home).join("GOG.com").join("Galaxy") + } +} + +#[derive(Debug, Fail)] +pub enum GogErrors { + #[fail( + display = "Gog path: {:?} could not be found. Try to specify a different path for Gog.", + path + )] + PathNotFound { path: PathBuf }, + + #[fail(display = "Gog config file not found at path: {:?}", path)] + ConfigFileNotFound { path: PathBuf }, + + #[fail( + display = "Gog config file at path: {:?} could not be red {}", + path, error + )] + ConfigFileCouldNotBeRead { path: PathBuf, error: String }, +} diff --git a/src/gog/gog_settings.rs b/src/gog/gog_settings.rs new file mode 100644 index 0000000..2c14c8d --- /dev/null +++ b/src/gog/gog_settings.rs @@ -0,0 +1,7 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct GogSettings { + pub enabled: bool, + pub location: Option, +} diff --git a/src/gog/mod.rs b/src/gog/mod.rs new file mode 100644 index 0000000..7fd4b36 --- /dev/null +++ b/src/gog/mod.rs @@ -0,0 +1,7 @@ +mod gog_config; +mod gog_game; +mod gog_platform; +mod gog_settings; + +pub use gog_platform::*; +pub use gog_settings::*; diff --git a/src/main.rs b/src/main.rs index 871e6fe..a039333 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,13 @@ use crate::{ itch::ItchPlatform, origin::OriginPlatform, + gog::GogPlatform, steamgriddb::{download_images, CachedSearch}, }; #[cfg(feature = "ui")] use std::{cell::RefCell, rc::Rc}; use std::{fs::File, io::Write, path::Path}; +mod gog; mod egs; mod itch; mod legendary; @@ -221,6 +223,14 @@ async fn run_sync(settings: &Settings) -> Result<(), Box> { &mut new_user_shortcuts, ); + + update_platform_shortcuts( + &GogPlatform{ + settings: settings.gog.clone(), + }, + &mut new_user_shortcuts, + ); + let shortcuts = new_user_shortcuts.iter().map(|f| f.borrow()).collect(); save_shortcuts(&shortcuts, Path::new(&shortcut_info.path)); diff --git a/src/settings.rs b/src/settings.rs index c05bd22..ca389fb 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,6 +1,7 @@ use crate::{ - egs::EpicGamesLauncherSettings, itch::ItchSettings, legendary::LegendarySettings, - origin::OriginSettings, steam::SteamSettings, steamgriddb::SteamGridDbSettings, + egs::EpicGamesLauncherSettings, gog::GogSettings, itch::ItchSettings, + legendary::LegendarySettings, origin::OriginSettings, steam::SteamSettings, + steamgriddb::SteamGridDbSettings, }; use config::{Config, ConfigError, Environment, File}; @@ -16,6 +17,7 @@ pub struct Settings { pub steamgrid_db: SteamGridDbSettings, pub steam: SteamSettings, pub origin: OriginSettings, + pub gog: GogSettings, } impl Settings {