1use std::fs::File;
2use std::io::{Read, Seek, SeekFrom};
3use std::path::{Component, PathBuf};
4
5use anyhow::Result;
6use zerocopy::FromBytes;
7
8use crate::toc::node::{DirectoryNode, Node, NodeKind};
9use crate::toc::toc_entry::{TocEntry, TOC_ENTRY_SIZE};
10
11pub(crate) struct Toc {
12 toc_path: PathBuf,
13 directories: Vec<Node>,
14 files: Vec<Node>,
15}
16
17impl Toc {
18 pub fn new(toc_path: PathBuf) -> Self {
19 Self {
20 toc_path,
21 directories: Vec::new(),
22 files: Vec::new(),
23 }
24 }
25
26 pub fn directories(&self) -> &Vec<Node> {
27 &self.directories
28 }
29
30 pub fn files(&self) -> &Vec<Node> {
31 &self.files
32 }
33
34 pub fn root(&self) -> Option<Node> {
35 self.directories.get(0).cloned()
36 }
37
38 pub fn is_loaded(&self) -> bool {
39 !self.directories.is_empty()
40 }
41
42 pub fn read_toc(&mut self) -> Result<()> {
43 if self.is_loaded() {
44 return Ok(()); }
46
47 self.unread_toc();
50
51 let mut toc_reader = File::open(&self.toc_path).unwrap();
52 let entry_count = (toc_reader.metadata().unwrap().len() as usize - 8) / TOC_ENTRY_SIZE;
53 toc_reader.seek(SeekFrom::Start(8)).unwrap();
54
55 self.files.reserve(entry_count);
58 self.directories.reserve(entry_count);
59
60 let mut file_count = 0;
61 let mut dir_count = 1; self.directories.insert(0, Node::root());
64
65 let mut buffer = vec![0u8; TOC_ENTRY_SIZE * entry_count];
66 toc_reader.read_exact(&mut buffer).unwrap();
67
68 let entries = TocEntry::slice_from(&buffer).unwrap();
69 for entry in entries {
70 if entry.timestamp == 0 {
73 continue;
74 }
75
76 let entry_name = {
79 let null_index = entry.name.iter().position(|&x| x == 0).unwrap_or(64);
80 let entry_name = std::str::from_utf8(&entry.name[..null_index])?;
81 entry_name
82 };
83
84 let parent_node = self
85 .directories
86 .get_mut(entry.parent_dir_index as usize)
87 .unwrap();
88
89 if entry.cache_offset == -1 {
91 let dir_node = Node::directory(entry_name);
92
93 parent_node.append(dir_node.clone());
94 self.directories.insert(dir_count, dir_node);
95
96 dir_count += 1;
97 } else {
98 let file_node = Node::file(
99 entry_name,
100 entry.cache_offset,
101 entry.timestamp,
102 entry.comp_len,
103 entry.len,
104 );
105
106 parent_node.append(file_node.clone());
107 self.files.insert(file_count, file_node);
108
109 file_count += 1;
110 }
111 }
112
113 self.directories.shrink_to_fit();
115 self.files.shrink_to_fit();
116
117 Ok(()) }
119
120 pub fn unread_toc(&mut self) {
121 self.directories.clear();
122 self.files.clear();
123 }
124
125 fn get_node(&self, path: PathBuf) -> Option<Node> {
126 if !self.is_loaded() {
127 return None;
128 }
129
130 if !path.has_root() {
131 panic!("Path must be absolute");
132 }
133
134 let mut components = path.components();
135 let mut current_node = self.root().unwrap().clone();
136
137 components.next();
139
140 for component in components {
141 match component {
142 Component::Normal(name) => {
143 let name = name.to_str().unwrap();
144 current_node = match current_node.get_child(name) {
145 Some(child) => child,
146 _ => return None,
147 };
148 }
149 Component::ParentDir => {
150 current_node = match current_node.parent() {
151 Some(parent) => parent,
152 _ => return None,
153 };
154 }
155 Component::CurDir => continue,
156 _ => return None,
157 }
158 }
159
160 Some(current_node)
161 }
162
163 pub fn get_directory_node(&self, path: PathBuf) -> Option<Node> {
164 match self.get_node(path) {
165 Some(node) => match node.kind() {
166 NodeKind::Directory => Some(node),
167 _ => None,
168 },
169 _ => None,
170 }
171 }
172
173 pub fn get_file_node(&self, path: PathBuf) -> Option<Node> {
174 match self.get_node(path) {
175 Some(node) => match node.kind() {
176 NodeKind::File => Some(node),
177 _ => None,
178 },
179 _ => None,
180 }
181 }
182}