wormhole_common/installer/
mods.rs1use 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}