ref: c0a9cf79a32d93079b471ad61b767b538d1213c1
dir: /src/rgbfix/main.c/
/* * RGBFix : Perform various tasks on a Gameboy image-file * */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "asmotor.h" /* * Option defines * */ #define OPTF_DEBUG 0x01L #define OPTF_PAD 0x02L #define OPTF_VALIDATE 0x04L #define OPTF_TITLE 0x08L #define OPTF_TRUNCATE 0x10L unsigned long ulOptions; /* * Misc. variables * */ unsigned char NintendoChar[48]= { 0xCE,0xED,0x66,0x66,0xCC,0x0D,0x00,0x0B,0x03,0x73,0x00,0x83,0x00,0x0C,0x00,0x0D, 0x00,0x08,0x11,0x1F,0x88,0x89,0x00,0x0E,0xDC,0xCC,0x6E,0xE6,0xDD,0xDD,0xD9,0x99, 0xBB,0xBB,0x67,0x63,0x6E,0x0E,0xEC,0xCC,0xDD,0xDC,0x99,0x9F,0xBB,0xB9,0x33,0x3E }; /* * Misc. routines * */ void PrintUsage( void ) { printf( "RGBFix v" RGBFIX_VERSION " (part of ASMotor " ASMOTOR_VERSION ")\n\n" ); printf( "Usage: rgbfix [options] image[.gb]\n" ); printf( "Options:\n" ); printf( "\t-h\t\tThis text\n" ); printf( "\t-d\t\tDebug: Don't change image\n" ); printf( "\t-p\t\tPad image to valid size\n\t\t\tPads to 32/64/128/256/512kB as appropriate\n" ); printf( "\t-r\t\ttRuncate image to valid size\n\t\t\tTruncates to 32/64/128/256/512kB as appropriate\n" ); printf( "\t-t<name>\tChange cartridge title field (16 characters)\n" ); printf( "\t-v\t\tValidate header\n\t\t\tCorrects - Nintendo Character Area (0x0104)\n\t\t\t\t - ROM type (0x0147)\n\t\t\t\t - ROM size (0x0148)\n\t\t\t\t - Checksums (0x014D-0x014F)\n" ); exit( 0 ); } void FatalError( char *s ) { printf( "\n***ERROR: %s\n\n", s ); PrintUsage(); } long int FileSize( FILE *f ) { long prevpos; long r; fflush( f ); prevpos=ftell( f ); fseek( f, 0, SEEK_END ); r=ftell( f ); fseek( f, prevpos, SEEK_SET ); return( r ); } int FileExists( char *s ) { FILE *f; if( (f=fopen(s,"rb"))!=NULL ) { fclose( f ); return( 1 ); } else return( 0 ); } /* * Das main * */ int main( int argc, char *argv[] ) { int argn=1; char filename[512]; char cartname[32]; FILE *f; ulOptions=0; if( (--argc)==0 ) PrintUsage(); while( *argv[argn]=='-' ) { argc-=1; switch( argv[argn++][1] ) { case '?': case 'h': PrintUsage(); break; case 'd': ulOptions|=OPTF_DEBUG; break; case 'p': ulOptions|=OPTF_PAD; break; case 'r': ulOptions|=OPTF_TRUNCATE; break; case 'v': ulOptions|=OPTF_VALIDATE; break; case 't': strncpy( cartname, argv[argn-1]+2, 16 ); ulOptions|=OPTF_TITLE; break; } } strcpy( filename, argv[argn++] ); if( !FileExists(filename) ) strcat( filename, ".gb" ); if( (f=fopen(filename,"rb+"))!=NULL ) { /* * -d (Debug) option code * */ if( ulOptions&OPTF_DEBUG ) { printf( "-d (Debug) option enabled...\n" ); } /* * -p (Pad) option code * */ if( ulOptions&OPTF_PAD ) { long size, padto; long bytesadded=0; size=FileSize( f ); padto=0x8000L; while( size>padto ) padto*=2; printf( "Padding to %ldkB:\n", padto/1024 ); /* if( padto<=0x80000L ) { */ if( size!=padto ) { fflush( stdout ); fseek( f, 0, SEEK_END ); while( size<padto ) { size+=1; if( (ulOptions&OPTF_DEBUG)==0 ) fputc( 0, f ); bytesadded+=1; } fflush( f ); printf( "\tAdded %ld bytes\n", bytesadded ); } else printf( "\tNo padding needed\n" ); /* } else FatalError( "Image size exceeds 512kB" ); */ } /* * -r (Truncate) option code * */ if( ulOptions&OPTF_TRUNCATE ) { long size, padto; char tempfile[512]; FILE *tf; size=FileSize( f ); padto=256*32768; while( size<padto ) padto/=2; printf( "Truncating to %ldkB:\n", padto/1024 ); tmpnam( tempfile ); if( (ulOptions&OPTF_DEBUG)==0 ) { if( (tf=fopen(tempfile,"wb"))!=NULL ) { fseek( f, 0, SEEK_SET ); while( padto-- ) { fputc( fgetc(f), tf ); } fclose( f ); fclose( tf ); remove( filename ); rename( tempfile, filename ); f=fopen( filename, "rb+" ); } } } /* * -t (Set carttitle) option code * */ if( ulOptions&OPTF_TITLE ) { printf( "Setting cartridge title:\n" ); if( (ulOptions&OPTF_DEBUG)==0 ) { fflush( f ); fseek( f, 0x0134L, SEEK_SET ); fwrite( cartname, 16, 1, f ); fflush( f ); } printf( "\tTitle set to %s\n", cartname ); } /* * -v (Validate header) option code * */ if( ulOptions&OPTF_VALIDATE ) { long i, byteschanged=0; long cartromsize, calcromsize=0, filesize; long carttype; unsigned short cartchecksum=0, calcchecksum=0; unsigned char cartcompchecksum=0, calccompchecksum=0; int ch; printf( "Validating header:\n" ); fflush( stdout ); /* Nintendo Character Area */ fflush( f ); fseek( f, 0x0104L, SEEK_SET ); for( i=0; i<48; i+=1 ) { int ch; ch=fgetc( f ); if( ch==EOF ) ch=0x00; if( ch!=NintendoChar[i] ) { byteschanged+=1; if( (ulOptions&OPTF_DEBUG)==0 ) { fseek( f, -1, SEEK_CUR ); fputc( NintendoChar[i], f ); fflush( f ); } } } fflush( f ); if( byteschanged ) printf( "\tChanged %ld bytes in the Nintendo Character Area\n", byteschanged ); else printf( "\tNintendo Character Area is OK\n" ); /* ROM size */ fflush( f ); fseek( f, 0x0148L, SEEK_SET ); cartromsize=fgetc( f ); if( cartromsize==EOF ) cartromsize=0x00; filesize=FileSize( f ); while( filesize>(0x8000L<<calcromsize) ) calcromsize+=1; if( calcromsize!=cartromsize ) { if( (ulOptions&OPTF_DEBUG)==0 ) { fseek( f, -1, SEEK_CUR ); fputc( calcromsize, f ); fflush( f ); } printf( "\tChanged ROM size byte from 0x%02lX (%ldkB) to 0x%02lX (%ldkB)\n", cartromsize, (0x8000L<<cartromsize)/1024, calcromsize, (0x8000L<<calcromsize)/1024 ); } else printf( "\tROM size byte is OK\n" ); /* Cartridge type */ fflush( f ); fseek( f, 0x0147L, SEEK_SET ); carttype=fgetc( f ); if( carttype==EOF ) carttype=0x00; if( FileSize(f)>0x8000L ) { /* carttype byte must != 0x00 */ if( carttype==0x00 ) { if( (ulOptions&OPTF_DEBUG)==0 ) { fseek( f, -1, SEEK_CUR ); fputc( 0x01, f ); fflush( f ); } printf( "\tCartridge type byte changed to 0x01\n" ); } else printf( "\tCartridge type byte is OK\n" ); } else { /* carttype byte can be anything? */ printf( "\tCartridge type byte is OK\n" ); } /* Checksum */ fflush( f ); fseek( f, 0, SEEK_SET ); for( i=0; i<(0x8000L<<calcromsize); i+=1 ) { ch=fgetc( f ); if( ch==EOF ) ch=0; if( i<0x0134L ) calcchecksum+=ch; else if( i<0x014DL ) { calccompchecksum+=ch; calcchecksum+=ch; } else if( i==0x014DL ) cartcompchecksum=ch; else if( i==0x014EL ) cartchecksum=ch<<8; else if( i==0x014FL ) cartchecksum|=ch; else calcchecksum+=ch; } calccompchecksum=0xE7-calccompchecksum; calcchecksum+=calccompchecksum; if( cartchecksum!=calcchecksum ) { fflush( f ); fseek( f, 0x014EL, SEEK_SET ); if( (ulOptions&OPTF_DEBUG)==0 ) { fputc( calcchecksum>>8, f ); fputc( calcchecksum&0xFF, f ); } fflush( f ); printf( "\tChecksum changed from 0x%04lX to 0x%04lX\n", (long)cartchecksum, (long)calcchecksum ); } else printf( "\tChecksum is OK\n" ); if( cartcompchecksum!=calccompchecksum ) { fflush( f ); fseek( f, 0x014DL, SEEK_SET ); if( (ulOptions&OPTF_DEBUG)==0 ) fputc( calccompchecksum, f ); fflush( f ); printf( "\tCompChecksum changed from 0x%02lX to 0x%02lX\n", (long)cartcompchecksum, (long)calccompchecksum ); } else printf( "\tCompChecksum is OK\n" ); } fclose( f ); } else { FatalError( "Unable to open file" ); } return( 0 ); }