diff --git a/src/fix/main.c b/src/fix/main.c index 51b8c08a..43181f8e 100644 --- a/src/fix/main.c +++ b/src/fix/main.c @@ -1,622 +1,34 @@ /* - * RGBFix : Perform various tasks on a Gameboy image-file + * Copyright © 2010 Anthony J. Bentley * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include -#include -#include #include -#include -#include -#include -#include -#include "asmotor.h" - -/* - * Option defines - * - */ - -#define OPTF_DEBUG 0x001L -#define OPTF_PAD 0x002L -#define OPTF_VALIDATE 0x004L -#define OPTF_TITLE 0x008L -#define OPTF_QUIET 0x020L -#define OPTF_RAMSIZE 0x040L -#define OPTF_MBCTYPE 0x080L -#define OPTF_GBCMODE 0x100L -#define OPTF_JAPAN 0x200L -#define OPTF_SGBMODE 0x400L -#define OPTF_NLICENSEE 0x800L - -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 - * - */ - -static void usage(void) -{ - printf("RGBFix v" RGBFIX_VERSION - " (part of ASMotor " ASMOTOR_VERSION ")\n\n"); - - printf("usage: rgbfix [-Ccdjqsv] [-m mbc_type] [-k licensee_str] [-p pad_value]\n"); - printf("\t [-r ram_size] [-t title_str] image[.gb]\n"); - - exit(EX_USAGE); -} - -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); -} -/* -0147 Cartridge type: - 0 - ROM ONLY 12 - ROM+MBC3+RAM - 1 - ROM+MBC1 13 - ROM+MBC3+RAM+BATT - 2 - ROM+MBC1+RAM 19 - ROM+MBC5 - 3 - ROM+MBC1+RAM+BATT 1A - ROM+MBC5+RAM - 5 - ROM+MBC2 1B - ROM+MBC5+RAM+BATT - 6 - ROM+MBC2+BATTERY 1C - ROM+MBC5+RUMBLE - 8 - ROM+RAM 1D - ROM+MBC5+RUMBLE+SRAM - 9 - ROM+RAM+BATTERY 1E - ROM+MBC5+RUMBLE+SRAM+BATT - B - ROM+MMM01 1F - Pocket Camera - C - ROM+MMM01+SRAM FD - Bandai TAMA5 - D - ROM+MMM01+SRAM+BATT FE - Hudson HuC-3 - F - ROM+MBC3+TIMER+BATT FF - Hudson HuC-1 - 10 - ROM+MBC3+TIMER+RAM+BATT - 11 - ROM+MBC3 -*/ -char * -MBC_String(unsigned char mbc_type) -{ - switch (mbc_type) { - case 0x00: - return "ROM ONLY"; - case 0x01: - return "ROM+MBC1"; - case 0x02: - return "ROM+MBC1+RAM"; - case 0x03: - return "ROM+MBC1+RAM+BATTERY"; - case 0x05: - return "ROM+MBC2"; - case 0x06: - return "ROM+MBC2+BATTERY"; - case 0x08: - return "ROM+RAM"; - case 0x09: - return "ROM+RAM+BATTERY"; - case 0x0B: - return "ROM+MMM01"; - case 0x0C: - return "ROM+MMM01+SRAM"; - case 0x0D: - return "ROM+MMM01+SRAM+BATTERY"; - case 0x0F: - return "ROM+MBC3+TIMER+BATTERY"; - case 0x10: - return "ROM+MBC3+TIMER+RAM+BATTERY"; - case 0x11: - return "ROM+MBC3"; - case 0x12: - return "ROM+MBC3+RAM"; - case 0x13: - return "ROM+MBC3+RAM+BATTERY"; - case 0x19: - return "ROM+MBC5"; - case 0x1A: - return "ROM+MBC5+RAM"; - case 0x1B: - return "ROM+MBC5+RAM+BATTERY"; - case 0x1C: - return "ROM+MBC5+RUMBLE"; - case 0x1D: - return "ROM+MBC5+RUMBLE+SRAM"; - case 0x1E: - return "ROM+MBC5+RUMBLE+SRAM+BATTERY"; - case 0x1F: - return "Pocket Camera"; - case 0xFD: - return "Bandai TAMA5"; - case 0xFE: - return "Hudson HuC-3"; - case 0xFF: - return "Hudson HuC-1"; - default: - return "Unknown MBC type"; - } -} -/* - * Das main - * - */ - -int +int main(int argc, char *argv[]) { - int ch; - char *ep; + FILE *rom; - char *filename; - char cartname[32]; - char nlicensee[3]; - FILE *f; - int pad_value = 0; - int ram_size = 0; - int mbc_type = 0; - int gbc_mode = 0; + if (argc < 2) + errx(1, "Usage: rgbfix romfile"); - ulOptions = 0; + if ((rom = fopen(argv[argc - 1], "rb+")) == NULL) + err(1, "Error opening file %s", argv[argc - 1]); - if (argc == 1) - usage(); + fclose(rom); - while ((ch = getopt(argc, argv, "Ccdjk:m:p:qr:st:v")) != -1) { - switch (ch) { - case 'c': - if (ulOptions & OPTF_GBCMODE) { - errx(EX_USAGE, "-c and -C can't be used together"); - } - ulOptions |= OPTF_GBCMODE; - gbc_mode = 0xC0; - break; - case 'C': - if (ulOptions & OPTF_GBCMODE) { - errx(EX_USAGE, "-c and -C can't be used together"); - } - ulOptions |= OPTF_GBCMODE; - gbc_mode = 0x80; - break; - case 'd': - ulOptions |= OPTF_DEBUG; - break; - case 'j': - ulOptions |= OPTF_JAPAN; - break; - case 'k': - strncpy(nlicensee, optarg, 2); - ulOptions |= OPTF_NLICENSEE; - break; - case 'm': - ulOptions |= OPTF_MBCTYPE; - mbc_type = strtoul(optarg, &ep, 0); - if (optarg[0] == '\0' || *ep != '\0') - errx(EX_USAGE, "Invalid argument for option 'm'"); - if (mbc_type < 0 || mbc_type > 0xFF) - errx(EX_USAGE, "Argument for option 'm' must be between 0 and 0xFF"); - break; - case 'p': - ulOptions |= OPTF_PAD; - pad_value = strtoul(optarg, &ep, 0); - if (optarg[0] == '\0' || *ep != '\0') - errx(EX_USAGE, "Invalid argument for option 'p'"); - if (pad_value < 0 || pad_value > 0xFF) - errx(EX_USAGE, "Argument for option 'p' must be between 0 and 0xFF"); - break; - case 'q': - ulOptions |= OPTF_QUIET; - break; - case 'r': - ulOptions |= OPTF_RAMSIZE; - ram_size = strtoul(optarg, &ep, 0); - if (optarg[0] == '\0' || *ep != '\0') - errx(EX_USAGE, "Invalid argument for option 'r'"); - if (ram_size < 0 || ram_size > 0xFF) - errx(EX_USAGE, "Argument for option 'r' must be between 0 and 0xFF"); - break; - case 's': - ulOptions |= OPTF_SGBMODE; - break; - case 't': - strncpy(cartname, optarg, 16); - ulOptions |= OPTF_TITLE; - break; - case 'v': - ulOptions |= OPTF_VALIDATE; - break; - default: - usage(); - /* NOTREACHED */ - } - } - argc -= optind; - argv += optind; - - if (argc == 0) - usage(); - - filename = argv[argc - 1]; - - if (!FileExists(filename)) - strncat(filename, ".gb", 3); - - f = fopen(filename, "rb+"); - if ((f = fopen(filename, "rb+")) != NULL) { - - /* - * -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) { - int size, padto; - unsigned int calcromsize, cartromsize; - int bytesadded = 0; - - size = FileSize(f); - padto = 0x8000L; - while (size > padto) - padto *= 2; - - if (!(ulOptions & OPTF_QUIET)) { - printf("Padding to %dKiB with pad value %#02x\n", padto / 1024, pad_value & 0xFF); - } - if (size != padto) { - fflush(stdout); - - fseek(f, 0, SEEK_END); - while (size < padto) { - size += 1; - if ((ulOptions & OPTF_DEBUG) == 0) - fputc(pad_value & 0xFF, f); - bytesadded += 1; - } - fflush(f); - - if (!(ulOptions & OPTF_QUIET)) { - printf("\tAdded %d bytes\n", bytesadded); - } - } else { - if (!(ulOptions & OPTF_QUIET)) { - printf("\tNo padding needed\n"); - } - } - /* ROM size byte */ - - calcromsize = 0; - while (size > (0x8000L << calcromsize)) - calcromsize += 1; - - fseek(f, 0x148, SEEK_SET); - cartromsize = fgetc(f); - - if (calcromsize != cartromsize) { - if (!(ulOptions & OPTF_DEBUG)) { - fseek(f, 0x148, SEEK_SET); - fputc(calcromsize, f); - fflush(f); - } - if (!(ulOptions & OPTF_QUIET)) { - printf("\tChanged ROM size byte from %#02x (%dKiB) to %#02x (%dKiB)\n", - cartromsize, - (0x8000 << cartromsize) / 1024, - calcromsize, - (0x8000 << calcromsize) / 1024); - } - } else if (!(ulOptions & OPTF_QUIET)) { - printf("\tROM size byte is OK\n"); - } - - if (calcromsize > 8) - warnx("ROM is %dKiB, max valid size is 8192KiB", (0x8000 << calcromsize) / 1024); - } - /* - * -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); - } - } - /* - * -k (Set new licensee) option code - */ - if (ulOptions & OPTF_NLICENSEE) { - if (!(ulOptions & OPTF_QUIET)) { - printf("Setting new licensee:\n"); - } - if ((ulOptions & OPTF_DEBUG) == 0) { - fflush(f); - fseek(f, 0x144, SEEK_SET); - fwrite(nlicensee, 2, 1, f); - fflush(f); - } - if (!(ulOptions & OPTF_QUIET)) { - printf("\tLicensee set to '%s'\n", nlicensee); - } - } - /* - * -r (Set ram size) option code - * - */ - if (ulOptions & OPTF_RAMSIZE) { - /* carttype byte can be anything? */ - if (!(ulOptions & OPTF_QUIET)) { - printf("Setting RAM size:\n"); - } - if (!(ulOptions & OPTF_DEBUG)) { - fflush(f); - fseek(f, 0x149L, SEEK_SET); - fputc(ram_size, f); - fflush(f); - } - if (!(ulOptions & OPTF_QUIET)) { - printf("\tRAM size set to %#02x\n", ram_size); - } - } - /* - * -j (Set region flag) option code - */ - if (ulOptions & OPTF_JAPAN) { - if (!(ulOptions & OPTF_DEBUG)) { - fflush(f); - fseek(f, 0x14A, SEEK_SET); - fputc(1, f); - fflush(f); - } - if (!(ulOptions & OPTF_QUIET)) { - printf("Setting region code:\n"); - printf("\tRegion code set to non-Japan\n"); - } - } - /* - * -m (Set MBC type) option code - */ - if (ulOptions & OPTF_MBCTYPE) { - /* carttype byte can be anything? */ - if (!(ulOptions & OPTF_QUIET)) { - printf("Setting MBC type:\n"); - } - if (!(ulOptions & OPTF_DEBUG)) { - fflush(f); - fseek(f, 0x147L, SEEK_SET); - fputc(mbc_type, f); - fflush(f); - } - if (!(ulOptions & OPTF_QUIET)) { - printf("\tMBC type set to %#02x (%s)\n", mbc_type, MBC_String(mbc_type)); - } - } - /* - * -C/-c (Set GBC only/compatible mode) - */ - if (ulOptions & OPTF_GBCMODE) { - if (!(ulOptions & OPTF_QUIET)) { - printf("Setting Game Boy Color flag:\n"); - } - if (!(ulOptions & OPTF_DEBUG)) { - fflush(f); - fseek(f, 0x143L, SEEK_SET); - fputc(gbc_mode, f); - fflush(f); - } - if (!(ulOptions & OPTF_QUIET) && gbc_mode == 0x80) { - printf("\tGBC flag set to GBC compatible (0x80)\n"); - } - if (!(ulOptions & OPTF_QUIET) && gbc_mode == 0xC0) { - printf("\tGBC flag set to only (0xC0)\n"); - } - - if (ulOptions & OPTF_TITLE) { - if (cartname[0xF]) { - warnx("Last character of cartridge title was overwritten by '-%c' option", gbc_mode == 0x80 ? 'C' : 'c'); - } - } - } - /* - * -s (Set SGB mode) option code - */ - if (ulOptions & OPTF_SGBMODE) { - if (!(ulOptions & OPTF_QUIET)) { - printf("Setting SGB mode:\n"); - } - if (!(ulOptions & OPTF_DEBUG)) { - fflush(f); - // set old licensee code to 0x33 - fseek(f, 0x14B, SEEK_SET); - fputc(0x33, f); - } - if (!(ulOptions & OPTF_QUIET)) { - printf("\tOld licensee code set to 0x33\n"); - } - if (!(ulOptions & OPTF_DEBUG)) { - // set SGB flag to 0x03 - fseek(f, 0x146, SEEK_SET); - fputc(3, f); - fflush(f); - } - if (!(ulOptions & OPTF_QUIET)) { - printf("\tSGB flag set to 0x03\n"); - } - } - /* - * -v (Validate header) option code - * - */ - - if (ulOptions & OPTF_VALIDATE) { - long i, byteschanged = 0; - unsigned int cartromsize, calcromsize = 0, filesize; - unsigned short cartchecksum = 0, calcchecksum = 0; - unsigned char cartcompchecksum = 0, calccompchecksum = - 0; - int ch; - - if (!(ulOptions & OPTF_QUIET)) { - 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; - - /* 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); - } else { - err(EX_NOINPUT, "Could not open file '%s'", filename); - } - - return (0); + return 0; }