Add RomFS Building from directory to elf2nro (#5)

build_romfs, romfsdir support in elf2nro
This commit is contained in:
SciresM 2018-04-19 11:48:55 -07:00 committed by yellows8
parent 42739f52c9
commit 8b93b8598b
7 changed files with 808 additions and 5 deletions

View File

@ -1,9 +1,11 @@
# Makefile.am -- Process this file with automake to produce Makefile.in
bin_PROGRAMS = elf2nso elf2nro build_pfs0 nacptool nxlink
bin_PROGRAMS = elf2nso elf2nro build_pfs0 build_romfs nacptool nxlink
build_pfs0_SOURCES = src/build_pfs0.c src/types.h
elf2nro_SOURCES = src/elf2nro.c src/elf64.h src/elf_common.h
build_romfs_SOURCES = src/build_romfs.c src/romfs.c src/romfs.h src/filepath.c src/filepath.h src/types.h
elf2nro_SOURCES = src/elf2nro.c src/elf64.h src/romfs.c src/filepath.c src/filepath.h src/romfs.h src/elf_common.h
elf2nso_SOURCES = src/elf2nso.c src/sha256.c src/sha256.h src/elf64.h src/elf_common.h

19
src/build_romfs.c Normal file
View File

@ -0,0 +1,19 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "romfs.h"
int main(int argc, char **argv) {
if (argc != 3) {
printf("Usage: %s <in directory> <out RomFS filepath>\n", argv[0]);
return 1;
}
build_romfs_by_paths(argv[1], argv[2]);
printf("Done!\n");
return 0;
}

View File

@ -6,6 +6,7 @@
#include <lz4.h>
#include "sha256.h"
#include "elf64.h"
#include "romfs.h"
typedef uint64_t u64;
typedef uint32_t u32;
@ -79,7 +80,8 @@ int main(int argc, char* argv[]) {
fprintf(stderr, "Options:\n");
fprintf(stderr, "--icon=<iconpath> Embeds icon into the output file.\n");
fprintf(stderr, "--nacp=<control.nacp> Embeds control.nacp into the output file.\n");
fprintf(stderr, "--romfs=<image> Embeds RomFS into the output file.\n");//TODO: Use directory for input instead.
fprintf(stderr, "--romfs=<image> Embeds RomFS into the output file.\n");
fprintf(stderr, "--romfsdir=<directory> Builds and embeds RomFS into the output file.\n");
return EXIT_FAILURE;
}
@ -103,13 +105,19 @@ int main(int argc, char* argv[]) {
}
int argi;
char* icon_path = NULL, *nacp_path = NULL, *romfs_path = NULL;
char* icon_path = NULL, *nacp_path = NULL, *romfs_path = NULL, *romfs_dir_path = NULL;
for (argi=3; argi<argc; argi++) {
if (strncmp(argv[argi], "--icon=", 7)==0) icon_path = &argv[argi][7];
if (strncmp(argv[argi], "--nacp=", 7)==0) nacp_path = &argv[argi][7];
if (strncmp(argv[argi], "--romfs=", 8)==0) romfs_path = &argv[argi][8];
if (strncmp(argv[argi], "--romfsdir=", 11)==0) romfs_dir_path = &argv[argi][11];
}
if (romfs_dir_path != NULL && romfs_path != NULL) {
fprintf(stderr, "Cannot have a RomFS and a RomFS Directory at the same time!\n");
return EXIT_FAILURE;
}
if (elf_len < sizeof(Elf64_Ehdr)) {
fprintf(stderr, "Input file doesn't fit ELF header!\n");
return EXIT_FAILURE;
@ -247,6 +255,12 @@ int main(int argc, char* argv[]) {
asset_hdr.romfs.offset = tmp_off;
asset_hdr.romfs.size = romfs_len;
tmp_off+= romfs_len;
} else if (romfs_dir_path) {
asset_hdr.romfs.offset = tmp_off;
asset_hdr.romfs.size = build_romfs_by_path_into_file(romfs_dir_path, out, file_off + tmp_off);
tmp_off+= asset_hdr.romfs.size;
fseek(out, file_off, SEEK_SET);
}
fwrite(&asset_hdr, sizeof(asset_hdr), 1, out);

128
src/filepath.c Normal file
View File

@ -0,0 +1,128 @@
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include "types.h"
#include "filepath.h"
void os_strncpy(oschar_t *dst, const char *src, size_t size) {
#ifdef _WIN32
MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, size);
#else
strncpy(dst, src, size);
#endif
}
void os_strncpy_to_char(char *dst, const oschar_t *src, size_t size) {
#ifdef _WIN32
WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, size, NULL, NULL);
#else
strncpy(dst, src, size);
#endif
}
int os_makedir(const oschar_t *dir) {
#ifdef _WIN32
return _wmkdir(dir);
#else
return mkdir(dir, 0777);
#endif
}
int os_rmdir(const oschar_t *dir) {
#ifdef _WIN32
return _wrmdir(dir);
#else
return remove(dir);
#endif
}
void filepath_update(filepath_t *fpath) {
memset(fpath->os_path, 0, MAX_SWITCHPATH * sizeof(oschar_t));
os_strncpy(fpath->os_path, fpath->char_path, MAX_SWITCHPATH);
}
void filepath_init(filepath_t *fpath) {
fpath->valid = VALIDITY_INVALID;
}
void filepath_copy(filepath_t *fpath, filepath_t *copy) {
if (copy != NULL && copy->valid == VALIDITY_VALID)
memcpy(fpath, copy, sizeof(filepath_t));
else
memset(fpath, 0, sizeof(filepath_t));
}
void filepath_os_append(filepath_t *fpath, oschar_t *path) {
char tmppath[MAX_SWITCHPATH];
if (fpath->valid == VALIDITY_INVALID)
return;
memset(tmppath, 0, MAX_SWITCHPATH);
os_strncpy_to_char(tmppath, path, MAX_SWITCHPATH);
strcat(fpath->char_path, OS_PATH_SEPARATOR);
strcat(fpath->char_path, tmppath);
filepath_update(fpath);
}
void filepath_append(filepath_t *fpath, const char *format, ...) {
char tmppath[MAX_SWITCHPATH];
va_list args;
if (fpath->valid == VALIDITY_INVALID)
return;
memset(tmppath, 0, MAX_SWITCHPATH);
va_start(args, format);
vsnprintf(tmppath, sizeof(tmppath), format, args);
va_end(args);
strcat(fpath->char_path, OS_PATH_SEPARATOR);
strcat(fpath->char_path, tmppath);
filepath_update(fpath);
}
void filepath_append_n(filepath_t *fpath, uint32_t n, const char *format, ...) {
char tmppath[MAX_SWITCHPATH];
va_list args;
if (fpath->valid == VALIDITY_INVALID || n > MAX_SWITCHPATH)
return;
memset(tmppath, 0, MAX_SWITCHPATH);
va_start(args, format);
vsnprintf(tmppath, sizeof(tmppath), format, args);
va_end(args);
strcat(fpath->char_path, OS_PATH_SEPARATOR);
strncat(fpath->char_path, tmppath, n);
filepath_update(fpath);
}
void filepath_set(filepath_t *fpath, const char *path) {
if (strlen(path) < MAX_SWITCHPATH) {
fpath->valid = VALIDITY_VALID;
memset(fpath->char_path, 0, MAX_SWITCHPATH);
strncpy(fpath->char_path, path, MAX_SWITCHPATH);
filepath_update(fpath);
} else {
fpath->valid = VALIDITY_INVALID;
}
}
oschar_t *filepath_get(filepath_t *fpath) {
if (fpath->valid == VALIDITY_INVALID)
return NULL;
else
return fpath->os_path;
}

