aseprite_loader/binary/
header.rs

1use nom::{
2    bytes::complete::{tag, take},
3    Parser,
4};
5
6use super::{
7    color_depth::{parse_color_depth, ColorDepth},
8    errors::ParseResult,
9    scalars::{byte, dword, short, word, Byte, Dword, Short, Word},
10};
11
12const HEADER_MAGIC_NUMBER: [u8; 2] = 0xA5E0u16.to_le_bytes();
13
14/// A 128-byte header (same as FLC/FLI header, but with other magic number)
15#[derive(Debug, Copy, Clone, PartialEq, Eq)]
16pub struct Header {
17    /// File size
18    pub file_size: Dword,
19    /// Amount of frames in the body of the file
20    pub frames: Word,
21    /// Width in pixels
22    pub width: Word,
23    /// Height in pixels
24    pub height: Word,
25    /// Color depth (bits per pixel)
26    pub color_depth: ColorDepth,
27    /// Flags:
28    ///   1 = Layer opacity has valid value
29    pub flags: Dword,
30    /// Speed (milliseconds between frame, like in FLC files)
31    /// DEPRECATED: You should use the frame duration field
32    /// from each frame header
33    #[deprecated = "You should use the frame durations instead"]
34    pub speed: Word,
35    /// Palette entry (index) which represent transparent color
36    /// in all non-background layers (only for Indexed sprites).
37    pub transparent_index: Byte,
38    /// The amount of colors in the palette
39    pub color_count: Word,
40    /// Pixel width (pixel ratio is "pixel width/pixel height").
41    /// If this or pixel height field is zero, pixel ratio is 1:1
42    pub pixel_width: Byte,
43    /// Pixel height
44    pub pixel_height: Byte,
45    /// X position of the grid
46    pub grid_x: Short,
47    /// Y position of the grid
48    pub grid_y: Short,
49    /// Grid width (zero if there is no grid, grid size
50    /// is 16x16 on Aseprite by default)
51    pub grid_width: Word,
52    /// Grid height (zero if there is no grid)
53    pub grid_height: Word,
54}
55
56pub fn parse_header(input: &[u8]) -> ParseResult<'_, Header> {
57    let (rest, input) = take(128usize)(input)?;
58    let (input, file_size) = dword(input)?;
59    let (input, _) = tag(HEADER_MAGIC_NUMBER.as_slice()).parse(input)?;
60    let (input, frames) = word(input)?;
61    let (input, width) = word(input)?;
62    let (input, height) = word(input)?;
63    let (input, color_depth) = parse_color_depth(input)?;
64    let (input, flags) = dword(input)?;
65    let (input, speed) = word(input)?;
66    let (input, _) = tag([0u8; 4].as_slice()).parse(input)?;
67    let (input, _) = tag([0u8; 4].as_slice()).parse(input)?;
68    let (input, transparent_index) = byte(input)?;
69    let (input, _) = take(3usize)(input)?;
70    let (input, color_count) = word(input)?;
71    let (input, pixel_width) = byte(input)?;
72    let (input, pixel_height) = byte(input)?;
73    let (input, grid_x) = short(input)?;
74    let (input, grid_y) = short(input)?;
75    let (input, grid_width) = word(input)?;
76    let (input, grid_height) = word(input)?;
77    let (input, _) = take(84usize)(input)?;
78    // Sanity check: Did we consume all 128 bytes?
79    assert_eq!(input.len(), 0);
80    #[allow(deprecated)]
81    Ok((
82        rest,
83        Header {
84            file_size,
85            frames,
86            width,
87            height,
88            color_depth,
89            flags,
90            speed,
91            transparent_index,
92            color_count,
93            pixel_width,
94            pixel_height,
95            grid_x,
96            grid_y,
97            grid_width,
98            grid_height,
99        },
100    ))
101}
102
103#[test]
104#[allow(deprecated)]
105fn test_parse_header() {
106    let input = std::fs::read("./tests/default.aseprite").unwrap();
107    let (_, header) = parse_header(&input).unwrap();
108    assert_eq!(
109        header,
110        Header {
111            file_size: 573,
112            frames: 1,
113            width: 32,
114            height: 32,
115            color_depth: ColorDepth::Rgba,
116            flags: 1,
117            speed: 100,
118            transparent_index: 0,
119            color_count: 32,
120            pixel_width: 1,
121            pixel_height: 1,
122            grid_x: 0,
123            grid_y: 0,
124            grid_width: 16,
125            grid_height: 16,
126        },
127    );
128}