refpack/header/mode/
maxis.rs

1////////////////////////////////////////////////////////////////////////////////
2// This Source Code Form is subject to the terms of the Mozilla Public         /
3// License, v. 2.0. If a copy of the MPL was not distributed with this         /
4// file, You can obtain one at https://mozilla.org/MPL/2.0/.                   /
5//                                                                             /
6////////////////////////////////////////////////////////////////////////////////
7
8use std::cmp::min;
9use std::io::{Read, Seek, Write};
10
11use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
12
13use crate::header::Header;
14use crate::header::mode::Mode;
15use crate::{RefPackError, RefPackResult, header};
16
17/// Header used by many Maxis and SimEA games
18///
19/// ## Structure
20/// - Little Endian u32: Compressed length
21/// - u8: Flags field; flags are unknown, and in all known cases is `0x10`
22/// - Magic Number: 0xFB
23/// - Big Endian u24/u32: Decompressed Length
24pub enum Maxis {}
25
26pub const FLAGS: u8 = 0x10;
27
28impl Mode for Maxis {
29    fn length(_decompressed_size: usize) -> usize {
30        9
31    }
32
33    fn read<R: Read + Seek>(reader: &mut R) -> RefPackResult<Header> {
34        let compressed_length_prewrap = reader.read_u32::<LittleEndian>()?;
35        let compressed_length = if compressed_length_prewrap == 0 {
36            None
37        } else {
38            Some(compressed_length_prewrap)
39        };
40        let flags = reader.read_u8()?;
41        if flags != FLAGS {
42            return Err(RefPackError::BadFlags(flags));
43        }
44        let magic = reader.read_u8()?;
45        if magic != header::MAGIC {
46            return Err(RefPackError::BadMagic(magic));
47        }
48        // Inexplicably this weird three byte number is stored Big Endian
49        let decompressed_length = reader.read_u24::<BigEndian>()?;
50        Ok(Header {
51            decompressed_length,
52            compressed_length,
53        })
54    }
55
56    fn write<W: Write + Seek>(header: Header, writer: &mut W) -> RefPackResult<()> {
57        writer.write_u32::<LittleEndian>(header.compressed_length.unwrap_or(0))?;
58        writer.write_u8(FLAGS)?;
59        writer.write_u8(header::MAGIC)?;
60        // This is only ever used to create a default size for the decompression buffer,
61        // so I believe this won't cause issues? Even official decompression seems to just ignore this
62        writer.write_u24::<BigEndian>(min(
63            header.decompressed_length,
64            0b1111_1111_1111_1111_1111_1111,
65        ))?;
66        Ok(())
67    }
68}
69
70#[cfg(test)]
71mod test {
72    use std::io::Cursor;
73
74    use proptest::prop_assert_eq;
75    use test_strategy::proptest;
76
77    use super::*;
78    use crate::header::Header;
79
80    #[proptest]
81    fn symmetrical_read_write(
82        #[any(decompressed_limit = 16_777_214, compressed_limit = Some(u32::MAX))] header: Header,
83    ) {
84        let mut write_buf = vec![];
85        let mut write_cur = Cursor::new(&mut write_buf);
86        header.write::<Maxis>(&mut write_cur).unwrap();
87        let mut read_cur = Cursor::new(&mut write_buf);
88        let got = Header::read::<Maxis>(&mut read_cur).unwrap();
89
90        prop_assert_eq!(header, got);
91    }
92
93    #[test]
94    fn reads_correctly() {
95        let mut buf = vec![255, 0, 0, 0, FLAGS, header::MAGIC, 0, 0, 255];
96        let mut cur = Cursor::new(&mut buf);
97        let got = Header::read::<Maxis>(&mut cur).unwrap();
98        let want = Header {
99            decompressed_length: 255,
100            compressed_length: Some(255),
101        };
102        assert_eq!(got, want);
103    }
104
105    #[test]
106    fn writes_correctly() {
107        let header = Header {
108            decompressed_length: 255,
109            compressed_length: Some(255),
110        };
111        let mut buf = vec![];
112        let mut cur = Cursor::new(&mut buf);
113        header.write::<Maxis>(&mut cur).unwrap();
114        let want = vec![255, 0, 0, 0, FLAGS, header::MAGIC, 0, 0, 255];
115        assert_eq!(buf, want);
116    }
117
118    #[test]
119    fn rejects_bad_flags() {
120        let mut buf = vec![0, 0, 0, 0, 0x50, 0, 0, 0, 0];
121        let mut cur = Cursor::new(&mut buf);
122        let err = Header::read::<Maxis>(&mut cur).unwrap_err();
123        assert_eq!(err.to_string(), RefPackError::BadFlags(0x50).to_string());
124    }
125
126    #[test]
127    fn rejects_bad_magic() {
128        let mut buf = vec![0, 0, 0, 0, FLAGS, 0x50, 0, 0, 0];
129        let mut cur = Cursor::new(&mut buf);
130        let err = Header::read::<Maxis>(&mut cur).unwrap_err();
131        assert_eq!(err.to_string(), RefPackError::BadMagic(0x50).to_string());
132    }
133}