diff options
author | Rangi <remy.oukaour+rangi42@gmail.com> | 2020-07-23 23:00:25 -0400 |
---|---|---|
committer | Rangi <remy.oukaour+rangi42@gmail.com> | 2020-07-23 23:49:30 -0400 |
commit | 8b40e990d31efbc2c5df4ad65b141dc1c1c24a47 (patch) | |
tree | 9cc9e9e655bfe766a46116f1f0205d108cf5cd87 | |
parent | f9f438473fe1c92a3d6313639db8048fe41ba9d0 (diff) |
Generate the Stadium 2 checksum data with pfero's tool instead of raw INCBINs
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | data/stadium/stadium_gold.bin | 1 | ||||
-rw-r--r-- | data/stadium/stadium_gold_debug.bin | 1 | ||||
-rw-r--r-- | data/stadium/stadium_silver.bin | 2 | ||||
-rw-r--r-- | data/stadium/stadium_silver_debug.bin | 2 | ||||
-rw-r--r-- | layout.link | 2 | ||||
-rw-r--r-- | main.asm | 22 | ||||
-rw-r--r-- | tools/Makefile | 3 | ||||
-rw-r--r-- | tools/stadium.c | 155 |
9 files changed, 167 insertions, 22 deletions
@@ -132,6 +132,7 @@ pokesilver_debug_opt = -cjsv -t POKEMON_SLV -i AAXE -k 01 -l 0x33 -m 0x10 -r 3 - %.gbc: $$(%_obj) layout.link $(RGBLINK) -n $*.sym -m $*.map -l layout.link -o $@ $(filter %.o,$^) $(RGBFIX) $($*_opt) $@ + tools/stadium $@ ### LZ compression rules diff --git a/data/stadium/stadium_gold.bin b/data/stadium/stadium_gold.bin deleted file mode 100644 index 1b690363..00000000 --- a/data/stadium/stadium_gold.bin +++ /dev/null @@ -1 +0,0 @@ -N64PS3nZ[05iBuQ]0U`5Ѷ*LP%'gER^]@jtYiO8SXXxDT.*634V_U-N7KeR1ܦz)^@
K9{muŨbqG6_W*Y'.E)gyyYkiu%HKmB>Aa0ѥ扟&#\Rյ&U+y:;$i-C)=>Xʷi*=e/lYiAI_7by\j<^ar}ik쵩1rh:ДmԮ>7ᅂU%
\ No newline at end of file diff --git a/data/stadium/stadium_gold_debug.bin b/data/stadium/stadium_gold_debug.bin deleted file mode 100644 index 665a5fcc..00000000 --- a/data/stadium/stadium_gold_debug.bin +++ /dev/null @@ -1 +0,0 @@ -N64PS3JZnkl,5]BuQ1P`5Ѷ*LP&gER^]@jtYiK8SXXxD|.*634V_U-N7KeR1ܦz,^CK{{muŨbqG6_W*Y'.E)gyyYjiu%HKmB>Aa0ѥ扟&#\Rյ&U+y:;$i-C)=>Xʷi*=e/lYiAI_7by\j<^ar}ik쵩1rh:ДmԮ>7ᅂ|6UJ
\ No newline at end of file diff --git a/data/stadium/stadium_silver.bin b/data/stadium/stadium_silver.bin deleted file mode 100644 index 7e092ab1..00000000 --- a/data/stadium/stadium_silver.bin +++ /dev/null @@ -1,2 +0,0 @@ -N64PS3yPZ1VB|<%v1Pe0-`5Ѷ*LP%'gE2^]@jtYԲi8$SX4Cd.h#V66Q2)whd˘% -嗫eQ<p`$(z){^?Kr{muŨbG6_W*U'.C)gyyK)}%HKmB>Aa0ѥ扟&#\Rյ&U+y:;$i-C)=>Xʷi*=e/lYiAI_7by\j<^ar}ik쵩1rh"{Ԯ>7ᅂR
\ No newline at end of file diff --git a/data/stadium/stadium_silver_debug.bin b/data/stadium/stadium_silver_debug.bin deleted file mode 100644 index cd1d857f..00000000 --- a/data/stadium/stadium_silver_debug.bin +++ /dev/null @@ -1,2 +0,0 @@ -N64PS3ZR|<v-P1(`5Ѷ*LP&gE2^]@jtYi8PSX4C.h#V66Q2)whd˘% -嗫eQ<p`$(z,^CmK{muŨbG6_W*U'.C)gyyK?}%HKmB>Aa0ѥ扟&#\Rյ&U+y:;$i-C)=>Xʷi*=e/lYiAI_7by\j<^ar}ik쵩1rh"{Ԯ>7ᅂ|7S
\ No newline at end of file diff --git a/layout.link b/layout.link index d740a955..ec891bbf 100644 --- a/layout.link +++ b/layout.link @@ -251,7 +251,7 @@ ROMX $70 "bank70_2" ROMX $7f org $7df8 - "Stadium Data" + "Stadium 2 Checksums" WRAM0 "Audio RAM" "WRAM" @@ -439,18 +439,12 @@ INCBIN "gfx/pokegear/pokegear.2bpp.lz" INCLUDE "data/credits_strings.asm" -SECTION "Stadium Data", ROMX +SECTION "Stadium 2 Checksums", ROMX[$7DF8], BANK[$7F] -IF DEF(_GOLD) -IF DEF(_DEBUG) -INCBIN "data/stadium/stadium_gold_debug.bin" -ELSE -INCBIN "data/stadium/stadium_gold.bin" -ENDC -ELIF DEF(_SILVER) -IF DEF(_DEBUG) -INCBIN "data/stadium/stadium_silver_debug.bin" -ELSE -INCBIN "data/stadium/stadium_silver.bin" -ENDC -ENDC +; The end of the ROM is taken up by checksums of the content, apparently used +; by Pokémon Stadium 2 due to the checksums' "N64PS3" header. (In Japan, +; Pokémon Stadium Gold and Silver was the third Stadium release for N64.) +; This SECTION reserves space for those checksums. +; If it is removed, also remove the "tools/stadium" command in the Makefile. + + ds $208 diff --git a/tools/Makefile b/tools/Makefile index 797ee2c9..0d06aa07 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -7,7 +7,8 @@ tools := \ lzcomp \ png_dimensions \ scan_includes \ - gfx + gfx \ + stadium all: $(tools) @: diff --git a/tools/stadium.c b/tools/stadium.c new file mode 100644 index 00000000..796b7fc2 --- /dev/null +++ b/tools/stadium.c @@ -0,0 +1,155 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <getopt.h> +#include <string.h> +#include <stdint.h> + +#include "common.h" + +// The Game Boy cartridge header stores a global checksum at 0x014E-0x014F +#define GLOBALOFF 0x014E +// "base" data; Crystal-only +#define BASESIZE 24 +// ASCII "N64PS3" header +#define N64PS3SIZE 6 +// N64PS3 + CRC +#define HEADERSIZE (N64PS3SIZE + 2) +// Checksum every half-bank +#define CHECKSIZE 0x2000 +// The CRC initial value (also used for checksums) +#define CRC_INIT 0xFEFE +// The CRC polynomial value +#define CRC_POLY 0xC387 + +typedef enum Base { BASE_NONE, BASE_US, BASE_EU, BASE_DEBUG } Base; + +uint8_t us_base[BASESIZE] = {'b', 'a', 's', 'e', + 0x01, 0x00, 0xBF, 0x6B, 0x40, 0x11, 0x00, 0x22, 0x00, 0x3A, + 0xF3, 0x38, 0x18, 0xFF, 0xFF, 0x0F, 0x07, 0x10, 0x68, 0x07}; + +uint8_t eu_base[BASESIZE] = {'b', 'a', 's', 'e', + 0x01, 0x01, 0x1E, 0xCF, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0C, + 0xA3, 0x38, 0x00, 0xFF, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x14}; + +uint8_t dbg_base[BASESIZE] = {'b', 'a', 's', 'e', + 0x01, 0x00, 0x07, 0x82, 0x40, 0x10, 0x00, 0x22, 0x00, 0x3A, + 0xE3, 0x38, 0x00, 0xFF, 0xFF, 0x07, 0x07, 0x10, 0x68, 0x06}; + +uint8_t n64ps3[N64PS3SIZE] = {'N', '6', '4', 'P', 'S', '3'}; + +static void usage(void) { + fprintf(stderr, "Usage: stadium [-h|--help] [-b|--base us|eu|dbg] romfile\n"); +} + +void parse_args(int argc, char *argv[], Base *b) { + struct option long_options[] = { + {"base", required_argument, 0, 'b'}, + {"help", no_argument, 0, 'h'}, + {0} + }; + for (int opt = 0; opt != -1;) { + switch (opt = getopt_long(argc, argv, "hb:", long_options)) { + case 'h': + usage(); + exit(0); + break; + case 'b': + *b = !strcmp(optarg, "us") ? BASE_US : + !strcmp(optarg, "eu") ? BASE_EU : + !strcmp(optarg, "dbg") ? BASE_DEBUG : + BASE_NONE; + break; + case 0: + case -1: + break; + default: + usage(); + exit(1); + break; + } + } +} + +#define SET_U16BE(file, off, v) do { \ + file[(off) + 0] = (uint8_t)(((v) & 0xFF00) >> 8); \ + file[(off) + 1] = (uint8_t)(((v) & 0x00FF) >> 0); \ +} while (0) + +void calculate_checksums(uint8_t *file, int filesize, Base base) { + int NUMCHECKS = filesize / CHECKSIZE; + int DATASIZE = HEADERSIZE + NUMCHECKS * 2; // 2 bytes per checksum + int ORIGIN = filesize - DATASIZE; // Stadium data goes at the end of the file + + // Clear the global checksum + SET_U16BE(file, GLOBALOFF, 0); + + // Write the appropriate base data, or none + int BASEOFF = ORIGIN - BASESIZE; + if (base == BASE_US) { + memcpy(file + BASEOFF, us_base, BASESIZE); + } else if (base == BASE_EU) { + memcpy(file + BASEOFF, eu_base, BASESIZE); + } else if (base == BASE_DEBUG) { + memcpy(file + BASEOFF, dbg_base, BASESIZE); + } + + // Initialize the Stadium data (this should be free space anyway) + memset(file + ORIGIN, 0, DATASIZE); + memcpy(file + ORIGIN, n64ps3, N64PS3SIZE); + + // Calculate the half-bank checksums + for (int i = 0; i < NUMCHECKS; i++) { + uint16_t checksum = CRC_INIT; + for (int j = 0; j < CHECKSIZE; j++) { + checksum += file[i * CHECKSIZE + j]; + } + SET_U16BE(file, ORIGIN + HEADERSIZE + i * 2, checksum); + } + + // Initialize the CRC table + uint16_t crc_table[256]; + for (int i = 0; i < 256; i++) { + uint16_t c = i; + uint16_t rem = 0; + for (int y = 0; y < 8; y++) { + rem = (rem >> 1) ^ ((rem ^ c) & 1 ? CRC_POLY : 0); + c >>= 1; + } + crc_table[i] = rem; + } + + // Calculate the CRC of the half-bank checksums + uint16_t crc = CRC_INIT; + for (int i = ORIGIN + HEADERSIZE; i < ORIGIN + DATASIZE; i++) { + crc = (crc >> 8) ^ crc_table[(crc & 0xFF) ^ file[i]]; + } + SET_U16BE(file, ORIGIN + HEADERSIZE - 2, crc); + + // Calculate the global checksum + uint16_t globalsum = 0; + for (int i = 0; i < filesize; i++) { + globalsum += file[i]; + } + SET_U16BE(file, GLOBALOFF, globalsum); +} + +int main(int argc, char *argv[]) { + Base base = BASE_NONE; + parse_args(argc, argv, &base); + + argc -= optind; + argv += optind; + if (argc < 1) { + usage(); + exit(1); + } + + char *filename = argv[0]; + int filesize; + uint8_t *file = read_u8(filename, &filesize); + calculate_checksums(file, filesize, base); + write_u8(filename, file, filesize); + + return 0; +} |