diff --git a/tools/rom/build.c b/tools/rom/build.c index 205ef821..d6494ede 100644 --- a/tools/rom/build.c +++ b/tools/rom/build.c @@ -220,7 +220,52 @@ bool Align(size_t alignment, FILE *fpRom, size_t *pAddress) { return true; } -bool WriteArm9Overlays(FILE *fpRom, size_t *pAddress, size_t *pNumOverlays, FatEntry *entries, size_t maxEntries) { +// Writes the ARM9 overlay table and returns the entries to `*pEntries`. `*pEntries` is allocated using `malloc`, so remember +// to `free` it. +bool WriteArm9OverlayTable(FILE *fpRom, size_t *pAddress, const Header *pHeader, const char *overlayDataFile, OverlayEntry **pEntries) { + size_t address = *pAddress; + + FILE *fp = fopen(ARM9_OVERLAY_TABLE_FILE, "rb"); + if (fp == NULL) FATAL("Failed to open ARM9 overlay table '" ARM9_OVERLAY_TABLE_FILE "'\n"); + fseek(fp, 0, SEEK_END); + size_t tableSize = ftell(fp); + if (tableSize % sizeof(OverlayEntry) != 0) { + FATAL("ARM9 overlay table has an invalid size (entries must be %d bytes long)\n", sizeof(OverlayEntry)); + } + size_t numEntries = tableSize / sizeof(OverlayEntry); + fseek(fp, 0, SEEK_SET); + + OverlayEntry *entries = malloc(tableSize); + if (entries == NULL) FATAL("Failed to allocate array for ARM9 overlay table entries"); + if (fread(entries, tableSize, 1, fp) != 1) FATAL("Failed to read ARM9 overlay table '" ARM9_OVERLAY_TABLE_FILE "'\n"); + fclose(fp); + + fp = fopen(overlayDataFile, "rb"); + if (fp == NULL) FATAL("Failed to open ARM9 overlay data file '%s'\n", overlayDataFile); + fseek(fp, 0, SEEK_END); + size_t dataSize = ftell(fp); + if (dataSize != numEntries * sizeof(OverlayData)) { + FATAL("ARM9 data file has an invalid size (expected %d entries with %d bytes each)\n", numEntries, sizeof(OverlayData)); + } + fseek(fp, 0, SEEK_SET); + + OverlayData *data = malloc(dataSize); + if (data == NULL) FATAL("Failed to allocate array for ARM9 overlay data entries\n"); + if (fread(data, dataSize, 1, fp) != 1) FATAL("Failed to read ARM9 overlay data '%s'\n", overlayDataFile); + fclose(fp); + for (size_t i = 0; i < numEntries; ++i) { + entries[i].fileId = data[i].fileId; + } + free(data); + + if (fwrite(entries, tableSize, 1, fpRom) != 1) FATAL("Failed to write ARM9 overlay table\n"); + + *pAddress = address; + *pEntries = entries; + return true; +} + +bool WriteArm9Overlays(FILE *fpRom, size_t *pAddress, size_t *pNumOverlays, FatEntry *fatEntries, OverlayEntry *table) { size_t address = *pAddress; uint32_t ovNum = 0; char fileName[32]; @@ -231,9 +276,12 @@ bool WriteArm9Overlays(FILE *fpRom, size_t *pAddress, size_t *pNumOverlays, FatE sprintf(fileName, "ov%02d.lz", ovNum); if (!Align(512, fpRom, &address)) return false; size_t startOffset = address; - if (!AppendFile(fpRom, fileName, &address, NULL)) break; - entries[ovNum].startOffset = startOffset; - entries[ovNum].endOffset = address; + uint32_t fileSize = 0; + if (!AppendFile(fpRom, fileName, &address, &fileSize)) break; + table[ovNum].compressedSize = fileSize; + table[ovNum].isCompressed = true; + fatEntries[ovNum].startOffset = startOffset; + fatEntries[ovNum].endOffset = address; ovNum += 1; } @@ -244,6 +292,14 @@ bool WriteArm9Overlays(FILE *fpRom, size_t *pAddress, size_t *pNumOverlays, FatE return true; } +bool RewriteArm9OverlayTable(FILE *fpRom, const Header *header, OverlayEntry *entries, size_t numEntries) { + size_t prevAddress = ftell(fpRom); + fseek(fpRom, header->arm9Overlays.offset, SEEK_SET); + if (fwrite(entries, sizeof(*entries), numEntries, fpRom) != numEntries) FATAL("Failed to rewrite ARM9 overlay table\n"); + fseek(fpRom, prevAddress, SEEK_SET); + return true; +} + typedef struct { FntEntry *table; uint16_t tableSize; @@ -606,6 +662,8 @@ void PrintUsage(const char *program) { } int main(int argc, const char **argv) { + + // --------------------- Parse command line arguments --------------------- const char *program = argv[0]; if (argc == 1) { PrintUsage(program); @@ -677,6 +735,8 @@ int main(int argc, const char **argv) { return 1; } + + // --------------------- Set up --------------------- char rootDir[256]; if (getcwd(rootDir, sizeof(rootDir)) == NULL) { fprintf(stderr, "Failed to get root directory\n"); @@ -697,6 +757,8 @@ int main(int argc, const char **argv) { GenerateCrcTable(); + + // --------------------- Write initial header --------------------- BuildInfo info; info.region = region; size_t address = 0; @@ -710,6 +772,21 @@ int main(int argc, const char **argv) { } address += sizeof(header); + + // --------------------- Get canonical file paths --------------------- + if (chdir(baseDir) != 0) { + fprintf(stderr, "Failed to enter base directory '%s'\n", baseDir); + return 1; + } + char *arm9overlayDataFile = NULL; + if (!AllocFullPath(ARM9_OVERLAY_DATA_FILE, &arm9overlayDataFile)) return 1; + if (chdir(rootDir) != 0) { + fprintf(stderr, "Failed to leave base directory '%s'\n", baseDir); + return 1; + } + + + // --------------------- Write ARM9 program --------------------- if (chdir(buildDir) != 0) { fprintf(stderr, "Failed to enter build directory '%s'\n", buildDir); return 1; @@ -720,13 +797,21 @@ int main(int argc, const char **argv) { if (!AppendFile(fpRom, ARM9_PROGRAM_FILE, &address, &header.arm9.size)) return 1; if (!AppendFile(fpRom, ARM9_FOOTER_FILE, &address, NULL)) return 1; + + // --------------------- Write ARM9 overlay table --------------------- if (!Align(512, fpRom, &address)) return 1; header.arm9Overlays.offset = address; - if (!AppendFile(fpRom, ARM9_OVERLAY_TABLE_FILE, &address, &header.arm9Overlays.size)) return 1; + OverlayEntry *arm9overlays; + if (!WriteArm9OverlayTable(fpRom, &address, &header, arm9overlayDataFile, &arm9overlays)) return 1; + FreeFullPath(&arm9overlayDataFile); + + // --------------------- Write ARM9 overlays --------------------- FatEntry overlayEntries[MAX_OVERLAYS]; size_t numOverlays = 0; - if (!WriteArm9Overlays(fpRom, &address, &numOverlays, overlayEntries, MAX_OVERLAYS)) return 1; + if (!WriteArm9Overlays(fpRom, &address, &numOverlays, overlayEntries, arm9overlays)) return 1; + if (!RewriteArm9OverlayTable(fpRom, &header, arm9overlays, numOverlays)) return 1; + free(arm9overlays); if (chdir(rootDir) != 0) { fprintf(stderr, "Failed to leave build directory '%s'\n", buildDir); @@ -738,6 +823,8 @@ int main(int argc, const char **argv) { return 1; } + + // --------------------- Write ARM7 program --------------------- if (!Align(512, fpRom, &address)) return 1; header.arm7.offset = address; if (!AppendFile(fpRom, ARM7_PROGRAM_FILE, &address, &header.arm7.size)) return 1; @@ -747,6 +834,8 @@ int main(int argc, const char **argv) { return 1; } + + // --------------------- Write file name table (FNT) --------------------- FileTree root; if (!MakeFileTree(&root)) return false; if (!SortFileTree(&root)) return false; @@ -757,6 +846,8 @@ int main(int argc, const char **argv) { if (!WriteFnt(fpRom, &address, &root, numOverlays, &numFiles)) return 1; header.fileNames.size = address - header.fileNames.offset; + + // --------------------- Write file allocation table (FAT) --------------------- if (!Align(512, fpRom, &address)) return 1; header.fileAllocs.offset = address; if (!WriteFat(fpRom, &address, numFiles)) return 1; @@ -769,6 +860,8 @@ int main(int argc, const char **argv) { return 1; } + + // --------------------- Write banner --------------------- if (!Align(512, fpRom, &address)) return false; header.bannerOffset = address; if (!WriteBanner(fpRom, &address)) return false; @@ -778,6 +871,8 @@ int main(int argc, const char **argv) { return 1; } + + // --------------------- Write assets --------------------- if (!Align(512, fpRom, &address)) return false; if (!AppendAssets(fpRom, &address, &root, fatEntries, numOverlays)) return false; @@ -795,15 +890,19 @@ int main(int argc, const char **argv) { fprintf(stderr, "Failed to leave base directory '%s'\n", baseDir); return 1; } - - char *arm7bios = NULL; - if (arm7biosFile != NULL && !AllocFullPath(arm7biosFile, &arm7bios)) return 1; - + + + // --------------------- Write padding --------------------- header.romSize = address; header.capacity = 15 - __builtin_clz(header.romSize); size_t romEnd = 1 << (17 + header.capacity); if (!Align(romEnd, fpRom, &address)) return 1; + + // --------------------- Update header --------------------- + char *arm7bios = NULL; + if (arm7biosFile != NULL && !AllocFullPath(arm7biosFile, &arm7bios)) return 1; + if (chdir(buildDir) != 0) { fprintf(stderr, "Failed to enter build directory '%s'\n", buildDir); return 1; diff --git a/tools/rom/extract.c b/tools/rom/extract.c index 6e8dd445..b69d234f 100644 --- a/tools/rom/extract.c +++ b/tools/rom/extract.c @@ -130,6 +130,24 @@ bool ExtractAssets(const uint8_t *rom, const uint8_t *fatStart, const uint8_t *f 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 *fp = fopen(ARM9_OVERLAY_DATA_FILE, "wb"); + if (fp == NULL) FATAL("Failed to open overlay data file '" ARM9_OVERLAY_DATA_FILE "'\n"); + for(; entry < end; ++entry) { + OverlayData data; + data.fileId = entry->fileId; + if (fwrite(&data, sizeof(data), 1, fp) != 1) { + FATAL("Failed to write overlay data to '" ARM9_OVERLAY_DATA_FILE "'\n"); + } + } + + fclose(fp); + return true; +} + void PrintUsage(const char *program) { printf( "extractrom " VERSION "\n" @@ -224,6 +242,8 @@ int main(int argc, const char **argv) { return 1; } + if (!ExtractOverlayData(rom, pHeader)) return 1; + if (chdir("..") != 0) { fprintf(stderr, "Failed to leave output directory '%s'\n", outDir); return 1; diff --git a/tools/rom/ph.h b/tools/rom/ph.h index 05d472a9..8cde8a52 100644 --- a/tools/rom/ph.h +++ b/tools/rom/ph.h @@ -20,6 +20,7 @@ #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" @@ -34,4 +35,8 @@ typedef struct { Region region; } BuildInfo; +typedef struct { + uint32_t fileId; +} OverlayData; + #endif diff --git a/tools/rom/rom.h b/tools/rom/rom.h index 165e8107..fa30ad89 100644 --- a/tools/rom/rom.h +++ b/tools/rom/rom.h @@ -64,6 +64,19 @@ typedef struct { /* 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;