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
78fn get_pack_file_index_length(view: &FileView) -> u32 {
80 LittleEndian::read_u32(&view.read(0x08..0x0C).unwrap().to_vec())
81}
82
83fn get_pack_file_index_size(view: &FileView) -> u32 {
85 LittleEndian::read_u32(&view.read(0x0C..0x10).unwrap().to_vec())
86}
87
88fn get_packed_file_index_length(view: &FileView) -> u32 {
90 LittleEndian::read_u32(&view.read(0x10..0x14).unwrap().to_vec())
91}
92
93fn 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 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 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 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}