1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use std::io;

use crate::archive::{Archivable, ArchivableWith, Archive};
use crate::constants::*;
use crate::PakVersion;

pub const FLAG_ENCRYPTED: u8 = 0x01;
pub const FLAG_DELETED: u8 = 0x02;

/// FPakEntry archivable
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct PakEntry {
    /// Offset into pak file where the file is stored.
    pub offset: u64,
    /// Serialized file size.
    pub size: u64,
    /// Uncompressed file size.
    pub uncompressed_size: u64,
    /// Compressed file SHA1 value.
    pub hash: [u8; 20],
    /// Array of compression blocks that describe how to decompress this pak entry.
    pub compression_blocks: Vec<PakCompressedBlock>,
    /// Size of a compressed block in the file.
    pub compression_block_size: u32,
    /// Index into the compression methods in this pakfile.
    pub compression_method_index: u32,
    /// Pak entry flags.
    pub flags: u8,
}

impl PakEntry {
    pub fn is_encrypted(&self) -> bool {
        (self.flags & FLAG_ENCRYPTED) == FLAG_ENCRYPTED
    }

    pub fn is_deleted(&self) -> bool {
        (self.flags & FLAG_DELETED) == FLAG_DELETED
    }
}
impl ArchivableWith<PakVersion> for PakEntry {
    fn ser_de_with<A: Archive>(&mut self, ar: &mut A, version: PakVersion) -> io::Result<()> {
        self.offset.ser_de(ar)?;
        self.size.ser_de(ar)?;
        self.uncompressed_size.ser_de(ar)?;
        match version {
            PakVersion::FNameBasedCompressionMethod422 => {
                let mut idx = 0u8;
                idx.ser_de(ar)?;
                self.compression_method_index = From::from(idx);
            }
            ver if ver < PakVersion::FNameBasedCompressionMethod422 => {
                let mut legacy_compression_method = 0;
                legacy_compression_method.ser_de(ar)?;
                self.compression_method_index = match legacy_compression_method {
                    x if x == COMPRESS_NONE => 0,
                    x if (x & COMPRESS_ZLIB) > 0 => 1,
                    x if (x & COMPRESS_GZIP) > 0 => 2,
                    x if (x & COMPRESS_CUSTOM) > 0 => 3,
                    _ => {
                        return Err(io::Error::new(
                            io::ErrorKind::Other,
                            "unknown legacy compression type",
                        ))
                    }
                };
            }
            _ => {
                self.compression_method_index.ser_de(ar)?;
            }
        }
        if version <= PakVersion::Initial {
            let mut ticks = 0u64;
            ticks.ser_de(ar)?;
        }

        self.hash.ser_de(ar)?;
        if version >= PakVersion::CompressionEncryption {
            if self.compression_method_index != 0 {
                self.compression_blocks.ser_de(ar)?;
            }
            self.flags.ser_de(ar)?;
            self.compression_block_size.ser_de(ar)?;
        }
        Ok(())
    }
}

#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct PakCompressedBlock {
    pub compressed_start: u64,
    pub compressed_end: u64,
}

impl Archivable for PakCompressedBlock {
    fn ser_de<A: Archive>(&mut self, ar: &mut A) -> io::Result<()> {
        self.compressed_start.ser_de(ar)?;
        self.compressed_end.ser_de(ar)?;
        Ok(())
    }
}