chore: add file downloading for ezpplauncher files, add ui replacement

This commit is contained in:
2025-07-04 14:56:47 +02:00
parent 41608afae2
commit 7e524debb9
11 changed files with 583 additions and 78 deletions

153
src-tauri/Cargo.lock generated
View File

@@ -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"

View File

@@ -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"

View File

@@ -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<UpdateFile>, Vec<UpdateFile>), 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::<Vec<UpdateFile>>()
.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<UpdateFile>,
all_files: Vec<UpdateFile>,
) -> Result<(), String> {
let osu_path = PathBuf::from(folder);
let client = Client::new();
let valid_paths: HashSet<PathBuf> = 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::<HashSet<_>>()
{
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
}

View File

@@ -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())