# Aseprite File Format (.ase/.aseprite) Specifications 1. [References](#references) 2. [Introduction](#introduction) 3. [Header](#header) 4. [Frames](#frames) 5. [Chunk Types](#chunk-types) 6. [File Format Changes](#file-format-changes) ## References ASE files use Intel (little-endian) byte order. * `BYTE`: An 8-bit unsigned integer value * `WORD`: A 16-bit unsigned integer value * `SHORT`: A 16-bit signed integer value * `DWORD`: A 32-bit unsigned integer value * `LONG`: A 32-bit signed integer value * `FIXED`: A 32-bit fixed point (16.16) value * `BYTE[n]`: "n" bytes. * `STRING`: - `WORD`: string length (number of bytes) - `BYTE[length]`: characters (in UTF-8) The `'\0'` character is not included. * `PIXEL`: One pixel, depending on the image pixel format: - **RGBA**: `BYTE[4]`, each pixel have 4 bytes in this order Red, Green, Blue, Alpha. - **Grayscale**: `BYTE[2]`, each pixel have 2 bytes in the order Value, Alpha. - **Indexed**: `BYTE`, each pixel uses 1 byte (the index). * `TILE`: **Tilemaps**: Each tile can be a 8-bit (`BYTE`), 16-bit (`WORD`), or 32-bit (`DWORD`) value and there are masks related to the meaning of each bit. ## Introduction The format is much like FLI/FLC files, but with different magic number and differents chunks. Also, the color depth can be 8, 16 or 32 for Indexed, Grayscale and RGB respectively, and images are compressed images with zlib. Color palettes are in FLI color chunks (it could be type=11 or type=4). For color depths more than 8bpp, palettes are optional. To read the sprite: * Read the [ASE header](#header) * For each frame do (how many frames? the ASE header has that information): + Read the [frame header](#frames) + For each chunk in this frame (how many chunks? the frame header has that information) - Read the chunk (it should be layer information, a cel or a palette) ## Header A 128-byte header (same as FLC/FLI header, but with other magic number): DWORD File size WORD Magic number (0xA5E0) WORD Frames WORD Width in pixels WORD Height in pixels WORD Color depth (bits per pixel) 32 bpp = RGBA 16 bpp = Grayscale 8 bpp = Indexed DWORD Flags: 1 = Layer opacity has valid value WORD Speed (milliseconds between frame, like in FLC files) DEPRECATED: You should use the frame duration field from each frame header DWORD Set be 0 DWORD Set be 0 BYTE Palette entry (index) which represent transparent color in all non-background layers (only for Indexed sprites). BYTE[3] Ignore these bytes WORD Number of colors (0 means 256 for old sprites) BYTE Pixel width (pixel ratio is "pixel width/pixel height"). If this or pixel height field is zero, pixel ratio is 1:1 BYTE Pixel height SHORT X position of the grid SHORT Y position of the grid WORD Grid width (zero if there is no grid, grid size is 16x16 on Aseprite by default) WORD Grid height (zero if there is no grid) BYTE[84] For future (set to zero) ## Frames After the header come the "frames" data. Each frame has this little header of 16 bytes: DWORD Bytes in this frame WORD Magic number (always 0xF1FA) WORD Old field which specifies the number of "chunks" in this frame. If this value is 0xFFFF, we might have more chunks to read in this frame (so we have to use the new field) WORD Frame duration (in milliseconds) BYTE[2] For future (set to zero) DWORD New field which specifies the number of "chunks" in this frame (if this is 0, use the old field) Then each chunk format is: DWORD Chunk size WORD Chunk type BYTE[] Chunk data ## Chunk Types ### Old palette chunk (0x0004) Ignore this chunk if you find the new palette chunk (0x2019) Aseprite v1.1 saves both chunks 0x0004 and 0x2019 just for backward compatibility. WORD Number of packets + For each packet BYTE Number of palette entries to skip from the last packet (start from 0) BYTE Number of colors in the packet (0 means 256) + For each color in the packet BYTE Red (0-255) BYTE Green (0-255) BYTE Blue (0-255) ### Old palette chunk (0x0011) Ignore this chunk if you find the new palette chunk (0x2019) WORD Number of packets + For each packet BYTE Number of palette entries to skip from the last packet (start from 0) BYTE Number of colors in the packet (0 means 256) + For each color in the packet BYTE Red (0-63) BYTE Green (0-63) BYTE Blue (0-63) ### Layer Chunk (0x2004) In the first frame should be a set of layer chunks to determine the entire layers layout: WORD Flags: 1 = Visible 2 = Editable 4 = Lock movement 8 = Background 16 = Prefer linked cels 32 = The layer group should be displayed collapsed 64 = The layer is a reference layer WORD Layer type 0 = Normal (image) layer 1 = Group 2 = Tilemap WORD Layer child level (see NOTE.1) WORD Default layer width in pixels (ignored) WORD Default layer height in pixels (ignored) WORD Blend mode (always 0 for layer set) Normal = 0 Multiply = 1 Screen = 2 Overlay = 3 Darken = 4 Lighten = 5 Color Dodge = 6 Color Burn = 7 Hard Light = 8 Soft Light = 9 Difference = 10 Exclusion = 11 Hue = 12 Saturation = 13 Color = 14 Luminosity = 15 Addition = 16 Subtract = 17 Divide = 18 BYTE Opacity Note: valid only if file header flags field has bit 1 set BYTE[3] For future (set to zero) STRING Layer name + If layer type = 2 DWORD Tileset index ### Cel Chunk (0x2005) This chunk determine where to put a cel in the specified layer/frame. WORD Layer index (see NOTE.2) SHORT X position SHORT Y position BYTE Opacity level WORD Cel Type 0 - Raw Image Data (unused, compressed image is preferred) 1 - Linked Cel 2 - Compressed Image 3 - Compressed Tilemap BYTE[7] For future (set to zero) + For cel type = 0 (Raw Image Data) WORD Width in pixels WORD Height in pixels PIXEL[] Raw pixel data: row by row from top to bottom, for each scanline read pixels from left to right. + For cel type = 1 (Linked Cel) WORD Frame position to link with + For cel type = 2 (Compressed Image) WORD Width in pixels WORD Height in pixels BYTE[] "Raw Cel" data compressed with ZLIB method (see NOTE.3) + For cel type = 3 (Compressed Tilemap) WORD Width in number of tiles WORD Height in number of tiles WORD Bits per tile (at the moment it's always 32-bit per tile) DWORD Bitmask for tile ID (e.g. 0x1fffffff for 32-bit tiles) DWORD Bitmask for X flip DWORD Bitmask for Y flip DWORD Bitmask for 90CW rotation BYTE[10] Reserved TILE[] Row by row, from top to bottom tile by tile compressed with ZLIB method (see NOTE.3) ### Cel Extra Chunk (0x2006) Adds extra information to the latest read cel. DWORD Flags (set to zero) 1 = Precise bounds are set FIXED Precise X position FIXED Precise Y position FIXED Width of the cel in the sprite (scaled in real-time) FIXED Height of the cel in the sprite BYTE[16] For future use (set to zero) ### Color Profile Chunk (0x2007) Color profile for RGB or grayscale values. WORD Type 0 - no color profile (as in old .aseprite files) 1 - use sRGB 2 - use the embedded ICC profile WORD Flags 1 - use special fixed gamma FIXED Fixed gamma (1.0 = linear) Note: The gamma in sRGB is 2.2 in overall but it doesn't use this fixed gamma, because sRGB uses different gamma sections (linear and non-linear). If sRGB is specified with a fixed gamma = 1.0, it means that this is Linear sRGB. BYTE[8] Reserved (set to zero) + If type = ICC: DWORD ICC profile data length BYTE[] ICC profile data. More info: http://www.color.org/ICC1V42.pdf ### External Files Chunk (0x2008) A list of external files linked with this file. It might be used to reference external palettes or tilesets. DWORD Number of entries BYTE[8] Reserved (set to zero) + For each entry DWORD Entry ID (this ID is referenced by tilesets or palettes) BYTE[8] Reserved (set to zero) STRING External file name ### Mask Chunk (0x2016) DEPRECATED SHORT X position SHORT Y position WORD Width WORD Height BYTE[8] For future (set to zero) STRING Mask name BYTE[] Bit map data (size = height*((width+7)/8)) Each byte contains 8 pixels (the leftmost pixels are packed into the high order bits) ### Path Chunk (0x2017) Never used. ### Tags Chunk (0x2018) After the tags chunk, you can write one user data chunk for each tag. E.g. if there are 10 tags, you can then write 10 user data chunks one for each tag. WORD Number of tags BYTE[8] For future (set to zero) + For each tag WORD From frame WORD To frame BYTE Loop animation direction 0 = Forward 1 = Reverse 2 = Ping-pong BYTE[8] For future (set to zero) BYTE[3] RGB values of the tag color Deprecated, used only for backward compatibility with Aseprite v1.2.x The color of the tag is the one in the user data field following the tags chunk BYTE Extra byte (zero) STRING Tag name ### Palette Chunk (0x2019) DWORD New palette size (total number of entries) DWORD First color index to change DWORD Last color index to change BYTE[8] For future (set to zero) + For each palette entry in [from,to] range (to-from+1 entries) WORD Entry flags: 1 = Has name BYTE Red (0-255) BYTE Green (0-255) BYTE Blue (0-255) BYTE Alpha (0-255) + If has name bit in entry flags STRING Color name ### User Data Chunk (0x2020) Insert this user data in the last read chunk. E.g. If we've read a layer, this user data belongs to that layer, if we've read a cel, it belongs to that cel, etc. There are some special cases: After a Tags chunk, there will be several user data fields, one for each tag, you should associate the user data in the same order as the tags are in the Tags chunk. In version 1.3 a sprite has associated user data, to consider this case there is an User Data Chunk at the first frame after the Palette Chunk. DWORD Flags 1 = Has text 2 = Has color + If flags have bit 1 STRING Text + If flags have bit 2 BYTE Color Red (0-255) BYTE Color Green (0-255) BYTE Color Blue (0-255) BYTE Color Alpha (0-255) ### Slice Chunk (0x2022) DWORD Number of "slice keys" DWORD Flags 1 = It's a 9-patches slice 2 = Has pivot information DWORD Reserved STRING Name + For each slice key DWORD Frame number (this slice is valid from this frame to the end of the animation) LONG Slice X origin coordinate in the sprite LONG Slice Y origin coordinate in the sprite DWORD Slice width (can be 0 if this slice hidden in the animation from the given frame) DWORD Slice height + If flags have bit 1 LONG Center X position (relative to slice bounds) LONG Center Y position DWORD Center width DWORD Center height + If flags have bit 2 LONG Pivot X position (relative to the slice origin) LONG Pivot Y position (relative to the slice origin) ### Tileset Chunk (0x2023) DWORD Tileset ID DWORD Tileset flags 1 - Include link to external file 2 - Include tiles inside this file 4 - 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) DWORD Number of tiles WORD Tile Width WORD Tile Height SHORT Base Index: Number to show in the screen from the tile with index 1 and so on (by default this is field is 1, so the data that is displayed is equivalent to the data in memory). But it can be 0 to display zero-based indexing (this field isn't used for the representation of the data in the file, it's just for UI purposes). BYTE[14] Reserved STRING Name of the tileset + If flag 1 is set DWORD ID of the external file. This ID is one entry of the the External Files Chunk. DWORD Tileset ID in the external file + If flag 2 is set DWORD Compressed data length PIXEL[] Compressed Tileset image (see NOTE.3): (Tile Width) x (Tile Height x Number of Tiles) ### Notes #### NOTE.1 The child level is used to show the relationship of this layer with the last one read, for example: Layer name and hierarchy Child Level ----------------------------------------------- - Background 0 `- Layer1 1 - Foreground 0 |- My set1 1 | `- Layer2 2 `- Layer3 1 #### NOTE.2 The layer index is a number to identify any layer in the sprite, for example: Layer name and hierarchy Layer index ----------------------------------------------- - Background 0 `- Layer1 1 - Foreground 2 |- My set1 3 | `- Layer2 4 `- Layer3 5 #### NOTE.3 Details about the ZLIB and DEFLATE compression methods: * https://www.ietf.org/rfc/rfc1950 * https://www.ietf.org/rfc/rfc1951 * Some extra notes that might help you to decode the data: http://george.chiramattel.com/blog/2007/09/deflatestream-block-length-does-not-match.html ## File Format Changes 1. The first change from the first release of the new .ase format, is the new frame duration field. This is because now each frame can have different duration. How to read both formats (old and new one)? You should set all frames durations to the "speed" field read from the main ASE header. Then, if you found a frame with the frame-duration field > 0, you should update the duration of the frame with that value.