1use crate::decompression_manager::DecompressionManager;
2use crate::downloader::Downloader;
3use crate::extension::Extension;
4use crate::index::Index;
5use std::io::{Read, Write};
6use std::path::PathBuf;
7
8#[derive(Default, Debug)]
9pub struct File {
10 pub from_archive: bool,
11 pub path: PathBuf,
12 pub data: Option<Vec<u8>>,
13 pub downloaded: bool,
14}
15
16impl File {
17 pub fn load(&mut self) -> std::io::Result<()> {
18 if self.path.exists() {
19 let mut buffer = Vec::<u8>::new();
20 let mut file = std::fs::File::options()
21 .read(true)
22 .open(self.path.clone())?;
23 file.read_to_end(&mut buffer)?;
24 file.flush()?;
25 self.data = Some(buffer);
26 return Ok(());
27 }
28 Err(std::io::Error::new(
29 std::io::ErrorKind::NotFound,
30 format!("File \"{:?}\" not found", self.path),
31 ))
32 }
33
34 pub fn save(&mut self) -> std::io::Result<()> {
35 if !self.downloaded {
36 let mut file = std::fs::File::options()
37 .write(true)
38 .truncate(true)
39 .create(true)
40 .open(self.path.clone())?;
41 match &self.data {
42 Some(data) => {
43 file.write_all(data.as_slice())?;
44 file.flush()?;
45 }
46 None => (),
47 }
48
49 Ok(())
50 } else {
51 println!("You cannot save a downloaded file using the asset manager.");
52 Ok(())
53 }
54 }
55}
56
57#[derive(Debug)]
58pub struct AssetsManager {
59 pub index: Index,
60 pub cache: DecompressionManager,
61 files: Vec<File>,
62 pub extension_list: Vec<Box<dyn Extension>>,
63 compression_formats: Vec<String>,
64 downloader: Downloader,
65}
66
67impl Default for AssetsManager {
68 fn default() -> Self {
69 return Self::new(Index::new("./", ""), DecompressionManager::default());
70 }
71}
72
73impl AssetsManager {
74 pub fn new(index: Index, cache: DecompressionManager) -> Self {
75 Self {
76 index,
77 cache,
78 files: Vec::new(),
79 extension_list: Vec::new(),
80 compression_formats: vec![String::from("zip")],
81 downloader: Downloader::default(),
82 }
83 }
84
85 pub fn move_file(&mut self, origin: &str, target: &str) -> std::io::Result<()> {
86 let origin = self.index.get_path(origin).unwrap();
87 std::fs::copy(origin.clone(), target.clone())?;
88 std::fs::remove_file(origin.clone())?;
89
90 self.index.remove_indexed_file(&origin);
91 let target_path = PathBuf::from(target);
92 if !target_path.exists() {
93 self.index.add_file(target_path);
94 }
95
96 Ok(())
97 }
98
99 pub fn remove_file(&mut self, origin: &str) -> std::io::Result<()> {
100 let origin = self.index.get_path(origin).unwrap();
101 std::fs::remove_file(origin.clone())?;
102 self.index.remove_indexed_file(&origin);
103
104 Ok(())
105 }
106
107 pub fn copy_file(&mut self, origin: &str, target: &str) -> std::io::Result<()> {
108 let origin = self.index.get_path(origin).unwrap();
109 std::fs::copy(origin.clone(), target.clone())?;
110
111 self.index.add_file(PathBuf::from(target));
112
113 Ok(())
114 }
115
116 pub fn create_file(&mut self, path: &str) -> std::io::Result<()> {
117 std::fs::File::options()
118 .create(true)
119 .write(true)
120 .open(path)?;
121 self.index.add_file(PathBuf::from(path));
122
123 Ok(())
124 }
125
126 pub fn remove_process_pass(&mut self, name: &str) {
127 for i in 0..self.extension_list.len() {
128 if self.extension_list[i].get_name() == name {
129 self.extension_list.remove(i);
130 }
131 }
132 }
133
134 pub fn add_compression_formats(&mut self, format: &str) {
135 self.compression_formats.push(String::from(format));
136 }
137
138 pub fn add_extension(&mut self, extension: Box<dyn Extension>) {
139 self.extension_list.push(extension);
140 }
141
142 pub fn load(&mut self, base_path: &str) -> std::io::Result<()> {
143 if (base_path.starts_with("http://") || base_path.starts_with("https://"))
144 && self.downloader.can_download(&base_path)
145 {
146 self.downloader.download_sync(
147 base_path.to_string(),
148 String::from("FastAssetAutoDownload_temp.tmp"),
149 );
150 let downloaded_content = std::fs::read("FastAssetAutoDownload_temp.tmp");
151 match downloaded_content {
152 Err(err) => {
153 println!("Error while reading downloaded file: {}", err);
154 }
155 Ok(content) => {
156 let file_path = PathBuf::from(base_path);
157 let new_file = File {
158 from_archive: false,
159 path: file_path.clone(),
160 data: Some(content),
161 downloaded: true,
162 };
163 self.index.files.push(file_path);
164 self.files.push(new_file);
165 return Ok(());
166 }
167 }
168 }
169
170 let mut path;
171 if !(base_path.contains('\\') || base_path.contains('/')) {
172 path = self.index.get_path(base_path);
173 } else {
174 path = Some(String::from(base_path));
175 }
176
177 for i in 0..self.extension_list.len() {
178 let mut process_pass = self.extension_list.swap_remove(i);
179 if !process_pass.on_load(self, &mut path) {
180 return Ok(());
181 }
182 self.extension_list.insert(i, process_pass);
183 }
184
185 match path {
186 Some(path) => {
187 let path = PathBuf::from(path);
188
189 let mut in_archive: Option<String> = None;
190 let mut path_until_archive = Vec::<String>::new();
191 let mut path_in_archive = Vec::<String>::new();
192 for i in path.components() {
193 let cmp = i.as_os_str().to_string_lossy();
194
195 if in_archive.is_none() {
196 path_until_archive.push(cmp.to_string());
197 } else {
198 path_in_archive.push(cmp.to_string());
199 }
200
201 for fmt in self.compression_formats.iter() {
202 if cmp.ends_with(&format!(".{}", fmt)) {
203 in_archive = Some(i.as_os_str().to_string_lossy().to_string());
204 }
205 }
206 }
207
208 match in_archive {
209 Some(_) => {
210 let mut file = File::default();
211
212 let mut archive = String::new();
213 path_until_archive.iter().for_each(|elem| {
214 archive.push_str(&format!("{}/", elem));
215 });
216 archive.pop();
217 file.from_archive = true;
218
219 let mut path = String::new();
220 path_in_archive.iter().for_each(|elem| {
221 path.push_str(&format!("{}/", elem));
222 });
223 path.pop();
224 file.path = PathBuf::from(path.clone());
225
226 self.cache.load_archive(
227 &archive,
228 Some(vec![&path]),
229 &mut self.extension_list,
230 );
231
232 file.from_archive = true;
233 file.path = PathBuf::from(path);
234 self.files.push(file);
235 }
236 None => {
237 let mut file = File::default();
238 file.path = path;
239 file.load()?;
240 self.files.push(file);
241 }
242 }
243 }
244 None => {}
245 }
246
247 Ok(())
248 }
249
250 pub fn unload(&mut self, mut path: &str, mut cache_decompressed: bool) {
251 for i in 0..self.extension_list.len() {
252 let mut process_pass = self.extension_list.swap_remove(i);
253 if !process_pass.on_unload(self, &mut path, &mut cache_decompressed) {
254 return;
255 }
256 self.extension_list.insert(i, process_pass);
257 }
258
259 for file in self.files.iter_mut() {
260 let file_path = file.path.to_string_lossy();
261 if path == file_path {
262 file.data = None;
263 if file.from_archive {
264 if cache_decompressed {
265 self.cache.cache(&file_path);
266 } else {
267 self.cache.unload(&file_path);
268 }
269 }
270 }
271 }
272 }
273
274 pub fn remove(&mut self, mut path: &str) {
275 for i in 0..self.extension_list.len() {
276 let mut process_pass = self.extension_list.swap_remove(i);
277 if !process_pass.on_remove(self, &mut path) {
278 return;
279 }
280 self.extension_list.insert(i, process_pass);
281 }
282
283 for i in 0..self.files.len() {
284 if i < self.files.len() {
285 let file_path = self.files[i].path.to_string_lossy();
286 if path == file_path {
287 if self.files[i].from_archive {
288 self.cache.remove(path);
289 }
290 self.files.remove(i);
291 }
292 }
293 }
294 }
295
296 pub fn find_file_index(&self, filename: &str) -> Option<usize> {
297 for i in 0..self.files.len() {
298 if self.files[i].path.file_name().unwrap().to_string_lossy() == filename {
299 return Some(i);
300 }
301 }
302 None
303 }
304
305 pub fn find_file_index_using_full_path(&self, path: &str) -> Option<usize> {
306 for i in 0..self.files.len() {
307 if self.files[i].path.to_string_lossy() == path {
308 return Some(i);
309 }
310 }
311 None
312 }
313
314 pub fn get(&mut self, path: &str) -> Option<Vec<u8>> {
315 let in_cache = self.cache.get_data(path);
316 return match in_cache {
317 Some(_) => in_cache,
318 None => {
319 let path = self
320 .index
321 .get_path(path)
322 .expect(&format!("Cannot get path of: {}", path));
323 let index;
324 if path.contains('\\') || path.contains('/') {
325 index = self.find_file_index_using_full_path(path.as_str());
326 } else {
327 index = self.find_file_index(path.as_str());
328 }
329 if index.is_none() {
330 return None;
331 }
332 self.files[index.unwrap()].data.clone()
333 }
334 };
335 }
336
337 pub fn get_ref(&mut self, path: &str) -> Option<&Option<Vec<u8>>> {
338 let path_buf = self.index.get_path(path).unwrap();
339 let is_full_path = path_buf.contains('\\') || path_buf.contains('/');
340 let in_cache = self.cache.get_data_ref(path);
341 match in_cache {
342 Some(_) => return in_cache,
343 None => {
344 for file in self.files.iter() {
345 if is_full_path && file.path.to_string_lossy() == path_buf {
346 return Some(&file.data);
347 }
348 }
349 }
350 }
351 None
352 }
353
354 pub fn get_mut(&mut self, path: &str) -> Option<&mut Option<Vec<u8>>> {
355 let path_buf = self.index.get_path(path).unwrap();
356 let in_cache = self.cache.get_data_mut(path);
357 match in_cache {
358 Some(_) => return in_cache,
359 None => {
360 for file in self.files.iter_mut() {
361 if file.path.to_string_lossy() == path_buf {
362 return Some(&mut file.data);
363 }
364 }
365 }
366 }
367 None
368 }
369
370 pub fn set_data(&mut self, path: &str, new_data: Vec<u8>) {
371 match self.get_mut(path) {
372 Some(data) => {
373 *data = Some(new_data);
374 }
375 None => (),
376 }
377 }
378
379 pub fn have_file(&self, filename: &str) -> bool {
380 for file in self.files.iter() {
381 if file.path.file_name().unwrap().to_string_lossy() == filename {
382 return true;
383 }
384 }
385
386 return self.index.have_file(filename);
387 }
388
389 pub fn save(&mut self, filename: &str) -> std::io::Result<()> {
390 for file in self.files.iter_mut() {
391 if file.path.file_name().unwrap().to_string_lossy() == filename {
392 return file.save();
393 }
394 }
395
396 Ok(())
397 }
398
399 pub fn get_files_matching_regex(&self, regex: &str) -> Vec<PathBuf> {
400 self.index.regex_search(regex)
401 }
402}