mirror of
https://github.com/switchbrew/switch-tools.git
synced 2025-06-22 14:02:40 +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
|
# 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
|
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
|
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 <lz4.h>
|
||||||
#include "sha256.h"
|
#include "sha256.h"
|
||||||
#include "elf64.h"
|
#include "elf64.h"
|
||||||
|
#include "romfs.h"
|
||||||
|
|
||||||
typedef uint64_t u64;
|
typedef uint64_t u64;
|
||||||
typedef uint32_t u32;
|
typedef uint32_t u32;
|
||||||
@ -79,7 +80,8 @@ int main(int argc, char* argv[]) {
|
|||||||
fprintf(stderr, "Options:\n");
|
fprintf(stderr, "Options:\n");
|
||||||
fprintf(stderr, "--icon=<iconpath> Embeds icon into the output file.\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, "--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;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,11 +105,17 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int argi;
|
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++) {
|
for (argi=3; argi<argc; argi++) {
|
||||||
if (strncmp(argv[argi], "--icon=", 7)==0) icon_path = &argv[argi][7];
|
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], "--nacp=", 7)==0) nacp_path = &argv[argi][7];
|
||||||
if (strncmp(argv[argi], "--romfs=", 8)==0) romfs_path = &argv[argi][8];
|
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)) {
|
if (elf_len < sizeof(Elf64_Ehdr)) {
|
||||||
@ -247,6 +255,12 @@ int main(int argc, char* argv[]) {
|
|||||||
asset_hdr.romfs.offset = tmp_off;
|
asset_hdr.romfs.offset = tmp_off;
|
||||||
asset_hdr.romfs.size = romfs_len;
|
asset_hdr.romfs.size = romfs_len;
|
||||||
tmp_off+= 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);
|
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