mirror of
https://github.com/zeldaret/ph
synced 2026-05-24 07:10:52 -04:00
Write FAT and asset files
This commit is contained in:
+102
-16
@@ -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);
|
||||
|
||||
+33
-31
@@ -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));
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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)
|
||||
|
||||
Reference in New Issue
Block a user