mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-26 04:02:11 +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_Nca = 11,
|
||||||
MenuId_NcaFsSections = 12,
|
MenuId_NcaFsSections = 12,
|
||||||
MenuId_NcaFsSectionsSubMenu = 13,
|
MenuId_NcaFsSectionsSubMenu = 13,
|
||||||
MenuId_Count = 14
|
MenuId_SystemTitles = 14,
|
||||||
|
MenuId_Count = 15
|
||||||
} MenuId;
|
} MenuId;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
|
@ -149,8 +150,8 @@ static u32 menuGetElementCount(const Menu *menu);
|
||||||
void freeStorageList(void);
|
void freeStorageList(void);
|
||||||
void updateStorageList(void);
|
void updateStorageList(void);
|
||||||
|
|
||||||
void freeTitleList(void);
|
void freeTitleList(Menu *menu);
|
||||||
void updateTitleList(void);
|
void updateTitleList(Menu *menu, Menu *submenu, bool is_system);
|
||||||
|
|
||||||
void freeNcaList(void);
|
void freeNcaList(void);
|
||||||
void updateNcaList(TitleInfo *title_info);
|
void updateNcaList(TitleInfo *title_info);
|
||||||
|
@ -810,7 +811,7 @@ static MenuElement *g_userTitlesSubMenuElements[] = {
|
||||||
NULL
|
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 = {
|
static Menu g_userTitlesSubMenu = {
|
||||||
.id = MenuId_UserTitlesSubMenu,
|
.id = MenuId_UserTitlesSubMenu,
|
||||||
.parent = NULL,
|
.parent = NULL,
|
||||||
|
@ -819,9 +820,7 @@ static Menu g_userTitlesSubMenu = {
|
||||||
.elements = g_userTitlesSubMenuElements
|
.elements = g_userTitlesSubMenuElements
|
||||||
};
|
};
|
||||||
|
|
||||||
static MenuElement **g_userTitlesMenuElements = NULL;
|
// Dynamically populated.
|
||||||
|
|
||||||
// Dynamically populated using g_userTitlesMenuElements.
|
|
||||||
static Menu g_userTitlesMenu = {
|
static Menu g_userTitlesMenu = {
|
||||||
.id = MenuId_UserTitles,
|
.id = MenuId_UserTitles,
|
||||||
.parent = NULL,
|
.parent = NULL,
|
||||||
|
@ -830,6 +829,15 @@ static Menu g_userTitlesMenu = {
|
||||||
.elements = NULL
|
.elements = NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Dynamically populated.
|
||||||
|
static Menu g_systemTitlesMenu = {
|
||||||
|
.id = MenuId_SystemTitles,
|
||||||
|
.parent = NULL,
|
||||||
|
.selected = 0,
|
||||||
|
.scroll = 0,
|
||||||
|
.elements = NULL
|
||||||
|
};
|
||||||
|
|
||||||
static MenuElement *g_rootMenuElements[] = {
|
static MenuElement *g_rootMenuElements[] = {
|
||||||
&(MenuElement){
|
&(MenuElement){
|
||||||
.str = "gamecard menu",
|
.str = "gamecard menu",
|
||||||
|
@ -853,7 +861,7 @@ static MenuElement *g_rootMenuElements[] = {
|
||||||
},
|
},
|
||||||
&(MenuElement){
|
&(MenuElement){
|
||||||
.str = "system titles menu",
|
.str = "system titles menu",
|
||||||
.child_menu = NULL,
|
.child_menu = &g_systemTitlesMenu,
|
||||||
.task_func = NULL,
|
.task_func = NULL,
|
||||||
.element_options = NULL,
|
.element_options = NULL,
|
||||||
.userdata = NULL
|
.userdata = NULL
|
||||||
|
@ -894,7 +902,8 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
updateStorageList();
|
updateStorageList();
|
||||||
|
|
||||||
updateTitleList();
|
updateTitleList(&g_userTitlesMenu, &g_userTitlesSubMenu, false);
|
||||||
|
updateTitleList(&g_systemTitlesMenu, &g_ncaMenu, true);
|
||||||
|
|
||||||
Menu *cur_menu = &g_rootMenu;
|
Menu *cur_menu = &g_rootMenu;
|
||||||
u32 element_count = menuGetElementCount(cur_menu), page_size = 30;
|
u32 element_count = menuGetElementCount(cur_menu), page_size = 30;
|
||||||
|
@ -906,6 +915,8 @@ int main(int argc, char *argv[])
|
||||||
TitleInfo *title_info = NULL;
|
TitleInfo *title_info = NULL;
|
||||||
u32 title_info_idx = 0, title_info_count = 0;
|
u32 title_info_idx = 0, title_info_count = 0;
|
||||||
|
|
||||||
|
bool is_system = false;
|
||||||
|
|
||||||
while(appletMainLoop())
|
while(appletMainLoop())
|
||||||
{
|
{
|
||||||
MenuElement *selected_element = cur_menu->elements[cur_menu->selected];
|
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("press + to exit\n");
|
||||||
consolePrint("______________________________\n\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;
|
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("selected title: %016lX - %s\n", app_metadata->title_id, selected_element->str);
|
||||||
consolePrint("______________________________\n\n");
|
consolePrint("______________________________\n\n");
|
||||||
} else
|
} 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");
|
if (!is_system)
|
||||||
consolePrint("name: %s\n", app_metadata->lang_entry.name);
|
{
|
||||||
consolePrint("publisher: %s\n", app_metadata->lang_entry.author);
|
consolePrint("title info:\n\n");
|
||||||
if (cur_menu->id == MenuId_UserTitlesSubMenu || cur_menu->id == MenuId_NSPTitleTypes || cur_menu->id == MenuId_TicketTitleTypes || \
|
consolePrint("name: %s\n", app_metadata->lang_entry.name);
|
||||||
cur_menu->id == MenuId_NcaTitleTypes) consolePrint("title id: %016lX\n", app_metadata->title_id);
|
consolePrint("publisher: %s\n", app_metadata->lang_entry.author);
|
||||||
consolePrint("______________________________\n\n");
|
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 || \
|
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)
|
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");
|
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("title id: %016lX\n", title_info->meta_key.id);
|
||||||
consolePrint("type: %s\n", titleGetNcmContentMetaTypeName(title_info->meta_key.type));
|
consolePrint("type: %s\n", titleGetNcmContentMetaTypeName(title_info->meta_key.type));
|
||||||
consolePrint("source storage: %s\n", titleGetNcmStorageIdName(title_info->storage_id));
|
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];
|
MenuElement *cur_element = cur_menu->elements[i];
|
||||||
MenuElementOption *cur_options = cur_element->element_options;
|
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 ? " -> " : " ");
|
consolePrint("%s", i == cur_menu->selected ? " -> " : " ");
|
||||||
if (cur_app_metadata) consolePrint("%016lX - ", cur_app_metadata->title_id);
|
if (cur_app_metadata) consolePrint("%016lX - ", cur_app_metadata->title_id);
|
||||||
|
@ -1051,7 +1066,7 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
if (titleIsGameCardInfoUpdated())
|
if (titleIsGameCardInfoUpdated())
|
||||||
{
|
{
|
||||||
updateTitleList();
|
updateTitleList(&g_userTitlesMenu, &g_userTitlesSubMenu, false);
|
||||||
data_update = true;
|
data_update = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1083,7 +1098,7 @@ int main(int argc, char *argv[])
|
||||||
} else
|
} else
|
||||||
if (child_menu->id == MenuId_NSP || child_menu->id == MenuId_Ticket || child_menu->id == MenuId_Nca)
|
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)
|
switch(title_type)
|
||||||
{
|
{
|
||||||
|
@ -1100,7 +1115,8 @@ int main(int argc, char *argv[])
|
||||||
title_info = user_app_data.aoc_patch_info;
|
title_info = user_app_data.aoc_patch_info;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
title_info = NULL;
|
/* Get TitleInfo element on demand. */
|
||||||
|
title_info = titleGetInfoFromStorageByTitleId(NcmStorageId_BuiltInSystem, app_metadata->title_id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1115,6 +1131,8 @@ int main(int argc, char *argv[])
|
||||||
consolePrint("failed to generate nca list\n");
|
consolePrint("failed to generate nca list\n");
|
||||||
error = true;
|
error = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!error && cur_menu->id == MenuId_SystemTitles) is_system = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!error)
|
if (!error)
|
||||||
|
@ -1123,9 +1141,14 @@ int main(int argc, char *argv[])
|
||||||
title_info_idx = 1;
|
title_info_idx = 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
consolePrint("\nthe selected title doesn't have available %s data\n", \
|
if (cur_menu->id == MenuId_SystemTitles)
|
||||||
title_type == NcmContentMetaType_Application ? "base application" : \
|
{
|
||||||
(title_type == NcmContentMetaType_Patch ? "update" : (title_type == NcmContentMetaType_AddOnContent ? "dlc" : "dlc update")));
|
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;
|
error = true;
|
||||||
}
|
}
|
||||||
|
@ -1278,7 +1301,7 @@ int main(int argc, char *argv[])
|
||||||
} else
|
} else
|
||||||
if ((btn_down & HidNpadButton_B) && cur_menu->parent)
|
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;
|
app_metadata = NULL;
|
||||||
} else
|
} else
|
||||||
|
@ -1304,6 +1327,12 @@ int main(int argc, char *argv[])
|
||||||
if (cur_menu->id == MenuId_Nca)
|
if (cur_menu->id == MenuId_Nca)
|
||||||
{
|
{
|
||||||
freeNcaList();
|
freeNcaList();
|
||||||
|
|
||||||
|
if (is_system)
|
||||||
|
{
|
||||||
|
titleFreeTitleInfo(&title_info);
|
||||||
|
is_system = false;
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
if (cur_menu->id == MenuId_NcaFsSections)
|
if (cur_menu->id == MenuId_NcaFsSections)
|
||||||
{
|
{
|
||||||
|
@ -1360,7 +1389,8 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
freeNcaList();
|
freeNcaList();
|
||||||
|
|
||||||
freeTitleList();
|
freeTitleList(&g_systemTitlesMenu);
|
||||||
|
freeTitleList(&g_userTitlesMenu);
|
||||||
|
|
||||||
freeStorageList();
|
freeStorageList();
|
||||||
|
|
||||||
|
@ -1462,7 +1492,6 @@ void freeStorageList(void)
|
||||||
|
|
||||||
void updateStorageList(void)
|
void updateStorageList(void)
|
||||||
{
|
{
|
||||||
char **tmp = NULL;
|
|
||||||
u32 elem_count = 0, idx = 0;
|
u32 elem_count = 0, idx = 0;
|
||||||
|
|
||||||
/* Free all previously allocated data. */
|
/* Free all previously allocated data. */
|
||||||
|
@ -1472,13 +1501,8 @@ void updateStorageList(void)
|
||||||
g_umsDevices = umsGetDevices(&g_umsDeviceCount);
|
g_umsDevices = umsGetDevices(&g_umsDeviceCount);
|
||||||
elem_count = (2 + g_umsDeviceCount); // sd card, usb host, ums devices
|
elem_count = (2 + g_umsDeviceCount); // sd card, usb host, ums devices
|
||||||
|
|
||||||
/* Reallocate buffer. */
|
/* Allocate buffer. */
|
||||||
tmp = realloc(g_storageOptions, (elem_count + 1) * sizeof(char*)); // NULL terminator
|
g_storageOptions = calloc(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. */
|
/* Generate UMS device strings. */
|
||||||
for(u32 i = 0; i < elem_count; i++)
|
for(u32 i = 0; i < elem_count; i++)
|
||||||
|
@ -1531,62 +1555,61 @@ void updateStorageList(void)
|
||||||
g_storageMenuElementOption.options = g_storageOptions;
|
g_storageMenuElementOption.options = g_storageOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
void freeTitleList(void)
|
void freeTitleList(Menu *menu)
|
||||||
{
|
{
|
||||||
/* Free all previously allocated data. */
|
if (!menu) return;
|
||||||
if (g_userTitlesMenuElements)
|
|
||||||
{
|
|
||||||
for(u32 i = 0; g_userTitlesMenuElements[i] != NULL; i++) free(g_userTitlesMenuElements[i]);
|
|
||||||
|
|
||||||
free(g_userTitlesMenuElements);
|
MenuElement **elements = menu->elements;
|
||||||
g_userTitlesMenuElements = NULL;
|
|
||||||
|
/* Free all previously allocated data. */
|
||||||
|
if (elements)
|
||||||
|
{
|
||||||
|
for(u32 i = 0; elements[i]; i++) free(elements[i]);
|
||||||
|
free(elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_userTitlesMenu.scroll = 0;
|
menu->scroll = 0;
|
||||||
g_userTitlesMenu.selected = 0;
|
menu->selected = 0;
|
||||||
g_userTitlesMenu.elements = NULL;
|
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;
|
u32 app_count = 0, idx = 0;
|
||||||
TitleApplicationMetadata **app_metadata = NULL;
|
TitleApplicationMetadata **app_metadata = NULL;
|
||||||
MenuElement **tmp = NULL;
|
MenuElement **elements = NULL;
|
||||||
|
|
||||||
/* Free all previously allocated data. */
|
/* Free all previously allocated data. */
|
||||||
freeTitleList();
|
freeTitleList(menu);
|
||||||
|
|
||||||
/* Get application metadata entries. */
|
/* Get application metadata entries. */
|
||||||
app_metadata = titleGetApplicationMetadataEntries(false, &app_count);
|
app_metadata = titleGetApplicationMetadataEntries(is_system, &app_count);
|
||||||
if (!app_metadata || !app_count) goto end;
|
if (!app_metadata || !app_count) goto end;
|
||||||
|
|
||||||
/* Reallocate buffer. */
|
/* Allocate buffer. */
|
||||||
tmp = realloc(g_userTitlesMenuElements, (app_count + 1) * sizeof(MenuElement*)); // NULL terminator
|
elements = calloc(app_count + 1, sizeof(MenuElement*)); // NULL terminator
|
||||||
|
|
||||||
g_userTitlesMenuElements = tmp;
|
|
||||||
tmp = NULL;
|
|
||||||
|
|
||||||
memset(g_userTitlesMenuElements, 0, (app_count + 1) * sizeof(MenuElement*)); // NULL terminator
|
|
||||||
|
|
||||||
/* Generate menu elements. */
|
/* Generate menu elements. */
|
||||||
for(u32 i = 0; i < app_count; i++)
|
for(u32 i = 0; i < app_count; i++)
|
||||||
{
|
{
|
||||||
TitleApplicationMetadata *cur_app_metadata = app_metadata[i];
|
TitleApplicationMetadata *cur_app_metadata = app_metadata[i];
|
||||||
|
|
||||||
if (!g_userTitlesMenuElements[idx])
|
if (!elements[idx])
|
||||||
{
|
{
|
||||||
g_userTitlesMenuElements[idx] = calloc(1, sizeof(MenuElement));
|
elements[idx] = calloc(1, sizeof(MenuElement));
|
||||||
if (!g_userTitlesMenuElements[idx]) continue;
|
if (!elements[idx]) continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_userTitlesMenuElements[idx]->str = cur_app_metadata->lang_entry.name;
|
elements[idx]->str = cur_app_metadata->lang_entry.name;
|
||||||
g_userTitlesMenuElements[idx]->child_menu = &g_userTitlesSubMenu;
|
elements[idx]->child_menu = submenu;
|
||||||
g_userTitlesMenuElements[idx]->userdata = cur_app_metadata;
|
elements[idx]->userdata = cur_app_metadata;
|
||||||
|
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_userTitlesMenu.elements = g_userTitlesMenuElements;
|
menu->elements = elements;
|
||||||
|
|
||||||
end:
|
end:
|
||||||
if (app_metadata) free(app_metadata);
|
if (app_metadata) free(app_metadata);
|
||||||
|
@ -1621,19 +1644,13 @@ void updateNcaList(TitleInfo *title_info)
|
||||||
{
|
{
|
||||||
u32 content_count = title_info->content_count, idx = 0;
|
u32 content_count = title_info->content_count, idx = 0;
|
||||||
NcmContentInfo *content_infos = title_info->content_infos;
|
NcmContentInfo *content_infos = title_info->content_infos;
|
||||||
MenuElement **tmp = NULL;
|
|
||||||
char nca_id_str[0x21] = {0};
|
char nca_id_str[0x21] = {0};
|
||||||
|
|
||||||
/* Free all previously allocated data. */
|
/* Free all previously allocated data. */
|
||||||
freeNcaList();
|
freeNcaList();
|
||||||
|
|
||||||
/* Reallocate buffer. */
|
/* Allocate buffer. */
|
||||||
tmp = realloc(g_ncaMenuElements, (content_count + 2) * sizeof(MenuElement*)); // Output storage, NULL terminator
|
g_ncaMenuElements = calloc(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
|
|
||||||
|
|
||||||
/* Generate menu elements. */
|
/* Generate menu elements. */
|
||||||
for(u32 i = 0; i < content_count; i++)
|
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;
|
TitleInfo *title_info = nca_user_data->title_info;
|
||||||
NcmContentInfo *content_info = &(title_info->content_infos[nca_user_data->content_idx]);
|
NcmContentInfo *content_info = &(title_info->content_infos[nca_user_data->content_idx]);
|
||||||
MenuElement **tmp = NULL;
|
|
||||||
u32 idx = 0;
|
u32 idx = 0;
|
||||||
|
|
||||||
/* Free all previously allocated data. */
|
/* Free all previously allocated data. */
|
||||||
freeNcaFsSectionsList();
|
freeNcaFsSectionsList();
|
||||||
|
|
||||||
/* Reallocate buffer. */
|
/* Allocate buffer. */
|
||||||
tmp = realloc(g_ncaFsSectionsMenuElements, (NCA_FS_HEADER_COUNT + 1) * sizeof(MenuElement*)); // NULL terminator
|
g_ncaFsSectionsMenuElements = calloc(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
|
|
||||||
|
|
||||||
/* Initialize NCA context. */
|
/* Initialize NCA context. */
|
||||||
g_ncaFsSectionsMenuCtx = calloc(1, sizeof(NcaContext));
|
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)
|
void updateNcaBasePatchList(TitleUserApplicationData *user_app_data, TitleInfo *title_info, NcaFsSectionContext *nca_fs_ctx)
|
||||||
{
|
{
|
||||||
char **tmp = NULL;
|
|
||||||
u32 elem_count = 1, idx = 1; // "no" option
|
u32 elem_count = 1, idx = 1; // "no" option
|
||||||
TitleInfo *cur_title_info = NULL;
|
TitleInfo *cur_title_info = NULL;
|
||||||
|
|
||||||
|
@ -1847,6 +1857,7 @@ void updateNcaBasePatchList(TitleUserApplicationData *user_app_data, TitleInfo *
|
||||||
g_ncaBasePatchTitleInfo = titleGetAddOnContentBaseOrPatchList(title_info);
|
g_ncaBasePatchTitleInfo = titleGetAddOnContentBaseOrPatchList(title_info);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
unsupported = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1856,13 +1867,8 @@ void updateNcaBasePatchList(TitleUserApplicationData *user_app_data, TitleInfo *
|
||||||
/* Calculate element count. */
|
/* Calculate element count. */
|
||||||
elem_count += titleGetCountFromInfoBlock(g_ncaBasePatchTitleInfo);
|
elem_count += titleGetCountFromInfoBlock(g_ncaBasePatchTitleInfo);
|
||||||
|
|
||||||
/* Reallocate buffer. */
|
/* Allocate buffer. */
|
||||||
tmp = realloc(g_ncaBasePatchOptions, (elem_count + 1) * sizeof(char*)); // NULL terminator
|
g_ncaBasePatchOptions = calloc(elem_count + 1, sizeof(char*)); // NULL terminator
|
||||||
|
|
||||||
g_ncaBasePatchOptions = tmp;
|
|
||||||
tmp = NULL;
|
|
||||||
|
|
||||||
memset(g_ncaBasePatchOptions, 0, (elem_count + 1) * sizeof(char*)); // NULL terminator
|
|
||||||
|
|
||||||
/* Set first option. */
|
/* Set first option. */
|
||||||
g_ncaBasePatchOptions[0] = (unsupported ? "unsupported by this content/section type combo" : (elem_count < 2 ? "none available" : "no"));
|
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. */
|
/* Override LayeredFS flag, if needed. */
|
||||||
if (use_layeredfs_dir && \
|
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))))
|
((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");
|
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;
|
} SmcGenerateAesKekOption;
|
||||||
|
|
||||||
|
NXDT_ASSERT(SmcGenerateAesKekOption, 0x4);
|
||||||
|
|
||||||
/// Helper inline functions.
|
/// Helper inline functions.
|
||||||
|
|
||||||
NX_INLINE void smcPrepareGenerateAesKekOption(bool is_device_unique, u32 key_type_idx, u32 seal_key_idx, SmcGenerateAesKekOption *out)
|
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_COUNT 4 /* GameCard, BuiltInSystem, BuiltInUser, SdCard. */
|
||||||
#define TITLE_STORAGE_INDEX(storage_id) ((storage_id) - NcmStorageId_GameCard)
|
#define TITLE_STORAGE_INDEX(storage_id) ((storage_id) - NcmStorageId_GameCard)
|
||||||
|
|
||||||
|
#define NCM_CMT_APP_OFFSET 0x7A
|
||||||
|
|
||||||
/* Type definitions. */
|
/* Type definitions. */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -83,25 +85,31 @@ static const char *g_titleNcmContentTypeNames[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *g_titleNcmContentMetaTypeNames[] = {
|
static const char *g_titleNcmContentMetaTypeNames[] = {
|
||||||
[NcmContentMetaType_Unknown] = "Unknown",
|
[NcmContentMetaType_Unknown] = "Unknown",
|
||||||
[NcmContentMetaType_SystemProgram] = "SystemProgram",
|
[NcmContentMetaType_SystemProgram] = "SystemProgram",
|
||||||
[NcmContentMetaType_SystemData] = "SystemData",
|
[NcmContentMetaType_SystemData] = "SystemData",
|
||||||
[NcmContentMetaType_SystemUpdate] = "SystemUpdate",
|
[NcmContentMetaType_SystemUpdate] = "SystemUpdate",
|
||||||
[NcmContentMetaType_BootImagePackage] = "BootImagePackage",
|
[NcmContentMetaType_BootImagePackage] = "BootImagePackage",
|
||||||
[NcmContentMetaType_BootImagePackageSafe] = "BootImagePackageSafe",
|
[NcmContentMetaType_BootImagePackageSafe] = "BootImagePackageSafe",
|
||||||
[NcmContentMetaType_Application - 0x7A] = "Application",
|
[NcmContentMetaType_Application - NCM_CMT_APP_OFFSET] = "Application",
|
||||||
[NcmContentMetaType_Patch - 0x7A] = "Patch",
|
[NcmContentMetaType_Patch - NCM_CMT_APP_OFFSET] = "Patch",
|
||||||
[NcmContentMetaType_AddOnContent - 0x7A] = "AddOnContent",
|
[NcmContentMetaType_AddOnContent - NCM_CMT_APP_OFFSET] = "AddOnContent",
|
||||||
[NcmContentMetaType_Delta - 0x7A] = "Delta",
|
[NcmContentMetaType_Delta - NCM_CMT_APP_OFFSET] = "Delta",
|
||||||
[NcmContentMetaType_DataPatch - 0x7A] = "DataPatch"
|
[NcmContentMetaType_DataPatch - NCM_CMT_APP_OFFSET] = "DataPatch"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *g_filenameTypeStrings[] = {
|
static const char *g_filenameTypeStrings[] = {
|
||||||
[NcmContentMetaType_Application - 0x80] = "BASE",
|
[NcmContentMetaType_Unknown] = "UNK",
|
||||||
[NcmContentMetaType_Patch - 0x80] = "UPD",
|
[NcmContentMetaType_SystemProgram] = "SYSPRG",
|
||||||
[NcmContentMetaType_AddOnContent - 0x80] = "DLC",
|
[NcmContentMetaType_SystemData] = "SYSDAT",
|
||||||
[NcmContentMetaType_Delta - 0x80] = "DELTA",
|
[NcmContentMetaType_SystemUpdate] = "SYSUPD",
|
||||||
[NcmContentMetaType_DataPatch - 0x80] = "DLCUPD"
|
[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. */
|
/* 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)
|
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 || \
|
if (!title_info || (title_info->meta_key.type > NcmContentMetaType_BootImagePackageSafe && title_info->meta_key.type < NcmContentMetaType_Application) || \
|
||||||
naming_convention > TitleNamingConvention_IdAndVersionOnly || (naming_convention == TitleNamingConvention_Full && \
|
title_info->meta_key.type > NcmContentMetaType_DataPatch || naming_convention > TitleNamingConvention_IdAndVersionOnly || \
|
||||||
illegal_char_replace_type > TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly))
|
(naming_convention == TitleNamingConvention_Full && illegal_char_replace_type > TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly))
|
||||||
{
|
{
|
||||||
LOG_MSG_ERROR("Invalid parameters!");
|
LOG_MSG_ERROR("Invalid parameters!");
|
||||||
return NULL;
|
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;
|
char title_name[0x400] = {0}, *version_str = NULL, *filename = NULL;
|
||||||
|
|
||||||
/* Generate filename for this title. */
|
/* 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);
|
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
|
} else
|
||||||
if (naming_convention == TitleNamingConvention_IdAndVersionOnly)
|
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. */
|
/* Duplicate generated filename. */
|
||||||
|
@ -1251,7 +1261,7 @@ const char *titleGetNcmContentTypeName(u8 content_type)
|
||||||
const char *titleGetNcmContentMetaTypeName(u8 content_meta_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;
|
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)
|
NX_INLINE void titleFreeApplicationMetadata(void)
|
||||||
|
|
Loading…
Reference in a new issue