aseprite_loader/binary/chunks/
tileset.rs

1use bitflags::bitflags;
2use nom::{
3    bytes::complete::take,
4    combinator::{cond, flat_map},
5};
6
7use crate::binary::{
8    errors::ParseResult,
9    scalars::{dword, parse_string, short, word, Dword, Short, Word},
10};
11
12#[derive(Debug)]
13pub struct TilesetChunk<'a> {
14    /// Tileset ID
15    pub id: Dword,
16    /// Tileset flags
17    pub flags: TilesetFlags,
18    /// Number of tiles
19    pub number_of_tiles: Dword,
20    /// Tile Width
21    pub width: Word,
22    /// Tile Height
23    pub height: Word,
24    /// Base Index: Number to show in the screen from the tile with
25    /// index 1 and so on (by default this is field is 1, so the data
26    /// that is displayed is equivalent to the data in memory). But it
27    /// can be 0 to display zero-based indexing (this field isn't used
28    /// for the representation of the data in the file, it's just for
29    /// UI purposes).
30    pub base_index: Short,
31    /// Name of the tileset
32    pub name: &'a str,
33    /// Link to external file
34    pub external_file: Option<TilesetExternalFile>,
35    /// Tiles inside this file
36    pub tiles: Option<TilesetTiles<'a>>,
37}
38
39#[derive(Debug, Copy, Clone)]
40pub struct TilesetExternalFile {
41    /// ID of the external file. This ID is one entry
42    /// of the the External Files Chunk.
43    pub external_file_id: Dword,
44    /// Tileset ID in the external file
45    pub tileset_id: Dword,
46}
47
48#[derive(Debug)]
49pub struct TilesetTiles<'a> {
50    /// Compressed Tileset image (see NOTE.3):
51    /// (Tile Width) x (Tile Height x Number of Tiles)
52    pub data: &'a [u8],
53}
54
55bitflags! {
56    #[derive(Debug)]
57    pub struct TilesetFlags: Dword {
58        /// 1 - Include link to external file
59        const EXTERNAL_FILE = 1;
60        /// 2 - Include tiles inside this file
61        const TILES = 2;
62        /// 4 - Tilemaps using this tileset use tile ID=0 as empty tile
63        /// (this is the new format). In rare cases this bit is off,
64        /// and the empty tile will be equal to 0xffffffff (used in
65        /// internal versions of Aseprite)
66        const TILE_0_EMPTY = 4;
67        /// 8 - Aseprite will try to match modified tiles with their X
68        /// flipped version automatically in Auto mode when using
69        /// this tileset.
70        const XFLIP = 8;
71        /// 16 - Same for Y flips
72        const YFLIP = 16;
73        /// 32 - Same for D(iagonal) flips
74        const DFLIP = 32;
75    }
76}
77
78pub fn parse_tileset_chunk(input: &[u8]) -> ParseResult<'_, TilesetChunk<'_>> {
79    let (input, id) = dword(input)?;
80    let (input, flags) = dword(input)?;
81    let flags = TilesetFlags::from_bits_truncate(flags);
82    let (input, number_of_tiles) = dword(input)?;
83    let (input, width) = word(input)?;
84    let (input, height) = word(input)?;
85    let (input, base_index) = short(input)?;
86    let (input, _) = take(14usize)(input)?;
87    let (input, name) = parse_string(input)?;
88    let (input, external_file) = cond(
89        flags.contains(TilesetFlags::EXTERNAL_FILE),
90        parse_external_file,
91    )(input)?;
92    let (input, tiles) = cond(flags.contains(TilesetFlags::TILES), parse_tiles)(input)?;
93    Ok((
94        input,
95        TilesetChunk {
96            id,
97            flags,
98            number_of_tiles,
99            width,
100            height,
101            base_index,
102            name,
103            external_file,
104            tiles,
105        },
106    ))
107}
108
109pub fn parse_external_file(input: &[u8]) -> ParseResult<'_, TilesetExternalFile> {
110    let (input, external_file_id) = dword(input)?;
111    let (input, tileset_id) = dword(input)?;
112    Ok((
113        input,
114        TilesetExternalFile {
115            external_file_id,
116            tileset_id,
117        },
118    ))
119}
120
121use nom::combinator::map;
122
123pub fn parse_tiles(input: &[u8]) -> ParseResult<'_, TilesetTiles<'_>> {
124    map(flat_map(dword, take), |data| TilesetTiles { data })(input)
125}