mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-22 18:26:39 +00:00
romfs: slightly improve interface.
This commit is contained in:
parent
c910fe6c0a
commit
d8c0984115
6 changed files with 270 additions and 97 deletions
|
@ -99,14 +99,16 @@ static void read_thread_func(void *arg)
|
|||
goto end;
|
||||
}
|
||||
|
||||
u64 file_table_offset = 0;
|
||||
u64 file_table_size = shared_data->romfs_ctx->file_table_size;
|
||||
RomFileSystemFileEntry *file_entry = NULL;
|
||||
|
||||
char path[FS_MAX_PATH] = {0};
|
||||
sprintf(path, "sdmc:/romfs");
|
||||
|
||||
while(file_table_offset < file_table_size)
|
||||
/* Reset current file table offset. */
|
||||
romfsResetFileTableOffset(shared_data->romfs_ctx);
|
||||
|
||||
/* Loop through all file entries. */
|
||||
while(romfsCanMoveToNextFileEntry(shared_data->romfs_ctx))
|
||||
{
|
||||
/* Check if the transfer has been cancelled by the user. */
|
||||
if (shared_data->transfer_cancelled)
|
||||
|
@ -130,7 +132,7 @@ static void read_thread_func(void *arg)
|
|||
}
|
||||
|
||||
/* Retrieve RomFS file entry information. */
|
||||
shared_data->read_error = (!(file_entry = romfsGetFileEntryByOffset(shared_data->romfs_ctx, file_table_offset)) || \
|
||||
shared_data->read_error = (!(file_entry = romfsGetCurrentFileEntry(shared_data->romfs_ctx)) || \
|
||||
!romfsGeneratePathFromFileEntry(shared_data->romfs_ctx, file_entry, path + 11, FS_MAX_PATH - 11, RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly));
|
||||
if (shared_data->read_error)
|
||||
{
|
||||
|
@ -197,7 +199,13 @@ static void read_thread_func(void *arg)
|
|||
|
||||
if (shared_data->read_error || shared_data->write_error || shared_data->transfer_cancelled) break;
|
||||
|
||||
file_table_offset += ALIGN_UP(sizeof(RomFileSystemFileEntry) + file_entry->name_length, 4);
|
||||
/* Move to the next file entry. */
|
||||
shared_data->read_error = !romfsMoveToNextFileEntry(shared_data->romfs_ctx);
|
||||
if (shared_data->read_error)
|
||||
{
|
||||
condvarWakeAll(&g_writeCondvar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait until the previous file data chunk has been written. */
|
||||
|
|
|
@ -160,7 +160,6 @@ static void dumpRomFs(TitleInfo *info, NcaFsSectionContext *nca_fs_ctx)
|
|||
{
|
||||
if (!buf || !info || !nca_fs_ctx) return;
|
||||
|
||||
u64 romfs_file_table_offset = 0;
|
||||
RomFileSystemContext romfs_ctx = {0};
|
||||
RomFileSystemFileEntry *romfs_file_entry = NULL;
|
||||
|
||||
|
@ -178,12 +177,12 @@ static void dumpRomFs(TitleInfo *info, NcaFsSectionContext *nca_fs_ctx)
|
|||
utilsCreateDirectoryTree(path, true);
|
||||
path_len = strlen(path);
|
||||
|
||||
while(romfs_file_table_offset < romfs_ctx.file_table_size)
|
||||
while(romfsCanMoveToNextFileEntry(&romfs_ctx))
|
||||
{
|
||||
if (!(romfs_file_entry = romfsGetFileEntryByOffset(&romfs_ctx, romfs_file_table_offset)) || \
|
||||
if (!(romfs_file_entry = romfsGetCurrentFileEntry(&romfs_ctx)) || \
|
||||
!romfsGeneratePathFromFileEntry(&romfs_ctx, romfs_file_entry, path + path_len, sizeof(path) - path_len, RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly))
|
||||
{
|
||||
consolePrint("romfs get entry / generate path failed for 0x%lX!\n", romfs_file_table_offset);
|
||||
consolePrint("romfs get entry / generate path failed for 0x%lX!\n", romfs_ctx.cur_file_offset);
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
@ -215,7 +214,11 @@ static void dumpRomFs(TitleInfo *info, NcaFsSectionContext *nca_fs_ctx)
|
|||
fclose(filefd);
|
||||
filefd = NULL;
|
||||
|
||||
romfs_file_table_offset += ALIGN_UP(sizeof(RomFileSystemFileEntry) + romfs_file_entry->name_length, 4);
|
||||
if (!romfsMoveToNextFileEntry(&romfs_ctx))
|
||||
{
|
||||
consolePrint("failed to move to next file entry!\n");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
consolePrint("romfs dump complete\n");
|
||||
|
|
|
@ -100,12 +100,14 @@ static void read_thread_func(void *arg)
|
|||
goto end;
|
||||
}
|
||||
|
||||
u64 file_table_offset = 0;
|
||||
u64 file_table_size = shared_data->romfs_ctx->file_table_size;
|
||||
RomFileSystemFileEntry *file_entry = NULL;
|
||||
char path[FS_MAX_PATH] = {0};
|
||||
|
||||
while(file_table_offset < file_table_size)
|
||||
/* Reset current file table offset. */
|
||||
romfsResetFileTableOffset(shared_data->romfs_ctx);
|
||||
|
||||
/* Loop through all file entries. */
|
||||
while(romfsCanMoveToNextFileEntry(shared_data->romfs_ctx))
|
||||
{
|
||||
/* Check if the transfer has been cancelled by the user */
|
||||
if (shared_data->transfer_cancelled)
|
||||
|
@ -115,7 +117,7 @@ static void read_thread_func(void *arg)
|
|||
}
|
||||
|
||||
/* Retrieve RomFS file entry information */
|
||||
shared_data->read_error = (!(file_entry = romfsGetFileEntryByOffset(shared_data->romfs_ctx, file_table_offset)) || \
|
||||
shared_data->read_error = (!(file_entry = romfsGetCurrentFileEntry(shared_data->romfs_ctx)) || \
|
||||
!romfsGeneratePathFromFileEntry(shared_data->romfs_ctx, file_entry, path, FS_MAX_PATH, RomFileSystemPathIllegalCharReplaceType_IllegalFsChars));
|
||||
if (shared_data->read_error)
|
||||
{
|
||||
|
@ -178,7 +180,13 @@ static void read_thread_func(void *arg)
|
|||
|
||||
if (shared_data->read_error || shared_data->write_error || shared_data->transfer_cancelled) break;
|
||||
|
||||
file_table_offset += ALIGN_UP(sizeof(RomFileSystemFileEntry) + file_entry->name_length, 4);
|
||||
/* Move to the next file entry. */
|
||||
shared_data->read_error = !romfsMoveToNextFileEntry(shared_data->romfs_ctx);
|
||||
if (shared_data->read_error)
|
||||
{
|
||||
condvarWakeAll(&g_writeCondvar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
|
|
@ -109,7 +109,7 @@ void utilsJoinThread(Thread *thread);
|
|||
__attribute__((format(printf, 3, 4))) bool utilsAppendFormattedStringToBuffer(char **dst, size_t *dst_size, const char *fmt, ...);
|
||||
|
||||
/// Replaces illegal FAT characters in the provided UTF-8 string with underscores.
|
||||
/// If 'ascii_only' is set to true, all codepoints outside the (0x20,0x7E] range will also be replaced with underscores.
|
||||
/// If 'ascii_only' is set to true, all codepoints outside the [0x20,0x7F) range will also be replaced with underscores.
|
||||
/// Replacements are performed on a per-codepoint basis, which means the string length can be reduced by this function.
|
||||
void utilsReplaceIllegalCharacters(char *str, bool ascii_only);
|
||||
|
||||
|
|
|
@ -33,7 +33,9 @@ extern "C" {
|
|||
#define ROMFS_OLD_HEADER_SIZE 0x28
|
||||
#define ROMFS_HEADER_SIZE 0x50
|
||||
|
||||
#define ROMFS_VOID_ENTRY 0xFFFFFFFF
|
||||
#define ROMFS_VOID_ENTRY UINT32_MAX
|
||||
|
||||
#define ROMFS_TABLE_ENTRY_ALIGNMENT 0x4
|
||||
|
||||
/// Header used by NCA0 RomFS sections.
|
||||
typedef struct {
|
||||
|
@ -80,28 +82,28 @@ typedef struct {
|
|||
|
||||
NXDT_ASSERT(RomFileSystemHeader, ROMFS_HEADER_SIZE);
|
||||
|
||||
/// Directory entry. Always aligned to a 4-byte boundary past the directory name.
|
||||
/// Directory entry. Always aligned to a ROMFS_TABLE_ENTRY_ALIGNMENT boundary past the directory name.
|
||||
typedef struct {
|
||||
u32 parent_offset; ///< Parent directory offset.
|
||||
u32 next_offset; ///< Next sibling directory offset.
|
||||
u32 directory_offset; ///< First child directory offset.
|
||||
u32 file_offset; ///< First child file offset.
|
||||
u32 next_offset; ///< Next sibling directory offset. May be set to ROMFS_VOID_ENTRY if there are no other directory entries at this level.
|
||||
u32 directory_offset; ///< First child directory offset. May be set to ROMFS_VOID_ENTRY if there are no child directories entries.
|
||||
u32 file_offset; ///< First child file offset. May be set to ROMFS_VOID_ENTRY if there are no child file entries.
|
||||
u32 bucket_offset; ///< Directory bucket offset.
|
||||
u32 name_length; ///< Name length.
|
||||
char name[]; ///< Name (UTF-8).
|
||||
char name[]; ///< Name (UTF-8, may not be NULL terminated depending on the whole entry alignment).
|
||||
} RomFileSystemDirectoryEntry;
|
||||
|
||||
NXDT_ASSERT(RomFileSystemDirectoryEntry, 0x18);
|
||||
|
||||
/// Directory entry. Always aligned to a 4-byte boundary past the file name.
|
||||
/// Directory entry. Always aligned to a ROMFS_TABLE_ENTRY_ALIGNMENT boundary past the file name.
|
||||
typedef struct {
|
||||
u32 parent_offset; ///< Parent directory offset.
|
||||
u32 next_offset; ///< Next sibling file offset.
|
||||
u32 next_offset; ///< Next sibling file offset. May be set to ROMFS_VOID_ENTRY if there are no other file entries at this level.
|
||||
u64 offset; ///< File data offset.
|
||||
u64 size; ///< File data size.
|
||||
u32 bucket_offset; ///< File bucket offset.
|
||||
u32 name_length; ///< Name length.
|
||||
char name[]; ///< Name (UTF-8).
|
||||
char name[]; ///< Name (UTF-8, may not be NULL terminated depending on the whole entry alignment).
|
||||
} RomFileSystemFileEntry;
|
||||
|
||||
NXDT_ASSERT(RomFileSystemFileEntry, 0x20);
|
||||
|
@ -118,7 +120,8 @@ typedef struct {
|
|||
u64 file_table_size; ///< RomFS file entries table size.
|
||||
RomFileSystemFileEntry *file_table; ///< RomFS file entries table.
|
||||
u64 body_offset; ///< RomFS file data body offset (relative to the start of the RomFS).
|
||||
u32 cur_dir_offset; ///< Current RomFS directory offset (relative to the start of the directory entries table). Used for RomFS browsing.
|
||||
u64 cur_dir_offset; ///< Current RomFS directory offset (relative to the start of the directory entries table). Used for RomFS browsing.
|
||||
u64 cur_file_offset; ///< Current RomFS file offset (relative to the start of the file entries table). Used for RomFS browsing.
|
||||
} RomFileSystemContext;
|
||||
|
||||
typedef struct {
|
||||
|
@ -173,8 +176,7 @@ bool romfsGeneratePathFromFileEntry(RomFileSystemContext *ctx, RomFileSystemFile
|
|||
/// Use the romfsWriteFileEntryPatchToMemoryBuffer() wrapper to write patch data generated by this function.
|
||||
bool romfsGenerateFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, const void *data, u64 data_size, u64 data_offset, RomFileSystemFileEntryPatch *out);
|
||||
|
||||
/// Miscellaneous functions.
|
||||
|
||||
/// Resets a previously initialized RomFileSystemContext.
|
||||
NX_INLINE void romfsFreeContext(RomFileSystemContext *ctx)
|
||||
{
|
||||
if (!ctx) return;
|
||||
|
@ -185,22 +187,93 @@ NX_INLINE void romfsFreeContext(RomFileSystemContext *ctx)
|
|||
memset(ctx, 0, sizeof(RomFileSystemContext));
|
||||
}
|
||||
|
||||
NX_INLINE RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByOffset(RomFileSystemContext *ctx, u32 dir_entry_offset)
|
||||
/// Functions to reset the current directory/file entry offset.
|
||||
NX_INLINE void romfsResetDirectoryTableOffset(RomFileSystemContext *ctx)
|
||||
{
|
||||
if (!ctx || !ctx->dir_table || (dir_entry_offset + sizeof(RomFileSystemDirectoryEntry)) > ctx->dir_table_size) return NULL;
|
||||
return (RomFileSystemDirectoryEntry*)((u8*)ctx->dir_table + dir_entry_offset);
|
||||
if (ctx) ctx->cur_dir_offset = 0;
|
||||
}
|
||||
|
||||
NX_INLINE RomFileSystemFileEntry *romfsGetFileEntryByOffset(RomFileSystemContext *ctx, u32 file_entry_offset)
|
||||
NX_INLINE void romfsResetFileTableOffset(RomFileSystemContext *ctx)
|
||||
{
|
||||
if (!ctx || !ctx->file_table || (file_entry_offset + sizeof(RomFileSystemFileEntry)) > ctx->file_table_size) return NULL;
|
||||
return (RomFileSystemFileEntry*)((u8*)ctx->file_table + file_entry_offset);
|
||||
if (ctx) ctx->cur_file_offset = 0;
|
||||
}
|
||||
|
||||
/// Checks if the provided RomFileSystemContext is valid.
|
||||
NX_INLINE bool romfsIsValidContext(RomFileSystemContext *ctx)
|
||||
{
|
||||
return (ctx && ncaStorageIsValidContext(ctx->default_storage_ctx) && ctx->size && ctx->dir_table_size && ctx->dir_table && ctx->file_table_size && ctx->file_table && \
|
||||
ctx->body_offset >= ctx->header.old_format.header_size && ctx->body_offset < ctx->size);
|
||||
}
|
||||
|
||||
/// Functions to retrieve a directory/file entry.
|
||||
NX_INLINE void *romfsGetEntryByOffset(RomFileSystemContext *ctx, void *entry_table, u64 entry_table_size, u64 entry_size, u64 entry_offset)
|
||||
{
|
||||
if (!romfsIsValidContext(ctx) || !entry_table || !entry_table_size || !entry_size || (entry_offset + entry_size) > entry_table_size) return NULL;
|
||||
return ((u8*)entry_table + entry_offset);
|
||||
}
|
||||
|
||||
NX_INLINE RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByOffset(RomFileSystemContext *ctx, u64 dir_entry_offset)
|
||||
{
|
||||
return (ctx ? (RomFileSystemDirectoryEntry*)romfsGetEntryByOffset(ctx, ctx->dir_table, ctx->dir_table_size, sizeof(RomFileSystemDirectoryEntry), dir_entry_offset) : NULL);
|
||||
}
|
||||
|
||||
NX_INLINE RomFileSystemDirectoryEntry *romfsGetCurrentDirectoryEntry(RomFileSystemContext *ctx)
|
||||
{
|
||||
return (ctx ? romfsGetDirectoryEntryByOffset(ctx, ctx->cur_dir_offset) : NULL);
|
||||
}
|
||||
|
||||
NX_INLINE RomFileSystemFileEntry *romfsGetFileEntryByOffset(RomFileSystemContext *ctx, u64 file_entry_offset)
|
||||
{
|
||||
return (ctx ? (RomFileSystemFileEntry*)romfsGetEntryByOffset(ctx, ctx->file_table, ctx->file_table_size, sizeof(RomFileSystemFileEntry), file_entry_offset) : NULL);
|
||||
}
|
||||
|
||||
NX_INLINE RomFileSystemFileEntry *romfsGetCurrentFileEntry(RomFileSystemContext *ctx)
|
||||
{
|
||||
return (ctx ? romfsGetFileEntryByOffset(ctx, ctx->cur_file_offset) : NULL);
|
||||
}
|
||||
|
||||
/// Functions to check if it's possible to move to the next directory/file entry based on the current directory/file entry offset.
|
||||
NX_INLINE bool romfsCanMoveToNextEntry(RomFileSystemContext *ctx, void *entry_table, u64 entry_table_size, u64 entry_size, u64 entry_offset)
|
||||
{
|
||||
if (!romfsIsValidContext(ctx) || !entry_table || !entry_table_size || entry_size < 4 || (entry_offset + entry_size) > entry_table_size) return false;
|
||||
u32 name_length = *((u32*)((u8*)entry_table + entry_offset + entry_size - 4));
|
||||
return ((entry_offset + ALIGN_UP(entry_size + name_length, ROMFS_TABLE_ENTRY_ALIGNMENT)) <= entry_table_size);
|
||||
}
|
||||
|
||||
NX_INLINE bool romfsCanMoveToNextDirectoryEntry(RomFileSystemContext *ctx)
|
||||
{
|
||||
return (ctx ? romfsCanMoveToNextEntry(ctx, ctx->dir_table, ctx->dir_table_size, sizeof(RomFileSystemDirectoryEntry), ctx->cur_dir_offset) : false);
|
||||
}
|
||||
|
||||
NX_INLINE bool romfsCanMoveToNextFileEntry(RomFileSystemContext *ctx)
|
||||
{
|
||||
return (ctx ? romfsCanMoveToNextEntry(ctx, ctx->file_table, ctx->file_table_size, sizeof(RomFileSystemFileEntry), ctx->cur_file_offset) : false);
|
||||
}
|
||||
|
||||
/// Functions to update the current directory/file entry offset to make it point to the next directory/file entry.
|
||||
NX_INLINE bool romfsMoveToNextEntry(RomFileSystemContext *ctx, void *entry_table, u64 entry_table_size, u64 entry_size, u64 *entry_offset)
|
||||
{
|
||||
if (!romfsIsValidContext(ctx) || !entry_table || !entry_table_size || entry_size < 4 || !entry_offset || (*entry_offset + entry_size) > entry_table_size) return false;
|
||||
u32 name_length = *((u32*)((u8*)entry_table + *entry_offset + entry_size - 4));
|
||||
*entry_offset += ALIGN_UP(entry_size + name_length, ROMFS_TABLE_ENTRY_ALIGNMENT);
|
||||
return true;
|
||||
}
|
||||
|
||||
NX_INLINE bool romfsMoveToNextDirectoryEntry(RomFileSystemContext *ctx)
|
||||
{
|
||||
return (ctx ? romfsMoveToNextEntry(ctx, ctx->dir_table, ctx->dir_table_size, sizeof(RomFileSystemDirectoryEntry), &(ctx->cur_dir_offset)) : false);
|
||||
}
|
||||
|
||||
NX_INLINE bool romfsMoveToNextFileEntry(RomFileSystemContext *ctx)
|
||||
{
|
||||
return (ctx ? romfsMoveToNextEntry(ctx, ctx->file_table, ctx->file_table_size, sizeof(RomFileSystemFileEntry), &(ctx->cur_file_offset)) : false);
|
||||
}
|
||||
|
||||
/// NCA patch management functions.
|
||||
NX_INLINE void romfsWriteFileEntryPatchToMemoryBuffer(RomFileSystemContext *ctx, RomFileSystemFileEntryPatch *patch, void *buf, u64 buf_size, u64 buf_offset)
|
||||
{
|
||||
if (!ctx || ctx->is_patch || !ncaStorageIsValidContext(ctx->default_storage_ctx) || ctx->default_storage_ctx->base_storage_type != NcaStorageBaseStorageType_Regular || !patch || \
|
||||
(!patch->use_old_format_patch && ctx->default_storage_ctx->nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs) || \
|
||||
if (!romfsIsValidContext(ctx) || ctx->is_patch || ctx->default_storage_ctx->base_storage_type != NcaStorageBaseStorageType_Regular || !patch || \
|
||||
(!patch->use_old_format_patch && ctx->default_storage_ctx->nca_fs_ctx->section_type != NcaFsSectionType_RomFs) || \
|
||||
(patch->use_old_format_patch && ctx->default_storage_ctx->nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs)) return;
|
||||
|
||||
NcaContext *nca_ctx = (NcaContext*)ctx->default_storage_ctx->nca_fs_ctx->nca_ctx;
|
||||
|
|
|
@ -182,7 +182,7 @@ end:
|
|||
|
||||
bool romfsReadFileSystemData(RomFileSystemContext *ctx, void *out, u64 read_size, u64 offset)
|
||||
{
|
||||
if (!ctx || !ncaStorageIsValidContext(ctx->default_storage_ctx) || !ctx->size || !out || !read_size || (offset + read_size) > ctx->size)
|
||||
if (!romfsIsValidContext(ctx) || !out || !read_size || (offset + read_size) > ctx->size)
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
return false;
|
||||
|
@ -200,7 +200,8 @@ bool romfsReadFileSystemData(RomFileSystemContext *ctx, void *out, u64 read_size
|
|||
|
||||
bool romfsReadFileEntryData(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, void *out, u64 read_size, u64 offset)
|
||||
{
|
||||
if (!ctx || !ctx->body_offset || !file_entry || !file_entry->size || (file_entry->offset + file_entry->size) > ctx->size || !out || !read_size || (offset + read_size) > file_entry->size)
|
||||
if (!romfsIsValidContext(ctx) || !file_entry || !file_entry->size || (file_entry->offset + file_entry->size) > ctx->size || !out || !read_size || \
|
||||
(offset + read_size) > file_entry->size)
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
return false;
|
||||
|
@ -218,75 +219,120 @@ bool romfsReadFileEntryData(RomFileSystemContext *ctx, RomFileSystemFileEntry *f
|
|||
|
||||
bool romfsGetTotalDataSize(RomFileSystemContext *ctx, u64 *out_size)
|
||||
{
|
||||
if (!ctx || !ctx->file_table_size || !ctx->file_table || !out_size)
|
||||
if (!romfsIsValidContext(ctx) || !out_size)
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 offset = 0, total_size = 0;
|
||||
RomFileSystemFileEntry *file_entry = NULL;
|
||||
u64 total_size = 0;
|
||||
bool success = false;
|
||||
|
||||
while(offset < ctx->file_table_size)
|
||||
/* Reset current file table offset. */
|
||||
romfsResetFileTableOffset(ctx);
|
||||
|
||||
/* Loop through all file entries. */
|
||||
while(romfsCanMoveToNextFileEntry(ctx))
|
||||
{
|
||||
if (!(file_entry = romfsGetFileEntryByOffset(ctx, offset)))
|
||||
/* Get current file entry. */
|
||||
if (!(file_entry = romfsGetCurrentFileEntry(ctx)))
|
||||
{
|
||||
LOG_MSG("Failed to retrieve file entry!");
|
||||
return false;
|
||||
LOG_MSG("Failed to retrieve current file entry! (0x%lX, 0x%lX).", ctx->cur_file_offset, ctx->file_table_size);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Update total data size. */
|
||||
total_size += file_entry->size;
|
||||
offset += ALIGN_UP(sizeof(RomFileSystemFileEntry) + file_entry->name_length, 4);
|
||||
|
||||
/* Move to the next file entry. */
|
||||
if (!romfsMoveToNextFileEntry(ctx))
|
||||
{
|
||||
LOG_MSG("Failed to move to the next file entry! (0x%lX, 0x%lX).", ctx->cur_file_offset, ctx->file_table_size);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update output values. */
|
||||
*out_size = total_size;
|
||||
success = true;
|
||||
|
||||
return true;
|
||||
end:
|
||||
/* Reset current file table offset. */
|
||||
romfsResetFileTableOffset(ctx);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool romfsGetDirectoryDataSize(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, u64 *out_size)
|
||||
{
|
||||
if (!ctx || !ctx->dir_table_size || !ctx->dir_table || !ctx->file_table_size || !ctx->file_table || !dir_entry || (dir_entry->file_offset == ROMFS_VOID_ENTRY && \
|
||||
dir_entry->directory_offset == ROMFS_VOID_ENTRY) || !out_size)
|
||||
if (!romfsIsValidContext(ctx) || !dir_entry || !out_size)
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 total_size = 0, child_dir_size = 0;
|
||||
u32 cur_file_offset = 0, cur_dir_offset = 0;
|
||||
/* Short-circuit: check if we're dealing with an empty directory. */
|
||||
if (dir_entry->file_offset == ROMFS_VOID_ENTRY && dir_entry->directory_offset == ROMFS_VOID_ENTRY)
|
||||
{
|
||||
*out_size = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
RomFileSystemFileEntry *cur_file_entry = NULL;
|
||||
RomFileSystemDirectoryEntry *cur_dir_entry = NULL;
|
||||
u64 total_size = 0, cur_entry_offset = 0, child_dir_size = 0;
|
||||
bool success = false;
|
||||
|
||||
cur_file_offset = dir_entry->file_offset;
|
||||
while(cur_file_offset != ROMFS_VOID_ENTRY)
|
||||
/* Loop through the child file entries' linked list. */
|
||||
cur_entry_offset = dir_entry->file_offset;
|
||||
while(cur_entry_offset != ROMFS_VOID_ENTRY)
|
||||
{
|
||||
if (!(cur_file_entry = romfsGetFileEntryByOffset(ctx, cur_file_offset)))
|
||||
/* Get current file entry. */
|
||||
if (!(cur_file_entry = romfsGetFileEntryByOffset(ctx, cur_entry_offset)))
|
||||
{
|
||||
LOG_MSG("Failed to retrieve file entry!");
|
||||
return false;
|
||||
LOG_MSG("Failed to retrieve file entry! (0x%lX, 0x%lX).", cur_entry_offset, ctx->file_table_size);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Update total data size. */
|
||||
total_size += cur_file_entry->size;
|
||||
cur_file_offset = cur_file_entry->next_offset;
|
||||
|
||||
/* Update current file entry offset. */
|
||||
cur_entry_offset = cur_file_entry->next_offset;
|
||||
}
|
||||
|
||||
cur_dir_offset = dir_entry->directory_offset;
|
||||
while(cur_dir_offset != ROMFS_VOID_ENTRY)
|
||||
/* Loop through the child directory entries' linked list. */
|
||||
cur_entry_offset = dir_entry->directory_offset;
|
||||
while(cur_entry_offset != ROMFS_VOID_ENTRY)
|
||||
{
|
||||
if (!(cur_dir_entry = romfsGetDirectoryEntryByOffset(ctx, cur_dir_offset)) || !romfsGetDirectoryDataSize(ctx, cur_dir_entry, &child_dir_size))
|
||||
/* Get current directory entry. */
|
||||
if (!(cur_dir_entry = romfsGetDirectoryEntryByOffset(ctx, cur_entry_offset)))
|
||||
{
|
||||
LOG_MSG("Failed to retrieve directory entry/size!");
|
||||
return false;
|
||||
LOG_MSG("Failed to retrieve directory entry! (0x%lX, 0x%lX).", cur_entry_offset, ctx->dir_table_size);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Calculate directory size. */
|
||||
if (!romfsGetDirectoryDataSize(ctx, cur_dir_entry, &child_dir_size))
|
||||
{
|
||||
LOG_MSG("Failed to get size for directory entry! (0x%lX, 0x%lX).", cur_entry_offset, ctx->dir_table_size);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Update total data size. */
|
||||
total_size += child_dir_size;
|
||||
cur_dir_offset = cur_dir_entry->next_offset;
|
||||
|
||||
/* Update current directory entry offset. */
|
||||
cur_entry_offset = cur_dir_entry->next_offset;
|
||||
}
|
||||
|
||||
/* Update output values. */
|
||||
*out_size = total_size;
|
||||
success = true;
|
||||
|
||||
return true;
|
||||
end:
|
||||
return success;
|
||||
}
|
||||
|
||||
RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext *ctx, const char *path)
|
||||
|
@ -295,22 +341,27 @@ RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext *
|
|||
char *path_dup = NULL, *pch = NULL, *state = NULL;
|
||||
RomFileSystemDirectoryEntry *dir_entry = NULL;
|
||||
|
||||
if (!ctx || !ctx->dir_table || !ctx->dir_table_size || !path || *path != '/' || !(path_len = strlen(path)) || !(dir_entry = romfsGetDirectoryEntryByOffset(ctx, 0)))
|
||||
if (!romfsIsValidContext(ctx) || !path || *path != '/' || !(dir_entry = romfsGetDirectoryEntryByOffset(ctx, 0)))
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if the root directory was requested. */
|
||||
/* Retrieve path length. */
|
||||
path_len = strlen(path);
|
||||
|
||||
/* Short-circuit: check if the root directory was requested. */
|
||||
if (path_len == 1) return dir_entry;
|
||||
|
||||
/* Duplicate path to avoid problems with strtok_r(). */
|
||||
if (!(path_dup = strdup(path)))
|
||||
{
|
||||
LOG_MSG("Unable to duplicate input path! (\"%s\").", path);
|
||||
return NULL;
|
||||
dir_entry = NULL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Tokenize duplicated path using path separators. */
|
||||
pch = strtok_r(path_dup, "/", &state);
|
||||
if (!pch)
|
||||
{
|
||||
|
@ -319,14 +370,17 @@ RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext *
|
|||
goto end;
|
||||
}
|
||||
|
||||
/* Loop through all path elements. */
|
||||
while(pch)
|
||||
{
|
||||
/* Get child directory entry using the current token. */
|
||||
if (!(dir_entry = romfsGetChildDirectoryEntryByName(ctx, dir_entry, pch)))
|
||||
{
|
||||
LOG_MSG("Failed to retrieve directory entry by name for \"%s\"! (\"%s\").", pch, path);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Move onto the next token. */
|
||||
pch = strtok_r(NULL, "/", &state);
|
||||
}
|
||||
|
||||
|
@ -345,20 +399,23 @@ RomFileSystemFileEntry *romfsGetFileEntryByPath(RomFileSystemContext *ctx, const
|
|||
RomFileSystemDirectoryEntry *dir_entry = NULL;
|
||||
NcaContext *nca_ctx = NULL;
|
||||
|
||||
if (!ctx || !ctx->file_table || !ctx->file_table_size || !ncaStorageIsValidContext(ctx->default_storage_ctx) || \
|
||||
!(nca_ctx = (NcaContext*)ctx->default_storage_ctx->nca_fs_ctx->nca_ctx) || !path || *path != '/' || (path_len = strlen(path)) <= 1)
|
||||
if (!romfsIsValidContext(ctx) || !(nca_ctx = (NcaContext*)ctx->default_storage_ctx->nca_fs_ctx->nca_ctx) || !path || *path != '/')
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Retrieve path length. */
|
||||
path_len = strlen(path);
|
||||
|
||||
/* Retrieve NCA content type. */
|
||||
content_type = nca_ctx->content_type;
|
||||
|
||||
/* Duplicate path. */
|
||||
if (!(path_dup = strdup(path)))
|
||||
{
|
||||
LOG_MSG("Unable to duplicate input path! (\"%s\").", path);
|
||||
return NULL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Remove any trailing slashes. */
|
||||
|
@ -403,42 +460,45 @@ end:
|
|||
bool romfsGeneratePathFromDirectoryEntry(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, char *out_path, size_t out_path_size, u8 illegal_char_replace_type)
|
||||
{
|
||||
size_t path_len = 0;
|
||||
u32 dir_offset = ROMFS_VOID_ENTRY, dir_entries_count = 0;
|
||||
u64 dir_offset = ROMFS_VOID_ENTRY;
|
||||
u32 dir_entries_count = 0;
|
||||
RomFileSystemDirectoryEntry **dir_entries = NULL, **tmp_dir_entries = NULL;
|
||||
bool success = false;
|
||||
|
||||
if (!ctx || !ctx->dir_table || !ctx->dir_table_size || !dir_entry || (!dir_entry->name_length && dir_entry->parent_offset) || !out_path || out_path_size < 2 || \
|
||||
if (!romfsIsValidContext(ctx) || !dir_entry || (!dir_entry->name_length && dir_entry->parent_offset) || !out_path || out_path_size < 2 || \
|
||||
illegal_char_replace_type > RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly)
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if we're dealing with the root directory entry. */
|
||||
/* Short-circuit: check if we're dealing with the root directory entry. */
|
||||
if (!dir_entry->name_length)
|
||||
{
|
||||
sprintf(out_path, "/");
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Allocate memory for our directory entries. */
|
||||
/* Allocate memory for our directory entries pointer array. */
|
||||
dir_entries = calloc(1, sizeof(RomFileSystemDirectoryEntry*));
|
||||
if (!dir_entries)
|
||||
{
|
||||
LOG_MSG("Unable to allocate memory for directory entries!");
|
||||
return false;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Update stats. */
|
||||
path_len = (1 + dir_entry->name_length);
|
||||
*dir_entries = dir_entry;
|
||||
dir_entries_count++;
|
||||
|
||||
while(true)
|
||||
{
|
||||
/* Get parent directory offset. Break out of the loop if we reached the root directory. */
|
||||
dir_offset = dir_entries[dir_entries_count - 1]->parent_offset;
|
||||
if (!dir_offset) break;
|
||||
|
||||
/* Reallocate directory entries. */
|
||||
/* Reallocate directory entries pointer array. */
|
||||
if (!(tmp_dir_entries = realloc(dir_entries, (dir_entries_count + 1) * sizeof(RomFileSystemDirectoryEntry*))))
|
||||
{
|
||||
LOG_MSG("Unable to reallocate directory entries buffer!");
|
||||
|
@ -448,6 +508,7 @@ bool romfsGeneratePathFromDirectoryEntry(RomFileSystemContext *ctx, RomFileSyste
|
|||
dir_entries = tmp_dir_entries;
|
||||
tmp_dir_entries = NULL;
|
||||
|
||||
/* Retrieve parent directory entry using the offset we got earlier. */
|
||||
RomFileSystemDirectoryEntry **cur_dir_entry = &(dir_entries[dir_entries_count]);
|
||||
if (!(*cur_dir_entry = romfsGetDirectoryEntryByOffset(ctx, dir_offset)) || !(*cur_dir_entry)->name_length)
|
||||
{
|
||||
|
@ -455,37 +516,44 @@ bool romfsGeneratePathFromDirectoryEntry(RomFileSystemContext *ctx, RomFileSyste
|
|||
goto end;
|
||||
}
|
||||
|
||||
/* Update stats. */
|
||||
path_len += (1 + (*cur_dir_entry)->name_length);
|
||||
dir_entries_count++;
|
||||
}
|
||||
|
||||
/* Make sure the output buffer is big enough to hold the full path + NULL terminator. */
|
||||
if (path_len >= out_path_size)
|
||||
{
|
||||
LOG_MSG("Output path length exceeds output buffer size!");
|
||||
LOG_MSG("Output path length exceeds output buffer size! (%lu >= %lu).", path_len, out_path_size);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Generate output path. */
|
||||
/* Generate output path, looping through our directory entries pointer array in reverse order. */
|
||||
*out_path = '\0';
|
||||
path_len = 0;
|
||||
|
||||
for(u32 i = dir_entries_count; i > 0; i--)
|
||||
{
|
||||
RomFileSystemDirectoryEntry **cur_dir_entry = &(dir_entries[i - 1]);
|
||||
/* Get current directory entry. */
|
||||
RomFileSystemDirectoryEntry *cur_dir_entry = dir_entries[i - 1];
|
||||
|
||||
/* Concatenate path separator and current directory name to the output buffer. */
|
||||
strcat(out_path, "/");
|
||||
strncat(out_path, (*cur_dir_entry)->name, (*cur_dir_entry)->name_length);
|
||||
strncat(out_path, cur_dir_entry->name, cur_dir_entry->name_length);
|
||||
path_len++;
|
||||
|
||||
if (illegal_char_replace_type)
|
||||
{
|
||||
/* Replace illegal characters within this directory name, then update the full path length. */
|
||||
utilsReplaceIllegalCharacters(out_path + path_len, illegal_char_replace_type == RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly);
|
||||
path_len += strlen(out_path + path_len);
|
||||
} else {
|
||||
path_len += (*cur_dir_entry)->name_length;
|
||||
/* Update full path length. */
|
||||
path_len += cur_dir_entry->name_length;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update return value. */
|
||||
success = true;
|
||||
|
||||
end:
|
||||
|
@ -498,8 +566,9 @@ bool romfsGeneratePathFromFileEntry(RomFileSystemContext *ctx, RomFileSystemFile
|
|||
{
|
||||
size_t path_len = 0;
|
||||
RomFileSystemDirectoryEntry *dir_entry = NULL;
|
||||
bool success = false;
|
||||
|
||||
if (!ctx || !ctx->file_table || !ctx->file_table_size || !file_entry || !file_entry->name_length || !out_path || out_path_size < 2 || \
|
||||
if (!romfsIsValidContext(ctx) || !file_entry || !file_entry->name_length || !out_path || out_path_size < 2 || \
|
||||
!(dir_entry = romfsGetDirectoryEntryByOffset(ctx, file_entry->parent_offset)) || illegal_char_replace_type > RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly)
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
|
@ -510,35 +579,41 @@ bool romfsGeneratePathFromFileEntry(RomFileSystemContext *ctx, RomFileSystemFile
|
|||
if (!romfsGeneratePathFromDirectoryEntry(ctx, dir_entry, out_path, out_path_size, illegal_char_replace_type))
|
||||
{
|
||||
LOG_MSG("Failed to retrieve RomFS directory path!");
|
||||
return false;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Check path length. */
|
||||
/* Make sure the output buffer is big enough to hold the full path + NULL terminator. */
|
||||
path_len = strlen(out_path);
|
||||
if ((1 + file_entry->name_length) >= (out_path_size - path_len))
|
||||
if ((path_len + 1 + file_entry->name_length) >= out_path_size)
|
||||
{
|
||||
LOG_MSG("Output path length exceeds output buffer size!");
|
||||
return false;
|
||||
LOG_MSG("Output path length exceeds output buffer size! (%lu >= %lu).", path_len + 1 + file_entry->name_length, out_path_size);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Concatenate file entry name. */
|
||||
/* Concatenate path separator if our parent directory isn't the root directory. */
|
||||
if (file_entry->parent_offset)
|
||||
{
|
||||
strcat(out_path, "/");
|
||||
path_len++;
|
||||
}
|
||||
|
||||
/* Concatenate file entry name. */
|
||||
strncat(out_path, file_entry->name, file_entry->name_length);
|
||||
|
||||
/* Replace illegal characters within the file name, if needed. */
|
||||
if (illegal_char_replace_type) utilsReplaceIllegalCharacters(out_path + path_len, illegal_char_replace_type == RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly);
|
||||
|
||||
return true;
|
||||
/* Update return value. */
|
||||
success = true;
|
||||
|
||||
end:
|
||||
return success;
|
||||
}
|
||||
|
||||
bool romfsGenerateFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, const void *data, u64 data_size, u64 data_offset, RomFileSystemFileEntryPatch *out)
|
||||
{
|
||||
if (!ctx || ctx->is_patch || !ncaStorageIsValidContext(ctx->default_storage_ctx) || ctx->default_storage_ctx->base_storage_type != NcaStorageBaseStorageType_Regular || \
|
||||
!ctx->body_offset || (ctx->default_storage_ctx->nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs && ctx->default_storage_ctx->nca_fs_ctx->section_type != NcaFsSectionType_RomFs) || \
|
||||
if (!romfsIsValidContext(ctx) || ctx->is_patch || ctx->default_storage_ctx->base_storage_type != NcaStorageBaseStorageType_Regular || \
|
||||
(ctx->default_storage_ctx->nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs && ctx->default_storage_ctx->nca_fs_ctx->section_type != NcaFsSectionType_RomFs) || \
|
||||
!file_entry || !file_entry->size || (file_entry->offset + file_entry->size) > ctx->size || !data || !data_size || (data_offset + data_size) > file_entry->size || !out)
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
|
@ -572,24 +647,28 @@ static RomFileSystemDirectoryEntry *romfsGetChildDirectoryEntryByName(RomFileSys
|
|||
size_t name_len = 0;
|
||||
RomFileSystemDirectoryEntry *child_dir_entry = NULL;
|
||||
|
||||
if (!ctx || !ctx->dir_table || !ctx->dir_table_size || !dir_entry || (dir_offset = dir_entry->directory_offset) == ROMFS_VOID_ENTRY || !name || !(name_len = strlen(name)))
|
||||
if (!dir_entry || (dir_offset = dir_entry->directory_offset) == ROMFS_VOID_ENTRY || !name || !(name_len = strlen(name)))
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Loop through the child directory entries' linked list. */
|
||||
while(dir_offset != ROMFS_VOID_ENTRY)
|
||||
{
|
||||
/* Get current directory entry. */
|
||||
if (!(child_dir_entry = romfsGetDirectoryEntryByOffset(ctx, dir_offset)))
|
||||
{
|
||||
LOG_MSG("Failed to retrieve directory entry at offset 0x%lX!", dir_offset);
|
||||
LOG_MSG("Failed to retrieve directory entry! (0x%lX, 0x%lX).", dir_offset, ctx->dir_table_size);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if we found the right child directory entry. */
|
||||
/* strncmp() is used here instead of strcmp() because names stored in RomFS sections are not always NULL terminated. */
|
||||
/* If the name ends at a 4-byte boundary, the next entry starts immediately. */
|
||||
if (child_dir_entry->name_length == name_len && !strncmp(child_dir_entry->name, name, name_len)) return child_dir_entry;
|
||||
|
||||
/* Update current directory entry offset. */
|
||||
dir_offset = child_dir_entry->next_offset;
|
||||
}
|
||||
|
||||
|
@ -602,21 +681,23 @@ static RomFileSystemFileEntry *romfsGetChildFileEntryByName(RomFileSystemContext
|
|||
size_t name_len = 0;
|
||||
RomFileSystemFileEntry *child_file_entry = NULL;
|
||||
|
||||
if (!ctx || !ctx->dir_table || !ctx->dir_table_size || !ctx->file_table || !ctx->file_table_size || !dir_entry || (file_offset = dir_entry->file_offset) == ROMFS_VOID_ENTRY || \
|
||||
!name || !(name_len = strlen(name)))
|
||||
if (!dir_entry || (file_offset = dir_entry->file_offset) == ROMFS_VOID_ENTRY || !name || !(name_len = strlen(name)))
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Loop through the child file entries' linked list. */
|
||||
while(file_offset != ROMFS_VOID_ENTRY)
|
||||
{
|
||||
/* Get current file entry. */
|
||||
if (!(child_file_entry = romfsGetFileEntryByOffset(ctx, file_offset)))
|
||||
{
|
||||
LOG_MSG("Failed to retrieve file entry at offset 0x%lX!", file_offset);
|
||||
LOG_MSG("Failed to retrieve file entry! (0x%lX, 0x%lX).", file_offset, ctx->file_table_size);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if we found the right child file entry. */
|
||||
/* strncmp() is used here instead of strcmp() because names stored in RomFS sections are not always NULL terminated. */
|
||||
/* If the name ends at a 4-byte boundary, the next entry starts immediately. */
|
||||
if (child_file_entry->name_length == name_len && !strncmp(child_file_entry->name, name, name_len)) return child_file_entry;
|
||||
|
|
Loading…
Reference in a new issue