Now I have to pull out my old Oracle of Ages editor...
Oracle of Ages used one of 4 possible formats: Uncompressed, LZ (short words), LZ (long words), and a bitmask indicating which of the next 16 bytes uses a common byte.
LZ:
mask = read a byte
This determines whether the next 8 things processed are LZ runs or raw bytes, starting with the most significant bit of 'mask'. After processing 8 things, if there are still bytes remaining to decompress, read a new byte for 'mask'.
LZ run (bit is 1):
short word mode:
read a byte
LLLDDDDD
L = length (add 1)
D = distance
If value of L is zero, read a byte and use that for length instead.
long word mode:
read two bytes
DDDDDDDD low byte of LZ distance
LLLLLDDD high byte of LZ distance, length (add 1)
If value of L is zero, read a byte and use that for length instead.
Add ~Distance (bitwise complement) to Destination pointer, copy Length bytes.
16 byte with common:
Read two bytes
MMMMMMMM Mask indicating which bytes use the common byte
MMMMMMMM
If M is zero, just copy 16 bytes.
Otherwise
Read a byte, this is the Common byte.
look at each bit of M (starting with most significant bit of first byte)
If it's 1, output the Common byte
otherwise, copy a byte.
Code I used to decompress it: (BP and CBP are classes that act like byte pointers but do bounds checking)
Code:
void lzdecompress(BP dest, CBP src,int length, bool LWM)
{
int bitsleft=1;
u8 mask=0;
while (length>0)
{
bitsleft--;
mask<<=1;
if (bitsleft==0)
{
bitsleft=8;
mask=*src++;
}
if (mask & 0x80)
{
int lzdist1=0,lzdist2=0;
int lzlength=0;
if (LWM)
{
lzdist1=*src++;
int a=*src++;
lzdist2=a & 7;
a=a>>3;
if (a!=0)
{
lzlength=a+2;
}
else
{
lzlength=*src++;
if (lzlength==0) lzlength=256;
}
}
else
{
int a=*src++;
lzdist1=a & 0x1F;
lzdist2=0;
a>>=5;
if (a==0)
{
lzlength=*src++;
if (lzlength==0) lzlength=256;
}
else
{
lzlength=a+1;
}
}
int i;
s16 lzdist16= (lzdist1^0xFF) | ((lzdist2^0xFF)<<8);
BP src2=dest+ lzdist16;
for (i=0;i<lzlength;i++)
{
*dest++=*src2++;
length--;
if (length==0) return;
}
}
else
{
*dest++=*src++;
length--;
}
}
}
void unpack_16_common(BP*dest_p, CBP*src_p)
{
BP &dest = *dest_p;
CBP &src = *src_p;
int length=16;
u16 mask=src.getu16be();
if (mask==0)
{
memcpy(&dest[0],&src[0],16);
dest+=16;
src+=16;
return;
}
else
{
u8 common=*src++;
int i;
for (i=0;i<16;i++)
{
if (mask & 0x8000)
{
*dest++=common;
}
else
{
*dest++=*src++;
}
mask<<=1;
}
}
}