diff options
-rw-r--r-- | tools/fixrom/fixrom.c | 67 |
1 files changed, 53 insertions, 14 deletions
diff --git a/tools/fixrom/fixrom.c b/tools/fixrom/fixrom.c index 1e526adb..59fbd327 100644 --- a/tools/fixrom/fixrom.c +++ b/tools/fixrom/fixrom.c @@ -6,8 +6,12 @@ #include <stdnoreturn.h> #include <stdarg.h> -#define HEADER_SIZE 0x4000 +#define HEADER_SIZE 0x4000 +#define HEADER_CODE_OFFSET 0xC +#define HEADER_SECURE_CRC_OFFSET 0x6C +#define HEADER_CRC_OFFSET 0x15E +// ROM header buffer uint8_t RomHeader[HEADER_SIZE]; static inline noreturn __attribute__((format(printf, 1, 2))) void fatal_error(const char * message, ...) @@ -21,6 +25,9 @@ static inline noreturn __attribute__((format(printf, 1, 2))) void fatal_error(co exit(EXIT_FAILURE); } +// Pedantic check to make sure you're writing to or reading from a valid range +// within the ROM header buffer. +// Call twice, once with the first offset and again with the last address. static inline void OffsetCheck(int offset) { if (offset < 0 || offset >= HEADER_SIZE) @@ -29,37 +36,54 @@ static inline void OffsetCheck(int offset) } } +// Wrapper for memcpy that writes to the ROM header buffer +static inline uint8_t * SafeCopyToHeader(int offset, const void * src, size_t size) +{ + OffsetCheck(offset); + OffsetCheck(offset + size - 1); + return memcpy(RomHeader + offset, src, size); +} + +// Read a 16-bit word from the header buffer static inline uint16_t HeaderReadU16LE(int offset) { OffsetCheck(offset); + OffsetCheck(offset + 1); return RomHeader[offset] | (RomHeader[offset + 1] << 8); } +// Read a 32-bit word from the header buffer static inline uint32_t HeaderReadU32LE(int offset) { OffsetCheck(offset); + OffsetCheck(offset + 3); return RomHeader[offset] | (RomHeader[offset + 1] << 8) | (RomHeader[offset + 2] << 16) | (RomHeader[offset + 3] << 24); } +// Write a 16-bit word to the header buffer static inline void HeaderWriteU16LE(int offset, uint16_t value) { OffsetCheck(offset); + OffsetCheck(offset + 1); RomHeader[offset] = value; RomHeader[offset + 1] = value >> 8; } +// Write a 32-bit word to the header buffer static inline void HeaderWriteU32LE(int offset, uint32_t value) { OffsetCheck(offset); + OffsetCheck(offset + 3); RomHeader[offset] = value; RomHeader[offset + 1] = value >> 8; RomHeader[offset + 2] = value >> 16; RomHeader[offset + 3] = value >> 24; } +// Standard CRC16 routine #define CRC16_POLYNOMIAL 0xA001 static uint16_t Calc_CRC16(uint8_t * data, size_t length, uint16_t crc) @@ -94,20 +118,24 @@ int main(int argc, char ** argv) int override_code = 0; FILE * rom = NULL; + // Parse arguments for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "--secure-crc") == 0) { + // Enforce zero or one if (override_crc) { fatal_error("multiple --secure-crc options specified"); } + // Parse integer char * endptr; unsigned long secure_crc_l = strtoul(argv[++i], &endptr, 0); if (secure_crc_l == 0 && endptr == argv[i]) { fatal_error("argument to --secure-crc must be an integer"); } - if (secure_crc_l >= 0x10000) + // Enforce width + if (secure_crc_l > UINT16_MAX) { fatal_error("argument to --secure-crc must be a 16-bit integer"); } @@ -116,55 +144,66 @@ int main(int argc, char ** argv) } else if (strcmp(argv[i], "--game-code") == 0) { + // Enforce zero or one if (override_code) { fatal_error("multiple --game-code options specified"); } - if (strlen(argv[++i]) > 4) + // Enforce max length + if (strlen(argv[++i]) > sizeof(game_code)) { fatal_error("argument to --game-code must be 4 characters or fewer"); } - strncpy(game_code, argv[i], 4); + memset(game_code, 0, sizeof(game_code)); + strncpy(game_code, argv[i], sizeof(game_code)); override_code = 1; } - else + // Positional arguments + else if (rom == NULL) { - if (rom != NULL) - { - fatal_error("unrecognized %s argument: %s", argv[i][0] == '-' ? "flag" : "positional", argv[i]); - } rom = fopen(argv[i], "r+b"); if (rom == NULL) { fatal_error(argv[i][0] == '-' ? "unrecognized flag argument: %s" : "unable to open file '%s' for reading", argv[i]); } } + else + { + // Invalid argument, complain about it + fatal_error("unrecognized %s argument: %s", argv[i][0] == '-' ? "flag" : "positional", argv[i]); + } } + // Read header to buffer if (fread(RomHeader, 1, HEADER_SIZE, rom) != HEADER_SIZE) { fatal_error("error reading the ROM header"); } + // Update CRC if (override_crc) { - HeaderWriteU16LE(0x6C, secure_crc); + HeaderWriteU16LE(HEADER_SECURE_CRC_OFFSET, secure_crc); } - + + // Update code if (override_code) { - memcpy(RomHeader + 0xC, game_code, 4); + SafeCopyToHeader(HEADER_CODE_OFFSET, game_code, sizeof(game_code)); } - uint16_t header_crc = Calc_CRC16((uint8_t *)RomHeader, 0x15E, 0xFFFF); - HeaderWriteU16LE(0x15E, header_crc); + // Recompute CRC of header not including the crc offset + uint16_t header_crc = Calc_CRC16((uint8_t *)RomHeader, HEADER_CRC_OFFSET, 0xFFFF); + HeaderWriteU16LE(HEADER_CRC_OFFSET, header_crc); + // Write the header back fseek(rom, 0, SEEK_SET); if (fwrite(RomHeader, 1, HEADER_SIZE, rom) != HEADER_SIZE) { fatal_error("error writing the ROM header"); } + // Hooray, we done did it! fclose(rom); return EXIT_SUCCESS; } |