mirror of
https://github.com/djibux/BoilR.git
synced 2026-09-01 05:53:41 +02:00
Add Heroic support (#46)
This commit is contained in:
+10
-4
@@ -8,7 +8,7 @@ class UserInterface {open
|
|||||||
} {
|
} {
|
||||||
Fl_Window win {
|
Fl_Window win {
|
||||||
label {BoilR} open
|
label {BoilR} open
|
||||||
xywh {1 1 500 525} type Double visible
|
xywh {1 1 500 575} type Double visible
|
||||||
} {
|
} {
|
||||||
|
|
||||||
# ----- Steam -----
|
# ----- Steam -----
|
||||||
@@ -43,7 +43,7 @@ class UserInterface {open
|
|||||||
|
|
||||||
# ----- Legendary -----
|
# ----- Legendary -----
|
||||||
Fl_Check_Button enable_legendary_checkbox {
|
Fl_Check_Button enable_legendary_checkbox {
|
||||||
label Legendary
|
label {Legendary}
|
||||||
xywh {25 175 155 25} down_box DOWN_BOX
|
xywh {25 175 155 25} down_box DOWN_BOX
|
||||||
}
|
}
|
||||||
Fl_Input legendary_executable_input {
|
Fl_Input legendary_executable_input {
|
||||||
@@ -106,16 +106,22 @@ class UserInterface {open
|
|||||||
xywh {175 425 300 25} align 5
|
xywh {175 425 300 25} align 5
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ----- Heroic -----
|
||||||
|
Fl_Check_Button enable_heroic_checkbox {
|
||||||
|
label Heroic
|
||||||
|
xywh {25 475 155 25} down_box DOWN_BOX
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# ----- Buttons -----
|
# ----- Buttons -----
|
||||||
|
|
||||||
Fl_Button save_settings_button {
|
Fl_Button save_settings_button {
|
||||||
label {Save Settings}
|
label {Save Settings}
|
||||||
xywh {25 475 125 25}
|
xywh {25 525 125 25}
|
||||||
}
|
}
|
||||||
Fl_Button synchronize_button {
|
Fl_Button synchronize_button {
|
||||||
label {Synchronize Games}
|
label {Synchronize Games}
|
||||||
xywh {175 475 300 25}
|
xywh {175 525 300 25}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,4 +29,7 @@ create_symlinks = true
|
|||||||
[lutris]
|
[lutris]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
|
||||||
|
[heroic]
|
||||||
|
enabled = true
|
||||||
|
|
||||||
[steam]
|
[steam]
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use steam_shortcuts_util::{shortcut::ShortcutOwned, Shortcut};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct HeroicGame {
|
||||||
|
pub app_name: String,
|
||||||
|
pub can_run_offline: bool,
|
||||||
|
pub title: String,
|
||||||
|
pub is_dlc: bool,
|
||||||
|
pub install_path: String,
|
||||||
|
pub executable: String,
|
||||||
|
pub config_folder: Option<String>,
|
||||||
|
pub legendary_location: Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<HeroicGame> for ShortcutOwned {
|
||||||
|
fn from(game: HeroicGame) -> Self {
|
||||||
|
|
||||||
|
let legendary = game.legendary_location.unwrap_or("legendary".to_string());
|
||||||
|
|
||||||
|
let icon = format!("\"{}\\{}\"", game.install_path, game.executable);
|
||||||
|
let launch = match game.config_folder{
|
||||||
|
Some(config_folder) => {
|
||||||
|
format!("env XDG_CONFIG_HOME={} {}", config_folder, legendary)
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
format!("{}", legendary)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let launch_options = format!("launch {}",game.app_name);
|
||||||
|
|
||||||
|
let shortcut = Shortcut::new(
|
||||||
|
0,
|
||||||
|
game.title.as_str(),
|
||||||
|
launch.as_str(),
|
||||||
|
"",
|
||||||
|
icon.as_str(),
|
||||||
|
"",
|
||||||
|
&launch_options.as_str()
|
||||||
|
);
|
||||||
|
let mut owned_shortcut = 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());
|
||||||
|
|
||||||
|
owned_shortcut
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
use super::{HeroicGame, HeroicSettings};
|
||||||
|
use crate::platform::{Platform, SettingsValidity};
|
||||||
|
use serde_json::from_str;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
pub struct HeroicPlatform {
|
||||||
|
pub settings: HeroicSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
enum InstallationMode {
|
||||||
|
FlatPak,
|
||||||
|
UserBin,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
fn get_legendary_location(install_mode: &InstallationMode) -> &'static str {
|
||||||
|
match install_mode{
|
||||||
|
InstallationMode::FlatPak => "/var/lib/flatpak/app/com.heroicgameslauncher.hgl/current/active/files/bin/heroic/resources/app.asar.unpacked/build/bin/linux/legendary",
|
||||||
|
InstallationMode::UserBin => "/opt/Heroic/resources/app.asar.unpacked/build/bin/linux/legendary"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
fn get_config_folder(install_mode: &InstallationMode) -> Option<String> {
|
||||||
|
match install_mode {
|
||||||
|
InstallationMode::FlatPak => {
|
||||||
|
let home_dir = std::env::var("HOME").unwrap_or("".to_string());
|
||||||
|
Some(
|
||||||
|
Path::new(&home_dir)
|
||||||
|
.join(".var/app/com.heroicgameslauncher.hgl/config")
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
InstallationMode::UserBin => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn find_legendary_location() -> Option<String> {
|
||||||
|
match heroic_folder_from_registry().or_else(heroic_folder_from_appdata) {
|
||||||
|
Some(heroic_folder) => {
|
||||||
|
let legendary_path = heroic_folder
|
||||||
|
.join("resources\\app.asar.unpacked\\build\\bin\\win32\\legendary.exe");
|
||||||
|
if legendary_path.exists() {
|
||||||
|
Some(legendary_path.to_path_buf().to_string_lossy().to_string())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn heroic_folder_from_registry() -> Option<PathBuf> {
|
||||||
|
use winreg::enums::*;
|
||||||
|
use winreg::RegKey;
|
||||||
|
|
||||||
|
let hklm = RegKey::predef(HKEY_CURRENT_USER);
|
||||||
|
if let Ok(launcher) = hklm.open_subkey("Software\\035fb1f9-7381-565b-92bb-ed6b2a3b99ba") {
|
||||||
|
let path_string: Result<String, _> = launcher.get_value("InstallLocation");
|
||||||
|
if let Ok(path_string) = path_string {
|
||||||
|
//.join("resources/app.asar.unpacked/build/bin/win32/legendary.exe")
|
||||||
|
let path = Path::new(&path_string);
|
||||||
|
if path.exists() {
|
||||||
|
return Some(path.to_path_buf());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn heroic_folder_from_appdata() -> Option<PathBuf> {
|
||||||
|
let key = "APPDATA";
|
||||||
|
match std::env::var(key) {
|
||||||
|
Ok(program_data) => {
|
||||||
|
let path = Path::new(&program_data).join("heroic");
|
||||||
|
if path.exists() {
|
||||||
|
Some(path.to_path_buf())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_err) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
fn get_shortcuts_from_install_mode(
|
||||||
|
install_mode: &InstallationMode,
|
||||||
|
) -> Result<Vec<HeroicGame>, Box<dyn Error>> {
|
||||||
|
let legendary = get_legendary_location(install_mode);
|
||||||
|
let config_folder = get_config_folder(install_mode);
|
||||||
|
get_shortcuts_from_location(config_folder, legendary.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_shortcuts_from_location(
|
||||||
|
config_folder: Option<String>,
|
||||||
|
legendary: String,
|
||||||
|
) -> Result<Vec<HeroicGame>, Box<dyn Error>> {
|
||||||
|
let output = if let Some(config_folder) = config_folder.clone() {
|
||||||
|
Command::new(&legendary)
|
||||||
|
.env("XDG_CONFIG_HOME", config_folder)
|
||||||
|
.arg("list-installed")
|
||||||
|
.arg("--json")
|
||||||
|
.output()?
|
||||||
|
} else {
|
||||||
|
Command::new(&legendary)
|
||||||
|
.arg("list-installed")
|
||||||
|
.arg("--json")
|
||||||
|
.output()?
|
||||||
|
};
|
||||||
|
let json = String::from_utf8_lossy(&output.stdout);
|
||||||
|
let mut legendary_ouput: Vec<HeroicGame> = from_str(&json)?;
|
||||||
|
legendary_ouput.iter_mut().for_each(|mut game| {
|
||||||
|
game.config_folder = config_folder.clone();
|
||||||
|
game.legendary_location = Some(legendary.to_string());
|
||||||
|
});
|
||||||
|
Ok(legendary_ouput)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Platform<HeroicGame, Box<dyn Error>> for HeroicPlatform {
|
||||||
|
fn enabled(&self) -> bool {
|
||||||
|
self.settings.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"Heroic"
|
||||||
|
}
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
fn get_shortcuts(&self) -> Result<Vec<HeroicGame>, Box<dyn Error>> {
|
||||||
|
let install_modes = vec![InstallationMode::FlatPak, InstallationMode::UserBin];
|
||||||
|
let first_working_instal = install_modes
|
||||||
|
.iter()
|
||||||
|
.find_map(|install_mode| get_shortcuts_from_install_mode(install_mode).ok());
|
||||||
|
match first_working_instal {
|
||||||
|
Some(res) => return Ok(res),
|
||||||
|
None => get_shortcuts_from_install_mode(&install_modes[0]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn get_shortcuts(&self) -> Result<Vec<HeroicGame>, Box<dyn Error>> {
|
||||||
|
let legendary = find_legendary_location().unwrap_or("legendary".to_string());
|
||||||
|
get_shortcuts_from_location(None, legendary)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
fn create_symlinks(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn settings_valid(&self) -> crate::platform::SettingsValidity {
|
||||||
|
let shortcuts_res = self.get_shortcuts();
|
||||||
|
match shortcuts_res {
|
||||||
|
Ok(_) => SettingsValidity::Valid,
|
||||||
|
Err(err) => SettingsValidity::Invalid {
|
||||||
|
reason: format!("{}", err),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
mod heroic_game;
|
||||||
|
mod heroic_platform;
|
||||||
|
mod settings;
|
||||||
|
|
||||||
|
pub use heroic_game::*;
|
||||||
|
pub use heroic_platform::*;
|
||||||
|
pub use settings::*;
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub struct HeroicSettings {
|
||||||
|
pub enabled: bool,
|
||||||
|
}
|
||||||
@@ -24,13 +24,13 @@ impl Platform<LegendaryGame, Box<dyn Error>> for LegendaryPlatform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_shortcuts(&self) -> Result<Vec<LegendaryGame>, Box<dyn Error>> {
|
fn get_shortcuts(&self) -> Result<Vec<LegendaryGame>, Box<dyn Error>> {
|
||||||
let legendary_command = Command::new("legendary")
|
let legendary_string = self
|
||||||
.arg("list-installed")
|
.settings
|
||||||
.arg("--json")
|
.executable
|
||||||
.output()?;
|
.clone()
|
||||||
let json = String::from_utf8_lossy(&legendary_command.stdout);
|
.unwrap_or("legendary".to_string());
|
||||||
let legendary_ouput = from_str(&json)?;
|
let legendary = legendary_string.as_str();
|
||||||
Ok(legendary_ouput)
|
execute_legendary_command(legendary)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
@@ -48,3 +48,13 @@ impl Platform<LegendaryGame, Box<dyn Error>> for LegendaryPlatform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn execute_legendary_command(program: &str) -> Result<Vec<LegendaryGame>, Box<dyn Error>> {
|
||||||
|
let legendary_command = Command::new(program)
|
||||||
|
.arg("list-installed")
|
||||||
|
.arg("--json")
|
||||||
|
.output()?;
|
||||||
|
let json = String::from_utf8_lossy(&legendary_command.stdout);
|
||||||
|
let legendary_ouput = from_str(&json)?;
|
||||||
|
Ok(legendary_ouput)
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ mod steam;
|
|||||||
mod steamgriddb;
|
mod steamgriddb;
|
||||||
mod sync;
|
mod sync;
|
||||||
mod uplay;
|
mod uplay;
|
||||||
|
mod heroic;
|
||||||
|
|
||||||
#[cfg(feature = "ui")]
|
#[cfg(feature = "ui")]
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
use steam_shortcuts_util::shortcut::ShortcutOwned;
|
use steam_shortcuts_util::shortcut::ShortcutOwned;
|
||||||
|
|
||||||
pub trait Platform<T, E>
|
pub trait Platform<T, E>
|
||||||
where
|
where
|
||||||
|
|||||||
+2
-1
@@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
egs::EpicGamesLauncherSettings, gog::GogSettings, itch::ItchSettings,
|
egs::EpicGamesLauncherSettings, gog::GogSettings, itch::ItchSettings,
|
||||||
legendary::LegendarySettings, lutris::settings::LutrisSettings, origin::OriginSettings,
|
legendary::LegendarySettings, lutris::settings::LutrisSettings, origin::OriginSettings,
|
||||||
steam::SteamSettings, steamgriddb::SteamGridDbSettings, uplay::UplaySettings,
|
steam::SteamSettings, steamgriddb::SteamGridDbSettings, uplay::UplaySettings, heroic::HeroicSettings,
|
||||||
};
|
};
|
||||||
|
|
||||||
use config::{Config, ConfigError, Environment, File};
|
use config::{Config, ConfigError, Environment, File};
|
||||||
@@ -20,6 +20,7 @@ pub struct Settings {
|
|||||||
pub gog: GogSettings,
|
pub gog: GogSettings,
|
||||||
pub uplay: UplaySettings,
|
pub uplay: UplaySettings,
|
||||||
pub lutris: LutrisSettings,
|
pub lutris: LutrisSettings,
|
||||||
|
pub heroic: HeroicSettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Settings {
|
impl Settings {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::{
|
|||||||
settings::Settings,
|
settings::Settings,
|
||||||
steam::{get_shortcuts_for_user, get_shortcuts_paths, ShortcutInfo, SteamUsersInfo},
|
steam::{get_shortcuts_for_user, get_shortcuts_paths, ShortcutInfo, SteamUsersInfo},
|
||||||
steamgriddb::download_images_for_users,
|
steamgriddb::download_images_for_users,
|
||||||
uplay::Uplay,
|
uplay::Uplay, heroic::HeroicPlatform,
|
||||||
};
|
};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
@@ -119,6 +119,12 @@ fn update_platforms(settings: &Settings, new_user_shortcuts: &mut Vec<ShortcutOw
|
|||||||
},
|
},
|
||||||
new_user_shortcuts,
|
new_user_shortcuts,
|
||||||
);
|
);
|
||||||
|
update_platform_shortcuts(
|
||||||
|
&HeroicPlatform {
|
||||||
|
settings: settings.heroic.clone(),
|
||||||
|
},
|
||||||
|
new_user_shortcuts,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save_shortcuts(shortcuts: &[ShortcutOwned], path: &Path) {
|
fn save_shortcuts(shortcuts: &[ShortcutOwned], path: &Path) {
|
||||||
|
|||||||
+4
-1
@@ -93,6 +93,9 @@ fn update_settings_with_ui_values(settings: &mut Settings, ui: &UserInterface) {
|
|||||||
// Lutris
|
// Lutris
|
||||||
settings.lutris.enabled = ui.enable_lutris_checkbox.value();
|
settings.lutris.enabled = ui.enable_lutris_checkbox.value();
|
||||||
settings.lutris.executable = empty_or_whitespace(ui.lutris_executable_input.value());
|
settings.lutris.executable = empty_or_whitespace(ui.lutris_executable_input.value());
|
||||||
|
|
||||||
|
// Heroic
|
||||||
|
settings.heroic.enabled = ui.enable_heroic_checkbox.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save_settings_to_file(settings: &Settings) {
|
fn save_settings_to_file(settings: &Settings) {
|
||||||
@@ -196,7 +199,7 @@ fn update_ui_with_settings(ui: &mut UserInterface, settings: &Settings) {
|
|||||||
{
|
{
|
||||||
ui.gog_winedrive_input.hide();
|
ui.gog_winedrive_input.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.enable_uplay_checkbox.set_value(settings.uplay.enabled);
|
ui.enable_uplay_checkbox.set_value(settings.uplay.enabled);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user