diff options
Diffstat (limited to 'checksum.c')
-rw-r--r-- | checksum.c | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/checksum.c b/checksum.c new file mode 100644 index 0000000..c728887 --- /dev/null +++ b/checksum.c @@ -0,0 +1,121 @@ +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <stdlib.h> + +#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; +} |