xp3/
reader.rs

1/*
2 * Created on Mon Dec 14 2020
3 *
4 * Copyright (c) storycraft. Licensed under the Apache Licence 2.0.
5 */
6
7use std::io::{self, Read, Seek, SeekFrom, Write};
8
9use byteorder::{ReadBytesExt, LittleEndian};
10use flate2::read::ZlibDecoder;
11
12use super::{VirtualXP3, XP3Error, XP3ErrorKind, XP3_MAGIC, archive::XP3Archive, header::{XP3Header, XP3HeaderVersion}, index::file::{IndexSegmentFlag, XP3FileIndexSegment}, index_set::XP3IndexSet};
13
14pub struct XP3Reader;
15
16impl XP3Reader {
17
18    /// Open XP3 archive.
19    pub fn open_archive<T: Read + Seek>(mut stream: T) -> Result<XP3Archive<T>, XP3Error> {
20        Ok(XP3Archive::new(Self::read_container(&mut stream)?, stream))
21    }
22
23    /// Read xp3 container using stream.
24    pub fn read_container<T: Read + Seek>(stream: &mut T) -> Result<VirtualXP3, XP3Error> {
25        let current = stream.seek(SeekFrom::Current(0))?;
26
27        Self::check_archive(stream)?;
28
29        // This is 0x01 currently
30        let _ = stream.read_u8()?;
31
32        let (_, header) = XP3Header::from_bytes(stream)?;
33
34        match header.version() {
35            XP3HeaderVersion::Old => {
36                
37            }
38
39            XP3HeaderVersion::Current { minor_version: _, index_size_offset } => {
40                stream.seek(SeekFrom::Current(index_size_offset as i64))?;
41            }
42        }
43
44        // Move to index part
45        let index_offset = stream.read_u64::<LittleEndian>()?;
46
47        stream.seek(SeekFrom::Start(current + index_offset))?;
48
49        let (_, index_set) = XP3IndexSet::from_bytes(stream)?;
50
51        // Reset read position to start.
52        stream.seek(SeekFrom::Start(current))?;
53
54        Ok(VirtualXP3::new(header, index_set))
55    }
56
57    /// Read data from provided segment.
58    pub fn read_segment<O: Read + Seek, T: Write>(segment: &XP3FileIndexSegment, from: &mut O, stream: &mut T) -> Result<(), XP3Error> {
59        let read_size = segment.saved_size();
60        let read_offset = segment.data_offset();
61
62        let pos = from.seek(SeekFrom::Current(read_offset as i64))?;
63
64        match segment.flag() {
65            IndexSegmentFlag::UnCompressed => {
66                io::copy(&mut from.take(read_size), stream)?;
67            },
68
69            IndexSegmentFlag::Compressed => {
70                let decoder = ZlibDecoder::new(from.take(read_size));
71
72                io::copy(&mut decoder.take(segment.original_size()), stream)?;
73            }
74        }
75
76        from.seek(SeekFrom::Start(pos - read_offset))?;
77
78        Ok(())
79    }
80
81    /// Check if stream is valid xp3 archive or not.
82    /// Will read 11 bytes from current position.
83    pub fn check_archive(stream: &mut impl Read) -> Result<(), XP3Error> {
84        let mut magic_buffer = [0_u8; 10];
85
86        stream.read_exact(&mut magic_buffer)?;
87
88        if magic_buffer != XP3_MAGIC {
89            return Err(XP3Error::new(XP3ErrorKind::InvalidFile, None));
90        }
91
92        Ok(())
93    }
94
95}