1use crate::{
2 property::WzStringMeta,
3 reader,
4 util::{offset::WzOffsetMeta, profile::WzProfile, version::PKGVersion},
5 wz_image, Reader, WzImage, WzNode, WzNodeArc, WzNodeArcVec, WzNodeName, WzObjectType, WzReader,
6 WzSliceReader,
7};
8use std::sync::Arc;
9
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12
13#[derive(Debug, thiserror::Error)]
14pub enum Error {
15 #[error("Lua parse error")]
16 LuaParseError,
17 #[error("parse as wz image failed, pos at {0}")]
18 ParseError(usize),
19 #[error("New Wz image header found. b = {0}, offset = {1}")]
20 UnknownWzDirectoryType(u8, usize),
21 #[error("Invalid wz version used for decryption, try parsing other version numbers.")]
22 InvalidWzVersion,
23 #[error("Entry count overflow, Invalid wz version used for decryption, try parsing other version numbers.")]
24 InvalidEntryCount,
25 #[error("Unknown pkg version, can't resolve children")]
26 UnknownPkgVersion,
27 #[error("Binary reading error")]
28 ReaderError(#[from] reader::Error),
29}
30
31#[derive(Debug, Default, Clone, PartialEq, Eq)]
32#[repr(u8)]
33pub(crate) enum WzDirectoryType {
34 #[default]
35 UnknownType = 1,
36 MetaAtOffset = 2,
38 WzDirectory = 3,
39 WzImage = 4,
40 NewUnknownType(u8),
41}
42
43impl From<u8> for WzDirectoryType {
44 fn from(value: u8) -> Self {
45 match value {
46 1 => WzDirectoryType::UnknownType,
47 2 => WzDirectoryType::MetaAtOffset,
48 3 => WzDirectoryType::WzDirectory,
49 4 => WzDirectoryType::WzImage,
50 _ => WzDirectoryType::NewUnknownType(value),
51 }
52 }
53}
54
55#[derive(Debug, Default, Clone, PartialEq, Eq)]
56pub(crate) enum WzDirectoryVerifyStatus {
57 #[default]
58 Init,
59 EntryCreated,
60 Verified,
61}
62
63#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
64#[derive(Debug, Clone, Default)]
65pub struct WzDirectory {
66 #[cfg_attr(feature = "serde", serde(skip))]
67 pub reader: Arc<WzReader>,
68 #[cfg_attr(feature = "serde", serde(skip))]
69 pub offset: usize,
70 #[cfg_attr(feature = "serde", serde(skip))]
71 pub block_size: usize,
72 #[cfg_attr(feature = "serde", serde(skip))]
73 pub hash: u64,
74 #[cfg_attr(feature = "serde", serde(skip))]
75 pub is_parsed: bool,
76 #[cfg_attr(feature = "serde", serde(skip))]
77 pub profile: WzProfile,
78 #[cfg_attr(feature = "serde", serde(skip))]
79 verify_status: WzDirectoryVerifyStatus,
80 #[cfg_attr(feature = "serde", serde(skip))]
81 entries: Vec<WzDirectoryEntry>,
82}
83
84impl WzDirectory {
85 #[inline]
86 pub fn hash_u32(&self) -> u32 {
87 self.hash as u32
88 }
89
90 pub fn new(offset: usize, block_size: usize, reader: &Arc<WzReader>, is_parsed: bool) -> Self {
91 Self {
92 reader: reader.clone(),
93 offset,
94 block_size,
95 hash: 0,
96 is_parsed,
97 verify_status: WzDirectoryVerifyStatus::Init,
98 profile: WzProfile::default(),
99 entries: Vec::new(),
100 }
101 }
102
103 pub fn with_hash(mut self, hash: u64) -> Self {
104 self.hash = hash;
105 self
106 }
107
108 pub fn reset_entry_parse(&mut self) {
109 self.verify_status = WzDirectoryVerifyStatus::Init;
110 self.entries.clear();
111 }
112
113 pub fn resolve_children(&mut self, parent: &WzNodeArc) -> Result<WzNodeArcVec, Error> {
114 if self.block_size == 0 {
115 return Ok(vec![]);
116 }
117
118 self.prepare_entries()?;
119
120 if self.calculate_offset_and_verify().is_err() {
121 return Err(Error::InvalidWzVersion);
122 }
123
124 let nodes: WzNodeArcVec = self
125 .entries
126 .iter()
127 .map(|entry| {
128 entry.into_wz_node_tuple(parent, self.hash, &self.profile, Arc::clone(&self.reader))
129 })
130 .collect();
131
132 for (_, node) in nodes.iter() {
134 let mut write = node.write().unwrap();
135 if let WzObjectType::Directory(dir) = &mut write.object_type {
136 dir.profile = self.profile.clone();
137 dir.hash = self.hash;
138 let children = dir.resolve_children(node)?;
139
140 for (name, child) in children {
141 write.children.insert(name, child);
142 }
143 }
144 }
145
146 self.verify_status = WzDirectoryVerifyStatus::Verified;
147 self.entries.clear();
148
149 Ok(nodes)
150 }
151
152 fn resolve_entry_pkg1(&self, reader: &WzSliceReader) -> Result<Vec<WzDirectoryEntry>, Error> {
153 let entry_count = reader.read_wz_int()?;
154
155 if !(0..=1000000).contains(&entry_count) {
156 return Err(Error::InvalidEntryCount);
157 }
158
159 let mut entries: Vec<WzDirectoryEntry> = Vec::with_capacity(entry_count as usize);
160
161 for _ in 0..entry_count {
162 let entry = WzDirectoryEntry::read_pkg1_entry(reader)?;
163
164 match entry.dir_type {
165 WzDirectoryType::UnknownType => {
166 reader.skip(4 + 4 + 2);
168 continue;
169 }
170 WzDirectoryType::NewUnknownType(dir_byte) => {
171 return Err(Error::UnknownWzDirectoryType(dir_byte, reader.pos.get()));
172 }
173 _ => {
174 }
176 }
177
178 entries.push(entry);
179 }
180
181 Ok(entries)
182 }
183
184 fn resolve_entry_pkg2(&self, reader: &WzSliceReader) -> Result<Vec<WzDirectoryEntry>, Error> {
185 let encrypted_entry_count = reader.read_wz_int()?;
186
187 let mut wz_dir_entries: Vec<WzDirectoryEntry> = vec![];
188
189 loop {
191 let entry = WzDirectoryEntry::read_pkg2_entry(
192 reader,
193 encrypted_entry_count,
194 wz_dir_entries.is_empty(),
195 )?;
196
197 match entry.dir_type {
198 WzDirectoryType::UnknownType => {
200 break;
201 }
202 WzDirectoryType::NewUnknownType(dir_byte) => {
203 return Err(Error::UnknownWzDirectoryType(dir_byte, reader.pos.get()));
204 }
205 _ => {
206 wz_dir_entries.push(entry);
207 }
208 }
209 }
210
211 let encrypted_offset_count = reader.read_wz_int()?;
212
213 if encrypted_offset_count != encrypted_entry_count || wz_dir_entries.is_empty() {
214 return Err(Error::InvalidWzVersion);
215 }
216
217 for entry in wz_dir_entries.iter_mut() {
218 entry.calculation_offset = reader.pos.get();
219 entry.encrypted_offset = reader.read_u32()?;
220 }
221
222 Ok(wz_dir_entries)
223 }
224
225 fn resolve_entry_pkg2_v64(
226 &self,
227 reader: &WzSliceReader,
228 ) -> Result<Vec<WzDirectoryEntry>, Error> {
229 let entry_count_calculator = self.profile.offset_version.get_entry_count_calculator();
230
231 let encrypted_entry_count = reader.read_wz_int64()?;
232 let entry_count: usize =
233 entry_count_calculator(&reader.header, self.hash, encrypted_entry_count)?;
234
235 if !(0..=1_000_000).contains(&entry_count) {
236 return Err(Error::InvalidEntryCount);
237 }
238
239 let mut wz_dir_entries: Vec<WzDirectoryEntry> = Vec::with_capacity(entry_count);
240
241 for i in 0..entry_count {
242 let entry =
243 WzDirectoryEntry::read_pkg2_entry(reader, encrypted_entry_count as i32, i == 0)?;
244
245 match entry.dir_type {
246 WzDirectoryType::WzDirectory | WzDirectoryType::WzImage => {
247 wz_dir_entries.push(entry);
248 }
249 WzDirectoryType::NewUnknownType(dir_byte) => {
250 return Err(Error::UnknownWzDirectoryType(dir_byte, reader.pos.get()));
251 }
252 _ => return Err(Error::InvalidWzVersion),
253 }
254 }
255
256 for entry in wz_dir_entries.iter_mut() {
257 entry.calculation_offset = reader.pos.get();
258 entry.encrypted_offset = reader.read_u32()?;
259 }
260
261 Ok(wz_dir_entries)
262 }
263
264 pub fn prepare_entries(&mut self) -> Result<(), Error> {
265 if self.verify_status != WzDirectoryVerifyStatus::Init {
266 return Ok(());
267 }
268
269 let reader = self.reader.create_slice_reader();
270
271 reader.seek(self.offset);
272
273 if reader.header.ident == PKGVersion::V1 {
274 self.entries = self.resolve_entry_pkg1(&reader)?;
275 } else if reader.header.ident == PKGVersion::V2 {
276 if reader.header.is_pkg2_64() {
277 self.entries = self.resolve_entry_pkg2_v64(&reader)?;
278 } else {
279 self.entries = self.resolve_entry_pkg2(&reader)?;
280 }
281 } else {
282 return Err(Error::UnknownPkgVersion);
283 }
284
285 self.verify_status = WzDirectoryVerifyStatus::EntryCreated;
286
287 Ok(())
288 }
289
290 pub fn calculate_offset_and_verify(&mut self) -> Result<(), Error> {
291 let reader = self.reader.create_slice_reader();
292
293 let offset_calculator = self.profile.offset_version.get_calculator();
294
295 for entry in self.entries.iter_mut() {
296 let meta = WzOffsetMeta {
297 hash: self.hash,
298 encrypted_offset: entry.encrypted_offset,
299 offset: entry.calculation_offset,
300 };
301
302 entry.offset = offset_calculator(&reader.header, &meta)?;
303
304 if entry.verify(&reader).is_err() {
305 return Err(Error::InvalidWzVersion);
306 }
307 }
308
309 Ok(())
310 }
311
312 pub fn verify_string_decryptor(&mut self) -> bool {
313 if self.verify_status != WzDirectoryVerifyStatus::EntryCreated {
314 return false;
315 }
316 if self.entries.is_empty() {
317 return true;
318 }
319 self.entries[0].resolve_name(&self.reader).is_ok()
320 }
321}
322
323#[derive(Debug, Default, Clone)]
324struct WzDirectoryEntry {
325 name: WzStringMeta,
326 dir_type: WzDirectoryType,
327 size: usize,
328 calculation_offset: usize,
329 encrypted_offset: u32,
330 offset: usize,
331 _checksum: i32,
332}
333
334impl WzDirectoryEntry {
335 pub fn read_pkg1_entry(reader: &WzSliceReader) -> Result<Self, Error> {
336 let mut entry = WzDirectoryEntry {
337 dir_type: reader.read_u8()?.into(),
338 ..Default::default()
339 };
340
341 match entry.dir_type {
342 WzDirectoryType::UnknownType => {
343 return Ok(entry);
344 }
345 WzDirectoryType::MetaAtOffset => {
346 let str_offset = reader.read_i32()?;
347
348 let offset = reader.header.fstart + str_offset as usize;
349
350 entry.dir_type = reader.read_u8_at(offset)?.into();
351 entry.name = reader.read_wz_string_meta_at(offset + 1)?;
352 }
353 WzDirectoryType::WzDirectory | WzDirectoryType::WzImage => {
354 entry.name = reader.read_wz_string_meta()?;
355 }
356 WzDirectoryType::NewUnknownType(_) => {
357 return Ok(entry);
358 }
359 }
360
361 entry.size = reader.read_wz_int()? as usize;
362 entry._checksum = reader.read_wz_int()?;
363 entry.calculation_offset = reader.pos.get();
364 entry.encrypted_offset = reader.read_u32()?;
365
366 Ok(entry)
367 }
368
369 pub fn read_pkg2_entry(
370 reader: &WzSliceReader,
371 encrypted_entry_count: i32,
372 use_pkg2_dir_read: bool,
373 ) -> Result<Self, Error> {
374 let mut entry = WzDirectoryEntry {
375 dir_type: reader.read_u8()?.into(),
376 ..Default::default()
377 };
378
379 match entry.dir_type {
380 WzDirectoryType::WzDirectory | WzDirectoryType::WzImage => {
381 if use_pkg2_dir_read && reader.pkg2_keys.read().unwrap().is_pkg2() {
382 if reader.header.is_pkg2_64() {
383 entry.name = reader.read_wz_string_pkg2_u64_dir_meta()?;
384 } else {
385 entry.name = reader.read_wz_string_pkg2_dir_meta()?;
386 }
387 } else {
388 entry.name = reader.read_wz_string_meta()?;
389 }
390 }
391 WzDirectoryType::NewUnknownType(_) => {
392 let current_pos = reader.pos.get();
393 reader.pos.set(current_pos - 1);
394 let test_wz_int = reader.read_wz_int()?;
395
396 if test_wz_int == encrypted_entry_count {
398 reader.pos.set(current_pos - 1);
399 entry.dir_type = WzDirectoryType::UnknownType;
400 }
401 return Ok(entry);
402 }
403 _ => {
404 return Err(Error::InvalidWzVersion);
405 }
406 }
407
408 entry.size = reader.read_wz_int()? as usize;
409 entry._checksum = reader.read_wz_int()?;
410
411 Ok(entry)
412 }
413
414 pub fn verify(&self, reader: &WzSliceReader) -> Result<(), Error> {
415 if !reader.is_valid_pos(self.offset + self.size) {
416 return Err(Error::InvalidWzVersion);
417 }
418
419 if self.dir_type == WzDirectoryType::WzImage {
420 let header_byte = reader.read_u8_at(self.offset)?;
421 if !wz_image::is_valid_image_header(header_byte) {
422 return Err(Error::InvalidWzVersion);
423 }
424 } else if self.dir_type == WzDirectoryType::WzDirectory {
426 reader.seek(self.offset);
427
428 let entry_count = if reader.header.is_pkg2_64() {
429 reader.read_wz_int64()?
430 } else {
431 reader.read_wz_int()? as i64
432 };
433
434 if entry_count < 0 {
435 return Err(Error::InvalidWzVersion);
436 }
437 }
438 Ok(())
439 }
440
441 pub fn resolve_name(&self, reader: &Arc<WzReader>) -> Result<WzNodeName, Error> {
442 Ok(reader
443 .resolve_wz_string_meta(
444 &self.name.string_type,
445 self.name.offset,
446 self.name.length as usize,
447 )?
448 .into())
449 }
450
451 pub fn try_into_wz_node_tuple(
452 &self,
453 parent: &WzNodeArc,
454 hash: u64,
455 profile: &WzProfile,
456 reader: Arc<WzReader>,
457 ) -> Result<(WzNodeName, WzNodeArc), Error> {
458 let node: WzNode;
459 let name: WzNodeName = self.resolve_name(&reader)?;
460
461 match self.dir_type {
462 WzDirectoryType::WzDirectory => {
463 let mut wz_dir =
464 WzDirectory::new(self.offset, self.size, &reader, false).with_hash(hash);
465 wz_dir.profile = profile.clone();
466 node = WzNode::new(&name, wz_dir, Some(parent));
467 }
468 WzDirectoryType::WzImage => {
469 let wz_image = WzImage::new(&name, self.offset, self.size, &reader);
470 node = WzNode::new(&name, wz_image, Some(parent));
471 }
472 _ => {
473 node = WzNode::empty();
474 }
475 }
476
477 Ok((name.clone(), node.into_lock()))
478 }
479
480 pub fn into_wz_node_tuple(
481 &self,
482 parent: &WzNodeArc,
483 hash: u64,
484 profile: &WzProfile,
485 reader: Arc<WzReader>,
486 ) -> (WzNodeName, WzNodeArc) {
487 self.try_into_wz_node_tuple(parent, hash, profile, reader)
488 .unwrap()
489 }
490}