From f18fcb9fbd9ce8f9b0b320b259ab4fe067cde3b0 Mon Sep 17 00:00:00 2001 From: PikalaxALT Date: Mon, 24 Jun 2019 09:20:15 -0400 Subject: [PATCH 1/4] Documentation of br_ips and ips_patch --- tools/br_ips/br_ips.c | 229 ++++++++++++++++++++++++++++----------- tools/br_ips/ips_patch.c | 28 ++++- 2 files changed, 190 insertions(+), 67 deletions(-) diff --git a/tools/br_ips/br_ips.c b/tools/br_ips/br_ips.c index 5426e4e26..9b84973e1 100644 --- a/tools/br_ips/br_ips.c +++ b/tools/br_ips/br_ips.c @@ -31,12 +31,24 @@ typedef int ssize_t; static const char SPLASH[] = "IPS patch creator for undisassembled data\n" "Created by PikalaxALT on 23 June 2019 All Rights Reserved\n"; -struct Baserom { +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"; + +struct Hunk { uint32_t offset; size_t size; }; static ssize_t getline(char ** lineptr, size_t * n, FILE * stream) { + // Static implementation of GNU getline ssize_t i = 0; int c; size_t size = *n; @@ -62,17 +74,20 @@ static ssize_t getline(char ** lineptr, size_t * n, FILE * stream) { return i; } -static void getIncbinsFromFile(struct Baserom ** incbins, size_t * num, size_t * maxnum, const char * fname, char ** strbuf, size_t * buffersize) { +static void getIncbinsFromFile(struct Hunk ** 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) FATAL_ERROR("unable to open file \"%s\" for reading\n", fname); - struct Baserom * data = *incbins; - size_t nincbins = *num; - size_t maxnincbins = *maxnum; - int line_n = 0; + struct Hunk * 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++; + // Replace the newline character with NUL char * nl_p = strchr(*strbuf, '\n'); if (nl_p != NULL) *nl_p = 0; + // If another file is included by this one, recurse into it. char * include = strstr(*strbuf, ".include"); if (include != NULL) { char incfname[128]; @@ -83,165 +98,251 @@ static void getIncbinsFromFile(struct Baserom ** incbins, size_t * num, size_t * if (endq_p == NULL) FATAL_ERROR("%s:%d: malformed include\n", fname, line_n); *endq_p = 0; strcpy(incfname, include); - getIncbinsFromFile(&data, &nincbins, &maxnincbins, incfname, strbuf, buffersize); + 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); - ssize_t incbinSize; + 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++; } - if (incbinOffset + incbinSize >= 0xFFFFFF + 0xFFFF) FATAL_ERROR("%s:%d: size exceeds encodable limit\n", fname, line_n); + // 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 (nincbins >= maxnincbins) { - maxnincbins <<= 1; - data = realloc(data, maxnincbins * sizeof(struct Baserom)); - if (data == NULL) FATAL_ERROR("unable to reallocate incbins buffer\n"); + if (nhunks >= maxnhunks) { + maxnhunks <<= 1; + data = realloc(data, maxnhunks * sizeof(struct Hunk)); + if (data == NULL) FATAL_ERROR("unable to reallocate hunks buffer\n"); } - data[nincbins].offset = incbinOffset; - data[nincbins].size = trueSize; + data[nhunks].offset = incbinOffset; + data[nhunks].size = trueSize; incbinOffset += trueSize; incbinSize -= trueSize; if (incbinOffset == 0x454F46) { incbinOffset--; - data[nincbins].size--; + data[nhunks].size--; incbinSize++; } - nincbins++; + nhunks++; } while (incbinSize > 0); } + // Error check if (!feof(file)) FATAL_ERROR("getline\n"); fclose(file); - *incbins = data; - *num = nincbins; - *maxnum = maxnincbins; + *hunks = data; + *num = nhunks; + *maxnum = maxnhunks; } -static struct Baserom * getAllIncbins(FILE * ld_script, size_t * num_p) { +static struct Hunk * 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; - struct Baserom * incbins = malloc(256 * sizeof(struct Baserom)); - if (incbins == NULL) FATAL_ERROR("failed to allocate incbins buffer\n"); + // Allocate the hunks array. + struct Hunk * hunks = malloc(256 * sizeof(struct Hunk)); + 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 incbins in libs - if (strstr(startptr, "src/") == startptr) continue; // no incbins in src/ + 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(&incbins, &num, &maxnum, fname_buf, &line, &linesiz); + getIncbinsFromFile(&hunks, &num, &maxnum, fname_buf, &line, &linesiz); } + // Error check if (!feof(ld_script)) FATAL_ERROR("getline\n"); free(line); *num_p = num; - return incbins; + return hunks; } static int cmp_baserom(const void * a, const void * b) { - const struct Baserom * aa = (const struct Baserom *)a; - const struct Baserom * bb = (const struct Baserom *)b; + // Comparison function for sorting Hunk structs. + // For more details, please refer to the qsort man pages. + // See also the function "collapseIncbins" below. + const struct Hunk * aa = (const struct Hunk *)a; + const struct Hunk * bb = (const struct Hunk *)b; return (aa->offset > bb->offset) - (aa->offset < bb->offset); } -static void collapseIncbins(struct Baserom * incbins, size_t * num_p) { +static void collapseIncbins(struct Hunk * hunks, size_t * num_p) { + // This function merges adjacent hunks where possible. size_t num = *num_p; - qsort(incbins, num, sizeof(struct Baserom), cmp_baserom); + // Sort the array by offset increasing. + qsort(hunks, num, sizeof(struct Hunk), 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++) { - while (incbins[i].offset + incbins[i].size == incbins[i + 1].offset) { - if (incbins[i].size == 0xFFFF) break; - while (incbins[i].size == 0) { - for (int j = i; j < num - 1; j++) incbins[j] = incbins[j + 1]; - num--; - if (i == num - 1) break; - } - if (i == num - 1) break; - incbins[i].size += incbins[i + 1].size; - if (incbins[i].size > 0xFFFF) { - incbins[i + 1].size = incbins[i].size - 0xFFFF; - incbins[i].size = 0xFFFF; - incbins[i + 1].offset = incbins[i].offset + 0xFFFF; - if (incbins[i + 1].offset == 0x454F46) { - incbins[i].size--; - incbins[i + 1].offset--; - incbins[i + 1].size++; + // 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; } - break; - } else { - for (int j = i + 1; j < num - 1; j++) incbins[j] = incbins[j + 1]; - num--; - if (i == num - 1) break; } } } *num_p = num; } -static void writePatch(const char * filename, const struct Baserom * incbins, size_t num, FILE * rom) { +static void writePatch(const char * filename, const struct Hunk * 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 = malloc(0x10000); if (readbuf == NULL) FATAL_ERROR("failed to allocate write buffer\n"); fwrite("PATCH", 1, 5, file); // magic for (int i = 0; i < num; i++) { - uint32_t offset = incbins[i].offset; + // Encode the offset + uint32_t offset = hunks[i].offset; putc(offset >> 16, file); putc(offset >> 8, file); putc(offset >> 0, file); - size_t size = incbins[i].size; + // 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); } -int main() { +// 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; - struct Baserom * incbins = getAllIncbins(ld_script, &num); + struct Hunk * hunks = getAllIncbins(ld_script, &num); fclose(ld_script); if (num == 0) { - puts("No baserom.gba incbins found!\n"); - } else { - collapseIncbins(incbins, &num); - writePatch("baserom.ips", incbins, num, rom); + // 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"); + } 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(incbins); - puts("IPS file created at baserom.ips\n"); + free(hunks); return 0; } diff --git a/tools/br_ips/ips_patch.c b/tools/br_ips/ips_patch.c index 03780cf72..506f23ad7 100644 --- a/tools/br_ips/ips_patch.c +++ b/tools/br_ips/ips_patch.c @@ -24,18 +24,36 @@ do { \ 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 (argc != 4) FATAL_ERROR("usage: %s ROM PATCH OUT\n", argv[0]); + // 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); @@ -46,16 +64,20 @@ int main(int argc, char ** argv) { 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; + 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); From e786deb7f6677d0eeb760cdc698837692ed92013 Mon Sep 17 00:00:00 2001 From: PikalaxALT Date: Mon, 24 Jun 2019 13:50:37 -0400 Subject: [PATCH 2/4] Update to br_ips Move FATAL_ERROR definition to header Terminate strings read by getline with NUL --- tools/br_ips/br_ips | Bin 0 -> 17624 bytes tools/br_ips/br_ips.c | 62 ++++++++++----------------------------- tools/br_ips/global.h | 27 +++++++++++++++++ tools/br_ips/ips_patch | Bin 0 -> 13088 bytes tools/br_ips/ips_patch.c | 19 +----------- 5 files changed, 44 insertions(+), 64 deletions(-) create mode 100644 tools/br_ips/br_ips create mode 100644 tools/br_ips/global.h create mode 100644 tools/br_ips/ips_patch diff --git a/tools/br_ips/br_ips b/tools/br_ips/br_ips new file mode 100644 index 0000000000000000000000000000000000000000..0b291d4e278c8d447c11ed16d3c1ad5ee954432f GIT binary patch literal 17624 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 iJF #include #include - -#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 - -#ifndef _SSIZE_T -#define _SSIZE_T -typedef int ssize_t; -#endif // _SSIZE_T +#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"; @@ -42,14 +20,9 @@ static const char HELP[] = "br_ips\n" "Options:\n" " -h - show this message and exit\n"; -struct Hunk { - uint32_t offset; - size_t size; -}; - -static ssize_t getline(char ** lineptr, size_t * n, FILE * stream) { +static int getline(char ** lineptr, size_t * n, FILE * stream) { // Static implementation of GNU getline - ssize_t i = 0; + int i = 0; int c; size_t size = *n; char * buf = *lineptr; @@ -62,31 +35,28 @@ static ssize_t getline(char ** lineptr, size_t * n, FILE * stream) { if (feof(stream)) return -1; c = getc(stream); buf[i++] = c; - if (i == size) { + if (i == size -1) { size <<= 1; buf = realloc(buf, size); if (buf == NULL) return -1; } } while (c != '\n'); - + buf[i] = 0; *lineptr = buf; *n = size; return i; } -static void getIncbinsFromFile(struct Hunk ** hunks, size_t * num, size_t * maxnum, const char * fname, char ** strbuf, size_t * buffersize) { +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) FATAL_ERROR("unable to open file \"%s\" for reading\n", fname); - struct Hunk * data = *hunks; + 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++; - // Replace the newline character with NUL - char * nl_p = strchr(*strbuf, '\n'); - if (nl_p != NULL) *nl_p = 0; // If another file is included by this one, recurse into it. char * include = strstr(*strbuf, ".include"); if (include != NULL) { @@ -136,7 +106,7 @@ static void getIncbinsFromFile(struct Hunk ** hunks, size_t * num, size_t * maxn size_t trueSize = incbinSize <= 0xFFFF ? incbinSize : 0xFFFF; if (nhunks >= maxnhunks) { maxnhunks <<= 1; - data = realloc(data, maxnhunks * sizeof(struct Hunk)); + data = realloc(data, maxnhunks * sizeof(hunk_t)); if (data == NULL) FATAL_ERROR("unable to reallocate hunks buffer\n"); } data[nhunks].offset = incbinOffset; @@ -159,7 +129,7 @@ static void getIncbinsFromFile(struct Hunk ** hunks, size_t * num, size_t * maxn *maxnum = maxnhunks; } -static struct Hunk * getAllIncbins(FILE * ld_script, size_t * num_p) { +static hunk_t * getAllIncbins(FILE * ld_script, size_t * num_p) { // Parse the ld script. // Strict adherence to syntax is expected. char * line = NULL; @@ -168,7 +138,7 @@ static struct Hunk * getAllIncbins(FILE * ld_script, size_t * num_p) { size_t maxnum = 256; size_t num = 0; // Allocate the hunks array. - struct Hunk * hunks = malloc(256 * sizeof(struct Hunk)); + 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; @@ -200,16 +170,16 @@ 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 struct Hunk * aa = (const struct Hunk *)a; - const struct Hunk * bb = (const struct Hunk *)b; + 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(struct Hunk * hunks, size_t * num_p) { +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(struct Hunk), cmp_baserom); + 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++) { @@ -269,7 +239,7 @@ static void collapseIncbins(struct Hunk * hunks, size_t * num_p) { *num_p = num; } -static void writePatch(const char * filename, const struct Hunk * hunks, size_t num, FILE * rom) { +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 @@ -323,7 +293,7 @@ int main(int argc, char ** argv) { 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; - struct Hunk * hunks = getAllIncbins(ld_script, &num); + 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 diff --git a/tools/br_ips/global.h b/tools/br_ips/global.h new file mode 100644 index 000000000..b82c516ba --- /dev/null +++ b/tools/br_ips/global.h @@ -0,0 +1,27 @@ +#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 b/tools/br_ips/ips_patch new file mode 100644 index 0000000000000000000000000000000000000000..67c561a7b1248118e5510cf0db5e403ec42f1e89 GIT binary patch literal 13088 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 OoLV^PeCkYI`~L=aGb!Bw literal 0 HcmV?d00001 diff --git a/tools/br_ips/ips_patch.c b/tools/br_ips/ips_patch.c index 506f23ad7..c912474a8 100644 --- a/tools/br_ips/ips_patch.c +++ b/tools/br_ips/ips_patch.c @@ -2,24 +2,7 @@ #include #include #include - -#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 +#include "global.h" static const char SPLASH[] = "Small IPS patch utility\n" "Created by PikalaxALT on 23 June 2019 All Rights Reserved\n"; From 631d5af665f8de63db7940fe7bdb82960686db2f Mon Sep 17 00:00:00 2001 From: PikalaxALT Date: Mon, 24 Jun 2019 20:16:00 -0400 Subject: [PATCH 3/4] Ignore br_ips and ips_patch --- .gitignore | 1 + tools/br_ips/br_ips | Bin 17624 -> 0 bytes tools/br_ips/ips_patch | Bin 13088 -> 0 bytes 3 files changed, 1 insertion(+) delete mode 100644 tools/br_ips/br_ips delete mode 100644 tools/br_ips/ips_patch diff --git a/.gitignore b/.gitignore index d64e00e65..7029feac1 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ build/ *.pl *.bak src/data/items.h # Autogenerated by jsonproc +tools/br_ips/{br_ips,ips_patch}{,.exe} diff --git a/tools/br_ips/br_ips b/tools/br_ips/br_ips deleted file mode 100644 index 0b291d4e278c8d447c11ed16d3c1ad5ee954432f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17624 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 iJF$tp(#ES@ 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 OoLV^PeCkYI`~L=aGb!Bw From fd543fd799caf59cabf55721e561fcfb0f59315d Mon Sep 17 00:00:00 2001 From: PikalaxALT Date: Tue, 25 Jun 2019 09:07:46 -0400 Subject: [PATCH 4/4] Refactor br_ips getline; Update INSTALL.md notes --- INSTALL.md | 12 +++++++++++- tools/br_ips/br_ips.c | 21 ++++++++++++--------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 729014051..4829680de 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -42,4 +42,14 @@ If only `.c` or `.s` files were changed, turn off the dependency scanning tempor make -j$(nproc) NODEP=1 -**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`. +**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`. diff --git a/tools/br_ips/br_ips.c b/tools/br_ips/br_ips.c index 8e4469b5a..64c454a3e 100644 --- a/tools/br_ips/br_ips.c +++ b/tools/br_ips/br_ips.c @@ -24,26 +24,29 @@ 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 (size == 0) { - size = BUFSIZ; - buf = realloc(buf, BUFSIZ); + if (buf == NULL || size < 4) { + size = 128; + *lineptr = buf = realloc(buf, 128); } if (buf == NULL) return -1; - do { - if (feof(stream)) return -1; + while (1) { c = getc(stream); + if (c == EOF) break; buf[i++] = c; - if (i == size -1) { + if (c == '\n') break; + if (i == size - 1) { size <<= 1; buf = realloc(buf, size); if (buf == NULL) return -1; + *lineptr = buf; + *n = size; } - } while (c != '\n'); + } + if (i == 0) return -1; buf[i] = 0; - *lineptr = buf; - *n = size; return i; }