aseprite_loader/binary/
header.rs

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