mirror of
https://github.com/djibux/BoilR.git
synced 2026-09-01 05:53:41 +02:00
Add feature to launch gog games through heroic (#168)
This commit is contained in:
@@ -1,7 +1,5 @@
|
|||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use super::heroic_platform::InstallationMode;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use std::path::Path;
|
||||||
use steam_shortcuts_util::{shortcut::ShortcutOwned, Shortcut};
|
use steam_shortcuts_util::{shortcut::ShortcutOwned, Shortcut};
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
@@ -12,49 +10,18 @@ pub struct HeroicGame {
|
|||||||
pub install_path: String,
|
pub install_path: String,
|
||||||
pub executable: String,
|
pub executable: String,
|
||||||
pub launch_parameters: String,
|
pub launch_parameters: String,
|
||||||
#[serde(skip_deserializing)]
|
|
||||||
pub install_mode: Option<InstallationMode>,
|
|
||||||
#[serde(skip_deserializing)]
|
|
||||||
pub launch_through_heroic: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HeroicGame {
|
impl HeroicGame {
|
||||||
pub fn is_installed(&self) -> bool {
|
pub fn is_installed(&self) -> bool {
|
||||||
if self.launch_through_heroic {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
Path::new(&self.install_path)
|
Path::new(&self.install_path)
|
||||||
.join(&self.executable)
|
.join(&self.executable)
|
||||||
.exists()
|
.exists()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<HeroicGame> for ShortcutOwned {
|
impl From<HeroicGame> for ShortcutOwned {
|
||||||
fn from(game: HeroicGame) -> Self {
|
fn from(game: HeroicGame) -> Self {
|
||||||
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 {} --no-gui",
|
|
||||||
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);
|
let target_path = Path::new(&game.install_path).join(game.executable);
|
||||||
|
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
@@ -90,8 +57,7 @@ impl From<HeroicGame> for ShortcutOwned {
|
|||||||
game.launch_parameters.as_str(),
|
game.launch_parameters.as_str(),
|
||||||
);
|
);
|
||||||
|
|
||||||
shortcut.to_owned()
|
let mut owned_shortcut = shortcut.to_owned();
|
||||||
};
|
|
||||||
owned_shortcut.tags.push("Heroic".to_owned());
|
owned_shortcut.tags.push("Heroic".to_owned());
|
||||||
owned_shortcut.tags.push("Ready TO Play".to_owned());
|
owned_shortcut.tags.push("Ready TO Play".to_owned());
|
||||||
owned_shortcut.tags.push("Installed".to_owned());
|
owned_shortcut.tags.push("Installed".to_owned());
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use steam_shortcuts_util::shortcut::ShortcutOwned;
|
use steam_shortcuts_util::{shortcut::ShortcutOwned, Shortcut};
|
||||||
|
|
||||||
use super::HeroicGame;
|
use super::{HeroicGame, InstallationMode};
|
||||||
use crate::gog::GogShortcut;
|
use crate::gog::GogShortcut;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -8,6 +8,38 @@ pub enum HeroicGameType {
|
|||||||
Epic(HeroicGame),
|
Epic(HeroicGame),
|
||||||
//The bool is if it is windows (true) or not (false)
|
//The bool is if it is windows (true) or not (false)
|
||||||
Gog(GogShortcut, bool),
|
Gog(GogShortcut, bool),
|
||||||
|
//The string is the app name
|
||||||
|
Heroic {
|
||||||
|
title: String,
|
||||||
|
app_name: String,
|
||||||
|
install_mode: InstallationMode,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HeroicGameType {
|
||||||
|
pub fn app_name(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
HeroicGameType::Epic(g) => g.app_name.as_ref(),
|
||||||
|
HeroicGameType::Gog(g, _) => g.game_id.as_ref(),
|
||||||
|
HeroicGameType::Heroic {
|
||||||
|
title,
|
||||||
|
app_name,
|
||||||
|
install_mode,
|
||||||
|
} => &app_name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn title(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
HeroicGameType::Epic(g) => g.title.as_ref(),
|
||||||
|
HeroicGameType::Gog(g, _) => g.name.as_ref(),
|
||||||
|
HeroicGameType::Heroic {
|
||||||
|
title,
|
||||||
|
app_name,
|
||||||
|
install_mode,
|
||||||
|
} => title.as_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<HeroicGameType> for ShortcutOwned {
|
impl From<HeroicGameType> for ShortcutOwned {
|
||||||
@@ -15,6 +47,24 @@ impl From<HeroicGameType> for ShortcutOwned {
|
|||||||
match heroic_game_type {
|
match heroic_game_type {
|
||||||
HeroicGameType::Epic(epic) => epic.into(),
|
HeroicGameType::Epic(epic) => epic.into(),
|
||||||
HeroicGameType::Gog(gog, _) => gog.into(),
|
HeroicGameType::Gog(gog, _) => gog.into(),
|
||||||
|
HeroicGameType::Heroic {
|
||||||
|
title,
|
||||||
|
app_name,
|
||||||
|
install_mode,
|
||||||
|
} => {
|
||||||
|
let launch_parameter = format!("heroic://launch/{}", app_name);
|
||||||
|
let (exe, parameter) = match install_mode {
|
||||||
|
InstallationMode::FlatPak => (
|
||||||
|
"flatpak",
|
||||||
|
format!(
|
||||||
|
"run com.heroicgameslauncher.hgl {} --no-gui",
|
||||||
|
launch_parameter
|
||||||
|
),
|
||||||
|
),
|
||||||
|
InstallationMode::UserBin => ("heroic", launch_parameter),
|
||||||
|
};
|
||||||
|
Shortcut::new("0", title.as_str(), exe, "", "", "", parameter.as_str()).to_owned()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ pub struct HeroicPlatform {
|
|||||||
pub settings: HeroicSettings,
|
pub settings: HeroicSettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone, Copy)]
|
||||||
pub enum InstallationMode {
|
pub enum InstallationMode {
|
||||||
FlatPak,
|
FlatPak,
|
||||||
UserBin,
|
UserBin,
|
||||||
@@ -74,6 +74,20 @@ fn get_shortcuts_from_location<P: AsRef<Path>>(path: P) -> Result<Vec<HeroicGame
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HeroicPlatform {
|
||||||
|
pub fn get_heroic_games(&self) -> Vec<HeroicGameType> {
|
||||||
|
let install_modes = vec![InstallationMode::FlatPak, InstallationMode::UserBin];
|
||||||
|
|
||||||
|
let mut heroic_games = self.get_epic_games(&install_modes);
|
||||||
|
if let Ok(gog_games) = get_gog_games(&self.settings, &install_modes) {
|
||||||
|
heroic_games.extend(gog_games);
|
||||||
|
} else {
|
||||||
|
println!("Did not find any GOG games in heroic")
|
||||||
|
}
|
||||||
|
heroic_games
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Platform<HeroicGameType, Box<dyn Error>> for HeroicPlatform {
|
impl Platform<HeroicGameType, Box<dyn Error>> for HeroicPlatform {
|
||||||
fn enabled(&self) -> bool {
|
fn enabled(&self) -> bool {
|
||||||
self.settings.enabled
|
self.settings.enabled
|
||||||
@@ -82,16 +96,9 @@ impl Platform<HeroicGameType, Box<dyn Error>> for HeroicPlatform {
|
|||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"Heroic"
|
"Heroic"
|
||||||
}
|
}
|
||||||
fn get_shortcuts(&self) -> Result<Vec<HeroicGameType>, Box<dyn Error>> {
|
|
||||||
let install_modes = vec![InstallationMode::FlatPak, InstallationMode::UserBin];
|
|
||||||
|
|
||||||
let mut heroic_games = self.get_epic_games(&install_modes)?;
|
fn get_shortcuts(&self) -> Result<Vec<HeroicGameType>, Box<dyn Error>> {
|
||||||
if let Ok(gog_games) = get_gog_games(&install_modes) {
|
Ok(self.get_heroic_games())
|
||||||
heroic_games.extend(gog_games);
|
|
||||||
} else {
|
|
||||||
println!("Did not find any GOG games in heroic")
|
|
||||||
}
|
|
||||||
Ok(heroic_games)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
@@ -111,90 +118,91 @@ impl Platform<HeroicGameType, Box<dyn Error>> for HeroicPlatform {
|
|||||||
|
|
||||||
fn needs_proton(&self, input: &HeroicGameType) -> bool {
|
fn needs_proton(&self, input: &HeroicGameType) -> bool {
|
||||||
match input {
|
match input {
|
||||||
HeroicGameType::Epic(game) => !game.launch_through_heroic,
|
HeroicGameType::Epic(_game) => true,
|
||||||
HeroicGameType::Gog(_, is_windows) => *is_windows,
|
HeroicGameType::Gog(_, is_windows) => *is_windows,
|
||||||
|
HeroicGameType::Heroic { .. } => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HeroicPlatform {
|
impl HeroicPlatform {
|
||||||
pub fn get_epic_games(
|
pub fn get_epic_games(&self, install_modes: &[InstallationMode]) -> Vec<HeroicGameType> {
|
||||||
&self,
|
let mut shortcuts = vec![];
|
||||||
install_modes: &[InstallationMode],
|
for install_mode in install_modes {
|
||||||
) -> Result<Vec<HeroicGameType>, Box<dyn Error>> {
|
if let Ok(mut games) = get_shortcuts_from_install_mode(install_mode) {
|
||||||
let shortcuts = self.get_heroic_games(install_modes);
|
games.sort_by_key(|m| {
|
||||||
let mut epic_shortcuts = vec![];
|
format!("{}-{}-{}", m.launch_parameters, m.executable, &m.app_name)
|
||||||
for shortcut in shortcuts {
|
});
|
||||||
epic_shortcuts.push(HeroicGameType::Epic(shortcut));
|
games.dedup_by_key(|m| {
|
||||||
}
|
format!("{}-{}-{}", m.launch_parameters, m.executable, &m.app_name)
|
||||||
Ok(epic_shortcuts)
|
});
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_heroic_games(&self, install_modes: &[InstallationMode]) -> Vec<HeroicGame> {
|
for game in games {
|
||||||
let mut shortcuts: Vec<HeroicGame> = install_modes
|
if self.settings.is_heroic_launch(&game.app_name) {
|
||||||
.iter()
|
shortcuts.push(HeroicGameType::Heroic {
|
||||||
.filter_map(|install_mode| {
|
title: game.title,
|
||||||
let mut shortcuts = get_shortcuts_from_install_mode(install_mode).ok();
|
app_name: game.app_name,
|
||||||
if let Some(shortcuts) = shortcuts.as_mut() {
|
install_mode: *install_mode,
|
||||||
for shortcut in shortcuts {
|
});
|
||||||
shortcut.install_mode = Some(install_mode.clone());
|
|
||||||
|
|
||||||
let game_in_list = self
|
|
||||||
.settings
|
|
||||||
.launch_games_through_heroic
|
|
||||||
.contains(&shortcut.app_name)
|
|
||||||
|| self
|
|
||||||
.settings
|
|
||||||
.launch_games_through_heroic
|
|
||||||
.contains(&shortcut.title);
|
|
||||||
|
|
||||||
shortcut.launch_through_heroic =
|
|
||||||
if self.settings.default_launch_through_heroic {
|
|
||||||
!game_in_list
|
|
||||||
} else {
|
} else {
|
||||||
game_in_list
|
if game.is_installed() {
|
||||||
};
|
shortcuts.push(HeroicGameType::Epic(game));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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
|
shortcuts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn get_gog_games(
|
fn get_gog_games(
|
||||||
|
settings: &HeroicSettings,
|
||||||
install_modes: &[InstallationMode],
|
install_modes: &[InstallationMode],
|
||||||
) -> Result<Vec<HeroicGameType>, Box<dyn Error>> {
|
) -> Result<Vec<HeroicGameType>, Box<dyn Error>> {
|
||||||
let gog_paths: Vec<HeroicGogPath> = install_modes
|
let mut gog_paths = vec![];
|
||||||
.iter()
|
for install_mode in install_modes {
|
||||||
.filter_map(|install_mode| {
|
|
||||||
let config = get_gog_installed_location(install_mode);
|
let config = get_gog_installed_location(install_mode);
|
||||||
if config.exists() {
|
if config.exists() {
|
||||||
Some(config)
|
if let Ok(config_string) = std::fs::read_to_string(config) {
|
||||||
} else {
|
if let Ok(config) = serde_json::from_str::<HeroicGogConfig>(&config_string) {
|
||||||
None
|
for c in config.installed {
|
||||||
|
gog_paths.push((install_mode, c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.filter_map(|config_path| std::fs::read_to_string(config_path).ok())
|
|
||||||
.filter_map(|config_string| serde_json::from_str::<HeroicGogConfig>(&config_string).ok())
|
|
||||||
.flat_map(|config| config.installed)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut is_windows_map = HashMap::new();
|
let mut is_windows_map = HashMap::new();
|
||||||
|
|
||||||
for path in gog_paths.iter() {
|
for (_, path) in gog_paths.iter() {
|
||||||
is_windows_map.insert(path.app_name.clone(), path.platform == "windows");
|
is_windows_map.insert(path.app_name.clone(), path.platform == "windows");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut gog_shortcuts = vec![];
|
||||||
|
|
||||||
|
let heroic_games = gog_paths
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, p)| settings.is_heroic_launch(&p.app_name))
|
||||||
|
.filter_map(|(install_mode, p)| {
|
||||||
|
let path = Path::new(&p.install_path);
|
||||||
|
if path.exists() {
|
||||||
|
let title = path.file_name();
|
||||||
|
Some(HeroicGameType::Heroic {
|
||||||
|
title: title.unwrap_or_default().to_string_lossy().to_string(),
|
||||||
|
app_name: p.app_name.clone(),
|
||||||
|
install_mode: **install_mode,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
gog_shortcuts.extend(heroic_games);
|
||||||
|
|
||||||
let game_folders = gog_paths
|
let game_folders = gog_paths
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|p| {
|
.filter(|(_, p)| !settings.is_heroic_launch(&p.app_name))
|
||||||
|
.filter_map(|(_, p)| {
|
||||||
let path = Path::new(&p.install_path);
|
let path = Path::new(&p.install_path);
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
Some(path.to_path_buf())
|
Some(path.to_path_buf())
|
||||||
@@ -203,9 +211,8 @@ fn get_gog_games(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let shortcuts = get_shortcuts_from_game_folders(game_folders);
|
let direct_shortcuts = get_shortcuts_from_game_folders(game_folders);
|
||||||
let mut gog_shortcuts = vec![];
|
for shortcut in direct_shortcuts {
|
||||||
for shortcut in shortcuts {
|
|
||||||
let is_windows = is_windows_map.get(&shortcut.game_id).unwrap_or(&false);
|
let is_windows = is_windows_map.get(&shortcut.game_id).unwrap_or(&false);
|
||||||
gog_shortcuts.push(HeroicGameType::Gog(shortcut, *is_windows));
|
gog_shortcuts.push(HeroicGameType::Gog(shortcut, *is_windows));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,3 +6,16 @@ pub struct HeroicSettings {
|
|||||||
pub launch_games_through_heroic: Vec<String>,
|
pub launch_games_through_heroic: Vec<String>,
|
||||||
pub default_launch_through_heroic: bool,
|
pub default_launch_through_heroic: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HeroicSettings {
|
||||||
|
pub fn is_heroic_launch<S: AsRef<str>>(&self, app_name: S) -> bool {
|
||||||
|
let contains = self
|
||||||
|
.launch_games_through_heroic
|
||||||
|
.contains(&app_name.as_ref().to_string());
|
||||||
|
if self.default_launch_through_heroic {
|
||||||
|
!contains
|
||||||
|
} else {
|
||||||
|
contains
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -98,21 +98,20 @@ self.settings.heroic.default_launch_through_heroic{
|
|||||||
let manifests =self.heroic_games.get_or_insert_with(||{
|
let manifests =self.heroic_games.get_or_insert_with(||{
|
||||||
let heroic_setting = self.settings.heroic.clone();
|
let heroic_setting = self.settings.heroic.clone();
|
||||||
|
|
||||||
let install_modes = vec![crate::heroic::InstallationMode::FlatPak, crate::heroic::InstallationMode::UserBin];
|
|
||||||
let heroic_platform =HeroicPlatform{
|
let heroic_platform =HeroicPlatform{
|
||||||
settings:heroic_setting
|
settings:heroic_setting
|
||||||
};
|
};
|
||||||
heroic_platform.get_heroic_games(&install_modes)
|
heroic_platform.get_heroic_games()
|
||||||
});
|
});
|
||||||
|
|
||||||
let safe_open_games = &mut self.settings.heroic.launch_games_through_heroic;
|
let safe_open_games = &mut self.settings.heroic.launch_games_through_heroic;
|
||||||
for manifest in manifests{
|
for manifest in manifests{
|
||||||
let key = &manifest.app_name;
|
let key = manifest.app_name();
|
||||||
let display_name = &manifest.title;
|
let display_name = manifest.title();
|
||||||
let mut safe_open = safe_open_games.contains(display_name) || safe_open_games.contains(key);
|
let mut safe_open = safe_open_games.contains(&display_name.to_string()) || safe_open_games.contains(&key.to_string());
|
||||||
if ui.checkbox(&mut safe_open, display_name).clicked(){
|
if ui.checkbox(&mut safe_open, display_name).clicked(){
|
||||||
if safe_open{
|
if safe_open{
|
||||||
safe_open_games.push(key.clone());
|
safe_open_games.push(key.to_string());
|
||||||
}else{
|
}else{
|
||||||
safe_open_games.retain(|m| m!= display_name && m!= key);
|
safe_open_games.retain(|m| m!= display_name && m!= key);
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-2
@@ -8,7 +8,12 @@ use tokio::{
|
|||||||
sync::watch::{self, Receiver},
|
sync::watch::{self, Receiver},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{egs::ManifestItem, heroic::HeroicGame, settings::Settings, sync::SyncProgress};
|
use crate::{
|
||||||
|
egs::ManifestItem,
|
||||||
|
heroic::{HeroicGame, HeroicGameType},
|
||||||
|
settings::Settings,
|
||||||
|
sync::SyncProgress,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
ui_colors::{
|
ui_colors::{
|
||||||
@@ -36,7 +41,7 @@ pub struct MyEguiApp {
|
|||||||
pub(crate) games_to_sync: Receiver<FetcStatus<Vec<(String, Vec<ShortcutOwned>)>>>,
|
pub(crate) games_to_sync: Receiver<FetcStatus<Vec<(String, Vec<ShortcutOwned>)>>>,
|
||||||
pub(crate) status_reciever: Receiver<SyncProgress>,
|
pub(crate) status_reciever: Receiver<SyncProgress>,
|
||||||
pub(crate) epic_manifests: Option<Vec<ManifestItem>>,
|
pub(crate) epic_manifests: Option<Vec<ManifestItem>>,
|
||||||
pub(crate) heroic_games: Option<Vec<HeroicGame>>,
|
pub(crate) heroic_games: Option<Vec<HeroicGameType>>,
|
||||||
pub(crate) image_selected_state: ImageSelectState,
|
pub(crate) image_selected_state: ImageSelectState,
|
||||||
pub(crate) backup_state: BackupState,
|
pub(crate) backup_state: BackupState,
|
||||||
pub(crate) disconect_state: DiconnectState,
|
pub(crate) disconect_state: DiconnectState,
|
||||||
|
|||||||
Reference in New Issue
Block a user