Semi-Random Puyo Puyo MD Research

Puyo Puyo game rips and modifications go here.
andlabs
Puyo Fan
Puyo Fan
Posts: 3
Joined: Wed Oct 06, 2010 10:14 pm

Semi-Random Puyo Puyo MD Research

Postby andlabs » Sat Oct 09, 2010 3:26 am

Hi guys. A month ago today (that is, 8 September 2010) I wanted to rip two sprites out from the Mega Drive version of the original Puyo Puyo the hard way, like I did with DAC samples from various games, and so far I've found a bit of information which I'm not sure whether or not it would be useful to anyone here, but here goes:

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;
}
Usage:

Code: Select all

puyodec PuyoPuyoROM.bin output.bin address_to_art_in_hex_without_prefixes_or_suffixes
Puyo Tools doesn't handle this format, and I haven't spoken to anyone at my university who''s familiar with compression methods (I am a CS major), so I assume it's new.

Bytecode
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;
}
Its output; looks best with tabs taking 8 spaces (which I personally don't use lol):

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_11


Sprite 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:
Image
Image
Image (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)
Image

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)
The playlist for pre-stage cutscene songs is at $2078 (each entry is one byte).

So yeah, enjoy all this information, and I'll be happy to do more search if wanted.

jchw

Re: Semi-Random Puyo Puyo MD Research

Postby jchw » Sat Oct 09, 2010 9:17 am

Well done. If you want we can probably port the routines into Puyo Tools.

andlabs
Puyo Fan
Puyo Fan
Posts: 3
Joined: Wed Oct 06, 2010 10:14 pm

Re: Semi-Random Puyo Puyo MD Research

Postby andlabs » Fri Nov 05, 2010 3:58 am

The only routine that would really be of use is the art compression format, however that may only be minimally useful if I'm unlucky (I'll get to that in a sec).

Unfortunately that sprite mapping script exported to Sonic 1 mapping format, and consequently I decided to start writing a sprite viewer that you can actually use =P However, I also wanted to add support for looking at Tsu's and Madou Monogatari Genesis's sprites, and the art compression format seems to either not be linked to the bytecode anymore or be completely different. I'm going to have to look into it a bit further, but if I am right and Puyo Puyo MD is the only one of these three games that uses the art compression format, it might limit the point of Puyo Tools having it. MUSHA?

Also I managed to get the DAC samples out of the three games. They are all stored as 4-bit LPCM using a chain of references to either data or silence; I will release them as soon as I can figure out their proper sampling rates. (I've ripped samples from several Genesis games so far, and a few packs (notably Ristar) have the samples at the wrong rates, so I'm trying hard to avoid that now.) (Also, on a side note, Madou Monogatari has 136 DAC samples — the most I've seen of any game so far.)

jchw

Re: Semi-Random Puyo Puyo MD Research

Postby jchw » Fri Nov 05, 2010 7:01 am

I haven't followed too closely, but I'm fairly sure any format that has to do with Puyo is not a big problem to implement.

That said, since Puyo Tools might drop some rather obscure formats in the future, maybe the code is better off staying in your utilities. I haven't tested your utilities yet (mainly because I don't have a setup prepared for messing with this yet) but if they work fine there's no real need to port the code anyways. (Although, if there's actually any demand here I can compile some windows binaries.)

Good luck with what you're working on though. Feel free to keep us posted.

andlabs
Puyo Fan
Puyo Fan
Posts: 3
Joined: Wed Oct 06, 2010 10:14 pm

Re: Semi-Random Puyo Puyo MD Research

Postby andlabs » Tue Jan 04, 2011 3:58 am

I haven't actually made progress with figuring out what format art Tsu/Madou Monogatari I (or is it Madoh, as the box claims?) use, however I did start disassembling the arcade Tsu ROM in the hopes of finding out (even though the VDP has slight differences in color handling, no Z80, and an extra chip for ADPCM sample playback, the System C-2 is pretty much a faster Mega Drive). Meanwhile, I spent a bit of time finding this:

madoumdfont1.png
Madou Monogatari I MD font preview 1

madoumdfont2.png
Madou Monogatari I MD font preview 2

madoumdfont3.png
Madou Monogatari I MD font preview 3


Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

// pietro gagliardi
// 3 jan 2011

typedef uint8_t byte;
typedef uint16_t word;

word tt[] = {
   0x1111,
   0x111F,
   0x11F1,
   0x11FF,
   0x1F11,
   0x1F1F,
   0x1FF1,
   0x1FFF,
   0xF111,
   0xF11F,
   0xF1F1,
   0xF1FF,
   0xFF11,
   0xFF1F,
   0xFFF1,
   0xFFFF
};

FILE *fin, *fout;

byte readbyte()
{
   byte b;
   
   fread(&b, 1, 1, fin);
   return b;
}

void writebyte(byte b) { fwrite(&b, 1, 1, fout); }

void writebeword(word w)
{
   writebyte((w >> 8) & 0xFF);
   writebyte(w & 0xFF);
}

void convert(uint32_t len)
{
   while (len) {
      byte b[0x10], c[0x10], d[0x10], e[0x10];
      int i;
      
      for (i = 0; i < 0x10; i++, len -= 4) {
         b[i] = readbyte();
         c[i] = readbyte();
         d[i] = readbyte();
         e[i] = readbyte();
      }
#define k(b) writebeword(tt[(b & 0xF0) >> 4]); writebeword(tt[b & 0x0F]);
#define f(t) for (i = (0+t); i < (8+t); i++)
      f(0) { k(b[i]); k(d[i]); }
      f(0) { k(c[i]); k(e[i]); }
      f(8) { k(b[i]); k(d[i]); }
      f(8) { k(c[i]); k(e[i]); }
   }
}

int main(int argc, char *argv[])
{
   uint32_t start, end;
   
   fin = fopen(argv[1], "rb");
   fout = fopen(argv[2], "wb");
   start = strtoul(argv[3], NULL, 16);
   end = strtoul(argv[4], NULL, 16);
   fseek(fin, start, SEEK_SET);
   convert(end-start);
   return 0;
}

Code: Select all

(screenshot 1) mtextview.exe "Madou Monogatari I (J) [!].bin" mtextout.bin BF000 C0100
(screenshots 2 and 3) mtextview.exe "Madou Monogatari I (J) [!].bin" mtextout2.bin B4FC0 BF000


The program generates data in the form of raw MD VDP tiles as they would be copied to the VDP in-game. If/when I do find out the text data storage format, I'll offer it up for translation (if I don't learn Japanese in that little time first, that is =P Or would that offer be better elsewhere?).

jchw

Re: Semi-Random Puyo Puyo MD Research

Postby jchw » Tue Jan 04, 2011 5:12 am

It's good to know that these formats are being cracked one way or another. It'd be nice if we had some sort of central location to put all of the documentation/code though.


Return to “Game Rips and Modifications”

Who is online

Users browsing this forum: No registered users and 2 guests