diff options
Diffstat (limited to 'tools/br_ips')
| -rw-r--r-- | tools/br_ips/Makefile | 13 | ||||
| -rw-r--r-- | tools/br_ips/br_ips.c | 249 | ||||
| -rw-r--r-- | tools/br_ips/ips_patch.c | 63 | 
3 files changed, 325 insertions, 0 deletions
| diff --git a/tools/br_ips/Makefile b/tools/br_ips/Makefile new file mode 100644 index 000000000..e80e2576b --- /dev/null +++ b/tools/br_ips/Makefile @@ -0,0 +1,13 @@ +CC := gcc +CFLAGS := -O3 + +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 new file mode 100644 index 000000000..140bc3891 --- /dev/null +++ b/tools/br_ips/br_ips.c @@ -0,0 +1,249 @@ +#define _POSIX_C_SOURCE 200808L // Don't use GNU getline +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.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 + +#ifndef _SSIZE_T +#define _SSIZE_T +typedef int ssize_t; +#endif // _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 { +    uint32_t offset; +    size_t size; +}; + +static ssize_t getline(char ** lineptr, size_t * n, FILE * stream) { +    ssize_t i = 0; +    int c; +    size_t size = *n; +    char * buf = *lineptr; +    if (size == 0) { +        size = BUFSIZ; +        buf = realloc(buf, BUFSIZ); +    } +    if (buf == NULL) return -1; +    do { +        if (feof(stream)) return -1; +        c = getc(stream); +        buf[i++] = c; +        if (i == size) { +            size <<= 1; +            buf = realloc(buf, size); +            if (buf == NULL) return -1;      +        } +    } while (c != '\n'); +     +    *lineptr = buf; +    *n = size; +    return i; +} + +static void getIncbinsFromFile(struct Baserom ** incbins, size_t * num, size_t * maxnum, const char * fname, char ** strbuf, size_t * buffersize) { +    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; +    while (getline(strbuf, buffersize, file) > 0) { +        line_n++; +        char * nl_p = strchr(*strbuf, '\n'); +        if (nl_p != NULL) *nl_p = 0; +        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, &nincbins, &maxnincbins, incfname, strbuf, buffersize); +            continue; +        } +        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; +        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; +        do { +            if (*line == 0) FATAL_ERROR("%s:%d: malformed incbin\n", fname, line_n); +            incbinSize = strtoul(line, &line, 0); +            line++; +        } while (incbinSize == 0); +        if (incbinOffset >= 0x01000000) FATAL_ERROR("%s:%d: offset exceeds encodable limit\n", fname, line_n); +        if (incbinOffset == 0x454F46) { // "EOF" +            incbinOffset--; +            incbinSize++; +        } +        if (incbinOffset + incbinSize >= 0xFFFFFF + 0xFFFF) FATAL_ERROR("%s:%d: size exceeds encodable limit\n", fname, line_n); +        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"); +            } +            // fprintf(stderr, "DEBUG: %s:%d: 0x%x, 0x%lx\n", fname, line_n, incbinOffset, trueSize); +            data[nincbins].offset = incbinOffset; +            data[nincbins].size = trueSize; +            incbinOffset += trueSize; +            incbinSize -= trueSize; +            if (incbinOffset == 0x454F46) { +                incbinOffset--; +                data[nincbins].size--; +                incbinSize++; +            } +            nincbins++; +        } while (incbinSize > 0); +    } +    if (!feof(file)) FATAL_ERROR("getline\n"); +    fclose(file); +    *incbins = data; +    *num = nincbins; +    *maxnum = maxnincbins; +} + +static struct Baserom * getAllIncbins(FILE * ld_script, size_t * num_p) { +    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"); +    while (getline(&line, &linesiz, ld_script) > 0) { +        char * endptr; +        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; +        while (isspace(*startptr)) startptr++; +        if (strstr(startptr, ".a:") != NULL) continue; // no incbins in libs +        if (strstr(startptr, "src/") == startptr) continue; // no incbins in src/ +        endptr[1] = 's'; +        endptr[2] = 0; +        strcpy(fname_buf, startptr); +        getIncbinsFromFile(&incbins, &num, &maxnum, fname_buf, &line, &linesiz); +    } +    if (!feof(ld_script)) FATAL_ERROR("getline\n"); +    free(line); +    *num_p = num; +    return incbins; +} + +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; +    return (aa->offset > bb->offset) - (aa->offset < bb->offset); +} + +static void collapseIncbins(struct Baserom * incbins, size_t * num_p) { +    size_t num = *num_p; +    qsort(incbins, num, sizeof(struct Baserom), cmp_baserom); +    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++; +                } +                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) { +    FILE * file = fopen(filename, "wb"); +    if (file == NULL) FATAL_ERROR("unable to open file \"%s\" for writing\n", filename); +    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; +        putc(offset >> 16, file); +        putc(offset >>  8, file); +        putc(offset >>  0, file); +        size_t size = incbins[i].size; +        putc(size >> 8, file); +        putc(size >> 0, file); +        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"); +        printf("DEBUG: 0x%x, 0x%lx\n", offset, size); +    } +    free(readbuf); +    fwrite("EOF", 1, 3, file); +    fclose(file); +} + +int main() { +    puts(SPLASH); +    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"); +    size_t num = 0; +    struct Baserom * incbins = 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); +    } +    fclose(rom); +    free(incbins); +    puts("IPS file created at baserom.ips\n"); +    return 0; +} diff --git a/tools/br_ips/ips_patch.c b/tools/br_ips/ips_patch.c new file mode 100644 index 000000000..03780cf72 --- /dev/null +++ b/tools/br_ips/ips_patch.c @@ -0,0 +1,63 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.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 + +static const char SPLASH[] = "Small IPS patch utility\n" +                             "Created by PikalaxALT on 23 June 2019 All Rights Reserved\n"; + +int main(int argc, char ** argv) { +    puts(SPLASH); +    if (argc != 4) FATAL_ERROR("usage: %s 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]); +    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"); +    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; +         +        offset = (unsigned char)getc(patch) << 16; +        offset |= (unsigned char)getc(patch) << 8; +        offset |= (unsigned char)getc(patch); +        if (offset == 0x454F46) break; +        size = (unsigned char)getc(patch) << 8; +        size |= (unsigned char)getc(patch); +        if (fread(buffer + offset, 1, size, patch) != size) FATAL_ERROR("read segment\n"); +    } +    fclose(patch); +    fwrite(buffer, 1, romsize, out); +    fclose(out); +    free(buffer); +    return 0; +} | 
