msg_tool\scripts\kirikiri\archive\xp3/
mod.rs

1mod archive;
2#[allow(dead_code)]
3mod consts;
4mod crypt;
5mod pe;
6mod read;
7mod reader;
8mod segmenter;
9mod writer;
10
11use crate::ext::io::*;
12use crate::scripts::base::*;
13use crate::types::*;
14use anyhow::Result;
15use clap::ValueEnum;
16use consts::ZSTD_SIGNATURE;
17use crypt::Crypt;
18pub use crypt::get_supported_games;
19pub use crypt::get_supported_games_with_title;
20use flate2::read::ZlibDecoder;
21use overf::wrapping;
22pub use segmenter::SegmenterConfig;
23use std::io::{Read, Seek, SeekFrom, Write};
24use std::sync::{Arc, Mutex};
25use writer::Xp3ArchiveWriter;
26use zstd::stream::read::Decoder as ZstdDecoder;
27
28pub fn parse_segmenter_config(str: &str) -> Result<SegmenterConfig> {
29    let parts: Vec<&str> = str.split(':').collect();
30    if parts.is_empty() {
31        return Ok(SegmenterConfig::default());
32    }
33    match parts[0].to_lowercase().as_str() {
34        "none" => Ok(SegmenterConfig::None),
35        "cdc" => {
36            if parts.len() != 4 {
37                return Err(anyhow::anyhow!(
38                    "Invalid FastCDC segmenter config. Expected format: fastcdc,min_size,avg_size,max_size"
39                ));
40            }
41            let min_size = parse_size::parse_size(parts[1])?;
42            let avg_size = parse_size::parse_size(parts[2])?;
43            let max_size = parse_size::parse_size(parts[3])?;
44            if min_size == 0 || avg_size == 0 || max_size == 0 {
45                return Err(anyhow::anyhow!(
46                    "Invalid FastCDC segmenter config. Sizes must be greater than 0."
47                ));
48            }
49            if !(min_size <= avg_size && avg_size <= max_size) {
50                return Err(anyhow::anyhow!(
51                    "Invalid FastCDC segmenter config. Expected min_size <= avg_size <= max_size."
52                ));
53            }
54            Ok(SegmenterConfig::FastCdc {
55                min_size: min_size as u32,
56                avg_size: avg_size as u32,
57                max_size: max_size as u32,
58            })
59        }
60        "fixed" => {
61            if parts.len() != 2 {
62                return Err(anyhow::anyhow!(
63                    "Invalid Fixed segmenter config. Expected format: fixed,size"
64                ));
65            }
66            let size = parse_size::parse_size(parts[1])?;
67            if size == 0 {
68                return Err(anyhow::anyhow!(
69                    "Invalid Fixed segmenter config. Size must be greater than 0."
70                ));
71            }
72            Ok(SegmenterConfig::Fixed(size as usize))
73        }
74        "custom" => {
75            if parts.len() != 2 {
76                return Err(anyhow::anyhow!(
77                    "Invalid Fixed segmenter config. Expected format: custom:json_path"
78                ));
79            }
80            let json_path = parts[1];
81            let data = std::fs::read_to_string(json_path)?;
82            Ok(SegmenterConfig::Custom(serde_json::from_str(&data)?))
83        }
84        _ => Err(anyhow::anyhow!("Unknown segmenter type: {}", parts[0])),
85    }
86}
87
88#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
89/// Control file extract behavior
90pub enum FileHashOption {
91    /// Extract all files
92    Both,
93    /// Extract files that have name
94    WithName,
95    /// Extract files that don't have name
96    WithoutName,
97}
98
99impl Default for FileHashOption {
100    fn default() -> Self {
101        Self::Both
102    }
103}
104
105#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
106pub enum PathHashOption {
107    /// Append both hash and normal path name to file name
108    Both,
109    /// Append only normal path name to file name
110    NameOnly,
111}
112
113impl Default for PathHashOption {
114    fn default() -> Self {
115        Self::Both
116    }
117}
118
119#[derive(Debug)]
120/// Builder for Kirikiri XP3 Archive
121pub struct Xp3ArchiveBuilder {}
122
123impl Xp3ArchiveBuilder {
124    /// Create a new Kirikiri XP3 Archive Builder
125    pub fn new() -> Self {
126        Self {}
127    }
128}
129
130impl ScriptBuilder for Xp3ArchiveBuilder {
131    fn default_encoding(&self) -> Encoding {
132        Encoding::Utf8
133    }
134
135    fn default_archive_encoding(&self) -> Option<Encoding> {
136        Some(Encoding::Utf8)
137    }
138
139    fn build_script(
140        &self,
141        buf: Vec<u8>,
142        filename: &str,
143        _encoding: Encoding,
144        _archive_encoding: Encoding,
145        config: &ExtraConfig,
146        _archive: Option<&Box<dyn Script>>,
147    ) -> Result<Box<dyn Script + Send + Sync>> {
148        let mut base_offset = 0;
149        if buf.starts_with(b"MZ") {
150            base_offset = pe::get_base_offset(&buf)?;
151        }
152        Ok(Box::new(Xp3Archive::new(
153            MemReader::new(buf),
154            config,
155            filename,
156            base_offset,
157        )?))
158    }
159
160    fn build_script_from_file(
161        &self,
162        filename: &str,
163        _encoding: Encoding,
164        _archive_encoding: Encoding,
165        config: &ExtraConfig,
166        _archive: Option<&Box<dyn Script>>,
167    ) -> Result<Box<dyn Script + Send + Sync>> {
168        let mut file = std::fs::File::open(filename)?;
169        let mut base_offset = 0;
170        if file.peek_and_equal(b"MZ").is_ok() {
171            let mp = pelite::FileMap::open(filename)?;
172            base_offset = pe::get_base_offset(&mp)?;
173        }
174        Ok(Box::new(Xp3Archive::new(
175            file,
176            config,
177            filename,
178            base_offset,
179        )?))
180    }
181
182    fn build_script_from_reader<'a>(
183        &self,
184        mut reader: Box<dyn ReadSeek + Send + Sync + 'a>,
185        filename: &str,
186        _encoding: Encoding,
187        _archive_encoding: Encoding,
188        config: &ExtraConfig,
189        _archive: Option<&Box<dyn Script>>,
190    ) -> Result<Box<dyn Script + Send + Sync + 'a>> {
191        let mut base_offset = 0;
192        if reader.peek_and_equal(b"MZ").is_ok() {
193            let mut data = Vec::new();
194            let pos = reader.stream_position()?;
195            reader.read_to_end(&mut data)?;
196            reader.seek(SeekFrom::Start(pos))?;
197            base_offset = pe::get_base_offset(&data)?;
198        }
199        Ok(Box::new(Xp3Archive::new(
200            reader,
201            config,
202            filename,
203            base_offset,
204        )?))
205    }
206
207    fn extensions(&self) -> &'static [&'static str] {
208        &["xp3", "bin", "dat", "exe"]
209    }
210
211    fn script_type(&self) -> &'static ScriptType {
212        &ScriptType::KirikiriXp3
213    }
214
215    fn is_archive(&self) -> bool {
216        true
217    }
218
219    fn create_archive(
220        &self,
221        filename: &str,
222        files: &[&str],
223        _encoding: Encoding,
224        config: &ExtraConfig,
225    ) -> Result<Box<dyn Archive>> {
226        Ok(Box::new(Xp3ArchiveWriter::new(filename, files, config)?))
227    }
228
229    fn is_this_format(&self, filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
230        if buf_len >= 11 && buf.starts_with(consts::XP3_MAGIC) {
231            return Some(100);
232        }
233        if buf_len >= 2 && buf.starts_with(b"MZ") {
234            let p = std::path::Path::new(filename);
235            if p.exists() {
236                if let Ok(file) = pelite::FileMap::open(p) {
237                    if pe::get_base_offset(&file).is_ok() {
238                        return Some(100);
239                    }
240                }
241            }
242        }
243        None
244    }
245}
246
247#[derive(Debug)]
248/// Kirikiri XP3 Archive
249pub struct Xp3Archive<'a> {
250    archive: archive::Xp3Archive<'a>,
251    decrypt_simple_crypt: bool,
252    decompress_mdf: bool,
253    force_extract: bool,
254    force_decrypt: bool,
255}
256
257impl<'a> Xp3Archive<'a> {
258    pub fn new<T: Read + Seek + std::fmt::Debug + Send + Sync + 'a>(
259        stream: T,
260        config: &ExtraConfig,
261        filename: &str,
262        base_offset: u64,
263    ) -> Result<Self> {
264        let mut archive = archive::Xp3Archive::new(stream, config, filename, base_offset)?;
265        if config.xp3_debug_archive {
266            println!("Debug info for {}:\n{:#?}", filename, archive);
267            // Try flush stdout.
268            let _ = std::io::stdout().flush();
269        }
270        archive.entries.retain(|entry| {
271            let i = &entry.name;
272            !(i.find("$$$ This is a protected archive. $$$").is_some()
273                // Fate/stay night has spelling mistake. We also filter it.
274                || i.find("$$$ This is a protectet archive. $$$").is_some()
275                || (i.to_lowercase().ends_with(".nene") && entry.original_size == 0))
276        });
277        Ok(Self {
278            archive,
279            decrypt_simple_crypt: config.xp3_simple_crypt,
280            decompress_mdf: config.xp3_mdf_decompress,
281            force_extract: config.xp3_force_extract,
282            force_decrypt: config.xp3_force_decrypt,
283        })
284    }
285}
286
287impl<'b> Script for Xp3Archive<'b> {
288    fn default_output_script_type(&self) -> OutputScriptType {
289        OutputScriptType::Json
290    }
291
292    fn default_format_type(&self) -> FormatOptions {
293        FormatOptions::None
294    }
295
296    fn is_archive(&self) -> bool {
297        true
298    }
299
300    fn iter_archive_filename<'a>(
301        &'a self,
302    ) -> Result<Box<dyn Iterator<Item = Result<String>> + 'a>> {
303        Ok(Box::new(
304            self.archive
305                .entries
306                .iter()
307                .map(|entry| Ok(entry.name.clone())),
308        ))
309    }
310
311    fn open_file<'a>(&'a self, index: usize) -> Result<Box<dyn ArchiveContent + Send + Sync + 'a>> {
312        let index = self
313            .archive
314            .entries
315            .iter()
316            .nth(index)
317            .ok_or(anyhow::anyhow!("Index out of bounds: {}", index))?
318            .clone();
319        let crypt = self.archive.crypt.clone();
320        let skip_decrypt = index.is_encrypted() && !crypt.decrypt_supported();
321        if skip_decrypt {
322            if !self.force_extract {
323                return Err(anyhow::anyhow!(
324                    "The archive is encrypted with a method that is not supported by the current crypt implementation. You may need to specify a game title by using --xp3-game-title <title>."
325                ));
326            }
327        }
328        let mut entry = Entry::new(
329            self.archive.inner.clone(),
330            index,
331            self.archive.base_offset,
332            crypt,
333            skip_decrypt,
334            self.force_decrypt,
335        );
336        let mut header = [0u8; 16];
337        let header_len = entry.read(&mut header)?;
338        entry.rewind()?;
339        entry.script_type = detect_script_type(&entry.index.name, &header, header_len);
340        if self
341            .archive
342            .crypt
343            .need_filter(&entry.index.name, &header, header_len)
344        {
345            if self.archive.crypt.filter_seek_supported() {
346                let index = entry.index.clone();
347                let mut result = self.archive.crypt.filter_with_seek(entry)?;
348                let header_len = result.read(&mut header)?;
349                result.rewind()?;
350                let script_type = detect_script_type(&index.name, &header, header_len);
351                return Ok(Box::new(CustomFilterWithSeekEntry {
352                    inner: result,
353                    index,
354                    script_type,
355                }));
356            } else {
357                let index = entry.index.clone();
358                let mut result = self.archive.crypt.filter(entry)?;
359                let header_len = result.read(&mut header)?;
360                let script_type = detect_script_type(&index.name, &header, header_len);
361                let prefix = header[..header_len].to_vec();
362                return Ok(Box::new(CustomFilterEntry {
363                    inner: PrefixStream::new(prefix, result),
364                    index,
365                    script_type,
366                }));
367            }
368        }
369        if self.decrypt_simple_crypt
370            && header_len >= 5
371            && header[0] == 0xFE
372            && header[1] == 0xFE
373            && header[3] == 0xFF
374            && header[4] == 0xFE
375        {
376            let crypt = header[2];
377            if crypt == 2 {
378                let index = entry.index.clone();
379                return Ok(Box::new(SimpleCryptZlib::new(entry, index)?));
380            }
381            if matches!(crypt, 0 | 1) {
382                let index = entry.index.clone();
383                return Ok(Box::new(SimpleCrypt::new(entry, index, crypt)?));
384            }
385        }
386        if self.decompress_mdf
387            && header_len >= 4
388            && &header[0..4] == b"mdf\0"
389            && entry.index.original_size > 8
390        {
391            let index = entry.index.clone();
392            return Ok(Box::new(MdfEntry::new(entry, index)?));
393        }
394        Ok(Box::new(entry))
395    }
396}
397
398fn detect_script_type(filename: &str, buf: &[u8], buf_len: usize) -> Option<ScriptType> {
399    #[cfg(feature = "kirikiri-img")]
400    if buf_len >= 11 && libtlg_rs::is_valid_tlg(buf) {
401        return Some(ScriptType::KirikiriTlg);
402    }
403    if buf_len >= 8 && (buf.starts_with(b"TJS/ns0\0") || buf.starts_with(b"TJS/4s0\0")) {
404        return Some(ScriptType::KirikiriTjsNs0);
405    }
406    if buf_len >= 8 && buf.starts_with(b"TJS2100\0") {
407        return Some(ScriptType::KirikiriTjs2);
408    }
409    let extension = std::path::Path::new(filename)
410        .extension()
411        .and_then(|s| s.to_str())
412        .unwrap_or("")
413        .to_lowercase();
414    match extension.as_str() {
415        "ks" => Some(ScriptType::Kirikiri),
416        "scn" => Some(ScriptType::KirikiriScn),
417        #[cfg(feature = "emote-img")]
418        "dref" => Some(ScriptType::EmoteDref),
419        #[cfg(feature = "emote-img")]
420        "pimg" => Some(ScriptType::EmotePimg),
421        _ => None,
422    }
423}
424
425struct Entry<'a> {
426    reader: Arc<Mutex<Box<dyn ReadSeek + Send + Sync + 'a>>>,
427    index: archive::Xp3Entry,
428    crypt: Arc<Box<dyn Crypt + Send + Sync>>,
429    /// used to cache segment reader that can't seek. Such as decompressor reader or some decrypter reader.
430    cache: Option<Box<dyn Read + Send + Sync + 'a>>,
431    /// used to store decrypted stream of current segment when the cryptor support seek when decrypting.
432    crypt_stream: Option<Box<dyn ReadSeek + Send + Sync + 'a>>,
433    pos: u64,
434    base_offset: u64,
435    entries_pos: Vec<u64>,
436    script_type: Option<ScriptType>,
437    skip_decrypt: bool,
438    force_decrypt: bool,
439}
440
441#[automatically_derived]
442impl<'a> std::fmt::Debug for Entry<'a> {
443    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
444        f.debug_struct("Entry")
445            .field("reader", &self.reader)
446            .field("index", &self.index)
447            .field("crypt", &self.crypt)
448            .field("cache", &self.cache.is_some())
449            .field("crypt_stream", &self.crypt_stream)
450            .field("pos", &self.pos)
451            .field("base_offset", &self.base_offset)
452            .field("entries_pos", &self.entries_pos)
453            .field("script_type", &self.script_type)
454            .field("skip_decrypt", &self.skip_decrypt)
455            .finish()
456    }
457}
458
459impl<'a> Entry<'a> {
460    fn new(
461        reader: Arc<Mutex<Box<dyn ReadSeek + Send + Sync + 'a>>>,
462        index: archive::Xp3Entry,
463        base_offset: u64,
464        crypt: Arc<Box<dyn Crypt + Send + Sync>>,
465        skip_decrypt: bool,
466        force_decrypt: bool,
467    ) -> Self {
468        let mut pos = 0;
469        let entries_pos = index
470            .segments
471            .iter()
472            .map(|seg| {
473                let p = pos;
474                pos += seg.original_size;
475                p
476            })
477            .collect();
478        Self {
479            reader,
480            index,
481            cache: None,
482            pos: 0,
483            entries_pos,
484            script_type: None,
485            base_offset,
486            crypt,
487            crypt_stream: None,
488            skip_decrypt,
489            force_decrypt,
490        }
491    }
492
493    fn new2(
494        reader: Arc<Mutex<Box<dyn ReadSeek + Send + Sync + 'a>>>,
495        index: archive::Xp3Entry,
496        base_offset: u64,
497        crypt: Arc<Box<dyn Crypt + Send + Sync>>,
498    ) -> Self {
499        Self::new(reader, index, base_offset, crypt, false, false)
500    }
501}
502
503impl<'b> ArchiveContent for Entry<'b> {
504    fn name(&self) -> &str {
505        &self.index.name
506    }
507
508    fn size(&self) -> Option<u64> {
509        Some(self.index.archived_size)
510    }
511
512    fn to_data<'a>(&'a mut self) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
513        Ok(Box::new(self))
514    }
515
516    fn script_type(&self) -> Option<&ScriptType> {
517        self.script_type.as_ref()
518    }
519}
520
521impl<'a> Read for Entry<'a> {
522    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
523        if self.pos >= self.index.original_size {
524            self.cache.take();
525            self.crypt_stream.take();
526            return Ok(0);
527        }
528        if let Some(cache) = self.cache.as_mut() {
529            let readed = cache.read(buf)?;
530            if readed > 0 {
531                self.pos += readed as u64;
532                return Ok(readed);
533            }
534            self.cache.take();
535        }
536        if let Some(crypt_stream) = self.crypt_stream.as_mut() {
537            let readed = crypt_stream.read(buf)?;
538            if readed > 0 {
539                self.pos += readed as u64;
540                return Ok(readed);
541            }
542            self.crypt_stream.take();
543        }
544        let seg_index = match self.entries_pos.binary_search(&self.pos) {
545            Ok(i) => i,
546            Err(i) => {
547                if i == 0 {
548                    0
549                } else {
550                    i - 1
551                }
552            }
553        };
554        let seg = &self.index.segments[seg_index];
555        let start_pos = seg.start + self.base_offset;
556        let seg_pos = self.entries_pos[seg_index];
557        let skip_pos = self.pos - seg_pos;
558        let read_size = seg.archived_size;
559        if !self.skip_decrypt
560            && (self.index.is_encrypted() || (self.force_decrypt && self.crypt.decrypt_supported()))
561        {
562            if seg.is_compressed || !self.crypt.decrypt_seek_supported() {
563                let mut cache: Box<dyn Read + Send + Sync> = if seg.is_compressed {
564                    let mut inner =
565                        MutexWrapper::new(self.reader.clone(), start_pos).take(read_size);
566                    let decompressed = if inner.peek_and_equal(ZSTD_SIGNATURE).is_ok() {
567                        Box::new(ZstdDecoder::new(inner)?) as Box<dyn Read + Send + Sync>
568                    } else {
569                        Box::new(ZlibDecoder::new(inner)) as Box<dyn Read + Send + Sync>
570                    };
571                    let decrypted =
572                        self.crypt
573                            .decrypt(&self.index, seg, decompressed)
574                            .map_err(|e| {
575                                std::io::Error::new(
576                                    std::io::ErrorKind::Other,
577                                    format!("Decryption failed: {}", e),
578                                )
579                            })?;
580                    Box::new(decrypted) as Box<dyn Read + Send + Sync>
581                } else {
582                    let inner = MutexWrapper::new(self.reader.clone(), start_pos).take(read_size);
583                    let decrypted = self
584                        .crypt
585                        .decrypt(&self.index, seg, Box::new(inner))
586                        .map_err(|e| {
587                            std::io::Error::new(
588                                std::io::ErrorKind::Other,
589                                format!("Decryption failed: {}", e),
590                            )
591                        })?;
592                    Box::new(decrypted) as Box<dyn Read + Send + Sync>
593                };
594                if skip_pos != 0 {
595                    let mut e = EmptyWriter::new();
596                    std::io::copy(&mut (&mut cache).take(skip_pos), &mut e)?; // skip
597                }
598                let readed = cache.read(buf)?;
599                self.pos += readed as u64;
600                self.cache = Some(cache);
601                return Ok(readed);
602            } else {
603                let inner = MutexWrapper::new(self.reader.clone(), start_pos).take(read_size);
604                let mut decrypted = self
605                    .crypt
606                    .decrypt_with_seek(&self.index, seg, Box::new(inner))
607                    .map_err(|e| {
608                        std::io::Error::new(
609                            std::io::ErrorKind::Other,
610                            format!("Decryption failed: {}", e),
611                        )
612                    })?;
613                if skip_pos != 0 {
614                    let mut e = EmptyWriter::new();
615                    std::io::copy(&mut (&mut decrypted).take(skip_pos), &mut e)?; // skip
616                }
617                let readed = decrypted.read(buf)?;
618                self.pos += readed as u64;
619                self.crypt_stream = Some(decrypted);
620                return Ok(readed);
621            }
622        }
623        if seg.is_compressed {
624            let mut inner = MutexWrapper::new(self.reader.clone(), start_pos).take(read_size);
625            let mut cache = if inner.peek_and_equal(ZSTD_SIGNATURE).is_ok() {
626                Box::new(ZstdDecoder::new(inner)?) as Box<dyn Read + Send + Sync>
627            } else {
628                Box::new(ZlibDecoder::new(inner)) as Box<dyn Read + Send + Sync>
629            };
630            if skip_pos != 0 {
631                let mut e = EmptyWriter::new();
632                std::io::copy(&mut (&mut cache).take(skip_pos), &mut e)?; // skip
633            }
634            let readed = cache.read(buf)?;
635            self.pos += readed as u64;
636            self.cache = Some(cache);
637            Ok(readed)
638        } else {
639            let mut lock = MutexWrapper::new(self.reader.clone(), start_pos + skip_pos);
640            let readed = (&mut lock).take(read_size - skip_pos).read(buf)?;
641            self.pos += readed as u64;
642            Ok(readed)
643        }
644    }
645}
646
647impl<'a> Seek for Entry<'a> {
648    fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
649        let new_pos = match pos {
650            SeekFrom::Start(p) => p,
651            SeekFrom::End(offset) => {
652                if offset < 0 {
653                    if (-offset) as u64 > self.index.original_size {
654                        return Err(std::io::Error::new(
655                            std::io::ErrorKind::InvalidInput,
656                            "Seek from end exceeds file length",
657                        ));
658                    }
659                    self.index.original_size - (-offset) as u64
660                } else {
661                    self.index.original_size + offset as u64
662                }
663            }
664            SeekFrom::Current(offset) => {
665                if offset < 0 {
666                    if (-offset) as u64 > self.pos {
667                        return Err(std::io::Error::new(
668                            std::io::ErrorKind::InvalidInput,
669                            "Seek from current exceeds file start",
670                        ));
671                    }
672                    self.pos - (-offset) as u64
673                } else {
674                    self.pos + offset as u64
675                }
676            }
677        };
678        if let Some(cache) = self.cache.as_mut() {
679            let old_seg_index = match self.entries_pos.binary_search(&self.pos) {
680                Ok(i) => i,
681                Err(i) => {
682                    if i == 0 {
683                        0
684                    } else {
685                        i - 1
686                    }
687                }
688            };
689            let new_seg_index = match self.entries_pos.binary_search(&new_pos) {
690                Ok(i) => i,
691                Err(i) => {
692                    if i == 0 {
693                        0
694                    } else {
695                        i - 1
696                    }
697                }
698            };
699            if old_seg_index != new_seg_index {
700                self.cache.take();
701            } else {
702                if new_pos >= self.pos {
703                    let skip_pos = new_pos - self.pos;
704                    let mut e = EmptyWriter::new();
705                    std::io::copy(&mut cache.take(skip_pos), &mut e)?; // skip
706                } else {
707                    self.cache.take();
708                }
709            }
710        }
711        if let Some(crypt_stream) = self.crypt_stream.as_mut() {
712            let old_seg_index = match self.entries_pos.binary_search(&self.pos) {
713                Ok(i) => i,
714                Err(i) => {
715                    if i == 0 {
716                        0
717                    } else {
718                        i - 1
719                    }
720                }
721            };
722            let new_seg_index = match self.entries_pos.binary_search(&new_pos) {
723                Ok(i) => i,
724                Err(i) => {
725                    if i == 0 {
726                        0
727                    } else {
728                        i - 1
729                    }
730                }
731            };
732            if old_seg_index != new_seg_index {
733                self.crypt_stream.take();
734            } else {
735                let offset = new_pos as i64 - self.pos as i64;
736                crypt_stream.seek(SeekFrom::Current(offset))?;
737            }
738        }
739        self.pos = new_pos;
740        Ok(self.pos)
741    }
742
743    fn rewind(&mut self) -> std::io::Result<()> {
744        self.pos = 0;
745        self.cache.take();
746        self.crypt_stream.take();
747        Ok(())
748    }
749
750    fn stream_position(&mut self) -> std::io::Result<u64> {
751        Ok(self.pos)
752    }
753}
754
755struct SimpleCryptZlib<'a> {
756    inner: PrefixStream<ZlibDecoder<StreamRegion<Entry<'a>>>>,
757    index: archive::Xp3Entry,
758}
759
760impl<'a> SimpleCryptZlib<'a> {
761    fn new(mut entry: Entry<'a>, index: archive::Xp3Entry) -> Result<Self> {
762        entry.seek(SeekFrom::Start(0x15))?;
763        let entry = StreamRegion::new(entry, 0x15, index.original_size)?;
764        let inner = PrefixStream::new(vec![0xFF, 0xFE], ZlibDecoder::new(entry));
765        Ok(Self { inner, index })
766    }
767}
768
769impl<'a> ArchiveContent for SimpleCryptZlib<'a> {
770    fn name(&self) -> &str {
771        &self.index.name
772    }
773
774    fn size(&self) -> Option<u64> {
775        Some(self.index.original_size)
776    }
777}
778
779impl<'a> Read for SimpleCryptZlib<'a> {
780    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
781        self.inner.read(buf)
782    }
783}
784
785#[derive(Debug)]
786struct SimpleCryptInner<'a> {
787    inner: StreamRegion<Entry<'a>>,
788    crypt: u8,
789}
790
791impl<'a> SimpleCryptInner<'a> {
792    fn new(mut entry: Entry<'a>, crypt: u8) -> Result<Self> {
793        entry.seek(SeekFrom::Start(5))?;
794        let size = entry.index.original_size;
795        let entry = StreamRegion::new(entry, 5, size)?;
796        Ok(Self {
797            inner: entry,
798            crypt,
799        })
800    }
801}
802
803impl<'a> Read for SimpleCryptInner<'a> {
804    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
805        let readed = self.inner.read(buf)?;
806        match self.crypt {
807            0 => {
808                for b in &mut buf[..readed] {
809                    let ch = *b as u16;
810                    if ch >= 20 {
811                        *b = wrapping! {ch ^ (((ch & 0xfe) << 8) ^ 1)} as u8;
812                    }
813                }
814            }
815            1 => {
816                for b in &mut buf[..readed] {
817                    let mut ch = *b as u32;
818                    ch = wrapping! {((ch & 0xaaaaaaaa) >> 1) | ((ch & 0x55555555) << 1)};
819                    *b = ch as u8;
820                }
821            }
822            _ => {}
823        }
824        Ok(readed)
825    }
826}
827
828impl<'a> Seek for SimpleCryptInner<'a> {
829    fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
830        self.inner.seek(pos)
831    }
832
833    fn rewind(&mut self) -> std::io::Result<()> {
834        self.inner.rewind()
835    }
836
837    fn stream_position(&mut self) -> std::io::Result<u64> {
838        self.inner.stream_position()
839    }
840}
841
842#[derive(Debug)]
843struct SimpleCrypt<'a> {
844    inner: PrefixStream<SimpleCryptInner<'a>>,
845    index: archive::Xp3Entry,
846}
847
848impl<'a> SimpleCrypt<'a> {
849    fn new(entry: Entry<'a>, index: archive::Xp3Entry, crypt: u8) -> Result<Self> {
850        let inner = PrefixStream::new(vec![0xFF, 0xFE], SimpleCryptInner::new(entry, crypt)?);
851        Ok(Self { inner, index })
852    }
853}
854
855impl<'b> ArchiveContent for SimpleCrypt<'b> {
856    fn name(&self) -> &str {
857        &self.index.name
858    }
859
860    fn size(&self) -> Option<u64> {
861        Some(self.index.original_size)
862    }
863
864    fn to_data<'a>(&'a mut self) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
865        Ok(Box::new(self))
866    }
867}
868
869impl<'a> Read for SimpleCrypt<'a> {
870    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
871        self.inner.read(buf)
872    }
873}
874
875impl<'a> Seek for SimpleCrypt<'a> {
876    fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
877        self.inner.seek(pos)
878    }
879
880    fn rewind(&mut self) -> std::io::Result<()> {
881        self.inner.rewind()
882    }
883
884    fn stream_position(&mut self) -> std::io::Result<u64> {
885        self.inner.stream_position()
886    }
887}
888
889#[derive(Debug)]
890struct MdfEntry<'a> {
891    inner: ZlibDecoder<StreamRegion<Entry<'a>>>,
892    index: archive::Xp3Entry,
893}
894
895impl<'a> MdfEntry<'a> {
896    fn new(mut entry: Entry<'a>, index: archive::Xp3Entry) -> Result<Self> {
897        entry.seek(SeekFrom::Start(8))?;
898        let entry = StreamRegion::new(entry, 8, index.original_size)?;
899        let inner = ZlibDecoder::new(entry);
900        Ok(Self { inner, index })
901    }
902}
903impl<'a> ArchiveContent for MdfEntry<'a> {
904    fn name(&self) -> &str {
905        &self.index.name
906    }
907
908    fn size(&self) -> Option<u64> {
909        Some(self.index.original_size)
910    }
911}
912
913impl<'a> Read for MdfEntry<'a> {
914    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
915        self.inner.read(buf)
916    }
917}
918
919#[derive(Debug)]
920struct CustomFilterEntry<'a> {
921    inner: PrefixStream<Box<dyn ReadDebug + Send + Sync + 'a>>,
922    index: archive::Xp3Entry,
923    script_type: Option<ScriptType>,
924}
925
926impl<'a> Read for CustomFilterEntry<'a> {
927    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
928        self.inner.read(buf)
929    }
930}
931
932impl<'a> ArchiveContent for CustomFilterEntry<'a> {
933    fn name(&self) -> &str {
934        &self.index.name
935    }
936
937    fn size(&self) -> Option<u64> {
938        Some(self.index.original_size)
939    }
940
941    fn script_type(&self) -> Option<&ScriptType> {
942        self.script_type.as_ref()
943    }
944}
945
946#[derive(Debug)]
947struct CustomFilterWithSeekEntry<'a> {
948    inner: Box<dyn ReadSeek + Send + Sync + 'a>,
949    index: archive::Xp3Entry,
950    script_type: Option<ScriptType>,
951}
952
953impl<'a> Read for CustomFilterWithSeekEntry<'a> {
954    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
955        self.inner.read(buf)
956    }
957}
958
959impl<'a> Seek for CustomFilterWithSeekEntry<'a> {
960    fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
961        self.inner.seek(pos)
962    }
963
964    fn rewind(&mut self) -> std::io::Result<()> {
965        self.inner.rewind()
966    }
967
968    fn stream_position(&mut self) -> std::io::Result<u64> {
969        self.inner.stream_position()
970    }
971}
972
973impl<'b> ArchiveContent for CustomFilterWithSeekEntry<'b> {
974    fn name(&self) -> &str {
975        &self.index.name
976    }
977
978    fn size(&self) -> Option<u64> {
979        Some(self.index.original_size)
980    }
981
982    fn script_type(&self) -> Option<&ScriptType> {
983        self.script_type.as_ref()
984    }
985
986    fn to_data<'a>(&'a mut self) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
987        Ok(Box::new(self))
988    }
989}