1use std::collections::HashMap;
2use std::convert::AsRef;
3use std::fs::File;
4use std::io::Read;
5use std::path::Path;
6use std::str;
7
8use libflate::zlib;
9use memmap::MmapOptions;
10use nom::{bytes::complete::*, number::complete::*};
11
12use crate::error::{Error, Result};
13
14#[derive(Debug)]
16pub struct Pigg {
17 files: HashMap<String, FileHeader>,
18 mmap: memmap::Mmap,
19}
20
21impl Pigg {
22 pub fn new<P: AsRef<Path>>(path: P) -> Result<Pigg> {
24 let f = File::open(path)?;
25 let mmap = unsafe { MmapOptions::new().map(&f)? };
27 let (mut remaining, header) = parse_header(&mmap[..])?;
28
29 let mut file_headers = vec![];
30 for _ in 0..header.num_files {
31 let (new_remaining, file_header) = parse_file_header(&remaining)?;
32 remaining = new_remaining;
33 file_headers.push(file_header);
34 }
35
36 let (_, string_pool) = StringPool::with(remaining)?;
37
38 let files: HashMap<_, _> = string_pool
39 .strings
40 .into_iter()
41 .zip(file_headers.into_iter())
42 .collect();
43
44 Ok(Pigg { files, mmap })
45 }
46
47 pub fn get_data(&self, path: &str) -> Result<Vec<u8>> {
49 let header = self
53 .files
54 .get(path)
55 .ok_or_else(|| Error::ItemNotFound(path.into()))?;
56
57 let begin = header.offset as usize;
59 let end = begin + header.pack_size as usize;
60 let mut data = Vec::new();
61 let mut decoder = zlib::Decoder::new(&self.mmap[begin..end])?;
62 decoder.read_to_end(&mut data)?;
63
64 Ok(data)
65 }
66
67 }
69
70fn parse_header(input: &[u8]) -> Result<(&[u8], Header)> {
71 let (input, _) = tag(&0x123u32.to_le_bytes())(input)?;
72
73 let (input, creator_version) = le_u16(input)?;
74 let (input, required_read_version) = le_u16(input)?;
75 let (input, archive_header_size) = le_u16(input)?;
76 let (input, file_header_size) = le_u16(input)?;
77 let (input, num_files) = le_u32(input)?;
78
79 Ok((
80 input,
81 Header {
82 creator_version,
83 required_read_version,
84 archive_header_size,
85 file_header_size,
86 num_files,
87 },
88 ))
89}
90
91#[derive(Debug)]
92struct Header {
93 creator_version: u16,
94 required_read_version: u16,
95 archive_header_size: u16,
96 file_header_size: u16,
97 num_files: u32,
98}
99
100fn parse_file_header(input: &[u8]) -> Result<(&[u8], FileHeader)> {
101 let (input, _) = tag(&0x3456u32.to_le_bytes())(input)?;
102 let (input, name_id) = le_i32(input)?;
103 let (input, size) = le_u32(input)?;
104 let (input, timestamp) = le_u32(input)?;
105 let (input, offset) = le_u32(input)?;
106 let (input, reserved) = le_u32(input)?;
107 let (input, header_data_id) = le_u32(input)?;
108 let (input, checksum) = le_u128(input)?;
109 let (input, pack_size) = le_u32(input)?;
110
111 Ok((
112 input,
113 FileHeader {
114 name_id,
115 size,
116 timestamp,
117 offset,
118 reserved,
119 header_data_id,
120 checksum,
121 pack_size,
122 },
123 ))
124}
125
126#[derive(Debug)]
127struct FileHeader {
128 name_id: i32,
129 size: u32,
130 timestamp: u32,
131 offset: u32,
132 reserved: u32,
133 header_data_id: u32,
134 checksum: u128,
135 pack_size: u32,
136}
137
138#[derive(Debug)]
139struct StringPool {
140 strings: Vec<String>,
141}
142
143impl StringPool {
144 fn with(input: &[u8]) -> Result<(&[u8], StringPool)> {
145 let (mut input, (num_strings, _pool_size)) = Self::get_header(input)?;
146 let mut strings = vec![];
149
150 for _ in 0..num_strings {
151 let (remaining, s) = Self::read_string(input)?;
152 strings.push(s);
153 input = remaining;
154 }
155
156 Ok((input, StringPool { strings }))
157 }
158
159 fn get_header(input: &[u8]) -> Result<(&[u8], (u32, u32))> {
160 let (input, _) = tag(&0x6789u32.to_le_bytes())(input)?;
161 let (input, num_strings) = le_u32(input)?;
162 let (input, pool_size) = le_u32(input)?;
163 Ok((input, (num_strings, pool_size)))
164 }
165
166 fn read_string(input: &[u8]) -> Result<(&[u8], String)> {
167 let (input, str_length) = le_u32(input)?;
168 let (input, str_bytes) = take(str_length - 1)(input)?;
169 let (input, _) = tag(&0u8.to_le_bytes())(input)?;
170
171 Ok((input, str::from_utf8(str_bytes)?.to_string()))
173 }
174}