88
src/filepath.h Normal file
View File

@ -0,0 +1,88 @@
#pragma once
#include "types.h"
#include <dirent.h>
#ifdef _WIN32
#include <direct.h>
#include <wchar.h>
#endif
#define __USE_LARGEFILE64
#define MAX_SWITCHPATH 0x300
typedef enum {
VALIDITY_UNCHECKED = 0,
VALIDITY_INVALID,
VALIDITY_VALID
} validity_t;
#ifdef _MSC_VER
inline int fseeko64(FILE *__stream, long long __off, int __whence)
{
return _fseeki64(__stream, __off, __whence);
}
#elif __APPLE__ || __CYGWIN__
// OS X file I/O is 64bit
#define fseeko64 fseek
#elif __linux__
extern int fseeko64 (FILE *__stream, __off64_t __off, int __whence);
#else
/* fseeko is guaranteed by POSIX, hopefully the OS made their off_t definition 64-bit;
* known sane on FreeBSD and OpenBSD.
*/
#define fseeko64 fseeko
#endif
#ifdef _WIN32
typedef wchar_t oschar_t; /* utf-16 */
typedef _WDIR osdir_t;
typedef struct _wdirent osdirent_t;
typedef struct _stati64 os_stat64_t;
#define os_fopen _wfopen
#define os_opendir _wopendir
#define os_readdir _wreaddir
#define os_stat _wstati64
#define os_fclose fclose
#define OS_MODE_READ L"rb"
#define OS_MODE_WRITE L"wb"
#define OS_MODE_EDIT L"rb+"
#else
typedef char oschar_t; /* utf-8 */
typedef DIR osdir_t;
typedef struct dirent osdirent_t;
typedef struct stat64 os_stat64_t;
#define os_fopen fopen
#define os_opendir opendir
#define os_readdir readdir
#define os_stat stat64
#define os_fclose fclose
#define OS_MODE_READ "rb"
#define OS_MODE_WRITE "wb"
#define OS_MODE_EDIT "rb+"
#endif
#define OS_PATH_SEPARATOR "/"
typedef struct filepath {
char char_path[MAX_SWITCHPATH];
oschar_t os_path[MAX_SWITCHPATH];
validity_t valid;
} filepath_t;
void os_strncpy(oschar_t *dst, const char *src, size_t size);
void os_strncpy_to_char(char *dst, const oschar_t *src, size_t size);
int os_makedir(const oschar_t *dir);
int os_rmdir(const oschar_t *dir);
void filepath_init(filepath_t *fpath);
void filepath_copy(filepath_t *fpath, filepath_t *copy);
void filepath_os_append(filepath_t *fpath, oschar_t *path);
void filepath_append(filepath_t *fpath, const char *format, ...);
void filepath_append_n(filepath_t *fpath, uint32_t n, const char *format, ...);
void filepath_set(filepath_t *fpath, const char *path);
oschar_t *filepath_get(filepath_t *fpath);

