#include #include #include #include #include #include #define DATA_START_OFFSET (0xa0) #define DATA_LEN (29) #define CHECKSUM_OFFSET (0xbd) static void print_usage(FILE *const stream, const char *const argv0) { fprintf(stream, "This program calculates Game Boy Advance ROM "); fprintf(stream, "checksum and places it into the header.\n"); fprintf(stream, "Usage: %s rom.bin\n", argv0); } static int fseek_set_wrapper(FILE *const stream, const long offset) { const int ret = fseek(stream, offset, SEEK_SET); if (ret) { const int err = errno; if (feof(stream)) { fprintf(stderr, "fseek(%zu): EOF, file is too small, exiting\n", offset); } else if (ferror(stream)) { fprintf(stderr, "fseek(%zu): Error (%d): \"%s\"\n", offset, err, strerror(err)); } else { assert(0); } } return ret; } static size_t fread_wrapper(void *const ptr, const size_t size, FILE *const stream) { const size_t ret = fread(ptr, 1, size, stream); if (ret >= size) { assert(ret == size); } else { const int err = errno; if (feof(stream)) { fprintf(stderr, "fread(%zu): EOF, file is too small, exiting\n", size); } else if (ferror(stream)) { fprintf(stderr, "fread(%zu): Error (%d): \"%s\"\n", size, err, strerror(err)); } else { assert(0); } } return ret; } static size_t fwrite_wrapper(void *const ptr, const size_t size, FILE *const stream) { const size_t ret = fwrite(ptr, 1, size, stream); if (ret >= size) { assert(ret == size); } else { const int err = errno; if (feof(stream)) { fprintf(stderr, "fwrite(%zu): EOF, file is too small, exiting\n", size); } else if (ferror(stream)) { fprintf(stderr, "fwrite(%zu): Error (%d): \"%s\"\n", size, err, strerror(err)); } else { assert(0); } } return ret; } static int run_checksum(FILE *const stream) { if (fseek_set_wrapper(stream, DATA_START_OFFSET)) { return EXIT_FAILURE; } uint8_t data_raw[DATA_LEN]; if (sizeof(data_raw) > fread_wrapper(&data_raw, sizeof(data_raw), stream)) { return EXIT_FAILURE; } uint8_t checksum; if (sizeof(checksum) > fread_wrapper(&checksum, sizeof(checksum), stream)) { return EXIT_FAILURE; } if (fseek_set_wrapper(stream, DATA_START_OFFSET)) { return EXIT_FAILURE; } uint8_t checksum_calculated = 0; // http://www.problemkaputt.de/gbatek-gba-cartridge-header.htm // chk=0:for i=0A0h to 0BCh:chk=chk-[i]:next:chk=(chk-19h) and 0FFh for (size_t i = 0; i < DATA_LEN; i++) { const uint8_t val = data_raw[i]; checksum_calculated -= val; } checksum_calculated -= 0x19; printf("checksum = 0x%02x, checksum_calculated = 0x%02x\n", checksum, checksum_calculated); if (fseek_set_wrapper(stream, CHECKSUM_OFFSET)) { return EXIT_FAILURE; } if (sizeof(checksum) > fwrite_wrapper(&checksum, sizeof(checksum), stream)) { return EXIT_FAILURE; } return EXIT_SUCCESS; } int main(const int argc, char *argv[]) { if (argc < 2) { print_usage(stderr, argv[0]); return EXIT_FAILURE; } const char* input_file_name = argv[1]; FILE *const input_stream = fopen(input_file_name, "rb+"); if (input_stream == NULL) { const int err = errno; fprintf(stderr, "main: fopen(\"%s\", \"r\"): Error (%d): \"%s\"\n", input_file_name, err, strerror(err)); return EXIT_FAILURE; } const int ret = run_checksum(input_stream); fclose(input_stream); return ret; }