Hehe... Sorry I didn't explain the format, but I didn't expect anyone to mess with the format itself. While coding the compressor I tried to separate the encoding (turning the tiles into a stream of bits) from the parsing (deciding how long each block should be).
If you look at the source you can see that I tried a few different parsing functions, and those are kinda self explanatory (I hope!). The parsing functions can use whatever method they see fit to fill an array of block lengths, which later are used for actual encoding.
But since you are interested in the format, let's get to the questions! You almost got it. Here is the format with as much detail as I can think of:
Code:
.start a new block by building the decompression tables:
.for each color (3 down to 0):
.2 bits indicate how many colors can follow the current color.
This value goes straight to table A, indexed by the current
color.
.if 1 or more colors can follow it, a color is specified in
encoded form. Since there are only 3 possibilities, the colors
are represented by the following codes: 0 (first possibility),
10 (second possibility) and 11 (third possibility). Depending
on the current color, the possibilities are different, because
they don't include the current color (for example, the 3
possible colors that can follow color 2 are 0, 1 or 3). The
specified color is enough to find all of them: if only one
color can follow the original one, the specified one is it. If
it's 2 colors, the specified one is discarded and the 2 that
are not it are used. If it's 3 colors, the other 2 are the
ones that are not it. These colors go to tables B (first color
that can follow the original), C (second) and D (third).
.decode the tiles that are part of the same block:
.1 bit selects between repeating the previous row or decoding a
new one (0 = new row, 1 = repeat previous row). It is possible
to repeat the first row of the first tile of a block, and a row
filled with color 0 is used in this case.
.if the row is not a repeat:
.2 bits specify the color of the first pixel. This color is
then used as an index into table A so that we know how many
colors can follow the current one.
.if no color can follow the current one, the same color is
repeated for the rest of the row.
.if there are colors following the current one, 1 bit selects
between repeating the previous pixel or reading a new one
(0 = new pixel, 1 = repeat).
.if the pixel is not a repeat, the next pixel depends on how
many colors follow the current one. If only 1 can follow it,
this is used (it's the color from table B). If 2 can follow
it, 1 bit selects between them (0 = color from table B, 1 =
color from table C). If 3 can follow it a value with 1 or 2
bits select between the 3 possibilities (0 = from table B,
10 = from table C, 11 = from table D).
.after each pixel is draw, its color is used as an index into
table A again so that the next pixel is processed the same way.
.once the tile is complete, 1 bit selects between starting a new
block or not (0 = no new block, 1 = new block).
I hope the above is clear enough, I'm having trouble organizing it any better than this.
Quote:
I assume the tables can be changed mid-tile, correct? Just not mid-row.
No, they can't. A compressed stream always starts with a new block (and the definition of new tables), and after each completed tile a bit will select between continuing the current block or ending it and starting a new one.
Quote:
Can you change tables B,C, or D? I only see how you'd change table A from this. What determines the contents of B,C,D?
These are changed whenever a color can be followed by colors that are not itself. The first possible color goes to table B, the second goes to table C and the third goes to table D. Naming them with single letters was not a very good choice, but back then I wasn't sure what to name them. In the decoder I named them ColorCount (A), NextColor0 (B), NextColor1 (C) and NextColor2 (D), I hope those make more sense.