1pub mod package;
4
5use std::io::{Read, Seek, SeekFrom};
6use std::collections::BTreeMap;
7use std::fs::{File, ReadDir};
8use std::sync::{Arc, Mutex};
9use std::path::PathBuf;
10use std::{fs, io};
11
12use indexmap::IndexMap;
13
14use package::{PackageReader, PackageFileReader};
15
16
17const PACKAGES_DIR_NAME: &'static str = "packages";
19
20
21#[derive(Debug, Clone)]
32pub struct ResFilesystem {
33 shared: Arc<Shared>,
36}
37
38#[derive(Debug)]
40struct Shared {
41 dir_path: PathBuf,
43 mutable: Mutex<SharedMut>,
45}
46
47#[derive(Debug)]
49struct SharedMut {
50 pending_package_path: Vec<PathBuf>,
52 package_reader_cache: IndexMap<PathBuf, PackageReader<File>>,
54 package_open_errors: Vec<(PathBuf, io::Error)>,
57 node_cache: NodeCache,
59}
60
61impl ResFilesystem {
62
63 pub fn new<P: Into<PathBuf>>(dir_path: P) -> io::Result<Self> {
67
68 let dir_path = dir_path.into();
69 let mut pending_package_cache = Vec::new();
70
71 for entry in fs::read_dir(dir_path.join(PACKAGES_DIR_NAME))? {
72
73 let entry = entry?;
74 let entry_type = entry.file_type()?;
75 if !entry_type.is_file() {
76 continue;
77 }
78
79 if !entry.file_name().as_encoded_bytes().ends_with(b".pkg") {
80 continue;
81 }
82
83 pending_package_cache.push(entry.path());
84
85 }
86
87 Ok(Self {
88 shared: Arc::new(Shared {
89 dir_path,
90 mutable: Mutex::new(SharedMut {
91 pending_package_path: pending_package_cache,
92 package_reader_cache: IndexMap::new(),
93 package_open_errors: Vec::new(),
94 node_cache: NodeCache::new(),
95 }),
96 }),
97 })
98
99 }
100
101 pub fn read<P: AsRef<str>>(&self, file_path: P) -> io::Result<ResReadFile> {
103
104 let file_path = file_path.as_ref();
105 if file_path.starts_with('/') {
106 return Err(io::ErrorKind::NotFound.into());
107 }
108
109 let native_file_path = self.shared.dir_path.join(file_path);
110 if native_file_path.is_file() {
111 match File::open(native_file_path) {
112 Ok(file) => return Ok(ResReadFile(ReadFileInner::Native(file))),
113 Err(_) => (), }
115 }
116
117 self.shared.mutable.lock().unwrap()
118 .read(file_path)
119 .map(|reader| ResReadFile(ReadFileInner::Package(reader)))
120
121 }
122
123 pub fn read_dir<P: AsRef<str>>(&self, dir_path: P) -> io::Result<ResReadDir> {
129
130 let dir_path = dir_path.as_ref();
132 if dir_path.starts_with('/') {
133 return Err(io::ErrorKind::NotFound.into());
134 }
135
136 let dir_path = dir_path.strip_suffix('/').unwrap_or(dir_path);
138
139 let native_dir_path = self.shared.dir_path.join(dir_path);
140 let native_read_dir = fs::read_dir(native_dir_path).ok();
141
142 let mut mutable = self.shared.mutable.lock().unwrap();
143 let mut dir_index = None;
144
145 while dir_index.is_none() {
148 if let Some((find_dir_index, _)) = mutable.node_cache.find_dir(dir_path) {
149 dir_index = Some(find_dir_index);
150 } else if !mutable.try_open_pending_package() {
151 if native_read_dir.is_none() {
154 return Err(io::ErrorKind::NotFound.into());
155 } else {
156 break;
157 }
158 }
159 }
160
161 Ok(ResReadDir {
162 dir_path: Arc::from(dir_path),
163 native_read_dir,
164 package_read_dir: dir_index.map(|dir_index| PackageReadDir {
165 shared: Arc::clone(&self.shared),
166 dir_index,
167 remaining_names: Vec::new(),
168 last_children_count: 0,
169 last_children_last_node_index: 0,
170 }),
171 })
172 }
173
174}
175
176impl SharedMut {
177
178 fn try_read(&mut self, file_path: &str) -> io::Result<Option<PackageFileReader<File>>> {
179
180 if let Some((_, file_info)) = self.node_cache.find_file(file_path) {
181
182 let (
183 package_path,
184 package_reader,
185 ) = self.package_reader_cache.get_index_mut(file_info.package_index).unwrap();
186 let mut file_reader = package_reader.read_by_index(file_info.file_index)?;
187
188 return file_reader.try_clone_with(File::open(package_path)?).map(Some);
191
192 } else {
193 Ok(None)
194 }
195
196 }
197
198 fn try_open_pending_package(&mut self) -> bool {
206
207 while let Some(package_path) = self.pending_package_path.pop() {
208
209 let package_file = match File::open(&package_path) {
210 Ok(file) => file,
211 Err(e) => {
212 self.package_open_errors.push((package_path, e));
213 continue;
214 }
215 };
216
217 let package_reader = match PackageReader::new(package_file) {
218 Ok(reader) => reader,
219 Err(e) => {
220 self.package_open_errors.push((package_path, e));
221 continue;
222 }
223 };
224
225 let (
226 package_index,
227 prev_package,
228 ) = self.package_reader_cache.insert_full(package_path, package_reader);
229 debug_assert!(prev_package.is_none(), "duplicate package reader");
230
231 self.node_cache.index_package(package_index, &self.package_reader_cache[package_index]);
232 return true;
238
239
240 }
241
242 false
243
244 }
245
246 fn read(&mut self, file_path: &str) -> io::Result<PackageFileReader<File>> {
248
249 if let Some(file_reader) = self.try_read(file_path)? {
250 return Ok(file_reader);
251 }
252
253 while self.try_open_pending_package() {
255 if let Some(file_reader) = self.try_read(file_path)? {
256 return Ok(file_reader);
257 }
258 }
259
260 Err(io::ErrorKind::NotFound.into())
261
262 }
263
264}
265
266
267#[derive(Debug)]
270pub struct ResReadFile(ReadFileInner);
271
272#[derive(Debug)]
274enum ReadFileInner {
275 Package(PackageFileReader<File>),
276 Native(File),
277}
278
279impl Read for ResReadFile {
280
281 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
282 match &mut self.0 {
283 ReadFileInner::Package(package) => package.read(buf),
284 ReadFileInner::Native(file) => file.read(buf),
285 }
286 }
287
288 fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
289 match &mut self.0 {
290 ReadFileInner::Package(package) => package.read_exact(buf),
291 ReadFileInner::Native(file) => file.read_exact(buf),
292 }
293 }
294
295}
296
297impl Seek for ResReadFile {
298
299 fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
300 match &mut self.0 {
301 ReadFileInner::Package(package) => package.seek(pos),
302 ReadFileInner::Native(file) => file.seek(pos),
303 }
304 }
305
306 fn stream_position(&mut self) -> io::Result<u64> {
307 match &mut self.0 {
308 ReadFileInner::Package(package) => package.stream_position(),
309 ReadFileInner::Native(file) => file.stream_position(),
310 }
311 }
312
313}
314
315
316#[derive(Debug)]
320pub struct ResReadDir {
321 dir_path: Arc<str>,
323 native_read_dir: Option<ReadDir>,
325 package_read_dir: Option<PackageReadDir>,
327}
328
329#[derive(Debug)]
330struct PackageReadDir {
331 shared: Arc<Shared>,
333 dir_index: usize,
335 remaining_names: Vec<(Arc<str>, usize)>,
338 last_children_count: usize,
340 last_children_last_node_index: usize,
342}
343
344impl Iterator for ResReadDir {
345
346 type Item = io::Result<ResDirEntry>;
347
348 fn next(&mut self) -> Option<Self::Item> {
349
350 if let Some(native_read_dir) = &mut self.native_read_dir {
351 match native_read_dir.next() {
352 Some(Ok(entry)) => {
353 let file_name = entry.file_name();
355 let file_type = entry.file_type().unwrap();
356 let file_name = file_name.to_str().unwrap();
357 return Some(Ok(ResDirEntry {
358 dir_path: Arc::clone(&self.dir_path),
359 name: Arc::from(file_name),
360 is_dir: file_type.is_dir(),
361 }))
362 },
363 Some(Err(e)) => return Some(Err(e)),
364 None => (),
365 }
366 }
367
368 if let Some(package_read_dir) = &mut self.package_read_dir {
369
370 let mut mutable = package_read_dir.shared.mutable.lock().unwrap();
373
374 loop {
375
376 let dir_info = mutable.node_cache.get_dir(package_read_dir.dir_index).unwrap();
377
378 if dir_info.children.len() != package_read_dir.last_children_count {
382
383 debug_assert!(dir_info.children.len() > package_read_dir.last_children_count);
384
385 let mut max_child_index = 0;
386 for (child_name, &child_index) in &dir_info.children {
387 max_child_index = max_child_index.max(child_index);
388 if child_index >= package_read_dir.last_children_last_node_index {
389 package_read_dir.remaining_names.push((Arc::clone(child_name), child_index));
390 }
391 }
392
393 package_read_dir.last_children_count = dir_info.children.len();
394 package_read_dir.last_children_last_node_index = max_child_index + 1;
395
396 }
397
398 if let Some((node_name, node_index)) = package_read_dir.remaining_names.pop() {
399 return Some(Ok(ResDirEntry {
400 dir_path: Arc::clone(&self.dir_path),
401 name: node_name,
402 is_dir: mutable.node_cache.get_dir(node_index).is_some(),
403 }));
404 }
405
406 if !mutable.try_open_pending_package() {
408 return None; }
410
411 }
412
413 }
414
415 None
416
417 }
418
419}
420
421pub struct ResDirEntry {
423 dir_path: Arc<str>,
424 name: Arc<str>,
425 is_dir: bool,
426}
427
428impl ResDirEntry {
429
430 #[inline]
432 pub fn name(&self) -> &str {
433 &self.name
434 }
435
436 pub fn path(&self) -> String {
438 format!("{}/{}", self.dir_path, self.name)
439 }
440
441 #[inline]
443 pub fn is_dir(&self) -> bool {
444 self.is_dir
445 }
446
447 #[inline]
449 pub fn is_file(&self) -> bool {
450 !self.is_dir
451 }
452
453}
454
455
456#[derive(Debug)]
458struct NodeCache {
459 nodes: Vec<NodeInfo>,
461 dir_count: usize,
463 dir_children_max_count: usize,
464 node_name_max_len: usize,
465}
466
467#[derive(Debug)]
469enum NodeInfo {
470 File(FileInfo),
472 Dir(DirInfo)
474}
475
476#[derive(Debug)]
477struct FileInfo {
478 package_index: usize,
480 file_index: usize,
482}
483
484#[derive(Debug, Default)]
485struct DirInfo {
486 children: BTreeMap<Arc<str>, usize>,
491}
492
493impl NodeCache {
494
495 fn new() -> Self {
497 Self {
498 nodes: vec![NodeInfo::Dir(DirInfo::default())],
499 dir_count: 0,
500 dir_children_max_count: 0,
501 node_name_max_len: 0,
502 }
503 }
504
505 fn index_package(&mut self, package_index: usize, package_reader: &PackageReader<File>) {
508
509 let mut last_dir_index = 0;
510 let mut last_dir_path = ""; for (file_index, file_path) in package_reader.names().enumerate() {
513
514 let (mut dir_path, file_name) = match file_path.rfind('/') {
519 Some(last_sep_index) => file_path.split_at(last_sep_index + 1),
520 None => ("", file_path),
521 };
522
523 debug_assert!(!file_name.is_empty(), "package names should only contains files");
524
525 self.node_name_max_len = self.node_name_max_len.max(file_name.len());
526
527 let mut current_dir_index;
531 if dir_path.starts_with(last_dir_path) {
532 dir_path = &dir_path[last_dir_path.len()..];
533 current_dir_index = last_dir_index;
534 } else {
535 current_dir_index = 0;
536 }
537
538 if !dir_path.is_empty() {
542 for dir_part in dir_path[..dir_path.len() - 1].split('/') {
543
544 self.node_name_max_len = self.node_name_max_len.max(dir_part.len());
545
546 let inner_len = self.nodes.len();
549 let dir = self.nodes[current_dir_index]
550 .as_dir_mut()
551 .expect("trying to make a directory where a file already exists");
552
553 if let Some(&child_index) = dir.children.get(dir_part) {
554 current_dir_index = child_index;
555 } else {
556 current_dir_index = inner_len;
557 dir.children.insert(Arc::from(dir_part), inner_len);
558 self.dir_children_max_count = self.dir_children_max_count.max(dir.children.len());
559 self.nodes.push(NodeInfo::Dir(DirInfo::default()));
560 self.dir_count += 1;
561 }
562
563 }
564 }
565
566 if last_dir_index != current_dir_index {
567 last_dir_index = current_dir_index;
568 last_dir_path = dir_path;
569 }
570
571 let inner_len = self.nodes.len();
573 let dir = self.nodes[current_dir_index]
574 .as_dir_mut()
575 .expect("current directory should effectively be a directory");
576
577 let prev_child = dir.children.insert(Arc::from(file_name), inner_len);
578 self.dir_children_max_count = self.dir_children_max_count.max(dir.children.len());
579 debug_assert!(prev_child.is_none(), "overwriting a file");
580 self.nodes.push(NodeInfo::File(FileInfo {
581 package_index,
582 file_index,
583 }));
584
585 }
586
587 }
588
589 fn find_dir(&self, dir_path: &str) -> Option<(usize, &DirInfo)> {
595
596 let mut current_dir_index = 0;
597 if !dir_path.is_empty() {
598 for dir_part in dir_path.split('/') {
599 current_dir_index = *self.nodes[current_dir_index]
600 .as_dir()?
601 .children
602 .get(dir_part)?;
603 }
604 }
605
606 self.nodes[current_dir_index]
607 .as_dir()
608 .map(|dir| (current_dir_index, dir))
609
610 }
611
612 fn find_file(&self, file_path: &str) -> Option<(usize, &FileInfo)> {
615
616 let (dir_path, file_name) = file_path.rsplit_once('/').unwrap_or(("", file_path));
618
619 let (_, dir) = self.find_dir(dir_path)?;
620 let file_index = *dir.children.get(file_name)?;
621 self.nodes[file_index]
622 .as_file()
623 .map(|file| (file_index, file))
624
625 }
626
627 fn get_dir(&self, index: usize) -> Option<&DirInfo> {
629 self.nodes.get(index)?.as_dir()
630 }
631
632 }
638
639impl NodeInfo {
640
641 #[inline]
642 fn as_file(&self) -> Option<&FileInfo> {
643 match self {
644 NodeInfo::File(file) => Some(file),
645 NodeInfo::Dir(_) => None,
646 }
647 }
648
649 #[inline]
650 fn as_dir(&self) -> Option<&DirInfo> {
651 match self {
652 NodeInfo::File(_) => None,
653 NodeInfo::Dir(dir) => Some(dir),
654 }
655 }
656
657 #[inline]
666 fn as_dir_mut(&mut self) -> Option<&mut DirInfo> {
667 match self {
668 NodeInfo::File(_) => None,
669 NodeInfo::Dir(dir) => Some(dir),
670 }
671 }
672
673}