xp3/
archive.rs

1/*
2 * Created on Sun Dec 13 2020
3 *
4 * Copyright (c) storycraft. Licensed under the Apache Licence 2.0.
5 */
6
7use std::{cell::RefCell, collections::hash_map::Iter, io::{self, Cursor, Read, Seek, Write}, marker::PhantomData};
8
9use super::{VirtualXP3, XP3Error, XP3ErrorKind, index::file::XP3FileIndex, reader::XP3Reader};
10
11/// An XP3 archive with XP3 container.
12/// Read only occur when user request.
13#[derive(Debug)]
14pub struct XP3Archive<T: Read + Seek> {
15
16    container: VirtualXP3,
17
18    stream: RefCell<T>
19
20}
21
22impl<T: Read + Seek> XP3Archive<T> {
23
24    pub fn new(container: VirtualXP3, data: T) -> Self {
25        Self {
26            container,
27            stream: RefCell::new(data)
28        }
29    }
30
31    pub fn container(&self) -> &VirtualXP3 {
32        &self.container
33    }
34
35    pub fn entries(&self) -> Iter<String, XP3FileIndex> {
36        self.container.index_set().entries()
37    }
38
39    /// Unpack file to stream
40    pub fn unpack(&self, name: &String, stream: &mut impl Write) -> Result<(), XP3Error> {
41        let item = self.container.index_set().get(name);
42
43        match item {
44            Some(index) => {
45                for segment in index.segments().iter() {
46                    XP3Reader::read_segment(segment, self.stream.borrow_mut().by_ref(), stream)?;
47                }
48
49                Ok(())
50            },
51
52            None => Err(XP3Error::new(XP3ErrorKind::FileNotFound, None))
53        }
54    }
55
56    /// Close xp3 archive
57    pub fn close(self) -> (VirtualXP3, T) {
58        (self.container, self.stream.into_inner())
59    }
60
61}
62
63/// XP3 filter method. (buffer, hash)
64pub trait XP3FilterMethod {
65
66    /// Returns read size
67    fn filter(buffer: &[u8], hash: u32, out: &mut impl Write) -> io::Result<usize>;
68
69}
70
71/// XP3 archive filter mainy used for encryption
72pub struct XP3ArchiveFilter<T, F: XP3FilterMethod> {
73
74    stream: T,
75    hash: u32,
76
77    phantom_method: PhantomData<F>
78
79}
80
81impl<T, F: XP3FilterMethod> XP3ArchiveFilter<T, F> {
82
83    pub fn new(stream: T, hash: u32) -> Self {
84        Self {
85            stream, hash,
86            phantom_method: PhantomData
87        }
88    }
89
90    /// Unwrap stream
91    pub fn into_inner(self) -> T {
92        self.stream
93    }
94
95}
96
97impl<T: Write, F: XP3FilterMethod> Write for XP3ArchiveFilter<T, F> {
98
99    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
100        F::filter(buf, self.hash, &mut self.stream)
101    }
102
103    fn flush(&mut self) -> io::Result<()> {
104        self.stream.flush()
105    }
106
107}
108
109impl<T: Read, F: XP3FilterMethod> Read for XP3ArchiveFilter<T, F> {
110
111    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
112        let mut raw_buf = vec![0_u8; buf.len()];
113
114        self.stream.read(&mut raw_buf)?;
115        
116        F::filter(&raw_buf, self.hash, &mut Cursor::new(buf))
117    }
118    
119}