aseprite_loader/binary/chunks/
tileset.rs

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