mirror of
https://github.com/djibux/BoilR.git
synced 2026-09-01 05:53:41 +02:00
Add Ubisoft connect (Linux Proton) support (#346)
* Added Ubisoft connect (Linux Proton) support * Update src/platforms/uplay/platform.rs Co-authored-by: Philip Kristoffersen <philipkristoffersen@gmail.com> * Update src/platforms/uplay/platform.rs Co-authored-by: Philip Kristoffersen <philipkristoffersen@gmail.com> * Fixed needing "find_subsequence". * Update platform.rs * Update platform.rs * Fix trying to use 'None' as a function. --------- Co-authored-by: Ofacy <git@ofacy.com> Co-authored-by: Philip Kristoffersen <philipkristoffersen@gmail.com>
This commit is contained in:
co-authored by
Philip Kristoffersen
Ofacy
parent
7df543f586
commit
c092ffba88
@@ -8,11 +8,18 @@ pub(crate) struct UplayGame {
|
||||
pub(crate) icon: String,
|
||||
pub(crate) id: String,
|
||||
pub(crate) launcher: PathBuf,
|
||||
pub(crate) launcher_compat_folder: Option<PathBuf>,
|
||||
pub(crate) launch_id: u8,
|
||||
}
|
||||
|
||||
impl From<UplayGame> for ShortcutOwned {
|
||||
fn from(game: UplayGame) -> Self {
|
||||
let launch = format!("\"uplay://launch/{}/0\"", game.id);
|
||||
let launch = match game.launcher_compat_folder{
|
||||
Some(compat_folder) => format!
|
||||
("STEAM_COMPAT_DATA_PATH=\"{}\" %command% \"uplay://launch/{}/{}\"",
|
||||
compat_folder.to_string_lossy(), game.id, game.launch_id),
|
||||
None => format!("\"uplay://launch/{}/{}\"", game.id, game.launch_id)
|
||||
};
|
||||
let start_dir = game
|
||||
.launcher
|
||||
.parent()
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
#[cfg(target_os = "windows")]
|
||||
//All of this is technically related to Ubisoft Connnect, not Ubisoft Play.
|
||||
|
||||
use std::path::Path;
|
||||
#[cfg(target_os = "windows")]
|
||||
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 super::{game::UplayGame, settings::UplaySettings};
|
||||
|
||||
@@ -16,10 +21,27 @@ pub struct UplayPlatform {
|
||||
pub settings: UplaySettings,
|
||||
}
|
||||
|
||||
impl NeedsPorton<UplayPlatform> for UplayGame {
|
||||
#[cfg(target_os = "windows")]
|
||||
fn needs_proton(&self, _platform: &UplayPlatform) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
fn needs_proton(&self, _platform: &UplayPlatform) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn create_symlinks(&self, _platform: &UplayPlatform) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn get_uplay_games() -> eyre::Result<Vec<UplayGame>> {
|
||||
#[cfg(target_family = "unix")]
|
||||
{
|
||||
Err(eyre::format_err!("Uplay is not supported on Linux"))
|
||||
get_games_from_proton()
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
@@ -27,8 +49,64 @@ fn get_uplay_games() -> eyre::Result<Vec<UplayGame>> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
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/
|
||||
games_path: PathBuf,
|
||||
//~/.steam/steam/steamapps/compatdata/X
|
||||
compat_folder: Option<PathBuf>,
|
||||
}
|
||||
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
fn get_launcher_path() -> eyre::Result<UplayPathData> {
|
||||
let mut res = UplayPathData::default();
|
||||
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 uplay_exe_path = dir
|
||||
.path()
|
||||
.join("pfx")
|
||||
.join("drive_c")
|
||||
.join("Program Files (x86)")
|
||||
.join("Ubisoft")
|
||||
.join("Ubisoft Game Launcher")
|
||||
.join("upc.exe");
|
||||
|
||||
let uplay_games = dir
|
||||
.path()
|
||||
.join("pfx")
|
||||
.join("drive_c")
|
||||
.join("Program Files (x86)")
|
||||
.join("Ubisoft")
|
||||
.join("Ubisoft Game Launcher")
|
||||
.join("games");
|
||||
|
||||
if uplay_exe_path.exists() && uplay_games.exists() {
|
||||
res.exe_path = uplay_exe_path;
|
||||
res.games_path = uplay_games;
|
||||
res.compat_folder = Some(dir.path());
|
||||
return Ok(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(eyre::eyre!(
|
||||
"Could not find uplay launcher"))
|
||||
}
|
||||
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn get_launcher_path() -> eyre::Result<PathBuf> {
|
||||
fn get_launcher_path() -> eyre::Result<UplayPathData> {
|
||||
let mut res = UplayPathData::default();
|
||||
use winreg::enums::*;
|
||||
use winreg::RegKey;
|
||||
|
||||
@@ -37,7 +115,8 @@ fn get_launcher_path() -> eyre::Result<PathBuf> {
|
||||
let launcher_dir: String = launcher_key.get_value("InstallDir")?;
|
||||
let path = Path::new(&launcher_dir).join("upc.exe");
|
||||
if path.exists() {
|
||||
Ok(path)
|
||||
res.exe_path = path;
|
||||
Ok(res)
|
||||
} else {
|
||||
Err(eyre::eyre!(
|
||||
"Could not find uplay launcher at path {:?}",
|
||||
@@ -54,7 +133,7 @@ fn get_games_from_winreg() -> eyre::Result<Vec<UplayGame>> {
|
||||
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
|
||||
let mut games = vec![];
|
||||
let mut installed_ids = vec![];
|
||||
let launcher_path = get_launcher_path()?;
|
||||
let launcher_path = get_launcher_path()?.exe_path;
|
||||
|
||||
if let Ok(installs) = hklm.open_subkey("SOFTWARE\\WOW6432Node\\Ubisoft\\Launcher\\Installs") {
|
||||
for i in installs.enum_keys().filter_map(|i| i.ok()) {
|
||||
@@ -82,6 +161,8 @@ fn get_games_from_winreg() -> eyre::Result<Vec<UplayGame>> {
|
||||
icon,
|
||||
id,
|
||||
launcher: launcher_path.clone(),
|
||||
launcher_compat_folder: None,
|
||||
launch_id: 0
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -90,6 +171,99 @@ fn get_games_from_winreg() -> eyre::Result<Vec<UplayGame>> {
|
||||
Ok(games)
|
||||
}
|
||||
|
||||
#[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;
|
||||
}
|
||||
splits.push(unsafe {std::str::from_utf8_unchecked(first).to_string()});
|
||||
buffer = second.split_at("version: 2.0".len()).1.to_vec();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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:") {
|
||||
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;
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(games)
|
||||
}
|
||||
|
||||
impl FromSettingsString for UplayPlatform {
|
||||
fn from_settings_string<S: AsRef<str>>(s: S) -> Self {
|
||||
UplayPlatform {
|
||||
|
||||
Reference in New Issue
Block a user