mirror of
https://github.com/djibux/BoilR.git
synced 2026-09-01 05:53:41 +02:00
338 lines
11 KiB
Rust
338 lines
11 KiB
Rust
use steam_shortcuts_util::{shortcut::ShortcutOwned, shortcuts_to_bytes};
|
|
use tokio::sync::watch::Sender;
|
|
|
|
use crate::{
|
|
egs::EpicPlatform,
|
|
legendary::LegendaryPlatform,
|
|
lutris::lutris_platform::LutrisPlatform,
|
|
platform::Platform,
|
|
settings::Settings,
|
|
steam::{
|
|
get_shortcuts_for_user, get_shortcuts_paths, setup_proton_games, write_collections,
|
|
Collection, ShortcutInfo, SteamUsersInfo,
|
|
},
|
|
steamgriddb::{download_images_for_users, ImageType},
|
|
uplay::Uplay,
|
|
};
|
|
|
|
#[cfg(target_family = "unix")]
|
|
use crate::heroic::HeroicPlatform;
|
|
|
|
#[cfg(target_family = "unix")]
|
|
use crate::flatpak::FlatpakPlatform;
|
|
|
|
use std::error::Error;
|
|
|
|
use crate::{gog::GogPlatform, itch::ItchPlatform, origin::OriginPlatform};
|
|
use std::{fs::File, io::Write, path::Path};
|
|
|
|
pub const BOILR_TAG: &str = "boilr";
|
|
|
|
pub enum SyncProgress {
|
|
NotStarted,
|
|
Starting,
|
|
FoundGames { games_found: usize },
|
|
FindingImages,
|
|
DownloadingImages { to_download: usize },
|
|
Done,
|
|
}
|
|
|
|
pub fn disconnect_shortcut(settings: &Settings, app_id: u32) -> Result<(), String> {
|
|
let mut userinfo_shortcuts = get_shortcuts_paths(&settings.steam)
|
|
.map_err(|e| format!("Getting shortcut paths failed: {e}"))?;
|
|
|
|
for user in userinfo_shortcuts.iter_mut() {
|
|
let mut shortcut_info = get_shortcuts_for_user(user);
|
|
|
|
for shortcut in shortcut_info.shortcuts.iter_mut() {
|
|
if shortcut.app_id == app_id {
|
|
shortcut.dev_kit_game_id = "".to_string();
|
|
shortcut.tags.retain(|s| s != BOILR_TAG);
|
|
}
|
|
}
|
|
save_shortcuts(&shortcut_info.shortcuts, Path::new(&shortcut_info.path));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn run_sync(
|
|
settings: &Settings,
|
|
sender: &mut Option<Sender<SyncProgress>>,
|
|
) -> Result<Vec<SteamUsersInfo>, String> {
|
|
if let Some(sender) = &sender {
|
|
let _ = sender.send(SyncProgress::Starting);
|
|
}
|
|
let mut userinfo_shortcuts = get_shortcuts_paths(&settings.steam)
|
|
.map_err(|e| format!("Getting shortcut paths failed: {e}"))?;
|
|
|
|
let platform_shortcuts = get_platform_shortcuts(settings);
|
|
let all_shortcuts: Vec<ShortcutOwned> = platform_shortcuts
|
|
.iter()
|
|
.flat_map(|s| s.1.clone())
|
|
.filter(|s| !settings.blacklisted_games.contains(&s.app_id))
|
|
.collect();
|
|
if let Some(sender) = &sender {
|
|
let _ = sender.send(SyncProgress::FoundGames {
|
|
games_found: all_shortcuts.len(),
|
|
});
|
|
}
|
|
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();
|
|
|
|
let mut shortcut_info = get_shortcuts_for_user(user);
|
|
println!(
|
|
"Found {} shortcuts for user: {}",
|
|
shortcut_info.shortcuts.len(),
|
|
user.user_id
|
|
);
|
|
|
|
remove_old_shortcuts(&mut shortcut_info);
|
|
|
|
shortcut_info.shortcuts.extend(all_shortcuts.clone());
|
|
|
|
fix_shortcut_icons(
|
|
user,
|
|
&mut shortcut_info.shortcuts,
|
|
settings.steam.optimize_for_big_picture,
|
|
);
|
|
|
|
save_shortcuts(&shortcut_info.shortcuts, Path::new(&shortcut_info.path));
|
|
|
|
if settings.steam.create_collections {
|
|
match write_shortcut_collections(&user.user_id, &platform_shortcuts) {
|
|
Ok(_) => (),
|
|
Err(_e) => eprintln!("Could not write collections, make sure steam is shut down"),
|
|
}
|
|
}
|
|
|
|
let duration = start_time.elapsed();
|
|
println!("Finished synchronizing games in: {:?}", duration);
|
|
}
|
|
|
|
Ok(userinfo_shortcuts)
|
|
}
|
|
|
|
pub async fn download_images(
|
|
settings: &Settings,
|
|
userinfo_shortcuts: &[SteamUsersInfo],
|
|
sender: &mut Option<Sender<SyncProgress>>,
|
|
) {
|
|
if settings.steamgrid_db.enabled {
|
|
if settings.steamgrid_db.prefer_animated {
|
|
println!("downloading animated images");
|
|
download_images_for_users(settings, userinfo_shortcuts, true, sender).await;
|
|
}
|
|
download_images_for_users(settings, userinfo_shortcuts, false, sender).await;
|
|
}
|
|
}
|
|
|
|
pub trait IsBoilRShortcut {
|
|
fn is_boilr_shortcut(&self) -> bool;
|
|
}
|
|
|
|
impl IsBoilRShortcut for ShortcutOwned {
|
|
fn is_boilr_shortcut(&self) -> bool {
|
|
let boilr_tag = BOILR_TAG.to_string();
|
|
self.tags.contains(&boilr_tag) || self.dev_kit_game_id.starts_with(&boilr_tag)
|
|
}
|
|
}
|
|
|
|
fn remove_old_shortcuts(shortcut_info: &mut ShortcutInfo) {
|
|
shortcut_info
|
|
.shortcuts
|
|
.retain(|shortcut| !shortcut.is_boilr_shortcut());
|
|
}
|
|
|
|
fn fix_shortcut_icons(
|
|
user: &SteamUsersInfo,
|
|
shortcuts: &mut Vec<ShortcutOwned>,
|
|
big_picture_mode: bool,
|
|
) {
|
|
let image_folder = Path::new(&user.steam_user_data_folder)
|
|
.join("config")
|
|
.join("grid");
|
|
let image_type = if big_picture_mode {
|
|
ImageType::BigPicture
|
|
} else {
|
|
ImageType::Icon
|
|
};
|
|
|
|
for shortcut in shortcuts {
|
|
if shortcut.is_boilr_shortcut() {
|
|
let app_id = steam_shortcuts_util::app_id_generator::calculate_app_id(
|
|
&shortcut.exe,
|
|
&shortcut.app_name,
|
|
);
|
|
for ext in ["ico", "png", "jpg", "webp"] {
|
|
let path = image_folder.join(image_type.file_name(app_id, ext));
|
|
if path.exists() {
|
|
shortcut.icon = path.to_string_lossy().to_string();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn write_shortcut_collections<S: AsRef<str>>(
|
|
steam_id: S,
|
|
platform_results: &[(String, Vec<ShortcutOwned>)],
|
|
) -> Result<(), Box<dyn Error>> {
|
|
let mut collections = vec![];
|
|
|
|
for (name, shortcuts) in platform_results {
|
|
let game_ids = shortcuts.iter().map(|s| (s.app_id as usize)).collect();
|
|
collections.push(Collection {
|
|
name: name.clone(),
|
|
game_ids,
|
|
});
|
|
}
|
|
println!("Writing {} collections ", collections.len());
|
|
write_collections(steam_id.as_ref(), &collections)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn get_platform_shortcuts(settings: &Settings) -> Vec<(String, Vec<ShortcutOwned>)> {
|
|
let mut platform_results = vec![
|
|
update_platform_shortcuts(&EpicPlatform::new(&settings.epic_games)),
|
|
update_platform_shortcuts(&LegendaryPlatform::new(settings.legendary.clone())),
|
|
update_platform_shortcuts(&ItchPlatform::new(settings.itch.clone())),
|
|
update_platform_shortcuts(&OriginPlatform {
|
|
settings: settings.origin.clone(),
|
|
}),
|
|
update_platform_shortcuts(&GogPlatform {
|
|
settings: settings.gog.clone(),
|
|
}),
|
|
update_platform_shortcuts(&Uplay {
|
|
settings: settings.uplay.clone(),
|
|
}),
|
|
update_platform_shortcuts(&LutrisPlatform {
|
|
settings: settings.lutris.clone(),
|
|
}),
|
|
];
|
|
#[cfg(target_family = "unix")]
|
|
{
|
|
platform_results.push(update_platform_shortcuts(&HeroicPlatform {
|
|
settings: settings.heroic.clone(),
|
|
}));
|
|
|
|
platform_results.push(update_platform_shortcuts(&FlatpakPlatform {
|
|
settings: settings.flatpak.clone(),
|
|
}));
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
{
|
|
platform_results.push(update_platform_shortcuts(&crate::amazon::AmazonPlatform {
|
|
settings: settings.amazon.clone(),
|
|
}));
|
|
}
|
|
platform_results.iter().filter_map(|p| p.clone()).collect()
|
|
}
|
|
|
|
fn save_shortcuts(shortcuts: &[ShortcutOwned], path: &Path) {
|
|
let mut shortcuts_refs = vec![];
|
|
for shortcut in shortcuts {
|
|
shortcuts_refs.push(shortcut.borrow());
|
|
}
|
|
let new_content = shortcuts_to_bytes(&shortcuts_refs);
|
|
match File::create(path) {
|
|
Ok(mut file) => match file.write_all(new_content.as_slice()) {
|
|
Ok(_) => {
|
|
println!("Saved {} shortcuts", shortcuts.len())
|
|
}
|
|
Err(e) => println!(
|
|
"Failed to save shortcuts to {} error: {}",
|
|
path.to_string_lossy(),
|
|
e
|
|
),
|
|
},
|
|
Err(e) => {
|
|
println!(
|
|
"Failed to save shortcuts to {} error: {}",
|
|
path.to_string_lossy(),
|
|
e
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn update_platform_shortcuts<P, T, E>(platform: &P) -> Option<(String, Vec<ShortcutOwned>)>
|
|
where
|
|
P: Platform<T, E>,
|
|
E: std::fmt::Debug + std::fmt::Display,
|
|
T: Into<ShortcutOwned>,
|
|
T: Clone,
|
|
{
|
|
if platform.enabled() {
|
|
if let crate::platform::SettingsValidity::Invalid { reason } = platform.settings_valid() {
|
|
eprintln!(
|
|
"Setting for platform {} are invalid, reason: {}",
|
|
platform.name(),
|
|
reason
|
|
);
|
|
return None;
|
|
}
|
|
|
|
let mut current_shortcuts = vec![];
|
|
|
|
#[cfg(target_family = "unix")]
|
|
if platform.create_symlinks() {
|
|
let name = platform.name();
|
|
super::symlinks::ensure_links_folder_created(name);
|
|
}
|
|
|
|
let shortcuts_to_add_result = platform.get_shortcuts();
|
|
|
|
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.clone().into();
|
|
shortcut_owned.dev_kit_game_id =
|
|
format!("{}-{}", BOILR_TAG, shortcut_owned.app_id);
|
|
shortcuts_to_add_transformed.push((shortcut, shortcut_owned));
|
|
}
|
|
|
|
let shortcuts_to_add = shortcuts_to_add_transformed;
|
|
|
|
println!(
|
|
"Found {} game(s) for platform {}",
|
|
shortcuts_to_add.len(),
|
|
platform.name()
|
|
);
|
|
|
|
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.is_empty() {
|
|
setup_proton_games(shortcuts_to_proton.as_slice());
|
|
}
|
|
|
|
let name = platform.name();
|
|
return Some((name.to_string(), current_shortcuts));
|
|
}
|
|
Err(err) => {
|
|
eprintln!("Error getting shortcuts from platform: {}", platform.name());
|
|
eprintln!("{}", err);
|
|
}
|
|
}
|
|
}
|
|
None
|
|
}
|