2020-04-26 09:35:01 +01:00
|
|
|
/*
|
2020-07-03 10:31:22 +01:00
|
|
|
* romfs.c
|
2020-04-26 09:35:01 +01:00
|
|
|
*
|
2020-12-23 17:48:57 +00:00
|
|
|
* Copyright (c) 2020-2021, DarkMatterCore <pabloacurielz@gmail.com>.
|
2020-07-03 10:31:22 +01:00
|
|
|
*
|
|
|
|
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
|
|
|
|
*
|
|
|
|
* nxdumptool is free software; you can redistribute it and/or modify it
|
2020-04-26 09:35:01 +01:00
|
|
|
* under the terms and conditions of the GNU General Public License,
|
|
|
|
* version 2, as published by the Free Software Foundation.
|
|
|
|
*
|
2020-07-03 10:31:22 +01:00
|
|
|
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
|
2020-04-26 09:35:01 +01:00
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
* more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "utils.h"
|
2020-07-03 10:31:22 +01:00
|
|
|
#include "romfs.h"
|
2020-04-26 09:35:01 +01:00
|
|
|
|
2020-04-27 23:37:15 +01:00
|
|
|
/* Function prototypes. */
|
|
|
|
|
|
|
|
static RomFileSystemDirectoryEntry *romfsGetChildDirectoryEntryByName(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, const char *name);
|
|
|
|
static RomFileSystemFileEntry *romfsGetChildFileEntryByName(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, const char *name);
|
|
|
|
|
2020-04-26 09:35:01 +01:00
|
|
|
bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_fs_ctx)
|
|
|
|
{
|
|
|
|
NcaContext *nca_ctx = NULL;
|
|
|
|
u64 dir_table_offset = 0, file_table_offset = 0;
|
2021-03-07 23:22:49 +00:00
|
|
|
bool success = false, dump_fs_header = false;
|
2020-04-26 09:35:01 +01:00
|
|
|
|
2020-07-22 09:03:28 +01:00
|
|
|
if (!out || !nca_fs_ctx || !nca_fs_ctx->enabled || !(nca_ctx = (NcaContext*)nca_fs_ctx->nca_ctx) || (nca_ctx->format_version == NcaVersion_Nca0 && \
|
|
|
|
(nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs || nca_fs_ctx->header.hash_type != NcaHashType_HierarchicalSha256)) || (nca_ctx->format_version != NcaVersion_Nca0 && \
|
2020-10-21 05:27:48 +01:00
|
|
|
(nca_fs_ctx->section_type != NcaFsSectionType_RomFs || nca_fs_ctx->header.hash_type != NcaHashType_HierarchicalIntegrity)) || (nca_ctx->rights_id_available && !nca_ctx->titlekey_retrieved))
|
2020-04-26 09:35:01 +01:00
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Invalid parameters!");
|
2020-04-26 09:35:01 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-07-22 09:03:28 +01:00
|
|
|
u32 layer_count = 0;
|
|
|
|
NcaRegion *hash_region = NULL;
|
|
|
|
NcaHierarchicalIntegrityVerificationLevelInformation *level_information = NULL;
|
|
|
|
|
2020-10-14 14:23:49 +01:00
|
|
|
/* Free output context beforehand. */
|
|
|
|
romfsFreeContext(out);
|
2020-07-06 01:10:07 +01:00
|
|
|
|
|
|
|
/* Fill context. */
|
2020-04-26 09:35:01 +01:00
|
|
|
out->nca_fs_ctx = nca_fs_ctx;
|
|
|
|
|
|
|
|
if (nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs)
|
|
|
|
{
|
2020-07-22 09:03:28 +01:00
|
|
|
if (!ncaValidateHierarchicalSha256Offsets(&(nca_fs_ctx->header.hash_data.hierarchical_sha256_data), nca_fs_ctx->section_size))
|
2020-04-26 09:35:01 +01:00
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Invalid HierarchicalSha256 block!");
|
|
|
|
goto end;
|
2020-04-26 09:35:01 +01:00
|
|
|
}
|
|
|
|
|
2020-07-22 09:03:28 +01:00
|
|
|
layer_count = nca_fs_ctx->header.hash_data.hierarchical_sha256_data.hash_region_count;
|
|
|
|
hash_region = &(nca_fs_ctx->header.hash_data.hierarchical_sha256_data.hash_region[layer_count - 1]);
|
|
|
|
|
|
|
|
out->offset = hash_region->offset;
|
|
|
|
out->size = hash_region->size;
|
2020-04-26 09:35:01 +01:00
|
|
|
} else {
|
2020-07-22 09:03:28 +01:00
|
|
|
if (!ncaValidateHierarchicalIntegrityOffsets(&(nca_fs_ctx->header.hash_data.integrity_meta_info), nca_fs_ctx->section_size))
|
2020-04-26 09:35:01 +01:00
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Invalid HierarchicalIntegrity block!");
|
|
|
|
goto end;
|
2020-04-26 09:35:01 +01:00
|
|
|
}
|
|
|
|
|
2020-07-22 09:03:28 +01:00
|
|
|
layer_count = NCA_IVFC_LEVEL_COUNT;
|
|
|
|
level_information = &(nca_fs_ctx->header.hash_data.integrity_meta_info.info_level_hash.level_information[layer_count - 1]);
|
|
|
|
|
|
|
|
out->offset = level_information->offset;
|
|
|
|
out->size = level_information->size;
|
2020-04-26 09:35:01 +01:00
|
|
|
}
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Read RomFS header. */
|
2020-04-26 09:35:01 +01:00
|
|
|
if (!ncaReadFsSection(nca_fs_ctx, &(out->header), sizeof(RomFileSystemHeader), out->offset))
|
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Failed to read RomFS header!");
|
|
|
|
goto end;
|
2020-04-26 09:35:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs && out->header.old_format.header_size != ROMFS_OLD_HEADER_SIZE) || \
|
|
|
|
(nca_fs_ctx->section_type == NcaFsSectionType_RomFs && out->header.cur_format.header_size != ROMFS_HEADER_SIZE))
|
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Invalid RomFS header size!");
|
|
|
|
dump_fs_header = true;
|
|
|
|
goto end;
|
2020-04-26 09:35:01 +01:00
|
|
|
}
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Read directory entries table. */
|
2020-04-26 11:04:31 +01:00
|
|
|
dir_table_offset = (nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs ? (u64)out->header.old_format.directory_entry_offset : out->header.cur_format.directory_entry_offset);
|
2020-04-26 09:35:01 +01:00
|
|
|
out->dir_table_size = (nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs ? (u64)out->header.old_format.directory_entry_size : out->header.cur_format.directory_entry_size);
|
|
|
|
|
2020-10-21 05:27:48 +01:00
|
|
|
if (!out->dir_table_size || (dir_table_offset + out->dir_table_size) > out->size)
|
2020-04-26 09:35:01 +01:00
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Invalid RomFS directory entries table!");
|
|
|
|
dump_fs_header = true;
|
|
|
|
goto end;
|
2020-04-26 09:35:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
out->dir_table = malloc(out->dir_table_size);
|
|
|
|
if (!out->dir_table)
|
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Unable to allocate memory for RomFS directory entries table!");
|
|
|
|
goto end;
|
2020-04-26 09:35:01 +01:00
|
|
|
}
|
|
|
|
|
2020-04-26 11:04:31 +01:00
|
|
|
if (!ncaReadFsSection(nca_fs_ctx, out->dir_table, out->dir_table_size, out->offset + dir_table_offset))
|
2020-04-26 09:35:01 +01:00
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Failed to read RomFS directory entries table!");
|
2020-07-13 07:36:17 +01:00
|
|
|
goto end;
|
2020-04-26 09:35:01 +01:00
|
|
|
}
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Read file entries table. */
|
2020-04-26 11:04:31 +01:00
|
|
|
file_table_offset = (nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs ? (u64)out->header.old_format.file_entry_offset : out->header.cur_format.file_entry_offset);
|
2020-04-26 09:35:01 +01:00
|
|
|
out->file_table_size = (nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs ? (u64)out->header.old_format.file_entry_size : out->header.cur_format.file_entry_size);
|
|
|
|
|
2020-10-21 05:27:48 +01:00
|
|
|
if (!out->file_table_size || (file_table_offset + out->file_table_size) > out->size)
|
2020-04-26 09:35:01 +01:00
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Invalid RomFS file entries table!");
|
|
|
|
dump_fs_header = true;
|
2020-07-13 07:36:17 +01:00
|
|
|
goto end;
|
2020-04-26 09:35:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
out->file_table = malloc(out->file_table_size);
|
|
|
|
if (!out->file_table)
|
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Unable to allocate memory for RomFS file entries table!");
|
2020-07-13 07:36:17 +01:00
|
|
|
goto end;
|
2020-04-26 09:35:01 +01:00
|
|
|
}
|
|
|
|
|
2020-04-26 11:04:31 +01:00
|
|
|
if (!ncaReadFsSection(nca_fs_ctx, out->file_table, out->file_table_size, out->offset + file_table_offset))
|
2020-04-26 09:35:01 +01:00
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Failed to read RomFS file entries table!");
|
2020-07-13 07:36:17 +01:00
|
|
|
goto end;
|
2020-04-26 09:35:01 +01:00
|
|
|
}
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Get file data body offset. */
|
2020-04-26 11:04:31 +01:00
|
|
|
out->body_offset = (nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs ? (u64)out->header.old_format.body_offset : out->header.cur_format.body_offset);
|
2020-04-30 09:25:03 +01:00
|
|
|
if (out->body_offset >= out->size)
|
2020-04-26 09:35:01 +01:00
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Invalid RomFS file data body!");
|
|
|
|
dump_fs_header = true;
|
2020-07-13 07:36:17 +01:00
|
|
|
goto end;
|
2020-04-26 09:35:01 +01:00
|
|
|
}
|
|
|
|
|
2021-03-07 23:22:49 +00:00
|
|
|
/* Update flag. */
|
2020-04-29 22:11:27 +01:00
|
|
|
success = true;
|
|
|
|
|
2020-07-13 07:36:17 +01:00
|
|
|
end:
|
2021-03-07 23:22:49 +00:00
|
|
|
if (!success)
|
|
|
|
{
|
|
|
|
if (dump_fs_header) LOG_DATA(&(out->header), sizeof(RomFileSystemHeader), "RomFS header dump:");
|
|
|
|
|
|
|
|
romfsFreeContext(out);
|
|
|
|
}
|
2020-04-29 22:11:27 +01:00
|
|
|
|
|
|
|
return success;
|
2020-04-26 09:35:01 +01:00
|
|
|
}
|
|
|
|
|
2020-04-26 11:04:31 +01:00
|
|
|
bool romfsReadFileSystemData(RomFileSystemContext *ctx, void *out, u64 read_size, u64 offset)
|
|
|
|
{
|
2020-10-21 05:27:48 +01:00
|
|
|
if (!ctx || !ctx->nca_fs_ctx || !ctx->size || !out || !read_size || (offset + read_size) > ctx->size)
|
2020-04-26 11:04:31 +01:00
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Invalid parameters!");
|
2020-04-26 11:04:31 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Read filesystem data. */
|
2020-04-26 11:04:31 +01:00
|
|
|
if (!ncaReadFsSection(ctx->nca_fs_ctx, out, read_size, ctx->offset + offset))
|
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Failed to read RomFS data!");
|
2020-04-26 11:04:31 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-04-26 09:35:01 +01:00
|
|
|
bool romfsReadFileEntryData(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, void *out, u64 read_size, u64 offset)
|
|
|
|
{
|
2020-10-21 05:27:48 +01:00
|
|
|
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)
|
2020-04-26 09:35:01 +01:00
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Invalid parameters!");
|
2020-04-26 09:35:01 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Read entry data. */
|
2020-04-26 11:04:31 +01:00
|
|
|
if (!romfsReadFileSystemData(ctx, out, read_size, ctx->body_offset + file_entry->offset + offset))
|
2020-04-26 09:35:01 +01:00
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Failed to read RomFS file entry data!");
|
2020-04-26 09:35:01 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool romfsGetTotalDataSize(RomFileSystemContext *ctx, u64 *out_size)
|
|
|
|
{
|
2020-04-27 23:37:15 +01:00
|
|
|
if (!ctx || !ctx->file_table_size || !ctx->file_table || !out_size)
|
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Invalid parameters!");
|
2020-04-27 23:37:15 +01:00
|
|
|
return false;
|
|
|
|
}
|
2020-04-26 09:35:01 +01:00
|
|
|
|
|
|
|
u64 offset = 0, total_size = 0;
|
|
|
|
RomFileSystemFileEntry *file_entry = NULL;
|
|
|
|
|
|
|
|
while(offset < ctx->file_table_size)
|
|
|
|
{
|
2020-04-27 23:37:15 +01:00
|
|
|
if (!(file_entry = romfsGetFileEntryByOffset(ctx, offset)))
|
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Failed to retrieve file entry!");
|
2020-04-27 23:37:15 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-04-26 09:35:01 +01:00
|
|
|
total_size += file_entry->size;
|
|
|
|
offset += ALIGN_UP(sizeof(RomFileSystemFileEntry) + file_entry->name_length, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
*out_size = total_size;
|
2020-04-27 23:37:15 +01:00
|
|
|
|
2020-04-26 09:35:01 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-04-27 23:37:15 +01:00
|
|
|
bool romfsGetDirectoryDataSize(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, u64 *out_size)
|
2020-04-26 09:35:01 +01:00
|
|
|
{
|
2020-04-27 23:37:15 +01:00
|
|
|
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)
|
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Invalid parameters!");
|
2020-04-27 23:37:15 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-04-26 09:35:01 +01:00
|
|
|
u64 total_size = 0, child_dir_size = 0;
|
2020-04-27 23:37:15 +01:00
|
|
|
u32 cur_file_offset = 0, cur_dir_offset = 0;
|
|
|
|
RomFileSystemFileEntry *cur_file_entry = NULL;
|
|
|
|
RomFileSystemDirectoryEntry *cur_dir_entry = NULL;
|
2020-04-26 09:35:01 +01:00
|
|
|
|
2020-04-27 23:37:15 +01:00
|
|
|
cur_file_offset = dir_entry->file_offset;
|
|
|
|
while(cur_file_offset != ROMFS_VOID_ENTRY)
|
|
|
|
{
|
|
|
|
if (!(cur_file_entry = romfsGetFileEntryByOffset(ctx, cur_file_offset)))
|
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Failed to retrieve file entry!");
|
2020-04-27 23:37:15 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
total_size += cur_file_entry->size;
|
|
|
|
cur_file_offset = cur_file_entry->next_offset;
|
|
|
|
}
|
2020-04-26 09:35:01 +01:00
|
|
|
|
2020-04-27 23:37:15 +01:00
|
|
|
cur_dir_offset = dir_entry->directory_offset;
|
|
|
|
while(cur_dir_offset != ROMFS_VOID_ENTRY)
|
2020-04-26 09:35:01 +01:00
|
|
|
{
|
2020-04-27 23:37:15 +01:00
|
|
|
if (!(cur_dir_entry = romfsGetDirectoryEntryByOffset(ctx, cur_dir_offset)) || !romfsGetDirectoryDataSize(ctx, cur_dir_entry, &child_dir_size))
|
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Failed to retrieve directory entry/size!");
|
2020-04-27 23:37:15 +01:00
|
|
|
return false;
|
|
|
|
}
|
2020-04-26 09:35:01 +01:00
|
|
|
|
2020-04-27 23:37:15 +01:00
|
|
|
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)))
|
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Invalid parameters!");
|
2020-04-27 23:37:15 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Check if the root directory was requested. */
|
2020-04-27 23:37:15 +01:00
|
|
|
if (path_len == 1) return dir_entry;
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Duplicate path to avoid problems with strtok(). */
|
2020-04-27 23:37:15 +01:00
|
|
|
if (!(path_dup = strdup(path)))
|
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Unable to duplicate input path! (\"%s\").", path);
|
2020-04-27 23:37:15 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
pch = strtok(path_dup, "/");
|
|
|
|
if (!pch)
|
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Failed to tokenize input path! (\"%s\").", path);
|
2020-04-27 23:37:15 +01:00
|
|
|
dir_entry = NULL;
|
2020-07-13 07:36:17 +01:00
|
|
|
goto end;
|
2020-04-27 23:37:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
while(pch)
|
|
|
|
{
|
|
|
|
if (!(dir_entry = romfsGetChildDirectoryEntryByName(ctx, dir_entry, pch)))
|
2020-04-26 09:35:01 +01:00
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Failed to retrieve directory entry by name for \"%s\"! (\"%s\").", pch, path);
|
2020-04-27 23:37:15 +01:00
|
|
|
break;
|
2020-04-26 09:35:01 +01:00
|
|
|
}
|
2020-04-27 23:37:15 +01:00
|
|
|
|
|
|
|
pch = strtok(NULL, "/");
|
2020-04-26 09:35:01 +01:00
|
|
|
}
|
|
|
|
|
2020-07-13 07:36:17 +01:00
|
|
|
end:
|
2020-04-27 23:37:15 +01:00
|
|
|
if (path_dup) free(path_dup);
|
|
|
|
|
|
|
|
return dir_entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
RomFileSystemFileEntry *romfsGetFileEntryByPath(RomFileSystemContext *ctx, const char *path)
|
|
|
|
{
|
|
|
|
size_t path_len = 0;
|
2020-10-09 10:58:53 +01:00
|
|
|
u8 content_type = 0;
|
2020-04-27 23:37:15 +01:00
|
|
|
char *path_dup = NULL, *filename = NULL;
|
|
|
|
RomFileSystemFileEntry *file_entry = NULL;
|
|
|
|
RomFileSystemDirectoryEntry *dir_entry = NULL;
|
|
|
|
|
2020-10-09 10:58:53 +01:00
|
|
|
if (!ctx || !ctx->file_table || !ctx->file_table_size || !ctx->nca_fs_ctx || !ctx->nca_fs_ctx->nca_ctx || !path || *path != '/' || (path_len = strlen(path)) <= 1)
|
2020-04-26 09:35:01 +01:00
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Invalid parameters!");
|
2020-04-27 23:37:15 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-10-09 10:58:53 +01:00
|
|
|
content_type = ((NcaContext*)ctx->nca_fs_ctx->nca_ctx)->content_type;
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Duplicate path. */
|
2020-04-27 23:37:15 +01:00
|
|
|
if (!(path_dup = strdup(path)))
|
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Unable to duplicate input path! (\"%s\").", path);
|
2020-04-27 23:37:15 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Remove any trailing slashes. */
|
2020-04-27 23:37:15 +01:00
|
|
|
while(path_dup[path_len - 1] == '/')
|
|
|
|
{
|
|
|
|
path_dup[path_len - 1] = '\0';
|
|
|
|
path_len--;
|
|
|
|
}
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Safety check. */
|
2020-04-27 23:37:15 +01:00
|
|
|
if (!path_len || !(filename = strrchr(path_dup, '/')))
|
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Invalid input path! (\"%s\").", path);
|
2020-07-13 07:36:17 +01:00
|
|
|
goto end;
|
2020-04-27 23:37:15 +01:00
|
|
|
}
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Remove leading slash and adjust filename string pointer. */
|
2020-04-27 23:37:15 +01:00
|
|
|
*filename++ = '\0';
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Retrieve directory entry. */
|
|
|
|
/* If the first character is NULL, then just retrieve the root directory entry. */
|
2020-04-27 23:37:15 +01:00
|
|
|
if (!(dir_entry = (*path_dup ? romfsGetDirectoryEntryByPath(ctx, path_dup) : romfsGetDirectoryEntryByOffset(ctx, 0))))
|
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Failed to retrieve directory entry for \"%s\"! (\"%s\").", *path_dup ? path_dup : "/", path);
|
2020-07-13 07:36:17 +01:00
|
|
|
goto end;
|
2020-04-27 23:37:15 +01:00
|
|
|
}
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Retrieve file entry. */
|
2020-10-06 16:41:26 +01:00
|
|
|
if (!(file_entry = romfsGetChildFileEntryByName(ctx, dir_entry, filename)))
|
|
|
|
{
|
2020-10-09 10:58:53 +01:00
|
|
|
/* Only log error if we're not dealing with NACP icons or a LegalInformation XML. */
|
2021-03-07 23:22:49 +00:00
|
|
|
bool skip_log = ((!strncmp(path, "/icon_", 6) && content_type == NcmContentType_Control) || (!strcmp(path, "/legalinfo.xml") && content_type == NcmContentType_LegalInformation));
|
|
|
|
if (!skip_log) LOG_MSG("Failed to retrieve file entry by name for \"%s\"! (\"%s\").", filename, path);
|
2020-10-06 16:41:26 +01:00
|
|
|
}
|
2020-04-27 23:37:15 +01:00
|
|
|
|
2020-07-13 07:36:17 +01:00
|
|
|
end:
|
2020-04-27 23:37:15 +01:00
|
|
|
if (path_dup) free(path_dup);
|
|
|
|
|
|
|
|
return file_entry;
|
|
|
|
}
|
|
|
|
|
2020-05-10 17:40:12 +01:00
|
|
|
bool romfsGeneratePathFromDirectoryEntry(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, char *out_path, size_t out_path_size, u8 illegal_char_replace_type)
|
2020-04-27 23:37:15 +01:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2020-05-10 17:40:12 +01:00
|
|
|
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 || \
|
|
|
|
illegal_char_replace_type > RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly)
|
2020-04-27 23:37:15 +01:00
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Invalid parameters!");
|
2020-04-27 23:37:15 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Check if we're dealing with the root directory entry. */
|
2020-04-27 23:37:15 +01:00
|
|
|
if (!dir_entry->name_length)
|
|
|
|
{
|
|
|
|
sprintf(out_path, "/");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Allocate memory for our directory entries. */
|
2020-04-27 23:37:15 +01:00
|
|
|
dir_entries = calloc(1, sizeof(RomFileSystemDirectoryEntry*));
|
|
|
|
if (!dir_entries)
|
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Unable to allocate memory for directory entries!");
|
2020-04-27 23:37:15 +01:00
|
|
|
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;
|
2020-04-26 09:35:01 +01:00
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Reallocate directory entries. */
|
2020-04-27 23:37:15 +01:00
|
|
|
if (!(tmp_dir_entries = realloc(dir_entries, (dir_entries_count + 1) * sizeof(RomFileSystemDirectoryEntry*))))
|
2020-04-26 09:35:01 +01:00
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Unable to reallocate directory entries buffer!");
|
2020-07-13 07:36:17 +01:00
|
|
|
goto end;
|
2020-04-26 09:35:01 +01:00
|
|
|
}
|
2020-04-27 23:37:15 +01:00
|
|
|
|
|
|
|
dir_entries = tmp_dir_entries;
|
|
|
|
tmp_dir_entries = NULL;
|
|
|
|
|
2020-10-15 01:06:53 +01:00
|
|
|
RomFileSystemDirectoryEntry **cur_dir_entry = &(dir_entries[dir_entries_count]);
|
|
|
|
if (!(*cur_dir_entry = romfsGetDirectoryEntryByOffset(ctx, dir_offset)) || !(*cur_dir_entry)->name_length)
|
2020-04-27 23:37:15 +01:00
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Failed to retrieve directory entry!");
|
2020-07-13 07:36:17 +01:00
|
|
|
goto end;
|
2020-04-27 23:37:15 +01:00
|
|
|
}
|
|
|
|
|
2020-10-15 01:06:53 +01:00
|
|
|
path_len += (1 + (*cur_dir_entry)->name_length);
|
2020-04-27 23:37:15 +01:00
|
|
|
dir_entries_count++;
|
2020-04-26 09:35:01 +01:00
|
|
|
}
|
|
|
|
|
2020-04-27 23:37:15 +01:00
|
|
|
if (path_len >= out_path_size)
|
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Output path length exceeds output buffer size!");
|
2020-07-13 07:36:17 +01:00
|
|
|
goto end;
|
2020-04-27 23:37:15 +01:00
|
|
|
}
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Generate output path. */
|
2020-04-27 23:37:15 +01:00
|
|
|
*out_path = '\0';
|
2020-10-15 01:06:53 +01:00
|
|
|
path_len = 0;
|
|
|
|
|
2020-04-27 23:37:15 +01:00
|
|
|
for(u32 i = dir_entries_count; i > 0; i--)
|
|
|
|
{
|
2020-10-15 01:06:53 +01:00
|
|
|
RomFileSystemDirectoryEntry **cur_dir_entry = &(dir_entries[i - 1]);
|
|
|
|
|
2020-04-27 23:37:15 +01:00
|
|
|
strcat(out_path, "/");
|
2020-10-15 01:06:53 +01:00
|
|
|
strncat(out_path, (*cur_dir_entry)->name, (*cur_dir_entry)->name_length);
|
|
|
|
path_len++;
|
|
|
|
|
|
|
|
if (illegal_char_replace_type) utilsReplaceIllegalCharacters(out_path + path_len, illegal_char_replace_type == RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly);
|
|
|
|
|
|
|
|
path_len += (*cur_dir_entry)->name_length;
|
2020-04-27 23:37:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
success = true;
|
|
|
|
|
2020-07-13 07:36:17 +01:00
|
|
|
end:
|
2020-04-27 23:37:15 +01:00
|
|
|
if (dir_entries) free(dir_entries);
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
2020-05-10 17:40:12 +01:00
|
|
|
bool romfsGeneratePathFromFileEntry(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, char *out_path, size_t out_path_size, u8 illegal_char_replace_type)
|
2020-04-27 23:37:15 +01:00
|
|
|
{
|
|
|
|
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 || \
|
2020-05-10 17:40:12 +01:00
|
|
|
!(dir_entry = romfsGetDirectoryEntryByOffset(ctx, file_entry->parent_offset)) || illegal_char_replace_type > RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly)
|
2020-04-27 23:37:15 +01:00
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Invalid parameters!");
|
2020-04-27 23:37:15 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Retrieve full RomFS path up to the file entry name. */
|
2020-05-10 17:40:12 +01:00
|
|
|
if (!romfsGeneratePathFromDirectoryEntry(ctx, dir_entry, out_path, out_path_size, illegal_char_replace_type))
|
2020-04-27 23:37:15 +01:00
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Failed to retrieve RomFS directory path!");
|
2020-04-27 23:37:15 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Check path length. */
|
2020-04-27 23:37:15 +01:00
|
|
|
path_len = strlen(out_path);
|
|
|
|
if ((1 + file_entry->name_length) >= (out_path_size - path_len))
|
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Output path length exceeds output buffer size!");
|
2020-04-27 23:37:15 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-07-06 01:10:07 +01:00
|
|
|
/* Concatenate file entry name. */
|
2020-10-15 01:06:53 +01:00
|
|
|
if (file_entry->parent_offset)
|
|
|
|
{
|
|
|
|
strcat(out_path, "/");
|
|
|
|
path_len++;
|
|
|
|
}
|
|
|
|
|
2020-04-27 23:37:15 +01:00
|
|
|
strncat(out_path, file_entry->name, file_entry->name_length);
|
2020-10-15 01:06:53 +01:00
|
|
|
|
|
|
|
if (illegal_char_replace_type) utilsReplaceIllegalCharacters(out_path + path_len, illegal_char_replace_type == RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly);
|
2020-04-26 09:35:01 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2020-04-27 23:37:15 +01:00
|
|
|
|
2020-04-28 09:58:17 +01:00
|
|
|
bool romfsGenerateFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, const void *data, u64 data_size, u64 data_offset, RomFileSystemFileEntryPatch *out)
|
|
|
|
{
|
|
|
|
if (!ctx || !ctx->nca_fs_ctx || !ctx->body_offset || (ctx->nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs && ctx->nca_fs_ctx->section_type != NcaFsSectionType_RomFs) || !file_entry || \
|
2020-10-21 05:27:48 +01:00
|
|
|
!file_entry->size || (file_entry->offset + file_entry->size) > ctx->size || !data || !data_size || (data_offset + data_size) > file_entry->size || !out)
|
2020-04-28 09:58:17 +01:00
|
|
|
{
|
2021-03-07 23:22:49 +00:00
|
|
|
LOG_MSG("Invalid parameters!");
|
2020-04-28 09:58:17 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool success = false;
|
|
|
|
u64 fs_offset = (ctx->body_offset + file_entry->offset + data_offset);
|
|
|
|
|
|
|
|
if (ctx->nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs)
|
|
|
|
{
|
|
|
|
out->use_old_format_patch = true;
|
|
|
|
success = ncaGenerateHierarchicalSha256Patch(ctx->nca_fs_ctx, data, data_size, fs_offset, &(out->old_format_patch));
|
|
|
|
} else {
|
|
|
|
out->use_old_format_patch = false;
|
2020-04-29 10:54:40 +01:00
|
|
|
success = ncaGenerateHierarchicalIntegrityPatch(ctx->nca_fs_ctx, data, data_size, fs_offset, &(out->cur_format_patch));
|
2020-04-28 09:58:17 +01:00
|
|
|
}
|
|
|
|
|
2020-10-28 22:48:46 +00:00
|
|
|
out->written = false;
|
|
|
|
|
2021-03-07 23:22:49 +00:00
|
|
|
if (!success) LOG_MSG("Failed to generate 0x%lX bytes Hierarchical%s patch at offset 0x%lX for RomFS file entry!", data_size, \
|
2020-07-22 21:35:23 +01:00
|
|
|
ctx->nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs ? "Sha256" : "Integrity", fs_offset);
|
|
|
|
|
2020-04-28 09:58:17 +01:00
|
|
|
return success;
|
|
|
|
}
|
2020-04-27 23:37:15 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2021-03-08 11:11:28 +00:00
|
|
|
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)))
|
|
|
|
{
|
|
|
|
LOG_MSG("Invalid parameters!");
|
|
|
|
return NULL;
|
|
|
|
}
|
2020-04-27 23:37:15 +01:00
|
|
|
|
|
|
|
while(dir_offset != ROMFS_VOID_ENTRY)
|
|
|
|
{
|
2021-03-08 11:11:28 +00:00
|
|
|
if (!(child_dir_entry = romfsGetDirectoryEntryByOffset(ctx, dir_offset)))
|
|
|
|
{
|
|
|
|
LOG_MSG("Failed to retrieve directory entry at offset 0x%lX!", dir_offset);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
2020-04-27 23:37:15 +01:00
|
|
|
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;
|
|
|
|
|
2021-03-08 11:11:28 +00:00
|
|
|
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)))
|
|
|
|
{
|
|
|
|
LOG_MSG("Invalid parameters!");
|
|
|
|
return NULL;
|
|
|
|
}
|
2020-04-27 23:37:15 +01:00
|
|
|
|
|
|
|
while(file_offset != ROMFS_VOID_ENTRY)
|
|
|
|
{
|
2021-03-08 11:11:28 +00:00
|
|
|
if (!(child_file_entry = romfsGetFileEntryByOffset(ctx, file_offset)))
|
|
|
|
{
|
|
|
|
LOG_MSG("Failed to retrieve file entry at offset 0x%lX!", file_offset);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
2020-04-27 23:37:15 +01:00
|
|
|
file_offset = child_file_entry->next_offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|