From b58cbc51e061dbd739b802acd1d03356f226b252 Mon Sep 17 00:00:00 2001 From: Philip Kristoffersen Date: Thu, 31 Mar 2022 17:33:33 +0200 Subject: [PATCH] Proton support (#64) Enable proton for heroic games --- Readme.md | 2 +- src/egs/epic_platform.rs | 7 ++ src/gog/gog_game.rs | 1 + src/gog/gog_platform.rs | 7 ++ src/heroic/heroic_game.rs | 39 +++--- src/heroic/heroic_platform.rs | 147 +++++----------------- src/heroic/mod.rs | 1 + src/itch/itch_platform.rs | 15 ++- src/legendary/legendary_platform.rs | 4 + src/lutris/lutris_platform.rs | 13 +- src/origin/origin_game.rs | 2 + src/origin/origin_platform.rs | 10 ++ src/platform.rs | 3 + src/steam/collections.rs | 4 +- src/steam/mod.rs | 3 + src/steam/proton_string.txt | 7 ++ src/steam/proton_vdf_util.rs | 137 ++++++++++++++++++++ src/sync/synchronization.rs | 42 +++++-- src/testdata/vdf/compatmappingsection.vdf | 68 ++++++++++ src/testdata/vdf/testconfig.vdf | 104 +++++++++++++++ src/testdata/vdf/testconfig_expected.vdf | 116 +++++++++++++++++ src/ui/ui.rs | 2 +- src/uplay/game.rs | 1 + src/uplay/platform.rs | 10 ++ 24 files changed, 589 insertions(+), 156 deletions(-) create mode 100644 src/steam/proton_string.txt create mode 100644 src/steam/proton_vdf_util.rs create mode 100644 src/testdata/vdf/compatmappingsection.vdf create mode 100644 src/testdata/vdf/testconfig.vdf create mode 100644 src/testdata/vdf/testconfig_expected.vdf diff --git a/Readme.md b/Readme.md index f1168d0..475e019 100644 --- a/Readme.md +++ b/Readme.md @@ -26,7 +26,7 @@ Optionally you can set BoilR up to automatically download artwork from [SteamGri - [x] [UPlay](https://ubisoftconnect.com) - [x] [Lutris](https://github.com/lutris/lutris) - [x] [Legendary](https://github.com/derrod/legendary) -- [x] [Heroic Launcher](https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher) (Only Epic Games for now) +- [x] [Heroic Launcher](https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher) (Only Linux & Epic Games for now) - [ ] XBox/Microsoft Store integration diff --git a/src/egs/epic_platform.rs b/src/egs/epic_platform.rs index c8ac7fa..0c70ecc 100644 --- a/src/egs/epic_platform.rs +++ b/src/egs/epic_platform.rs @@ -42,4 +42,11 @@ impl Platform for EpicPlatform { }, } } + + fn needs_proton(&self, _input: &ManifestItem) -> bool { + #[cfg(target_family = "unix")] + return true; + #[cfg(target_os = "windows")] + return false; + } } diff --git a/src/gog/gog_game.rs b/src/gog/gog_game.rs index be8c4bf..8bbfd24 100644 --- a/src/gog/gog_game.rs +++ b/src/gog/gog_game.rs @@ -25,6 +25,7 @@ pub(crate) struct PlayTask { pub working_dir: Option, } +#[derive(Clone)] pub(crate) struct GogShortcut { pub name: String, pub game_folder: String, diff --git a/src/gog/gog_platform.rs b/src/gog/gog_platform.rs index 5114deb..46abc5e 100644 --- a/src/gog/gog_platform.rs +++ b/src/gog/gog_platform.rs @@ -149,6 +149,13 @@ impl Platform for GogPlatform { }, } } + + fn needs_proton(&self, _input: &GogShortcut) -> bool { + #[cfg(target_family = "unix")] + return true; + #[cfg(target_os = "windows")] + return false; + } } #[cfg(target_family = "unix")] diff --git a/src/heroic/heroic_game.rs b/src/heroic/heroic_game.rs index 7e362e5..58f0202 100644 --- a/src/heroic/heroic_game.rs +++ b/src/heroic/heroic_game.rs @@ -1,43 +1,42 @@ +use std::path::Path; + 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, - pub legendary_location: Option + pub launch_parameters: String, } impl From 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 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_os = "windows")] + let target = target_path.to_string_lossy().to_string(); + let shortcut = Shortcut::new( "0", game.title.as_str(), - launch.as_str(), + &target, "", - icon.as_str(), "", - &launch_options.as_str() + "", + &game.launch_parameters.as_str(), ); let mut owned_shortcut = shortcut.to_owned(); owned_shortcut.tags.push("Heroic".to_owned()); diff --git a/src/heroic/heroic_platform.rs b/src/heroic/heroic_platform.rs index e1bd9de..bd4c3cd 100644 --- a/src/heroic/heroic_platform.rs +++ b/src/heroic/heroic_platform.rs @@ -1,128 +1,51 @@ use super::{HeroicGame, HeroicSettings}; use crate::platform::{Platform, SettingsValidity}; -use serde_json::from_str; +use std::collections::HashMap; 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, + 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 { +fn get_installed_json_location(install_mode: &InstallationMode) -> PathBuf { + let home_dir = std::env::var("HOME").unwrap_or("".to_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, + InstallationMode::FlatPak => Path::new(&home_dir) + .join(".var/app/com.heroicgameslauncher.hgl/config/legendary/installed.json"), + InstallationMode::UserBin => Path::new(&home_dir).join(".config/legendary/installed.json"), } + .to_path_buf() } -#[cfg(target_os = "windows")] -fn find_legendary_location() -> Option { - 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 { - 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 = 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 { - 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, Box> { - let legendary = get_legendary_location(install_mode); - let config_folder = get_config_folder(install_mode); - get_shortcuts_from_location(config_folder, legendary.to_string()) + let installed_path = get_installed_json_location(install_mode); + get_shortcuts_from_location(installed_path) } -fn get_shortcuts_from_location( - config_folder: Option, - legendary: String, -) -> Result, Box> { - 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 = 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) +fn get_shortcuts_from_location>(path: P) -> Result, Box> { + let installed_json_path = path.as_ref(); + if installed_json_path.exists() { + let json = std::fs::read_to_string(installed_json_path)?; + let games_map = serde_json::from_str::>(&json)?; + let mut games = vec![]; + for game in games_map.values() { + games.push(game.clone()); + } + return Ok(games); + } + return Ok(vec![]); } impl Platform> for HeroicPlatform { @@ -133,22 +56,14 @@ impl Platform> for HeroicPlatform { fn name(&self) -> &str { "Heroic" } - #[cfg(target_family = "unix")] - fn get_shortcuts(&self) -> Result, Box> { + fn get_shortcuts(&self) -> Result, Box> { let install_modes = vec![InstallationMode::FlatPak, InstallationMode::UserBin]; - let first_working_instal = install_modes + let shortcuts = 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, Box> { - let legendary = find_legendary_location().unwrap_or("legendary".to_string()); - get_shortcuts_from_location(None, legendary) + .filter_map(|install_mode| get_shortcuts_from_install_mode(install_mode).ok()) + .flatten() + .collect(); + Ok(shortcuts) } #[cfg(target_family = "unix")] @@ -165,4 +80,8 @@ impl Platform> for HeroicPlatform { }, } } + + fn needs_proton(&self, _input: &HeroicGame) -> bool { + return true; + } } diff --git a/src/heroic/mod.rs b/src/heroic/mod.rs index cf69564..e9d8eb2 100644 --- a/src/heroic/mod.rs +++ b/src/heroic/mod.rs @@ -2,6 +2,7 @@ mod heroic_game; mod heroic_platform; mod settings; + pub use heroic_game::*; pub use heroic_platform::*; pub use settings::*; diff --git a/src/itch/itch_platform.rs b/src/itch/itch_platform.rs index af14363..8593aa7 100644 --- a/src/itch/itch_platform.rs +++ b/src/itch/itch_platform.rs @@ -51,10 +51,7 @@ impl Platform for ItchPlatform { //This is done to paths dedupe let paths: HashSet<&DbPaths> = paths.iter().collect(); - let res = paths - .iter() - .filter_map(|e| dbpath_to_game(*e)) - .collect(); + let res = paths.iter().filter_map(|e| dbpath_to_game(*e)).collect(); Ok(res) } @@ -72,6 +69,16 @@ impl Platform for ItchPlatform { }, } } + #[cfg(target_os = "windows")] + fn needs_proton(&self, _input: &ItchGame) -> bool { + return false; + } + + #[cfg(target_family = "unix")] + fn needs_proton(&self, input: &ItchGame) -> bool { + //We can only really guess here + return input.executable.ends_with("exe"); + } } fn dbpath_to_game(paths: &DbPaths) -> Option { diff --git a/src/legendary/legendary_platform.rs b/src/legendary/legendary_platform.rs index f0e3cdd..139591a 100644 --- a/src/legendary/legendary_platform.rs +++ b/src/legendary/legendary_platform.rs @@ -47,6 +47,10 @@ impl Platform> for LegendaryPlatform { }, } } + + fn needs_proton(&self, _input: &LegendaryGame) -> bool { + return false; + } } fn execute_legendary_command(program: &str) -> Result, Box> { diff --git a/src/lutris/lutris_platform.rs b/src/lutris/lutris_platform.rs index 4d94db2..6925d6d 100644 --- a/src/lutris/lutris_platform.rs +++ b/src/lutris/lutris_platform.rs @@ -9,7 +9,6 @@ pub struct LutrisPlatform { pub settings: LutrisSettings, } - impl Platform> for LutrisPlatform { fn enabled(&self) -> bool { self.settings.enabled @@ -21,10 +20,14 @@ impl Platform> for LutrisPlatform { fn get_shortcuts(&self) -> Result, Box> { let default_lutris_exe = "lutris".to_string(); - let lutris_executable = self.settings.executable.as_ref().unwrap_or(&default_lutris_exe); + let lutris_executable = self + .settings + .executable + .as_ref() + .unwrap_or(&default_lutris_exe); let lutris_command = Command::new(lutris_executable).arg("-lo").output()?; let output = String::from_utf8_lossy(&lutris_command.stdout).to_string(); - let games = parse_lutris_games(output.as_str()); + let games = parse_lutris_games(output.as_str()); let mut res = vec![]; for game in games { if game.platform != "steam" { @@ -48,4 +51,8 @@ impl Platform> for LutrisPlatform { }, } } + + fn needs_proton(&self, _input: &LutrisGame) -> bool { + return false; + } } diff --git a/src/origin/origin_game.rs b/src/origin/origin_game.rs index d834ed6..3f48fb7 100644 --- a/src/origin/origin_game.rs +++ b/src/origin/origin_game.rs @@ -1,4 +1,6 @@ use steam_shortcuts_util::{shortcut::ShortcutOwned, Shortcut}; + +#[derive(Clone)] pub struct OriginGame { pub id: String, pub title: String, diff --git a/src/origin/origin_platform.rs b/src/origin/origin_platform.rs index 713c501..6f04b58 100644 --- a/src/origin/origin_platform.rs +++ b/src/origin/origin_platform.rs @@ -75,6 +75,16 @@ impl Platform for OriginPlatform { }, } } + + fn needs_proton(&self, _input: &OriginGame) -> bool { + #[cfg(target_os = "windows")] + return false; + #[cfg(target_family = "unix")] + { + //TODO Update this when origin gets support on linux + return true; + } + } } fn get_folder_mfst_file_content(game_folder_path: &Path) -> Option { diff --git a/src/platform.rs b/src/platform.rs index 7211db4..6700541 100644 --- a/src/platform.rs +++ b/src/platform.rs @@ -14,6 +14,9 @@ where #[cfg(target_family = "unix")] fn create_symlinks(&self) -> bool; + + // HOME/.local/share/Steam/config/config.vdf + fn needs_proton(&self, input: &T) -> bool; } pub enum SettingsValidity { diff --git a/src/steam/collections.rs b/src/steam/collections.rs index 0131c7c..7984ff0 100644 --- a/src/steam/collections.rs +++ b/src/steam/collections.rs @@ -185,7 +185,7 @@ fn get_vdf_path>(steamid: S) -> Option { } return None; } - Err(e) => return None, + Err(_e) => return None, } } @@ -299,7 +299,7 @@ fn get_level_db_location() -> Option { } return None; } - Err(e) => return None, + Err(_e) => return None, } } diff --git a/src/steam/mod.rs b/src/steam/mod.rs index cdb3496..672d5ff 100644 --- a/src/steam/mod.rs +++ b/src/steam/mod.rs @@ -1,7 +1,10 @@ mod settings; mod utils; mod collections; +mod proton_vdf_util; + pub use settings::SteamSettings; pub use utils::*; pub use collections::*; +pub use proton_vdf_util::*; \ No newline at end of file diff --git a/src/steam/proton_string.txt b/src/steam/proton_string.txt new file mode 100644 index 0000000..a01ce00 --- /dev/null +++ b/src/steam/proton_string.txt @@ -0,0 +1,7 @@ + +="X" +={ ++"name" "proton_experimental" ++"config" "" ++"Priority" "250" +=} \ No newline at end of file diff --git a/src/steam/proton_vdf_util.rs b/src/steam/proton_vdf_util.rs new file mode 100644 index 0000000..3980377 --- /dev/null +++ b/src/steam/proton_vdf_util.rs @@ -0,0 +1,137 @@ +use std::path::Path; + +use nom::FindSubstring; + + +pub fn setup_proton_games>(games: &[B]){ + if let Ok(home) = std::env::var("HOME"){ + let config_file = Path::new(&home).join(".local/share/Steam/config/config.vdf"); + if config_file.exists(){ + if let Ok(config_content) = std::fs::read_to_string(&config_file){ + let new_string = enable_proton_games(config_content, games); + std::fs::write(config_file, new_string).unwrap(); + } + } + } + + +} + +fn enable_proton_games, B: AsRef>(vdf_content: S, games: &[B]) -> String { + let vdf_content = vdf_content.as_ref(); + if let Some(section_info) = find_indexes(vdf_content) { + let (base_indent_string, field_indent_string) = { + let mut a = String::new(); + let mut b = String::new(); + for _i in 0..=section_info.base_indentation { + a.push('\t'); + b.push('\t'); + } + b.push('\t'); + (a, b) + }; + + let proton_replace_string = include_str!("proton_string.txt"); + let section_str = &vdf_content[section_info.start..section_info.append_end]; + let games_strings_to_add = games + .iter() + .filter(|g| { + let game_section_start = format!("\"{}\"\n", g.as_ref()); + !section_str.contains(&game_section_start) + }) + .map(|game_id| { + let res = proton_replace_string.to_string(); + let res = res.replace("\"X\"", &format!("\"{}\"", game_id.as_ref())); + let res = res.replace("=", &base_indent_string); + let res = res.replace("+", &field_indent_string); + res + }); + let mut new_section = section_str.to_string(); + for game_string in games_strings_to_add { + new_section.push_str(&game_string); + } + new_section.push_str(§ion_info.end_key); + + let before_section = &vdf_content[..section_info.start]; + let after_section = &vdf_content[section_info.end..]; + return format!("{}{}{}", before_section, new_section, after_section); + } else { + //TODO make this an error instead? + println!("Could not find proton section in steam, try to manually set proton on at least one game and then rerun"); + } + return vdf_content.to_string(); +} + +struct SectionInfo { + start: usize, + end: usize, + append_end: usize, + base_indentation: usize, + end_key: String, +} + +fn find_indexes>(vdf_content: S) -> Option { + let compat_key = "\"CompatToolMapping\"\n"; + let vdf_content = vdf_content.as_ref(); + if let Some(compat_index) = vdf_content.find_substring(compat_key) { + let compat_index = compat_index + compat_key.len(); + let after_key = vdf_content[compat_index..].to_string(); + if let Some(base_indentation) = after_key.find('{') { + let mut end_key = "\n".to_string(); + for _i in 0..base_indentation { + end_key.push('\t'); + } + end_key.push('}'); + if let Some(end_index) = after_key.as_str().find_substring(&end_key) { + return Some(SectionInfo { + start: compat_index, + end: compat_index + end_index + end_key.len(), + append_end: compat_index + end_index, + base_indentation, + end_key: end_key.to_string(), + }); + } + } + } + None +} + +#[cfg(test)] +#[cfg(target_family = "unix")] +mod tests { + + use super::*; + + #[test] + pub fn can_find_index_test() { + let input = include_str!("../testdata/vdf/testconfig.vdf"); + let SectionInfo { + start, + end, + base_indentation, + .. + } = find_indexes(input).unwrap(); + + let actual = input[start..end].to_string(); + let expected = include_str!("../testdata/vdf/compatmappingsection.vdf"); + assert_eq!(expected, actual); + assert_eq!(4, base_indentation); + + } + + #[test] + pub fn enable_proton_test() { + let input = include_str!("../testdata/vdf/testconfig.vdf"); + let output = enable_proton_games(input, &vec!["42", "43", "44"]); + let expected = include_str!("../testdata/vdf/testconfig_expected.vdf"); + assert_eq!(expected,output ); + } + + #[test] + pub fn enable_proton_test_empty() { + let input = include_str!("../testdata/vdf/testconfig.vdf"); + let output = enable_proton_games(input, &vec!["2719403116"]); + let expected = include_str!("../testdata/vdf/testconfig.vdf"); + assert_eq!(expected,output ); + } +} diff --git a/src/sync/synchronization.rs b/src/sync/synchronization.rs index f58241e..04fa9cf 100644 --- a/src/sync/synchronization.rs +++ b/src/sync/synchronization.rs @@ -1,19 +1,22 @@ use steam_shortcuts_util::{shortcut::ShortcutOwned, shortcuts_to_bytes}; use crate::{ - egs::EpicPlatform, - heroic::HeroicPlatform, + egs::EpicPlatform, legendary::LegendaryPlatform, lutris::lutris_platform::LutrisPlatform, platform::Platform, settings::Settings, steam::{ - get_shortcuts_for_user, get_shortcuts_paths, write_collections, Collection, ShortcutInfo, - SteamUsersInfo, + get_shortcuts_for_user, get_shortcuts_paths, setup_proton_games, write_collections, + Collection, ShortcutInfo, SteamUsersInfo, }, steamgriddb::download_images_for_users, uplay::Uplay, }; + +#[cfg(target_family = "unix")] +use crate::heroic::HeroicPlatform; + use std::error::Error; use crate::{gog::GogPlatform, itch::ItchPlatform, origin::OriginPlatform}; @@ -29,6 +32,9 @@ pub async fn run_sync(settings: &Settings) -> Result<(), Box> { .iter() .flat_map(|s| s.1.clone()) .collect(); + for shortcut in &all_shortcuts { + println!("Appid: {} name: {}", shortcut.app_id, shortcut.app_name); + } println!("Found {} user(s)", userinfo_shortcuts.len()); for user in userinfo_shortcuts.iter_mut() { let start_time = std::time::Instant::now(); @@ -45,6 +51,7 @@ pub async fn run_sync(settings: &Settings) -> Result<(), Box> { shortcut_info.shortcuts.extend(all_shortcuts.clone()); fix_shortcut_icons(user, &mut shortcut_info.shortcuts); + save_shortcuts(&shortcut_info.shortcuts, Path::new(&shortcut_info.path)); if settings.steam.create_collections { @@ -121,7 +128,7 @@ fn write_shortcut_collections>( } fn get_platform_shortcuts(settings: &Settings) -> Vec<(String, Vec)> { - let platform_results = vec![ + let mut platform_results = vec![ update_platform_shortcuts(&EpicPlatform::new(settings.epic_games.clone())), update_platform_shortcuts(&LegendaryPlatform::new(settings.legendary.clone())), update_platform_shortcuts(&ItchPlatform::new(settings.itch.clone())), @@ -137,10 +144,14 @@ fn get_platform_shortcuts(settings: &Settings) -> Vec<(String, Vec, E: std::fmt::Debug + std::fmt::Display, T: Into, + T: Clone, { if platform.enabled() { if let crate::platform::SettingsValidity::Invalid { reason } = platform.settings_valid() { @@ -199,12 +211,13 @@ where match shortcuts_to_add_result { Ok(shortcuts_to_add) => { + let mut shortcuts_to_proton = vec![]; let mut shortcuts_to_add_transformed = vec![]; for shortcut in shortcuts_to_add { - let mut shortcut_owned: ShortcutOwned = shortcut.into(); + let mut shortcut_owned: ShortcutOwned = shortcut.clone().into(); shortcut_owned.dev_kit_game_id = format!("{}-{}", BOILR_TAG, shortcut_owned.app_id); - shortcuts_to_add_transformed.push(shortcut_owned); + shortcuts_to_add_transformed.push((shortcut, shortcut_owned)); } let shortcuts_to_add = shortcuts_to_add_transformed; @@ -215,15 +228,22 @@ where platform.name() ); - for shortcut_owned in shortcuts_to_add { + for (orign_shortcut, shortcut_owned) in shortcuts_to_add { #[cfg(target_family = "unix")] let shortcut_owned = if platform.create_symlinks() { crate::sync::symlinks::create_sym_links(&shortcut_owned) } else { shortcut_owned }; + if platform.needs_proton(&orign_shortcut) { + shortcuts_to_proton.push(format!("{}", shortcut_owned.app_id)); + } current_shortcuts.push(shortcut_owned.clone()); } + if shortcuts_to_proton.len() > 0 { + setup_proton_games(shortcuts_to_proton.as_slice()); + } + let name = platform.name(); return Some((name.to_string(), current_shortcuts)); } diff --git a/src/testdata/vdf/compatmappingsection.vdf b/src/testdata/vdf/compatmappingsection.vdf new file mode 100644 index 0000000..1006cbb --- /dev/null +++ b/src/testdata/vdf/compatmappingsection.vdf @@ -0,0 +1,68 @@ + { + "1102190" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "0" + { + "name" "proton_experimental" + "config" "" + "Priority" "75" + } + "337340" + { + "name" "proton_7" + "config" "" + "Priority" "250" + } + "1794680" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "3601140154" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "3230402366" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "3065048582" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "4068205213" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "3667283406" + { + "name" "" + "config" "" + "Priority" "250" + } + "2719403116" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "43" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + } \ No newline at end of file diff --git a/src/testdata/vdf/testconfig.vdf b/src/testdata/vdf/testconfig.vdf new file mode 100644 index 0000000..8bebcf3 --- /dev/null +++ b/src/testdata/vdf/testconfig.vdf @@ -0,0 +1,104 @@ +"InstallConfigStore" +{ + "Software" + { + "Valve" + { + "Steam" + { + "AutoUpdateWindowEnabled" "0" + "ipv6check_http_state" "bad" + "ipv6check_udp_state" "bad" + "ShaderCacheManager" + { + "HasCurrentBucket" "1" + "ProcessingQueue" "" + "EnableShaderBackgroundProcessing" "1" + } + "Rate" "30000" + "LastConfigstoreUploadTime" "1648654726" + "CompatToolMapping" + { + "1102190" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "0" + { + "name" "proton_experimental" + "config" "" + "Priority" "75" + } + "337340" + { + "name" "proton_7" + "config" "" + "Priority" "250" + } + "1794680" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "3601140154" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "3230402366" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "3065048582" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "4068205213" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "3667283406" + { + "name" "" + "config" "" + "Priority" "250" + } + "2719403116" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "43" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + } + } + } + } + "Music" + { + "LocalLibrary" + { + "Directories" + { + "0" "2" + "1" "3" + } + } + } + +} diff --git a/src/testdata/vdf/testconfig_expected.vdf b/src/testdata/vdf/testconfig_expected.vdf new file mode 100644 index 0000000..c4b6f94 --- /dev/null +++ b/src/testdata/vdf/testconfig_expected.vdf @@ -0,0 +1,116 @@ +"InstallConfigStore" +{ + "Software" + { + "Valve" + { + "Steam" + { + "AutoUpdateWindowEnabled" "0" + "ipv6check_http_state" "bad" + "ipv6check_udp_state" "bad" + "ShaderCacheManager" + { + "HasCurrentBucket" "1" + "ProcessingQueue" "" + "EnableShaderBackgroundProcessing" "1" + } + "Rate" "30000" + "LastConfigstoreUploadTime" "1648654726" + "CompatToolMapping" + { + "1102190" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "0" + { + "name" "proton_experimental" + "config" "" + "Priority" "75" + } + "337340" + { + "name" "proton_7" + "config" "" + "Priority" "250" + } + "1794680" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "3601140154" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "3230402366" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "3065048582" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "4068205213" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "3667283406" + { + "name" "" + "config" "" + "Priority" "250" + } + "2719403116" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "43" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "42" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + "44" + { + "name" "proton_experimental" + "config" "" + "Priority" "250" + } + } + } + } + } + "Music" + { + "LocalLibrary" + { + "Directories" + { + "0" "2" + "1" "3" + } + } + } + +} diff --git a/src/ui/ui.rs b/src/ui/ui.rs index 3f112c3..9790d12 100644 --- a/src/ui/ui.rs +++ b/src/ui/ui.rs @@ -202,8 +202,8 @@ fn update_ui_with_settings(ui: &mut UserInterface, settings: &Settings) { #[cfg(not(target_family = "unix"))] { ui.gog_winedrive_input.hide(); + ui.enable_heroic_checkbox.hide(); } - ui.enable_uplay_checkbox.set_value(settings.uplay.enabled); diff --git a/src/uplay/game.rs b/src/uplay/game.rs index 436917f..839f805 100644 --- a/src/uplay/game.rs +++ b/src/uplay/game.rs @@ -1,5 +1,6 @@ use steam_shortcuts_util::shortcut::{Shortcut, ShortcutOwned}; +#[derive(Clone)] pub(crate) struct Game { pub(crate) name: String, pub(crate) icon: String, diff --git a/src/uplay/platform.rs b/src/uplay/platform.rs index 33095be..af2f395 100644 --- a/src/uplay/platform.rs +++ b/src/uplay/platform.rs @@ -42,6 +42,16 @@ impl Platform> for Uplay { fn create_symlinks(&self) -> bool { false } + + fn needs_proton(&self, _input: &Game) -> bool { + #[cfg(target_os = "windows")] + return false; + #[cfg(target_family = "unix")] + { + //TODO update this when uplay gets proton support on linux + return true; + } + } } #[cfg(target_os = "windows")]