From 3aae84a025fd593fd25f803f4f6baebaa2b7a291 Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Sun, 4 Dec 2022 11:29:47 +0100 Subject: [PATCH] QoL changes. * gc_dumper: add UMS device support. * nsp_dumper: add DLC Update support. * cnmt: add a reminder about the extended data size in NcmContentMetaType_DataPatch CNMTs. * nca: update NcaKeyGeneration enum comment. * title: update system titles array, severely overhaul the way linked lists work in Title* structs to properly support DLC updates. --- code_templates/gc_dumper.c | 341 +++++++++++++++++++++++++++--------- code_templates/nsp_dumper.c | 17 +- include/core/nca.h | 2 +- include/core/title.h | 26 ++- source/core/cnmt.c | 2 +- source/core/title.c | 173 +++++++----------- 6 files changed, 357 insertions(+), 204 deletions(-) diff --git a/code_templates/gc_dumper.c b/code_templates/gc_dumper.c index 02d04cc..cb81d1f 100644 --- a/code_templates/gc_dumper.c +++ b/code_templates/gc_dumper.c @@ -1,7 +1,7 @@ /* * main.c * - * Copyright (c) 2020, DarkMatterCore . + * Copyright (c) 2020-2022, DarkMatterCore . * * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). * @@ -37,7 +37,7 @@ typedef void (*MenuElementOptionFunction)(u32 idx); typedef struct { u32 selected; ///< Used to keep track of the selected option. MenuElementOptionFunction options_func; ///< Pointer to a function to be called each time a new option is selected. Should be set to NULL if not used. - const char **options; ///< Pointer to multiple char pointers with strings representing options. Last element must be set to NULL. + char **options; ///< Pointer to multiple char pointers with strings representing options. Last element must be set to NULL. } MenuElementOption; typedef bool (*MenuElementFunction)(void); @@ -70,10 +70,21 @@ typedef struct /* Function prototypes. */ +static void utilsScanPads(void); +static u64 utilsGetButtonsDown(void); +static u64 utilsGetButtonsHeld(void); +static void utilsWaitForButtonPress(u64 flag); + static void consolePrint(const char *text, ...); +static void consoleRefresh(void); static u32 menuGetElementCount(const Menu *menu); +void freeStorageList(void); +void updateStorageList(void); + +NX_INLINE bool useUsbHost(void); + static bool waitForGameCard(void); static bool waitForUsb(void); @@ -89,7 +100,6 @@ static bool saveGameCardIdSet(void); static bool saveGameCardImage(void); static bool saveConsoleLafwBlob(void); -static void changeStorageOption(u32 idx); static void changeKeyAreaOption(u32 idx); static void changeCertificateOption(u32 idx); static void changeTrimOption(u32 idx); @@ -100,10 +110,9 @@ static void write_thread_func(void *arg); /* Global variables. */ -static bool g_useUsbHost = false, g_appendKeyArea = true, g_keepCertificate = false, g_trimDump = false, g_calcCrc = false; +static bool g_appendKeyArea = true, g_keepCertificate = false, g_trimDump = false, g_calcCrc = false; -static const char *g_storageOptions[] = { "sd card", "usb host", NULL }; -static const char *g_xciOptions[] = { "no", "yes", NULL }; +static char *g_xciOptions[] = { "no", "yes", NULL }; static MenuElement *g_xciMenuElements[] = { &(MenuElement){ @@ -162,6 +171,12 @@ static Menu g_xciMenu = { .elements = g_xciMenuElements }; +static MenuElementOption g_storageMenuElementOption = { + .selected = 0, + .options_func = NULL, + .options = NULL +}; + static MenuElement *g_rootMenuElements[] = { &(MenuElement){ .str = "dump gamecard xci", @@ -203,11 +218,7 @@ static MenuElement *g_rootMenuElements[] = { .str = "output storage", .child_menu = NULL, .task_func = NULL, - .element_options = &(MenuElementOption){ - .selected = 0, - .options_func = &changeStorageOption, - .options = g_storageOptions - } + .element_options = &g_storageMenuElementOption }, NULL }; @@ -219,40 +230,16 @@ static Menu g_rootMenu = { .elements = g_rootMenuElements }; -static Mutex g_fileMutex = 0; +static Mutex g_conMutex = 0, g_fileMutex = 0; static CondVar g_readCondvar = 0, g_writeCondvar = 0; static char path[FS_MAX_PATH] = {0}, txt_info[FS_MAX_PATH] = {0}; static bool g_appletStatus = true; -static void utilsScanPads(void) -{ - padUpdate(&g_padState); -} - -static u64 utilsGetButtonsDown(void) -{ - return padGetButtonsDown(&g_padState); -} - -static u64 utilsGetButtonsHeld(void) -{ - return padGetButtons(&g_padState); -} - -static void utilsWaitForButtonPress(u64 flag) -{ - /* Don't consider stick movement as button inputs. */ - if (!flag) flag = ~(HidNpadButton_StickLLeft | HidNpadButton_StickLRight | HidNpadButton_StickLUp | HidNpadButton_StickLDown | HidNpadButton_StickRLeft | HidNpadButton_StickRRight | \ - HidNpadButton_StickRUp | HidNpadButton_StickRDown); - - while(appletMainLoop()) - { - utilsScanPads(); - if (utilsGetButtonsDown() & flag) break; - } -} +static UsbHsFsDevice *g_umsDevices = NULL; +static u32 g_umsDeviceCount = 0; +static char **g_storageOptions = NULL; int main(int argc, char *argv[]) { @@ -275,12 +262,13 @@ int main(int argc, char *argv[]) consoleInit(NULL); - chdir(DEVOPTAB_SDMC_DEVICE GAMECARD_PATH); + updateStorageList(); while(appletMainLoop()) { consoleClear(); - printf("\npress b to %s.\n\n", cur_menu->parent ? "go back" : "exit"); + consolePrint("\npress b to %s.\n", cur_menu->parent ? "go back" : "exit"); + if (g_umsDeviceCount) consolePrint("press x to safely remove all ums devices.\n\n"); u32 limit = (cur_menu->scroll + page_size); MenuElement *selected_element = cur_menu->elements[cur_menu->selected]; @@ -293,33 +281,44 @@ int main(int argc, char *argv[]) MenuElement *cur_element = cur_menu->elements[i]; MenuElementOption *cur_options = cur_menu->elements[i]->element_options; - printf("%s%s", i == cur_menu->selected ? " -> " : " ", cur_element->str); + consolePrint("%s%s", i == cur_menu->selected ? " -> " : " ", cur_element->str); if (cur_options) { - printf(": "); - if (cur_options->selected > 0) printf("< "); - printf("%s", cur_options->options[cur_options->selected]); - if (cur_options->options[cur_options->selected + 1]) printf(" >"); + consolePrint(": "); + if (cur_options->selected > 0) consolePrint("< "); + consolePrint("%s", cur_options->options[cur_options->selected]); + if (cur_options->options[cur_options->selected + 1]) consolePrint(" >"); } - printf("\n"); + consolePrint("\n"); } - printf("\n"); - consoleUpdate(NULL); + consolePrint("\n"); + consoleRefresh(); + bool data_update = false; u64 btn_down = 0, btn_held = 0; + while((g_appletStatus = appletMainLoop())) { utilsScanPads(); btn_down = utilsGetButtonsDown(); btn_held = utilsGetButtonsHeld(); if (btn_down || btn_held) break; + + if (umsIsDeviceInfoUpdated()) + { + updateStorageList(); + data_update = true; + break; + } } if (!g_appletStatus) break; + if (data_update) continue; + if (btn_down & HidNpadButton_A) { Menu *child_menu = (Menu*)selected_element->child_menu; @@ -340,14 +339,20 @@ int main(int argc, char *argv[]) } /* Wait for USB session. */ - if (g_useUsbHost && !waitForUsb()) break; + if (useUsbHost() && !waitForUsb()) break; /* Generate dump text. */ generateDumpTxt(); /* Run task. */ utilsSetLongRunningProcessState(true); - if (selected_element->task_func()) saveDumpTxt(); + + if (selected_element->task_func()) + { + saveDumpTxt(); + if (!useUsbHost()) updateStorageList(); // update free space + } + utilsSetLongRunningProcessState(false); /* Display prompt. */ @@ -411,12 +416,20 @@ int main(int argc, char *argv[]) cur_menu = cur_menu->parent; element_count = menuGetElementCount(cur_menu); + } else + if ((btn_down & HidNpadButton_X) && g_umsDeviceCount) + { + for(u32 i = 0; i < g_umsDeviceCount; i++) usbHsFsUnmountDevice(&(g_umsDevices[i]), false); + + updateStorageList(); } if (btn_held & (HidNpadButton_StickLDown | HidNpadButton_StickRDown | HidNpadButton_StickLUp | HidNpadButton_StickRUp)) svcSleepThread(50000000); // 50 ms } out: + freeStorageList(); + utilsCloseResources(); consoleExit(NULL); @@ -424,13 +437,52 @@ out: return ret; } +static void utilsScanPads(void) +{ + padUpdate(&g_padState); +} + +static u64 utilsGetButtonsDown(void) +{ + return padGetButtonsDown(&g_padState); +} + +static u64 utilsGetButtonsHeld(void) +{ + return padGetButtons(&g_padState); +} + +static void utilsWaitForButtonPress(u64 flag) +{ + /* Don't consider stick movement as button inputs. */ + if (!flag) flag = ~(HidNpadButton_StickLLeft | HidNpadButton_StickLRight | HidNpadButton_StickLUp | HidNpadButton_StickLDown | HidNpadButton_StickRLeft | HidNpadButton_StickRRight | \ + HidNpadButton_StickRUp | HidNpadButton_StickRDown); + + consoleRefresh(); + + while(appletMainLoop()) + { + utilsScanPads(); + if (utilsGetButtonsDown() & flag) break; + } +} + static void consolePrint(const char *text, ...) { + mutexLock(&g_conMutex); va_list v; va_start(v, text); vfprintf(stdout, text, v); va_end(v); + mutexUnlock(&g_conMutex); +} + +static void consoleRefresh(void) +{ + mutexLock(&g_conMutex); + fflush(stdout); consoleUpdate(NULL); + mutexUnlock(&g_conMutex); } static u32 menuGetElementCount(const Menu *menu) @@ -442,10 +494,105 @@ static u32 menuGetElementCount(const Menu *menu) return cnt; } +void freeStorageList(void) +{ + u32 elem_count = (2 + g_umsDeviceCount); // sd card, usb host, ums devices + + /* Free all previously allocated data. */ + if (g_storageOptions) + { + for(u32 i = 0; i < elem_count && g_storageOptions[i]; i++) + { + free(g_storageOptions[i]); + g_storageOptions[i] = NULL; + } + + free(g_storageOptions); + g_storageOptions = NULL; + } + + if (g_umsDevices) + { + free(g_umsDevices); + g_umsDevices = NULL; + } + + g_umsDeviceCount = 0; +} + +void updateStorageList(void) +{ + char **tmp = NULL; + u32 elem_count = 0, idx = 0; + + /* Free all previously allocated data. */ + freeStorageList(); + + /* Get UMS devices. */ + g_umsDevices = umsGetDevices(&g_umsDeviceCount); + elem_count = (2 + g_umsDeviceCount); // sd card, usb host, ums devices + + /* Reallocate buffer. */ + tmp = realloc(g_storageOptions, (elem_count + 1) * sizeof(char*)); // NULL terminator + + g_storageOptions = tmp; + tmp = NULL; + + memset(g_storageOptions, 0, (elem_count + 1) * sizeof(char*)); // NULL terminator + + /* Generate UMS device strings. */ + for(u32 i = 0; i < elem_count; i++) + { + u64 total = 0, free = 0; + char total_str[36] = {0}, free_str[32] = {0}; + + g_storageOptions[idx] = calloc(sizeof(char), 0x300); + if (!g_storageOptions[idx]) continue; + + if (i == 1) + { + sprintf(g_storageOptions[idx], "usb host (pc)"); + } else { + sprintf(total_str, "%s/", i == 0 ? DEVOPTAB_SDMC_DEVICE : g_umsDevices[i - 2].name); + utilsGetFileSystemStatsByPath(total_str, &total, &free); + utilsGenerateFormattedSizeString(total, total_str, sizeof(total_str)); + utilsGenerateFormattedSizeString(free, free_str, sizeof(free_str)); + + if (i == 0) + { + sprintf(g_storageOptions[idx], DEVOPTAB_SDMC_DEVICE " (%s / %s)", free_str, total_str); + } else { + UsbHsFsDevice *ums_device = &(g_umsDevices[i]); + + if (ums_device->product_name[0]) + { + sprintf(g_storageOptions[idx], "%s (%s, LUN %u, FS #%u, %s)", ums_device->name, ums_device->product_name, ums_device->lun, ums_device->fs_idx, LIBUSBHSFS_FS_TYPE_STR(ums_device->fs_type)); + } else { + sprintf(g_storageOptions[idx], "%s (LUN %u, FS #%u, %s)", ums_device->name, ums_device->lun, ums_device->fs_idx, LIBUSBHSFS_FS_TYPE_STR(ums_device->fs_type)); + } + + sprintf(g_storageOptions[idx] + strlen(g_storageOptions[idx]), " (%s / %s)", free_str, total_str); + } + } + + idx++; + } + + /* Update storage menu element options. */ + if (g_storageMenuElementOption.selected >= elem_count) g_storageMenuElementOption.selected = 0; + g_storageMenuElementOption.options = g_storageOptions; +} + +NX_INLINE bool useUsbHost(void) +{ + return (g_storageMenuElementOption.selected == 1); +} + static bool waitForGameCard(void) { consoleClear(); consolePrint("waiting for gamecard...\n"); + consoleRefresh(); u8 status = GameCardStatus_NotInserted; @@ -487,6 +634,7 @@ static bool waitForUsb(void) if (usbIsReady()) return true; consolePrint("waiting for usb session...\n"); + consoleRefresh(); while((g_appletStatus = appletMainLoop())) { @@ -526,7 +674,7 @@ static bool saveFileData(const char *path, void *data, size_t data_size) return false; } - if (g_useUsbHost) + if (useUsbHost()) { if (!usbSendFilePropertiesCommon(data_size, path)) { @@ -572,20 +720,36 @@ static bool saveDumpTxt(void) static char *generateOutputFileName(const char *extension) { - char *filename = NULL, *output = NULL; + char *filename = NULL, *prefix = NULL, *output = NULL; - if (!extension || !*extension || !(filename = titleGenerateGameCardFileName(TitleNamingConvention_Full, g_useUsbHost ? TitleFileNameIllegalCharReplaceType_IllegalFsChars : TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly))) + if (!extension || !*extension || !(filename = titleGenerateGameCardFileName(TitleNamingConvention_Full, g_storageMenuElementOption.selected > 0 ? TitleFileNameIllegalCharReplaceType_IllegalFsChars : TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly))) { consolePrint("failed to get gamecard filename!\n"); return NULL; } - output = utilsGeneratePath(NULL, filename, extension); + if (!useUsbHost()) + { + prefix = calloc(sizeof(char), 0x300); + if (!prefix) + { + consolePrint("failed to generate prefix!\n"); + free(filename); + return NULL; + } + + sprintf(prefix, "%s:/gamecard_data/", g_storageMenuElementOption.selected == 0 ? DEVOPTAB_SDMC_DEVICE : g_umsDevices[g_storageMenuElementOption.selected - 2].name); + } + + output = utilsGeneratePath(prefix, filename, extension); + + if (prefix) free(prefix); free(filename); if (output) { snprintf(path, MAX_ELEMENTS(path), "%s", output); + utilsCreateDirectoryTree(path, false); } else { consolePrint("failed to generate output filename!\n"); } @@ -628,7 +792,7 @@ static bool saveGameCardSpecificData(void) if (!saveFileData(filename, &(gc_security_information.specific_data), sizeof(GameCardSpecificData))) goto end; - printf("successfully saved specific data as \"%s\"\n", filename); + consolePrint("successfully saved specific data as \"%s\"\n", filename); success = true; end: @@ -660,7 +824,7 @@ static bool saveGameCardCertificate(void) if (!saveFileData(filename, &gc_cert, sizeof(FsGameCardCertificate))) goto end; - printf("successfully saved certificate as \"%s\"\n", filename); + consolePrint("successfully saved certificate as \"%s\"\n", filename); success = true; end: @@ -686,7 +850,7 @@ static bool saveGameCardInitialData(void) if (!saveFileData(filename, &(gc_security_information.initial_data), sizeof(GameCardInitialData))) goto end; - printf("successfully saved initial data as \"%s\"\n", filename); + consolePrint("successfully saved initial data as \"%s\"\n", filename); success = true; end: @@ -712,7 +876,7 @@ static bool saveGameCardIdSet(void) if (!saveFileData(filename, &id_set, sizeof(FsGameCardIdSet))) goto end; - printf("successfully saved gamecard id set as \"%s\"\n", filename); + consolePrint("successfully saved gamecard id set as \"%s\"\n", filename); success = true; end: @@ -723,7 +887,7 @@ end: static bool saveGameCardImage(void) { - u64 gc_size = 0; + u64 gc_size = 0, free_space = 0; u32 key_area_crc = 0; GameCardKeyArea gc_key_area = {0}; @@ -776,7 +940,7 @@ static bool saveGameCardImage(void) filename = generateOutputFileName(path); if (!filename) goto end; - if (g_useUsbHost) + if (useUsbHost()) { if (!usbSendFilePropertiesCommon(gc_size, filename)) { @@ -790,12 +954,33 @@ static bool saveGameCardImage(void) goto end; } } else { - if (gc_size > FAT32_FILESIZE_LIMIT && !utilsCreateConcatenationFile(filename)) + if (!utilsGetFileSystemStatsByPath(path, NULL, &free_space)) { - consolePrint("failed to create concatenation file for \"%s\"!\n", filename); + consolePrint("failed to retrieve free space from selected device\n"); goto end; } + if (gc_size >= free_space) + { + consolePrint("dump size exceeds free space\n"); + goto end; + } + + if (g_storageMenuElementOption.selected == 0) + { + if (gc_size > FAT32_FILESIZE_LIMIT && !utilsCreateConcatenationFile(filename)) + { + consolePrint("failed to create concatenation file for \"%s\"!\n", filename); + goto end; + } + } else { + if (g_umsDevices[g_storageMenuElementOption.selected - 2].fs_type < UsbHsFsDeviceFileSystemType_exFAT && gc_size > FAT32_FILESIZE_LIMIT) + { + consolePrint("split dumps not supported for FAT12/16/32 volumes in UMS devices (yet)\n"); + goto end; + } + } + shared_data.fp = fopen(filename, "wb"); if (!shared_data.fp) { @@ -822,6 +1007,7 @@ static bool saveGameCardImage(void) bool btn_cancel_cur_state = false, btn_cancel_prev_state = false; consolePrint("hold b to cancel\n\n"); + consoleRefresh(); start = time(NULL); @@ -865,8 +1051,8 @@ static bool saveGameCardImage(void) prev_time = ts.tm_sec; prev_size = size; - printf("%lu / %lu (%u%%) | Time elapsed: %lu\n", size, shared_data.total_size, percent, (now - start)); - consoleUpdate(NULL); + consolePrint("%lu / %lu (%u%%) | Time elapsed: %lu\n", size, shared_data.total_size, percent, (now - start)); + consoleRefresh(); } start = (time(NULL) - start); @@ -889,25 +1075,27 @@ static bool saveGameCardImage(void) goto end; } - printf("process completed in %lu seconds\n", start); + consolePrint("process completed in %lu seconds\n", start); success = true; if (g_calcCrc) { - if (g_appendKeyArea) printf("key area crc: %08X | ", key_area_crc); - printf("xci crc: %08X", shared_data.xci_crc); - if (g_appendKeyArea) printf(" | xci crc (with key area): %08X", shared_data.full_xci_crc); - printf("\n"); + if (g_appendKeyArea) consolePrint("key area crc: %08X | ", key_area_crc); + consolePrint("xci crc: %08X", shared_data.xci_crc); + if (g_appendKeyArea) consolePrint(" | xci crc (with key area): %08X", shared_data.full_xci_crc); + consolePrint("\n"); } end: + consoleRefresh(); + if (shared_data.fp) { fclose(shared_data.fp); shared_data.fp = NULL; } - if (!success && !g_useUsbHost) utilsRemoveConcatenationFile(filename); + if (!success && !useUsbHost()) utilsRemoveConcatenationFile(filename); if (shared_data.data) free(shared_data.data); @@ -942,18 +1130,13 @@ static bool saveConsoleLafwBlob(void) if (!saveFileData(path, &lafw_blob, sizeof(LotusAsicFirmwareBlob))) goto end; - printf("successfully saved lafw blob as \"%s\"\n", path); + consolePrint("successfully saved lafw blob as \"%s\"\n", path); success = true; end: return success; } -static void changeStorageOption(u32 idx) -{ - g_useUsbHost = (idx > 0); -} - static void changeKeyAreaOption(u32 idx) { g_appendKeyArea = (idx > 0); @@ -1063,13 +1246,13 @@ static void write_thread_func(void *arg) if (shared_data->read_error || shared_data->transfer_cancelled) { - if (shared_data->transfer_cancelled && g_useUsbHost) usbCancelFileTransfer(); + if (shared_data->transfer_cancelled && useUsbHost()) usbCancelFileTransfer(); mutexUnlock(&g_fileMutex); break; } /* Write current file data chunk */ - if (g_useUsbHost) + if (useUsbHost()) { shared_data->write_error = !usbSendFileData(shared_data->data, shared_data->data_size); } else { diff --git a/code_templates/nsp_dumper.c b/code_templates/nsp_dumper.c index 75f54a7..5a5756c 100644 --- a/code_templates/nsp_dumper.c +++ b/code_templates/nsp_dumper.c @@ -48,7 +48,8 @@ typedef struct static const char *dump_type_strings[] = { "dump base application", "dump update", - "dump dlc" + "dump dlc", + "dump dlc update" }; static const u32 dump_type_strings_count = MAX_ELEMENTS(dump_type_strings); @@ -953,7 +954,8 @@ static void nspDump(TitleInfo *title_info) consoleClear(); consolePrint("%s info:\n\n", title_info->meta_key.type == NcmContentMetaType_Application ? "base application" : \ - (title_info->meta_key.type == NcmContentMetaType_Patch ? "update" : "dlc")); + (title_info->meta_key.type == NcmContentMetaType_Patch ? "update" : \ + (title_info->meta_key.type == NcmContentMetaType_AddOnContent ? "dlc" : "dlc update"))); if (app_metadata) { @@ -1142,8 +1144,6 @@ int main(int argc, char *argv[]) ums_devices = umsGetDevices(&ums_device_count); - utilsSleep(1); - while((applet_status = appletMainLoop())) { consoleClear(); @@ -1174,7 +1174,8 @@ int main(int argc, char *argv[]) } consolePrint("selected %s info:\n\n", title_info->meta_key.type == NcmContentMetaType_Application ? "base application" : \ - (title_info->meta_key.type == NcmContentMetaType_Patch ? "update" : "dlc")); + (title_info->meta_key.type == NcmContentMetaType_Patch ? "update" : \ + (title_info->meta_key.type == NcmContentMetaType_AddOnContent ? "dlc" : "dlc update"))); consolePrint("source storage: %s\n", titleGetNcmStorageIdName(title_info->storage_id)); if (title_info->meta_key.type != NcmContentMetaType_Application) consolePrint("title id: %016lX\n", title_info->meta_key.id); consolePrint("version: %u (%u.%u.%u-%u.%u)\n", title_info->version.value, title_info->version.system_version.major, title_info->version.system_version.minor, \ @@ -1341,12 +1342,12 @@ int main(int argc, char *argv[]) } else if (menu == 2) { - if ((type_idx == 0 && !user_app_data.app_info) || (type_idx == 1 && !user_app_data.patch_info) || (type_idx == 2 && !user_app_data.aoc_info)) + if ((type_idx == 0 && !user_app_data.app_info) || (type_idx == 1 && !user_app_data.patch_info) || (type_idx == 2 && !user_app_data.aoc_info) || (type_idx == 3 && !user_app_data.aoc_patch_info)) { - consolePrint("\nthe selected title doesn't have available %s data\n", type_idx == 0 ? "base application" : (type_idx == 1 ? "update" : "dlc")); + consolePrint("\nthe selected title doesn't have available %s data\n", type_idx == 0 ? "base application" : (type_idx == 1 ? "update" : (type_idx == 2 ? "dlc" : "dlc update"))); error = true; } else { - title_info = (type_idx == 0 ? user_app_data.app_info : (type_idx == 1 ? user_app_data.patch_info : user_app_data.aoc_info)); + title_info = (type_idx == 0 ? user_app_data.app_info : (type_idx == 1 ? user_app_data.patch_info : (type_idx == 2 ? user_app_data.aoc_info : user_app_data.aoc_patch_info))); list_count = titleGetCountFromInfoBlock(title_info); list_idx = 1; } diff --git a/include/core/nca.h b/include/core/nca.h index 983e60c..1850276 100644 --- a/include/core/nca.h +++ b/include/core/nca.h @@ -91,7 +91,7 @@ typedef enum { NcaKeyGeneration_Since1210NUP = 12, ///< 12.1.0. NcaKeyGeneration_Since1300NUP = 13, ///< 13.0.0 - 13.2.1. NcaKeyGeneration_Since1400NUP = 14, ///< 14.0.0 - 14.1.2. - NcaKeyGeneration_Since1500NUP = 15, ///< 15.0.0. + NcaKeyGeneration_Since1500NUP = 15, ///< 15.0.0 - 15.0.1. NcaKeyGeneration_Current = NcaKeyGeneration_Since1500NUP, NcaKeyGeneration_Max = 32 } NcaKeyGeneration; diff --git a/include/core/title.h b/include/core/title.h index 6c567e5..5301499 100644 --- a/include/core/title.h +++ b/include/core/title.h @@ -46,10 +46,10 @@ typedef struct { } TitleApplicationMetadata; /// Generated using ncm calls. -/// User applications: the parent pointer is always unused. The previous/next pointers reference other user applications with the same ID. -/// Patches: the parent pointer always references the first corresponding user application. The previous/next pointers reference other patches with the same ID. -/// Add-on contents: the parent pointer always references the first corresponding user application. The previous/next pointers reference sibling add-on contents. -/// Add-on content patches: the parent pointer always references the first corresponding add-on content. The previous/next pointers reference other patches with the same ID. +/// User applications: the previous/next pointers reference other user applications with the same ID. +/// Patches: the previous/next pointers reference other patches with the same ID. +/// Add-on contents: the previous/next pointers reference sibling add-on contents. +/// Add-on content patches: the previous/next pointers reference other patches with the same ID and/or other patches for sibling add-on contents. typedef struct _TitleInfo { u8 storage_id; ///< NcmStorageId. NcmContentMetaKey meta_key; ///< Used with ncm calls. @@ -59,15 +59,16 @@ typedef struct _TitleInfo { u64 size; ///< Total title size. char size_str[32]; ///< Total title size string. TitleApplicationMetadata *app_metadata; ///< User application metadata. - struct _TitleInfo *parent, *previous, *next; ///< Linked lists. + struct _TitleInfo *previous, *next; ///< Linked lists. } TitleInfo; /// Used to deal with user applications stored in the eMMC, SD card and/or gamecard. -/// The parent, previous and next pointers from the TitleInfo elements are used to traverse through multiple user applications, patches and/or add-on contents. +/// The previous and next pointers from the TitleInfo elements are used to traverse through multiple user applications, patches, add-on contents and or add-on content patches. typedef struct { - TitleInfo *app_info; ///< Pointer to a TitleInfo element holding info for the first detected user application entry matching the provided application ID. - TitleInfo *patch_info; ///< Pointer to a TitleInfo element holding info for the first detected patch entry matching the provided application ID. - TitleInfo *aoc_info; ///< Pointer to a TitleInfo element holding info for the first detected add-on content entry matching the provided application ID. + TitleInfo *app_info; ///< Pointer to a TitleInfo element for the first detected user application entry matching the provided application ID. + TitleInfo *patch_info; ///< Pointer to a TitleInfo element for the first detected patch entry matching the provided application ID. + TitleInfo *aoc_info; ///< Pointer to a TitleInfo element for the first detected add-on content entry matching the provided application ID. + TitleInfo *aoc_patch_info; ///< Pointer to a TitleInfo element for the first detected add-on content patch entry matching the provided application ID. } TitleUserApplicationData; typedef enum { @@ -250,6 +251,13 @@ NX_INLINE bool titleCheckIfDataPatchIdBelongsToApplicationId(u64 app_id, u64 dat return titleCheckIfAddOnContentIdBelongsToApplicationId(app_id, titleGetAddOnContentIdByDataPatchId(data_patch_id)); } +NX_INLINE bool titleCheckIfDataPatchIdsAreSiblings(u64 data_patch_id_1, u64 data_patch_id_2) +{ + u64 app_id_1 = titleGetApplicationIdByDataPatchId(data_patch_id_1); + u64 app_id_2 = titleGetApplicationIdByDataPatchId(data_patch_id_2); + return (app_id_1 == app_id_2 && titleCheckIfDataPatchIdBelongsToApplicationId(app_id_1, data_patch_id_1) && titleCheckIfDataPatchIdBelongsToApplicationId(app_id_2, data_patch_id_2)); +} + NX_INLINE u32 titleGetContentCountByType(TitleInfo *info, u8 content_type) { if (!info || !info->content_count || !info->content_infos || content_type > NcmContentType_DeltaFragment) return 0; diff --git a/source/core/cnmt.c b/source/core/cnmt.c index 09b1a20..028eada 100644 --- a/source/core/cnmt.c +++ b/source/core/cnmt.c @@ -192,7 +192,7 @@ bool cnmtInitializeContext(ContentMetaContext *out, NcaContext *nca_ctx) case NcmContentMetaType_DataPatch: invalid_ext_header_size = (out->packaged_header->extended_header_size != (u16)sizeof(ContentMetaDataPatchMetaExtendedHeader)); out->extended_data_size = (!invalid_ext_header_size ? ((ContentMetaDataPatchMetaExtendedHeader*)out->extended_header)->extended_data_size : 0); - invalid_ext_data_size = (out->extended_data_size <= sizeof(ContentMetaPatchMetaExtendedDataHeader)); + invalid_ext_data_size = (out->extended_data_size <= sizeof(ContentMetaPatchMetaExtendedDataHeader)); // TODO: check if this is right. break; default: invalid_ext_header_size = (out->packaged_header->extended_header_size != 0); diff --git a/source/core/title.c b/source/core/title.c index 4083e9b..4516415 100644 --- a/source/core/title.c +++ b/source/core/title.c @@ -238,14 +238,14 @@ static const TitleSystemEntry g_systemTitles[] = { { 0x0100000000001007, "playerSelect" }, { 0x0100000000001008, "swkbd" }, { 0x0100000000001009, "miiEdit" }, - { 0x010000000000100A, "web" }, - { 0x010000000000100B, "shop" }, + { 0x010000000000100A, "LibAppletWeb" }, + { 0x010000000000100B, "LibAppletShop" }, { 0x010000000000100C, "overlayDisp" }, { 0x010000000000100D, "photoViewer" }, { 0x010000000000100E, "set" }, - { 0x010000000000100F, "offlineWeb" }, - { 0x0100000000001010, "loginShare" }, - { 0x0100000000001011, "wifiWebAuth" }, + { 0x010000000000100F, "LibAppletOff" }, + { 0x0100000000001010, "LibAppletLns" }, + { 0x0100000000001011, "LibAppletAuth" }, { 0x0100000000001012, "starter" }, { 0x0100000000001013, "myPage" }, { 0x0100000000001014, "PlayReport" }, @@ -524,7 +524,7 @@ static bool titleRefreshGameCardTitleInfo(void); static bool titleIsUserApplicationContentAvailable(u64 app_id); static TitleInfo *_titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id); -static TitleInfo *titleDuplicateTitleInfo(TitleInfo *title_info, TitleInfo *parent, TitleInfo *previous, TitleInfo *next); +static TitleInfo *titleDuplicateTitleInfo(TitleInfo *title_info, TitleInfo *previous, TitleInfo *next); static int titleSystemTitleMetadataEntrySortFunction(const void *a, const void *b); static int titleUserApplicationMetadataEntrySortFunction(const void *a, const void *b); @@ -754,7 +754,7 @@ TitleInfo *titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id) TitleInfo *title_info = (g_titleInterfaceInit ? _titleGetInfoFromStorageByTitleId(storage_id, title_id) : NULL); if (title_info) { - ret = titleDuplicateTitleInfo(title_info, NULL, NULL, NULL); + ret = titleDuplicateTitleInfo(title_info, NULL, NULL); if (!ret) LOG_MSG_ERROR("Failed to duplicate title info for %016lX!", title_id); } } @@ -770,15 +770,12 @@ void titleFreeTitleInfo(TitleInfo **info) /* Free content infos. */ if (ptr->content_infos) free(ptr->content_infos); - /* Free parent linked list. */ - titleFreeTitleInfo(&(ptr->parent)); - /* Free previous sibling(s). */ tmp1 = ptr->previous; while(tmp1) { tmp2 = tmp1->previous; - tmp1->parent = tmp1->previous = tmp1->next = NULL; + tmp1->previous = tmp1->next = NULL; titleFreeTitleInfo(&tmp1); tmp1 = tmp2; } @@ -788,7 +785,7 @@ void titleFreeTitleInfo(TitleInfo **info) while(tmp1) { tmp2 = tmp1->next; - tmp1->parent = tmp1->previous = tmp1->next = NULL; + tmp1->previous = tmp1->next = NULL; titleFreeTitleInfo(&tmp1); tmp1 = tmp2; } @@ -809,36 +806,30 @@ bool titleGetUserApplicationData(u64 app_id, TitleUserApplicationData *out) break; } - TitleInfo *app_info = NULL, *patch_info = NULL, *aoc_info = NULL; + bool error = false; + TitleInfo *app_info = NULL, *patch_info = NULL, *aoc_info = NULL, *aoc_patch_info = NULL; /* Clear output. */ titleFreeUserApplicationData(out); +#define TITLE_ALLOCATE_USER_APP_DATA(elem, msg, decl) \ + if (elem##_info && !out->elem##_info) { \ + out->elem##_info = titleDuplicateTitleInfo(elem##_info, NULL, NULL); \ + if (!out->elem##_info) { \ + LOG_MSG_ERROR("Failed to duplicate %s info for %016lX!", msg, app_id); \ + decl; \ + } \ + } + /* Get info for the first user application title. */ app_info = _titleGetInfoFromStorageByTitleId(NcmStorageId_Any, app_id); - if (app_info) - { - out->app_info = titleDuplicateTitleInfo(app_info, NULL, NULL, NULL); - if (!out->app_info) - { - LOG_MSG_ERROR("Failed to duplicate user application info for %016lX!", app_id); - break; - } - } + TITLE_ALLOCATE_USER_APP_DATA(app, "user application", break); /* Get info for the first patch title. */ patch_info = _titleGetInfoFromStorageByTitleId(NcmStorageId_Any, titleGetPatchIdByApplicationId(app_id)); - if (patch_info) - { - out->patch_info = titleDuplicateTitleInfo(patch_info, out->app_info, NULL, NULL); - if (!out->patch_info) - { - LOG_MSG_ERROR("Failed to duplicate patch info for %016lX!", app_id); - break; - } - } + TITLE_ALLOCATE_USER_APP_DATA(patch, "patch", break); - /* Get info for the first add-on content title. */ + /* Get info for the first add-on content and add-on content patch titles. */ for(u8 i = NcmStorageId_GameCard; i <= NcmStorageId_SdCard; i++) { if (i == NcmStorageId_BuiltInSystem) continue; @@ -849,28 +840,33 @@ bool titleGetUserApplicationData(u64 app_id, TitleUserApplicationData *out) for(u32 j = 0; j < title_storage->title_count; j++) { TitleInfo *title_info = title_storage->titles[j]; - if (title_info && title_info->meta_key.type == NcmContentMetaType_AddOnContent && titleCheckIfAddOnContentIdBelongsToApplicationId(app_id, title_info->meta_key.id)) + if (!title_info) continue; + + if (title_info->meta_key.type == NcmContentMetaType_AddOnContent && titleCheckIfAddOnContentIdBelongsToApplicationId(app_id, title_info->meta_key.id)) { aoc_info = title_info; break; + } else + if (title_info->meta_key.type == NcmContentMetaType_DataPatch && titleCheckIfDataPatchIdBelongsToApplicationId(app_id, title_info->meta_key.id)) + { + aoc_patch_info = title_info; + break; } } - if (aoc_info) break; + TITLE_ALLOCATE_USER_APP_DATA(aoc, "add-on content", error = true; break); + + TITLE_ALLOCATE_USER_APP_DATA(aoc_patch, "add-on content patch", error = true; break); + + if (out->aoc_info && out->aoc_patch_info) break; } - if (aoc_info) - { - out->aoc_info = titleDuplicateTitleInfo(aoc_info, out->app_info, NULL, NULL); - if (!out->aoc_info) - { - LOG_MSG_ERROR("Failed to duplicate add-on content info for %016lX!", app_id); - break; - } - } + if (error) break; + +#undef TITLE_ALLOCATE_USER_APP_DATA /* Check retrieved title info. */ - ret = (app_info || patch_info || aoc_info); + ret = (app_info || patch_info || aoc_info || aoc_patch_info); if (!ret) LOG_MSG_ERROR("Failed to retrieve user application data for ID \"%016lX\"!", app_id); } @@ -884,32 +880,17 @@ void titleFreeUserApplicationData(TitleUserApplicationData *user_app_data) { if (!user_app_data) return; - TitleInfo *tmp = NULL; - /* Free user application info. */ titleFreeTitleInfo(&(user_app_data->app_info)); - /* Make sure to clear all references to the parent linked list beforehand. */ - /* Unlike titleDuplicateTitleInfo(), we don't need to traverse backwards because elements from TitleUserApplicationData always point to the first title info. */ - tmp = user_app_data->patch_info; - while(tmp) - { - tmp->parent = NULL; - tmp = tmp->next; - } - - tmp = user_app_data->aoc_info; - while(tmp) - { - tmp->parent = NULL; - tmp = tmp->next; - } - /* Free patch info. */ titleFreeTitleInfo(&(user_app_data->patch_info)); /* Free add-on content info. */ titleFreeTitleInfo(&(user_app_data->aoc_info)); + + /* Free add-on content patch info. */ + titleFreeTitleInfo(&(user_app_data->aoc_patch_info)); } bool titleAreOrphanTitlesAvailable(void) @@ -942,7 +923,7 @@ TitleInfo **titleGetOrphanTitles(u32 *out_count) /* Duplicate orphan title info entries. */ for(u32 i = 0; i < g_orphanTitleInfoCount; i++) { - orphan_info[i] = titleDuplicateTitleInfo(g_orphanTitleInfo[i], NULL, NULL, NULL); + orphan_info[i] = titleDuplicateTitleInfo(g_orphanTitleInfo[i], NULL, NULL); if (!orphan_info[i]) { LOG_MSG_ERROR("Failed to duplicate info for orphan title %016lX!", g_orphanTitleInfo[i]->meta_key.id); @@ -2071,7 +2052,7 @@ static void titleUpdateTitleInfoLinkedLists(void) TitleInfo *child_info = titles[j]; if (!child_info) continue; - child_info->parent = child_info->previous = child_info->next = NULL; + child_info->previous = child_info->next = NULL; /* If we're dealing with a title that's not an user application, patch, add-on content or add-on content patch, flag it as orphan and proceed onto the next one. */ if (child_info->meta_key.type < NcmContentMetaType_Application || (child_info->meta_key.type > NcmContentMetaType_AddOnContent && \ @@ -2081,33 +2062,21 @@ static void titleUpdateTitleInfoLinkedLists(void) continue; } - if (child_info->meta_key.type != NcmContentMetaType_Application) + if (child_info->meta_key.type != NcmContentMetaType_Application && !child_info->app_metadata) { /* We're dealing with a patch, an add-on content or an add-on content patch. */ - /* Patch, AddOnContent: retrieve pointer to the first parent user application entry and set it as the parent title. */ - /* DataPatch: retrieve pointer to the first parent add-on content entry and set it as the parent title. */ + /* We'll just retrieve a pointer to the first matching user application entry and use it to set a pointer to an application metadata entry. */ u64 app_id = (child_info->meta_key.type == NcmContentMetaType_Patch ? titleGetApplicationIdByPatchId(child_info->meta_key.id) : \ (child_info->meta_key.type == NcmContentMetaType_AddOnContent ? titleGetApplicationIdByAddOnContentId(child_info->meta_key.id) : \ - titleGetAddOnContentIdByDataPatchId(child_info->meta_key.id))); + titleGetApplicationIdByDataPatchId(child_info->meta_key.id))); - child_info->parent = _titleGetInfoFromStorageByTitleId(NcmStorageId_Any, app_id); - - /* Set pointer to application metadata. */ - if (child_info->parent && !child_info->app_metadata) - { - if (child_info->meta_key.type != NcmContentMetaType_DataPatch || child_info->parent->app_metadata) - { - child_info->app_metadata = child_info->parent->app_metadata; - } else { - /* We may be dealing with a parent add-on content with a yet-to-be-assigned application metadata pointer. */ - TitleInfo *tmp_title_info = _titleGetInfoFromStorageByTitleId(NcmStorageId_Any, titleGetApplicationIdByDataPatchId(child_info->meta_key.id)); - if (tmp_title_info) child_info->parent->app_metadata = child_info->app_metadata = tmp_title_info->app_metadata; - } - } - - /* Add orphan title info entry if we have no application metadata. */ - if (!child_info->app_metadata) + TitleInfo *parent = _titleGetInfoFromStorageByTitleId(NcmStorageId_Any, app_id); + if (parent) { + /* Set pointer to application metadata. */ + child_info->app_metadata = parent->app_metadata; + } else { + /* Add orphan title info entry since we have no application metadata. */ titleAddOrphanTitleInfoEntry(child_info); continue; } @@ -2134,8 +2103,9 @@ static void titleUpdateTitleInfoLinkedLists(void) if (!prev_info) continue; if (prev_info->meta_key.type == child_info->meta_key.type && \ - ((child_info->meta_key.type != NcmContentMetaType_AddOnContent && prev_info->meta_key.id == child_info->meta_key.id) || \ - (child_info->meta_key.type == NcmContentMetaType_AddOnContent && titleCheckIfAddOnContentIdsAreSiblings(prev_info->meta_key.id, child_info->meta_key.id)))) + (((child_info->meta_key.type == NcmContentMetaType_Application || child_info->meta_key.type == NcmContentMetaType_Patch) && prev_info->meta_key.id == child_info->meta_key.id) || \ + (child_info->meta_key.type == NcmContentMetaType_AddOnContent && titleCheckIfAddOnContentIdsAreSiblings(prev_info->meta_key.id, child_info->meta_key.id)) || \ + (child_info->meta_key.type == NcmContentMetaType_DataPatch && titleCheckIfDataPatchIdsAreSiblings(prev_info->meta_key.id, child_info->meta_key.id)))) { prev_info->next = child_info; child_info->previous = prev_info; @@ -2404,7 +2374,7 @@ static TitleInfo *_titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id) return out; } -static TitleInfo *titleDuplicateTitleInfo(TitleInfo *title_info, TitleInfo *parent, TitleInfo *previous, TitleInfo *next) +static TitleInfo *titleDuplicateTitleInfo(TitleInfo *title_info, TitleInfo *previous, TitleInfo *next) { if (!title_info || title_info->storage_id < NcmStorageId_GameCard || title_info->storage_id > NcmStorageId_SdCard || !title_info->meta_key.id || \ (title_info->meta_key.type > NcmContentMetaType_BootImagePackageSafe && title_info->meta_key.type < NcmContentMetaType_Application) || \ @@ -2416,7 +2386,7 @@ static TitleInfo *titleDuplicateTitleInfo(TitleInfo *title_info, TitleInfo *pare TitleInfo *title_info_dup = NULL, *tmp1 = NULL, *tmp2 = NULL; NcmContentInfo *content_infos_dup = NULL; - bool dup_parent = false, dup_previous = false, dup_next = false, success = false; + bool dup_previous = false, dup_next = false, success = false; /* Allocate memory for the new TitleInfo element. */ title_info_dup = calloc(1, sizeof(TitleInfo)); @@ -2428,7 +2398,7 @@ static TitleInfo *titleDuplicateTitleInfo(TitleInfo *title_info, TitleInfo *pare /* Copy TitleInfo data. */ memcpy(title_info_dup, title_info, sizeof(TitleInfo)); - title_info_dup->parent = title_info_dup->previous = title_info_dup->next = NULL; + title_info_dup->previous = title_info_dup->next = NULL; /* Allocate memory for NcmContentInfo elements. */ content_infos_dup = calloc(title_info->content_count, sizeof(NcmContentInfo)); @@ -2444,12 +2414,12 @@ static TitleInfo *titleDuplicateTitleInfo(TitleInfo *title_info, TitleInfo *pare /* Update content infos pointer. */ title_info_dup->content_infos = content_infos_dup; -#define TITLE_DUPLICATE_LINKED_LIST(elem, prnt, prv, nxt) \ +#define TITLE_DUPLICATE_LINKED_LIST(elem, prv, nxt) \ if (title_info->elem) { \ if (elem) { \ title_info_dup->elem = elem; \ } else { \ - title_info_dup->elem = titleDuplicateTitleInfo(title_info->elem, prnt, prv, nxt); \ + title_info_dup->elem = titleDuplicateTitleInfo(title_info->elem, prv, nxt); \ if (!title_info_dup->elem) goto end; \ dup_##elem = true; \ } \ @@ -2460,7 +2430,7 @@ static TitleInfo *titleDuplicateTitleInfo(TitleInfo *title_info, TitleInfo *pare tmp1 = title_info_dup->elem; \ while(tmp1) { \ tmp2 = tmp1->elem; \ - tmp1->parent = tmp1->previous = tmp1->next = NULL; \ + tmp1->previous = tmp1->next = NULL; \ titleFreeTitleInfo(&tmp1); \ tmp1 = tmp2; \ } \ @@ -2469,14 +2439,8 @@ static TitleInfo *titleDuplicateTitleInfo(TitleInfo *title_info, TitleInfo *pare /* Duplicate linked lists based on two different principles: */ /* 1) Linked list pointers will only be populated if their corresponding pointer is also populated in the TitleInfo element to duplicate. */ /* 2) Pointers passed into this function take precedence before actual data duplication. */ - - /* Duplicate parent linked list and update pointer to parent TitleInfo entry -- this will be used while duplicating siblings in the next linked lists. */ - TITLE_DUPLICATE_LINKED_LIST(parent, NULL, NULL, NULL); - if (title_info->parent) parent = title_info_dup->parent; - - /* Duplicate previous and next linked lists. */ - TITLE_DUPLICATE_LINKED_LIST(previous, parent, NULL, title_info_dup); - TITLE_DUPLICATE_LINKED_LIST(next, parent, title_info_dup, NULL); + TITLE_DUPLICATE_LINKED_LIST(previous, NULL, title_info_dup); + TITLE_DUPLICATE_LINKED_LIST(next, title_info_dup, NULL); /* Update flag. */ success = true; @@ -2490,17 +2454,14 @@ end: if (title_info_dup) { - /* Free parent linked list (if duplicated). */ - /* Parent TitleInfo entries are user applications with no reference to child titles, so it's safe to free them first with titleFreeTitleInfo(). */ - if (dup_parent) titleFreeTitleInfo(&(title_info_dup->parent)); - /* Free previous and next linked lists (if duplicated). */ - /* We need to take care of not freeing the parent linked list, either because we may have already freed it, or because it may have been passed as an argument. */ + /* We need to take care of not freeing the linked lists right away, either because we may have already freed them, or because they may have been passed as arguments. */ /* Furthermore, both the next pointer from the previous sibling and the previous pointer from the next sibling reference our current duplicated entry. */ /* To avoid issues, we'll just clear all linked list pointers. */ TITLE_FREE_DUPLICATED_LINKED_LIST(previous); TITLE_FREE_DUPLICATED_LINKED_LIST(next); + /* Free allocated buffer and update return pointer. */ free(title_info_dup); title_info_dup = NULL; }