From e90b025568e298402854f9e6213337bdce7c6fe8 Mon Sep 17 00:00:00 2001
From: alterwain <alterwain@protonmail.com>
Date: Thu, 20 Mar 2025 18:49:16 +0300
Subject: [PATCH] 	modified:   Cargo.toml 	modified:   src/config.rs 
 modified:   src/launcher.rs 	modified:   src/main.rs 	modified:  
 src/minecraft.rs 	modified:   src/util.rs 	modified:  
 src/www/portable.html

---
 Cargo.toml            |   8 +-
 src/config.rs         |  18 +++
 src/launcher.rs       | 358 +++++++++++++++++++++++++++++++++++-------
 src/main.rs           |  12 +-
 src/minecraft.rs      |  47 ++++--
 src/util.rs           |   7 +-
 src/www/portable.html |  10 +-
 7 files changed, 384 insertions(+), 76 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index 9d50065..99655a6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,4 +22,10 @@ log = "0.4.26"
 env_logger = "0.11.7"
 toml = "0.8.20"
 nicotine = { git = "https://gitea.awain.net/alterwain/Nicotine.git", version = "0.1.22" }
-rfd = "0.14"
\ No newline at end of file
+rfd = "0.14"
+
+[profile.release]
+strip = true
+opt-level = "s"
+lto = true
+codegen-units = 1
\ No newline at end of file
diff --git a/src/config.rs b/src/config.rs
index 1528ff9..ebb3fb3 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -49,6 +49,24 @@ impl LauncherConfig {
         }
     }
 