545
src/romfs.c Normal file
View File

@ -0,0 +1,545 @@
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdio.h>
#include <dirent.h>
#include "types.h"
#include "romfs.h"
#include "filepath.h"
#include <sys/stat.h>
#include <sys/types.h>
struct romfs_fent_ctx;
typedef struct romfs_dirent_ctx {
filepath_t sum_path;
filepath_t cur_path;
uint32_t entry_offset;
struct romfs_dirent_ctx *parent; /* Parent node */
struct romfs_dirent_ctx *child; /* Child node */
struct romfs_dirent_ctx *sibling; /* Sibling node */
struct romfs_fent_ctx *file; /* File node */
struct romfs_dirent_ctx *next; /* Next node */
} romfs_dirent_ctx_t;
typedef struct romfs_fent_ctx {
filepath_t sum_path;
filepath_t cur_path;
uint32_t entry_offset;
uint64_t offset;
uint64_t size;
romfs_dirent_ctx_t *parent; /* Parent dir */
struct romfs_fent_ctx *sibling; /* Sibling file */
struct romfs_fent_ctx *next; /* Logical next file */
} romfs_fent_ctx_t;
typedef struct {
romfs_fent_ctx_t *files;
uint64_t num_dirs;
uint64_t num_files;
uint64_t dir_table_size;
uint64_t file_table_size;
uint64_t dir_hash_table_size;
uint64_t file_hash_table_size;
uint64_t file_partition_size;
} romfs_ctx_t;
typedef struct {
uint64_t header_size;
uint64_t dir_hash_table_ofs;
uint64_t dir_hash_table_size;
uint64_t dir_table_ofs;
uint64_t dir_table_size;
uint64_t file_hash_table_ofs;
uint64_t file_hash_table_size;
uint64_t file_table_ofs;
uint64_t file_table_size;
uint64_t file_partition_ofs;
} romfs_header_t;
typedef struct {
uint32_t parent;
uint32_t sibling;
uint32_t child;
uint32_t file;
uint32_t hash;
uint32_t name_size;
char name[];
} romfs_direntry_t;
typedef struct {
uint32_t parent;
uint32_t sibling;
uint64_t offset;
uint64_t size;
uint32_t hash;
uint32_t name_size;
char name[];
} romfs_fentry_t;
#define ROMFS_ENTRY_EMPTY 0xFFFFFFFF
#define ROMFS_FILEPARTITION_OFS 0x200
romfs_direntry_t *romfs_get_direntry(romfs_direntry_t *directories, uint32_t offset) {
return (romfs_direntry_t *)((char *)directories + offset);
}
romfs_fentry_t *romfs_get_fentry(romfs_fentry_t *files, uint32_t offset) {
return (romfs_fentry_t *)((char *)files + offset);
}
uint32_t calc_path_hash(uint32_t parent, const unsigned char *path, uint32_t start, size_t path_len) {
uint32_t hash = parent ^ 123456789;
for (uint32_t i = 0; i < path_len; i++) {
hash = (hash >> 5) | (hash << 27);
hash ^= path[start + i];
}
return hash;
}
uint32_t align(uint32_t offset, uint32_t alignment) {
uint32_t mask = ~(alignment-1);
return (offset + (alignment-1)) & mask;
}
uint64_t align64(uint64_t offset, uint64_t alignment) {
uint64_t mask = ~(uint64_t)(alignment-1);
return (offset + (alignment-1)) & mask;
}
uint32_t romfs_get_hash_table_count(uint32_t num_entries) {
if (num_entries < 3) {
return 3;
} else if (num_entries < 19) {
return num_entries | 1;
}
uint32_t count = num_entries;
while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 || count % 11 == 0 || count % 13 == 0 || count % 17 == 0) {
count++;
}
return count;
}
void romfs_visit_dir(romfs_dirent_ctx_t *parent, romfs_ctx_t *romfs_ctx) {
osdir_t *dir = NULL;
osdirent_t *cur_dirent = NULL;
romfs_dirent_ctx_t *child_dir_tree = NULL;
romfs_fent_ctx_t *child_file_tree = NULL;
romfs_dirent_ctx_t *cur_dir = NULL;
romfs_fent_ctx_t *cur_file = NULL;
filepath_t cur_path;
filepath_t cur_sum_path;
os_stat64_t cur_stats;
if ((dir = os_opendir(parent->sum_path.os_path)) == NULL) {
fprintf(stderr, "Failed to open directory %s!\n", parent->sum_path.char_path);
exit(EXIT_FAILURE);
}
while ((cur_dirent = os_readdir(dir))) {
filepath_init(&cur_path);
filepath_set(&cur_path, "");
filepath_os_append(&cur_path, cur_dirent->d_name);
if (strcmp(cur_path.char_path, "/.") == 0 || strcmp(cur_path.char_path, "/..") == 0) {
/* Special case . and .. */
continue;
}
filepath_copy(&cur_sum_path, &parent->sum_path);
filepath_os_append(&cur_sum_path, cur_dirent->d_name);
if (os_stat(cur_sum_path.os_path, &cur_stats) == -1) {
fprintf(stderr, "Failed to stat %s\n", cur_sum_path.char_path);
exit(EXIT_FAILURE);
}
if ((cur_stats.st_mode & S_IFMT) == S_IFDIR) {
/* Directory */
if ((cur_dir = calloc(1, sizeof(romfs_dirent_ctx_t))) == NULL) {
fprintf(stderr, "Failed to allocate RomFS directory context!\n");
exit(EXIT_FAILURE);
}
romfs_ctx->num_dirs++;
cur_dir->parent = parent;
filepath_copy(&cur_dir->sum_path, &cur_sum_path);
filepath_copy(&cur_dir->cur_path, &cur_path);
romfs_ctx->dir_table_size += 0x18 + align(strlen(cur_dir->cur_path.char_path)-1, 4);
/* Ordered insertion on sibling */
if (child_dir_tree == NULL || strcmp(cur_dir->sum_path.char_path, child_dir_tree->sum_path.char_path) < 0) {
cur_dir->sibling = child_dir_tree;
child_dir_tree = cur_dir;
} else {
romfs_dirent_ctx_t *child, *prev;
prev = child_dir_tree;
child = child_dir_tree->sibling;
while (child != NULL) {
if (strcmp(cur_dir->sum_path.char_path, child->sum_path.char_path) < 0) {
break;
}
prev = child;
child = child->sibling;
}
prev->sibling = cur_dir;
cur_dir->sibling = child;
}
/* Ordered insertion on next */
romfs_dirent_ctx_t *tmp = parent->next, *tmp_prev = parent;
while (tmp != NULL) {
if (strcmp(cur_dir->sum_path.char_path, tmp->sum_path.char_path) < 0) {
break;
}
tmp_prev = tmp;
tmp = tmp->next;
}
tmp_prev->next = cur_dir;
cur_dir->next = tmp;
cur_dir = NULL;
} else if ((cur_stats.st_mode & S_IFMT) == S_IFREG) {
/* File */
if ((cur_file = calloc(1, sizeof(romfs_fent_ctx_t))) == NULL) {
fprintf(stderr, "Failed to allocate RomFS File context!\n");
exit(EXIT_FAILURE);
}
romfs_ctx->num_files++;
cur_file->parent = parent;
filepath_copy(&cur_file->sum_path, &cur_sum_path);
filepath_copy(&cur_file->cur_path, &cur_path);
cur_file->size = cur_stats.st_size;
romfs_ctx->file_table_size += 0x20 + align(strlen(cur_file->cur_path.char_path)-1, 4);
/* Ordered insertion on sibling */
if (child_file_tree == NULL || strcmp(cur_file->sum_path.char_path, child_file_tree->sum_path.char_path) < 0) {
cur_file->sibling = child_file_tree;
child_file_tree = cur_file;
} else {
romfs_fent_ctx_t *child, *prev;
prev = child_file_tree;
child = child_file_tree->sibling;
while (child != NULL) {
if (strcmp(cur_file->sum_path.char_path, child->sum_path.char_path) < 0) {
break;
}
prev = child;
child = child->sibling;
}
prev->sibling = cur_file;
cur_file->sibling = child;
}
/* Ordered insertion on next */
if (romfs_ctx->files == NULL || strcmp(cur_file->sum_path.char_path, romfs_ctx->files->sum_path.char_path) < 0) {
cur_file->next = romfs_ctx->files;
romfs_ctx->files = cur_file;
} else {
romfs_fent_ctx_t *child, *prev;
prev = romfs_ctx->files;
child = romfs_ctx->files->next;
while (child != NULL) {
if (strcmp(cur_file->sum_path.char_path, child->sum_path.char_path) < 0) {
break;
}
prev = child;
child = child->next;
}
prev->next = cur_file;
cur_file->next = child;
}
cur_file = NULL;
} else {
fprintf(stderr, "Invalid FS object type for %s!\n", cur_path.char_path);
exit(EXIT_FAILURE);
}
}
parent->child = child_dir_tree;
parent->file = child_file_tree;
cur_dir = child_dir_tree;
while (cur_dir != NULL) {
romfs_visit_dir(cur_dir, romfs_ctx);
cur_dir = cur_dir->sibling;
}
}
size_t build_romfs_into_file(filepath_t *in_dirpath, FILE *f_out, off_t base_offset) {
romfs_dirent_ctx_t *root_ctx = calloc(1, sizeof(romfs_dirent_ctx_t));
if (root_ctx == NULL) {
fprintf(stderr, "Failed to allocate root context!\n");
exit(EXIT_FAILURE);
}
root_ctx->parent = root_ctx;
romfs_ctx_t romfs_ctx;
memset(&romfs_ctx, 0, sizeof(romfs_ctx));
filepath_copy(&root_ctx->sum_path, in_dirpath);
filepath_init(&root_ctx->cur_path);
filepath_set(&root_ctx->cur_path, "");
romfs_ctx.dir_table_size = 0x18; /* Root directory. */
romfs_ctx.num_dirs = 1;
/* Visit all directories. */
printf("Visiting directories...\n");
romfs_visit_dir(root_ctx, &romfs_ctx);
uint32_t dir_hash_table_entry_count = romfs_get_hash_table_count(romfs_ctx.num_dirs);
uint32_t file_hash_table_entry_count = romfs_get_hash_table_count(romfs_ctx.num_files);
romfs_ctx.dir_hash_table_size = 4 * dir_hash_table_entry_count;
romfs_ctx.file_hash_table_size = 4 * file_hash_table_entry_count;
romfs_header_t header;
memset(&header, 0, sizeof(header));
romfs_fent_ctx_t *cur_file = NULL;
romfs_dirent_ctx_t *cur_dir = NULL;
uint32_t entry_offset = 0;
uint32_t *dir_hash_table = malloc(romfs_ctx.dir_hash_table_size);
if (dir_hash_table == NULL) {
fprintf(stderr, "Failed to allocate directory hash table!\n");
exit(EXIT_FAILURE);
}
for (uint32_t i = 0; i < dir_hash_table_entry_count; i++) {
dir_hash_table[i] = le_word(ROMFS_ENTRY_EMPTY);
}
uint32_t *file_hash_table = malloc(romfs_ctx.file_hash_table_size);
if (file_hash_table == NULL) {
fprintf(stderr, "Failed to allocate file hash table!\n");
exit(EXIT_FAILURE);
}
for (uint32_t i = 0; i < file_hash_table_entry_count; i++) {
file_hash_table[i] = le_word(ROMFS_ENTRY_EMPTY);
}
romfs_direntry_t *dir_table = calloc(1, romfs_ctx.dir_table_size);
if (dir_table == NULL) {
fprintf(stderr, "Failed to allocate directory table!\n");
exit(EXIT_FAILURE);
}
romfs_fentry_t *file_table = calloc(1, romfs_ctx.file_table_size);
if (file_table == NULL) {
fprintf(stderr, "Failed to allocate file table!\n");
exit(EXIT_FAILURE);
}
printf("Calculating metadata...\n");
/* Determine file offsets. */
cur_file = romfs_ctx.files;
entry_offset = 0;
while (cur_file != NULL) {
romfs_ctx.file_partition_size = align64(romfs_ctx.file_partition_size, 0x10);
cur_file->offset = romfs_ctx.file_partition_size;
romfs_ctx.file_partition_size += cur_file->size;
cur_file->entry_offset = entry_offset;
entry_offset += 0x20 + align(strlen(cur_file->cur_path.char_path)-1, 4);
cur_file = cur_file->next;
}
/* Determine dir offsets. */
cur_dir = root_ctx;
entry_offset = 0;
while (cur_dir != NULL) {
cur_dir->entry_offset = entry_offset;
if (cur_dir == root_ctx) {
entry_offset += 0x18;
} else {
entry_offset += 0x18 + align(strlen(cur_dir->cur_path.char_path)-1, 4);
}
cur_dir = cur_dir->next;
}
/* Populate file tables. */
cur_file = romfs_ctx.files;
while (cur_file != NULL) {
romfs_fentry_t *cur_entry = romfs_get_fentry(file_table, cur_file->entry_offset);
cur_entry->parent = le_word(cur_file->parent->entry_offset);
cur_entry->sibling = le_word(cur_file->sibling == NULL ? ROMFS_ENTRY_EMPTY : cur_file->sibling->entry_offset);
cur_entry->offset = le_dword(cur_file->offset);
cur_entry->size = le_dword(cur_file->size);
uint32_t name_size = strlen(cur_file->cur_path.char_path)-1;
uint32_t hash = calc_path_hash(cur_file->parent->entry_offset, (unsigned char *)cur_file->cur_path.char_path, 1, name_size);
cur_entry->hash = file_hash_table[hash % file_hash_table_entry_count];
file_hash_table[hash % file_hash_table_entry_count] = le_word(cur_file->entry_offset);
cur_entry->name_size = name_size;
memcpy(cur_entry->name, cur_file->cur_path.char_path + 1, name_size);
cur_file = cur_file->next;
}
/* Populate dir tables. */
cur_dir = root_ctx;
while (cur_dir != NULL) {
romfs_direntry_t *cur_entry = romfs_get_direntry(dir_table, cur_dir->entry_offset);
cur_entry->parent = le_word(cur_dir->parent->entry_offset);
cur_entry->sibling = le_word(cur_dir->sibling == NULL ? ROMFS_ENTRY_EMPTY : cur_dir->sibling->entry_offset);
cur_entry->child = le_word(cur_dir->child == NULL ? ROMFS_ENTRY_EMPTY : cur_dir->child->entry_offset);
cur_entry->file = le_word(cur_dir->file == NULL ? ROMFS_ENTRY_EMPTY : cur_dir->file->entry_offset);
uint32_t name_size = (cur_dir == root_ctx) ? 0 : strlen(cur_dir->cur_path.char_path)-1;
uint32_t hash = calc_path_hash((cur_dir == root_ctx) ? 0 : cur_dir->parent->entry_offset, (unsigned char *)cur_dir->cur_path.char_path, 1, name_size);
cur_entry->hash = dir_hash_table[hash % dir_hash_table_entry_count];
dir_hash_table[hash % dir_hash_table_entry_count] = le_word(cur_dir->entry_offset);
cur_entry->name_size = name_size;
memcpy(cur_entry->name, cur_dir->cur_path.char_path + 1, name_size);
romfs_dirent_ctx_t *temp = cur_dir;
cur_dir = cur_dir->next;
free(temp);
}
header.header_size = le_dword(sizeof(header));
header.file_hash_table_size = le_dword(romfs_ctx.file_hash_table_size);
header.file_table_size = le_dword(romfs_ctx.file_table_size);
header.dir_hash_table_size = le_dword(romfs_ctx.dir_hash_table_size);
header.dir_table_size = le_dword(romfs_ctx.dir_table_size);
header.file_partition_ofs = le_dword(ROMFS_FILEPARTITION_OFS);
/* Abuse of endianness follows. */
uint64_t dir_hash_table_ofs = align64(romfs_ctx.file_partition_size + ROMFS_FILEPARTITION_OFS, 4);
header.dir_hash_table_ofs = dir_hash_table_ofs;
header.dir_table_ofs = header.dir_hash_table_ofs + romfs_ctx.dir_hash_table_size;
header.file_hash_table_ofs = header.dir_table_ofs + romfs_ctx.dir_table_size;
header.file_table_ofs = header.file_hash_table_ofs + romfs_ctx.file_hash_table_size;
header.dir_hash_table_ofs = le_dword(header.dir_hash_table_ofs);
header.dir_table_ofs = le_dword(header.dir_table_ofs);
header.file_hash_table_ofs = le_dword(header.file_hash_table_ofs);
header.file_table_ofs = le_dword(header.file_table_ofs);
fseeko64(f_out, base_offset, SEEK_SET);
fwrite(&header, 1, sizeof(header), f_out);
/* Write files. */
unsigned char *buffer = malloc(0x400000);
if (buffer == NULL) {
fprintf(stderr, "Failed to allocate work buffer!\n");
exit(EXIT_FAILURE);
}
cur_file = romfs_ctx.files;
while (cur_file != NULL) {
FILE *f_in = os_fopen(cur_file->sum_path.os_path, OS_MODE_READ);
if (f_in == NULL) {
fprintf(stderr, "Failed to open %s!\n", cur_file->sum_path.char_path);
exit(EXIT_FAILURE);
}
printf("Writing %s to RomFS image...\n", cur_file->sum_path.char_path);
fseeko64(f_out, base_offset + cur_file->offset + ROMFS_FILEPARTITION_OFS, SEEK_SET);
uint64_t offset = 0;
uint64_t read_size = 0x400000;
while (offset < cur_file->size) {
if (cur_file->size - offset < read_size) {
read_size = cur_file->size - offset;
}
if (fread(buffer, 1, read_size, f_in) != read_size) {
fprintf(stderr, "Failed to read from %s!\n", cur_file->sum_path.char_path);
exit(EXIT_FAILURE);
}
if (fwrite(buffer, 1, read_size, f_out) != read_size) {
fprintf(stderr, "Failed to write to output!\n");
exit(EXIT_FAILURE);
}
offset += read_size;
}
os_fclose(f_in);
romfs_fent_ctx_t *temp = cur_file;
cur_file = cur_file->next;
free(temp);
}
free(buffer);
fseeko64(f_out, base_offset + dir_hash_table_ofs, SEEK_SET);
if (fwrite(dir_hash_table, 1, romfs_ctx.dir_hash_table_size, f_out) != romfs_ctx.dir_hash_table_size) {
fprintf(stderr, "Failed to write dir hash table!\n");
exit(EXIT_FAILURE);
}
free(dir_hash_table);
if (fwrite(dir_table, 1, romfs_ctx.dir_table_size, f_out) != romfs_ctx.dir_table_size) {
fprintf(stderr, "Failed to write dir table!\n");
exit(EXIT_FAILURE);
}
free(dir_table);
if (fwrite(file_hash_table, 1, romfs_ctx.file_hash_table_size, f_out) != romfs_ctx.file_hash_table_size) {
fprintf(stderr, "Failed to write file hash table!\n");
exit(EXIT_FAILURE);
}
free(file_hash_table);
if (fwrite(file_table, 1, romfs_ctx.file_table_size, f_out) != romfs_ctx.file_table_size) {
fprintf(stderr, "Failed to write file table!\n");
exit(EXIT_FAILURE);
}
free(file_table);
return dir_hash_table_ofs + romfs_ctx.dir_hash_table_size + romfs_ctx.dir_table_size + romfs_ctx.file_hash_table_size + romfs_ctx.file_table_size;
}
size_t build_romfs(filepath_t *in_dirpath, filepath_t *out_romfspath) {
FILE *f_out = NULL;
if ((f_out = os_fopen(out_romfspath->os_path, OS_MODE_WRITE)) == NULL) {
fprintf(stderr, "Failed to open %s!\n", out_romfspath->char_path);
exit(EXIT_FAILURE);
}
size_t sz = build_romfs_into_file(in_dirpath, f_out, 0);
fclose(f_out);
return sz;
}
size_t build_romfs_by_paths(char *dir, char *out_fn) {
filepath_t dirpath;
filepath_t outpath;
filepath_init(&dirpath);
filepath_init(&outpath);
filepath_set(&dirpath, dir);
filepath_set(&outpath, out_fn);
return build_romfs(&dirpath, &outpath);
}
size_t build_romfs_by_path_into_file(char *dir, FILE *f_out, off_t offset) {
filepath_t dirpath;
filepath_init(&dirpath);
filepath_set(&dirpath, dir);
return build_romfs_into_file(&dirpath, f_out, offset);
}

7
src/romfs.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include "filepath.h"
size_t build_romfs_by_paths(char *dir, char *out_fn);
size_t build_romfs_by_path_into_file(char *dir, FILE *f_out, off_t base_offset);