Note: except in C and gawk code, $ is used to denote hexadecimal numbers.
Art compression format, C program to decompress:
Code: Select all
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
/* pietro gagliardi 12 sep 2010 */
typedef uint8_t byte;
byte readbyte(FILE *fin)
{
byte b;
fread(&b, 1, 1, fin);
return b;
}
#define writebyte(fout, b) fwrite(&b, 1, 1, fout);
void cache(byte b, FILE *fout)
{
static byte VDPoutbuf[4];
static byte VDPoutptr = 0;
VDPoutbuf[VDPoutptr++] = b;
if (VDPoutptr == 4) {
VDPoutptr = 0;
writebyte(fout, VDPoutbuf[0]);
writebyte(fout, VDPoutbuf[1]);
writebyte(fout, VDPoutbuf[2]);
writebyte(fout, VDPoutbuf[3]);
}
}
void PuyoDec(FILE *fin, FILE *fout)
{
byte Storebuf[0x100];
byte Storeptr = 0;
byte b, c;
#define rb() readbyte(fin)
for (;;) {
b = rb();
if (b == 0)
return;
else if (b < 0x80) { // raw: copy and store
b--;
do {
c = rb();
cache(c, fout);
Storebuf[Storeptr] = c;
Storeptr++;
b--;
} while (b != 0xFF);
} else if (b >= 0x80) { // load from store and restore
byte Loadptr;
b &= 0x7F;
b += 2;
Loadptr = Storeptr - rb() - 1;
do {
c = Storebuf[Loadptr];
cache(c, fout);
Storebuf[Storeptr] = c;
Storeptr++;
Loadptr++;
b--;
} while (b != 0xFF);
}
}
}
int main(int argc, char *argv[])
{
FILE *fin, *fout;
size_t l;
fin = fopen(argv[1], "rb");
fout = fopen(argv[2], "wb");
l = strtoul(argv[3], NULL, 16);
fseek(fin, l, SEEK_SET);
PuyoDec(fin, fout);
fclose(fin);
fclose(fout);
return 0;
}Code: Select all
puyodec PuyoPuyoROM.bin output.bin address_to_art_in_hex_without_prefixes_or_suffixesBytecode
The logic for the title screen/demo cycle at least (I have to check if it applies to the game itself as well) is powered through a simple bytecode-esque interpreter. I've managed to figure out most of the opcodes, but there are a few that I still haven't figured out. The interpreter:
Code: Select all
/* 5-6 october 2010 */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
typedef uint8_t byte;
typedef uint16_t word;
typedef uint32_t ulong;
FILE *fin;
byte readbyte(void)
{
byte b;
fread(&b, 1, 1, fin);
return b;
}
word readbeword(void) { word w = readbyte() << 8; return w | readbyte(); }
ulong readbelong(void)
{
ulong l = readbyte() << 24;
l |= readbyte() << 16;
l |= readbyte() << 8;
return l | readbyte();
}
#define CODESTART 0x1632
typedef struct _codeword codeword;
struct _codeword {
int opcode;
word wordarg;
ulong longarg;
ulong *extargs;
ulong sourceaddr;
int haslabel;
};
codeword *cw = NULL;
size_t ncw = 0;
codeword *addword(ulong sourceaddr, word opcode, word wordarg, ulong longarg)
{
ncw++;
cw = (codeword *) realloc(cw, ncw * sizeof(codeword));
cw[ncw - 1].sourceaddr = sourceaddr;
cw[ncw - 1].opcode = opcode;
cw[ncw - 1].wordarg = wordarg;
cw[ncw - 1].longarg = longarg;
cw[ncw - 1].extargs = NULL;
cw[ncw - 1].haslabel = 0;
return &cw[ncw - 1];
}
#define ARW 1
#define ARL 2
#define ARLW 3
#define ARWL 4
#define ARWBB 5
int nargs[] = {
0, 0, ARW, 0,
ARLW, ARL, ARL, ARL,
ARL, ARW, ARW, ARWL,
ARW, ARW, ARWBB, ARW,
ARW, 0, 0, ARW
};
void readrawcode(void)
{
fseek(fin, CODESTART, SEEK_SET);
for (;;) {
word op, w = 0;
ulong l = 0;
codeword *cc;
ulong sourceaddr;
#define RW() readbeword()
#define RL() readbelong()
sourceaddr = ftell(fin);
op = RW();
if (op > 0x13)
return;
switch (nargs[op]) {
case ARW:
w = RW();
break;
case ARL:
l = RL();
break;
case ARLW:
l = RL();
w = RW();
break;
case ARWL:
w = RW();
l = RL();
break;
case ARWBB:
w = RW();
l = RW(); /* store BB in l */
break;
}
cc = addword(sourceaddr, op, w, l);
if (op == 9) { /* switch */
word ti;
cc->extargs = (ulong *) malloc(w * sizeof (ulong));
for (ti = 0; ti < w; ti++)
cc->extargs[ti] = RL();
}
}
}
ulong marklabel(ulong n)
{
ulong i;
static ulong fail=0xFFFFFFFF;
for (i = 0; i < ncw; i++)
if (cw[i].sourceaddr == n) {
cw[i].haslabel = 1;
return i;
}
if(n<fail){fail=n;printf("fail around %Xn",n);}
return ncw;
}
void rebasecodeandfindlabels(void)
{
size_t i;
for (i = 0; i < ncw; i++)
if (cw[i].opcode >= 6 && cw[i].opcode <= 8)
cw[i].longarg = marklabel(cw[i].longarg);
else if (cw[i].opcode == 9) {
int j;
for (j = 0; j < cw[i].wordarg; j++)
cw[i].extargs[j] = marklabel(cw[i].extargs[j]);
}
}
#define PALD_LOC 0x2210
void printcode(void)
{
size_t i;
for (i = 0; i < ncw; i++) {
codeword *cc = &cw[i];
word j;
if (cc->haslabel)
printf("loc_%Xt", i);
else
printf("t");
switch (cc->opcode) {
case 4:
printf("STORE #$%X, $%Xn", cc->wordarg, cc->longarg);
break;
case 5:
printf("JSR $%Xn", cc->longarg);
break;
case 6:
printf("GOTO loc_%Xn", cc->longarg);
break;
case 7:
printf("GOTO_Z loc_%Xn", cc->longarg);
break;
case 8:
printf("GOTO_NZ loc_%Xn", cc->longarg);
break;
case 9:
printf("SWITCHn");
for (j = 0; j < cc->wordarg; j++)
printf("ttloc_%Xn", cc->extargs[j]);
break;
case 0xA:
printf("LOADVDPREGSET %dn", cc->wordarg);
break;
case 0xB:
printf("DECOMPTOVDP dat_%X, $%Xn", cc->longarg, cc->wordarg);
break;
case 0xD:
printf("LOADPALETTELINE $%X, %dn", ((cc->wordarg >> 3) & 0x1FFF) + PALD_LOC, cc->wordarg & 3);
break;
case 0xE:
printf("LOADPALETTELINE_E $%X, %d, $%X, $%Xn", ((cc->wordarg >> 3) & 0x1FFF) + PALD_LOC, cc->wordarg & 3, cc->longarg >> 8, cc->longarg);
break;
case 0xF:
printf("PLAYSONG $%Xn", cc->wordarg);
break;
default:
printf("UNK_%X", cc->opcode);
switch (nargs[cc->opcode]) {
case ARW:
printf(" #$%X", cc->wordarg);
break;
case ARL:
printf(" #$%X", cc->longarg);
break;
case ARWL:
printf(" #$%X, #$%X", cc->wordarg, cc->longarg);
break;
case ARLW:
printf(" #$%X, #$%X", cc->longarg, cc->wordarg);
break;
case ARWBB:
printf(" #$%X, #$%X, #$%X", cc->wordarg, cc->longarg >> 8, cc->longarg);
break;
}
printf("n");
}
}
}
int main(int argc, char *argv[])
{
fin = fopen(argv[1], "rb");
readrawcode();
rebasecodeandfindlabels();
printcode();
return 0;
}Code: Select all
JSR $FB8
JSR $21E2
JSR $1DBB2
GOTO_Z loc_11
LOADVDPREGSET 1
DECOMPTOVDP dat_69B00, $0
DECOMPTOVDP dat_63136, $A000
JSR $1CD34
UNK_1
LOADPALETTELINE $2390, 0
LOADPALETTELINE $2590, 1
LOADPALETTELINE $24B0, 2
UNK_0
LOADPALETTELINE $2210, 0
LOADPALETTELINE $2210, 1
LOADPALETTELINE $2210, 2
UNK_1
loc_11 JSR $FB8
LOADVDPREGSET 1
DECOMPTOVDP dat_61400, $A000
JSR $A49E
JSR $C960
UNK_0
LOADPALETTELINE $2210, 0
JSR $29D6
UNK_1
STORE #$0, $FF013A
STORE #$0, $FF05D2
UNK_1
loc_1D LOADVDPREGSET 0
DECOMPTOVDP dat_7A300, $0
DECOMPTOVDP dat_61400, $A000
UNK_13 #$82
PLAYSONG $3
UNK_C #$20
UNK_1
JSR $CE58
LOADPALETTELINE_E $2230, 0, $0, $0
LOADPALETTELINE_E $24F0, 1, $0, $0
LOADPALETTELINE_E $25F0, 2, $0, $0
LOADPALETTELINE_E $2630, 3, $0, $0
UNK_0
JSR $29D6
LOADPALETTELINE $2210, 0
LOADPALETTELINE $2210, 1
LOADPALETTELINE $2210, 2
LOADPALETTELINE $2210, 3
UNK_1
STORE #$0, $FF013A
STORE #$0, $FF05D2
UNK_1
JSR $BA4
UNK_2 #$6
UNK_12
SWITCH
loc_37
loc_1CD
loc_1B1
loc_103
loc_37 LOADVDPREGSET 0
DECOMPTOVDP dat_61400, $A000
JSR $C548
DECOMPTOVDP dat_6C400, $0
PLAYSONG $4
UNK_C #$1F
UNK_1
LOADPALETTELINE $22B0, 0
LOADPALETTELINE $2290, 1
LOADPALETTELINE $2370, 3
UNK_1
JSR $B0F6
UNK_0
UNK_11
LOADPALETTELINE $2210, 0
LOADPALETTELINE $2210, 1
LOADPALETTELINE $2210, 2
LOADPALETTELINE $2210, 3
UNK_1
JSR $29D6
UNK_12
SWITCH
loc_79
loc_173
loc_197
loc_4D
loc_4D LOADVDPREGSET 1
PLAYSONG $4
DECOMPTOVDP dat_69B00, $0
DECOMPTOVDP dat_63136, $A000
UNK_1
JSR $1D35C
UNK_1
LOADPALETTELINE $24B0, 0
LOADPALETTELINE $2570, 1
LOADPALETTELINE $2590, 2
LOADPALETTELINE $25B0, 3
UNK_1
JSR $1D3A0
UNK_0
LOADPALETTELINE $2210, 0
LOADPALETTELINE $2210, 1
LOADPALETTELINE $2210, 2
LOADPALETTELINE $2210, 3
JSR $29D6
UNK_12
UNK_1
GOTO_NZ loc_64
GOTO loc_1D
loc_64 LOADVDPREGSET 1
DECOMPTOVDP dat_34800, $6000
DECOMPTOVDP dat_41D38, $8000
DECOMPTOVDP dat_69B00, $0
DECOMPTOVDP dat_63136, $A000
JSR $1CCDC
UNK_1
LOADPALETTELINE $2290, 0
LOADPALETTELINE $2390, 1
LOADPALETTELINE $24B0, 2
LOADPALETTELINE $25B0, 3
JSR $1CD28
UNK_0
LOADPALETTELINE $2210, 0
LOADPALETTELINE $2210, 1
LOADPALETTELINE $2210, 2
LOADPALETTELINE $2210, 3
UNK_1
UNK_12
JSR $29D6
GOTO loc_4D
loc_79 LOADVDPREGSET 0
DECOMPTOVDP dat_2CD00, $8000
DECOMPTOVDP dat_61400, $A000
UNK_C #$1E
UNK_1
JSR $C162
UNK_1
LOADPALETTELINE $2270, 0
LOADPALETTELINE $2290, 1
LOADPALETTELINE $2490, 2
LOADPALETTELINE $2530, 3
UNK_0
LOADPALETTELINE $2210, 0
LOADPALETTELINE $2210, 1
LOADPALETTELINE $2210, 2
LOADPALETTELINE $2210, 3
UNK_1
JSR $29D6
LOADVDPREGSET 1
JSR $BDC
DECOMPTOVDP dat_34800, $6000
STORE #$FF20, $FF05D2
STORE #$FF60, $FF05D4
JSR $9B7A
JSR $93F4
JSR $A1A4
JSR $A2AC
JSR $204E
UNK_1
JSR $A2F4
LOADPALETTELINE_E $2290, 0, $0, $0
JSR $2128
UNK_0
JSR $2204
GOTO_Z loc_A8
UNK_3
LOADPALETTELINE $2210, 0
LOADPALETTELINE $2210, 1
LOADPALETTELINE $2210, 2
LOADPALETTELINE $2210, 3
UNK_1
UNK_12
JSR $29D6
UNK_2 #$10
JSR $2FB8
GOTO_Z loc_79
GOTO loc_11
loc_A8 UNK_3
UNK_1
JSR $208A
UNK_11
JSR $21D0
UNK_1
STORE #$0, $FF1892
UNK_0
JSR $BC6
DECOMPTOVDP dat_64000, $2000
DECOMPTOVDP dat_61400, $A000
UNK_12
UNK_3
LOADPALETTELINE $2230, 0
LOADPALETTELINE $2250, 1
LOADPALETTELINE $2290, 3
JSR $2128
STORE #$802, $FF1108
JSR $2EBA
UNK_1
JSR $3056
UNK_1
JSR $55A4
JSR $C14A
UNK_0
STORE #$1003, $FF1108
UNK_11
LOADPALETTELINE_E $2210, 0, $0, $0
LOADPALETTELINE_E $2210, 1, $0, $0
LOADPALETTELINE_E $2210, 2, $0, $0
LOADPALETTELINE_E $2210, 3, $0, $0
UNK_3
JSR $29D6
UNK_12
GOTO_NZ loc_CD
JSR $2FB8
SWITCH
loc_79
loc_103
loc_EA
loc_CD LOADVDPREGSET 1
JSR $BDC
STORE #$0, $FF05D2
STORE #$0, $FF05D4
DECOMPTOVDP dat_61400, $A000
DECOMPTOVDP dat_29800, $0
PLAYSONG $7
UNK_C #$6
UNK_1
JSR $C652
LOADPALETTELINE_E $2290, 0, $0, $0
LOADPALETTELINE_E $2290, 1, $0, $0
LOADPALETTELINE_E $23B0, 2, $0, $0
LOADPALETTELINE_E $2230, 3, $0, $0
UNK_3
JSR $C6CC
UNK_0
UNK_11
LOADPALETTELINE_E $2210, 0, $0, $0
LOADPALETTELINE_E $2210, 1, $0, $0
LOADPALETTELINE_E $2210, 2, $0, $0
LOADPALETTELINE_E $2210, 3, $0, $0
UNK_3
UNK_12
JSR $BA4
JSR $29D6
GOTO_Z loc_79
STORE #$0, $FF1890
GOTO loc_15C
loc_EA STORE #$1010, $FF0112
LOADVDPREGSET 1
JSR $BDC
DECOMPTOVDP dat_34800, $6000
STORE #$FF20, $FF05D2
STORE #$FF60, $FF05D4
JSR $93F4
JSR $A2AC
JSR $204E
UNK_1
JSR $A2F4
LOADPALETTELINE_E $2290, 0, $0, $0
LOADPALETTELINE_E $2270, 2, $0, $0
LOADPALETTELINE_E $2290, 3, $0, $0
UNK_0
UNK_11
LOADPALETTELINE_E $2210, 0, $0, $0
LOADPALETTELINE_E $2210, 1, $0, $0
LOADPALETTELINE_E $2210, 2, $0, $0
LOADPALETTELINE_E $2210, 3, $0, $0
UNK_3
JSR $29D6
UNK_12
STORE #$0, $FF1890
GOTO loc_15C
loc_103 STORE #$FFFF, $FFFC02
JSR $1DC02
LOADVDPREGSET 0
PLAYSONG $12
DECOMPTOVDP dat_2FC00, $0
UNK_C #$9
UNK_1
JSR $BFDE
JSR $D908
LOADPALETTELINE_E $23F0, 0, $0, $0
LOADPALETTELINE_E $2410, 1, $0, $0
LOADPALETTELINE_E $2230, 2, $0, $0
UNK_0
GOTO_Z loc_118
LOADPALETTELINE $2210, 0
LOADPALETTELINE $2210, 1
LOADPALETTELINE $2210, 2
JSR $29D6
UNK_12
UNK_1
GOTO loc_11F
loc_118 LOADPALETTELINE_E $2210, 1, $4, $400
LOADPALETTELINE_E $2210, 2, $4, $400
UNK_2 #$50
UNK_3
JSR $29D6
UNK_12
UNK_2 #$C0
loc_11F UNK_2 #$80
LOADVDPREGSET 1
JSR $BDC
STORE #$FF20, $FF05D2
STORE #$FF60, $FF05D4
PLAYSONG $11
DECOMPTOVDP dat_34800, $6000
DECOMPTOVDP dat_61DD0, $A000
STORE #$0, $FF0112
loc_128 JSR $DAF4
JSR $D908
UNK_0
UNK_1
SWITCH
loc_135
loc_128
loc_12D
loc_12D LOADPALETTELINE $2210, 0
LOADPALETTELINE $2210, 1
LOADPALETTELINE $2210, 2
LOADPALETTELINE $2210, 3
JSR $29D6
UNK_12
UNK_1
GOTO loc_13F
loc_135 UNK_11
LOADPALETTELINE_E $2210, 0, $6, $600
LOADPALETTELINE_E $2210, 1, $6, $600
LOADPALETTELINE_E $2210, 2, $6, $600
LOADPALETTELINE_E $2210, 3, $6, $600
UNK_2 #$70
UNK_3
JSR $29D6
UNK_2 #$80
UNK_12
loc_13F LOADVDPREGSET 1
PLAYSONG $A
DECOMPTOVDP dat_2FC00, $0
DECOMPTOVDP dat_63136, $A000
UNK_C #$A
UNK_1
LOADPALETTELINE_E $23F0, 0, $0, $0
LOADPALETTELINE_E $2550, 1, $0, $0
UNK_3
JSR $BE0E
JSR $D908
UNK_0
GOTO_Z loc_153
LOADPALETTELINE $2210, 0
LOADPALETTELINE $2210, 1
JSR $29D6
UNK_1
UNK_12
STORE #$0, $FF1890
GOTO loc_15C
loc_153 UNK_11
JSR $29D6
LOADPALETTELINE_E $2210, 0, $0, $0
LOADPALETTELINE_E $2210, 1, $0, $0
UNK_2 #$10
UNK_3
JSR $29D6
UNK_12
STORE #$0, $FF1890
loc_15C LOADVDPREGSET 0
JSR $BC6
PLAYSONG $4
DECOMPTOVDP dat_28B00, $0
DECOMPTOVDP dat_64000, $2000
DECOMPTOVDP dat_2CD00, $6000
DECOMPTOVDP dat_61400, $A000
UNK_C #$8
UNK_1
JSR $BB0E
LOADPALETTELINE_E $2450, 0, $0, $0
LOADPALETTELINE_E $2250, 1, $0, $0
LOADPALETTELINE_E $2270, 2, $0, $0
UNK_0
UNK_11
LOADPALETTELINE_E $2210, 0, $0, $0
LOADPALETTELINE_E $2210, 1, $0, $0
LOADPALETTELINE_E $2210, 2, $0, $0
UNK_2 #$10
UNK_3
JSR $29D6
UNK_12
GOTO loc_11
loc_173 STORE #$0, $FF0128
JSR $7F00
LOADVDPREGSET 1
DECOMPTOVDP dat_69300, $0
DECOMPTOVDP dat_61400, $A000
UNK_C #$4
UNK_1
PLAYSONG $6
LOADPALETTELINE $2230, 0
LOADPALETTELINE $2250, 1
LOADPALETTELINE $2270, 2
LOADPALETTELINE $23D0, 3
STORE #$802, $FF1108
loc_180 STORE #$0, $FF0112
LOADPALETTELINE $2230, 0
DECOMPTOVDP dat_64000, $2000
JSR $7F9C
JSR $2EBA
JSR $3056
UNK_0
GOTO_NZ loc_18C
JSR $29D6
UNK_C #$4
UNK_1
GOTO loc_180
loc_18C STORE #$1003, $FF1108
UNK_11
LOADPALETTELINE_E $2210, 0, $0, $0
LOADPALETTELINE_E $2210, 1, $0, $0
LOADPALETTELINE_E $2210, 2, $0, $0
LOADPALETTELINE_E $2210, 3, $0, $0
UNK_3
JSR $29D6
UNK_12
STORE #$FF, $FF1890
GOTO loc_15C
loc_197 LOADVDPREGSET 1
DECOMPTOVDP dat_69B00, $0
DECOMPTOVDP dat_64000, $2000
DECOMPTOVDP dat_61400, $A000
UNK_C #$16
UNK_1
PLAYSONG $5
LOADPALETTELINE_E $2230, 0, $0, $0
LOADPALETTELINE_E $2250, 1, $0, $0
LOADPALETTELINE_E $24B0, 2, $0, $0
LOADPALETTELINE_E $2510, 3, $0, $0
STORE #$802, $FF1108
JSR $3056
UNK_0
STORE #$1003, $FF1108
UNK_11
LOADPALETTELINE_E $2210, 0, $0, $0
LOADPALETTELINE_E $2210, 1, $0, $0
LOADPALETTELINE_E $2210, 2, $0, $0
LOADPALETTELINE_E $2210, 3, $0, $0
UNK_2 #$10
UNK_3
JSR $29D6
UNK_12
STORE #$100, $FF1890
GOTO loc_15C
loc_1B1 STORE #$400, $FF1882
LOADVDPREGSET 1
DECOMPTOVDP dat_1E000, $0
UNK_C #$3
UNK_1
DECOMPTOVDP dat_64000, $2000
DECOMPTOVDP dat_61400, $A000
LOADPALETTELINE $2230, 0
LOADPALETTELINE $2250, 1
LOADPALETTELINE $2270, 2
LOADPALETTELINE $2290, 3
JSR $C9BA
JSR $2EBA
JSR $3056
UNK_1
JSR $55A4
UNK_1
JSR $C14A
JSR $C946
UNK_0
JSR $29D6
LOADPALETTELINE $2210, 0
LOADPALETTELINE $2210, 1
LOADPALETTELINE $2210, 2
LOADPALETTELINE $2210, 3
UNK_1
GOTO_NZ loc_1D
GOTO loc_1EB
loc_1CD STORE #$400, $FF1882
STORE #$303, $FF0112
LOADVDPREGSET 1
DECOMPTOVDP dat_64000, $2000
DECOMPTOVDP dat_1E000, $0
DECOMPTOVDP dat_62088, $6600
DECOMPTOVDP dat_61400, $A000
UNK_C #$3
UNK_C #$2A
UNK_1
PLAYSONG $4
JSR $C9BA
JSR $10560
JSR $105CC
UNK_1
LOADPALETTELINE $2230, 0
LOADPALETTELINE $2250, 1
LOADPALETTELINE $2270, 2
LOADPALETTELINE $25D0, 3
JSR $C960
UNK_0
LOADPALETTELINE $2210, 0
LOADPALETTELINE $2210, 1
LOADPALETTELINE $2210, 2
LOADPALETTELINE $2210, 3
UNK_1
JSR $29D6
UNK_12
GOTO_NZ loc_1D
GOTO loc_1EB
loc_1EB LOADVDPREGSET 0
JSR $BC6
PLAYSONG $4
DECOMPTOVDP dat_28B00, $0
DECOMPTOVDP dat_64000, $2000
DECOMPTOVDP dat_2CD00, $6000
DECOMPTOVDP dat_61400, $A000
UNK_C #$8
UNK_1
JSR $C960
STORE #$FF, $FF1890
JSR $C9BA
JSR $BB0E
LOADPALETTELINE $2450, 0
LOADPALETTELINE $2250, 1
LOADPALETTELINE $2270, 2
UNK_0
JSR $29D6
UNK_11
LOADPALETTELINE $2210, 0
LOADPALETTELINE $2210, 1
LOADPALETTELINE $2210, 2
UNK_1
JSR $29D6
UNK_12
GOTO_NZ loc_1D
GOTO loc_11Sprite Mappings
The sprite mappings are all loaded from a global bank which is stored at location $1126E. This has a list of addresses, each containing a single mapping for some sprite. The sprite construction function places the right mapping # into its object status table, and so far all I know is that sprite map number $26 (starting from 0) is the one for Arle on the title screen, which also has the text plus the "INSERT COIN" string from the arcade version. Mappings won't be useful to anyone here at the moment, but here's a GNU awk (not regular awk, sorry) script to convert the mappings taken from my IDA Pro disassembly text into a format Sonic ROM hacking tools will understand:
Code: Select all
# 8 oct 2010
function hpad(b) {
sub(/$/, "", b)
if (length(b) == 3) return "0" b
if (length(b) == 2) return "00" b
if (length(b) == 1) return "000" b
return b
}
function leftbyte(b, h) {
return "$" substr(hpad(b), 1, 2)
}
function rightbyte(b) {
return "$" substr(hpad(b), 3)
}
BEGIN { nlbl = 0; sout = "" }
/dc.w/ {
labels[nlbl] = "sprite_" (nlbl + 0)
sout = sout sprintf("%s:n", labels[nlbl])
nlbl++
sout = sout "tdc.bt" $4 "n"
}
/SpriteMapping/ {
gsub(/[<>,]/, "")
y = rightbyte($3)
s = $4
t = $6
x = rightbyte($7)
sout = sout sprintf("tdc.bt%s, %s, %s, %s, %sn", y, s, leftbyte(t), rightbyte(t), x)
}
END {
print "SPRITE:"
for (i = 0; i < nlbl; i++)
printf "tdc.wt%s-SPRITEn", labels[i]
print sout
}Ok so now that I have all this information, how do I view art? If you know the address of the palette line for the sprites (such as those in the bytecode interpreter) and made the mappings file with the awk script including moving that last bit? Simple: get SonMapEd, load the uncompressed art and sprite mappings from the File > Load Data File submenu, load the palette by entering the raw hex value of the palette address in the appropriate box of the File > Load From ROM dialog (after loading the ROM of course), and then select Options > Game Format > Sonic Cracakers (Incomplete) to get palette mapping right. And there you have it:


(raw output from File > Save to Image > Render Sprite Frame before manually making it transparent; yes that is one single piece in the mappings, not two)
I have information on plane mappings as well but that needs more explanation and more research; if you're interested I will do so. If you want more ROM addresses I will do so; all I know is that dat_7A300 (raw address $7A300) is all the title screen tiles.
Now for some final random points:
Byte at $FF0112 is current level number. 0-2 are the Lessons, 3-$F are actual game levels. Thus you can use a patch code to change the RAM in real time and jump around if you like, or skip right to Satan by using (Gens/GS patch code) FF0112:0F at the title screen.
Music IDs; these are the values written to the Z80 to trigger a song.
Code: Select all
1 Final
2 Theme
3 Baroque
4 Cooking
5 Morning
6 Toy
7 Sorrow
8 Sticker
9 Sunset
$A appears to be Sunset too (might be different, not sure; if you want to see for yourself and know how the Genesis Z80 works, split out from $76C00 to $78000; that's the sound driver)
$B Rejection
$C Memories
$D Harpy
$E Warning
$F Satan
$10 Brave
$11 Ondo
$12 (game complete jingle)
$13 (stage complete jingle)So yeah, enjoy all this information, and I'll be happy to do more search if wanted.
