1use std::cmp::Ordering;
2use std::convert::TryFrom;
3use std::io::{Read, Seek, SeekFrom};
4
5use byteorder::{LittleEndian, ReadBytesExt};
6use hash40::{Hash40, ReadHash40};
7
8pub trait Prc: Sized {
10 fn read_param<R: Read + Seek>(reader: &mut R, offsets: FileOffsets) -> Result<Self>;
13
14 fn read_from_struct<R: Read + Seek>(
19 reader: &mut R,
20 hash: Hash40,
21 offsets: FileOffsets,
22 struct_data: StructData,
23 ) -> Result<Self> {
24 struct_data.read_child(reader, hash, offsets)
25 }
26
27 fn read_file<R: Read + Seek>(reader: &mut R) -> Result<Self> {
31 let offsets = prepare(reader)?;
32 Self::read_param(reader, offsets)
33 }
34}
35
36pub type Result<T> = std::result::Result<T, Error>;
39
40#[derive(Debug)]
43pub struct Error {
44 pub path: Vec<ErrorPathPart>,
45 pub position: std::io::Result<u64>,
46 pub kind: ErrorKind,
47}
48
49#[derive(Debug)]
51pub enum ErrorKind {
52 WrongParamNumber { expected: ParamNumber, received: u8 },
53 ParamNotFound(Hash40),
54 Io(std::io::Error),
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub enum ErrorPathPart {
61 Index(u32),
62 Hash(Hash40),
63}
64
65#[derive(Debug, Copy, Clone)]
68pub struct FileOffsets {
69 pub hashes: u64,
70 pub ref_table: u64,
71}
72
73#[derive(Debug, Copy, Clone)]
75pub struct StructData {
76 pub position: u64,
77 pub len: u32,
78 pub ref_offset: u32,
79}
80
81#[repr(u8)]
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84pub enum ParamNumber {
85 Bool = 1,
86 I8,
87 U8,
88 I16,
89 U16,
90 I32,
91 U32,
92 Float,
93 Hash,
94 String,
95 List,
96 Struct,
97}
98
99pub fn check_type<R: Read + Seek>(reader: &mut R, value: ParamNumber) -> Result<()> {
100 let pre_pos = reader.stream_position();
101 let read = reader.read_u8().map_err(|e| Error::new(e, reader))?;
102
103 if read != value.into() {
104 Err(Error::new_with_pos(
105 ErrorKind::WrongParamNumber {
106 expected: value,
107 received: read,
108 },
109 pre_pos,
110 ))
111 } else {
112 Ok(())
113 }
114}
115
116impl StructData {
117 pub fn from_stream<R: Read + Seek>(reader: &mut R) -> Result<Self> {
118 let position = reader
119 .seek(SeekFrom::Current(0))
120 .map_err(|e| Error::new(e, reader))?;
121
122 check_type(reader, ParamNumber::Struct)?;
123
124 let len = reader
125 .read_u32::<LittleEndian>()
126 .map_err(|e| Error::new(e, reader))?;
127 let ref_offset = reader
128 .read_u32::<LittleEndian>()
129 .map_err(|e| Error::new(e, reader))?;
130
131 reader
132 .seek(SeekFrom::Start(position))
133 .map_err(|e| Error::new(e, reader))?;
134
135 Ok(Self {
136 position,
137 len,
138 ref_offset,
139 })
140 }
141
142 fn search_child<R: Read + Seek>(
144 &self,
145 reader: &mut R,
146 hash: Hash40,
147 offsets: FileOffsets,
148 ) -> Result<()> {
149 let mut low = 0;
150 let mut high = self.len as i64 - 1;
152 while low <= high {
153 let i = (low + high) / 2;
154 reader
155 .seek(SeekFrom::Start(
156 offsets.ref_table + self.ref_offset as u64 + (i as u64 * 8),
157 ))
158 .map_err(|e| Error::new(e, reader))?;
159
160 let hash_index = reader
161 .read_u32::<LittleEndian>()
162 .map_err(|e| Error::new(e, reader))?;
163 let param_offset = reader
164 .read_u32::<LittleEndian>()
165 .map_err(|e| Error::new(e, reader))?;
166
167 reader
168 .seek(SeekFrom::Start(offsets.hashes + (hash_index as u64 * 8)))
169 .map_err(|e| Error::new(e, reader))?;
170 let read_hash = reader
171 .read_hash40::<LittleEndian>()
172 .map_err(|e| Error::new(e, reader))?;
173
174 match read_hash.cmp(&hash) {
175 Ordering::Less => low = i + 1,
176 Ordering::Greater => high = i - 1,
177 Ordering::Equal => {
178 reader
179 .seek(SeekFrom::Start(self.position + (param_offset as u64)))
180 .map_err(|e| Error::new(e, reader))?;
181 return Ok(());
182 }
183 }
184 }
185 Err(Error::new_with_pos(
186 ErrorKind::ParamNotFound(hash),
187 Ok(self.position),
188 ))
189 }
190
191 pub fn read_child<R: Read + Seek, T: Prc>(
194 &self,
195 reader: &mut R,
196 hash: Hash40,
197 offsets: FileOffsets,
198 ) -> Result<T> {
199 self.search_child(reader, hash, offsets)?;
201
202 T::read_param(reader, offsets).map_err(|mut e| {
204 e.path.insert(0, ErrorPathPart::Hash(hash));
205 Error {
206 path: e.path,
207 position: e.position,
208 kind: e.kind,
209 }
210 })
211 }
212}
213
214pub fn prepare<R: Read + Seek>(reader: &mut R) -> Result<FileOffsets> {
216 prepare_internal(reader).map_err(|e| Error::new(e, reader))
217}
218
219fn prepare_internal<R: Read + Seek>(reader: &mut R) -> std::io::Result<FileOffsets> {
220 reader.seek(SeekFrom::Current(8))?;
221 let hashes_size = reader.read_u32::<LittleEndian>()?;
222 let ref_table_size = reader.read_u32::<LittleEndian>()?;
223
224 let hashes = reader.seek(SeekFrom::Current(0))?;
225
226 reader.seek(SeekFrom::Current(hashes_size as i64))?;
227 let ref_table = reader.seek(SeekFrom::Current(0))?;
228
229 reader.seek(SeekFrom::Current(ref_table_size as i64))?;
230 Ok(FileOffsets { hashes, ref_table })
231}
232
233impl Prc for bool {
236 fn read_param<R: Read + Seek>(reader: &mut R, _offsets: FileOffsets) -> Result<Self> {
237 check_type(reader, ParamNumber::Bool)?;
238 reader
239 .read_u8()
240 .map_err(|e| Error::new(e, reader))
241 .map(|byte| byte > 0)
242 }
243}
244
245macro_rules! impl_read_byte {
246 ($(($param_type:ty, $num:path, $read_func:ident)),*) => {
247 $(
248 impl Prc for $param_type {
249 fn read_param<R: Read + Seek>(reader: &mut R, _offsets: FileOffsets) -> Result<Self> {
250 check_type(reader, $num)?;
251 ReadBytesExt::$read_func(reader).map_err(|e| Error::new(e, reader))
252 }
253 }
254 )*
255 };
256}
257
258macro_rules! impl_read_value {
259 ($(($param_type:ty, $num:path, $read_func:ident)),*) => {
260 $(
261 impl Prc for $param_type {
262 fn read_param<R: Read + Seek>(reader: &mut R, _offsets: FileOffsets) -> Result<Self> {
263 check_type(reader, $num)?;
264 ReadBytesExt::$read_func::<LittleEndian>(reader).map_err(|e| Error::new(e, reader))
265 }
266 }
267 )*
268 };
269}
270
271impl_read_byte!(
272 (i8, ParamNumber::I8, read_i8),
273 (u8, ParamNumber::U8, read_u8)
274);
275
276impl_read_value!(
277 (i16, ParamNumber::I16, read_i16),
278 (u16, ParamNumber::U16, read_u16),
279 (i32, ParamNumber::I32, read_i32),
280 (u32, ParamNumber::U32, read_u32),
281 (f32, ParamNumber::Float, read_f32)
282);
283
284impl Prc for Hash40 {
285 fn read_param<R: Read + Seek>(reader: &mut R, offsets: FileOffsets) -> Result<Self> {
286 check_type(reader, ParamNumber::Hash)?;
287 let hash_index = reader
288 .read_u32::<LittleEndian>()
289 .map_err(|e| Error::new(e, reader))?;
290 let end_position = reader
291 .seek(SeekFrom::Current(0))
292 .map_err(|e| Error::new(e, reader))?;
293
294 reader
295 .seek(SeekFrom::Start(offsets.hashes + (hash_index as u64 * 8)))
296 .map_err(|e| Error::new(e, reader))?;
297 let hash = reader
298 .read_hash40::<LittleEndian>()
299 .map_err(|e| Error::new(e, reader))?;
300
301 reader
302 .seek(SeekFrom::Start(end_position))
303 .map_err(|e| Error::new(e, reader))?;
304 Ok(hash)
305 }
306}
307
308impl Prc for String {
309 fn read_param<R: Read + Seek>(reader: &mut R, offsets: FileOffsets) -> Result<Self> {
310 check_type(reader, ParamNumber::String)?;
311 let str_offset = reader
312 .read_u32::<LittleEndian>()
313 .map_err(|e| Error::new(e, reader))?;
314 let end_position = reader
315 .seek(SeekFrom::Current(0))
316 .map_err(|e| Error::new(e, reader))?;
317
318 reader
319 .seek(SeekFrom::Start(offsets.ref_table + str_offset as u64))
320 .map_err(|e| Error::new(e, reader))?;
321 let mut string = String::new();
322
323 loop {
324 let byte = reader.read_u8().map_err(|e| Error::new(e, reader))?;
325 if byte == 0 {
326 break;
327 }
328 string.push(byte as char);
329 }
330
331 reader
332 .seek(SeekFrom::Start(end_position))
333 .map_err(|e| Error::new(e, reader))?;
334 Ok(string)
335 }
336}
337
338impl<T: Prc> Prc for Vec<T> {
339 fn read_param<R: Read + Seek>(reader: &mut R, offsets: FileOffsets) -> Result<Self> {
340 let start = reader
341 .seek(SeekFrom::Current(0))
342 .map_err(|e| Error::new(e, reader))?;
343 check_type(reader, ParamNumber::List)?;
344 let len = reader
345 .read_u32::<LittleEndian>()
346 .map_err(|e| Error::new(e, reader))?;
347
348 let mut list = Vec::with_capacity(len as usize);
349
350 for i in 0..len {
351 reader
352 .seek(SeekFrom::Start(start + 5 + (i as u64 * 4)))
353 .map_err(|e| Error::new(e, reader))?;
354 let offset = reader
355 .read_u32::<LittleEndian>()
356 .map_err(|e| Error::new(e, reader))?;
357 reader
358 .seek(SeekFrom::Start(start + offset as u64))
359 .map_err(|e| Error::new(e, reader))?;
360
361 let child = T::read_param(reader, offsets).map_err(|mut e| {
363 e.path.insert(0, ErrorPathPart::Index(i));
364 Error {
365 path: e.path,
366 position: e.position,
367 kind: e.kind,
368 }
369 })?;
370 list.push(child);
371 }
372
373 Ok(list)
374 }
375}
376
377impl<T: Prc> Prc for Option<T> {
378 fn read_param<R: Read + Seek>(_: &mut R, _: FileOffsets) -> Result<Self> {
379 unimplemented!("Option's should be read only by using the `read_from_struct` method")
380 }
381
382 fn read_from_struct<R: Read + Seek>(
383 reader: &mut R,
384 hash: Hash40,
385 offsets: FileOffsets,
386 struct_data: StructData,
387 ) -> Result<Self> {
388 struct_data
389 .search_child(reader, hash, offsets)
390 .and_then(|_| T::read_param(reader, offsets))
391 .map(|param| Some(param))
392 .or_else(|err| match &err.kind {
393 ErrorKind::ParamNotFound(_) => Ok(None),
394 _ => Err(err),
395 })
396 }
397}
398
399impl TryFrom<u8> for ParamNumber {
400 type Error = u8;
401
402 fn try_from(value: u8) -> std::result::Result<Self, Self::Error> {
403 match value {
404 1 => Ok(ParamNumber::Bool),
405 2 => Ok(ParamNumber::I8),
406 3 => Ok(ParamNumber::U8),
407 4 => Ok(ParamNumber::I16),
408 5 => Ok(ParamNumber::U16),
409 6 => Ok(ParamNumber::I32),
410 7 => Ok(ParamNumber::U32),
411 8 => Ok(ParamNumber::Float),
412 9 => Ok(ParamNumber::Hash),
413 10 => Ok(ParamNumber::String),
414 11 => Ok(ParamNumber::List),
415 12 => Ok(ParamNumber::Struct),
416 _ => Err(value),
417 }
418 }
419}
420
421impl From<ParamNumber> for u8 {
422 fn from(param_num: ParamNumber) -> Self {
423 param_num as u8
424 }
425}
426
427impl Error {
428 fn new<E: Into<ErrorKind>, S: Seek>(kind: E, seek: &mut S) -> Self {
429 Error {
430 path: vec![],
431 position: seek.stream_position(),
432 kind: kind.into(),
433 }
434 }
435
436 fn new_with_pos<E: Into<ErrorKind>>(kind: E, pos: std::io::Result<u64>) -> Self {
437 Error {
438 path: vec![],
439 position: pos,
440 kind: kind.into(),
441 }
442 }
443}
444
445impl From<std::io::Error> for ErrorKind {
446 fn from(e: std::io::Error) -> Self {
447 ErrorKind::Io(e)
448 }
449}