That's using a modified LZS as described in xeno-gears.htm . There should be an implementation in the q-gears sourceforge version, but here is the code I'm using:
void * decompress_lzs(void * src, unsigned int srcLen, void * dst, unsigned int dstLen)
{
int iofs = 0, oofs = 0;
u8 cmd=0, bit=0;
while ((iofs < srcLen) && (oofs < dstLen)) {
if (bit == 0) {
cmd = ((u8*)src)[iofs++];
bit = 8;
}
if (~cmd&1) {
((u8*)dst)[oofs++]=((u8*)src)[iofs++];
} else {
int a = ((u8*)src)[iofs++];
int b = ((u8*)src)[iofs++];
u16 o = ((u16)(b&0x0F)<<8) | a;
u8 len = ((b&0xF0)>>4)+3;
int rofs = oofs - o;
for (int j=0; j<len; j++) {
if (rofs < 0) {
((u8*)dst)[oofs++]=0;
} else {
((u8*)dst)[oofs++]=((u8*)dst)[rofs];
}
rofs++;
}
}
cmd>>=1;
bit--;
}
return dst;
}
Some blocks start with a u32 with the decompressed length - make sure you skip that! I spent a while trying to match background format with the description only to find out I accidentally decoded the length words as compressed data...