Update cargo toml and refactor uplay (#347)

* Update dependencies

* Conditional imports for linux

* Fix windows warnings

* Sample testconfiguration for uplay

* Refactor uplay code to remove unsafe blocks

* Update version to 1.8.0

* Update cargo-lock for flatpak

* Update flatpak appdata with realse 1.8.0

* Update src/platforms/uplay/platform.rs

Co-authored-by: Ofacy <github@ofacy.com>

---------

Co-authored-by: Ofacy <github@ofacy.com>
This commit is contained in:
Philip Kristoffersen
2023-05-29 15:46:45 +02:00
committed by GitHub
co-authored by Ofacy
parent c092ffba88
commit d5f038a2fa
8 changed files with 8514 additions and 1816 deletions
Generated
+588 -429
View File
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -1,7 +1,7 @@
[package] [package]
edition = "2021" edition = "2021"
name = "boilr" name = "boilr"
version = "1.7.22" version = "1.8.0"
[dependencies] [dependencies]
base64 = "^0.21.0" base64 = "^0.21.0"
@@ -15,7 +15,7 @@ serde_json = "^1.0.91"
steam_shortcuts_util = "^1.1.8" steam_shortcuts_util = "^1.1.8"
steamgriddb_api = "^0.3.1" steamgriddb_api = "^0.3.1"
sysinfo = "^0.27.1" sysinfo = "^0.29.0"
eyre = "^0.6.8" eyre = "^0.6.8"
color-eyre = "^0.6.2" color-eyre = "^0.6.2"
dyn-clone = "^1.0.10" dyn-clone = "^1.0.10"
@@ -29,10 +29,10 @@ features = ["serde"]
version = "^5.4.0" version = "^5.4.0"
[dependencies.eframe] [dependencies.eframe]
version = "^0.20.1" version = "^0.22.0"
[dependencies.egui] [dependencies.egui]
version = "^0.20.1" version = "^0.22.0"
[dependencies.futures] [dependencies.futures]
version = "^0.3.25" version = "^0.3.25"
@@ -62,7 +62,7 @@ version = "^0.7.1"
winres = "^0.1.12" winres = "^0.1.12"
[target."cfg(windows)".dependencies] [target."cfg(windows)".dependencies]
winreg = "^0.10.1" winreg = "^0.50.0"
sqlite = "^0.30.3" sqlite = "^0.30.3"
[features] [features]
+1734 -1279
View File
File diff suppressed because it is too large Load Diff
@@ -26,6 +26,15 @@ https://hughsie.github.io/oars/index.html
--> -->
<content_rating type="oars-1.1" /> <content_rating type="oars-1.1" />
<releases> <releases>
<release version="1.8.0" date="2023-05-29">
<description>
<ul>
<li>Update dependencies</li>
<li>Support for UPlay</li>
</ul>
</description>
</release>
<release version="1.7.22" date="2023-05-15"> <release version="1.7.22" date="2023-05-15">
<description> <description>
<ul> <ul>
+1 -1
View File
@@ -9,7 +9,7 @@ pub(crate) struct UplayGame {
pub(crate) id: String, pub(crate) id: String,
pub(crate) launcher: PathBuf, pub(crate) launcher: PathBuf,
pub(crate) launcher_compat_folder: Option<PathBuf>, pub(crate) launcher_compat_folder: Option<PathBuf>,
pub(crate) launch_id: u8, pub(crate) launch_id: usize,
} }
impl From<UplayGame> for ShortcutOwned { impl From<UplayGame> for ShortcutOwned {
+119 -90
View File
@@ -3,16 +3,12 @@
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::io::Read;
use std::io::BufReader;
use std::fs::File;
use crate::platforms::load_settings; use crate::platforms::load_settings;
use crate::platforms::to_shortcuts_simple; use crate::platforms::to_shortcuts_simple;
use crate::platforms::FromSettingsString; use crate::platforms::FromSettingsString;
use crate::platforms::GamesPlatform; use crate::platforms::GamesPlatform;
use crate::platforms::ShortcutToImport;
use crate::platforms::NeedsPorton; use crate::platforms::NeedsPorton;
use crate::platforms::ShortcutToImport;
use super::{game::UplayGame, settings::UplaySettings}; use super::{game::UplayGame, settings::UplaySettings};
@@ -37,7 +33,6 @@ impl NeedsPorton<UplayPlatform> for UplayGame {
} }
} }
fn get_uplay_games() -> eyre::Result<Vec<UplayGame>> { fn get_uplay_games() -> eyre::Result<Vec<UplayGame>> {
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
{ {
@@ -54,12 +49,13 @@ struct UplayPathData {
//~/.steam/steam/steamapps/compatdata/X/pfx/drive_c/Program Files (x86)/Ubisoft/Ubisoft Game Launcher/upc.exe //~/.steam/steam/steamapps/compatdata/X/pfx/drive_c/Program Files (x86)/Ubisoft/Ubisoft Game Launcher/upc.exe
exe_path: PathBuf, exe_path: PathBuf,
//~/.steam/steam/steamapps/compatdata/X/pfx/drive_c/Program Files (x86)/Ubisoft/Ubisoft Game Launcher/games/ //~/.steam/steam/steamapps/compatdata/X/pfx/drive_c/Program Files (x86)/Ubisoft/Ubisoft Game Launcher/games/
#[cfg(target_family = "unix")]
games_path: PathBuf, games_path: PathBuf,
//~/.steam/steam/steamapps/compatdata/X //~/.steam/steam/steamapps/compatdata/X
#[cfg(target_family = "unix")]
compat_folder: Option<PathBuf>, compat_folder: Option<PathBuf>,
} }
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
fn get_launcher_path() -> eyre::Result<UplayPathData> { fn get_launcher_path() -> eyre::Result<UplayPathData> {
let mut res = UplayPathData::default(); let mut res = UplayPathData::default();
@@ -99,11 +95,9 @@ fn get_launcher_path() -> eyre::Result<UplayPathData> {
} }
} }
} }
Err(eyre::eyre!( Err(eyre::eyre!("Could not find uplay launcher"))
"Could not find uplay launcher"))
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
fn get_launcher_path() -> eyre::Result<UplayPathData> { fn get_launcher_path() -> eyre::Result<UplayPathData> {
let mut res = UplayPathData::default(); let mut res = UplayPathData::default();
@@ -162,7 +156,7 @@ fn get_games_from_winreg() -> eyre::Result<Vec<UplayGame>> {
id, id,
launcher: launcher_path.clone(), launcher: launcher_path.clone(),
launcher_compat_folder: None, launcher_compat_folder: None,
launch_id: 0 launch_id: 0,
}) })
} }
} }
@@ -173,95 +167,107 @@ fn get_games_from_winreg() -> eyre::Result<Vec<UplayGame>> {
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
fn get_games_from_proton() -> eyre::Result<Vec<UplayGame>> { fn get_games_from_proton() -> eyre::Result<Vec<UplayGame>> {
let mut games = vec![];
let launcher_path = get_launcher_path()?; let launcher_path = get_launcher_path()?;
let parent = launcher_path.exe_path.parent().unwrap_or_else(|| Path::new("/")); let parent = launcher_path
let file = File::open(parent .exe_path
.join("cache") .parent()
.join("configuration") .unwrap_or_else(|| Path::new("/"));
.join("configurations"))?; let file = parent
let mut reader = BufReader::new(file); .join("cache")
let mut buffer = Vec::new(); .join("configuration")
.join("configurations");
// Read file into vector. let buffer = std::fs::read(file)?;
reader.read_to_end(&mut buffer)?; let splits = get_file_splits(&buffer);
let configurations = splits.iter().filter(|s| is_valid_game_config(s));
let mut splits: Vec<String> = Vec::new(); let parsed_configurations= configurations.flat_map(|config| parse_game_config(config));
let games = parsed_configurations.map(|game|{
while !buffer.is_empty() { UplayGame{
let game_header = b"version: 2.0"; name:game.shortcut_name.to_string(),
let foundindex: usize = match buffer.windows(game_header.len()).position(|window| window == game_header) { icon: parent.join("data").join("games").join(game.icon_image).to_string_lossy().to_string(),
Some(index) => {index}, id : game.register
None => {break;}, .strip_prefix("HKEY_LOCAL_MACHINE\\SOFTWARE\\Ubisoft\\Launcher\\Installs\\")
}; .unwrap_or_default()
let (mut first, second) = buffer.split_at(foundindex); .strip_suffix("\\InstallDir")
if first.len() >= 14usize { .unwrap_or_default()
first = first.split_at(first.len()-14).0; .to_string(),
launcher : launcher_path.exe_path.clone(),
launcher_compat_folder: launcher_path.compat_folder.clone(),
launch_id: game.launch_id
} }
splits.push(unsafe {std::str::from_utf8_unchecked(first).to_string()}); });
buffer = second.split_at("version: 2.0".len()).1.to_vec(); Ok(games.collect())
} }
struct GameConfig<'a> {
icon_image: &'a str,
shortcut_name: &'a str,
register: &'a str,
launch_id: usize,
}
fn parse_game_config(split: &str) -> Vec<GameConfig> {
let mut res = vec![];
for gameconfig in splits { let mut icon_image = "";
if !gameconfig.contains("executables:") {continue}; let mut shortcut_name = "";
if !gameconfig.contains("online:") {continue}; let mut register = "";
if !gameconfig.contains("shortcut_name:") {continue}; let mut inonline = false;
if !gameconfig.contains("register:") {continue}; let mut launch_id = 0;
for line in split.lines().map(|line| line.trim()) {
let mut inonline = false; if line.starts_with("online:") {
let mut shortcut_name: String = "".to_string(); inonline = true;
let mut game_id: String = "".to_string(); continue;
let mut icon_image: PathBuf = "".into(); }
let mut launch_id = 0; if line.starts_with("offline:") {
for line in gameconfig.split('\n') { break;
let trimed = line.trim(); }
if trimed.starts_with("online:") { if let Some(split) = line.strip_prefix("icon_image: ") {
inonline = true; if split.is_empty() {
continue;
}
if trimed.starts_with("offline:") {
break; break;
} }; // invalid config.
if trimed.starts_with("icon_image: ") { icon_image = split;
let split = trimed.split_at("icon_image: ".len()).1; }
if split.is_empty() {break}; // invalid config. if !inonline {
icon_image = parent.join("data").join("games").join(split); continue;
} };
if !inonline {continue}; if let Some(split) = line.strip_prefix("- shortcut_name: ") {
if trimed.starts_with("- shortcut_name:") { if split.is_empty() {
let split = trimed.split_at("- shortcut_name:".len()).1; break;
if split.is_empty() {break}; // invalid config. }; // invalid config.
shortcut_name = split.to_string(); shortcut_name = split;
continue; continue;
} }
if trimed.starts_with("register: ") { if let Some(split) = line.strip_prefix("register: ") {
let split = trimed.split_at("register: ".len()).1; if split.is_empty() {
if split.is_empty() {break}; // invalid config. break;
game_id = split }; // invalid config.
.strip_prefix("HKEY_LOCAL_MACHINE\\SOFTWARE\\Ubisoft\\Launcher\\Installs\\").unwrap_or_default() register = split;
.strip_suffix("\\InstallDir").unwrap_or_default().to_string(); continue;
continue; }
}
if trimed == "denuvo: yes" { if line == "denuvo: yes" {
games.push(UplayGame { res.push(GameConfig {
name: shortcut_name.clone(), icon_image,
icon: icon_image.to_string_lossy().to_string(), shortcut_name,
id: game_id.clone(), register,
launcher: launcher_path.exe_path.clone(), launch_id,
launcher_compat_folder: launcher_path.compat_folder.clone(), });
launch_id, launch_id += 1;
});
launch_id += 1;
}
} }
} }
Ok(games) res
}
fn is_valid_game_config(config: &str) -> bool {
let requires = ["executables:", "online:", "shortcut_name:", "register:"];
requires.iter().all(|req| config.contains(req))
}
fn get_file_splits(buffer: &[u8]) -> Vec<String> {
let new_string = String::from_utf8_lossy(buffer);
let sections = new_string.split("version: 2.0").map(|s| s.replace('', ""));
sections.collect()
} }
impl FromSettingsString for UplayPlatform { impl FromSettingsString for UplayPlatform {
@@ -298,3 +304,26 @@ impl GamesPlatform for UplayPlatform {
"uplay" "uplay"
} }
} }
#[cfg(test)]
mod test {
use super::*;
#[test]
fn can_parse_configuration_file() {
let content = include_bytes!("testconfiguration");
let splits = get_file_splits(content);
assert_eq!(501, splits.len());
}
#[test]
fn can_parse_into_game_config() {
let content = include_bytes!("testconfiguration");
let splits = get_file_splits(content);
let games:Vec<_> = splits.iter().flat_map(|split| parse_game_config(split)).collect();
assert_eq!(2, games.len());
assert_eq!(Some("For Honor"),games.get(0).map(|h|h.shortcut_name));
assert_eq!(Some("WATCH_DOGS® 2"),games.get(1).map(|h|h.shortcut_name));
}
}
File diff suppressed because it is too large Load Diff
+5 -11
View File
@@ -151,8 +151,9 @@ fn try_get_rename_map() -> Result<HashMap<u32, String>, Box<dyn Error>> {
Ok(deserialized) Ok(deserialized)
} }
#[derive(PartialEq, Clone)] #[derive(PartialEq, Clone, Default)]
enum Menues { enum Menues {
#[default]
Import, Import,
Settings, Settings,
Images, Images,
@@ -160,12 +161,6 @@ enum Menues {
Disconnect, Disconnect,
} }
impl Default for Menues {
fn default() -> Menues {
Menues::Import
}
}
fn create_games_to_sync(rt: &mut Runtime, platforms: &[Box<dyn GamesPlatform>]) -> GamesToSync { fn create_games_to_sync(rt: &mut Runtime, platforms: &[Box<dyn GamesPlatform>]) -> GamesToSync {
let mut to_sync = vec![]; let mut to_sync = vec![];
for platform in platforms { for platform in platforms {
@@ -367,15 +362,14 @@ pub fn run_ui(args: Vec<String>) -> eyre::Result<()>{
vsync: !no_v_sync, vsync: !no_v_sync,
..Default::default() ..Default::default()
}; };
eframe::run_native( let run_result = eframe::run_native(
"BoilR", "BoilR",
native_options, native_options,
Box::new(|cc| { Box::new(|cc| {
setup(&cc.egui_ctx); setup(&cc.egui_ctx);
Box::new(app) Box::new(app)
}), }));
); run_result.map_err(|e| eyre::eyre!("Could not initialize: {:?}", e))
Ok(())
} }
fn is_fullscreen(args: &[String]) -> bool { fn is_fullscreen(args: &[String]) -> bool {