From 521f570c28cb2df69e9c9183cced244045be4ba9 Mon Sep 17 00:00:00 2001 From: alex baldwin Date: Sat, 18 Jul 2026 21:38:33 -0400 Subject: [PATCH 1/8] tileset support --- aseprite.ksy | 184 ++++++++++++++++++++++++++++++++++++++++++++++++--- index.js | 6 ++ 2 files changed, 182 insertions(+), 8 deletions(-) diff --git a/aseprite.ksy b/aseprite.ksy index 226e68f..5a7ce90 100644 --- a/aseprite.ksy +++ b/aseprite.ksy @@ -62,7 +62,7 @@ types: Palette entry (index) which represents the transparent color in all non-background layers (only for pixel_format=indexed (8bpp) sprites). - - type: b24 + - size: 3 - id: num_colors type: u2 doc: | @@ -104,11 +104,21 @@ types: types: flags_bitset: seq: - - type: b7 + - type: b6 - id: valid_opacity type: b1 - doc: Layer opacity has a valid value - - type: b24 + doc: | + Layer opacity has a valid value + - id: valid_groups + type: b1 + doc: | + Layer blend mode/opacity is valid for groups + (composite groups separately first when rendering) + - id: layers_uuid + type: b1 + doc: | + Layers have an UUID + - type: b23 frame: seq: - id: header @@ -179,6 +189,7 @@ types: 'chunk_type_enum::palette': palette_chunk 'chunk_type_enum::userdata': userdata_chunk 'chunk_type_enum::slice': slice_chunk + 'chunk_type_enum::tileset': tileset_chunk _: dummy_chunk enums: chunk_type_enum: @@ -194,6 +205,7 @@ types: 0x2019: palette 0x2020: userdata 0x2022: slice + 0x2023: tileset types: dummy_chunk: seq: @@ -411,10 +423,21 @@ types: encoding: utf-8 doc: | Layer name + - id: tileset_index + type: u4 + if: type == type_enum::tilemap + doc: | + Tileset index + - id: uuid + size: 16 + if: _parent._parent._parent.header.flags.layers_uuid == true + doc: | + Layer's universally unique identifier enums: type_enum: 0: image 1: group + 2: tilemap blend_mode_enum: 0: normal 1: multiply @@ -503,7 +526,13 @@ types: enum: cel_type_enum doc: | The format of the cel's contents - - size: 7 + - id: z_index + type: s2 + doc: | + 0 = default layer ordering + +N = show this cel N layers later + -N = show this cel N layers back + - size: 5 - id: raw_width type: u2 if: type == cel_type_enum::raw @@ -564,11 +593,55 @@ types: Pixels are read row by row from top to bottom, for each scanline read pixels from left to right. + - id: tiles_width + type: u2 + if: type == cel_type_enum::compressed_tilemap + doc: | + Width in number of tiles + - id: tiles_height + type: u2 + if: type == cel_type_enum::compressed_tilemap + doc: | + Height in number of tiles + - id: bits_per_tile + type: u2 + if: type == cel_type_enum::compressed_tilemap + doc: | + Bits per tile (at the moment it's always 32-bit per tile) + - id: tile_id_bitmask + type: u4 + if: type == cel_type_enum::compressed_tilemap + doc: | + Bitmask for tile ID (e.g. 0x1fffffff for 32-bit tiles) + - id: x_flip_bitmask + type: u4 + if: type == cel_type_enum::compressed_tilemap + doc: | + Bitmask for X flip + - id: y_flip_bitmask + type: u4 + if: type == cel_type_enum::compressed_tilemap + doc: | + Bitmask for Y flip + - id: d_flip_bitmask + type: u4 + if: type == cel_type_enum::compressed_tilemap + doc: | + Bitmask for diagonal flip (swap X/Y axis) + - size: 10 + if: type == cel_type_enum::compressed_tilemap + - id: tiles_compressed + size: _parent.size - 54 + if: type == cel_type_enum::compressed_tilemap + doc: | + Row by row, from top to bottom tile by tile + compressed with ZLIB method (see NOTE.3) enums: cel_type_enum: 0: raw 1: linked 2: compressed + 3: compressed_tilemap cel_extra_chunk: seq: - id: flags @@ -805,15 +878,19 @@ types: types: flags_bitset: seq: - - type: b6 + - type: b5 + - id: has_text + type: b1 + doc: | + Userdata has textual information - id: has_color type: b1 doc: | Userdata has color information - - id: has_text + - id: has_properties type: b1 doc: | - Userdata has textual information + Userdata has properties - type: b24 mask_chunk: seq: @@ -848,3 +925,94 @@ types: doc: | Each byte contains 8 pixels (the leftmost pixels are packed into the high order bits) + tileset_chunk: + seq: + - id: tileset_id + type: u4 + doc: | + Tileset ID + - id: flags + type: flags_bitset + doc: | + Flags for tileset + - id: num_tiles + type: u4 + doc: | + Number of tiles + - id: tile_width + type: u2 + doc: | + Tile width + - id: tile_height + type: u2 + doc: | + Tile height + - id: base_index + type: s2 + doc: | + UI only + - size: 14 + - id: name_size + type: u2 + - id: name + type: str + size: name_size + encoding: utf-8 + doc: | + Tileset name + - id: external_file_id + type: u4 + if: flags.include_external == true + doc: | + ID of the external file. This ID is one entry + of the the External Files Chunk. + - id: external_tileset_id + type: u4 + if: flags.include_external == true + doc: | + Tileset ID in the external file + - id: data_length + type: u4 + if: flags.include_internal == true + doc: | + Data length of the compressed Tileset image + - id: pixels_compressed + size: data_length + if: flags.include_internal == true + doc: | + Compressed Tileset image (see NOTE.3): + (Tile Width) x (Tile Height x Number of Tiles) + types: + flags_bitset: + seq: + - type: b4 + - id: include_external + type: b1 + doc: | + Include link to external file + - id: include_internal + type: b1 + doc: | + Include tiles inside this file + - id: empty_zero + type: b1 + doc: | + Tilemaps using this tileset use tile ID=0 as empty tile + (this is the new format). In rare cases this bit is off, + and the empty tile will be equal to 0xffffffff (used in + internal versions of Aseprite) + - id: x_auto + type: b1 + doc: | + Aseprite will try to match modified tiles with their X + flipped version automatically in Auto mode when using + this tileset. + - id: y_auto + type: b1 + doc: | + Same as x_auto but for Y flips + - id: d_auto + type: b1 + doc: | + Same as x_auto but for diagonal flips + - type: b22 diff --git a/index.js b/index.js index 33904ca..7560e71 100644 --- a/index.js +++ b/index.js @@ -42,7 +42,13 @@ function inflate(ase) { const cel = chunk.data; if (cel.type === CelTypeEnum.COMPRESSED) { cel.pixels = zlib.inflateSync(cel.pixelsCompressed); + } else if (cel.type === CelTypeEnum.COMPRESSED_TILEMAP) { + cel.tiles = zlib.inflateSync(cel.tilesCompressed); } + } else if (chunk.type === ChunkTypeEnum.TILESET) { + chunk.data.pixels = zlib.inflateSync( + chunk.data.pixelsCompressed, + ); } } } From 824f79dcacbf813c9aad3982b30eb1f9d9a513fb Mon Sep 17 00:00:00 2001 From: alex baldwin Date: Sun, 19 Jul 2026 15:13:52 -0400 Subject: [PATCH 2/8] =?UTF-8?q?reverse=20bitset=20sequences=20=F0=9F=98=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aseprite.ksy | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/aseprite.ksy b/aseprite.ksy index 5a7ce90..1527bea 100644 --- a/aseprite.ksy +++ b/aseprite.ksy @@ -104,21 +104,21 @@ types: types: flags_bitset: seq: - - type: b6 - - id: valid_opacity + - type: b5 + - id: layers_uuid type: b1 doc: | - Layer opacity has a valid value + Layers have an UUID - id: valid_groups type: b1 doc: | Layer blend mode/opacity is valid for groups (composite groups separately first when rendering) - - id: layers_uuid + - id: valid_opacity type: b1 doc: | - Layers have an UUID - - type: b23 + Layer opacity has a valid value + - type: b24 frame: seq: - id: header @@ -879,18 +879,18 @@ types: flags_bitset: seq: - type: b5 - - id: has_text + - id: has_properties type: b1 doc: | - Userdata has textual information + Userdata has properties - id: has_color type: b1 doc: | Userdata has color information - - id: has_properties + - id: has_text type: b1 doc: | - Userdata has properties + Userdata has textual information - type: b24 mask_chunk: seq: @@ -986,14 +986,20 @@ types: flags_bitset: seq: - type: b4 - - id: include_external + - id: d_auto type: b1 doc: | - Include link to external file - - id: include_internal + Same as x_auto but for diagonal flips + - id: y_auto type: b1 doc: | - Include tiles inside this file + Same as x_auto but for Y flips + - id: x_auto + type: b1 + doc: | + Aseprite will try to match modified tiles with their X + flipped version automatically in Auto mode when using + this tileset. - id: empty_zero type: b1 doc: | @@ -1001,18 +1007,12 @@ types: (this is the new format). In rare cases this bit is off, and the empty tile will be equal to 0xffffffff (used in internal versions of Aseprite) - - id: x_auto - type: b1 - doc: | - Aseprite will try to match modified tiles with their X - flipped version automatically in Auto mode when using - this tileset. - - id: y_auto + - id: include_internal type: b1 doc: | - Same as x_auto but for Y flips - - id: d_auto + Include tiles inside this file + - id: include_external type: b1 doc: | - Same as x_auto but for diagonal flips + Include link to external file - type: b22 From 351f8ae1bf9d9f99af7dafb24c0169fdaca98218 Mon Sep 17 00:00:00 2001 From: alex baldwin Date: Sun, 19 Jul 2026 15:18:15 -0400 Subject: [PATCH 3/8] add external files chunk --- aseprite.ksy | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/aseprite.ksy b/aseprite.ksy index 1527bea..263a2c5 100644 --- a/aseprite.ksy +++ b/aseprite.ksy @@ -184,6 +184,7 @@ types: 'chunk_type_enum::cel': cel_chunk 'chunk_type_enum::cel_extra': cel_extra_chunk 'chunk_type_enum::color_profile': color_profile_chunk + 'chunk_type_enum::external_files': external_files_chunk 'chunk_type_enum::mask': mask_chunk 'chunk_type_enum::tags': tags_chunk 'chunk_type_enum::palette': palette_chunk @@ -199,6 +200,7 @@ types: 0x2005: cel 0x2006: cel_extra 0x2007: color_profile + 0x2008: external_files 0x2016: mask 0x2017: path 0x2018: tags @@ -430,7 +432,7 @@ types: Tileset index - id: uuid size: 16 - if: _parent._parent._parent.header.flags.layers_uuid == true + if: _root.header.flags.layers_uuid == true doc: | Layer's universally unique identifier enums: @@ -925,6 +927,34 @@ types: doc: | Each byte contains 8 pixels (the leftmost pixels are packed into the high order bits) + external_files_chunk: + seq: + - id: num_entries + type: u4 + - size: 8 + - id: entries + type: entry + repeat: expr + repeat-expr: num_entries + types: + entry: + seq: + - id: id + type: u4 + - id: type + type: u1 + doc: | + 0 - External palette + 1 - External tileset + 2 - Extension name for properties + 3 - Extension name for tile management (can exist one per sprite) + - size: 7 + - id: external_id_size + type: u2 + - id: external_id + type: str + size: external_id_size + encoding: utf-8 tileset_chunk: seq: - id: tileset_id From b5254101ae9d0f53705fd5f220ccf28ee1156012 Mon Sep 17 00:00:00 2001 From: alex baldwin Date: Sun, 19 Jul 2026 15:24:58 -0400 Subject: [PATCH 4/8] fix tileset flag padding --- aseprite.ksy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aseprite.ksy b/aseprite.ksy index 263a2c5..68587f0 100644 --- a/aseprite.ksy +++ b/aseprite.ksy @@ -1015,7 +1015,7 @@ types: types: flags_bitset: seq: - - type: b4 + - type: b2 - id: d_auto type: b1 doc: | @@ -1045,4 +1045,4 @@ types: type: b1 doc: | Include link to external file - - type: b22 + - type: b24 From b1852f4cfbceb3850d14d1fd36c08bc19662ec52 Mon Sep 17 00:00:00 2001 From: alex baldwin Date: Sun, 19 Jul 2026 16:02:07 -0400 Subject: [PATCH 5/8] add docstrings --- aseprite.ksy | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/aseprite.ksy b/aseprite.ksy index 68587f0..9c4d08c 100644 --- a/aseprite.ksy +++ b/aseprite.ksy @@ -931,6 +931,8 @@ types: seq: - id: num_entries type: u4 + doc: | + Number of entries - size: 8 - id: entries type: entry @@ -941,6 +943,8 @@ types: seq: - id: id type: u4 + doc: | + Entry ID (this ID is referenced by tilesets, palettes, or extended properties) - id: type type: u1 doc: | @@ -949,12 +953,14 @@ types: 2 - Extension name for properties 3 - Extension name for tile management (can exist one per sprite) - size: 7 - - id: external_id_size + - id: name_size type: u2 - - id: external_id + - id: name type: str - size: external_id_size + size: name_size encoding: utf-8 + doc: | + External file name or extension ID tileset_chunk: seq: - id: tileset_id From d07d82429eb0d27b1afbbeb5c3fccf4ad4866a61 Mon Sep 17 00:00:00 2001 From: alex baldwin Date: Sun, 19 Jul 2026 16:03:23 -0400 Subject: [PATCH 6/8] format --- index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/index.js b/index.js index 7560e71..ebdea7e 100644 --- a/index.js +++ b/index.js @@ -46,9 +46,7 @@ function inflate(ase) { cel.tiles = zlib.inflateSync(cel.tilesCompressed); } } else if (chunk.type === ChunkTypeEnum.TILESET) { - chunk.data.pixels = zlib.inflateSync( - chunk.data.pixelsCompressed, - ); + chunk.data.pixels = zlib.inflateSync(chunk.data.pixelsCompressed); } } } From 3d5b74a56aaca3ca14ece125e079b0ff7131293c Mon Sep 17 00:00:00 2001 From: alex baldwin Date: Sun, 19 Jul 2026 16:27:48 -0400 Subject: [PATCH 7/8] remove userdata has_properties for now --- aseprite.ksy | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/aseprite.ksy b/aseprite.ksy index 9c4d08c..1e716ab 100644 --- a/aseprite.ksy +++ b/aseprite.ksy @@ -880,11 +880,7 @@ types: types: flags_bitset: seq: - - type: b5 - - id: has_properties - type: b1 - doc: | - Userdata has properties + - type: b6 - id: has_color type: b1 doc: | From a76ccf83199a0c3c3092c1427f080d748ede55ca Mon Sep 17 00:00:00 2001 From: alex baldwin Date: Mon, 20 Jul 2026 20:56:44 -0400 Subject: [PATCH 8/8] remove note references --- aseprite.ksy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aseprite.ksy b/aseprite.ksy index 1e716ab..6ed2540 100644 --- a/aseprite.ksy +++ b/aseprite.ksy @@ -637,7 +637,7 @@ types: if: type == cel_type_enum::compressed_tilemap doc: | Row by row, from top to bottom tile by tile - compressed with ZLIB method (see NOTE.3) + compressed with ZLIB method enums: cel_type_enum: 0: raw @@ -1012,7 +1012,7 @@ types: size: data_length if: flags.include_internal == true doc: | - Compressed Tileset image (see NOTE.3): + Compressed Tileset image: (Tile Width) x (Tile Height x Number of Tiles) types: flags_bitset: