pakt/
lib.rs

1mod error;
2mod util;
3
4use std::io::{self, Read, Seek, SeekFrom, BufRead, Write};
5use std::collections::HashMap;
6use crate::error::ErrorKind;
7use crate::util::as_u32_le;
8
9const MAGIC: &str = "PAKT";
10const VERSION: u32 = 1;
11
12struct FileInfo {
13    offset: u32,
14    size: u32
15}
16
17struct Decoder<'a> {
18    buffer: Box<dyn SeekRead + 'a>,
19    total_files: u32,
20    contents: HashMap<String, FileInfo>
21}
22
23impl<'a> Decoder<'a> {
24    fn from(mut buffer: impl SeekRead + 'a) -> Result<Self, error::ErrorKind> {
25        let mut temp_buffer_4_bytes = [0u8; 4];
26        buffer.read(&mut temp_buffer_4_bytes)?;
27        if temp_buffer_4_bytes != MAGIC.as_bytes() {
28            return Err(ErrorKind::InvalidMagicNumber);
29        }
30        buffer.read(&mut temp_buffer_4_bytes)?;
31        if temp_buffer_4_bytes != VERSION.to_le_bytes() {
32            return Err(ErrorKind::InvalidVersion);
33        }
34        buffer.seek(SeekFrom::Current(20 * 4));
35        buffer.read(&mut temp_buffer_4_bytes)?;
36        let total_files = as_u32_le(&temp_buffer_4_bytes);
37
38        let mut contents = HashMap::new();
39        let mut string_buf = Vec::<u8>::new();
40        for _ in 0..total_files {
41            string_buf.clear();
42            // Name
43            buffer.read(&mut temp_buffer_4_bytes)?; // String length
44            string_buf.resize(as_u32_le(&temp_buffer_4_bytes) as usize, 0);
45            buffer.read(&mut string_buf)?;
46            // Offset
47            buffer.read(&mut temp_buffer_4_bytes)?;
48            let offset = as_u32_le(&temp_buffer_4_bytes);
49            // Size
50            buffer.read(&mut temp_buffer_4_bytes)?;
51            let size = as_u32_le(&temp_buffer_4_bytes);
52
53            contents.insert(String::from_utf8(string_buf.clone()).unwrap(), FileInfo {
54                size,
55                offset,
56            });
57        }
58
59        Ok(Self {
60            total_files,
61            contents,
62            buffer: Box::new(buffer)
63        })
64    }
65
66    fn total_files(&self) -> u32 {
67        self.total_files
68    }
69
70    fn extract(&mut self, path: &str, mut write: impl Write) -> Result<(), ErrorKind> {
71        let file_info = &self.contents[path];
72        self.buffer.seek(SeekFrom::Start(file_info.offset as u64));
73        io::copy(&mut self.buffer.as_mut().take(file_info.size as u64), &mut write);
74        Ok(())
75    }
76}
77
78trait SeekRead: Seek + Read {}
79impl<T: Seek + Read> SeekRead for T {}
80
81pub struct BufferInfo {
82    buffer: Box<dyn SeekRead>,
83    size: u32
84}
85
86pub struct Encoder {
87    contents: HashMap<String, BufferInfo>,
88    paths_len: u32
89}
90
91impl Encoder {
92    pub fn new() -> Self {
93        Self {
94            contents: HashMap::new(),
95            paths_len: 0
96        }
97    }
98
99    pub fn add_file(&mut self, path: &str, mut buffer: Box<dyn SeekRead>) {
100        self.paths_len += path.len() as u32;
101        let size = buffer.seek(SeekFrom::End(0)).unwrap() as u32;
102        buffer.seek(SeekFrom::Start(0));
103        self.contents.insert(path.to_owned(), BufferInfo {
104            buffer,
105            size
106        });
107        println!("path {} size {}", path, size);
108    }
109
110    pub fn write(&mut self, mut write_buffer: impl io::Write) {
111        // Magic number
112        write_buffer.write(MAGIC.as_bytes());
113        // Version
114        write_buffer.write(&VERSION.to_le_bytes());
115        // Reserved
116        write_buffer.write(&[0u8; 20 * 4]);
117        write_buffer.write(&(self.contents.len() as u32).to_le_bytes());
118
119        let total_files = self.contents.len() as u32;
120        let mut pad = 4 + 4 + 20 * 4 + 4 + self.paths_len + (4 + 4 + 4) * total_files;
121        for (path, buffer_info) in self.contents.iter_mut() {
122            // Name
123            write_buffer.write(&(path.len() as u32).to_le_bytes()); // String length
124            write_buffer.write(path.as_bytes());
125            // Offset
126            write_buffer.write(&pad.to_le_bytes());
127            // Size
128            write_buffer.write(&(buffer_info.size).to_le_bytes());
129            pad += buffer_info.size;
130        }
131        for (path, mut read_buffer) in self.contents.iter_mut() {
132            io::copy(&mut read_buffer.buffer, &mut write_buffer);
133        }
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    #[test]
140    fn encode() {
141        use super::*;
142        use std::fs::File;
143        use std::io::prelude::*;
144
145        let mut encoder = Encoder::new();
146        encoder.add_file("tempeh.jpg", Box::new(File::open("test/image/tempeh.jpg").unwrap()));
147        encoder.write(File::create("test/output/encode.pakt").unwrap());
148    }
149
150    #[test]
151    fn encode_and_extract() {
152        use super::*;
153        use std::fs::File;
154        use std::io::prelude::*;
155        use file_diff::diff;
156
157        let mut encoder = Encoder::new();
158        encoder.add_file("tempeh.jpg", Box::new(File::open("test/image/tempeh.jpg").unwrap()));
159        encoder.add_file("imperial.ogg", Box::new(File::open("test/music/imperial.ogg").unwrap()));
160        encoder.write(File::create("test/output/encode_and_read.pakt").unwrap());
161
162        let mut decoder = Decoder::from(File::open("test/output/encode_and_read.pakt").unwrap()).unwrap();
163        assert_eq!(decoder.total_files(), 2);
164        decoder.extract("tempeh.jpg", File::create("test/output/tempeh.jpg").unwrap());
165        decoder.extract("imperial.ogg", File::create("test/output/imperial.ogg").unwrap());
166        assert!(diff("test/output/tempeh.jpg", "test/image/tempeh.jpg"));
167        assert!(diff("test/output/imperial.ogg", "test/music/imperial.ogg"));
168    }
169}