tw_pack_lib/
parse.rs

1use std::fmt;
2use std::fs::File;
3use std::ops::Range;
4use std::sync::Mutex;
5
6use byteorder::LittleEndian;
7use byteorder::ByteOrder;
8use cached_file_view::FileView;
9
10use error::{Error, Result};
11
12pub struct PackIndexIterator<'a> {
13    view: &'a FileView,
14    next_item: u32,
15    index_position: u32,
16    content_position: u32
17}
18
19#[derive(Clone)]
20pub struct LazyLoadingPackedFile {
21    pub file_view: FileView,
22    pub range: Range<u64>,
23    pub is_encrypted: bool
24}
25
26impl fmt::Display for ::PackFile {
27    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28        write!(f, "PackFile (encrypted index: {}, encrypted content: {}, padding: {}, timestamped files: {})", has_encrypted_index(&self.view), has_encrypted_content(&self.view), has_padding(&self.view), has_index_with_timestamps(&self.view))
29    }
30}
31
32impl fmt::Display for ::PackedFile {
33    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
34        write!(f, "PackedFile {{ timestamp: {:?}, name: \"{}\" }}", self.timestamp, self.path)
35    }
36}
37
38impl<'a> IntoIterator for &'a ::PackFile {
39    type Item = ::PackedFile;
40    type IntoIter = PackIndexIterator<'a>;
41    fn into_iter(self) -> Self::IntoIter {
42        let payload_position = if has_padding(&self.view) {
43            let unpadded = get_static_header_size(&self.view) + get_pack_file_index_size(&self.view) + get_packed_file_index_size(&self.view);
44            let remainder = unpadded % 8;
45            if remainder > 0 {
46                unpadded + 8 - remainder
47            } else {
48                unpadded
49            }
50        } else {
51            get_static_header_size(&self.view) + get_pack_file_index_size(&self.view) + get_packed_file_index_size(&self.view)
52        };
53        PackIndexIterator {
54            view: &self.view,
55            next_item: get_packed_file_index_length(&self.view),
56            index_position: (get_static_header_size(&self.view) + get_pack_file_index_size(&self.view)),
57            content_position: payload_position
58        }
59    }
60}
61
62pub fn get_preamble(view: &FileView) -> u32 {
63    LittleEndian::read_u32(&view.read(0x00..0x04).unwrap().to_vec())
64}
65
66pub fn get_file_type(view: &FileView) -> u32 {
67    LittleEndian::read_u32(&view.read(0x04..0x08).unwrap().to_vec()) & 0xf
68}
69
70pub fn get_bitmask(view: &FileView) -> ::PFHFlags {
71    ::PFHFlags::from_bits_truncate(LittleEndian::read_u32(&view.read(0x04..0x08).unwrap().to_vec()) & !0xf)
72}
73
74pub fn get_timestamp(view: &FileView) -> u32 {
75    LittleEndian::read_u32(&view.read(0x18..0x1C).unwrap().to_vec())
76}
77
78/// Get the amount of items in the PackFile Index.
79fn get_pack_file_index_length(view: &FileView) -> u32 {
80    LittleEndian::read_u32(&view.read(0x08..0x0C).unwrap().to_vec())
81}
82
83/// Get the size in bytes of the PackFile Index.
84fn get_pack_file_index_size(view: &FileView) -> u32 {
85    LittleEndian::read_u32(&view.read(0x0C..0x10).unwrap().to_vec())
86}
87
88/// Get the amount of items in the PackedFile Index.
89fn get_packed_file_index_length(view: &FileView) -> u32 {
90    LittleEndian::read_u32(&view.read(0x10..0x14).unwrap().to_vec())
91}
92
93/// Get the size in bytes of the PackedFile Index.
94fn get_packed_file_index_size(view: &FileView) -> u32 {
95    LittleEndian::read_u32(&view.read(0x14..0x18).unwrap().to_vec())
96}
97
98fn _get_signature_offset(view: &FileView) -> u32 {
99    LittleEndian::read_u32(&view.read(0x28..0x2C).unwrap().to_vec())
100}
101
102fn get_static_header_size(raw_data: &FileView) -> u32 {
103    if get_preamble(&raw_data) == ::PFH4_PREAMBLE {
104        0x1C
105    } else if get_preamble(&raw_data) == ::PFH5_PREAMBLE {
106        if has_big_header(&raw_data) {
107            0x30
108        } else {
109            0x1C
110        }
111    } else {
112        panic!("Invalid preamble! ###{:?}", get_preamble(&raw_data))
113    }
114}
115
116fn has_big_header(view: &FileView) -> bool {
117    get_bitmask(&view).contains(::PFHFlags::HAS_BIG_HEADER)
118}
119
120fn has_encrypted_index(view: &FileView) -> bool {
121    get_bitmask(&view).contains(::PFHFlags::HAS_ENCRYPTED_INDEX)
122}
123
124fn has_index_with_timestamps(view: &FileView) -> bool {
125    get_bitmask(&view).contains(::PFHFlags::HAS_INDEX_WITH_TIMESTAMPS)
126}
127
128fn has_encrypted_content(view: &FileView) -> bool {
129    get_bitmask(&view).contains(::PFHFlags::HAS_ENCRYPTED_CONTENT)
130}
131
132fn has_padding(raw_data: &FileView) -> bool {
133    get_preamble(&raw_data) == ::PFH5_PREAMBLE && has_encrypted_content(&raw_data)
134}
135
136pub fn get_pack_file_index(view: &FileView) -> Vec<String> {
137    let raw_index = view.read(get_static_header_size(view) as u64..(get_static_header_size(view) + get_pack_file_index_size(view)) as u64).unwrap().to_vec();
138    let mut pack_file_index = vec![];
139    let mut pos: usize = 0;
140    for _ in 0..get_pack_file_index_length(view) {
141        let mut pack_file_name = String::new();
142
143        // For each byte...
144        loop {
145            let character = raw_index[pos];
146            if character == 0 {
147                pack_file_index.push(pack_file_name);
148                pos += 1;
149                break;
150            } else {
151                pack_file_name.push(character as char);
152                pos += 1;
153            }
154        }
155    }
156    pack_file_index
157}
158
159
160impl<'a> PackIndexIterator<'a> {
161    fn read_index_u32(&self) -> Result<u32> {
162        Ok(LittleEndian::read_u32(&self.view.read(self.index_position as u64..self.index_position as u64 + 4 as u64)?.to_vec()))
163    }
164
165    fn get_next(&mut self) -> Result<::PackedFile> {
166        if self.next_item >= 1 {
167            self.next_item -= 1;
168
169            // read 4 bytes item length
170            let mut item_length = self.read_index_u32()?;
171            item_length = if has_encrypted_index(&self.view) {
172                ::crypto::decrypt_index_item_file_length(self.next_item, item_length)
173            } else {
174                item_length
175            };
176            self.index_position = self.index_position.checked_add(4).ok_or(Error::IndexIteratorError)?;
177
178            // read 4 bytes whatever, if present
179            let timestamp = if has_index_with_timestamps(&self.view) {
180                let d = self.read_index_u32()?;
181                self.index_position = self.index_position.checked_add(4).ok_or(Error::IndexIteratorError)?;
182                Some(d)
183            } else {
184                None
185            };
186
187            if get_preamble(&self.view) == ::PFH5_PREAMBLE && !has_big_header(&self.view) {
188                self.index_position = self.index_position.checked_add(1).ok_or(Error::IndexIteratorError)?;
189            }
190
191            let remaining_index_size = get_packed_file_index_size(&self.view) - (self.index_position - get_static_header_size(&self.view) - get_pack_file_index_size(&self.view));
192            let (file_path, len) = if has_encrypted_index(&self.view) {
193                ::crypto::decrypt_index_item_filename(&self.view.read(self.index_position as u64..(self.index_position + remaining_index_size) as u64)?.to_vec(), item_length as u8)
194            } else {
195                let mut  buf = vec!();
196                let mut i = 0;
197                loop {
198                    let c = self.view.read((self.index_position + i) as u64..(self.index_position + i + 1) as u64)?.to_vec()[0];
199                    i += 1;
200                    if c == 0 {
201                        break;
202                    }
203                    buf.push(c);
204                    if i >= remaining_index_size {
205                        return Err(Error::IndexIteratorError);
206                    }
207                }
208                (buf, i)
209            };
210            self.index_position += len;
211
212            let padded_item_length = if has_encrypted_content(&self.view) {
213                let remainder = item_length % 8;
214                if remainder > 0 {
215                    item_length.checked_add(8-remainder).ok_or(Error::IndexIteratorError)?
216                } else {
217                    item_length
218                }
219            } else {
220                item_length
221            };
222
223            let start = self.content_position as u64;
224            let end = (self.content_position + item_length) as u64;
225
226            if has_padding(&self.view) {
227                self.content_position = self.content_position.checked_add(padded_item_length).ok_or(Error::IndexIteratorError)?;
228            } else {
229                self.content_position = self.content_position.checked_add(item_length).ok_or(Error::IndexIteratorError)?;
230            }
231
232            Ok(::PackedFile {
233                timestamp,
234                path: String::from_utf8(file_path).map_err(|_| Error::IndexIteratorError)?,
235                data: Mutex::new(::PackedFileData::LazyLoading(LazyLoadingPackedFile {
236                        file_view: (*self.view).clone(),
237                        is_encrypted: has_encrypted_content(&self.view),
238                        range: start..end
239                    })
240                )
241            })
242        } else {
243            Err(Error::IndexIteratorError)
244        }
245    }
246}
247
248impl<'a> Iterator for PackIndexIterator<'a> {
249    type Item = ::PackedFile;
250    fn next(&mut self) -> Option<::PackedFile> {
251        match self.get_next() {
252            Ok(item) => Some(item),
253            Err(_) => None
254        }
255    }
256}
257
258pub fn parse_pack(input_file: File) -> Result<::PackFile> {
259    let file_view = FileView::new(input_file)?;
260
261    if file_view.length < 4 || file_view.length < get_static_header_size(&file_view) as u64 {
262        return Err(Error::InvalidFileError)
263    }
264
265    if file_view.length < (get_static_header_size(&file_view) + get_pack_file_index_size(&file_view)) as u64 {
266        return Err(Error::InvalidFileError)
267    }
268
269    if get_preamble(&file_view) == ::PFH3_PREAMBLE || get_preamble(&file_view) == ::PFH2_PREAMBLE || get_preamble(&file_view) == ::PFH0_PREAMBLE {
270        return Err(Error::UnsupportedPackFile)
271    }
272
273    if get_preamble(&file_view) != ::PFH5_PREAMBLE && get_preamble(&file_view) != ::PFH4_PREAMBLE {
274        return Err(Error::InvalidHeaderError)
275    }
276
277    if get_file_type(&file_view) > 4 {
278        return Err(Error::InvalidHeaderError)
279    }
280
281    if !::PFHFlags::from_bits(LittleEndian::read_u32(&file_view.read(0x04..0x08)?.to_vec()) & !0xf).is_some() {
282        eprintln!("Warning: Bitmask has unknown bits set")
283    }
284
285    let begin = file_view.read(0..(get_static_header_size(&file_view) as u64 + get_pack_file_index_size(&file_view) as u64 + get_packed_file_index_size(&file_view) as u64))?;
286    Ok(::PackFile {
287        view: file_view,
288        begin: begin
289    })
290}