+    pub fn assets_path(&self) -> PathBuf {
+        let mut p = self.launcher_dir();
+        p.push("assets");
+        p
+    }
+
+    pub fn libraries_path(&self) -> PathBuf {
+        let mut p = self.launcher_dir();
+        p.push("libraries");
+        p
+    }
+
+    pub fn instances_path(&self) -> PathBuf {
+        let mut p = self.launcher_dir();
+        p.push("instances");
+        p
+    }
+
     pub fn config_path(&self) -> PathBuf {
         let mut p = self.launcher_dir();
         p.push("config.toml");
diff --git a/src/launcher.rs b/src/launcher.rs
index 804539d..6dd7c1b 100644
--- a/src/launcher.rs
+++ b/src/launcher.rs
@@ -5,12 +5,13 @@ use std::path::PathBuf;
 use base64::Engine;
 use base64::prelude::BASE64_STANDARD;
 use tokio::fs::File;
-use tokio::io::{AsyncBufReadExt, AsyncReadExt, BufReader};
+use tokio::io::{AsyncBufReadExt, BufReader};
 use tokio::process::Command;
 use tokio::sync::mpsc;
 use tokio::sync::mpsc::UnboundedSender;
 use crate::config::{LauncherCredentials, LauncherServer};
 use crate::minecraft;
+use crate::minecraft::multimc::Pack;
 use crate::minecraft::session::SignUpResponse;
 use crate::minecraft::versions::Version;
 use crate::{config::LauncherConfig, minecraft::versions::VersionConfig, util};
@@ -167,8 +168,7 @@ impl Launcher {
 
     pub fn get_instances_list(&self) -> Vec<(String, String, String)> {
         let mut v = Vec::new();
-        let mut instances = self.config.launcher_dir();
-        instances.push("instances");
+        let instances = self.config.instances_path();
         if let Ok(entries) = std::fs::read_dir(instances) {
             for entry in entries {
                 if entry.is_err() { continue; }
@@ -180,7 +180,7 @@ impl Launcher {
                     let config: VersionConfig = serde_json::from_slice(&data).unwrap();
                     let c_type = config.r#type;
                     let c_type = c_type.as_str();
-                    v.push((config.id, c_type.to_string(),  format!("data:image/png;base64,{}", BASE64_STANDARD.encode(match c_type {
+                    v.push((entry.file_name().into_string().unwrap(), c_type.to_string(),  format!("data:image/png;base64,{}", BASE64_STANDARD.encode(match c_type {
                         "old_alpha" => include_bytes!("www/icons/alpha.png").to_vec(),
                         "old_beta" => include_bytes!("www/icons/beta.png").to_vec(),
                         "release" | "snapshot" => include_bytes!("www/icons/release.png").to_vec(),
@@ -194,8 +194,7 @@ impl Launcher {
 
     pub fn get_screenshots(&self) -> Vec<(String, String)> {
         let mut v = Vec::new();
-        let mut instances = self.config.launcher_dir();
-        instances.push("instances");
+        let instances = self.config.instances_path();
         if let Ok(entries) = std::fs::read_dir(instances) {
             for entry in entries {
                 if entry.is_err() { continue; }
@@ -230,18 +229,15 @@ impl Launcher {
             token = server.credentials.password.clone();
         }
         
-        let mut instances = self.config.launcher_dir();
-        instances.push("instances");
+        let mut instances = self.config.instances_path();
         instances.push(&instance_name);
         instances.push("client.json");
 
-        let mut client_jar = self.config.launcher_dir();
-        client_jar.push("instances");
+        let mut client_jar = self.config.instances_path();
         client_jar.push(&instance_name);
         client_jar.push("client.jar");
 
-        let mut instance_dir = self.config.launcher_dir();
-        instance_dir.push("instances");
+        let mut instance_dir = self.config.instances_path();
         instance_dir.push(&instance_name);
         instance_dir.push("data");
         let _ = std::fs::create_dir_all(&instance_dir);
@@ -258,8 +254,7 @@ impl Launcher {
         
         cmd.arg(["-Xmx", &self.config.ram_amount.to_string(), "M"].concat());
 
-        let mut natives_path = self.config.launcher_dir();
-        natives_path.push("instances");
+        let mut natives_path = self.config.instances_path();
         natives_path.push(&instance_name);
         natives_path.push("natives");
 
@@ -267,28 +262,28 @@ impl Launcher {
         cmd.arg(["-Dminecraft.client.jar=", client_jar.to_str().unwrap()].concat());
         cmd.arg("-cp");
 
+        let mut minecraft_arguments = None;
+
         if let Ok(data) = std::fs::read(&instances) {
             let config: VersionConfig = serde_json::from_slice(&data).unwrap();
+            minecraft_arguments = Some(config.minecraftArguments);
             let mut libraries_cmd = Vec::new();
             for library in config.libraries {
                 if let Some(classifier) = &library.downloads.classifiers {
                     if let Some(natives) = &classifier.natives {
                         let rel_path = &natives.path;
-                        let mut libs = self.config.launcher_dir();
-                        libs.push("libraries");
+                        let libs = self.config.libraries_path();
                         let rel_path = [libs.to_str().unwrap(), "\\", &rel_path.replace("/", "\\")].concat();
                         let data = std::fs::read(rel_path).unwrap();
 
                         let _ = zip_extract::extract(Cursor::new(data), &natives_path, true);
                     }
                 } else {
-                    let mut libs = self.config.launcher_dir();
-                    libs.push("libraries");
+                    let mut libs = self.config.libraries_path();
                     libs.push(library.to_pathbuf_file(false));
                     if library.name.contains("com.mojang:authlib") {
                         if let Some(server) = special_server {
-                            let mut patched_auth = self.config.launcher_dir();
-                            patched_auth.push("libraries");
+                            let mut patched_auth = self.config.libraries_path();
                             patched_auth.push(library.to_pathbuf_file(true));
                             let _ = nicotine::patch_jar(libs.to_str().unwrap(), patched_auth.to_str().unwrap(), [b"https://sessionserver.mojang.com/session/minecraft/".as_slice(), b".minecraft.net".as_slice()].as_slice(),  &[&[if self.config.allow_http { "http://" } else { "https://" }, &server.domain, ":", &server.session_server_port.to_string(), "/api/"].concat(), &server.domain]);
                             libraries_cmd.push([patched_auth.to_str().unwrap(), ";"].concat());
@@ -302,14 +297,31 @@ impl Launcher {
             cmd.arg(libraries_cmd.concat());
             cmd.arg(config.mainClass.clone());
 
-            let mut game_dir = self.config.launcher_dir();
-            game_dir.push("instances");
+            let mut game_dir = self.config.instances_path();
             game_dir.push(&instance_name);
             game_dir.push("data");
 
-            let mut assets_dir = self.config.launcher_dir();
-            assets_dir.push("assets");
-            cmd.args(["--username", username, "--version", &instance_name, "--gameDir", game_dir.to_str().unwrap(), "--assetsDir", assets_dir.to_str().unwrap(), "--assetIndex", &config.assetIndex.id, "--uuid", &uuid, "--accessToken", &token, "--userProperties", "{}", "--userType", "mojang", "--width", "925", "--height", "530"]);
+            let mut assets_dir = self.config.assets_path();
+
+            let minecraft_arguments = minecraft_arguments.unwrap();
+            let minecraft_arguments = minecraft_arguments.split(" ");
+            for minecraft_argument in minecraft_arguments {
+                cmd.arg(match minecraft_argument {
+                    "${auth_player_name}" => username,
+                    "${version_name}" => &instance_name,
+                    "${game_directory}" => game_dir.to_str().unwrap(),
+                    "${assets_root}" => assets_dir.to_str().unwrap(),
+                    "${assets_index_name}" => &config.assetIndex.as_ref().unwrap().id,
+                    "${auth_uuid}" => &uuid,
+                    "${auth_access_token}" => &token,
+                    "${user_properties}" => "{}",
+                    "${user_type}" => "mojang",
+                    "${version_type}" => "modified",
+                    _ => minecraft_argument
+                });
+            }
+
+            //cmd.args(["--username", username, "--version", &instance_name, "--gameDir", game_dir.to_str().unwrap(), "--assetsDir", assets_dir.to_str().unwrap(), "--assetIndex", &config.assetIndex.id, "--uuid", &uuid, "--accessToken", &token, "--userProperties", "{}", "--userType", "mojang", "--width", "925", "--height", "530"]);
             assets_dir.push("skins");
             let _ = std::fs::remove_dir_all(assets_dir);
             if let Some(server) = special_server {
@@ -318,6 +330,7 @@ impl Launcher {
                 cmd.arg("--port");
                 cmd.arg(server.port.to_string());
             }
+            
             let mut child = cmd.spawn().unwrap();
 
             tokio::spawn(async move {
@@ -349,37 +362,272 @@ impl Launcher {
     }
 
 
+    pub async fn import_multimc(&self, instance_path: PathBuf, sender: UnboundedSender<(u8, String)>) -> Result<(), Box<dyn Error + Send + Sync>> {
+        let (sx, mut rx) = mpsc::unbounded_channel();
+
+
+        let instance_name = instance_path.file_name().unwrap().to_str().unwrap();
+        let instance_name = [&instance_name[..instance_name.len()-4], "_", &util::random_string(4)].concat();
+        let mut instance_dir = self.config.instances_path();
+        instance_dir.push(&instance_name);
+        let _ = std::fs::create_dir_all(&instance_dir);
+        let data = std::fs::read(instance_path)?;
+        zip_extract::extract(Cursor::new(data), &instance_dir, true)?;
+        let mut multimc_data = instance_dir.clone();
+        multimc_data.push(".minecraft");
+        let mut data_dir = instance_dir.clone();
+        data_dir.push("data");
+        std::fs::rename(multimc_data, data_dir)?;
+
+        let mut pack_path = instance_dir.clone();
+        pack_path.push("mmc-pack.json");
+        println!("pack_path {}", pack_path.to_str().unwrap());
+        let multimc_data = std::fs::read(pack_path)?;
+        let pack_mmc: Pack = serde_json::from_slice(&multimc_data)?;
+
+        let mut minecraft_config = None;
+        let mut forge_version = None;
+
+        for component in pack_mmc.components.iter().filter(|c| c.cachedName.is_some()) {
+            match component.cachedName.as_ref().unwrap().as_str() {
+                "Minecraft" => minecraft_config = Some(crate::minecraft::versions::find_version_object(&component.version).await?),
+                "Forge" => {
+                    forge_version = Some(component.version.clone());
+                }
+                _ => {}
+            }
+        }
+
+        let mut overall_size = 0;
+        let mut cnt = 0;
+
+        let mut client_json_path = self.config.instances_path();
+        client_json_path.push(&instance_name);
+        client_json_path.push("client.json");
+
+        if minecraft_config.is_some() {
+            let config = minecraft_config.clone().unwrap();
+            let config_cl = config.clone();
+            
+            overall_size = config.downloads.as_ref().unwrap().client.size as usize;
+            cnt = 0;
+
+            let client_jar_url = config.downloads.as_ref().unwrap().client.url.clone();
+
+            let mut client_jar_path = self.config.instances_path();
+            client_jar_path.push(&instance_name);
+            client_jar_path.push("client.jar");
+
+            std::fs::write(&client_json_path, serde_json::to_string_pretty(&config_cl).unwrap())?;
+
+            let _ = util::download_file(&client_jar_url, client_jar_path.to_str().unwrap(), sx.clone(), "Downloading client.jar", false).await;
+            cnt += 1;
+
+            let libraries = self.config.libraries_path();
+
+            for i in 0..config.libraries.len() {
+                let library = &config.libraries[i];
+                if let Some(artifact) = &library.downloads.artifact {
+                    let mut dl_path = libraries.clone();
+                    let mut dl_pp = libraries.clone();
+                    dl_pp.push(library.to_pathbuf_path());
+                    let _ = std::fs::create_dir_all(dl_pp);
+                    dl_path.push(library.to_pathbuf_file(false));
+                    if File::open(dl_path.to_str().unwrap()).await.is_err() {
+                        overall_size += artifact.size as usize;
+                        let _ = util::download_file(&artifact.url, dl_path.to_str().unwrap(), sx.clone(), "Downloading libraries", false).await;
+                        cnt += 1;
+                    }
+                }
+
+                if let Some(classifiers) = &library.downloads.classifiers {
+                    if let Some(natives) = &classifiers.natives {
+                        let mut dl_path = libraries.clone();
+                        dl_path.push(&natives.path);
+                        let t_p = dl_path.to_str().unwrap().split("/").collect::<Vec<&str>>();
+                        let t_p = t_p[..t_p.len()-1].join("/");
+                        let _ = std::fs::create_dir_all(&t_p);
+                        if File::open(dl_path.to_str().unwrap()).await.is_err() {
+                            overall_size += natives.size as usize;
+                            let _ = util::download_file(&natives.url, dl_path.to_str().unwrap(), sx.clone(), "Downloading natives", false).await;
+                            cnt += 1;
+                        }
+                    }
+                }
+            }
+
+            let assets_path = self.config.assets_path();
+
+            let mut indexes = assets_path.clone();
+            indexes.push("indexes");
+            let _ = std::fs::create_dir_all(indexes);
+
+            let mut objects = assets_path.clone();
+            objects.push("objects");
+            let _ = std::fs::create_dir_all(objects);
+
+            let mut index = assets_path.clone();
+            index.push(config.assetIndex.as_ref().unwrap().to_path());
+
+            let _ = util::download_file(&config.assetIndex.as_ref().unwrap().url, index.to_str().unwrap(), sx.clone(), "Downloading assets indexes", false).await;
+            cnt += 1;
+
+            let asset_index = config.assetIndex.as_ref().unwrap().url.clone();
+
+            overall_size += config.assetIndex.as_ref().unwrap().size as usize;
+            overall_size += config.assetIndex.as_ref().unwrap().totalSize as usize;
+
+            let assets = crate::minecraft::assets::fetch_assets_list(&asset_index).await.unwrap().objects;
+
+            for (_key, asset) in assets {
+                let mut single_object = assets_path.clone();
+                single_object.push(asset.to_path());
+
+                let mut single_object_path = assets_path.clone();
+                single_object_path.push(asset.to_small_path());
+                let _ = std::fs::create_dir_all(single_object_path);
+
+                if File::open(single_object.to_str().unwrap()).await.is_err() {
+                    let _ = util::download_file(&asset.to_url(), single_object.to_str().unwrap(), sx.clone(), "Downloading assets objects", false).await;
+                    cnt += 1;
+                }
+            }
+        }
+
+        if minecraft_config.is_some() && forge_version.is_some() {
+            let mut forge_installer_path = self.config.libraries_path();
+            forge_installer_path.push("forge_installer.jar");
+
+            let mut forge_installer_unpack = self.config.libraries_path();
+            forge_installer_unpack.push("installer_unpacked");
+
+            std::fs::create_dir_all(&forge_installer_unpack)?;
+
+            let forge_installer_url = format!("https://maven.minecraftforge.net/net/minecraftforge/forge/{}-{}/forge-{}-{}-installer.jar", minecraft_config.as_ref().unwrap().id, forge_version.as_ref().unwrap(), minecraft_config.as_ref().unwrap().id, forge_version.as_ref().unwrap());
+
+            let _ = util::download_file(&forge_installer_url, forge_installer_path.to_str().unwrap(), sx.clone(), "Downloading forge installer", true).await;
+            cnt += 1;
+
+            let forge_installer_data = std::fs::read(&forge_installer_path)?;
+
+            zip_extract::extract(Cursor::new(forge_installer_data), &forge_installer_unpack, true)?;
+
+            let mut forge_library_path = self.config.libraries_path();
+            forge_library_path.push("net");
+            forge_library_path.push("minecraftforge");
+            forge_library_path.push("forge");
+            forge_library_path.push(format!("{}-{}", minecraft_config.as_ref().unwrap().id, forge_version.as_ref().unwrap()));
+
+            let mut forge_version_json = forge_installer_unpack.clone();
+            forge_version_json.push("version.json");
+
+            let mut forge_installer_library = forge_installer_unpack.clone();
+            forge_installer_library.push("maven");
+            forge_installer_library.push("net");
+            forge_installer_library.push("minecraftforge");
+            forge_installer_library.push("forge");
+            forge_installer_library.push(format!("{}-{}", minecraft_config.as_ref().unwrap().id, forge_version.as_ref().unwrap()));
+            forge_installer_library.push(format!("forge-{}-{}.jar", minecraft_config.as_ref().unwrap().id, forge_version.as_ref().unwrap()));
+
+            std::fs::create_dir_all(&forge_library_path)?;
+
+            forge_library_path.push(format!("forge-{}-{}.jar", minecraft_config.as_ref().unwrap().id, forge_version.as_ref().unwrap()));
+
+            std::fs::copy(&forge_installer_library, &forge_library_path)?;
+
+            let version_json = std::fs::read(&forge_version_json)?;
+            let version_json: VersionConfig = serde_json::from_slice(&version_json)?;
+
+            let mut edited = minecraft_config.clone().unwrap();
+            edited.mainClass = version_json.mainClass.clone();
+            edited.minecraftArguments = version_json.minecraftArguments;
+            edited.libraries.retain(|l| !version_json.libraries.iter().any(|t| t.name == l.name));
+            for i in 0..version_json.libraries.len() {
+                edited.libraries.push(version_json.libraries[i].clone());
+            }
+
+            std::fs::write(&client_json_path, serde_json::to_string_pretty(&edited).unwrap())?;
+
+            std::fs::remove_dir_all(forge_installer_unpack)?;
+            std::fs::remove_file(forge_installer_path)?;
+
+            let libraries = self.config.libraries_path();
+
+            for i in 0..edited.libraries.len() {
+                let library = &edited.libraries[i];
+                if let Some(artifact) = &library.downloads.artifact {
+                    let mut dl_path = libraries.clone();
+                    let mut dl_pp = libraries.clone();
+                    dl_pp.push(library.to_pathbuf_path());
+                    let _ = std::fs::create_dir_all(dl_pp);
+                    dl_path.push(library.to_pathbuf_file(false));
+                    if File::open(dl_path.to_str().unwrap()).await.is_err() {
+                        overall_size += artifact.size as usize;
+                        let _ = util::download_file(&artifact.url, dl_path.to_str().unwrap(), sx.clone(), "Downloading libraries", false).await;
+                        cnt += 1;
+                    }
+                }
+
+                if let Some(classifiers) = &library.downloads.classifiers {
+                    if let Some(natives) = &classifiers.natives {
+                        let mut dl_path = libraries.clone();
+                        dl_path.push(&natives.path);
+                        let t_p = dl_path.to_str().unwrap().split("/").collect::<Vec<&str>>();
+                        let t_p = t_p[..t_p.len()-1].join("/");
+                        let _ = std::fs::create_dir_all(&t_p);
+                        if File::open(dl_path.to_str().unwrap()).await.is_err() {
+                            overall_size += natives.size as usize;
+                            let _ = util::download_file(&natives.url, dl_path.to_str().unwrap(), sx.clone(), "Downloading natives", false).await;
+                            cnt += 1;
+                        }
+                    }
+                }
+            }
+        }
+
+        tokio::spawn(async move {
+            let mut current_size = 0;
+            let mut current_cnt = 0;
+            while let Some((size, status)) = rx.recv().await {
+                current_size += size;
+                current_cnt += 1;
+                let _ = sender.send((((current_size as f32 / overall_size as f32) * 100.0) as u8, status));
+                if current_cnt >= cnt {
+                    let _ = sender.send((100, "_".to_string()));
+                }
+            }
+        });
+
+        Ok(())
+    }
+
     pub async fn new_vanilla_instance(&mut self, config: VersionConfig, version_object: &Version, sender: UnboundedSender<(u8, String)>) {
         
         let (sx, mut rx) = mpsc::unbounded_channel();
         
-        let root = self.config.launcher_dir();
-        let mut instances = root.clone();
-        instances.push("instances");
+        let mut instances = self.config.instances_path();
         instances.push(&config.id);
 
         let _ = std::fs::create_dir_all(&instances);
 
         instances.push("client.jar");
 
-        let mut overall_size = config.downloads.client.size as usize;
+        let mut overall_size = config.downloads.as_ref().unwrap().client.size as usize;
         let mut cnt = 0;
 
-        let client_jar_url = config.downloads.client.url;
+        let client_jar_url = config.downloads.as_ref().unwrap().client.url.clone();
 
-        let mut client_json_path = root.clone();
-        client_json_path.push("instances");
+        let mut client_json_path = self.config.instances_path();
         client_json_path.push(config.id);
         client_json_path.push("client.json");
 
-        let _ = util::download_file(&version_object.url, client_json_path.to_str().unwrap(), sx.clone(), "Downloading client.json");
+        let _ = util::download_file(&version_object.url, client_json_path.to_str().unwrap(), sx.clone(), "Downloading client.json", false).await;
         cnt += 1;
 
-        let _ = util::download_file(&client_jar_url, instances.to_str().unwrap(), sx.clone(), "Downloading client.jar");
+        let _ = util::download_file(&client_jar_url, instances.to_str().unwrap(), sx.clone(), "Downloading client.jar", false).await;
         cnt += 1;
 
-        let mut libraries = root.clone();
-        libraries.push("libraries");
+        let libraries = self.config.libraries_path();
 
         for i in 0..config.libraries.len() {
             let library = &config.libraries[i];
@@ -391,7 +639,7 @@ impl Launcher {
                 dl_path.push(library.to_pathbuf_file(false));
                 if File::open(dl_path.to_str().unwrap()).await.is_err() {
                     overall_size += artifact.size as usize;
-                    let _ = util::download_file(&artifact.url, dl_path.to_str().unwrap(), sx.clone(), "Downloading libraries");
+                    let _ = util::download_file(&artifact.url, dl_path.to_str().unwrap(), sx.clone(), "Downloading libraries", false).await;
                     cnt += 1;
                 }
             }
@@ -405,15 +653,14 @@ impl Launcher {
                     let _ = std::fs::create_dir_all(&t_p);
                     if File::open(dl_path.to_str().unwrap()).await.is_err() {
                         overall_size += natives.size as usize;
-                        let _ = util::download_file(&natives.url, dl_path.to_str().unwrap(), sx.clone(), "Downloading natives");
+                        let _ = util::download_file(&natives.url, dl_path.to_str().unwrap(), sx.clone(), "Downloading natives", false).await;
                         cnt += 1;
                     }
                 }
             }
         }
 
-        let mut assets_path = root.clone();
-        assets_path.push("assets");
+        let assets_path = self.config.assets_path();
 
         let mut indexes = assets_path.clone();
         indexes.push("indexes");
@@ -424,15 +671,15 @@ impl Launcher {
         let _ = std::fs::create_dir_all(objects);
 
         let mut index = assets_path.clone();
-        index.push(config.assetIndex.to_path());
+        index.push(config.assetIndex.as_ref().unwrap().to_path());
 
-        let _ = util::download_file(&config.assetIndex.url, index.to_str().unwrap(), sx.clone(), "Downloading assets indexes");
+        let _ = util::download_file(&config.assetIndex.as_ref().unwrap().url.clone(), index.to_str().unwrap(), sx.clone(), "Downloading assets indexes", false).await;
         cnt += 1;
 
-        let asset_index = config.assetIndex.url;
+        let asset_index = config.assetIndex.as_ref().unwrap().url.clone();
 
-        overall_size += config.assetIndex.size as usize;
-        overall_size += config.assetIndex.totalSize as usize;
+        overall_size += config.assetIndex.as_ref().unwrap().size as usize;
+        overall_size += config.assetIndex.as_ref().unwrap().totalSize as usize;
 
         let assets = crate::minecraft::assets::fetch_assets_list(&asset_index).await.unwrap().objects;
 
@@ -445,7 +692,7 @@ impl Launcher {
             let _ = std::fs::create_dir_all(single_object_path);
 
             if File::open(single_object.to_str().unwrap()).await.is_err() {
-                let _ = util::download_file(&asset.to_url(), single_object.to_str().unwrap(), sx.clone(), "Downloading assets objects");
+                let _ = util::download_file(&asset.to_url(), single_object.to_str().unwrap(), sx.clone(), "Downloading assets objects", false).await;
                 cnt += 1;
             }
         }
@@ -466,19 +713,14 @@ impl Launcher {
 
     pub fn init_dirs(&self) {
         let root = self.config.launcher_dir();
-        std::fs::create_dir_all(&root);
+        let _ = std::fs::create_dir_all(&root);
         // instances assets libraries config.toml
-        let mut instances = root.clone();
-        instances.push("instances");
+        let instances = self.config.instances_path();
+        let assets = self.config.assets_path();
+        let libraries = self.config.libraries_path();
 
-        let mut assets = root.clone();
-        assets.push("assets");
-
-        let mut libraries = root.clone();
-        libraries.push("libraries");
-
-        std::fs::create_dir_all(&instances);
-        std::fs::create_dir_all(&assets);
-        std::fs::create_dir_all(&libraries);
+        let _ = std::fs::create_dir_all(&instances);
+        let _ = std::fs::create_dir_all(&assets);
+        let _ = std::fs::create_dir_all(&libraries);
     }
 }
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
index c9ee5af..dd0cf3e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,5 @@
+#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
+
 use std::sync::Mutex;
 
 use launcher::Launcher;
@@ -37,7 +39,7 @@ impl ApplicationHandler for App {
   fn resumed(&mut self, event_loop: &ActiveEventLoop) {
     let window = event_loop.create_window(Window::default_attributes().with_inner_size(LogicalSize::new(900, 600)).with_min_inner_size(LogicalSize::new(900, 600)).with_title("XCraft")).unwrap();
     let webview = WebViewBuilder::new()
-      .with_asynchronous_custom_protocol("xcraft".into(), move |wid, request, responder| {
+      .with_asynchronous_custom_protocol("xcraft".into(), move |_wid, request, responder| {
           let uri = request.uri().to_string();
           if let Ok(msg) = serde_json::from_slice(request.body()) {
             let _ = SENDER.lock().unwrap().as_ref().unwrap().send((uri, Some(msg), responder));
@@ -120,6 +122,14 @@ async fn main() {
                             responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: Vec::new() }).unwrap()));
                         }
                     }
+                    "import_multimc" => {
+                        if let Some(instance_path) = FileDialog::new().add_filter("Archive", &["zip"]).pick_file() {
+                            responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["show_loading".to_string(), "sidebar_off".to_string()] }).unwrap()));
+                            if let Err(e) = launcher.import_multimc(instance_path, sx.clone()).await {
+                                println!("Error: {}", e);
+                            }
+                        }
+                    }
                     "download_vanilla" => {
                         let version = params.unwrap().params[0].clone();
                         if let Ok(versions) = crate::minecraft::versions::fetch_versions_list().await {
diff --git a/src/minecraft.rs b/src/minecraft.rs
index b5e9d01..f3d71eb 100644
--- a/src/minecraft.rs
+++ b/src/minecraft.rs
@@ -27,17 +27,18 @@ pub mod versions {
         pub snapshot: String 
     }
 
-    #[derive(Serialize, Deserialize)]
+    #[derive(Serialize, Deserialize, Clone)]
     pub struct VersionConfig {
-        pub assetIndex: ConfigAssetIndex,
+        pub assetIndex: Option<ConfigAssetIndex>,
         pub mainClass: String,
-        pub downloads: ConfigDownloads,
+        pub minecraftArguments: String,
+        pub downloads: Option<ConfigDownloads>,
         pub id: String,
         pub r#type: String,
         pub libraries: Vec<VersionLibrary>
     }
 
-    #[derive(Serialize, Deserialize)]
+    #[derive(Serialize, Deserialize, Clone)]
     pub struct VersionLibrary {
         pub downloads: LibraryDownloads,
         pub name: String,
@@ -82,13 +83,13 @@ pub mod versions {
         }
     }
 
-    #[derive(Serialize, Deserialize)]
+    #[derive(Serialize, Deserialize, Clone)]
     pub struct LibraryClassifiers {
         #[serde(rename = "natives-windows")]
         pub natives: Option<LibraryNatives>
     }
 
-    #[derive(Serialize, Deserialize)]
+    #[derive(Serialize, Deserialize, Clone)]
     pub struct LibraryNatives {
         pub path: String,
         pub sha1: String,
@@ -96,13 +97,13 @@ pub mod versions {
         pub url: String
     }
 
-    #[derive(Serialize, Deserialize)]
+    #[derive(Serialize, Deserialize, Clone)]
     pub struct LibraryDownloads {
         pub artifact: Option<LibraryArtifact>,
         pub classifiers: Option<LibraryClassifiers>
     }
 
-    #[derive(Serialize, Deserialize)]
+    #[derive(Serialize, Deserialize, Clone)]
     pub struct LibraryArtifact {
         pub path: String,
         pub sha1: String, 
@@ -110,19 +111,19 @@ pub mod versions {
         pub url: String
     }
 
-    #[derive(Serialize, Deserialize)]
+    #[derive(Serialize, Deserialize, Clone)]
     pub struct ConfigDownloads {
         pub client: ConfigDownloadsClient
     }
     
-    #[derive(Serialize, Deserialize)]
+    #[derive(Serialize, Deserialize, Clone)]
     pub struct ConfigDownloadsClient {
         pub sha1: String,
         pub size: u64,
         pub url: String
     }
 
-    #[derive(Serialize, Deserialize)]
+    #[derive(Serialize, Deserialize, Clone)]
     pub struct ConfigAssetIndex {
         pub id: String,
         pub sha1: String,
@@ -154,6 +155,14 @@ pub mod versions {
         let resp: VersionConfig = serde_json::from_slice(&resp)?;
         Ok(resp)
     }
+
+    pub async fn find_version_object(version: &str) -> Result<VersionConfig, Box<dyn Error + Send + Sync>> {
+        let versions = fetch_versions_list().await?;
+        let versions = versions.versions;
+        let version = versions.iter().find(|v| v.id == version).unwrap();
+        let config = fetch_version_object(version).await?;
+        Ok(config)
+    }
 }
 
 pub mod session {
@@ -199,6 +208,22 @@ pub mod session {
     }
 }
 
+pub mod multimc {
+    use serde::{Deserialize, Serialize};
+
+    #[derive(Serialize, Deserialize)]
+    pub struct Pack {
+        pub components: Vec<Component>
+    }
+
+    #[derive(Serialize, Deserialize)]
+    pub struct Component {
+        pub cachedName: Option<String>,
+        pub version: String,
+        pub uid: String,
+    }
+}
+
 pub mod server {
     use std::error::Error;
 
diff --git a/src/util.rs b/src/util.rs
index efb0b59..3aeaa57 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -11,11 +11,11 @@ pub fn random_string(len: usize) -> String {
         .collect()
 }
 
-pub fn download_file(url: &str, file_path: &str, sender: UnboundedSender<(usize, String)>, status: &str) -> Result<(), Box<dyn std::error::Error>> {
+pub async fn download_file(url: &str, file_path: &str, sender: UnboundedSender<(usize, String)>, status: &str, join: bool) -> Result<(), Box<dyn std::error::Error>> {
     let url = url.to_string();
     let file_path = file_path.to_string();
     let status = status.to_string();
-    tokio::spawn( async move {
+    let g = tokio::spawn( async move {
         if let Ok(mut res) = surf::get(url).await {
             let mut downloaded = 0;
             let mut buf = vec![0; 8192]; // Buffer for reading chunks
@@ -36,6 +36,9 @@ pub fn download_file(url: &str, file_path: &str, sender: UnboundedSender<(usize,
             let _ = sender.send((0, status.clone()));
         }
     });
+    if join {
+        g.await;
+    }
     Ok(())
 }
 
diff --git a/src/www/portable.html b/src/www/portable.html
index 0e32fab..a16c1e2 100644
--- a/src/www/portable.html
+++ b/src/www/portable.html
@@ -189,8 +189,8 @@
               </button>
               <p class="text-sm text-gray-500 mt-1">No installation required. Saves everything in a local folder.</p>
 
-              <button onclick="installMinecraft()"
-                  class="mt-4 w-full bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded transition">
+              <button
+                  class="mt-4 w-full bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded transition cursor-not-allowed">
                   Install Minecraft
               </button>
               <p class="text-sm text-gray-500 mt-1">Installs Minecraft on your system for long-term use.</p>
@@ -230,7 +230,7 @@
               <div class="mt-4 text-gray-500 text-sm">or</div>
 
               <button 
-                  class="mt-3 w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded transition">
+                  onclick="importMultiMcInstance()" class="mt-3 w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded transition">
                   <i class="fa-solid fa-file-zipper"></i> Add MultiMC Instance
               </button>
 
@@ -598,6 +598,10 @@
               }
           }
 
+          function importMultiMcInstance() {
+            $.get("import_multimc", processParams);
+          }
+
           function uploadSkin() {
             $.post({ url: "upload_skin", data: JSON.stringify({ params: [accountNick, accountDomain] }) }, processParams);
           }