Skip to main content

s25/
reader.rs

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
10/// An .S25 archive.
11pub 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    /// Opens an S25 archive.
30    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    /// Loads an S25 archive from raw bytes.
60    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    /// Returns the total entries in the archive.
92    pub fn total_entries(&self) -> usize {
93        self.entries.len()
94    }
95
96    /// Returns the total layers in the archive.
97    pub fn total_layers(&self) -> usize {
98        self.total_entries() / 100 + 1
99    }
100}
101
102/// Metadata for an S25 image entry.
103#[derive(Clone, Copy, Debug, PartialEq)]
104pub struct S25ImageMetadata {
105    /// Width.
106    pub width: i32,
107    /// Height.
108    pub height: i32,
109    /// X-axis value of the image offset.
110    pub offset_x: i32,
111    /// Y-axis value of the image offset.
112    pub offset_y: i32,
113    /// Whether the image uses incremental encoding.
114    pub incremental: bool,
115    /// Position of image.
116    pub(crate) head: i32,
117}
118
119/// An S25 image entry.
120#[derive(Clone)]
121pub struct S25Image {
122    /// Metadata.
123    pub metadata: S25ImageMetadata,
124    /// Uncompressed image buffer.
125    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    /// Loads the metadata of an image entry.
139    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    /// Loads an image entry.
166    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        // データ開始位置にカーソルを移動
180        self.file.seek(SeekFrom::Start(metadata.head as u64))?;
181
182        if metadata.incremental {
183            return self.unpack_incremental(metadata, buf);
184        }
185
186        // non-incrementalな画像エントリーをロードする
187        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])?; // 1バイトだけ読み飛ばす
202                    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        // すべての行を走査してデコードしていく
214        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            // 偶数で正規化
229            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                    // 拡張カウント
243                    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                    // BGR
259                    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                    // BGR fill
275                    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                    // ABGR
296                    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                    // ABGR fill
312                    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    // incremental S25
342    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    // fn read_line(&mut self) {}
350}