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]
edition = "2021"
name = "boilr"
version = "1.7.22"
version = "1.8.0"
[dependencies]
base64 = "^0.21.0"
@@ -15,7 +15,7 @@ serde_json = "^1.0.91"
steam_shortcuts_util = "^1.1.8"
steamgriddb_api = "^0.3.1"
sysinfo = "^0.27.1"
sysinfo = "^0.29.0"
eyre = "^0.6.8"
color-eyre = "^0.6.2"
dyn-clone = "^1.0.10"
@@ -29,10 +29,10 @@ features = ["serde"]
version = "^5.4.0"
[dependencies.eframe]
version = "^0.20.1"
version = "^0.22.0"
[dependencies.egui]
version = "^0.20.1"
version = "^0.22.0"
[dependencies.futures]
version = "^0.3.25"
@@ -62,7 +62,7 @@ version = "^0.7.1"
winres = "^0.1.12"
[target."cfg(windows)".dependencies]
winreg = "^0.10.1"
winreg = "^0.50.0"
sqlite = "^0.30.3"
[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" />
<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">
<description>
<ul>
+1 -1
View File
@@ -9,7 +9,7 @@ pub(crate) struct UplayGame {
pub(crate) id: String,
pub(crate) launcher: PathBuf,
pub(crate) launcher_compat_folder: Option<PathBuf>,
pub(crate) launch_id: u8,
pub(crate) launch_id: usize,
}
impl From<UplayGame> for ShortcutOwned {
+119 -90
View File
@@ -3,16 +3,12 @@
use std::path::Path;
use std::path::PathBuf;
use std::io::Read;
use std::io::BufReader;
use std::fs::File;
use crate::platforms::load_settings;
use crate::platforms::to_shortcuts_simple;
use crate::platforms::FromSettingsString;
use crate::platforms::GamesPlatform;
use crate::platforms::ShortcutToImport;
use crate::platforms::NeedsPorton;
use crate::platforms::ShortcutToImport;
use super::{game::UplayGame, settings::UplaySettings};
@@ -37,7 +33,6 @@ impl NeedsPorton<UplayPlatform> for UplayGame {
}
}
fn get_uplay_games() -> eyre::Result<Vec<UplayGame>> {
#[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
exe_path: PathBuf,
//~/.steam/steam/steamapps/compatdata/X/pfx/drive_c/Program Files (x86)/Ubisoft/Ubisoft Game Launcher/games/
#[cfg(target_family = "unix")]
games_path: PathBuf,
//~/.steam/steam/steamapps/compatdata/X
#[cfg(target_family = "unix")]
compat_folder: Option<PathBuf>,
}
#[cfg(target_family = "unix")]
fn get_launcher_path() -> eyre::Result<UplayPathData> {
let mut res = UplayPathData::default();
@@ -99,11 +95,9 @@ fn get_launcher_path() -> eyre::Result<UplayPathData> {
}
}
}
Err(eyre::eyre!(
"Could not find uplay launcher"))
Err(eyre::eyre!("Could not find uplay launcher"))
}
#[cfg(target_os = "windows")]
fn get_launcher_path() -> eyre::Result<UplayPathData> {
let mut res = UplayPathData::default();
@@ -162,7 +156,7 @@ fn get_games_from_winreg() -> eyre::Result<Vec<UplayGame>> {
id,
launcher: launcher_path.clone(),
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")]
fn get_games_from_proton() -> eyre::Result<Vec<UplayGame>> {
let mut games = vec![];
let launcher_path = get_launcher_path()?;
let parent = launcher_path.exe_path.parent().unwrap_or_else(|| Path::new("/"));
let file = File::open(parent
.join("cache")
.join("configuration")
.join("configurations"))?;
let mut reader = BufReader::new(file);
let mut buffer = Vec::new();
// Read file into vector.
reader.read_to_end(&mut buffer)?;
let mut splits: Vec<String> = Vec::new();
while !buffer.is_empty() {
let game_header = b"version: 2.0";
let foundindex: usize = match buffer.windows(game_header.len()).position(|window| window == game_header) {
Some(index) => {index},
None => {break;},
};
let (mut first, second) = buffer.split_at(foundindex);
if first.len() >= 14usize {
first = first.split_at(first.len()-14).0;
let parent = launcher_path
.exe_path
.parent()
.unwrap_or_else(|| Path::new("/"));
let file = parent
.join("cache")
.join("configuration")
.join("configurations");
let buffer = std::fs::read(file)?;
let splits = get_file_splits(&buffer);
let configurations = splits.iter().filter(|s| is_valid_game_config(s));
let parsed_configurations= configurations.flat_map(|config| parse_game_config(config));
let games = parsed_configurations.map(|game|{
UplayGame{
name:game.shortcut_name.to_string(),
icon: parent.join("data").join("games").join(game.icon_image).to_string_lossy().to_string(),
id : game.register
.strip_prefix("HKEY_LOCAL_MACHINE\\SOFTWARE\\Ubisoft\\Launcher\\Installs\\")
.unwrap_or_default()
.strip_suffix("\\InstallDir")
.unwrap_or_default()
.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,
}
for gameconfig in splits {
if !gameconfig.contains("executables:") {continue};
if !gameconfig.contains("online:") {continue};
if !gameconfig.contains("shortcut_name:") {continue};
if !gameconfig.contains("register:") {continue};
let mut inonline = false;
let mut shortcut_name: String = "".to_string();
let mut game_id: String = "".to_string();
let mut icon_image: PathBuf = "".into();
let mut launch_id = 0;
for line in gameconfig.split('\n') {
let trimed = line.trim();
if trimed.starts_with("online:") {
inonline = true;
continue;
}
if trimed.starts_with("offline:") {
fn parse_game_config(split: &str) -> Vec<GameConfig> {
let mut res = vec![];
let mut icon_image = "";
let mut shortcut_name = "";
let mut register = "";
let mut inonline = false;
let mut launch_id = 0;
for line in split.lines().map(|line| line.trim()) {
if line.starts_with("online:") {
inonline = true;
continue;
}
if line.starts_with("offline:") {
break;
}
if let Some(split) = line.strip_prefix("icon_image: ") {
if split.is_empty() {
break;
}
if trimed.starts_with("icon_image: ") {
let split = trimed.split_at("icon_image: ".len()).1;
if split.is_empty() {break}; // invalid config.
icon_image = parent.join("data").join("games").join(split);
}
if !inonline {continue};
if trimed.starts_with("- shortcut_name:") {
let split = trimed.split_at("- shortcut_name:".len()).1;
if split.is_empty() {break}; // invalid config.
shortcut_name = split.to_string();
continue;
}
}; // invalid config.
icon_image = split;
}
if !inonline {
continue;
};
if let Some(split) = line.strip_prefix("- shortcut_name: ") {
if split.is_empty() {
break;
}; // invalid config.
shortcut_name = split;
continue;
}
if trimed.starts_with("register: ") {
let split = trimed.split_at("register: ".len()).1;
if split.is_empty() {break}; // invalid config.
game_id = split
.strip_prefix("HKEY_LOCAL_MACHINE\\SOFTWARE\\Ubisoft\\Launcher\\Installs\\").unwrap_or_default()
.strip_suffix("\\InstallDir").unwrap_or_default().to_string();
continue;
}
if let Some(split) = line.strip_prefix("register: ") {
if split.is_empty() {
break;
}; // invalid config.
register = split;
continue;
}
if trimed == "denuvo: yes" {
games.push(UplayGame {
name: shortcut_name.clone(),
icon: icon_image.to_string_lossy().to_string(),
id: game_id.clone(),
launcher: launcher_path.exe_path.clone(),
launcher_compat_folder: launcher_path.compat_folder.clone(),
launch_id,
});
launch_id += 1;
}
if line == "denuvo: yes" {
res.push(GameConfig {
icon_image,
shortcut_name,
register,
launch_id,
});
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 {
@@ -298,3 +304,26 @@ impl GamesPlatform for UplayPlatform {
"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)
}
#[derive(PartialEq, Clone)]
#[derive(PartialEq, Clone, Default)]
enum Menues {
#[default]
Import,
Settings,
Images,
@@ -160,12 +161,6 @@ enum Menues {
Disconnect,
}
impl Default for Menues {
fn default() -> Menues {
Menues::Import
}
}
fn create_games_to_sync(rt: &mut Runtime, platforms: &[Box<dyn GamesPlatform>]) -> GamesToSync {
let mut to_sync = vec![];
for platform in platforms {
@@ -367,15 +362,14 @@ pub fn run_ui(args: Vec<String>) -> eyre::Result<()>{
vsync: !no_v_sync,
..Default::default()
};
eframe::run_native(
let run_result = eframe::run_native(
"BoilR",
native_options,
Box::new(|cc| {
setup(&cc.egui_ctx);
Box::new(app)
}),
);
Ok(())
}));
run_result.map_err(|e| eyre::eyre!("Could not initialize: {:?}", e))
}
fn is_fullscreen(args: &[String]) -> bool {