SLP media file specification
============================


slp files are stored in the .drs files.

they contain all the (animation) textures.


slp files for movable objects
-----------------------------

for unit slp files, 5 states are stored:

0) Attacking
1) Standing Ground
2) Dying
3) Rotting
4) Moving

each state has ~50 textures for the animation -> ~250 textures per unit

for each animation, 5 directions of it are stored.

the other 3 directions are generated later by flipping the sprite on the y axis.

villagers have way more than the 5 animations, boats have 2.


slp files for static objects
----------------------------

contain the image of the building..

some static objects have multiple chunks, as moving creep can pass them somehow.


slp files for weapons
----------------------

for dust, arrows, etc.


slp files for shadows
---------------------

every object in game has a shadow. moving units have frames for their shadow in the same slp,
but buildings and other objects have their shadows in seperate slps.

-> large numbers of shadow SLPs unidentifable.



SLP file format
===============

the slp file starts with a header:

struct slp_header {
 char  version[4];
 int   num_frames;
 char  comment[24];
};

slp_header = Struct("< 4s i 24s")


after the header, there are num_frames entries of slp_frame_info.

struct slp_frame_info {
 unsigned int cmd_table_offset;
 unsigned int outline_table_offset;
 unsigned int palette_offset;
 unsigned int properties; //changes palette to use, but still unknown..
 int          width;
 int          height;
 int          hotspot_x; //center of the unit
 int          hotspot_y; //is referenced as the unit location by the engine
};

slp_frame_info = Struct("< I I I I i i i i")

slp_frame_info stores metainformation about one single frame (texture) within the slp.


palette offset is probably unused.
properties is more or less unused, but:
  0x10 means "default game palette",
  0x00 means "global color palette"

they are probably the same, you can ignore it.

one image of size width x height has height rows, of course.

at outline_table_offset (after the frame_infos),
an array of slp_frame_row_edge structs begins, length = height.

struct slp_frame_row_edge {
 unsigned short left_space;
 unsigned short right_space;
};

slp_frame_row_edge = Struct("< H H")

the right or left value is 0x8000 if the row is completely transparent.
no command bytes will end these transparent rows, you have to skip them later.

else, left and right specify the number of transparent pixels, from each side to the center.

width-left-right = number of pixels in this line.

after those padding frames, at cmd_table_offset, an array of 4byte-ints begins, length = height.

struct slp_command_offset {
 unsigned int offset;
}

slp_command_offset = Struct("< I")

each table/int defines the offset (beginning) of the first command of a row.
int[0] is offset to the first drawing command for the image.

these are not actually necessary to check, the commands can be read sequentially.
the command locations can be used for checking purposes though.


at cmd_table_offset + 4*height the command data itself begins.



slp drawing commands
====================

the image is drawn line by line, a line is finished with command eol.

a command is a one-byte number followed by command-specific data.

the command byte can contain command data.

e.g. color_list command (0x00) only checks the least-significant bits to  command type and the rest of the byte is the number of palette indices to follow.
a command is parsed, data values after it are drawn, then the next command byte is reached.

each commands triggers a drawing method for n=pixel_count pixels.


all the commands tell you to draw a palette_index, for n pixels.


the pixel_count value is stored at these locations:
  next           = next_byte after command
  >>2            = cmd_byte shifted 2 times right (most significant bits used as data value)
  >>n|next       = pixel_count = cmd_byte >> n; if pixel_count == 0: pixel_count = next_byte
                   -> 8-n most significant bits of cmd_byte if they are != 0, else next byte.
  >>4*256 + next = (cmd_byte & 0xf0 << 4) + next_byte


command_name     cmd_byte  pixel_count     description
-----------------------------------------------------
color_list         = 0x00, >>2            for count: (palette_index=next, draw color); count < 2^6 == 64
skip               = 0x01, >>2|next       for count: (draw transparent pixel)
big_color_list     = 0x02, <<4 + next     for count: (palette_index=next, draw this color)
big_skip           = 0x03, <<4 + next     for count: (draw even more transparent pixels)
player_color_list  = 0x06, >>4|next       for count: (palette_index=(next | player_color [0,15])) (maybe next+player*16)?
fill               = 0x07, >>4|next       palette_index=next, for count: (draw)
fill_player_color  = 0x0A, >>4|next       palette_index=next+(player*16), for count: (draw)
shadow             = 0x0B, >>4|next       for count: (draw shadow pixel, transparent)
eol                = 0x0F, 0              end of commands for this row, row is complete.

 -extended cmds, for outlines behind trees/buildings
