Vertical XOR

The Vertical XOR encoding Scheme
The images in the game are generally encoded using several layers. This page defines what is almost always the final layer. This 'layer' of encoding is what we call a 'vertical XOR'. We call it that because each pixel is stored as the difference between itself and the pixel above it.

So, to get the pixel value of the pixel at point (x,y) in the image, you have to XOR it's original value with the value of the pixel at (x,y-1). But note that the first line in the image is never encoded (Or simply XOR'd with 0).

So, you start out with 0x00, then apply '0x32', which produces 0x32. Then XOR this with 0x55 to get 0x67 and so on vertically downwards. This means that in areas where there are no changes, the bytes are 0x00 (no change from above). Couple this with a Huffman Coding Scheme which can make the space taken to hold a 0x00 byte one bit, and you can compress most images down to a fraction of their original size. Genius! The final bytes which result are indexes into the EGA Colour Palette.

Colour Palette
Because the game runs in EGA, it uses 16 colour mode for all its graphics. This means there's one nibble for each pixel, thereby allowing 2 pixels per byte. Because the game uses the EGA's emulation of the full CGA palette, it can support only 16 colours. These 16 colours are as follows:

Here is an index color gif with the correct color values :



The TITLE.PIC image is stored using just this scheme. Why it wasn't huffman compressed as well is a bit of a mystery. The END.CPA image was first converted into this XOR format, and then stored in a Huffman Coding.

Encoding Format
The layout of the file runs in nibbles from top-left to bottom right.

There is a black border along the top and left sides of the original TITLE.PIC image. Maybe it's there to support the 3D effect of the surronding GUI border. For new images the full image size of 288x128 pixels is usable.

The image is constructed by XORring successive rows together. It's not clear why they did it this way, since the pixel values themselves would have taken no more or less space. I suspect it's because they didn't want people to really muck about with the image, which they could if it was a known format. Saying this, it only took about 20 minutes of faffing to decipher it, so it's not incredibly successful as an encryption scheme either ;)

Decoding an image.
So, in order to read the image in, you need an accumulator array. Pseudo-code to do it is as follows: Once you do this, the image will accumulate 'downwards', using XOR to flip bits on and off to create the colour patterns of the familiar Title image we know and love.
 * 1) Read in the whole file into memory linearly.
 * 2) Set a pointer [SI] to the beginning of the file
 * 3) Set a pointer [DI] to the first pixel in line 2 (i.e. byte 144)
 * 4) Set DI = SI xor DI
 * 5) increment both SI and DI
 * 6) Repeat until you've reached the end of the file (file size - 144 bytes).

Encoding an Image.
To re-encode an image to create a new title image, you would reverse the process, which (being xor) is identical to encoding it. Since you'll probably have your image in a 2-d format, here's the pseudocode for if you do... Scale your title image to 288x128 and 16 colours. Then, follow the following procedure.


 * 1) Create an accumulator array of 144 bytes, and fill it with zeros.
 * 2) Set a counter to zero.
 * 3) Read in two pixels from your image, and join them together in the order [ p1
 * p2 ] in a single byte.
 * 1) XOR that byte with the accumulator[counter] byte,
 * 2) Place the result into the accumulator[counter] byte.
 * 3) Write the result out to the file as a byte.
 * 4) Increment the counter.
 * 5) if the counter == 145, reset it to zero.
 * 6) If you've NOT reached the end of the image, goto step 3.

Simple eh? Makes you wonder why they bothered, since any 16-bit encoding would have worked just as well.. You could have understood it if the file was zipped (gzip reduces the title.pic to 7k, halving its size, but it doesn't look like the image files were compressed).