mirror of
https://github.com/zeldaret/ph
synced 2026-05-24 07:10:52 -04:00
Read entire asset file tree
This commit is contained in:
+20
-20
@@ -195,22 +195,18 @@ bool GrowFntSubtable(FntContext *pContext, size_t growSize) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteFntSubtable(FILE *fpRom, size_t *pAddress, FntContext *pContext) {
|
||||
bool WriteFntSubtable(FILE *fpRom, size_t *pAddress, FntTree *tree, FntContext *pContext) {
|
||||
size_t address = *pAddress;
|
||||
FntSubEntry *entries[MAX_DIR_SIZE];
|
||||
size_t numEntries;
|
||||
|
||||
FntContext ctx;
|
||||
memcpy(&ctx, pContext, sizeof(ctx));
|
||||
size_t subtableStart = ctx.subtableSize;
|
||||
|
||||
if (!GetFiles(entries, MAX_DIR_SIZE, &numEntries)) return false;
|
||||
qsort(entries, numEntries, sizeof(FntSubEntry*), CompareFnt);
|
||||
|
||||
// Create initial subtable entries
|
||||
size_t numFiles = 0;
|
||||
for (size_t i = 0; i < numEntries; ++i) {
|
||||
FntSubEntry *entry = entries[i];
|
||||
for (size_t i = 0; i < tree->numChildren; ++i) {
|
||||
FntTree *child = &tree->children[i];
|
||||
FntSubEntry *entry = child->entry;
|
||||
if (!entry->isSubdir) numFiles += 1;
|
||||
|
||||
size_t entrySize = sizeof(*entry) + entry->length + (entry->isSubdir ? 2 : 0);
|
||||
@@ -226,8 +222,9 @@ bool WriteFntSubtable(FILE *fpRom, size_t *pAddress, FntContext *pContext) {
|
||||
ctx.subtableSize += 1;
|
||||
|
||||
// Recurse child directories
|
||||
for (size_t i = 0; i < numEntries; ++i) {
|
||||
FntSubEntry *entry = entries[i];
|
||||
for (size_t i = 0; i < tree->numChildren; ++i) {
|
||||
FntTree *child = &tree->children[i];
|
||||
FntSubEntry *entry = child->entry;
|
||||
if (!entry->isSubdir) continue;
|
||||
uint16_t subdirId = 0xf000 | ctx.tableSize;
|
||||
WRITE_SUBDIR_ID(entry, subdirId);
|
||||
@@ -246,30 +243,27 @@ bool WriteFntSubtable(FILE *fpRom, size_t *pAddress, FntContext *pContext) {
|
||||
|
||||
char name[128];
|
||||
strncpy(name, entry->name, entry->length);
|
||||
if (chdir(name) != 0) FATAL("Failed to enter assets subdirectory '%s'\n", name);
|
||||
if (!WriteFntSubtable(fpRom, &address, &ctx)) return false;
|
||||
if (chdir("..") != 0) FATAL("Failed to leave assets subdirectory '%s'\n", name);
|
||||
if (!WriteFntSubtable(fpRom, &address, child, &ctx)) return false;
|
||||
|
||||
ctx.parentId = oldParentId;
|
||||
}
|
||||
|
||||
// Update subdir IDs
|
||||
size_t subtableOffset = 0;
|
||||
for (size_t i = 0; i < numEntries; ++i) {
|
||||
FntSubEntry *entry = entries[i];
|
||||
for (size_t i = 0; i < tree->numChildren; ++i) {
|
||||
FntTree *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;
|
||||
}
|
||||
|
||||
FreeFiles(entries, numEntries);
|
||||
|
||||
memcpy(pContext, &ctx, sizeof(ctx));
|
||||
*pAddress = address;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteFnt(FILE *fpRom, size_t *pAddress, size_t firstFileId) {
|
||||
bool WriteFnt(FILE *fpRom, size_t *pAddress, FntTree *pRoot, size_t firstFileId) {
|
||||
size_t address = *pAddress;
|
||||
|
||||
FntContext ctx;
|
||||
@@ -281,7 +275,7 @@ bool WriteFnt(FILE *fpRom, size_t *pAddress, size_t firstFileId) {
|
||||
ctx.subtableSize = 0;
|
||||
ctx.subtableMax = INITIAL_SUBTABLE_SIZE;
|
||||
|
||||
if (!WriteFntSubtable(fpRom, &address, &ctx)) return false;
|
||||
if (!WriteFntSubtable(fpRom, &address, pRoot, &ctx)) return false;
|
||||
free(ctx.subtable);
|
||||
|
||||
*pAddress = address;
|
||||
@@ -430,9 +424,13 @@ 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;
|
||||
|
||||
if (!Align(256, fpRom, &address)) return 1;
|
||||
header.fileNames.offset = address;
|
||||
if (!WriteFnt(fpRom, &address, numOverlays)) return 1;
|
||||
if (!WriteFnt(fpRom, &address, &root, numOverlays)) return 1;
|
||||
|
||||
if (!Align(256, fpRom, &address)) return 1;
|
||||
header.fileAllocs.offset = address;
|
||||
@@ -440,6 +438,8 @@ int main(int argc, const char **argv) {
|
||||
|
||||
// TODO (aetias): Write files
|
||||
|
||||
if (!FreeFntTree(&root)) return false;
|
||||
|
||||
if (chdir(rootDir) != 0) {
|
||||
fprintf(stderr, "Failed to leave assets directory '%s'\n", assetsDir);
|
||||
return 1;
|
||||
|
||||
+129
-47
@@ -4,68 +4,150 @@
|
||||
#include "util.h"
|
||||
#include "rom.h"
|
||||
|
||||
bool GetFiles(FntSubEntry **entries, size_t maxLength, size_t *pLength) {
|
||||
bool MakeFntTree(FntTree *pTree);
|
||||
|
||||
bool IterFiles(bool (*callback)(const char *name, bool isDir, void*), void *userData) {
|
||||
#ifdef _WIN32
|
||||
size_t length = 0;
|
||||
WIN32_FIND_DATA findData;
|
||||
WIN32_FIND_DATAA findData;
|
||||
HANDLE hFind = FindFirstFileA("*", &findData);
|
||||
if (hFind == INVALID_HANDLE_VALUE) FATAL("Failed to open directory to get files\n");
|
||||
if (hFind == INVALID_HANDLE_VALUE) FATAL("Failed to open directory to iterate files\n");
|
||||
do {
|
||||
if (length >= maxLength) FATAL("Max file entries surpassed\n");
|
||||
size_t nameLength = strlen(findData.cFileName);
|
||||
if (nameLength > 127) FATAL("File name '%s' longer than 127 characters\n", findData.cFileName);
|
||||
|
||||
bool isSubdir = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
size_t extraSize = isSubdir ? 2 : 0;
|
||||
FntSubEntry *entry = (FntSubEntry*) malloc(sizeof(FntSubEntry) + nameLength + extraSize);
|
||||
entry->isSubdir = isSubdir;
|
||||
entry->length = nameLength;
|
||||
memcpy(entry->name, findData.cFileName, nameLength);
|
||||
WRITE_SUBDIR_ID(entry, 0);
|
||||
|
||||
entries[length] = entry;
|
||||
length += 1;
|
||||
const char *name = findData.cFileName;
|
||||
bool isDir = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
if (!callback(name, isDir, userData)) return false;
|
||||
} while (FindNextFileA(hFind, &findData));
|
||||
FindClose(hFind);
|
||||
*pLength = length;
|
||||
return true;
|
||||
#elif __linux__
|
||||
size_t length = 0;
|
||||
#else __linux__
|
||||
DIR *dir = opendir(".");
|
||||
if (dir == NULL) FATAL("Failed to open directory to get files\n");
|
||||
struct dirent *dirent;
|
||||
while ((dirent = readdir(dir)) != NULL) {
|
||||
if (length >= maxLength) FATAL("Max file entries surpassed\n");
|
||||
size_t nameLength = strlen(dirent->d_name);
|
||||
if (nameLength > 127) FATAL("File name '%s' longer than 127 characters\n", dirent->d_name);
|
||||
|
||||
bool isSubdir = dirent->d_type == DT_DIR;
|
||||
size_t extraSize = isSubdir ? 2 : 0;
|
||||
FntSubEntry *entry = (FntSubEntry*) malloc(sizeof(FntSubEntry) + nameLength + extraSize);
|
||||
entry->isSubdir = isSubdir;
|
||||
entry->length = nameLength;
|
||||
memcpy(entry->name, findData.cFileName, nameLength);
|
||||
|
||||
entries[length] = entry;
|
||||
length += 1;
|
||||
struct dirent entry;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
const char *name = entry->d_name;
|
||||
bool isDir = entry->d_type == DT_DIR;
|
||||
if (!callback(name, isDir, userData)) return false;
|
||||
}
|
||||
closedir(dir);
|
||||
*pLength = length;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool FreeFiles(FntSubEntry **pEntries, size_t length) {
|
||||
for (size_t i = 0; i < length; ++i) free(pEntries[i]);
|
||||
typedef struct FntTree {
|
||||
struct FntTree *children;
|
||||
size_t numChildren;
|
||||
size_t maxChildren;
|
||||
FntSubEntry *entry;
|
||||
} FntTree;
|
||||
|
||||
bool _GrowFntTreeChildren(FntTree *pTree, size_t minChildren) {
|
||||
FntTree tree;
|
||||
memcpy(&tree, pTree, sizeof(tree));
|
||||
if (tree.numChildren >= minChildren) return true;
|
||||
|
||||
size_t newSize = tree.maxChildren;
|
||||
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");
|
||||
tree.children = children;
|
||||
} else {
|
||||
FntTree *children = realloc(tree.children, newSize * sizeof(FntTree));
|
||||
if (children == NULL) FATAL("Failed to reallocate FNT tree children\n");
|
||||
tree.children = children;
|
||||
}
|
||||
tree.maxChildren = newSize;
|
||||
|
||||
memcpy(pTree, &tree, sizeof(tree));
|
||||
return true;
|
||||
}
|
||||
|
||||
int CompareFnt(const FntSubEntry *a, const FntSubEntry *b) {
|
||||
size_t minSize = ((a->length < b->length) ? a : b)->length;
|
||||
int cmp = strncmp(a->name, b->name, minSize);
|
||||
bool _FntTreeFileCallback(const char *name, bool isDir, void *userData) {
|
||||
FntTree *pTree = (FntTree*) 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 (chdir(name) != 0) FATAL("Failed to enter FNT directory '%s'\n", name);
|
||||
|
||||
FntTree child;
|
||||
if (!MakeFntTree(&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);
|
||||
} 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);
|
||||
|
||||
FntTree child;
|
||||
child.children = NULL;
|
||||
child.numChildren = 0;
|
||||
child.maxChildren = 0;
|
||||
child.entry = entry;
|
||||
if (!_GrowFntTreeChildren(pTree, pTree->numChildren + 1)) return false;
|
||||
memcpy(&pTree->children[pTree->numChildren], &child, sizeof(child));
|
||||
pTree->numChildren += 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool MakeFntTree(FntTree *pTree) {
|
||||
FntTree tree;
|
||||
tree.maxChildren = 0;
|
||||
tree.numChildren = 0;
|
||||
if (!_GrowFntTreeChildren(&tree, 64)) return false;
|
||||
tree.entry = NULL;
|
||||
|
||||
if (!IterFiles(_FntTreeFileCallback, &tree)) return false;
|
||||
memcpy(pTree, &tree, sizeof(tree));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FreeFntTree(FntTree *pTree) {
|
||||
for (size_t i = 0; i < pTree->numChildren; ++i) {
|
||||
if (!FreeFntTree(&pTree->children[i])) return false;
|
||||
}
|
||||
if (pTree->children != NULL) {
|
||||
free(pTree->children);
|
||||
pTree->children = NULL;
|
||||
}
|
||||
pTree->numChildren = 0;
|
||||
pTree->maxChildren = 0;
|
||||
if (pTree->entry != NULL) {
|
||||
free(pTree->entry);
|
||||
pTree->entry = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int CompareFntTree(const FntTree *a, const FntTree *b) {
|
||||
size_t lenA = a->entry->length;
|
||||
size_t lenB = b->entry->length;
|
||||
size_t minSize = (lenA < lenB) ? lenA : lenB;
|
||||
int cmp = strncmp(a->entry->name, b->entry->name, minSize);
|
||||
if (cmp != 0) return cmp;
|
||||
if (a->length < b->length) return -1;
|
||||
if (a->length > b->length) return 1;
|
||||
if (lenA < lenB) return -1;
|
||||
if (lenA > lenB) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool SortFntTree(FntTree *pTree) {
|
||||
FntTree tree;
|
||||
memcpy(&tree, pTree, sizeof(tree));
|
||||
|
||||
qsort(tree.children, tree.numChildren, sizeof(*tree.children), CompareFntTree);
|
||||
for (size_t i = 0; i < tree.numChildren; ++i) {
|
||||
if (!SortFntTree(&tree.children[i])) return false;
|
||||
}
|
||||
|
||||
memcpy(pTree, &tree, sizeof(tree));
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user