#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