1use std::ffi::CString;
17use std::io::{Error, ErrorKind, Read, Result, Seek, SeekFrom};
18
19#[derive(Debug, Clone)]
21pub struct Header {
22 pub name: String,
24 pub size: u32,
26 pub files: u32,
28 pub indices: u32,
30}
31
32impl Header {
33 pub fn from_reader<M: Read>(mut reader: M) -> Result<Self> {
35 let name = {
36 let mut buf = vec![0; 4];
37 reader.read_exact(&mut buf)?;
38 String::from_utf8(buf).map_err(|e| Error::new(ErrorKind::InvalidData, e))?
39 };
40
41 let size = {
42 let mut buf = [0; 4];
43 reader.read_exact(&mut buf)?;
44 u32::from_le_bytes(buf)
45 };
46
47 let files = {
48 let mut buf = [0; 4];
49 reader.read_exact(&mut buf)?;
50 u32::from_be_bytes(buf)
51 };
52
53 let indices = {
54 let mut buf = [0; 4];
55 reader.read_exact(&mut buf)?;
56 u32::from_be_bytes(buf)
57 };
58
59 Ok(Header {
60 name,
61 size,
62 files,
63 indices,
64 })
65 }
66}
67
68#[derive(Debug, Clone)]
70pub struct TableEntry {
71 pub pos: u32,
73 pub size: u32,
75 pub name: String,
77}
78
79impl TableEntry {
80 pub fn from_reader<M: Read>(mut reader: M) -> Result<Self> {
84 let pos = {
85 let mut buf = [0; 4];
86 reader.read_exact(&mut buf)?;
87 u32::from_be_bytes(buf)
88 };
89
90 let size = {
91 let mut buf = [0; 4];
92 reader.read_exact(&mut buf)?;
93 u32::from_be_bytes(buf)
94 };
95
96 let bytes = reader
97 .bytes()
98 .scan((), |_, opt| opt.ok())
99 .take_while(|&n| n != 0)
100 .collect::<Vec<u8>>();
101
102 let name = CString::new(bytes)?.to_string_lossy().into_owned();
103
104 Ok(TableEntry { pos, size, name })
105 }
106}
107
108pub struct EmbeddedFile<M: Read + Seek> {
110 reader: M,
111 offset: u64,
112 size: u64,
113 cursor: u64,
114}
115
116impl<M: Read + Seek> EmbeddedFile<M> {
117 fn new(reader: M, offset: u64, size: u64) -> Self {
118 EmbeddedFile {
119 reader,
120 offset,
121 size,
122 cursor: 0,
123 }
124 }
125}
126
127impl<M: Read + Seek> Read for EmbeddedFile<M> {
128 fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
129 self.reader
130 .seek(SeekFrom::Start(self.offset + self.cursor))?;
131
132 let len = buf.len().min((self.size - self.cursor) as usize);
133
134 let read = self.reader.read(&mut buf[..len])?;
135
136 self.cursor += read as u64;
137
138 Ok(read)
139 }
140}
141
142impl<M: Read + Seek> Seek for EmbeddedFile<M> {
143 fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
144 let offset = match pos {
145 SeekFrom::Start(pos) => pos as i64,
146 SeekFrom::End(pos) => self.size as i64 + pos,
147 SeekFrom::Current(pos) => self.cursor as i64 + pos,
148 };
149
150 if offset < 0 {
151 return Err(Error::new(
152 ErrorKind::InvalidInput,
153 String::from("Cannot seek behind 0"),
154 ));
155 }
156
157 self.cursor = offset as u64;
158
159 Ok(self.cursor)
160 }
161}
162
163pub fn from_reader<M: Read>(mut reader: M) -> Result<(Header, Vec<TableEntry>)> {
165 let header = Header::from_reader(&mut reader)?;
166
167 let mut entries = Vec::with_capacity(header.files as usize);
168
169 for _ in 0..header.files as usize {
170 let entry = TableEntry::from_reader(&mut reader)?;
171
172 entries.push(entry);
173 }
174
175 Ok((header, entries))
176}
177
178pub fn open_file<M: Read + Seek>(reader: M, entry: &TableEntry) -> EmbeddedFile<M> {
180 EmbeddedFile::new(reader, entry.pos as u64, entry.size as u64)
181}