diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index b1aa0dd..2876c7f 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -622,7 +622,7 @@ dependencies = [ "bitflags 2.9.1", "core-foundation 0.10.1", "core-graphics-types", - "foreign-types", + "foreign-types 0.5.0", "libc", ] @@ -1084,6 +1084,8 @@ name = "ezpplauncher" version = "0.1.0" dependencies = [ "hardware-id", + "md5", + "reqwest", "serde", "serde_json", "serde_repr", @@ -1096,6 +1098,7 @@ dependencies = [ "tauri-plugin-shell", "tauri-plugin-single-instance", "tauri-plugin-sql", + "tokio", "winapi", "winreg 0.55.0", ] @@ -1158,6 +1161,15 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + [[package]] name = "foreign-types" version = "0.5.0" @@ -1165,7 +1177,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared", + "foreign-types-shared 0.3.1", ] [[package]] @@ -1179,6 +1191,12 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "foreign-types-shared" version = "0.3.1" @@ -1806,6 +1824,22 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.14" @@ -2324,6 +2358,12 @@ dependencies = [ "digest", ] +[[package]] +name = "md5" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0" + [[package]] name = "memchr" version = "2.7.5" @@ -2387,6 +2427,23 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "ndk" version = "0.9.0" @@ -2777,6 +2834,50 @@ dependencies = [ "pathdiff", ] +[[package]] +name = "openssl" +version = "0.10.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -3499,10 +3600,12 @@ dependencies = [ "http-body-util", "hyper", "hyper-rustls", + "hyper-tls", "hyper-util", "js-sys", "log", "mime", + "native-tls", "percent-encoding", "pin-project-lite", "quinn", @@ -3513,6 +3616,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper", "tokio", + "tokio-native-tls", "tokio-rustls", "tokio-util", "tower", @@ -3675,6 +3779,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "schemars" version = "0.8.22" @@ -3732,6 +3845,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.9.1", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "selectors" version = "0.24.0" @@ -4040,7 +4176,7 @@ dependencies = [ "bytemuck", "cfg_aliases", "core-graphics", - "foreign-types", + "foreign-types 0.5.0", "js-sys", "log", "objc2 0.5.2", @@ -4976,6 +5112,7 @@ dependencies = [ "io-uring", "libc", "mio", + "parking_lot", "pin-project-lite", "signal-hook-registry", "slab", @@ -4996,6 +5133,16 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.2" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 3e8bf3e..b33adec 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -31,6 +31,9 @@ winreg = "0.55.0" tauri-plugin-cors-fetch = "4.1.0" sysinfo = "0.35.2" winapi = { version = "0.3", features = ["winuser"] } +reqwest = { version = "0.12.22", features = ["json", "stream"] } +md5 = "0.8.0" +tokio = { version = "1.46.0", features = ["full"] } [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 d589788..47053e6 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -1,9 +1,16 @@ use hardware_id::get_id; +use reqwest::Client; +use serde::{Deserialize, Serialize}; +use std::collections::HashSet; use std::path::PathBuf; use std::process::Command; use std::thread; use std::time::Duration; use sysinfo::System; +use tauri::AppHandle; +use tauri::Emitter; +use tokio::fs; +use tokio::io::AsyncWriteExt; use winreg::RegKey; use winreg::enums::*; @@ -368,3 +375,199 @@ pub fn run_osu(folder: String) -> Result<(), String> { Ok(()) } + +#[derive(Deserialize, Serialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct UpdateFile { + pub name: String, + pub folder: String, + pub url: String, + pub size: usize, + pub md5: String, +} + +#[tauri::command] +pub async fn get_ezpp_launcher_update_files( + folder: String, + update_url: String, +) -> Result<(Vec, Vec), String> { + let osu_path = PathBuf::from(folder); + let client = Client::new(); + + let update_files = client + .patch(update_url) + .header("User-Agent", "EZPPLauncher") + .send() + .await + .map_err(|e| e.to_string())? + .json::>() + .await + .map_err(|e| e.to_string())?; + + let mut files_to_download = Vec::new(); + + for file in &update_files { + let file_path = osu_path.join(&file.folder).join(&file.name); + if file_path.exists() { + let data = fs::read(&file_path).await.map_err(|e| e.to_string())?; + let hash = format!("{:x}", md5::compute(&data)); + if hash.to_lowercase() != file.md5.to_lowercase() { + files_to_download.push(file.clone()); + } + } else { + files_to_download.push(file.clone()); + } + } + + Ok((files_to_download, update_files)) +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct UpdateStatus { + pub file_name: String, + pub downloaded: u64, + pub size: usize, + pub progress: f64, +} + +#[tauri::command] +pub async fn download_ezpp_launcher_update_files( + app: AppHandle, + folder: String, + update_files: Vec, + all_files: Vec, +) -> Result<(), String> { + let osu_path = PathBuf::from(folder); + let client = Client::new(); + + let valid_paths: HashSet = all_files + .iter() + .map(|f| osu_path.join(&f.folder).join(&f.name)) + .collect(); + + for folder in all_files + .iter() + .map(|f| osu_path.join(&f.folder)) + .collect::>() + { + if folder.exists() && folder != osu_path { + let mut dir = fs::read_dir(&folder).await.map_err(|e| e.to_string())?; + while let Some(entry) = dir.next_entry().await.map_err(|e| e.to_string())? { + let path = entry.path(); + if !valid_paths.contains(&path) { + fs::remove_file(&path).await.ok(); + } + } + } + } + + for file in update_files { + let file_path = osu_path.join(&file.folder).join(&file.name); + let parent = file_path.parent().unwrap(); + if !parent.exists() { + fs::create_dir_all(parent) + .await + .map_err(|e| e.to_string())?; + } + + let mut response = client + .get(&file.url) + .send() + .await + .map_err(|e| e.to_string())?; + + let mut file_out = fs::File::create(&file_path) + .await + .map_err(|e| e.to_string())?; + let mut downloaded = 0u64; + + while let Some(chunk) = response.chunk().await.map_err(|e| e.to_string())? { + downloaded += chunk.len() as u64; + file_out + .write_all(&chunk) + .await + .map_err(|e| e.to_string())?; + + // Emit progress to frontend + app.emit( + "download-progress", + UpdateStatus { + file_name: file.name.clone(), + downloaded, + size: file.size, + progress: ((downloaded as f64 / file.size as f64 * 100.0) * 100.0).trunc() + / 100.0, + }, + ) + .unwrap_or_default(); + } + } + + Ok(()) +} + +#[derive(Serialize)] +#[serde(tag = "type", content = "details")] +pub enum ReplaceUIError { + FileNotFound(String), + PermissionDenied(String), + IoError(String), +} + +#[tauri::command] +pub fn replace_ui_files(folder: String, revert: bool) -> Result<(), ReplaceUIError> { + let osu_path = PathBuf::from(folder); + let ezpp_ui = osu_path.join("EZPPLauncher").join("ezpp!ui.dll"); + let osu_ui = osu_path.join("osu!ui.dll"); + let ezpp_gameplay = osu_path.join("EZPPLauncher").join("ezpp!gameplay.dll"); + let osu_gameplay = osu_path.join("osu!gameplay.dll"); + + let osu_ui_bak = osu_path.join("osu!ui.dll.bak"); + let osu_gameplay_bak = osu_path.join("osu!gameplay.dll.bak"); + + let try_rename = |from: &PathBuf, to: &PathBuf| -> Result<(), ReplaceUIError> { + if !from.exists() { + return Err(ReplaceUIError::FileNotFound(from.display().to_string())); + } + std::fs::rename(from, to).map_err(|e| match e.kind() { + std::io::ErrorKind::NotFound => { + ReplaceUIError::FileNotFound(from.display().to_string()) + } + std::io::ErrorKind::PermissionDenied => { + ReplaceUIError::PermissionDenied(from.display().to_string()) + } + _ => ReplaceUIError::IoError(e.to_string()), + }) + }; + + if !revert { + try_rename(&osu_ui, &osu_ui_bak)?; + try_rename(&ezpp_ui, &osu_ui)?; + + try_rename(&osu_gameplay, &osu_gameplay_bak)?; + try_rename(&ezpp_gameplay, &osu_gameplay)?; + } else { + try_rename(&osu_ui, &ezpp_ui)?; + try_rename(&osu_ui_bak, &osu_ui)?; + + try_rename(&osu_gameplay, &ezpp_gameplay)?; + try_rename(&osu_gameplay_bak, &osu_gameplay)?; + } + + Ok(()) +} + +#[tauri::command] +pub fn is_osu_running() -> bool { + let mut sys = System::new_all(); + sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true); + + for process in sys.processes().values() { + if process.name().eq_ignore_ascii_case("osu!.exe") { + return true; + } + } + + false +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 9ef6ecc..33248a5 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -4,9 +4,10 @@ use tauri::Manager; 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, run_osu, run_osu_updater, set_osu_config_values, - set_osu_user_config_values, valid_osu_folder, get_osu_skin + download_ezpp_launcher_update_files, find_osu_installation, get_beatmapsets_count, + get_ezpp_launcher_update_files, get_hwid, get_osu_release_stream, get_osu_skin, + get_osu_version, get_skins_count, replace_ui_files, run_osu, run_osu_updater, + set_osu_config_values, set_osu_user_config_values, valid_osu_folder, is_osu_running }; #[cfg_attr(mobile, tauri::mobile_entry_point)] @@ -36,7 +37,11 @@ pub fn run() { set_osu_user_config_values, run_osu_updater, run_osu, - get_osu_skin + get_osu_skin, + get_ezpp_launcher_update_files, + download_ezpp_launcher_update_files, + replace_ui_files, + is_osu_running ]) .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_dialog::init()) diff --git a/src/lib/config.ts b/src/lib/config.ts index 7b8f342..d5f8028 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -1,7 +1,7 @@ import { exists, mkdir, readTextFile, writeFile } from '@tauri-apps/plugin-fs'; import * as path from '@tauri-apps/api/path'; -import { invoke } from '@tauri-apps/api/core'; import { Crypto } from './crypto'; +import { getHWID } from './osuUtil'; export class Config { private fileName: string; @@ -16,7 +16,7 @@ export class Config { } async init(): Promise { - const hwid: string = (await invoke('get_hwid')) ?? 'recorderinsandybridge'; + const hwid = (await getHWID()) ?? 'recorderinsandybridge'; this.crypto = new Crypto(hwid); diff --git a/src/lib/osuUtil.ts b/src/lib/osuUtil.ts index 364d24e..e83f602 100644 --- a/src/lib/osuUtil.ts +++ b/src/lib/osuUtil.ts @@ -1,4 +1,23 @@ import { invoke } from '@tauri-apps/api/core'; +import type { UpdateFile, UpdateStatus } from './types'; +import { listen } from '@tauri-apps/api/event'; + +const updateUrl = 'https://ez-pp.farm/ezpplauncher'; + +export const getHWID = async () => { + const hwid = await invoke('get_hwid'); + return typeof hwid === 'string' ? hwid : undefined; +}; + +export const isValidOsuFolder = async (folder: string): Promise => { + const result = await invoke('valid_osu_folder', { folder }); + return typeof result === 'boolean' ? result : false; +}; + +export const autoDetectOsuInstallFolder = async () => { + const result = await invoke('find_osu_installation'); + return typeof result === 'string' ? result : undefined; +}; export const setUserConfigValues = async ( osuFolderPath: string, @@ -22,3 +41,71 @@ export const getReleaseStream = async (folder: string) => { const result = await invoke('get_osu_release_stream', { folder }); return typeof result === 'string' ? result : undefined; }; + +export const getVersion = async (folder: string) => { + const result = await invoke('get_osu_version', { folder }); + return typeof result === 'string' ? result : undefined; +}; + +export const getBeatmapSetsCount = async (folder: string) => { + const result = await invoke('get_beatmapsets_count', { + folder, + }); + return typeof result === 'number' ? result : 0; +}; + +export const getSkinsCount = async (folder: string) => { + const result = await invoke('get_skins_count', { + folder, + }); + return typeof result === 'number' ? result : 0; +}; + +export const getSkin = async (folder: string) => { + const result = await invoke('get_osu_skin', { + folder, + }); + + return typeof result === 'string' ? result : 'Default'; +}; + +export const runUpdater = async (folder: string) => await invoke('run_osu_updater', { folder }); +export const runOsu = async (folder: string) => await invoke('run_osu', { folder }); + +export const getEZPPLauncherUpdateFiles = async (folder: string) => { + const result = await invoke('get_ezpp_launcher_update_files', { folder, updateUrl }); + if (typeof result === 'object') { + const [filesToDownload, updateFiles] = result as [UpdateFile[], UpdateFile[]]; + return { + filesToDownload, + updateFiles, + }; + } + + return undefined; +}; + +export const downloadEZPPLauncherUpdateFiles = async ( + folder: string, + updateFiles: UpdateFile[], + allFiles: UpdateFile[], + progressCallback: (file: UpdateStatus) => void +) => { + const downloadStatusListen = await listen('download-progress', (event) => + progressCallback(event.payload as UpdateStatus) + ); + try { + await invoke('download_ezpp_launcher_update_files', { folder, updateFiles, allFiles }); + } finally { + downloadStatusListen(); + } +}; + +export const replaceUIFiles = async (folder: string, revert: boolean) => { + await invoke('replace_ui_files', { folder, revert }); +}; + +export const isOsuRunning = async () => { + const result = await invoke('is_osu_running'); + return typeof result === 'boolean' ? result : false; +}; diff --git a/src/lib/types.ts b/src/lib/types.ts index e06fe95..98c4825 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -119,3 +119,18 @@ export type StreamsResult = { user_count: number; }[]; }; + +export type UpdateFile = { + folder: string; + md5: string; + name: string; + size: number; + url: string; +}; + +export type UpdateStatus = { + fileName: string; + downloaded: number; + size: number; + progress: number; +}; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 4d32c7b..db0b38d 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -81,3 +81,15 @@ export const compareBuildNumbers = (current: string, target: string): number => return -1; } }; + +export const formatBytes = (bytes: number, decimals = 2) => { + if (!bytes) return '0 B'; + + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))}${sizes[i]}`; +}; diff --git a/src/pages/Launch.svelte b/src/pages/Launch.svelte index 5c9f72c..1590377 100644 --- a/src/pages/Launch.svelte +++ b/src/pages/Launch.svelte @@ -37,6 +37,7 @@ import Progress from '@/components/ui/progress/progress.svelte'; import { compareBuildNumbers, + formatBytes, formatTimeReadable, numberHumanReadable, releaseStreamToReadable, @@ -55,7 +56,6 @@ } from '@/userSettings'; import Input from '@/components/ui/input/input.svelte'; import { open } from '@tauri-apps/plugin-dialog'; - import { invoke } from '@tauri-apps/api/core'; import { toast } from 'svelte-sonner'; import Login from './Login.svelte'; import { currentUser, userAuth } from '@/userAuthentication'; @@ -69,10 +69,26 @@ } from '@/gamemode'; import { currentUserInfo } from '@/data'; import { osuapi } from '@/api/osuapi'; - import { getReleaseStream, setConfigValues, setUserConfigValues } from '@/osuUtil'; + import { + downloadEZPPLauncherUpdateFiles, + getBeatmapSetsCount, + getEZPPLauncherUpdateFiles, + getReleaseStream, + getSkin, + getSkinsCount, + getVersion, + isOsuRunning, + isValidOsuFolder, + replaceUIFiles, + runOsu, + runUpdater, + setConfigValues, + setUserConfigValues, + } from '@/osuUtil'; import { getCurrentWindow } from '@tauri-apps/api/window'; let selectedTab = $state('home'); + let progress = $state(-1); let launchInfo = $state(''); let selectedGamemode = $derived( @@ -96,7 +112,7 @@ if (selectedPath === $osuInstallationPath) { return; } - const validFolder: boolean = await invoke('valid_osu_folder', { folder: selectedPath }); + const validFolder = await isValidOsuFolder(selectedPath); if (!validFolder) { toast.error('Oops...', { description: @@ -109,27 +125,29 @@ $userSettings.save(); toast.success('osu! installation path set successfully.'); - const beatmapSetCount: number | null = await invoke('get_beatmapsets_count', { - folder: selectedPath, - }); + const beatmapSetCount: number | null = await getBeatmapSetsCount(selectedPath); if (beatmapSetCount) { beatmapSets.set(beatmapSetCount); } - const skinsCount: number | null = await invoke('get_skins_count', { - folder: selectedPath, - }); + const skinsCount: number | null = await getSkinsCount(selectedPath); if (skinsCount) { skins.set(skinsCount); } - const skin: string = await invoke('get_osu_skin', { - folder: $osuInstallationPath, - }); + const skin: string = await getSkin(selectedPath); currentSkin.set(skin); } }; const launch = async (offline: boolean) => { + const osuRunning = await isOsuRunning(); + if (osuRunning) { + toast.error('Hold on a second!', { + description: + 'osu! is currently running, please exit osu! before launching via EZPPLauncher!', + }); + return; + } if (!$osuBuild) { toast.error('Hmmm...', { description: 'There was an issue detecting your installed osu! version', @@ -137,10 +155,11 @@ return; } const osuPath = $osuInstallationPath; + launchInfo = 'Validating osu! installation...'; launching.set(true); - const validFolder: boolean = await invoke('valid_osu_folder', { folder: osuPath }); + const validFolder = await isValidOsuFolder(osuPath); if (!validFolder) { toast.error('Hmmm...', { description: 'Your selected osu! installation folder is not valid.', @@ -149,6 +168,34 @@ return; } + try { + launchInfo = 'Looking for EZPPLauncher File updates...'; + const updateResult = await getEZPPLauncherUpdateFiles(osuPath); + if (updateResult) { + if (updateResult.filesToDownload.length > 0) { + launchInfo = 'Found EZPPLauncher File updates!'; + await new Promise((res) => setTimeout(res, 1000)); + await downloadEZPPLauncherUpdateFiles( + osuPath, + updateResult.filesToDownload, + updateResult.updateFiles, + (file) => { + progress = file.progress; + launchInfo = `Downloading ${file.fileName}(${formatBytes( + file.downloaded + )}/${formatBytes(file.size)})...`; + } + ); + progress = -1; + } else { + launchInfo = 'EZPPLauncher Files are up to date!'; + await new Promise((res) => setTimeout(res, 1500)); + } + } + } catch (err) { + console.log(err); + } + try { const streamInfo = await osuapi.latestBuildVersion('stable40'); if (!streamInfo) { @@ -181,7 +228,7 @@ ]); osuStream.set('Stable40'); osuBuild.set(`b${streamInfo}`); - await invoke('run_osu_updater', { folder: osuPath }); + await runUpdater(osuPath); launchInfo = 'osu! is now up to date!'; if (forceUpdate) await setConfigValues(osuPath, [ @@ -246,34 +293,27 @@ } await new Promise((res) => setTimeout(res, 1500)); launchInfo = 'Launching osu!...'; + await replaceUIFiles(osuPath, false); await new Promise((res) => setTimeout(res, 1000)); await getCurrentWindow().hide(); - await invoke('run_osu', { folder: osuPath }); + await runOsu(osuPath); launchInfo = 'Cleaning up...'; await getCurrentWindow().show(); + await new Promise((res) => setTimeout(res, 1000)); + await replaceUIFiles(osuPath, true); - const osuReleaseStream: string = await invoke('get_osu_release_stream', { - folder: osuPath, - }); + const osuReleaseStream = await getReleaseStream(osuPath); osuStream.set(osuReleaseStream); - const osuVersion: string = await invoke('get_osu_version', { - folder: osuPath, - }); + const osuVersion = await getVersion(osuPath); osuBuild.set(osuVersion); - const beatmapSetCount: number | null = await invoke('get_beatmapsets_count', { - folder: osuPath, - }); + const beatmapSetCount = await getBeatmapSetsCount(osuPath); if (beatmapSetCount) beatmapSets.set(beatmapSetCount); - const skinCount: number | null = await invoke('get_skins_count', { - folder: osuPath, - }); + const skinCount = await getSkinsCount(osuPath); if (skinCount) skins.set(skinCount); - const skin: string = await invoke('get_osu_skin', { - folder: $osuInstallationPath, - }); + const skin = await getSkin(osuPath); currentSkin.set(skin); launching.set(false); @@ -300,7 +340,7 @@ Launching...
- + {launchInfo}
diff --git a/src/pages/Loading.svelte b/src/pages/Loading.svelte index 26a514b..6e4a0d1 100644 --- a/src/pages/Loading.svelte +++ b/src/pages/Loading.svelte @@ -26,7 +26,14 @@ import { ezppfarm } from '@/api/ezpp'; import { toast } from 'svelte-sonner'; import { currentUserInfo } from '@/data'; - import { invoke } from '@tauri-apps/api/core'; + import { + getBeatmapSetsCount, + getReleaseStream, + getSkin, + getSkinsCount, + getVersion, + isValidOsuFolder, + } from '@/osuUtil'; let ezppLogo: HTMLImageElement; let spinnerCircle: SVGCircleElement; @@ -105,9 +112,7 @@ if (!$firstStartup) { currentLoadingInfo.set('Checking osu installation path...'); - const validFolder: boolean = await invoke('valid_osu_folder', { - folder: $osuInstallationPath, - }); + const validFolder = await isValidOsuFolder($osuInstallationPath); if (!validFolder) { osuInstallationPath.set(''); $userSettings.value('osu_installation_path').del(); @@ -117,29 +122,19 @@ }); } else { currentLoadingInfo.set('Getting osu version...'); - const osuReleaseStream: string = await invoke('get_osu_release_stream', { - folder: $osuInstallationPath, - }); + const osuReleaseStream = await getReleaseStream($osuInstallationPath); osuStream.set(osuReleaseStream); - const osuVersion: string = await invoke('get_osu_version', { - folder: $osuInstallationPath, - }); + const osuVersion = await getVersion($osuInstallationPath); osuBuild.set(osuVersion); currentLoadingInfo.set('Counting beatmapsets...'); - const beatmapSetCount: number | null = await invoke('get_beatmapsets_count', { - folder: $osuInstallationPath, - }); + const beatmapSetCount = await getBeatmapSetsCount($osuInstallationPath); if (beatmapSetCount) beatmapSets.set(beatmapSetCount); currentLoadingInfo.set('Counting skins...'); - const skinCount: number | null = await invoke('get_skins_count', { - folder: $osuInstallationPath, - }); + const skinCount = await getSkinsCount($osuInstallationPath); if (skinCount) skins.set(skinCount); - const skin: string = await invoke('get_osu_skin', { - folder: $osuInstallationPath, - }); + const skin: string = await getSkin($osuInstallationPath); currentSkin.set(skin); } } diff --git a/src/pages/SetupWizard.svelte b/src/pages/SetupWizard.svelte index 0787252..7061263 100644 --- a/src/pages/SetupWizard.svelte +++ b/src/pages/SetupWizard.svelte @@ -4,8 +4,7 @@ import Input from '@/components/ui/input/input.svelte'; import { animate } from 'animejs'; import { onMount } from 'svelte'; - import { fade, scale } from 'svelte/transition'; - import { invoke } from '@tauri-apps/api/core'; + import { fade } from 'svelte/transition'; import { Check, CheckCircle, CircleOff, Settings2 } from 'lucide-svelte'; import { open } from '@tauri-apps/plugin-dialog'; import Checkbox from '@/components/ui/checkbox/checkbox.svelte'; @@ -20,6 +19,15 @@ import { beatmapSets, currentSkin, currentView, osuBuild, osuStream, skins } from '@/global'; import Launch from './Launch.svelte'; import Confetti from 'svelte-confetti'; + import { + autoDetectOsuInstallFolder, + getBeatmapSetsCount, + getReleaseStream, + getSkin, + getSkinsCount, + getVersion, + isValidOsuFolder, + } from '@/osuUtil'; let selectedStep = $state(1); const steps = ['Welcome', 'Locate your osu! Installation', 'Appearance Settings']; @@ -60,7 +68,7 @@ }); if (typeof selectedPath === 'string') { - const validFolder: boolean = await invoke('valid_osu_folder', { folder: selectedPath }); + const validFolder = await isValidOsuFolder(selectedPath); manualSelect = true; if (!validFolder) { manualSelectValid = false; @@ -78,39 +86,29 @@ await $userSettings.save(); osuInstallationPath.set(osuInstallPath); - const beatmapSetCount: number | null = await invoke('get_beatmapsets_count', { - folder: osuInstallPath, - }); + const beatmapSetCount = await getBeatmapSetsCount(osuInstallPath); if (beatmapSetCount) { beatmapSets.set(beatmapSetCount); } - const skinsCount: number | null = await invoke('get_skins_count', { - folder: osuInstallPath, - }); + const skinsCount: number | null = await getSkinsCount(osuInstallPath); if (skinsCount) { skins.set(skinsCount); } - const skin: string = await invoke('get_osu_skin', { - folder: $osuInstallationPath, - }); + const skin: string = await getSkin(osuInstallPath); currentSkin.set(skin); - const osuReleaseStream: string = await invoke('get_osu_release_stream', { - folder: $osuInstallationPath, - }); + const osuReleaseStream = await getReleaseStream(osuInstallPath); osuStream.set(osuReleaseStream); - const osuVersion: string = await invoke('get_osu_version', { - folder: $osuInstallationPath, - }); + const osuVersion = await getVersion(osuInstallPath); osuBuild.set(osuVersion); currentView.set(Launch); }; onMount(async () => { - const osuPath: string | null = await invoke('find_osu_installation'); + const osuPath = await autoDetectOsuInstallFolder(); if (osuPath) { osuInstallPath = osuPath; autoDetectedOsuPath = true;