mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2025-01-09 11:07:23 -03:00
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:
parent
99429fd7b4
commit
b71f0d7b87
36 changed files with 849 additions and 745 deletions
29
README.md
29
README.md
|
@ -3,16 +3,37 @@ todo:
|
|||
|
||||
hfs0: filelist generation methods
|
||||
|
||||
nca: continue reencryption methods
|
||||
tik: automatically dump tickets to the SD card?
|
||||
tik: use dumped tickets when the original ones can't be found in the ES savefile?
|
||||
|
||||
nca: function to write encrypted nca headers / nca fs headers (don't forget nca0 please)
|
||||
|
||||
pfs0: filelist generation methods
|
||||
pfs0: full header aligned to 0x20 (nsp)
|
||||
pfs0: patch writing function
|
||||
pfs0: function to write patches
|
||||
|
||||
romfs: filelist generation methods
|
||||
romfs: patch writing function
|
||||
romfs: function to write patches
|
||||
|
||||
bktr: filelist generation methods (wrappers for romfs functions)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
char content_info_path[FS_MAX_PATH] = {0};
|
||||
sprintf(content_info_path, "sdmc:/%016lX.bin", xml_program_info.title_id);
|
||||
|
||||
FILE *content_info = fopen(content_info_path, "wb");
|
||||
if (content_info)
|
||||
{
|
||||
fwrite(titleContentInfos, 1, titleContentInfoCnt * sizeof(NcmContentInfo), content_info);
|
||||
fclose(content_info);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bktr: filelist generation methods
|
||||
|
||||
Result txIsFat32(bool *mode) {
|
||||
Result rc = serviceDispatch(&g_tx, 137);
|
||||
|
|
|
@ -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
46
source/aes.c
Normal 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
31
source/aes.h
Normal 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__ */
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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, "-");
|
||||
|
|
|
@ -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
33
source/crc32_fast.h
Normal 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__ */
|
|
@ -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).
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
295
source/nca.c
295
source/nca.c
|
@ -21,10 +21,11 @@
|
|||
#include "utils.h"
|
||||
#include "nca.h"
|
||||
#include "keys.h"
|
||||
#include "aes.h"
|
||||
#include "rsa.h"
|
||||
#include "gamecard.h"
|
||||
|
||||
#define NCA_CRYPTO_BUFFER_SIZE 0x800000 /* 8 MiB */
|
||||
#define NCA_CRYPTO_BUFFER_SIZE 0x800000 /* 8 MiB. */
|
||||
|
||||
/* Global variables. */
|
||||
|
||||
|
@ -38,8 +39,6 @@ static const u8 g_nca0KeyAreaHash[SHA256_HASH_SIZE] = {
|
|||
|
||||
/* Function prototypes. */
|
||||
|
||||
static size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src, size_t size, u64 sector, size_t sector_size, bool encrypt); /* Not used anywhere else */
|
||||
|
||||
static bool ncaDecryptHeader(NcaContext *ctx);
|
||||
static bool ncaDecryptKeyArea(NcaContext *ctx);
|
||||
|
||||
|
@ -88,7 +87,7 @@ bool ncaEncryptKeyArea(NcaContext *ctx)
|
|||
const u8 *kaek = NULL;
|
||||
Aes128Context key_area_ctx = {0};
|
||||
|
||||
/* Check if we're dealing with a NCA0 with a plain text key area */
|
||||
/* Check if we're dealing with a NCA0 with a plain text key area. */
|
||||
if (ctx->format_version == NcaVersion_Nca0 && !ncaCheckIfVersion0KeyAreaIsEncrypted(ctx))
|
||||
{
|
||||
memcpy(ctx->header.encrypted_keys, ctx->decrypted_keys, 0x40);
|
||||
|
@ -161,7 +160,7 @@ bool ncaEncryptHeader(NcaContext *ctx)
|
|||
|
||||
break;
|
||||
case NcaVersion_Nca0:
|
||||
/* NCA0 FS section headers will be encrypted in-place, but they need to be written to their proper offsets */
|
||||
/* NCA0 FS section headers will be encrypted in-place, but they need to be written to their proper offsets. */
|
||||
aes128XtsContextCreate(&nca0_fs_header_ctx, ctx->decrypted_keys[0].key, ctx->decrypted_keys[1].key, true);
|
||||
|
||||
for(i = 0; i < NCA_FS_HEADER_COUNT; i++)
|
||||
|
@ -197,7 +196,10 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Fill NCA context */
|
||||
/* Clear output NCA context. */
|
||||
memset(out, 0, sizeof(NcaContext));
|
||||
|
||||
/* Fill NCA context. */
|
||||
out->storage_id = storage_id;
|
||||
out->ncm_storage = (out->storage_id != NcmStorageId_GameCard ? ncm_storage : NULL);
|
||||
|
||||
|
@ -214,11 +216,9 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm
|
|||
return false;
|
||||
}
|
||||
|
||||
out->rights_id_available = out->dirty_header = false;
|
||||
|
||||
if (out->storage_id == NcmStorageId_GameCard)
|
||||
{
|
||||
/* Retrieve gamecard NCA offset */
|
||||
/* Retrieve gamecard NCA offset. */
|
||||
char nca_filename[0x30] = {0};
|
||||
sprintf(nca_filename, "%s.%s", out->content_id_str, out->content_type == NcmContentType_Meta ? "cnmt.nca" : "nca");
|
||||
|
||||
|
@ -229,14 +229,14 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm
|
|||
}
|
||||
}
|
||||
|
||||
/* Read NCA header */
|
||||
/* Read NCA header. */
|
||||
if (!ncaReadContentFile(out, &(out->header), sizeof(NcaHeader), 0))
|
||||
{
|
||||
LOGFILE("Failed to read NCA \"%s\" header!", out->content_id_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Decrypt NCA header */
|
||||
/* Decrypt NCA header. */
|
||||
if (!ncaDecryptHeader(out))
|
||||
{
|
||||
LOGFILE("Failed to decrypt NCA \"%s\" header!", out->content_id_str);
|
||||
|
@ -249,24 +249,24 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Fill additional NCA context info */
|
||||
/* Fill additional NCA context info. */
|
||||
out->key_generation = ncaGetKeyGenerationValue(out);
|
||||
out->rights_id_available = ncaCheckRightsIdAvailability(out);
|
||||
|
||||
if (out->rights_id_available)
|
||||
{
|
||||
/* Retrieve ticket */
|
||||
/* This will return true if it has already been retrieved */
|
||||
if (!tikRetrieveTicketByRightsId(tik, &(out->header.rights_id), out->storage_id == NcmStorageId_GameCard))
|
||||
/* Retrieve ticket. */
|
||||
/* This will return true if it has already been retrieved. */
|
||||
if (tikRetrieveTicketByRightsId(tik, &(out->header.rights_id), out->storage_id == NcmStorageId_GameCard))
|
||||
{
|
||||
/* Copy decrypted titlekey. */
|
||||
memcpy(out->titlekey, tik->dec_titlekey, 0x10);
|
||||
out->titlekey_retrieved = true;
|
||||
} else {
|
||||
LOGFILE("Error retrieving ticket for NCA \"%s\"!", out->content_id_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Copy decrypted titlekey */
|
||||
memcpy(out->titlekey, tik->dec_titlekey, 0x10);
|
||||
} else {
|
||||
/* Decrypt key area */
|
||||
/* Decrypt key area. */
|
||||
if (out->format_version != NcaVersion_Nca0 && !ncaDecryptKeyArea(out))
|
||||
{
|
||||
LOGFILE("Error decrypting NCA key area!");
|
||||
|
@ -277,32 +277,28 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm
|
|||
/* Parse sections */
|
||||
for(u8 i = 0; i < NCA_FS_HEADER_COUNT; i++)
|
||||
{
|
||||
if (!out->header.fs_entries[i].enable_entry) continue;
|
||||
/* Skip NCA section if it's not enabled in the FS entries, or if the NCA uses titlekey crypto and the titlekey couldn't be retrieved. */
|
||||
if (!out->header.fs_entries[i].enable_entry || (out->rights_id_available && !out->titlekey_retrieved)) continue;
|
||||
|
||||
/* Fill section context */
|
||||
/* Fill section context. */
|
||||
out->fs_contexts[i].nca_ctx = out;
|
||||
out->fs_contexts[i].section_num = i;
|
||||
out->fs_contexts[i].section_offset = NCA_FS_ENTRY_BLOCK_OFFSET(out->header.fs_entries[i].start_block_offset);
|
||||
out->fs_contexts[i].section_size = (NCA_FS_ENTRY_BLOCK_OFFSET(out->header.fs_entries[i].end_block_offset) - out->fs_contexts[i].section_offset);
|
||||
out->fs_contexts[i].section_type = NcaFsSectionType_Invalid; /* Placeholder */
|
||||
out->fs_contexts[i].section_type = NcaFsSectionType_Invalid; /* Placeholder. */
|
||||
out->fs_contexts[i].header = &(out->header.fs_headers[i]);
|
||||
|
||||
memset(out->fs_contexts[i].ctr, 0, sizeof(out->fs_contexts[i].ctr));
|
||||
memset(&(out->fs_contexts[i].ctr_ctx), 0, sizeof(Aes128CtrContext));
|
||||
memset(&(out->fs_contexts[i].xts_decrypt_ctx), 0, sizeof(Aes128XtsContext));
|
||||
memset(&(out->fs_contexts[i].xts_encrypt_ctx), 0, sizeof(Aes128XtsContext));
|
||||
|
||||
/* Determine encryption type */
|
||||
/* Determine encryption type. */
|
||||
out->fs_contexts[i].encryption_type = (out->format_version == NcaVersion_Nca0 ? NcaEncryptionType_AesXts : out->header.fs_headers[i].encryption_type);
|
||||
if (out->fs_contexts[i].encryption_type == NcaEncryptionType_Auto)
|
||||
{
|
||||
switch(out->fs_contexts[i].section_num)
|
||||
{
|
||||
case 0: /* ExeFS Partition FS */
|
||||
case 1: /* RomFS */
|
||||
case 0: /* ExeFS Partition FS. */
|
||||
case 1: /* RomFS. */
|
||||
out->fs_contexts[i].encryption_type = NcaEncryptionType_AesCtr;
|
||||
break;
|
||||
case 2: /* Logo Partition FS */
|
||||
case 2: /* Logo Partition FS. */
|
||||
out->fs_contexts[i].encryption_type = NcaEncryptionType_None;
|
||||
break;
|
||||
default:
|
||||
|
@ -310,10 +306,14 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm
|
|||
}
|
||||
}
|
||||
|
||||
/* Check if we're dealing with an invalid encryption type value */
|
||||
if (out->fs_contexts[i].encryption_type == NcaEncryptionType_Auto || out->fs_contexts[i].encryption_type > NcaEncryptionType_AesCtrEx) continue;
|
||||
/* Check if we're dealing with an invalid encryption type value. */
|
||||
if (out->fs_contexts[i].encryption_type == NcaEncryptionType_Auto || out->fs_contexts[i].encryption_type > NcaEncryptionType_AesCtrEx)
|
||||
{
|
||||
memset(&(out->fs_contexts[i]), 0, sizeof(NcaFsSectionContext));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Determine FS section type */
|
||||
/* Determine FS section type. */
|
||||
if (out->fs_contexts[i].header->fs_type == NcaFsType_PartitionFs && out->fs_contexts[i].header->hash_type == NcaHashType_HierarchicalSha256)
|
||||
{
|
||||
out->fs_contexts[i].section_type = NcaFsSectionType_PartitionFs;
|
||||
|
@ -327,16 +327,20 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm
|
|||
out->fs_contexts[i].section_type = NcaFsSectionType_Nca0RomFs;
|
||||
}
|
||||
|
||||
/* Check if we're dealing with an invalid section type value */
|
||||
if (out->fs_contexts[i].section_type >= NcaFsSectionType_Invalid) continue;
|
||||
/* Check if we're dealing with an invalid section type value. */
|
||||
if (out->fs_contexts[i].section_type >= NcaFsSectionType_Invalid)
|
||||
{
|
||||
memset(&(out->fs_contexts[i]), 0, sizeof(NcaFsSectionContext));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Initialize crypto related fields */
|
||||
/* Initialize crypto related fields. */
|
||||
if (out->fs_contexts[i].encryption_type > NcaEncryptionType_None && out->fs_contexts[i].encryption_type <= NcaEncryptionType_AesCtrEx)
|
||||
{
|
||||
/* Initialize section CTR */
|
||||
/* Initialize section CTR. */
|
||||
ncaInitializeAesCtrIv(out->fs_contexts[i].ctr, out->fs_contexts[i].header->section_ctr, out->fs_contexts[i].section_offset);
|
||||
|
||||
/* Initialize AES context */
|
||||
/* Initialize AES context. */
|
||||
if (out->rights_id_available)
|
||||
{
|
||||
aes128CtrContextCreate(&(out->fs_contexts[i].ctr_ctx), out->titlekey, out->fs_contexts[i].ctr);
|
||||
|
@ -347,12 +351,15 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm
|
|||
} else
|
||||
if (out->fs_contexts[i].encryption_type == NcaEncryptionType_AesXts)
|
||||
{
|
||||
/* We need to create two different contexts: one for decryption and another one for encryption */
|
||||
/* We need to create two different contexts: one for decryption and another one for encryption. */
|
||||
aes128XtsContextCreate(&(out->fs_contexts[i].xts_decrypt_ctx), out->decrypted_keys[0].key, out->decrypted_keys[1].key, false);
|
||||
aes128XtsContextCreate(&(out->fs_contexts[i].xts_encrypt_ctx), out->decrypted_keys[0].key, out->decrypted_keys[1].key, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable FS context if we got up to this point. */
|
||||
out->fs_contexts[i].enabled = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -372,16 +379,16 @@ bool ncaReadContentFile(NcaContext *ctx, void *out, u64 read_size, u64 offset)
|
|||
|
||||
if (ctx->storage_id != NcmStorageId_GameCard)
|
||||
{
|
||||
/* Retrieve NCA data normally */
|
||||
/* This strips NAX0 crypto from SD card NCAs (not used on eMMC NCAs) */
|
||||
/* Retrieve NCA data normally. */
|
||||
/* This strips NAX0 crypto from SD card NCAs (not used on eMMC NCAs). */
|
||||
rc = ncmContentStorageReadContentIdFile(ctx->ncm_storage, out, read_size, &(ctx->content_id), offset);
|
||||
ret = R_SUCCEEDED(rc);
|
||||
if (!ret) LOGFILE("Failed to read 0x%lX bytes block at offset 0x%lX from NCA \"%s\"! (0x%08X) (ncm)", read_size, offset, ctx->content_id_str, rc);
|
||||
if (!ret) LOGFILE("Failed to read 0x%lX bytes block at offset 0x%lX from NCA \"%s\"! (0x%08X) (ncm).", read_size, offset, ctx->content_id_str, rc);
|
||||
} else {
|
||||
/* Retrieve NCA data using raw gamecard reads */
|
||||
/* Fixes NCA read issues with gamecards under HOS < 4.0.0 when using ncmContentStorageReadContentIdFile() */
|
||||
/* Retrieve NCA data using raw gamecard reads. */
|
||||
/* Fixes NCA read issues with gamecards under HOS < 4.0.0 when using ncmContentStorageReadContentIdFile(). */
|
||||
ret = gamecardReadStorage(out, read_size, ctx->gamecard_offset + offset);
|
||||
if (!ret) LOGFILE("Failed to read 0x%lX bytes block at offset 0x%lX from NCA \"%s\"! (gamecard)", read_size, offset, ctx->content_id_str);
|
||||
if (!ret) LOGFILE("Failed to read 0x%lX bytes block at offset 0x%lX from NCA \"%s\"! (gamecard).", read_size, offset, ctx->content_id_str);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -415,8 +422,8 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
|
|||
|
||||
bool success = false;
|
||||
|
||||
if (!ctx || !(nca_ctx = (NcaContext*)ctx->nca_ctx) || !ctx->header || ctx->header->hash_type != NcaHashType_HierarchicalSha256 || ctx->header->encryption_type == NcaEncryptionType_AesCtrEx || \
|
||||
!data || !data_size || !(hash_block_size = ctx->header->hash_info.hierarchical_sha256.hash_block_size) || \
|
||||
if (!ctx || !ctx->enabled || !(nca_ctx = (NcaContext*)ctx->nca_ctx) || !ctx->header || ctx->header->hash_type != NcaHashType_HierarchicalSha256 || \
|
||||
ctx->header->encryption_type == NcaEncryptionType_AesCtrEx || !data || !data_size || !(hash_block_size = ctx->header->hash_info.hierarchical_sha256.hash_block_size) || \
|
||||
!(hash_data_layer_size = ctx->header->hash_info.hierarchical_sha256.hash_data_layer_info.size) || \
|
||||
!(hash_target_layer_size = ctx->header->hash_info.hierarchical_sha256.hash_target_layer_info.size) || data_offset >= hash_target_layer_size || \
|
||||
(data_offset + data_size) > hash_target_layer_size || !out)
|
||||
|
@ -425,7 +432,7 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
|
|||
goto exit;
|
||||
}
|
||||
|
||||
/* Calculate required offsets and sizes */
|
||||
/* Calculate required offsets and sizes. */
|
||||
hash_data_layer_offset = ctx->header->hash_info.hierarchical_sha256.hash_data_layer_info.offset;
|
||||
hash_target_layer_offset = ctx->header->hash_info.hierarchical_sha256.hash_target_layer_info.offset;
|
||||
|
||||
|
@ -440,7 +447,7 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
|
|||
|
||||
u64 hash_target_data_offset = (data_offset - ALIGN_DOWN(data_offset, hash_block_size));
|
||||
|
||||
/* Allocate memory for the full hash data layer */
|
||||
/* Allocate memory for the full hash data layer. */
|
||||
hash_data_layer = malloc(hash_data_layer_size);
|
||||
if (!hash_data_layer)
|
||||
{
|
||||
|
@ -448,14 +455,14 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
|
|||
goto exit;
|
||||
}
|
||||
|
||||
/* Read full hash data layer */
|
||||
/* Read full hash data layer. */
|
||||
if (!_ncaReadFsSection(ctx, hash_data_layer, hash_data_layer_size, hash_data_layer_offset, false))
|
||||
{
|
||||
LOGFILE("Failed to read full HierarchicalSha256 hash data layer!");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Allocate memory for the modified hash target layer block */
|
||||
/* Allocate memory for the modified hash target layer block. */
|
||||
hash_target_block = malloc(hash_target_size);
|
||||
if (!hash_target_block)
|
||||
{
|
||||
|
@ -463,24 +470,24 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
|
|||
goto exit;
|
||||
}
|
||||
|
||||
/* Read hash target layer block */
|
||||
/* Read hash target layer block. */
|
||||
if (!_ncaReadFsSection(ctx, hash_target_block, hash_target_size, hash_target_start_offset, false))
|
||||
{
|
||||
LOGFILE("Failed to read HierarchicalSha256 hash target layer block!");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Replace data */
|
||||
/* Replace data. */
|
||||
memcpy(hash_target_block + hash_target_data_offset, data, data_size);
|
||||
|
||||
/* Recalculate hashes */
|
||||
/* Recalculate hashes. */
|
||||
for(u64 i = 0, j = 0; i < hash_target_size; i += hash_block_size, j++)
|
||||
{
|
||||
if (hash_block_size > (hash_target_size - i)) hash_block_size = (hash_target_size - i);
|
||||
sha256CalculateHash(hash_data_layer + hash_data_start_offset + (j * SHA256_HASH_SIZE), hash_target_block + i, hash_block_size);
|
||||
}
|
||||
|
||||
/* Reencrypt modified hash data layer block */
|
||||
/* Reencrypt modified hash data layer block. */
|
||||
out->hash_data_layer_patch.data = _ncaGenerateEncryptedFsSectionBlock(ctx, hash_data_layer + hash_data_start_offset, hash_data_size, hash_data_layer_offset + hash_data_start_offset, \
|
||||
&(out->hash_data_layer_patch.size), &(out->hash_data_layer_patch.offset), false);
|
||||
if (!out->hash_data_layer_patch.data)
|
||||
|
@ -489,7 +496,7 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
|
|||
goto exit;
|
||||
}
|
||||
|
||||
/* Reencrypt hash target layer block */
|
||||
/* Reencrypt hash target layer block. */
|
||||
out->hash_target_layer_patch.data = _ncaGenerateEncryptedFsSectionBlock(ctx, hash_target_block + hash_target_data_offset, data_size, hash_target_layer_offset + data_offset, \
|
||||
&(out->hash_target_layer_patch.size), &(out->hash_target_layer_patch.offset), false);
|
||||
if (!out->hash_target_layer_patch.data)
|
||||
|
@ -498,13 +505,13 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
|
|||
goto exit;
|
||||
}
|
||||
|
||||
/* Recalculate master hash from hash info block */
|
||||
/* Recalculate master hash from hash info block. */
|
||||
sha256CalculateHash(ctx->header->hash_info.hierarchical_sha256.master_hash, hash_data_layer, hash_data_layer_size);
|
||||
|
||||
/* Recalculate FS header hash */
|
||||
/* Recalculate FS header hash. */
|
||||
sha256CalculateHash(nca_ctx->header.fs_hashes[ctx->section_num].hash, ctx->header, sizeof(NcaFsHeader));
|
||||
|
||||
/* Enable the 'dirty_header' flag */
|
||||
/* Enable the 'dirty_header' flag. */
|
||||
nca_ctx->dirty_header = true;
|
||||
|
||||
success = true;
|
||||
|
@ -534,15 +541,15 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void
|
|||
|
||||
u8 *hash_data_block = NULL, *hash_target_block = NULL;
|
||||
|
||||
if (!ctx || !(nca_ctx = (NcaContext*)ctx->nca_ctx) || !ctx->header || ctx->header->hash_type != NcaHashType_HierarchicalIntegrity || ctx->header->encryption_type == NcaEncryptionType_AesCtrEx || \
|
||||
!data || !data_size || !out || data_offset >= ctx->header->hash_info.hierarchical_integrity.hash_target_layer_info.size || \
|
||||
if (!ctx || !ctx->enabled || !(nca_ctx = (NcaContext*)ctx->nca_ctx) || !ctx->header || ctx->header->hash_type != NcaHashType_HierarchicalIntegrity || \
|
||||
ctx->header->encryption_type == NcaEncryptionType_AesCtrEx || !data || !data_size || !out || data_offset >= ctx->header->hash_info.hierarchical_integrity.hash_target_layer_info.size || \
|
||||
(data_offset + data_size) > ctx->header->hash_info.hierarchical_integrity.hash_target_layer_info.size)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Process each IVFC layer */
|
||||
/* Process each IVFC layer. */
|
||||
for(u8 i = (NCA_IVFC_HASH_DATA_LAYER_COUNT + 1); i > 0; i--)
|
||||
{
|
||||
NcaHierarchicalIntegrityLayerInfo *cur_layer_info = (i > NCA_IVFC_HASH_DATA_LAYER_COUNT ? &(ctx->header->hash_info.hierarchical_integrity.hash_target_layer_info) : \
|
||||
|
@ -558,7 +565,7 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void
|
|||
goto exit;
|
||||
}
|
||||
|
||||
/* Calculate required offsets and sizes */
|
||||
/* Calculate required offsets and sizes. */
|
||||
u64 hash_block_size = NCA_IVFC_BLOCK_SIZE(cur_layer_info->block_size);
|
||||
|
||||
u64 hash_data_layer_offset = 0;
|
||||
|
@ -569,7 +576,7 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void
|
|||
|
||||
if (parent_layer_info)
|
||||
{
|
||||
/* HierarchicalIntegrity layer from L1 to L5 */
|
||||
/* HierarchicalIntegrity layer from L1 to L5. */
|
||||
hash_data_layer_offset = parent_layer_info->offset;
|
||||
|
||||
hash_data_start_offset = ((cur_data_offset / hash_block_size) * SHA256_HASH_SIZE);
|
||||
|
@ -580,8 +587,8 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void
|
|||
hash_target_end_offset = (hash_target_layer_offset + ALIGN_UP(cur_data_offset + cur_data_size, hash_block_size));
|
||||
hash_target_size = (hash_target_end_offset - hash_target_start_offset);
|
||||
} else {
|
||||
/* HierarchicalIntegrity master layer */
|
||||
/* The master hash is calculated over the whole layer and saved to the NCA FS header */
|
||||
/* HierarchicalIntegrity master layer. */
|
||||
/* The master hash is calculated over the whole layer and saved to the NCA FS header. */
|
||||
hash_target_start_offset = hash_target_layer_offset;
|
||||
hash_target_end_offset = (hash_target_layer_offset + hash_target_layer_size);
|
||||
hash_target_size = hash_target_layer_size;
|
||||
|
@ -589,7 +596,7 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void
|
|||
|
||||
hash_target_data_offset = (cur_data_offset - ALIGN_DOWN(cur_data_offset, hash_block_size));
|
||||
|
||||
/* Allocate memory for our hash target layer block */
|
||||
/* Allocate memory for our hash target layer block. */
|
||||
hash_target_block = calloc(hash_target_size, sizeof(u8));
|
||||
if (!hash_target_block)
|
||||
{
|
||||
|
@ -597,26 +604,26 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void
|
|||
goto exit;
|
||||
}
|
||||
|
||||
/* Adjust hash target layer end offset and size if needed to avoid read errors */
|
||||
/* Adjust hash target layer end offset and size if needed to avoid read errors. */
|
||||
if (hash_target_end_offset > (hash_target_layer_offset + hash_target_layer_size))
|
||||
{
|
||||
hash_target_end_offset = (hash_target_layer_offset + hash_target_layer_size);
|
||||
hash_target_size = (hash_target_end_offset - hash_target_start_offset);
|
||||
}
|
||||
|
||||
/* Read hash target layer block */
|
||||
/* Read hash target layer block. */
|
||||
if (!_ncaReadFsSection(ctx, hash_target_block, hash_target_size, hash_target_start_offset, false))
|
||||
{
|
||||
LOGFILE("Failed to read HierarchicalIntegrity hash target layer block!");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Replace hash target layer block data */
|
||||
/* Replace hash target layer block data. */
|
||||
memcpy(hash_target_block + hash_target_data_offset, (i > NCA_IVFC_HASH_DATA_LAYER_COUNT ? data : cur_data), cur_data_size);
|
||||
|
||||
if (parent_layer_info)
|
||||
{
|
||||
/* Allocate memory for our hash data layer block */
|
||||
/* Allocate memory for our hash data layer block. */
|
||||
hash_data_block = calloc(hash_data_size, sizeof(u8));
|
||||
if (!hash_data_block)
|
||||
{
|
||||
|
@ -624,23 +631,23 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void
|
|||
goto exit;
|
||||
}
|
||||
|
||||
/* Read hash target layer block */
|
||||
/* Read hash target layer block. */
|
||||
if (!_ncaReadFsSection(ctx, hash_data_block, hash_data_size, hash_data_layer_offset + hash_data_start_offset, false))
|
||||
{
|
||||
LOGFILE("Failed to read HierarchicalIntegrity hash data layer block!");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Recalculate hashes */
|
||||
/* Size isn't truncated for blocks smaller than the hash block size, unlike HierarchicalSha256, so we just keep using the same hash block size throughout the loop */
|
||||
/* For these specific cases, the rest of the block should be filled with zeroes (already taken care of by using calloc()) */
|
||||
/* Recalculate hashes. */
|
||||
/* Size isn't truncated for blocks smaller than the hash block size, unlike HierarchicalSha256, so we just keep using the same hash block size throughout the loop. */
|
||||
/* For these specific cases, the rest of the block should be filled with zeroes (already taken care of by using calloc()). */
|
||||
for(u64 i = 0, j = 0; i < hash_target_size; i += hash_block_size, j++) sha256CalculateHash(hash_data_block + (j * SHA256_HASH_SIZE), hash_target_block + i, hash_block_size);
|
||||
} else {
|
||||
/* Recalculate master hash from hash info block */
|
||||
/* Recalculate master hash from hash info block. */
|
||||
sha256CalculateHash(ctx->header->hash_info.hierarchical_integrity.master_hash, hash_target_block, hash_target_size);
|
||||
}
|
||||
|
||||
/* Reencrypt hash target layer block */
|
||||
/* Reencrypt hash target layer block. */
|
||||
cur_layer_patch->data = _ncaGenerateEncryptedFsSectionBlock(ctx, hash_target_block + hash_target_data_offset, cur_data_size, hash_target_layer_offset + cur_data_offset, \
|
||||
&(cur_layer_patch->size), &(cur_layer_patch->offset), false);
|
||||
if (!cur_layer_patch->data)
|
||||
|
@ -649,16 +656,16 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void
|
|||
goto exit;
|
||||
}
|
||||
|
||||
/* Free hash target layer block */
|
||||
/* Free hash target layer block. */
|
||||
free(hash_target_block);
|
||||
hash_target_block = NULL;
|
||||
|
||||
if (parent_layer_info)
|
||||
{
|
||||
/* Free previous layer data if necessary */
|
||||
/* Free previous layer data if necessary. */
|
||||
if (cur_data) free(cur_data);
|
||||
|
||||
/* Prepare data for the next target layer */
|
||||
/* Prepare data for the next target layer. */
|
||||
cur_data = hash_data_block;
|
||||
cur_data_offset = hash_data_start_offset;
|
||||
cur_data_size = hash_data_size;
|
||||
|
@ -666,10 +673,10 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void
|
|||
}
|
||||
}
|
||||
|
||||
/* Recalculate FS header hash */
|
||||
/* Recalculate FS header hash. */
|
||||
sha256CalculateHash(nca_ctx->header.fs_hashes[ctx->section_num].hash, ctx->header, sizeof(NcaFsHeader));
|
||||
|
||||
/* Enable the 'dirty_header' flag */
|
||||
/* Enable the 'dirty_header' flag. */
|
||||
nca_ctx->dirty_header = true;
|
||||
|
||||
success = true;
|
||||
|
@ -688,31 +695,6 @@ exit:
|
|||
return success;
|
||||
}
|
||||
|
||||
static size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src, size_t size, u64 sector, size_t sector_size, bool encrypt)
|
||||
{
|
||||
if (!ctx || !dst || !src || !size || !sector_size || (size % sector_size) != 0)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t i, crypt_res = 0;
|
||||
u64 cur_sector = sector;
|
||||
|
||||
u8 *dst_u8 = (u8*)dst;
|
||||
const u8 *src_u8 = (const u8*)src;
|
||||
|
||||
for(i = 0; i < size; i += sector_size, cur_sector++)
|
||||
{
|
||||
/* We have to force a sector reset on each new sector to actually enable Nintendo AES-XTS cipher tweak */
|
||||
aes128XtsContextResetSector(ctx, cur_sector, true);
|
||||
crypt_res = (encrypt ? aes128XtsEncrypt(ctx, dst_u8 + i, src_u8 + i, sector_size) : aes128XtsDecrypt(ctx, dst_u8 + i, src_u8 + i, sector_size));
|
||||
if (crypt_res != sector_size) break;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static bool ncaDecryptHeader(NcaContext *ctx)
|
||||
{
|
||||
if (!ctx || !strlen(ctx->content_id_str))
|
||||
|
@ -772,7 +754,7 @@ static bool ncaDecryptHeader(NcaContext *ctx)
|
|||
case NCA_NCA0_MAGIC:
|
||||
ctx->format_version = NcaVersion_Nca0;
|
||||
|
||||
/* We first need to decrypt the key area from the NCA0 header in order to access its FS section headers */
|
||||
/* We first need to decrypt the key area from the NCA0 header in order to access its FS section headers. */
|
||||
if (!ncaDecryptKeyArea(ctx))
|
||||
{
|
||||
LOGFILE("Error decrypting NCA0 \"%s\" key area!", ctx->content_id_str);
|
||||
|
@ -785,7 +767,7 @@ static bool ncaDecryptHeader(NcaContext *ctx)
|
|||
{
|
||||
if (!ctx->header.fs_entries[i].enable_entry) continue;
|
||||
|
||||
/* FS headers are not part of NCA0 headers */
|
||||
/* FS headers are not part of NCA0 headers. */
|
||||
fs_header_offset = NCA_FS_ENTRY_BLOCK_OFFSET(ctx->header.fs_entries[i].start_block_offset);
|
||||
if (!ncaReadContentFile(ctx, &(ctx->header.fs_headers[i]), NCA_FS_HEADER_LENGTH, fs_header_offset))
|
||||
{
|
||||
|
@ -804,7 +786,7 @@ static bool ncaDecryptHeader(NcaContext *ctx)
|
|||
|
||||
break;
|
||||
default:
|
||||
LOGFILE("Invalid NCA \"%s\" magic word! Wrong header key? (0x%08X)", ctx->content_id_str, magic);
|
||||
LOGFILE("Invalid NCA \"%s\" magic word! Wrong header key? (0x%08X).", ctx->content_id_str, magic);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -823,7 +805,7 @@ static bool ncaDecryptKeyArea(NcaContext *ctx)
|
|||
const u8 *kek_src = NULL;
|
||||
u8 key_count, tmp_kek[0x10] = {0};
|
||||
|
||||
/* Check if we're dealing with a NCA0 with a plain text key area */
|
||||
/* Check if we're dealing with a NCA0 with a plain text key area. */
|
||||
if (ctx->format_version == NcaVersion_Nca0 && !ncaCheckIfVersion0KeyAreaIsEncrypted(ctx))
|
||||
{
|
||||
memcpy(ctx->decrypted_keys, ctx->header.encrypted_keys, 0x40);
|
||||
|
@ -840,7 +822,7 @@ static bool ncaDecryptKeyArea(NcaContext *ctx)
|
|||
rc = splCryptoGenerateAesKek(kek_src, ctx->key_generation, 0, tmp_kek);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOGFILE("splCryptoGenerateAesKek failed! (0x%08X)", rc);
|
||||
LOGFILE("splCryptoGenerateAesKek failed! (0x%08X).", rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -851,7 +833,7 @@ static bool ncaDecryptKeyArea(NcaContext *ctx)
|
|||
rc = splCryptoGenerateAesKey(tmp_kek, ctx->header.encrypted_keys[i].key, ctx->decrypted_keys[i].key);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOGFILE("splCryptoGenerateAesKey failed! (0x%08X)", rc);
|
||||
LOGFILE("splCryptoGenerateAesKey failed! (0x%08X).", rc);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -946,9 +928,9 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
|
|||
|
||||
bool ret = false;
|
||||
|
||||
if (!g_ncaCryptoBuffer || !ctx || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < NCA_FULL_HEADER_LENGTH || ctx->section_type >= NcaFsSectionType_Invalid || \
|
||||
ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type > NcaEncryptionType_AesCtrEx || !ctx->header || !out || !read_size || offset >= ctx->section_size || \
|
||||
(offset + read_size) > ctx->section_size)
|
||||
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < NCA_FULL_HEADER_LENGTH || \
|
||||
ctx->section_type >= NcaFsSectionType_Invalid || ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type > NcaEncryptionType_AesCtrEx || !ctx->header || !out || !read_size || \
|
||||
offset >= ctx->section_size || (offset + read_size) > ctx->section_size)
|
||||
{
|
||||
LOGFILE("Invalid NCA FS section header parameters!");
|
||||
goto exit;
|
||||
|
@ -970,26 +952,26 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
|
|||
goto exit;
|
||||
}
|
||||
|
||||
/* Optimization for reads from plaintext FS sections or reads that are aligned to the AES-CTR / AES-XTS sector size */
|
||||
/* Optimization for reads from plaintext FS sections or reads that are aligned to the AES-CTR / AES-XTS sector size. */
|
||||
if (ctx->encryption_type == NcaEncryptionType_None || \
|
||||
(ctx->encryption_type == NcaEncryptionType_AesXts && !(content_offset % NCA_AES_XTS_SECTOR_SIZE) && !(read_size % NCA_AES_XTS_SECTOR_SIZE)) || \
|
||||
((ctx->encryption_type == NcaEncryptionType_AesCtr || ctx->encryption_type == NcaEncryptionType_AesCtrEx) && !(content_offset % AES_BLOCK_SIZE) && !(read_size % AES_BLOCK_SIZE)))
|
||||
{
|
||||
/* Read data */
|
||||
/* Read data. */
|
||||
if (!ncaReadContentFile(nca_ctx, out, read_size, content_offset))
|
||||
{
|
||||
LOGFILE("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned)", read_size, content_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
LOGFILE("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", read_size, content_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Return right away if we're dealing with a plaintext FS section */
|
||||
/* Return right away if we're dealing with a plaintext FS section. */
|
||||
if (ctx->encryption_type == NcaEncryptionType_None)
|
||||
{
|
||||
ret = true;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Decrypt data */
|
||||
/* Decrypt data. */
|
||||
if (ctx->encryption_type == NcaEncryptionType_AesXts)
|
||||
{
|
||||
sector_num = ((ctx->encryption_type == NcaEncryptionType_AesXts ? offset : (content_offset - NCA_HEADER_LENGTH)) / NCA_AES_XTS_SECTOR_SIZE);
|
||||
|
@ -997,7 +979,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
|
|||
crypt_res = aes128XtsNintendoCrypt(&(ctx->xts_decrypt_ctx), out, out, read_size, sector_num, NCA_AES_XTS_SECTOR_SIZE, false);
|
||||
if (crypt_res != read_size)
|
||||
{
|
||||
LOGFILE("Failed to AES-XTS decrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned)", read_size, content_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
LOGFILE("Failed to AES-XTS decrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", read_size, content_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
goto exit;
|
||||
}
|
||||
} else
|
||||
|
@ -1012,7 +994,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
|
|||
goto exit;
|
||||
}
|
||||
|
||||
/* Calculate offsets and block sizes */
|
||||
/* Calculate offsets and block sizes. */
|
||||
block_start_offset = ALIGN_DOWN(content_offset, ctx->encryption_type == NcaEncryptionType_AesXts ? NCA_AES_XTS_SECTOR_SIZE : AES_BLOCK_SIZE);
|
||||
block_end_offset = ALIGN_UP(content_offset + read_size, ctx->encryption_type == NcaEncryptionType_AesXts ? NCA_AES_XTS_SECTOR_SIZE : AES_BLOCK_SIZE);
|
||||
block_size = (block_end_offset - block_start_offset);
|
||||
|
@ -1021,14 +1003,14 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
|
|||
chunk_size = (block_size > NCA_CRYPTO_BUFFER_SIZE ? NCA_CRYPTO_BUFFER_SIZE : block_size);
|
||||
out_chunk_size = (block_size > NCA_CRYPTO_BUFFER_SIZE ? (NCA_CRYPTO_BUFFER_SIZE - data_start_offset) : read_size);
|
||||
|
||||
/* Read data */
|
||||
/* Read data. */
|
||||
if (!ncaReadContentFile(nca_ctx, g_ncaCryptoBuffer, chunk_size, block_start_offset))
|
||||
{
|
||||
LOGFILE("Failed to read 0x%lX bytes encrypted data block at offset 0x%lX from NCA \"%s\" FS section #%u! (unaligned)", chunk_size, block_start_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
LOGFILE("Failed to read 0x%lX bytes encrypted data block at offset 0x%lX from NCA \"%s\" FS section #%u! (unaligned).", chunk_size, block_start_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Decrypt data */
|
||||
/* Decrypt data. */
|
||||
if (ctx->encryption_type == NcaEncryptionType_AesXts)
|
||||
{
|
||||
sector_num = ((ctx->encryption_type == NcaEncryptionType_AesXts ? offset : (content_offset - NCA_HEADER_LENGTH)) / NCA_AES_XTS_SECTOR_SIZE);
|
||||
|
@ -1036,7 +1018,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
|
|||
crypt_res = aes128XtsNintendoCrypt(&(ctx->xts_decrypt_ctx), g_ncaCryptoBuffer, g_ncaCryptoBuffer, chunk_size, sector_num, NCA_AES_XTS_SECTOR_SIZE, false);
|
||||
if (crypt_res != chunk_size)
|
||||
{
|
||||
LOGFILE("Failed to AES-XTS decrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (unaligned)", chunk_size, block_start_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
LOGFILE("Failed to AES-XTS decrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (unaligned).", chunk_size, block_start_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
goto exit;
|
||||
}
|
||||
} else
|
||||
|
@ -1047,7 +1029,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
|
|||
aes128CtrCrypt(&(ctx->ctr_ctx), g_ncaCryptoBuffer, g_ncaCryptoBuffer, chunk_size);
|
||||
}
|
||||
|
||||
/* Copy decrypted data */
|
||||
/* Copy decrypted data. */
|
||||
memcpy(out, g_ncaCryptoBuffer + data_start_offset, out_chunk_size);
|
||||
|
||||
ret = (block_size > NCA_CRYPTO_BUFFER_SIZE ? _ncaReadFsSection(ctx, (u8*)out + out_chunk_size, read_size - out_chunk_size, offset + out_chunk_size, false) : true);
|
||||
|
@ -1064,8 +1046,9 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi
|
|||
|
||||
bool ret = false;
|
||||
|
||||
if (!g_ncaCryptoBuffer || !ctx || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < NCA_FULL_HEADER_LENGTH || ctx->section_type != NcaFsSectionType_PatchRomFs || \
|
||||
ctx->encryption_type != NcaEncryptionType_AesCtrEx || !ctx->header || !out || !read_size || offset >= ctx->section_size || (offset + read_size) > ctx->section_size)
|
||||
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < NCA_FULL_HEADER_LENGTH || \
|
||||
ctx->section_type != NcaFsSectionType_PatchRomFs || ctx->encryption_type != NcaEncryptionType_AesCtrEx || !ctx->header || !out || !read_size || offset >= ctx->section_size || \
|
||||
(offset + read_size) > ctx->section_size)
|
||||
{
|
||||
LOGFILE("Invalid NCA FS section header parameters!");
|
||||
goto exit;
|
||||
|
@ -1084,13 +1067,13 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi
|
|||
goto exit;
|
||||
}
|
||||
|
||||
/* Optimization for reads that are aligned to the AES-CTR sector size */
|
||||
/* Optimization for reads that are aligned to the AES-CTR sector size. */
|
||||
if (!(content_offset % AES_BLOCK_SIZE) && !(read_size % AES_BLOCK_SIZE))
|
||||
{
|
||||
/* Read data */
|
||||
/* Read data. */
|
||||
if (!ncaReadContentFile(nca_ctx, out, read_size, content_offset))
|
||||
{
|
||||
LOGFILE("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned)", read_size, content_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
LOGFILE("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", read_size, content_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
|
@ -1103,7 +1086,7 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi
|
|||
goto exit;
|
||||
}
|
||||
|
||||
/* Calculate offsets and block sizes */
|
||||
/* Calculate offsets and block sizes. */
|
||||
block_start_offset = ALIGN_DOWN(content_offset, AES_BLOCK_SIZE);
|
||||
block_end_offset = ALIGN_UP(content_offset + read_size, AES_BLOCK_SIZE);
|
||||
block_size = (block_end_offset - block_start_offset);
|
||||
|
@ -1112,19 +1095,19 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi
|
|||
chunk_size = (block_size > NCA_CRYPTO_BUFFER_SIZE ? NCA_CRYPTO_BUFFER_SIZE : block_size);
|
||||
out_chunk_size = (block_size > NCA_CRYPTO_BUFFER_SIZE ? (NCA_CRYPTO_BUFFER_SIZE - data_start_offset) : read_size);
|
||||
|
||||
/* Read data */
|
||||
/* Read data. */
|
||||
if (!ncaReadContentFile(nca_ctx, g_ncaCryptoBuffer, chunk_size, block_start_offset))
|
||||
{
|
||||
LOGFILE("Failed to read 0x%lX bytes encrypted data block at offset 0x%lX from NCA \"%s\" FS section #%u! (unaligned)", chunk_size, block_start_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
LOGFILE("Failed to read 0x%lX bytes encrypted data block at offset 0x%lX from NCA \"%s\" FS section #%u! (unaligned).", chunk_size, block_start_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Decrypt data */
|
||||
/* Decrypt data. */
|
||||
ncaUpdateAesCtrExIv(ctx->ctr, ctr_val, block_start_offset);
|
||||
aes128CtrContextResetCtr(&(ctx->ctr_ctx), ctx->ctr);
|
||||
aes128CtrCrypt(&(ctx->ctr_ctx), g_ncaCryptoBuffer, g_ncaCryptoBuffer, chunk_size);
|
||||
|
||||
/* Copy decrypted data */
|
||||
/* Copy decrypted data. */
|
||||
memcpy(out, g_ncaCryptoBuffer + data_start_offset, out_chunk_size);
|
||||
|
||||
ret = (block_size > NCA_CRYPTO_BUFFER_SIZE ? _ncaReadAesCtrExStorageFromBktrSection(ctx, (u8*)out + out_chunk_size, read_size - out_chunk_size, offset + out_chunk_size, ctr_val, false) : true);
|
||||
|
@ -1142,9 +1125,9 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
|
|||
u8 *out = NULL;
|
||||
bool success = false;
|
||||
|
||||
if (!g_ncaCryptoBuffer || !ctx || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < NCA_FULL_HEADER_LENGTH || ctx->section_type >= NcaFsSectionType_Invalid || \
|
||||
ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type >= NcaEncryptionType_AesCtrEx || !ctx->header || !data || !data_size || data_offset >= ctx->section_size || \
|
||||
(data_offset + data_size) > ctx->section_size || !out_block_size || !out_block_offset)
|
||||
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < NCA_FULL_HEADER_LENGTH || \
|
||||
ctx->section_type >= NcaFsSectionType_Invalid || ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type >= NcaEncryptionType_AesCtrEx || !ctx->header || !data || !data_size || \
|
||||
data_offset >= ctx->section_size || (data_offset + data_size) > ctx->section_size || !out_block_size || !out_block_offset)
|
||||
{
|
||||
LOGFILE("Invalid NCA FS section header parameters!");
|
||||
goto exit;
|
||||
|
@ -1166,23 +1149,23 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
|
|||
goto exit;
|
||||
}
|
||||
|
||||
/* Optimization for blocks from plaintext FS sections or blocks that are aligned to the AES-CTR / AES-XTS sector size */
|
||||
/* Optimization for blocks from plaintext FS sections or blocks that are aligned to the AES-CTR / AES-XTS sector size. */
|
||||
if (ctx->encryption_type == NcaEncryptionType_None || \
|
||||
(ctx->encryption_type == NcaEncryptionType_AesXts && !(content_offset % NCA_AES_XTS_SECTOR_SIZE) && !(data_size % NCA_AES_XTS_SECTOR_SIZE)) || \
|
||||
(ctx->encryption_type == NcaEncryptionType_AesCtr && !(content_offset % AES_BLOCK_SIZE) && !(data_size % AES_BLOCK_SIZE)))
|
||||
{
|
||||
/* Allocate memory */
|
||||
/* Allocate memory. */
|
||||
out = malloc(data_size);
|
||||
if (!out)
|
||||
{
|
||||
LOGFILE("Unable to allocate 0x%lX bytes buffer! (aligned)", data_size);
|
||||
LOGFILE("Unable to allocate 0x%lX bytes buffer! (aligned).", data_size);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Copy data */
|
||||
/* Copy data. */
|
||||
memcpy(out, data, data_size);
|
||||
|
||||
/* Encrypt data */
|
||||
/* Encrypt data. */
|
||||
if (ctx->encryption_type == NcaEncryptionType_AesXts)
|
||||
{
|
||||
sector_num = ((ctx->encryption_type == NcaEncryptionType_AesXts ? data_offset : (content_offset - NCA_HEADER_LENGTH)) / NCA_AES_XTS_SECTOR_SIZE);
|
||||
|
@ -1190,7 +1173,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
|
|||
crypt_res = aes128XtsNintendoCrypt(&(ctx->xts_encrypt_ctx), out, out, data_size, sector_num, NCA_AES_XTS_SECTOR_SIZE, true);
|
||||
if (crypt_res != data_size)
|
||||
{
|
||||
LOGFILE("Failed to AES-XTS encrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned)", data_size, content_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
LOGFILE("Failed to AES-XTS encrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", data_size, content_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
goto exit;
|
||||
}
|
||||
} else
|
||||
|
@ -1208,7 +1191,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
|
|||
goto exit;
|
||||
}
|
||||
|
||||
/* Calculate block offsets and size */
|
||||
/* Calculate block offsets and size. */
|
||||
block_start_offset = ALIGN_DOWN(data_offset, ctx->encryption_type == NcaEncryptionType_AesXts ? NCA_AES_XTS_SECTOR_SIZE : AES_BLOCK_SIZE);
|
||||
block_end_offset = ALIGN_UP(data_offset + data_size, ctx->encryption_type == NcaEncryptionType_AesXts ? NCA_AES_XTS_SECTOR_SIZE : AES_BLOCK_SIZE);
|
||||
block_size = (block_end_offset - block_start_offset);
|
||||
|
@ -1216,25 +1199,25 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
|
|||
plain_chunk_offset = (data_offset - block_start_offset);
|
||||
content_offset = (ctx->section_offset + block_start_offset);
|
||||
|
||||
/* Allocate memory */
|
||||
/* Allocate memory. */
|
||||
out = malloc(block_size);
|
||||
if (!out)
|
||||
{
|
||||
LOGFILE("Unable to allocate 0x%lX bytes buffer! (unaligned)", block_size);
|
||||
LOGFILE("Unable to allocate 0x%lX bytes buffer! (unaligned).", block_size);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Read decrypted data using aligned offset and size */
|
||||
/* Read decrypted data using aligned offset and size. */
|
||||
if (!_ncaReadFsSection(ctx, out, block_size, block_start_offset, false))
|
||||
{
|
||||
LOGFILE("Failed to read decrypted NCA \"%s\" FS section #%u data block!", nca_ctx->content_id_str, ctx->section_num);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Replace plaintext data */
|
||||
/* Replace plaintext data. */
|
||||
memcpy(out + plain_chunk_offset, data, data_size);
|
||||
|
||||
/* Reencrypt data */
|
||||
/* Reencrypt data. */
|
||||
if (ctx->encryption_type == NcaEncryptionType_AesXts)
|
||||
{
|
||||
sector_num = ((ctx->encryption_type == NcaEncryptionType_AesXts ? block_start_offset : (content_offset - NCA_HEADER_LENGTH)) / NCA_AES_XTS_SECTOR_SIZE);
|
||||
|
@ -1242,7 +1225,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
|
|||
crypt_res = aes128XtsNintendoCrypt(&(ctx->xts_encrypt_ctx), out, out, block_size, sector_num, NCA_AES_XTS_SECTOR_SIZE, true);
|
||||
if (crypt_res != block_size)
|
||||
{
|
||||
LOGFILE("Failed to AES-XTS encrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned)", block_size, content_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
LOGFILE("Failed to AES-XTS encrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", block_size, content_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
goto exit;
|
||||
}
|
||||
} else
|
||||
|
|
|
@ -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().
|
||||
|
|
97
source/pfs.c
97
source/pfs.c
|
@ -23,20 +23,22 @@
|
|||
|
||||
bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *nca_fs_ctx)
|
||||
{
|
||||
if (!out || !nca_fs_ctx || nca_fs_ctx->section_type != NcaFsSectionType_PartitionFs || !nca_fs_ctx->header || nca_fs_ctx->header->fs_type != NcaFsType_PartitionFs || \
|
||||
if (!out || !nca_fs_ctx || !nca_fs_ctx->enabled || nca_fs_ctx->section_type != NcaFsSectionType_PartitionFs || !nca_fs_ctx->header || nca_fs_ctx->header->fs_type != NcaFsType_PartitionFs || \
|
||||
nca_fs_ctx->header->hash_type != NcaHashType_HierarchicalSha256)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Fill context */
|
||||
u32 magic = 0;
|
||||
PartitionFileSystemHeader pfs_header = {0};
|
||||
PartitionFileSystemEntry *main_npdm_entry = NULL;
|
||||
|
||||
/* Clear output partition FS context. */
|
||||
memset(out, 0, sizeof(PartitionFileSystemContext));
|
||||
|
||||
/* Fill context. */
|
||||
out->nca_fs_ctx = nca_fs_ctx;
|
||||
out->offset = 0;
|
||||
out->size = 0;
|
||||
out->is_exefs = false;
|
||||
out->header_size = 0;
|
||||
out->header = NULL;
|
||||
|
||||
if (!ncaValidateHierarchicalSha256Offsets(&(nca_fs_ctx->header->hash_info.hierarchical_sha256), nca_fs_ctx->section_size))
|
||||
{
|
||||
|
@ -47,11 +49,7 @@ bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *
|
|||
out->offset = nca_fs_ctx->header->hash_info.hierarchical_sha256.hash_target_layer_info.offset;
|
||||
out->size = nca_fs_ctx->header->hash_info.hierarchical_sha256.hash_target_layer_info.size;
|
||||
|
||||
/* Read partial PFS header */
|
||||
u32 magic = 0;
|
||||
PartitionFileSystemHeader pfs_header = {0};
|
||||
PartitionFileSystemEntry *main_npdm_entry = NULL;
|
||||
|
||||
/* Read partial PFS header. */
|
||||
if (!ncaReadFsSection(nca_fs_ctx, &pfs_header, sizeof(PartitionFileSystemHeader), out->offset))
|
||||
{
|
||||
LOGFILE("Failed to read partial partition FS header!");
|
||||
|
@ -61,7 +59,7 @@ bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *
|
|||
magic = __builtin_bswap32(pfs_header.magic);
|
||||
if (magic != PFS0_MAGIC)
|
||||
{
|
||||
LOGFILE("Invalid partition FS magic word! (0x%08X)", magic);
|
||||
LOGFILE("Invalid partition FS magic word! (0x%08X).", magic);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -71,10 +69,10 @@ bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Calculate full partition FS header size */
|
||||
/* Calculate full partition FS header size. */
|
||||
out->header_size = (sizeof(PartitionFileSystemHeader) + (pfs_header.entry_count * sizeof(PartitionFileSystemEntry)) + pfs_header.name_table_size);
|
||||
|
||||
/* Allocate memory for the full partition FS header */
|
||||
/* Allocate memory for the full partition FS header. */
|
||||
out->header = calloc(out->header_size, sizeof(u8));
|
||||
if (!out->header)
|
||||
{
|
||||
|
@ -82,7 +80,7 @@ bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Read full partition FS header */
|
||||
/* Read full partition FS header. */
|
||||
if (!ncaReadFsSection(nca_fs_ctx, out->header, out->header_size, out->offset))
|
||||
{
|
||||
LOGFILE("Failed to read full partition FS header!");
|
||||
|
@ -90,7 +88,7 @@ bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Check if we're dealing with an ExeFS section */
|
||||
/* Check if we're dealing with an ExeFS section. */
|
||||
if ((main_npdm_entry = pfsGetEntryByName(out, "main.npdm")) != NULL && pfsReadEntryData(out, main_npdm_entry, &magic, sizeof(u32), 0) && \
|
||||
__builtin_bswap32(magic) == NPDM_META_MAGIC) out->is_exefs = true;
|
||||
|
||||
|
@ -105,7 +103,7 @@ bool pfsReadPartitionData(PartitionFileSystemContext *ctx, void *out, u64 read_s
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Read partition data */
|
||||
/* Read partition data. */
|
||||
if (!ncaReadFsSection(ctx->nca_fs_ctx, out, read_size, ctx->offset + offset))
|
||||
{
|
||||
LOGFILE("Failed to read partition FS data!");
|
||||
|
@ -124,7 +122,7 @@ bool pfsReadEntryData(PartitionFileSystemContext *ctx, PartitionFileSystemEntry
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Read entry data */
|
||||
/* Read entry data. */
|
||||
if (!pfsReadPartitionData(ctx, out, read_size, ctx->header_size + fs_entry->offset + offset))
|
||||
{
|
||||
LOGFILE("Failed to read partition FS entry data!");
|
||||
|
@ -134,6 +132,67 @@ bool pfsReadEntryData(PartitionFileSystemContext *ctx, PartitionFileSystemEntry
|
|||
return true;
|
||||
}
|
||||
|
||||
bool pfsGetEntryIndexByName(PartitionFileSystemContext *ctx, const char *name, u32 *out_idx)
|
||||
{
|
||||
size_t name_len = 0;
|
||||
PartitionFileSystemEntry *fs_entry = NULL;
|
||||
u32 entry_count = pfsGetEntryCount(ctx);
|
||||
char *name_table = pfsGetNameTable(ctx);
|
||||
|
||||
if (!entry_count || !name_table || !name || !(name_len = strlen(name)) || !out_idx)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
for(u32 i = 0; i < entry_count; i++)
|
||||
{
|
||||
if (!(fs_entry = pfsGetEntryByIndex(ctx, i)))
|
||||
{
|
||||
LOGFILE("Failed to retrieve partition FS entry #%u!", i);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strlen(name_table + fs_entry->name_offset) == name_len && !strcmp(name_table + fs_entry->name_offset, name))
|
||||
{
|
||||
*out_idx = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LOGFILE("Unable to find partition FS entry \"%s\"!", name);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pfsGetTotalDataSize(PartitionFileSystemContext *ctx, u64 *out_size)
|
||||
{
|
||||
u64 total_size = 0;
|
||||
u32 entry_count = pfsGetEntryCount(ctx);
|
||||
PartitionFileSystemEntry *fs_entry = NULL;
|
||||
|
||||
if (!entry_count || !out_size)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
for(u32 i = 0; i < entry_count; i++)
|
||||
{
|
||||
if (!(fs_entry = pfsGetEntryByIndex(ctx, i)))
|
||||
{
|
||||
LOGFILE("Failed to retrieve partition FS entry #%u!", i);
|
||||
return false;
|
||||
}
|
||||
|
||||
total_size += fs_entry->size;
|
||||
}
|
||||
|
||||
*out_size = total_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pfsGenerateEntryPatch(PartitionFileSystemContext *ctx, PartitionFileSystemEntry *fs_entry, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalSha256Patch *out)
|
||||
{
|
||||
if (!ctx || !ctx->nca_fs_ctx || !ctx->header_size || !ctx->header || !fs_entry || fs_entry->offset >= ctx->size || !fs_entry->size || (fs_entry->offset + fs_entry->size) > ctx->size || !data || \
|
||||
|
|
49
source/pfs.h
49
source/pfs.h
|
@ -25,7 +25,7 @@
|
|||
|
||||
#include "nca.h"
|
||||
|
||||
#define PFS0_MAGIC 0x50465330 /* "PFS0" */
|
||||
#define PFS0_MAGIC 0x50465330 /* "PFS0". */
|
||||
|
||||
typedef struct {
|
||||
u32 magic; ///< "PFS0".
|
||||
|
@ -69,6 +69,12 @@ bool pfsReadPartitionData(PartitionFileSystemContext *ctx, void *out, u64 read_s
|
|||
/// Input offset must be relative to the start of the partition FS entry.
|
||||
bool pfsReadEntryData(PartitionFileSystemContext *ctx, PartitionFileSystemEntry *fs_entry, void *out, u64 read_size, u64 offset);
|
||||
|
||||
/// Retrieves a partition FS entry index by its name.
|
||||
bool pfsGetEntryIndexByName(PartitionFileSystemContext *ctx, const char *name, u32 *out_idx);
|
||||
|
||||
/// Calculates the extracted partition FS size.
|
||||
bool pfsGetTotalDataSize(PartitionFileSystemContext *ctx, u64 *out_size);
|
||||
|
||||
/// Generates HierarchicalSha256 FS section patch data using a partition FS context + entry, which can be used to replace NCA data in content dumping operations.
|
||||
/// Input offset must be relative to the start of the partition FS entry data.
|
||||
/// This function shares the same limitations as ncaGenerateHierarchicalSha256Patch().
|
||||
|
@ -103,29 +109,6 @@ NX_INLINE char *pfsGetEntryNameByIndex(PartitionFileSystemContext *ctx, u32 idx)
|
|||
return (name_table + fs_entry->name_offset);
|
||||
}
|
||||
|
||||
NX_INLINE bool pfsGetEntryIndexByName(PartitionFileSystemContext *ctx, const char *name, u32 *out_idx)
|
||||
{
|
||||
size_t name_len = 0;
|
||||
PartitionFileSystemEntry *fs_entry = NULL;
|
||||
u32 entry_count = pfsGetEntryCount(ctx);
|
||||
char *name_table = pfsGetNameTable(ctx);
|
||||
|
||||
if (!entry_count || !name_table || !name || !(name_len = strlen(name)) || !out_idx) return false;
|
||||
|
||||
for(u32 i = 0; i < entry_count; i++)
|
||||
{
|
||||
if (!(fs_entry = pfsGetEntryByIndex(ctx, i))) return false;
|
||||
|
||||
if (strlen(name_table + fs_entry->name_offset) == name_len && !strcmp(name_table + fs_entry->name_offset, name))
|
||||
{
|
||||
*out_idx = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NX_INLINE PartitionFileSystemEntry *pfsGetEntryByName(PartitionFileSystemContext *ctx, const char *name)
|
||||
{
|
||||
u32 idx = 0;
|
||||
|
@ -133,22 +116,4 @@ NX_INLINE PartitionFileSystemEntry *pfsGetEntryByName(PartitionFileSystemContext
|
|||
return pfsGetEntryByIndex(ctx, idx);
|
||||
}
|
||||
|
||||
NX_INLINE bool pfsGetTotalDataSize(PartitionFileSystemContext *ctx, u64 *out_size)
|
||||
{
|
||||
u64 total_size = 0;
|
||||
u32 entry_count = pfsGetEntryCount(ctx);
|
||||
PartitionFileSystemEntry *fs_entry = NULL;
|
||||
if (!entry_count || !out_size) return false;
|
||||
|
||||
for(u32 i = 0; i < entry_count; i++)
|
||||
{
|
||||
if (!(fs_entry = pfsGetEntryByIndex(ctx, i))) return false;
|
||||
total_size += fs_entry->size;
|
||||
}
|
||||
|
||||
*out_size = total_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* __PFS_H__ */
|
||||
|
|
|
@ -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), \
|
||||
|
|
42
source/rsa.c
42
source/rsa.c
|
@ -109,38 +109,38 @@ bool rsa2048GenerateSha256BasedCustomAcidSignature(void *dst, const void *src, s
|
|||
mbedtls_pk_context pk;
|
||||
mbedtls_pk_init(&pk);
|
||||
|
||||
/* Calculate SHA-256 checksum for the input data */
|
||||
/* Calculate SHA-256 checksum for the input data. */
|
||||
sha256CalculateHash(hash, src, size);
|
||||
|
||||
/* Seed the random number generator */
|
||||
/* Seed the random number generator. */
|
||||
ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const u8*)pers, strlen(pers));
|
||||
if (ret != 0)
|
||||
{
|
||||
LOGFILE("mbedtls_ctr_drbg_seed failed! (%d)", ret);
|
||||
LOGFILE("mbedtls_ctr_drbg_seed failed! (%d).", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Parse private key */
|
||||
/* Parse private key. */
|
||||
ret = mbedtls_pk_parse_key(&pk, (u8*)g_rsa2048CustomAcidPrivateKey, strlen(g_rsa2048CustomAcidPrivateKey) + 1, NULL, 0);
|
||||
if (ret != 0)
|
||||
{
|
||||
LOGFILE("mbedtls_pk_parse_key failed! (%d)", ret);
|
||||
LOGFILE("mbedtls_pk_parse_key failed! (%d).", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Set RSA padding */
|
||||
/* Set RSA padding. */
|
||||
mbedtls_rsa_set_padding(mbedtls_pk_rsa(pk), MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA256);
|
||||
|
||||
/* Calculate hash signature */
|
||||
/* Calculate hash signature. */
|
||||
ret = mbedtls_pk_sign(&pk, MBEDTLS_MD_SHA256, hash, 0, buf, &olen, mbedtls_ctr_drbg_random, &ctr_drbg);
|
||||
if (ret != 0)
|
||||
{
|
||||
LOGFILE("mbedtls_pk_sign failed! (%d)", ret);
|
||||
LOGFILE("mbedtls_pk_sign failed! (%d).", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Copy signature to output buffer */
|
||||
memcpy(dst, buf, RSA2048_SIGNATURE_SIZE);
|
||||
/* Copy signature to output buffer. */
|
||||
memcpy(dst, buf, RSA2048_SIG_SIZE);
|
||||
success = true;
|
||||
|
||||
out:
|
||||
|
@ -165,12 +165,12 @@ bool rsa2048OaepDecryptAndVerify(void *dst, size_t dst_size, const void *signatu
|
|||
}
|
||||
|
||||
Result rc = 0;
|
||||
u8 m_buf[RSA2048_SIGNATURE_SIZE] = {0};
|
||||
u8 m_buf[RSA2048_SIG_SIZE] = {0};
|
||||
|
||||
rc = splUserExpMod(signature, modulus, exponent, exponent_size, m_buf);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOGFILE("splUserExpMod failed! (0x%08X)", rc);
|
||||
LOGFILE("splUserExpMod failed! (0x%08X).", rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -180,13 +180,13 @@ bool rsa2048OaepDecryptAndVerify(void *dst, size_t dst_size, const void *signatu
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Unmask salt */
|
||||
rsaCalculateMgf1AndXor(m_buf + 1, 0x20, m_buf + 0x21, RSA2048_SIGNATURE_SIZE - 0x21);
|
||||
/* Unmask salt. */
|
||||
rsaCalculateMgf1AndXor(m_buf + 1, 0x20, m_buf + 0x21, RSA2048_SIG_SIZE - 0x21);
|
||||
|
||||
/* Unmask DB */
|
||||
rsaCalculateMgf1AndXor(m_buf + 0x21, RSA2048_SIGNATURE_SIZE - 0x21, m_buf + 1, 0x20);
|
||||
/* Unmask DB. */
|
||||
rsaCalculateMgf1AndXor(m_buf + 0x21, RSA2048_SIG_SIZE - 0x21, m_buf + 1, 0x20);
|
||||
|
||||
/* Validate label hash */
|
||||
/* Validate label hash. */
|
||||
const u8 *db = (const u8*)(m_buf + 0x21);
|
||||
if (memcmp(db, label_hash, SHA256_HASH_SIZE) != 0)
|
||||
{
|
||||
|
@ -194,9 +194,9 @@ bool rsa2048OaepDecryptAndVerify(void *dst, size_t dst_size, const void *signatu
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Validate message prefix */
|
||||
/* Validate message prefix. */
|
||||
const u8 *data = (const u8*)(db + 0x20);
|
||||
size_t remaining = (RSA2048_SIGNATURE_SIZE - 0x41);
|
||||
size_t remaining = (RSA2048_SIG_SIZE - 0x41);
|
||||
|
||||
while(!*data && remaining)
|
||||
{
|
||||
|
@ -221,7 +221,7 @@ bool rsa2048OaepDecryptAndVerify(void *dst, size_t dst_size, const void *signatu
|
|||
|
||||
static void rsaCalculateMgf1AndXor(void *data, size_t data_size, const void *h_src, size_t h_src_size)
|
||||
{
|
||||
if (!data || !data_size || !h_src || !h_src_size || h_src_size > RSA2048_SIGNATURE_SIZE)
|
||||
if (!data || !data_size || !h_src || !h_src_size || h_src_size > RSA2048_SIG_SIZE)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return;
|
||||
|
@ -232,7 +232,7 @@ static void rsaCalculateMgf1AndXor(void *data, size_t data_size, const void *h_s
|
|||
u8 *data_u8 = (u8*)data;
|
||||
|
||||
u8 mgf1_buf[SHA256_HASH_SIZE] = {0};
|
||||
u8 h_buf[RSA2048_SIGNATURE_SIZE] = {0};
|
||||
u8 h_buf[RSA2048_SIG_SIZE] = {0};
|
||||
|
||||
memcpy(h_buf, h_src, h_src_size);
|
||||
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
198
source/usb.c
198
source/usb.c
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Heavily based in usb_comms from libnx.
|
||||
*
|
||||
* Copyright (c) 2018-2020, Switchbrew, libnx contributors.
|
||||
* Copyright (c) 2018-2020, Switchbrew and libnx contributors.
|
||||
* Copyright (c) 2020, DarkMatterCore <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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
131
source/utils.c
131
source/utils.c
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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, ...);
|
||||
|
||||
|
|
Loading…
Reference in a new issue