summaryrefslogtreecommitdiff
path: root/checksum.c
diff options
context:
space:
mode:
Diffstat (limited to 'checksum.c')
-rw-r--r--checksum.c121
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;
+}