mirror of
https://github.com/pret/pmd-sky.git
synced 2026-03-21 17:25:15 -05:00
154 lines
4.4 KiB
C
154 lines
4.4 KiB
C
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
|
|
#ifndef NDEBUG
|
|
#ifdef _MSC_VER
|
|
#define debug_printf(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
|
|
#else
|
|
#define debug_printf(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
|
|
#endif //_MSC_VER
|
|
#else
|
|
#define debug_printf(fmt, ...) ((void)0)
|
|
#endif //NDEBUG
|
|
|
|
#define FIND_FAIL (-1u)
|
|
|
|
struct NtrDirHeader {
|
|
unsigned offset;
|
|
unsigned short first_file;
|
|
unsigned short count_or_parent;
|
|
};
|
|
|
|
static char FILEBUF[BUFSIZ];
|
|
|
|
unsigned find_file(struct NtrDirHeader * fnt, const char * cfilename) {
|
|
unsigned file_id = fnt->first_file;
|
|
char * filename = strdup(cfilename);
|
|
char * tokenizer = filename;
|
|
int found = 0;
|
|
char * tree = (char *)fnt + fnt->offset;
|
|
char * token;
|
|
|
|
while ((token = strtok(tokenizer, "/")) != NULL) {
|
|
tokenizer = NULL;
|
|
debug_printf("TOKEN: %s\n", token);
|
|
long toklen = strlen(token);
|
|
while (*tree) {
|
|
char flag = *tree++;
|
|
#ifndef NDEBUG
|
|
char *entname = malloc((flag & 0x7F) + 1);
|
|
*stpncpy(entname, tree, flag & 0x7F) = 0;
|
|
debug_printf("testing entry %s...", entname);
|
|
free(entname);
|
|
#endif //NDEBUG
|
|
if ((toklen != (flag & 0x7F)) || strncmp(tree, token, toklen) != 0) {
|
|
debug_printf("no; is %s\n", (flag & 0x80) ? "dir" : "file");
|
|
// Next entry
|
|
tree += (flag & 0x7F);
|
|
if (flag & 0x80) {
|
|
tree += 2; // skip dir id
|
|
}
|
|
else {
|
|
file_id++;
|
|
}
|
|
}
|
|
else {
|
|
debug_printf("yes; is %s\n", (flag & 0x80) ? "dir" : "file");
|
|
tree += (flag & 0x7F);
|
|
if (flag & 0x80) {
|
|
// navigate to next dir
|
|
unsigned short dir_id = (unsigned char) tree[0] | ((unsigned char) tree[1] << 8);
|
|
file_id = fnt[dir_id & 0xFFF].first_file;
|
|
tree = (char *)fnt + fnt[dir_id & 0xFFF].offset;
|
|
break;
|
|
}
|
|
else {
|
|
found = 1;
|
|
token = strtok(NULL, "/");
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
free(filename);
|
|
if (!found || token != NULL) {
|
|
file_id = FIND_FAIL;
|
|
}
|
|
return file_id;
|
|
}
|
|
|
|
int main(int argc, char ** argv) {
|
|
if (argc < 3) {
|
|
fprintf(stderr, "missing required argument: %s\n", (argc == 1) ? "BASEROM" : "FILENAME");
|
|
return 1;
|
|
}
|
|
FILE *baserom = fopen(argv[1], "rb");
|
|
if (baserom == NULL) {
|
|
fprintf(stderr, "unable to open ROM %s for reading\n", argv[1]);
|
|
return 1;
|
|
}
|
|
debug_printf("opened baserom\n");
|
|
|
|
// fnt offset, fnt size, fat offset, fat size
|
|
unsigned offsets[4];
|
|
fseek(baserom, 64, SEEK_SET);
|
|
if (fread(offsets, 4, 4, baserom) != 4) {
|
|
fprintf(stderr, "failed to read from baserom\n");
|
|
fclose(baserom);
|
|
return 1;
|
|
}
|
|
debug_printf("read offsets\n");
|
|
|
|
// read fnt
|
|
struct NtrDirHeader *fnt = malloc(offsets[1]);
|
|
if (fnt == NULL) {
|
|
fprintf(stderr, "unable to allocate FNT buffer\n");
|
|
fclose(baserom);
|
|
return 1;
|
|
}
|
|
fseek(baserom, offsets[0], SEEK_SET);
|
|
if (fread(fnt, 1, offsets[1], baserom) != offsets[1]) {
|
|
fprintf(stderr, "unable to read FNT\n");
|
|
free(fnt);
|
|
fclose(baserom);
|
|
return 1;
|
|
}
|
|
debug_printf("read fnt\n");
|
|
|
|
unsigned file_id = find_file(fnt, argv[2]);
|
|
free(fnt);
|
|
if (file_id == FIND_FAIL) {
|
|
fprintf(stderr, "file not found");
|
|
fclose(baserom);
|
|
return 1;
|
|
}
|
|
debug_printf("found file with id %u\n", file_id);
|
|
|
|
// Extract the file to stdout
|
|
if (8 * file_id >= offsets[3]) {
|
|
fprintf(stderr, "nitrofs lookup failed");
|
|
fclose(baserom);
|
|
return 1;
|
|
}
|
|
|
|
FILE *out = fdopen(dup(fileno(stdout)), "wb");
|
|
fseek(baserom, offsets[2] + 8 * file_id, SEEK_SET);
|
|
fread(offsets, 4, 2, baserom);
|
|
fseek(baserom, offsets[0], SEEK_SET);
|
|
while (offsets[1] - offsets[0] > BUFSIZ) {
|
|
fread(FILEBUF, 1, BUFSIZ, baserom);
|
|
fwrite(FILEBUF, 1, BUFSIZ, out);
|
|
offsets[0] += BUFSIZ;
|
|
}
|
|
fread(FILEBUF, 1, offsets[1] - offsets[0], baserom);
|
|
fwrite(FILEBUF, 1, offsets[1] - offsets[0], out);
|
|
fclose(out);
|
|
|
|
fclose(baserom);
|
|
return 0;
|
|
}
|