diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index cc594d0..b1aa0dd 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -100,9 +100,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +checksum = "16c74e56284d2188cabb6ad99603d1ace887a5d7e7b695d01b728155ed9ed427" dependencies = [ "concurrent-queue", "event-listener-strategy", @@ -1087,6 +1087,7 @@ dependencies = [ "serde", "serde_json", "serde_repr", + "sysinfo", "tauri", "tauri-build", "tauri-plugin-cors-fetch", @@ -1095,6 +1096,7 @@ dependencies = [ "tauri-plugin-shell", "tauri-plugin-single-instance", "tauri-plugin-sql", + "winapi", "winreg 0.55.0", ] @@ -2008,6 +2010,17 @@ dependencies = [ "cfb", ] +[[package]] +name = "io-uring" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + [[package]] name = "ipnet" version = "2.11.0" @@ -2429,6 +2442,15 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "num-bigint-dig" version = "0.8.4" @@ -2645,6 +2667,16 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "objc2-io-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a" +dependencies = [ + "libc", + "objc2-core-foundation", +] + [[package]] name = "objc2-io-surface" version = "0.3.1" @@ -4368,6 +4400,20 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "sysinfo" +version = "0.35.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3ffa3e4ff2b324a57f7aeb3c349656c7b127c3c189520251a648102a92496e" +dependencies = [ + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "windows", +] + [[package]] name = "system-configuration" version = "0.6.1" @@ -4921,16 +4967,18 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.45.1" +version = "1.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "1140bb80481756a8cbe10541f37433b459c5aa1e727b4c020fbfebdc25bf3ec4" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "pin-project-lite", "signal-hook-registry", + "slab", "socket2", "tokio-macros", "tracing", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 7b092ad..3e8bf3e 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -29,6 +29,8 @@ tauri-plugin-fs = "2.4.0" hardware-id = "0.3.0" winreg = "0.55.0" tauri-plugin-cors-fetch = "4.1.0" +sysinfo = "0.35.2" +winapi = { version = "0.3", features = ["winuser"] } [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] tauri-plugin-single-instance = "2.3.0" diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 4a47c11..2532b73 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -1,8 +1,13 @@ use hardware_id::get_id; use std::path::PathBuf; +use std::process::Command; +use std::thread; +use std::time::Duration; +use sysinfo::System; use winreg::RegKey; use winreg::enums::*; +use crate::utils::get_window_title_by_pid; use crate::utils::set_osu_user_config_value; use crate::utils::{check_folder_completeness, get_osu_config, get_osu_user_config}; @@ -217,3 +222,43 @@ pub fn set_osu_config_value( ) -> Result { set_osu_user_config_value(&osu_folder_path, &key, &value) } + +#[tauri::command] +pub fn run_osu_updater(osu_path: String) -> Result<(), String> { + let mut updater_process = Command::new(osu_path.clone()) + .arg("-repair") + .spawn() + .map_err(|e| e.to_string())?; + + thread::sleep(Duration::from_millis(500)); + + let mut sys = System::new_all(); + sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true); + + let mut termination_thread_running = true; + + while termination_thread_running { + for (_, process) in sys.processes() { + if process.name() == "osu!.exe" { + let process_id = process.pid(); + let window_title = get_window_title_by_pid(process_id); + + if !window_title.is_empty() { + if let Ok(_) = process.kill_and_wait() { + termination_thread_running = false; + break; + } + } + } + } + + if !termination_thread_running { + break; + } + thread::sleep(Duration::from_millis(500)); + } + + updater_process.wait().map_err(|e| e.to_string())?; + + Ok(()) +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 4869b0d..23e2b76 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,11 +1,11 @@ // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ use tauri::Manager; -pub mod utils; pub mod commands; +pub mod utils; use crate::commands::{ find_osu_installation, get_beatmapsets_count, get_hwid, get_osu_release_stream, - get_osu_version, get_skins_count, valid_osu_folder, set_osu_config_value + get_osu_version, get_skins_count, run_osu_updater, set_osu_config_value, valid_osu_folder, }; #[cfg_attr(mobile, tauri::mobile_entry_point)] @@ -31,7 +31,8 @@ pub fn run() { get_skins_count, get_osu_version, get_osu_release_stream, - set_osu_config_value + set_osu_config_value, + run_osu_updater ]) .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_dialog::init()) diff --git a/src-tauri/src/utils.rs b/src-tauri/src/utils.rs index fad1983..228630b 100644 --- a/src-tauri/src/utils.rs +++ b/src-tauri/src/utils.rs @@ -1,5 +1,13 @@ +use std::ffi::OsString; use std::fs; +use std::os::windows::ffi::OsStringExt; use std::path::Path; +use std::ptr; +use sysinfo::Pid; +use winapi::{ + shared::minwindef::LPARAM, + um::winuser::{FindWindowW, GetWindowTextW, GetWindowThreadProcessId}, +}; pub fn check_folder_completeness>(folder_path: P, required_files: &[&str]) -> f32 { let mut found = 0; @@ -112,3 +120,28 @@ pub fn get_osu_config>( return Some(config_map); } + +pub fn get_window_title_by_pid(pid: Pid) -> String { + let mut window_title = String::new(); + + unsafe { + let hwnd = FindWindowW(ptr::null_mut(), ptr::null_mut()); + + if hwnd.is_null() { + return String::new(); + } + + let mut process_id = 0; + GetWindowThreadProcessId(hwnd, &mut process_id); + + if process_id == pid.as_u32() { + let mut title = vec![0u16; 512]; + let length = GetWindowTextW(hwnd, title.as_mut_ptr(), title.len() as i32); + + let title = OsString::from_wide(&title[..length as usize]); + window_title = title.to_string_lossy().into_owned(); + } + } + + window_title +} diff --git a/src/pages/Launch.svelte b/src/pages/Launch.svelte index a5344cb..29e8b3f 100644 --- a/src/pages/Launch.svelte +++ b/src/pages/Launch.svelte @@ -95,9 +95,10 @@ } const validFolder: boolean = await invoke('valid_osu_folder', { folder: selectedPath }); if (!validFolder) { - toast.error( - 'The selected folder is not a valid osu! installation folder. Please select the correct folder.' - ); + toast.error('Oops...', { + description: + 'The selected folder is not a valid osu! installation folder. Please select the correct folder.', + }); return; } osuInstallationPath.set(selectedPath); @@ -121,8 +122,18 @@ }); return; } - launchInfo = 'Looking for updates...'; + launchInfo = 'Validating osu! installation...'; launching = true; + + const validFolder: boolean = await invoke('valid_osu_folder', { folder: $osuInstallationPath }); + if (!validFolder) { + toast.error('Hmmm...', { + description: 'Your selected osu! installation folder is not valid.', + }); + launching = false; + return; + } + try { const streamInfo = await osuapi.latestBuildVersion($osuStream ?? 'stable40'); if (!streamInfo) { @@ -137,6 +148,9 @@ if (versions > 0) { launchInfo = 'Update found!'; + await new Promise((res) => setTimeout(res, 1500)); + launchInfo = 'Running osu! updater...'; + await invoke('run_osu_updater', { folder: $osuInstallationPath }); } else { launchInfo = 'You are up to date!'; }