1use crate::error::StfsError;
2use crate::hash::HashTableMeta;
3use crate::header::StfsVolumeDescriptor;
4use crate::io::ReadAt;
5use crate::types::*;
6use byteorder::BigEndian;
7use byteorder::LittleEndian;
8use byteorder::ReadBytesExt;
9use serde::Serialize;
10use std::collections::HashMap;
11use std::io::Cursor;
12
13#[derive(Default, Clone, Debug, Serialize)]
14pub struct StfsFileEntry {
15 pub index: usize,
16 pub name: String,
17 pub flags: u8,
18 pub block_count: usize,
19 pub starting_block_num: BlockNumber,
20 pub path_indicator: u16,
21 pub file_size: usize,
22 pub created_time_stamp: u32,
23 pub access_time_stamp: u32,
24 pub file_entry_address: u64,
25}
26
27#[derive(Debug, Clone, Serialize)]
28pub struct WalkEntry {
29 pub path: String,
30 pub entry: StfsFileEntry,
31}
32
33impl StfsFileEntry {
34 pub fn is_directory(&self) -> bool {
35 self.flags & 2 != 0
36 }
37}
38
39#[derive(Debug, Serialize)]
40pub struct StfsFileTable {
41 pub entries: Vec<StfsFileEntry>,
42}
43
44#[derive(Debug, Clone)]
45pub struct StfsTreeNode {
46 pub entry: StfsFileEntry,
47 pub children: Vec<StfsTreeNode>,
48}
49
50impl StfsFileTable {
51 pub fn read<R: ReadAt>(
52 source: &R,
53 hash_meta: &HashTableMeta,
54 stfs_vol: &StfsVolumeDescriptor,
55 sex: StfsPackageSex,
56 ) -> Result<Self, StfsError> {
57 let mut entries = Vec::new();
58 let mut block = stfs_vol.file_table_block_num;
59
60 for block_idx in 0..(stfs_vol.file_table_block_count as usize) {
61 let current_addr = hash_meta.block_to_addr(block, sex);
62 let block_data = source.read_at(current_addr..current_addr + BLOCK_SIZE)?;
63 let block_data = block_data.as_ref();
64 let mut cursor = Cursor::new(block_data);
65
66 for file_entry_idx in 0..0x40usize {
67 let entry_offset = file_entry_idx * 0x40;
68 let file_entry_address = current_addr as u64 + entry_offset as u64;
69 let index = (block_idx * 0x40) + file_entry_idx;
70
71 let name = read_utf8_with_max_len(&block_data[entry_offset..], 0x28);
73 cursor.set_position((entry_offset + 0x28) as u64);
74
75 let name_len = cursor.read_u8().map_err(|_| StfsError::ReadError {
76 offset: current_addr + entry_offset + 0x28,
77 message: "failed to read name_len".into(),
78 })?;
79
80 if name_len & 0x3F == 0 {
81 continue;
82 }
83
84 if name_len == 0 {
85 break;
86 }
87
88 let block_count = cursor.read_u24::<LittleEndian>()? as usize;
89
90 cursor.set_position(cursor.position() + 3);
92
93 let starting_block_num = BlockNumber(cursor.read_u24::<LittleEndian>()? as usize);
94 let path_indicator = cursor.read_u16::<BigEndian>()?;
95 let file_size = cursor.read_u32::<BigEndian>()? as usize;
96 let created_time_stamp = cursor.read_u32::<BigEndian>()?;
97 let access_time_stamp = cursor.read_u32::<BigEndian>()?;
98 let flags = name_len >> 6;
99
100 entries.push(StfsFileEntry {
101 index,
102 name,
103 flags,
104 block_count,
105 starting_block_num,
106 path_indicator,
107 file_size,
108 created_time_stamp,
109 access_time_stamp,
110 file_entry_address,
111 });
112 }
113
114 let hash_entry = hash_meta.read_block_hash_entry(source, block, sex, stfs_vol)?;
116 block = hash_entry.next_block;
117 }
118
119 Ok(StfsFileTable { entries })
120 }
121
122 pub fn build_tree(&self) -> StfsTreeNode {
123 let root = StfsFileEntry { name: String::new(), ..Default::default() };
124
125 let mut folder_children: HashMap<u16, Vec<usize>> = HashMap::new();
126
127 for (i, entry) in self.entries.iter().enumerate() {
129 folder_children.entry(entry.path_indicator).or_default().push(i);
130 }
131
132 fn build_children(
133 entries: &[StfsFileEntry],
134 folder_children: &HashMap<u16, Vec<usize>>,
135 parent_index: u16,
136 ) -> Vec<StfsTreeNode> {
137 let Some(child_indices) = folder_children.get(&parent_index) else {
138 return Vec::new();
139 };
140
141 child_indices
142 .iter()
143 .map(|&i| {
144 let entry = &entries[i];
145 let children = if entry.is_directory() {
146 build_children(entries, folder_children, entry.index as u16)
147 } else {
148 Vec::new()
149 };
150 StfsTreeNode { entry: entry.clone(), children }
151 })
152 .collect()
153 }
154
155 let children = build_children(&self.entries, &folder_children, 0xFFFF);
156
157 StfsTreeNode { entry: root, children }
158 }
159
160 pub fn walk_files(&self) -> Vec<WalkEntry> {
161 let tree = self.build_tree();
162 let mut result = Vec::new();
163
164 fn walk(node: &StfsTreeNode, path: &str, result: &mut Vec<WalkEntry>) {
165 for child in &node.children {
166 let child_path =
167 if path.is_empty() { child.entry.name.clone() } else { format!("{}/{}", path, child.entry.name) };
168 if child.entry.is_directory() {
169 walk(child, &child_path, result);
170 } else {
171 result.push(WalkEntry { path: child_path, entry: child.entry.clone() });
172 }
173 }
174 }
175
176 walk(&tree, "", &mut result);
177 result
178 }
179}
180
181fn read_utf8_with_max_len(data: &[u8], len: usize) -> String {
182 let end = data[..len].iter().position(|b| *b == 0).unwrap_or(len);
183 String::from_utf8(data[..end].to_vec()).expect("failed to convert data to utf8")
184}