diff --git a/tools/compress/.gitignore b/tools/compress/.gitignore deleted file mode 100644 index b0512009..00000000 --- a/tools/compress/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -compress -compress.exe diff --git a/tools/compress/Makefile b/tools/compress/Makefile deleted file mode 100644 index 8b42a0bb..00000000 --- a/tools/compress/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -CC := gcc -CFLAGS := -g - -ifneq ($(DEBUG),1) - CFLAGS += -O2 -DNDEBUG -endif - -ifeq ($(OS),Windows_NT) - OUTFILE := compress.exe -else - OUTFILE := compress -endif - -.PHONY: all clean - -all: $(OUTFILE) - -clean: - rm -f $(OUTFILE) - -$(OUTFILE): main.c - $(CC) $(CFLAGS) -o $(OUTFILE) main.c diff --git a/tools/compress/main.c b/tools/compress/main.c deleted file mode 100644 index 536ee520..00000000 --- a/tools/compress/main.c +++ /dev/null @@ -1,287 +0,0 @@ -// Compresses files using a backwards LZ77 algorithm. -// This is used to compress code after linking so that the compressed code matches the base ROM. - -#include -#include -#include -#include -#include - -#define VERSION "1.0" - -#ifndef min -# define min(a,b) ((a) < (b) ? (a) : (b)) -#endif - -#define WRITE16(buf,val) do { (buf)[0] = (val) & 0xFF; (buf)[1] = ((val) >> 8) & 0xFF; } while (0) -#define WRITE24(buf,val) do { (buf)[0] = (val) & 0xFF; (buf)[1] = ((val) >> 8) & 0xFF; (buf)[2] = ((val) >> 16) & 0xFF; } while (0) -#define WRITE32(buf,val) do { (buf)[0] = (val) & 0xFF; (buf)[1] = ((val) >> 8) & 0xFF; (buf)[2] = ((val) >> 16) & 0xFF; (buf)[3] = ((val) >> 24) & 0xFF; } while (0) -#define READ16(buf) ((buf)[0] | ((buf)[1] << 8)) -#define READ24(buf) ((buf)[0] | ((buf)[1] << 8) | ((buf)[2] << 16)) -#define READ32(buf) ((buf)[0] | ((buf)[1] << 8) | ((buf)[2] << 16) | ((buf)[3] << 24)) - -#define LEN_BITS 4 -#define DIST_BITS 12 -#define MIN_SUBSEQ 3 - -#define LEN_MASK ((1 << (LEN_BITS)) - 1) -#define DIST_MASK ((1 << (DIST_BITS)) - 1) - -#define MAX_SUBSEQ ((MIN_SUBSEQ) + LEN_MASK) -#define LOOKAHEAD (1 << (DIST_BITS)) - -typedef struct { - uint8_t *pos; - uint32_t totalBytesSaved; - uint8_t flags; -} BlockInfo; - -bool FindSubsequence(const uint8_t *buf, const uint8_t *start, const uint8_t *end, size_t *pLen, size_t *pDist) { - size_t bestLen = 0; - size_t bestDist = 0; - size_t maxLookahead = min(LOOKAHEAD + MAX_SUBSEQ, end - buf - 1); - for (size_t i = 0; i < maxLookahead; ++i) { - const uint8_t *needle = buf; - const uint8_t *haystack = buf + 1 + i; - if (*needle != *haystack) continue; - size_t len = 0; - while (needle - len >= start && needle[-len] == haystack[-len] && haystack - len > buf && len < MAX_SUBSEQ) { - ++len; - } - size_t dist = haystack - needle - MIN_SUBSEQ; - if (len > bestLen && dist <= DIST_MASK) { - bestLen = len; - bestDist = dist; - } - } - if (bestLen < MIN_SUBSEQ) return false; - *pLen = bestLen; - *pDist = bestDist; - return true; -} - -bool Compress(const uint8_t *src, uint8_t *dst, size_t size, uint8_t **pResult, size_t *pLen, size_t *pNumIdentical) { - const uint8_t *end = src + size; - const uint8_t *read = end; - uint8_t *writeEnd = dst + size; - uint8_t *write = writeEnd - 1; - uint8_t *pFlags = write; - uint8_t flags; - size_t flagCount = 0; - int32_t bytesSaved = 0; - BlockInfo *blockInfos = malloc(((size + 7) / 8) * sizeof(BlockInfo)); - size_t numBlocks = 0; - while (read > src) { - flags <<= 1; - size_t len, dist; - if (FindSubsequence(read - 1, src, end, &len, &dist)) { - // write length-distance pair - size_t pair = (((len - MIN_SUBSEQ) & LEN_MASK) << DIST_BITS) | (dist & DIST_MASK); - read -= len; - write -= 2; - WRITE16(write, pair); - flags |= 1; - bytesSaved += len - 2; - } else { - // write literal - write -= 1; - read -= 1; - *write = *read; - } - flagCount++; - if (flagCount == 8) { - flagCount = 0; - *pFlags = flags; - bytesSaved -= 1; - write -= 1; - pFlags = write; - - blockInfos[numBlocks].pos = write; - blockInfos[numBlocks].totalBytesSaved = bytesSaved; - blockInfos[numBlocks].flags = flags; - numBlocks++; - - flags = 0; - } - } - if (flagCount != 0) { - blockInfos[numBlocks].pos = write; - blockInfos[numBlocks].totalBytesSaved = bytesSaved; - numBlocks++; - - *pFlags = flags << (8 - flagCount); - } - else { - write += 1; - } - size_t numIdentical = 0; - for (int32_t i = 0; i < numBlocks - 1; ++i) { - if (blockInfos[i].totalBytesSaved == bytesSaved) { - size_t flagBytesSaved = 0; - for (; i >= 0; --i, ++flagBytesSaved) { - // Save more bytes by ignoring blocks that have no length-distance pairs in them - if (blockInfos[i].flags != 0) break; - } - numIdentical = blockInfos[i].pos - write; - write += 1; - - // See if it's possible to remove some chunks based on the number of flag bytes saved - flags = blockInfos[i].flags; - for (int32_t j = 0; j < 8 && flagBytesSaved > 0; ++j) { - if ((flags & 0x80) != 0) { - if (flagBytesSaved < 2) break; - numIdentical += 2; - flagBytesSaved -= 2; - } else { - numIdentical += 1; - flagBytesSaved -= 1; - } - flags >>= 1; - } - - memcpy(write, src, numIdentical); - while (write[numIdentical] == read[numIdentical]) { - numIdentical += 1; - } - break; - } - } - free(blockInfos); - *pResult = write; - *pLen = writeEnd - write; - *pNumIdentical = numIdentical; - return true; -} - -bool WriteFooter(FILE *fp, const char *fileName, bool addPadding, uint32_t compressedSize, uint32_t originalSize, uint32_t start, uint32_t numIdentical) { - size_t padding = 0; - if (addPadding) padding = (0x4 - (compressedSize & 0x3)) & 0x3; - for (size_t i = 0; i < padding; ++i) fputc(0xFF, fp); - compressedSize += padding + 8; - uint32_t readOffset = padding + 8; - uint32_t writeOffset = originalSize - compressedSize - start; - compressedSize -= numIdentical; - if (fwrite(&compressedSize, 3, 1, fp) != 1) { - if (fp == stdout) fprintf(stderr, "Failed to write compressed size to stdout\n"); - else fprintf(stderr, "Failed to write compressed size to '%s'\n", fileName); - return false; - } - if (fwrite(&readOffset, 1, 1, fp) != 1) { - if (fp == stdout) fprintf(stderr, "Failed to write read offset to stdout\n"); - else fprintf(stderr, "Failed to write read offset to '%s'\n", fileName); - return false; - } - if (fwrite(&writeOffset, 4, 1, fp) != 1) { - if (fp == stdout) fprintf(stderr, "Failed to write write offset to stdout\n"); - else fprintf(stderr, "Failed to write write offset to '%s'\n", fileName); - return false; - } - return true; -} - -void PrintUsage(const char *program) { - printf( - "compress " VERSION "\n" - "\n" - "Usage: %s [-o OUTFILE] [-s START] [-p] -i INFILE\n" - " -o OUTFILE\tWrites result to file instead of stdout\n" - " -s START \tOffset to start compressing from\n" - " -p \tPad compressed data to 4-alignment\n" - " -i INFILE \tInput file to compress", - program - ); -} - -int main(int argc, const char **argv) { - const char *program = argv[0]; - if (argc == 1) { - PrintUsage(program); - return 0; - } - const char *outFile = NULL; - const char *inFile = NULL; - int start = 0; - bool padCompressedBlock = false; - for (int i = 1; i < argc; ++i) { - if (strcmp(argv[i], "-o") == 0) { - if (++i >= argc) { - fprintf(stderr, "Expected filename after -o\n"); - return 1; - } - outFile = argv[i]; - } else if (strcmp(argv[i], "-s") == 0) { - if (++i >= argc) { - fprintf(stderr, "Expected start offset after -s\n"); - return 1; - } - if (sscanf(argv[i], "%i", &start) != 1) { - fprintf(stderr, "Invalid start offset '%s' could not be parsed\n", argv[i]); - return 1; - } - } else if (strcmp(argv[i], "-p") == 0) { - padCompressedBlock = true; - } else if (strcmp(argv[i], "-i") == 0) { - if (++i >= argc) { - fprintf(stderr, "Expected filename after -i\n"); - return 1; - } - inFile = argv[i]; - } else { - fprintf(stderr, "Unknown option '%s'\n", argv[i]); - return 1; - } - } - if (inFile == NULL) { - PrintUsage(program); - fprintf(stderr, "Please provide an input file to compress, see usage above.\n"); - return 1; - } - - FILE *fpIn = fopen(inFile, "rb"); - if (fpIn == NULL) { - fprintf(stderr, "Failed to open input file '%s'\n", inFile); - return 1; - } - fseek(fpIn, 0, SEEK_END); - size_t inSize = ftell(fpIn); - uint8_t *buf = malloc(inSize); - if (buf == NULL) { - fprintf(stderr, "Failed to allocate input buffer for '%s'\n", inFile); - return 1; - } - fseek(fpIn, 0, SEEK_SET); - if (fread(buf, inSize, 1, fpIn) != 1) { - fprintf(stderr, "Failed to read input file '%s'\n", inFile); - return 1; - } - fclose(fpIn); - - FILE *fpOut = stdout; - if (outFile != NULL) { - fpOut = fopen(outFile, "wb"); - if (fpOut == NULL) { - fprintf(stderr, "Failed to open output file '%s'\n", outFile); - return 1; - } - } - if (start > 0 && fwrite(buf, start, 1, fpOut) != 1) { - if (fpOut == stdout) fprintf(stderr, "Failed to write uncompressed header to stdout\n"); - else fprintf(stderr, "Failed to write uncompressed header to '%s'\n", outFile); - return 1; - } - - uint8_t *outBuf = malloc(inSize - start); - uint8_t *result; - size_t resultLen, numIdentical; - if (!Compress(buf + start, outBuf, inSize - start, &result, &resultLen, &numIdentical)) return 1; - if (fwrite(result, resultLen, 1, fpIn) != 1) { - if (fpOut == stdout) fprintf(stderr, "Failed to write compressed output to stdout\n"); - else fprintf(stderr, "Failed to write compressed output to '%s'\n", outFile); - return 1; - } - if (!WriteFooter(fpOut, outFile, padCompressedBlock, resultLen, inSize, start, numIdentical)) return 1; - free(outBuf); - - if (outFile != NULL) fclose(fpOut); - free(buf); -} diff --git a/tools/rom/.gitignore b/tools/rom/.gitignore deleted file mode 100644 index ee6c2dcd..00000000 --- a/tools/rom/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -extractrom -extractrom.exe -buildrom -buildrom.exe diff --git a/tools/rom/Makefile b/tools/rom/Makefile deleted file mode 100644 index 201c69b0..00000000 --- a/tools/rom/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -CC := gcc -CFLAGS := -g -Wall -I../include - -ifneq ($(DEBUG),1) - CFLAGS += -O2 -DNDEBUG -endif - -ifeq ($(OS),Windows_NT) - EXTRACTFILE := extractrom.exe - BUILDFILE := buildrom.exe -else - EXTRACTFILE := extractrom - BUILDFILE := buildrom - CFLAGS += -fshort-wchar -endif - -.PHONY: all clean - -all: $(EXTRACTFILE) $(BUILDFILE) - -clean: - rm -f $(EXTRACTFILE) - rm -f $(BUILDFILE) - -$(EXTRACTFILE): extract.c - $(CC) $(CFLAGS) -o $(EXTRACTFILE) extract.c - -$(BUILDFILE): build.c - $(CC) $(CFLAGS) -o $(BUILDFILE) build.c diff --git a/tools/rom/build.c b/tools/rom/build.c deleted file mode 100644 index 20eadb0b..00000000 --- a/tools/rom/build.c +++ /dev/null @@ -1,1020 +0,0 @@ -#include -#include -#include -#include - -#include "rom.h" -#include "ph.h" -#include "util.h" -#include "files.h" - -#define VERSION "1.1.1" - -#define BUFFER_SIZE 1024 * 1024 -uint8_t *readBuffer = NULL; - -#define MAX_DIR_SIZE 256 -#define INITIAL_SUBTABLE_SIZE 1024 * 1024 -#define INITIAL_TABLE_SIZE 256 -#define MAX_OVERLAYS 128 - -const uint8_t logo[] = { - 0x24, 0xff, 0xae, 0x51, 0x69, 0x9a, 0xa2, 0x21, 0x3d, 0x84, 0x82, 0x0a, 0x84, 0xe4, 0x09, 0xad, - 0x11, 0x24, 0x8b, 0x98, 0xc0, 0x81, 0x7f, 0x21, 0xa3, 0x52, 0xbe, 0x19, 0x93, 0x09, 0xce, 0x20, - 0x10, 0x46, 0x4a, 0x4a, 0xf8, 0x27, 0x31, 0xec, 0x58, 0xc7, 0xe8, 0x33, 0x82, 0xe3, 0xce, 0xbf, - 0x85, 0xf4, 0xdf, 0x94, 0xce, 0x4b, 0x09, 0xc1, 0x94, 0x56, 0x8a, 0xc0, 0x13, 0x72, 0xa7, 0xfc, - 0x9f, 0x84, 0x4d, 0x73, 0xa3, 0xca, 0x9a, 0x61, 0x58, 0x97, 0xa3, 0x27, 0xfc, 0x03, 0x98, 0x76, - 0x23, 0x1d, 0xc7, 0x61, 0x03, 0x04, 0xae, 0x56, 0xbf, 0x38, 0x84, 0x00, 0x40, 0xa7, 0x0e, 0xfd, - 0xff, 0x52, 0xfe, 0x03, 0x6f, 0x95, 0x30, 0xf1, 0x97, 0xfb, 0xc0, 0x85, 0x60, 0xd6, 0x80, 0x25, - 0xa9, 0x63, 0xbe, 0x03, 0x01, 0x4e, 0x38, 0xe2, 0xf9, 0xa2, 0x34, 0xff, 0xbb, 0x3e, 0x03, 0x44, - 0x78, 0x00, 0x90, 0xcb, 0x88, 0x11, 0x3a, 0x94, 0x65, 0xc0, 0x7c, 0x63, 0x87, 0xf0, 0x3c, 0xaf, - 0xd6, 0x25, 0xe4, 0x8b, 0x38, 0x0a, 0xac, 0x72, 0x21, 0xd4, 0xf8, 0x07, -}; - -uint16_t crcTable[0x100]; -void GenerateCrcTable() { - uint16_t polynomial = 0xa001; - for (size_t i = 0; i < 0x100; ++i) { - uint16_t value = i; - for (size_t j = 0; j < 8; ++j) { - if (value & 1) value = polynomial ^ (value >> 1); - else value >>= 1; - } - crcTable[i] = value; - } -} - -uint16_t Crc(const void *buf, size_t size) { - uint16_t crc = 0xffff; - const uint8_t *data = buf; - for (size_t i = 0; i < size; ++i) { - crc = crcTable[(crc & 0xff) ^ data[i]] ^ (crc >> 8); - } - return crc; -} - -typedef struct { - /* 0000 */ uint32_t subkeys[0x12]; - /* 0048 */ uint32_t sbox[4][0x100]; - /* 1048 */ -} Blowfish; -Blowfish blowfish; - -uint32_t BlowfishF(size_t x) { - uint32_t f; - f = blowfish.sbox[0][(x >> 24) & 0xff]; - f += blowfish.sbox[1][(x >> 16) & 0xff]; - f ^= blowfish.sbox[2][(x >> 8) & 0xff]; - f += blowfish.sbox[3][x & 0xff]; - return f; -} - -void BlowfishEncrypt(uint32_t *pLeft, uint32_t *pRight) { - uint32_t tmp; - uint32_t x = *pRight; - uint32_t y = *pLeft; - for (size_t i = 0; i < 0x10; ++i) { - tmp = x ^ blowfish.subkeys[i]; - x = y ^ BlowfishF(tmp); - y = tmp; - } - *pLeft = x ^ blowfish.subkeys[0x10]; - *pRight = y ^ blowfish.subkeys[0x11]; -} - -void BlowfishApplyCode(uint32_t code[3]) { - BlowfishEncrypt(&code[1], &code[2]); - BlowfishEncrypt(&code[0], &code[1]); - for (size_t i = 0; i < 0x12; ++i) { - uint32_t x = REVERSE32(code[i % 2]); - blowfish.subkeys[i] ^= x; - } - - uint32_t scratch0 = 0; - uint32_t scratch1 = 0; - for (size_t i = 0; i < 0x12; i += 2) { - BlowfishEncrypt(&scratch0, &scratch1); - blowfish.subkeys[i + 0] = scratch1; - blowfish.subkeys[i + 1] = scratch0; - } - for (size_t i = 0; i < 4; ++i) { - for (size_t j = 0; j < 0x100; j += 2) { - BlowfishEncrypt(&scratch0, &scratch1); - blowfish.sbox[i][j + 0] = scratch1; - blowfish.sbox[i][j + 1] = scratch0; - } - } -} - -bool BlowfishInit(const uint8_t *encKey, const Header *pHeader, size_t level) { - memcpy(&blowfish, encKey, sizeof(blowfish)); - - uint32_t code[3]; - memcpy(&code[0], pHeader->gamecode, sizeof(code[0])); - code[1] = code[0] >> 1; - code[2] = code[0] << 1; - BlowfishApplyCode(code); - if (level >= 2) BlowfishApplyCode(code); - if (level >= 3) { - code[1] <<= 1; - code[2] >>= 1; - BlowfishApplyCode(code); - } - return true; -} - -void InitHeader(Header *pHeader, const BuildInfo *info) { - memcpy(&pHeader->title, TITLE, sizeof(pHeader->title)); - memcpy(&pHeader->gamecode, GAMECODE_PREFIX, 3); - pHeader->gamecode[3] = info->region; - memcpy(&pHeader->makercode, "01", sizeof(pHeader->makercode)); - - pHeader->unitcode = 0; - pHeader->encSeedSelect = 0; - pHeader->capacity = 0; - memset(&pHeader->reserved0, 0, sizeof(pHeader->reserved0)); - pHeader->dsRegion = 0; - pHeader->romVersion = 0; - pHeader->autostart = 0; - - pHeader->arm9.offset = 0; - pHeader->arm9.entry = 0; - pHeader->arm9.baseAddr = 0; - pHeader->arm9.size = 0; - - pHeader->arm7.offset = 0; - pHeader->arm7.entry = 0x2380000; - pHeader->arm7.baseAddr = 0x2380000; - pHeader->arm7.size = 0; - - pHeader->fileNames.offset = 0; - pHeader->fileNames.size = 0; - pHeader->fileAllocs.offset = 0; - pHeader->fileAllocs.size = 0; - pHeader->arm9Overlays.offset = 0; - pHeader->arm9Overlays.size = 0; - pHeader->arm7Overlays.offset = 0; - pHeader->arm7Overlays.size = 0; - - pHeader->normalCmdSetting = 0x00416657; - pHeader->key1CmdSetting = 0x081808f8; - pHeader->bannerOffset = 0; - pHeader->secureAreaCrc = 0; - pHeader->secureAreaDelay = 0x0d7e; - pHeader->arm9AutoloadCallback = 0; - pHeader->arm7AutoloadCallback = 0x2380158; - pHeader->secureAreaDisable = 0; - pHeader->romSize = 0; - pHeader->headerSize = sizeof(Header); - pHeader->autoloadBlockInfosOffset = 0; - memset(&pHeader->reserved1, 0, sizeof(pHeader->reserved1)); - pHeader->romEnd = 0; - pHeader->rwEnd = 0; - memset(&pHeader->reserved2, 0, sizeof(pHeader->reserved2)); - memset(&pHeader->reserved3, 0, sizeof(pHeader->reserved3)); - memcpy(&pHeader->logo, logo, sizeof(pHeader->logo)); - pHeader->logoCrc = Crc(logo, sizeof(logo)); - pHeader->headerCrc = 0; - pHeader->debugRomOffset = 0; - pHeader->debugSize = 0; - pHeader->debugRamAddr = 0; - memset(&pHeader->reserved4, 0, sizeof(pHeader->reserved4)); - memset(&pHeader->reserved5, 0, sizeof(pHeader->reserved5)); - memset(&pHeader->reserved6, 0, sizeof(pHeader->reserved6)); - memset(&pHeader->reserved7, 0, sizeof(pHeader->reserved7)); -} - -typedef struct { - uint32_t autoloadBlocksStart; - uint32_t autoloadBlocksEnd; - uint32_t autoloadCode; - uint32_t bssStart; - uint32_t bssEnd; -} Arm9ModuleInfo; - -typedef struct { - uint32_t autoloadCallback; - uint32_t autoloadBlockInfos; - uint32_t entryAddr; - uint32_t baseAddr; - Arm9ModuleInfo moduleInfo; -} Arm9Metadata; - -bool LoadArm9Metadata(Arm9Metadata *pMetadata) { - File file; - if (!FileOpenRead(ARM9_METADATA_FILE, &file)) return false; - if (FileRead(&file, pMetadata, sizeof(*pMetadata), 1) == 0) return false; - FileClose(&file); - return true; -} - -// Reads an entire file into a newly allocated buffer and writes it to *pBuffer if successful. -// If pFileSize != NULL, this function writes the file's size into *pFileSize. -// The buffer can be freed with free(). -bool ReadFileAlloc(const char *filePath, uint8_t **pBuffer, uint32_t *pFileSize) { - File file; - if (!FileOpenRead(filePath, &file)) return false; - size_t size = FileSize(&file); - - uint8_t *buffer = malloc(size); - if (buffer == NULL) FATAL("Failed to allocate buffer for file '%s'\n", filePath); - uint8_t *current = buffer; - uint8_t *end = buffer + size; - - while (current < end) { - size_t bytesRead = FileRead(&file, current, 1, BUFFER_SIZE); - if (bytesRead == 0) return false; - current += bytesRead; - } - FileClose(&file); - - *pBuffer = buffer; - if (pFileSize != NULL) *pFileSize = size; - return true; -} - -bool AppendFile(File *rom, const char *filePath, size_t *pAddress, uint32_t *pFileSize) { - assert(readBuffer != NULL); - - File file; - if (!FileOpenRead(filePath, &file)) return false; - size_t size = FileSize(&file); - - size_t bytesWritten = 0; - while (bytesWritten < size) { - size_t bytesRead = FileRead(&file, readBuffer, 1, BUFFER_SIZE); - if (bytesRead == 0) return false; - if (!FileWrite(rom, readBuffer, bytesRead, 1)) FATAL("Failed to write file '%s' to output ROM\n", filePath); - bytesWritten += bytesRead; - } - FileClose(&file); - - *pAddress += size; - if (pFileSize != NULL) *pFileSize = size; - return true; -} - -bool WriteArm9Program(File *rom, size_t *pAddress, uint32_t *pFileSize, uint32_t *pSecureArea, const Arm9Metadata *metadata) { - size_t address = *pAddress; - - uint8_t *arm9; - uint32_t fileSize; - if (!ReadFileAlloc(ARM9_PROGRAM_FILE, &arm9, &fileSize)) return false; - - // Write module info, see spAutoloadBlockInfosStart in asm/main.s - // This might seem unsafe since the ARM9 program is compressed, but the addresses we're writing to are in the secure area, - // which is uncompressed. - - size_t offset = metadata->autoloadBlockInfos; - // First five values, from spAutoloadBlockInfosStart to spBssEnd - memcpy(&arm9[offset], &metadata->moduleInfo, sizeof(metadata->moduleInfo)); - offset += sizeof(metadata->moduleInfo); - // Last value, sCompressedCodeEnd - uint32_t compressedCodeEnd = 0x2000000 + fileSize; - memcpy(&arm9[offset], &compressedCodeEnd, sizeof(compressedCodeEnd)); - offset += compressedCodeEnd; - - if (!FileWrite(rom, arm9, fileSize, 1)) FATAL("Failed to write ARM9 program\n"); - address += fileSize; - - memcpy(pSecureArea, arm9, SECURE_AREA_SIZE); - free(arm9); - - *pAddress = address; - *pFileSize = fileSize; - return true; -} - -bool Align(size_t alignment, File *rom, size_t *pAddress) { - assert((alignment & (alignment - 1)) == 0); - - size_t mask = alignment - 1; - size_t address = FileOffset(rom); - size_t nextAddr = (address + mask) & ~mask; - char padValue = 0xff; - while (address < nextAddr) { - if (!FileWrite(rom, &padValue, sizeof(padValue), 1)) FATAL("Failed to pad output ROM at address 0x%lx\n", address); - address += sizeof(padValue); - } - *pAddress = address; - return true; -} - -bool WriteArm9OverlayTable( - File *rom, - size_t *pAddress, - Header *pHeader, - const char *overlayDataFile, - OverlayEntry **pEntries, - OverlayData **pData, - size_t *pNumOverlays -) { - size_t address = *pAddress; - - File file; - if (!FileOpenRead(ARM9_OVERLAY_TABLE_FILE, &file)) { - FATAL("Failed to open ARM9 overlay table '" ARM9_OVERLAY_TABLE_FILE "'\n"); - } - size_t tableSize = FileSize(&file); - if (tableSize % sizeof(OverlayEntry) != 0) { - FATAL("ARM9 overlay table has an invalid size (entries must be %ld bytes long)\n", sizeof(OverlayEntry)); - } - size_t numOverlays = tableSize / sizeof(OverlayEntry); - - OverlayEntry *entries = malloc(tableSize); - if (entries == NULL) FATAL("Failed to allocate array for ARM9 overlay table entries"); - if (FileRead(&file, entries, tableSize, 1) == 0) FATAL("Failed to read ARM9 overlay table '" ARM9_OVERLAY_TABLE_FILE "'\n"); - FileClose(&file); - - if (!FileOpenRead(overlayDataFile, &file)) FATAL("Failed to open ARM9 overlay data file '%s'\n", overlayDataFile); - size_t dataSize = FileSize(&file); - if (dataSize != numOverlays * sizeof(OverlayData)) { - FATAL("ARM9 overlay data file has an invalid size (expected %ld overlays with %ld bytes each)\n", numOverlays, sizeof(OverlayData)); - } - - OverlayData *data = malloc(dataSize); - if (data == NULL) FATAL("Failed to allocate array for ARM9 overlay data entries\n"); - if (FileRead(&file, data, dataSize, 1) == 0) FATAL("Failed to read ARM9 overlay data '%s'\n", overlayDataFile); - FileClose(&file); - for (size_t i = 0; i < numOverlays; ++i) { - entries[i].fileId = data[i].fileId; - } - - if (!FileWrite(rom, entries, tableSize, 1)) FATAL("Failed to write ARM9 overlay table\n"); - - *pAddress = address; - *pEntries = entries; - *pData = data; - *pNumOverlays = numOverlays; - pHeader->arm9Overlays.size = tableSize; - return true; -} - -bool WriteArm9OverlayFiles( - File *rom, - size_t *pAddress, - size_t numOverlays, - FatEntry *fatEntries, - OverlayEntry *table, - const OverlayData *data -) { - size_t address = *pAddress; - char fileName[32]; - - if (!ChangeDir(OVERLAYS_SUBDIR)) return false; - - for (size_t ovNum = 0; ovNum < numOverlays; ++ovNum) { - sprintf(fileName, "ov%02ld.lz", ovNum); - if (!Align(512, rom, &address)) return false; - size_t startOffset = address; - uint32_t fileSize = 0; - if (!AppendFile(rom, fileName, &address, &fileSize)) return false; - table[ovNum].compressedSize = fileSize; - table[ovNum].isCompressed = true; - uint32_t fileId = data[ovNum].fileId; - if (fileId >= MAX_OVERLAYS) FATAL("Overlay %ld's file ID (%d) exceeds the maximum %d\n", ovNum, fileId, MAX_OVERLAYS); - fatEntries[fileId].startOffset = startOffset; - fatEntries[fileId].endOffset = address; - } - - if (!ChangeDir("..")) return false; - - *pAddress = address; - return true; -} - -bool RewriteArm9OverlayTable(File *rom, const Header *header, OverlayEntry *entries, size_t numEntries) { - size_t prevAddress = FileOffset(rom); - FileGoTo(rom, header->arm9Overlays.offset); - if (!FileWrite(rom, entries, sizeof(*entries), numEntries)) FATAL("Failed to rewrite ARM9 overlay table\n"); - FileGoTo(rom, prevAddress); - return true; -} - -bool WriteArm9Overlays( - File *rom, - size_t *pAddress, - Header *pHeader, - const char *overlayDataFile, - FatEntry *fatEntries, - size_t *pNumOverlays -) { - size_t address = *pAddress; - - OverlayEntry *entries; - OverlayData *data; - size_t numOverlays; - if (!WriteArm9OverlayTable(rom, &address, pHeader, overlayDataFile, &entries, &data, &numOverlays)) return false; - if (!WriteArm9OverlayFiles(rom, &address, numOverlays, fatEntries, entries, data)) return false; - if (!RewriteArm9OverlayTable(rom, pHeader, entries, numOverlays)) return false; - - free(data); - free(entries); - - *pAddress = address; - *pNumOverlays = numOverlays; - return true; -} - -typedef struct { - FntEntry *table; - uint16_t tableSize; - uint16_t tableMax; - - uint16_t nextFileId; - uint16_t parentId; - - uint8_t *subtable; - size_t subtableSize; - size_t subtableMax; -} FntContext; - -bool GrowFntTable(FntContext *pContext, size_t minEntries) { - FntContext ctx; - memcpy(&ctx, pContext, sizeof(ctx)); - - if (minEntries <= ctx.tableMax) return true; - while (minEntries > ctx.tableMax) { - ctx.tableMax *= 2; - } - - FntEntry *newTable = realloc(ctx.table, ctx.tableMax * sizeof(FntEntry)); - if (newTable == NULL) FATAL("Failed to reallocate FNT table to %d entries\n", ctx.tableMax); - ctx.table = newTable; - - memcpy(pContext, &ctx, sizeof(ctx)); - return true; -} - -bool GrowFntSubtable(FntContext *pContext, size_t growSize) { - FntContext ctx; - memcpy(&ctx, pContext, sizeof(ctx)); - - if (ctx.subtableSize + growSize < ctx.subtableMax) return true; - while (ctx.subtableSize + growSize >= ctx.subtableMax) { - ctx.subtableMax *= 2; - } - - uint8_t *newTable = realloc(ctx.subtable, ctx.subtableMax); - if (newTable == NULL) FATAL("Failed to reallocate FNT subtable to %ld bytes\n", ctx.subtableMax); - ctx.subtable = newTable; - - memcpy(pContext, &ctx, sizeof(ctx)); - return true; -} - -bool WriteFntSubtable(FileTree *tree, FntContext *pContext) { - FntContext ctx; - memcpy(&ctx, pContext, sizeof(ctx)); - size_t subtableStart = ctx.subtableSize; - - // Create initial subtable entries - uint16_t fileId = ctx.nextFileId; - for (size_t i = 0; i < tree->numChildren; ++i) { - FileTree *child = &tree->children[i]; - FntSubEntry *entry = child->entry; - if (!entry->isSubdir) { - child->firstFileId = fileId; - fileId += 1; - } - - size_t entrySize = sizeof(*entry) + entry->length + (entry->isSubdir ? 2 : 0); - if (!GrowFntSubtable(&ctx, entrySize)) return false; - - FntSubEntry *dest = (FntSubEntry*) (ctx.subtable + ctx.subtableSize); - memcpy(dest, entry, entrySize); - ctx.subtableSize += entrySize; - } - - if (!GrowFntSubtable(&ctx, 1)) return false; - ctx.subtable[ctx.subtableSize] = 0; // End of subtable - ctx.subtableSize += 1; - - ctx.nextFileId = fileId; - - // Recurse child directories - for (size_t i = 0; i < tree->numChildren; ++i) { - FileTree *child = &tree->children[i]; - FntSubEntry *entry = child->entry; - if (!entry->isSubdir) continue; - uint16_t subdirId = 0xf000 | ctx.tableSize; - WRITE_SUBDIR_ID(entry, subdirId); - child->firstFileId = ctx.nextFileId; - FntEntry mainEntry; - mainEntry.subtableOffset = ctx.subtableSize; // will add main table length later - mainEntry.firstFile = ctx.nextFileId; - mainEntry.parentId = ctx.parentId; - if (!GrowFntTable(&ctx, ctx.tableSize + 1)) return false; - memcpy(&ctx.table[ctx.tableSize], &mainEntry, sizeof(mainEntry)); - ctx.tableSize += 1; - - uint16_t oldParentId = ctx.parentId; - ctx.parentId = subdirId; - - char name[128]; - strncpy(name, entry->name, entry->length); - name[entry->length] = '\0'; - if (!WriteFntSubtable(child, &ctx)) return false; - - ctx.parentId = oldParentId; - } - - // Update subdir IDs - size_t subtableOffset = 0; - for (size_t i = 0; i < tree->numChildren; ++i) { - FileTree *child = &tree->children[i]; - FntSubEntry *entry = child->entry; - size_t entrySize = sizeof(*entry) + entry->length + (entry->isSubdir ? 2 : 0); - memcpy(ctx.subtable + subtableStart + subtableOffset, entry, entrySize); - subtableOffset += entrySize; - } - - memcpy(pContext, &ctx, sizeof(ctx)); - return true; -} - -bool WriteFnt(File *rom, size_t *pAddress, FileTree *pRoot, size_t firstFileId, size_t *pNumFiles) { - size_t address = *pAddress; - - FntContext ctx; - ctx.table = malloc(INITIAL_TABLE_SIZE * sizeof(FntEntry)); - if (ctx.table == NULL) FATAL("Failed to allocate FNT table\n"); - ctx.tableSize = 1; - ctx.tableMax = INITIAL_TABLE_SIZE; - - ctx.nextFileId = firstFileId; - ctx.parentId = 0xf000; // root directory - - ctx.subtable = malloc(INITIAL_SUBTABLE_SIZE); - if (ctx.subtable == NULL) FATAL("Failed to allocate FNT subtable\n"); - ctx.subtableSize = 0; - ctx.subtableMax = INITIAL_SUBTABLE_SIZE; - - ctx.table[0].subtableOffset = 0; // will add main table length later - ctx.table[0].firstFile = firstFileId; - ctx.table[0].parentId = 0; // will be set to number of directories later - - if (!WriteFntSubtable(pRoot, &ctx)) return false; - ctx.table[0].parentId = ctx.tableSize; - - size_t tableLength = ctx.tableSize * sizeof(FntEntry); - for (size_t i = 0; i < ctx.tableSize; ++i) { - ctx.table[i].subtableOffset += tableLength; - } - if (!FileWrite(rom, ctx.table, sizeof(FntEntry), ctx.tableSize)) FATAL("Failed to write FNT table\n"); - address += ctx.tableSize * sizeof(FntEntry); - if (!FileWrite(rom, ctx.subtable, ctx.subtableSize, 1)) FATAL("Failed to write FNT subtables\n"); - address += ctx.subtableSize; - - free(ctx.table); - free(ctx.subtable); - - *pAddress = address; - *pNumFiles = ctx.nextFileId; - return true; -} - -bool WriteFat(File *rom, size_t *pAddress, size_t numFiles) { - size_t address = *pAddress; - - FatEntry blank; - blank.startOffset = 0; - blank.endOffset = 0; - for (size_t i = 0; i < numFiles; ++i) { - if (!FileWrite(rom, &blank, sizeof(blank), 1)) FATAL("Failed to write blank placeholder FAT entry\n"); - } - address += sizeof(blank) * numFiles; - - *pAddress = address; - return true; -} - -bool ReadTitle(const char *language, const char *titleFile, wchar_t *title, size_t titleSize) { - char buf[1024]; - memset(buf, 0, sizeof(buf)); - File file; - if (!FileOpenRead(titleFile, &file)) FATAL("Failed to open %s banner title '%s'\n", language, titleFile); - - size_t fileSize = FileSize(&file); - if (fileSize > sizeof(buf) - 1) FATAL("Buffer too small for %s banner title '%s'\n", language, titleFile); - - if (FileRead(&file, buf, fileSize, 1) != 1) FATAL("Failed to read %s banner title '%s'\n", language, titleFile); - FileClose(&file); - - memset(title, 0, titleSize); - if (!Utf8ToWchar(buf, fileSize, title, titleSize)) return false; - return true; -} - -bool WriteBanner(File *rom, size_t *pAddress) { - size_t address = *pAddress; - - File file; - - Banner banner; - banner.version = 1; - memset(banner.reserved0, 0, sizeof(banner.reserved0)); - - if (!FileOpenRead(ICON_BITMAP_FILE, &file)) FATAL("Failed to open banner icon bitmap '" ICON_BITMAP_FILE "'\n"); - if (FileRead(&file, banner.iconBitmap, sizeof(banner.iconBitmap), 1) != 1) { - FATAL("Failed to read banner icon bitmap '" ICON_BITMAP_FILE "'\n"); - } - FileClose(&file); - - if (!FileOpenRead(ICON_PALETTE_FILE, &file)) FATAL("Failed to open banner icon palette '" ICON_PALETTE_FILE "'\n"); - if (FileRead(&file, banner.iconPalette, sizeof(banner.iconPalette), 1) != 1) { - FATAL("Failed to read banner icon palette '" ICON_PALETTE_FILE "'\n"); - } - FileClose(&file); - - if (!ReadTitle("Japanese", TITLE_JAP_FILE, banner.japaneseTitle, sizeof(banner.japaneseTitle))) return false; - if (!ReadTitle("English", TITLE_ENG_FILE, banner.englishTitle, sizeof(banner.englishTitle))) return false; - if (!ReadTitle("French", TITLE_FRE_FILE, banner.frenchTitle, sizeof(banner.frenchTitle))) return false; - if (!ReadTitle("German", TITLE_GER_FILE, banner.germanTitle, sizeof(banner.germanTitle))) return false; - if (!ReadTitle("Italian", TITLE_ITA_FILE, banner.italianTitle, sizeof(banner.italianTitle))) return false; - if (!ReadTitle("Spanish", TITLE_SPA_FILE, banner.spanishTitle, sizeof(banner.spanishTitle))) return false; - - uint8_t *crcStart = (uint8_t*) &banner + offsetof(Banner, iconBitmap); - uint8_t *crcEnd = (uint8_t*) &banner + sizeof(banner); - banner.crc = Crc(crcStart, crcEnd - crcStart); - - if (!FileWrite(rom, &banner, sizeof(banner), 1)) FATAL("Failed to write banner\n"); - address += sizeof(banner); - - *pAddress = address; - return true; -} - -bool TraverseAndAppendAssets(File *rom, size_t *pAddress, const FileTree *tree, FatEntry *entries, uint16_t firstFileId) { - size_t address = *pAddress; - - for (size_t i = 0; i < tree->numChildren; ++i) { - FileTree *child = &tree->children[i]; - if (child->addedToFat) continue; - FntSubEntry *entry = child->entry; - char name[128]; - strncpy(name, entry->name, entry->length); - name[entry->length] = '\0'; - if (!entry->isSubdir) { - if (!Align(512, rom, &address)) return false; - size_t startOffset = address; - if (!AppendFile(rom, name, &address, NULL)) return false; - // For files, `firstFileId` is the ID of the file - entries[child->firstFileId].startOffset = startOffset; - entries[child->firstFileId].endOffset = address; - continue; - } - if (!ChangeDir(name)) return false; - if (!TraverseAndAppendAssets(rom, &address, child, entries, child->firstFileId)) return false; - if (!ChangeDir("..")) return false; - } - - *pAddress = address; - return true; -} - -bool AppendAssets( - File *rom, - size_t *pAddress, - FileTree *root, - FatEntry *entries, - size_t numOverlays, - const char *assetsListFile -) { - size_t address = *pAddress; - - if (assetsListFile == NULL) { - if (!TraverseAndAppendAssets(rom, &address, root, entries, numOverlays)) return false; - *pAddress = address; - return true; - } - - File file; - if (!FileOpenRead(assetsListFile, &file)) FATAL("Failed to open assets list file '%s'\n", assetsListFile); - size_t listSize = FileSize(&file); - char *assetsList = malloc(listSize + 1); - if (assetsList == NULL) FATAL("Failed to allocate string for assets list file '%s'\n", assetsListFile); - if (FileRead(&file, assetsList, listSize, 1) != 1) FATAL("Failed to read from assets list file '%s'\n", assetsListFile); - FileClose(&file); - assetsList[listSize] = '\0'; - - char assetsDir[256]; - if (!GetCurrentDir(assetsDir, sizeof(assetsDir))) FATAL("Failed to get assets directory\n"); - - char *const listEnd = assetsList + listSize; - for (char *path = assetsList, *next; path < listEnd; path = next) { - next = SplitLine(path); - if (*path != '/') continue; - FileTree *subTree = FindSubTree(root, path); - if (subTree == NULL) continue; - - // First file ID of root directory is given from number of overlays - uint16_t firstFileId = subTree->entry == NULL ? numOverlays : subTree->firstFileId; - - if (path[1] != '\0' && !ChangeDir(&path[1])) return false; - if (!TraverseAndAppendAssets(rom, &address, subTree, entries, firstFileId)) return false; - if (!ChangeDir(assetsDir)) return false; - - subTree->addedToFat = true; - } - - free(assetsList); - - *pAddress = address; - return true; -} - -bool RewriteFat(File *rom, size_t fatStart, const FatEntry *entries, size_t numFiles) { - size_t prevPos = FileOffset(rom); - FileGoTo(rom, fatStart); - if (!FileWrite(rom, entries, sizeof(*entries), numFiles)) FATAL("Failed to rewrite FAT table\n"); - FileGoTo(rom, prevPos); - return true; -} - -bool FinalizeHeader(File *rom, Header *pHeader, const char *arm7bios, uint32_t *secureArea, const Arm9Metadata *metadata) { - Header header; - memcpy(&header, pHeader, sizeof(header)); - - if (arm7bios != NULL) { - File file; - if (!FileOpenRead(arm7bios, &file)) FATAL("Failed to open ARM7 BIOS '%s'\n", arm7bios); - FileGoTo(&file, 0x30); - uint8_t encKey[sizeof(Blowfish)]; - if (FileRead(&file, &encKey, sizeof(encKey), 1) != 1) FATAL("Failed to read encrypion key\n"); - FileClose(&file); - - if (!BlowfishInit(encKey, pHeader, 3)) return false; - for (size_t i = 2; i < 0x200; i += 2) { - BlowfishEncrypt(&secureArea[i], &secureArea[i + 1]); - } - memcpy(secureArea, "encryObj", 8); - BlowfishEncrypt(&secureArea[0], &secureArea[1]); - if (!BlowfishInit(encKey, pHeader, 2)) return false; - BlowfishEncrypt(&secureArea[0], &secureArea[1]); - header.secureAreaCrc = Crc(secureArea, SECURE_AREA_SIZE); - } - - header.arm9.entry = metadata->entryAddr; - header.arm9.baseAddr = metadata->baseAddr; - header.arm9AutoloadCallback = metadata->autoloadCallback; - header.autoloadBlockInfosOffset = header.arm9.offset + metadata->autoloadBlockInfos; - - header.headerCrc = Crc(&header, offsetof(Header, headerCrc)); - - size_t prevPos = FileOffset(rom); - FileGoTo(rom, 0); - if (!FileWrite(rom, &header, sizeof(header), 1)) { - fprintf(stderr, "Failed to rewrite header\n"); - return 1; - } - FileGoTo(rom, prevPos); - - memcpy(pHeader, &header, sizeof(header)); - return true; -} - -void PrintUsage(const char *program) { - printf( - "buildrom " VERSION "\n" - "\n" - "Usage: %s -a BASEDIR -b BUILDDIR -r REGION -o OUTFILE [-7 ARM7BIOS] [-s ASSETS]\n" - " -a BASEDIR \tBase directory generated by extractrom\n" - " -b BUILDDIR\tBuild directory generated by Makefile\n" - " -r REGION \tJ = Japan, E = USA, P = Europe\n" - " -o OUTFILE \tOutput ROM file\n" - " -7 ARM7BIOS\tPath to ARM7 BIOS file\n" - " -s ASSETS \tPath to assets list\n", - program - ); -} - -int main(int argc, char **argv) { - - // --------------------- Parse command line arguments --------------------- - const char *program = argv[0]; - if (argc == 1) { - PrintUsage(program); - return 0; - } - const char *baseDir = NULL; - const char *buildDir = NULL; - const char *romFile = NULL; - const char *arm7biosFile = NULL; - char *assetsListFile = NULL; - Region region = 0; - for (int i = 1; i < argc; ++i) { - if (strcmp(argv[i], "-o") == 0) { - if (++i >= argc) { - fprintf(stderr, "Expected filename after -o\n"); - return 1; - } - romFile = argv[i]; - } else if (strcmp(argv[i], "-a") == 0) { - if (++i >= argc) { - fprintf(stderr, "Expected pathname after -a\n"); - return 1; - } - baseDir = argv[i]; - } else if (strcmp(argv[i], "-b") == 0) { - if (++i >= argc) { - fprintf(stderr, "Expected pathname after -b\n"); - return 1; - } - buildDir = argv[i]; - } else if (strcmp(argv[i], "-7") == 0) { - if (++i >= argc) { - fprintf(stderr, "Expected pathname after -7\n"); - return 1; - } - arm7biosFile = argv[i]; - } else if (strcmp(argv[i], "-s") == 0) { - if (++i >= argc) { - fprintf(stderr, "Expected pathname after -s\n"); - return 1; - } - assetsListFile = argv[i]; - } else if (strcmp(argv[i], "-r") == 0) { - if (++i >= argc) { - fprintf(stderr, "Expected region after -r\n"); - return 1; - } - if (strlen(argv[i]) != 1) { - fprintf(stderr, "Region must be a single character\n"); - return 1; - } - region = argv[i][0]; - } else { - fprintf(stderr, "Unknown option '%s'\n", argv[i]); - return 1; - } - } - if (baseDir == NULL) { - PrintUsage(program); - fprintf(stderr, "Please provide a base directory, see usage above\n"); - return 1; - } - if (buildDir == NULL) { - PrintUsage(program); - fprintf(stderr, "Please provide a build directory, see usage above\n"); - return 1; - } - if (region != REGION_JAPAN && region != REGION_USA && region != REGION_EUROPE) { - PrintUsage(program); - fprintf(stderr, "Invalid region '%c', see usage above\n", region); - return 1; - } - if (romFile == NULL) { - PrintUsage(program); - fprintf(stderr, "Please provide an output ROM file, see usage above\n"); - return 1; - } - - - // --------------------- Set up --------------------- - char rootDir[256]; - if (!GetCurrentDir(rootDir, sizeof(rootDir))) { - fprintf(stderr, "Failed to get root directory\n"); - return 1; - } - - File rom; - if (!FileOpenWrite(romFile, &rom)) { - fprintf(stderr, "Failed to open output ROM file '%s'\n", romFile); - return 1; - } - - readBuffer = malloc(BUFFER_SIZE); - if (readBuffer == NULL) { - fprintf(stderr, "Failed to allocate read buffer to %d bytes\n", BUFFER_SIZE); - return 1; - } - - GenerateCrcTable(); - - - // --------------------- Write initial header --------------------- - BuildInfo info; - info.region = region; - size_t address = 0; - - Header header; - InitHeader(&header, &info); - - if (!FileWrite(&rom, &header, sizeof(header), 1)) { - fprintf(stderr, "Failed to write NDS header\n"); - return 1; - } - address += sizeof(header); - - - // --------------------- Get canonical file paths --------------------- - if (assetsListFile != NULL && !AllocFullPath(assetsListFile, &assetsListFile)) return 1; - if (!ChangeDir(baseDir)) return 1; - char *arm9overlayDataFile = NULL; - if (!AllocFullPath(ARM9_OVERLAY_DATA_FILE, &arm9overlayDataFile)) return 1; - if (!ChangeDir(rootDir)) return 1; - - - // --------------------- Write ARM9 program --------------------- - if (!ChangeDir(buildDir)) return 1; - - if (!Align(512, &rom, &address)) return 1; - header.arm9.offset = address; - - Arm9Metadata metadata; - if (!LoadArm9Metadata(&metadata)) return 1; - uint32_t secureArea[SECURE_AREA_SIZE / sizeof(uint32_t)]; - if (!WriteArm9Program(&rom, &address, &header.arm9.size, secureArea, &metadata)) return 1; - if (!AppendFile(&rom, ARM9_FOOTER_FILE, &address, NULL)) return 1; - - - // --------------------- Write ARM9 overlay table and overlay files --------------------- - if (!Align(512, &rom, &address)) return 1; - header.arm9Overlays.offset = address; - size_t numOverlays = 0; - FatEntry overlayEntries[MAX_OVERLAYS]; - if (!WriteArm9Overlays(&rom, &address, &header, arm9overlayDataFile, overlayEntries, &numOverlays)) return 1; - FreeFullPath(&arm9overlayDataFile); - - if (!ChangeDir(rootDir)) return 1; - - if (!ChangeDir(baseDir)) return 1; - - - // --------------------- Write ARM7 program --------------------- - if (!Align(512, &rom, &address)) return 1; - header.arm7.offset = address; - if (!AppendFile(&rom, ARM7_PROGRAM_FILE, &address, &header.arm7.size)) return 1; - - if (!ChangeDir(ASSETS_SUBDIR)) return 1; - - - // --------------------- Write file name table (FNT) --------------------- - FileTree root; - if (!MakeFileTree(&root)) return false; - if (!SortFileTree(&root, CompareFileTreeNormal)) return false; - - if (!Align(512, &rom, &address)) return 1; - size_t numFiles = 0; - header.fileNames.offset = address; - if (!WriteFnt(&rom, &address, &root, numOverlays, &numFiles)) return 1; - header.fileNames.size = address - header.fileNames.offset; - - - // --------------------- Write file allocation table (FAT) --------------------- - if (!Align(512, &rom, &address)) return 1; - header.fileAllocs.offset = address; - if (!WriteFat(&rom, &address, numFiles)) return 1; - header.fileAllocs.size = address - header.fileAllocs.offset; - FatEntry *fatEntries = malloc(numFiles * sizeof(FatEntry)); - memcpy(fatEntries, overlayEntries, numOverlays * sizeof(*fatEntries)); - - if (!ChangeDir("..")) return 1; - - - // --------------------- Write banner --------------------- - if (!Align(512, &rom, &address)) return false; - header.bannerOffset = address; - if (!WriteBanner(&rom, &address)) return false; - - if (!ChangeDir(ASSETS_SUBDIR)) return 1; - - - // --------------------- Write assets --------------------- - if (!Align(512, &rom, &address)) return false; - if (!SortFileTree(&root, CompareFileTreeAscii)) return false; - if (!AppendAssets(&rom, &address, &root, fatEntries, numOverlays, assetsListFile)) return false; - if (assetsListFile != NULL) FreeFullPath(&assetsListFile); - - if (!RewriteFat(&rom, header.fileAllocs.offset, fatEntries, numFiles)) - free(fatEntries); - - if (!FreeFileTree(&root)) return false; - - if (!ChangeDir("..")) return 1; - - if (!ChangeDir(rootDir)) return 1; - - - // --------------------- Write padding --------------------- - header.romSize = address; - header.capacity = 15 - __builtin_clz(header.romSize); - size_t romEnd = 1 << (17 + header.capacity); - if (!Align(romEnd, &rom, &address)) return 1; - - - // --------------------- Update header --------------------- - char *arm7bios = NULL; - if (arm7biosFile != NULL && !AllocFullPath(arm7biosFile, &arm7bios)) return 1; - - if (!ChangeDir(buildDir)) return 1; - - if (!FinalizeHeader(&rom, &header, arm7bios, secureArea, &metadata)) return false; - FreeFullPath(&arm7bios); - - if (!ChangeDir(rootDir)) return 1; - - free(readBuffer); - FileClose(&rom); -} diff --git a/tools/rom/extract.c b/tools/rom/extract.c deleted file mode 100644 index b677b76c..00000000 --- a/tools/rom/extract.c +++ /dev/null @@ -1,395 +0,0 @@ -#include -#include -#include -#include -#include - -#include "rom.h" -#include "ph.h" -#include "util.h" - -#define VERSION "1.0.2" -#define INDENT 4 - -// Command line flags for debugging purposes -bool printFileHierarchy = false; -bool printFileAllocOrder = false; - -void Indent(size_t depth) { - char spaces[INDENT + 1]; - memset(spaces, ' ', INDENT); - spaces[INDENT] = '\0'; - for (size_t i = 0; i < depth; ++i) { - puts(spaces); - } -} - -bool CheckRegion(const Header *pHeader, BuildInfo *pInfo) { - Region region = pHeader->gamecode[3]; - if ( - memcmp(pHeader->gamecode, GAMECODE_PREFIX, 3) != 0 || ( - region != REGION_JAPAN && - region != REGION_USA && - region != REGION_EUROPE - ) - ) { - FATAL("Invalid gamecode prefix '%.4s'\n", pHeader->gamecode); - } - - pInfo->region = region; - return true; -} - -bool ExtractArm7(const uint8_t *rom, ProgramOffset *pArm7) { - File file; - if (!FileOpenWrite(ARM7_PROGRAM_FILE, &file)) FATAL("Failed to create ARM7 program '" ARM7_PROGRAM_FILE "'\n"); - if (!FileWrite(&file, rom + pArm7->offset, pArm7->size, 1)) FATAL("Failed to write ARM7 program '" ARM7_PROGRAM_FILE "'\n"); - FileClose(&file); - return true; -} - -bool ExtractTitle(const char *language, const char *fileName, const wchar_t *title, size_t titleSize) { - char buf[1024]; - File file; - if (!FileOpenWrite(fileName, &file)) FATAL("Failed to create %s banner title '%s'\n", language, fileName); - - size_t resultSize = 0; - if (!WcharToUtf8((wchar_t*) title, titleSize, buf, sizeof(buf), &resultSize)) return false; - size_t len = strlen(buf); - if (!FileWrite(&file, buf, len, 1)) FATAL("Failed to write %s banner title '%s'\n", language, fileName); - FileClose(&file); - return true; -} - -bool ExtractBanner(const Banner *pBanner, const BuildInfo *pInfo) { - if (!MakeDir(BANNER_SUBDIR)) return 1; - - File file; - - if (!FileOpenWrite(ICON_BITMAP_FILE, &file)) FATAL("Failed to create banner icon bitmap '" ICON_BITMAP_FILE "'\n"); - if (!FileWrite(&file, pBanner->iconBitmap, sizeof(pBanner->iconBitmap), 1)) { - FATAL("Failed to write banner icon bitmap '" ICON_BITMAP_FILE "'\n"); - } - FileClose(&file); - - if (!FileOpenWrite(ICON_PALETTE_FILE, &file)) FATAL("Failed to create banner icon palette '" ICON_PALETTE_FILE "'\n"); - if (!FileWrite(&file, pBanner->iconPalette, sizeof(pBanner->iconPalette), 1)) { - FATAL("Failed to write banner icon palette '" ICON_PALETTE_FILE "'\n"); - } - FileClose(&file); - - if (!ExtractTitle("Japanese", TITLE_JAP_FILE, pBanner->japaneseTitle, sizeof(pBanner->japaneseTitle))) return false; - if (!ExtractTitle("English", TITLE_ENG_FILE, pBanner->englishTitle, sizeof(pBanner->englishTitle))) return false; - if (!ExtractTitle("French", TITLE_FRE_FILE, pBanner->frenchTitle, sizeof(pBanner->frenchTitle))) return false; - if (!ExtractTitle("German", TITLE_GER_FILE, pBanner->germanTitle, sizeof(pBanner->germanTitle))) return false; - if (!ExtractTitle("Italian", TITLE_ITA_FILE, pBanner->italianTitle, sizeof(pBanner->italianTitle))) return false; - if (!ExtractTitle("Spanish", TITLE_SPA_FILE, pBanner->spanishTitle, sizeof(pBanner->spanishTitle))) return false; - - return true; -} - -typedef struct { - char basePath[256]; - size_t basePathLen; - char **filePathList; - size_t numFilePaths; - size_t maxFilePaths; - - size_t depth; -} ExtractContext; - -bool GrowFilePathList(ExtractContext *pContext, size_t minEntries) { - ExtractContext ctx; - memcpy(&ctx, pContext, sizeof(ctx)); - - if (minEntries <= ctx.maxFilePaths) return true; - while (minEntries > ctx.maxFilePaths) { - ctx.maxFilePaths *= 2; - } - - char **newFilePathList = realloc(ctx.filePathList, ctx.maxFilePaths * sizeof(*ctx.filePathList)); - if (newFilePathList == NULL) FATAL("Failed to reallocate file path list to %ld entries\n", ctx.maxFilePaths); - ctx.filePathList = newFilePathList; - - // Fill new entries with 0 - memset(&ctx.filePathList[pContext->maxFilePaths], 0, (ctx.maxFilePaths - pContext->maxFilePaths) * sizeof(*ctx.filePathList)); - - memcpy(pContext, &ctx, sizeof(ctx)); - return true; -} - -bool ExtractSubtable( - const uint8_t *rom, - const uint8_t *fatStart, - const uint8_t *fntStart, - const FntEntry *pFntEntry, - ExtractContext *pContext -) { - ExtractContext ctx; - memcpy(&ctx, pContext, sizeof(ctx)); - - const uint8_t *subEntryAddr = fntStart + pFntEntry->subtableOffset; - const FntSubEntry *pSubEntry = (const FntSubEntry*) subEntryAddr; - uint16_t fileId = pFntEntry->firstFile; - while(pSubEntry->length > 0) { - char name[128]; - memcpy(name, pSubEntry->name, pSubEntry->length); - name[pSubEntry->length] = '\0'; - - if (!pSubEntry->isSubdir) { - if (printFileHierarchy) { - Indent(ctx.depth); - printf("%d: %s\n", fileId, name); - } - - const FatEntry *pFatEntry = (const FatEntry*) fatStart + fileId; - size_t fileSize = pFatEntry->endOffset - pFatEntry->startOffset; - const uint8_t *pFileBytes = rom + pFatEntry->startOffset; - - File file; - if (!FileOpenWrite(name, &file)) FATAL("Failed to open assets file '%s'\n", name); - if (!FileWrite(&file, pFileBytes, fileSize, 1)) FATAL("Failed to write to assets file '%s'\n", name); - FileClose(&file); - - if (printFileAllocOrder) { - if (!GrowFilePathList(&ctx, fileId + 1)) return false; - char *filePath = malloc(ctx.basePathLen + pSubEntry->length + 1); - if (filePath == NULL) FATAL("Failed to allocate string for file path\n"); - sprintf(filePath, "%s%s", ctx.basePath, name); - ctx.filePathList[fileId] = filePath; - } - - subEntryAddr += sizeof(FntSubEntry) + pSubEntry->length; - pSubEntry = (const FntSubEntry*) subEntryAddr; - ++fileId; - continue; - } - - if (printFileHierarchy) { - Indent(ctx.depth); - printf("%s {\n", name); - } - if (!MakeDir(name)) return false; - if (!ChangeDir(name)) return false; - - uint16_t subdirId = READ_SUBDIR_ID(pSubEntry); - uint16_t subdirIndex = subdirId & 0xfff; - ctx.depth += 1; - - if (printFileAllocOrder) { - size_t basePathSpace = sizeof(ctx.basePath) - ctx.basePathLen; - size_t basePathAdded = snprintf(&ctx.basePath[ctx.basePathLen], basePathSpace, "%s/", name); - if (basePathAdded >= basePathSpace) FATAL("Maximum base path size was exceeded\n"); - ctx.basePathLen += basePathAdded; - if (!ExtractSubtable(rom, fatStart, fntStart, (FntEntry*) fntStart + subdirIndex, &ctx)) return false; - ctx.basePathLen -= basePathAdded; - memset(&ctx.basePath[ctx.basePathLen], 0, basePathAdded); - } else { - if (!ExtractSubtable(rom, fatStart, fntStart, (FntEntry*) fntStart + subdirIndex, &ctx)) return false; - } - ctx.depth -= 1; - - if (printFileHierarchy) { - Indent(ctx.depth); - printf("}\n"); - } - - if (!ChangeDir("..")) return false; - subEntryAddr += sizeof(FntSubEntry) + pSubEntry->length + sizeof(subdirId); - pSubEntry = (const FntSubEntry*) subEntryAddr; - } - - memcpy(pContext, &ctx, sizeof(ctx)); - return true; -} - -typedef struct { - uint32_t fileId; - uint32_t startOffset; - uint32_t endOffset; -} FatInfo; - -int32_t CompareFatInfo(const void *a, const void *b) { - const FatInfo *infoA = (FatInfo*) a; - const FatInfo *infoB = (FatInfo*) b; - return infoA->startOffset - infoB->startOffset; -} - -bool PrintFileAllocOrder(ExtractContext *ctx, const uint8_t *fatAddr) { - const FatEntry *fatStart = (FatEntry*) fatAddr; - const FatEntry *fat; - for (fat = fatStart; fat->startOffset != 0xFFFFFFFF; ++fat); - size_t numFiles = fat - fatStart; - - FatInfo *fatInfo = malloc(numFiles * sizeof(FatInfo)); - if (fatInfo == NULL) FATAL("Failed to allocate array for printing file allocation order\n"); - for (size_t i = 0; i < numFiles; ++i) { - fatInfo[i].fileId = i; - fatInfo[i].startOffset = fatStart[i].startOffset; - fatInfo[i].endOffset = fatStart[i].endOffset; - } - - qsort(fatInfo, numFiles, sizeof(*fatInfo), CompareFatInfo); - - for (size_t i = 0; i < numFiles; ++i) { - uint32_t fileId = fatInfo[i].fileId; - if (ctx->filePathList[fileId] == NULL) continue; - printf("%s\n", ctx->filePathList[fileId]); - } - - free(fatInfo); - return true; -} - -bool ExtractAssets(const uint8_t *rom, const uint8_t *fatStart, const uint8_t *fntStart) { - ExtractContext ctx; - memset(ctx.basePath, 0, sizeof(ctx.basePath)); - ctx.basePathLen = 0; - ctx.numFilePaths = 0; - if (printFileAllocOrder) { - ctx.maxFilePaths = 4096; - ctx.filePathList = calloc(ctx.maxFilePaths, sizeof(*ctx.filePathList)); - } else { - ctx.maxFilePaths = 0; - ctx.filePathList = NULL; - } - ctx.depth = 0; - - if (!ExtractSubtable(rom, fatStart, fntStart, (FntEntry*) fntStart, &ctx)) return false; - if (printFileAllocOrder) { - if (!PrintFileAllocOrder(&ctx, fatStart)) return false; - for (size_t i = 0; i < ctx.numFilePaths; ++i) { - if (ctx.filePathList[i] != NULL) free(ctx.filePathList[i]); - } - free(ctx.filePathList); - } - return true; -} - -bool ExtractOverlayData(const uint8_t *rom, const Header *header) { - const OverlayEntry *entry = (OverlayEntry*) (rom + header->arm9Overlays.offset); - const OverlayEntry *end = entry + header->arm9Overlays.size / sizeof(OverlayEntry); - - File file; - if (!FileOpenWrite(ARM9_OVERLAY_DATA_FILE, &file)) { - FATAL("Failed to open overlay data file '" ARM9_OVERLAY_DATA_FILE "'\n"); - } - for(; entry < end; ++entry) { - OverlayData data; - data.fileId = entry->fileId; - if (!FileWrite(&file, &data, sizeof(data), 1)) FATAL("Failed to write overlay data to '" ARM9_OVERLAY_DATA_FILE "'\n"); - } - - FileClose(&file); - return true; -} - -void PrintUsage(const char *program) { - printf( - "extractrom " VERSION "\n" - "\n" - "Usage: %s -i ROMFILE -o OUTDIR [-n] [-a] [-l]\n" - " -o OUTDIR \tDirectory to extract files to\n" - " -i ROMFILE\tROM to extract from\n" - " -n \tPrints file name hierarchy to stdout\n" - " -a \tPrints file allocation order to stdout\n", - program - ); -} - -int main(int argc, const char **argv) { - - // --------------------- Parse command line arguments --------------------- - const char *program = argv[0]; - if (argc == 1) { - PrintUsage(program); - return 0; - } - const char *romFile = NULL; - const char *outDir = NULL; - for (int i = 1; i < argc; ++i) { - if (strcmp(argv[i], "-o") == 0) { - if (++i >= argc) { - fprintf(stderr, "Expected dirname after -o\n"); - return 1; - } - outDir = argv[i]; - } else if (strcmp(argv[i], "-i") == 0) { - if (++i >= argc) { - fprintf(stderr, "Expected filename after -i\n"); - return 1; - } - romFile = argv[i]; - } else if (strcmp(argv[i], "-n") == 0) { - printFileHierarchy = true; - } else if (strcmp(argv[i], "-a") == 0) { - printFileAllocOrder = true; - } else { - fprintf(stderr, "Unknown option '%s'\n", argv[i]); - return 1; - } - } - if (romFile == NULL) { - PrintUsage(program); - fprintf(stderr, "Please provide a ROM file, see usage above.\n"); - return 1; - } - if (outDir == NULL) { - PrintUsage(program); - fprintf(stderr, "Please provide an output directory, see usage above.\n"); - return 1; - } - - - // --------------------- Load ROM --------------------- - File fileRom; - if (!FileOpenRead(romFile, &fileRom)) { - fprintf(stderr, "Failed to open input ROM '%s'\n", romFile); - return 1; - } - size_t romSize = FileSize(&fileRom); - uint8_t *rom = malloc(romSize); - if (rom == NULL) { - fprintf(stderr, "Failed to allocate buffer for '%s'\n", romFile); - return 1; - } - if (FileRead(&fileRom, rom, romSize, 1) != 1) { - fprintf(stderr, "Failed to read from '%s'\n", romFile); - return 1; - } - FileClose(&fileRom); - - - // --------------------- Set up --------------------- - Header *pHeader = (Header*) rom; - BuildInfo info; - if (!CheckRegion(pHeader, &info)) return 1; - if (!MakeDir(outDir)) return 1; - if (!ChangeDir(outDir)) return 1; - - - // --------------------- Extract ARM7 program --------------------- - if (!ExtractArm7(rom, &pHeader->arm7)) return 1; - - - // --------------------- Extract banner --------------------- - Banner *pBanner = (Banner*) (rom + pHeader->bannerOffset); - if (!ExtractBanner(pBanner, &info)) return 1; - - - // --------------------- Extract assets --------------------- - if (!MakeDir(ASSETS_SUBDIR)) return 1; - if (!ChangeDir(ASSETS_SUBDIR)) return 1; - const uint8_t *fntStart = rom + pHeader->fileNames.offset; - const uint8_t *fatStart = rom + pHeader->fileAllocs.offset; - if (!ExtractAssets(rom, fatStart, fntStart)) return 1; - if (!ChangeDir("..")) return 1; - - - // --------------------- Extract overlay data --------------------- - if (!ExtractOverlayData(rom, pHeader)) return 1; - - if (!ChangeDir("..")) return 1; - - free(rom); -} diff --git a/tools/rom/files.h b/tools/rom/files.h deleted file mode 100644 index 3b5d8863..00000000 --- a/tools/rom/files.h +++ /dev/null @@ -1,223 +0,0 @@ -#ifndef __FILES_H -#define __FILES_H - -#include - -#include "util.h" -#include "rom.h" - -typedef struct FileTree { - struct FileTree *children; - uint16_t numChildren; - uint16_t maxChildren; - uint16_t firstFileId; - bool addedToFat; - FntSubEntry *entry; -} FileTree; - -bool MakeFileTree(FileTree *pTree); - -bool IterFiles(bool (*callback)(const char *name, bool isDir, void*), void *userData) { -#ifdef __UTIL_WINDOWS - WIN32_FIND_DATAA findData; - HANDLE hFind = FindFirstFileA("*", &findData); - if (hFind == INVALID_HANDLE_VALUE) FATAL("Failed to open directory to iterate files\n"); - do { - const char *name = findData.cFileName; - if (strcmp(name, ".") == 0) continue; - if (strcmp(name, "..") == 0) continue; - bool isDir = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; - if (!callback(name, isDir, userData)) return false; - } while (FindNextFileA(hFind, &findData)); - FindClose(hFind); -#elif defined(__UTIL_LINUX) - DIR *dir = opendir("."); - struct dirent *entry; - while ((entry = readdir(dir)) != NULL) { - const char *name = entry->d_name; - if (strcmp(name, ".") == 0) continue; - if (strcmp(name, "..") == 0) continue; - bool isDir = entry->d_type == DT_DIR; - if (!callback(name, isDir, userData)) return false; - } - closedir(dir); -#endif - return true; -} - -bool _GrowFileTreeChildren(FileTree *pTree, size_t minChildren) { - FileTree tree; - memcpy(&tree, pTree, sizeof(tree)); - if (tree.maxChildren >= minChildren) return true; - - size_t newSize = tree.maxChildren; - if (newSize == 0) newSize = minChildren; - while (newSize < minChildren) newSize *= 2; - if (tree.children == NULL) { - FileTree *children = malloc(newSize * sizeof(FileTree)); - if (children == NULL) FATAL("Failed to allocate file tree children\n"); - tree.children = children; - } else { - FileTree *children = realloc(tree.children, newSize * sizeof(FileTree)); - if (children == NULL) FATAL("Failed to reallocate file tree children\n"); - tree.children = children; - } - tree.maxChildren = newSize; - - memcpy(pTree, &tree, sizeof(tree)); - return true; -} - -bool _FileTreeFileCallback(const char *name, bool isDir, void *userData) { - FileTree *pTree = (FileTree*) userData; - size_t nameLength = strlen(name); - - if (isDir) { - FntSubEntry *entry = malloc(sizeof(FntSubEntry) + nameLength + 2); - if (entry == NULL) FATAL("Failed to allocate FNT sub entry for directory '%s'\n", name); - entry->isSubdir = true; - entry->length = nameLength; - memcpy(entry->name, name, nameLength); - WRITE_SUBDIR_ID(entry, 0); - - if (!ChangeDir(name)) return false; - - FileTree child; - if (!MakeFileTree(&child)) return false; - child.entry = entry; - if (!_GrowFileTreeChildren(pTree, pTree->numChildren + 1)) return false; - memcpy(&pTree->children[pTree->numChildren], &child, sizeof(child)); - pTree->numChildren += 1; - - if (!ChangeDir("..")) return false; - } else { - FntSubEntry *entry = malloc(sizeof(FntSubEntry) + nameLength); - if (entry == NULL) FATAL("Failed to allocate FNT sub entry for file '%s'\n", name); - entry->isSubdir = false; - entry->length = nameLength; - memcpy(entry->name, name, nameLength); - - FileTree child; - child.children = NULL; - child.numChildren = 0; - child.maxChildren = 0; - child.firstFileId = 0; - child.addedToFat = false; - child.entry = entry; - if (!_GrowFileTreeChildren(pTree, pTree->numChildren + 1)) return false; - memcpy(&pTree->children[pTree->numChildren], &child, sizeof(child)); - pTree->numChildren += 1; - } - return true; -} - -bool MakeFileTree(FileTree *pTree) { - FileTree tree; - tree.children = NULL; - tree.numChildren = 0; - tree.maxChildren = 0; - tree.addedToFat = false; - if (!_GrowFileTreeChildren(&tree, 64)) return false; - tree.entry = NULL; - - if (!IterFiles(_FileTreeFileCallback, &tree)) return false; - memcpy(pTree, &tree, sizeof(tree)); - return true; -} - -bool FreeFileTree(FileTree *pTree) { - for (size_t i = 0; i < pTree->numChildren; ++i) { - if (!FreeFileTree(&pTree->children[i])) return false; - } - if (pTree->children != NULL) { - free(pTree->children); - pTree->children = NULL; - } - pTree->numChildren = 0; - pTree->maxChildren = 0; - pTree->addedToFat = false; - if (pTree->entry != NULL) { - free(pTree->entry); - pTree->entry = NULL; - } - return true; -} - -int CompareFileTreeNormal(const void *a, const void *b) { - FileTree *treeA = (FileTree*) a; - FileTree *treeB = (FileTree*) b; - - // Files before directories - bool dirA = treeA->entry == NULL || treeA->entry->isSubdir; - bool dirB = treeB->entry == NULL || treeB->entry->isSubdir; - if (dirA && !dirB) return 1; - if (!dirA && dirB) return -1; - - size_t lenA = treeA->entry->length; - size_t lenB = treeB->entry->length; - size_t minSize = (lenA < lenB) ? lenA : lenB; - - // Alphabetic name order - const char *nameA = treeA->entry->name; - const char *nameB = treeB->entry->name; - for (size_t i = 0; i < minSize; ++i) { - const char chA = tolower(nameA[i]); - const char chB = tolower(nameB[i]); - if (chA != chB) return chA - chB; - } - - // Shortest name first - if (lenA < lenB) return -1; - if (lenA > lenB) return 1; - return 0; -} - -int CompareFileTreeAscii(const void *a, const void *b) { - FileTree *treeA = (FileTree*) a; - FileTree *treeB = (FileTree*) b; - - size_t lenA = treeA->entry->length; - size_t lenB = treeB->entry->length; - size_t minSize = (lenA < lenB) ? lenA : lenB; - - // ASCII order - int cmp = strncmp(treeA->entry->name, treeB->entry->name, minSize); - if (cmp != 0) return cmp; - - // Shortest name first - if (lenA < lenB) return -1; - if (lenA > lenB) return 1; - return 0; -} - -bool SortFileTree(FileTree *pTree, int (*compare)(const void*, const void*)) { - if (pTree->numChildren <= 1) return true; - FileTree tree; - memcpy(&tree, pTree, sizeof(tree)); - - qsort(tree.children, tree.numChildren, sizeof(*tree.children), compare); - for (size_t i = 0; i < tree.numChildren; ++i) { - if (!SortFileTree(&tree.children[i], compare)) return false; - } - - memcpy(pTree, &tree, sizeof(tree)); - return true; -} - -FileTree* FindSubTree(FileTree *tree, const char *path) { - if (*path == '/') ++path; - const char *nextPath = strchr(path, '/'); - if (nextPath == NULL) return tree; - - size_t dirNameLen = nextPath - path; - for (size_t i = 0; i < tree->numChildren; ++i) { - FileTree *child = &tree->children[i]; - if (child->entry->length != dirNameLen) continue; - if (strncmp(child->entry->name, path, dirNameLen) != 0) continue; - return FindSubTree(child, nextPath); - } - - return NULL; -} - -#endif diff --git a/tools/rom/ph.h b/tools/rom/ph.h deleted file mode 100644 index 8cde8a52..00000000 --- a/tools/rom/ph.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef __PH_H -#define __PH_H - -#define TITLE "ZELDA_DS:PH" -#define GAMECODE_PREFIX "AZE" - -#define BANNER_SUBDIR "banner" -#define ICON_BITMAP_FILE BANNER_SUBDIR "/icon.bin" -#define ICON_PALETTE_FILE BANNER_SUBDIR "/icon.pal" -#define TITLE_JAP_FILE BANNER_SUBDIR "/title_jap.txt" -#define TITLE_ENG_FILE BANNER_SUBDIR "/title_eng.txt" -#define TITLE_FRE_FILE BANNER_SUBDIR "/title_fre.txt" -#define TITLE_GER_FILE BANNER_SUBDIR "/title_ger.txt" -#define TITLE_ITA_FILE BANNER_SUBDIR "/title_ita.txt" -#define TITLE_SPA_FILE BANNER_SUBDIR "/title_spa.txt" - -#define ASSETS_SUBDIR "assets" - -#define ARM9_PROGRAM_FILE "arm9.lz" -#define ARM9_FOOTER_FILE "arm9_footer.bin" -#define ARM9_METADATA_FILE "arm9_metadata.bin" -#define ARM9_OVERLAY_TABLE_FILE "arm9_ovt.bin" -#define ARM9_OVERLAY_DATA_FILE "arm9_ovdata.bin" -#define OVERLAYS_SUBDIR "overlays" - -#define ARM7_PROGRAM_FILE "arm7.bin" - -typedef enum { - REGION_JAPAN = 'J', - REGION_USA = 'E', - REGION_EUROPE = 'P', -} Region; - -typedef struct { - Region region; -} BuildInfo; - -typedef struct { - uint32_t fileId; -} OverlayData; - -#endif diff --git a/tools/rom/rom.h b/tools/rom/rom.h deleted file mode 100644 index 28a88850..00000000 --- a/tools/rom/rom.h +++ /dev/null @@ -1,124 +0,0 @@ -#ifndef __ROM_H -#define __ROM_H - -#include -#include -#include - -#define SECURE_AREA_SIZE 0x4000 - -typedef struct { - /* 00 */ uint32_t offset; - /* 04 */ uint32_t entry; - /* 08 */ uint32_t baseAddr; - /* 0c */ uint32_t size; - /* 10 */ -} ProgramOffset; - -typedef struct { - /* 0 */ uint32_t offset; - /* 4 */ uint32_t size; - /* 8 */ -} TableOffset; - -typedef struct { - /* 0000 */ char title[0xc]; - /* 000c */ char gamecode[0x4]; - /* 0010 */ char makercode[0x2]; - /* 0012 */ uint8_t unitcode; - /* 0013 */ uint8_t encSeedSelect; - /* 0014 */ uint8_t capacity; - /* 0015 */ uint8_t reserved0[0x8]; - /* 001d */ uint8_t dsRegion; - /* 001e */ uint8_t romVersion; - /* 001f */ uint8_t autostart; - /* 0020 */ ProgramOffset arm9; - /* 0030 */ ProgramOffset arm7; - /* 0040 */ TableOffset fileNames; - /* 0048 */ TableOffset fileAllocs; - /* 0050 */ TableOffset arm9Overlays; - /* 0058 */ TableOffset arm7Overlays; - /* 0060 */ uint32_t normalCmdSetting; - /* 0064 */ uint32_t key1CmdSetting; - /* 0068 */ uint32_t bannerOffset; - /* 006c */ uint16_t secureAreaCrc; - /* 006e */ uint16_t secureAreaDelay; - /* 0070 */ uint32_t arm9AutoloadCallback; - /* 0074 */ uint32_t arm7AutoloadCallback; - /* 0078 */ uint64_t secureAreaDisable; - /* 0080 */ uint32_t romSize; - /* 0084 */ uint32_t headerSize; - /* 0088 */ uint32_t autoloadBlockInfosOffset; - /* 008c */ uint8_t reserved1[0x8]; - /* 0094 */ uint16_t romEnd; - /* 0096 */ uint16_t rwEnd; - /* 0098 */ uint8_t reserved2[0x18]; - /* 00b0 */ uint8_t reserved3[0x10]; - /* 00c0 */ uint8_t logo[0x9c]; - /* 015c */ uint16_t logoCrc; - /* 015e */ uint16_t headerCrc; - /* 0160 */ uint32_t debugRomOffset; - /* 0164 */ uint32_t debugSize; - /* 0168 */ uint32_t debugRamAddr; - /* 016c */ uint8_t reserved4[0x4]; - /* 0170 */ uint8_t reserved5[0x90]; - /* 0200 */ uint8_t reserved6[0xe00]; - /* 1000 */ uint8_t reserved7[0x3000]; - /* 4000 */ -} Header; - -typedef struct { - /* 00 */ uint32_t id; - /* 04 */ uint32_t baseAddr; - /* 08 */ uint32_t textSize; - /* 0c */ uint32_t bssSize; - /* 10 */ uint32_t sinitStart; - /* 14 */ uint32_t sinitEnd; - /* 18 */ uint32_t fileId; - /* 1c */ uint32_t compressedSize : 24; - /* 1f */ uint32_t isCompressed : 8; - /* 20 */ -} OverlayEntry; - -typedef struct { - /* 0000 */ uint16_t version; - /* 0002 */ uint16_t crc; - /* 0004 */ uint8_t reserved0[0x1c]; - /* 0020 */ uint8_t iconBitmap[0x200]; - /* 0220 */ uint16_t iconPalette[0x10]; - /* 0240 */ wchar_t japaneseTitle[0x80]; - /* 0340 */ wchar_t englishTitle[0x80]; - /* 0440 */ wchar_t frenchTitle[0x80]; - /* 0540 */ wchar_t germanTitle[0x80]; - /* 0640 */ wchar_t italianTitle[0x80]; - /* 0740 */ wchar_t spanishTitle[0x80]; - /* 0540 */ -} Banner; - -typedef struct { - /* 0 */ uint32_t subtableOffset; - /* 4 */ uint16_t firstFile; - /* 6 */ uint16_t parentId; - /* 8 */ -} FntEntry; - -typedef struct { - /* 0.0 */ uint8_t length : 7; - /* 0.7 */ bool isSubdir : 1; - /* 1.0 */ char name[]; - // If isSubdir - /* 1.0 + length */ // uint16_t subdirId; - /* 1.0 + length + 2 */ - // Else - /* 1.0 + length */ -} FntSubEntry; - -#define READ_SUBDIR_ID(entry) READ16(entry + sizeof(*entry) + entry->length); -#define WRITE_SUBDIR_ID(entry,id) WRITE16(entry + sizeof(*entry) + entry->length, id) - -typedef struct { - /* 0 */ uint32_t startOffset; - /* 4 */ uint32_t endOffset; -} FatEntry; - -#endif