mirror of
https://github.com/zeldaret/ph
synced 2026-05-24 07:10:52 -04:00
171 lines
5.4 KiB
C
171 lines
5.4 KiB
C
#ifndef __FILES_H
|
|
#define __FILES_H
|
|
|
|
#include "util.h"
|
|
#include "rom.h"
|
|
|
|
typedef struct FileTree {
|
|
struct FileTree *children;
|
|
uint16_t numChildren;
|
|
uint16_t maxChildren;
|
|
uint16_t firstFileId;
|
|
FntSubEntry *entry;
|
|
} FileTree;
|
|
|
|
bool MakeFileTree(FileTree *pTree);
|
|
|
|
bool IterFiles(bool (*callback)(const char *name, bool isDir, void*), void *userData) {
|
|
#ifdef _WIN32
|
|
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 __linux__
|
|
DIR *dir = opendir(".");
|
|
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);
|
|
#endif
|
|
}
|
|
|
|
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 (chdir(name) != 0) FATAL("Failed to enter directory '%s'\n", name);
|
|
|
|
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 (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);
|
|
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.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;
|
|
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;
|
|
if (pTree->entry != NULL) {
|
|
free(pTree->entry);
|
|
pTree->entry = NULL;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int CompareFileTree(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;
|
|
|
|
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;
|
|
}
|
|
if (lenA < lenB) return -1;
|
|
if (lenA > lenB) return 1;
|
|
return 0;
|
|
}
|
|
|
|
bool SortFileTree(FileTree *pTree) {
|
|
if (pTree->numChildren <= 1) return true;
|
|
FileTree tree;
|
|
memcpy(&tree, pTree, sizeof(tree));
|
|
|
|
qsort(tree.children, tree.numChildren, sizeof(*tree.children), CompareFileTree);
|
|
for (size_t i = 0; i < tree.numChildren; ++i) {
|
|
if (!SortFileTree(&tree.children[i])) return false;
|
|
}
|
|
|
|
memcpy(pTree, &tree, sizeof(tree));
|
|
return true;
|
|
}
|
|
|
|
#endif
|