mirror of
https://github.com/switchbrew/switch-tools.git
synced 2025-06-21 13:32:39 +02:00
Add RomFS Building from directory to elf2nro (#5)
build_romfs, romfsdir support in elf2nro
This commit is contained in:
parent
42739f52c9
commit
8b93b8598b
@ -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
19
src/build_romfs.c
Normal 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;
|
||||
}
|
@ -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
128
src/filepath.c
Normal 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
88
src/filepath.h
Normal 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
545
src/romfs.c
Normal 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
7
src/romfs.h
Normal 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);
|
Loading…
Reference in New Issue
Block a user