sar_core/parser/
payload.rs

1use crate::{
2    core::{
3        result::Result,
4        sa::{self, Position, SymbolArt, SymbolArtLayer},
5        symbol,
6    },
7    parser::decode,
8};
9
10/// Parses a byte array into a Payload structure
11pub fn parse(bytes: impl Into<Box<[u8]>>) -> Result<impl SymbolArt + std::fmt::Debug> {
12    let body = get_body(bytes.into())?;
13    Payload::parse(&body)
14}
15
16/// Extracts and decompresses the body of the SAR file
17fn get_body(mut bytes: Box<[u8]>) -> Result<Box<[u8]>> {
18    let compression = decode::validate_format(&bytes)?;
19    let (_, body) = bytes.split_at_mut(4);
20
21    decode::decrypt(body);
22    match compression {
23        decode::Compression::None => Ok(Box::from(body)),
24        decode::Compression::Compressed => decode::decompress(body),
25    }
26}
27
28/// Represents the main payload of a SAR file containing header, layers, and name information.
29#[derive(Debug, Clone, PartialEq)]
30pub struct Payload {
31    /// The header containing metadata about the SAR file
32    header: Header,
33    /// Vector of layers that make up the SAR file content
34    layers: Vec<Layer>,
35    /// Name of the SAR file in UTF-16LE format (up to 13 characters)
36    name: Vec<u16>,
37}
38
39impl Payload {
40    /// Parses a byte slice into a Payload structure
41    pub fn parse(bytes: &[u8]) -> Result<Self> {
42        let header = Header::parse(&bytes[0..std::mem::size_of::<Header>()])?;
43        let layers = Layers::parse(&bytes[std::mem::size_of::<Header>()..])?.into();
44        let name = Self::parse_name(bytes, &header)?;
45
46        Ok(Self {
47            header,
48            layers,
49            name,
50        })
51    }
52
53    /// Parses the name field from the byte slice
54    fn parse_name(bytes: &[u8], header: &Header) -> Result<Vec<u16>> {
55        let size_of_header = std::mem::size_of::<Header>();
56        let size_of_layer = std::mem::size_of::<Layer>();
57        let start = size_of_header + size_of_layer * header.layers() as usize;
58
59        let name_bytes = bytes[usize::min(start, bytes.len())..]
60            .chunks_exact(2)
61            .take(13) // Name is at most 13 chars
62            .map(|b| u16::from_le_bytes(b.try_into().unwrap()))
63            .collect::<Vec<_>>();
64
65        Ok(name_bytes)
66    }
67}
68
69const HEADER_SIZE_TEAM_FLAG: u8 = 0x40;
70const HEADER_SIZE_NORMAL: u8 = 0x80;
71
72impl SymbolArt for Payload {
73    type Layer = Layer;
74
75    fn author_id(&self) -> u32 {
76        self.header.author_id
77    }
78
79    fn height(&self) -> u8 {
80        match self.header.height {
81            HEADER_SIZE_NORMAL => 96,
82            HEADER_SIZE_TEAM_FLAG => 32,
83            _ => panic!("Invalid height having: {}", self.header.height),
84        }
85    }
86
87    fn width(&self) -> u8 {
88        match self.header.height {
89            HEADER_SIZE_NORMAL => 193,
90            HEADER_SIZE_TEAM_FLAG => 32,
91            _ => panic!("Invalid width having: {}", self.header.height),
92        }
93    }
94
95    fn layers(&self) -> Vec<Layer> {
96        self.layers.clone()
97    }
98
99    fn name(&self) -> String {
100        String::from_utf16_lossy(&self.name)
101    }
102}
103
104/// Represents the header of a SAR file containing metadata
105#[derive(Debug, Clone, PartialEq)]
106pub struct Header {
107    /// Author ID in big endian format
108    pub(super) author_id: u32,
109    /// Number of layers in the SAR file
110    pub(super) layers: u8,
111    /// Height of the SAR file
112    pub(super) height: u8,
113    /// Width of the SAR file
114    pub(super) width: u8,
115    /// Sound effect identifier
116    pub(super) sound_effect: u8,
117}
118
119impl Header {
120    /// Parses a byte slice into a Header structure
121    pub(super) fn parse(bytes: &[u8]) -> Result<Self> {
122        Ok(Header {
123            author_id: u32::from_be_bytes(bytes[0..4].try_into().unwrap()),
124            layers: bytes[4],
125            height: bytes[5],
126            width: bytes[6],
127            sound_effect: bytes[7],
128        })
129    }
130
131    pub(super) fn layers(&self) -> u8 {
132        self.layers
133    }
134}
135
136/// Represents a collection of layers in a SAR file
137pub struct Layers {
138    layers: Vec<Layer>,
139}
140
141impl Layers {
142    /// Parses a byte slice into a Layers structure
143    pub(super) fn parse(bytes: &[u8]) -> Result<Self> {
144        let layers = bytes
145            .chunks_exact(std::mem::size_of::<Layer>())
146            .map(Layer::parse)
147            .collect::<Result<Vec<_>>>()?;
148
149        Ok(Self { layers })
150    }
151}
152
153impl From<Layers> for Vec<Layer> {
154    fn from(layers: Layers) -> Self {
155        layers.layers
156    }
157}
158
159/// Represents a single layer in a SAR file
160#[derive(Debug, Clone, PartialEq, Copy)]
161pub struct Layer {
162    /// Top-left position of the layer
163    pub(super) top_left: Position,
164    /// Bottom-left position of the layer
165    pub(super) bottom_left: Position,
166    /// Top-right position of the layer
167    pub(super) top_right: Position,
168    /// Bottom-right position of the layer
169    pub(super) bottom_right: Position,
170    /// Whether the layer is hidden
171    pub(super) is_hidden: bool,
172    /// Symbol ID of the layer
173    pub(super) symbol_id: u16,
174    /// Alpha/transparency value of the layer
175    pub(super) alpha: u8,
176    /// Red color component
177    pub(super) color_r: u8,
178    /// Green color component
179    pub(super) color_g: u8,
180    /// Blue color component
181    pub(super) color_b: u8,
182}
183
184// Bit masks for layer data
185const LAYER_IS_HIDDEN: u32 = 0b10000000000000000000000000000000;
186const MASK_SYMBOL_ID: u32 = 0b01111111111000000000000000000000;
187const MASK_ALPHA: u32 = 0b00000000000111000000000000000000;
188const MASK_COLOR_R: u32 = 0b00000000000000000000000000111111;
189const MASK_COLOR_G: u32 = 0b00000000000000000000111111000000;
190const MASK_COLOR_B: u32 = 0b00000000000000111111000000000000;
191
192impl Layer {
193    /// Parses a byte slice into a Layer structure
194    fn parse(bytes: &[u8]) -> Result<Self> {
195        let top_left = Position::parse(&bytes[0..2])?;
196        let bottom_left = Position::parse(&bytes[2..4])?;
197        let top_right = Position::parse(&bytes[4..6])?;
198        let bottom_right = Position::parse(&bytes[6..8])?;
199
200        let layer_data = u32::from_le_bytes(bytes[8..12].try_into().unwrap());
201
202        Ok(Self {
203            top_left,
204            bottom_left,
205            top_right,
206            bottom_right,
207            is_hidden: Self::extract_is_hidden(layer_data),
208            symbol_id: Self::extract_symbol_id(layer_data),
209            alpha: Self::extract_alpha(layer_data),
210            color_r: Self::extract_color_r(layer_data),
211            color_g: Self::extract_color_g(layer_data),
212            color_b: Self::extract_color_b(layer_data),
213        })
214    }
215
216    /// Extracts the hidden flag from the layer data
217    fn extract_is_hidden(layer_data: u32) -> bool {
218        (layer_data & LAYER_IS_HIDDEN) != 0
219    }
220
221    /// Extracts the symbol ID from the layer data
222    fn extract_symbol_id(layer_data: u32) -> u16 {
223        ((layer_data & MASK_SYMBOL_ID) >> 21) as u16
224    }
225
226    /// Extracts the alpha value from the layer data
227    fn extract_alpha(layer_data: u32) -> u8 {
228        ((layer_data & MASK_ALPHA) >> 18) as u8
229    }
230
231    /// Extracts the red color component from the layer data
232    fn extract_color_r(layer_data: u32) -> u8 {
233        (layer_data & MASK_COLOR_R) as u8
234    }
235
236    /// Extracts the green color component from the layer data
237    fn extract_color_g(layer_data: u32) -> u8 {
238        ((layer_data & MASK_COLOR_G) >> 6) as u8
239    }
240
241    /// Extracts the blue color component from the layer data
242    fn extract_color_b(layer_data: u32) -> u8 {
243        ((layer_data & MASK_COLOR_B) >> 12) as u8
244    }
245}
246
247/// The factor used to convert the alpha value to a 8-bit value.
248/// SAR files use a 3-bit alpha value, so we need to scale it up to 8-bit
249///
250/// We use 37 to avoid floating point arithmetic for better performance
251/// 255 / 7 = 36.4285714286
252const ALPHA_FACTOR: u8 = 37;
253
254/// The factor used to convert the color value to a 8-bit value.
255/// SAR files use a 4-bit color value, so we need to scale it up to 8-bit
256///
257/// We use 4 to avoid floating point arithmetic for better performance
258/// 255 / 63 = 4.0476190476
259const COLOR_FACTOR: u8 = 4;
260
261impl SymbolArtLayer for Layer {
262    fn top_left(&self) -> Position {
263        self.top_left
264    }
265
266    fn bottom_left(&self) -> Position {
267        self.bottom_left
268    }
269
270    fn top_right(&self) -> Position {
271        self.top_right
272    }
273
274    fn bottom_right(&self) -> Position {
275        self.bottom_right
276    }
277
278    fn symbol(&self) -> symbol::Symbol {
279        symbol::Symbol::new(self.symbol_id.into())
280    }
281
282    fn color(&self) -> sa::Color {
283        let a = self.alpha.saturating_mul(ALPHA_FACTOR);
284        let r = self.color_r.saturating_mul(COLOR_FACTOR);
285        let g = self.color_g.saturating_mul(COLOR_FACTOR);
286        let b = self.color_b.saturating_mul(COLOR_FACTOR);
287        sa::Color::new(a, r, g, b)
288    }
289
290    fn is_hidden(&self) -> bool {
291        self.is_hidden
292    }
293}
294
295impl Position {
296    /// Parses a byte slice into a Position structure
297    fn parse(bytes: &[u8]) -> Result<Self> {
298        Ok(Self {
299            x: bytes[0],
300            y: bytes[1],
301        })
302    }
303}
304
305#[cfg(test)]
306mod tests {
307    use super::*;
308    use crate::{
309        core::sa::Position,
310        test::{RAW_FILE, RAW_FILE_UNCOMPRESSED},
311    };
312
313    #[test]
314    fn test_get_body() {
315        let bytes = Box::from(RAW_FILE);
316        let body = get_body(bytes).unwrap();
317        assert_eq!(body.len(), 1682);
318    }
319
320    #[test]
321    fn test_parse() {
322        let bytes = Box::from(RAW_FILE);
323        let body = get_body(bytes).unwrap();
324        let payload = Payload::parse(&body).unwrap();
325
326        let expected_name = &[12394, 12363, 12383, 12373, 12435]; // "なかたさん"
327        let expected = Payload {
328            header: Header {
329                author_id: 881302016,
330                layers: 104,
331                height: 128,
332                width: 193,
333                sound_effect: 3,
334            },
335            layers: vec![
336                Layer {
337                    top_left: Position { x: 0, y: 0 },
338                    bottom_left: Position { x: 0, y: 0 },
339                    top_right: Position { x: 0, y: 0 },
340                    bottom_right: Position { x: 0, y: 0 },
341                    is_hidden: false,
342                    symbol_id: 0,
343                    alpha: 0,
344                    color_r: 0,
345                    color_g: 0,
346                    color_b: 0,
347                };
348                104
349            ],
350            name: expected_name.to_vec(),
351        };
352
353        assert_eq!(payload.header, expected.header);
354        assert_eq!(payload.layers.len(), expected.layers.len());
355        assert_eq!(payload.name, expected.name);
356    }
357
358    #[test]
359    fn test_parse_uncompressed() {
360        let bytes = Box::from(RAW_FILE_UNCOMPRESSED);
361        let body = get_body(bytes).unwrap();
362        let payload = Payload::parse(&body).unwrap();
363
364        assert_eq!(
365            payload.name,
366            vec![84, 104, 97, 110, 107, 32, 121, 111, 117, 32, 33, 33]
367        );
368    }
369}