mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-08 11:51:48 +00:00
poc: add system title dumping capabilities
system_title_dumper PoC build is no more. Other changes include: * smc: add missing assert for SmcGenerateAesKekOption struct. * title: update titleGenerateFileName() to add support for system titles.
This commit is contained in:
parent
cd73c6360f
commit
a8a348afc8
4 changed files with 129 additions and 612 deletions
|
@ -79,7 +79,8 @@ typedef enum {
|
|||
MenuId_Nca = 11,
|
||||
MenuId_NcaFsSections = 12,
|
||||
MenuId_NcaFsSectionsSubMenu = 13,
|
||||
MenuId_Count = 14
|
||||
MenuId_SystemTitles = 14,
|
||||
MenuId_Count = 15
|
||||
} MenuId;
|
||||
|
||||
typedef struct
|
||||
|
@ -149,8 +150,8 @@ static u32 menuGetElementCount(const Menu *menu);
|
|||
void freeStorageList(void);
|
||||
void updateStorageList(void);
|
||||
|
||||
void freeTitleList(void);
|
||||
void updateTitleList(void);
|
||||
void freeTitleList(Menu *menu);
|
||||
void updateTitleList(Menu *menu, Menu *submenu, bool is_system);
|
||||
|
||||
void freeNcaList(void);
|
||||
void updateNcaList(TitleInfo *title_info);
|
||||
|
@ -810,7 +811,7 @@ static MenuElement *g_userTitlesSubMenuElements[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
// Dynamically set as child_menu for all g_userTitlesMenuElements entries.
|
||||
// Dynamically set as child_menu for all g_userTitlesMenu entries.
|
||||
static Menu g_userTitlesSubMenu = {
|
||||
.id = MenuId_UserTitlesSubMenu,
|
||||
.parent = NULL,
|
||||
|
@ -819,9 +820,7 @@ static Menu g_userTitlesSubMenu = {
|
|||
.elements = g_userTitlesSubMenuElements
|
||||
};
|
||||
|
||||
static MenuElement **g_userTitlesMenuElements = NULL;
|
||||
|
||||
// Dynamically populated using g_userTitlesMenuElements.
|
||||
// Dynamically populated.
|
||||
static Menu g_userTitlesMenu = {
|
||||
.id = MenuId_UserTitles,
|
||||
.parent = NULL,
|
||||
|
@ -830,6 +829,15 @@ static Menu g_userTitlesMenu = {
|
|||
.elements = NULL
|
||||
};
|
||||
|
||||
// Dynamically populated.
|
||||
static Menu g_systemTitlesMenu = {
|
||||
.id = MenuId_SystemTitles,
|
||||
.parent = NULL,
|
||||
.selected = 0,
|
||||
.scroll = 0,
|
||||
.elements = NULL
|
||||
};
|
||||
|
||||
static MenuElement *g_rootMenuElements[] = {
|
||||
&(MenuElement){
|
||||
.str = "gamecard menu",
|
||||
|
@ -853,7 +861,7 @@ static MenuElement *g_rootMenuElements[] = {
|
|||
},
|
||||
&(MenuElement){
|
||||
.str = "system titles menu",
|
||||
.child_menu = NULL,
|
||||
.child_menu = &g_systemTitlesMenu,
|
||||
.task_func = NULL,
|
||||
.element_options = NULL,
|
||||
.userdata = NULL
|
||||
|
@ -894,7 +902,8 @@ int main(int argc, char *argv[])
|
|||
|
||||
updateStorageList();
|
||||
|
||||
updateTitleList();
|
||||
updateTitleList(&g_userTitlesMenu, &g_userTitlesSubMenu, false);
|
||||
updateTitleList(&g_systemTitlesMenu, &g_ncaMenu, true);
|
||||
|
||||
Menu *cur_menu = &g_rootMenu;
|
||||
u32 element_count = menuGetElementCount(cur_menu), page_size = 30;
|
||||
|
@ -906,6 +915,8 @@ int main(int argc, char *argv[])
|
|||
TitleInfo *title_info = NULL;
|
||||
u32 title_info_idx = 0, title_info_count = 0;
|
||||
|
||||
bool is_system = false;
|
||||
|
||||
while(appletMainLoop())
|
||||
{
|
||||
MenuElement *selected_element = cur_menu->elements[cur_menu->selected];
|
||||
|
@ -929,7 +940,7 @@ int main(int argc, char *argv[])
|
|||
consolePrint("press + to exit\n");
|
||||
consolePrint("______________________________\n\n");
|
||||
|
||||
if (cur_menu->id == MenuId_UserTitles)
|
||||
if (cur_menu->id == MenuId_UserTitles || cur_menu->id == MenuId_SystemTitles)
|
||||
{
|
||||
app_metadata = (TitleApplicationMetadata*)selected_element->userdata;
|
||||
|
||||
|
@ -937,14 +948,17 @@ int main(int argc, char *argv[])
|
|||
consolePrint("selected title: %016lX - %s\n", app_metadata->title_id, selected_element->str);
|
||||
consolePrint("______________________________\n\n");
|
||||
} else
|
||||
if (cur_menu->id >= MenuId_UserTitlesSubMenu && cur_menu->id < MenuId_Count)
|
||||
if (cur_menu->id >= MenuId_UserTitlesSubMenu && cur_menu->id < MenuId_SystemTitles)
|
||||
{
|
||||
consolePrint("title info:\n\n");
|
||||
consolePrint("name: %s\n", app_metadata->lang_entry.name);
|
||||
consolePrint("publisher: %s\n", app_metadata->lang_entry.author);
|
||||
if (cur_menu->id == MenuId_UserTitlesSubMenu || cur_menu->id == MenuId_NSPTitleTypes || cur_menu->id == MenuId_TicketTitleTypes || \
|
||||
cur_menu->id == MenuId_NcaTitleTypes) consolePrint("title id: %016lX\n", app_metadata->title_id);
|
||||
consolePrint("______________________________\n\n");
|
||||
if (!is_system)
|
||||
{
|
||||
consolePrint("title info:\n\n");
|
||||
consolePrint("name: %s\n", app_metadata->lang_entry.name);
|
||||
consolePrint("publisher: %s\n", app_metadata->lang_entry.author);
|
||||
if (cur_menu->id == MenuId_UserTitlesSubMenu || cur_menu->id == MenuId_NSPTitleTypes || cur_menu->id == MenuId_TicketTitleTypes || \
|
||||
cur_menu->id == MenuId_NcaTitleTypes) consolePrint("title id: %016lX\n", app_metadata->title_id);
|
||||
consolePrint("______________________________\n\n");
|
||||
}
|
||||
|
||||
if (cur_menu->id == MenuId_NSP || cur_menu->id == MenuId_Ticket || cur_menu->id == MenuId_Nca || \
|
||||
cur_menu->id == MenuId_NcaFsSections || cur_menu->id == MenuId_NcaFsSectionsSubMenu)
|
||||
|
@ -957,6 +971,7 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
consolePrint("selected title info:\n\n");
|
||||
if (is_system) consolePrint("name: %s\n", app_metadata->lang_entry.name);
|
||||
consolePrint("title id: %016lX\n", title_info->meta_key.id);
|
||||
consolePrint("type: %s\n", titleGetNcmContentMetaTypeName(title_info->meta_key.type));
|
||||
consolePrint("source storage: %s\n", titleGetNcmStorageIdName(title_info->storage_id));
|
||||
|
@ -1004,7 +1019,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
MenuElement *cur_element = cur_menu->elements[i];
|
||||
MenuElementOption *cur_options = cur_element->element_options;
|
||||
TitleApplicationMetadata *cur_app_metadata = (cur_menu->id == MenuId_UserTitles ? (TitleApplicationMetadata*)cur_element->userdata : NULL);
|
||||
TitleApplicationMetadata *cur_app_metadata = ((cur_menu->id == MenuId_UserTitles || cur_menu->id == MenuId_SystemTitles) ? (TitleApplicationMetadata*)cur_element->userdata : NULL);
|
||||
|
||||
consolePrint("%s", i == cur_menu->selected ? " -> " : " ");
|
||||
if (cur_app_metadata) consolePrint("%016lX - ", cur_app_metadata->title_id);
|
||||
|
@ -1051,7 +1066,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
if (titleIsGameCardInfoUpdated())
|
||||
{
|
||||
updateTitleList();
|
||||
updateTitleList(&g_userTitlesMenu, &g_userTitlesSubMenu, false);
|
||||
data_update = true;
|
||||
break;
|
||||
}
|
||||
|
@ -1083,7 +1098,7 @@ int main(int argc, char *argv[])
|
|||
} else
|
||||
if (child_menu->id == MenuId_NSP || child_menu->id == MenuId_Ticket || child_menu->id == MenuId_Nca)
|
||||
{
|
||||
u32 title_type = *((u32*)selected_element->userdata);
|
||||
u32 title_type = (cur_menu->id != MenuId_SystemTitles ? *((u32*)selected_element->userdata) : NcmContentMetaType_Unknown);
|
||||
|
||||
switch(title_type)
|
||||
{
|
||||
|
@ -1100,7 +1115,8 @@ int main(int argc, char *argv[])
|
|||
title_info = user_app_data.aoc_patch_info;
|
||||
break;
|
||||
default:
|
||||
title_info = NULL;
|
||||
/* Get TitleInfo element on demand. */
|
||||
title_info = titleGetInfoFromStorageByTitleId(NcmStorageId_BuiltInSystem, app_metadata->title_id);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1115,6 +1131,8 @@ int main(int argc, char *argv[])
|
|||
consolePrint("failed to generate nca list\n");
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (!error && cur_menu->id == MenuId_SystemTitles) is_system = true;
|
||||
}
|
||||
|
||||
if (!error)
|
||||
|
@ -1123,9 +1141,14 @@ int main(int argc, char *argv[])
|
|||
title_info_idx = 1;
|
||||
}
|
||||
} else {
|
||||
consolePrint("\nthe selected title doesn't have available %s data\n", \
|
||||
title_type == NcmContentMetaType_Application ? "base application" : \
|
||||
(title_type == NcmContentMetaType_Patch ? "update" : (title_type == NcmContentMetaType_AddOnContent ? "dlc" : "dlc update")));
|
||||
if (cur_menu->id == MenuId_SystemTitles)
|
||||
{
|
||||
consolePrint("\nunable to retrieve data for system title %016lX\n", app_metadata->title_id);
|
||||
} else {
|
||||
consolePrint("\nthe selected title doesn't have available %s data\n", \
|
||||
title_type == NcmContentMetaType_Application ? "base application" : \
|
||||
(title_type == NcmContentMetaType_Patch ? "update" : (title_type == NcmContentMetaType_AddOnContent ? "dlc" : "dlc update")));
|
||||
}
|
||||
|
||||
error = true;
|
||||
}
|
||||
|
@ -1278,7 +1301,7 @@ int main(int argc, char *argv[])
|
|||
} else
|
||||
if ((btn_down & HidNpadButton_B) && cur_menu->parent)
|
||||
{
|
||||
if (cur_menu->id == MenuId_UserTitles)
|
||||
if (cur_menu->id == MenuId_UserTitles || cur_menu->id == MenuId_SystemTitles)
|
||||
{
|
||||
app_metadata = NULL;
|
||||
} else
|
||||
|
@ -1304,6 +1327,12 @@ int main(int argc, char *argv[])
|
|||
if (cur_menu->id == MenuId_Nca)
|
||||
{
|
||||
freeNcaList();
|
||||
|
||||
if (is_system)
|
||||
{
|
||||
titleFreeTitleInfo(&title_info);
|
||||
is_system = false;
|
||||
}
|
||||
} else
|
||||
if (cur_menu->id == MenuId_NcaFsSections)
|
||||
{
|
||||
|
@ -1360,7 +1389,8 @@ int main(int argc, char *argv[])
|
|||
|
||||
freeNcaList();
|
||||
|
||||
freeTitleList();
|
||||
freeTitleList(&g_systemTitlesMenu);
|
||||
freeTitleList(&g_userTitlesMenu);
|
||||
|
||||
freeStorageList();
|
||||
|
||||
|
@ -1462,7 +1492,6 @@ void freeStorageList(void)
|
|||
|
||||
void updateStorageList(void)
|
||||
{
|
||||
char **tmp = NULL;
|
||||
u32 elem_count = 0, idx = 0;
|
||||
|
||||
/* Free all previously allocated data. */
|
||||
|
@ -1472,13 +1501,8 @@ void updateStorageList(void)
|
|||
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
|
||||
/* Allocate buffer. */
|
||||
g_storageOptions = calloc(elem_count + 1, sizeof(char*)); // NULL terminator
|
||||
|
||||
/* Generate UMS device strings. */
|
||||
for(u32 i = 0; i < elem_count; i++)
|
||||
|
@ -1531,62 +1555,61 @@ void updateStorageList(void)
|
|||
g_storageMenuElementOption.options = g_storageOptions;
|
||||
}
|
||||
|
||||
void freeTitleList(void)
|
||||
void freeTitleList(Menu *menu)
|
||||
{
|
||||
/* Free all previously allocated data. */
|
||||
if (g_userTitlesMenuElements)
|
||||
{
|
||||
for(u32 i = 0; g_userTitlesMenuElements[i] != NULL; i++) free(g_userTitlesMenuElements[i]);
|
||||
if (!menu) return;
|
||||
|
||||
free(g_userTitlesMenuElements);
|
||||
g_userTitlesMenuElements = NULL;
|
||||
MenuElement **elements = menu->elements;
|
||||
|
||||
/* Free all previously allocated data. */
|
||||
if (elements)
|
||||
{
|
||||
for(u32 i = 0; elements[i]; i++) free(elements[i]);
|
||||
free(elements);
|
||||
}
|
||||
|
||||
g_userTitlesMenu.scroll = 0;
|
||||
g_userTitlesMenu.selected = 0;
|
||||
g_userTitlesMenu.elements = NULL;
|
||||
menu->scroll = 0;
|
||||
menu->selected = 0;
|
||||
menu->elements = NULL;
|
||||
}
|
||||
|
||||
void updateTitleList(void)
|
||||
void updateTitleList(Menu *menu, Menu *submenu, bool is_system)
|
||||
{
|
||||
if (!menu || !submenu) return;
|
||||
|
||||
u32 app_count = 0, idx = 0;
|
||||
TitleApplicationMetadata **app_metadata = NULL;
|
||||
MenuElement **tmp = NULL;
|
||||
MenuElement **elements = NULL;
|
||||
|
||||
/* Free all previously allocated data. */
|
||||
freeTitleList();
|
||||
freeTitleList(menu);
|
||||
|
||||
/* Get application metadata entries. */
|
||||
app_metadata = titleGetApplicationMetadataEntries(false, &app_count);
|
||||
app_metadata = titleGetApplicationMetadataEntries(is_system, &app_count);
|
||||
if (!app_metadata || !app_count) goto end;
|
||||
|
||||
/* Reallocate buffer. */
|
||||
tmp = realloc(g_userTitlesMenuElements, (app_count + 1) * sizeof(MenuElement*)); // NULL terminator
|
||||
|
||||
g_userTitlesMenuElements = tmp;
|
||||
tmp = NULL;
|
||||
|
||||
memset(g_userTitlesMenuElements, 0, (app_count + 1) * sizeof(MenuElement*)); // NULL terminator
|
||||
/* Allocate buffer. */
|
||||
elements = calloc(app_count + 1, sizeof(MenuElement*)); // NULL terminator
|
||||
|
||||
/* Generate menu elements. */
|
||||
for(u32 i = 0; i < app_count; i++)
|
||||
{
|
||||
TitleApplicationMetadata *cur_app_metadata = app_metadata[i];
|
||||
|
||||
if (!g_userTitlesMenuElements[idx])
|
||||
if (!elements[idx])
|
||||
{
|
||||
g_userTitlesMenuElements[idx] = calloc(1, sizeof(MenuElement));
|
||||
if (!g_userTitlesMenuElements[idx]) continue;
|
||||
elements[idx] = calloc(1, sizeof(MenuElement));
|
||||
if (!elements[idx]) continue;
|
||||
}
|
||||
|
||||
g_userTitlesMenuElements[idx]->str = cur_app_metadata->lang_entry.name;
|
||||
g_userTitlesMenuElements[idx]->child_menu = &g_userTitlesSubMenu;
|
||||
g_userTitlesMenuElements[idx]->userdata = cur_app_metadata;
|
||||
elements[idx]->str = cur_app_metadata->lang_entry.name;
|
||||
elements[idx]->child_menu = submenu;
|
||||
elements[idx]->userdata = cur_app_metadata;
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
g_userTitlesMenu.elements = g_userTitlesMenuElements;
|
||||
menu->elements = elements;
|
||||
|
||||
end:
|
||||
if (app_metadata) free(app_metadata);
|
||||
|
@ -1621,19 +1644,13 @@ void updateNcaList(TitleInfo *title_info)
|
|||
{
|
||||
u32 content_count = title_info->content_count, idx = 0;
|
||||
NcmContentInfo *content_infos = title_info->content_infos;
|
||||
MenuElement **tmp = NULL;
|
||||
char nca_id_str[0x21] = {0};
|
||||
|
||||
/* Free all previously allocated data. */
|
||||
freeNcaList();
|
||||
|
||||
/* Reallocate buffer. */
|
||||
tmp = realloc(g_ncaMenuElements, (content_count + 2) * sizeof(MenuElement*)); // Output storage, NULL terminator
|
||||
|
||||
g_ncaMenuElements = tmp;
|
||||
tmp = NULL;
|
||||
|
||||
memset(g_ncaMenuElements, 0, (content_count + 2) * sizeof(MenuElement*)); // Output storage, NULL terminator
|
||||
/* Allocate buffer. */
|
||||
g_ncaMenuElements = calloc(content_count + 2, sizeof(MenuElement*)); // Output storage, NULL terminator
|
||||
|
||||
/* Generate menu elements. */
|
||||
for(u32 i = 0; i < content_count; i++)
|
||||
|
@ -1733,19 +1750,13 @@ void updateNcaFsSectionsList(NcaUserData *nca_user_data)
|
|||
{
|
||||
TitleInfo *title_info = nca_user_data->title_info;
|
||||
NcmContentInfo *content_info = &(title_info->content_infos[nca_user_data->content_idx]);
|
||||
MenuElement **tmp = NULL;
|
||||
u32 idx = 0;
|
||||
|
||||
/* Free all previously allocated data. */
|
||||
freeNcaFsSectionsList();
|
||||
|
||||
/* Reallocate buffer. */
|
||||
tmp = realloc(g_ncaFsSectionsMenuElements, (NCA_FS_HEADER_COUNT + 1) * sizeof(MenuElement*)); // NULL terminator
|
||||
|
||||
g_ncaFsSectionsMenuElements = tmp;
|
||||
tmp = NULL;
|
||||
|
||||
memset(g_ncaFsSectionsMenuElements, 0, (NCA_FS_HEADER_COUNT + 1) * sizeof(MenuElement*)); // NULL terminator
|
||||
/* Allocate buffer. */
|
||||
g_ncaFsSectionsMenuElements = calloc(NCA_FS_HEADER_COUNT + 1, sizeof(MenuElement*)); // NULL terminator
|
||||
|
||||
/* Initialize NCA context. */
|
||||
g_ncaFsSectionsMenuCtx = calloc(1, sizeof(NcaContext));
|
||||
|
@ -1815,7 +1826,6 @@ void freeNcaBasePatchList(void)
|
|||
|
||||
void updateNcaBasePatchList(TitleUserApplicationData *user_app_data, TitleInfo *title_info, NcaFsSectionContext *nca_fs_ctx)
|
||||
{
|
||||
char **tmp = NULL;
|
||||
u32 elem_count = 1, idx = 1; // "no" option
|
||||
TitleInfo *cur_title_info = NULL;
|
||||
|
||||
|
@ -1847,6 +1857,7 @@ void updateNcaBasePatchList(TitleUserApplicationData *user_app_data, TitleInfo *
|
|||
g_ncaBasePatchTitleInfo = titleGetAddOnContentBaseOrPatchList(title_info);
|
||||
break;
|
||||
default:
|
||||
unsupported = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
@ -1856,13 +1867,8 @@ void updateNcaBasePatchList(TitleUserApplicationData *user_app_data, TitleInfo *
|
|||
/* Calculate element count. */
|
||||
elem_count += titleGetCountFromInfoBlock(g_ncaBasePatchTitleInfo);
|
||||
|
||||
/* Reallocate buffer. */
|
||||
tmp = realloc(g_ncaBasePatchOptions, (elem_count + 1) * sizeof(char*)); // NULL terminator
|
||||
|
||||
g_ncaBasePatchOptions = tmp;
|
||||
tmp = NULL;
|
||||
|
||||
memset(g_ncaBasePatchOptions, 0, (elem_count + 1) * sizeof(char*)); // NULL terminator
|
||||
/* Allocate buffer. */
|
||||
g_ncaBasePatchOptions = calloc(elem_count + 1, sizeof(char*)); // NULL terminator
|
||||
|
||||
/* Set first option. */
|
||||
g_ncaBasePatchOptions[0] = (unsupported ? "unsupported by this content/section type combo" : (elem_count < 2 ? "none available" : "no"));
|
||||
|
@ -3128,7 +3134,8 @@ static bool saveNintendoContentArchiveFsSection(void *userdata)
|
|||
|
||||
/* Override LayeredFS flag, if needed. */
|
||||
if (use_layeredfs_dir && \
|
||||
(((title_type == NcmContentMetaType_Application || title_type == NcmContentMetaType_Patch) && (content_type != NcmContentType_Program || nca_fs_ctx->section_idx > 1)) || \
|
||||
(title_type < NcmContentMetaType_Application || \
|
||||
((title_type == NcmContentMetaType_Application || title_type == NcmContentMetaType_Patch) && (content_type != NcmContentType_Program || nca_fs_ctx->section_idx > 1)) || \
|
||||
((title_type == NcmContentMetaType_AddOnContent || title_type == NcmContentMetaType_DataPatch) && (content_type != NcmContentType_Data || nca_fs_ctx->section_idx != 0))))
|
||||
{
|
||||
consolePrint("layeredfs setting disabled (unsupported by current content/section type combo)\n");
|
||||
|
|
|
@ -1,502 +0,0 @@
|
|||
/*
|
||||
* main.c
|
||||
*
|
||||
* Copyright (c) 2020-2023, DarkMatterCore <pabloacurielz@gmail.com>.
|
||||
*
|
||||
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
|
||||
*
|
||||
* nxdumptool is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* nxdumptool is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "nxdt_utils.h"
|
||||
#include "nca.h"
|
||||
#include "title.h"
|
||||
#include "pfs.h"
|
||||
#include "romfs.h"
|
||||
|
||||
#define BLOCK_SIZE 0x800000
|
||||
#define OUTPATH "sdmc:/systitle_dumps"
|
||||
|
||||
bool g_borealisInitialized = false;
|
||||
|
||||
static PadState g_padState = {0};
|
||||
|
||||
static u8 *buf = NULL;
|
||||
static FILE *filefd = NULL;
|
||||
static char path[FS_MAX_PATH * 2] = {0};
|
||||
|
||||
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 void consolePrint(const char *text, ...)
|
||||
{
|
||||
va_list v;
|
||||
va_start(v, text);
|
||||
vfprintf(stdout, text, v);
|
||||
va_end(v);
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
|
||||
static void dumpPartitionFs(TitleInfo *info, NcaFsSectionContext *nca_fs_ctx)
|
||||
{
|
||||
if (!buf || !info || !nca_fs_ctx) return;
|
||||
|
||||
u32 pfs_entry_count = 0;
|
||||
PartitionFileSystemContext pfs_ctx = {0};
|
||||
PartitionFileSystemEntry *pfs_entry = NULL;
|
||||
char *pfs_entry_name = NULL;
|
||||
|
||||
size_t path_len = 0;
|
||||
*path = '\0';
|
||||
|
||||
if (!pfsInitializeContext(&pfs_ctx, nca_fs_ctx))
|
||||
{
|
||||
consolePrint("pfs initialize ctx failed!\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!(pfs_entry_count = pfsGetEntryCount(&pfs_ctx)))
|
||||
{
|
||||
consolePrint("pfs entry count is zero!\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), OUTPATH "/%016lX - %s/%s (%s)/Section %u (%s)", info->meta_key.id, info->app_metadata->lang_entry.name, ((NcaContext*)nca_fs_ctx->nca_ctx)->content_id_str, \
|
||||
titleGetNcmContentTypeName(((NcaContext*)nca_fs_ctx->nca_ctx)->content_type), nca_fs_ctx->section_idx, ncaGetFsSectionTypeName(nca_fs_ctx));
|
||||
utilsCreateDirectoryTree(path, true);
|
||||
path_len = strlen(path);
|
||||
|
||||
for(u32 i = 0; i < pfs_entry_count; i++)
|
||||
{
|
||||
if (!(pfs_entry = pfsGetEntryByIndex(&pfs_ctx, i)) || !(pfs_entry_name = pfsGetEntryNameByIndex(&pfs_ctx, i)) || !strlen(pfs_entry_name))
|
||||
{
|
||||
consolePrint("pfs get entry / get name #%u failed!\n", i);
|
||||
goto end;
|
||||
}
|
||||
|
||||
path[path_len] = '\0';
|
||||
strcat(path, "/");
|
||||
strcat(path, pfs_entry_name);
|
||||
utilsReplaceIllegalCharacters(path + path_len + 1, true);
|
||||
|
||||
filefd = fopen(path, "wb");
|
||||
if (!filefd)
|
||||
{
|
||||
consolePrint("failed to create \"%s\"!\n", path);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Set file size. */
|
||||
ftruncate(fileno(filefd), (off_t)pfs_entry->size);
|
||||
|
||||
consolePrint("dumping \"%s\"...\n", pfs_entry_name);
|
||||
|
||||
u64 blksize = BLOCK_SIZE;
|
||||
for(u64 j = 0; j < pfs_entry->size; j += blksize)
|
||||
{
|
||||
if (blksize > (pfs_entry->size - j)) blksize = (pfs_entry->size - j);
|
||||
|
||||
if (!pfsReadEntryData(&pfs_ctx, pfs_entry, buf, blksize, j))
|
||||
{
|
||||
consolePrint("failed to read 0x%lX block from offset 0x%lX!\n", blksize, j);
|
||||
goto end;
|
||||
}
|
||||
|
||||
fwrite(buf, 1, blksize, filefd);
|
||||
}
|
||||
|
||||
fclose(filefd);
|
||||
filefd = NULL;
|
||||
}
|
||||
|
||||
consolePrint("pfs dump complete\n");
|
||||
|
||||
end:
|
||||
if (filefd)
|
||||
{
|
||||
fclose(filefd);
|
||||
remove(path);
|
||||
}
|
||||
|
||||
if (*path) utilsCommitSdCardFileSystemChanges();
|
||||
|
||||
pfsFreeContext(&pfs_ctx);
|
||||
}
|
||||
|
||||
static void dumpRomFs(TitleInfo *info, NcaFsSectionContext *nca_fs_ctx)
|
||||
{
|
||||
if (!buf || !info || !nca_fs_ctx) return;
|
||||
|
||||
RomFileSystemContext romfs_ctx = {0};
|
||||
RomFileSystemFileEntry *romfs_file_entry = NULL;
|
||||
|
||||
size_t path_len = 0;
|
||||
*path = '\0';
|
||||
|
||||
if (!romfsInitializeContext(&romfs_ctx, nca_fs_ctx, NULL))
|
||||
{
|
||||
consolePrint("romfs initialize ctx failed!\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), OUTPATH "/%016lX - %s/%s (%s)/Section %u (%s)", info->meta_key.id, info->app_metadata->lang_entry.name, ((NcaContext*)nca_fs_ctx->nca_ctx)->content_id_str, \
|
||||
titleGetNcmContentTypeName(((NcaContext*)nca_fs_ctx->nca_ctx)->content_type), nca_fs_ctx->section_idx, ncaGetFsSectionTypeName(nca_fs_ctx));
|
||||
utilsCreateDirectoryTree(path, true);
|
||||
path_len = strlen(path);
|
||||
|
||||
while(romfsCanMoveToNextFileEntry(&romfs_ctx))
|
||||
{
|
||||
if (!(romfs_file_entry = romfsGetCurrentFileEntry(&romfs_ctx)) || \
|
||||
!romfsGeneratePathFromFileEntry(&romfs_ctx, romfs_file_entry, path + path_len, sizeof(path) - path_len, RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly))
|
||||
{
|
||||
consolePrint("romfs get entry / generate path failed for 0x%lX!\n", romfs_ctx.cur_file_offset);
|
||||
goto end;
|
||||
}
|
||||
|
||||
utilsCreateDirectoryTree(path, false);
|
||||
|
||||
filefd = fopen(path, "wb");
|
||||
if (!filefd)
|
||||
{
|
||||
consolePrint("failed to create \"%s\"!\n", path);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Set file size. */
|
||||
ftruncate(fileno(filefd), (off_t)romfs_file_entry->size);
|
||||
|
||||
consolePrint("dumping \"%s\"...\n", path + path_len);
|
||||
|
||||
u64 blksize = BLOCK_SIZE;
|
||||
for(u64 j = 0; j < romfs_file_entry->size; j += blksize)
|
||||
{
|
||||
if (blksize > (romfs_file_entry->size - j)) blksize = (romfs_file_entry->size - j);
|
||||
|
||||
if (!romfsReadFileEntryData(&romfs_ctx, romfs_file_entry, buf, blksize, j))
|
||||
{
|
||||
consolePrint("failed to read 0x%lX block from offset 0x%lX!\n", blksize, j);
|
||||
goto end;
|
||||
}
|
||||
|
||||
fwrite(buf, 1, blksize, filefd);
|
||||
}
|
||||
|
||||
fclose(filefd);
|
||||
filefd = NULL;
|
||||
|
||||
if (!romfsMoveToNextFileEntry(&romfs_ctx))
|
||||
{
|
||||
consolePrint("failed to move to next file entry!\n");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
consolePrint("romfs dump complete\n");
|
||||
|
||||
end:
|
||||
if (filefd)
|
||||
{
|
||||
fclose(filefd);
|
||||
remove(path);
|
||||
}
|
||||
|
||||
if (*path) utilsCommitSdCardFileSystemChanges();
|
||||
|
||||
romfsFreeContext(&romfs_ctx);
|
||||
}
|
||||
|
||||
static void dumpFsSection(TitleInfo *info, NcaFsSectionContext *nca_fs_ctx)
|
||||
{
|
||||
if (!buf || !info || !nca_fs_ctx) return;
|
||||
|
||||
switch(nca_fs_ctx->section_type)
|
||||
{
|
||||
case NcaFsSectionType_PartitionFs:
|
||||
dumpPartitionFs(info, nca_fs_ctx);
|
||||
break;
|
||||
case NcaFsSectionType_RomFs:
|
||||
case NcaFsSectionType_Nca0RomFs:
|
||||
dumpRomFs(info, nca_fs_ctx);
|
||||
break;
|
||||
default:
|
||||
consolePrint("invalid section type!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret = EXIT_SUCCESS;
|
||||
|
||||
if (!utilsInitializeResources(argc, (const char**)argv))
|
||||
{
|
||||
ret = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Configure input. */
|
||||
/* Up to 8 different, full controller inputs. */
|
||||
/* Individual Joy-Cons not supported. */
|
||||
padConfigureInput(8, HidNpadStyleSet_NpadFullCtrl);
|
||||
padInitializeWithMask(&g_padState, 0x1000000FFUL);
|
||||
|
||||
consoleInit(NULL);
|
||||
|
||||
u32 app_count = 0;
|
||||
TitleApplicationMetadata **app_metadata = NULL;
|
||||
TitleInfo *cur_title_info = NULL;
|
||||
|
||||
u32 selected_idx = 0, menu = 0, page_size = 30, scroll = 0;
|
||||
u32 title_idx = 0, title_scroll = 0, nca_idx = 0;
|
||||
char nca_id_str[0x21] = {0};
|
||||
|
||||
NcaContext *nca_ctx = NULL;
|
||||
|
||||
bool applet_status = true;
|
||||
|
||||
app_metadata = titleGetApplicationMetadataEntries(true, &app_count);
|
||||
if (!app_metadata || !app_count)
|
||||
{
|
||||
consolePrint("app metadata failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("app metadata succeeded\n");
|
||||
|
||||
buf = malloc(BLOCK_SIZE);
|
||||
if (!buf)
|
||||
{
|
||||
consolePrint("buf failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("buf succeeded\n");
|
||||
|
||||
nca_ctx = calloc(1, sizeof(NcaContext));
|
||||
if (!nca_ctx)
|
||||
{
|
||||
consolePrint("nca ctx buf failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("nca ctx buf succeeded\n");
|
||||
|
||||
utilsSleep(1);
|
||||
|
||||
while((applet_status = appletMainLoop()))
|
||||
{
|
||||
consoleClear();
|
||||
|
||||
printf("select a %s.", menu == 0 ? "system title to view its contents" : (menu == 1 ? "content" : "fs section"));
|
||||
printf("\npress b to %s.\n\n", menu == 0 ? "exit" : "go back");
|
||||
|
||||
if (menu == 0)
|
||||
{
|
||||
printf("title: %u / %u\n", selected_idx + 1, app_count);
|
||||
printf("selected title: %016lX - %s\n\n", app_metadata[selected_idx]->title_id, app_metadata[selected_idx]->lang_entry.name);
|
||||
}
|
||||
|
||||
if (menu >= 1) printf("selected title: %016lX - %s\n\n", app_metadata[title_idx]->title_id, app_metadata[title_idx]->lang_entry.name);
|
||||
|
||||
if (menu == 2) printf("selected content: %s (%s)\n\n", nca_id_str, titleGetNcmContentTypeName(cur_title_info->content_infos[nca_idx].content_type));
|
||||
|
||||
u32 max_val = (menu == 0 ? app_count : (menu == 1 ? cur_title_info->content_count : NCA_FS_HEADER_COUNT));
|
||||
for(u32 i = scroll; i < max_val; i++)
|
||||
{
|
||||
if (i >= (scroll + page_size)) break;
|
||||
|
||||
printf("%s", i == selected_idx ? " -> " : " ");
|
||||
|
||||
if (menu == 0)
|
||||
{
|
||||
printf("%016lX - %s\n", app_metadata[i]->title_id, app_metadata[i]->lang_entry.name);
|
||||
} else
|
||||
if (menu == 1)
|
||||
{
|
||||
utilsGenerateHexStringFromData(nca_id_str, sizeof(nca_id_str), cur_title_info->content_infos[i].content_id.c, sizeof(cur_title_info->content_infos[i].content_id.c), false);
|
||||
printf("%s (%s)\n", nca_id_str, titleGetNcmContentTypeName(cur_title_info->content_infos[i].content_type));
|
||||
} else
|
||||
if (menu == 2)
|
||||
{
|
||||
printf("fs section #%u (%s)\n", i + 1, ncaGetFsSectionTypeName(&(nca_ctx->fs_ctx[i])));
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
consoleUpdate(NULL);
|
||||
|
||||
u64 btn_down = 0, btn_held = 0;
|
||||
while((applet_status = appletMainLoop()))
|
||||
{
|
||||
utilsScanPads();
|
||||
btn_down = utilsGetButtonsDown();
|
||||
btn_held = utilsGetButtonsHeld();
|
||||
if (btn_down || btn_held) break;
|
||||
}
|
||||
|
||||
if (!applet_status) break;
|
||||
|
||||
if (btn_down & HidNpadButton_A)
|
||||
{
|
||||
bool error = false;
|
||||
|
||||
if (menu == 0)
|
||||
{
|
||||
title_idx = selected_idx;
|
||||
title_scroll = scroll;
|
||||
} else
|
||||
if (menu == 1)
|
||||
{
|
||||
nca_idx = selected_idx;
|
||||
utilsGenerateHexStringFromData(nca_id_str, sizeof(nca_id_str), cur_title_info->content_infos[nca_idx].content_id.c, sizeof(cur_title_info->content_infos[nca_idx].content_id.c), false);
|
||||
}
|
||||
|
||||
menu++;
|
||||
|
||||
if (menu == 1)
|
||||
{
|
||||
cur_title_info = titleGetInfoFromStorageByTitleId(NcmStorageId_BuiltInSystem, app_metadata[title_idx]->title_id);
|
||||
if (!cur_title_info)
|
||||
{
|
||||
consolePrint("failed to get title info\n");
|
||||
error = true;
|
||||
}
|
||||
} else
|
||||
if (menu == 2)
|
||||
{
|
||||
if (!ncaInitializeContext(nca_ctx, cur_title_info->storage_id, 0, &(cur_title_info->meta_key), &(cur_title_info->content_infos[nca_idx]), NULL))
|
||||
{
|
||||
consolePrint("nca initialize ctx failed\n");
|
||||
error = true;
|
||||
}
|
||||
} else
|
||||
if (menu == 3)
|
||||
{
|
||||
consoleClear();
|
||||
utilsSetLongRunningProcessState(true);
|
||||
dumpFsSection(cur_title_info, &(nca_ctx->fs_ctx[selected_idx]));
|
||||
utilsSetLongRunningProcessState(false);
|
||||
}
|
||||
|
||||
if (error || menu >= 3)
|
||||
{
|
||||
consolePrint("press any button to continue\n");
|
||||
utilsWaitForButtonPress(0);
|
||||
menu--;
|
||||
} else {
|
||||
selected_idx = scroll = 0;
|
||||
}
|
||||
} else
|
||||
if ((btn_down & HidNpadButton_Down) || (btn_held & (HidNpadButton_StickLDown | HidNpadButton_StickRDown)))
|
||||
{
|
||||
selected_idx++;
|
||||
|
||||
if (selected_idx >= max_val)
|
||||
{
|
||||
if (btn_down & HidNpadButton_Down)
|
||||
{
|
||||
selected_idx = scroll = 0;
|
||||
} else {
|
||||
selected_idx = (max_val - 1);
|
||||
}
|
||||
} else
|
||||
if (selected_idx >= (scroll + (page_size / 2)) && max_val > (scroll + page_size))
|
||||
{
|
||||
scroll++;
|
||||
}
|
||||
} else
|
||||
if ((btn_down & HidNpadButton_Up) || (btn_held & (HidNpadButton_StickLUp | HidNpadButton_StickRUp)))
|
||||
{
|
||||
selected_idx--;
|
||||
|
||||
if (selected_idx == UINT32_MAX)
|
||||
{
|
||||
if (btn_down & HidNpadButton_Up)
|
||||
{
|
||||
selected_idx = (max_val - 1);
|
||||
scroll = (max_val >= page_size ? (max_val - page_size) : 0);
|
||||
} else {
|
||||
selected_idx = 0;
|
||||
}
|
||||
} else
|
||||
if (selected_idx < (scroll + (page_size / 2)) && scroll > 0)
|
||||
{
|
||||
scroll--;
|
||||
}
|
||||
} else
|
||||
if (btn_down & HidNpadButton_B)
|
||||
{
|
||||
menu--;
|
||||
|
||||
if (menu == UINT32_MAX)
|
||||
{
|
||||
break;
|
||||
} else {
|
||||
selected_idx = (menu == 0 ? title_idx : nca_idx);
|
||||
scroll = (menu == 0 ? title_scroll : 0);
|
||||
if (menu == 0) titleFreeTitleInfo(&cur_title_info);
|
||||
}
|
||||
}
|
||||
|
||||
if (btn_held & (HidNpadButton_StickLDown | HidNpadButton_StickRDown | HidNpadButton_StickLUp | HidNpadButton_StickRUp)) svcSleepThread(50000000); // 50 ms
|
||||
}
|
||||
|
||||
if (!applet_status) menu = UINT32_MAX;
|
||||
|
||||
out2:
|
||||
if (menu != UINT32_MAX)
|
||||
{
|
||||
consolePrint("press any button to exit\n");
|
||||
utilsWaitForButtonPress(0);
|
||||
}
|
||||
|
||||
if (nca_ctx) free(nca_ctx);
|
||||
|
||||
if (buf) free(buf);
|
||||
|
||||
if (app_metadata) free(app_metadata);
|
||||
|
||||
out:
|
||||
utilsCloseResources();
|
||||
|
||||
consoleExit(NULL);
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -60,6 +60,8 @@ typedef struct {
|
|||
};
|
||||
} SmcGenerateAesKekOption;
|
||||
|
||||
NXDT_ASSERT(SmcGenerateAesKekOption, 0x4);
|
||||
|
||||
/// Helper inline functions.
|
||||
|
||||
NX_INLINE void smcPrepareGenerateAesKekOption(bool is_device_unique, u32 key_type_idx, u32 seal_key_idx, SmcGenerateAesKekOption *out)
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#define TITLE_STORAGE_COUNT 4 /* GameCard, BuiltInSystem, BuiltInUser, SdCard. */
|
||||
#define TITLE_STORAGE_INDEX(storage_id) ((storage_id) - NcmStorageId_GameCard)
|
||||
|
||||
#define NCM_CMT_APP_OFFSET 0x7A
|
||||
|
||||
/* Type definitions. */
|
||||
|
||||
typedef struct {
|
||||
|
@ -83,25 +85,31 @@ static const char *g_titleNcmContentTypeNames[] = {
|
|||
};
|
||||
|
||||
static const char *g_titleNcmContentMetaTypeNames[] = {
|
||||
[NcmContentMetaType_Unknown] = "Unknown",
|
||||
[NcmContentMetaType_SystemProgram] = "SystemProgram",
|
||||
[NcmContentMetaType_SystemData] = "SystemData",
|
||||
[NcmContentMetaType_SystemUpdate] = "SystemUpdate",
|
||||
[NcmContentMetaType_BootImagePackage] = "BootImagePackage",
|
||||
[NcmContentMetaType_BootImagePackageSafe] = "BootImagePackageSafe",
|
||||
[NcmContentMetaType_Application - 0x7A] = "Application",
|
||||
[NcmContentMetaType_Patch - 0x7A] = "Patch",
|
||||
[NcmContentMetaType_AddOnContent - 0x7A] = "AddOnContent",
|
||||
[NcmContentMetaType_Delta - 0x7A] = "Delta",
|
||||
[NcmContentMetaType_DataPatch - 0x7A] = "DataPatch"
|
||||
[NcmContentMetaType_Unknown] = "Unknown",
|
||||
[NcmContentMetaType_SystemProgram] = "SystemProgram",
|
||||
[NcmContentMetaType_SystemData] = "SystemData",
|
||||
[NcmContentMetaType_SystemUpdate] = "SystemUpdate",
|
||||
[NcmContentMetaType_BootImagePackage] = "BootImagePackage",
|
||||
[NcmContentMetaType_BootImagePackageSafe] = "BootImagePackageSafe",
|
||||
[NcmContentMetaType_Application - NCM_CMT_APP_OFFSET] = "Application",
|
||||
[NcmContentMetaType_Patch - NCM_CMT_APP_OFFSET] = "Patch",
|
||||
[NcmContentMetaType_AddOnContent - NCM_CMT_APP_OFFSET] = "AddOnContent",
|
||||
[NcmContentMetaType_Delta - NCM_CMT_APP_OFFSET] = "Delta",
|
||||
[NcmContentMetaType_DataPatch - NCM_CMT_APP_OFFSET] = "DataPatch"
|
||||
};
|
||||
|
||||
static const char *g_filenameTypeStrings[] = {
|
||||
[NcmContentMetaType_Application - 0x80] = "BASE",
|
||||
[NcmContentMetaType_Patch - 0x80] = "UPD",
|
||||
[NcmContentMetaType_AddOnContent - 0x80] = "DLC",
|
||||
[NcmContentMetaType_Delta - 0x80] = "DELTA",
|
||||
[NcmContentMetaType_DataPatch - 0x80] = "DLCUPD"
|
||||
[NcmContentMetaType_Unknown] = "UNK",
|
||||
[NcmContentMetaType_SystemProgram] = "SYSPRG",
|
||||
[NcmContentMetaType_SystemData] = "SYSDAT",
|
||||
[NcmContentMetaType_SystemUpdate] = "SYSUPD",
|
||||
[NcmContentMetaType_BootImagePackage] = "BIP",
|
||||
[NcmContentMetaType_BootImagePackageSafe] = "BIPS",
|
||||
[NcmContentMetaType_Application - NCM_CMT_APP_OFFSET] = "BASE",
|
||||
[NcmContentMetaType_Patch - NCM_CMT_APP_OFFSET] = "UPD",
|
||||
[NcmContentMetaType_AddOnContent - NCM_CMT_APP_OFFSET] = "DLC",
|
||||
[NcmContentMetaType_Delta - NCM_CMT_APP_OFFSET] = "DELTA",
|
||||
[NcmContentMetaType_DataPatch - NCM_CMT_APP_OFFSET] = "DLCUPD"
|
||||
};
|
||||
|
||||
/* Info retrieved from https://switchbrew.org/wiki/Title_list. */
|
||||
|
@ -1072,15 +1080,17 @@ bool titleIsGameCardInfoUpdated(void)
|
|||
|
||||
char *titleGenerateFileName(TitleInfo *title_info, u8 naming_convention, u8 illegal_char_replace_type)
|
||||
{
|
||||
if (!title_info || title_info->meta_key.type < NcmContentMetaType_Application || title_info->meta_key.type > NcmContentMetaType_DataPatch || \
|
||||
naming_convention > TitleNamingConvention_IdAndVersionOnly || (naming_convention == TitleNamingConvention_Full && \
|
||||
illegal_char_replace_type > TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly))
|
||||
if (!title_info || (title_info->meta_key.type > NcmContentMetaType_BootImagePackageSafe && title_info->meta_key.type < NcmContentMetaType_Application) || \
|
||||
title_info->meta_key.type > NcmContentMetaType_DataPatch || naming_convention > TitleNamingConvention_IdAndVersionOnly || \
|
||||
(naming_convention == TitleNamingConvention_Full && illegal_char_replace_type > TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly))
|
||||
{
|
||||
LOG_MSG_ERROR("Invalid parameters!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u8 type = (title_info->meta_key.type - 0x80);
|
||||
u8 type_idx = title_info->meta_key.type;
|
||||
if (type_idx >= NcmContentMetaType_Application) type_idx -= NCM_CMT_APP_OFFSET;
|
||||
|
||||
char title_name[0x400] = {0}, *version_str = NULL, *filename = NULL;
|
||||
|
||||
/* Generate filename for this title. */
|
||||
|
@ -1101,11 +1111,11 @@ char *titleGenerateFileName(TitleInfo *title_info, u8 naming_convention, u8 ille
|
|||
if (illegal_char_replace_type) utilsReplaceIllegalCharacters(title_name, illegal_char_replace_type == TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly);
|
||||
}
|
||||
|
||||
sprintf(title_name + strlen(title_name), "[%016lX][v%u][%s]", title_info->meta_key.id, title_info->meta_key.version, g_filenameTypeStrings[type]);
|
||||
sprintf(title_name + strlen(title_name), "[%016lX][v%u][%s]", title_info->meta_key.id, title_info->meta_key.version, g_filenameTypeStrings[type_idx]);
|
||||
} else
|
||||
if (naming_convention == TitleNamingConvention_IdAndVersionOnly)
|
||||
{
|
||||
sprintf(title_name, "%016lX_v%u_%s", title_info->meta_key.id, title_info->meta_key.version, g_filenameTypeStrings[type]);
|
||||
sprintf(title_name, "%016lX_v%u_%s", title_info->meta_key.id, title_info->meta_key.version, g_filenameTypeStrings[type_idx]);
|
||||
}
|
||||
|
||||
/* Duplicate generated filename. */
|
||||
|
@ -1251,7 +1261,7 @@ const char *titleGetNcmContentTypeName(u8 content_type)
|
|||
const char *titleGetNcmContentMetaTypeName(u8 content_meta_type)
|
||||
{
|
||||
if ((content_meta_type > NcmContentMetaType_BootImagePackageSafe && content_meta_type < NcmContentMetaType_Application) || content_meta_type > NcmContentMetaType_DataPatch) return NULL;
|
||||
return (content_meta_type <= NcmContentMetaType_BootImagePackageSafe ? g_titleNcmContentMetaTypeNames[content_meta_type] : g_titleNcmContentMetaTypeNames[content_meta_type - 0x7A]);
|
||||
return (content_meta_type <= NcmContentMetaType_BootImagePackageSafe ? g_titleNcmContentMetaTypeNames[content_meta_type] : g_titleNcmContentMetaTypeNames[content_meta_type - NCM_CMT_APP_OFFSET]);
|
||||
}
|
||||
|
||||
NX_INLINE void titleFreeApplicationMetadata(void)
|
||||
|
|
Loading…
Reference in a new issue