Created
July 13, 2016 00:25
-
-
Save phrohdoh/2bbfe1ef324db9d7f4d24d23c2568938 to your computer and use it in GitHub Desktop.
Age of Empires 1997 SLP format
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
What follows is a description of the SLP format. | |
The actual author wishes to remain unknown, please email bryce@lanset.com with | |
your questions. However, I did not write this document. | |
Header | |
This structure totals 32 bytes and is packed to 1-byte boundaries. | |
typedef struct Shape_File_Header | |
{ | |
char Version[4]; // Usually '2.0N' | |
long Num_Shapes; | |
char Comment[24]; | |
} Shape_File_Header; | |
The header is immediately followed by an array of size "Num_Shapes" of | |
structures with info for each shape. | |
typedef struct Shape_Info | |
{ | |
UInt32 Shape_Data_Offsets; | |
UInt32 Shape_Outline_Offset; | |
UInt32 Palette_Offset; | |
UInt32 Properties; | |
int Width; | |
int Height; | |
int Hotspot_X; | |
int Hotspot_Y; | |
} Shape_Info; | |
The Shape_Data_Offsets and Shape_Outline_Offset point to arrays of data, each | |
of length "Height". The "Shape_Data_Offsets" array is an array of UInt32 | |
values, the "Shape_Outline_Offset" array consists of 2 UInt16 values. | |
For each vertical line of data in the shape, the Shape_Data_Offsets array | |
points to the start of the data for that line (for fast lookup in the blitter). | |
The Shape_Outline_Offset array contians two x-values for each line of the | |
sprite. The first 16-bit value defines how far from the left-edge the sprite | |
data starts drawing, while the second value defines how far from the right-edge | |
the sprite stops drawing. The (offset right - offset left) value thus gives the | |
length in pixels of the vertical line of the sprite. | |
I haven't seen a SLP with a value for the palette offset, so I'm not sure if | |
it's used. The "Properties" field is more or less unused by the game. A value | |
of 0x10 indicates to use the default game palette, and a value of 0x00 | |
indicates to use the "global" palette. I believe both are the same for our | |
purposes. | |
The Hotspot_X and _Y values are used (I think) to represent the center of the | |
SLP for purposes of moving and manipulating them. For SLPs that are cursors, | |
they represent the trigger point. | |
The actual SLP data starts at the "Shape_Data_Offset" for each line, and is | |
run-length encoded using a set of 15 commands. You start by taking the first | |
byte in the data for the line and acting on it as a command. The command code | |
itself is stored in the low-nibble of the byte, e.g. command = data & 0x0f. | |
case 0, 4, 8, 0x0c: Block Copy (short) (LLLL LL00) | |
In these cases, the length of the block copy is stored in the top 6 bits of the | |
command byte, e.g. length = command >> 2. The data to copy follows the command | |
byte and is "length" bytes long. Thus, these commands are good for small chunks | |
of non-repeating data, up to a length of 64 bytes. | |
case 1, 5, 9, 0x0d: Skip pixels (short) (LLLL LL01) | |
These commands skip a range of pixels, up to 64. The length is encoded like | |
with the prior command, i.e. length = command >> 2. This command is mainly used | |
to draw an empty space in the middle of a sprite, as it just moves the pointer | |
to the drawing buffer forward. | |
case 2: Block Copy (big) (0000 0010) | |
Like the first Block Copy, but supports ranges > 64 bytes. The top 4 bits of | |
the command byte are shifted left 4 bits and added to the next byte in the | |
command stream to get a length of 0-0xfff, i.e. length = ((command & 0xf0) << | |
4) + next_byte. Following that is the stream of data to copy, of size "length". | |
case 3: Skip pixels (big) (0000 0011) | |
The length is determined just like the big block copy command, and it behaves | |
like the other skip command in that it just moves the pointer to the drawing | |
buffer. | |
case 6: Copy & Transform block (LLLL 0110) | |
The length of this block is determined by the high 4 bits of the command byte. | |
If they are non-zero, then the length is in the range of 1-15. If the high 4 | |
bits are zero, then the next byte in the stream is read and used as the length. | |
After that, a range of "length" bytes is read, and each byte is or-ed with the | |
player color to determine the final color of the byte. The player color value | |
is an index into the palette, and I believe the range is 0-15. | |
case 7: Fill block (LLLL 0111) | |
The length is determined as with case 6. The next byte in the stream determines | |
the color of the run. The run length is then filled with this color for | |
"length" bytes. | |
case 0x0a: Transform block (LLLL 1010) | |
The length is determined as in cases 6 and 7. The next byte in the stream | |
determines the initial color of the block run, and it is and-ed to the shadow | |
"and" mask, and then or-ed to the shadow "or" mask. These masks are typically | |
something like 0xff00ff00 and 0x00ff00ff, and are used to draw shadow effects | |
in the game. This is typically used to overlay a checkerboard shadow sprite | |
onto the existing buffer. | |
case 0x0b: Shadow pixels (LLLL 1011) | |
The length is determined as in cases 6, 7 and 0x0a. For the length of the run, | |
the destination pixels already in the buffer are used as a lookup into a | |
"shadow table" and this lookup pixel is then used to draw into the buffer. The | |
shadow table is typically a color-tinted variation of the real color table, and | |
is generally used to draw things like the red-tinted checkerboard sprites when | |
you try to place a building in an area where it cannot be placed. | |
case 0x0e: Extended commands (0XXX 1110) | |
The high 4 bits are used to determine an extended command, so the entire | |
command byte is used. These commands are mainly used to draw the sprite | |
outlines that you see when you move behind trees. The subcommands are as | |
follows: | |
0x0e & 0x1e: (000X 1110) | |
These commands are used to hint to the renderer about the command that | |
follows. (The byte immediately following this command is just a regular command | |
byte.) If the special command is 0x0e, then the command that follows is only | |
drawn if the sprite is not x-flipped. If the special command is 0x1e, then the | |
command that follows is only drawn if the sprite is x-flipped. | |
0x2e & 0x3e: (001X 1110) | |
These set the transform color tables used in the regular commands. 0x2e | |
sets the renderer for the normal transform color table, 0x3e sets it for the | |
alternate transform color table. | |
0x4e & 0x6e: Draw "special color 1 or 2", 1 byte (01X0 1110) | |
The destination draw buffer is filled with 1 byte, which is the color table | |
value specified by "special color" 1 or 2 (depending on the command). These are | |
typically the colors that are used to draw the outline color that you see when | |
a sprite moves behind a tree. Special color 1 is usually the player color, | |
special color 2 is (in the case of SWGB) typically black to enhance the | |
outline. | |
0x5e & 0x7e: Draw "special color 1 or 2" as a run (01X1 1110) | |
The byte following the special command is used to determine the length of | |
the run. The destination buffer is filled with the special color 1 or 2 for | |
"length" bytes. | |
0x8e through 0xfe. These are unused. (1XXX 1110) | |
case 0x0f: End of Line (XXXX 1111) | |
Presence of this command indicates that the sprite commands for the current | |
line are finished and that the parser should move onto the next vertical line | |
in the sprite. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment