diff --git a/README.md b/README.md index 8cea31f..f67ae5f 100644 --- a/README.md +++ b/README.md @@ -3,16 +3,37 @@ todo: hfs0: filelist generation methods - nca: continue reencryption methods + tik: automatically dump tickets to the SD card? + tik: use dumped tickets when the original ones can't be found in the ES savefile? + + nca: function to write encrypted nca headers / nca fs headers (don't forget nca0 please) pfs0: filelist generation methods pfs0: full header aligned to 0x20 (nsp) - pfs0: patch writing function + pfs0: function to write patches romfs: filelist generation methods - romfs: patch writing function + romfs: function to write patches + + bktr: filelist generation methods (wrappers for romfs functions) + + + + + + char content_info_path[FS_MAX_PATH] = {0}; + sprintf(content_info_path, "sdmc:/%016lX.bin", xml_program_info.title_id); + + FILE *content_info = fopen(content_info_path, "wb"); + if (content_info) + { + fwrite(titleContentInfos, 1, titleContentInfoCnt * sizeof(NcmContentInfo), content_info); + fclose(content_info); + } + + + - bktr: filelist generation methods Result txIsFat32(bool *mode) { Result rc = serviceDispatch(&g_tx, 137); diff --git a/nxdumptool_todo.txt b/nxdumptool_todo.txt deleted file mode 100644 index b36ec30..0000000 --- a/nxdumptool_todo.txt +++ /dev/null @@ -1,20 +0,0 @@ - char content_info_path[FS_MAX_PATH] = {0}; - sprintf(content_info_path, "sdmc:/%016lX.bin", xml_program_info.title_id); - - FILE *content_info = fopen(content_info_path, "wb"); - if (content_info) - { - fwrite(titleContentInfos, 1, titleContentInfoCnt * sizeof(NcmContentInfo), content_info); - fclose(content_info); - } - - - - - - - improve cert.c/h - improve headers - - improve comments and button handling - improve function names \ No newline at end of file diff --git a/source/aes.c b/source/aes.c new file mode 100644 index 0000000..902d6ca --- /dev/null +++ b/source/aes.c @@ -0,0 +1,46 @@ +/* + * aes.c + * + * Copyright (c) 2020, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * + * nxdumptool is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * nxdumptool is distributed in the hope it will be useful, but WITHOUT + * 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 . + */ + +#include "utils.h" + +size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src, size_t size, u64 sector, size_t sector_size, bool encrypt) +{ + if (!ctx || !dst || !src || !size || !sector_size || (size % sector_size) != 0) + { + LOGFILE("Invalid parameters!"); + return 0; + } + + size_t i, crypt_res = 0; + u64 cur_sector = sector; + + u8 *dst_u8 = (u8*)dst; + const u8 *src_u8 = (const u8*)src; + + for(i = 0; i < size; i += sector_size, cur_sector++) + { + /* We have to force a sector reset on each new sector to actually enable Nintendo AES-XTS cipher tweak. */ + aes128XtsContextResetSector(ctx, cur_sector, true); + crypt_res = (encrypt ? aes128XtsEncrypt(ctx, dst_u8 + i, src_u8 + i, sector_size) : aes128XtsDecrypt(ctx, dst_u8 + i, src_u8 + i, sector_size)); + if (crypt_res != sector_size) break; + } + + return i; +} diff --git a/source/aes.h b/source/aes.h new file mode 100644 index 0000000..b220634 --- /dev/null +++ b/source/aes.h @@ -0,0 +1,31 @@ +/* + * aes.h + * + * Copyright (c) 2020, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * + * nxdumptool is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * nxdumptool is distributed in the hope it will be useful, but WITHOUT + * 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 . + */ + +#pragma once + +#ifndef __AES_H__ +#define __AES_H__ + +/// Performs an AES-128-XTS crypto operation using the non-standard Nintendo XTS tweak. +/// The Aes128XtsContext element should have been previously initialized with aes128XtsContextCreate(). 'encrypt' should match the value of 'is_encryptor' used with that call. +/// 'dst' and 'src' can both point to the same address. +size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src, size_t size, u64 sector, size_t sector_size, bool encrypt); + +#endif /* __AES_H__ */ diff --git a/source/bktr.c b/source/bktr.c index 605b52b..ff67834 100644 --- a/source/bktr.c +++ b/source/bktr.c @@ -49,18 +49,18 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct return false; } - /* Initialize base NCA RomFS context */ + /* Initialize base NCA RomFS context. */ if (!romfsInitializeContext(&(out->base_romfs_ctx), base_nca_fs_ctx)) { LOGFILE("Failed to initialize base NCA RomFS context!"); return false; } - /* Fill context */ + /* Fill context. */ bool success = false; NcaPatchInfo *patch_info = &(update_nca_fs_ctx->header->patch_info); - /* Allocate space for an extra (fake) indirect storage entry, to simplify our logic */ + /* Allocate space for an extra (fake) indirect storage entry, to simplify our logic. */ out->indirect_block = calloc(1, patch_info->indirect_size + ((0x3FF0 / sizeof(u64)) * sizeof(BktrIndirectStorageEntry))); if (!out->indirect_block) { @@ -68,14 +68,14 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct goto exit; } - /* Read indirect storage block data */ + /* Read indirect storage block data. */ if (!ncaReadFsSection(update_nca_fs_ctx, out->indirect_block, patch_info->indirect_size, patch_info->indirect_offset)) { LOGFILE("Failed to read BKTR Indirect Storage Block data!"); goto exit; } - /* Allocate space for an extra (fake) AesCtrEx storage entry, to simplify our logic */ + /* Allocate space for an extra (fake) AesCtrEx storage entry, to simplify our logic. */ out->aes_ctr_ex_block = calloc(1, patch_info->aes_ctr_ex_size + (((0x3FF0 / sizeof(u64)) + 1) * sizeof(BktrAesCtrExStorageEntry))); if (!out->aes_ctr_ex_block) { @@ -83,7 +83,7 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct goto exit; } - /* Read AesCtrEx storage block data */ + /* Read AesCtrEx storage block data. */ if (!ncaReadFsSection(update_nca_fs_ctx, out->aes_ctr_ex_block, patch_info->aes_ctr_ex_size, patch_info->aes_ctr_ex_offset)) { LOGFILE("Failed to read BKTR AesCtrEx Storage Block data!"); @@ -133,8 +133,8 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct last_aes_ctr_ex_bucket->aes_ctr_ex_storage_entries[last_aes_ctr_ex_bucket->entry_count + 1].offset = update_nca_fs_ctx->section_size; last_aes_ctr_ex_bucket->aes_ctr_ex_storage_entries[last_aes_ctr_ex_bucket->entry_count + 1].generation = 0; - /* Initialize update NCA RomFS context */ - /* Don't verify offsets from Patch RomFS sections, because they reflect the full, patched RomFS image */ + /* Initialize update NCA RomFS context. */ + /* Don't verify offsets from Patch RomFS sections, because they reflect the full, patched RomFS image. */ out->patch_romfs_ctx.nca_fs_ctx = update_nca_fs_ctx; out->patch_romfs_ctx.offset = out->offset = update_nca_fs_ctx->header->hash_info.hierarchical_integrity.hash_target_layer_info.offset; out->patch_romfs_ctx.size = out->size = update_nca_fs_ctx->header->hash_info.hierarchical_integrity.hash_target_layer_info.size; @@ -152,7 +152,7 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct goto exit; } - /* Read directory entries table */ + /* Read directory entries table. */ u64 dir_table_offset = out->patch_romfs_ctx.header.cur_format.directory_entry_offset; out->patch_romfs_ctx.dir_table_size = out->patch_romfs_ctx.header.cur_format.directory_entry_size; @@ -175,7 +175,7 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct goto exit; } - /* Read file entries table */ + /* Read file entries table. */ u64 file_table_offset = out->patch_romfs_ctx.header.cur_format.file_entry_offset; out->patch_romfs_ctx.file_table_size = out->patch_romfs_ctx.header.cur_format.file_entry_size; @@ -198,7 +198,7 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct goto exit; } - /* Get file data body offset */ + /* Get file data body offset. */ out->patch_romfs_ctx.body_offset = out->body_offset = out->patch_romfs_ctx.header.cur_format.body_offset; success = true; @@ -217,7 +217,7 @@ bool bktrReadFileSystemData(BktrContext *ctx, void *out, u64 read_size, u64 offs return false; } - /* Read filesystem data */ + /* Read filesystem data. */ if (!bktrPhysicalSectionRead(ctx, out, read_size, ctx->offset + offset)) { LOGFILE("Failed to read Patch RomFS data!"); @@ -236,7 +236,7 @@ bool bktrReadFileEntryData(BktrContext *ctx, RomFileSystemFileEntry *file_entry, return false; } - /* Read entry data */ + /* Read entry data. */ if (!bktrReadFileSystemData(ctx, out, read_size, ctx->body_offset + file_entry->offset + offset)) { LOGFILE("Failed to read Patch RomFS file entry data!"); @@ -295,8 +295,8 @@ static bool bktrPhysicalSectionRead(BktrContext *ctx, void *out, u64 read_size, BktrIndirectStorageEntry *indirect_entry = NULL, *next_indirect_entry = NULL; u64 section_offset = 0, indirect_block_size = 0; - /* Determine which FS section to use + the actual offset to start reading from */ - /* There's no better way to do this than making all BKTR addresses virtual */ + /* Determine which FS section to use + the actual offset to start reading from. */ + /* There's no better way to do this than making all BKTR addresses virtual. */ indirect_entry = bktrGetIndirectStorageEntry(ctx->indirect_block, offset); if (!indirect_entry) { @@ -307,12 +307,12 @@ static bool bktrPhysicalSectionRead(BktrContext *ctx, void *out, u64 read_size, next_indirect_entry = (indirect_entry + 1); section_offset = (offset - indirect_entry->virtual_offset + indirect_entry->physical_offset); - /* Perform read operation */ + /* Perform read operation. */ bool success = false; if ((offset + read_size) <= next_indirect_entry->virtual_offset) { - /* Read only within the current indirect storage entry */ - /* If we're not dealing with an indirect storage entry with a patch index, just retrieve the data from the base RomFS */ + /* Read only within the current indirect storage entry. */ + /* If we're not dealing with an indirect storage entry with a patch index, just retrieve the data from the base RomFS. */ if (indirect_entry->indirect_storage_index == BktrIndirectStorageIndex_Patch) { success = bktrAesCtrExStorageRead(ctx, out, read_size, offset, section_offset); @@ -322,7 +322,7 @@ static bool bktrPhysicalSectionRead(BktrContext *ctx, void *out, u64 read_size, if (!success) LOGFILE("Failed to read 0x%lX bytes block from base RomFS at offset 0x%lX!", read_size, section_offset); } } else { - /* Handle reads that span multiple indirect storage entries */ + /* Handle reads that span multiple indirect storage entries. */ indirect_block_size = (next_indirect_entry->virtual_offset - offset); success = (bktrPhysicalSectionRead(ctx, out, indirect_block_size, offset) && \ @@ -352,14 +352,14 @@ static bool bktrAesCtrExStorageRead(BktrContext *ctx, void *out, u64 read_size, next_aes_ctr_ex_entry = (aes_ctr_ex_entry + 1); - /* Perform read operation */ + /* Perform read operation. */ bool success = false; if ((section_offset + read_size) <= next_aes_ctr_ex_entry->offset) { - /* Read only within the current AesCtrEx storage entry */ + /* Read only within the current AesCtrEx storage entry. */ success = ncaReadAesCtrExStorageFromBktrSection(ctx->patch_romfs_ctx.nca_fs_ctx, out, read_size, section_offset, aes_ctr_ex_entry->generation); } else { - /* Handle read that spans multiple AesCtrEx storage entries */ + /* Handle read that spans multiple AesCtrEx storage entries. */ u64 aes_ctr_ex_block_size = (next_aes_ctr_ex_entry->offset - section_offset); success = (bktrPhysicalSectionRead(ctx, out, aes_ctr_ex_block_size, virtual_offset) && \ @@ -398,10 +398,10 @@ static BktrIndirectStorageEntry *bktrGetIndirectStorageEntry(BktrIndirectStorage return NULL; } - /* Check for edge case, short circuit */ + /* Check for edge case, short circuit. */ if (bucket->entry_count == 1) return &(bucket->indirect_storage_entries[0]); - /* Binary search */ + /* Binary search. */ u32 low = 0, high = (bucket->entry_count - 1); while(low <= high) { @@ -409,10 +409,10 @@ static BktrIndirectStorageEntry *bktrGetIndirectStorageEntry(BktrIndirectStorage if (bucket->indirect_storage_entries[mid].virtual_offset > offset) { - /* Too high */ + /* Too high. */ high = (mid - 1); } else { - /* Check for success */ + /* Check for success. */ if (mid == (bucket->entry_count - 1) || bucket->indirect_storage_entries[mid + 1].virtual_offset > offset) return &(bucket->indirect_storage_entries[mid]); low = (mid + 1); } @@ -460,10 +460,10 @@ static BktrAesCtrExStorageEntry *bktrGetAesCtrExStorageEntry(BktrAesCtrExStorage return NULL; } - /* Check for edge case, short circuit */ + /* Check for edge case, short circuit. */ if (bucket->entry_count == 1) return &(bucket->aes_ctr_ex_storage_entries[0]); - /* Binary search */ + /* Binary search. */ u32 low = 0, high = (bucket->entry_count - 1); while(low <= high) { @@ -471,10 +471,10 @@ static BktrAesCtrExStorageEntry *bktrGetAesCtrExStorageEntry(BktrAesCtrExStorage if (bucket->aes_ctr_ex_storage_entries[mid].offset > offset) { - /* Too high */ + /* Too high. */ high = (mid - 1); } else { - /* Check for success */ + /* Check for success. */ if (mid == (bucket->entry_count - 1) || bucket->aes_ctr_ex_storage_entries[mid + 1].offset > offset) return &(bucket->aes_ctr_ex_storage_entries[mid]); low = (mid + 1); } diff --git a/source/bktr.h b/source/bktr.h index 5a05066..a9c2054 100644 --- a/source/bktr.h +++ b/source/bktr.h @@ -113,50 +113,42 @@ bool bktrIsFileEntryUpdated(BktrContext *ctx, RomFileSystemFileEntry *file_entry NX_INLINE RomFileSystemDirectoryEntry *bktrGetDirectoryEntryByOffset(BktrContext *ctx, u32 dir_entry_offset) { - if (!ctx) return NULL; - return romfsGetDirectoryEntryByOffset(&(ctx->patch_romfs_ctx), dir_entry_offset); + return (ctx != NULL ? romfsGetDirectoryEntryByOffset(&(ctx->patch_romfs_ctx), dir_entry_offset) : NULL); } NX_INLINE RomFileSystemFileEntry *bktrGetFileEntryByOffset(BktrContext *ctx, u32 file_entry_offset) { - if (!ctx) return NULL; - return romfsGetFileEntryByOffset(&(ctx->patch_romfs_ctx), file_entry_offset); + return (ctx != NULL ? romfsGetFileEntryByOffset(&(ctx->patch_romfs_ctx), file_entry_offset) : NULL); } NX_INLINE bool bktrGetTotalDataSize(BktrContext *ctx, u64 *out_size) { - if (!ctx) return false; - return romfsGetTotalDataSize(&(ctx->patch_romfs_ctx), out_size); + return (ctx != NULL ? romfsGetTotalDataSize(&(ctx->patch_romfs_ctx), out_size) : false); } NX_INLINE bool bktrGetDirectoryDataSize(BktrContext *ctx, RomFileSystemDirectoryEntry *dir_entry, u64 *out_size) { - if (!ctx) return false; - return romfsGetDirectoryDataSize(&(ctx->patch_romfs_ctx), dir_entry, out_size); + return (ctx != NULL ? romfsGetDirectoryDataSize(&(ctx->patch_romfs_ctx), dir_entry, out_size) : false); } NX_INLINE RomFileSystemDirectoryEntry *bktrGetDirectoryEntryByPath(BktrContext *ctx, const char *path) { - if (!ctx) return NULL; - return romfsGetDirectoryEntryByPath(&(ctx->patch_romfs_ctx), path); + return (ctx != NULL ? romfsGetDirectoryEntryByPath(&(ctx->patch_romfs_ctx), path) : NULL); } NX_INLINE RomFileSystemFileEntry *bktrGetFileEntryByPath(BktrContext *ctx, const char *path) { - if (!ctx) return NULL; - return romfsGetFileEntryByPath(&(ctx->patch_romfs_ctx), path); + return (ctx != NULL ? romfsGetFileEntryByPath(&(ctx->patch_romfs_ctx), path) : NULL); } NX_INLINE bool bktrGeneratePathFromDirectoryEntry(BktrContext *ctx, RomFileSystemDirectoryEntry *dir_entry, char *out_path, size_t out_path_size, u8 illegal_char_replace_type) { - if (!ctx) return false; - return romfsGeneratePathFromDirectoryEntry(&(ctx->patch_romfs_ctx), dir_entry, out_path, out_path_size, illegal_char_replace_type); + return (ctx != NULL ? romfsGeneratePathFromDirectoryEntry(&(ctx->patch_romfs_ctx), dir_entry, out_path, out_path_size, illegal_char_replace_type) : false); } NX_INLINE bool bktrGeneratePathFromFileEntry(BktrContext *ctx, RomFileSystemFileEntry *file_entry, char *out_path, size_t out_path_size, u8 illegal_char_replace_type) { - if (!ctx) return false; - return romfsGeneratePathFromFileEntry(&(ctx->patch_romfs_ctx), file_entry, out_path, out_path_size, illegal_char_replace_type); + return (ctx != NULL ? romfsGeneratePathFromFileEntry(&(ctx->patch_romfs_ctx), file_entry, out_path, out_path_size, illegal_char_replace_type) : false); } #endif /* __BKTR_H__ */ diff --git a/source/cert.c b/source/cert.c index 831da5e..9897d97 100644 --- a/source/cert.c +++ b/source/cert.c @@ -325,8 +325,8 @@ static bool _certRetrieveCertificateChainBySignatureIssuer(CertificateChain *dst return false; } - /* Copy string to avoid problems with strtok */ - /* The "Root-" parent from the issuer string is skipped */ + /* Copy string to avoid problems with strtok(). */ + /* The "Root-" parent from the issuer string is skipped. */ snprintf(issuer_copy, 0x40, "%s", issuer + 5); char *pch = strtok(issuer_copy, "-"); @@ -355,8 +355,8 @@ static u32 certGetCertificateCountInSignatureIssuer(const char *issuer) u32 count = 0; char issuer_copy[0x40] = {0}; - /* Copy string to avoid problems with strtok */ - /* The "Root-" parent from the issuer string is skipped */ + /* Copy string to avoid problems with strtok(). */ + /* The "Root-" parent from the issuer string is skipped. */ snprintf(issuer_copy, 0x40, issuer + 5); char *pch = strtok(issuer_copy, "-"); diff --git a/source/crc32_fast.c b/source/crc32_fast.c index de1efa5..7bf6ba4 100644 --- a/source/crc32_fast.c +++ b/source/crc32_fast.c @@ -21,51 +21,42 @@ * along with this program. If not, see . */ -/* Standard CRC32 checksum: fast public domain implementation for - * little-endian architectures. Written for compilation with an - * optimizer set to perform loop unwinding. Outputs the checksum for - * each file given as a command line argument. Invalid file names and - * files that cause errors are silently skipped. The program reads - * from stdin if it is called with no arguments. */ - #include "utils.h" -u32 crc32_for_byte(u32 r) +static u32 crc32FastGetTableValueByIndex(u32 r) { - for(int j = 0; j < 8; ++j) r = (r & 1 ? 0 : (u32)0xEDB88320L) ^ r >> 1; - return r ^ (u32)0xFF000000L; + for(u32 j = 0; j < 8; ++j) r = ((r & 1 ? 0 : (u32)0xEDB88320) ^ r >> 1); + return (r ^ (u32)0xFF000000); } -/* Any unsigned integer type with at least 32 bits may be used as - * accumulator type for fast crc32-calulation, but unsigned long is - * probably the optimal choice for most systems. */ -typedef unsigned long accum_t; - -void init_tables(u32 *table, u32 *wtable) +static void crc32FastInitializeTables(u32 *table, u32 *wtable) { - for(u64 i = 0; i < 0x100; ++i) table[i] = crc32_for_byte(i); + for(u32 i = 0; i < 0x100; ++i) table[i] = crc32FastGetTableValueByIndex(i); - for(u64 k = 0; k < sizeof(accum_t); ++k) + for(u32 k = 0; k < 4; ++k) { - for(u64 w, i = 0; i < 0x100; ++i) + for(u32 w = 0, i = 0; i < 0x100; ++i) { - for(u64 j = w = 0; j < sizeof(accum_t); ++j) w = table[(u8)(j == k ? w ^ i : w)] ^ w >> 8; - wtable[(k << 8) + i] = w ^ (k ? wtable[0] : 0); + for(u32 j = 0; j < 4; ++j) w = (table[(u8)(j == k ? (w ^ i) : w)] ^ w >> 8); + wtable[i + (k << 8)] = (w ^ (k ? wtable[0] : 0)); } } } -void crc32(const void *data, u64 n_bytes, u32 *crc) +void crc32FastCalculate(const void *data, u64 n_bytes, u32 *crc) { - static u32 table[0x100], wtable[0x100 * sizeof(accum_t)]; - u64 n_accum = n_bytes / sizeof(accum_t); + if (!data || !n_bytes || !crc) return; + + static u32 table[0x100] = {0}, wtable[0x100 * 4] = {0}; + u64 n_accum = (n_bytes / 4); + + if (!*table) crc32FastInitializeTables(table, wtable); - if (!*table) init_tables(table, wtable); for(u64 i = 0; i < n_accum; ++i) { - accum_t a = *crc ^ ((accum_t*)data)[i]; - for(u64 j = *crc = 0; j < sizeof(accum_t); ++j) *crc ^= wtable[(j << 8) + (u8)(a >> 8 * j)]; + u32 a = (*crc ^ ((u32*)data)[i]); + for(u32 j = *crc = 0; j < 4; ++j) *crc ^= wtable[(j << 8) + (u8)(a >> 8 * j)]; } - for(u64 i = n_accum * sizeof(accum_t); i < n_bytes; ++i) *crc = table[(u8)*crc ^ ((u8*)data)[i]] ^ *crc >> 8; + for(u64 i = (n_accum * 4); i < n_bytes; ++i) *crc = (table[(u8)*crc ^ ((u8*)data)[i]] ^ *crc >> 8); } diff --git a/source/crc32_fast.h b/source/crc32_fast.h new file mode 100644 index 0000000..7255880 --- /dev/null +++ b/source/crc32_fast.h @@ -0,0 +1,33 @@ +/* + * crc32_fast.h + * + * Based on the standard CRC32 checksum fast public domain implementation for + * little-endian architecures by Björn Samuelsson (http://home.thep.lu.se/~bjorn/crc). + * + * Copyright (c) 2020, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * + * nxdumptool is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * nxdumptool is distributed in the hope it will be useful, but WITHOUT + * 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 . + */ + +#pragma once + +#ifndef __CRC32_FAST_H__ +#define __CRC32_FAST_H__ + +/// Calculates a CRC32 checksum over the provided input buffer. Checksum calculation in chunks is supported. +/// CRC32 calculation state is both read from and saved to 'crc', which should be zero during the first call to this function. +void crc32FastCalculate(const void *data, u64 n_bytes, u32 *crc); + +#endif /* __CRC32_FAST_H__ */ diff --git a/source/es.c b/source/es.c index 99514de..44409bd 100644 --- a/source/es.c +++ b/source/es.c @@ -1,6 +1,7 @@ /* * es.c * + * Copyright (c) 2018-2020, Addubz. * Copyright (c) 2020, DarkMatterCore . * * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). diff --git a/source/es.h b/source/es.h index 59335d7..9053509 100644 --- a/source/es.h +++ b/source/es.h @@ -1,6 +1,7 @@ /* * es.h * + * Copyright (c) 2018-2020, Addubz. * Copyright (c) 2020, DarkMatterCore . * * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). diff --git a/source/fs_ext.c b/source/fs_ext.c index 375345a..5b71ee1 100644 --- a/source/fs_ext.c +++ b/source/fs_ext.c @@ -21,7 +21,7 @@ #include "utils.h" #include "fs_ext.h" -/* IFileSystemProxy */ +/* IFileSystemProxy. */ Result fsOpenGameCardStorage(FsStorage *out, const FsGameCardHandle *handle, u32 partition) { struct { @@ -43,7 +43,7 @@ Result fsOpenGameCardDetectionEventNotifier(FsEventNotifier *out) ); } -/* IDeviceOperator */ +/* IDeviceOperator. */ Result fsDeviceOperatorUpdatePartitionInfo(FsDeviceOperator *d, const FsGameCardHandle *handle, u32 *out_title_version, u64 *out_title_id) { struct { diff --git a/source/fs_ext.h b/source/fs_ext.h index 36915eb..595dcd7 100644 --- a/source/fs_ext.h +++ b/source/fs_ext.h @@ -26,7 +26,7 @@ /// Located at offset 0x7000 in the gamecard image. typedef struct { u8 signature[0x100]; ///< RSA-2048 PKCS #1 signature over the rest of the data. - u32 magic; ///< "CERT" + u32 magic; ///< "CERT". u8 reserved_1[0x4]; u8 kek_index; u8 reserved_2[0x7]; @@ -35,11 +35,11 @@ typedef struct { u8 encrypted_data[0xD0]; } FsGameCardCertificate; -/* IFileSystemProxy */ +/// IFileSystemProxy. Result fsOpenGameCardStorage(FsStorage *out, const FsGameCardHandle *handle, u32 partition); Result fsOpenGameCardDetectionEventNotifier(FsEventNotifier *out); -/* IDeviceOperator */ +/// IDeviceOperator. Result fsDeviceOperatorUpdatePartitionInfo(FsDeviceOperator *d, const FsGameCardHandle *handle, u32 *out_title_version, u64 *out_title_id); Result fsDeviceOperatorGetGameCardDeviceCertificate(FsDeviceOperator *d, const FsGameCardHandle *handle, FsGameCardCertificate *out); diff --git a/source/fspusb.c b/source/fspusb.c index f13bcac..724d8d8 100644 --- a/source/fspusb.c +++ b/source/fspusb.c @@ -60,7 +60,7 @@ Result fspusbGetDriveLabel(s32 interface_id, char *out_label, size_t out_label_s } Result fspusbSetDriveLabel(s32 interface_id, const char *label) { - char inputlbl[11 + 1] = {0}; // Actual limit is 11 characters + char inputlbl[11 + 1] = {0}; /* Actual limit is 11 characters. */ strncpy(inputlbl, label, 11); return serviceDispatchIn(&g_fspusbSrv, 3, interface_id, .buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcMapAlias }, diff --git a/source/fspusb.h b/source/fspusb.h index 0a6c4bf..b944b27 100644 --- a/source/fspusb.h +++ b/source/fspusb.h @@ -24,12 +24,12 @@ #ifndef __FSPUSB_H__ #define __FSPUSB_H__ -/// This is basically FATFS' file system types. +/// This is basically FatFs' file system types. typedef enum { FspUsbFileSystemType_FAT12 = 1, FspUsbFileSystemType_FAT16 = 2, FspUsbFileSystemType_FAT32 = 3, - FspUsbFileSystemType_exFAT = 4, + FspUsbFileSystemType_exFAT = 4 } FspUsbFileSystemType; /// Initialize fsp-usb. diff --git a/source/gamecard.c b/source/gamecard.c index e88b9a7..5d4ba54 100644 --- a/source/gamecard.c +++ b/source/gamecard.c @@ -21,11 +21,11 @@ #include "utils.h" #include "gamecard.h" -#define GAMECARD_HFS0_MAGIC 0x48465330 /* "HFS0" */ +#define GAMECARD_HFS0_MAGIC 0x48465330 /* "HFS0". */ -#define GAMECARD_READ_BUFFER_SIZE 0x800000 /* 8 MiB */ +#define GAMECARD_READ_BUFFER_SIZE 0x800000 /* 8 MiB. */ -#define GAMECARD_ACCESS_WAIT_TIME 3 /* Seconds */ +#define GAMECARD_ACCESS_WAIT_TIME 3 /* Seconds. */ #define GAMECARD_UPDATE_TID (u64)0x0100000000000816 @@ -135,7 +135,7 @@ bool gamecardInitialize(void) bool ret = g_gamecardInterfaceInit; if (ret) goto out; - /* Allocate memory for the gamecard read buffer */ + /* Allocate memory for the gamecard read buffer. */ g_gameCardReadBuf = malloc(GAMECARD_READ_BUFFER_SIZE); if (!g_gameCardReadBuf) { @@ -143,17 +143,17 @@ bool gamecardInitialize(void) goto out; } - /* Open device operator */ + /* Open device operator. */ rc = fsOpenDeviceOperator(&g_deviceOperator); if (R_FAILED(rc)) { - LOGFILE("fsOpenDeviceOperator failed! (0x%08X)", rc); + LOGFILE("fsOpenDeviceOperator failed! (0x%08X).", rc); goto out; } g_openDeviceOperator = true; - /* Open gamecard detection event notifier */ + /* Open gamecard detection event notifier. */ rc = fsOpenGameCardDetectionEventNotifier(&g_gameCardEventNotifier); if (R_FAILED(rc)) { @@ -163,7 +163,7 @@ bool gamecardInitialize(void) g_openEventNotifier = true; - /* Retrieve gamecard detection kernel event */ + /* Retrieve gamecard detection kernel event. */ rc = fsEventNotifierGetEventHandle(&g_gameCardEventNotifier, &g_gameCardKernelEvent, true); if (R_FAILED(rc)) { @@ -173,13 +173,13 @@ bool gamecardInitialize(void) g_loadKernelEvent = true; - /* Create usermode exit event */ + /* Create usermode exit event. */ ueventCreate(&g_gameCardDetectionThreadExitEvent, true); - /* Create usermode gamecard status change event */ + /* Create usermode gamecard status change event. */ ueventCreate(&g_gameCardStatusChangeEvent, true); - /* Create gamecard detection thread */ + /* Create gamecard detection thread. */ if (!(g_gameCardDetectionThreadCreated = gamecardCreateDetectionThread())) goto out; ret = g_gamecardInterfaceInit = true; @@ -194,35 +194,35 @@ void gamecardExit(void) { mutexLock(&g_gamecardMutex); - /* Destroy gamecard detection thread */ + /* Destroy gamecard detection thread. */ if (g_gameCardDetectionThreadCreated) { gamecardDestroyDetectionThread(); g_gameCardDetectionThreadCreated = false; } - /* Close gamecard detection kernel event */ + /* Close gamecard detection kernel event. */ if (g_loadKernelEvent) { eventClose(&g_gameCardKernelEvent); g_loadKernelEvent = false; } - /* Close gamecard detection event notifier */ + /* Close gamecard detection event notifier. */ if (g_openEventNotifier) { fsEventNotifierClose(&g_gameCardEventNotifier); g_openEventNotifier = false; } - /* Close device operator */ + /* Close device operator. */ if (g_openDeviceOperator) { fsDeviceOperatorClose(&g_deviceOperator); g_openDeviceOperator = false; } - /* Free gamecard read buffer */ + /* Free gamecard read buffer. */ if (g_gameCardReadBuf) { free(g_gameCardReadBuf); @@ -244,12 +244,9 @@ UEvent *gamecardGetStatusChangeUserEvent(void) bool gamecardIsReady(void) { - bool ret = false; - mutexLock(&g_gamecardMutex); - ret = (g_gameCardInserted && g_gameCardInfoLoaded); + bool ret = (g_gameCardInserted && g_gameCardInfoLoaded); mutexUnlock(&g_gamecardMutex); - return ret; } @@ -260,61 +257,37 @@ bool gamecardReadStorage(void *out, u64 read_size, u64 offset) bool gamecardGetHeader(GameCardHeader *out) { - bool ret = false; - mutexLock(&g_gamecardMutex); - if (g_gameCardInserted && g_gameCardInfoLoaded && out) - { - memcpy(out, &g_gameCardHeader, sizeof(GameCardHeader)); - ret = true; - } + bool ret = (g_gameCardInserted && g_gameCardInfoLoaded && out); + if (ret) memcpy(out, &g_gameCardHeader, sizeof(GameCardHeader)); mutexUnlock(&g_gamecardMutex); - return ret; } bool gamecardGetTotalSize(u64 *out) { - bool ret = false; - mutexLock(&g_gamecardMutex); - if (g_gameCardInserted && g_gameCardInfoLoaded && out) - { - *out = (g_gameCardStorageNormalAreaSize + g_gameCardStorageSecureAreaSize); - ret = true; - } + bool ret = (g_gameCardInserted && g_gameCardInfoLoaded && out); + if (ret) *out = (g_gameCardStorageNormalAreaSize + g_gameCardStorageSecureAreaSize); mutexUnlock(&g_gamecardMutex); - return ret; } bool gamecardGetTrimmedSize(u64 *out) { - bool ret = false; - mutexLock(&g_gamecardMutex); - if (g_gameCardInserted && g_gameCardInfoLoaded && out) - { - *out = (sizeof(GameCardHeader) + ((u64)g_gameCardHeader.valid_data_end_address * GAMECARD_MEDIA_UNIT_SIZE)); - ret = true; - } + bool ret = (g_gameCardInserted && g_gameCardInfoLoaded && out); + if (ret) *out = (sizeof(GameCardHeader) + ((u64)g_gameCardHeader.valid_data_end_address * GAMECARD_MEDIA_UNIT_SIZE)); mutexUnlock(&g_gamecardMutex); - return ret; } bool gamecardGetRomCapacity(u64 *out) { - bool ret = false; - mutexLock(&g_gamecardMutex); - if (g_gameCardInserted && g_gameCardInfoLoaded && out) - { - *out = g_gameCardCapacity; - ret = true; - } + bool ret = (g_gameCardInserted && g_gameCardInfoLoaded && out); + if (ret) *out = g_gameCardCapacity; mutexUnlock(&g_gamecardMutex); - return ret; } @@ -477,7 +450,7 @@ bool gamecardGetEntryInfoFromHashFileSystemPartitionByName(u8 hfs_partition_type { if (hfs_partition_type == GameCardHashFileSystemPartitionType_Root) { - *out_offset = g_gameCardHfsPartitions[fs_entry_idx].offset; /* No need to recalculate what we already have */ + *out_offset = g_gameCardHfsPartitions[fs_entry_idx].offset; /* No need to recalculate what we already have. */ } else { *out_offset = (g_gameCardHfsPartitions[hfs_partition_idx].offset + g_gameCardHfsPartitions[hfs_partition_idx].header_size + fs_entry->offset); } @@ -507,10 +480,10 @@ static bool gamecardCreateDetectionThread(void) static void gamecardDestroyDetectionThread(void) { - /* Signal the exit event to terminate the gamecard detection thread */ + /* Signal the exit event to terminate the gamecard detection thread. */ ueventSignal(&g_gameCardDetectionThreadExitEvent); - /* Wait for the gamecard detection thread to exit */ + /* Wait for the gamecard detection thread to exit. */ thrd_join(g_gameCardDetectionThread, NULL); } @@ -524,34 +497,34 @@ static int gamecardDetectionThreadFunc(void *arg) Waiter gamecard_event_waiter = waiterForEvent(&g_gameCardKernelEvent); Waiter exit_event_waiter = waiterForUEvent(&g_gameCardDetectionThreadExitEvent); - /* Retrieve initial gamecard insertion status */ - /* Load gamecard info right away if a gamecard is inserted */ + /* Retrieve initial gamecard insertion status. */ + /* Load gamecard info right away if a gamecard is inserted. */ g_gameCardInserted = gamecardIsInserted(); if (g_gameCardInserted) gamecardLoadInfo(); ueventSignal(&g_gameCardStatusChangeEvent); while(true) { - /* Wait until an event is triggered */ + /* Wait until an event is triggered. */ rc = waitMulti(&idx, -1, gamecard_event_waiter, exit_event_waiter); if (R_FAILED(rc)) continue; - /* Exit event triggered */ + /* Exit event triggered. */ if (idx == 1) break; mutexLock(&g_gamecardMutex); - /* Retrieve current gamecard insertion status */ - /* Only proceed if we're dealing with a status change */ + /* Retrieve current gamecard insertion status. */ + /* Only proceed if we're dealing with a status change. */ g_gameCardInserted = gamecardIsInserted(); gamecardFreeInfo(); if (g_gameCardInserted) { - /* Don't access the gamecard immediately to avoid conflicts with HOS / sysmodules */ + /* Don't access the gamecard immediately to avoid conflicts with HOS / sysmodules. */ utilsSleep(GAMECARD_ACCESS_WAIT_TIME); - /* Load gamecard info */ + /* Load gamecard info. */ gamecardLoadInfo(); } @@ -560,7 +533,7 @@ static int gamecardDetectionThreadFunc(void *arg) ueventSignal(&g_gameCardStatusChangeEvent); } - /* Free gamecard info and close gamecard handle */ + /* Free gamecard info and close gamecard handle. */ gamecardFreeInfo(); g_gameCardInserted = false; @@ -582,44 +555,44 @@ static void gamecardLoadInfo(void) GameCardHashFileSystemHeader *fs_header = NULL; GameCardHashFileSystemEntry *fs_entry = NULL; - /* Retrieve gamecard storage area sizes */ - /* gamecardReadStorageArea() actually checks if the storage area sizes are greater than zero, so we must first perform this step */ + /* Retrieve gamecard storage area sizes. */ + /* gamecardReadStorageArea() actually checks if the storage area sizes are greater than zero, so we must first perform this step. */ if (!gamecardGetStorageAreasSizes()) { LOGFILE("Failed to retrieve gamecard storage area sizes!"); goto out; } - /* Read gamecard header */ + /* Read gamecard header. */ if (!gamecardReadStorageArea(&g_gameCardHeader, sizeof(GameCardHeader), 0, false)) { LOGFILE("Failed to read gamecard header!"); goto out; } - /* Check magic word from gamecard header */ + /* Check magic word from gamecard header. */ if (__builtin_bswap32(g_gameCardHeader.magic) != GAMECARD_HEAD_MAGIC) { LOGFILE("Invalid gamecard header magic word! (0x%08X)", __builtin_bswap32(g_gameCardHeader.magic)); goto out; } - /* Get gamecard capacity */ + /* Get gamecard capacity. */ g_gameCardCapacity = gamecardGetCapacityFromRomSizeValue(g_gameCardHeader.rom_size); if (!g_gameCardCapacity) { - LOGFILE("Invalid gamecard capacity value! (0x%02X)", g_gameCardHeader.rom_size); + LOGFILE("Invalid gamecard capacity value! (0x%02X).", g_gameCardHeader.rom_size); goto out; } if (utilsGetCustomFirmwareType() == UtilsCustomFirmwareType_SXOS) { - /* The total size for the secure storage area is maxed out under SX OS */ - /* Let's try to calculate it manually */ + /* The total size for the secure storage area is maxed out under SX OS. */ + /* Let's try to calculate it manually. */ g_gameCardStorageSecureAreaSize = ((g_gameCardCapacity - ((g_gameCardCapacity / GAMECARD_ECC_BLOCK_SIZE) * GAMECARD_ECC_DATA_SIZE)) - g_gameCardStorageNormalAreaSize); } - /* Allocate memory for the root hash FS header */ + /* Allocate memory for the root hash FS header. */ g_gameCardHfsRootHeader = calloc(g_gameCardHeader.partition_fs_header_size, sizeof(u8)); if (!g_gameCardHfsRootHeader) { @@ -627,7 +600,7 @@ static void gamecardLoadInfo(void) goto out; } - /* Read root hash FS header */ + /* Read root hash FS header. */ if (!gamecardReadStorageArea(g_gameCardHfsRootHeader, g_gameCardHeader.partition_fs_header_size, g_gameCardHeader.partition_fs_header_address, false)) { LOGFILE("Failed to read root hash FS header from offset 0x%lX!", g_gameCardHeader.partition_fs_header_address); @@ -638,7 +611,7 @@ static void gamecardLoadInfo(void) if (__builtin_bswap32(fs_header->magic) != GAMECARD_HFS0_MAGIC) { - LOGFILE("Invalid magic word in root hash FS header! (0x%08X)", __builtin_bswap32(fs_header->magic)); + LOGFILE("Invalid magic word in root hash FS header! (0x%08X).", __builtin_bswap32(fs_header->magic)); goto out; } @@ -649,7 +622,7 @@ static void gamecardLoadInfo(void) goto out; } - /* Allocate memory for the hash FS partitions info */ + /* Allocate memory for the hash FS partitions info. */ g_gameCardHfsPartitions = calloc(fs_header->entry_count, sizeof(GameCardHashFileSystemEntry)); if (!g_gameCardHfsPartitions) { @@ -657,7 +630,7 @@ static void gamecardLoadInfo(void) goto out; } - /* Read hash FS partitions */ + /* Read hash FS partitions. */ for(u32 i = 0; i < fs_header->entry_count; i++) { fs_entry = gamecardGetHashFileSystemEntryByIndex(g_gameCardHfsRootHeader, i); @@ -670,7 +643,7 @@ static void gamecardLoadInfo(void) g_gameCardHfsPartitions[i].offset = (g_gameCardHeader.partition_fs_header_address + g_gameCardHeader.partition_fs_header_size + fs_entry->offset); g_gameCardHfsPartitions[i].size = fs_entry->size; - /* Partially read the current hash FS partition header */ + /* Partially read the current hash FS partition header. */ GameCardHashFileSystemHeader partition_header = {0}; if (!gamecardReadStorageArea(&partition_header, sizeof(GameCardHashFileSystemHeader), g_gameCardHfsPartitions[i].offset, false)) { @@ -680,7 +653,7 @@ static void gamecardLoadInfo(void) if (__builtin_bswap32(partition_header.magic) != GAMECARD_HFS0_MAGIC) { - LOGFILE("Invalid magic word in hash FS partition #%u header! (0x%08X)", i, __builtin_bswap32(partition_header.magic)); + LOGFILE("Invalid magic word in hash FS partition #%u header! (0x%08X).", i, __builtin_bswap32(partition_header.magic)); goto out; } @@ -690,11 +663,11 @@ static void gamecardLoadInfo(void) goto out; } - /* Calculate the full header size for the current hash FS partition and round it to a GAMECARD_MEDIA_UNIT_SIZE bytes boundary */ + /* Calculate the full header size for the current hash FS partition and round it to a GAMECARD_MEDIA_UNIT_SIZE bytes boundary. */ g_gameCardHfsPartitions[i].header_size = (sizeof(GameCardHashFileSystemHeader) + (partition_header.entry_count * sizeof(GameCardHashFileSystemEntry)) + partition_header.name_table_size); g_gameCardHfsPartitions[i].header_size = ALIGN_UP(g_gameCardHfsPartitions[i].header_size, GAMECARD_MEDIA_UNIT_SIZE); - /* Allocate memory for the hash FS partition header */ + /* Allocate memory for the hash FS partition header. */ g_gameCardHfsPartitions[i].header = calloc(g_gameCardHfsPartitions[i].header_size, sizeof(u8)); if (!g_gameCardHfsPartitions[i].header) { @@ -702,7 +675,7 @@ static void gamecardLoadInfo(void) goto out; } - /* Finally, read the full hash FS partition header */ + /* Finally, read the full hash FS partition header. */ if (!gamecardReadStorageArea(g_gameCardHfsPartitions[i].header, g_gameCardHfsPartitions[i].header_size, g_gameCardHfsPartitions[i].offset, false)) { LOGFILE("Failed to read full hash FS partition #%u header from offset 0x%lX!", i, g_gameCardHfsPartitions[i].offset); @@ -763,10 +736,10 @@ static bool gamecardGetHandle(void) Result rc1 = 0, rc2 = 0; FsStorage tmp_storage = {0}; - /* 10 tries */ + /* 10 tries. */ for(u8 i = 0; i < 10; i++) { - /* First try to open a gamecard storage area using the current gamecard handle */ + /* First try to open a gamecard storage area using the current gamecard handle. */ rc1 = fsOpenGameCardStorage(&tmp_storage, &g_gameCardHandle, 0); if (R_SUCCEEDED(rc1)) { @@ -774,18 +747,18 @@ static bool gamecardGetHandle(void) break; } - /* If the previous call failed, we may have an invalid handle, so let's close the current one and try to retrieve a new one */ + /* If the previous call failed, we may have an invalid handle, so let's close the current one and try to retrieve a new one. */ gamecardCloseHandle(); rc2 = fsDeviceOperatorGetGameCardHandle(&g_deviceOperator, &g_gameCardHandle); } if (R_FAILED(rc1) || R_FAILED(rc2)) { - /* Close leftover gamecard handle */ + /* Close leftover gamecard handle. */ gamecardCloseHandle(); - if (R_FAILED(rc1)) LOGFILE("fsOpenGameCardStorage failed! (0x%08X)", rc1); - if (R_FAILED(rc2)) LOGFILE("fsDeviceOperatorGetGameCardHandle failed! (0x%08X)", rc2); + if (R_FAILED(rc1)) LOGFILE("fsOpenGameCardStorage failed! (0x%08X).", rc1); + if (R_FAILED(rc2)) LOGFILE("fsDeviceOperatorGetGameCardHandle failed! (0x%08X).", rc2); return false; } @@ -812,20 +785,20 @@ static bool gamecardOpenStorageArea(u8 area) gamecardCloseStorageArea(); Result rc = 0; - u32 partition = (area - 1); /* Zero-based index */ + u32 partition = (area - 1); /* Zero-based index. */ - /* Retrieve a new gamecard handle */ + /* Retrieve a new gamecard handle. */ if (!gamecardGetHandle()) { LOGFILE("Failed to retrieve gamecard handle!"); return false; } - /* Open storage area */ + /* Open storage area. */ rc = fsOpenGameCardStorage(&g_gameCardStorage, &g_gameCardHandle, partition); if (R_FAILED(rc)) { - LOGFILE("fsOpenGameCardStorage failed to open %s storage area! (0x%08X)", GAMECARD_STORAGE_AREA_NAME(area), rc); + LOGFILE("fsOpenGameCardStorage failed to open %s storage area! (0x%08X).", GAMECARD_STORAGE_AREA_NAME(area), rc); gamecardCloseHandle(); return false; } @@ -852,45 +825,45 @@ static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset, bool l u8 *out_u8 = (u8*)out; u8 area = (offset < g_gameCardStorageNormalAreaSize ? GameCardStorageArea_Normal : GameCardStorageArea_Secure); - /* Handle reads that span both the normal and secure gamecard storage areas */ + /* Handle reads that span both the normal and secure gamecard storage areas. */ if (area == GameCardStorageArea_Normal && (offset + read_size) > g_gameCardStorageNormalAreaSize) { - /* Calculate normal storage area size difference */ + /* Calculate normal storage area size difference. */ u64 diff_size = (g_gameCardStorageNormalAreaSize - offset); if (!gamecardReadStorageArea(out_u8, diff_size, offset, false)) goto exit; - /* Adjust variables to read right from the start of the secure storage area */ + /* Adjust variables to read right from the start of the secure storage area. */ read_size -= diff_size; offset = g_gameCardStorageNormalAreaSize; out_u8 += diff_size; area = GameCardStorageArea_Secure; } - /* Open a storage area if needed */ - /* If the right storage area has already been opened, this will return true */ + /* Open a storage area if needed. */ + /* If the right storage area has already been opened, this will return true. */ if (!gamecardOpenStorageArea(area)) { LOGFILE("Failed to open %s storage area!", GAMECARD_STORAGE_AREA_NAME(area)); goto exit; } - /* Calculate appropiate storage area offset and retrieve the right storage area pointer */ + /* Calculate appropiate storage area offset and retrieve the right storage area pointer. */ u64 base_offset = (area == GameCardStorageArea_Normal ? offset : (offset - g_gameCardStorageNormalAreaSize)); if (!(base_offset % GAMECARD_MEDIA_UNIT_SIZE) && !(read_size % GAMECARD_MEDIA_UNIT_SIZE)) { - /* Optimization for reads that are already aligned to a GAMECARD_MEDIA_UNIT_SIZE boundary */ + /* Optimization for reads that are already aligned to a GAMECARD_MEDIA_UNIT_SIZE boundary. */ rc = fsStorageRead(&g_gameCardStorage, base_offset, out_u8, read_size); if (R_FAILED(rc)) { - LOGFILE("fsStorageRead failed to read 0x%lX bytes at offset 0x%lX from %s storage area! (0x%08X) (aligned)", read_size, base_offset, GAMECARD_STORAGE_AREA_NAME(area), rc); + LOGFILE("fsStorageRead failed to read 0x%lX bytes at offset 0x%lX from %s storage area! (0x%08X) (aligned).", read_size, base_offset, GAMECARD_STORAGE_AREA_NAME(area), rc); goto exit; } success = true; } else { - /* Fix offset and/or size to avoid unaligned reads */ + /* Fix offset and/or size to avoid unaligned reads. */ u64 block_start_offset = ALIGN_DOWN(base_offset, GAMECARD_MEDIA_UNIT_SIZE); u64 block_end_offset = ALIGN_UP(base_offset + read_size, GAMECARD_MEDIA_UNIT_SIZE); u64 block_size = (block_end_offset - block_start_offset); @@ -902,7 +875,7 @@ static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset, bool l rc = fsStorageRead(&g_gameCardStorage, block_start_offset, g_gameCardReadBuf, chunk_size); if (R_FAILED(rc)) { - LOGFILE("fsStorageRead failed to read 0x%lX bytes at offset 0x%lX from %s storage area! (0x%08X) (unaligned)", chunk_size, block_start_offset, GAMECARD_STORAGE_AREA_NAME(area), rc); + LOGFILE("fsStorageRead failed to read 0x%lX bytes at offset 0x%lX from %s storage area! (0x%08X) (unaligned).", chunk_size, block_start_offset, GAMECARD_STORAGE_AREA_NAME(area), rc); goto exit; } @@ -956,7 +929,7 @@ static bool gamecardGetStorageAreasSizes(void) if (R_FAILED(rc) || !area_size) { - LOGFILE("fsStorageGetSize failed to retrieve %s storage area size! (0x%08X)", GAMECARD_STORAGE_AREA_NAME(area), rc); + LOGFILE("fsStorageGetSize failed to retrieve %s storage area size! (0x%08X).", GAMECARD_STORAGE_AREA_NAME(area), rc); g_gameCardStorageNormalAreaSize = g_gameCardStorageSecureAreaSize = 0; return false; } diff --git a/source/gamecard.h b/source/gamecard.h index b8ebeb2..a9b711c 100644 --- a/source/gamecard.h +++ b/source/gamecard.h @@ -25,8 +25,8 @@ #include "fs_ext.h" -#define GAMECARD_HEAD_MAGIC 0x48454144 /* "HEAD" */ -#define GAMECARD_CERT_MAGIC 0x43455254 /* "CERT" */ +#define GAMECARD_HEAD_MAGIC 0x48454144 /* "HEAD". */ +#define GAMECARD_CERT_MAGIC 0x43455254 /* "CERT". */ #define GAMECARD_MEDIA_UNIT_SIZE 0x200 diff --git a/source/keys.c b/source/keys.c index 5c05a6f..57406e8 100644 --- a/source/keys.c +++ b/source/keys.c @@ -24,9 +24,11 @@ #include "keys.h" #include "nca.h" -#define KEYS_FILE_PATH "sdmc:/switch/prod.keys" /* Location used by Lockpick_RCM */ +#define KEYS_FILE_PATH "sdmc:/switch/prod.keys" /* Location used by Lockpick_RCM. */ #define FS_SYSMODULE_TID (u64)0x0100000000000000 +#define BOOT_SYSMODULE_TID (u64)0x0100000000000005 +#define SPL_SYSMODULE_TID (u64)0x0100000000000028 #define SEGMENT_TEXT BIT(0) #define SEGMENT_RODATA BIT(1) @@ -55,18 +57,18 @@ typedef struct { } keysMemoryInfo; typedef struct { - ///< Needed to decrypt the NCA header using AES-128-XTS + ///< Needed to decrypt the NCA header using AES-128-XTS. u8 header_kek_source[0x10]; ///< Seed for header kek. Retrieved from the .rodata section in the FS sysmodule. u8 header_key_source[0x20]; ///< Seed for NCA header key. Retrieved from the .data section in the FS sysmodule. u8 header_kek[0x10]; ///< NCA header kek. Generated from header_kek_source. u8 header_key[0x20]; ///< NCA header key. Generated from header_kek and header_key_source. - ///< Needed to derive the KAEK used to decrypt the NCA key area + ///< Needed to derive the KAEK used to decrypt the NCA key area. u8 key_area_key_application_source[0x10]; ///< Seed for kaek 0. Retrieved from the .rodata section in the FS sysmodule. u8 key_area_key_ocean_source[0x10]; ///< Seed for kaek 1. Retrieved from the .rodata section in the FS sysmodule. u8 key_area_key_system_source[0x10]; ///< Seed for kaek 2. Retrieved from the .rodata section in the FS sysmodule. - ///< Needed to decrypt the titlekey block from a ticker. Retrieved from the Lockpick_RCM keys file. + ///< Needed to decrypt the titlekey block from a ticket. Retrieved from the Lockpick_RCM keys file. u8 eticket_rsa_kek[0x10]; ///< eTicket RSA kek. u8 titlekeks[0x20][0x10]; ///< Titlekey encryption keys. @@ -158,11 +160,11 @@ bool keysLoadNcaKeyset(void) bool ret = g_ncaKeysetLoaded; if (ret) goto exit; - if (!(envIsSyscallHinted(0x60) && /* svcDebugActiveProcess */ - envIsSyscallHinted(0x63) && /* svcGetDebugEvent */ - envIsSyscallHinted(0x65) && /* svcGetProcessList */ - envIsSyscallHinted(0x69) && /* svcQueryDebugProcessMemory */ - envIsSyscallHinted(0x6A))) /* svcReadDebugProcessMemory */ + if (!(envIsSyscallHinted(0x60) && /* svcDebugActiveProcess. */ + envIsSyscallHinted(0x63) && /* svcGetDebugEvent. */ + envIsSyscallHinted(0x65) && /* svcGetProcessList. */ + envIsSyscallHinted(0x69) && /* svcQueryDebugProcessMemory. */ + envIsSyscallHinted(0x6A))) /* svcReadDebugProcessMemory. */ { LOGFILE("Debug SVC permissions not available!"); goto exit; @@ -217,7 +219,7 @@ const u8 *keysGetKeyAreaEncryptionKeySource(u8 kaek_index) ptr = (const u8*)(g_ncaKeyset.key_area_key_system_source); break; default: - LOGFILE("Invalid KAEK index! (0x%02X)", kaek_index); + LOGFILE("Invalid KAEK index! (0x%02X).", kaek_index); break; } @@ -233,7 +235,7 @@ const u8 *keysGetTitlekek(u8 key_generation) { if (key_generation > 0x20) { - LOGFILE("Invalid key generation value! (0x%02X)", key_generation); + LOGFILE("Invalid key generation value! (0x%02X).", key_generation); return NULL; } @@ -246,13 +248,13 @@ const u8 *keysGetKeyAreaEncryptionKey(u8 key_generation, u8 kaek_index) { if (key_generation > 0x20) { - LOGFILE("Invalid key generation value! (0x%02X)", key_generation); + LOGFILE("Invalid key generation value! (0x%02X).", key_generation); return NULL; } if (kaek_index > NcaKeyAreaEncryptionKeyIndex_System) { - LOGFILE("Invalid KAEK index! (0x%02X)", kaek_index); + LOGFILE("Invalid KAEK index! (0x%02X).", kaek_index); return NULL; } @@ -273,33 +275,33 @@ static bool keysRetrieveDebugHandleFromProcessByProgramId(Handle *out, u64 progr u64 d[8] = {0}; Handle debug_handle = INVALID_HANDLE; - if (program_id > 0x0100000000000005 && program_id != 0x0100000000000028) + if (program_id > BOOT_SYSMODULE_TID && program_id != SPL_SYSMODULE_TID) { - /* If not a kernel process, get PID from pm:dmnt */ + /* If not a kernel process, get PID from pm:dmnt. */ u64 pid; rc = pmdmntGetProcessId(&pid, program_id); if (R_FAILED(rc)) { - LOGFILE("pmdmntGetProcessId failed! (0x%08X)", rc); + LOGFILE("pmdmntGetProcessId failed! (0x%08X).", rc); return false; } rc = svcDebugActiveProcess(&debug_handle, pid); if (R_FAILED(rc)) { - LOGFILE("svcDebugActiveProcess failed! (0x%08X)", rc); + LOGFILE("svcDebugActiveProcess failed! (0x%08X).", rc); return false; } rc = svcGetDebugEvent((u8*)&d, debug_handle); if (R_FAILED(rc)) { - LOGFILE("svcGetDebugEvent failed! (0x%08X)", rc); + LOGFILE("svcGetDebugEvent failed! (0x%08X).", rc); return false; } } else { - /* Otherwise, query svc for the process list */ + /* Otherwise, query svc for the process list. */ u32 i, num_processes = 0; u64 *pids = calloc(300, sizeof(u64)); @@ -312,7 +314,7 @@ static bool keysRetrieveDebugHandleFromProcessByProgramId(Handle *out, u64 progr rc = svcGetProcessList((s32*)&num_processes, pids, 300); if (R_FAILED(rc)) { - LOGFILE("svcGetProcessList failed! (0x%08X)", rc); + LOGFILE("svcGetProcessList failed! (0x%08X).", rc); return false; } @@ -332,7 +334,7 @@ static bool keysRetrieveDebugHandleFromProcessByProgramId(Handle *out, u64 progr if (i == (num_processes - 1)) { - LOGFILE("Kernel process lookup failed! (0x%08X)", rc); + LOGFILE("Kernel process lookup failed! (0x%08X).", rc); return false; } } @@ -368,13 +370,13 @@ static bool keysRetrieveProcessMemory(keysMemoryLocation *location) return false; } - /* Locate "real" .text segment as Atmosphere emuMMC has two */ + /* Locate "real" .text segment as Atmosphere emuMMC has two. */ for(;;) { rc = svcQueryDebugProcessMemory(&mem_info, &page_info, debug_handle, addr); if (R_FAILED(rc)) { - LOGFILE("svcQueryDebugProcessMemory failed! (0x%08X)", rc); + LOGFILE("svcQueryDebugProcessMemory failed! (0x%08X).", rc); success = false; goto out; } @@ -392,15 +394,15 @@ static bool keysRetrieveProcessMemory(keysMemoryLocation *location) rc = svcQueryDebugProcessMemory(&mem_info, &page_info, debug_handle, addr); if (R_FAILED(rc)) { - LOGFILE("svcQueryDebugProcessMemory failed! (0x%08X)", rc); + LOGFILE("svcQueryDebugProcessMemory failed! (0x%08X).", rc); success = false; break; } - /* Code to allow for bitmasking segments */ + /* Code to allow for bitmasking segments. */ if ((mem_info.perm & Perm_R) && ((mem_info.type & 0xFF) >= MemType_CodeStatic) && ((mem_info.type & 0xFF) < MemType_Heap) && ((segment <<= 1) >> 1 & location->mask) > 0) { - /* If location->data == NULL, realloc will essentially act as a malloc */ + /* If location->data == NULL, realloc will essentially act as a malloc. */ tmp = realloc(location->data, location->data_size + mem_info.size); if (!tmp) { @@ -415,7 +417,7 @@ static bool keysRetrieveProcessMemory(keysMemoryLocation *location) rc = svcReadDebugProcessMemory(location->data + location->data_size, debug_handle, mem_info.addr, mem_info.size); if (R_FAILED(rc)) { - LOGFILE("svcReadDebugProcessMemory failed! (0x%08X)", rc); + LOGFILE("svcReadDebugProcessMemory failed! (0x%08X).", rc); success = false; break; } @@ -472,7 +474,7 @@ static bool keysRetrieveKeysFromProcessMemory(keysMemoryInfo *info) goto out; } - /* Hash every key length-sized byte chunk in the process memory buffer until a match is found */ + /* Hash every key length-sized byte chunk in the process memory buffer until a match is found. */ for(u64 j = 0; j < info->location.data_size; j++) { if ((info->location.data_size - j) < info->keys[i].size) break; @@ -481,7 +483,7 @@ static bool keysRetrieveKeysFromProcessMemory(keysMemoryInfo *info) if (!memcmp(tmp_hash, info->keys[i].hash, SHA256_HASH_SIZE)) { - /* Jackpot */ + /* Jackpot. */ memcpy(info->keys[i].dst, info->location.data + j, info->keys[i].size); found = true; break; @@ -510,21 +512,21 @@ static bool keysDeriveNcaHeaderKey(void) rc = splCryptoGenerateAesKek(g_ncaKeyset.header_kek_source, 0, 0, g_ncaKeyset.header_kek); if (R_FAILED(rc)) { - LOGFILE("splCryptoGenerateAesKek(header_kek_source) failed! (0x%08X)", rc); + LOGFILE("splCryptoGenerateAesKek(header_kek_source) failed! (0x%08X).", rc); return false; } rc = splCryptoGenerateAesKey(g_ncaKeyset.header_kek, g_ncaKeyset.header_key_source + 0x00, g_ncaKeyset.header_key + 0x00); if (R_FAILED(rc)) { - LOGFILE("splCryptoGenerateAesKey(header_key_source + 0x00) failed! (0x%08X)", rc); + LOGFILE("splCryptoGenerateAesKey(header_key_source + 0x00) failed! (0x%08X).", rc); return false; } rc = splCryptoGenerateAesKey(g_ncaKeyset.header_kek, g_ncaKeyset.header_key_source + 0x10, g_ncaKeyset.header_key + 0x10); if (R_FAILED(rc)) { - LOGFILE("splCryptoGenerateAesKey(header_key_source + 0x10) failed! (0x%08X)", rc); + LOGFILE("splCryptoGenerateAesKey(header_key_source + 0x10) failed! (0x%08X).", rc); return false; } @@ -634,12 +636,10 @@ static int keysGetKeyAndValueFromFile(FILE *f, char **key, char **value) if (*p != '_' && (*p < '0' && *p > '9') && (*p < 'a' && *p > 'z')) return -1; } - /* Bail if the final ++p put us at the end of string */ + /* Bail if the final ++p put us at the end of string. */ if (*p == '\0') return -1; - /* We should be at the end of key now and either whitespace or [,=] - * follows. - */ + /* We should be at the end of key now and either whitespace or [,=] follows. */ if (*p == '=' || *p == ',') { *p++ = '\0'; @@ -656,7 +656,7 @@ static int keysGetKeyAndValueFromFile(FILE *f, char **key, char **value) SKIP_SPACE(p); v = p; - /* Skip trailing whitespace */ + /* Skip trailing whitespace. */ for (p = end - 1; *p == '\t' || *p == ' '; --p); *(p + 1) = '\0'; @@ -731,9 +731,9 @@ static bool keysReadKeysFromFile(void) while(true) { ret = keysGetKeyAndValueFromFile(keys_file, &key, &value); - if (ret == 1 || ret == -2) break; /* Break from the while loop if EOF is reached or if an I/O error occurs */ + if (ret == 1 || ret == -2) break; /* Break from the while loop if EOF is reached or if an I/O error occurs. */ - /* Ignore malformed lines */ + /* Ignore malformed lines. */ if (ret != 0 || !key || !value) continue; if (!common_eticket_rsa_kek && !personalized_eticket_rsa_kek && !strcasecmp(key, "eticket_rsa_kek")) @@ -744,8 +744,8 @@ static bool keysReadKeysFromFile(void) } else if (!personalized_eticket_rsa_kek && !strcasecmp(key, "eticket_rsa_kek_personalized")) { - /* Use the personalized eTicket RSA kek if available */ - /* This only appears on consoles that use the new PRODINFO key generation scheme */ + /* Use the personalized eTicket RSA kek if available. */ + /* This only appears on consoles that use the new PRODINFO key generation scheme. */ if ((parse_fail = !keysParseHexKey(g_ncaKeyset.eticket_rsa_kek, key, value, sizeof(g_ncaKeyset.eticket_rsa_kek)))) break; personalized_eticket_rsa_kek = true; key_count++; @@ -793,7 +793,7 @@ static bool keysReadKeysFromFile(void) if (parse_fail || !key_count) { - if (!key_count) LOGFILE("Unable to parse necessary keys from \"%s\"! (keys file empty?)", KEYS_FILE_PATH); + if (!key_count) LOGFILE("Unable to parse necessary keys from \"%s\"! (keys file empty?).", KEYS_FILE_PATH); return false; } diff --git a/source/main.c b/source/main.c index 0874aba..8ac0e02 100644 --- a/source/main.c +++ b/source/main.c @@ -464,8 +464,7 @@ int main(int argc, char *argv[]) struct tm *ts = localtime(&now); size_t size = shared_data.data_written; - hidScanInput(); - btn_cancel_cur_state = (utilsHidKeysAllHeld() & KEY_B); + btn_cancel_cur_state = (utilsReadInput(UtilsInputType_Down) & KEY_B); if (btn_cancel_cur_state && btn_cancel_cur_state != btn_cancel_prev_state) { @@ -534,7 +533,7 @@ int main(int argc, char *argv[]) out2: consolePrint("press any button to exit\n"); - utilsWaitForButtonPress(); + utilsWaitForButtonPress(KEY_NONE); lrExit(); diff --git a/source/nca.c b/source/nca.c index d6fb314..d78834b 100644 --- a/source/nca.c +++ b/source/nca.c @@ -21,10 +21,11 @@ #include "utils.h" #include "nca.h" #include "keys.h" +#include "aes.h" #include "rsa.h" #include "gamecard.h" -#define NCA_CRYPTO_BUFFER_SIZE 0x800000 /* 8 MiB */ +#define NCA_CRYPTO_BUFFER_SIZE 0x800000 /* 8 MiB. */ /* Global variables. */ @@ -38,8 +39,6 @@ static const u8 g_nca0KeyAreaHash[SHA256_HASH_SIZE] = { /* Function prototypes. */ -static size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src, size_t size, u64 sector, size_t sector_size, bool encrypt); /* Not used anywhere else */ - static bool ncaDecryptHeader(NcaContext *ctx); static bool ncaDecryptKeyArea(NcaContext *ctx); @@ -88,7 +87,7 @@ bool ncaEncryptKeyArea(NcaContext *ctx) const u8 *kaek = NULL; Aes128Context key_area_ctx = {0}; - /* Check if we're dealing with a NCA0 with a plain text key area */ + /* Check if we're dealing with a NCA0 with a plain text key area. */ if (ctx->format_version == NcaVersion_Nca0 && !ncaCheckIfVersion0KeyAreaIsEncrypted(ctx)) { memcpy(ctx->header.encrypted_keys, ctx->decrypted_keys, 0x40); @@ -161,7 +160,7 @@ bool ncaEncryptHeader(NcaContext *ctx) break; case NcaVersion_Nca0: - /* NCA0 FS section headers will be encrypted in-place, but they need to be written to their proper offsets */ + /* NCA0 FS section headers will be encrypted in-place, but they need to be written to their proper offsets. */ aes128XtsContextCreate(&nca0_fs_header_ctx, ctx->decrypted_keys[0].key, ctx->decrypted_keys[1].key, true); for(i = 0; i < NCA_FS_HEADER_COUNT; i++) @@ -197,7 +196,10 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm return false; } - /* Fill NCA context */ + /* Clear output NCA context. */ + memset(out, 0, sizeof(NcaContext)); + + /* Fill NCA context. */ out->storage_id = storage_id; out->ncm_storage = (out->storage_id != NcmStorageId_GameCard ? ncm_storage : NULL); @@ -214,11 +216,9 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm return false; } - out->rights_id_available = out->dirty_header = false; - if (out->storage_id == NcmStorageId_GameCard) { - /* Retrieve gamecard NCA offset */ + /* Retrieve gamecard NCA offset. */ char nca_filename[0x30] = {0}; sprintf(nca_filename, "%s.%s", out->content_id_str, out->content_type == NcmContentType_Meta ? "cnmt.nca" : "nca"); @@ -229,14 +229,14 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm } } - /* Read NCA header */ + /* Read NCA header. */ if (!ncaReadContentFile(out, &(out->header), sizeof(NcaHeader), 0)) { LOGFILE("Failed to read NCA \"%s\" header!", out->content_id_str); return false; } - /* Decrypt NCA header */ + /* Decrypt NCA header. */ if (!ncaDecryptHeader(out)) { LOGFILE("Failed to decrypt NCA \"%s\" header!", out->content_id_str); @@ -249,24 +249,24 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm return false; } - /* Fill additional NCA context info */ + /* Fill additional NCA context info. */ out->key_generation = ncaGetKeyGenerationValue(out); out->rights_id_available = ncaCheckRightsIdAvailability(out); if (out->rights_id_available) { - /* Retrieve ticket */ - /* This will return true if it has already been retrieved */ - if (!tikRetrieveTicketByRightsId(tik, &(out->header.rights_id), out->storage_id == NcmStorageId_GameCard)) + /* Retrieve ticket. */ + /* This will return true if it has already been retrieved. */ + if (tikRetrieveTicketByRightsId(tik, &(out->header.rights_id), out->storage_id == NcmStorageId_GameCard)) { + /* Copy decrypted titlekey. */ + memcpy(out->titlekey, tik->dec_titlekey, 0x10); + out->titlekey_retrieved = true; + } else { LOGFILE("Error retrieving ticket for NCA \"%s\"!", out->content_id_str); - return false; } - - /* Copy decrypted titlekey */ - memcpy(out->titlekey, tik->dec_titlekey, 0x10); } else { - /* Decrypt key area */ + /* Decrypt key area. */ if (out->format_version != NcaVersion_Nca0 && !ncaDecryptKeyArea(out)) { LOGFILE("Error decrypting NCA key area!"); @@ -277,32 +277,28 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm /* Parse sections */ for(u8 i = 0; i < NCA_FS_HEADER_COUNT; i++) { - if (!out->header.fs_entries[i].enable_entry) continue; + /* Skip NCA section if it's not enabled in the FS entries, or if the NCA uses titlekey crypto and the titlekey couldn't be retrieved. */ + if (!out->header.fs_entries[i].enable_entry || (out->rights_id_available && !out->titlekey_retrieved)) continue; - /* Fill section context */ + /* Fill section context. */ out->fs_contexts[i].nca_ctx = out; out->fs_contexts[i].section_num = i; out->fs_contexts[i].section_offset = NCA_FS_ENTRY_BLOCK_OFFSET(out->header.fs_entries[i].start_block_offset); out->fs_contexts[i].section_size = (NCA_FS_ENTRY_BLOCK_OFFSET(out->header.fs_entries[i].end_block_offset) - out->fs_contexts[i].section_offset); - out->fs_contexts[i].section_type = NcaFsSectionType_Invalid; /* Placeholder */ + out->fs_contexts[i].section_type = NcaFsSectionType_Invalid; /* Placeholder. */ out->fs_contexts[i].header = &(out->header.fs_headers[i]); - memset(out->fs_contexts[i].ctr, 0, sizeof(out->fs_contexts[i].ctr)); - memset(&(out->fs_contexts[i].ctr_ctx), 0, sizeof(Aes128CtrContext)); - memset(&(out->fs_contexts[i].xts_decrypt_ctx), 0, sizeof(Aes128XtsContext)); - memset(&(out->fs_contexts[i].xts_encrypt_ctx), 0, sizeof(Aes128XtsContext)); - - /* Determine encryption type */ + /* Determine encryption type. */ out->fs_contexts[i].encryption_type = (out->format_version == NcaVersion_Nca0 ? NcaEncryptionType_AesXts : out->header.fs_headers[i].encryption_type); if (out->fs_contexts[i].encryption_type == NcaEncryptionType_Auto) { switch(out->fs_contexts[i].section_num) { - case 0: /* ExeFS Partition FS */ - case 1: /* RomFS */ + case 0: /* ExeFS Partition FS. */ + case 1: /* RomFS. */ out->fs_contexts[i].encryption_type = NcaEncryptionType_AesCtr; break; - case 2: /* Logo Partition FS */ + case 2: /* Logo Partition FS. */ out->fs_contexts[i].encryption_type = NcaEncryptionType_None; break; default: @@ -310,10 +306,14 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm } } - /* Check if we're dealing with an invalid encryption type value */ - if (out->fs_contexts[i].encryption_type == NcaEncryptionType_Auto || out->fs_contexts[i].encryption_type > NcaEncryptionType_AesCtrEx) continue; + /* Check if we're dealing with an invalid encryption type value. */ + if (out->fs_contexts[i].encryption_type == NcaEncryptionType_Auto || out->fs_contexts[i].encryption_type > NcaEncryptionType_AesCtrEx) + { + memset(&(out->fs_contexts[i]), 0, sizeof(NcaFsSectionContext)); + continue; + } - /* Determine FS section type */ + /* Determine FS section type. */ if (out->fs_contexts[i].header->fs_type == NcaFsType_PartitionFs && out->fs_contexts[i].header->hash_type == NcaHashType_HierarchicalSha256) { out->fs_contexts[i].section_type = NcaFsSectionType_PartitionFs; @@ -327,16 +327,20 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm out->fs_contexts[i].section_type = NcaFsSectionType_Nca0RomFs; } - /* Check if we're dealing with an invalid section type value */ - if (out->fs_contexts[i].section_type >= NcaFsSectionType_Invalid) continue; + /* Check if we're dealing with an invalid section type value. */ + if (out->fs_contexts[i].section_type >= NcaFsSectionType_Invalid) + { + memset(&(out->fs_contexts[i]), 0, sizeof(NcaFsSectionContext)); + continue; + } - /* Initialize crypto related fields */ + /* Initialize crypto related fields. */ if (out->fs_contexts[i].encryption_type > NcaEncryptionType_None && out->fs_contexts[i].encryption_type <= NcaEncryptionType_AesCtrEx) { - /* Initialize section CTR */ + /* Initialize section CTR. */ ncaInitializeAesCtrIv(out->fs_contexts[i].ctr, out->fs_contexts[i].header->section_ctr, out->fs_contexts[i].section_offset); - /* Initialize AES context */ + /* Initialize AES context. */ if (out->rights_id_available) { aes128CtrContextCreate(&(out->fs_contexts[i].ctr_ctx), out->titlekey, out->fs_contexts[i].ctr); @@ -347,12 +351,15 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm } else if (out->fs_contexts[i].encryption_type == NcaEncryptionType_AesXts) { - /* We need to create two different contexts: one for decryption and another one for encryption */ + /* We need to create two different contexts: one for decryption and another one for encryption. */ aes128XtsContextCreate(&(out->fs_contexts[i].xts_decrypt_ctx), out->decrypted_keys[0].key, out->decrypted_keys[1].key, false); aes128XtsContextCreate(&(out->fs_contexts[i].xts_encrypt_ctx), out->decrypted_keys[0].key, out->decrypted_keys[1].key, true); } } } + + /* Enable FS context if we got up to this point. */ + out->fs_contexts[i].enabled = true; } return true; @@ -372,16 +379,16 @@ bool ncaReadContentFile(NcaContext *ctx, void *out, u64 read_size, u64 offset) if (ctx->storage_id != NcmStorageId_GameCard) { - /* Retrieve NCA data normally */ - /* This strips NAX0 crypto from SD card NCAs (not used on eMMC NCAs) */ + /* Retrieve NCA data normally. */ + /* This strips NAX0 crypto from SD card NCAs (not used on eMMC NCAs). */ rc = ncmContentStorageReadContentIdFile(ctx->ncm_storage, out, read_size, &(ctx->content_id), offset); ret = R_SUCCEEDED(rc); - if (!ret) LOGFILE("Failed to read 0x%lX bytes block at offset 0x%lX from NCA \"%s\"! (0x%08X) (ncm)", read_size, offset, ctx->content_id_str, rc); + if (!ret) LOGFILE("Failed to read 0x%lX bytes block at offset 0x%lX from NCA \"%s\"! (0x%08X) (ncm).", read_size, offset, ctx->content_id_str, rc); } else { - /* Retrieve NCA data using raw gamecard reads */ - /* Fixes NCA read issues with gamecards under HOS < 4.0.0 when using ncmContentStorageReadContentIdFile() */ + /* Retrieve NCA data using raw gamecard reads. */ + /* Fixes NCA read issues with gamecards under HOS < 4.0.0 when using ncmContentStorageReadContentIdFile(). */ ret = gamecardReadStorage(out, read_size, ctx->gamecard_offset + offset); - if (!ret) LOGFILE("Failed to read 0x%lX bytes block at offset 0x%lX from NCA \"%s\"! (gamecard)", read_size, offset, ctx->content_id_str); + if (!ret) LOGFILE("Failed to read 0x%lX bytes block at offset 0x%lX from NCA \"%s\"! (gamecard).", read_size, offset, ctx->content_id_str); } return ret; @@ -415,8 +422,8 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da bool success = false; - if (!ctx || !(nca_ctx = (NcaContext*)ctx->nca_ctx) || !ctx->header || ctx->header->hash_type != NcaHashType_HierarchicalSha256 || ctx->header->encryption_type == NcaEncryptionType_AesCtrEx || \ - !data || !data_size || !(hash_block_size = ctx->header->hash_info.hierarchical_sha256.hash_block_size) || \ + if (!ctx || !ctx->enabled || !(nca_ctx = (NcaContext*)ctx->nca_ctx) || !ctx->header || ctx->header->hash_type != NcaHashType_HierarchicalSha256 || \ + ctx->header->encryption_type == NcaEncryptionType_AesCtrEx || !data || !data_size || !(hash_block_size = ctx->header->hash_info.hierarchical_sha256.hash_block_size) || \ !(hash_data_layer_size = ctx->header->hash_info.hierarchical_sha256.hash_data_layer_info.size) || \ !(hash_target_layer_size = ctx->header->hash_info.hierarchical_sha256.hash_target_layer_info.size) || data_offset >= hash_target_layer_size || \ (data_offset + data_size) > hash_target_layer_size || !out) @@ -425,7 +432,7 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da goto exit; } - /* Calculate required offsets and sizes */ + /* Calculate required offsets and sizes. */ hash_data_layer_offset = ctx->header->hash_info.hierarchical_sha256.hash_data_layer_info.offset; hash_target_layer_offset = ctx->header->hash_info.hierarchical_sha256.hash_target_layer_info.offset; @@ -440,7 +447,7 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da u64 hash_target_data_offset = (data_offset - ALIGN_DOWN(data_offset, hash_block_size)); - /* Allocate memory for the full hash data layer */ + /* Allocate memory for the full hash data layer. */ hash_data_layer = malloc(hash_data_layer_size); if (!hash_data_layer) { @@ -448,14 +455,14 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da goto exit; } - /* Read full hash data layer */ + /* Read full hash data layer. */ if (!_ncaReadFsSection(ctx, hash_data_layer, hash_data_layer_size, hash_data_layer_offset, false)) { LOGFILE("Failed to read full HierarchicalSha256 hash data layer!"); goto exit; } - /* Allocate memory for the modified hash target layer block */ + /* Allocate memory for the modified hash target layer block. */ hash_target_block = malloc(hash_target_size); if (!hash_target_block) { @@ -463,24 +470,24 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da goto exit; } - /* Read hash target layer block */ + /* Read hash target layer block. */ if (!_ncaReadFsSection(ctx, hash_target_block, hash_target_size, hash_target_start_offset, false)) { LOGFILE("Failed to read HierarchicalSha256 hash target layer block!"); goto exit; } - /* Replace data */ + /* Replace data. */ memcpy(hash_target_block + hash_target_data_offset, data, data_size); - /* Recalculate hashes */ + /* Recalculate hashes. */ for(u64 i = 0, j = 0; i < hash_target_size; i += hash_block_size, j++) { if (hash_block_size > (hash_target_size - i)) hash_block_size = (hash_target_size - i); sha256CalculateHash(hash_data_layer + hash_data_start_offset + (j * SHA256_HASH_SIZE), hash_target_block + i, hash_block_size); } - /* Reencrypt modified hash data layer block */ + /* Reencrypt modified hash data layer block. */ out->hash_data_layer_patch.data = _ncaGenerateEncryptedFsSectionBlock(ctx, hash_data_layer + hash_data_start_offset, hash_data_size, hash_data_layer_offset + hash_data_start_offset, \ &(out->hash_data_layer_patch.size), &(out->hash_data_layer_patch.offset), false); if (!out->hash_data_layer_patch.data) @@ -489,7 +496,7 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da goto exit; } - /* Reencrypt hash target layer block */ + /* Reencrypt hash target layer block. */ out->hash_target_layer_patch.data = _ncaGenerateEncryptedFsSectionBlock(ctx, hash_target_block + hash_target_data_offset, data_size, hash_target_layer_offset + data_offset, \ &(out->hash_target_layer_patch.size), &(out->hash_target_layer_patch.offset), false); if (!out->hash_target_layer_patch.data) @@ -498,13 +505,13 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da goto exit; } - /* Recalculate master hash from hash info block */ + /* Recalculate master hash from hash info block. */ sha256CalculateHash(ctx->header->hash_info.hierarchical_sha256.master_hash, hash_data_layer, hash_data_layer_size); - /* Recalculate FS header hash */ + /* Recalculate FS header hash. */ sha256CalculateHash(nca_ctx->header.fs_hashes[ctx->section_num].hash, ctx->header, sizeof(NcaFsHeader)); - /* Enable the 'dirty_header' flag */ + /* Enable the 'dirty_header' flag. */ nca_ctx->dirty_header = true; success = true; @@ -534,15 +541,15 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void u8 *hash_data_block = NULL, *hash_target_block = NULL; - if (!ctx || !(nca_ctx = (NcaContext*)ctx->nca_ctx) || !ctx->header || ctx->header->hash_type != NcaHashType_HierarchicalIntegrity || ctx->header->encryption_type == NcaEncryptionType_AesCtrEx || \ - !data || !data_size || !out || data_offset >= ctx->header->hash_info.hierarchical_integrity.hash_target_layer_info.size || \ + if (!ctx || !ctx->enabled || !(nca_ctx = (NcaContext*)ctx->nca_ctx) || !ctx->header || ctx->header->hash_type != NcaHashType_HierarchicalIntegrity || \ + ctx->header->encryption_type == NcaEncryptionType_AesCtrEx || !data || !data_size || !out || data_offset >= ctx->header->hash_info.hierarchical_integrity.hash_target_layer_info.size || \ (data_offset + data_size) > ctx->header->hash_info.hierarchical_integrity.hash_target_layer_info.size) { LOGFILE("Invalid parameters!"); goto exit; } - /* Process each IVFC layer */ + /* Process each IVFC layer. */ for(u8 i = (NCA_IVFC_HASH_DATA_LAYER_COUNT + 1); i > 0; i--) { NcaHierarchicalIntegrityLayerInfo *cur_layer_info = (i > NCA_IVFC_HASH_DATA_LAYER_COUNT ? &(ctx->header->hash_info.hierarchical_integrity.hash_target_layer_info) : \ @@ -558,7 +565,7 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void goto exit; } - /* Calculate required offsets and sizes */ + /* Calculate required offsets and sizes. */ u64 hash_block_size = NCA_IVFC_BLOCK_SIZE(cur_layer_info->block_size); u64 hash_data_layer_offset = 0; @@ -569,7 +576,7 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void if (parent_layer_info) { - /* HierarchicalIntegrity layer from L1 to L5 */ + /* HierarchicalIntegrity layer from L1 to L5. */ hash_data_layer_offset = parent_layer_info->offset; hash_data_start_offset = ((cur_data_offset / hash_block_size) * SHA256_HASH_SIZE); @@ -580,8 +587,8 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void hash_target_end_offset = (hash_target_layer_offset + ALIGN_UP(cur_data_offset + cur_data_size, hash_block_size)); hash_target_size = (hash_target_end_offset - hash_target_start_offset); } else { - /* HierarchicalIntegrity master layer */ - /* The master hash is calculated over the whole layer and saved to the NCA FS header */ + /* HierarchicalIntegrity master layer. */ + /* The master hash is calculated over the whole layer and saved to the NCA FS header. */ hash_target_start_offset = hash_target_layer_offset; hash_target_end_offset = (hash_target_layer_offset + hash_target_layer_size); hash_target_size = hash_target_layer_size; @@ -589,7 +596,7 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void hash_target_data_offset = (cur_data_offset - ALIGN_DOWN(cur_data_offset, hash_block_size)); - /* Allocate memory for our hash target layer block */ + /* Allocate memory for our hash target layer block. */ hash_target_block = calloc(hash_target_size, sizeof(u8)); if (!hash_target_block) { @@ -597,26 +604,26 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void goto exit; } - /* Adjust hash target layer end offset and size if needed to avoid read errors */ + /* Adjust hash target layer end offset and size if needed to avoid read errors. */ if (hash_target_end_offset > (hash_target_layer_offset + hash_target_layer_size)) { hash_target_end_offset = (hash_target_layer_offset + hash_target_layer_size); hash_target_size = (hash_target_end_offset - hash_target_start_offset); } - /* Read hash target layer block */ + /* Read hash target layer block. */ if (!_ncaReadFsSection(ctx, hash_target_block, hash_target_size, hash_target_start_offset, false)) { LOGFILE("Failed to read HierarchicalIntegrity hash target layer block!"); goto exit; } - /* Replace hash target layer block data */ + /* Replace hash target layer block data. */ memcpy(hash_target_block + hash_target_data_offset, (i > NCA_IVFC_HASH_DATA_LAYER_COUNT ? data : cur_data), cur_data_size); if (parent_layer_info) { - /* Allocate memory for our hash data layer block */ + /* Allocate memory for our hash data layer block. */ hash_data_block = calloc(hash_data_size, sizeof(u8)); if (!hash_data_block) { @@ -624,23 +631,23 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void goto exit; } - /* Read hash target layer block */ + /* Read hash target layer block. */ if (!_ncaReadFsSection(ctx, hash_data_block, hash_data_size, hash_data_layer_offset + hash_data_start_offset, false)) { LOGFILE("Failed to read HierarchicalIntegrity hash data layer block!"); goto exit; } - /* Recalculate hashes */ - /* Size isn't truncated for blocks smaller than the hash block size, unlike HierarchicalSha256, so we just keep using the same hash block size throughout the loop */ - /* For these specific cases, the rest of the block should be filled with zeroes (already taken care of by using calloc()) */ + /* Recalculate hashes. */ + /* Size isn't truncated for blocks smaller than the hash block size, unlike HierarchicalSha256, so we just keep using the same hash block size throughout the loop. */ + /* For these specific cases, the rest of the block should be filled with zeroes (already taken care of by using calloc()). */ for(u64 i = 0, j = 0; i < hash_target_size; i += hash_block_size, j++) sha256CalculateHash(hash_data_block + (j * SHA256_HASH_SIZE), hash_target_block + i, hash_block_size); } else { - /* Recalculate master hash from hash info block */ + /* Recalculate master hash from hash info block. */ sha256CalculateHash(ctx->header->hash_info.hierarchical_integrity.master_hash, hash_target_block, hash_target_size); } - /* Reencrypt hash target layer block */ + /* Reencrypt hash target layer block. */ cur_layer_patch->data = _ncaGenerateEncryptedFsSectionBlock(ctx, hash_target_block + hash_target_data_offset, cur_data_size, hash_target_layer_offset + cur_data_offset, \ &(cur_layer_patch->size), &(cur_layer_patch->offset), false); if (!cur_layer_patch->data) @@ -649,16 +656,16 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void goto exit; } - /* Free hash target layer block */ + /* Free hash target layer block. */ free(hash_target_block); hash_target_block = NULL; if (parent_layer_info) { - /* Free previous layer data if necessary */ + /* Free previous layer data if necessary. */ if (cur_data) free(cur_data); - /* Prepare data for the next target layer */ + /* Prepare data for the next target layer. */ cur_data = hash_data_block; cur_data_offset = hash_data_start_offset; cur_data_size = hash_data_size; @@ -666,10 +673,10 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void } } - /* Recalculate FS header hash */ + /* Recalculate FS header hash. */ sha256CalculateHash(nca_ctx->header.fs_hashes[ctx->section_num].hash, ctx->header, sizeof(NcaFsHeader)); - /* Enable the 'dirty_header' flag */ + /* Enable the 'dirty_header' flag. */ nca_ctx->dirty_header = true; success = true; @@ -688,31 +695,6 @@ exit: return success; } -static size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src, size_t size, u64 sector, size_t sector_size, bool encrypt) -{ - if (!ctx || !dst || !src || !size || !sector_size || (size % sector_size) != 0) - { - LOGFILE("Invalid parameters!"); - return 0; - } - - size_t i, crypt_res = 0; - u64 cur_sector = sector; - - u8 *dst_u8 = (u8*)dst; - const u8 *src_u8 = (const u8*)src; - - for(i = 0; i < size; i += sector_size, cur_sector++) - { - /* We have to force a sector reset on each new sector to actually enable Nintendo AES-XTS cipher tweak */ - aes128XtsContextResetSector(ctx, cur_sector, true); - crypt_res = (encrypt ? aes128XtsEncrypt(ctx, dst_u8 + i, src_u8 + i, sector_size) : aes128XtsDecrypt(ctx, dst_u8 + i, src_u8 + i, sector_size)); - if (crypt_res != sector_size) break; - } - - return i; -} - static bool ncaDecryptHeader(NcaContext *ctx) { if (!ctx || !strlen(ctx->content_id_str)) @@ -772,7 +754,7 @@ static bool ncaDecryptHeader(NcaContext *ctx) case NCA_NCA0_MAGIC: ctx->format_version = NcaVersion_Nca0; - /* We first need to decrypt the key area from the NCA0 header in order to access its FS section headers */ + /* We first need to decrypt the key area from the NCA0 header in order to access its FS section headers. */ if (!ncaDecryptKeyArea(ctx)) { LOGFILE("Error decrypting NCA0 \"%s\" key area!", ctx->content_id_str); @@ -785,7 +767,7 @@ static bool ncaDecryptHeader(NcaContext *ctx) { if (!ctx->header.fs_entries[i].enable_entry) continue; - /* FS headers are not part of NCA0 headers */ + /* FS headers are not part of NCA0 headers. */ fs_header_offset = NCA_FS_ENTRY_BLOCK_OFFSET(ctx->header.fs_entries[i].start_block_offset); if (!ncaReadContentFile(ctx, &(ctx->header.fs_headers[i]), NCA_FS_HEADER_LENGTH, fs_header_offset)) { @@ -804,7 +786,7 @@ static bool ncaDecryptHeader(NcaContext *ctx) break; default: - LOGFILE("Invalid NCA \"%s\" magic word! Wrong header key? (0x%08X)", ctx->content_id_str, magic); + LOGFILE("Invalid NCA \"%s\" magic word! Wrong header key? (0x%08X).", ctx->content_id_str, magic); return false; } @@ -823,7 +805,7 @@ static bool ncaDecryptKeyArea(NcaContext *ctx) const u8 *kek_src = NULL; u8 key_count, tmp_kek[0x10] = {0}; - /* Check if we're dealing with a NCA0 with a plain text key area */ + /* Check if we're dealing with a NCA0 with a plain text key area. */ if (ctx->format_version == NcaVersion_Nca0 && !ncaCheckIfVersion0KeyAreaIsEncrypted(ctx)) { memcpy(ctx->decrypted_keys, ctx->header.encrypted_keys, 0x40); @@ -840,7 +822,7 @@ static bool ncaDecryptKeyArea(NcaContext *ctx) rc = splCryptoGenerateAesKek(kek_src, ctx->key_generation, 0, tmp_kek); if (R_FAILED(rc)) { - LOGFILE("splCryptoGenerateAesKek failed! (0x%08X)", rc); + LOGFILE("splCryptoGenerateAesKek failed! (0x%08X).", rc); return false; } @@ -851,7 +833,7 @@ static bool ncaDecryptKeyArea(NcaContext *ctx) rc = splCryptoGenerateAesKey(tmp_kek, ctx->header.encrypted_keys[i].key, ctx->decrypted_keys[i].key); if (R_FAILED(rc)) { - LOGFILE("splCryptoGenerateAesKey failed! (0x%08X)", rc); + LOGFILE("splCryptoGenerateAesKey failed! (0x%08X).", rc); return false; } } @@ -946,9 +928,9 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size bool ret = false; - if (!g_ncaCryptoBuffer || !ctx || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < NCA_FULL_HEADER_LENGTH || ctx->section_type >= NcaFsSectionType_Invalid || \ - ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type > NcaEncryptionType_AesCtrEx || !ctx->header || !out || !read_size || offset >= ctx->section_size || \ - (offset + read_size) > ctx->section_size) + if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < NCA_FULL_HEADER_LENGTH || \ + ctx->section_type >= NcaFsSectionType_Invalid || ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type > NcaEncryptionType_AesCtrEx || !ctx->header || !out || !read_size || \ + offset >= ctx->section_size || (offset + read_size) > ctx->section_size) { LOGFILE("Invalid NCA FS section header parameters!"); goto exit; @@ -970,26 +952,26 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size goto exit; } - /* Optimization for reads from plaintext FS sections or reads that are aligned to the AES-CTR / AES-XTS sector size */ + /* Optimization for reads from plaintext FS sections or reads that are aligned to the AES-CTR / AES-XTS sector size. */ if (ctx->encryption_type == NcaEncryptionType_None || \ (ctx->encryption_type == NcaEncryptionType_AesXts && !(content_offset % NCA_AES_XTS_SECTOR_SIZE) && !(read_size % NCA_AES_XTS_SECTOR_SIZE)) || \ ((ctx->encryption_type == NcaEncryptionType_AesCtr || ctx->encryption_type == NcaEncryptionType_AesCtrEx) && !(content_offset % AES_BLOCK_SIZE) && !(read_size % AES_BLOCK_SIZE))) { - /* Read data */ + /* Read data. */ if (!ncaReadContentFile(nca_ctx, out, read_size, content_offset)) { - LOGFILE("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned)", read_size, content_offset, nca_ctx->content_id_str, ctx->section_num); + LOGFILE("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", read_size, content_offset, nca_ctx->content_id_str, ctx->section_num); goto exit; } - /* Return right away if we're dealing with a plaintext FS section */ + /* Return right away if we're dealing with a plaintext FS section. */ if (ctx->encryption_type == NcaEncryptionType_None) { ret = true; goto exit; } - /* Decrypt data */ + /* Decrypt data. */ if (ctx->encryption_type == NcaEncryptionType_AesXts) { sector_num = ((ctx->encryption_type == NcaEncryptionType_AesXts ? offset : (content_offset - NCA_HEADER_LENGTH)) / NCA_AES_XTS_SECTOR_SIZE); @@ -997,7 +979,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size crypt_res = aes128XtsNintendoCrypt(&(ctx->xts_decrypt_ctx), out, out, read_size, sector_num, NCA_AES_XTS_SECTOR_SIZE, false); if (crypt_res != read_size) { - LOGFILE("Failed to AES-XTS decrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned)", read_size, content_offset, nca_ctx->content_id_str, ctx->section_num); + LOGFILE("Failed to AES-XTS decrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", read_size, content_offset, nca_ctx->content_id_str, ctx->section_num); goto exit; } } else @@ -1012,7 +994,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size goto exit; } - /* Calculate offsets and block sizes */ + /* Calculate offsets and block sizes. */ block_start_offset = ALIGN_DOWN(content_offset, ctx->encryption_type == NcaEncryptionType_AesXts ? NCA_AES_XTS_SECTOR_SIZE : AES_BLOCK_SIZE); block_end_offset = ALIGN_UP(content_offset + read_size, ctx->encryption_type == NcaEncryptionType_AesXts ? NCA_AES_XTS_SECTOR_SIZE : AES_BLOCK_SIZE); block_size = (block_end_offset - block_start_offset); @@ -1021,14 +1003,14 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size chunk_size = (block_size > NCA_CRYPTO_BUFFER_SIZE ? NCA_CRYPTO_BUFFER_SIZE : block_size); out_chunk_size = (block_size > NCA_CRYPTO_BUFFER_SIZE ? (NCA_CRYPTO_BUFFER_SIZE - data_start_offset) : read_size); - /* Read data */ + /* Read data. */ if (!ncaReadContentFile(nca_ctx, g_ncaCryptoBuffer, chunk_size, block_start_offset)) { - LOGFILE("Failed to read 0x%lX bytes encrypted data block at offset 0x%lX from NCA \"%s\" FS section #%u! (unaligned)", chunk_size, block_start_offset, nca_ctx->content_id_str, ctx->section_num); + LOGFILE("Failed to read 0x%lX bytes encrypted data block at offset 0x%lX from NCA \"%s\" FS section #%u! (unaligned).", chunk_size, block_start_offset, nca_ctx->content_id_str, ctx->section_num); goto exit; } - /* Decrypt data */ + /* Decrypt data. */ if (ctx->encryption_type == NcaEncryptionType_AesXts) { sector_num = ((ctx->encryption_type == NcaEncryptionType_AesXts ? offset : (content_offset - NCA_HEADER_LENGTH)) / NCA_AES_XTS_SECTOR_SIZE); @@ -1036,7 +1018,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size crypt_res = aes128XtsNintendoCrypt(&(ctx->xts_decrypt_ctx), g_ncaCryptoBuffer, g_ncaCryptoBuffer, chunk_size, sector_num, NCA_AES_XTS_SECTOR_SIZE, false); if (crypt_res != chunk_size) { - LOGFILE("Failed to AES-XTS decrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (unaligned)", chunk_size, block_start_offset, nca_ctx->content_id_str, ctx->section_num); + LOGFILE("Failed to AES-XTS decrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (unaligned).", chunk_size, block_start_offset, nca_ctx->content_id_str, ctx->section_num); goto exit; } } else @@ -1047,7 +1029,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size aes128CtrCrypt(&(ctx->ctr_ctx), g_ncaCryptoBuffer, g_ncaCryptoBuffer, chunk_size); } - /* Copy decrypted data */ + /* Copy decrypted data. */ memcpy(out, g_ncaCryptoBuffer + data_start_offset, out_chunk_size); ret = (block_size > NCA_CRYPTO_BUFFER_SIZE ? _ncaReadFsSection(ctx, (u8*)out + out_chunk_size, read_size - out_chunk_size, offset + out_chunk_size, false) : true); @@ -1064,8 +1046,9 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi bool ret = false; - if (!g_ncaCryptoBuffer || !ctx || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < NCA_FULL_HEADER_LENGTH || ctx->section_type != NcaFsSectionType_PatchRomFs || \ - ctx->encryption_type != NcaEncryptionType_AesCtrEx || !ctx->header || !out || !read_size || offset >= ctx->section_size || (offset + read_size) > ctx->section_size) + if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < NCA_FULL_HEADER_LENGTH || \ + ctx->section_type != NcaFsSectionType_PatchRomFs || ctx->encryption_type != NcaEncryptionType_AesCtrEx || !ctx->header || !out || !read_size || offset >= ctx->section_size || \ + (offset + read_size) > ctx->section_size) { LOGFILE("Invalid NCA FS section header parameters!"); goto exit; @@ -1084,13 +1067,13 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi goto exit; } - /* Optimization for reads that are aligned to the AES-CTR sector size */ + /* Optimization for reads that are aligned to the AES-CTR sector size. */ if (!(content_offset % AES_BLOCK_SIZE) && !(read_size % AES_BLOCK_SIZE)) { - /* Read data */ + /* Read data. */ if (!ncaReadContentFile(nca_ctx, out, read_size, content_offset)) { - LOGFILE("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned)", read_size, content_offset, nca_ctx->content_id_str, ctx->section_num); + LOGFILE("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", read_size, content_offset, nca_ctx->content_id_str, ctx->section_num); goto exit; } @@ -1103,7 +1086,7 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi goto exit; } - /* Calculate offsets and block sizes */ + /* Calculate offsets and block sizes. */ block_start_offset = ALIGN_DOWN(content_offset, AES_BLOCK_SIZE); block_end_offset = ALIGN_UP(content_offset + read_size, AES_BLOCK_SIZE); block_size = (block_end_offset - block_start_offset); @@ -1112,19 +1095,19 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi chunk_size = (block_size > NCA_CRYPTO_BUFFER_SIZE ? NCA_CRYPTO_BUFFER_SIZE : block_size); out_chunk_size = (block_size > NCA_CRYPTO_BUFFER_SIZE ? (NCA_CRYPTO_BUFFER_SIZE - data_start_offset) : read_size); - /* Read data */ + /* Read data. */ if (!ncaReadContentFile(nca_ctx, g_ncaCryptoBuffer, chunk_size, block_start_offset)) { - LOGFILE("Failed to read 0x%lX bytes encrypted data block at offset 0x%lX from NCA \"%s\" FS section #%u! (unaligned)", chunk_size, block_start_offset, nca_ctx->content_id_str, ctx->section_num); + LOGFILE("Failed to read 0x%lX bytes encrypted data block at offset 0x%lX from NCA \"%s\" FS section #%u! (unaligned).", chunk_size, block_start_offset, nca_ctx->content_id_str, ctx->section_num); goto exit; } - /* Decrypt data */ + /* Decrypt data. */ ncaUpdateAesCtrExIv(ctx->ctr, ctr_val, block_start_offset); aes128CtrContextResetCtr(&(ctx->ctr_ctx), ctx->ctr); aes128CtrCrypt(&(ctx->ctr_ctx), g_ncaCryptoBuffer, g_ncaCryptoBuffer, chunk_size); - /* Copy decrypted data */ + /* Copy decrypted data. */ memcpy(out, g_ncaCryptoBuffer + data_start_offset, out_chunk_size); ret = (block_size > NCA_CRYPTO_BUFFER_SIZE ? _ncaReadAesCtrExStorageFromBktrSection(ctx, (u8*)out + out_chunk_size, read_size - out_chunk_size, offset + out_chunk_size, ctr_val, false) : true); @@ -1142,9 +1125,9 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const u8 *out = NULL; bool success = false; - if (!g_ncaCryptoBuffer || !ctx || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < NCA_FULL_HEADER_LENGTH || ctx->section_type >= NcaFsSectionType_Invalid || \ - ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type >= NcaEncryptionType_AesCtrEx || !ctx->header || !data || !data_size || data_offset >= ctx->section_size || \ - (data_offset + data_size) > ctx->section_size || !out_block_size || !out_block_offset) + if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < NCA_FULL_HEADER_LENGTH || \ + ctx->section_type >= NcaFsSectionType_Invalid || ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type >= NcaEncryptionType_AesCtrEx || !ctx->header || !data || !data_size || \ + data_offset >= ctx->section_size || (data_offset + data_size) > ctx->section_size || !out_block_size || !out_block_offset) { LOGFILE("Invalid NCA FS section header parameters!"); goto exit; @@ -1166,23 +1149,23 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const goto exit; } - /* Optimization for blocks from plaintext FS sections or blocks that are aligned to the AES-CTR / AES-XTS sector size */ + /* Optimization for blocks from plaintext FS sections or blocks that are aligned to the AES-CTR / AES-XTS sector size. */ if (ctx->encryption_type == NcaEncryptionType_None || \ (ctx->encryption_type == NcaEncryptionType_AesXts && !(content_offset % NCA_AES_XTS_SECTOR_SIZE) && !(data_size % NCA_AES_XTS_SECTOR_SIZE)) || \ (ctx->encryption_type == NcaEncryptionType_AesCtr && !(content_offset % AES_BLOCK_SIZE) && !(data_size % AES_BLOCK_SIZE))) { - /* Allocate memory */ + /* Allocate memory. */ out = malloc(data_size); if (!out) { - LOGFILE("Unable to allocate 0x%lX bytes buffer! (aligned)", data_size); + LOGFILE("Unable to allocate 0x%lX bytes buffer! (aligned).", data_size); goto exit; } - /* Copy data */ + /* Copy data. */ memcpy(out, data, data_size); - /* Encrypt data */ + /* Encrypt data. */ if (ctx->encryption_type == NcaEncryptionType_AesXts) { sector_num = ((ctx->encryption_type == NcaEncryptionType_AesXts ? data_offset : (content_offset - NCA_HEADER_LENGTH)) / NCA_AES_XTS_SECTOR_SIZE); @@ -1190,7 +1173,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const crypt_res = aes128XtsNintendoCrypt(&(ctx->xts_encrypt_ctx), out, out, data_size, sector_num, NCA_AES_XTS_SECTOR_SIZE, true); if (crypt_res != data_size) { - LOGFILE("Failed to AES-XTS encrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned)", data_size, content_offset, nca_ctx->content_id_str, ctx->section_num); + LOGFILE("Failed to AES-XTS encrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", data_size, content_offset, nca_ctx->content_id_str, ctx->section_num); goto exit; } } else @@ -1208,7 +1191,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const goto exit; } - /* Calculate block offsets and size */ + /* Calculate block offsets and size. */ block_start_offset = ALIGN_DOWN(data_offset, ctx->encryption_type == NcaEncryptionType_AesXts ? NCA_AES_XTS_SECTOR_SIZE : AES_BLOCK_SIZE); block_end_offset = ALIGN_UP(data_offset + data_size, ctx->encryption_type == NcaEncryptionType_AesXts ? NCA_AES_XTS_SECTOR_SIZE : AES_BLOCK_SIZE); block_size = (block_end_offset - block_start_offset); @@ -1216,25 +1199,25 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const plain_chunk_offset = (data_offset - block_start_offset); content_offset = (ctx->section_offset + block_start_offset); - /* Allocate memory */ + /* Allocate memory. */ out = malloc(block_size); if (!out) { - LOGFILE("Unable to allocate 0x%lX bytes buffer! (unaligned)", block_size); + LOGFILE("Unable to allocate 0x%lX bytes buffer! (unaligned).", block_size); goto exit; } - /* Read decrypted data using aligned offset and size */ + /* Read decrypted data using aligned offset and size. */ if (!_ncaReadFsSection(ctx, out, block_size, block_start_offset, false)) { LOGFILE("Failed to read decrypted NCA \"%s\" FS section #%u data block!", nca_ctx->content_id_str, ctx->section_num); goto exit; } - /* Replace plaintext data */ + /* Replace plaintext data. */ memcpy(out + plain_chunk_offset, data, data_size); - /* Reencrypt data */ + /* Reencrypt data. */ if (ctx->encryption_type == NcaEncryptionType_AesXts) { sector_num = ((ctx->encryption_type == NcaEncryptionType_AesXts ? block_start_offset : (content_offset - NCA_HEADER_LENGTH)) / NCA_AES_XTS_SECTOR_SIZE); @@ -1242,7 +1225,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const crypt_res = aes128XtsNintendoCrypt(&(ctx->xts_encrypt_ctx), out, out, block_size, sector_num, NCA_AES_XTS_SECTOR_SIZE, true); if (crypt_res != block_size) { - LOGFILE("Failed to AES-XTS encrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned)", block_size, content_offset, nca_ctx->content_id_str, ctx->section_num); + LOGFILE("Failed to AES-XTS encrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", block_size, content_offset, nca_ctx->content_id_str, ctx->section_num); goto exit; } } else diff --git a/source/nca.h b/source/nca.h index 8e2335c..bc3445e 100644 --- a/source/nca.h +++ b/source/nca.h @@ -257,6 +257,7 @@ typedef enum { } NcaFsSectionType; typedef struct { + bool enabled; void *nca_ctx; ///< NcaContext. Used to perform NCA reads. u8 section_num; u64 section_offset; @@ -284,6 +285,7 @@ typedef struct { u8 key_generation; ///< NcaKeyGenerationOld / NcaKeyGeneration. Retrieved from the decrypted header. u8 id_offset; ///< Retrieved from NcmContentInfo. bool rights_id_available; + bool titlekey_retrieved; u8 titlekey[0x10]; bool dirty_header; NcaHeader header; @@ -316,6 +318,7 @@ void ncaFreeCryptoBuffer(void); /// If 'storage_id' != NcmStorageId_GameCard, the 'ncm_storage' argument must point to a valid NcmContentStorage instance, previously opened using the same NcmStorageId value. /// If 'storage_id' == NcmStorageId_GameCard, the 'hfs_partition_type' argument must be a valid GameCardHashFileSystemPartitionType value. /// If the NCA holds a populated Rights ID field, and if the Ticket element pointed to by 'tik' hasn't been filled, ticket data will be retrieved. +/// If ticket data can't be retrieved, the context will still be initialized, but anything that involves working with plaintext FS section blocks won't be possible (e.g. ncaReadFsSection()). bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm_storage, u8 hfs_partition_type, const NcmContentInfo *content_info, Ticket *tik); /// Reads raw encrypted data from a NCA using an input context, previously initialized by ncaInitializeContext(). diff --git a/source/pfs.c b/source/pfs.c index 2e9dc30..4c7a5e3 100644 --- a/source/pfs.c +++ b/source/pfs.c @@ -23,20 +23,22 @@ bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *nca_fs_ctx) { - if (!out || !nca_fs_ctx || nca_fs_ctx->section_type != NcaFsSectionType_PartitionFs || !nca_fs_ctx->header || nca_fs_ctx->header->fs_type != NcaFsType_PartitionFs || \ + if (!out || !nca_fs_ctx || !nca_fs_ctx->enabled || nca_fs_ctx->section_type != NcaFsSectionType_PartitionFs || !nca_fs_ctx->header || nca_fs_ctx->header->fs_type != NcaFsType_PartitionFs || \ nca_fs_ctx->header->hash_type != NcaHashType_HierarchicalSha256) { LOGFILE("Invalid parameters!"); return false; } - /* Fill context */ + u32 magic = 0; + PartitionFileSystemHeader pfs_header = {0}; + PartitionFileSystemEntry *main_npdm_entry = NULL; + + /* Clear output partition FS context. */ + memset(out, 0, sizeof(PartitionFileSystemContext)); + + /* Fill context. */ out->nca_fs_ctx = nca_fs_ctx; - out->offset = 0; - out->size = 0; - out->is_exefs = false; - out->header_size = 0; - out->header = NULL; if (!ncaValidateHierarchicalSha256Offsets(&(nca_fs_ctx->header->hash_info.hierarchical_sha256), nca_fs_ctx->section_size)) { @@ -47,11 +49,7 @@ bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext * out->offset = nca_fs_ctx->header->hash_info.hierarchical_sha256.hash_target_layer_info.offset; out->size = nca_fs_ctx->header->hash_info.hierarchical_sha256.hash_target_layer_info.size; - /* Read partial PFS header */ - u32 magic = 0; - PartitionFileSystemHeader pfs_header = {0}; - PartitionFileSystemEntry *main_npdm_entry = NULL; - + /* Read partial PFS header. */ if (!ncaReadFsSection(nca_fs_ctx, &pfs_header, sizeof(PartitionFileSystemHeader), out->offset)) { LOGFILE("Failed to read partial partition FS header!"); @@ -61,7 +59,7 @@ bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext * magic = __builtin_bswap32(pfs_header.magic); if (magic != PFS0_MAGIC) { - LOGFILE("Invalid partition FS magic word! (0x%08X)", magic); + LOGFILE("Invalid partition FS magic word! (0x%08X).", magic); return false; } @@ -71,10 +69,10 @@ bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext * return false; } - /* Calculate full partition FS header size */ + /* Calculate full partition FS header size. */ out->header_size = (sizeof(PartitionFileSystemHeader) + (pfs_header.entry_count * sizeof(PartitionFileSystemEntry)) + pfs_header.name_table_size); - /* Allocate memory for the full partition FS header */ + /* Allocate memory for the full partition FS header. */ out->header = calloc(out->header_size, sizeof(u8)); if (!out->header) { @@ -82,7 +80,7 @@ bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext * return false; } - /* Read full partition FS header */ + /* Read full partition FS header. */ if (!ncaReadFsSection(nca_fs_ctx, out->header, out->header_size, out->offset)) { LOGFILE("Failed to read full partition FS header!"); @@ -90,7 +88,7 @@ bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext * return false; } - /* Check if we're dealing with an ExeFS section */ + /* Check if we're dealing with an ExeFS section. */ if ((main_npdm_entry = pfsGetEntryByName(out, "main.npdm")) != NULL && pfsReadEntryData(out, main_npdm_entry, &magic, sizeof(u32), 0) && \ __builtin_bswap32(magic) == NPDM_META_MAGIC) out->is_exefs = true; @@ -105,7 +103,7 @@ bool pfsReadPartitionData(PartitionFileSystemContext *ctx, void *out, u64 read_s return false; } - /* Read partition data */ + /* Read partition data. */ if (!ncaReadFsSection(ctx->nca_fs_ctx, out, read_size, ctx->offset + offset)) { LOGFILE("Failed to read partition FS data!"); @@ -124,7 +122,7 @@ bool pfsReadEntryData(PartitionFileSystemContext *ctx, PartitionFileSystemEntry return false; } - /* Read entry data */ + /* Read entry data. */ if (!pfsReadPartitionData(ctx, out, read_size, ctx->header_size + fs_entry->offset + offset)) { LOGFILE("Failed to read partition FS entry data!"); @@ -134,6 +132,67 @@ bool pfsReadEntryData(PartitionFileSystemContext *ctx, PartitionFileSystemEntry return true; } +bool pfsGetEntryIndexByName(PartitionFileSystemContext *ctx, const char *name, u32 *out_idx) +{ + size_t name_len = 0; + PartitionFileSystemEntry *fs_entry = NULL; + u32 entry_count = pfsGetEntryCount(ctx); + char *name_table = pfsGetNameTable(ctx); + + if (!entry_count || !name_table || !name || !(name_len = strlen(name)) || !out_idx) + { + LOGFILE("Invalid parameters!"); + return false; + } + + for(u32 i = 0; i < entry_count; i++) + { + if (!(fs_entry = pfsGetEntryByIndex(ctx, i))) + { + LOGFILE("Failed to retrieve partition FS entry #%u!", i); + return false; + } + + if (strlen(name_table + fs_entry->name_offset) == name_len && !strcmp(name_table + fs_entry->name_offset, name)) + { + *out_idx = i; + return true; + } + } + + LOGFILE("Unable to find partition FS entry \"%s\"!", name); + + return false; +} + +bool pfsGetTotalDataSize(PartitionFileSystemContext *ctx, u64 *out_size) +{ + u64 total_size = 0; + u32 entry_count = pfsGetEntryCount(ctx); + PartitionFileSystemEntry *fs_entry = NULL; + + if (!entry_count || !out_size) + { + LOGFILE("Invalid parameters!"); + return false; + } + + for(u32 i = 0; i < entry_count; i++) + { + if (!(fs_entry = pfsGetEntryByIndex(ctx, i))) + { + LOGFILE("Failed to retrieve partition FS entry #%u!", i); + return false; + } + + total_size += fs_entry->size; + } + + *out_size = total_size; + + return true; +} + bool pfsGenerateEntryPatch(PartitionFileSystemContext *ctx, PartitionFileSystemEntry *fs_entry, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalSha256Patch *out) { if (!ctx || !ctx->nca_fs_ctx || !ctx->header_size || !ctx->header || !fs_entry || fs_entry->offset >= ctx->size || !fs_entry->size || (fs_entry->offset + fs_entry->size) > ctx->size || !data || \ diff --git a/source/pfs.h b/source/pfs.h index 40707f3..c716a72 100644 --- a/source/pfs.h +++ b/source/pfs.h @@ -25,7 +25,7 @@ #include "nca.h" -#define PFS0_MAGIC 0x50465330 /* "PFS0" */ +#define PFS0_MAGIC 0x50465330 /* "PFS0". */ typedef struct { u32 magic; ///< "PFS0". @@ -69,6 +69,12 @@ bool pfsReadPartitionData(PartitionFileSystemContext *ctx, void *out, u64 read_s /// Input offset must be relative to the start of the partition FS entry. bool pfsReadEntryData(PartitionFileSystemContext *ctx, PartitionFileSystemEntry *fs_entry, void *out, u64 read_size, u64 offset); +/// Retrieves a partition FS entry index by its name. +bool pfsGetEntryIndexByName(PartitionFileSystemContext *ctx, const char *name, u32 *out_idx); + +/// Calculates the extracted partition FS size. +bool pfsGetTotalDataSize(PartitionFileSystemContext *ctx, u64 *out_size); + /// Generates HierarchicalSha256 FS section patch data using a partition FS context + entry, which can be used to replace NCA data in content dumping operations. /// Input offset must be relative to the start of the partition FS entry data. /// This function shares the same limitations as ncaGenerateHierarchicalSha256Patch(). @@ -103,29 +109,6 @@ NX_INLINE char *pfsGetEntryNameByIndex(PartitionFileSystemContext *ctx, u32 idx) return (name_table + fs_entry->name_offset); } -NX_INLINE bool pfsGetEntryIndexByName(PartitionFileSystemContext *ctx, const char *name, u32 *out_idx) -{ - size_t name_len = 0; - PartitionFileSystemEntry *fs_entry = NULL; - u32 entry_count = pfsGetEntryCount(ctx); - char *name_table = pfsGetNameTable(ctx); - - if (!entry_count || !name_table || !name || !(name_len = strlen(name)) || !out_idx) return false; - - for(u32 i = 0; i < entry_count; i++) - { - if (!(fs_entry = pfsGetEntryByIndex(ctx, i))) return false; - - if (strlen(name_table + fs_entry->name_offset) == name_len && !strcmp(name_table + fs_entry->name_offset, name)) - { - *out_idx = i; - return true; - } - } - - return false; -} - NX_INLINE PartitionFileSystemEntry *pfsGetEntryByName(PartitionFileSystemContext *ctx, const char *name) { u32 idx = 0; @@ -133,22 +116,4 @@ NX_INLINE PartitionFileSystemEntry *pfsGetEntryByName(PartitionFileSystemContext return pfsGetEntryByIndex(ctx, idx); } -NX_INLINE bool pfsGetTotalDataSize(PartitionFileSystemContext *ctx, u64 *out_size) -{ - u64 total_size = 0; - u32 entry_count = pfsGetEntryCount(ctx); - PartitionFileSystemEntry *fs_entry = NULL; - if (!entry_count || !out_size) return false; - - for(u32 i = 0; i < entry_count; i++) - { - if (!(fs_entry = pfsGetEntryByIndex(ctx, i))) return false; - total_size += fs_entry->size; - } - - *out_size = total_size; - - return true; -} - #endif /* __PFS_H__ */ diff --git a/source/romfs.c b/source/romfs.c index 3829102..b6c1484 100644 --- a/source/romfs.c +++ b/source/romfs.c @@ -31,7 +31,7 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_ NcaContext *nca_ctx = NULL; u64 dir_table_offset = 0, file_table_offset = 0; - if (!out || !nca_fs_ctx || !nca_fs_ctx->header || !(nca_ctx = (NcaContext*)nca_fs_ctx->nca_ctx) || (nca_ctx->format_version == NcaVersion_Nca0 && \ + if (!out || !nca_fs_ctx || !nca_fs_ctx->enabled || !nca_fs_ctx->header || !(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 && \ (nca_fs_ctx->section_type != NcaFsSectionType_RomFs || nca_fs_ctx->header->hash_type != NcaHashType_HierarchicalIntegrity))) { @@ -39,15 +39,11 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_ return false; } - /* Fill context */ + /* Clear output RomFS context. */ + memset(out, 0, sizeof(RomFileSystemContext)); + + /* Fill context. */ out->nca_fs_ctx = nca_fs_ctx; - out->offset = 0; - out->size = 0; - out->dir_table_size = 0; - out->dir_table = NULL; - out->file_table_size = 0; - out->file_table = NULL; - out->body_offset = 0; if (nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs) { @@ -70,7 +66,7 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_ out->size = nca_fs_ctx->header->hash_info.hierarchical_integrity.hash_target_layer_info.size; } - /* Read RomFS header */ + /* Read RomFS header. */ if (!ncaReadFsSection(nca_fs_ctx, &(out->header), sizeof(RomFileSystemHeader), out->offset)) { LOGFILE("Failed to read RomFS header!"); @@ -86,7 +82,7 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_ bool success = false; - /* Read directory entries table */ + /* Read directory entries table. */ 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); 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); @@ -109,7 +105,7 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_ goto exit; } - /* Read file entries table */ + /* Read file entries table. */ 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); 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); @@ -132,7 +128,7 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_ goto exit; } - /* Get file data body offset */ + /* Get file data body offset. */ out->body_offset = (nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs ? (u64)out->header.old_format.body_offset : out->header.cur_format.body_offset); if (out->body_offset >= out->size) { @@ -156,7 +152,7 @@ bool romfsReadFileSystemData(RomFileSystemContext *ctx, void *out, u64 read_size return false; } - /* Read filesystem data */ + /* Read filesystem data. */ if (!ncaReadFsSection(ctx->nca_fs_ctx, out, read_size, ctx->offset + offset)) { LOGFILE("Failed to read RomFS data!"); @@ -175,7 +171,7 @@ bool romfsReadFileEntryData(RomFileSystemContext *ctx, RomFileSystemFileEntry *f return false; } - /* Read entry data */ + /* Read entry data. */ if (!romfsReadFileSystemData(ctx, out, read_size, ctx->body_offset + file_entry->offset + offset)) { LOGFILE("Failed to read RomFS file entry data!"); @@ -270,10 +266,10 @@ RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext * return NULL; } - /* Check if the root directory was requested */ + /* Check if the root directory was requested. */ if (path_len == 1) return dir_entry; - /* Duplicate path to avoid problems with strtok() */ + /* Duplicate path to avoid problems with strtok(). */ if (!(path_dup = strdup(path))) { LOGFILE("Unable to duplicate input path!"); @@ -318,39 +314,39 @@ RomFileSystemFileEntry *romfsGetFileEntryByPath(RomFileSystemContext *ctx, const return NULL; } - /* Duplicate path */ + /* Duplicate path. */ if (!(path_dup = strdup(path))) { LOGFILE("Unable to duplicate input path!"); return NULL; } - /* Remove any trailing slashes */ + /* Remove any trailing slashes. */ while(path_dup[path_len - 1] == '/') { path_dup[path_len - 1] = '\0'; path_len--; } - /* Safety check */ + /* Safety check. */ if (!path_len || !(filename = strrchr(path_dup, '/'))) { LOGFILE("Invalid input path!"); goto out; } - /* Remove leading slash and adjust filename string pointer */ + /* 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 */ + /* 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 */ + /* Retrieve file entry. */ if (!(file_entry = romfsGetChildFileEntryByName(ctx, dir_entry, filename))) LOGFILE("Failed to retrieve file entry by name!"); out: @@ -373,14 +369,14 @@ bool romfsGeneratePathFromDirectoryEntry(RomFileSystemContext *ctx, RomFileSyste return false; } - /* Check if we're dealing with the root directory entry */ + /* 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. */ dir_entries = calloc(1, sizeof(RomFileSystemDirectoryEntry*)); if (!dir_entries) { @@ -397,7 +393,7 @@ bool romfsGeneratePathFromDirectoryEntry(RomFileSystemContext *ctx, RomFileSyste dir_offset = dir_entries[dir_entries_count - 1]->parent_offset; if (!dir_offset) break; - /* Reallocate directory entries */ + /* Reallocate directory entries. */ if (!(tmp_dir_entries = realloc(dir_entries, (dir_entries_count + 1) * sizeof(RomFileSystemDirectoryEntry*)))) { LOGFILE("Unable to reallocate directory entries buffer!"); @@ -423,7 +419,7 @@ bool romfsGeneratePathFromDirectoryEntry(RomFileSystemContext *ctx, RomFileSyste goto out; } - /* Generate output path */ + /* Generate output path. */ *out_path = '\0'; for(u32 i = dir_entries_count; i > 0; i--) { @@ -453,14 +449,14 @@ bool romfsGeneratePathFromFileEntry(RomFileSystemContext *ctx, RomFileSystemFile return false; } - /* Retrieve full RomFS path up to the file entry name */ + /* Retrieve full RomFS path up to the file entry name. */ if (!romfsGeneratePathFromDirectoryEntry(ctx, dir_entry, out_path, out_path_size, illegal_char_replace_type)) { LOGFILE("Failed to retrieve RomFS directory path!"); return false; } - /* Check path length */ + /* Check path length. */ path_len = strlen(out_path); if ((1 + file_entry->name_length) >= (out_path_size - path_len)) { @@ -468,7 +464,7 @@ bool romfsGeneratePathFromFileEntry(RomFileSystemContext *ctx, RomFileSystemFile return false; } - /* Concatenate file entry name */ + /* Concatenate file entry name. */ if (file_entry->parent_offset) strcat(out_path, "/"); strncat(out_path, file_entry->name, file_entry->name_length); if (illegal_char_replace_type) utilsReplaceIllegalCharacters(out_path + (strlen(out_path) - file_entry->name_length), \ diff --git a/source/rsa.c b/source/rsa.c index b6dc0c8..dc82ad1 100644 --- a/source/rsa.c +++ b/source/rsa.c @@ -109,38 +109,38 @@ bool rsa2048GenerateSha256BasedCustomAcidSignature(void *dst, const void *src, s mbedtls_pk_context pk; mbedtls_pk_init(&pk); - /* Calculate SHA-256 checksum for the input data */ + /* Calculate SHA-256 checksum for the input data. */ sha256CalculateHash(hash, src, size); - /* Seed the random number generator */ + /* Seed the random number generator. */ ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const u8*)pers, strlen(pers)); if (ret != 0) { - LOGFILE("mbedtls_ctr_drbg_seed failed! (%d)", ret); + LOGFILE("mbedtls_ctr_drbg_seed failed! (%d).", ret); goto out; } - /* Parse private key */ + /* Parse private key. */ ret = mbedtls_pk_parse_key(&pk, (u8*)g_rsa2048CustomAcidPrivateKey, strlen(g_rsa2048CustomAcidPrivateKey) + 1, NULL, 0); if (ret != 0) { - LOGFILE("mbedtls_pk_parse_key failed! (%d)", ret); + LOGFILE("mbedtls_pk_parse_key failed! (%d).", ret); goto out; } - /* Set RSA padding */ + /* Set RSA padding. */ mbedtls_rsa_set_padding(mbedtls_pk_rsa(pk), MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA256); - /* Calculate hash signature */ + /* Calculate hash signature. */ ret = mbedtls_pk_sign(&pk, MBEDTLS_MD_SHA256, hash, 0, buf, &olen, mbedtls_ctr_drbg_random, &ctr_drbg); if (ret != 0) { - LOGFILE("mbedtls_pk_sign failed! (%d)", ret); + LOGFILE("mbedtls_pk_sign failed! (%d).", ret); goto out; } - /* Copy signature to output buffer */ - memcpy(dst, buf, RSA2048_SIGNATURE_SIZE); + /* Copy signature to output buffer. */ + memcpy(dst, buf, RSA2048_SIG_SIZE); success = true; out: @@ -165,12 +165,12 @@ bool rsa2048OaepDecryptAndVerify(void *dst, size_t dst_size, const void *signatu } Result rc = 0; - u8 m_buf[RSA2048_SIGNATURE_SIZE] = {0}; + u8 m_buf[RSA2048_SIG_SIZE] = {0}; rc = splUserExpMod(signature, modulus, exponent, exponent_size, m_buf); if (R_FAILED(rc)) { - LOGFILE("splUserExpMod failed! (0x%08X)", rc); + LOGFILE("splUserExpMod failed! (0x%08X).", rc); return false; } @@ -180,13 +180,13 @@ bool rsa2048OaepDecryptAndVerify(void *dst, size_t dst_size, const void *signatu return false; } - /* Unmask salt */ - rsaCalculateMgf1AndXor(m_buf + 1, 0x20, m_buf + 0x21, RSA2048_SIGNATURE_SIZE - 0x21); + /* Unmask salt. */ + rsaCalculateMgf1AndXor(m_buf + 1, 0x20, m_buf + 0x21, RSA2048_SIG_SIZE - 0x21); - /* Unmask DB */ - rsaCalculateMgf1AndXor(m_buf + 0x21, RSA2048_SIGNATURE_SIZE - 0x21, m_buf + 1, 0x20); + /* Unmask DB. */ + rsaCalculateMgf1AndXor(m_buf + 0x21, RSA2048_SIG_SIZE - 0x21, m_buf + 1, 0x20); - /* Validate label hash */ + /* Validate label hash. */ const u8 *db = (const u8*)(m_buf + 0x21); if (memcmp(db, label_hash, SHA256_HASH_SIZE) != 0) { @@ -194,9 +194,9 @@ bool rsa2048OaepDecryptAndVerify(void *dst, size_t dst_size, const void *signatu return false; } - /* Validate message prefix */ + /* Validate message prefix. */ const u8 *data = (const u8*)(db + 0x20); - size_t remaining = (RSA2048_SIGNATURE_SIZE - 0x41); + size_t remaining = (RSA2048_SIG_SIZE - 0x41); while(!*data && remaining) { @@ -221,7 +221,7 @@ bool rsa2048OaepDecryptAndVerify(void *dst, size_t dst_size, const void *signatu static void rsaCalculateMgf1AndXor(void *data, size_t data_size, const void *h_src, size_t h_src_size) { - if (!data || !data_size || !h_src || !h_src_size || h_src_size > RSA2048_SIGNATURE_SIZE) + if (!data || !data_size || !h_src || !h_src_size || h_src_size > RSA2048_SIG_SIZE) { LOGFILE("Invalid parameters!"); return; @@ -232,7 +232,7 @@ static void rsaCalculateMgf1AndXor(void *data, size_t data_size, const void *h_s u8 *data_u8 = (u8*)data; u8 mgf1_buf[SHA256_HASH_SIZE] = {0}; - u8 h_buf[RSA2048_SIGNATURE_SIZE] = {0}; + u8 h_buf[RSA2048_SIG_SIZE] = {0}; memcpy(h_buf, h_src, h_src_size); diff --git a/source/rsa.h b/source/rsa.h index f46a4a8..34d516d 100644 --- a/source/rsa.h +++ b/source/rsa.h @@ -25,11 +25,16 @@ #ifndef __RSA_H__ #define __RSA_H__ -#define RSA2048_SIGNATURE_SIZE 0x100 +#define RSA2048_SIG_SIZE 0x100 +/// Generates a SHA-256 based RSA-2048 signature, suitable to replace the ACID signature in NCA headers. bool rsa2048GenerateSha256BasedCustomAcidSignature(void *dst, const void *src, size_t size); + +/// Returns the custom ACID public key that can be used to verify the signature generated by rsa2048GenerateSha256BasedCustomAcidSignature(). +/// Suitable to replace the ACID public key in main.npdm files. const u8 *rsa2048GetCustomAcidPublicKey(void); +/// Performs RSA-2048 OAEP decryption and verification. Used to decrypt the titlekey block from tickets with personalized crypto. bool rsa2048OaepDecryptAndVerify(void *dst, size_t dst_size, const void *signature, const void *modulus, const void *exponent, size_t exponent_size, const void *label_hash, size_t *out_size); #endif /* __RSA_H__ */ diff --git a/source/save.c b/source/save.c index e2b5a4a..1c56f5b 100644 --- a/source/save.c +++ b/source/save.c @@ -269,14 +269,14 @@ static u32 save_remap_read(remap_storage_ctx_t *ctx, void *buffer, u64 offset, s fr = f_lseek(ctx->file, ctx->base_storage_offset + entry->physical_offset + entry_pos); if (fr || f_tell(ctx->file) != (ctx->base_storage_offset + entry->physical_offset + entry_pos)) { - LOGFILE("Failed to seek to offset 0x%lX in savefile! (%u)", ctx->base_storage_offset + entry->physical_offset + entry_pos, fr); + LOGFILE("Failed to seek to offset 0x%lX in savefile! (%u).", ctx->base_storage_offset + entry->physical_offset + entry_pos, fr); return out_pos; } fr = f_read(ctx->file, (u8*)buffer + out_pos, bytes_to_read, &br); if (fr || br != bytes_to_read) { - LOGFILE("Failed to read %u bytes chunk from offset 0x%lX in savefile! (%u)", bytes_to_read, ctx->base_storage_offset + entry->physical_offset + entry_pos, fr); + LOGFILE("Failed to read %u bytes chunk from offset 0x%lX in savefile! (%u).", bytes_to_read, ctx->base_storage_offset + entry->physical_offset + entry_pos, fr); return (out_pos + br); } @@ -470,14 +470,14 @@ static size_t save_ivfc_level_fread(ivfc_level_save_ctx_t *ctx, void *buffer, u6 fr = f_lseek(ctx->save_ctx->file, ctx->hash_offset + offset); if (fr || f_tell(ctx->save_ctx->file) != (ctx->hash_offset + offset)) { - LOGFILE("Failed to seek to offset 0x%lX in savefile! (%u)", ctx->hash_offset + offset, fr); + LOGFILE("Failed to seek to offset 0x%lX in savefile! (%u).", ctx->hash_offset + offset, fr); return (size_t)br; } fr = f_read(ctx->save_ctx->file, buffer, count, &br); if (fr || br != count) { - LOGFILE("Failed to read IVFC level data from offset 0x%lX in savefile! (%u)", ctx->hash_offset + offset, fr); + LOGFILE("Failed to read IVFC level data from offset 0x%lX in savefile! (%u).", ctx->hash_offset + offset, fr); return (size_t)br; } @@ -815,7 +815,8 @@ u32 save_allocation_table_storage_read(allocation_table_storage_ctx_t *ctx, void { u32 bytes_to_request = (chunk_remaining < sector_size ? chunk_remaining : sector_size); - if (!save_ivfc_storage_read(&ctx->base_storage->integrity_storages[3], (u8*)buffer + out_pos + i, physical_offset + i, bytes_to_request, ctx->base_storage->data_level->save_ctx->tool_ctx.action & ACTION_VERIFY)) + if (!save_ivfc_storage_read(&ctx->base_storage->integrity_storages[3], (u8*)buffer + out_pos + i, physical_offset + i, bytes_to_request, \ + ctx->base_storage->data_level->save_ctx->tool_ctx.action & ACTION_VERIFY)) { LOGFILE("Failed to read %u bytes chunk from IVFC storage at physical offset 0x%lX!", bytes_to_request, physical_offset + i); return (out_pos + bytes_to_read - chunk_remaining); @@ -1232,7 +1233,7 @@ bool save_process(save_ctx_t *ctx) fr = f_read(ctx->file, &ctx->header, sizeof(ctx->header), &br); if (fr || br != sizeof(ctx->header)) { - LOGFILE("Failed to read savefile header A! (%u)", fr); + LOGFILE("Failed to read savefile header A! (%u).", fr); return success; } @@ -1242,14 +1243,14 @@ bool save_process(save_ctx_t *ctx) fr = f_lseek(ctx->file, 0x4000); if (fr || f_tell(ctx->file) != 0x4000) { - LOGFILE("Failed to seek to offset 0x4000 in savefile! (%u)", fr); + LOGFILE("Failed to seek to offset 0x4000 in savefile! (%u).", fr); return success; } fr = f_read(ctx->file, &ctx->header, sizeof(ctx->header), &br); if (fr || br != sizeof(ctx->header)) { - LOGFILE("Failed to read savefile header B! (%u)", fr); + LOGFILE("Failed to read savefile header B! (%u).", fr); return success; } @@ -1281,7 +1282,7 @@ bool save_process(save_ctx_t *ctx) fr = f_lseek(ctx->file, ctx->header.layout.file_map_entry_offset); if (fr || f_tell(ctx->file) != ctx->header.layout.file_map_entry_offset) { - LOGFILE("Failed to seek to file map entry offset 0x%lX in savefile! (%u)", ctx->header.layout.file_map_entry_offset, fr); + LOGFILE("Failed to seek to file map entry offset 0x%lX in savefile! (%u).", ctx->header.layout.file_map_entry_offset, fr); return success; } @@ -1290,7 +1291,7 @@ bool save_process(save_ctx_t *ctx) fr = f_read(ctx->file, &ctx->data_remap_storage.map_entries[i], 0x20, &br); if (fr || br != 0x20) { - LOGFILE("Failed to read data remap storage entry #%u! (%u)", i, fr); + LOGFILE("Failed to read data remap storage entry #%u! (%u).", i, fr); goto out; } @@ -1418,7 +1419,7 @@ bool save_process(save_ctx_t *ctx) fr = f_lseek(ctx->file, ctx->header.layout.meta_map_entry_offset); if (fr || f_tell(ctx->file) != ctx->header.layout.meta_map_entry_offset) { - LOGFILE("Failed to seek to meta map entry offset 0x%lX in savefile! (%u)", ctx->header.layout.meta_map_entry_offset, fr); + LOGFILE("Failed to seek to meta map entry offset 0x%lX in savefile! (%u).", ctx->header.layout.meta_map_entry_offset, fr); goto out; } @@ -1427,7 +1428,7 @@ bool save_process(save_ctx_t *ctx) fr = f_read(ctx->file, &ctx->meta_remap_storage.map_entries[i], 0x20, &br); if (fr || br != 0x20) { - LOGFILE("Failed to read meta remap storage entry #%u! (%u)", i, fr); + LOGFILE("Failed to read meta remap storage entry #%u! (%u).", i, fr); goto out; } @@ -1512,20 +1513,20 @@ bool save_process(save_ctx_t *ctx) if (!save_ivfc_storage_init(&ctx->fat_ivfc_storage, ctx->header.layout.fat_ivfc_master_hash_a, &ctx->header.fat_ivfc_header)) { - LOGFILE("Failed to initialize FAT storage (IVFC)!"); + LOGFILE("Failed to initialize FAT storage! (IVFC)."); goto out; } ctx->fat_storage = calloc(1, ctx->fat_ivfc_storage._length); if (!ctx->fat_storage) { - LOGFILE("Failed to allocate memory for FAT storage (IVFC)!"); + LOGFILE("Failed to allocate memory for FAT storage! (IVFC)."); goto out; } if (save_remap_read(&ctx->meta_remap_storage, ctx->fat_storage, ctx->header.fat_ivfc_header.level_headers[ctx->header.fat_ivfc_header.num_levels - 2].logical_offset, ctx->fat_ivfc_storage._length) != ctx->fat_ivfc_storage._length) { - LOGFILE("Failed to read FAT storage from meta remap storage (IVFC)!"); + LOGFILE("Failed to read FAT storage from meta remap storage! (IVFC)."); goto out; } } @@ -1739,7 +1740,7 @@ save_ctx_t *save_open_savefile(const char *path, u32 action) fr = f_open(save_fd, path, FA_READ | FA_OPEN_EXISTING); if (fr != FR_OK) { - LOGFILE("Failed to open \"%s\" savefile from BIS System partition! (%u)", path, fr); + LOGFILE("Failed to open \"%s\" savefile from BIS System partition! (%u).", path, fr); goto out; } diff --git a/source/save.h b/source/save.h index e86cac8..9b604fd 100644 --- a/source/save.h +++ b/source/save.h @@ -51,7 +51,7 @@ typedef enum { typedef struct save_ctx_t save_ctx_t; typedef struct { - u32 magic; /* DISF */ + u32 magic; /* "DISF". */ u32 version; u8 hash[0x20]; u64 file_map_entry_offset; @@ -111,7 +111,7 @@ typedef struct { #pragma pack(pop) typedef struct { - u32 magic; /* DPFS */ + u32 magic; /* "DPFS". */ u32 version; duplex_info_t layers[3]; } duplex_header_t; @@ -124,7 +124,7 @@ typedef struct { } journal_map_header_t; typedef struct { - u32 magic; /* JNGL */ + u32 magic; /* "JNGL". */ u32 version; u64 total_size; u64 journal_size; @@ -132,7 +132,7 @@ typedef struct { } journal_header_t; typedef struct { - u32 magic; /* SAVE */ + u32 magic; /* "SAVE". */ u32 version; u64 block_count; u64 block_size; @@ -151,7 +151,7 @@ typedef struct { } fat_header_t; typedef struct { - u32 magic; /* RMAP */ + u32 magic; /* "RMAP". */ u32 version; u32 map_entry_count; u32 map_segment_count; diff --git a/source/service_guard.h b/source/service_guard.h index 3c16f95..7574c9e 100644 --- a/source/service_guard.h +++ b/source/service_guard.h @@ -1,7 +1,7 @@ /* * service_guard.h * - * Copyright (c) 2019, Switchbrew, libnx contributors. + * Copyright (c) 2018-2020, Switchbrew and libnx contributors. * Copyright (c) 2020, DarkMatterCore . * * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). diff --git a/source/services.c b/source/services.c index 2fe4289..05116c8 100644 --- a/source/services.c +++ b/source/services.c @@ -29,9 +29,9 @@ /* Type definitions. */ -typedef bool (*ServiceCondFunction)(void *arg); /* Used to perform a runtime condition check (e.g. system version) before initializing the service */ -typedef Result (*ServiceInitFunction)(void); /* Used to initialize the service */ -typedef void (*ServiceCloseFunction)(void); /* Used to close the service */ +typedef bool (*ServiceCondFunction)(void *arg); /* Used to perform a runtime condition check (e.g. system version) before initializing the service. */ +typedef Result (*ServiceInitFunction)(void); /* Used to initialize the service. */ +typedef void (*ServiceCloseFunction)(void); /* Used to close the service. */ typedef struct ServicesInfoEntry { bool initialized; @@ -56,13 +56,13 @@ static ServicesInfoEntry g_serviceInfo[] = { { false, "ns", NULL, &nsInitialize, &nsExit }, { false, "csrng", NULL, &csrngInitialize, &csrngExit }, { false, "spl", NULL, &splInitialize, &splExit }, - { false, "spl:mig", &servicesSplCryptoCheckAvailability, &splCryptoInitialize, &splCryptoExit }, /* Checks if spl:mig is really available (e.g. avoid calling splInitialize twice) */ + { false, "spl:mig", &servicesSplCryptoCheckAvailability, &splCryptoInitialize, &splCryptoExit }, /* Checks if spl:mig is really available (e.g. avoid calling splInitialize twice). */ { false, "pm:dmnt", NULL, &pmdmntInitialize, &pmdmntExit }, { false, "pl:u", NULL, &servicesPlUserInitialize, &plExit }, { false, "psm", NULL, &psmInitialize, &psmExit }, { false, "nifm:u", NULL, &servicesNifmUserInitialize, &nifmExit }, - { false, "clk", &servicesClkGetServiceType, NULL, NULL }, /* Placeholder for pcv / clkrst */ - { false, "fsp-usb", &servicesFspUsbCheckAvailability, &fspusbInitialize, &fspusbExit }, /* Checks if fsp-usb is really available */ + { false, "clk", &servicesClkGetServiceType, NULL, NULL }, /* Placeholder for pcv / clkrst. */ + { false, "fsp-usb", &servicesFspUsbCheckAvailability, &fspusbInitialize, &fspusbExit }, /* Checks if fsp-usb is really available. */ { false, "es", NULL, &esInitialize, &esExit }, { false, "set:cal", NULL, &setcalInitialize, &setcalExit } }; @@ -83,27 +83,27 @@ bool servicesInitialize(void) for(u32 i = 0; i < g_serviceInfoCount; i++) { - /* Check if this service has been already initialized or if it actually has a valid initialize function */ + /* Check if this service has been already initialized or if it actually has a valid initialize function. */ if (g_serviceInfo[i].initialized || g_serviceInfo[i].init_func == NULL) continue; - /* Check if this service depends on a condition function */ + /* Check if this service depends on a condition function. */ if (g_serviceInfo[i].cond_func != NULL) { - /* Run the condition function - it will update the current service member */ - /* Skip this service if the required conditions aren't met */ + /* Run the condition function - it will update the current service member. */ + /* Skip this service if the required conditions aren't met. */ if (!g_serviceInfo[i].cond_func(&(g_serviceInfo[i]))) continue; } - /* Initialize service */ + /* Initialize service. */ rc = g_serviceInfo[i].init_func(); if (R_FAILED(rc)) { - LOGFILE("Failed to initialize %s service! (0x%08X)", g_serviceInfo[i].name, rc); + LOGFILE("Failed to initialize %s service! (0x%08X).", g_serviceInfo[i].name, rc); ret = false; break; } - /* Update initialized flag */ + /* Update initialized flag. */ g_serviceInfo[i].initialized = true; } @@ -118,10 +118,10 @@ void servicesClose(void) for(u32 i = 0; i < g_serviceInfoCount; i++) { - /* Check if this service has not been initialized, or if it doesn't have a valid close function */ + /* Check if this service has not been initialized, or if it doesn't have a valid close function. */ if (!g_serviceInfo[i].initialized || g_serviceInfo[i].close_func == NULL) continue; - /* Close service */ + /* Close service. */ g_serviceInfo[i].close_func(); } @@ -199,11 +199,11 @@ static Result servicesClkrstInitialize(void) { Result rc = 0; - /* Open clkrst service handle */ + /* Open clkrst service handle. */ rc = clkrstInitialize(); if (R_FAILED(rc)) return rc; - /* Initialize CPU and MEM clkrst sessions */ + /* Initialize CPU and MEM clkrst sessions. */ memset(&g_clkrstCpuSession, 0, sizeof(ClkrstSession)); memset(&g_clkrstMemSession, 0, sizeof(ClkrstSession)); @@ -226,11 +226,11 @@ static Result servicesClkrstInitialize(void) static void servicesClkrstExit(void) { - /* Close CPU and MEM clkrst sessions */ + /* Close CPU and MEM clkrst sessions. */ clkrstCloseSession(&g_clkrstMemSession); clkrstCloseSession(&g_clkrstCpuSession); - /* Close clkrst service handle */ + /* Close clkrst service handle. */ clkrstExit(); } @@ -241,11 +241,11 @@ static bool servicesClkGetServiceType(void *arg) ServicesInfoEntry *info = (ServicesInfoEntry*)arg; if (strlen(info->name) != 3 || strcmp(info->name, "clk") != 0 || info->init_func != NULL || info->close_func != NULL) return false; - /* Determine which service needs to be used to control hardware clock rates, depending on the system version */ - /* This may either be pcv (sysver lower than 8.0.0) or clkrst (sysver equal to or greater than 8.0.0) */ + /* Determine which service needs to be used to control hardware clock rates, depending on the system version. */ + /* This may either be pcv (sysver lower than 8.0.0) or clkrst (sysver equal to or greater than 8.0.0). */ g_clkSvcUsePcv = hosversionBefore(8, 0, 0); - /* Fill service info */ + /* Fill service info. */ sprintf(info->name, "%s", (g_clkSvcUsePcv ? "pcv" : "clkrst")); info->init_func = (g_clkSvcUsePcv ? &pcvInitialize : &servicesClkrstInitialize); info->close_func = (g_clkSvcUsePcv ? &pcvExit : &servicesClkrstExit); @@ -260,7 +260,7 @@ static bool servicesSplCryptoCheckAvailability(void *arg) ServicesInfoEntry *info = (ServicesInfoEntry*)arg; if (strlen(info->name) != 7 || strcmp(info->name, "spl:mig") != 0 || info->init_func == NULL || info->close_func == NULL) return false; - /* Check if spl:mig is available (sysver equal to or greater than 4.0.0) */ + /* Check if spl:mig is available (sysver equal to or greater than 4.0.0). */ return !hosversionBefore(4, 0, 0); } @@ -271,6 +271,6 @@ static bool servicesFspUsbCheckAvailability(void *arg) ServicesInfoEntry *info = (ServicesInfoEntry*)arg; if (strlen(info->name) != 7 || strcmp(info->name, "fsp-usb") != 0 || info->init_func == NULL || info->close_func == NULL) return false; - /* Check if fsp-usb is actually running in the background */ + /* Check if fsp-usb is actually running in the background. */ return servicesCheckRunningServiceByName("fsp-usb"); } diff --git a/source/services.h b/source/services.h index 6e5d9e5..e834d16 100644 --- a/source/services.h +++ b/source/services.h @@ -23,18 +23,25 @@ #ifndef __SERVICES_H__ #define __SERVICES_H__ -/* Hardware clocks expressed in MHz */ +/* Hardware clocks expressed in MHz. */ #define CPU_CLKRT_NORMAL 1020 #define CPU_CLKRT_OVERCLOCKED 1785 #define MEM_CLKRT_NORMAL 1331 #define MEM_CLKRT_OVERCLOCKED 1600 +/// Initializes the background services needed by the application. bool servicesInitialize(); + +/// Closes services previously initialized by servicesInitialize(). void servicesClose(); +/// Checks if a service is running by its name. bool servicesCheckRunningServiceByName(const char *name); + +/// Check if a service has been initialized by its name. bool servicesCheckInitializedServiceByName(const char *name); +/// Changes CPU/MEM clock rates at runtime. void servicesChangeHardwareClockRates(u32 cpu_rate, u32 mem_rate); #endif /* __SERVICES_H__ */ diff --git a/source/tik.c b/source/tik.c index 2dfa1c7..51e7d5a 100644 --- a/source/tik.c +++ b/source/tik.c @@ -1,6 +1,7 @@ /* * tik.c * + * Copyright (c) 2019, shchmue. * Copyright (c) 2020, DarkMatterCore . * * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). @@ -183,7 +184,7 @@ static bool tikRetrieveTicketFromGameCardByRightsId(Ticket *dst, const FsRightsI if (tik_size < SIGNED_TIK_MIN_SIZE || tik_size > SIGNED_TIK_MAX_SIZE) { - LOGFILE("Invalid size for \"%s\"! (0x%lX)", tik_filename, tik_size); + LOGFILE("Invalid size for \"%s\"! (0x%lX).", tik_filename, tik_size); return false; } @@ -545,7 +546,7 @@ static bool tikRetrieveEticketDeviceKey(void) aes128CtrCrypt(&eticket_aes_ctx, &(eticket_devkey->exponent), &(eticket_devkey->exponent), sizeof(tikEticketDeviceKeyData) - 0x10); /* Public exponent value must be 0x10001. */ - /* It is stored use big endian byte order. */ + /* It is stored using big endian byte order. */ public_exponent = __builtin_bswap32(eticket_devkey->public_exponent); if (public_exponent != ETICKET_DEVKEY_PUBLIC_EXPONENT) { diff --git a/source/usb.c b/source/usb.c index 6ad707e..2cca612 100644 --- a/source/usb.c +++ b/source/usb.c @@ -3,7 +3,7 @@ * * Heavily based in usb_comms from libnx. * - * Copyright (c) 2018-2020, Switchbrew, libnx contributors. + * Copyright (c) 2018-2020, Switchbrew and libnx contributors. * Copyright (c) 2020, DarkMatterCore . * * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). @@ -26,10 +26,10 @@ #define USB_ABI_VERSION 1 -#define USB_CMD_HEADER_MAGIC 0x4E584454 /* "NXDT" */ +#define USB_CMD_HEADER_MAGIC 0x4E584454 /* "NXDT". */ -#define USB_TRANSFER_ALIGNMENT 0x1000 /* 4 KiB */ -#define USB_TRANSFER_TIMEOUT 5 /* 5 seconds */ +#define USB_TRANSFER_ALIGNMENT 0x1000 /* 4 KiB. */ +#define USB_TRANSFER_TIMEOUT 5 /* 5 seconds. */ /* Type definitions. */ @@ -144,21 +144,21 @@ bool usbInitialize(void) rwlockWriteLock(&g_usbDeviceLock); - /* Allocate USB transfer buffer */ + /* Allocate USB transfer buffer. */ if (!usbAllocateTransferBuffer()) { LOGFILE("Failed to allocate memory for the USB transfer buffer!"); goto exit; } - /* Initialize USB device interface */ + /* Initialize USB device interface. */ if (!usbInitializeComms()) { LOGFILE("Failed to initialize USB device interface!"); goto exit; } - /* Retrieve USB state change kernel event */ + /* Retrieve USB state change kernel event. */ g_usbStateChangeEvent = usbDsGetStateChangeEvent(); if (!g_usbStateChangeEvent) { @@ -166,13 +166,13 @@ bool usbInitialize(void) goto exit; } - /* Create usermode exit event */ + /* Create usermode exit event. */ ueventCreate(&g_usbDetectionThreadExitEvent, true); - /* Create usermode USB timeout event */ + /* Create usermode USB timeout event. */ ueventCreate(&g_usbTimeoutEvent, true); - /* Create USB detection thread */ + /* Create USB detection thread. */ if (!(g_usbDetectionThreadCreated = usbCreateDetectionThread())) goto exit; ret = true; @@ -187,20 +187,20 @@ void usbExit(void) { rwlockWriteLock(&g_usbDeviceLock); - /* Destroy USB detection thread */ + /* Destroy USB detection thread. */ if (g_usbDetectionThreadCreated) { usbDestroyDetectionThread(); g_usbDetectionThreadCreated = false; } - /* Clear USB state change kernel event */ + /* Clear USB state change kernel event. */ g_usbStateChangeEvent = NULL; - /* Close USB device interface */ + /* Close USB device interface. */ usbCloseComms(); - /* Free USB transfer buffer */ + /* Free USB transfer buffer. */ usbFreeTransferBuffer(); rwlockWriteUnlock(&g_usbDeviceLock); @@ -283,7 +283,7 @@ bool usbSendFileData(void *data, u64 data_size) goto exit; } - /* Optimization for buffers that already are page aligned */ + /* Optimization for buffers that already are page aligned. */ if (IS_ALIGNED((u64)data, USB_TRANSFER_ALIGNMENT)) { buf = data; @@ -301,7 +301,7 @@ bool usbSendFileData(void *data, u64 data_size) ret = true; g_usbTransferRemainingSize -= data_size; - /* Check if this is the last chunk */ + /* Check if this is the last chunk. */ if (!g_usbTransferRemainingSize) { if (!usbRead(g_usbTransferBuffer, sizeof(UsbStatus))) @@ -349,7 +349,7 @@ static void usbDestroyDetectionThread(void) /* Signal the exit event to terminate the USB detection thread */ ueventSignal(&g_usbDetectionThreadExitEvent); - /* Wait for the USB detection thread to exit */ + /* Wait for the USB detection thread to exit. */ thrd_join(g_usbDetectionThread, NULL); } @@ -366,31 +366,31 @@ static int usbDetectionThreadFunc(void *arg) while(true) { - /* Wait until an event is triggered */ + /* Wait until an event is triggered. */ rc = waitMulti(&idx, -1, usb_change_event_waiter, usb_timeout_event_waiter, exit_event_waiter); if (R_FAILED(rc)) continue; - /* Exit event triggered */ + /* Exit event triggered. */ if (idx == 2) break; rwlockWriteLock(&g_usbDeviceLock); rwlockWriteLock(&(g_usbDeviceInterface.lock)); /* Retrieve current USB connection status */ - /* Only proceed if we're dealing with a status change */ + /* Only proceed if we're dealing with a status change. */ g_usbHostAvailable = usbIsHostAvailable(); g_usbSessionStarted = false; g_usbTransferRemainingSize = 0; - /* Start a USB session if we're connected to a host device and if the status change event was triggered */ - /* This will essentially hang this thread and all other threads that call USB-related functions until a session is established */ + /* Start a USB session if we're connected to a host device and if the status change event was triggered. */ + /* This will essentially hang this thread and all other threads that call USB-related functions until a session is established. */ if (g_usbHostAvailable && idx == 1) g_usbSessionStarted = usbStartSession(); rwlockWriteUnlock(&(g_usbDeviceInterface.lock)); rwlockWriteUnlock(&g_usbDeviceLock); } - /* Close USB session if needed */ + /* Close USB session if needed. */ if (g_usbHostAvailable && g_usbSessionStarted) usbEndSession(); g_usbHostAvailable = g_usbSessionStarted = false; g_usbTransferRemainingSize = 0; @@ -539,7 +539,7 @@ static bool usbInitializeComms(void) rc = usbDsInitialize(); if (R_FAILED(rc)) { - LOGFILE("usbDsInitialize failed! (0x%08X)", rc); + LOGFILE("usbDsInitialize failed! (0x%08X).", rc); goto exit; } @@ -548,32 +548,32 @@ static bool usbInitializeComms(void) u8 manufacturer = 0, product = 0, serial_number = 0; static const u16 supported_langs[1] = { 0x0409 }; - /* Send language descriptor */ + /* Send language descriptor. */ rc = usbDsAddUsbLanguageStringDescriptor(NULL, supported_langs, sizeof(supported_langs) / sizeof(u16)); - if (R_FAILED(rc)) LOGFILE("usbDsAddUsbLanguageStringDescriptor failed! (0x%08X)", rc); + if (R_FAILED(rc)) LOGFILE("usbDsAddUsbLanguageStringDescriptor failed! (0x%08X).", rc); - /* Send manufacturer */ + /* Send manufacturer. */ if (R_SUCCEEDED(rc)) { rc = usbDsAddUsbStringDescriptor(&manufacturer, APP_AUTHOR); - if (R_FAILED(rc)) LOGFILE("usbDsAddUsbStringDescriptor failed! (0x%08X) (manufacturer)", rc); + if (R_FAILED(rc)) LOGFILE("usbDsAddUsbStringDescriptor failed! (0x%08X) (manufacturer).", rc); } - /* Send product */ + /* Send product. */ if (R_SUCCEEDED(rc)) { rc = usbDsAddUsbStringDescriptor(&product, APP_TITLE); - if (R_FAILED(rc)) LOGFILE("usbDsAddUsbStringDescriptor failed! (0x%08X) (product)", rc); + if (R_FAILED(rc)) LOGFILE("usbDsAddUsbStringDescriptor failed! (0x%08X) (product).", rc); } - /* Send serial number */ + /* Send serial number. */ if (R_SUCCEEDED(rc)) { rc = usbDsAddUsbStringDescriptor(&serial_number, APP_VERSION); - if (R_FAILED(rc)) LOGFILE("usbDsAddUsbStringDescriptor failed! (0x%08X) (serial number)", rc); + if (R_FAILED(rc)) LOGFILE("usbDsAddUsbStringDescriptor failed! (0x%08X) (serial number).", rc); } - /* Send device descriptors */ + /* Send device descriptors. */ struct usb_device_descriptor device_descriptor = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, @@ -591,56 +591,56 @@ static bool usbInitializeComms(void) .bNumConfigurations = 0x01 }; - /* Full Speed is USB 1.1 */ + /* Full Speed is USB 1.1. */ if (R_SUCCEEDED(rc)) { rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_Full, &device_descriptor); - if (R_FAILED(rc)) LOGFILE("usbDsSetUsbDeviceDescriptor failed! (0x%08X) (USB 1.1)", rc); + if (R_FAILED(rc)) LOGFILE("usbDsSetUsbDeviceDescriptor failed! (0x%08X) (USB 1.1).", rc); } - /* High Speed is USB 2.0 */ + /* High Speed is USB 2.0. */ device_descriptor.bcdUSB = 0x0200; if (R_SUCCEEDED(rc)) { rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_High, &device_descriptor); - if (R_FAILED(rc)) LOGFILE("usbDsSetUsbDeviceDescriptor failed! (0x%08X) (USB 2.0)", rc); + if (R_FAILED(rc)) LOGFILE("usbDsSetUsbDeviceDescriptor failed! (0x%08X) (USB 2.0).", rc); } - /* Super Speed is USB 3.0 */ - /* Upgrade packet size to 512 */ + /* Super Speed is USB 3.0. */ + /* Upgrade packet size to 512. */ device_descriptor.bcdUSB = 0x0300; device_descriptor.bMaxPacketSize0 = 0x09; if (R_SUCCEEDED(rc)) { rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_Super, &device_descriptor); - if (R_FAILED(rc)) LOGFILE("usbDsSetUsbDeviceDescriptor failed! (0x%08X) (USB 3.0)", rc); + if (R_FAILED(rc)) LOGFILE("usbDsSetUsbDeviceDescriptor failed! (0x%08X) (USB 3.0).", rc); } - /* Define Binary Object Store */ + /* Define Binary Object Store. */ u8 bos[0x16] = { - /* USB 1.1 */ - 0x05, /* .bLength */ - USB_DT_BOS, /* .bDescriptorType */ - 0x16, 0x00, /* .wTotalLength */ - 0x02, /* .bNumDeviceCaps */ + /* USB 1.1. */ + 0x05, /* bLength. */ + USB_DT_BOS, /* bDescriptorType. */ + 0x16, 0x00, /* wTotalLength. */ + 0x02, /* bNumDeviceCaps. */ - /* USB 2.0 */ - 0x07, /* .bLength */ - USB_DT_DEVICE_CAPABILITY, /* .bDescriptorType */ - 0x02, /* .bDevCapabilityType */ - 0x02, 0x00, 0x00, 0x00, /* dev_capability_data */ + /* USB 2.0. */ + 0x07, /* bLength. */ + USB_DT_DEVICE_CAPABILITY, /* bDescriptorType. */ + 0x02, /* bDevCapabilityType. */ + 0x02, 0x00, 0x00, 0x00, /* dev_capability_data. */ - /* USB 3.0 */ - 0x0A, /* .bLength */ - USB_DT_DEVICE_CAPABILITY, /* .bDescriptorType */ - 0x03, /* .bDevCapabilityType */ + /* USB 3.0. */ + 0x0A, /* bLength. */ + USB_DT_DEVICE_CAPABILITY, /* bDescriptorType. */ + 0x03, /* bDevCapabilityType. */ 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00 }; if (R_SUCCEEDED(rc)) { rc = usbDsSetBinaryObjectStore(bos, sizeof(bos)); - if (R_FAILED(rc)) LOGFILE("usbDsSetBinaryObjectStore failed! (0x%08X)", rc); + if (R_FAILED(rc)) LOGFILE("usbDsSetBinaryObjectStore failed! (0x%08X).", rc); } } else { static const UsbDsDeviceInfo device_info = { @@ -652,14 +652,14 @@ static bool usbInitializeComms(void) .SerialNumber = APP_VERSION }; - /* Set VID, PID and BCD */ + /* Set VID, PID and BCD. */ rc = usbDsSetVidPidBcd(&device_info); - if (R_FAILED(rc)) LOGFILE("usbDsSetVidPidBcd failed! (0x%08X)", rc); + if (R_FAILED(rc)) LOGFILE("usbDsSetVidPidBcd failed! (0x%08X).", rc); } if (R_FAILED(rc)) goto exit; - /* Initialize USB device interface */ + /* Initialize USB device interface. */ rwlockWriteLock(&(g_usbDeviceInterface.lock)); rwlockWriteLock(&(g_usbDeviceInterface.lock_in)); rwlockWriteLock(&(g_usbDeviceInterface.lock_out)); @@ -681,7 +681,7 @@ static bool usbInitializeComms(void) rc = usbDsEnable(); if (R_FAILED(rc)) { - LOGFILE("usbDsEnable failed! (0x%08X)", rc); + LOGFILE("usbDsEnable failed! (0x%08X).", rc); goto exit; } } @@ -768,14 +768,14 @@ static bool usbInitializeDeviceInterface5x(void) .wBytesPerInterval = 0x00, }; - /* Enable device interface */ + /* Enable device interface. */ g_usbDeviceInterface.initialized = true; - /* Setup interface */ + /* Setup interface. */ rc = usbDsRegisterInterface(&(g_usbDeviceInterface.interface)); if (R_FAILED(rc)) { - LOGFILE("usbDsRegisterInterface failed! (0x%08X)", rc); + LOGFILE("usbDsRegisterInterface failed! (0x%08X).", rc); return false; } @@ -783,111 +783,111 @@ static bool usbInitializeDeviceInterface5x(void) endpoint_descriptor_in.bEndpointAddress += (interface_descriptor.bInterfaceNumber + 1); endpoint_descriptor_out.bEndpointAddress += (interface_descriptor.bInterfaceNumber + 1); - /* Full Speed config (USB 1.1) */ + /* Full Speed config (USB 1.1). */ rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_Full, &interface_descriptor, USB_DT_INTERFACE_SIZE); if (R_FAILED(rc)) { - LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 1.1) (interface)", rc); + LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 1.1) (interface).", rc); return false; } rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_Full, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE); if (R_FAILED(rc)) { - LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 1.1) (in endpoint)", rc); + LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 1.1) (in endpoint).", rc); return false; } rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_Full, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE); if (R_FAILED(rc)) { - LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 1.1) (out endpoint)", rc); + LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 1.1) (out endpoint).", rc); return false; } - /* High Speed config (USB 2.0) */ + /* High Speed config (USB 2.0). */ endpoint_descriptor_in.wMaxPacketSize = 0x200; endpoint_descriptor_out.wMaxPacketSize = 0x200; rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_High, &interface_descriptor, USB_DT_INTERFACE_SIZE); if (R_FAILED(rc)) { - LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 2.0) (interface)", rc); + LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 2.0) (interface).", rc); return false; } rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_High, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE); if (R_FAILED(rc)) { - LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 2.0) (in endpoint)", rc); + LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 2.0) (in endpoint).", rc); return false; } rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_High, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE); if (R_FAILED(rc)) { - LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 2.0) (out endpoint)", rc); + LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 2.0) (out endpoint).", rc); return false; } - /* Super Speed config (USB 3.0) */ + /* Super Speed config (USB 3.0). */ endpoint_descriptor_in.wMaxPacketSize = 0x400; endpoint_descriptor_out.wMaxPacketSize = 0x400; rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_Super, &interface_descriptor, USB_DT_INTERFACE_SIZE); if (R_FAILED(rc)) { - LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 3.0) (interface)", rc); + LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 3.0) (interface).", rc); return false; } rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_Super, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE); if (R_FAILED(rc)) { - LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 3.0) (in endpoint)", rc); + LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 3.0) (in endpoint).", rc); return false; } rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_Super, &endpoint_companion, USB_DT_SS_ENDPOINT_COMPANION_SIZE); if (R_FAILED(rc)) { - LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 3.0) (in endpoint companion)", rc); + LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 3.0) (in endpoint companion).", rc); return false; } rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_Super, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE); if (R_FAILED(rc)) { - LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 3.0) (out endpoint)", rc); + LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 3.0) (out endpoint).", rc); return false; } rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_Super, &endpoint_companion, USB_DT_SS_ENDPOINT_COMPANION_SIZE); if (R_FAILED(rc)) { - LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 3.0) (out endpoint companion)", rc); + LOGFILE("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 3.0) (out endpoint companion).", rc); return false; } - /* Setup endpoints */ + /* Setup endpoints. */ rc = usbDsInterface_RegisterEndpoint(g_usbDeviceInterface.interface, &(g_usbDeviceInterface.endpoint_in), endpoint_descriptor_in.bEndpointAddress); if (R_FAILED(rc)) { - LOGFILE("usbDsInterface_RegisterEndpoint failed! (0x%08X) (in endpoint)", rc); + LOGFILE("usbDsInterface_RegisterEndpoint failed! (0x%08X) (in endpoint).", rc); return false; } rc = usbDsInterface_RegisterEndpoint(g_usbDeviceInterface.interface, &(g_usbDeviceInterface.endpoint_out), endpoint_descriptor_out.bEndpointAddress); if (R_FAILED(rc)) { - LOGFILE("usbDsInterface_RegisterEndpoint failed! (0x%08X) (out endpoint)", rc); + LOGFILE("usbDsInterface_RegisterEndpoint failed! (0x%08X) (out endpoint).", rc); return false; } rc = usbDsInterface_EnableInterface(g_usbDeviceInterface.interface); if (R_FAILED(rc)) { - LOGFILE("usbDsInterface_EnableInterface failed! (0x%08X)", rc); + LOGFILE("usbDsInterface_EnableInterface failed! (0x%08X).", rc); return false; } @@ -923,36 +923,36 @@ static bool usbInitializeDeviceInterface1x(void) .wMaxPacketSize = 0x200, }; - /* Enable device interface */ + /* Enable device interface. */ g_usbDeviceInterface.initialized = true; - /* Setup interface */ + /* Setup interface. */ rc = usbDsGetDsInterface(&(g_usbDeviceInterface.interface), &interface_descriptor, "usb"); if (R_FAILED(rc)) { - LOGFILE("usbDsGetDsInterface failed! (0x%08X)", rc); + LOGFILE("usbDsGetDsInterface failed! (0x%08X).", rc); return false; } - /* Setup endpoints */ + /* Setup endpoints. */ rc = usbDsInterface_GetDsEndpoint(g_usbDeviceInterface.interface, &(g_usbDeviceInterface.endpoint_in), &endpoint_descriptor_in); if (R_FAILED(rc)) { - LOGFILE("usbDsInterface_GetDsEndpoint failed! (0x%08X) (in endpoint)", rc); + LOGFILE("usbDsInterface_GetDsEndpoint failed! (0x%08X) (in endpoint).", rc); return false; } rc = usbDsInterface_GetDsEndpoint(g_usbDeviceInterface.interface, &(g_usbDeviceInterface.endpoint_out), &endpoint_descriptor_out); if (R_FAILED(rc)) { - LOGFILE("usbDsInterface_GetDsEndpoint failed! (0x%08X) (out endpoint)", rc); + LOGFILE("usbDsInterface_GetDsEndpoint failed! (0x%08X) (out endpoint).", rc); return false; } rc = usbDsInterface_EnableInterface(g_usbDeviceInterface.interface); if (R_FAILED(rc)) { - LOGFILE("usbDsInterface_EnableInterface failed! (0x%08X)", rc); + LOGFILE("usbDsInterface_EnableInterface failed! (0x%08X).", rc); return false; } @@ -1002,48 +1002,48 @@ static bool usbTransferData(void *buf, u64 size, UsbDsEndpoint *endpoint) UsbDsReportData report_data = {0}; u32 transferred_size = 0; - /* Start an USB transfer using the provided endpoint */ + /* Start an USB transfer using the provided endpoint. */ rc = usbDsEndpoint_PostBufferAsync(endpoint, buf, size, &urb_id); if (R_FAILED(rc)) { - LOGFILE("usbDsEndpoint_PostBufferAsync failed! (0x%08X)", rc); + LOGFILE("usbDsEndpoint_PostBufferAsync failed! (0x%08X).", rc); return false; } - /* Wait for the transfer to finish */ - /* If we're starting an USB transfer session, use an infinite timeout value to let the user start the companion app */ + /* Wait for the transfer to finish. */ + /* If we're starting an USB transfer session, use an infinite timeout value to let the user start the companion app. */ u64 timeout = (g_usbSessionStarted ? (USB_TRANSFER_TIMEOUT * (u64)1000000000) : UINT64_MAX); rc = eventWait(&(endpoint->CompletionEvent), timeout); eventClear(&(endpoint->CompletionEvent)); if (R_FAILED(rc)) { - /* Cancel transfer */ + /* Cancel transfer. */ usbDsEndpoint_Cancel(endpoint); - /* Safety measure: wait until the completion event is triggered again before proceeding */ + /* Safety measure: wait until the completion event is triggered again before proceeding. */ eventWait(&(endpoint->CompletionEvent), UINT64_MAX); eventClear(&(endpoint->CompletionEvent)); - /* Signal usermode USB timeout event if needed */ - /* This will "reset" the USB connection by making the background thread wait until a new session is established */ + /* Signal usermode USB timeout event if needed. */ + /* This will "reset" the USB connection by making the background thread wait until a new session is established. */ if (g_usbSessionStarted) ueventSignal(&g_usbTimeoutEvent); - LOGFILE("eventWait failed! (0x%08X)", rc); + LOGFILE("eventWait failed! (0x%08X).", rc); return false; } rc = usbDsEndpoint_GetReportData(endpoint, &report_data); if (R_FAILED(rc)) { - LOGFILE("usbDsEndpoint_GetReportData failed! (0x%08X)", rc); + LOGFILE("usbDsEndpoint_GetReportData failed! (0x%08X).", rc); return false; } rc = usbDsParseReportData(&report_data, urb_id, NULL, &transferred_size); if (R_FAILED(rc)) { - LOGFILE("usbDsParseReportData failed! (0x%08X)", rc); + LOGFILE("usbDsParseReportData failed! (0x%08X).", rc); return false; } diff --git a/source/usb.h b/source/usb.h index bbe7ce1..3ab5f7e 100644 --- a/source/usb.h +++ b/source/usb.h @@ -3,7 +3,7 @@ * * Heavily based in usb_comms from libnx. * - * Copyright (c) 2018-2020, Switchbrew, libnx contributors. + * Copyright (c) 2018-2020, Switchbrew and libnx contributors. * Copyright (c) 2020, DarkMatterCore . * * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). @@ -26,7 +26,7 @@ #ifndef __USB_H__ #define __USB_H__ -#define USB_TRANSFER_BUFFER_SIZE 0x800000 /* 8 MiB */ +#define USB_TRANSFER_BUFFER_SIZE 0x800000 /* 8 MiB. */ /// Initializes the USB interface, input and output endpoints and allocates an internal transfer buffer. bool usbInitialize(void); diff --git a/source/utils.c b/source/utils.c index d3c0dfc..96074e2 100644 --- a/source/utils.c +++ b/source/utils.c @@ -1,6 +1,7 @@ /* * utils.c * + * Copyright (c) 2018-2020, WerWolv. * Copyright (c) 2020, DarkMatterCore . * * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). @@ -50,6 +51,9 @@ static Mutex g_logfileMutex = 0; /* Function prototypes. */ +static u64 utilsHidKeysAllDown(void); +static u64 utilsHidKeysAllHeld(void); + static bool utilsMountEmmcBisSystemPartitionStorage(void); static void utilsUnmountEmmcBisSystemPartitionStorage(void); @@ -64,70 +68,70 @@ bool utilsInitializeResources(void) bool ret = g_resourcesInitialized; if (ret) goto exit; - /* Initialize needed services */ + /* Initialize needed services. */ if (!servicesInitialize()) { LOGFILE("Failed to initialize needed services!"); goto exit; } - /* Initialize USB interface */ + /* Initialize USB interface. */ if (!usbInitialize()) { LOGFILE("Failed to initialize USB interface!"); goto exit; } - /* Load NCA keyset */ + /* Load NCA keyset. */ if (!keysLoadNcaKeyset()) { LOGFILE("Failed to load NCA keyset!"); goto exit; } - /* Allocate NCA crypto buffer */ + /* Allocate NCA crypto buffer. */ if (!ncaAllocateCryptoBuffer()) { LOGFILE("Unable to allocate memory for NCA crypto buffer!"); goto exit; } - /* Initialize gamecard interface */ + /* Initialize gamecard interface. */ if (!gamecardInitialize()) { LOGFILE("Failed to initialize gamecard interface!"); goto exit; } - /* Retrieve SD card FsFileSystem */ + /* Retrieve SD card FsFileSystem element. */ if (!(g_sdCardFileSystem = fsdevGetDeviceFileSystem("sdmc:"))) { LOGFILE("fsdevGetDeviceFileSystem failed!"); goto exit; } - /* Mount eMMC BIS System partition */ + /* Mount eMMC BIS System partition. */ if (!utilsMountEmmcBisSystemPartitionStorage()) goto exit; - /* Get applet type */ + /* Get applet type. */ g_programAppletType = appletGetAppletType(); - /* Disable screen dimming and auto sleep */ + /* Disable screen dimming and auto sleep. */ appletSetMediaPlaybackState(true); - /* Retrieve custom firmware type */ + /* Retrieve custom firmware type. */ _utilsGetCustomFirmwareType(); - /* Overclock system */ + /* Overclock system. */ utilsOverclockSystem(true); - /* Setup an applet hook to change the hardware clocks after a system mode change (docked <-> undocked) */ + /* Setup an applet hook to change the hardware clocks after a system mode change (docked <-> undocked). */ appletHook(&g_systemOverclockCookie, utilsOverclockSystemAppletHook, NULL); - /* Initialize FreeType */ + /* Initialize FreeType. */ //if (!freeTypeHelperInitialize()) return false; - /* Initialize LVGL */ + /* Initialize LVGL. */ //if (!lvglHelperInitialize()) return false; ret = g_resourcesInitialized = true; @@ -142,37 +146,37 @@ void utilsCloseResources(void) { mutexLock(&g_resourcesMutex); - /* Free LVGL resources */ + /* Free LVGL resources. */ //lvglHelperExit(); - /* Free FreeType resouces */ + /* Free FreeType resources. */ //freeTypeHelperExit(); - /* Unset our overclock applet hook */ + /* Unset our overclock applet hook. */ appletUnhook(&g_systemOverclockCookie); - /* Restore hardware clocks */ + /* Restore hardware clocks. */ utilsOverclockSystem(false); - /* Enable screen dimming and auto sleep */ + /* Enable screen dimming and auto sleep. */ appletSetMediaPlaybackState(false); - /* Unblock HOME button presses */ + /* Unblock HOME button presses. */ utilsChangeHomeButtonBlockStatus(false); - /* Unmount eMMC BIS System partition */ + /* Unmount eMMC BIS System partition. */ utilsUnmountEmmcBisSystemPartitionStorage(); - /* Deinitialize gamecard interface */ + /* Deinitialize gamecard interface. */ gamecardExit(); - /* Free NCA crypto buffer */ + /* Free NCA crypto buffer. */ ncaFreeCryptoBuffer(); - /* Close USB interface */ + /* Close USB interface. */ usbExit(); - /* Close initialized services */ + /* Close initialized services. */ servicesClose(); g_resourcesInitialized = false; @@ -180,37 +184,28 @@ void utilsCloseResources(void) mutexUnlock(&g_resourcesMutex); } -u64 utilsHidKeysAllDown(void) +u64 utilsReadInput(u8 input_type) +{ + if (input_type != UtilsInputType_Down && input_type != UtilsInputType_Held) return 0; + + hidScanInput(); + + return (input_type == UtilsInputType_Down ? utilsHidKeysAllDown() : utilsHidKeysAllHeld()); +} + +void utilsWaitForButtonPress(u64 flag) { - u8 controller; u64 keys_down = 0; - for(controller = 0; controller < (u8)CONTROLLER_P1_AUTO; controller++) keys_down |= hidKeysDown((HidControllerID)controller); - - return keys_down; -} - -u64 utilsHidKeysAllHeld(void) -{ - u8 controller; - u64 keys_held = 0; - - for(controller = 0; controller < (u8)CONTROLLER_P1_AUTO; controller++) keys_held |= hidKeysHeld((HidControllerID)controller); - - return keys_held; -} - -void utilsWaitForButtonPress(void) -{ - u64 flag, keys_down; - - /* Don't consider touch screen presses nor stick movement as button inputs */ - flag = ~(KEY_TOUCH | KEY_LSTICK_LEFT | KEY_LSTICK_RIGHT | KEY_LSTICK_UP | KEY_LSTICK_DOWN | KEY_RSTICK_LEFT | KEY_RSTICK_RIGHT | KEY_RSTICK_UP | KEY_RSTICK_DOWN); + if (!flag) + { + /* Don't consider touch screen presses nor stick movement as button inputs. */ + flag = ~(KEY_TOUCH | KEY_LSTICK_LEFT | KEY_LSTICK_RIGHT | KEY_LSTICK_UP | KEY_LSTICK_DOWN | KEY_RSTICK_LEFT | KEY_RSTICK_RIGHT | KEY_RSTICK_UP | KEY_RSTICK_DOWN); + } while(appletMainLoop()) { - hidScanInput(); - keys_down = utilsHidKeysAllDown(); + keys_down = utilsReadInput(UtilsInputType_Down); if (keys_down & flag) break; } } @@ -310,7 +305,7 @@ bool utilsGetFreeFileSystemSpace(FsFileSystem *fs, u64 *out) Result rc = fsFsGetFreeSpace(fs, "/", (s64*)out); if (R_FAILED(rc)) { - LOGFILE("fsFsGetFreeSpace failed! (0x%08X)", rc); + LOGFILE("fsFsGetFreeSpace failed! (0x%08X).", rc); return false; } @@ -341,16 +336,16 @@ bool utilsCreateConcatenationFile(const char *path) return false; } - /* Safety check: remove any existant file/directory at the destination path */ + /* Safety check: remove any existant file/directory at the destination path. */ remove(path); fsdevDeleteDirectoryRecursively(path); /* Create ConcatenationFile */ - /* If the call succeeds, the caller function will be able to operate on this file using stdio calls */ + /* If the call succeeds, the caller function will be able to operate on this file using stdio calls. */ rc = fsdevCreateFile(path, 0, FsCreateOption_BigFile); if (R_FAILED(rc)) { - LOGFILE("fsdevCreateFile failed for \"%s\"! (0x%08X)", path, rc); + LOGFILE("fsdevCreateFile failed for \"%s\"! (0x%08X).", path, rc); return false; } @@ -366,7 +361,7 @@ void utilsChangeHomeButtonBlockStatus(bool block) { mutexLock(&g_homeButtonMutex); - /* Only change HOME button blocking status if we're running as a regular application or a system application, and if it's current blocking status is different than the requested one */ + /* Only change HOME button blocking status if we're running as a regular application or a system application, and if it's current blocking status is different than the requested one. */ if (!utilsAppletModeCheck() && block != g_homeButtonBlocked) { if (block) @@ -399,6 +394,26 @@ void utilsOverclockSystem(bool overclock) servicesChangeHardwareClockRates(cpuClkRate, memClkRate); } +static u64 utilsHidKeysAllDown(void) +{ + u8 controller; + u64 keys_down = 0; + + for(controller = 0; controller < (u8)CONTROLLER_P1_AUTO; controller++) keys_down |= hidKeysDown((HidControllerID)controller); + + return keys_down; +} + +static u64 utilsHidKeysAllHeld(void) +{ + u8 controller; + u64 keys_held = 0; + + for(controller = 0; controller < (u8)CONTROLLER_P1_AUTO; controller++) keys_held |= hidKeysHeld((HidControllerID)controller); + + return keys_held; +} + static bool utilsMountEmmcBisSystemPartitionStorage(void) { Result rc = 0; @@ -407,7 +422,7 @@ static bool utilsMountEmmcBisSystemPartitionStorage(void) rc = fsOpenBisStorage(&g_emmcBisSystemPartitionStorage, FsBisPartitionId_System); if (R_FAILED(rc)) { - LOGFILE("Failed to open eMMC BIS System partition storage! (0x%08X)", rc); + LOGFILE("Failed to open eMMC BIS System partition storage! (0x%08X).", rc); return false; } @@ -421,7 +436,7 @@ static bool utilsMountEmmcBisSystemPartitionStorage(void) fr = f_mount(g_emmcBisSystemPartitionFatFsObj, BIS_SYSTEM_PARTITION_MOUNT_NAME, 1); if (fr != FR_OK) { - LOGFILE("Failed to mount eMMC BIS System partition! (%u)", fr); + LOGFILE("Failed to mount eMMC BIS System partition! (%u).", fr); return false; } @@ -457,6 +472,6 @@ static void utilsOverclockSystemAppletHook(AppletHookType hook, void *param) if (hook != AppletHookType_OnOperationMode && hook != AppletHookType_OnPerformanceMode) return; - /* To do: read config here to actually know the value to use with utilsOverclockSystem */ + /* To do: read config here to actually know the value to use with utilsOverclockSystem. */ utilsOverclockSystem(false); } diff --git a/source/utils.h b/source/utils.h index 8fc059e..eb08efa 100644 --- a/source/utils.h +++ b/source/utils.h @@ -51,13 +51,12 @@ #define BIS_SYSTEM_PARTITION_MOUNT_NAME "sys:" - +#define KEY_NONE 0 /// Need to move this to npdm.c/h eventually. -#define NPDM_META_MAGIC 0x4D455441 /* "META" */ - +#define NPDM_META_MAGIC 0x4D455441 /* "META". */ @@ -65,6 +64,10 @@ +typedef enum { + UtilsInputType_Down = 0, + UtilsInputType_Held = 1 +} UtilsInputType; typedef enum { UtilsCustomFirmwareType_Unknown = 0, @@ -76,10 +79,8 @@ typedef enum { bool utilsInitializeResources(void); void utilsCloseResources(void); -u64 utilsHidKeysAllDown(void); -u64 utilsHidKeysAllHeld(void); - -void utilsWaitForButtonPress(void); +u64 utilsReadInput(u8 input_type); +void utilsWaitForButtonPress(u64 flag); void utilsWriteLogMessage(const char *func_name, const char *fmt, ...);