summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--.travis.yml4
-rw-r--r--INSTALL.md12
-rw-r--r--baserom.ipsbin40940 -> 0 bytes
-rw-r--r--tools/br_ips/Makefile16
-rw-r--r--tools/br_ips/br_ips.c328
-rw-r--r--tools/br_ips/global.h27
-rw-r--r--tools/br_ips/ips_patch.c68
8 files changed, 1 insertions, 456 deletions
diff --git a/.gitignore b/.gitignore
index 67e416fd7..c0b0cb86c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,8 +46,6 @@ src/data/wild_encounters.h
tags
tools/agbcc
tools/binutils
-tools/br_ips/br_ips
-tools/br_ips/ips_patch
types_*.taghl
!.travis/calcrom/calcrom.pl
!sound/programmable_wave_samples/*.pcm
diff --git a/.travis.yml b/.travis.yml
index af94cfe81..519ba1aca 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,10 +25,6 @@ matrix:
- g++-7
env: _="Build"
script:
- - head -c 16777216 /dev/zero > tmp.bin
- - make ips_patch -C tools/br_ips
- - tools/br_ips/ips_patch tmp.bin baserom.ips baserom.gba
- - rm tmp.bin
- make tools CXX=g++-7
- make -j2 compare
after_success:
diff --git a/INSTALL.md b/INSTALL.md
index 4829680de..729014051 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -42,14 +42,4 @@ If only `.c` or `.s` files were changed, turn off the dependency scanning tempor
make -j$(nproc) NODEP=1
-**Note (until further notice):** If this is your first time building Pokemon FireRed, an unmodified copy of Pokemon FireRed is required in the project root under the name `baserom.gba`. To generate this, you should run the following commands:
-
- make ips_patch -C tools/br_ips
- head -c 16777216 /dev/zero > tmp.bin
- tools/br_ips/ips_patch tmp.bin baserom.ips baserom.gba
- make compare -j$(nproc)
- cp pokefirered.gba baserom.gba
-
-Alternatively, you can obtain an unmodified copy of Pokemon FireRed and use that as baserom.gba. Make sure the SHA1 checksum matches with what's provided in [the README](README.md).
-
-**Note 2:** If the build command is not recognized on Linux, including the Linux environment used within Windows, run `nproc` and replace `$(nproc)` with the returned value (e.g.: `make -j4`). Because `nproc` is not available on macOS, the alternative is `sysctl -n hw.ncpu`.
+**Note:** If the build command is not recognized on Linux, including the Linux environment used within Windows, run `nproc` and replace `$(nproc)` with the returned value (e.g.: `make -j4`). Because `nproc` is not available on macOS, the alternative is `sysctl -n hw.ncpu`.
diff --git a/baserom.ips b/baserom.ips
deleted file mode 100644
index 55a409caf..000000000
--- a/baserom.ips
+++ /dev/null
Binary files differ
diff --git a/tools/br_ips/Makefile b/tools/br_ips/Makefile
deleted file mode 100644
index 8b6ad51bf..000000000
--- a/tools/br_ips/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-CC := gcc
-CFLAGS := -O3 -std=c99
-
-.PHONY: all
-
-all: br_ips ips_patch
- @:
-
-clean:
- rm -f br_ips ips_patch br_ips.exe ips_patch.exe
-
-br_ips: br_ips.c
- $(CC) $(CFLAGS) -o $@ $^
-
-ips_patch: ips_patch.c
- $(CC) $(CFLAGS) -o $@ $^
diff --git a/tools/br_ips/br_ips.c b/tools/br_ips/br_ips.c
deleted file mode 100644
index 58e28202f..000000000
--- a/tools/br_ips/br_ips.c
+++ /dev/null
@@ -1,328 +0,0 @@
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <string.h>
-#include "global.h"
-
-static const char SPLASH[] = "IPS patch creator for undisassembled data\n"
- "Created by PikalaxALT on 23 June 2019 All Rights Reserved\n";
-
-static const char HELP[] = "br_ips\n"
- "This utility is meant to be run with no arguments in the project root of a PRET AGB disassembly.\n"
- "baserom.gba and ld_script.txt are required files which must be present in the project root.\n"
- "ld_script.txt is a GNU linker script. For more details, see\n"
- "https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_chapter/ld_3.html#SEC6.\n"
- "All ELF targets in the linker script with Makefile rules \"%.o: %.s\" must have their sources present\n"
- "at the indicated paths relative to the project root.\n"
- "\n"
- "Options:\n"
- " -h - show this message and exit\n";
-
-#if !defined(__CYGWIN__) && !defined(__APPLE__) && (_POSIX_C_SOURCE < 200809L || !_GNU_SOURCE)
-static int getline(char ** lineptr, size_t * n, FILE * stream) {
- // Static implementation of GNU getline
- int i = 0;
- int c;
- if (n == NULL || lineptr == NULL || stream == NULL) return -1;
- size_t size = *n;
- char * buf = *lineptr;
- if (buf == NULL || size < 4) {
- size = 128;
- *lineptr = buf = realloc(buf, 128);
- }
- if (buf == NULL) return -1;
- while (1) {
- c = getc(stream);
- if (c == EOF) break;
- buf[i++] = c;
- if (c == '\n') break;
- if (i == size - 1) {
- size <<= 1;
- buf = realloc(buf, size);
- if (buf == NULL) return -1;
- *lineptr = buf;
- *n = size;
- }
- }
- if (i == 0) return -1;
- buf[i] = 0;
- return i;
-}
-#endif
-
-static void getIncbinsFromFile(hunk_t ** hunks, size_t * num, size_t * maxnum, const char * fname, char ** strbuf, size_t * buffersize) {
- // Recursively find incbinned segments and encode them as hunks.
- FILE * file = fopen(fname, "r");
- if (file == NULL) return;
- hunk_t * data = *hunks;
- size_t nhunks = *num;
- size_t maxnhunks = *maxnum;
- int line_n = 0; // for error prints
- while (getline(strbuf, buffersize, file) > 0) {
- line_n++;
- // If another file is included by this one, recurse into it.
- char * include = strstr(*strbuf, ".include");
- if (include != NULL) {
- char incfname[128];
- include = strchr(include, '"');
- if (include == NULL) FATAL_ERROR("%s:%d: malformed include\n", fname, line_n);
- include++;
- char * endq_p = strchr(include, '"');
- if (endq_p == NULL) FATAL_ERROR("%s:%d: malformed include\n", fname, line_n);
- *endq_p = 0;
- strcpy(incfname, include);
- getIncbinsFromFile(&data, &nhunks, &maxnhunks, incfname, strbuf, buffersize);
- continue;
- }
- // Check for a .incbin "baserom.gba" directive
- char * line = strstr(*strbuf, ".incbin");
- if (line == NULL) continue;
- line = strstr(line + sizeof(".incbin"), "\"baserom.gba\",");
- if (line == NULL) continue;
- line += sizeof("\"baserom.gba\",") - 1;
- uint32_t incbinOffset;
- // Enforce the structure .incbin "baserom.gba", offset, size
- // Data cannot be located at offset 0, as that is the entry
- // point (ARM code).
- do {
- if (*line == 0) FATAL_ERROR("%s:%d: malformed incbin\n", fname, line_n);
- incbinOffset = strtoul(line, &line, 0);
- line++;
- } while (incbinOffset == 0);
- size_t incbinSize;
- do {
- if (*line == 0) FATAL_ERROR("%s:%d: malformed incbin\n", fname, line_n);
- incbinSize = strtoul(line, &line, 0);
- line++;
- } while (incbinSize == 0);
- // Offset must fit in three bytes
- if (incbinOffset >= 0x01000000) FATAL_ERROR("%s:%d: offset exceeds encodable limit\n", fname, line_n);
- // Avoid confusion with the end sentinel
- if (incbinOffset == 0x454F46) { // "EOF"
- incbinOffset--;
- incbinSize++;
- }
- // Cannot read past a certain point due to format restrictions
- if (incbinOffset + incbinSize > 0xFFFFFF + 0xFFFF) FATAL_ERROR("%s:%d: size exceeds encodable limit\n", fname, line_n);
- // Break up the incbin into hunks of maximum size 0xFFFF
- do {
- size_t trueSize = incbinSize <= 0xFFFF ? incbinSize : 0xFFFF;
- if (nhunks >= maxnhunks) {
- maxnhunks <<= 1;
- data = realloc(data, maxnhunks * sizeof(hunk_t));
- if (data == NULL) FATAL_ERROR("unable to reallocate hunks buffer\n");
- }
- data[nhunks].offset = incbinOffset;
- data[nhunks].size = trueSize;
- incbinOffset += trueSize;
- incbinSize -= trueSize;
- if (incbinOffset == 0x454F46) {
- incbinOffset--;
- data[nhunks].size--;
- incbinSize++;
- }
- nhunks++;
- } while (incbinSize > 0);
- }
- // Error check
- if (!feof(file)) FATAL_ERROR("getline\n");
- fclose(file);
- *hunks = data;
- *num = nhunks;
- *maxnum = maxnhunks;
-}
-
-static hunk_t * getAllIncbins(FILE * ld_script, size_t * num_p) {
- // Parse the ld script.
- // Strict adherence to syntax is expected.
- char * line = NULL;
- size_t linesiz = 0;
- char fname_buf[128];
- size_t maxnum = 256;
- size_t num = 0;
- // Allocate the hunks array.
- hunk_t * hunks = malloc(256 * sizeof(hunk_t));
- if (hunks == NULL) FATAL_ERROR("failed to allocate hunks buffer\n");
- while (getline(&line, &linesiz, ld_script) > 0) {
- char * endptr;
- // We only expect hunks in rodata, script_data, and gfx_data sections.
- if ((endptr = strstr(line, ".o(.rodata);")) == NULL
- && (endptr = strstr(line, ".o(script_data);")) == NULL
- && (endptr = strstr(line, ".o(gfx_data);")) == NULL) continue;
- char * startptr = line;
- // Skip whitespace.
- while (isspace(*startptr)) startptr++;
- if (strstr(startptr, ".a:") != NULL) continue; // no hunks in libs
- if (strstr(startptr, "src/") == startptr) continue; // no hunks in src/
- // Replace the extension with .s and truncate the string
- endptr[1] = 's';
- endptr[2] = 0;
- // We're reusing the already-allocated string buffer, so
- // copy the filename to the stack for use in error prints.
- strcpy(fname_buf, startptr);
- getIncbinsFromFile(&hunks, &num, &maxnum, fname_buf, &line, &linesiz);
- }
- // Error check
- if (!feof(ld_script)) FATAL_ERROR("getline\n");
- free(line);
- *num_p = num;
- return hunks;
-}
-
-static int cmp_baserom(const void * a, const void * b) {
- // Comparison function for sorting Hunk structs.
- // For more details, please refer to the qsort man pages.
- // See also the function "collapseIncbins" below.
- const hunk_t * aa = (const hunk_t *)a;
- const hunk_t * bb = (const hunk_t *)b;
- return (aa->offset > bb->offset) - (aa->offset < bb->offset);
-}
-
-static void collapseIncbins(hunk_t * hunks, size_t * num_p) {
- // This function merges adjacent hunks where possible.
- size_t num = *num_p;
- // Sort the array by offset increasing.
- qsort(hunks, num, sizeof(hunk_t), cmp_baserom);
- // We stop at num - 1 because we need to be able to look one
- // entry ahead in the hunks array.
- for (int i = 0; i < num - 1; i++) {
- // Loop until the next hunk is not adjacent to the current.
- while (hunks[i].offset + hunks[i].size == hunks[i + 1].offset) {
- // If this hunk cannot be merged with the next, proceed to the next.
- if (hunks[i].size == 0xFFFF || (hunks[i].size == 0xFFFE && hunks[i + 1].offset == 0x454F45)) break;
- // If this hunk is empty, remove it.
- if (hunks[i].size == 0) {
- int j;
- // Find the next non-empty hunk
- for (j = i + 1; j < num; j++) {
- if (hunks[j].size != 0) break;
- }
- if (j == num) {
- // All remaining hunks are empty
- num = i;
- break;
- }
- // Compaction
- // Use a for loop instead of memcpy to avoid UB from
- // overlapping buffers.
- for (int k = 0; k < num - j; k++) hunks[i + k] = hunks[j + k];
- num -= j - i;
- if (i >= num - 1) break;
- }
- else
- {
- // Combine this hunk with the next
- hunks[i].size += hunks[i + 1].size;
- if (hunks[i].size > 0xFFFF) {
- // Split the hunk back up, it's too big to encode.
- // Set the earlier hunk to the maximum permitted size,
- // and the following hunk to the remainder.
- hunks[i + 1].size = hunks[i].size - 0xFFFF;
- hunks[i].size = 0xFFFF;
- hunks[i + 1].offset = hunks[i].offset + 0xFFFF;
- // If this operation would confuse the hunk with the
- // EOF sentinel, fix that.
- if (hunks[i + 1].offset == 0x454F46) {
- hunks[i].size--;
- hunks[i + 1].offset--;
- hunks[i + 1].size++;
- }
- break;
- } else {
- // Compaction
- // Use a for loop instead of memcpy to avoid UB from
- // overlapping buffers.
- for (int j = i + 1; j < num - 1; j++) hunks[j] = hunks[j + 1];
- num--;
- if (i >= num - 1) break;
- }
- }
- }
- }
- *num_p = num;
-}
-
-static void writePatch(const char * filename, const hunk_t * hunks, size_t num, FILE * rom) {
- // Create an IPS patch.
- // The file is headed with a magic code which is "PATCH" in ASCII.
- // Following that are the "hunks": 3-byte offset, 2-byte size, and
- // the literal data. The file is ended with "EOF", again in ASCII.
- // For that reason, an offset of 0x454F46 cannot be encoded directly.
- // Offset and size are encoded big-endian.
- FILE * file = fopen(filename, "wb");
- if (file == NULL) FATAL_ERROR("unable to open file \"%s\" for writing\n", filename);
- // Maximum hunk size is 65535 bytes. For convenience, we allocate a
- // round 65536 (0x10000). This has no effect on memory consumption,
- // as malloc will round this up anyway.
- char * readbuf = NULL;
- if (rom != NULL) {
- readbuf = malloc(0x10000);
- if (readbuf == NULL) FATAL_ERROR("failed to allocate write buffer\n");
- }
- fwrite("PATCH", 1, 5, file); // magic
- if (readbuf != NULL) {
- for (int i = 0; i < num; i++) {
- // Encode the offset
- uint32_t offset = hunks[i].offset;
- putc(offset >> 16, file);
- putc(offset >> 8, file);
- putc(offset >> 0, file);
- // Encode the size
- size_t size = hunks[i].size;
- putc(size >> 8, file);
- putc(size >> 0, file);
- // Yank the data straight from the ROM
- if (fseek(rom, offset, SEEK_SET)) FATAL_ERROR("seek\n");
- if (fread(readbuf, 1, size, rom) != size) FATAL_ERROR("read\n");
- if (fwrite(readbuf, 1, size, file) != size) FATAL_ERROR("write\n");
- }
- free(readbuf);
- }
- // Write the EOF magic
- fwrite("EOF", 1, 3, file);
- fclose(file);
-}
-
-// This script takes no arguments.
-int main(int argc, char ** argv) {
- // Show a friendly message
- puts(SPLASH);
- // If requested, show help message
- if (argc >= 2 && strcmp(argv[1], "-h") == 0) {
- puts(HELP);
- return 0;
- }
- // This script expects to be in a PRET AGB disassembly project root.
- // Required files include baserom.gba, ld_script.txt, and all paths
- // referenced in ld_script.txt relative to the project root.
- FILE * rom = fopen("baserom.gba", "rb");
- if (rom == NULL) FATAL_ERROR("unable to open \"baserom.gba\" for reading\n");
- FILE * ld_script = fopen("ld_script.txt", "r");
- if (ld_script == NULL) FATAL_ERROR("unable to open \"ld_script.txt\" for reading\n");
- // Find all instances where segments of baserom.gba are incbinned literaly.
- size_t num = 0;
- hunk_t * hunks = getAllIncbins(ld_script, &num);
- fclose(ld_script);
- if (num == 0) {
- // If this line is printed, the script was unable to find any
- // `.incbin "baserom.gba"` lines.
- // If this is incorrect, please contact the developer.
- puts("No baserom.gba hunks found!\n"
- "If there are baserom.gba hunks in this project,\n"
- "please ping PikalaxALT on the pret discord,\n"
- "channel #gen-3-help.\n");
- writePatch("baserom.ips", NULL, 0, NULL);
- } else {
- // Merge neighboring hunks to reduce the number of hunks.
- collapseIncbins(hunks, &num);
- // Encode the hunks in the IPS patch.
- writePatch("baserom.ips", hunks, num, rom);
- }
- // Communicate status to the user.
- puts("IPS file created at baserom.ips\n");
- // Clean up and return.
- fclose(rom);
- free(hunks);
- return 0;
-}
diff --git a/tools/br_ips/global.h b/tools/br_ips/global.h
deleted file mode 100644
index b82c516ba..000000000
--- a/tools/br_ips/global.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef GUARD_BR_IPS_GLOBAL_H
-#define GUARD_BR_IPS_GLOBAL_H
-
-#ifdef _MSC_VER
-
-#define FATAL_ERROR(format, ...) \
-do { \
- fprintf(stderr, format, __VA_ARGS__); \
- exit(1); \
-} while (0)
-
-#else
-
-#define FATAL_ERROR(format, ...) \
-do { \
- fprintf(stderr, format, ##__VA_ARGS__); \
- exit(1); \
-} while (0)
-
-#endif // _MSC_VER
-
-typedef struct Hunk {
- uint32_t offset;
- size_t size;
-} hunk_t;
-
-#endif //GUARD_BR_IPS_GLOBAL_H
diff --git a/tools/br_ips/ips_patch.c b/tools/br_ips/ips_patch.c
deleted file mode 100644
index c912474a8..000000000
--- a/tools/br_ips/ips_patch.c
+++ /dev/null
@@ -1,68 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
-#include "global.h"
-
-static const char SPLASH[] = "Small IPS patch utility\n"
- "Created by PikalaxALT on 23 June 2019 All Rights Reserved\n";
-
-static const char HELP[] = "ips_patch [-h] ROM PATCH OUT\n"
- "\n"
- " ROM - input ROM file\n"
- " PATCH - IPS patch file to apply\n"
- " OUT - path to write patched ROM\n"
- "\n"
- "Options:\n"
- " -h - show this message and exit\n";
-
-int main(int argc, char ** argv) {
- // Show a friendly message
- puts(SPLASH);
- // If requested, show help message
- if (argc >= 2 && strcmp(argv[1], "-h") == 0) {
- puts(HELP);
- return 0;
- }
- // Enforce CLI syntax
- if (argc != 4) FATAL_ERROR("usage: %s [-h] ROM PATCH OUT\n", argv[0]);
- FILE * rom = fopen(argv[1], "rb");
- if (rom == NULL) FATAL_ERROR("failed to open file \"%s\" for reading\n", argv[1]);
- FILE * patch = fopen(argv[2], "rb");
- if (patch == NULL) FATAL_ERROR("failed to open file \"%s\" for reading\n", argv[2]);
- FILE * out = fopen(argv[3], "wb");
- if (patch == NULL) FATAL_ERROR("failed to open file \"%s\" for writing\n", argv[3]);
- // IPS magic header
- char magic[5];
- if (fread(magic, 1, 5, patch) != 5) FATAL_ERROR("read magic\n");
- if (memcmp(magic, "PATCH", 5) != 0) FATAL_ERROR("malformed IPS patch\n");
- // Read the ROM into allocated memory.
- fseek(rom, 0, SEEK_END);
- size_t romsize = ftell(rom);
- fseek(rom, 0, SEEK_SET);
- char * buffer = malloc(romsize);
- if (buffer == NULL) FATAL_ERROR("failed to allocate dest buffer\n");
- if (fread(buffer, 1, romsize, rom) != romsize) FATAL_ERROR("read ROM\n");
- fclose(rom);
- while (1) {
- uint32_t offset;
- size_t size;
- // Read each hunk into the buffer, overwriting previous data.
- // If two or more hunks overlap, the newest one is retained.
- // A good IPS patch creator will avoid this.
- offset = (unsigned char)getc(patch) << 16;
- offset |= (unsigned char)getc(patch) << 8;
- offset |= (unsigned char)getc(patch);
- if (offset == 0x454F46) break; // end sentinel "EOF"
- size = (unsigned char)getc(patch) << 8;
- size |= (unsigned char)getc(patch);
- if (offset + size > romsize) FATAL_ERROR("segment extends past end of ROM\n");
- if (fread(buffer + offset, 1, size, patch) != size) FATAL_ERROR("read segment\n");
- }
- fclose(patch);
- // Write the patched ROM
- fwrite(buffer, 1, romsize, out);
- fclose(out);
- free(buffer);
- return 0;
-}