#include <stdio.h>
#include <stdlib.h>
#include <string.h>
///// Necessary Structures
#define WIDTHBYTES(w,bpp) (((w*bpp+31)>>5)<<2)
#ifdef BIGENDIAN
#define SWAPWORD(a) \
((WORD) ((((a)>>8)&0x00FF))\
|((((a)<<8)&0xFF00)))
#define SWAPDWORD(a) \
((DWORD) ((((a)>>24)&0x000000FF))\
|((((a)>>8) &0x0000FF00))\
|((((a)<<8) &0x00FF0000))\
|((((a)<<24)&0xFF000000)))
#else
#define SWAPWORD(a) a
#define SWAPDWORD(a) a
#endif
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
typedef signed long LONG;
typedef int BOOL;
#define TRUE 1
#define FALSE 0
#pragma pack(1)
typedef struct tagBITMAPINFOHEADER {
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
#pragma pack()
///// GB Graphics to Bitmap Files
void GbSetTile(
BYTE *tile,
BYTE *data,
DWORD stride,
BYTE clrOrigin
){
int y;
BYTE k1,k2;
BYTE * bits;
for(y=0;y<8;y++){
k1=tile[2*y];
k2=tile[2*y+1];
bits=data+y*stride;
bits[0]=clrOrigin|((k1&0x80)>>7)|((k2&0x80)>>6);
bits[1]=clrOrigin|((k1&0x40)>>6)|((k2&0x40)>>5);
bits[2]=clrOrigin|((k1&0x20)>>5)|((k2&0x20)>>4);
bits[3]=clrOrigin|((k1&0x10)>>4)|((k2&0x10)>>3);
bits[4]=clrOrigin|((k1&0x08)>>3)|((k2&0x08)>>2);
bits[5]=clrOrigin|((k1&0x04)>>2)|((k2&0x04)>>1);
bits[6]=clrOrigin|((k1&0x02)>>1)|((k2&0x02) );
bits[7]=clrOrigin|((k1&0x01) )|((k2&0x01)<<1);
}
}
void SaveGbBitmapTiles(
char *filename,
BYTE *buffer,
DWORD sz,
DWORD cxTile,
DWORD cyTile,
BOOL orient // TRUE - ltr/utd; FALSE - utd/ltr
){
int i;
DWORD xx=0,yy=0;
BITMAPINFOHEADER *ret;
BITMAPFILEHEADER bfh;
FILE *h;
BYTE *bits;
DWORD stride;
DWORD neededsz;
DWORD numtiles=cxTile*cyTile;
LONG width=cxTile*8;
LONG height=cyTile*8;
DWORD bminfo;
BYTE *ctbl;
bminfo=sizeof(BITMAPINFOHEADER)+1024;//header and palette
stride=WIDTHBYTES(width,8);
neededsz=bminfo+stride*height; //add image size
ret=malloc(neededsz);
if(!ret)return;
memset(ret,0,neededsz);
ret->biSize=sizeof(BITMAPINFOHEADER);
ret->biWidth=width;
ret->biHeight=-height;
ret->biPlanes=SWAPWORD(1);
ret->biBitCount=SWAPWORD(8);
ret->biSize=SWAPDWORD(ret->biSize);
ret->biWidth=SWAPDWORD(ret->biWidth);
ret->biHeight=SWAPDWORD(ret->biHeight);
// Calculate color table
ctbl=((BYTE*)ret)+sizeof(BITMAPINFOHEADER);
ctbl[0]=ctbl[1]=ctbl[2] =0xFF;
ctbl[4]=ctbl[5]=ctbl[6] =0xAA;
ctbl[8]=ctbl[9]=ctbl[10] =0x55;
ctbl[12]=ctbl[13]=ctbl[14]=0x00;
// Calculate pointer to image
bits=((BYTE*)ret)+bminfo;
for(i=0;i<numtiles;i++){
BYTE *d=bits+(yy<<3)*stride+(xx<<3);
GbSetTile(buffer,d,stride,0);
buffer+=16;
if(orient){
xx++;if(xx==cxTile){xx=0;yy++;}
} else {
yy++;if(yy==cyTile){yy=0;xx++;}
}
}
bfh.bfType='B'|('M'<<8);
bfh.bfSize=sizeof(BITMAPFILEHEADER)+neededsz;
bfh.bfReserved1=0;
bfh.bfReserved2=0;
bfh.bfOffBits=sizeof(BITMAPFILEHEADER)+bminfo;
bfh.bfType=SWAPWORD(bfh.bfType);
bfh.bfSize=SWAPDWORD(bfh.bfSize);
bfh.bfOffBits=SWAPDWORD(bfh.bfOffBits);
h=fopen(filename,"wb");
if(!h)return;
fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,h);
fwrite(ret,neededsz,1,h);
fclose(h);
free(ret);
}
///// Helper function
int xgetc(FILE *f, DWORD o){
DWORD sav=ftell(f);
int c;
fseek(f,o,SEEK_SET);
c=getc(f);
fseek(f,sav,SEEK_SET);
return c;
}
///// Graphics compression
#define RLC(a) \
( (((a)&0x7F)<<1)|(((a)&0x80)>>7) )
#define RRC(a) \
( (((a)&0xFE)>>1)|(((a)&0x01)<<7) )
#define SWAP(a) \
( (((a)&0xF0)>>4)|(((a)&0x0F)<<4) )
#define SLA(a) \
(((a)<<1)&0xFE)
#define SLA16(a) \
(((a)<<1)&0xFFFE)
typedef struct{
BYTE *ptr;
BYTE cursize1;
BYTE cursize2;
BYTE size1;
BYTE size2;
BYTE curbyte;
BYTE curbit;
BYTE d084;
BYTE d085;
BYTE d086;
BYTE d087;
WORD o;
WORD pos0;
WORD pos1;
WORD table1;
WORD table2;
WORD maxsize;
WORD error;
} GGCONTEXT;
WORD BitMaskTable[]={
0x0001,
0x0003,
0x0007,
0x000F,
0x001F,
0x003F,
0x007F,
0x00FF,
0x01FF,
0x03FF,
0x07FF,
0x0FFF,
0x1FFF,
0x3FFF,
0x7FFF,
0xFFFF
};
BYTE BitTables[4][8]={
{0x08,0xC4,0xE6,0x2A,0xF7,0x3B,0x19,0xD5},
{0xF7,0x3B,0x19,0xD5,0x08,0xC4,0xE6,0x2A},
{0x01,0x32,0x76,0x45,0xFE,0xCD,0x89,0xBA},
{0xFE,0xCD,0x89,0xBA,0x01,0x32,0x76,0x45}
};
BYTE Tables2[16]={
0x00,0x08,0x04,0x0C,0x02,0x0A,0x06,0x0E,
0x01,0x09,0x05,0x0D,0x03,0x0B,0x07,0x0F
};
#define TESTPOS(ggc,x)\
if(x>=(ggc)->maxsize){\
(ggc)->error=1;\
return 0;\
}
#define NEXTBIT(x) \
do{\
if(!(--ggc.curbit)){\
ggc.curbyte=xgetc(f,offset+(ggc.o++));\
ggc.curbit=8;\
}\
ggc.curbyte=RLC(ggc.curbyte);\
x=ggc.curbyte&0x01;\
}while(0)
#define PUTTEMP(tmp) \
do{\
e=tmp;\
switch(ggc.d084){\
case 1:\
e=SLA(e);e=SLA(e);break;\
case 2:\
e=SWAP(e);break;\
case 3:\
e=RRC(e);e=RRC(e);break;\
}\
TESTPOS(&ggc,ggc.pos0);\
ggc.ptr[ggc.pos0]|=e;\
}while(0)
#define INCPTR \
do{\
ggc.cursize2++;\
if(ggc.cursize2!=ggc.size2){\
ggc.pos0++;\
} else {\
ggc.cursize2=0;\
if(ggc.d084){\
ggc.d084--;\
ggc.pos0=ggc.pos1;\
} else {\
ggc.d084=3;\
ggc.cursize1+=0x08;\
if(ggc.cursize1!=ggc.size1){\
ggc.pos0++;\
ggc.pos1=ggc.pos0;\
} else {\
ggc.cursize1=0;\
if(!(ggc.d085&0x02)){\
ggc.d085^=0x01;\
ggc.d085|=0x02;\
goto label2574;\
} else {\
done=TRUE;\
}\
}\
}\
}\
}while(0)
int GetGraphicsRoutine1(GGCONTEXT *ggc, WORD hl){
BYTE b;
BYTE tmp,c,d;
BOOL d087Old;
WORD a;
BYTE e;
WORD i,j;
ggc->cursize1=ggc->cursize2=0;
ggc->pos0=ggc->pos1=hl;
if(ggc->d087){
ggc->table1=0;
ggc->table2=1;
} else {
ggc->table1=2;
ggc->table2=3;
}
e=0;
do{
do{
BYTE bit;
TESTPOS(ggc,ggc->pos0);
a=b=ggc->ptr[ggc->pos0];
a=SWAP(a)&0x0F;
c=a&0x01;
a>>=1;
bit=(ggc->d087)?(e&0x04):(e&0x01);
if(bit){
a=BitTables[ggc->table2][a];
} else {
a=BitTables[ggc->table1][a];
}
if(!(c&0x01))a=SWAP(a);
a&=0x0F;
e=a;
d=SWAP(a);
a=b&0x0F;
c=a&0x01;
a>>=1;
bit=(ggc->d087)?(e&0x04):(e&0x01);
if(bit){
a=BitTables[ggc->table2][a];
} else {
a=BitTables[ggc->table1][a];
}
if(!(c&0x01))a=SWAP(a);
a&=0x0F;
e=a;
a|=d;
TESTPOS(ggc,ggc->pos0);
ggc->ptr[ggc->pos0]=a;
ggc->pos0+=ggc->size1;
ggc->cursize1+=0x08;
}while(ggc->cursize1!=ggc->size1);
e=0;
ggc->cursize1=0;
ggc->cursize2++;
if(ggc->cursize2!=ggc->size2){
ggc->pos1++;
ggc->pos0=ggc->pos1;
}
}while(ggc->cursize2!=ggc->size2);
ggc->cursize2=0;
return !(ggc->error);
}
int GetGraphicsRoutine2(GGCONTEXT *ggc){
BYTE b;
BYTE tmp,c;
BOOL d087Old;
WORD a;
WORD mask,de,hl;
BYTE e;
WORD i,j;
ggc->cursize1=ggc->cursize2=0;
ggc->pos0=(ggc->d085&0x01)?0:0x188;
ggc->pos1=(ggc->d085&0x01)?0x188:0;
GetGraphicsRoutine1(ggc,ggc->pos0);
ggc->pos0=(ggc->d085&0x01)?0:0x188;
ggc->pos1=(ggc->d085&0x01)?0x188:0;
hl=ggc->pos0;
de=ggc->pos1;
do{
do{
if(ggc->d087){
TESTPOS(ggc,de);
a=ggc->ptr[de];
b=a;
a=SWAP(a)&0x0F;
c=Tables2[a];
c=SWAP(c);
a=b&0x0F;
a=Tables2[a];
a|=c;
ggc->ptr[de]=a;
}
TESTPOS(ggc,hl);
TESTPOS(ggc,de);
a=ggc->ptr[hl++]^ggc->ptr[de];
ggc->ptr[de++]=a;
ggc->cursize2++;
}while(ggc->cursize2!=ggc->size2);
ggc->cursize2=0;
ggc->cursize1+=0x08;
}while(ggc->cursize1!=ggc->size1);
ggc->cursize1=0;
return !(ggc->error);
}
int GetGraphics(
FILE *f,
DWORD offset,
BYTE *ptr,
DWORD sz
){
GGCONTEXT ggc;
BYTE b;
BYTE tmp,c;
BOOL d087Old;
WORD a;
WORD mask,de,hl;
BYTE e;
WORD i,j;
BOOL done=FALSE;
BYTE tmpbuf[0x310];
ggc.size1=0;
ggc.size2=0;
ggc.cursize1=0;
ggc.cursize2=0;
ggc.curbit=1;
ggc.d085=0;
ggc.d086=1;
ggc.d084=3;
ggc.d087=0;
ggc.pos0=0;
ggc.pos1=0;
ggc.ptr=tmpbuf;
ggc.table1=0;
ggc.table2=0;
ggc.o=0;
ggc.maxsize=0x310;
ggc.error=0;
ggc.curbyte=xgetc(f,offset+(ggc.o++));
if(ggc.curbyte!=0x44
&&ggc.curbyte!=0x55
&&ggc.curbyte!=0x66
&&ggc.curbyte!=0x77)return 0;
b=ggc.curbyte;
ggc.size1=(b&0x0F)*8;
b=SWAP(b);
ggc.size2=(b&0x0F)*8;
memset(tmpbuf,0,0x310);
NEXTBIT(tmp);
ggc.d085=tmp;
label2574:
ggc.pos0=ggc.pos1=(ggc.d085&0x01)?0x188:0;
if(ggc.d085&0x02){
NEXTBIT(tmp);
if(tmp){
NEXTBIT(tmp);
tmp++;
}
ggc.d086=tmp;
}
NEXTBIT(tmp);
while(!done){
if(tmp){
while(1){
NEXTBIT(tmp);
c=tmp;
NEXTBIT(tmp);
tmp|=SLA(c);
if(tmp){
PUTTEMP(tmp);
INCPTR;
} else break;
}
}
if(done)break;
c=0;
do{
NEXTBIT(tmp);
if(tmp)c++;
}while(tmp);
mask=BitMaskTable[c];
de=0;
c++;
do{
NEXTBIT(tmp);
de=tmp|de;
if(--c){
de=SLA16(de);
}
}while(c);
de+=mask;
do{
tmp=0;
PUTTEMP(tmp);
INCPTR;
if(done)break;
de--;
if(de==0){
tmp=1;//to continue upper loop
break;
}
}while(1);
}
if(ggc.d086==0){
GetGraphicsRoutine1(&ggc,0);
GetGraphicsRoutine1(&ggc,0x188);
} else if(ggc.d086==1){
GetGraphicsRoutine2(&ggc);
} else {
ggc.pos0=(ggc.d085&0x01)?0:0x188;
ggc.pos1=(ggc.d085&0x01)?0x188:0;
d087Old=ggc.d087;
ggc.d087=0;
GetGraphicsRoutine1(&ggc,ggc.pos1);
ggc.pos0=(ggc.d085&0x01)?0:0x188;
ggc.pos1=(ggc.d085&0x01)?0x188:0;
ggc.d087=d087Old;
GetGraphicsRoutine2(&ggc);
}
for(i=0;i<0x188;i++){
ptr[(i<<1)]=ggc.ptr[i];
ptr[(i<<1)|1]=ggc.ptr[0x188+i];
}
return !(ggc.error);
}
int NumberToIndex(BYTE *ord,DWORD len,int n){
int i;
for(i=0;i<len;i++){
if(ord[i]==n)return i;
}
return -1;
}
void RedBlueGraphics(FILE *f){
int i;
BYTE rbpkdx[28];
BYTE buffer[1024];
BYTE bmp[256];
BYTE pb[256];
WORD pal[4];
BYTE palno;
fseek(f,0x41024,SEEK_SET);
fread(pb,256,1,f); // read internal order
for(i=1;i<=151;i++){
DWORD wBack,wFront;
WORD bSize;
DWORD o1=0x24000;
BOOL have=TRUE;
DWORD bank;
int j=NumberToIndex(pb,0xBE,i)+1;
if(i==151){
fseek(f,0x425b,SEEK_SET);
} else {
DWORD o=0x383de;
fseek(f,o+((i-1)*28),SEEK_SET);
}
if(j==0x15){
bank=1;
} else if(j==0xB6){
bank=0x0B;
} else if(j<0x1F){
bank=0x09;
} else if(j<0x4A){
bank=0x0A;
} else if(j<0x74){
bank=0x0B;
} else if(j<0x99){
bank=0x0C;
} else {
bank=0x0D;
}
o1=(bank-1)<<14;
fread(rbpkdx,28,1,f);
bSize=rbpkdx[10]&0x0F;
wFront=rbpkdx[11]|(rbpkdx[12]<<8);
wBack=rbpkdx[13]|(rbpkdx[14]<<8);
memset(buffer,0,1024);
// Get front graphic
have=GetGraphics(f,o1+wFront,buffer,1024);
if(!have)continue;
sprintf(bmp,"%03d.bmp",i);
SaveGbBitmapTiles(bmp,buffer,bSize*bSize*16,bSize,bSize,FALSE);
// Get back graphic
have=GetGraphics(f,o1+wBack,buffer,1024);
sprintf(bmp,"%03db.bmp",i);
SaveGbBitmapTiles(bmp,buffer,16*16,4,4,FALSE);
}
}
int main(int argc,char **argv){
FILE *f=fopen("red.gb","rb");
if(!f){
printf("Can't open Pokemon Red English ROM. Please name the ROM red.gb and try again.\r\n");
return 1;
}
RedBlueGraphics(f);
fclose(f);
return 0;
}