msg_tool\scripts\qlie\archive\pack/
v31.rs

1use super::encryption::*;
2use super::types::*;
3use crate::ext::io::*;
4use crate::scripts::base::*;
5use crate::types::*;
6use crate::utils::encoding::*;
7use anyhow::Result;
8use rand::Rng;
9use std::io::{Seek, Write};
10
11struct MListEntry<T> {
12    back: *mut MListEntry<T>,
13    next: *mut MListEntry<T>,
14    data: T,
15}
16
17struct MList<T> {
18    head: *mut MListEntry<T>,
19    depth: usize,
20}
21
22impl<T> MList<T> {
23    pub fn new() -> Self {
24        Self {
25            head: std::ptr::null_mut(),
26            depth: 0,
27        }
28    }
29
30    pub fn push(&mut self, data: T, way: bool) -> usize {
31        let entry = Box::new(MListEntry {
32            back: std::ptr::null_mut(),
33            next: std::ptr::null_mut(),
34            data,
35        });
36        let entry_ptr = Box::into_raw(entry);
37        if self.head.is_null() {
38            self.head = entry_ptr;
39            unsafe {
40                (*self.head).back = self.head;
41                (*self.head).next = self.head;
42            }
43        } else {
44            if way {
45                unsafe {
46                    (*(*self.head).back).next = entry_ptr;
47                    (*entry_ptr).back = (*self.head).back;
48                    (*entry_ptr).next = self.head;
49                    (*self.head).back = entry_ptr;
50                }
51            } else {
52                unsafe {
53                    (*(*self.head).back).next = entry_ptr;
54                    (*entry_ptr).back = (*self.head).back;
55                    (*entry_ptr).next = self.head;
56                    (*self.head).back = entry_ptr;
57                    self.head = entry_ptr;
58                }
59            }
60        }
61        self.depth += 1;
62        self.depth
63    }
64
65    pub fn pop(&mut self, way: bool) -> Option<T> {
66        if self.head.is_null() {
67            return None;
68        }
69        if self.depth > 0 {
70            self.depth -= 1;
71            let ret;
72            if way {
73                unsafe {
74                    ret = (*self.head).back;
75                    (*(*ret).back).next = (*ret).next;
76                    (*self.head).back = (*ret).back;
77                }
78            } else {
79                unsafe {
80                    ret = self.head;
81                    (*(*ret).back).next = (*ret).next;
82                    (*(*ret).next).back = (*ret).back;
83                    self.head = (*ret).next;
84                }
85            }
86            if self.depth == 0 {
87                self.head = std::ptr::null_mut();
88            }
89            let boxed = unsafe { Box::from_raw(ret) };
90            return Some(boxed.data);
91        }
92        None
93    }
94}
95
96impl<T> Drop for MList<T> {
97    fn drop(&mut self) {
98        if self.head.is_null() {
99            return;
100        }
101        let mut current = self.head;
102        loop {
103            unsafe {
104                let next = (*current).next;
105                let _ = Box::from_raw(current);
106                if next == self.head {
107                    break;
108                }
109                current = next;
110            }
111        }
112    }
113}
114
115pub struct QliePackArchiveWriterV31<T: Write + Seek> {
116    writer: T,
117    encryption: Encryption31,
118    qkey: QlieKey,
119    header: QlieHeader,
120    hash: QlieHash14,
121    has_key_file: bool,
122    entries: Vec<QlieEntry>,
123    key: u32,
124    common_key: Option<Vec<u8>>,
125    compress_files: bool,
126}
127
128struct FilenameEntry {
129    name: Vec<u16>,
130    hash: u32,
131    index: u32,
132}
133
134fn get_pos(hash: u32, count: u32) -> u32 {
135    let v = (hash as u16 as u32)
136        .wrapping_add(hash >> 8)
137        .wrapping_add(hash >> 16);
138    v % count
139}
140
141impl<T: Write + Seek> QliePackArchiveWriterV31<T> {
142    pub fn new(writer: T, files: &[&str], config: &ExtraConfig) -> Result<Self> {
143        let has_key_file = files.iter().any(|f| *f == QLIE_KEY_FILE);
144        let mut file_count = files.len() as u32;
145        if !has_key_file {
146            if config.qlie_pack_keyfile.is_none() {
147                anyhow::bail!(
148                    "Qlie Pack Archive key file is required but not provided. Put a key file named '{}' in the directory or specify the path using '--qlie-pack-keyfile' option.",
149                    QLIE_KEY_FILE
150                );
151            }
152            // Add 1 for the key file
153            file_count += 1;
154        }
155        let header = QlieHeader {
156            signature: *b"FilePackVer3.1\x00\x00",
157            file_count,
158            index_offset: 0,
159        };
160        let encryption = Encryption31::new();
161        let mut qkey = QlieKey {
162            signature: *QLIE_KEY_SIGNATURE,
163            hash_size: 0,
164            key: [0; 0x400],
165        };
166        rand::rng().fill(&mut qkey.key[..0x100]);
167        let key = encryption.compute_hash(&qkey.key[..0x100])? & 0xFFFFFFF;
168        encrypt(&mut qkey.signature, key)?;
169        let mut entries = Vec::new();
170        let mut list = Vec::with_capacity(256);
171        for _ in 0..256 {
172            list.push(MList::<FilenameEntry>::new());
173        }
174        let key_entry = QlieEntry {
175            name: QLIE_KEY_FILE.to_string(),
176            ..Default::default()
177        };
178        entries.push(key_entry);
179        let key_filename: Vec<_> = QLIE_KEY_FILE.encode_utf16().collect();
180        let key_hash = encryption.compute_name_hash(&key_filename)?;
181        let key_name_entry = FilenameEntry {
182            name: key_filename,
183            hash: key_hash,
184            index: 0,
185        };
186        let pos = get_pos(key_hash, 256);
187        list[pos as usize].push(key_name_entry, true);
188        for name in files {
189            if *name == QLIE_KEY_FILE {
190                continue;
191            }
192            let filename: Vec<_> = name.encode_utf16().collect();
193            let name_hash = encryption.compute_name_hash(&filename)?;
194            let entry = QlieEntry {
195                name: name.to_string(),
196                ..Default::default()
197            };
198            entries.push(entry);
199            let name_entry = FilenameEntry {
200                name: filename,
201                hash: name_hash,
202                index: (entries.len() - 1) as u32,
203            };
204            let pos = get_pos(name_hash, 256);
205            list[pos as usize].push(name_entry, true);
206        }
207        let mut hash_data = MemWriter::new();
208        for mut list in list {
209            hash_data.write_u32(list.depth as u32)?;
210            while let Some(entry) = list.pop(false) {
211                hash_data.write_u16(entry.name.len() as u16)?;
212                hash_data.write_struct(&entry.name, false, Encoding::Utf16LE, &None)?;
213                hash_data.write_u64(entry.index as u64 * 4)?;
214                hash_data.write_u32(entry.hash)?;
215            }
216        }
217        for i in 0..file_count {
218            hash_data.write_u32(i)?;
219        }
220        let mut hash_data = hash_data.into_inner();
221        encrypt(&mut hash_data, 0x0428)?;
222        let hash = QlieHash14 {
223            signature: *HASH_VER_1_4_SIGNATURE,
224            table_size: 256,
225            file_count: header.file_count,
226            index_size: header.file_count * 4,
227            hash_data_size: hash_data.len() as u32,
228            is_compressed: 0,
229            unk: [0; 32],
230            hash_data,
231        };
232        qkey.hash_size = hash.hash_data_size + 68;
233        let mut inner = Self {
234            writer,
235            encryption,
236            qkey,
237            header,
238            hash,
239            has_key_file,
240            entries,
241            key,
242            common_key: None,
243            compress_files: config.qlie_pack_compress_files,
244        };
245        if !has_key_file {
246            let key_path = config.qlie_pack_keyfile.as_ref().unwrap();
247            let key_data = std::fs::read(key_path)?;
248            inner.write_key(key_data)?;
249        }
250        Ok(inner)
251    }
252
253    fn write_key(&mut self, key_data: Vec<u8>) -> Result<()> {
254        let entry = &mut self.entries[0];
255        entry.size = key_data.len() as u32;
256        entry.offset = self.writer.stream_position()?;
257        entry.unpacked_size = entry.size;
258        entry.is_packed = 0;
259        entry.is_encrypted = 1;
260        self.common_key = Some(get_common_key(&key_data)?);
261        let hasher = Encryption31Hasher::new();
262        let size = entry.size;
263        let compute = EntryWriter {
264            entry,
265            inner: &mut self.writer,
266            hasher,
267        };
268        let mut encryptor =
269            Encryption31EncryptV1::new(compute, size, QLIE_KEY_FILE.to_string(), self.key)?;
270        encryptor.write_all(&key_data)?;
271        Ok(())
272    }
273}
274
275struct Writer<'a> {
276    inner: Box<dyn Write + 'a>,
277    mem: MemWriter,
278}
279
280impl std::fmt::Debug for Writer<'_> {
281    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
282        f.debug_struct("Writer").field("mem", &self.mem).finish()
283    }
284}
285
286impl<'a> Write for Writer<'a> {
287    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
288        self.mem.write(buf)
289    }
290
291    fn flush(&mut self) -> std::io::Result<()> {
292        self.mem.flush()
293    }
294}
295
296impl<'a> Seek for Writer<'a> {
297    fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
298        self.mem.seek(pos)
299    }
300
301    fn stream_position(&mut self) -> std::io::Result<u64> {
302        self.mem.stream_position()
303    }
304
305    fn rewind(&mut self) -> std::io::Result<()> {
306        self.mem.rewind()
307    }
308}
309
310impl<'a> Drop for Writer<'a> {
311    fn drop(&mut self) {
312        let _ = self.inner.write_all(&self.mem.data);
313        let _ = self.inner.flush();
314    }
315}
316
317struct Writer2<'a, T: Write + Seek> {
318    inner: &'a mut QliePackArchiveWriterV31<T>,
319    entry_idx: usize,
320    mem: MemWriter,
321    is_v1: bool,
322    compress_file: bool,
323}
324
325impl<'a, T: Write + Seek> Writer2<'a, T> {
326    fn close(&mut self) -> Result<()> {
327        let entry = &mut self.inner.entries[self.entry_idx];
328        entry.size = self.mem.data.len() as u32;
329        entry.offset = self.inner.writer.stream_position()?;
330        entry.unpacked_size = entry.size;
331        entry.is_packed = 0;
332        let hasher = Encryption31Hasher::new();
333        let size = entry.size;
334        let compute = EntryWriter {
335            entry,
336            inner: &mut self.inner.writer,
337            hasher,
338        };
339        if self.is_v1 {
340            compute.entry.is_encrypted = 1;
341            let name = compute.entry.name.clone();
342            let mut encryptor = Encryption31EncryptV1::new(compute, size, name, self.inner.key)?;
343            encryptor.write_all(&self.mem.data)?;
344            self.inner.common_key = Some(get_common_key(&self.mem.data)?);
345        } else {
346            compute.entry.is_encrypted = 2;
347            let data = if self.compress_file {
348                let compressed = compress(&self.mem.data)?;
349                if compressed.len() >= self.mem.data.len() {
350                    let mut nw = MemWriter::new();
351                    std::mem::swap(&mut self.mem, &mut nw);
352                    nw.into_inner()
353                } else {
354                    compute.entry.is_packed = 1;
355                    compute.entry.size = compressed.len() as u32;
356                    // {
357                    //     let mut decom = decompress(Box::new(MemReaderRef::new(&compressed)))?;
358                    //     let mut decompressed = Vec::new();
359                    //     decom.read_to_end(&mut decompressed)?;
360                    //     println!("File: {}, Original Size: {}, Compressed Size: {}", compute.entry.name, decompressed.len(), compressed.len());
361                    //     assert_eq!(self.mem.data.as_slice(), decompressed.as_slice());
362                    // }
363                    compressed
364                }
365            } else {
366                let mut nw = MemWriter::new();
367                std::mem::swap(&mut self.mem, &mut nw);
368                nw.into_inner()
369            };
370            let name = compute.entry.name.clone();
371            let common_key = self
372                .inner
373                .common_key
374                .as_ref()
375                .ok_or_else(|| anyhow::anyhow!("Common key is not available"))?;
376            let mut encryptor = Encryption31EncryptV2::new(
377                compute,
378                data.len() as u32,
379                name,
380                self.inner.key,
381                common_key.to_vec(),
382            )?;
383            encryptor.write_all(&data)?;
384        }
385        Ok(())
386    }
387}
388
389impl<T: Write + Seek> std::fmt::Debug for Writer2<'_, T> {
390    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
391        f.debug_struct("Writer").field("mem", &self.mem).finish()
392    }
393}
394
395impl<'a, T: Write + Seek> Write for Writer2<'a, T> {
396    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
397        self.mem.write(buf)
398    }
399
400    fn flush(&mut self) -> std::io::Result<()> {
401        self.mem.flush()
402    }
403}
404
405impl<'a, T: Write + Seek> Seek for Writer2<'a, T> {
406    fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
407        self.mem.seek(pos)
408    }
409
410    fn stream_position(&mut self) -> std::io::Result<u64> {
411        self.mem.stream_position()
412    }
413
414    fn rewind(&mut self) -> std::io::Result<()> {
415        self.mem.rewind()
416    }
417}
418
419impl<'a, T: Write + Seek> Drop for Writer2<'a, T> {
420    fn drop(&mut self) {
421        let _ = self.close();
422    }
423}
424
425struct EntryWriter<'a, T: Write> {
426    entry: &'a mut QlieEntry,
427    inner: T,
428    hasher: Encryption31Hasher,
429}
430
431impl<'a, T: Write> Write for EntryWriter<'a, T> {
432    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
433        let writed = self.inner.write(buf)?;
434        self.hasher
435            .update(&buf[..writed])
436            .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
437        Ok(writed)
438    }
439
440    fn flush(&mut self) -> std::io::Result<()> {
441        self.inner.flush()
442    }
443}
444
445impl<'a, T: Write> Drop for EntryWriter<'a, T> {
446    fn drop(&mut self) {
447        if let Ok(hash) = self.hasher.finalize() {
448            self.entry.hash = hash;
449        }
450    }
451}
452
453impl<T: Write + Seek> Archive for QliePackArchiveWriterV31<T> {
454    fn prelist<'a>(&'a self) -> Result<Option<Box<dyn Iterator<Item = Result<String>> + 'a>>> {
455        if !self.has_key_file {
456            Ok(None)
457        } else {
458            let iter = std::iter::once(Ok(QLIE_KEY_FILE.to_string()));
459            Ok(Some(Box::new(iter)))
460        }
461    }
462
463    fn new_file<'a>(
464        &'a mut self,
465        name: &str,
466        size: Option<u64>,
467    ) -> Result<Box<dyn WriteSeek + 'a>> {
468        let inner = self.new_file_non_seek(name, size)?;
469        Ok(Box::new(Writer {
470            inner,
471            mem: MemWriter::new(),
472        }))
473    }
474
475    fn new_file_non_seek<'a>(
476        &'a mut self,
477        name: &str,
478        size: Option<u64>,
479    ) -> Result<Box<dyn Write + 'a>> {
480        if self.common_key.is_none() {
481            if name != QLIE_KEY_FILE {
482                anyhow::bail!("Common key is not available before writing key file");
483            }
484            let entry_idx = self
485                .entries
486                .iter()
487                .position(|e| e.name == name)
488                .ok_or_else(|| anyhow::anyhow!("File {} not found in entries", name))?;
489            return Ok(Box::new(Writer2 {
490                inner: self,
491                entry_idx,
492                mem: MemWriter::new(),
493                is_v1: true,
494                // Disable compression for key file
495                compress_file: false,
496            }));
497        }
498        if size.is_none() || self.compress_files {
499            let entry_idx = self
500                .entries
501                .iter()
502                .position(|e| e.name == name)
503                .ok_or_else(|| anyhow::anyhow!("File {} not found in entries", name))?;
504            let compress_file = self.compress_files;
505            return Ok(Box::new(Writer2 {
506                inner: self,
507                entry_idx,
508                mem: MemWriter::new(),
509                is_v1: false,
510                compress_file,
511            }));
512        }
513        let entry_idx = self
514            .entries
515            .iter()
516            .position(|e| e.name == name)
517            .ok_or_else(|| anyhow::anyhow!("File {} not found in entries", name))?;
518        let entry = &mut self.entries[entry_idx];
519        entry.size = size.unwrap() as u32;
520        entry.offset = self.writer.stream_position()?;
521        entry.unpacked_size = entry.size;
522        entry.is_packed = 0;
523        entry.is_encrypted = 2;
524        let common_key = self
525            .common_key
526            .as_ref()
527            .ok_or_else(|| anyhow::anyhow!("Common key is not available"))?;
528        let hasher = Encryption31Hasher::new();
529        let size = entry.size;
530        let compute = EntryWriter {
531            entry,
532            inner: &mut self.writer,
533            hasher,
534        };
535        let encryptor = Encryption31EncryptV2::new(
536            compute,
537            size,
538            name.to_string(),
539            self.key,
540            common_key.to_vec(),
541        )?;
542        Ok(Box::new(encryptor))
543    }
544
545    fn write_header(&mut self) -> Result<()> {
546        self.header.index_offset = self.writer.stream_position()?;
547        for entry in &self.entries {
548            let name_length = entry.name.encode_utf16().count() as u16;
549            self.writer.write_u16(name_length)?;
550            let mut encoded = encode_string(Encoding::Utf16LE, &entry.name, true)?;
551            self.encryption
552                .encrypt_name(&mut encoded, self.key as i32)?;
553            self.writer.write_all(&encoded)?;
554            self.writer.write_u64(entry.offset)?;
555            self.writer.write_u32(entry.size)?;
556            self.writer.write_u32(entry.unpacked_size)?;
557            self.writer.write_u32(entry.is_packed)?;
558            self.writer.write_u32(entry.is_encrypted)?;
559            self.writer.write_u32(entry.hash)?;
560        }
561        self.writer
562            .write_struct(&self.hash, false, Encoding::Utf8, &None)?;
563        self.writer
564            .write_struct(&self.qkey, false, Encoding::Utf8, &None)?;
565        self.writer
566            .write_struct(&self.header, false, Encoding::Utf8, &None)?;
567        Ok(())
568    }
569}
570
571#[test]
572fn test_drop_mlist() {
573    use std::sync::Arc;
574    use std::sync::atomic::{AtomicI32, Ordering};
575    let t = Arc::new(AtomicI32::new(0));
576    struct Test {
577        value: i32,
578        t: Arc<AtomicI32>,
579    }
580
581    impl Test {
582        fn new(value: i32, t: Arc<AtomicI32>) -> Self {
583            Self { value, t }
584        }
585    }
586
587    impl Drop for Test {
588        fn drop(&mut self) {
589            self.t.fetch_add(self.value, Ordering::SeqCst);
590        }
591    }
592    {
593        let mut list: MList<Test> = MList::new();
594        list.push(Test::new(1, t.clone()), true);
595        list.push(Test::new(2, t.clone()), true);
596        list.push(Test::new(3, t.clone()), true);
597    }
598    let v = t.load(Ordering::SeqCst);
599    assert_eq!(v, 6);
600}
601
602#[test]
603fn test_mlist() {
604    let mut list = MList::new();
605    list.push(1, true);
606    list.push(2, true);
607    list.push(3, true);
608    assert_eq!(list.depth, 3);
609    assert_eq!(list.pop(false), Some(1));
610    assert_eq!(list.depth, 2);
611    assert_eq!(list.pop(false), Some(2));
612    assert_eq!(list.depth, 1);
613    assert_eq!(list.pop(false), Some(3));
614    assert_eq!(list.depth, 0);
615    assert_eq!(list.pop(false), None);
616}