pmd-sky/tools/asmdiff/ntrextractfile.c
AnonymousRandomPerson 608e361e57 Initialized base repo
2023-06-28 23:35:19 -04:00

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;
}