diff --git a/tools/cw/2.0/sp2/ELFIO.dll b/tools/cw/2.0/sp2/ELFIO.dll new file mode 100644 index 0000000000..c84adf0e18 Binary files /dev/null and b/tools/cw/2.0/sp2/ELFIO.dll differ diff --git a/tools/cw/2.0/sp2/MSL_All-DLL80_x86.dll b/tools/cw/2.0/sp2/MSL_All-DLL80_x86.dll new file mode 100644 index 0000000000..a733bbb982 Binary files /dev/null and b/tools/cw/2.0/sp2/MSL_All-DLL80_x86.dll differ diff --git a/tools/cw/2.0/sp2/lmgr8c.dll b/tools/cw/2.0/sp2/lmgr8c.dll new file mode 100644 index 0000000000..35bc6be4a8 Binary files /dev/null and b/tools/cw/2.0/sp2/lmgr8c.dll differ diff --git a/tools/cw/2.0/sp2/mwasmarm.exe b/tools/cw/2.0/sp2/mwasmarm.exe new file mode 100644 index 0000000000..8797554d89 Binary files /dev/null and b/tools/cw/2.0/sp2/mwasmarm.exe differ diff --git a/tools/cw/2.0/sp2/mwccarm.exe b/tools/cw/2.0/sp2/mwccarm.exe new file mode 100644 index 0000000000..b2159431db Binary files /dev/null and b/tools/cw/2.0/sp2/mwccarm.exe differ diff --git a/tools/cw/2.0/sp2/mwldarm.exe b/tools/cw/2.0/sp2/mwldarm.exe new file mode 100644 index 0000000000..8608e71aa6 Binary files /dev/null and b/tools/cw/2.0/sp2/mwldarm.exe differ diff --git a/tools/cw/2.0/sp2p2/ELFIO.dll b/tools/cw/2.0/sp2p2/ELFIO.dll new file mode 100644 index 0000000000..c84adf0e18 Binary files /dev/null and b/tools/cw/2.0/sp2p2/ELFIO.dll differ diff --git a/tools/cw/2.0/sp2p2/MSL_All-DLL80_x86.dll b/tools/cw/2.0/sp2p2/MSL_All-DLL80_x86.dll new file mode 100644 index 0000000000..a733bbb982 Binary files /dev/null and b/tools/cw/2.0/sp2p2/MSL_All-DLL80_x86.dll differ diff --git a/tools/cw/2.0/sp2p2/lmgr8c.dll b/tools/cw/2.0/sp2p2/lmgr8c.dll new file mode 100644 index 0000000000..35bc6be4a8 Binary files /dev/null and b/tools/cw/2.0/sp2p2/lmgr8c.dll differ diff --git a/tools/cw/2.0/sp2p2/mwasmarm.exe b/tools/cw/2.0/sp2p2/mwasmarm.exe new file mode 100644 index 0000000000..65aea82eee Binary files /dev/null and b/tools/cw/2.0/sp2p2/mwasmarm.exe differ diff --git a/tools/cw/2.0/sp2p2/mwccarm.exe b/tools/cw/2.0/sp2p2/mwccarm.exe new file mode 100644 index 0000000000..8454b8a32d Binary files /dev/null and b/tools/cw/2.0/sp2p2/mwccarm.exe differ diff --git a/tools/cw/2.0/sp2p2/mwldarm.exe b/tools/cw/2.0/sp2p2/mwldarm.exe new file mode 100644 index 0000000000..8608e71aa6 Binary files /dev/null and b/tools/cw/2.0/sp2p2/mwldarm.exe differ diff --git a/tools/cw/license.dat b/tools/cw/license.dat new file mode 100644 index 0000000000..02d416c700 --- /dev/null +++ b/tools/cw/license.dat @@ -0,0 +1,12 @@ +############################################################### +# CodeWarrior Development Studio for Nintendo DS v1.1 # +############################################################### + +FEATURE Win32_CWIDE_Unlimited metrowks 5.5 permanent uncounted \ + D683F3FF3C1E HOSTID=ANY TS_OK +FEATURE Win32_Plugins_ARM metrowks 2.0 permanent uncounted \ + 5DDA68DFE129 HOSTID=ANY TS_OK +FEATURE Win32_Plugins_ARM_Nitro metrowks 1.0 permanent uncounted \ + 0CF6F6A5792E HOSTID=ANY TS_OK +FEATURE Win32_Plugins_ARM_DBG metrowks 2.0 permanent uncounted \ + C81996650D7D HOSTID=ANY TS_OK diff --git a/tools/fixdep/fixdep.py b/tools/fixdep/fixdep.py new file mode 100644 index 0000000000..5264ba15cc --- /dev/null +++ b/tools/fixdep/fixdep.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 + +import argparse +import os +import platform +import re + + +class PathRecord: + def __init__(self, id: int, path: str): + self.id = id + self.path = path + + def fix(self): + root = '/mnt/c/' if is_wsl_accessing_windows() else '/' + self.path = re.sub(r'[A-Z]:[/\\]', root, self.path).replace('\\', '/') + + def to_bytearray(self) -> bytearray: + byte_array = bytearray() + + byte_array.extend((len(self.path) + len(self.path) % 4 + 0x4).to_bytes(4, byteorder='little')) + byte_array.extend(self.path.encode('utf-8')) + byte_array.extend(bytearray(len(self.path) % 4)) + byte_array.extend((~self.id).to_bytes(4, byteorder='little', signed=True)) + + return byte_array + + +class DepsRecord: + def __init__(self, id: int, data: bytes): + self.id = id + self.data = data + + def to_bytearray(self) -> bytearray: + byte_array = bytearray(self.id.to_bytes(4, 'little')) + byte_array.extend(self.data) + return byte_array + + +def is_wsl_accessing_windows() -> bool: + return ("microsoft" in platform.uname()[2].lower() and os.path.realpath(os.path.abspath(__file__)).startswith("/mnt/")) + + +def main(): + if platform.system().lower() == 'windows': + return + + args = parse_args() + with open(args.depfile, 'rb+') as ninja_deps: + assert ninja_deps.read(0xC).decode('utf-8') == '# ninjadeps\n' + ninja_version = int.from_bytes(ninja_deps.read(0x4), 'little') + + records = list() + path_counter = 0 + while entry_key_bytes := ninja_deps.read(0x4): + entry_key = int.from_bytes(entry_key_bytes, 'little') + if (entry_key >> 31) & 1: + # deps record + entry_len = entry_key & 0xFFFF + records.append(DepsRecord(entry_key, ninja_deps.read(entry_len))) + else: + # path record + path = ninja_deps.read(entry_key - 0x4).decode('utf-8').rstrip() + records.append(PathRecord(path_counter, path)) + path_counter += 1 + ninja_deps.read(0x4) # Skip checksum + + ninja_deps.seek(0x10) + + for record in records: + if isinstance(record, PathRecord): + record.fix() + ninja_deps.write(record.to_bytearray()) + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("depfile", type=str, help="Path to the .ninja_deps file") + return parser.parse_args() + + +if __name__ == '__main__': + main() diff --git a/tools/fixdep/meson.build b/tools/fixdep/meson.build new file mode 100644 index 0000000000..1a30d41215 --- /dev/null +++ b/tools/fixdep/meson.build @@ -0,0 +1 @@ +fixdep_py = find_program('fixdep.py', native: true) \ No newline at end of file diff --git a/tools/fixrom/.gitignore b/tools/fixrom/.gitignore deleted file mode 100644 index 8ab48d98f9..0000000000 --- a/tools/fixrom/.gitignore +++ /dev/null @@ -1 +0,0 @@ -fixrom diff --git a/tools/fixrom/fixrom.c b/tools/fixrom/fixrom.c index 6c5f5da12e..51606d4b40 100644 --- a/tools/fixrom/fixrom.c +++ b/tools/fixrom/fixrom.c @@ -95,7 +95,7 @@ static uint16_t Calc_CRC16(uint8_t * data, size_t length, uint16_t crc) crc >>= 4; crc ^= y; y = CrcTable[(x >> bit) & 15]; - crc ^= y; + crc ^= y; bit += 4; if (bit == 16) { diff --git a/tools/fixrom/meson.build b/tools/fixrom/meson.build new file mode 100644 index 0000000000..4227fe29d0 --- /dev/null +++ b/tools/fixrom/meson.build @@ -0,0 +1 @@ +fixrom_exe = executable('fixrom', 'fixrom.c', native: true) \ No newline at end of file diff --git a/tools/maketools/makebanner.exe b/tools/maketools/makebanner.exe new file mode 100644 index 0000000000..ea38ce4310 Binary files /dev/null and b/tools/maketools/makebanner.exe differ diff --git a/tools/maketools/makelcf.exe b/tools/maketools/makelcf.exe new file mode 100644 index 0000000000..a5e812a048 Binary files /dev/null and b/tools/maketools/makelcf.exe differ diff --git a/tools/maketools/makerom.exe b/tools/maketools/makerom.exe new file mode 100644 index 0000000000..a58d3a5e04 Binary files /dev/null and b/tools/maketools/makerom.exe differ diff --git a/tools/meson.build b/tools/meson.build new file mode 100644 index 0000000000..6bcfc2cce5 --- /dev/null +++ b/tools/meson.build @@ -0,0 +1,9 @@ +# Native tools +subdir('fixdep') +subdir('fixrom') +subdir('postconf') + +# Prebuilt tools +makebanner_exe = find_program('makebanner', native: true) +makelcf_exe = find_program('makelcf', native: true) +makerom_exe = find_program('makerom', native: true) \ No newline at end of file diff --git a/tools/mwasmarm_patcher/.gitignore b/tools/mwasmarm_patcher/.gitignore deleted file mode 100644 index c9b889c594..0000000000 --- a/tools/mwasmarm_patcher/.gitignore +++ /dev/null @@ -1 +0,0 @@ -mwasmarm_patcher diff --git a/tools/mwasmarm_patcher/mwasmarm_patcher.c b/tools/mwasmarm_patcher/mwasmarm_patcher.c deleted file mode 100644 index 1d7aa2044f..0000000000 --- a/tools/mwasmarm_patcher/mwasmarm_patcher.c +++ /dev/null @@ -1,254 +0,0 @@ -#include -#include -#include -#include -#include - -// mwasmarm patcher v1.2 -// Patches the Metrowerk C compiler assembler to stop the line ending bug and the 0x400 incbin bug. - -// Changelog: -// v1.1: Added patch definitions and looped over them to find the matching -// definition as well as the version. -// v1.2: Switched to array system for applying multiple patches for compiler -// versions and added 0x400 incbin fix for each version. - -struct PatchPair { - int offsetPatch; - int newByte; -}; - -struct PatchDef { - char *version; - char *sha1before; - char *sha1after; - struct PatchPair *patches; -}; - -// Patch definitions for each of the respective assembler versions. -struct PatchPair g12BasePatches[] = { - { 0x57614, 0x5 }, - { 0xD47, 0x8D }, - {0} -}; - -struct PatchPair g20BasePatches[] = { - { 0x57644, 0x5 }, - { 0xD47, 0x8D }, - {0} -}; - -struct PatchPair g20sp2p4Patches[] = { - { 0x57834, 0x5 }, - { 0xD47, 0x8D }, - {0} -}; - -// Table of definitions for each assembler version -struct PatchDef gPatchDefs[] = { - // mwasmarm 1.2/base definition - { - "mwasmarm 1.2/base", - "87f942cc0a0e90e73550d8d6f3fffcdeb5f69fa5", - "3395ac5decf49135d892e93a3e6dd38676025983", - g12BasePatches - }, - // mwasmarm 2.0/base definition - { - "mwasmarm 2.0/base", - "9d63877c776245129b4727b41d3e9e63cfc9cd28", - "ef75c3fb9f8d90cb4881386c41d8dc3ab4de7153", - g20BasePatches - }, - // mwasmarm 2.0/sp2p4 definition - { - "mwasmarm 2.0/sp2p4", - "448cb0c7f1ace4393e9a9562f819f7a9f049be83", - "caa84dd90b1987ab7b42749bd5c9dcfdcfef59f3", - g20sp2p4Patches - }, - {0} -}; - -// --------------------------------------------------------- -// Credit to ax6 for implementation of sha1 hash functions -// --------------------------------------------------------- - -void sha1_process_block (const unsigned char * block, uint32_t * state); - -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((const uint8_t *)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; -} - -// --------------------------------------------------------- -// ax6 code end -// --------------------------------------------------------- - -__attribute__((format(printf, 1, 2))) -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("Usage:\n" - "\tmwasmarm_patcher [OPTIONS] FILENAME\n\n" - "Arguments:\n" - "\tFILENAME: path to MWASMARM.exe program\n\n" - "OPTIONS:\n" - "\t-q/--quietly: Suppress verbose output\n" - "\t-h/--help: Print this message and exit\n"); -} - -int main(int argc, char *argv[]) { - int quietly = 0; - char* filename = NULL; - for (int i = 1; i < argc; i++) - { - if (argv[i][0] == '-') { - if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quietly") == 0) - quietly = 1; - else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { - print_help(); - exit(0); - } - else - { - print_help(); - fatal_printf("Unrecognized option: %s\n", argv[i]); - } - } else if (filename != NULL) { - print_help(); - fatal_printf("Excess filename supplied\n"); - } - else - filename = argv[i]; - } - if (filename == NULL) { - print_help(); - fatal_printf("Missing required argument: filename\n"); - } else { - // Open the file and read it's sha1 hash. - FILE *f = fopen(filename, "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); - - char buf[(SHA_DIGEST_LENGTH*2)+1]; - for (int i=0; i < SHA_DIGEST_LENGTH; i++) { - sprintf(&(buf[i*2]), "%02x", sha1[i]); - } - free(sha1); - - // loop over each patch definition to attempt to locate a supported version and, if - // needed, apply the patch definitions. - for (int i = 0; gPatchDefs[i].sha1before != NULL; i++) { - // check if already patched for the current loop. - if (!strcmp(buf, gPatchDefs[i].sha1after)) { - if (!quietly) printf("Supported patched version detected (%s): no action needed\n", gPatchDefs[i].version); - return 0; - } else if(!strcmp(buf, gPatchDefs[i].sha1before)) { - // we found an unpatched version: apply the patches. - for (int j = 0; gPatchDefs[i].patches[j].offsetPatch != 0; j++) { - fseek(f, gPatchDefs[i].patches[j].offsetPatch, SEEK_SET); - fputc(gPatchDefs[i].patches[j].newByte, f); - } - if (!quietly) printf("Supported unpatched version detected (%s): assembler patched\n", gPatchDefs[i].version); - return 0; - } - } - // Unable to locate supported version, quitting - fatal_printf("ERROR: Unsupported mwasmarm.exe version\n"); - } - return 0; -} diff --git a/tools/postconf/meson.build b/tools/postconf/meson.build new file mode 100644 index 0000000000..eea6eb8635 --- /dev/null +++ b/tools/postconf/meson.build @@ -0,0 +1 @@ +postconf_py = find_program('postconf.py', native: true) \ No newline at end of file diff --git a/tools/postconf/postconf.py b/tools/postconf/postconf.py new file mode 100644 index 0000000000..99bd661910 --- /dev/null +++ b/tools/postconf/postconf.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 + +import os +import platform +import re + +BUILD_DIRECTORY = os.environ["MESON_BUILD_ROOT"] +SOURCE_DIRECTORY = os.environ["MESON_SOURCE_ROOT"] +COMPILER_RULE_PATTERN = r'rule c_COMPILER[\r\n]+(?:\s\w+ =.+[\r\n]+){4}' + + +def add_compiler_rules(fileString: str) -> str: + rule = re.search(COMPILER_RULE_PATTERN, fileString).group(0) + fileString = fileString.replace(rule, rule + rule.replace('sp2p2', 'sp2').replace('c_COMPILER', 'c_COMPILER_NitroSDK')) + fileString = re.sub(r'(build lib/external/NitroSDK.+?c_COMPILER\b)', r'\1_NitroSDK', fileString) + return fileString + + +def backslash_to_forward_slash(fileString: str) -> str: + return fileString.replace('\\\\', '/') + + +def fix_static_libs(fileString: str) -> str: + '''Replace single quotes around linked libraries with double quotes''' + return fileString.replace("'lib", '"lib').replace(".a'", '.a"') + + +def is_wsl_accessing_windows() -> bool: + return ("microsoft" in platform.uname()[2].lower() and os.path.realpath(os.path.abspath(__file__)).startswith("/mnt/")) + + +def main(): + BUILD_NINJA = f'{BUILD_DIRECTORY}/build.ninja' + COMPILE_COMMANDS = f'{BUILD_DIRECTORY}/compile_commands.json' + + with open(BUILD_NINJA, 'r') as build_ninja_in, open(COMPILE_COMMANDS, 'r') as compile_commands_in: + build_ninja_string = build_ninja_in.read() + compile_commands_string = compile_commands_in.read() + + # build.ninja edits + build_ninja_string = add_compiler_rules(build_ninja_string) + build_ninja_string = backslash_to_forward_slash(build_ninja_string) + build_ninja_string = fix_static_libs(build_ninja_string) + build_ninja_string = nasm_to_asm(build_ninja_string) + build_ninja_string = silence_static_linking_warnings(build_ninja_string) + + # compile_commands.json edits + compile_commands_string = backslash_to_forward_slash(compile_commands_string) + + # For WSL accessing Windows, paths to PCH input files must be relative + if is_wsl_accessing_windows(): + build_ninja_string = relativize_pch_paths(build_ninja_string) + compile_commands_string = relativize_pch_paths(compile_commands_string) + + with open(BUILD_NINJA, 'w') as build_ninja_out, open(COMPILE_COMMANDS, 'w') as compile_commands_out: + build_ninja_out.write(build_ninja_string) + compile_commands_out.write(compile_commands_string) + + +def nasm_to_asm(fileString: str) -> str: + return fileString.replace('Nasm', 'ASM') + + +def rename_objects(fileString: str) -> str: + '''Remove filepaths from object names''' + return re.sub(r'(?:src|asm)_(?:overlay\d+_)*(\w+\.[cs]\.[do])', r'\1', fileString) + + +def relativize_pch_paths(fileString: str) -> str: + '''Make paths to headers to be precompiled relative (for WSL)''' + return re.sub(r'c_PCH [\w/]+?lib', r'c_PCH ../lib', fileString) + + +def silence_static_linking_warnings(fileString: str) -> str: + return re.sub(r'mwldarm\.exe(")* \$LINK_ARGS', r'mwldarm.exe\1 $LINK_ARGS -w nocmdline', fileString) + + +if __name__ == '__main__': + main() \ No newline at end of file