From 44931d43a7526cf5be48ed2b90d416640262cf9c Mon Sep 17 00:00:00 2001 From: Aetias Date: Sat, 14 Oct 2023 12:38:22 +0200 Subject: [PATCH] Write FAT and asset files --- tools/rom/build.c | 118 +++++++++++++++++++++++++++++++++++++++------- tools/rom/files.h | 64 +++++++++++++------------ tools/rom/util.h | 2 +- 3 files changed, 136 insertions(+), 48 deletions(-) diff --git a/tools/rom/build.c b/tools/rom/build.c index 92e71530..0407dab5 100644 --- a/tools/rom/build.c +++ b/tools/rom/build.c @@ -15,6 +15,7 @@ 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, @@ -148,7 +149,7 @@ bool Align(size_t alignment, FILE *fpRom, size_t *pAddress) { return true; } -bool WriteArm9Overlays(FILE *fpRom, size_t *pAddress, size_t *pNumOverlays) { +bool WriteArm9Overlays(FILE *fpRom, size_t *pAddress, size_t *pNumOverlays, FatEntry *entries, size_t maxEntries) { size_t address = *pAddress; uint32_t ovNum = 0; char fileName[32]; @@ -158,8 +159,10 @@ bool WriteArm9Overlays(FILE *fpRom, size_t *pAddress, size_t *pNumOverlays) { while (true) { sprintf(fileName, "ov%02d.lz", ovNum); if (!Align(256, fpRom, &address)) return false; - // TODO (aetias): Store start and end address in FAT + size_t startOffset = address; if (!AppendFile(fpRom, fileName, &address, NULL)) return false; + entries[ovNum].startOffset = startOffset; + entries[ovNum].endOffset = address; } if (chdir("..") != 0) FATAL("Failed to leave overlays directory '" OVERLAYS_SUBDIR "'\n"); @@ -216,7 +219,7 @@ bool GrowFntSubtable(FntContext *pContext, size_t growSize) { return true; } -bool WriteFntSubtable(FntTree *tree, FntContext *pContext) { +bool WriteFntSubtable(FileTree *tree, FntContext *pContext) { FntContext ctx; memcpy(&ctx, pContext, sizeof(ctx)); size_t subtableStart = ctx.subtableSize; @@ -224,7 +227,7 @@ bool WriteFntSubtable(FntTree *tree, FntContext *pContext) { // Create initial subtable entries size_t numFiles = 0; for (size_t i = 0; i < tree->numChildren; ++i) { - FntTree *child = &tree->children[i]; + FileTree *child = &tree->children[i]; FntSubEntry *entry = child->entry; if (!entry->isSubdir) numFiles += 1; @@ -242,11 +245,12 @@ bool WriteFntSubtable(FntTree *tree, FntContext *pContext) { // Recurse child directories for (size_t i = 0; i < tree->numChildren; ++i) { - FntTree *child = &tree->children[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; @@ -268,7 +272,7 @@ bool WriteFntSubtable(FntTree *tree, FntContext *pContext) { // Update subdir IDs size_t subtableOffset = 0; for (size_t i = 0; i < tree->numChildren; ++i) { - FntTree *child = &tree->children[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); @@ -279,7 +283,7 @@ bool WriteFntSubtable(FntTree *tree, FntContext *pContext) { return true; } -bool WriteFnt(FILE *fpRom, size_t *pAddress, FntTree *pRoot, size_t firstFileId) { +bool WriteFnt(FILE *fpRom, size_t *pAddress, FileTree *pRoot, size_t firstFileId, size_t *pNumFiles) { size_t address = *pAddress; FntContext ctx; @@ -309,11 +313,85 @@ bool WriteFnt(FILE *fpRom, size_t *pAddress, FntTree *pRoot, size_t firstFileId) ctx.table[i].subtableOffset += tableLength; } if (fwrite(ctx.table, sizeof(FntEntry), ctx.tableSize, fpRom) != ctx.tableSize) FATAL("Failed to write FNT table\n"); + address += ctx.tableSize * sizeof(FntEntry); if (fwrite(ctx.subtable, ctx.subtableSize, 1, fpRom) != 1) FATAL("Failed to write FNT subtables\n"); + address += ctx.subtableSize; free(ctx.table); free(ctx.subtable); + *pAddress = address; + *pNumFiles = ctx.nextFileId; + return true; +} + +typedef struct { + FatEntry *entries; +} FatContext; + +bool AppendAssets(FILE *fpRom, size_t *pAddress, const FileTree *tree, FatContext *pCtx) { + size_t address = *pAddress; + FatContext ctx; + memcpy(&ctx, pCtx, sizeof(ctx)); + + // Traverse directories + for (size_t i = 0; i < tree->numChildren; ++i) { + FileTree *child = &tree->children[i]; + FntSubEntry *entry = child->entry; + if (!entry->isSubdir) continue; + char name[128]; + strncpy(name, entry->name, entry->length); + if (chdir(name) != 0) FATAL("Failed to enter assets directory '%s'\n", name); + if (!AppendAssets(fpRom, &address, child, &ctx)) return false; + if (chdir("..") != 0) FATAL("Failed to leave assets directory '%s'\n", name); + } + + // Append files + size_t fileId = tree->firstFileId; + for (size_t i = 0; i < tree->numChildren; ++i, ++fileId) { + FileTree *child = &tree->children[i]; + FntSubEntry *entry = child->entry; + if (entry->isSubdir) continue; + char name[128]; + strncpy(name, entry->name, entry->length); + if (!Align(256, fpRom, &address)) return false; + size_t startOffset = address; + if (!AppendFile(fpRom, name, &address, NULL)) return false; + ctx.entries[fileId].startOffset = startOffset; + ctx.entries[fileId].endOffset = address; + } + + *pAddress = address; + memcpy(pCtx, &ctx, sizeof(ctx)); + return true; +} + +bool WriteFat(FILE *fpRom, size_t *pAddress, const FileTree *root, size_t numFiles, FatEntry *overlayEntries, size_t numOverlays) { + size_t address = *pAddress; + size_t fatStart = address; + + FatEntry blank; + blank.startOffset = 0; + blank.endOffset = 0; + for (size_t i = 0; i < numFiles; ++i) { + if (fwrite(&blank, sizeof(blank), 1, fpRom) != 1) FATAL("Failed to write blank placeholder FAT entry\n"); + } + address += sizeof(blank) * numFiles; + + FatContext ctx; + ctx.entries = malloc(numFiles * sizeof(*ctx.entries)); + if (ctx.entries == NULL) FATAL("Failed to allocate FAT entries, %d needed\n", numFiles); + memcpy(ctx.entries, overlayEntries, numOverlays * sizeof(*overlayEntries)); + + if (!Align(256, fpRom, &address)) return false; + if (!AppendAssets(fpRom, &address, root, &ctx)) return false; + + fseek(fpRom, fatStart, SEEK_SET); + if (fwrite(ctx.entries, sizeof(*ctx.entries), numFiles, fpRom) != numFiles) FATAL("Failed to write FAT table\n"); + fseek(fpRom, 0, SEEK_END); + + free(ctx.entries); + *pAddress = address; return true; } @@ -443,8 +521,9 @@ int main(int argc, const char **argv) { header.arm9Overlays.offset = address; if (!AppendFile(fpRom, ARM9_OVERLAY_TABLE_FILE, &address, &header.arm9Overlays.size)) return 1; + FatEntry overlayEntries[MAX_OVERLAYS]; size_t numOverlays = 0; - if (!WriteArm9Overlays(fpRom, &address, &numOverlays)) return 1; + if (!WriteArm9Overlays(fpRom, &address, &numOverlays, &overlayEntries, MAX_OVERLAYS)) return 1; if (chdir(rootDir) != 0) { fprintf(stderr, "Failed to leave build directory '%s'\n", buildDir); @@ -460,21 +539,22 @@ int main(int argc, const char **argv) { header.arm7.offset = address; if (!AppendFile(fpRom, ARM7_PROGRAM_FILE, &address, &header.arm7.size)) return 1; - FntTree root; - if (!MakeFntTree(&root)) return false; - if (!SortFntTree(&root)) return false; + FileTree root; + if (!MakeFileTree(&root)) return false; + if (!SortFileTree(&root)) return false; if (!Align(256, fpRom, &address)) return 1; + size_t numFiles = 0; header.fileNames.offset = address; - if (!WriteFnt(fpRom, &address, &root, numOverlays)) return 1; + if (!WriteFnt(fpRom, &address, &root, numOverlays, &numFiles)) return 1; + header.fileNames.size = address - header.fileNames.offset; if (!Align(256, fpRom, &address)) return 1; header.fileAllocs.offset = address; - // TODO (aetias): Write initial FAT + if (!WriteFat(fpRom, &address, &root, numFiles, overlayEntries, numOverlays)) return 1; + header.fileAllocs.offset = address - header.fileAllocs.offset; - // TODO (aetias): Write files - - if (!FreeFntTree(&root)) return false; + if (!FreeFileTree(&root)) return false; if (chdir(rootDir) != 0) { fprintf(stderr, "Failed to leave assets directory '%s'\n", assetsDir); @@ -484,6 +564,12 @@ int main(int argc, const char **argv) { size_t romEnd = 1 << (32 - __builtin_clz(address)); if (!Align(romEnd, fpRom, &address)) return 1; + fseek(fpRom, 0, SEEK_SET); + if (fwrite(&header, sizeof(header), 1, fpRom) != 1) { + fprintf(stderr, "Failed to rewrite header\n"); + return 1; + } + free(readBuffer); flose(fpRom); free(rootDir); diff --git a/tools/rom/files.h b/tools/rom/files.h index 5e0c44dd..b25811e6 100644 --- a/tools/rom/files.h +++ b/tools/rom/files.h @@ -4,7 +4,7 @@ #include "util.h" #include "rom.h" -bool MakeFntTree(FntTree *pTree); +bool MakeFileTree(FileTree *pTree); bool IterFiles(bool (*callback)(const char *name, bool isDir, void*), void *userData) { #ifdef _WIN32 @@ -29,15 +29,16 @@ bool IterFiles(bool (*callback)(const char *name, bool isDir, void*), void *user #endif } -typedef struct FntTree { - struct FntTree *children; - size_t numChildren; - size_t maxChildren; +typedef struct FileTree { + struct FileTree *children; + uint16_t numChildren; + uint16_t maxChildren; + uint16_t firstFileId; FntSubEntry *entry; -} FntTree; +} FileTree; -bool _GrowFntTreeChildren(FntTree *pTree, size_t minChildren) { - FntTree tree; +bool _GrowFileTreeChildren(FileTree *pTree, size_t minChildren) { + FileTree tree; memcpy(&tree, pTree, sizeof(tree)); if (tree.numChildren >= minChildren) return true; @@ -45,12 +46,12 @@ bool _GrowFntTreeChildren(FntTree *pTree, size_t minChildren) { if (newSize == 0) newSize = minChildren; while (newSize < minChildren) newSize *= 2; if (tree.children == NULL) { - FntTree *children = malloc(newSize * sizeof(FntTree)); - if (children == NULL) FATAL("Failed to allocate FNT tree children\n"); + FileTree *children = malloc(newSize * sizeof(FileTree)); + if (children == NULL) FATAL("Failed to allocate file tree children\n"); tree.children = children; } else { - FntTree *children = realloc(tree.children, newSize * sizeof(FntTree)); - if (children == NULL) FATAL("Failed to reallocate FNT tree children\n"); + 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; @@ -59,8 +60,8 @@ bool _GrowFntTreeChildren(FntTree *pTree, size_t minChildren) { return true; } -bool _FntTreeFileCallback(const char *name, bool isDir, void *userData) { - FntTree *pTree = (FntTree*) userData; +bool _FileTreeFileCallback(const char *name, bool isDir, void *userData) { + FileTree *pTree = (FileTree*) userData; size_t nameLength = strlen(name); if (isDir) { @@ -71,15 +72,15 @@ bool _FntTreeFileCallback(const char *name, bool isDir, void *userData) { memcpy(entry->name, name, nameLength); WRITE_SUBDIR_ID(entry, 0); - if (chdir(name) != 0) FATAL("Failed to enter FNT directory '%s'\n", name); + if (chdir(name) != 0) FATAL("Failed to enter directory '%s'\n", name); - FntTree child; - if (!MakeFntTree(&child)) return false; + FileTree child; + if (!MakeFileTree(&child)) return false; child.entry = entry; memcpy(&pTree->children[pTree->numChildren], &child, sizeof(child)); pTree->numChildren += 1; - if (chdir("..") != 0) FATAL("Failed to leave FNT directory '%s'\n", name); + if (chdir("..") != 0) FATAL("Failed to leave directory '%s'\n", name); } else { FntSubEntry *entry = malloc(sizeof(FntSubEntry) + nameLength); if (entry == NULL) FATAL("Failed to allocate FNT sub entry for file '%s'\n", name); @@ -87,32 +88,33 @@ bool _FntTreeFileCallback(const char *name, bool isDir, void *userData) { entry->length = nameLength; memcpy(entry->name, name, nameLength); - FntTree child; + FileTree child; child.children = NULL; child.numChildren = 0; child.maxChildren = 0; + child.firstFileId = 0; child.entry = entry; - if (!_GrowFntTreeChildren(pTree, pTree->numChildren + 1)) return false; + if (!_GrowFileTreeChildren(pTree, pTree->numChildren + 1)) return false; memcpy(&pTree->children[pTree->numChildren], &child, sizeof(child)); pTree->numChildren += 1; } } -bool MakeFntTree(FntTree *pTree) { - FntTree tree; +bool MakeFileTree(FileTree *pTree) { + FileTree tree; tree.maxChildren = 0; tree.numChildren = 0; - if (!_GrowFntTreeChildren(&tree, 64)) return false; + if (!_GrowFileTreeChildren(&tree, 64)) return false; tree.entry = NULL; - if (!IterFiles(_FntTreeFileCallback, &tree)) return false; + if (!IterFiles(_FileTreeFileCallback, &tree)) return false; memcpy(pTree, &tree, sizeof(tree)); return true; } -bool FreeFntTree(FntTree *pTree) { +bool FreeFileTree(FileTree *pTree) { for (size_t i = 0; i < pTree->numChildren; ++i) { - if (!FreeFntTree(&pTree->children[i])) return false; + if (!FreeFileTree(&pTree->children[i])) return false; } if (pTree->children != NULL) { free(pTree->children); @@ -126,7 +128,7 @@ bool FreeFntTree(FntTree *pTree) { } } -int CompareFntTree(const FntTree *a, const FntTree *b) { +int CompareFileTree(const FileTree *a, const FileTree *b) { size_t lenA = a->entry->length; size_t lenB = b->entry->length; size_t minSize = (lenA < lenB) ? lenA : lenB; @@ -137,13 +139,13 @@ int CompareFntTree(const FntTree *a, const FntTree *b) { return 0; } -bool SortFntTree(FntTree *pTree) { - FntTree tree; +bool SortFileTree(FileTree *pTree) { + FileTree tree; memcpy(&tree, pTree, sizeof(tree)); - qsort(tree.children, tree.numChildren, sizeof(*tree.children), CompareFntTree); + qsort(tree.children, tree.numChildren, sizeof(*tree.children), CompareFileTree); for (size_t i = 0; i < tree.numChildren; ++i) { - if (!SortFntTree(&tree.children[i])) return false; + if (!SortFileTree(&tree.children[i])) return false; } memcpy(pTree, &tree, sizeof(tree)); diff --git a/tools/rom/util.h b/tools/rom/util.h index 5d60cb4b..0ef47173 100644 --- a/tools/rom/util.h +++ b/tools/rom/util.h @@ -5,7 +5,7 @@ #include #include -#define FATAL(...) do { fprintf(stderr, __VA_ARGS__); return false; } while (0); +#define FATAL(...) do { fprintf(stderr, __VA_ARGS__); return false; } while (0) #define WRITE16(buf,val) do { ((char*) buf)[0] = (val) & 0xFF; ((char*) buf)[1] = ((val) >> 8) & 0xFF; } while (0) #define WRITE24(buf,val) do { ((char*) buf)[0] = (val) & 0xFF; ((char*) buf)[1] = ((val) >> 8) & 0xFF; ((char*) buf)[2] = ((val) >> 16) & 0xFF; } while (0)