1use std::fs::File;
2use std::io::{BufReader, Read, Seek, SeekFrom};
3use std::path::Path;
4
5use crate::{utils, Error, Result};
6
7const S25_MAGIC: &[u8; 4] = b"S25\0";
8const S25_BYTES_PER_PIXEL: usize = 4;
9
10pub struct S25Archive<A = File> {
12 file: BufReader<A>,
13 entries: Vec<Option<i32>>,
14}
15
16impl<T> From<T> for S25Archive<T>
17where
18 T: Read + Seek,
19{
20 fn from(t: T) -> Self {
21 Self {
22 file: BufReader::new(t),
23 entries: Default::default(),
24 }
25 }
26}
27
28impl S25Archive<File> {
29 pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
31 let path = path.as_ref();
32
33 let file = File::open(path)?;
34 let mut file = BufReader::new(file);
35
36 let mut magic_buf = [0u8; 4];
37 file.read_exact(&mut magic_buf)?;
38 if &magic_buf != S25_MAGIC {
39 return Err(Error::InvalidArchive);
40 }
41
42 let total_entries = utils::io::read_i32(&mut file)?;
43
44 let mut entries = vec![];
45
46 for _ in 0..total_entries {
47 let mut offset = [0u8; 4];
48 file.read_exact(&mut offset)?;
49 let offset = i32::from_le_bytes(offset);
50
51 entries.push(if offset == 0 { None } else { Some(offset) });
52 }
53
54 Ok(S25Archive { file, entries })
55 }
56}
57
58impl<'a> S25Archive<std::io::Cursor<&'a [u8]>> {
59 pub fn from_raw_bytes<'b>(bytes: &'b [u8]) -> Result<Self>
61 where
62 'b: 'a,
63 {
64 use std::io::Cursor;
65
66 let mut file = BufReader::new(Cursor::new(bytes));
67
68 let mut magic_buf = [0u8; 4];
69 file.read_exact(&mut magic_buf)?;
70 if &magic_buf != S25_MAGIC {
71 return Err(Error::InvalidArchive);
72 }
73
74 let total_entries = utils::io::read_i32(&mut file)?;
75
76 let mut entries = vec![];
77
78 for _ in 0..total_entries {
79 let mut offset = [0u8; 4];
80 file.read_exact(&mut offset)?;
81 let offset = i32::from_le_bytes(offset);
82
83 entries.push(if offset == 0 { None } else { Some(offset) });
84 }
85
86 Ok(S25Archive { file, entries })
87 }
88}
89
90impl<A> S25Archive<A> {
91 pub fn total_entries(&self) -> usize {
93 self.entries.len()
94 }
95
96 pub fn total_layers(&self) -> usize {
98 self.total_entries() / 100 + 1
99 }
100}
101
102#[derive(Clone, Copy, Debug, PartialEq)]
104pub struct S25ImageMetadata {
105 pub width: i32,
107 pub height: i32,
109 pub offset_x: i32,
111 pub offset_y: i32,
113 pub incremental: bool,
115 pub(crate) head: i32,
117}
118
119#[derive(Clone)]
121pub struct S25Image {
122 pub metadata: S25ImageMetadata,
124 pub bgra_buffer: Vec<u8>,
126}
127
128impl S25Image {
129 pub fn rgba_buffer(&self) -> Vec<u8> {
130 crate::utils::rgba_to_bgra(&self.bgra_buffer)
131 }
132}
133
134impl<A> S25Archive<A>
135where
136 A: Read + Seek,
137{
138 pub fn load_image_metadata(&mut self, entry: usize) -> Result<S25ImageMetadata> {
140 let offset = self
141 .entries
142 .get(entry)
143 .copied()
144 .flatten()
145 .ok_or(Error::NoEntry)?;
146
147 self.file.seek(SeekFrom::Start(offset as u64))?;
148
149 let width = utils::io::read_i32(&mut self.file)?;
150 let height = utils::io::read_i32(&mut self.file)?;
151 let offset_x = utils::io::read_i32(&mut self.file)?;
152 let offset_y = utils::io::read_i32(&mut self.file)?;
153 let incremental = 0 != (utils::io::read_i32(&mut self.file)? as u32 & 0x80000000);
154
155 Ok(S25ImageMetadata {
156 width,
157 height,
158 offset_x,
159 offset_y,
160 incremental,
161 head: offset + 0x14,
162 })
163 }
164
165 pub fn load_image(&mut self, entry: usize) -> Result<S25Image> {
167 let metadata = self.load_image_metadata(entry)?;
168 let mut buf = vec![0u8; (metadata.width * metadata.height) as usize * S25_BYTES_PER_PIXEL];
169
170 self.unpack(&metadata, &mut buf)?;
171
172 Ok(S25Image {
173 metadata,
174 bgra_buffer: buf,
175 })
176 }
177
178 fn unpack(&mut self, metadata: &S25ImageMetadata, buf: &mut [u8]) -> Result<()> {
179 self.file.seek(SeekFrom::Start(metadata.head as u64))?;
181
182 if metadata.incremental {
183 return self.unpack_incremental(metadata, buf);
184 }
185
186 let mut rows = Vec::with_capacity(metadata.height as usize);
188 for _ in 0..metadata.height {
189 rows.push(utils::io::read_i32(&mut self.file)? as u32);
190 }
191
192 let mut offset = 0;
193
194 let rows: Vec<_> = rows
195 .into_iter()
196 .map(|row_offset| -> Result<_> {
197 self.file.seek(SeekFrom::Start(row_offset as u64))?;
198 let row_length = utils::io::read_i16(&mut self.file)? as u16;
199
200 let row_length = if row_offset & 0x01 != 0 {
201 self.file.read_exact(&mut [0u8])?; row_length & (!0x01)
203 } else {
204 row_length
205 };
206 let mut decode_buf = vec![0; row_length as usize];
207 self.file.read_exact(&mut decode_buf)?;
208
209 Ok(decode_buf)
210 })
211 .collect();
212
213 for row in rows {
215 self.decode_line(&row?, buf, &mut offset, metadata.width);
216 }
217
218 Ok(())
219 }
220
221 fn decode_line(&mut self, decode_buf: &[u8], buf: &mut [u8], offset: &mut usize, width: i32) {
222 use std::convert::TryFrom;
223
224 let mut decode_counter = 0usize;
225 let mut count_remaining = width;
226
227 while count_remaining > 0 && decode_counter < decode_buf.len() {
228 decode_counter += decode_counter & 0x01;
230
231 let count = u16::from_le_bytes(
232 *<&[u8; 2]>::try_from(&decode_buf[decode_counter..][..2]).unwrap(),
233 );
234 decode_counter += 2;
235
236 let (method, skip) = (count >> 13, (count >> 11) & 0x03);
237 decode_counter += skip as usize;
238
239 let count = {
240 let count = count & 0x7ff;
241 if count == 0 {
242 let new_count = i32::from_le_bytes(
244 *<&[u8; 4]>::try_from(&decode_buf[decode_counter..][..4]).unwrap(),
245 );
246 decode_counter += 4;
247 new_count
248 } else {
249 count as i32
250 }
251 .min(count_remaining)
252 };
253
254 count_remaining -= count;
255
256 match method {
257 2 => {
258 for _ in 0..count {
260 if buf.len() < (*offset + 4) || decode_buf.len() <= (decode_counter + 2) {
261 break;
262 }
263
264 buf[*offset] = decode_buf[decode_counter];
265 buf[*offset + 1] = decode_buf[decode_counter + 1];
266 buf[*offset + 2] = decode_buf[decode_counter + 2];
267 buf[*offset + 3] = 0xff;
268
269 decode_counter += 3;
270 *offset += 4;
271 }
272 }
273 3 => {
274 if let [r, g, b] = decode_buf[decode_counter..][..3] {
276 decode_counter += 3;
277
278 for _ in 0..count {
279 if buf.len() < (*offset + 4) {
280 break;
281 }
282
283 buf[*offset] = r;
284 buf[*offset + 1] = g;
285 buf[*offset + 2] = b;
286 buf[*offset + 3] = 0xff;
287
288 *offset += 4;
289 }
290 } else {
291 unreachable!();
292 }
293 }
294 4 => {
295 for _ in 0..count {
297 if buf.len() < (*offset + 4) {
298 break;
299 }
300
301 buf[*offset] = decode_buf[decode_counter + 1];
302 buf[*offset + 1] = decode_buf[decode_counter + 2];
303 buf[*offset + 2] = decode_buf[decode_counter + 3];
304 buf[*offset + 3] = decode_buf[decode_counter + 0];
305
306 decode_counter += 4;
307 *offset += 4;
308 }
309 }
310 5 => {
311 if let [a, r, g, b] = decode_buf[decode_counter..][..4] {
313 decode_counter += 4;
314
315 for _ in 0..count {
316 if buf.len() < (*offset + 4) {
317 break;
318 }
319
320 buf[*offset] = r;
321 buf[*offset + 1] = g;
322 buf[*offset + 2] = b;
323 buf[*offset + 3] = a;
324 *offset += 4;
325 }
326 } else {
327 unreachable!();
328 }
329 }
330 _ => {
331 if count < 0 {
332 *offset -= (-count) as usize * 4;
333 } else {
334 *offset += count as usize * 4;
335 }
336 }
337 }
338 }
339 }
340
341 fn unpack_incremental(&mut self, metadata: &S25ImageMetadata, buf: &mut [u8]) -> Result<()> {
343 let _ = metadata;
344 let _ = buf;
345
346 Err(Error::UnsupportedFileFormat)
347 }
348
349 }