diff --git a/src/defaultconfig.toml b/src/defaultconfig.toml index 8120d18..0c2c976 100644 --- a/src/defaultconfig.toml +++ b/src/defaultconfig.toml @@ -38,6 +38,7 @@ flatpak = false [heroic] enabled = true +launch_games_through_heroic =[] [amazon] enabled = true diff --git a/src/heroic/heroic_game.rs b/src/heroic/heroic_game.rs index ca4d1f8..f529dbc 100644 --- a/src/heroic/heroic_game.rs +++ b/src/heroic/heroic_game.rs @@ -1,9 +1,10 @@ use std::path::Path; -use serde::{Deserialize, Serialize}; +use super::heroic_platform::InstallationMode; +use serde::Deserialize; use steam_shortcuts_util::{shortcut::ShortcutOwned, Shortcut}; -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug, Clone)] pub struct HeroicGame { pub app_name: String, pub title: String, @@ -11,6 +12,10 @@ pub struct HeroicGame { pub install_path: String, pub executable: String, pub launch_parameters: String, + #[serde(skip_deserializing)] + pub install_mode: Option, + #[serde(skip_deserializing)] + pub launch_through_heroic: bool, } impl HeroicGame { @@ -23,41 +28,63 @@ impl HeroicGame { impl From for ShortcutOwned { fn from(game: HeroicGame) -> Self { - let target_path = Path::new(&game.install_path).join(game.executable); + let mut owned_shortcut = if game.launch_through_heroic && game.install_mode.is_some() { + let launch_parameter = format!("heroic://launch/{}", game.app_name); + let (exe, parameter) = match game.install_mode.unwrap() { + InstallationMode::FlatPak => ( + "flatpak", + format!("run com.heroicgameslauncher.hgl {}", launch_parameter), + ), + InstallationMode::UserBin => ("heroic", launch_parameter), + }; + Shortcut::new( + "0", + game.title.as_str(), + exe, + "", + "", + "", + parameter.as_str(), + ) + .to_owned() + } else { + let target_path = Path::new(&game.install_path).join(game.executable); - #[cfg(target_family = "unix")] - let mut target = target_path.to_string_lossy().to_string(); - #[cfg(target_family = "unix")] - { - if !target.starts_with('\"') && !target.ends_with('\"') { - target = format!("\"{}\"", target); + #[cfg(target_family = "unix")] + let mut target = target_path.to_string_lossy().to_string(); + #[cfg(target_family = "unix")] + { + if !target.starts_with('\"') && !target.ends_with('\"') { + target = format!("\"{}\"", target); + } } - } - #[cfg(target_family = "unix")] - let mut install_path = game.install_path.to_string(); - #[cfg(target_family = "unix")] - { - if !install_path.starts_with('\"') && !install_path.ends_with('\"') { - install_path = format!("\"{}\"", install_path); + #[cfg(target_family = "unix")] + let mut install_path = game.install_path.to_string(); + #[cfg(target_family = "unix")] + { + if !install_path.starts_with('\"') && !install_path.ends_with('\"') { + install_path = format!("\"{}\"", install_path); + } } - } - #[cfg(target_os = "windows")] - let install_path = game.install_path.to_string(); + #[cfg(target_os = "windows")] + let install_path = game.install_path.to_string(); - #[cfg(target_os = "windows")] - let target = target_path.to_string_lossy().to_string(); + #[cfg(target_os = "windows")] + let target = target_path.to_string_lossy().to_string(); - let shortcut = Shortcut::new( - "0", - game.title.as_str(), - &target, - &install_path, - &target, - "", - game.launch_parameters.as_str(), - ); - let mut owned_shortcut = shortcut.to_owned(); + let shortcut = Shortcut::new( + "0", + game.title.as_str(), + &target, + &install_path, + &target, + "", + game.launch_parameters.as_str(), + ); + + shortcut.to_owned() + }; owned_shortcut.tags.push("Heroic".to_owned()); owned_shortcut.tags.push("Ready TO Play".to_owned()); owned_shortcut.tags.push("Installed".to_owned()); diff --git a/src/heroic/heroic_platform.rs b/src/heroic/heroic_platform.rs index 49aa16c..86332eb 100644 --- a/src/heroic/heroic_platform.rs +++ b/src/heroic/heroic_platform.rs @@ -13,7 +13,8 @@ pub struct HeroicPlatform { pub settings: HeroicSettings, } -enum InstallationMode { +#[derive(Deserialize, Debug, Clone)] +pub enum InstallationMode { FlatPak, UserBin, } @@ -84,7 +85,7 @@ impl Platform> for HeroicPlatform { fn get_shortcuts(&self) -> Result, Box> { let install_modes = vec![InstallationMode::FlatPak, InstallationMode::UserBin]; - let mut heroic_games = get_epic_games(&install_modes)?; + let mut heroic_games = self.get_epic_games(&install_modes)?; if let Ok(gog_games) = get_gog_games(&install_modes) { heroic_games.extend(gog_games); } else { @@ -110,31 +111,58 @@ impl Platform> for HeroicPlatform { fn needs_proton(&self, input: &HeroicGameType) -> bool { match input { - HeroicGameType::Epic(_) => true, + HeroicGameType::Epic(game) => !game.launch_through_heroic, HeroicGameType::Gog(_, is_windows) => *is_windows, } } } -fn get_epic_games( - install_modes: &[InstallationMode], -) -> Result, Box> { - let mut shortcuts: Vec = install_modes - .iter() - .filter_map(|install_mode| get_shortcuts_from_install_mode(install_mode).ok()) - .flatten() - .filter(|s| s.is_installed()) - .collect(); - - shortcuts.sort_by_key(|m| format!("{}-{}-{}", m.launch_parameters, m.executable, &m.app_name)); - shortcuts.dedup_by_key(|m| format!("{}-{}-{}", m.launch_parameters, m.executable, &m.app_name)); - let mut epic_shortcuts = vec![]; - for shortcut in shortcuts { - epic_shortcuts.push(HeroicGameType::Epic(shortcut)); +impl HeroicPlatform { + pub fn get_epic_games( + &self, + install_modes: &[InstallationMode], + ) -> Result, Box> { + let shortcuts = self.get_heroic_games(install_modes); + let mut epic_shortcuts = vec![]; + for shortcut in shortcuts { + epic_shortcuts.push(HeroicGameType::Epic(shortcut)); + } + Ok(epic_shortcuts) } - Ok(epic_shortcuts) -} + pub fn get_heroic_games(&self, install_modes: &[InstallationMode]) -> Vec { + let mut shortcuts: Vec = install_modes + .iter() + .filter_map(|install_mode| { + let mut shortcuts = get_shortcuts_from_install_mode(install_mode).ok(); + if let Some(shortcuts) = shortcuts.as_mut() { + for shortcut in shortcuts { + shortcut.install_mode = Some(install_mode.clone()); + if self + .settings + .launch_games_through_heroic + .contains(&shortcut.app_name) + || self + .settings + .launch_games_through_heroic + .contains(&shortcut.title) + { + shortcut.launch_through_heroic = true; + } + } + } + shortcuts + }) + .flatten() + .filter(|s| s.is_installed()) + .collect(); + shortcuts + .sort_by_key(|m| format!("{}-{}-{}", m.launch_parameters, m.executable, &m.app_name)); + shortcuts + .dedup_by_key(|m| format!("{}-{}-{}", m.launch_parameters, m.executable, &m.app_name)); + shortcuts + } +} fn get_gog_games( install_modes: &[InstallationMode], ) -> Result, Box> { diff --git a/src/heroic/settings.rs b/src/heroic/settings.rs index 2aa00d5..3f61d71 100644 --- a/src/heroic/settings.rs +++ b/src/heroic/settings.rs @@ -3,4 +3,5 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize, Serialize, Clone)] pub struct HeroicSettings { pub enabled: bool, + pub launch_games_through_heroic: Vec, } diff --git a/src/ui/ui_settings.rs b/src/ui/ui_settings.rs index c0604ae..3ed47e9 100644 --- a/src/ui/ui_settings.rs +++ b/src/ui/ui_settings.rs @@ -2,7 +2,7 @@ use copypasta::ClipboardProvider; use eframe::egui; use egui::ScrollArea; -use crate::egs::EpicPlatform; +use crate::{egs::EpicPlatform, heroic::HeroicPlatform}; use super::{ ui_colors::{BACKGROUND_COLOR, EXTRA_BACKGROUND_COLOR}, @@ -39,6 +39,43 @@ impl MyEguiApp { ui.heading("Heroic"); ui.checkbox(&mut self.settings.heroic.enabled, "Import from Heroic"); + + let safe_mode_header = match self.settings.heroic.launch_games_through_heroic.len() { + 0 => "Force games to launch through Heroic Launcher".to_string(), + 1 => "One game forced to launch through Heroic Launcher".to_string(), + x => format!("{} games forced to launch through Heroic Launcher", x), + }; + + egui::CollapsingHeader::new(safe_mode_header) + .id_source("Heroic_Launcher_safe_launch") + .show(ui, |ui| { + ui.label("Some games must be started from the Heroic Launcher, select those games below and BoilR will create shortcuts that opens the games through the Heroic Launcher."); + let manifests =self.heroic_games.get_or_insert_with(||{ + let heroic_setting = self.settings.heroic.clone(); + + let install_modes = vec![crate::heroic::InstallationMode::FlatPak, crate::heroic::InstallationMode::UserBin]; + let heroic_platform =HeroicPlatform{ + settings:heroic_setting + }; + let heroic_games = heroic_platform.get_heroic_games(&install_modes); + heroic_games + }); + + let safe_open_games = &mut self.settings.heroic.launch_games_through_heroic; + for manifest in manifests{ + let key = &manifest.app_name; + let display_name = &manifest.title; + let mut safe_open = safe_open_games.contains(display_name) || safe_open_games.contains(key); + if ui.checkbox(&mut safe_open, display_name).clicked(){ + if safe_open{ + safe_open_games.push(key.clone()); + }else{ + safe_open_games.retain(|m| m!= display_name && m!= key); + } + } + } + }) ; + ui.add_space(SECTION_SPACING); } @@ -187,9 +224,9 @@ impl MyEguiApp { .unwrap_or_default(); ui.label("Authentication key: "); if ui.text_edit_singleline(&mut auth_key).changed() { - if auth_key.is_empty(){ + if auth_key.is_empty() { self.settings.steamgrid_db.auth_key = None; - }else{ + } else { self.settings.steamgrid_db.auth_key = Some(auth_key.to_string()); } } @@ -263,14 +300,16 @@ impl MyEguiApp { }); ui.horizontal(|ui| { let mut empty_string = "".to_string(); - let epic_location = epic_settings.launcher_exe.as_mut().unwrap_or(&mut empty_string); - ui.label("Epic Launcher Location: ").on_hover_text( - "The location of the Epic launcher exe", - ); + let epic_location = epic_settings + .launcher_exe + .as_mut() + .unwrap_or(&mut empty_string); + ui.label("Epic Launcher Location: ") + .on_hover_text("The location of the Epic launcher exe"); if ui.text_edit_singleline(epic_location).changed() { - if epic_location.is_empty(){ + if epic_location.is_empty() { epic_settings.launcher_exe = None; - }else{ + } else { epic_settings.launcher_exe = Some(epic_location.to_string()); } } diff --git a/src/ui/uiapp.rs b/src/ui/uiapp.rs index 9dcc993..41b532d 100644 --- a/src/ui/uiapp.rs +++ b/src/ui/uiapp.rs @@ -8,7 +8,7 @@ use tokio::{ sync::watch::{self, Receiver}, }; -use crate::{egs::ManifestItem, settings::Settings, sync::SyncProgress}; +use crate::{egs::ManifestItem, heroic::HeroicGame, settings::Settings, sync::SyncProgress}; use super::{ ui_colors::{ @@ -36,6 +36,7 @@ pub struct MyEguiApp { pub(crate) games_to_sync: Receiver)>>>, pub(crate) status_reciever: Receiver, pub(crate) epic_manifests: Option>, + pub(crate) heroic_games: Option>, pub(crate) image_selected_state: ImageSelectState, pub(crate) backup_state: BackupState, } @@ -51,6 +52,7 @@ impl MyEguiApp { ui_images: UiImages::default(), status_reciever: watch::channel(SyncProgress::NotStarted).1, epic_manifests: None, + heroic_games: None, image_selected_state: ImageSelectState::default(), backup_state: BackupState::default(), }