1use crate::crypto::{decrypt_block, hash_string, hash_type};
4use crate::{Error, Result};
5use byteorder::{LittleEndian, ReadBytesExt};
6use std::io::{Read, Seek, SeekFrom};
7
8#[repr(C)]
10#[derive(Debug, Clone, Copy)]
11pub struct BlockEntry {
12 pub file_pos: u32,
14 pub compressed_size: u32,
16 pub file_size: u32,
18 pub flags: u32,
20}
21
22impl BlockEntry {
23 pub const FLAG_IMPLODE: u32 = 0x00000100;
26 pub const FLAG_COMPRESS: u32 = 0x00000200;
28 pub const FLAG_ENCRYPTED: u32 = 0x00010000;
30 pub const FLAG_FIX_KEY: u32 = 0x00020000;
32 pub const FLAG_PATCH_FILE: u32 = 0x00100000;
34 pub const FLAG_SINGLE_UNIT: u32 = 0x01000000;
36 pub const FLAG_DELETE_MARKER: u32 = 0x02000000;
38 pub const FLAG_SECTOR_CRC: u32 = 0x04000000;
40 pub const FLAG_EXISTS: u32 = 0x80000000;
42
43 pub fn is_compressed(&self) -> bool {
45 (self.flags & (Self::FLAG_IMPLODE | Self::FLAG_COMPRESS)) != 0
46 }
47
48 pub fn is_encrypted(&self) -> bool {
50 (self.flags & Self::FLAG_ENCRYPTED) != 0
51 }
52
53 pub fn is_single_unit(&self) -> bool {
55 (self.flags & Self::FLAG_SINGLE_UNIT) != 0
56 }
57
58 pub fn has_sector_crc(&self) -> bool {
60 (self.flags & Self::FLAG_SECTOR_CRC) != 0
61 }
62
63 pub fn exists(&self) -> bool {
65 (self.flags & Self::FLAG_EXISTS) != 0
66 }
67
68 pub fn has_fix_key(&self) -> bool {
70 (self.flags & Self::FLAG_FIX_KEY) != 0
71 }
72
73 pub fn is_patch_file(&self) -> bool {
75 (self.flags & Self::FLAG_PATCH_FILE) != 0
76 }
77
78 pub fn from_bytes(data: &[u8]) -> Result<Self> {
80 if data.len() < 16 {
81 return Err(Error::invalid_format("Block entry too small"));
82 }
83
84 let mut cursor = std::io::Cursor::new(data);
85 Ok(Self {
86 file_pos: cursor.read_u32::<LittleEndian>()?,
87 compressed_size: cursor.read_u32::<LittleEndian>()?,
88 file_size: cursor.read_u32::<LittleEndian>()?,
89 flags: cursor.read_u32::<LittleEndian>()?,
90 })
91 }
92}
93
94#[derive(Debug)]
96pub struct BlockTable {
97 entries: Vec<BlockEntry>,
98}
99
100impl BlockTable {
101 pub fn new(size: usize) -> Result<Self> {
103 let entries = vec![
104 BlockEntry {
105 file_pos: 0,
106 compressed_size: 0,
107 file_size: 0,
108 flags: 0,
109 };
110 size
111 ];
112 Ok(Self { entries })
113 }
114
115 pub fn read<R: Read + Seek>(reader: &mut R, offset: u64, size: u32) -> Result<Self> {
117 reader.seek(SeekFrom::Start(offset))?;
119
120 let byte_size = size as usize * 16; let mut raw_data = vec![0u8; byte_size];
123 reader.read_exact(&mut raw_data)?;
124
125 let key = hash_string("(block table)", hash_type::FILE_KEY);
127
128 let mut u32_buffer: Vec<u32> = raw_data
130 .chunks_exact(4)
131 .map(|chunk| u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]))
132 .collect();
133
134 decrypt_block(&mut u32_buffer, key);
135
136 for (chunk, &decrypted) in raw_data.chunks_exact_mut(4).zip(&u32_buffer) {
138 chunk.copy_from_slice(&decrypted.to_le_bytes());
139 }
140
141 let mut entries = Vec::with_capacity(size as usize);
143 for i in 0..size as usize {
144 let offset = i * 16;
145 let entry = BlockEntry::from_bytes(&raw_data[offset..offset + 16])?;
146 entries.push(entry);
147 }
148
149 Ok(Self { entries })
150 }
151
152 pub fn from_bytes(data: &[u8], size: u32) -> Result<Self> {
154 let expected_size = size as usize * 16; if data.len() < expected_size {
157 return Err(Error::block_table("Insufficient data for block table"));
158 }
159
160 let mut raw_data = data[..expected_size].to_vec();
162
163 let key = hash_string("(block table)", hash_type::FILE_KEY);
165
166 let mut u32_buffer: Vec<u32> = raw_data
168 .chunks_exact(4)
169 .map(|chunk| u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]))
170 .collect();
171
172 decrypt_block(&mut u32_buffer, key);
173
174 for (chunk, &decrypted) in raw_data.chunks_exact_mut(4).zip(&u32_buffer) {
176 chunk.copy_from_slice(&decrypted.to_le_bytes());
177 }
178
179 let mut entries = Vec::with_capacity(size as usize);
181 for i in 0..size as usize {
182 let offset = i * 16;
183 let entry = BlockEntry::from_bytes(&raw_data[offset..offset + 16])?;
184 entries.push(entry);
185 }
186
187 Ok(Self { entries })
188 }
189
190 pub fn entries(&self) -> &[BlockEntry] {
192 &self.entries
193 }
194
195 pub fn get(&self, index: usize) -> Option<&BlockEntry> {
197 self.entries.get(index)
198 }
199
200 pub fn size(&self) -> usize {
202 self.entries.len()
203 }
204
205 pub fn new_mut(size: usize) -> Result<Self> {
207 let entries = vec![
208 BlockEntry {
209 file_pos: 0,
210 compressed_size: 0,
211 file_size: 0,
212 flags: 0,
213 };
214 size
215 ];
216 Ok(Self { entries })
217 }
218
219 pub fn get_mut(&mut self, index: usize) -> Option<&mut BlockEntry> {
221 self.entries.get_mut(index)
222 }
223
224 pub fn entries_mut(&mut self) -> &mut [BlockEntry] {
226 &mut self.entries
227 }
228
229 pub fn clear(&mut self) {
231 for entry in &mut self.entries {
232 *entry = BlockEntry {
233 file_pos: 0,
234 compressed_size: 0,
235 file_size: 0,
236 flags: 0,
237 };
238 }
239 }
240}
241
242#[derive(Debug)]
244pub struct HiBlockTable {
245 entries: Vec<u16>,
246}
247
248impl HiBlockTable {
249 pub fn read<R: Read + Seek>(reader: &mut R, offset: u64, size: u32) -> Result<Self> {
251 reader.seek(SeekFrom::Start(offset))?;
252
253 let mut entries = Vec::with_capacity(size as usize);
254 for _ in 0..size {
255 entries.push(reader.read_u16::<LittleEndian>()?);
256 }
257
258 Ok(Self { entries })
259 }
260
261 pub fn get(&self, index: usize) -> Option<u16> {
263 self.entries.get(index).copied()
264 }
265
266 pub fn get_file_pos_high(&self, index: usize) -> u64 {
268 self.get(index).unwrap_or(0) as u64
269 }
270
271 pub fn new(size: usize) -> Self {
273 Self {
274 entries: vec![0; size],
275 }
276 }
277
278 pub fn set(&mut self, index: usize, value: u16) {
280 if let Some(entry) = self.entries.get_mut(index) {
281 *entry = value;
282 }
283 }
284
285 pub fn entries(&self) -> &[u16] {
287 &self.entries
288 }
289
290 pub fn is_needed(&self) -> bool {
292 self.entries.iter().any(|&v| v != 0)
293 }
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299
300 #[test]
301 fn test_block_entry_flags() {
302 let compressed = BlockEntry {
303 file_pos: 0,
304 compressed_size: 100,
305 file_size: 200,
306 flags: BlockEntry::FLAG_COMPRESS | BlockEntry::FLAG_EXISTS,
307 };
308 assert!(compressed.is_compressed());
309 assert!(!compressed.is_encrypted());
310 assert!(compressed.exists());
311
312 let encrypted = BlockEntry {
313 file_pos: 0,
314 compressed_size: 100,
315 file_size: 100,
316 flags: BlockEntry::FLAG_ENCRYPTED | BlockEntry::FLAG_FIX_KEY | BlockEntry::FLAG_EXISTS,
317 };
318 assert!(encrypted.is_encrypted());
319 assert!(encrypted.has_fix_key());
320 assert!(!encrypted.is_compressed());
321 }
322}