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 HashEntry {
12 pub name_1: u32,
14 pub name_2: u32,
16 pub locale: u16,
18 pub platform: u16,
20 pub block_index: u32,
22}
23
24impl HashEntry {
25 pub const EMPTY_NEVER_USED: u32 = 0xFFFFFFFF;
27 pub const EMPTY_DELETED: u32 = 0xFFFFFFFE;
29
30 pub fn empty() -> Self {
32 Self {
33 name_1: 0,
34 name_2: 0,
35 locale: 0,
36 platform: 0,
37 block_index: Self::EMPTY_NEVER_USED,
38 }
39 }
40
41 pub fn is_empty(&self) -> bool {
43 self.block_index == Self::EMPTY_NEVER_USED
44 }
45
46 pub fn is_deleted(&self) -> bool {
48 self.block_index == Self::EMPTY_DELETED
49 }
50
51 pub fn is_valid(&self) -> bool {
53 self.block_index < Self::EMPTY_DELETED
54 }
55
56 pub fn from_bytes(data: &[u8]) -> Result<Self> {
58 if data.len() < 16 {
59 return Err(Error::invalid_format("Hash entry too small"));
60 }
61
62 let mut cursor = std::io::Cursor::new(data);
63 Ok(Self {
64 name_1: cursor.read_u32::<LittleEndian>()?,
65 name_2: cursor.read_u32::<LittleEndian>()?,
66 locale: cursor.read_u16::<LittleEndian>()?,
67 platform: cursor.read_u16::<LittleEndian>()?,
68 block_index: cursor.read_u32::<LittleEndian>()?,
69 })
70 }
71}
72
73#[derive(Debug)]
75pub struct HashTable {
76 entries: Vec<HashEntry>,
77 mask: usize,
78}
79
80impl HashTable {
81 pub fn new(size: usize) -> Result<Self> {
83 if !crate::is_power_of_two(size as u32) {
85 return Err(Error::hash_table("Hash table size must be power of 2"));
86 }
87
88 let entries = vec![HashEntry::empty(); size];
89 Ok(Self {
90 entries,
91 mask: size - 1,
92 })
93 }
94
95 pub fn read<R: Read + Seek>(reader: &mut R, offset: u64, size: u32) -> Result<Self> {
97 if !crate::is_power_of_two(size) {
99 return Err(Error::hash_table("Hash table size must be power of 2"));
100 }
101
102 reader.seek(SeekFrom::Start(offset))?;
104
105 let byte_size = size as usize * 16; let mut raw_data = vec![0u8; byte_size];
108 reader.read_exact(&mut raw_data)?;
109
110 let key = hash_string("(hash table)", hash_type::FILE_KEY);
112
113 let mut u32_buffer: Vec<u32> = raw_data
115 .chunks_exact(4)
116 .map(|chunk| u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]))
117 .collect();
118
119 decrypt_block(&mut u32_buffer, key);
120
121 for (chunk, &decrypted) in raw_data.chunks_exact_mut(4).zip(&u32_buffer) {
123 chunk.copy_from_slice(&decrypted.to_le_bytes());
124 }
125
126 let mut entries = Vec::with_capacity(size as usize);
128 for i in 0..size as usize {
129 let offset = i * 16;
130 let entry = HashEntry::from_bytes(&raw_data[offset..offset + 16])?;
131 entries.push(entry);
132 }
133
134 Ok(Self {
135 entries,
136 mask: size as usize - 1,
137 })
138 }
139
140 pub fn from_bytes(data: &[u8], size: u32) -> Result<Self> {
142 if !crate::is_power_of_two(size) {
144 return Err(Error::hash_table("Hash table size must be power of 2"));
145 }
146
147 let expected_size = size as usize * 16; if data.len() < expected_size {
150 return Err(Error::hash_table("Insufficient data for hash table"));
151 }
152
153 let mut raw_data = data[..expected_size].to_vec();
155
156 let key = hash_string("(hash table)", hash_type::FILE_KEY);
158
159 let mut u32_buffer: Vec<u32> = raw_data
161 .chunks_exact(4)
162 .map(|chunk| u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]))
163 .collect();
164
165 decrypt_block(&mut u32_buffer, key);
166
167 for (chunk, &decrypted) in raw_data.chunks_exact_mut(4).zip(&u32_buffer) {
169 chunk.copy_from_slice(&decrypted.to_le_bytes());
170 }
171
172 let mut entries = Vec::with_capacity(size as usize);
174 for i in 0..size as usize {
175 let offset = i * 16;
176 let entry = HashEntry::from_bytes(&raw_data[offset..offset + 16])?;
177 entries.push(entry);
178 }
179
180 Ok(Self {
181 entries,
182 mask: size as usize - 1,
183 })
184 }
185
186 pub fn entries(&self) -> &[HashEntry] {
188 &self.entries
189 }
190
191 pub fn get(&self, index: usize) -> Option<&HashEntry> {
193 self.entries.get(index)
194 }
195
196 pub fn size(&self) -> usize {
198 self.entries.len()
199 }
200
201 pub fn find_file(&self, filename: &str, locale: u16) -> Option<(usize, &HashEntry)> {
203 let name_a = hash_string(filename, hash_type::NAME_A);
205 let name_b = hash_string(filename, hash_type::NAME_B);
206 let start_index = hash_string(filename, hash_type::TABLE_OFFSET) as usize;
207
208 let mut index = start_index & self.mask;
209 let end_index = index;
210
211 loop {
213 let entry = &self.entries[index];
214
215 if entry.name_1 == name_a && entry.name_2 == name_b {
217 if (locale == 0 || entry.locale == 0 || entry.locale == locale) && entry.is_valid()
219 {
220 return Some((index, entry));
221 }
222 }
223
224 if entry.is_empty() {
226 return None;
227 }
228
229 index = (index + 1) & self.mask;
231
232 if index == end_index {
234 return None;
235 }
236 }
237 }
238
239 pub fn new_mut(size: usize) -> Result<Self> {
241 if !crate::is_power_of_two(size as u32) {
243 return Err(Error::hash_table("Hash table size must be power of 2"));
244 }
245
246 let entries = vec![HashEntry::empty(); size];
247 Ok(Self {
248 entries,
249 mask: size - 1,
250 })
251 }
252
253 pub fn get_mut(&mut self, index: usize) -> Option<&mut HashEntry> {
255 self.entries.get_mut(index)
256 }
257
258 pub fn entries_mut(&mut self) -> &mut [HashEntry] {
260 &mut self.entries
261 }
262
263 pub fn clear(&mut self) {
265 for entry in &mut self.entries {
266 *entry = HashEntry::empty();
267 }
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use super::*;
274
275 #[test]
276 fn test_hash_entry_states() {
277 let empty = HashEntry::empty();
278 assert!(empty.is_empty());
279 assert!(!empty.is_deleted());
280 assert!(!empty.is_valid());
281
282 let deleted = HashEntry {
283 name_1: 0,
284 name_2: 0,
285 locale: 0,
286 platform: 0,
287 block_index: HashEntry::EMPTY_DELETED,
288 };
289 assert!(!deleted.is_empty());
290 assert!(deleted.is_deleted());
291 assert!(!deleted.is_valid());
292
293 let valid = HashEntry {
294 name_1: 0x12345678,
295 name_2: 0x9ABCDEF0,
296 locale: 0,
297 platform: 0,
298 block_index: 0,
299 };
300 assert!(!valid.is_empty());
301 assert!(!valid.is_deleted());
302 assert!(valid.is_valid());
303 }
304
305 #[test]
306 fn test_hash_table_size_validation() {
307 assert!(HashTable::new(16).is_ok());
309 assert!(HashTable::new(256).is_ok());
310 assert!(HashTable::new(4096).is_ok());
311
312 assert!(HashTable::new(15).is_err());
314 assert!(HashTable::new(100).is_err());
315 assert!(HashTable::new(0).is_err());
316 }
317}