diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index b8068fe..2b3878c 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -68,6 +68,23 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +[[package]] +name = "async-compression" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" +dependencies = [ + "bzip2", + "flate2", + "futures-core", + "futures-io", + "memchr", + "pin-project-lite", + "xz2", + "zstd", + "zstd-safe", +] + [[package]] name = "async-trait" version = "0.1.73" @@ -79,6 +96,23 @@ dependencies = [ "syn 2.0.37", ] +[[package]] +name = "async_zip" +version = "0.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "795310de3218cde15219fc98c1cf7d8fe9db4865aab27fcf1d535d6cb61c6b54" +dependencies = [ + "async-compression", + "chrono", + "crc32fast", + "futures-util", + "log", + "pin-project", + "thiserror", + "tokio", + "tokio-util", +] + [[package]] name = "atk" version = "0.15.1" @@ -218,6 +252,27 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "cairo-rs" version = "0.15.12" @@ -258,6 +313,7 @@ version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ + "jobserver", "libc", ] @@ -1547,6 +1603,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.64" @@ -1598,6 +1663,7 @@ name = "launcher-tauri" version = "0.0.0" dependencies = [ "anyhow", + "async_zip", "directories", "log4rs", "rand 0.8.5", @@ -1608,6 +1674,8 @@ dependencies = [ "tauri", "tauri-build", "tokio", + "tokio-stream", + "tokio-tar", "urlencoding", "uuid", "warp", @@ -1712,6 +1780,17 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "lzma-sys" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "mac" version = "0.1.1" @@ -2614,9 +2693,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.15" +version = "0.38.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f9da0cbd88f9f09e7814e388301c8414c51c62aa6ce1e4b5c551d49d96e531" +checksum = "f25469e9ae0f3d0047ca8b93fc56843f38e6774f0914a107ff8b41be8be8e0b7" dependencies = [ "bitflags 2.4.0", "errno", @@ -2917,9 +2996,9 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b21f559e07218024e7e9f90f96f601825397de0e25420135f7f952453fed0b" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] @@ -3557,6 +3636,21 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tar" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5714c010ca3e5c27114c1cdeb9d14641ace49874aa5626d7149e47aedace75" +dependencies = [ + "filetime", + "futures-core", + "libc", + "redox_syscall 0.3.5", + "tokio", + "tokio-stream", + "xattr", +] + [[package]] name = "tokio-tungstenite" version = "0.20.1" @@ -3577,6 +3671,7 @@ checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -4456,6 +4551,15 @@ dependencies = [ "libc", ] +[[package]] +name = "xz2" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +dependencies = [ + "lzma-sys", +] + [[package]] name = "yaml-rust" version = "0.4.5" @@ -4464,3 +4568,33 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ "linked-hash-map", ] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.8+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +dependencies = [ + "cc", + "libc", + "pkg-config", +] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index f1ad555..3f4319b 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -27,6 +27,9 @@ anyhow = "1.0.66" rand = "0.8.5" directories = "5.0.0" sha256 = "1.4.0" +tokio-tar = "0.3.1" +async_zip = { version = "0.0.15", features = ["full"] } +tokio-stream = "0.1.14" [features] # by default Tauri runs in production mode diff --git a/src-tauri/src/launcher/altarik/mod.rs b/src-tauri/src/launcher/altarik/mod.rs index c5adfdc..d5eeb0a 100644 --- a/src-tauri/src/launcher/altarik/mod.rs +++ b/src-tauri/src/launcher/altarik/mod.rs @@ -1,6 +1,16 @@ -use anyhow::Result; +use std::path::{PathBuf, Path}; + +use anyhow::{Result, bail}; use reqwest::Client; use serde::{Serialize, Deserialize}; +use async_zip::base::read::seek::ZipFileReader; +use tokio::{fs::{OpenOptions, File, self}, sync::mpsc, io::AsyncWriteExt}; +use tokio_tar::Archive; +use tokio_stream::StreamExt; + +use crate::launcher::ProgressMessage; + +use super::{ACTUAL_OS, manifest::OSName}; #[derive(Serialize, Deserialize, Clone)] @@ -63,4 +73,162 @@ impl AltarikManifest { Ok(received) } +} + +impl Chapter { + + + +} + +impl JavaPlatform { + + pub async fn download_java(&self, root_path: &Path, reqwest: &Client, log_channel: mpsc::Sender) -> Result<()> { + let download_path = root_path.join("runtime").join("download"); + let (url, extension) = match ACTUAL_OS { + OSName::Linux => { + (self.linux.clone(), "tar.gz") + }, + OSName::Windows => { + (self.win32.clone(), "zip") + }, + _ => { + bail!("Your current is not supported") + } + }; + let url = match url { + Some(url) => url, + None => bail!("No available executable available for your platform") + }; + + let filepath = download_path.join(format!("{}.{}", url.x64.name.clone(), extension)); + let mut should_download = false; + if !filepath.exists() { + should_download = true; + } else { + let hash = sha256::try_digest(filepath.clone()); + match hash { + Ok(hash) => { + if hash != url.x64.sha256sum { + println!("Hash of java archive is not correct, redownloading"); + should_download = true; + } + }, + Err(_) => should_download = true + } + } + if should_download { + println!("Downloading java"); + if filepath.exists() { + fs::remove_file(filepath.clone()).await?; // remove content before writing and appending to it + } + let mut file = OpenOptions::new() + .write(true) + .append(true) + .create(true) + .open(filepath) + .await?; + let mut res = reqwest.get(url.x64.link.clone()).send().await?; + log_channel.send(ProgressMessage { p_type: String::from("java"), current: 0, total: 100 }).await?; + let length = if let Some(length) = res.content_length() { + length as usize + } else { + 0 + }; + let mut downloaded_size = 0; + while let Some(chunk) = res.chunk().await? { + downloaded_size += chunk.len(); + file.write_all(&chunk).await?; + log_channel.send(ProgressMessage { p_type: String::from("java"), current: if length != 0 { downloaded_size / length } else { 0 }, total: 100 }).await?; + } + println!("{} downloaded", url.x64.name) + } else { + println!("{} already downloaded", url.x64.name) + } + Ok(()) + } + + pub async fn extract_java(&self, root_path: &Path) -> Result<()> { + let (url, extension) = match ACTUAL_OS { + OSName::Linux => { + (self.linux.clone(), "tar.gz") + }, + OSName::Windows => { + (self.win32.clone(), "zip") + }, + _ => { + bail!("Your current is not supported") + } + }; + let url = match url { + Some(url) => url, + None => bail!("No available executable available for your platform") + }; + let archive_path = root_path.join("runtime").join("download").join(format!("{}.{}", url.x64.name, extension)); + let extract_path = root_path.join("runtime"); + // if !extract_path.exists() { + // fs::create_dir(extract_path.clone()).await?; + // } + extract_zip(archive_path, extract_path).await?; + + Ok(()) + } + +} + +impl ModsPack { + + + pub async fn download_mods() { + // TODO + } + + +} + +async fn extract_targz(archive_path: PathBuf, extract_path: PathBuf) -> Result<()> { + let file = File::open(archive_path.clone()).await?; + let mut archive = Archive::new(file); + let mut entries = archive.entries()?; + while let Some(entry) = entries.next().await { + let file = entry?; + println!("{:?}", archive_path); + println!("{}", file.path()?.to_string_lossy().to_string()); + } + Ok(()) +} + +async fn extract_zip(archive_path: PathBuf, extract_path: PathBuf) -> Result<()> { + // TODO add log_channel to send progression to user + let file = File::open(archive_path.clone()).await?; + let mut reader = ZipFileReader::with_tokio(file).await?; + for index in 0..reader.file().entries().len() { + if let Some(entry) = reader.file().entries().get(index) { + let entry = entry.entry(); + // println!("{}", entry.filename().as_str()?); + let filename = entry.filename().as_str()?; + let path = extract_path.join(filename); + if entry.dir()? { + if !path.exists() { + fs::create_dir_all(path).await?; + } + } else { + let mut entry_reader = reader.reader_with_entry(index).await?; + if path.exists() { + fs::remove_file(&path).await?; + } + let mut writer = OpenOptions::new() + .write(true) + .append(true) + .create_new(true) + .open(&path) + .await?; + let mut buf = Vec::with_capacity(1024 * 128); + entry_reader.read_to_end_checked(&mut buf).await?; + writer.write_all(&buf).await?; + } + } + } + + Ok(()) } \ No newline at end of file diff --git a/src-tauri/src/launcher/mod.rs b/src-tauri/src/launcher/mod.rs index 46bc33f..7b259b3 100644 --- a/src-tauri/src/launcher/mod.rs +++ b/src-tauri/src/launcher/mod.rs @@ -6,7 +6,7 @@ use std::path::{Path, self, PathBuf}; use anyhow::{Result, bail}; use reqwest::{Client, StatusCode}; use serde::{Serialize, Deserialize}; -use tokio::{fs::{self, File, OpenOptions}, io::{AsyncWriteExt, AsyncSeekExt}, sync::mpsc}; +use tokio::{fs::{self, File}, io::{AsyncWriteExt, AsyncSeekExt}, sync::mpsc}; use crate::authentification::GameProfile; @@ -113,67 +113,14 @@ impl<'a> MinecraftClient<'_> { let lib = &self.opts.root_path.join("libraries"); let asset = &self.opts.root_path.join("assets").join("objects"); self.save_version_index().await?; - self.download_java(chapter).await?; + chapter.java.platform.download_java(self.opts.root_path, &self.reqwest_client, self.opts.log_channel.clone()).await?; + chapter.java.platform.extract_java(self.opts.root_path).await?; self.download_libraries(lib).await?; self.download_assets(asset).await?; self.opts.log_channel.send(ProgressMessage { p_type: "completed".to_string(), current: 0, total: 0 }).await?; Ok(()) } - async fn download_java(&mut self, chapter: Chapter) -> Result<()> { - let download_path = self.opts.root_path.join("runtime").join("download"); - // let extract_path = self.opts.root_path.join("runtime"); - let (url, extension) = match ACTUAL_OS { - OSName::Linux => { - (chapter.java.platform.linux, "tar.gz") - }, - OSName::Windows => { - (chapter.java.platform.win32, "zip") - }, - _ => { - bail!("Your current is not supported") - } - }; - let url = match url { - Some(url) => url, - None => bail!("No available executable available for your platform") - }; - - let filepath = download_path.join(format!("{}.{}", url.x64.name.clone(), extension)); - let mut should_download = false; - if !filepath.exists() { - should_download = true; - } else { - let hash = sha256::try_digest(filepath.clone()); - match hash { - Ok(hash) => { - if hash != url.x64.sha256sum { - println!("Hash of java archive is not correct, redownloading"); - should_download = true; - } - }, - Err(_) => should_download = true - } - } - if should_download { - println!("Downloading java"); - if filepath.exists() { - fs::remove_file(filepath.clone()).await?; // remove content before writing and appending to it - } - let mut file = OpenOptions::new() - .write(true) - .create(true) - .open(filepath) - .await?; - let b = self.reqwest_client.get(url.x64.link.clone()).send().await?.bytes().await?; - file.write(&b).await?; - println!("{} downloaded", url.x64.name) - } else { - println!("{} already downloaded", url.x64.name) - } - Ok(()) - } - async fn download_libraries(&mut self, lib: &PathBuf) -> Result<()> { self.filter_non_necessary_librairies(); let total = self.details.libraries.len(); diff --git a/yarn.lock b/yarn.lock index e327525..02d61e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -432,16 +432,16 @@ integrity sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ== "@types/react-dom@^18.0.6": - version "18.2.8" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.8.tgz#338f1b0a646c9f10e0a97208c1d26b9f473dffd6" - integrity sha512-bAIvO5lN/U8sPGvs1Xm61rlRHHaq5rp5N3kp9C+NJ/Q41P8iqjkXSu0+/qu8POsjH9pNWb0OYabFez7taP7omw== + version "18.2.10" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.10.tgz#06247cb600e39b63a0a385f6a5014c44bab296f2" + integrity sha512-5VEC5RgXIk1HHdyN1pHlg0cOqnxHzvPGpMMyGAP5qSaDRmyZNDaQ0kkVAkK6NYlDhP6YBID3llaXlmAS/mdgCA== dependencies: "@types/react" "*" "@types/react@*", "@types/react@^18.0.15": - version "18.2.24" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.24.tgz#3c7d68c02e0205a472f04abe4a0c1df35d995c05" - integrity sha512-Ee0Jt4sbJxMu1iDcetZEIKQr99J1Zfb6D4F3qfUWoR1JpInkY1Wdg4WwCyBjL257D0+jGqSl1twBjV8iCaC0Aw== + version "18.2.25" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.25.tgz#99fa44154132979e870ff409dc5b6e67f06f0199" + integrity sha512-24xqse6+VByVLIr+xWaQ9muX1B4bXJKXBbjszbld/UEDslGLY53+ZucF44HCmLbMPejTzGG9XgR+3m2/Wqu1kw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*"