ref: 5e9c2ee2ab9c34ee82b80bcdc40c5099f5985698
dir: /src/fix/main.c/
/* * RGBFix : Perform various tasks on a Gameboy image-file * */ #include <stdio.h> #include <stdlib.h> #include <stdarg.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 #define OPTF_QUIET 0x20L 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"); printf("\t-q\t\tExecute quietly (suppress all text except errors)\n"); exit(0); } void vFatalError(char *s, va_list ap) { fprintf(stderr, "*ERROR* : "); vfprintf(stderr, s, ap); fprintf(stderr, "\n"); } void FatalError(char *s, ...) { va_list ap; va_start (ap, s); vFatalError(s, ap); va_end(ap); exit(5); } void vWarning(char *s, va_list ap) { fprintf(stderr, "*WARNING* : "); vfprintf(stderr, s, ap); fprintf(stderr, "\n"); } void Warning(char *s, ...) { va_list ap; va_start(ap, s); vWarning(s, ap); va_end(ap); } 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; case 'q': ulOptions|=OPTF_QUIET; break; } } strcpy(filename, argv[argn++]); if (!FileExists(filename)) strcat(filename, ".gb"); f = fopen(filename, "rb+"); if (!f) FatalError("Unable to open file"); /* * -d (Debug) option code * */ if ((ulOptions & OPTF_DEBUG) && !(ulOptions & OPTF_QUIET)) { 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; if(!(ulOptions & OPTF_QUIET)) { 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); if(!(ulOptions & OPTF_QUIET)) { printf("\tAdded %ld bytes\n", bytesadded); } } else { if(!(ulOptions & OPTF_QUIET)) { printf("\tNo padding needed\n"); } } /* } else FatalError( "Image size exceeds 512kB" ); */ } /* * -r (Truncate) option code * */ if (ulOptions & OPTF_TRUNCATE) { long size, padto; char tempfile[] = "/tmp/rgbfix-XXXXXX"; FILE *tf; size = FileSize(f); padto = 256 * 32768; while (size < padto) padto /= 2; if(!(ulOptions & OPTF_QUIET)) { printf("Truncating to %ldkB:\n", padto / 1024); } mkstemp(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) { if(!(ulOptions & OPTF_QUIET)) { printf("Setting cartridge title:\n"); } if ((ulOptions & OPTF_DEBUG) == 0) { fflush(f); fseek(f, 0x0134L, SEEK_SET); fwrite(cartname, 16, 1, f); fflush(f); } if(!(ulOptions & OPTF_QUIET)) { 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; if(!(ulOptions & OPTF_VALIDATE)) { 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(!(ulOptions & OPTF_QUIET)) { 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); } if(!(ulOptions & OPTF_QUIET)) { printf("\tChanged ROM size byte from 0x%02lX (%ldkB) to 0x%02lX (%ldkB)\n", cartromsize, (0x8000L << cartromsize) / 1024, calcromsize, (0x8000L << calcromsize) / 1024); } } else if(!(ulOptions & OPTF_QUIET)) { 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); } if(!(ulOptions & OPTF_QUIET)) { printf ("\tCartridge type byte changed to 0x01\n"); } } else if(!(ulOptions & OPTF_QUIET)) { printf("\tCartridge type byte is OK\n"); } } else { /* carttype byte can be anything? */ if(!(ulOptions & OPTF_QUIET)) { 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); if(!(ulOptions & OPTF_QUIET)) { printf ("\tChecksum changed from 0x%04lX to 0x%04lX\n", (long)cartchecksum, (long)calcchecksum); } } else { if(!(ulOptions & OPTF_QUIET)) { 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); if(!(ulOptions & OPTF_QUIET)) { printf ("\tCompChecksum changed from 0x%02lX to 0x%02lX\n", (long)cartcompchecksum, (long)calccompchecksum); } } else { if(!(ulOptions & OPTF_QUIET)) { printf("\tCompChecksum is OK\n"); } } } fclose(f); return (0); }