1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use byteorder::{LittleEndian, ReadBytesExt};
use rayon::prelude::*;
use std::collections::BTreeMap;
use std::io::{Cursor, Read};
use std::path::Path;
use anyhow::Result;
pub mod fat;
pub mod fnt;
use self::fat::FileAllocTable;
use self::fnt::{Directory, FileEntry, ROOT_ID};
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct FileSystem {
pub dirs: BTreeMap<u16, Directory>,
overlays: Vec<FileEntry>,
}
impl FileSystem {
pub fn new(fnt: &[u8], fat: &[u8]) -> Result<Self> {
let mut cursor = Cursor::new(fnt);
let mut dirs = BTreeMap::new();
cursor.set_position(6);
let count = cursor.read_u16::<LittleEndian>()?;
cursor.set_position(0);
for index in 0..count {
let id = ROOT_ID + index;
dirs.insert(id, Directory::new(&mut cursor, id)?);
}
let fat = FileAllocTable::new(fat)?;
let mut fnt = Self {
dirs,
overlays: Vec::new(),
};
fnt.populate(&mut cursor, &fat)?;
Ok(fnt)
}
pub fn count(&self) -> usize {
self.dirs.len()
}
pub fn files(&self) -> Vec<&FileEntry> {
self.dirs
.par_iter()
.flat_map(|(_, ref dir)| {
&dir.files
})
.collect::<_>()
}
pub fn start_id(&self) -> u16 {
self.dirs[&ROOT_ID].start_id()
}
pub fn overlays(&self) -> &[FileEntry] {
&self.overlays
}
fn populate(&mut self, cursor: &mut Cursor<&[u8]>, fat: &FileAllocTable) -> Result<()> {
self._populate(cursor, "", ROOT_ID, fat)?;
self.overlays = (0..self.start_id())
.into_par_iter()
.map(|id| {
let alloc_info = fat.get(id).unwrap();
FileEntry::new(id, &format!("overlay_{:04}", id), alloc_info)
})
.collect::<_>();
Ok(())
}
fn _populate<P: AsRef<Path>>(&mut self, mut cursor: &mut Cursor<&[u8]>, path: P, id: u16, fat: &FileAllocTable) -> Result<()> {
let mut file_id = {
let dir = self.dirs.get_mut(&id).unwrap();
dir.set_path(&path);
cursor.set_position(dir.offset() as u64);
dir.start_id()
};
let mut files = Vec::new();
let mut len = cursor.read_u8()?;
while len != 0 {
let name = self.read_name(&mut cursor, len)?;
if len > 0x80 {
let dir_id = cursor.read_u16::<LittleEndian>()?;
let pos = cursor.position();
let new_path = path.as_ref().join(name);
self._populate(&mut cursor, new_path, dir_id, fat)?;
cursor.set_position(pos);
} else {
let file_path = path.as_ref().join(name);
let alloc_info = fat.get(file_id).unwrap();
files.push(FileEntry::new(file_id, &file_path, alloc_info));
file_id += 1;
}
len = cursor.read_u8()?;
}
let dir = self.dirs.get_mut(&id).unwrap();
dir.append_files(&files);
Ok(())
}
fn read_name<R: Read>(&self, cursor: &mut R, mut len: u8) -> Result<String> {
let mut name = String::new();
if len > 0x80 {
len -= 0x80;
}
cursor.take(u64::from(len))
.read_to_string(&mut name)?;
Ok(name)
}
}