Some more changes.

* Codestyle fixes.
* NCA contexts for NCAs with titlekey crypto will now be generated even if the ticket can't be retrieved, in order to be able to use ncaReadContentFile() with them.
* Moved aes128XtsNintendoCrypt() out of nca.c.
This commit is contained in:
Pablo Curiel 2020-07-05 20:10:07 -04:00
parent 99429fd7b4
commit b71f0d7b87
36 changed files with 849 additions and 745 deletions

View file

@ -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);

View file

@ -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

46
source/aes.c Normal file
View file

@ -0,0 +1,46 @@
/*
* aes.c
*
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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;
}

31
source/aes.h Normal file
View file

@ -0,0 +1,31 @@
/*
* aes.h
*
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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__ */

View file

@ -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);
}

View file

@ -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__ */

View file

@ -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, "-");

View file

@ -21,51 +21,42 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* 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);
}

33
source/crc32_fast.h Normal file
View file

@ -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 <pabloacurielz@gmail.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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__ */

View file

@ -1,6 +1,7 @@
/*
* es.c
*
* Copyright (c) 2018-2020, Addubz.
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).

View file

@ -1,6 +1,7 @@
/*
* es.h
*
* Copyright (c) 2018-2020, Addubz.
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).

View file

@ -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 {

View file

@ -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);

View file

@ -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 },

View file

@ -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.

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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();

View file

@ -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

View file

@ -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().

View file

@ -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 || \

View file

@ -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__ */

View file

@ -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), \

View file

@ -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);

View file

@ -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__ */

View file

@ -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;
}

View file

@ -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;

View file

@ -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 <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).

View file

@ -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");
}

View file

@ -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__ */

View file

@ -1,6 +1,7 @@
/*
* tik.c
*
* Copyright (c) 2019, shchmue.
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* 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)
{

View file

@ -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 <pabloacurielz@gmail.com>.
*
* 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;
}

View file

@ -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 <pabloacurielz@gmail.com>.
*
* 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);

View file

@ -1,6 +1,7 @@
/*
* utils.c
*
* Copyright (c) 2018-2020, WerWolv.
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* 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);
}

View file

@ -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, ...);