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;
}
|