mirror of
https://github.com/zeldaret/ph
synced 2026-05-24 23:21:37 -04:00
Append assets to ROM in predefined directory order
This commit is contained in:
+34
@@ -0,0 +1,34 @@
|
||||
/Effect/
|
||||
/Environment/
|
||||
/Event/
|
||||
/Font/
|
||||
/Map/
|
||||
/Map2D/
|
||||
/MapObj/
|
||||
/MapUnit/
|
||||
/Menu/
|
||||
/Npc/
|
||||
/Other/
|
||||
/Player/
|
||||
/Ship/
|
||||
/SoundData/
|
||||
/Test/
|
||||
/Japanese/Menu/
|
||||
/Japanese/Message/
|
||||
/English/Menu/
|
||||
/English/Message/
|
||||
/German/Menu/
|
||||
/German/Message/
|
||||
/French/Menu/
|
||||
/French/Message/
|
||||
/Italian/Menu/
|
||||
/Italian/Message/
|
||||
/Spanish/Menu/
|
||||
/Spanish/Message/
|
||||
/Japanese/
|
||||
/English/
|
||||
/French/
|
||||
/German/
|
||||
/Italian/
|
||||
/Spanish/
|
||||
/
|
||||
+75
-36
@@ -571,55 +571,83 @@ bool WriteBanner(FILE *fpRom, size_t *pAddress) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AppendAssetFiles(FILE *fpRom, size_t *pAddress, const FileTree *tree, FatEntry *entries, size_t firstFileId) {
|
||||
bool TraverseAndAppendAssets(FILE *fpRom, size_t *pAddress, const FileTree *tree, FatEntry *entries, uint16_t firstFileId) {
|
||||
size_t address = *pAddress;
|
||||
|
||||
size_t fileId = 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);
|
||||
name[entry->length] = '\0';
|
||||
if (!Align(512, fpRom, &address)) return false;
|
||||
size_t startOffset = address;
|
||||
if (!AppendFile(fpRom, name, &address, NULL)) return false;
|
||||
entries[fileId].startOffset = startOffset;
|
||||
entries[fileId].endOffset = address;
|
||||
}
|
||||
*pAddress = address;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TraverseAndAppendAssets(FILE *fpRom, size_t *pAddress, const FileTree *tree, FatEntry *entries) {
|
||||
size_t address = *pAddress;
|
||||
|
||||
// Traverse directories
|
||||
for (size_t i = 0; i < tree->numChildren; ++i) {
|
||||
FileTree *child = &tree->children[i];
|
||||
if (child->addedToFat) continue;
|
||||
FntSubEntry *entry = child->entry;
|
||||
if (!entry->isSubdir) continue;
|
||||
char name[128];
|
||||
strncpy(name, entry->name, entry->length);
|
||||
name[entry->length] = '\0';
|
||||
if (!entry->isSubdir) {
|
||||
if (!Align(512, fpRom, &address)) return false;
|
||||
size_t startOffset = address;
|
||||
if (!AppendFile(fpRom, name, &address, NULL)) return false;
|
||||
entries[fileId].startOffset = startOffset;
|
||||
entries[fileId].endOffset = address;
|
||||
++fileId;
|
||||
continue;
|
||||
}
|
||||
if (chdir(name) != 0) FATAL("Failed to enter assets directory '%s'\n", name);
|
||||
if (!TraverseAndAppendAssets(fpRom, &address, child, entries)) return false;
|
||||
if (!TraverseAndAppendAssets(fpRom, &address, child, entries, child->firstFileId)) return false;
|
||||
if (chdir("..") != 0) FATAL("Failed to leave assets directory '%s'\n", name);
|
||||
}
|
||||
|
||||
if (tree->entry != NULL) { // Directory is not root
|
||||
AppendAssetFiles(fpRom, &address, tree, entries, tree->firstFileId);
|
||||
}
|
||||
|
||||
*pAddress = address;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AppendAssets(FILE *fpRom, size_t *pAddress, const FileTree *root, FatEntry *entries, size_t numOverlays) {
|
||||
bool AppendAssets(
|
||||
FILE *fpRom,
|
||||
size_t *pAddress,
|
||||
FileTree *root,
|
||||
FatEntry *entries,
|
||||
size_t numOverlays,
|
||||
const char *assetsListFile
|
||||
) {
|
||||
size_t address = *pAddress;
|
||||
|
||||
if (!TraverseAndAppendAssets(fpRom, &address, root, entries)) return false;
|
||||
if (!AppendAssetFiles(fpRom, &address, root, entries, numOverlays)) return false;
|
||||
if (assetsListFile == NULL) {
|
||||
if (!TraverseAndAppendAssets(fpRom, &address, root, entries, numOverlays)) return false;
|
||||
*pAddress = address;
|
||||
return true;
|
||||
}
|
||||
|
||||
FILE *fp = fopen(assetsListFile, "rb");
|
||||
if (fp == NULL) FATAL("Failed to open assets list file '%s'\n", assetsListFile);
|
||||
fseek(fp, 0, SEEK_END);
|
||||
size_t listSize = ftell(fp);
|
||||
char *assetsList = malloc(listSize + 1);
|
||||
if (assetsList == NULL) FATAL("Failed to allocate string for assets list file '%s'\n", assetsListFile);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
if (fread(assetsList, listSize, 1, fp) != 1) FATAL("Failed to read from assets list file '%s'\n", assetsListFile);
|
||||
fclose(fp);
|
||||
assetsList[listSize] = '\0';
|
||||
|
||||
char assetsDir[256];
|
||||
if (getcwd(assetsDir, sizeof(assetsDir)) == NULL) FATAL("Failed to get assets directory\n");
|
||||
|
||||
char *const listEnd = assetsList + listSize;
|
||||
for (char *path = assetsList, *next; path < listEnd; path = next) {
|
||||
next = SplitLine(path);
|
||||
if (*path != '/') continue;
|
||||
FileTree *subTree = FindSubTree(root, path);
|
||||
if (subTree == NULL) continue;
|
||||
|
||||
// First file ID of root directory is given from number of overlays
|
||||
uint16_t firstFileId = subTree->entry == NULL ? numOverlays : subTree->firstFileId;
|
||||
|
||||
if (path[1] != '\0' && chdir(&path[1]) != 0) FATAL("Failed to enter assets directory '%s'\n", path);
|
||||
if (!TraverseAndAppendAssets(fpRom, &address, subTree, entries, firstFileId)) return false;
|
||||
if (chdir(assetsDir) != 0) FATAL("Failed to leave assets directory '%s'\n", path);
|
||||
|
||||
subTree->addedToFat = true;
|
||||
}
|
||||
|
||||
free(assetsList);
|
||||
|
||||
*pAddress = address;
|
||||
return true;
|
||||
@@ -695,17 +723,18 @@ void PrintUsage(const char *program) {
|
||||
printf(
|
||||
"buildrom " VERSION "\n"
|
||||
"\n"
|
||||
"Usage: %s -a BASEDIR -b BUILDDIR -r REGION -o OUTFILE [-7 ARM7BIOS]\n"
|
||||
"Usage: %s -a BASEDIR -b BUILDDIR -r REGION -o OUTFILE [-7 ARM7BIOS] [-s ASSETS]\n"
|
||||
" -a BASEDIR \tBase directory generated by extractrom\n"
|
||||
" -b BUILDDIR\tBuild directory generated by Makefile\n"
|
||||
" -r REGION \tJ = Japan, E = USA, P = Europe\n"
|
||||
" -o OUTFILE \tOutput ROM file\n"
|
||||
" -7 ARM7BIOS\tPath to ARM7 BIOS file\n",
|
||||
" -7 ARM7BIOS\tPath to ARM7 BIOS file\n"
|
||||
" -s ASSETS \tPath to assets list\n",
|
||||
program
|
||||
);
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
// --------------------- Parse command line arguments ---------------------
|
||||
const char *program = argv[0];
|
||||
@@ -717,6 +746,7 @@ int main(int argc, const char **argv) {
|
||||
const char *buildDir = NULL;
|
||||
const char *romFile = NULL;
|
||||
const char *arm7biosFile = NULL;
|
||||
char *assetsListFile = NULL;
|
||||
Region region = 0;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (strcmp(argv[i], "-o") == 0) {
|
||||
@@ -743,6 +773,12 @@ int main(int argc, const char **argv) {
|
||||
return 1;
|
||||
}
|
||||
arm7biosFile = argv[i];
|
||||
} else if (strcmp(argv[i], "-s") == 0) {
|
||||
if (++i >= argc) {
|
||||
fprintf(stderr, "Expected pathname after -s\n");
|
||||
return 1;
|
||||
}
|
||||
assetsListFile = argv[i];
|
||||
} else if (strcmp(argv[i], "-r") == 0) {
|
||||
if (++i >= argc) {
|
||||
fprintf(stderr, "Expected region after -r\n");
|
||||
@@ -818,6 +854,7 @@ int main(int argc, const char **argv) {
|
||||
|
||||
|
||||
// --------------------- Get canonical file paths ---------------------
|
||||
if (assetsListFile != NULL && !AllocFullPath(assetsListFile, &assetsListFile)) return 1;
|
||||
if (chdir(baseDir) != 0) {
|
||||
fprintf(stderr, "Failed to enter base directory '%s'\n", baseDir);
|
||||
return 1;
|
||||
@@ -875,7 +912,7 @@ int main(int argc, const char **argv) {
|
||||
// --------------------- Write file name table (FNT) ---------------------
|
||||
FileTree root;
|
||||
if (!MakeFileTree(&root)) return false;
|
||||
if (!SortFileTree(&root)) return false;
|
||||
if (!SortFileTree(&root, CompareFileTreeNormal)) return false;
|
||||
|
||||
if (!Align(512, fpRom, &address)) return 1;
|
||||
size_t numFiles = 0;
|
||||
@@ -911,7 +948,9 @@ int main(int argc, const char **argv) {
|
||||
|
||||
// --------------------- Write assets ---------------------
|
||||
if (!Align(512, fpRom, &address)) return false;
|
||||
if (!AppendAssets(fpRom, &address, &root, fatEntries, numOverlays)) return false;
|
||||
if (!SortFileTree(&root, CompareFileTreeAscii)) return false;
|
||||
if (!AppendAssets(fpRom, &address, &root, fatEntries, numOverlays, assetsListFile)) return false;
|
||||
if (assetsListFile != NULL) FreeFullPath(&assetsListFile);
|
||||
|
||||
if (!RewriteFat(fpRom, header.fileAllocs.offset, fatEntries, numFiles))
|
||||
free(fatEntries);
|
||||
|
||||
+30
-4
@@ -9,6 +9,7 @@ typedef struct FileTree {
|
||||
uint16_t numChildren;
|
||||
uint16_t maxChildren;
|
||||
uint16_t firstFileId;
|
||||
bool addedToFat;
|
||||
FntSubEntry *entry;
|
||||
} FileTree;
|
||||
|
||||
@@ -96,6 +97,7 @@ bool _FileTreeFileCallback(const char *name, bool isDir, void *userData) {
|
||||
child.numChildren = 0;
|
||||
child.maxChildren = 0;
|
||||
child.firstFileId = 0;
|
||||
child.addedToFat = false;
|
||||
child.entry = entry;
|
||||
if (!_GrowFileTreeChildren(pTree, pTree->numChildren + 1)) return false;
|
||||
memcpy(&pTree->children[pTree->numChildren], &child, sizeof(child));
|
||||
@@ -109,6 +111,7 @@ bool MakeFileTree(FileTree *pTree) {
|
||||
tree.children = NULL;
|
||||
tree.numChildren = 0;
|
||||
tree.maxChildren = 0;
|
||||
tree.addedToFat = false;
|
||||
if (!_GrowFileTreeChildren(&tree, 64)) return false;
|
||||
tree.entry = NULL;
|
||||
|
||||
@@ -127,6 +130,7 @@ bool FreeFileTree(FileTree *pTree) {
|
||||
}
|
||||
pTree->numChildren = 0;
|
||||
pTree->maxChildren = 0;
|
||||
pTree->addedToFat = false;
|
||||
if (pTree->entry != NULL) {
|
||||
free(pTree->entry);
|
||||
pTree->entry = NULL;
|
||||
@@ -134,7 +138,7 @@ bool FreeFileTree(FileTree *pTree) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int CompareFileTree(const void *a, const void *b) {
|
||||
int CompareFileTreeNormal(const void *a, const void *b) {
|
||||
FileTree *treeA = (FileTree*) a;
|
||||
FileTree *treeB = (FileTree*) b;
|
||||
|
||||
@@ -163,18 +167,40 @@ int CompareFileTree(const void *a, const void *b) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool SortFileTree(FileTree *pTree) {
|
||||
int CompareFileTreeAscii(const void *a, const void *b) {
|
||||
FileTree *treeA = (FileTree*) a;
|
||||
FileTree *treeB = (FileTree*) b;
|
||||
return strcmp(treeA->entry->name, treeB->entry->name);
|
||||
}
|
||||
|
||||
bool SortFileTree(FileTree *pTree, int (*compare)(const void*, const void*)) {
|
||||
if (pTree->numChildren <= 1) return true;
|
||||
FileTree tree;
|
||||
memcpy(&tree, pTree, sizeof(tree));
|
||||
|
||||
qsort(tree.children, tree.numChildren, sizeof(*tree.children), CompareFileTree);
|
||||
qsort(tree.children, tree.numChildren, sizeof(*tree.children), compare);
|
||||
for (size_t i = 0; i < tree.numChildren; ++i) {
|
||||
if (!SortFileTree(&tree.children[i])) return false;
|
||||
if (!SortFileTree(&tree.children[i], compare)) return false;
|
||||
}
|
||||
|
||||
memcpy(pTree, &tree, sizeof(tree));
|
||||
return true;
|
||||
}
|
||||
|
||||
FileTree* FindSubTree(FileTree *tree, const char *path) {
|
||||
if (*path == '/') ++path;
|
||||
const char *nextPath = strchr(path, '/');
|
||||
if (nextPath == NULL) return tree;
|
||||
|
||||
size_t dirNameLen = nextPath - path;
|
||||
for (size_t i = 0; i < tree->numChildren; ++i) {
|
||||
FileTree *child = &tree->children[i];
|
||||
if (child->entry->length != dirNameLen) continue;
|
||||
if (strncmp(child->entry->name, path, dirNameLen) != 0) continue;
|
||||
return FindSubTree(child, nextPath);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -85,4 +85,14 @@ void FreeFullPath(char **pFullPath) {
|
||||
*pFullPath = NULL;
|
||||
}
|
||||
|
||||
char* SplitLine(char *str) {
|
||||
while (*str != '\n' && *str != '\r') {
|
||||
if (*str == '\0') return str;
|
||||
++str;
|
||||
}
|
||||
*str++ = '\0';
|
||||
while (*str == '\n' || *str == '\r') ++str;
|
||||
return str;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user