summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--INSTALL.md46
-rw-r--r--Makefile55
-rw-r--r--tools/mwasmarm_patcher/.gitignore1
-rw-r--r--tools/mwasmarm_patcher/Makefile11
-rw-r--r--tools/mwasmarm_patcher/mwasmarm_patcher.c188
6 files changed, 291 insertions, 13 deletions
diff --git a/.gitignore b/.gitignore
index f35ecba0..74eab542 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,6 @@ cmake-build-debug/
# ROM
*.nds
*.srl
+
+# Tool executables
+*.exe
diff --git a/INSTALL.md b/INSTALL.md
index 05073d81..2803a159 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -1,13 +1,49 @@
-#### 1. Copy baserom(s) into root folder
+### 1. Copy baserom(s) into root folder
-Put a clean copy of Pokemon Diamond (US) nds rom at `./baserom.us.nds`.
+Put a clean copy of Pokemon Diamond (US) nds rom at `./baserom.nds`.
-#### 2. Install MWCC compiler
+### 2. Install MWCC compiler
The build system requires the use of the Metrowerk C Compiler 2.0/base to compile matching files. We cannot distribute the correct compiler here so join the PRET discord and download the pinned mwccarm.zip zip in #pokediamond and extract it to tools/. Run each of the executables so they ask for a license.dat and provide the one in the rar (it may also ask for it when compiling). This only needs to be done once.
In the future, a GCC option will be available so MWCC is not required to build, however it is required for a matching ROM.
-#### 3. Build ROM
+### 3. Dependencies
-Run `make` to build the ROM. \ No newline at end of file
+#### Linux
+
+Building the ROM requires the following packages:
+
+* make
+* git
+* build-essentials
+* binutils-arm-none-eabi
+* wine (to run the mwcc executables)
+
+NOTE: If you are using Arch/Manjaro or Void you will only need base-devel instead of build-essentials or make or git. You will still need wine.
+
+Also, if you are using WSL on Windows, please pass NOWINE=1 when compiling, and wine is not necessary for a WSL environment.
+
+#### Windows
+
+Before following the respective guides, please install devkitARM and ensure the DEVKITPRO and DEVKITARM variables are added to bashrc such that:
+
+Msys2:
+export DEVKITPRO=C:/devkitPro
+export DEVKITARM=${DEVKITPRO}/devkitARM
+
+Cygwin:
+export DEVKITPRO=/cygdrive/c/devkitPro
+export DEVKITARM=${DEVKITPRO}/devkitARM
+
+You will still require the following packages:
+
+* make
+* git
+* build-essentials
+
+Install them using either the Cygwin package manager or using pacman on Msys2.
+
+### 4. Build ROM
+
+Run `make` to build the ROM.
diff --git a/Makefile b/Makefile
index cd4882a9..a61690bd 100644
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,33 @@
# Makefile to build Pokemon Diamond image
+.PHONY: clean tidy all default patch_mwasmarm
+
+# Try to include devkitarm if installed
+TOOLCHAIN := $(DEVKITARM)
+
+ifneq (,$(wildcard $(TOOLCHAIN)/base_tools))
+include $(TOOLCHAIN)/base_tools
+endif
+
### Default target ###
default: all
+# If you are using WSL, it is recommended you build with NOWINE=1.
+NOWINE ?= 0
+
+ifeq ($(OS),Windows_NT)
+EXE := .exe
+WINE :=
+else
+EXE :=
+WINE := wine
+endif
+
+ifeq ($(NOWINE),1)
+WINE :=
+endif
+
################ Target Executable and Sources ###############
BUILD_DIR := build
@@ -24,12 +48,20 @@ S_FILES := $(foreach dir,$(ASM_DIRS),$(wildcard $(dir)/*.s))
# Object files
O_FILES := $(foreach file,$(C_FILES),$(BUILD_DIR)/$(file:.c=.o)) \
$(foreach file,$(S_FILES),$(BUILD_DIR)/$(file:.s=.o)) \
+
+################### Universal Dependencies ###################
+
+# Make tools if out of date
+DUMMY != make -s -C tools/mwasmarm_patcher >&2 || echo FAIL
+ifeq ($(DUMMY),FAIL)
+ $(error Failed to build tools)
+endif
##################### Compiler Options #######################
MWCCVERSION := 2.0/base
-CROSS := arm-linux-gnueabi-
+CROSS := arm-none-eabi-
MWCCARM := tools/mwccarm/$(MWCCVERSION)/mwccarm.exe
# Argh... due to EABI version shenanigans, we can't use GNU LD to link together
@@ -40,10 +72,10 @@ MWCCARM := tools/mwccarm/$(MWCCVERSION)/mwccarm.exe
MWLDARM := tools/mwccarm/$(MWCCVERSION)/mwldarm.exe
MWASMARM := tools/mwccarm/$(MWCCVERSION)/mwasmarm.exe
-AS := $(MWASMARM)
-CC := $(MWCCARM)
+AS := $(WINE) $(MWASMARM)
+CC := $(WINE) $(MWCCARM)
CPP := cpp -P
-LD := $(MWLDARM)
+LD := $(WINE) $(MWLDARM)
AR := $(CROSS)ar
OBJDUMP := $(CROSS)objdump
OBJCOPY := $(CROSS)objcopy
@@ -51,22 +83,29 @@ OBJCOPY := $(CROSS)objcopy
# ./tools/mwccarm/2.0/base/mwasmarm.exe -proc arm5te asm/arm9_thumb.s -o arm9.o
ASFLAGS = -proc arm5te
CFLAGS = -O4,p -proc v5te -thumb -fp soft -lang c -Cpp_exceptions off
-LDFLAGS = -nodead -w off -proc v5te -interworking
+LDFLAGS = -map -nodead -w off -proc v5te -interworking
####################### Other Tools #########################
# DS TOOLS
TOOLS_DIR = tools
SHA1SUM = sha1sum
+MWASMARM_PATCHER = tools/mwasmarm_patcher/mwasmarm_patcher$(EXE)
######################### Targets ###########################
-all: $(ROM)
+all: patch_mwasmarm $(ROM)
@$(SHA1SUM) -c $(TARGET).sha1
-clean:
+clean: tidy
+ make -C tools/mwasmarm_patcher clean
+
+tidy:
$(RM) -r $(BUILD_DIR)
+patch_mwasmarm:
+ $(MWASMARM_PATCHER) $(MWASMARM)
+
ALL_DIRS := $(BUILD_DIR) $(addprefix $(BUILD_DIR)/,$(SRC_DIRS) $(ASM_DIRS))
$(BUILD_DIR)/%.o: %.c
@@ -75,7 +114,7 @@ $(BUILD_DIR)/%.o: %.c
$(BUILD_DIR)/%.o: %.s
$(AS) $(ASFLAGS) $< -o $@
-$(BUILD_DIR)/$(LD_SCRIPT): $(LD_SCRIPT)
+$(BUILD_DIR)/$(LD_SCRIPT): $(LD_SCRIPT) undefined_syms.txt
$(CPP) $(VERSION_CFLAGS) -MMD -MP -MT $@ -MF $@.d -I include/ -I . -DBUILD_DIR=$(BUILD_DIR) -o $@ $<
$(ELF): $(O_FILES) $(BUILD_DIR)/$(LD_SCRIPT)
diff --git a/tools/mwasmarm_patcher/.gitignore b/tools/mwasmarm_patcher/.gitignore
new file mode 100644
index 00000000..f03366b2
--- /dev/null
+++ b/tools/mwasmarm_patcher/.gitignore
@@ -0,0 +1 @@
+mwasmarm_patcher
diff --git a/tools/mwasmarm_patcher/Makefile b/tools/mwasmarm_patcher/Makefile
new file mode 100644
index 00000000..df3f5c20
--- /dev/null
+++ b/tools/mwasmarm_patcher/Makefile
@@ -0,0 +1,11 @@
+.PHONY: all clean
+
+CC := gcc
+CFLAGS := -O3
+
+all: mwasmarm_patcher
+
+clean: ; rm -f mwasmarm_patcher$(EXE)
+
+mwasmarm_patcher: mwasmarm_patcher.c
+ $(CC) $(CFLAGS) -o $@ $<
diff --git a/tools/mwasmarm_patcher/mwasmarm_patcher.c b/tools/mwasmarm_patcher/mwasmarm_patcher.c
new file mode 100644
index 00000000..cfea660c
--- /dev/null
+++ b/tools/mwasmarm_patcher/mwasmarm_patcher.c
@@ -0,0 +1,188 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+// mwasmarm patcher v1.1
+// Patches the Metrowerk C compiler assembler to stop the line ending bug.
+
+// Changelog:
+// v1.1: Added patch definitions and looped over them to find the matching
+// definition as well as the version.
+
+struct PatchDef {
+ char *version;
+ char *sha1before;
+ char *sha1after;
+ int offsetPatch;
+ int newByte;
+};
+
+struct PatchDef gPatchDefs[] = {
+ // mwasmarm 1.2/base definition
+ {
+ "mwasmarm 1.2/base",
+ "87f942cc0a0e90e73550d8d6f3fffcdeb5f69fa5",
+ "2f1ccff22eaa443bb79235ca6477d3b86bdfd7e4",
+ 0x57614,
+ 0x5
+ },
+ // mwasmarm 2.0/base definition
+ {
+ "mwasmarm 2.0/base",
+ "9d63877c776245129b4727b41d3e9e63cfc9cd28",
+ "f5dea73bf90791e104cb59458bebae8b08a55484",
+ 0x57644,
+ 0x5
+ },
+ // mwasmarm 2.0/sp2p4 definition
+ {
+ "mwasmarm 2.0/sp2p4",
+ "448cb0c7f1ace4393e9a9562f819f7a9f049be83",
+ "c82161527277b991a1b77e14617a93bcd19cf95c",
+ 0x57834,
+ 0x5
+ },
+ {
+ }
+};
+
+void sha1_process_block (const unsigned char * block, uint32_t * state);
+
+// Credit to ax6 for implementation
+unsigned char * calculate_sha1 (const void * data, unsigned length) {
+ uint32_t state[5] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0};
+ const char * current;
+ unsigned remaining;
+ for (current = data, remaining = length; remaining >= 64; current += 64, remaining -= 64) sha1_process_block(current, state);
+ // technically only {0} is necessary, but better safe than sorry
+ unsigned char last_block[64] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ memcpy(last_block, current, remaining);
+ last_block[remaining] = 0x80;
+ if (remaining >= 56) {
+ sha1_process_block(last_block, state);
+ memset(last_block, 0, 64);
+ }
+ unsigned long long bit_length = ((unsigned long long) length) << 3;
+ for (remaining = 5; remaining; remaining --) {
+ last_block[58 + remaining] = bit_length;
+ bit_length >>= 8;
+ }
+ sha1_process_block(last_block, state);
+ unsigned char * result = malloc(20);
+ for (remaining = 0; remaining < 20; remaining ++) result[remaining] = state[remaining >> 2] >> ((~remaining & 3) << 3);
+ return result;
+}
+
+static inline unsigned sha1_rotate (unsigned value, unsigned count) {
+ return (value << count) | (value >> (32 - count));
+}
+
+void sha1_process_block (const unsigned char * block, uint32_t * state) {
+ uint32_t words[80];
+ unsigned pos, temp, count, a, b, c, d, e;
+ // constants used by SHA-1; they are actually simply the square roots of 2, 3, 5 and 10 as a fixed-point number (2.30 format)
+ const uint32_t hash_constants[4] = {0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6};
+ memset(words, 0, 16 * sizeof(uint32_t));
+ for (pos = 0; pos < 64; pos ++) words[pos >> 2] = (words[pos >> 2] << 8) | block[pos];
+ for (pos = 16; pos < 80; pos ++) words[pos] = sha1_rotate(words[pos - 3] ^ words[pos - 8] ^ words[pos - 14] ^ words[pos - 16], 1);
+ a = *state;
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ for (pos = 0; pos < 4; pos ++) for (count = 0; count < 20; count ++) {
+ temp = sha1_rotate(a, 5) + e + words[pos * 20 + count] + hash_constants[pos];
+ switch (pos) {
+ case 0:
+ temp += (b & c) | (~b & d);
+ break;
+ case 2:
+ temp += (b & c) | (b & d) | (c & d);
+ break;
+ default:
+ temp += b ^ c ^ d;
+ }
+ e = d;
+ d = c;
+ c = sha1_rotate(b, 30);
+ b = a;
+ a = temp;
+ }
+ *state += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+}
+
+void fatal_printf(char *str, ...) {
+ va_list args;
+ va_start(args, str);
+ vprintf(str, args);
+ va_end(args);
+ exit(1);
+}
+
+// return size in bytes
+int get_file_size (FILE * fp) {
+ int curpos = ftell(fp);
+ fseek(fp, 0, SEEK_END);
+ int result = ftell(fp);
+ fseek(fp, curpos, SEEK_SET);
+ return result;
+}
+
+#define SHA_DIGEST_LENGTH 20
+
+void print_help(void) {
+ printf("mwasmarm patcher usage: input (example: mwasmarm_patcher mwasmarm.exe)\n");
+}
+
+int main(int argc, char *argv[]) {
+ if (argc != 2) {
+ print_help();
+ return 1;
+ } else {
+ // Open the file and sha1 read it.
+ FILE *f = fopen(argv[1], "rb+");
+ if(f == NULL) {
+ fatal_printf("ERROR: No file detected\n");
+ }
+ int fsize = get_file_size(f);
+ unsigned char *string = malloc(fsize + 1);
+ if(string == NULL) {
+ fatal_printf("ERROR: Failed to allocate string variable\n");
+ }
+ int readvar = fread(string, 1, fsize, f); // var to surpress warning
+
+ // Check if sha1 matches either known assembler hashes.
+ unsigned char *sha1 = calculate_sha1(string, fsize);
+ if(sha1 == NULL) {
+ fatal_printf("ERROR: Failed to retrieve sha1 hash\n");
+ }
+ free(string);
+
+ unsigned char buf[SHA_DIGEST_LENGTH*2];
+ for (int i=0; i < SHA_DIGEST_LENGTH; i++) {
+ sprintf((unsigned char*)&(buf[i*2]), "%02x", sha1[i]);
+ }
+
+ for(int i = 0; gPatchDefs[i].sha1before != NULL; i++) {
+ // check if already patched for the current loop.
+ if(!strcmp(buf, gPatchDefs[i].sha1after)) {
+ printf("Supported patched version detected (%s): no action needed\n", gPatchDefs[i].version);
+ return 0;
+ } else if(!strcmp(buf, gPatchDefs[i].sha1before)) {
+ fseek(f, gPatchDefs[i].offsetPatch, SEEK_SET);
+ fputc(gPatchDefs[i].newByte, f);
+ printf("Supported unpatched version detected (%s): assembler patched\n", gPatchDefs[i].version);
+ return 0;
+ }
+ }
+ fatal_printf("ERROR: Unsupported mwasmarm.exe version\n");
+ }
+ return 0;
+}