Whee art compression format.
All values little endian.
First long is offset from beginning of data that marks the beginning of invalid (extra) tiles... because the way this compression method works will mean the last few pieces will be extra. When decompressing to VRAM, this isn't really a problem the end user will see...
Second long (unk in the code below) is the number of bytes decompressed.
DATA
Each bit in each byte starting a chunk is scanned right to left.
Bit==1 -> output next byte raw; copy to buffer
Bit==0 -> copy from buffer to output and buffer, next word: nibbles abcd — acd (?) is pointer into buffer, b is count - 3 — look at the code, meh
The buffer is $1000 bytes and is pre-filled with data (see the code below).
Since both Ranger-X/Ex-Ranza and Crusader of Centy/Soleil use this compression, an appropriate name
might be Toyota compression after Toshio Toyota, the only programmer who worked on both games — I'm not sure if YuYu Hakusho Gaiden (a different YuYu game for the Mega Drive from that fighter everyone knows about) uses it because I can't tell where/how it loads art yet

(and yes,
Nextech made Crusader of Centy,
not Atlus; Gau Entertainment is Nextech)
Most of the art in both games is stored in banks. For the US Crusader of Centy ROM, here are some, if not all, bank addresses:
$59000
$F0000
$100000
$111400
$115B00
$116E00
$120000
At each of these locations is a list of longwords — add the base address to the longword to get the address of the art.
Some of these might be plane mappings too, bleh. I rushed this post out, so just reply if ysomething confuses/doesn't work/more questions/etc.
CODE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
/* pietro gagliardi 27 february-17 march 2011 */
typedef uint8_t byte;
typedef uint16_t word;
FILE *fin, *fout;
byte readbyte(void)
{
byte b;
fread(&b, 1, 1, fin);
return b;
}
word readleword(void)
{
word w;
w = readbyte();
return w | (((word) readbyte()) << 8);
}
uint32_t readlelong(void)
{
uint32_t w;
w = readbyte();
w |= ((uint32_t) readbyte()) << 8;
w |= ((uint32_t) readbyte()) << 16;
return w | (((uint32_t) readbyte()) << 24);
}
void writebyte(byte b)
{
fwrite(&b, 1, 1, fout);
}
uint32_t size, end, unk;
void init_buf(byte *a0)
{
byte d0;
word d1;
d0 = 0;
do {
d1 = 0xC + 1;
do {
*a0++ = d0;
d1--;
} while (d1 != 0);
d0++;
} while (d0 != 0);
do {
*a0++ = d0;
d0++;
} while (d0 != 0);
do {
d0--;
*a0++ = d0;
} while (d0 != 0);
d1 = 0x7F + 1;
do {
*a0++ = d0;
d1--;
} while (d1 != 0);
d0 = 0x20;
d1 = 0x6D + 1;
do {
*a0++ = d0;
d1--;
} while (d1 != 0);
}
uint32_t decompress(void)
{
uint32_t n = 0, at;
byte kbuf[0x1000];
word kloc = 0xFEE;
int I, j;
printf("%X ", kbuf);
init_buf(kbuf);
size = readlelong();
end += size + 8; /* +8 to count the two longs */
printf("end: %X\n", end);
unk = readlelong();
printf("unk: %X\n", unk);
printf("%X\n", ftell(fin));
do {
byte bits, b;
int I;
#define w() writebyte(b); kbuf[kloc++] = b; kloc &= 0xFFF; n++
bits = readbyte();
for (I = 0; I < 8; I++) {
if (bits & 1) { /* 1 bit == just copy next */
b = readbyte();
w();
} else { /* 0 bit == reread */
word loc;
byte ni;
/* loc = readleword();
ni = (loc & 0xF) + 2;
loc >>= 4;
loc &= 0xFFF;*/
{
byte q;
q = readbyte();
ni = readbyte();
loc = (ni & 0xF0) << 4;
loc |= q;
loc &= 0xFFF;
ni &= 0xF;
ni += 2;
}
for (ni++; ni; ni--) { // start by adding 1 because the original uses dbf
b = kbuf[loc++];
w();
loc &= 0xFFF;
}
}
bits >>= 1;
at = ftell(fin);
if (at >= end) {
printf("\n\nEARLY END: %X %X\n", end, at);
return n;
}
}
at = ftell(fin);
} while (at < end);
return n;
}
int main(int argc, char *argv[])
{
uint32_t outsize;
fin = fopen(argv[1], "rb");
fout = fopen(argv[2], "wb");
end = strtoul(argv[3], NULL, 16);
fseek(fin, end, SEEK_SET);
outsize = decompress();
printf("size of output: %X (%d)\n", outsize, outsize % 32);
return 0;
}
(this code still has some debug stuff in it — I rushed this post)
This post has been edited by Andlabs: 18 March 2011 - 11:59 PM