diff options
author | PikalaxALT <pikalaxalt@gmail.com> | 2019-06-23 19:29:44 -0400 |
---|---|---|
committer | PikalaxALT <pikalaxalt@gmail.com> | 2019-06-23 19:29:44 -0400 |
commit | 8e7a1dac011e7e96d1220106c3865743f3e34175 (patch) | |
tree | c485906f5685aa239d20f125911c687aae2c4593 | |
parent | 64498f41072ee4a3f27c7b716a46c2952afa4721 (diff) |
Initial attempt to bootstrap Travis CI
-rw-r--r-- | .travis.yml | 35 | ||||
-rw-r--r-- | .travis/calcrom/webhook.sh | 16 | ||||
-rw-r--r-- | baserom.ips | bin | 0 -> 2401387 bytes | |||
-rw-r--r-- | data/data_83FECCC.s | 2 | ||||
-rw-r--r-- | data/graphics.s | 3 | ||||
-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 |
8 files changed, 378 insertions, 3 deletions
diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..af94cfe81 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,35 @@ +language: generic +dist: trusty +sudo: false +addons: + apt: + packages: + - gcc-multilib + - linux-libc-dev + - zlib-dev +cache: + apt: true +install: + - pushd $HOME + - travis_retry git clone https://github.com/luckytyphlosion/agbcc.git -b new_layout_with_libs + - cd agbcc && make && make install prefix=$TRAVIS_BUILD_DIR + - popd +matrix: + include: + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - 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: + - .travis/calcrom/webhook.sh pokefirered diff --git a/.travis/calcrom/webhook.sh b/.travis/calcrom/webhook.sh new file mode 100644 index 000000000..86da74c87 --- /dev/null +++ b/.travis/calcrom/webhook.sh @@ -0,0 +1,16 @@ +#!/bin/bash -ex + +# Only run this script if it's the master branch build. +if [[ "$TRAVIS_BRANCH" != "master" || "$TRAVIS_PULL_REQUEST" != "false" ]]; then + exit 0 +fi + +build_name=$1 +map_file=$build_name.map +if [ ! -f $map_file ]; then + echo "$map_file does not exist!" + exit 1 +fi + +output=$(perl $(dirname "$0")/calcrom.pl $build_name.map | sed -E ':a;N;$!ba;s/\r{0,1}\n/\\n/g') +curl -d "{\"username\": \"$CALCROM_DISCORD_WEBHOOK_USERNAME\", \"avatar_url\": \"$CALCROM_DISCORD_WEBHOOK_AVATAR_URL\", \"content\":\"\`\`\`$build_name progress:\\n$output\`\`\`\"}" -H "Content-Type: application/json" -X POST $CALCROM_DISCORD_WEBHOOK_URL diff --git a/baserom.ips b/baserom.ips Binary files differnew file mode 100644 index 000000000..c28018137 --- /dev/null +++ b/baserom.ips diff --git a/data/data_83FECCC.s b/data/data_83FECCC.s index 1a1ab6d0a..30a3f101a 100644 --- a/data/data_83FECCC.s +++ b/data/data_83FECCC.s @@ -665,7 +665,7 @@ gUnknown_843EE64:: @ 843EE64 .asciz "SEARCH" .align 2 - .incbin "baserom.gba", 0x43EEC0, 0x43F004-0x43EEC0 + .incbin "baserom.gba", 0x43EEC0, 0x144 gUnknown_843F004:: @ 843F004 .incbin "baserom.gba", 0x43F004, 0x100 diff --git a/data/graphics.s b/data/graphics.s index 35d5873fa..b655cadc7 100644 --- a/data/graphics.s +++ b/data/graphics.s @@ -16412,5 +16412,4 @@ gUnknown_8EAFFC0:: @ 8EAFFC0 gUnknown_8EB0ADC:: @ 8EB0ADC .incbin "baserom.gba", 0xEB0ADC, 0x44 -gUnknown_8EB0B20:: @ 8EB0B20 - .incbin "baserom.gba", 0xEB0B20 + @ EOF 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; +} |