diff --git a/src/defaultconfig.toml b/src/defaultconfig.toml index 76d4ce9..e49852e 100644 --- a/src/defaultconfig.toml +++ b/src/defaultconfig.toml @@ -17,7 +17,6 @@ create_symlinks = true [epic_games] enabled = true -create_symlinks = true safe_launch = [] [uplay] diff --git a/src/egs/epic_platform.rs b/src/egs/epic_platform.rs index bb64fc8..6cdfed4 100644 --- a/src/egs/epic_platform.rs +++ b/src/egs/epic_platform.rs @@ -32,7 +32,7 @@ impl Platform for EpicPlatform { #[cfg(target_family = "unix")] fn create_symlinks(&self) -> bool { - self.settings.create_symlinks + false } fn settings_valid(&self) -> SettingsValidity { diff --git a/src/egs/get_manifests.rs b/src/egs/get_manifests.rs index 150e4aa..437b728 100644 --- a/src/egs/get_manifests.rs +++ b/src/egs/get_manifests.rs @@ -4,20 +4,14 @@ use std::env::{self}; use std::fs::{DirEntry, File}; use std::io::BufReader; -use std::path::{Path, PathBuf}; +use std::path::Path; use failure::*; #[derive(Debug, Fail)] pub enum EpicGamesManifestsError { - #[fail(display = "Path to EpicGamesLauncher not defined, it must be defined on linux")] - PathNotDefined, - - #[fail( - display = "EpicGamesLauncher path: {} could not be found. Try to specify a different path for the EpicGamesLauncher.", - path - )] - PathNotFound { path: String }, + #[fail(display = "EpicGamesLauncher not found")] + NotFound, #[fail( display = "Could not read EpicGamesLauncher manifest directory at {} error: {}", @@ -29,164 +23,77 @@ pub enum EpicGamesManifestsError { pub(crate) fn get_egs_manifests( settings: &EpicGamesLauncherSettings, ) -> Result, EpicGamesManifestsError> { - use EpicGamesManifestsError::*; + let locations = crate::egs::get_locations(); + match locations { + Some(locations) => { + let manifest_dir_path = locations.manifest_folder_path; + let manifest_dir_result = std::fs::read_dir(&manifest_dir_path); - let manifest_dir_path = get_manifest_dir_path(settings)?; - let manifest_dir_result = std::fs::read_dir(&manifest_dir_path); + match manifest_dir_result { + Ok(manifest_dir) => { + let all_manifests = manifest_dir + .filter_map(|dir| dir.ok()) + .filter_map(get_manifest_item); + let mut manifests = vec![]; + for mut manifest in all_manifests { + #[cfg(target_family = "unix")] + { + if let Some(compat_folder) = locations.compat_folder_path.as_ref() { + //Strip off the c:\\ + manifest.manifest_location = compat_folder + .join("pfx") + .join("drive_c") + .join(&manifest.manifest_location[3..].replace("\\", "/")) + .to_path_buf() + .to_string_lossy() + .to_string(); - #[cfg(target_os = "windows")] - let launcher_path = launcher_location_from_registry().unwrap_or_else(guess_default_launcher_location); - match manifest_dir_result { - Ok(manifest_dir) => { - let manifests = manifest_dir - .filter_map(|dir| dir.ok()) - .filter_map(get_manifest_item) - .filter(is_game_installed) - .filter(is_game_launchable); - let mut manifests: Vec = manifests.collect(); - manifests.sort_by_key(|m| { - format!( - "{}-{}-{}", - m.install_location, m.launch_executable, m.is_managed - ) - }); - manifests.dedup_by_key(|m| { - format!( - "{}-{}-{}", - m.install_location, m.launch_executable, m.is_managed - ) - }); - for mut manifest in &mut manifests { - if settings.safe_launch.contains(&manifest.display_name) - || settings.safe_launch.contains(&manifest.get_key()) - { - manifest.safe_launch = true; - #[cfg(target_os = "windows")] - { - manifest.launcher_path = Some(launcher_path.clone()); + manifest.install_location = compat_folder + .join("pfx") + .join("drive_c") + .join(&manifest.install_location[3..].replace("\\", "/")) + .to_path_buf() + .to_string_lossy() + .to_string(); + dbg!(&manifest.manifest_location); + } + } + if is_game_installed(&manifest) && is_game_launchable(&manifest) { + manifests.push(manifest); + } } + + manifests.sort_by_key(|m| { + format!( + "{}-{}-{}", + m.install_location, m.launch_executable, m.is_managed + ) + }); + manifests.dedup_by_key(|m| { + format!( + "{}-{}-{}", + m.install_location, m.launch_executable, m.is_managed + ) + }); + for mut manifest in &mut manifests { + manifest.launcher_path = Some(locations.launcher_path.clone()); + manifest.compat_folder = locations.compat_folder_path.clone(); + if settings.safe_launch.contains(&manifest.display_name) + || settings.safe_launch.contains(&manifest.get_key()) + { + manifest.safe_launch = true; + } + } + Ok(manifests) } - } - Ok(manifests) - } - Err(err) => Err(ReadDirError { - error: err, - path: manifest_dir_path, - }), - } -} - -fn get_manifest_dir_path( - settings: &EpicGamesLauncherSettings, -) -> Result { - use EpicGamesManifestsError::*; - if let Some(location) = &settings.location { - let path = Path::new(location); - if path.exists() { - return Ok(path.to_str().unwrap().to_string()); - } else { - return Err(PathNotFound { - path: path.to_str().unwrap().to_string(), - }); - } - } else { - let path = get_default_manifests_location(); - - match path { - Some(path) => Ok(path.to_str().unwrap().to_string()), - None => Err(PathNotDefined), - } - } -} - -pub fn get_default_manifests_location() -> Option { - #[cfg(target_family = "unix")] - { - //No path defined for epic gamestore, and we cannot guess on linux - None - } - - #[cfg(target_os = "windows")] - { - let path = match manifest_location_from_registry() { - Some(path) => path, - None => guess_default_manifest_location(), - }; - if path.exists() { - Some(path) - } else { - None - } - } -} - -#[cfg(target_os = "windows")] -fn manifest_location_from_registry() -> Option { - use winreg::enums::*; - use winreg::RegKey; - - let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); - if let Ok(launcher) = hklm.open_subkey("SOFTWARE\\WOW6432Node\\Epic Games\\EpicGamesLauncher") { - let path_string: Result = launcher.get_value("AppDataPath"); - if let Ok(path_string) = path_string { - let path = Path::new(&path_string).join("Manifests"); - if path.exists() { - return Some(path); + Err(err) => Err(EpicGamesManifestsError::ReadDirError { + error: err, + path: manifest_dir_path.to_string_lossy().to_string(), + }), } } + None => Err(EpicGamesManifestsError::NotFound), } - None -} - -#[cfg(target_os = "windows")] -fn guess_default_manifest_location() -> PathBuf { - let key = "SYSTEMDRIVE"; - let system_drive = - env::var(key).expect("We are on windows, we must know what the SYSTEMDRIVE is"); - - let path = Path::new(format!("{}\\", system_drive).as_str()) - .join("ProgramData") - .join("Epic") - .join("EpicGamesLauncher") - .join("Data") - .join("Manifests"); - path -} - -#[cfg(target_os = "windows")] -fn launcher_location_from_registry() -> Option { - use winreg::enums::*; - use winreg::RegKey; - - let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); - - if let Ok(launcher) = hklm.open_subkey("SOFTWARE\\Classes\\com.epicgames.launcher\\shell\\open\\command") { - let launch_string: Result = launcher.get_value(""); - if let Ok(launch_string) = launch_string { - let path = Path::new(&launch_string[1..launch_string.len()-4]); - if path.exists() { - return Some(path.to_path_buf()); - } - } - } - None -} - -#[cfg(target_os = "windows")] -fn guess_default_launcher_location() -> PathBuf { - let key = "SYSTEMDRIVE"; - let system_drive = - env::var(key).expect("We are on windows, we must know what the SYSTEMDRIVE is"); - - let path = Path::new(format!("{}\\", system_drive).as_str()) - .join("Program Files (x86)") - .join("Epic Games") - .join("Launcher") - .join("Portal") - .join("Binaries") - .join("Win64") - .join("EpicGamesLauncher.exe"); - path } fn is_game_installed(manifest: &ManifestItem) -> bool { @@ -221,10 +128,9 @@ fn get_manifest_item(dir_entry: DirEntry) -> Option { // assert_eq!(Some(std::path::Path::new("C:\\Program Files (x86)\\Epic Games\\Launcher\\Portal\\Binaries\\Win64\\EpicGamesLauncher.exe").to_path_buf()),launcher); // } - // #[test] // pub fn test_launcher_guess(){ // let launcher = guess_default_launcher_location(); // assert_eq!(std::path::Path::new("C:\\Program Files (x86)\\Epic Games\\Launcher\\Portal\\Binaries\\Win64\\EpicGamesLauncher.exe").to_path_buf(),launcher); // } -// } \ No newline at end of file +// } diff --git a/src/egs/manifest_item.rs b/src/egs/manifest_item.rs index 970f409..a174d2f 100644 --- a/src/egs/manifest_item.rs +++ b/src/egs/manifest_item.rs @@ -1,6 +1,9 @@ -use std::{collections::HashMap, path::{Path, PathBuf}}; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; -use serde::{Deserialize}; +use serde::Deserialize; use steam_shortcuts_util::{shortcut::ShortcutOwned, Shortcut}; #[derive(Deserialize, Debug, Clone)] @@ -37,33 +40,83 @@ pub(crate) struct ManifestItem { //This is not acutally in the manifest, but it will get added by get_manifests.rs pub launcher_path: Option, + + //This is not acutally in the manifest, but it will get added by get_manifests.rs if on linux + pub compat_folder: Option, } fn exe_shortcut(manifest: ManifestItem) -> ShortcutOwned { let exe = manifest.exe(); let start_dir = manifest.install_location.clone(); + let exe = exe.trim_matches('\"'); let start_dir = start_dir.trim_matches('\"'); + + #[cfg(target_family = "unix")] + let start_dir = format!("\"{}\"", start_dir); + + #[cfg(target_family = "unix")] + let exe = format!("\"{}\"", exe); + + let parameters = match manifest.compat_folder.as_ref() { + Some(compat_folder) => format!( + "STEAM_COMPAT_DATA_PATH=\"{}\" %command%", + compat_folder.to_string_lossy(), + ), + None => String::default(), + }; + Shortcut::new( "0", manifest.display_name.as_str(), - exe, - start_dir, - exe, - "", + &exe, + &start_dir, + &exe, "", + parameters.as_str(), ) .to_owned() } fn launcher_shortcut(manifest: ManifestItem) -> ShortcutOwned { let icon = manifest.exe(); - let url = manifest.get_launch_url(); + let url = match manifest.compat_folder.as_ref() { + Some(compat_folder) => format!( + "STEAM_COMPAT_DATA_PATH=\"{}\" %command% '{}'", + compat_folder.to_string_lossy(), + manifest.get_launch_url() + ), + None => manifest.get_launch_url(), + }; + + let parent_folder = manifest + .launcher_path + .as_ref() + .map(|p| { + p.parent() + .unwrap_or(Path::new("")) + .to_string_lossy() + .to_string() + }) + .unwrap_or_default(); + + #[cfg(target_family = "unix")] + let parent_folder = format!("\"{}\"", parent_folder); + + let launcher_path = manifest + .launcher_path + .as_ref() + .map(|p| p.to_string_lossy().to_string()) + .unwrap_or_default(); + + #[cfg(target_family = "unix")] + let launcher_path = format!("\"{}\"", launcher_path); + Shortcut::new( "0", manifest.display_name.as_str(), - manifest.launcher_path.as_ref().map(|p| p.to_string_lossy().to_string()).unwrap_or_default().as_str(), - manifest.launcher_path.map(|p| p.parent().unwrap_or(Path::new("")).to_string_lossy().to_string()).unwrap_or_default().as_str(), + launcher_path.as_str(), + parent_folder.as_str(), icon.as_str(), "", url.as_str(), @@ -150,7 +203,7 @@ mod tests { let mut manifest: ManifestItem = serde_json::from_str(json).unwrap(); manifest.is_managed = true; let shortcut: ShortcutOwned = manifest.clone().into(); - + assert_eq!(shortcut.launch_options, manifest.get_launch_url()); } #[test] @@ -164,7 +217,7 @@ mod tests { #[cfg(target_os = "windows")] assert_eq!(shortcut.exe, "C:\\Games\\MarvelGOTG\\retail/gotg.exe"); #[cfg(target_family = "unix")] - assert_eq!(shortcut.exe, "C:\\Games\\MarvelGOTG/retail/gotg.exe"); + assert_eq!(shortcut.exe, "\"C:\\Games\\MarvelGOTG/retail/gotg.exe\""); assert_eq!(shortcut.launch_options, ""); } diff --git a/src/egs/mod.rs b/src/egs/mod.rs index 9a7efe3..68324c7 100644 --- a/src/egs/mod.rs +++ b/src/egs/mod.rs @@ -1,10 +1,11 @@ mod epic_platform; mod get_manifests; mod manifest_item; +mod paths; mod settings; pub use epic_platform::*; -pub use get_manifests::get_default_manifests_location; use get_manifests::get_egs_manifests; pub(crate) use manifest_item::*; +pub use paths::*; pub use settings::EpicGamesLauncherSettings; diff --git a/src/egs/paths.rs b/src/egs/paths.rs new file mode 100644 index 0000000..b7702c5 --- /dev/null +++ b/src/egs/paths.rs @@ -0,0 +1,175 @@ +use std::path::PathBuf; + +#[derive(Default, Clone, Debug)] +pub struct EpicPaths { + pub(crate) launcher_path: PathBuf, + pub(crate) compat_folder_path: Option, + pub(crate) manifest_folder_path: PathBuf, +} + +pub fn get_locations() -> Option { + #[cfg(target_family = "unix")] + { + unix::get_locations() + } + #[cfg(target_os = "windows")] + { + windows::get_locations() + } +} + +#[cfg(target_family = "unix")] +mod unix { + use super::EpicPaths; + use std::path::Path; + + pub fn get_locations() -> Option { + if let Ok(home) = std::env::var("HOME") { + let compat_folder_path = Path::new(&home) + .join(".steam") + .join("steam") + .join("steamapps") + .join("compatdata"); + + if let Ok(compat_folder) = std::fs::read_dir(&compat_folder_path) { + for dir in compat_folder.flatten() { + let binary_path = dir + .path() + .join("pfx") + .join("drive_c") + .join("Program Files (x86)") + .join("Epic Games") + .join("Launcher") + .join("Engine") + .join("Binaries"); + if binary_path.exists() { + let launcher_path = if binary_path + .join("Win32") + .join("EpicGamesLauncher.exe") + .exists() + { + binary_path.join("Win32").join("EpicGamesLauncher.exe") + } else { + binary_path.join("Win64").join("EpicGamesLauncher.exe") + }; + if launcher_path.exists() { + //We found a launcher, lets find the manifests + + let manifest_folder_path = dir + .path() + .join("pfx") + .join("drive_c") + .join("ProgramData") + .join("Epic") + .join("EpicGamesLauncher") + .join("Data") + .join("Manifests"); + if manifest_folder_path.exists() { + //We found all we need + return Some(EpicPaths { + launcher_path, + compat_folder_path: Some(dir.path().to_path_buf()), + manifest_folder_path, + }); + } + } + } + } + } + } + None + } +} + + #[cfg(target_os = "windows")] + mod windows { + use super::EpicPaths; + use std::{path::{Path, PathBuf}, env}; + + fn manifest_location_from_registry() -> Option { + use winreg::enums::*; + use winreg::RegKey; + + let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); + if let Ok(launcher) = + hklm.open_subkey("SOFTWARE\\WOW6432Node\\Epic Games\\EpicGamesLauncher") + { + let path_string: Result = launcher.get_value("AppDataPath"); + if let Ok(path_string) = path_string { + let path = Path::new(&path_string).join("Manifests"); + if path.exists() { + return Some(path); + } + } + } + None + } + + fn guess_default_launcher_location() -> PathBuf { + let key = "SYSTEMDRIVE"; + let system_drive = + env::var(key).expect("We are on windows, we must know what the SYSTEMDRIVE is"); + + let path = Path::new(format!("{}\\", system_drive).as_str()) + .join("Program Files (x86)") + .join("Epic Games") + .join("Launcher") + .join("Portal") + .join("Binaries") + .join("Win64") + .join("EpicGamesLauncher.exe"); + path + } + + fn launcher_location_from_registry() -> Option { + use winreg::enums::*; + use winreg::RegKey; + + let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); + + if let Ok(launcher) = + hklm.open_subkey("SOFTWARE\\Classes\\com.epicgames.launcher\\shell\\open\\command") + { + let launch_string: Result = launcher.get_value(""); + if let Ok(launch_string) = launch_string { + let path = Path::new(&launch_string[1..launch_string.len() - 4]); + if path.exists() { + return Some(path.to_path_buf()); + } + } + } + None + } + + fn guess_default_manifest_location() -> PathBuf { + let key = "SYSTEMDRIVE"; + let system_drive = + env::var(key).expect("We are on windows, we must know what the SYSTEMDRIVE is"); + + let path = Path::new(format!("{}\\", system_drive).as_str()) + .join("ProgramData") + .join("Epic") + .join("EpicGamesLauncher") + .join("Data") + .join("Manifests"); + path + } + + pub fn get_locations() -> Option { + { + let manifest_folder_path = manifest_location_from_registry() + .unwrap_or_else(guess_default_manifest_location); + let launcer_path = launcher_location_from_registry() + .unwrap_or_else(guess_default_launcher_location); + if launcer_path.exists() && manifest_folder_path.exists() { + Some(EpicPaths { + compat_folder_path: None, + manifest_folder_path, + launcher_path: launcer_path, + }) + } else { + None + } + } + } + } diff --git a/src/egs/settings.rs b/src/egs/settings.rs index 6f073c0..40b4494 100644 --- a/src/egs/settings.rs +++ b/src/egs/settings.rs @@ -3,11 +3,5 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct EpicGamesLauncherSettings { pub enabled: bool, - pub location: Option, - pub launcher_exe : Option, - - #[cfg(target_family = "unix")] - pub create_symlinks: bool, - pub safe_launch: Vec, } diff --git a/src/ui/ui_settings.rs b/src/ui/ui_settings.rs index 5434cf6..9b97d20 100644 --- a/src/ui/ui_settings.rs +++ b/src/ui/ui_settings.rs @@ -301,33 +301,6 @@ self.settings.heroic.default_launch_through_heroic{ ui.heading("Epic Games"); ui.checkbox(&mut epic_settings.enabled, "Import from Epic Games"); if epic_settings.enabled { - ui.horizontal(|ui| { - let mut empty_string = "".to_string(); - let epic_location = epic_settings.location.as_mut().unwrap_or(&mut empty_string); - ui.label("Epic Manifests Location: ").on_hover_text( - "The location where Epic stores its manifest files that BoilR needs to read", - ); - if ui.text_edit_singleline(epic_location).changed() { - epic_settings.location = Some(epic_location.to_string()); - } - }); - 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"); - if ui.text_edit_singleline(epic_location).changed() { - if epic_location.is_empty() { - epic_settings.launcher_exe = None; - } else { - epic_settings.launcher_exe = Some(epic_location.to_string()); - } - } - }); - let safe_mode_header = match epic_settings.safe_launch.len() { 0 => "Force games to launch through Epic Launcher".to_string(), 1 => "One game forced to launch through Epic Launcher".to_string(),