SPU file structure

Brought to you by the ESS Open Firmware Team

 

file structure is

      table 1 + table 2 + table 3

 

words and dwords are in big endian

 

Table 1

offset   size  description

0x0      0x2   Offset to table 3.  If table 3 is not present, it will point 1 byte past the end of the file.

0x2      0x2   Offset to table 2.

0x4      ???   variable length picture data.  Format described below.

 

 

Table 2

Set of commands.  Presented here in the order the commands are usually placed into the table (hence the offset).

 

0x0      0x5/6 Unknown initial values.  May be version of SPU & table loc.

               examples:

 

                image1.spu   :   00 08 03 c6 00  (table offset)

                image2.spu   :   00 00 06 62 01  (table offset + 18)

                111.spu      :00 00 00 5d 00 01  (table offset + 19)

                a8image1.spu :   00 00 03 48 00  (table offset)

                yamalogo.spu :00 00 0b 73 00 01  (table offset + 19)

                logo.spu     :00 00 00 69 00 01  (table offset + 19)

 

               First two bytes are usually 0.  Older images may have other values, but the newest images always have 0.

 

               The next 2 bytes are either the table 2 offset, or the table 2 offset + 0x18 or 0x19.

               If it is the table 2 offset + 0x19, then there will be an additional 0 after it.  This is typical for the newer images.

 

               Following that will be either a 0 or 1.  Newer images have a 1.

 

0x6      0x1   Command 0x03: Set palette

0x7      0x2   Palette, one nibble per color.  Defaults listed below.  Changes as per table 3.

               0x0 = black

               0x1 = dk red

               0x2 = dk green

               0x3 = dk yellow (brown)

               0x4 = dk blue

               0x5 = dk purple

               0x6 = dk cyan

               0x7 = lt grey

               0x8 = dk grey

               0x9 = red

               0xa = green

               0xb = yellow

               0xc = blue

               0xd = purple

               0xe = cyan

               0xf = white

 

0x9      0x1   Command 0x04:  Set contrast / intensity (transparency)

0xa      0x1   High nibble = emph2, low nibble = emph1

0xb      0x1   High nibble = pattern, Low nibble = back

 

0xc      0x1   Command 0x05:  Set Initial Coordinates

0xd      0x3   Horizontal (0x1001 * start line + bitmap width - 1)

0x10     0x3   Vertical   (0x1001 * start col + bitmap height - 1)

 

0x13     0x1   Command 0x06:  Image 1/Image 2 Offsets

0x14     0x2   Always 0x0004 (Start of Image 1)

0x16     0x2   Offset to the start of the second half of the interlaced image.

 

0x18     0x1   Command 0xFF:  Time Delay

0x19     0x2   Time parameter 1:  Usually starts at 5 and increments 6n, where n = value of fade in, last, or fade out.

0x1b     0x2   Time parameter 2:  Starts at 0x5d and increases 8 each step. If fade in is done, this will start at 0x65 to force the image to not be displayed at all during the 0x5d time frame.

 

The remainder of table 2 consists of commands as above.  The exact

commands are created depending upon the settings of fade in / last / fade out.

If Fade In is > 0, then there will be 8 command sets of 0xff & 0x04 to fade in

the image.  The time the image stays on the screen will be encoded with

one set of 0xff & 0x04 commands, but if there is a fade in then there may be

other commands setting the palette, etc.  Fade out will follow, also

with 8 command sets if the setting is > 0.

 

????     0x1   Command 0x02:  End Of Commands.  Note:  This is NOT always present.  Sometimes it just does 0xff as padding.  Most recent files include this value.

 

The ending bytes are followed by 0xff to pad to the nearest DWORD (not less

than 1 0xff).

 

Table 3 (only present if the image is a logo.  Screensaver images are logos).  0x40 bytes.

structure is 15 palette DWORDs indicating color.  Format is in YUV. Values

are rounded to the nearest 0x10.

Y = (0.257 * R) + (0.504 * G) + (0.098 * B) + 16

Cr = (0.439 * R) - (0.368 * G) - (0.071 * B) + 128

Cb = -(0.148 * R) - (0.291 * G) + (0.439 * B) + 128

 

00108080 - black

0030c070 - dk red

00505060 - dk green

00709040 - dk yellow

002070c0 - dk blue

0040b0a0 - dk purple

00604090 - dk cyan

00b08080 - lt grey

00808080 - dk grey

0050f060 - lt red

00902030 - lt green

