summaryrefslogtreecommitdiff
path: root/checksum.c
blob: c728887fb23dcfe6533ac4a6bedfbc90a6c57430 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
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;
}