mc_schem/world/
files_reader.rs1use std::borrow::Cow;
2use std::collections::HashMap;
3use std::fs::File;
4use std::io::{Read, Seek};
5use std::ops::{Index, Range};
6use std::path::Path;
7use std::sync::Arc;
8
9use sevenz_rust::SevenZReader;
10
11use crate::error::Error;
12use crate::world::{ArcSlice, FileInfo, FilesInMemory, FilesRead, FolderOnDisk, SubDirectory};
13
14impl ArcSlice {
15    pub fn from(src: Arc<Vec<u8>>) -> Self {
16        let range = 0..src.len();
17        return Self { data_owner: src, range };
18    }
19
20    pub fn clone_from(src: &[u8]) -> Self {
21        let data_owner = Arc::new(src.to_vec());
22        return Self { data_owner, range: 0..src.len() };
23    }
24
25    pub fn slice(&self, range: Range<usize>) -> Self {
26        let start = self.range.start + range.start;
27        let end = start + range.len();
28        assert!(start >= self.range.start);
29        assert!(end <= self.range.end);
30        assert!(start <= end);
31
32        return Self { data_owner: self.data_owner.clone(), range: start..end };
33    }
34
35    pub fn as_slice(&self) -> &[u8] {
36        return &self.data_owner[self.range.clone()];
37    }
38
39    pub fn len(&self) -> usize {
40        return self.range.len();
41    }
42    pub fn is_empty(&self) -> bool {
43        return self.len() == 0;
44    }
45}
46
47impl Index<usize> for ArcSlice {
48    type Output = u8;
49
50    fn index(&self, index: usize) -> &Self::Output {
51        let index = self.range.start + index;
52        return &self.data_owner[index];
53    }
54}
55
56impl Index<Range<usize>> for ArcSlice {
57    type Output = [u8];
58
59    fn index(&self, index: Range<usize>) -> &Self::Output {
60        let start = self.range.start + index.start;
61        let end = start + index.len();
62        assert!(end <= self.range.end);
63        return &self.data_owner[start..end];
64    }
65}
66
67fn impl_sub_dir<'a, T: FilesRead>(src: &'a T, dir: &str) -> SubDirectory<'a> {
68    let mut dir = dir.replace('\\', "/");
69    if !dir.ends_with('/') {
70        dir.push('/');
71    }
72    return SubDirectory {
73        root: src,
74        dirname_with_slash: dir,
75    };
76}
77
78impl FolderOnDisk {
79    pub fn new(path: &str) -> Self {
80        let mut ret = FolderOnDisk { path: path.replace('\\', "/") };
81        if ret.path.ends_with('/') {
82            ret.path.pop();
83        }
84        return ret;
85    }
86}
87
88impl FilesRead for FolderOnDisk {
89    fn sub_directory(&self, dir: &str) -> SubDirectory {
90        return impl_sub_dir(self, dir);
91    }
92
93    fn path(&self) -> String {
94        return self.path.clone();
95    }
96    fn files(&self) -> Vec<FileInfo> {
97        let mut result = Vec::new();
98        for entry in walkdir::WalkDir::new(&self.path) {
99            if let Ok(entry) = entry {
100                let filename: &str;
101                if let Some(f) = entry.file_name().to_str() {
102                    filename = f;
103                } else {
104                    continue;
105                }
106
107                if let Ok(metadata) = entry.metadata() {
108                    if !metadata.is_file() {
109                        continue;
110                    }
111                    let tmp = FileInfo {
112                        name: filename.to_string(),
113                        full_name: filename.to_string(),
114                        size: metadata.len(),
115                    };
116                    result.push(tmp);
117                }
118            }
119        }
120
121        return result;
122    }
123
124    fn open_file(&self, filename: &str) -> Result<Box<dyn Read>, Error> {
125        let filename = format!("{}/{filename}", self.path);
126        return match File::open(filename) {
127            Ok(file) => Ok(Box::new(file)),
128            Err(e) => Err(Error::FileOpenError(e))
129        };
130    }
131
132    fn read_file(&self, filename: &str, dest: &mut Vec<u8>) -> Result<(), Error> {
133        let filename = format!("{}/{filename}", self.path);
134        let mut src = match File::open(&filename) {
135            Ok(file) => file,
136            Err(e) => return Err(Error::FileOpenError(e))
137        };
138
139        let metadata = match std::fs::metadata(filename) {
140            Ok(md) => md,
141            Err(e) => return Err(Error::FileOpenError(e))
142        };
143        dest.reserve(metadata.len() as usize);
144        if let Err(e) = src.read_to_end(dest) {
145            return Err(Error::IOReadError(e));
146        }
147        return Ok(());
148    }
149}
150
151impl FilesInMemory {
152    pub fn from_7z_reader<T: Read + Seek>(mut src: SevenZReader<T>, source: Option<String>) -> Result<FilesInMemory, Error> {
153        let mut result = FilesInMemory {
154            files: HashMap::new(),
155            source: source.unwrap_or("7z file loaded from SevenZReader, filename unknown".to_string()),
156        };
157        let for_each_res = src.for_each_entries(|entry, reader| {
158            let mut vec = Vec::with_capacity(entry.size as usize);
159            match reader.read_to_end(&mut vec) {
160                Ok(_) => {}
161                Err(e) => return Err(sevenz_rust::Error::Io(e, Cow::from("")))
162            }
163
164            result.files.insert(entry.name.clone(), Arc::new(vec));
165            return Ok(true);
166        });
167        if let Err(e7z) = for_each_res {
168            return Err(Error::SevenZipDecompressError(e7z));
169        }
170        return Ok(result);
171    }
172
173    pub fn from_7z_file(path: impl AsRef<Path> + std::fmt::Display, password: &str) -> Result<FilesInMemory, Error> {
174        let filename = path.to_string();
175        let szr = match SevenZReader::open(path, sevenz_rust::Password::from(password)) {
176            Ok(r) => r,
177            Err(e) => return Err(Error::SevenZipDecompressError(e)),
178        };
179        return Self::from_7z_reader(szr, Some(filename));
180    }
181}
182
183impl FilesRead for FilesInMemory {
184    fn sub_directory(&self, dir: &str) -> SubDirectory {
185        return impl_sub_dir(self, dir);
186    }
187
188    fn path(&self) -> String {
189        return self.source.clone();
190    }
191
192    fn files(&self) -> Vec<FileInfo> {
193        let mut vec = Vec::with_capacity(self.files.len());
194        for (name, bytes) in &self.files {
195            vec.push(FileInfo {
196                name: name.clone(),
197                full_name: name.clone(),
198                size: bytes.len() as u64,
199            });
200        }
201        return vec;
202    }
203
204    fn open_file(&self, filename: &str) -> Result<Box<dyn Read + '_>, Error> {
205        return match self.files.get(filename) {
206            Some(bytes) => {
207                Ok(Box::new(bytes.as_slice()))
208            }
209            None => {
210                Err(Error::NoSuchFile {
211                    filename: filename.to_string(),
212                    expected_to_exist_in: self.source.clone(),
213                })
214            }
215        };
216    }
217
218    fn read_file(&self, filename: &str, dest: &mut Vec<u8>) -> Result<(), Error> {
219        return match self.files.get(filename) {
220            Some(bytes) => {
221                dest.resize(bytes.len(), 0);
222                dest.clone_from_slice(&bytes);
223                Ok(())
224            }
225            None => {
226                Err(Error::NoSuchFile {
227                    filename: filename.to_string(),
228                    expected_to_exist_in: self.source.clone(),
229                })
230            }
231        };
232    }
233
234    fn read_file_nocopy(&self, filename: &str) -> Result<Option<ArcSlice>, Error> {
235        return match self.files.get(filename) {
236            Some(bytes) => {
237                Ok(Some(ArcSlice::from(bytes.clone())))
238            }
239            None => {
240                Err(Error::NoSuchFile {
241                    filename: filename.to_string(),
242                    expected_to_exist_in: self.source.clone(),
243                })
244            }
245        };
246    }
247}
248
249impl FilesRead for SubDirectory<'_> {
250    fn sub_directory(&self, dir: &str) -> SubDirectory {
251        let mut new_dir = self.dirname_with_slash.clone();
252        new_dir.push_str(dir);
253        if !new_dir.ends_with('/') {
254            new_dir.push('/');
255        }
256        return SubDirectory {
257            root: self.root,
258            dirname_with_slash: new_dir,
259        }
260    }
261
262    fn path(&self) -> String {
263        let mut ret = self.dirname_with_slash.clone();
264        ret.pop();
265        return ret;
266    }
267
268    fn files(&self) -> Vec<FileInfo> {
269        let src = self.root.files();
270        let mut result = Vec::with_capacity(src.len());
271
272        for mut info in src {
273            if info.name.starts_with(&self.dirname_with_slash) {
274                info.name = info.name[self.dirname_with_slash.len()..info.name.len()].to_string();
275                result.push(info);
276            }
277        }
278
279        return result;
280    }
281
282    fn open_file(&self, filename: &str) -> Result<Box<dyn Read + '_>, Error> {
283        let mut new_filename = self.dirname_with_slash.clone();
284        new_filename.push_str(filename);
285        return self.root.open_file(&new_filename);
286    }
287
288    fn read_file(&self, filename: &str, dest: &mut Vec<u8>) -> Result<(), Error> {
289        let mut new_filename = self.dirname_with_slash.clone();
290        new_filename.push_str(filename);
291        return self.root.read_file(&new_filename, dest);
292    }
293}
294