00d09010 - lt yellow

003070f0 - lt blue

0070e0d0 - lt purple

00b010a0 - lt cyan

00f08080 - white

 

That is the default order, assigning index 0 to 15 in order.  However,

this order can be modified causing the index values to mean something

different.  Eg, if you were to swap dk red and black, then dk red

becomes index 0, and black becomes index 1.

 

 

Variable Length Picture Data format

 

Lines are interlaced.  Therefore, there must be an even number of lines.

 

Next line always starts on a byte boundary. 

 

Picture is always a 4 color picture.  The 4 colors map to the 4 colors in the palette.

eg, if the palette is 0x57f0, then

    lo byte lo nibble = 0,

    lo byte hi nibble = f,

    hi byte lo nibble = 7,

    hi byte hi nibble = 5.

        and those colors correspond to the colors in the palette (5 = dk purple).

 

The RLE encoding is similar to the gaussian distributed data optimal coding

method, but has been modified.

 

Decode from hi nibble to lo nibble (reading extra nibbles marked by 'y'), from the start of the image as follows:

 

0000 = lo byte lo nibble, repeat to end of line (end of scan line mark)

0001 = lo byte hi nibble, repeat to end of line (end of scan line mark)

0002 = hi byte lo nibble, repeat to end of line (end of scan line mark)

0003 = hi byte hi nibble, repeat to end of line (end of scan line mark)

0004-f = not valid

01yy  64 of lo byte lo nibble

02yy  128 of lo byte lo nibble

03yy  212 of lo byte lo nibble

04y   16 of lo byte lo nibble

05y   20 of lo byte lo nibble

06y   24 of lo byte lo nibble

07y   28 of lo byte lo nibble

08y   32 of lo byte lo nibble

09y   36 of lo byte lo nibble

0ay   40 of lo byte lo nibble

0by   44 of lo byte lo nibble

0cy   48 of lo byte lo nibble

0dy   52 of lo byte lo nibble

0ey   56 of lo byte lo nibble

0fy   60 of lo byte lo nibble

1y   4 of lo byte lo nibble

2y   8 of lo byte lo nibble

3y   12 of lo byte lo nibble

4 = lo byte lo nibble one pixel

5 = lo byte hi nibble one pixel

6 = hi byte lo nibble one pixel

7 = hi byte hi nibble one pixel

8  = 2 of lo byte lo nibble

9  = 2 of lo byte hi nibble

a  = 2 of hi byte lo nibble

b  = 2 of hi byte hi nibble

c  = 3 of lo byte lo nibble

d  = 3 of lo byte hi nibble

e  = 3 of hi byte lo nibble

f  = 3 of hi byte hi nibble

 

 

For example.  Suppose image is 6x6.  Image data is:

00 00 49 a0 00 00 47 65 70 00 00 47 56 70 00 00 4a 90 00 00 00 00

 

row #1:  0000 means repeat pal #0 to end of row.

000000

next interlaced row is 3.

row #3:  4 means a single instance of pal #0.  9 indicates 2 instances of pal #1.

         a indicates 2 instance of pal #2. 0000 = repeat to end of row pal #0. 

         The remaining 0 is discarded to be byte aligned. 

011220

next interlaced row is 5.

row #5:  47657 correspond to one pixel each, the 0000 means repeat 0.  We discard the remaining 0, and

         since the pic is already 5 pixels wide, the repeat is ignored.

032130

 

row #2:  47567, similar to prior row.  0000, again -- same occurance.

031230

 

row #4:  4 is a single pixel pal #0, a is 2 pixels of pal #2. 9 is 2 pixels of pal #1. 0000, again discard

         0 and ignore since we have already hit the target size.

022110

 

row #6:  0000 Same as row #1.  Repeat pal #0 to end of row.

000000

 

 

Put together in order, you get:

 

000000

031230

011220

022110

032130

000000

 

Here are some examples to illustrate the more complex items.

 

Suppose you had a "01 25" sequence  in the image.  That would be equivalent to 0x0155 / 4 = 0x55 with a remainder of 1.  That means 85 (decimal) repeating pixels of pal #1. 

 

01 02 = 0x0102.  0x0102/4 = 64 (decimal) repeating sequences of pal #2. 

 

0a 66 = 0x0a6. 0x0a6 / 4 = 41 (decimal) sequences of pal # 3.  The 6 on the end (of 0a 66) would indicate that the sequence is followed by a single pixel of pal #2.