From efe76093e4ea1e37d1c29cd04ed03a5124a7ae2d Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Mon, 27 Apr 2020 18:37:15 -0400 Subject: [PATCH] RomFS (almost) done. Need to test read functions. --- README.md | 15 ++- source/main.c | 70 +++++++++- source/nca.h | 69 +++++----- source/pfs.h | 2 +- source/romfs.c | 338 +++++++++++++++++++++++++++++++++++++++++++++---- source/romfs.h | 20 ++- 6 files changed, 444 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index 3a01ec6..599bd2f 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,14 @@ hfs0: filelist generation methods - nca: more data replacement methods ??? + nca: continue reencryption methods pfs0: filelist generation methods pfs0: full header aligned to 0x20 (nsp) - romfs: filelist generation methods + romfs: test read functions romfs: data replacement methods - - + romfs: filelist generation methods @@ -100,6 +99,14 @@ If you like my work and you'd like to support me in any way, it's not necessary, Changelog -------------- +**v1.1.10:** + +* Built using libnx v3.1.0. +* Updated save.c/h to reflect changes made by shchmue in Lockpick_RCM. Fixes crashes under HOS 10.0.0. +* Fixed a nasty stack corruption issue caused by improper handling of FatFs objects. Fixes ES savefile mounting errors throughout the application (e.g. batch mode, ticket dumping). + +This is only a bugfix release. I don't expect to release any new versions until the rewrite is finished - the only exception being fixing some kind of feature-breaking bug. Please understand. + **v1.1.9:** * Built using libnx commit d7e6207. diff --git a/source/main.c b/source/main.c index 790e3b0..be3504b 100644 --- a/source/main.c +++ b/source/main.c @@ -92,7 +92,10 @@ int main(int argc, char *argv[]) } }; + char romfs_path[FS_MAX_PATH] = {0}; u64 romfs_size = 0; + RomFileSystemDirectoryEntry *romfs_dir_entry = NULL; + RomFileSystemFileEntry *romfs_file_entry = NULL; RomFileSystemContext romfs_ctx = {0}; buf = malloc(0x400000); @@ -201,6 +204,72 @@ int main(int argc, char *argv[]) consoleUpdate(NULL); + romfs_dir_entry = romfsGetDirectoryEntryByOffset(&romfs_ctx, 0x74); // "Resources" + if (!romfs_dir_entry) + { + printf("romfs dir entry failed\n"); + goto out2; + } + + printf("romfs dir entry success: %s | %p\n", romfs_dir_entry->name, romfs_dir_entry); + consoleUpdate(NULL); + + if (romfsGetDirectoryDataSize(&romfs_ctx, romfs_dir_entry, &romfs_size)) + { + printf("romfs dir size succeeded: 0x%lX\n", romfs_size); + } else { + printf("romfs dir size failed\n"); + } + + consoleUpdate(NULL); + + romfs_file_entry = romfsGetFileEntryByOffset(&romfs_ctx, romfs_dir_entry->file_offset); // "mscorlib.dll-resources.dat" + if (!romfs_file_entry) + { + printf("romfs file entry failed\n"); + goto out2; + } + + printf("romfs file entry success: %s | %p\n", romfs_file_entry->name, romfs_file_entry); + consoleUpdate(NULL); + + if (!romfsGeneratePathFromDirectoryEntry(&romfs_ctx, romfs_dir_entry, romfs_path, FS_MAX_PATH)) + { + printf("romfs generate dir path failed\n"); + goto out2; + } + + printf("romfs generate dir path success: %s\n", romfs_path); + consoleUpdate(NULL); + + romfs_dir_entry = romfsGetDirectoryEntryByPath(&romfs_ctx, romfs_path); + if (!romfs_dir_entry) + { + printf("romfs get dir entry by path failed\n"); + goto out2; + } + + printf("romfs get dir entry by path success: %s | %p\n", romfs_dir_entry->name, romfs_dir_entry); + consoleUpdate(NULL); + + if (!romfsGeneratePathFromFileEntry(&romfs_ctx, romfs_file_entry, romfs_path, FS_MAX_PATH)) + { + printf("romfs generate file path failed\n"); + goto out2; + } + + printf("romfs generate file path success: %s\n", romfs_path); + consoleUpdate(NULL); + + romfs_file_entry = romfsGetFileEntryByPath(&romfs_ctx, romfs_path); + if (!romfs_file_entry) + { + printf("romfs get file entry by path failed\n"); + goto out2; + } + + printf("romfs get file entry by path success: %s | %p\n", romfs_file_entry->name, romfs_file_entry); + consoleUpdate(NULL); @@ -208,7 +277,6 @@ int main(int argc, char *argv[]) - out2: while(appletMainLoop()) diff --git a/source/nca.h b/source/nca.h index 60073c6..aa54c89 100644 --- a/source/nca.h +++ b/source/nca.h @@ -34,6 +34,7 @@ #define NCA_HIERARCHICAL_SHA256_LAYER_COUNT 2 #define NCA_IVFC_MAGIC 0x49564643 /* "IVFC" */ +#define NCA_IVFC_LAYER_COUNT 7 #define NCA_IVFC_HASH_DATA_LAYER_COUNT 5 #define NCA_IVFC_BLOCK_SIZE(x) (1 << (x)) @@ -70,18 +71,26 @@ typedef enum { NcaKeyAreaEncryptionKeyIndex_System = 2 } NcaKeyAreaEncryptionKeyIndex; -/// 'NcaKeyGeneration_Latest' will always point to the last known key generation value. +typedef struct { + u8 relstep; + u8 micro; + u8 minor; + u8 major; +} NcaSdkAddOnVersion; + +/// 'NcaKeyGeneration_Current' will always point to the last known key generation value. typedef enum { - NcaKeyGeneration_301_302 = 3, - NcaKeyGeneration_400_410 = 4, - NcaKeyGeneration_500_510 = 5, - NcaKeyGeneration_600_610 = 6, - NcaKeyGeneration_620 = 7, - NcaKeyGeneration_700_801 = 8, - NcaKeyGeneration_810_811 = 9, - NcaKeyGeneration_900_901 = 10, - NcaKeyGeneration_910_920 = 11, - NcaKeyGeneration_Latest = NcaKeyGeneration_910_920 + NcaKeyGeneration_301_302 = 3, + NcaKeyGeneration_400_410 = 4, + NcaKeyGeneration_500_510 = 5, + NcaKeyGeneration_600_610 = 6, + NcaKeyGeneration_620 = 7, + NcaKeyGeneration_700_801 = 8, + NcaKeyGeneration_810_811 = 9, + NcaKeyGeneration_900_901 = 10, + NcaKeyGeneration_910_920 = 11, + NcaKeyGeneration_1000_1001 = 12, + NcaKeyGeneration_Current = NcaKeyGeneration_1000_1001 } NcaKeyGeneration; typedef struct { @@ -210,34 +219,26 @@ typedef struct { } NcaFsHeader; typedef struct { - u8 main_signature[0x100]; ///< RSA-PSS signature over header with fixed key. - u8 acid_signature[0x100]; ///< RSA-PSS signature over header with key in NPDM. - u32 magic; ///< "NCA0" / "NCA2" / "NCA3". - u8 distribution_type; ///< NcaDistributionType. - u8 content_type; ///< NcaContentType. - u8 key_generation_old; ///< NcaKeyGenerationOld. - u8 kaek_index; ///< NcaKeyAreaEncryptionKeyIndex. + u8 main_signature[0x100]; ///< RSA-PSS signature over header with fixed key. + u8 acid_signature[0x100]; ///< RSA-PSS signature over header with key in NPDM. + u32 magic; ///< "NCA0" / "NCA2" / "NCA3". + u8 distribution_type; ///< NcaDistributionType. + u8 content_type; ///< NcaContentType. + u8 key_generation_old; ///< NcaKeyGenerationOld. + u8 kaek_index; ///< NcaKeyAreaEncryptionKeyIndex. u64 content_size; u64 program_id; u32 content_index; - union { - u32 sdk_addon_version; - struct { - u8 sdk_addon_revision; - u8 sdk_addon_micro; - u8 sdk_addon_minor; - u8 sdk_addon_major; - }; - }; - u8 key_generation; ///< NcaKeyGeneration. + NcaSdkAddOnVersion sdk_addon_version; + u8 key_generation; ///< NcaKeyGeneration. u8 main_signature_key_generation; u8 reserved_1[0xE]; - FsRightsId rights_id; ///< Used for titlekey crypto. - NcaFsEntry fs_entries[4]; ///< Start and end offsets for each NCA FS section. - NcaFsHash fs_hashes[4]; ///< SHA-256 hashes calculated over each NCA FS section header. - NcaKey encrypted_keys[4]; ///< Only the encrypted key at index #2 is used. The other three are zero filled before the key area is encrypted. + FsRightsId rights_id; ///< Used for titlekey crypto. + NcaFsEntry fs_entries[4]; ///< Start and end offsets for each NCA FS section. + NcaFsHash fs_hashes[4]; ///< SHA-256 hashes calculated over each NCA FS section header. + NcaKey encrypted_keys[4]; ///< Only the encrypted key at index #2 is used. The other three are zero filled before the key area is encrypted. u8 reserved_2[0xC0]; - NcaFsHeader fs_headers[4]; /// NCA FS section headers. + NcaFsHeader fs_headers[4]; /// NCA FS section headers. } NcaHeader; typedef enum { @@ -370,7 +371,7 @@ NX_INLINE bool ncaValidateHierarchicalSha256Offsets(NcaHierarchicalSha256 *hiera NX_INLINE bool ncaValidateHierarchicalIntegrityOffsets(NcaHierarchicalIntegrity *hierarchical_integrity, u64 section_size) { if (!hierarchical_integrity || !section_size || __builtin_bswap32(hierarchical_integrity->magic) != NCA_IVFC_MAGIC || !hierarchical_integrity->master_hash_size || \ - hierarchical_integrity->layer_count < NCA_IVFC_HASH_DATA_LAYER_COUNT) return false; + hierarchical_integrity->layer_count != NCA_IVFC_LAYER_COUNT) return false; /* Validate layer offsets and sizes */ for(u8 i = 0; i < (NCA_IVFC_HASH_DATA_LAYER_COUNT + 1); i++) diff --git a/source/pfs.h b/source/pfs.h index f6a4a79..86379a6 100644 --- a/source/pfs.h +++ b/source/pfs.h @@ -53,7 +53,7 @@ typedef struct { u64 hash_block_size; ///< New hash block size. u8 *hash_block; ///< New hash block contents. u64 data_block_offset; ///< New data block offset (relative to the start of the NCA content file). - u64 data_block_size; ///< New data block size. + u64 data_block_size; ///< New data block size (aligned to the NcaHierarchicalSha256 block size). u8 *data_block; ///< New data block contents. } PartitionFileSystemPatchInfo; diff --git a/source/romfs.c b/source/romfs.c index 23abb56..a5337f9 100644 --- a/source/romfs.c +++ b/source/romfs.c @@ -21,6 +21,11 @@ #include "romfs.h" #include "utils.h" +/* Function prototypes. */ + +static RomFileSystemDirectoryEntry *romfsGetChildDirectoryEntryByName(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, const char *name); +static RomFileSystemFileEntry *romfsGetChildFileEntryByName(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, const char *name); + bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_fs_ctx) { NcaContext *nca_ctx = NULL; @@ -181,57 +186,336 @@ bool romfsReadFileEntryData(RomFileSystemContext *ctx, RomFileSystemFileEntry *f bool romfsGetTotalDataSize(RomFileSystemContext *ctx, u64 *out_size) { - if (!ctx || !ctx->file_table_size || !ctx->file_table || !out_size) return false; + if (!ctx || !ctx->file_table_size || !ctx->file_table || !out_size) + { + LOGFILE("Invalid parameters!"); + return false; + } u64 offset = 0, total_size = 0; RomFileSystemFileEntry *file_entry = NULL; while(offset < ctx->file_table_size) { - if (!(file_entry = romfsGetFileEntry(ctx, offset))) return false; + if (!(file_entry = romfsGetFileEntryByOffset(ctx, offset))) + { + LOGFILE("Failed to retrieve file entry!"); + return false; + } + total_size += file_entry->size; offset += ALIGN_UP(sizeof(RomFileSystemFileEntry) + file_entry->name_length, 4); } *out_size = total_size; + return true; } -bool romfsGetDirectoryDataSize(RomFileSystemContext *ctx, u32 dir_entry_offset, u64 *out_size) +bool romfsGetDirectoryDataSize(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, u64 *out_size) { - u64 total_size = 0, child_dir_size = 0; - RomFileSystemDirectoryEntry *dir_entry = NULL; - RomFileSystemFileEntry *file_entry = NULL; - - if (!ctx || !ctx->dir_table_size || !ctx->dir_table || !ctx->file_table_size || !ctx->file_table || !out_size || !(dir_entry = romfsGetDirectoryEntry(ctx, dir_entry_offset)) || \ - (!dir_entry->name_length && dir_entry_offset > 0)) return false; - - if (dir_entry->file_offset != ROMFS_VOID_ENTRY) + 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 (!(file_entry = romfsGetFileEntry(ctx, dir_entry->file_offset))) return false; - total_size += file_entry->size; - - while(file_entry->next_offset != ROMFS_VOID_ENTRY) - { - if (!(file_entry = romfsGetFileEntry(ctx, file_entry->next_offset))) return false; - total_size += file_entry->size; - } + LOGFILE("Invalid parameters!"); + return false; } - if (dir_entry->directory_offset != ROMFS_VOID_ENTRY) + u64 total_size = 0, child_dir_size = 0; + u32 cur_file_offset = 0, cur_dir_offset = 0; + RomFileSystemFileEntry *cur_file_entry = NULL; + RomFileSystemDirectoryEntry *cur_dir_entry = NULL; + + cur_file_offset = dir_entry->file_offset; + while(cur_file_offset != ROMFS_VOID_ENTRY) { - if (!romfsGetDirectoryDataSize(ctx, dir_entry->directory_offset, &child_dir_size)) return false; - total_size += child_dir_size; - - while(dir_entry->next_offset != ROMFS_VOID_ENTRY) + if (!(cur_file_entry = romfsGetFileEntryByOffset(ctx, cur_file_offset))) { - if (!romfsGetDirectoryDataSize(ctx, dir_entry->next_offset, &child_dir_size)) return false; - total_size += child_dir_size; - if (!(dir_entry = romfsGetDirectoryEntry(ctx, dir_entry->next_offset))) return false; + LOGFILE("Failed to retrieve file entry!"); + return false; } + + total_size += cur_file_entry->size; + cur_file_offset = cur_file_entry->next_offset; + } + + cur_dir_offset = dir_entry->directory_offset; + while(cur_dir_offset != ROMFS_VOID_ENTRY) + { + if (!(cur_dir_entry = romfsGetDirectoryEntryByOffset(ctx, cur_dir_offset)) || !romfsGetDirectoryDataSize(ctx, cur_dir_entry, &child_dir_size)) + { + LOGFILE("Failed to retrieve directory entry/size!"); + return false; + } + + total_size += child_dir_size; + cur_dir_offset = cur_dir_entry->next_offset; } *out_size = total_size; return true; } + +RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext *ctx, const char *path) +{ + size_t path_len = 0; + char *path_dup = NULL, *pch = 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))) + { + LOGFILE("Invalid parameters!"); + return NULL; + } + + /* Check if the root directory was requested */ + if (path_len == 1) return dir_entry; + + /* Duplicate path to avoid problems with strtok() */ + if (!(path_dup = strdup(path))) + { + LOGFILE("Unable to duplicate input path!"); + return NULL; + } + + pch = strtok(path_dup, "/"); + if (!pch) + { + LOGFILE("Failed to tokenize input path!"); + dir_entry = NULL; + goto out; + } + + while(pch) + { + if (!(dir_entry = romfsGetChildDirectoryEntryByName(ctx, dir_entry, pch))) + { + LOGFILE("Failed to retrieve directory entry by name!"); + break; + } + + pch = strtok(NULL, "/"); + } + +out: + if (path_dup) free(path_dup); + + return dir_entry; +} + +RomFileSystemFileEntry *romfsGetFileEntryByPath(RomFileSystemContext *ctx, const char *path) +{ + size_t path_len = 0; + char *path_dup = NULL, *filename = NULL; + RomFileSystemFileEntry *file_entry = NULL; + RomFileSystemDirectoryEntry *dir_entry = NULL; + + if (!ctx || !ctx->file_table || !ctx->file_table_size || !path || *path != '/' || (path_len = strlen(path)) <= 1) + { + LOGFILE("Invalid parameters!"); + return NULL; + } + + /* Duplicate path */ + if (!(path_dup = strdup(path))) + { + LOGFILE("Unable to duplicate input path!"); + return NULL; + } + + /* Remove any trailing slashes */ + while(path_dup[path_len - 1] == '/') + { + path_dup[path_len - 1] = '\0'; + path_len--; + } + + /* Safety check */ + if (!path_len || !(filename = strrchr(path_dup, '/'))) + { + LOGFILE("Invalid input path!"); + goto out; + } + + /* Remove leading slash and adjust filename string pointer */ + *filename++ = '\0'; + + /* Retrieve directory entry */ + /* If the first character is NULL, then just retrieve the root directory entry */ + if (!(dir_entry = (*path_dup ? romfsGetDirectoryEntryByPath(ctx, path_dup) : romfsGetDirectoryEntryByOffset(ctx, 0)))) + { + LOGFILE("Failed to retrieve directory entry!"); + goto out; + } + + /* Retrieve file entry */ + if (!(file_entry = romfsGetChildFileEntryByName(ctx, dir_entry, filename))) LOGFILE("Failed to retrieve file entry by name!"); + +out: + if (path_dup) free(path_dup); + + return file_entry; +} + +bool romfsGeneratePathFromDirectoryEntry(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, char *out_path, size_t out_path_size) +{ + size_t path_len = 0; + u32 dir_offset = ROMFS_VOID_ENTRY, 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) + { + LOGFILE("Invalid parameters!"); + return false; + } + + /* 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 */ + dir_entries = calloc(1, sizeof(RomFileSystemDirectoryEntry*)); + if (!dir_entries) + { + LOGFILE("Unable to allocate memory for directory entries!"); + return false; + } + + path_len = (1 + dir_entry->name_length); + *dir_entries = dir_entry; + dir_entries_count++; + + while(true) + { + dir_offset = dir_entries[dir_entries_count - 1]->parent_offset; + if (!dir_offset) break; + + /* Reallocate directory entries */ + if (!(tmp_dir_entries = realloc(dir_entries, (dir_entries_count + 1) * sizeof(RomFileSystemDirectoryEntry*)))) + { + LOGFILE("Unable to reallocate directory entries buffer!"); + goto out; + } + + dir_entries = tmp_dir_entries; + tmp_dir_entries = NULL; + + if (!(dir_entries[dir_entries_count] = romfsGetDirectoryEntryByOffset(ctx, dir_offset)) || !dir_entries[dir_entries_count]->name_length) + { + LOGFILE("Failed to retrieve directory entry!"); + goto out; + } + + path_len += (1 + dir_entries[dir_entries_count]->name_length); + dir_entries_count++; + } + + if (path_len >= out_path_size) + { + LOGFILE("Output path length exceeds output buffer size!"); + goto out; + } + + /* Generate output path */ + *out_path = '\0'; + for(u32 i = dir_entries_count; i > 0; i--) + { + strcat(out_path, "/"); + strncat(out_path, dir_entries[i - 1]->name, dir_entries[i - 1]->name_length); + } + + success = true; + +out: + if (dir_entries) free(dir_entries); + + return success; +} + +bool romfsGeneratePathFromFileEntry(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, char *out_path, size_t out_path_size) +{ + size_t path_len = 0; + RomFileSystemDirectoryEntry *dir_entry = NULL; + + if (!ctx || !ctx->file_table || !ctx->file_table_size || !file_entry || !file_entry->name_length || !out_path || out_path_size < 2 || \ + !(dir_entry = romfsGetDirectoryEntryByOffset(ctx, file_entry->parent_offset))) + { + LOGFILE("Invalid parameters!"); + return false; + } + + /* Retrieve full RomFS path up to the file entry name */ + if (!romfsGeneratePathFromDirectoryEntry(ctx, dir_entry, out_path, out_path_size)) + { + LOGFILE("Failed to retrieve RomFS directory path!"); + return false; + } + + /* Check path length */ + path_len = strlen(out_path); + if ((1 + file_entry->name_length) >= (out_path_size - path_len)) + { + LOGFILE("Output path length exceeds output buffer size!"); + return false; + } + + /* Concatenate file entry name */ + strcat(out_path, "/"); + strncat(out_path, file_entry->name, file_entry->name_length); + + return true; +} + + + + + + + + + + + + + +static RomFileSystemDirectoryEntry *romfsGetChildDirectoryEntryByName(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, const char *name) +{ + u64 dir_offset = 0; + 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))) return NULL; + + while(dir_offset != ROMFS_VOID_ENTRY) + { + if (!(child_dir_entry = romfsGetDirectoryEntryByOffset(ctx, dir_offset)) || !child_dir_entry->name_length) return NULL; + if (!strncmp(child_dir_entry->name, name, name_len)) return child_dir_entry; + dir_offset = child_dir_entry->next_offset; + } + + return NULL; +} + +static RomFileSystemFileEntry *romfsGetChildFileEntryByName(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, const char *name) +{ + u64 file_offset = 0; + 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))) return NULL; + + while(file_offset != ROMFS_VOID_ENTRY) + { + if (!(child_file_entry = romfsGetFileEntryByOffset(ctx, file_offset)) || !child_file_entry->name_length) return NULL; + if (!strncmp(child_file_entry->name, name, name_len)) return child_file_entry; + file_offset = child_file_entry->next_offset; + } + + return NULL; +} diff --git a/source/romfs.h b/source/romfs.h index c8ba35d..41f056d 100644 --- a/source/romfs.h +++ b/source/romfs.h @@ -124,17 +124,31 @@ bool romfsReadFileEntryData(RomFileSystemContext *ctx, RomFileSystemFileEntry *f bool romfsGetTotalDataSize(RomFileSystemContext *ctx, u64 *out_size); /// Calculates the extracted size from a RomFS directory. -bool romfsGetDirectoryDataSize(RomFileSystemContext *ctx, u32 dir_entry_offset, u64 *out_size); +bool romfsGetDirectoryDataSize(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, u64 *out_size); + +/// Retrieves a RomFS directory entry by path. +/// Input path must have a leading slash ('/'). If just a single slash is provided, a pointer to the root directory entry shall be returned. +RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext *ctx, const char *path); + +/// Retrieves a RomFS file entry by path. +/// Input path must have a leading slash ('/'). +RomFileSystemFileEntry *romfsGetFileEntryByPath(RomFileSystemContext *ctx, const char *path); + +/// Generates a path string from a RomFS directory entry. +bool romfsGeneratePathFromDirectoryEntry(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, char *out_path, size_t out_path_size); + +/// Generates a path string from a RomFS file entry. +bool romfsGeneratePathFromFileEntry(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, char *out_path, size_t out_path_size); /// Miscellaneous functions. -NX_INLINE RomFileSystemDirectoryEntry *romfsGetDirectoryEntry(RomFileSystemContext *ctx, u32 dir_entry_offset) +NX_INLINE RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByOffset(RomFileSystemContext *ctx, u32 dir_entry_offset) { 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); } -NX_INLINE RomFileSystemFileEntry *romfsGetFileEntry(RomFileSystemContext *ctx, u32 file_entry_offset) +NX_INLINE RomFileSystemFileEntry *romfsGetFileEntryByOffset(RomFileSystemContext *ctx, u32 file_entry_offset) { 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);