mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2025-01-03 14:26:01 +00:00
Fix building with latest libnx + QoL changes.
libnx now implements fsDeviceOperatorGetGameCardIdSet(), so I got rid of my own implementation. Other changes include: * cnmt: add cnmtVerifyContentHash(). * defines: add SHA256_HASH_STR_SIZE. * fs_ext: add FsCardId1MakerCode, FsCardId1MemoryType and FsCardId2CardType enums. * fs_ext: update FsCardId* structs. * gamecard: change all package_id definitions from u64 -> u8[0x8]. * gamecard: fix misleading struct member names in GameCardHeader. * gamecard: rename gamecardGetIdSet() -> gamecardGetCardIdSet(). * gamecard_tab: fix Package ID printing. * gamecard_tab: add Card ID Set printing. * host: add executable flag to Python scripts. * keys: detect if we're dealing with a wiped eTicket RSA device key (e.g. via set:cal blanking). If so, the application will still launch, but all operations related to personalized titlekey crypto are disabled. * pfs: rename PartitionFileSystemFileContext -> PartitionFileSystemImageContext and propagate the change throughout the codebase. * pfs: rename PFS_FULL_HEADER_ALIGNMENT -> PFS_HEADER_PADDING_ALIGNMENT and update pfsWriteImageContextHeaderToMemoryBuffer() accordingly. * poc: print certain button prompts with reversed colors, in the hopes of getting the user's attention. * poc: NSP, Ticket and NCA submenus for updates and DLC updates now display the highest available title by default. * poc: simplified output path generation for extracted NCA FS section dumps. * poc: handle edge cases where a specific NCA from an update has no matching equivalent by type/ID offset in its base title (e.g. Fall Guys' HtmlDocument NCA). * poc: implement NCA checksum validation while generating NSP dumps. * romfs: update romfsInitializeContext() to allow its base_nca_fs_ctx argument to be NULL. * usb: use USB_BOS_SIZE only once. * workflow: update commit hash referenced by "rewrite-prerelease" tag on update.
This commit is contained in:
parent
ec6deede37
commit
e1df86fb27
21 changed files with 360 additions and 177 deletions
9
.github/workflows/rewrite.yml
vendored
9
.github/workflows/rewrite.yml
vendored
|
@ -63,8 +63,8 @@ jobs:
|
|||
./build.sh
|
||||
|
||||
#- name: Build nxdumptool-rewrite GUI binary
|
||||
# working-directory: nxdumptool
|
||||
# run: |
|
||||
# cd "$GITHUB_WORKSPACE/nxdumptool"
|
||||
# make -j$(nproc)
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
|
@ -94,14 +94,15 @@ jobs:
|
|||
- name: Upload artifact to prerelease
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
# only update on prerelease
|
||||
# Only update attachments on "rewrite-prerelease" tag. Make sure to update the commit referenced by the tag as well by using the branch name.
|
||||
prerelease: True
|
||||
tag: "rewrite-prerelease"
|
||||
commit: "rewrite"
|
||||
updateOnlyUnreleased: True
|
||||
# remove old artifacts & replace with new ones
|
||||
# Remove old artifacts and replace with new ones.
|
||||
removeArtifacts: True
|
||||
replacesArtifacts: True
|
||||
# update preference
|
||||
# Update preferences.
|
||||
allowUpdates: True
|
||||
omitBody: True
|
||||
omitBodyDuringUpdate: True
|
||||
|
|
|
@ -143,6 +143,7 @@ static u64 utilsGetButtonsHeld(void);
|
|||
static void utilsWaitForButtonPress(u64 flag);
|
||||
|
||||
static void consolePrint(const char *text, ...);
|
||||
static void consolePrintReversedColors(const char *text, ...);
|
||||
static void consoleRefresh(void);
|
||||
|
||||
static u32 menuGetElementCount(const Menu *menu);
|
||||
|
@ -153,6 +154,8 @@ void updateStorageList(void);
|
|||
void freeTitleList(Menu *menu);
|
||||
void updateTitleList(Menu *menu, Menu *submenu, bool is_system);
|
||||
|
||||
static TitleInfo *getLatestTitleInfo(TitleInfo *title_info, u32 *out_idx, u32 *out_count);
|
||||
|
||||
void freeNcaList(void);
|
||||
void updateNcaList(TitleInfo *title_info);
|
||||
static void switchNcaListTitle(Menu *cur_menu, u32 *element_count, TitleInfo *title_info);
|
||||
|
@ -966,8 +969,8 @@ int main(int argc, char *argv[])
|
|||
{
|
||||
if (cur_menu->id != MenuId_NcaFsSections && cur_menu->id != MenuId_NcaFsSectionsSubMenu && (title_info->previous || title_info->next))
|
||||
{
|
||||
consolePrint("press l/zl and/or r/zr to change the selected title\n");
|
||||
consolePrint("title: %u / %u\n", title_info_idx, title_info_count);
|
||||
consolePrintReversedColors("press l/zl/r/zr to change the selected title\n");
|
||||
consolePrintReversedColors("title: %u / %u\n", title_info_idx + 1, title_info_count);
|
||||
consolePrint("______________________________\n\n");
|
||||
}
|
||||
|
||||
|
@ -988,7 +991,8 @@ int main(int argc, char *argv[])
|
|||
|
||||
if (cur_menu->id == MenuId_Nca)
|
||||
{
|
||||
consolePrint("press y to switch to %s mode\n", g_ncaMenuRawMode ? "nca fs section" : "raw nca");
|
||||
consolePrintReversedColors("current mode: %s\n", g_ncaMenuRawMode ? "raw nca" : "nca fs section");
|
||||
consolePrintReversedColors("press y to switch to %s mode\n", g_ncaMenuRawMode ? "nca fs section" : "raw nca");
|
||||
consolePrint("______________________________\n\n");
|
||||
}
|
||||
|
||||
|
@ -1123,6 +1127,8 @@ int main(int argc, char *argv[])
|
|||
|
||||
if (title_info)
|
||||
{
|
||||
title_info = getLatestTitleInfo(title_info, &title_info_idx, &title_info_count);
|
||||
|
||||
if (child_menu->id == MenuId_Nca)
|
||||
{
|
||||
updateNcaList(title_info);
|
||||
|
@ -1135,12 +1141,6 @@ int main(int argc, char *argv[])
|
|||
|
||||
if (!error && cur_menu->id == MenuId_SystemTitles) is_system = true;
|
||||
}
|
||||
|
||||
if (!error)
|
||||
{
|
||||
title_info_count = titleGetCountFromInfoBlock(title_info);
|
||||
title_info_idx = 1;
|
||||
}
|
||||
} else {
|
||||
if (cur_menu->id == MenuId_SystemTitles)
|
||||
{
|
||||
|
@ -1446,6 +1446,22 @@ static void consolePrint(const char *text, ...)
|
|||
mutexUnlock(&g_conMutex);
|
||||
}
|
||||
|
||||
static void consolePrintReversedColors(const char *text, ...)
|
||||
{
|
||||
mutexLock(&g_conMutex);
|
||||
|
||||
printf(CONSOLE_ESC(7m));
|
||||
|
||||
va_list v;
|
||||
va_start(v, text);
|
||||
vfprintf(stdout, text, v);
|
||||
va_end(v);
|
||||
|
||||
printf(CONSOLE_ESC(0m));
|
||||
|
||||
mutexUnlock(&g_conMutex);
|
||||
}
|
||||
|
||||
static void consoleRefresh(void)
|
||||
{
|
||||
mutexLock(&g_conMutex);
|
||||
|
@ -1616,6 +1632,49 @@ end:
|
|||
if (app_metadata) free(app_metadata);
|
||||
}
|
||||
|
||||
static TitleInfo *getLatestTitleInfo(TitleInfo *title_info, u32 *out_idx, u32 *out_count)
|
||||
{
|
||||
if (!title_info || !out_idx || !out_count || (title_info->meta_key.type != NcmContentMetaType_Patch && title_info->meta_key.type != NcmContentMetaType_DataPatch)) return title_info;
|
||||
|
||||
u32 idx = 0, count = 1;
|
||||
TitleInfo *cur_info = title_info->previous, *out = title_info;
|
||||
|
||||
while(cur_info)
|
||||
{
|
||||
count++;
|
||||
|
||||
if (cur_info->version.value > out->version.value)
|
||||
{
|
||||
out = cur_info;
|
||||
idx = count;
|
||||
}
|
||||
|
||||
cur_info = cur_info->previous;
|
||||
}
|
||||
|
||||
idx = (out != title_info ? (count - idx) : (count - 1));
|
||||
|
||||
cur_info = title_info->next;
|
||||
|
||||
while(cur_info)
|
||||
{
|
||||
count++;
|
||||
|
||||
if (cur_info->version.value > out->version.value)
|
||||
{
|
||||
out = cur_info;
|
||||
idx = (count - 1);
|
||||
}
|
||||
|
||||
cur_info = cur_info->next;
|
||||
}
|
||||
|
||||
*out_idx = idx;
|
||||
*out_count = count;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void freeNcaList(void)
|
||||
{
|
||||
/* Free all previously allocated data. */
|
||||
|
@ -1966,7 +2025,7 @@ static bool waitForGameCard(void)
|
|||
|
||||
if (status != GameCardStatus_InsertedAndInfoLoaded)
|
||||
{
|
||||
consolePrint("press any button\n");
|
||||
consolePrint("press any button to go back\n");
|
||||
utilsWaitForButtonPress(0);
|
||||
return false;
|
||||
}
|
||||
|
@ -2502,7 +2561,7 @@ static bool saveGameCardIdSet(void *userdata)
|
|||
u32 crc = 0;
|
||||
char *filename = NULL;
|
||||
|
||||
if (!gamecardGetIdSet(&id_set))
|
||||
if (!gamecardGetCardIdSet(&id_set))
|
||||
{
|
||||
consolePrint("failed to get gamecard id set\n");
|
||||
goto end;
|
||||
|
@ -2987,8 +3046,9 @@ static bool saveNintendoContentArchive(void *userdata)
|
|||
|
||||
consolePrint("nca size: 0x%lX\n", shared_thread_data->total_size);
|
||||
|
||||
snprintf(path, MAX_ELEMENTS(path), "/%s.%s", nca_thread_data.nca_ctx->content_id_str, content_info->content_type == NcmContentType_Meta ? "cnmt.nca" : "nca");
|
||||
snprintf(subdir, MAX_ELEMENTS(subdir), "NCA/%s", nca_thread_data.nca_ctx->storage_id == NcmStorageId_BuiltInSystem ? "System" : "User");
|
||||
snprintf(path, MAX_ELEMENTS(path), "/%s.%s", nca_thread_data.nca_ctx->content_id_str, content_info->content_type == NcmContentType_Meta ? "cnmt.nca" : "nca");
|
||||
|
||||
filename = generateOutputTitleFileName(title_info, subdir, path);
|
||||
if (!filename) goto end;
|
||||
|
||||
|
@ -3136,14 +3196,8 @@ static bool saveNintendoContentArchiveFsSection(void *userdata)
|
|||
}
|
||||
|
||||
/* Initialize base/patch NCA context, if needed. */
|
||||
if (g_ncaBasePatchTitleInfo)
|
||||
if (base_patch_content_info)
|
||||
{
|
||||
if (!base_patch_content_info)
|
||||
{
|
||||
consolePrint("unable to find content with type %s and id offset %u in selected base/patch title!\n", titleGetNcmContentTypeName(content_type), nca_ctx->id_offset);
|
||||
goto end;
|
||||
}
|
||||
|
||||
base_patch_nca_ctx = calloc(1, sizeof(NcaContext));
|
||||
if (!base_patch_nca_ctx)
|
||||
{
|
||||
|
@ -3160,6 +3214,12 @@ static bool saveNintendoContentArchiveFsSection(void *userdata)
|
|||
|
||||
/* Use a matching NCA FS section entry. */
|
||||
base_patch_nca_fs_ctx = &(base_patch_nca_ctx->fs_ctx[nca_fs_ctx->section_idx]);
|
||||
} else
|
||||
if (title_type != NcmContentMetaType_Patch)
|
||||
{
|
||||
/* Handles edge cases where a specific NCA from an update has no matching equivalent by type/ID offset in its base title (e.g. Fall Guys' HtmlDocument NCA). */
|
||||
consolePrint("unable to find content with type %s and id offset %u in selected base/patch title!\n", titleGetNcmContentTypeName(content_type), nca_ctx->id_offset);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (section_type == NcaFsSectionType_PartitionFs)
|
||||
|
@ -3233,8 +3293,7 @@ static bool saveRawPartitionFsSection(PartitionFileSystemContext *pfs_ctx, bool
|
|||
filename = generateOutputLayeredFsFileName(title_id + nca_ctx->id_offset, NULL, "exefs.nsp");
|
||||
} else {
|
||||
snprintf(subdir, MAX_ELEMENTS(subdir), "NCA FS/%s/Raw", nca_ctx->storage_id == NcmStorageId_BuiltInSystem ? "System" : "User");
|
||||
snprintf(path, MAX_ELEMENTS(path), "/%s #%u (%s)/Section #%u (%s).nsp", titleGetNcmContentTypeName(nca_ctx->content_type), nca_ctx->id_offset, nca_ctx->content_id_str, \
|
||||
nca_fs_ctx->section_idx, ncaGetFsSectionTypeName(nca_fs_ctx));
|
||||
snprintf(path, MAX_ELEMENTS(path), "/%s #%u/%u.nsp", titleGetNcmContentTypeName(nca_ctx->content_type), nca_ctx->id_offset, nca_fs_ctx->section_idx);
|
||||
|
||||
TitleInfo *title_info = (title_id == g_ncaUserTitleInfo->meta_key.id ? g_ncaUserTitleInfo : g_ncaBasePatchTitleInfo);
|
||||
filename = generateOutputTitleFileName(title_info, subdir, path);
|
||||
|
@ -3389,8 +3448,7 @@ static bool saveRawRomFsSection(RomFileSystemContext *romfs_ctx, bool use_layere
|
|||
filename = generateOutputLayeredFsFileName(title_id + nca_ctx->id_offset, NULL, "romfs.bin");
|
||||
} else {
|
||||
snprintf(subdir, MAX_ELEMENTS(subdir), "NCA FS/%s/Raw", nca_ctx->storage_id == NcmStorageId_BuiltInSystem ? "System" : "User");
|
||||
snprintf(path, MAX_ELEMENTS(path), "/%s #%u (%s)/Section #%u (%s).bin", titleGetNcmContentTypeName(nca_ctx->content_type), nca_ctx->id_offset, nca_ctx->content_id_str, \
|
||||
nca_fs_ctx->section_idx, ncaGetFsSectionTypeName(nca_fs_ctx));
|
||||
snprintf(path, MAX_ELEMENTS(path), "/%s #%u/%u.bin", titleGetNcmContentTypeName(nca_ctx->content_type), nca_ctx->id_offset, nca_fs_ctx->section_idx);
|
||||
|
||||
TitleInfo *title_info = (title_id == g_ncaUserTitleInfo->meta_key.id ? g_ncaUserTitleInfo : g_ncaBasePatchTitleInfo);
|
||||
filename = generateOutputTitleFileName(title_info, subdir, path);
|
||||
|
@ -4055,8 +4113,7 @@ static void extractedPartitionFsReadThreadFunc(void *arg)
|
|||
filename = generateOutputLayeredFsFileName(title_id + nca_ctx->id_offset, NULL, "exefs");
|
||||
} else {
|
||||
snprintf(subdir, MAX_ELEMENTS(subdir), "NCA FS/%s/Extracted", nca_ctx->storage_id == NcmStorageId_BuiltInSystem ? "System" : "User");
|
||||
snprintf(pfs_path, MAX_ELEMENTS(pfs_path), "/%s #%u (%s)/Section #%u (%s)", titleGetNcmContentTypeName(nca_ctx->content_type), nca_ctx->id_offset, nca_ctx->content_id_str, \
|
||||
nca_fs_ctx->section_idx, ncaGetFsSectionTypeName(nca_fs_ctx));
|
||||
snprintf(pfs_path, MAX_ELEMENTS(pfs_path), "/%s #%u/%u", titleGetNcmContentTypeName(nca_ctx->content_type), nca_ctx->id_offset, nca_fs_ctx->section_idx);
|
||||
|
||||
TitleInfo *title_info = (title_id == g_ncaUserTitleInfo->meta_key.id ? g_ncaUserTitleInfo : g_ncaBasePatchTitleInfo);
|
||||
filename = generateOutputTitleFileName(title_info, subdir, pfs_path);
|
||||
|
@ -4360,8 +4417,7 @@ static void extractedRomFsReadThreadFunc(void *arg)
|
|||
filename = generateOutputLayeredFsFileName(title_id + nca_ctx->id_offset, NULL, "romfs");
|
||||
} else {
|
||||
snprintf(subdir, MAX_ELEMENTS(subdir), "NCA FS/%s/Extracted", nca_ctx->storage_id == NcmStorageId_BuiltInSystem ? "System" : "User");
|
||||
snprintf(romfs_path, MAX_ELEMENTS(romfs_path), "/%s #%u (%s)/Section #%u (%s)", titleGetNcmContentTypeName(nca_ctx->content_type), nca_ctx->id_offset, nca_ctx->content_id_str, \
|
||||
nca_fs_ctx->section_idx, ncaGetFsSectionTypeName(nca_fs_ctx));
|
||||
snprintf(romfs_path, MAX_ELEMENTS(romfs_path), "/%s #%u/%u", titleGetNcmContentTypeName(nca_ctx->content_type), nca_ctx->id_offset, nca_fs_ctx->section_idx);
|
||||
|
||||
TitleInfo *title_info = (title_id == g_ncaUserTitleInfo->meta_key.id ? g_ncaUserTitleInfo : g_ncaBasePatchTitleInfo);
|
||||
filename = generateOutputTitleFileName(title_info, subdir, romfs_path);
|
||||
|
@ -4427,7 +4483,7 @@ static void extractedRomFsReadThreadFunc(void *arg)
|
|||
|
||||
/* Retrieve RomFS file entry information and generate output path. */
|
||||
shared_thread_data->read_error = (!(romfs_file_entry = romfsGetCurrentFileEntry(romfs_ctx)) || \
|
||||
!romfsGeneratePathFromFileEntry(romfs_ctx, romfs_file_entry, romfs_path + filename_len, FS_MAX_PATH - filename_len, romfs_illegal_char_replace_type));
|
||||
!romfsGeneratePathFromFileEntry(romfs_ctx, romfs_file_entry, romfs_path + filename_len, sizeof(romfs_path) - filename_len, romfs_illegal_char_replace_type));
|
||||
if (shared_thread_data->read_error)
|
||||
{
|
||||
condvarWakeAll(&g_writeCondvar);
|
||||
|
@ -4758,15 +4814,15 @@ static void nspThreadFunc(void *arg)
|
|||
u8 *raw_cert_chain = NULL;
|
||||
u64 raw_cert_chain_size = 0;
|
||||
|
||||
PartitionFileSystemFileContext pfs_file_ctx = {0};
|
||||
pfsInitializeFileContext(&pfs_file_ctx);
|
||||
PartitionFileSystemImageContext pfs_img_ctx = {0};
|
||||
pfsInitializeImageContext(&pfs_img_ctx);
|
||||
|
||||
char entry_name[64] = {0};
|
||||
u64 nsp_header_size = 0, nsp_size = 0, nsp_offset = 0;
|
||||
char *tmp_name = NULL;
|
||||
|
||||
Sha256Context sha256_ctx = {0};
|
||||
u8 sha256_hash[SHA256_HASH_SIZE] = {0};
|
||||
Sha256Context clean_sha256_ctx = {0}, dirty_sha256_ctx = {0};
|
||||
u8 clean_sha256_hash[SHA256_HASH_SIZE] = {0}, dirty_sha256_hash[SHA256_HASH_SIZE] = {0};
|
||||
|
||||
if (!nsp_thread_data || !(title_info = (TitleInfo*)nsp_thread_data->data) || !title_info->content_count || !title_info->content_infos) goto end;
|
||||
|
||||
|
@ -5023,7 +5079,7 @@ static void nspThreadFunc(void *arg)
|
|||
NcaContext *cur_nca_ctx = &(nca_ctx[i]);
|
||||
sprintf(entry_name, "%s.%s", cur_nca_ctx->content_id_str, cur_nca_ctx->content_type == NcmContentType_Meta ? "cnmt.nca" : "nca");
|
||||
|
||||
if (!pfsAddEntryInformationToFileContext(&pfs_file_ctx, entry_name, cur_nca_ctx->content_size, NULL))
|
||||
if (!pfsAddEntryInformationToImageContext(&pfs_img_ctx, entry_name, cur_nca_ctx->content_size, NULL))
|
||||
{
|
||||
consolePrint("pfs add entry failed: %s\n", entry_name);
|
||||
goto end;
|
||||
|
@ -5034,7 +5090,7 @@ static void nspThreadFunc(void *arg)
|
|||
if (generate_authoringtool_data)
|
||||
{
|
||||
sprintf(entry_name, "%s.cnmt.xml", meta_nca_ctx->content_id_str);
|
||||
if (!pfsAddEntryInformationToFileContext(&pfs_file_ctx, entry_name, cnmt_ctx.authoring_tool_xml_size, &(meta_nca_ctx->content_type_ctx_data_idx)))
|
||||
if (!pfsAddEntryInformationToImageContext(&pfs_img_ctx, entry_name, cnmt_ctx.authoring_tool_xml_size, &(meta_nca_ctx->content_type_ctx_data_idx)))
|
||||
{
|
||||
consolePrint("pfs add entry failed: %s\n", entry_name);
|
||||
goto end;
|
||||
|
@ -5055,7 +5111,7 @@ static void nspThreadFunc(void *arg)
|
|||
{
|
||||
ProgramInfoContext *cur_program_info_ctx = (ProgramInfoContext*)cur_nca_ctx->content_type_ctx;
|
||||
sprintf(entry_name, "%s.programinfo.xml", cur_nca_ctx->content_id_str);
|
||||
ret = pfsAddEntryInformationToFileContext(&pfs_file_ctx, entry_name, cur_program_info_ctx->authoring_tool_xml_size, &(cur_nca_ctx->content_type_ctx_data_idx));
|
||||
ret = pfsAddEntryInformationToImageContext(&pfs_img_ctx, entry_name, cur_program_info_ctx->authoring_tool_xml_size, &(cur_nca_ctx->content_type_ctx_data_idx));
|
||||
break;
|
||||
}
|
||||
case NcmContentType_Control:
|
||||
|
@ -5066,7 +5122,7 @@ static void nspThreadFunc(void *arg)
|
|||
{
|
||||
NacpIconContext *icon_ctx = &(cur_nacp_ctx->icon_ctx[j]);
|
||||
sprintf(entry_name, "%s.nx.%s.jpg", cur_nca_ctx->content_id_str, nacpGetLanguageString(icon_ctx->language));
|
||||
if (!pfsAddEntryInformationToFileContext(&pfs_file_ctx, entry_name, icon_ctx->icon_size, j == 0 ? &(cur_nca_ctx->content_type_ctx_data_idx) : NULL))
|
||||
if (!pfsAddEntryInformationToImageContext(&pfs_img_ctx, entry_name, icon_ctx->icon_size, j == 0 ? &(cur_nca_ctx->content_type_ctx_data_idx) : NULL))
|
||||
{
|
||||
consolePrint("pfs add entry failed: %s\n", entry_name);
|
||||
goto end;
|
||||
|
@ -5074,14 +5130,14 @@ static void nspThreadFunc(void *arg)
|
|||
}
|
||||
|
||||
sprintf(entry_name, "%s.nacp.xml", cur_nca_ctx->content_id_str);
|
||||
ret = pfsAddEntryInformationToFileContext(&pfs_file_ctx, entry_name, cur_nacp_ctx->authoring_tool_xml_size, !cur_nacp_ctx->icon_count ? &(cur_nca_ctx->content_type_ctx_data_idx) : NULL);
|
||||
ret = pfsAddEntryInformationToImageContext(&pfs_img_ctx, entry_name, cur_nacp_ctx->authoring_tool_xml_size, !cur_nacp_ctx->icon_count ? &(cur_nca_ctx->content_type_ctx_data_idx) : NULL);
|
||||
break;
|
||||
}
|
||||
case NcmContentType_LegalInformation:
|
||||
{
|
||||
LegalInfoContext *cur_legal_info_ctx = (LegalInfoContext*)cur_nca_ctx->content_type_ctx;
|
||||
sprintf(entry_name, "%s.legalinfo.xml", cur_nca_ctx->content_id_str);
|
||||
ret = pfsAddEntryInformationToFileContext(&pfs_file_ctx, entry_name, cur_legal_info_ctx->authoring_tool_xml_size, &(cur_nca_ctx->content_type_ctx_data_idx));
|
||||
ret = pfsAddEntryInformationToImageContext(&pfs_img_ctx, entry_name, cur_legal_info_ctx->authoring_tool_xml_size, &(cur_nca_ctx->content_type_ctx_data_idx));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -5099,28 +5155,28 @@ static void nspThreadFunc(void *arg)
|
|||
if (retrieve_tik_cert)
|
||||
{
|
||||
sprintf(entry_name, "%s.tik", tik.rights_id_str);
|
||||
if (!pfsAddEntryInformationToFileContext(&pfs_file_ctx, entry_name, tik.size, NULL))
|
||||
if (!pfsAddEntryInformationToImageContext(&pfs_img_ctx, entry_name, tik.size, NULL))
|
||||
{
|
||||
consolePrint("pfs add entry failed: %s\n", entry_name);
|
||||
goto end;
|
||||
}
|
||||
|
||||
sprintf(entry_name, "%s.cert", tik.rights_id_str);
|
||||
if (!pfsAddEntryInformationToFileContext(&pfs_file_ctx, entry_name, raw_cert_chain_size, NULL))
|
||||
if (!pfsAddEntryInformationToImageContext(&pfs_img_ctx, entry_name, raw_cert_chain_size, NULL))
|
||||
{
|
||||
consolePrint("pfs add entry failed: %s\n", entry_name);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
// write buffer to memory buffer
|
||||
if (!pfsWriteFileContextHeaderToMemoryBuffer(&pfs_file_ctx, buf, BLOCK_SIZE, &nsp_header_size))
|
||||
// write pfs header to memory buffer
|
||||
if (!pfsWriteImageContextHeaderToMemoryBuffer(&pfs_img_ctx, buf, BLOCK_SIZE, &nsp_header_size))
|
||||
{
|
||||
consolePrint("pfs write header to mem #1 failed\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
nsp_size = (nsp_header_size + pfs_file_ctx.fs_size);
|
||||
nsp_size = (nsp_header_size + pfs_img_ctx.fs_size);
|
||||
consolePrint("nsp header size: 0x%lX | nsp size: 0x%lX\n", nsp_header_size, nsp_size);
|
||||
consoleRefresh();
|
||||
|
||||
|
@ -5183,8 +5239,8 @@ static void nspThreadFunc(void *arg)
|
|||
NcaContext *cur_nca_ctx = &(nca_ctx[i]);
|
||||
u64 blksize = BLOCK_SIZE;
|
||||
|
||||
memset(&sha256_ctx, 0, sizeof(Sha256Context));
|
||||
sha256ContextCreate(&sha256_ctx);
|
||||
sha256ContextCreate(&clean_sha256_ctx);
|
||||
sha256ContextCreate(&dirty_sha256_ctx);
|
||||
|
||||
if (cur_nca_ctx->content_type == NcmContentType_Meta && (!cnmtGenerateNcaPatch(&cnmt_ctx) || !ncaEncryptHeader(cur_nca_ctx)))
|
||||
{
|
||||
|
@ -5196,7 +5252,7 @@ static void nspThreadFunc(void *arg)
|
|||
|
||||
if (dev_idx == 1)
|
||||
{
|
||||
tmp_name = pfsGetEntryNameByIndexFromFileContext(&pfs_file_ctx, i);
|
||||
tmp_name = pfsGetEntryNameByIndexFromImageContext(&pfs_img_ctx, i);
|
||||
if (!usbSendFileProperties(cur_nca_ctx->content_size, tmp_name))
|
||||
{
|
||||
consolePrint("usb send file properties \"%s\" failed\n", tmp_name);
|
||||
|
@ -5225,6 +5281,9 @@ static void nspThreadFunc(void *arg)
|
|||
goto end;
|
||||
}
|
||||
|
||||
// update clean hash calculation
|
||||
sha256ContextUpdate(&clean_sha256_ctx, buf, blksize);
|
||||
|
||||
if (dirty_header)
|
||||
{
|
||||
// write re-encrypted headers
|
||||
|
@ -5250,8 +5309,8 @@ static void nspThreadFunc(void *arg)
|
|||
dirty_header = (!cur_nca_ctx->header_written || cur_nca_ctx->content_type_ctx_patch);
|
||||
}
|
||||
|
||||
// update hash calculation
|
||||
sha256ContextUpdate(&sha256_ctx, buf, blksize);
|
||||
// update dirty hash calculation
|
||||
sha256ContextUpdate(&dirty_sha256_ctx, buf, blksize);
|
||||
|
||||
// write nca chunk
|
||||
if (dev_idx == 1)
|
||||
|
@ -5266,24 +5325,35 @@ static void nspThreadFunc(void *arg)
|
|||
}
|
||||
}
|
||||
|
||||
// get hash
|
||||
sha256ContextGetHash(&sha256_ctx, sha256_hash);
|
||||
// get hashes
|
||||
sha256ContextGetHash(&clean_sha256_ctx, clean_sha256_hash);
|
||||
sha256ContextGetHash(&dirty_sha256_ctx, dirty_sha256_hash);
|
||||
|
||||
// update content id and hash
|
||||
ncaUpdateContentIdAndHash(cur_nca_ctx, sha256_hash);
|
||||
|
||||
// update cnmt
|
||||
if (!cnmtUpdateContentInfo(&cnmt_ctx, cur_nca_ctx))
|
||||
// verify content hash
|
||||
if (!cnmtVerifyContentHash(&cnmt_ctx, cur_nca_ctx, clean_sha256_hash))
|
||||
{
|
||||
consolePrint("cnmt update content info failed\n");
|
||||
consolePrint("sha256 checksum mismatch for nca \"%s\"\n", cur_nca_ctx->content_id_str);
|
||||
goto end;
|
||||
}
|
||||
|
||||
// update pfs entry name
|
||||
if (!pfsUpdateEntryNameFromFileContext(&pfs_file_ctx, i, cur_nca_ctx->content_id_str))
|
||||
if (memcmp(clean_sha256_hash, dirty_sha256_hash, SHA256_HASH_SIZE) != 0)
|
||||
{
|
||||
consolePrint("pfs update entry name failed for nca \"%s\"\n", cur_nca_ctx->content_id_str);
|
||||
goto end;
|
||||
// update content id and hash
|
||||
ncaUpdateContentIdAndHash(cur_nca_ctx, dirty_sha256_hash);
|
||||
|
||||
// update cnmt
|
||||
if (!cnmtUpdateContentInfo(&cnmt_ctx, cur_nca_ctx))
|
||||
{
|
||||
consolePrint("cnmt update content info failed\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
// update pfs entry name
|
||||
if (!pfsUpdateEntryNameFromImageContext(&pfs_img_ctx, i, cur_nca_ctx->content_id_str))
|
||||
{
|
||||
consolePrint("pfs update entry name failed for nca \"%s\"\n", cur_nca_ctx->content_id_str);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5299,7 +5369,7 @@ static void nspThreadFunc(void *arg)
|
|||
// write cnmt xml
|
||||
if (dev_idx == 1)
|
||||
{
|
||||
tmp_name = pfsGetEntryNameByIndexFromFileContext(&pfs_file_ctx, meta_nca_ctx->content_type_ctx_data_idx);
|
||||
tmp_name = pfsGetEntryNameByIndexFromImageContext(&pfs_img_ctx, meta_nca_ctx->content_type_ctx_data_idx);
|
||||
if (!usbSendFileProperties(cnmt_ctx.authoring_tool_xml_size, tmp_name) || !usbSendFileData(cnmt_ctx.authoring_tool_xml, cnmt_ctx.authoring_tool_xml_size))
|
||||
{
|
||||
consolePrint("send \"%s\" failed\n", tmp_name);
|
||||
|
@ -5313,7 +5383,7 @@ static void nspThreadFunc(void *arg)
|
|||
nsp_thread_data->data_written += cnmt_ctx.authoring_tool_xml_size;
|
||||
|
||||
// update cnmt xml pfs entry name
|
||||
if (!pfsUpdateEntryNameFromFileContext(&pfs_file_ctx, meta_nca_ctx->content_type_ctx_data_idx, meta_nca_ctx->content_id_str))
|
||||
if (!pfsUpdateEntryNameFromImageContext(&pfs_img_ctx, meta_nca_ctx->content_type_ctx_data_idx, meta_nca_ctx->content_id_str))
|
||||
{
|
||||
consolePrint("pfs update entry name cnmt xml failed\n");
|
||||
goto end;
|
||||
|
@ -5353,7 +5423,7 @@ static void nspThreadFunc(void *arg)
|
|||
// write icon
|
||||
if (dev_idx == 1)
|
||||
{
|
||||
tmp_name = pfsGetEntryNameByIndexFromFileContext(&pfs_file_ctx, data_idx);
|
||||
tmp_name = pfsGetEntryNameByIndexFromImageContext(&pfs_img_ctx, data_idx);
|
||||
if (!usbSendFileProperties(icon_ctx->icon_size, tmp_name) || !usbSendFileData(icon_ctx->icon_data, icon_ctx->icon_size))
|
||||
{
|
||||
consolePrint("send \"%s\" failed\n", tmp_name);
|
||||
|
@ -5367,7 +5437,7 @@ static void nspThreadFunc(void *arg)
|
|||
nsp_thread_data->data_written += icon_ctx->icon_size;
|
||||
|
||||
// update pfs entry name
|
||||
if (!pfsUpdateEntryNameFromFileContext(&pfs_file_ctx, data_idx++, cur_nca_ctx->content_id_str))
|
||||
if (!pfsUpdateEntryNameFromImageContext(&pfs_img_ctx, data_idx++, cur_nca_ctx->content_id_str))
|
||||
{
|
||||
consolePrint("pfs update entry name failed for icon \"%s\" (%u)\n", cur_nca_ctx->content_id_str, icon_ctx->language);
|
||||
goto end;
|
||||
|
@ -5390,7 +5460,7 @@ static void nspThreadFunc(void *arg)
|
|||
// write xml
|
||||
if (dev_idx == 1)
|
||||
{
|
||||
tmp_name = pfsGetEntryNameByIndexFromFileContext(&pfs_file_ctx, data_idx);
|
||||
tmp_name = pfsGetEntryNameByIndexFromImageContext(&pfs_img_ctx, data_idx);
|
||||
if (!usbSendFileProperties(authoring_tool_xml_size, tmp_name) || !usbSendFileData(authoring_tool_xml, authoring_tool_xml_size))
|
||||
{
|
||||
consolePrint("send \"%s\" failed\n", tmp_name);
|
||||
|
@ -5404,7 +5474,7 @@ static void nspThreadFunc(void *arg)
|
|||
nsp_thread_data->data_written += authoring_tool_xml_size;
|
||||
|
||||
// update pfs entry name
|
||||
if (!pfsUpdateEntryNameFromFileContext(&pfs_file_ctx, data_idx, cur_nca_ctx->content_id_str))
|
||||
if (!pfsUpdateEntryNameFromImageContext(&pfs_img_ctx, data_idx, cur_nca_ctx->content_id_str))
|
||||
{
|
||||
consolePrint("pfs update entry name failed for xml \"%s\"\n", cur_nca_ctx->content_id_str);
|
||||
goto end;
|
||||
|
@ -5416,7 +5486,7 @@ static void nspThreadFunc(void *arg)
|
|||
// write ticket
|
||||
if (dev_idx == 1)
|
||||
{
|
||||
tmp_name = pfsGetEntryNameByIndexFromFileContext(&pfs_file_ctx, pfs_file_ctx.header.entry_count - 2);
|
||||
tmp_name = pfsGetEntryNameByIndexFromImageContext(&pfs_img_ctx, pfs_img_ctx.header.entry_count - 2);
|
||||
if (!usbSendFileProperties(tik.size, tmp_name) || !usbSendFileData(tik.data, tik.size))
|
||||
{
|
||||
consolePrint("send \"%s\" failed\n", tmp_name);
|
||||
|
@ -5432,7 +5502,7 @@ static void nspThreadFunc(void *arg)
|
|||
// write cert
|
||||
if (dev_idx == 1)
|
||||
{
|
||||
tmp_name = pfsGetEntryNameByIndexFromFileContext(&pfs_file_ctx, pfs_file_ctx.header.entry_count - 1);
|
||||
tmp_name = pfsGetEntryNameByIndexFromImageContext(&pfs_img_ctx, pfs_img_ctx.header.entry_count - 1);
|
||||
if (!usbSendFileProperties(raw_cert_chain_size, tmp_name) || !usbSendFileData(raw_cert_chain, raw_cert_chain_size))
|
||||
{
|
||||
consolePrint("send \"%s\" failed\n", tmp_name);
|
||||
|
@ -5447,7 +5517,7 @@ static void nspThreadFunc(void *arg)
|
|||
}
|
||||
|
||||
// write new pfs0 header
|
||||
if (!pfsWriteFileContextHeaderToMemoryBuffer(&pfs_file_ctx, buf, BLOCK_SIZE, &nsp_header_size))
|
||||
if (!pfsWriteImageContextHeaderToMemoryBuffer(&pfs_img_ctx, buf, BLOCK_SIZE, &nsp_header_size))
|
||||
{
|
||||
consolePrint("pfs write header to mem #2 failed\n");
|
||||
goto end;
|
||||
|
@ -5492,7 +5562,7 @@ end:
|
|||
}
|
||||
}
|
||||
|
||||
pfsFreeFileContext(&pfs_file_ctx);
|
||||
pfsFreeImageContext(&pfs_img_ctx);
|
||||
|
||||
if (raw_cert_chain) free(raw_cert_chain);
|
||||
|
||||
|
|
|
@ -318,6 +318,9 @@ typedef struct {
|
|||
/// Initializes a ContentMetaContext using a previously initialized NcaContext (which must belong to a Meta NCA).
|
||||
bool cnmtInitializeContext(ContentMetaContext *out, NcaContext *nca_ctx);
|
||||
|
||||
/// Looks for a NcmPackagedContentInfo entry with a content ID that matches the one from the input NcaContext and verifies its hash.
|
||||
bool cnmtVerifyContentHash(ContentMetaContext *cnmt_ctx, NcaContext *nca_ctx, const u8 *hash);
|
||||
|
||||
/// Updates NcmPackagedContentInfo data for the content entry with size, type and ID offset values that match the ones from the input NcaContext.
|
||||
bool cnmtUpdateContentInfo(ContentMetaContext *cnmt_ctx, NcaContext *nca_ctx);
|
||||
|
||||
|
|
|
@ -45,19 +45,57 @@ typedef struct {
|
|||
|
||||
NXDT_ASSERT(FsGameCardCertificate, 0x200);
|
||||
|
||||
typedef enum {
|
||||
FsCardId1MakerCode_MegaChips = 0xC2,
|
||||
FsCardId1MakerCode_Lapis = 0xAE,
|
||||
FsCardId1MakerCode_Unknown = 0x36 ///< Seen in TLoZ:TotK, SMBW and other modern releases.
|
||||
} FsCardId1MakerCode;
|
||||
|
||||
typedef enum {
|
||||
FsCardId1MemoryType_None = 0,
|
||||
FsCardId1MemoryType_CardModeT1 = BIT(0),
|
||||
FsCardId1MemoryType_CardModeT2 = BIT(1),
|
||||
FsCardId1MemoryType_Unknown1 = BIT(2), ///< Related to CardMode?
|
||||
FsCardId1MemoryType_IsNand = BIT(3), ///< 0: Rom, 1: Nand.
|
||||
FsCardId1MemoryType_Unknown2 = BIT(4), ///< Related to Nand memory type?
|
||||
FsCardId1MemoryType_IsLate = BIT(5), ///< 0: Fast, 1: Late.
|
||||
FsCardId1MemoryType_Unknown3 = BIT(6),
|
||||
FsCardId1MemoryType_Unknown4 = BIT(7),
|
||||
FsCardId1MemoryType_Count = 8, ///< Total values supported by this enum.
|
||||
|
||||
///< Values defined in Atmosphère source code.
|
||||
FsCardId1MemoryType_T1RomFast = FsCardId1MemoryType_CardModeT1,
|
||||
FsCardId1MemoryType_T2RomFast = FsCardId1MemoryType_CardModeT2,
|
||||
FsCardId1MemoryType_T1NandFast = (FsCardId1MemoryType_IsNand | FsCardId1MemoryType_CardModeT1),
|
||||
FsCardId1MemoryType_T2NandFast = (FsCardId1MemoryType_IsNand | FsCardId1MemoryType_CardModeT2),
|
||||
FsCardId1MemoryType_T1RomLate = (FsCardId1MemoryType_IsLate | FsCardId1MemoryType_CardModeT1),
|
||||
FsCardId1MemoryType_T2RomLate = (FsCardId1MemoryType_IsLate | FsCardId1MemoryType_CardModeT2),
|
||||
FsCardId1MemoryType_T1NandLate = (FsCardId1MemoryType_IsLate | FsCardId1MemoryType_IsNand | FsCardId1MemoryType_CardModeT1),
|
||||
FsCardId1MemoryType_T2NandLate = (FsCardId1MemoryType_IsLate | FsCardId1MemoryType_IsNand | FsCardId1MemoryType_CardModeT2)
|
||||
} FsCardId1MemoryType;
|
||||
|
||||
typedef struct {
|
||||
u8 maker_code; ///< Usually 0xC2 (Macronix).
|
||||
u8 maker_code; ///< FsCardId1MakerCode.
|
||||
u8 memory_capacity; ///< Matches GameCardRomSize.
|
||||
u8 reserved; ///< Known values: 0x06, 0x09, 0x0A.
|
||||
u8 memory_type; ///< Usually 0x21.
|
||||
u8 reserved; ///< Known values: 0x00, 0x01, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0C, 0x0D, 0x0E, 0x80.
|
||||
u8 memory_type; ///< FsCardId1MemoryType.
|
||||
} FsCardId1;
|
||||
|
||||
NXDT_ASSERT(FsCardId1, 0x4);
|
||||
|
||||
typedef enum {
|
||||
FsCardId2CardType_Rom = 0,
|
||||
FsCardId2CardType_WritableDevT1 = 1,
|
||||
FsCardId2CardType_WritableProdT1 = 2,
|
||||
FsCardId2CardType_WritableDevT2 = 3,
|
||||
FsCardId2CardType_WritableProdT2 = 4,
|
||||
FsCardId2CardType_Count = 5 ///< Total values supported by this enum.
|
||||
} FsCardId2CardType;
|
||||
|
||||
typedef struct {
|
||||
u8 card_security_number; ///< Usually 0x02.
|
||||
u8 card_type; ///< Usually 0x00.
|
||||
u8 reserved[0x2]; ///< Usually filled with zeroes.
|
||||
u8 sel_t1_key; ///< Matches sel_t1_key value from GameCardHeader (usually 0x02).
|
||||
u8 card_type; ///< FsCardId2CardType.
|
||||
u8 reserved[0x2]; ///< Usually filled with zeroes.
|
||||
} FsCardId2;
|
||||
|
||||
NXDT_ASSERT(FsCardId2, 0x4);
|
||||
|
@ -84,7 +122,6 @@ Result fsOpenGameCardDetectionEventNotifier(FsEventNotifier *out);
|
|||
/// 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);
|
||||
Result fsDeviceOperatorGetGameCardIdSet(FsDeviceOperator *d, FsGameCardIdSet *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ typedef struct {
|
|||
union {
|
||||
u8 value[0x10];
|
||||
struct {
|
||||
u64 package_id; ///< Matches package_id from GameCardHeader.
|
||||
u8 package_id[0x8]; ///< Matches package_id from GameCardHeader.
|
||||
u8 reserved[0x8]; ///< Just zeroes.
|
||||
};
|
||||
};
|
||||
|
@ -200,14 +200,14 @@ NXDT_ASSERT(GameCardInfo, 0x70);
|
|||
typedef struct {
|
||||
u8 signature[0x100]; ///< RSA-2048-PSS with SHA-256 signature over the rest of the header.
|
||||
u32 magic; ///< "HEAD".
|
||||
u32 rom_area_start_page_address; ///< Expressed in GAMECARD_PAGE_SIZE units.
|
||||
u32 backup_area_start_page_address; ///< Always 0xFFFFFFFF.
|
||||
u32 rom_area_start_page; ///< Expressed in GAMECARD_PAGE_SIZE units.
|
||||
u32 backup_area_start_page; ///< Always 0xFFFFFFFF.
|
||||
GameCardKeyIndex key_index;
|
||||
u8 rom_size; ///< GameCardRomSize.
|
||||
u8 header_version; ///< Always 0.
|
||||
u8 version; ///< Always 0x00.
|
||||
u8 flags; ///< GameCardFlags.
|
||||
u64 package_id; ///< Used for challenge-response authentication.
|
||||
u32 valid_data_end_address; ///< Expressed in GAMECARD_PAGE_SIZE units.
|
||||
u8 package_id[0x8]; ///< Used for challenge-response authentication.
|
||||
u32 valid_data_end_page; ///< Expressed in GAMECARD_PAGE_SIZE units.
|
||||
u8 reserved[0x4];
|
||||
u8 card_info_iv[AES_128_KEY_SIZE]; ///< AES-128-CBC IV for the CardInfo area (reversed).
|
||||
u64 partition_fs_header_address; ///< Root Hash File System header offset.
|
||||
|
@ -215,9 +215,9 @@ typedef struct {
|
|||
u8 partition_fs_header_hash[SHA256_HASH_SIZE];
|
||||
u8 initial_data_hash[SHA256_HASH_SIZE];
|
||||
u32 sel_sec; ///< GameCardSelSec.
|
||||
u32 sel_t1_key; ///< Always 2.
|
||||
u32 sel_key; ///< Always 0.
|
||||
u32 lim_area; ///< Expressed in GAMECARD_PAGE_SIZE units.
|
||||
u32 sel_t1_key; ///< Always 0x02.
|
||||
u32 sel_key; ///< Always 0x00.
|
||||
u32 lim_area_page; ///< Expressed in GAMECARD_PAGE_SIZE units.
|
||||
GameCardInfo card_info;
|
||||
} GameCardHeader;
|
||||
|
||||
|
@ -291,7 +291,7 @@ bool gamecardGetSecurityInformation(GameCardSecurityInformation *out);
|
|||
|
||||
/// Fills the provided FsGameCardIdSet pointer.
|
||||
/// This area can't be read using gamecardReadStorage().
|
||||
bool gamecardGetIdSet(FsGameCardIdSet *out);
|
||||
bool gamecardGetCardIdSet(FsGameCardIdSet *out);
|
||||
|
||||
/// Fills the provided pointers with LAFW blob data from FS program memory.
|
||||
/// 'out_lafw_blob' or 'out_lafw_version' may be set to NULL, but at least one of them must be a valid pointer.
|
||||
|
|
|
@ -444,7 +444,7 @@ struct _NcaContext {
|
|||
NcmContentId content_id; ///< Content ID for this NCA. Used to read NCA data from eMMC/SD. Retrieved from NcmContentInfo.
|
||||
char content_id_str[0x21];
|
||||
u8 hash[SHA256_HASH_SIZE]; ///< Manually calculated (if needed).
|
||||
char hash_str[0x41];
|
||||
char hash_str[SHA256_HASH_STR_SIZE];
|
||||
u8 format_version; ///< NcaVersion.
|
||||
u8 content_type; ///< NcmContentType. Retrieved from NcmContentInfo.
|
||||
u64 content_size; ///< Retrieved from NcmContentInfo.
|
||||
|
@ -561,7 +561,7 @@ bool ncaEncryptHeader(NcaContext *ctx);
|
|||
void ncaWriteEncryptedHeaderDataToMemoryBuffer(NcaContext *ctx, void *buf, u64 buf_size, u64 buf_offset);
|
||||
|
||||
/// Updates the content ID and hash from a NCA context using a provided SHA-256 checksum.
|
||||
void ncaUpdateContentIdAndHash(NcaContext *ctx, u8 hash[SHA256_HASH_SIZE]);
|
||||
void ncaUpdateContentIdAndHash(NcaContext *ctx, const u8 *hash);
|
||||
|
||||
/// Returns a pointer to a string holding the name of the section type from the provided NCA FS section context.
|
||||
const char *ncaGetFsSectionTypeName(NcaFsSectionContext *ctx);
|
||||
|
|
|
@ -61,13 +61,13 @@ typedef struct {
|
|||
u8 *header; ///< PartitionFileSystemHeader + (PartitionFileSystemEntry * entry_count) + Name Table.
|
||||
} PartitionFileSystemContext;
|
||||
|
||||
/// Used with Partition FS images (e.g. NSPs).
|
||||
/// Used to generate Partition FS images (e.g. NSPs).
|
||||
typedef struct {
|
||||
PartitionFileSystemHeader header; ///< Partition FS header. Holds the entry count and name table size.
|
||||
PartitionFileSystemEntry *entries; ///< Partition FS entries.
|
||||
char *name_table; ///< Name table.
|
||||
u64 fs_size; ///< Partition FS data size. Updated each time a new entry is added.
|
||||
} PartitionFileSystemFileContext;
|
||||
} PartitionFileSystemImageContext;
|
||||
|
||||
/// Initializes a Partition FS context.
|
||||
bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *nca_fs_ctx);
|
||||
|
@ -92,15 +92,15 @@ bool pfsGetTotalDataSize(PartitionFileSystemContext *ctx, u64 *out_size);
|
|||
/// Use the pfsWriteEntryPatchToMemoryBuffer() wrapper to write patch data generated by this function.
|
||||
bool pfsGenerateEntryPatch(PartitionFileSystemContext *ctx, PartitionFileSystemEntry *fs_entry, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalSha256Patch *out);
|
||||
|
||||
/// Adds a new Partition FS entry to an existing PartitionFileSystemFileContext, using the provided entry name and size.
|
||||
/// Adds a new Partition FS entry to an existing PartitionFileSystemImageContext, using the provided entry name and size.
|
||||
/// If 'out_entry_idx' is a valid pointer, the index to the new Partition FS entry will be saved to it.
|
||||
bool pfsAddEntryInformationToFileContext(PartitionFileSystemFileContext *ctx, const char *entry_name, u64 entry_size, u32 *out_entry_idx);
|
||||
bool pfsAddEntryInformationToImageContext(PartitionFileSystemImageContext *ctx, const char *entry_name, u64 entry_size, u32 *out_entry_idx);
|
||||
|
||||
/// Updates the name from a Partition FS entry in an existing PartitionFileSystemFileContext, using an entry index and the new entry name.
|
||||
bool pfsUpdateEntryNameFromFileContext(PartitionFileSystemFileContext *ctx, u32 entry_idx, const char *new_entry_name);
|
||||
/// Updates the name from a Partition FS entry in an existing PartitionFileSystemImageContext, using an entry index and the new entry name.
|
||||
bool pfsUpdateEntryNameFromImageContext(PartitionFileSystemImageContext *ctx, u32 entry_idx, const char *new_entry_name);
|
||||
|
||||
/// Generates a full Partition FS header from an existing PartitionFileSystemFileContext and writes it to the provided memory buffer.
|
||||
bool pfsWriteFileContextHeaderToMemoryBuffer(PartitionFileSystemFileContext *ctx, void *buf, u64 buf_size, u64 *out_header_size);
|
||||
/// Generates a full Partition FS header from an existing PartitionFileSystemImageContext and writes it to the provided memory buffer.
|
||||
bool pfsWriteImageContextHeaderToMemoryBuffer(PartitionFileSystemImageContext *ctx, void *buf, u64 buf_size, u64 *out_header_size);
|
||||
|
||||
/// Miscellaneous functions.
|
||||
|
||||
|
@ -165,35 +165,35 @@ NX_INLINE void pfsFreeEntryPatch(NcaHierarchicalSha256Patch *patch)
|
|||
ncaFreeHierarchicalSha256Patch(patch);
|
||||
}
|
||||
|
||||
NX_INLINE void pfsFreeFileContext(PartitionFileSystemFileContext *ctx)
|
||||
NX_INLINE void pfsFreeImageContext(PartitionFileSystemImageContext *ctx)
|
||||
{
|
||||
if (!ctx) return;
|
||||
if (ctx->entries) free(ctx->entries);
|
||||
if (ctx->name_table) free(ctx->name_table);
|
||||
memset(ctx, 0, sizeof(PartitionFileSystemFileContext));
|
||||
memset(ctx, 0, sizeof(PartitionFileSystemImageContext));
|
||||
}
|
||||
|
||||
NX_INLINE void pfsInitializeFileContext(PartitionFileSystemFileContext *ctx)
|
||||
NX_INLINE void pfsInitializeImageContext(PartitionFileSystemImageContext *ctx)
|
||||
{
|
||||
if (!ctx) return;
|
||||
pfsFreeFileContext(ctx);
|
||||
pfsFreeImageContext(ctx);
|
||||
ctx->header.magic = __builtin_bswap32(PFS0_MAGIC);
|
||||
}
|
||||
|
||||
NX_INLINE u32 pfsGetEntryCountFromFileContext(PartitionFileSystemFileContext *ctx)
|
||||
NX_INLINE u32 pfsGetEntryCountFromImageContext(PartitionFileSystemImageContext *ctx)
|
||||
{
|
||||
return (ctx ? ctx->header.entry_count : 0);
|
||||
}
|
||||
|
||||
NX_INLINE PartitionFileSystemEntry *pfsGetEntryByIndexFromFileContext(PartitionFileSystemFileContext *ctx, u32 idx)
|
||||
NX_INLINE PartitionFileSystemEntry *pfsGetEntryByIndexFromImageContext(PartitionFileSystemImageContext *ctx, u32 idx)
|
||||
{
|
||||
if (idx >= pfsGetEntryCountFromFileContext(ctx)) return NULL;
|
||||
if (idx >= pfsGetEntryCountFromImageContext(ctx)) return NULL;
|
||||
return &(ctx->entries[idx]);
|
||||
}
|
||||
|
||||
NX_INLINE char *pfsGetEntryNameByIndexFromFileContext(PartitionFileSystemFileContext *ctx, u32 idx)
|
||||
NX_INLINE char *pfsGetEntryNameByIndexFromImageContext(PartitionFileSystemImageContext *ctx, u32 idx)
|
||||
{
|
||||
PartitionFileSystemEntry *fs_entry = pfsGetEntryByIndexFromFileContext(ctx, idx);
|
||||
PartitionFileSystemEntry *fs_entry = pfsGetEntryByIndexFromImageContext(ctx, idx);
|
||||
if (!fs_entry || !ctx->name_table) return NULL;
|
||||
return (ctx->name_table + fs_entry->name_offset);
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ typedef enum {
|
|||
} RomFileSystemPathIllegalCharReplaceType;
|
||||
|
||||
/// Initializes a RomFS or Patch RomFS context.
|
||||
/// 'base_nca_fs_ctx' must always be provided.
|
||||
/// 'base_nca_fs_ctx' shall be NULL *only* if a NCA from an update has no matching equivalent available in its base title.
|
||||
/// 'patch_nca_fs_ctx' shall be NULL if not dealing with a Patch RomFS.
|
||||
bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *base_nca_fs_ctx, NcaFsSectionContext *patch_nca_fs_ctx);
|
||||
|
||||
|
|
|
@ -60,7 +60,9 @@
|
|||
|
||||
/* Global constants used throughout the application. */
|
||||
|
||||
#define THIRTY_FPS_DELAY (u64)33333333 /* 1 / 30 = 33.33 milliseconds. */
|
||||
#define SHA256_HASH_STR_SIZE ((SHA256_HASH_SIZE * 2) + 1) /* Includes NULL terminator. */
|
||||
|
||||
#define THIRTY_FPS_DELAY (u64)33333333 /* 1 / 30 = 33.33 milliseconds. */
|
||||
|
||||
#define FS_SYSMODULE_TID (u64)0x0100000000000000
|
||||
#define BOOT_SYSMODULE_TID (u64)0x0100000000000005
|
||||
|
@ -69,7 +71,7 @@
|
|||
#define SYSTEM_UPDATE_TID (u64)0x0100000000000816
|
||||
#define QLAUNCH_TID (u64)0x0100000000001000
|
||||
|
||||
#define FAT32_FILESIZE_LIMIT (u64)0xFFFFFFFF /* 4 GiB - 1 (4294967295 bytes). */
|
||||
#define FAT32_FILESIZE_LIMIT (u64)0xFFFFFFFF /* 4 GiB - 1 (4294967295 bytes). */
|
||||
|
||||
#define UTF8_BOM "\xEF\xBB\xBF"
|
||||
#define CRLF "\r\n"
|
||||
|
@ -93,22 +95,22 @@
|
|||
#define NRO_PATH DEVOPTAB_SDMC_DEVICE APP_BASE_PATH NRO_NAME
|
||||
#define NRO_TMP_PATH NRO_PATH ".tmp"
|
||||
|
||||
#define PROD_KEYS_FILE_PATH DEVOPTAB_SDMC_DEVICE HBMENU_BASE_PATH "prod.keys" /* Location used by Lockpick_RCM for retail unit keys. */
|
||||
#define DEV_KEYS_FILE_PATH DEVOPTAB_SDMC_DEVICE HBMENU_BASE_PATH "dev.keys" /* Location used by Lockpick_RCM for development unit keys. */
|
||||
#define PROD_KEYS_FILE_PATH DEVOPTAB_SDMC_DEVICE HBMENU_BASE_PATH "prod.keys" /* Location used by Lockpick_RCM for retail unit keys. */
|
||||
#define DEV_KEYS_FILE_PATH DEVOPTAB_SDMC_DEVICE HBMENU_BASE_PATH "dev.keys" /* Location used by Lockpick_RCM for development unit keys. */
|
||||
|
||||
#define LOG_FILE_NAME APP_TITLE ".log"
|
||||
#define LOG_BUF_SIZE 0x400000 /* 4 MiB. */
|
||||
#define LOG_FORCE_FLUSH 0 /* Forces a log buffer flush each time the logfile is written to. */
|
||||
#define LOG_BUF_SIZE 0x400000 /* 4 MiB. */
|
||||
#define LOG_FORCE_FLUSH 0 /* Forces a log buffer flush each time the logfile is written to. */
|
||||
|
||||
#define BIS_SYSTEM_PARTITION_MOUNT_NAME "sys:"
|
||||
|
||||
#define DOWNLOAD_TASK_INTERVAL 100 /* 100 milliseconds. */
|
||||
#define DOWNLOAD_TASK_INTERVAL 100 /* 100 milliseconds. */
|
||||
|
||||
#define HTTP_USER_AGENT APP_TITLE "/" APP_VERSION " (Nintendo Switch)"
|
||||
#define HTTP_CONNECT_TIMEOUT 10L /* 10 seconds. */
|
||||
#define HTTP_LOW_SPEED_LIMIT 30L /* 30 bytes per second. */
|
||||
#define HTTP_CONNECT_TIMEOUT 10L /* 10 seconds. */
|
||||
#define HTTP_LOW_SPEED_LIMIT 30L /* 30 bytes per second. */
|
||||
#define HTTP_LOW_SPEED_TIME HTTP_CONNECT_TIMEOUT
|
||||
#define HTTP_BUFFER_SIZE 131072L /* 128 KiB. */
|
||||
#define HTTP_BUFFER_SIZE 131072L /* 128 KiB. */
|
||||
|
||||
#define GITHUB_URL "https://github.com"
|
||||
#define GITHUB_API_URL "https://api.github.com"
|
||||
|
|
|
@ -42,6 +42,7 @@ namespace nxdt::views
|
|||
|
||||
void ProcessGameCardStatus(GameCardStatus gc_status);
|
||||
std::string GetFormattedSizeString(GameCardSizeFunc func);
|
||||
std::string GetCardIdSetString(FsGameCardIdSet *card_id_set);
|
||||
void PopulateList(void);
|
||||
|
||||
public:
|
||||
|
|
|
@ -25,7 +25,8 @@
|
|||
"lafw_version_value": "%lu or greater (%s)",
|
||||
"sdk_version": "SDK version",
|
||||
"compatibility_type": "Compatibility type",
|
||||
"package_id": "Package ID"
|
||||
"package_id": "Package ID",
|
||||
"card_id_set": "Card ID Set"
|
||||
},
|
||||
|
||||
"dump_options": "Dump options",
|
||||
|
|
|
@ -269,6 +269,57 @@ end:
|
|||
return success;
|
||||
}
|
||||
|
||||
bool cnmtVerifyContentHash(ContentMetaContext *cnmt_ctx, NcaContext *nca_ctx, const u8 *hash)
|
||||
{
|
||||
if (!cnmtIsValidContext(cnmt_ctx) || !nca_ctx || !*(nca_ctx->content_id_str) || nca_ctx->content_type > NcmContentType_DeltaFragment || !nca_ctx->content_size || !hash)
|
||||
{
|
||||
LOG_MSG_ERROR("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return right away if we're dealing with a Meta NCA. */
|
||||
if (nca_ctx->content_type == NcmContentType_Meta) return true;
|
||||
|
||||
NcmPackagedContentInfo *packaged_content_info = NULL;
|
||||
bool success = false;
|
||||
|
||||
/* Loop through all of our content info entries. */
|
||||
for(u16 i = 0; i < cnmt_ctx->packaged_header->content_count; i++)
|
||||
{
|
||||
/* Check if we got a matching content ID. */
|
||||
packaged_content_info = &(cnmt_ctx->packaged_content_info[i]);
|
||||
|
||||
if (!memcmp(packaged_content_info->info.content_id.c, nca_ctx->content_id.c, sizeof(nca_ctx->content_id.c))) break;
|
||||
|
||||
packaged_content_info = NULL;
|
||||
}
|
||||
|
||||
if (!packaged_content_info)
|
||||
{
|
||||
LOG_MSG_ERROR("Unable to find CNMT content record for \"%s\" NCA! (title ID %016lX, size 0x%lX, type 0x%02X, ID offset 0x%02X).", nca_ctx->content_id_str, \
|
||||
cnmt_ctx->packaged_header->title_id, nca_ctx->content_size, nca_ctx->content_type, nca_ctx->id_offset);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Verify content hash. */
|
||||
success = (memcmp(packaged_content_info->hash, hash, SHA256_HASH_SIZE) == 0);
|
||||
#if LOG_LEVEL <= LOG_LEVEL_ERROR
|
||||
if (!success)
|
||||
{
|
||||
char got[SHA256_HASH_STR_SIZE] = {0}, expected[SHA256_HASH_STR_SIZE] = {0};
|
||||
|
||||
utilsGenerateHexString(got, sizeof(got), hash, SHA256_HASH_SIZE, true);
|
||||
utilsGenerateHexString(expected, sizeof(expected), packaged_content_info->hash, SHA256_HASH_SIZE, true);
|
||||
|
||||
LOG_MSG_ERROR("Invalid hash for \"%s\" NCA! Got \"%s\", expected \"%s\" (title ID %016lX, size 0x%lX, type 0x%02X, ID offset 0x%02X).", nca_ctx->content_id_str, \
|
||||
got, expected, cnmt_ctx->packaged_header->title_id, nca_ctx->content_size, nca_ctx->content_type, nca_ctx->id_offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
end:
|
||||
return success;
|
||||
}
|
||||
|
||||
bool cnmtUpdateContentInfo(ContentMetaContext *cnmt_ctx, NcaContext *nca_ctx)
|
||||
{
|
||||
if (!cnmtIsValidContext(cnmt_ctx) || !nca_ctx || !*(nca_ctx->content_id_str) || !*(nca_ctx->hash_str) || nca_ctx->content_type > NcmContentType_DeltaFragment || !nca_ctx->content_size)
|
||||
|
@ -302,7 +353,7 @@ bool cnmtUpdateContentInfo(ContentMetaContext *cnmt_ctx, NcaContext *nca_ctx)
|
|||
}
|
||||
}
|
||||
|
||||
if (!success) LOG_MSG_ERROR("Unable to find CNMT content info entry for \"%s\" NCA! (Title ID %016lX, size 0x%lX, type 0x%02X, ID offset 0x%02X).", nca_ctx->content_id_str, \
|
||||
if (!success) LOG_MSG_ERROR("Unable to find CNMT content info entry for \"%s\" NCA! (title ID %016lX, size 0x%lX, type 0x%02X, ID offset 0x%02X).", nca_ctx->content_id_str, \
|
||||
cnmt_ctx->packaged_header->title_id, nca_ctx->content_size, nca_ctx->content_type, nca_ctx->id_offset);
|
||||
|
||||
return success;
|
||||
|
@ -368,7 +419,7 @@ bool cnmtGenerateAuthoringToolXml(ContentMetaContext *cnmt_ctx, NcaContext *nca_
|
|||
u32 i, j;
|
||||
char *xml_buf = NULL;
|
||||
u64 xml_buf_size = 0;
|
||||
char digest_str[0x41] = {0};
|
||||
char digest_str[SHA256_HASH_STR_SIZE] = {0};
|
||||
u8 count = 0, content_meta_type = cnmt_ctx->packaged_header->content_meta_type;
|
||||
bool success = false, invalid_nca = false;
|
||||
|
||||
|
|
|
@ -78,17 +78,3 @@ Result fsDeviceOperatorGetGameCardDeviceCertificate(FsDeviceOperator *d, const F
|
|||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result fsDeviceOperatorGetGameCardIdSet(FsDeviceOperator *d, FsGameCardIdSet *out)
|
||||
{
|
||||
const struct {
|
||||
u64 buf_size;
|
||||
} in = { sizeof(FsGameCardIdSet) };
|
||||
|
||||
Result rc = serviceDispatchIn(&d->s, 208, in,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
||||
.buffers = { { out, sizeof(FsGameCardIdSet) } }
|
||||
);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -297,7 +297,7 @@ bool gamecardGetSecurityInformation(GameCardSecurityInformation *out)
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool gamecardGetIdSet(FsGameCardIdSet *out)
|
||||
bool gamecardGetCardIdSet(FsGameCardIdSet *out)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
|
@ -305,7 +305,7 @@ bool gamecardGetIdSet(FsGameCardIdSet *out)
|
|||
{
|
||||
if (!g_gameCardInterfaceInit || g_gameCardStatus != GameCardStatus_InsertedAndInfoLoaded || !out) break;
|
||||
|
||||
Result rc = fsDeviceOperatorGetGameCardIdSet(&g_deviceOperator, out);
|
||||
Result rc = fsDeviceOperatorGetGameCardIdSet(&g_deviceOperator, out, sizeof(FsGameCardIdSet), (s64)sizeof(FsGameCardIdSet));
|
||||
if (R_FAILED(rc)) LOG_MSG_ERROR("fsDeviceOperatorGetGameCardIdSet failed! (0x%X)", rc);
|
||||
|
||||
ret = R_SUCCEEDED(rc);
|
||||
|
@ -405,7 +405,7 @@ bool gamecardGetTrimmedSize(u64 *out)
|
|||
SCOPED_LOCK(&g_gameCardMutex)
|
||||
{
|
||||
ret = (g_gameCardInterfaceInit && g_gameCardStatus == GameCardStatus_InsertedAndInfoLoaded && out);
|
||||
if (ret) *out = (sizeof(GameCardHeader) + GAMECARD_PAGE_OFFSET(g_gameCardHeader.valid_data_end_address));
|
||||
if (ret) *out = (sizeof(GameCardHeader) + GAMECARD_PAGE_OFFSET(g_gameCardHeader.valid_data_end_page));
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -947,7 +947,7 @@ static bool gamecardReadSecurityInformation(GameCardSecurityInformation *out)
|
|||
{
|
||||
if ((g_fsProgramMemory.data_size - offset) < sizeof(GameCardInitialData)) break;
|
||||
|
||||
if (memcmp(g_fsProgramMemory.data + offset, &(g_gameCardHeader.package_id), sizeof(g_gameCardHeader.package_id)) != 0) continue;
|
||||
if (memcmp(g_fsProgramMemory.data + offset, g_gameCardHeader.package_id, sizeof(g_gameCardHeader.package_id)) != 0) continue;
|
||||
|
||||
sha256CalculateHash(tmp_hash, g_fsProgramMemory.data + offset, sizeof(GameCardInitialData));
|
||||
|
||||
|
|
|
@ -115,6 +115,8 @@ static KeysNxKeyset g_nxKeyset = {0};
|
|||
|
||||
static bool g_latestMasterKeyAvailable = false;
|
||||
|
||||
static bool g_wipedSetCal = false;
|
||||
|
||||
bool keysLoadKeyset(void)
|
||||
{
|
||||
bool ret = false;
|
||||
|
@ -228,7 +230,7 @@ bool keysDecryptRsaOaepWrappedTitleKey(const void *rsa_wrapped_titlekey, void *o
|
|||
|
||||
SCOPED_LOCK(&g_keysetMutex)
|
||||
{
|
||||
if (!g_keysetLoaded) break;
|
||||
if (!g_keysetLoaded || !g_wipedSetCal) break;
|
||||
|
||||
size_t out_keydata_size = 0;
|
||||
u8 out_keydata[RSA2048_BYTES] = {0};
|
||||
|
@ -769,6 +771,7 @@ static bool keysGetDecryptedEticketRsaDeviceKey(void)
|
|||
u32 public_exponent = 0;
|
||||
Aes128CtrContext eticket_aes_ctx = {0};
|
||||
EticketRsaDeviceKey *eticket_rsa_key = (EticketRsaDeviceKey*)g_eTicketRsaDeviceKey.key;
|
||||
bool success = false;
|
||||
|
||||
/* Decrypt eTicket RSA device key. */
|
||||
aes128CtrContextCreate(&eticket_aes_ctx, g_nxKeyset.eticket_rsa_kek, eticket_rsa_key->ctr);
|
||||
|
@ -779,18 +782,24 @@ static bool keysGetDecryptedEticketRsaDeviceKey(void)
|
|||
public_exponent = __builtin_bswap32(eticket_rsa_key->public_exponent);
|
||||
if (public_exponent != ETICKET_RSA_DEVICE_KEY_PUBLIC_EXPONENT)
|
||||
{
|
||||
LOG_MSG_ERROR("Invalid public exponent for decrypted eTicket RSA device key! Wrong keys? (0x%X).", public_exponent);
|
||||
return false;
|
||||
if (public_exponent == 0)
|
||||
{
|
||||
/* Bail out if we're dealing with a wiped calibration area. */
|
||||
LOG_MSG_ERROR("eTicket RSA device key is empty! Personalized titlekey crypto won't be handled. Restore an eMMC backup or disable set:cal blanking options.");
|
||||
success = g_wipedSetCal = true;
|
||||
} else {
|
||||
LOG_MSG_ERROR("Invalid public exponent for decrypted eTicket RSA device key! Wrong keys? (0x%X).", public_exponent);
|
||||
}
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Test RSA key pair. */
|
||||
if (!keysTestEticketRsaDeviceKey(&(eticket_rsa_key->public_exponent), eticket_rsa_key->private_exponent, eticket_rsa_key->modulus))
|
||||
{
|
||||
LOG_MSG_ERROR("eTicket RSA device key test failed! Wrong keys?");
|
||||
return false;
|
||||
}
|
||||
success = keysTestEticketRsaDeviceKey(&(eticket_rsa_key->public_exponent), eticket_rsa_key->private_exponent, eticket_rsa_key->modulus);
|
||||
if (!success) LOG_MSG_ERROR("eTicket RSA device key test failed! Wrong keys?");
|
||||
|
||||
return true;
|
||||
end:
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool keysTestEticketRsaDeviceKey(const void *e, const void *d, const void *n)
|
||||
|
|
|
@ -531,7 +531,7 @@ void ncaWriteEncryptedHeaderDataToMemoryBuffer(NcaContext *ctx, void *buf, u64 b
|
|||
}
|
||||
}
|
||||
|
||||
void ncaUpdateContentIdAndHash(NcaContext *ctx, u8 hash[SHA256_HASH_SIZE])
|
||||
void ncaUpdateContentIdAndHash(NcaContext *ctx, const u8 *hash)
|
||||
{
|
||||
if (!ctx) return;
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include "pfs.h"
|
||||
#include "npdm.h"
|
||||
|
||||
#define PFS_FULL_HEADER_ALIGNMENT 0x20
|
||||
#define PFS_HEADER_PADDING_ALIGNMENT 0x20
|
||||
|
||||
bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *nca_fs_ctx)
|
||||
{
|
||||
|
@ -244,7 +244,7 @@ bool pfsGenerateEntryPatch(PartitionFileSystemContext *ctx, PartitionFileSystemE
|
|||
return true;
|
||||
}
|
||||
|
||||
bool pfsAddEntryInformationToFileContext(PartitionFileSystemFileContext *ctx, const char *entry_name, u64 entry_size, u32 *out_entry_idx)
|
||||
bool pfsAddEntryInformationToImageContext(PartitionFileSystemImageContext *ctx, const char *entry_name, u64 entry_size, u32 *out_entry_idx)
|
||||
{
|
||||
if (!ctx || !entry_name || !*entry_name)
|
||||
{
|
||||
|
@ -304,7 +304,7 @@ bool pfsAddEntryInformationToFileContext(PartitionFileSystemFileContext *ctx, co
|
|||
return true;
|
||||
}
|
||||
|
||||
bool pfsUpdateEntryNameFromFileContext(PartitionFileSystemFileContext *ctx, u32 entry_idx, const char *new_entry_name)
|
||||
bool pfsUpdateEntryNameFromImageContext(PartitionFileSystemImageContext *ctx, u32 entry_idx, const char *new_entry_name)
|
||||
{
|
||||
if (!ctx || !ctx->header.entry_count || !ctx->header.name_table_size || !ctx->entries || !ctx->name_table || entry_idx >= ctx->header.entry_count || !new_entry_name || !*new_entry_name)
|
||||
{
|
||||
|
@ -329,7 +329,7 @@ bool pfsUpdateEntryNameFromFileContext(PartitionFileSystemFileContext *ctx, u32
|
|||
return true;
|
||||
}
|
||||
|
||||
bool pfsWriteFileContextHeaderToMemoryBuffer(PartitionFileSystemFileContext *ctx, void *buf, u64 buf_size, u64 *out_header_size)
|
||||
bool pfsWriteImageContextHeaderToMemoryBuffer(PartitionFileSystemImageContext *ctx, void *buf, u64 buf_size, u64 *out_header_size)
|
||||
{
|
||||
if (!ctx || !ctx->header.entry_count || !ctx->header.name_table_size || !ctx->entries || !ctx->name_table || !buf || !out_header_size)
|
||||
{
|
||||
|
@ -339,20 +339,20 @@ bool pfsWriteFileContextHeaderToMemoryBuffer(PartitionFileSystemFileContext *ctx
|
|||
|
||||
PartitionFileSystemHeader *header = &(ctx->header);
|
||||
u8 *buf_u8 = (u8*)buf;
|
||||
u64 header_size = 0, full_header_size = 0, block_offset = 0, block_size = 0;
|
||||
u64 header_size = 0, padded_header_size = 0, block_offset = 0, block_size = 0;
|
||||
u32 padding_size = 0;
|
||||
|
||||
/* Calculate header size. */
|
||||
header_size = (sizeof(PartitionFileSystemHeader) + (header->entry_count * sizeof(PartitionFileSystemEntry)) + header->name_table_size);
|
||||
|
||||
/* Calculate full header size and padding size. */
|
||||
full_header_size = (IS_ALIGNED(header_size, PFS_FULL_HEADER_ALIGNMENT) ? ALIGN_UP(header_size + 1, PFS_FULL_HEADER_ALIGNMENT) : ALIGN_UP(header_size, PFS_FULL_HEADER_ALIGNMENT));
|
||||
padding_size = (u32)(full_header_size - header_size);
|
||||
/* Calculate padded header size and padding size. */
|
||||
padded_header_size = (IS_ALIGNED(header_size, PFS_HEADER_PADDING_ALIGNMENT) ? ALIGN_UP(header_size + 1, PFS_HEADER_PADDING_ALIGNMENT) : ALIGN_UP(header_size, PFS_HEADER_PADDING_ALIGNMENT));
|
||||
padding_size = (u32)(padded_header_size - header_size);
|
||||
|
||||
/* Check buffer size. */
|
||||
if (buf_size < full_header_size)
|
||||
if (buf_size < padded_header_size)
|
||||
{
|
||||
LOG_MSG_ERROR("Not enough space available in input buffer to write full Partition FS header! (got 0x%lX, need 0x%lX).", buf_size, full_header_size);
|
||||
LOG_MSG_ERROR("Not enough space available in input buffer to write full Partition FS header! (got 0x%lX, need 0x%lX).", buf_size, padded_header_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -374,7 +374,7 @@ bool pfsWriteFileContextHeaderToMemoryBuffer(PartitionFileSystemFileContext *ctx
|
|||
memset(buf_u8 + block_offset, 0, padding_size);
|
||||
|
||||
/* Update output header size. */
|
||||
*out_header_size = full_header_size;
|
||||
*out_header_size = padded_header_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -34,10 +34,10 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *base
|
|||
bool dump_fs_header = false, success = false;
|
||||
|
||||
/* Check if the base RomFS is missing (e.g. Fortnite, World of Tanks Blitz, etc.). */
|
||||
bool missing_base_romfs = (base_nca_fs_ctx && (!base_nca_fs_ctx->enabled || (base_nca_fs_ctx->section_type != NcaFsSectionType_RomFs && \
|
||||
base_nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs)));
|
||||
bool missing_base_romfs = (!base_nca_fs_ctx || !base_nca_fs_ctx->enabled || (base_nca_fs_ctx->section_type != NcaFsSectionType_RomFs && \
|
||||
base_nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs));
|
||||
|
||||
if (!out || !base_nca_fs_ctx || (!patch_nca_fs_ctx && (missing_base_romfs || base_nca_fs_ctx->has_sparse_layer)) || \
|
||||
if (!out || (!patch_nca_fs_ctx && (missing_base_romfs || base_nca_fs_ctx->has_sparse_layer)) || \
|
||||
(!missing_base_romfs && (!(base_nca_ctx = base_nca_fs_ctx->nca_ctx) || (base_nca_ctx->format_version == NcaVersion_Nca0 && \
|
||||
(base_nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs || base_nca_fs_ctx->hash_type != NcaHashType_HierarchicalSha256)) || \
|
||||
(base_nca_ctx->format_version != NcaVersion_Nca0 && (base_nca_fs_ctx->section_type != NcaFsSectionType_RomFs || \
|
||||
|
@ -54,7 +54,7 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *base
|
|||
romfsFreeContext(out);
|
||||
|
||||
NcaStorageContext *base_storage_ctx = &(out->storage_ctx[0]), *patch_storage_ctx = &(out->storage_ctx[1]);
|
||||
bool is_nca0_romfs = (base_nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs);
|
||||
bool is_nca0_romfs = (base_nca_fs_ctx && base_nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs);
|
||||
|
||||
/* Initialize base NCA storage context. */
|
||||
if (!missing_base_romfs && !ncaStorageInitializeContext(base_storage_ctx, base_nca_fs_ctx, NULL))
|
||||
|
|
|
@ -1241,7 +1241,7 @@ fallback:
|
|||
{
|
||||
strcat(app_name, "_");
|
||||
cur_filename_len = strlen(app_name);
|
||||
utilsGenerateHexString(app_name + cur_filename_len, sizeof(app_name) - cur_filename_len, &(gc_header.package_id), sizeof(gc_header.package_id), false);
|
||||
utilsGenerateHexString(app_name + cur_filename_len, sizeof(app_name) - cur_filename_len, gc_header.package_id, sizeof(gc_header.package_id), false);
|
||||
}
|
||||
|
||||
filename = strdup(app_name);
|
||||
|
|
|
@ -848,7 +848,7 @@ static bool usbInitializeComms5x(void)
|
|||
|
||||
bos_desc->bLength = sizeof(struct usb_bos_descriptor);
|
||||
bos_desc->bDescriptorType = USB_DT_BOS;
|
||||
bos_desc->wTotalLength = USB_BOS_SIZE;
|
||||
bos_desc->wTotalLength = sizeof(bos);
|
||||
bos_desc->bNumDeviceCaps = 2; /* USB 2.0 + USB 3.0. No extra capabilities for USB 1.x. */
|
||||
|
||||
usb2_ext_desc->bLength = sizeof(struct usb_2_0_extension_descriptor);
|
||||
|
@ -961,7 +961,7 @@ static bool usbInitializeComms5x(void)
|
|||
}
|
||||
|
||||
/* Set Binary Object Store. */
|
||||
rc = usbDsSetBinaryObjectStore(bos, USB_BOS_SIZE);
|
||||
rc = usbDsSetBinaryObjectStore(bos, sizeof(bos));
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOG_MSG_ERROR("usbDsSetBinaryObjectStore failed! (0x%X).", rc);
|
||||
|
|
|
@ -96,12 +96,28 @@ namespace nxdt::views
|
|||
return std::string(strbuf);
|
||||
}
|
||||
|
||||
std::string GameCardTab::GetCardIdSetString(FsGameCardIdSet *card_id_set)
|
||||
{
|
||||
char card_id_set_str[0x20] = {0};
|
||||
|
||||
utilsGenerateHexString(card_id_set_str, sizeof(card_id_set_str), &(card_id_set->id1), sizeof(card_id_set->id1), true);
|
||||
|
||||
card_id_set_str[8] = ' ';
|
||||
utilsGenerateHexString(card_id_set_str + 9, sizeof(card_id_set_str) - 9, &(card_id_set->id2), sizeof(card_id_set->id2), true);
|
||||
|
||||
card_id_set_str[17] = ' ';
|
||||
utilsGenerateHexString(card_id_set_str + 18, sizeof(card_id_set_str) - 18, &(card_id_set->id3), sizeof(card_id_set->id3), true);
|
||||
|
||||
return std::string(card_id_set_str);
|
||||
}
|
||||
|
||||
void GameCardTab::PopulateList(void)
|
||||
{
|
||||
TitleApplicationMetadata **app_metadata = NULL;
|
||||
u32 app_metadata_count = 0;
|
||||
GameCardHeader card_header = {0};
|
||||
GameCardInfo card_info = {0};
|
||||
FsGameCardIdSet card_id_set = {0};
|
||||
|
||||
bool update_focused_view = this->IsListItemFocused();
|
||||
int focus_stack_index = this->GetFocusStackViewIndex();
|
||||
|
@ -123,6 +139,12 @@ namespace nxdt::views
|
|||
/* Display the applications that are part of the inserted gamecard. */
|
||||
this->list->addView(new brls::Header("gamecard_tab/list/user_titles/header"_i18n));
|
||||
|
||||
/* Information about how to handle user titles. */
|
||||
brls::Label *user_titles_info = new brls::Label(brls::LabelStyle::DESCRIPTION, i18n::getStr("gamecard_tab/list/user_titles/info"_i18n, \
|
||||
"root_view/tabs/user_titles"_i18n), true);
|
||||
user_titles_info->setHorizontalAlign(NVG_ALIGN_CENTER);
|
||||
this->list->addView(user_titles_info);
|
||||
|
||||
/* Populate list. */
|
||||
for(u32 i = 0; i < app_metadata_count; i++)
|
||||
{
|
||||
|
@ -131,12 +153,6 @@ namespace nxdt::views
|
|||
this->list->addView(title);
|
||||
}
|
||||
|
||||
/* Information about how to handle user titles. */
|
||||
brls::Label *user_titles_info = new brls::Label(brls::LabelStyle::DESCRIPTION, i18n::getStr("gamecard_tab/list/user_titles/info"_i18n, \
|
||||
"root_view/tabs/user_titles"_i18n), true);
|
||||
user_titles_info->setHorizontalAlign(NVG_ALIGN_CENTER);
|
||||
this->list->addView(user_titles_info);
|
||||
|
||||
/* Free application metadata array. */
|
||||
free(app_metadata);
|
||||
}
|
||||
|
@ -153,6 +169,7 @@ namespace nxdt::views
|
|||
brls::TableRow *sdk_version = properties_table->addRow(brls::TableRowType::BODY, "gamecard_tab/list/properties_table/sdk_version"_i18n);
|
||||
brls::TableRow *compatibility_type = properties_table->addRow(brls::TableRowType::BODY, "gamecard_tab/list/properties_table/compatibility_type"_i18n);
|
||||
brls::TableRow *package_id = properties_table->addRow(brls::TableRowType::BODY, "gamecard_tab/list/properties_table/package_id"_i18n);
|
||||
brls::TableRow *card_id_set_row = properties_table->addRow(brls::TableRowType::BODY, "gamecard_tab/list/properties_table/card_id_set"_i18n);
|
||||
|
||||
capacity->setValue(this->GetFormattedSizeString(&gamecardGetRomCapacity));
|
||||
total_size->setValue(this->GetFormattedSizeString(&gamecardGetTotalSize));
|
||||
|
@ -160,6 +177,7 @@ namespace nxdt::views
|
|||
|
||||
gamecardGetHeader(&card_header);
|
||||
gamecardGetDecryptedCardInfoArea(&card_info);
|
||||
gamecardGetCardIdSet(&card_id_set);
|
||||
|
||||
const SystemVersion upp_version = card_info.upp_version.system_version;
|
||||
|
||||
|
@ -213,7 +231,11 @@ namespace nxdt::views
|
|||
compat_type >= GameCardCompatibilityType_Count ? "generic/unknown"_i18n : gamecardGetCompatibilityTypeString(compat_type), \
|
||||
compat_type));
|
||||
|
||||
package_id->setValue(fmt::format("{:016X}", card_header.package_id));
|
||||
char package_id_str[0x11] = {0};
|
||||
utilsGenerateHexString(package_id_str, sizeof(package_id_str), card_header.package_id, sizeof(card_header.package_id), true);
|
||||
package_id->setValue(std::string(package_id_str));
|
||||
|
||||
card_id_set_row->setValue(this->GetCardIdSetString(&card_id_set));
|
||||
|
||||
this->list->addView(properties_table);
|
||||
|
||||
|
|
Loading…
Reference in a new issue