libnx/tools/build_pfs0.c

227 lines
4.6 KiB
C

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <dirent.h>
#include "types.h"
//Build with: gcc -o build_pfs0 build_pfs0.c
#define MAX_FS_ENTRIES 0x10//If ever needed, this constant and the size of stringtable can be increased.
typedef struct {
u32 magicnum;//"PFS0"
u32 file_count;
u32 stringtable_size;
u32 val_xc;//"Zero/Reserved"
} pfs0_header;
typedef struct {
u64 offset;
u64 size;
u32 stringtable_offset;
u32 val_x14;//"Normally zero?"
} pfs0_fsentry;
int build_pfs0(char *in_dirpath, char *out_pfs0_filepath)
{
DIR *dir = NULL;
struct dirent *cur_dirent = NULL;
struct stat objstats;
FILE *fout = NULL, *fin = NULL;
int ret=0;
u32 tmplen=0;
u32 pos;
u8 *tmpbuf;
u32 objcount = 0;
u32 stringtable_offset=0;
u64 filedata_reloffset=0;
pfs0_header header;
pfs0_fsentry fsentries[MAX_FS_ENTRIES];
pfs0_fsentry *fsentry;
char objpath[256];
char stringtable[0x100];
memset(&header, 0, sizeof(header));
memset(fsentries, 0, sizeof(fsentries));
memset(stringtable, 0, sizeof(stringtable));
dir = opendir(in_dirpath);
if(dir==NULL)
{
printf("Failed to open dirpath.\n");
return 1;
}
fout = fopen(out_pfs0_filepath, "wb");
if(fout==NULL)
{
printf("Failed to open PFS0 filepath.\n");
closedir(dir);
return 1;
}
while((cur_dirent = readdir(dir)))
{
if(strcmp(cur_dirent->d_name, ".")==0 || strcmp(cur_dirent->d_name, "..")==0)continue;
memset(objpath, 0, sizeof(objpath));
snprintf(objpath, sizeof(objpath)-1, "%s%s", in_dirpath, cur_dirent->d_name);
if(stat(objpath, &objstats)==-1)
{
printf("Failed to stat: %s\n", objpath);
ret = 2;
break;
}
if((objstats.st_mode & S_IFMT) == S_IFDIR)//directory
{
printf("Directories aren't supported, skipping... (%s)\n", objpath);
}
else if((objstats.st_mode & S_IFMT) == S_IFREG)//file
{
if(objcount>=MAX_FS_ENTRIES)
{
printf("Maximum fs object count already reached.\n");
ret = 3;
break;
}
fsentry = &fsentries[objcount];
fsentry->offset = filedata_reloffset;
fsentry->size = objstats.st_size;
filedata_reloffset+= fsentry->size;
fsentry->stringtable_offset = stringtable_offset;
tmplen = strlen(cur_dirent->d_name)+1;
if(stringtable_offset+tmplen > sizeof(stringtable))
{
printf("Max size of stringtable reached.\n");
ret = 4;
break;
}
strncpy(&stringtable[stringtable_offset], cur_dirent->d_name, sizeof(stringtable)-stringtable_offset);
stringtable_offset+= tmplen;
objcount++;
}
else
{
printf("Invalid FS object type.\n");
ret = 14;
break;
}
}
closedir(dir);
if(ret==0)
{
stringtable_offset = (stringtable_offset+0x1f) & ~0x1f;
header.magicnum = le_word(0x30534650);
header.file_count = le_word(objcount);
header.stringtable_size = le_word(stringtable_offset);
fwrite(&header, 1, sizeof(header), fout);
fwrite(fsentries, 1, sizeof(pfs0_fsentry)*objcount, fout);
fwrite(stringtable, 1, stringtable_offset, fout);
stringtable_offset = 0;
for(pos=0; pos<objcount; pos++)
{
tmplen = strlen(&stringtable[stringtable_offset]);
if(tmplen==0)
{
printf("Empty string entry found in stringtable.\n");
ret = 5;
break;
}
tmplen++;
if(stringtable_offset+tmplen > sizeof(stringtable))
{
printf("Max size of stringtable reached during stringtable entry reading.\n");
ret = 4;
break;
}
memset(objpath, 0, sizeof(objpath));
snprintf(objpath, sizeof(objpath)-1, "%s%s", in_dirpath, &stringtable[stringtable_offset]);
stringtable_offset+=tmplen;
fin = fopen(objpath, "rb");
if(fin==NULL)
{
printf("Failed to open filepath for filedata.\n");
ret = 1;
break;
}
tmpbuf = malloc(fsentries[pos].size);
if(tmpbuf==NULL)
{
printf("Failed to allocate filedata.\n");
ret = 6;
fclose(fin);
break;
}
tmplen = fread(tmpbuf, 1, fsentries[pos].size, fin);
fclose(fin);
fwrite(tmpbuf, 1, fsentries[pos].size, fout);
free(tmpbuf);
}
}
fclose(fout);
return ret;
}
int main(int argc, char **argv)
{
int ret = 0;
u32 pos;
char dirpath[256];
if(argc < 3)
{
printf("%s\n", argv[0]);
printf("Build a PFS0 from an input directory.\n");
printf("Usage:\n");
printf("%s <in directory> <out PFS0 filepath>\n", argv[0]);
return 0;
}
pos = strlen(argv[1]);
if(pos==0 || pos>sizeof(dirpath)-1)
{
printf("Dirpath is length is invalid.\n");
return 1;
}
pos--;
memset(dirpath, 0, sizeof(dirpath));
strncpy(dirpath, argv[1], sizeof(dirpath)-1);
if(dirpath[pos] != '/')dirpath[pos+1] = '/';
ret = build_pfs0(dirpath, argv[2]);
if(ret)printf("Failed to build PFS0.\n");
return 0;
}