render_hint_xflip  = 0x0E, 0              draw the following command if sprite is not flipped right to left
render_h_notxflip  = 0x1E, 0              draw following command if this sprite is xflipped
table_use_normal   = 0x2E, 0              transform color table: for regular cmd_bytes -> set to normal table
table_use_alternat = 0x3E, 0              transform color table: -> set to alternate table
outline_1          = 0x4E, 1 (spec_col=1) palette_index = player_index = player * 16, if obstructed, player col, else transparent
outline_2          = 0x6E, 1 (spec_col=2) palette_index = 0, if pixel obstructed, draw a pixel as black outline, else transparent
outline_span_2     = 0x5E, next           palette_index = player_index = player * 16, can be >=1 pixel
outline_span_2     = 0x7E, next           palette_index = 0, same as obstruct_black, >=1 pixel


note that commands with pixel_count == >>i do reuse the most significant bits of cmd_byte.
=> check the match for 8-i least significant bits only!

"<<4 + next" also double-uses the more significant bits, but now a 16-bit number is composed:
let c be an cmd_byte bit. cmd_byte = cccccccc
n is a next_byte bit. next_byte = nnnnnnnn
    <<4 + next = (cmd_byte & 0xf0) << 4 + next_byte
<=> cccc0000 << 4 + nnnnnnnn
<=> ccccnnnnnnnn
this number is then used as pixel_count.

bit double-using list for all commands:
r or s means this bit (may) be reused in this command:

rrrrrr00 == 0x00
rrrrrr01 == 0x01
rrrr0010 == 0x02  rrrr << 4
rrrr0011 == 0x03  rrrr << 4
----0100 == 0x04  0x04 and 0x05 was left out, because it would conflict with 0x00 and 0x01 as a prefix
----0101 == 0x05   you can read the prefix from right to left, and for 0x00 and 0x04 it would be 0brrrrx100,
rrrr0110 == 0x06   where x could be 0, which conflicts.
rrrr0111 == 0x07
----1000 == 0x08  = cmd 0x00, draw 2 times. 0x08 and 0x09 are also missing, because of the conflict from above
----1001 == 0x09  = cmd 0x01, draw 2 times.
00001010 == 0x0A
00001011 == 0x0B
----1100 == 0x0C  for the same reason, 0x0C and 0x0D are missing, the prefix woule be 0b0000xx{00,01}
----1101 == 0x0D   where xx could be 11 for command 0x00/0x01, that'd conflict with 0x0C/0x0D
xxxx1110 == 0x0E  xxxx = the extended command identifier
00001111 == 0x0F


0x06 comment addition:
player_color: index into the palette, range 0-15.

0x0A comment addition:
also called transform_block, may behave different than described above:
color = next
for count:
 draw = ((color & 0xff00ff00) | 0x00ff00ff).
0xnn.. are masks used to draw shadow effects, but 0xffffff makes no sense for and and or with a byte.
it's may be used for overlaying the checkerboard shadow sprite onto the current frame.
you'll see that when placing a buildings.

0x0B:
destination pixels already in the buffer are used as a lookup into a shadow_table
this lookup pixel is then used to draw into the buffer.

shadow table: color-tinted variation of the real color table
also used to draw things like the red-tinted checkerboard when placing buildings at forbidden places
(drawing real shadows is like drawing black, but with alpha=e.g. 0.5)


command 0x{4,5,6,7}E  indicate that the following command(s) are outlines of the graphic.
this is a potential outline pixel, it's what you see when a unit is behind something.
the engine should only draw them as the player color/black, if the pixel(s) are obstructed.
if they are not obstructed, draw them as transparent.
=> the e.g. left outline is one pixel more to the left than the first color.

for our purpose, these outline pixels should be stored to a second image, which is rendered
ontop of the tree/building by the fragment shader.

spec_col == special_color: 2 means black (enhance the outline, cartoonlike), 1 is player color.



with all this information it's possible to draw a line by several palette_index colors correctly,
but what real color must be drawn for one palette_index?


palette_index
=============

the drawing palette is stored inside the interfac.drs, its an indexed list of (r g b) tuples.

the file id is 50500+x, the palettes (color tables) are stored the same way as slps are.

x is the palette index, which should be 0, experiment with [1,10]...

interfac.drs contains many of these files, but the ingame art uses id 50500.

the palette is a jasc paint shop pro file, starting with "JASC-PAL\r\n".

read this line at the offset to verify existance.
next line stores version information, should be "0100".
next line stores number of entries, important.

now follow "x y z\r\n" lines, storing 24bit a color tuple.

index these, they are the references in the slp file.



you should now be able to draw stuff.