Skip to content

Sprite sheet

ProgSys edited this page May 29, 2020 · 52 revisions

Most sprite sheets are located at SUBDATA.DAT/START.DAT/ANM000.DAT and each sprite sheets is made of 8 parts:

  1. Header + 4 data pointers
  2. Animations
  3. Layers
  4. Sprite sheets informations
  5. Keyframes
  6. Cutouts
  7. Color table
  8. Sprite sheet images

1. Header

Type Size Description
uShort 2 byte Number of animations
uShort 2 byte Number of layers
uShort 2 byte Number of colortable sets?
uShort 2 byte Number of sprite sheets
uShort 2 byte Number of keyframes
uShort 2 byte Number of cutouts
uShort 2 byte Number of color tabels, is always 1?
uShort 2 byte Number of sprite sheets?

The four data pointers:

Type Size Description
uInt 4 byte Points to keyframes
uInt 4 byte Points to cutouts
uInt 4 byte Points to the color tables
uInt 4 byte Points to the image data

2. Animations

For each animations:

Type Size Description
uShort 2 byte Start keyframe index
uShort 2 byte Animation ID (like a name)

The end keyframe of an animation is the next animation starting keyframe.

3. Layers

A Layer is a set of cutouts, which get rendered in order.

For each layer:

Type Size Description
uShort 2 byte Start cutout index
uShort 2 byte Number of cutouts

4. Sprite sheet informations

At start there is a uInt (4 byte) with the size of the color table.

This contains the infomations about each sprite sheet. The width and height can be any number but the game matches them to the given power (2^x). So the sizes should be 16,32,64,128,256,512,1024 so on.

'The power of color table' determine the encoding of the ID sprite sheet. If the power is biger then 4 (16 colors) then each ID is saved as a uChar 1 byte.

For each sheet:

Type Size Description
uInt 4 byte Pointer to the image
uShort 2 byte Width
uShort 2 byte Height
uChar 1 byte The power of color table
uChar 1 byte image swizzled (not used on PC)
uChar 1 byte The power of width
uChar 1 byte The power of height

5. Keyframes

For each Keyframe:

Type Size Description
uShort 4 byte Layer index
uChar 2 byte Delay (ns?, s?)
uChar 2 byte Type: 1 - Start, 0 - Continue, 2 - End (End keyframe is not displayed?), 3 - effect marker?
uShort 4 byte ingame x offset? (mostly 0)
uShort 4 byte ingame y offset? (mostly 0)

Changed blinking keyframes, the delay has been increased and the end keyframe has been moved forward.

6. Cutouts

A cutout is a part of sprite sheet, which is build with a given color table.

For each cutout:

Type Size Description
uShort 2 byte External sheet ID, if not 0 then the keyframe references a outside file
uChar 1 byte Sprite sheet index
uChar 1 byte Color table offset
Short 2 byte Anchor X, point of rotation and mirror
Short 2 byte Anchor Y, point of rotation and mirror
uShort 2 byte X
uShort 2 byte Y
uShort 2 byte Width
uShort 2 byte Height
Short 2 byte Scale X (0 - 100)
Short 2 byte Scale Y (0 - 100)
Short 2 byte Offset X, from the anchor point
Short 2 byte Offset Y, from the anchor point
Short 2 byte Rotation is degree
uChar 1 byte Transparency (0-128)
uChar 1 byte 0x10-Mirror horizontally, 0x08-Mirror vertically, 0x04 - Blend mode

Left, 'scale X' was set from 100 to 50. Right, 'colortable index' was set from 0 to 2 (this color table is used for the character portrait).

7. Color table

The color table is build out if sets of 16 BGRA pixels. The size is 'size'*16*4

> Color table example.

8. Sprite sheet images

The image data holds a offset to a color table value, to this is also added a starting offset, which is defined in the cutouts. This means to fully reconstruct the colors you also need to use the cutouts.

There are two known fromats:

16 or less colors:

This is the most common format, in which each byte holds two indexes for a color table => two pixels.

const char c = reader.readChar();
unsigned char index1 = c & 0x0F;
unsigned char index2 = (c >> 4) & 0x0F;

XKeeper custom sprite sheet. Left, the image IDs 0-32. Right, color image build with two separate color tables, one for the character and one for the portrait.

up to 256 colors:

Each byte is one offset. This is used if used color table has more then 16 colors.

/*
 * colortabels - every color table as one long array
 * cut - the cutout information
 * cut.colortable - the colortable with cutout uses (start offset)
 * IDImage - the 16 or 256 ID image (offset)
 */
rgbaImage[i] = colortabels[cut.colortable*16 + IDImage[i]];