wormhole_common/installer/
mods.rs

1use std::{
2    fs::{self, File},
3    io,
4    path::PathBuf,
5};
6
7use crate::{
8    instances::{Instance, InstanceMod, KSPGame},
9    mods::spacedock::SpaceDockAPI,
10    util::copy_dir_all,
11};
12
13use rand::{distributions::Alphanumeric, thread_rng, Rng};
14use zip::ZipArchive;
15
16pub struct ModInstaller {
17    pub install_path: PathBuf,
18}
19
20impl ModInstaller {
21    pub fn new(install_path: PathBuf) -> Self {
22        return Self { install_path };
23    }
24
25    pub async fn install_from_spacedock(&self, id: i32) {
26        let api = SpaceDockAPI::new();
27        let url = api.get_mod_download(id).await;
28
29        let tmp_name = thread_rng()
30            .sample_iter(&Alphanumeric)
31            .take(10)
32            .map(char::from)
33            .collect::<String>();
34
35        let response = reqwest::get(url)
36            .await
37            .expect("Could not download the mod!");
38
39        let body = response.bytes().await.expect("Could not read the mod!");
40        let out_path = &self.install_path.join(format!(".{}.mod.zip", tmp_name));
41
42        let mut out_file = File::create(out_path).expect("Could not create the mod file!");
43
44        io::copy(&mut body.as_ref(), &mut out_file).expect("Could not copy the mod to the file!");
45
46        let mod_tmp_path = &self.install_path.join(format!(".{}.mod_tmp", tmp_name));
47
48        if mod_tmp_path.exists() {
49            fs::remove_dir_all(mod_tmp_path).expect("Could not delete the mod tmp folder!");
50        }
51
52        fs::create_dir_all(mod_tmp_path).expect("Could not create the mod tmp folder!");
53
54        let mut zip = ZipArchive::new(out_file).unwrap();
55        let zip_size = zip.len();
56
57        let mut files: Vec<String> = Vec::new();
58
59        for i in 0..zip_size {
60            let file = zip.by_index(i).unwrap();
61            let name = file.name().to_string();
62
63            files.push(name.clone());
64        }
65
66        zip.extract(mod_tmp_path)
67            .expect("Could not extract the mod!");
68
69        fs::remove_file(out_path).expect("Could not delete the mod file!");
70
71        let mod_info = api.get_mod(id).await;
72
73        if let Some(game_id) = mod_info.game_id {
74            if let Some(game) = KSPGame::from_id(game_id) {
75                let instance = Instance::get_active_instance(game.clone());
76
77                let mut instance_mod = InstanceMod {
78                    id: mod_info.id.unwrap(),
79                    name: mod_info.name.unwrap(),
80                    paths: Vec::new(),
81                };
82
83                if game.eq(&KSPGame::KSP2) {
84                    let bep_in_ex_dir = mod_tmp_path.join("BepInEx");
85
86                    if bep_in_ex_dir.exists() {
87                        copy_dir_all(bep_in_ex_dir.clone(), self.install_path.join("BepInEx"))
88                            .expect("Could not move the BepInEx folder!");
89
90                        for file in bep_in_ex_dir.read_dir().unwrap() {
91                            let file = file.unwrap();
92
93                            if file.file_name() == "plugins" || file.file_name() == "config" {
94                                for file2 in file.path().read_dir().unwrap() {
95                                    let file2 = file2.unwrap();
96
97                                    instance_mod.paths.push(
98                                        "BepInEx/".to_string()
99                                            + file.file_name().into_string().unwrap().as_str()
100                                            + "/"
101                                            + file2.file_name().into_string().unwrap().as_str(),
102                                    );
103                                }
104                            }
105
106                            instance_mod.paths.push(
107                                "BepInEx/".to_string()
108                                    + file.file_name().into_string().unwrap().as_str(),
109                            );
110                        }
111                    } else {
112                        copy_dir_all(
113                            mod_tmp_path,
114                            self.install_path.join("BepInEx").join("plugins"),
115                        )
116                        .expect("Could not move the BepInEx folder!");
117
118                        for file in bep_in_ex_dir.read_dir().unwrap() {
119                            let file = file.unwrap();
120
121                            instance_mod.paths.push(
122                                "BepInEx/plugins/".to_string()
123                                    + file.file_name().into_string().unwrap().as_str(),
124                            );
125                        }
126                    }
127                } else {
128                    let mod_contents =
129                        fs::read_dir(mod_tmp_path).expect("Could not read the mod tmp folder!");
130
131                    let files = mod_contents
132                        .filter_map(|entry| entry.ok())
133                        .collect::<Vec<_>>();
134
135                    let files_strs = files
136                        .iter()
137                        .map(|entry| entry.file_name().into_string().unwrap())
138                        .collect::<Vec<_>>();
139
140                    if files_strs.contains(&"GameData".to_string()) {
141                        copy_dir_all(mod_tmp_path, self.install_path.clone())
142                            .expect("Could not move the GameData folder!");
143
144                        for file in mod_tmp_path.read_dir().unwrap() {
145                            let file = file.unwrap();
146
147                            if file.file_name() == "GameData" {
148                                for file2 in file.path().read_dir().unwrap() {
149                                    let file2 = file2.unwrap();
150
151                                    instance_mod.paths.push(
152                                        "GameData/".to_string()
153                                            + file2.file_name().into_string().unwrap().as_str(),
154                                    );
155                                }
156                            }
157
158                            instance_mod
159                                .paths
160                                .push(file.file_name().into_string().unwrap());
161                        }
162                    } else {
163                        copy_dir_all(mod_tmp_path, self.install_path.join("GameData"))
164                            .expect("Could not move the GameData folder!");
165
166                        for file in mod_tmp_path.read_dir().unwrap() {
167                            let file = file.unwrap();
168
169                            instance_mod.paths.push(
170                                "GameData/".to_string()
171                                    + file.file_name().into_string().unwrap().as_str(),
172                            );
173                        }
174                    }
175                }
176
177                if let Some(mut instance) = instance {
178                    instance.mods.push(instance_mod);
179                    instance.save();
180                }
181            }
182        }
183
184        fs::remove_dir_all(mod_tmp_path).expect("Could not delete the mod tmp folder!");
185    }
186}