1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-26 04:02:11 +00:00

poc: add NCA FS section menu

Other changes include:

* nca: add content_size_str field to NcaContext struct.
* nxdt_utils: modify utilsGenerateFormattedSizeString() to avoid displaying decimal places if dealing with plain bytes.
This commit is contained in:
Pablo Curiel 2023-05-26 17:59:03 +02:00
parent 54e7c0e9be
commit 8e2b0a74b8
4 changed files with 204 additions and 82 deletions

View file

@ -77,7 +77,7 @@ typedef enum {
MenuId_Ticket = 9, MenuId_Ticket = 9,
MenuId_NCATitleTypes = 10, MenuId_NCATitleTypes = 10,
MenuId_NCA = 11, MenuId_NCA = 11,
MenuId_NCAFsSection = 12, MenuId_NCAFsSections = 12,
MenuId_Count = 13 MenuId_Count = 13
} MenuId; } MenuId;
@ -141,6 +141,10 @@ void updateTitleList(void);
void freeNcaList(void); void freeNcaList(void);
void updateNcaList(TitleInfo *title_info); void updateNcaList(TitleInfo *title_info);
static void switchNcaListTitle(Menu *cur_menu, u32 *element_count, TitleInfo *title_info);
void freeNcaFsSectionsList(void);
void updateNcaFsSectionsList(NcaUserData *nca_user_data);
NX_INLINE bool useUsbHost(void); NX_INLINE bool useUsbHost(void);
@ -250,6 +254,14 @@ static MenuElementOption g_storageMenuElementOption = {
.options = NULL .options = NULL
}; };
static MenuElement g_storageMenuElement = {
.str = "output storage",
.child_menu = NULL,
.task_func = NULL,
.element_options = &g_storageMenuElementOption,
.userdata = NULL
};
static MenuElement *g_xciMenuElements[] = { static MenuElement *g_xciMenuElements[] = {
&(MenuElement){ &(MenuElement){
.str = "start xci dump", .str = "start xci dump",
@ -306,13 +318,7 @@ static MenuElement *g_xciMenuElements[] = {
}, },
.userdata = NULL .userdata = NULL
}, },
&(MenuElement){ &g_storageMenuElement,
.str = "output storage",
.child_menu = NULL,
.task_func = NULL,
.element_options = &g_storageMenuElementOption,
.userdata = NULL
},
NULL NULL
}; };
@ -370,13 +376,7 @@ static MenuElement *g_gameCardHfsMenuElements[] = {
}, },
.userdata = NULL .userdata = NULL
}, },
&(MenuElement){ &g_storageMenuElement,
.str = "output storage",
.child_menu = NULL,
.task_func = NULL,
.element_options = &g_storageMenuElementOption,
.userdata = NULL
},
NULL NULL
}; };
@ -456,13 +456,7 @@ static MenuElement *g_gameCardMenuElements[] = {
.element_options = NULL, .element_options = NULL,
.userdata = NULL .userdata = NULL
}, },
&(MenuElement){ &g_storageMenuElement,
.str = "output storage",
.child_menu = NULL,
.task_func = NULL,
.element_options = &g_storageMenuElementOption,
.userdata = NULL
},
NULL NULL
}; };
@ -570,13 +564,7 @@ static MenuElement *g_nspMenuElements[] = {
}, },
.userdata = NULL .userdata = NULL
}, },
&(MenuElement){ &g_storageMenuElement,
.str = "output storage",
.child_menu = NULL,
.task_func = NULL,
.element_options = &g_storageMenuElementOption,
.userdata = NULL
},
NULL NULL
}; };
@ -608,13 +596,7 @@ static MenuElement *g_ticketMenuElements[] = {
}, },
.userdata = NULL .userdata = NULL
}, },
&(MenuElement){ &g_storageMenuElement,
.str = "output storage",
.child_menu = NULL,
.task_func = NULL,
.element_options = &g_storageMenuElementOption,
.userdata = NULL
},
NULL NULL
}; };
@ -626,6 +608,20 @@ static Menu g_ticketMenu = {
.elements = g_ticketMenuElements .elements = g_ticketMenuElements
}; };
static bool g_ncaMenuRawMode = false;
static NcaContext *g_ncaFsSectionsMenuCtx = NULL;
static MenuElement **g_ncaFsSectionsMenuElements = NULL;
// Dynamically populated using g_ncaFsSectionsMenuElements.
static Menu g_ncaFsSectionsMenu = {
.id = MenuId_NCAFsSections,
.parent = NULL,
.selected = 0,
.scroll = 0,
.elements = NULL
};
static MenuElement **g_ncaMenuElements = NULL; static MenuElement **g_ncaMenuElements = NULL;
// Dynamically populated using g_ncaMenuElements. // Dynamically populated using g_ncaMenuElements.
@ -702,7 +698,7 @@ static MenuElement *g_userTitlesSubMenuElements[] = {
.userdata = NULL .userdata = NULL
}, },
&(MenuElement){ &(MenuElement){
.str = "nca dump options", .str = "nca / nca fs dump options",
.child_menu = &(Menu){ .child_menu = &(Menu){
.id = MenuId_NCATitleTypes, .id = MenuId_NCATitleTypes,
.parent = NULL, .parent = NULL,
@ -831,8 +827,9 @@ int main(int argc, char *argv[])
consoleClear(); consoleClear();
consolePrint("______________________________\n\n"); consolePrint("______________________________\n\n");
consolePrint("press b to %s.\n", cur_menu->parent ? "go back" : "exit"); if (cur_menu->parent) consolePrint("press b to go back\n");
if (g_umsDeviceCount) consolePrint("press x to safely remove all ums devices\n"); if (g_umsDeviceCount) consolePrint("press x to safely remove all ums devices\n");
consolePrint("press + to exit\n");
consolePrint("______________________________\n\n"); consolePrint("______________________________\n\n");
if (cur_menu->id == MenuId_UserTitles) if (cur_menu->id == MenuId_UserTitles)
@ -848,12 +845,12 @@ int main(int argc, char *argv[])
consolePrint("title info:\n\n"); consolePrint("title info:\n\n");
consolePrint("name: %s\n", app_metadata->lang_entry.name); consolePrint("name: %s\n", app_metadata->lang_entry.name);
consolePrint("publisher: %s\n", app_metadata->lang_entry.author); consolePrint("publisher: %s\n", app_metadata->lang_entry.author);
consolePrint("title id: %016lX\n", app_metadata->title_id); if (cur_menu->id == MenuId_UserTitlesSubMenu) consolePrint("title id: %016lX\n", app_metadata->title_id);
consolePrint("______________________________\n\n"); 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)
{ {
if (title_info->previous || title_info->next) if (cur_menu->id != MenuId_NCAFsSections && (title_info->previous || title_info->next))
{ {
consolePrint("press l/zl and/or r/zr to change the selected title\n"); consolePrint("press l/zl and/or r/zr to change the selected title\n");
consolePrint("title: %u / %u\n", title_info_idx, title_info_count); consolePrint("title: %u / %u\n", title_info_idx, title_info_count);
@ -873,6 +870,22 @@ int main(int argc, char *argv[])
if (cur_menu->id == MenuId_NSP) g_nspMenuElements[0]->userdata = title_info; if (cur_menu->id == MenuId_NSP) g_nspMenuElements[0]->userdata = title_info;
if (cur_menu->id == MenuId_Ticket) g_ticketMenuElements[0]->userdata = title_info; if (cur_menu->id == MenuId_Ticket) g_ticketMenuElements[0]->userdata = title_info;
if (cur_menu->id == MenuId_NCA)
{
consolePrint("press y to switch to %s mode\n", g_ncaMenuRawMode ? "nca fs section" : "raw nca");
consolePrint("______________________________\n\n");
}
if (cur_menu->id == MenuId_NCAFsSections)
{
consolePrint("selected nca info:\n\n");
consolePrint("content id: %s\n", g_ncaFsSectionsMenuCtx->content_id_str);
consolePrint("content type: %s\n", titleGetNcmContentTypeName(g_ncaFsSectionsMenuCtx->content_type));
consolePrint("id offset: %u\n", g_ncaFsSectionsMenuCtx->id_offset);
consolePrint("size: %s\n", g_ncaFsSectionsMenuCtx->content_size_str);
consolePrint("______________________________\n\n");
}
} }
} }
@ -1007,6 +1020,16 @@ int main(int argc, char *argv[])
error = true; error = true;
} }
} else
if (child_menu->id == MenuId_NCAFsSections)
{
updateNcaFsSectionsList((NcaUserData*)selected_element->userdata);
if (!g_ncaFsSectionsMenuElements || !g_ncaFsSectionsMenuElements[0])
{
consolePrint("failed to generate nca fs sections list\n");
error = true;
}
} }
if (!error) if (!error)
@ -1135,6 +1158,10 @@ int main(int argc, char *argv[])
if (cur_menu->id == MenuId_NCA) if (cur_menu->id == MenuId_NCA)
{ {
freeNcaList(); freeNcaList();
} else
if (cur_menu->id == MenuId_NCAFsSections)
{
freeNcaFsSectionsList();
} }
cur_menu->selected = 0; cur_menu->selected = 0;
@ -1152,54 +1179,35 @@ int main(int argc, char *argv[])
{ {
title_info = title_info->previous; title_info = title_info->previous;
title_info_idx--; title_info_idx--;
switchNcaListTitle(cur_menu, &element_count, title_info);
if (cur_menu->id == MenuId_NCA)
{
updateNcaList(title_info);
if (!g_ncaMenuElements || !g_ncaMenuElements[0])
{
freeNcaList();
consolePrint("\nfailed to generate nca list for newly selected title\npress any button to go back\n");
consoleRefresh();
utilsWaitForButtonPress(0);
cur_menu->selected = 0;
cur_menu->scroll = 0;
cur_menu = cur_menu->parent;
element_count = menuGetElementCount(cur_menu);
}
}
} else } else
if (((btn_down & (HidNpadButton_R)) || (btn_held & HidNpadButton_ZR)) && (cur_menu->id == MenuId_NSP || cur_menu->id == MenuId_Ticket || cur_menu->id == MenuId_NCA) && title_info->next) if (((btn_down & (HidNpadButton_R)) || (btn_held & HidNpadButton_ZR)) && (cur_menu->id == MenuId_NSP || cur_menu->id == MenuId_Ticket || cur_menu->id == MenuId_NCA) && title_info->next)
{ {
title_info = title_info->next; title_info = title_info->next;
title_info_idx++; title_info_idx++;
switchNcaListTitle(cur_menu, &element_count, title_info);
} else
if ((btn_down & HidNpadButton_Y) && cur_menu->id == MenuId_NCA)
{
/* Change NCA menu element properties. */
g_ncaMenuRawMode ^= 1;
if (cur_menu->id == MenuId_NCA) for(u32 i = 0; g_ncaMenuElements[i]; i++)
{ {
updateNcaList(title_info); g_ncaMenuElements[i]->child_menu = (g_ncaMenuRawMode ? NULL : &g_ncaFsSectionsMenu);
if (!g_ncaMenuElements || !g_ncaMenuElements[0]) g_ncaMenuElements[i]->task_func = (g_ncaMenuRawMode ? &saveNintendoContentArchive : NULL);
{
freeNcaList();
consolePrint("\nfailed to generate nca list for newly selected title\npress any button to go back\n");
consoleRefresh();
utilsWaitForButtonPress(0);
cur_menu->selected = 0;
cur_menu->scroll = 0;
cur_menu = cur_menu->parent;
element_count = menuGetElementCount(cur_menu);
}
} }
} else
if (btn_down & HidNpadButton_Plus)
{
break;
} }
svcSleepThread(50000000); // 50 ms if (btn_held & (HidNpadButton_StickLDown | HidNpadButton_StickRDown | HidNpadButton_StickLUp | HidNpadButton_StickRUp | HidNpadButton_ZL | HidNpadButton_ZR)) svcSleepThread(50000000); // 50 ms
} }
end: end:
titleFreeUserApplicationData(&user_app_data); freeNcaFsSectionsList();
freeNcaList(); freeNcaList();
@ -1207,6 +1215,8 @@ end:
freeStorageList(); freeStorageList();
titleFreeUserApplicationData(&user_app_data);
utilsCloseResources(); utilsCloseResources();
consoleExit(NULL); consoleExit(NULL);
@ -1429,7 +1439,11 @@ void freeNcaList(void)
/* Free all previously allocated data. */ /* Free all previously allocated data. */
if (g_ncaMenuElements) if (g_ncaMenuElements)
{ {
for(u32 i = 0; g_ncaMenuElements[i] != NULL; i++) u32 count = 0;
for(count = 0; g_ncaMenuElements[count]; count++);
for(u32 i = 0; count > 0 && i < (count - 1); i++) // Don't free output storage element
{ {
if (g_ncaMenuElements[i]->str) free(g_ncaMenuElements[i]->str); if (g_ncaMenuElements[i]->str) free(g_ncaMenuElements[i]->str);
if (g_ncaMenuElements[i]->userdata) free(g_ncaMenuElements[i]->userdata); if (g_ncaMenuElements[i]->userdata) free(g_ncaMenuElements[i]->userdata);
@ -1456,12 +1470,12 @@ void updateNcaList(TitleInfo *title_info)
freeNcaList(); freeNcaList();
/* Reallocate buffer. */ /* Reallocate buffer. */
tmp = realloc(g_ncaMenuElements, (content_count + 1) * sizeof(MenuElement*)); // NULL terminator tmp = realloc(g_ncaMenuElements, (content_count + 2) * sizeof(MenuElement*)); // Output storage, NULL terminator
g_ncaMenuElements = tmp; g_ncaMenuElements = tmp;
tmp = NULL; tmp = NULL;
memset(g_ncaMenuElements, 0, (content_count + 1) * sizeof(MenuElement*)); // NULL terminator 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++)
@ -1495,16 +1509,113 @@ void updateNcaList(TitleInfo *title_info)
nca_user_data->content_idx = i; nca_user_data->content_idx = i;
g_ncaMenuElements[idx]->str = nca_info_str; g_ncaMenuElements[idx]->str = nca_info_str;
//g_ncaMenuElements[idx]->child_menu = NULL; g_ncaMenuElements[idx]->child_menu = (g_ncaMenuRawMode ? NULL : &g_ncaFsSectionsMenu);
g_ncaMenuElements[idx]->task_func = &saveNintendoContentArchive; g_ncaMenuElements[idx]->task_func = (g_ncaMenuRawMode ? &saveNintendoContentArchive : NULL);
g_ncaMenuElements[idx]->userdata = nca_user_data; g_ncaMenuElements[idx]->userdata = nca_user_data;
idx++; idx++;
} }
g_ncaMenuElements[content_count] = &g_storageMenuElement;
g_ncaMenu.elements = g_ncaMenuElements; g_ncaMenu.elements = g_ncaMenuElements;
} }
static void switchNcaListTitle(Menu *cur_menu, u32 *element_count, TitleInfo *title_info)
{
if (!cur_menu || cur_menu->id != MenuId_NCA || !element_count) return;
updateNcaList(title_info);
if (!g_ncaMenuElements || !g_ncaMenuElements[0])
{
freeNcaList();
consolePrint("\nfailed to generate nca list for newly selected title\npress any button to go back\n");
consoleRefresh();
utilsWaitForButtonPress(0);
cur_menu->selected = 0;
cur_menu->scroll = 0;
cur_menu = cur_menu->parent;
*element_count = menuGetElementCount(cur_menu);
}
}
void freeNcaFsSectionsList(void)
{
/* Free all previously allocated data. */
if (g_ncaFsSectionsMenuCtx)
{
free(g_ncaFsSectionsMenuCtx);
g_ncaFsSectionsMenuCtx = NULL;
}
if (g_ncaFsSectionsMenuElements)
{
for(u32 i = 0; g_ncaFsSectionsMenuElements[i] != NULL; i++)
{
if (g_ncaFsSectionsMenuElements[i]->str) free(g_ncaFsSectionsMenuElements[i]->str);
free(g_ncaFsSectionsMenuElements[i]);
}
free(g_ncaFsSectionsMenuElements);
g_ncaFsSectionsMenuElements = NULL;
}
g_ncaFsSectionsMenu.scroll = 0;
g_ncaFsSectionsMenu.selected = 0;
g_ncaFsSectionsMenu.elements = NULL;
}
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
/* Initialize NCA context. */
g_ncaFsSectionsMenuCtx = calloc(1, sizeof(NcaContext));
if (!ncaInitializeContext(g_ncaFsSectionsMenuCtx, title_info->storage_id, (title_info->storage_id == NcmStorageId_GameCard ? HashFileSystemPartitionType_Secure : 0), \
content_info, title_info->version.value, NULL)) return;
/* Generate menu elements. */
for(u32 i = 0; i < NCA_FS_HEADER_COUNT; i++)
{
NcaFsSectionContext *cur_nca_fs_ctx = &(g_ncaFsSectionsMenuCtx->fs_ctx[i]);
char *nca_fs_info_str = NULL, nca_fs_size_str[16] = {0};
g_ncaFsSectionsMenuElements[idx] = calloc(1, sizeof(MenuElement));
if (!g_ncaFsSectionsMenuElements[idx]) continue;
nca_fs_info_str = calloc(128, sizeof(char));
if (!nca_fs_info_str) continue;
utilsGenerateFormattedSizeString((double)cur_nca_fs_ctx->section_size, nca_fs_size_str, sizeof(nca_fs_size_str));
sprintf(nca_fs_info_str, "FS section #%u: %s (%s)", i + 1, ncaGetFsSectionTypeName(cur_nca_fs_ctx), nca_fs_size_str);
g_ncaFsSectionsMenuElements[idx]->str = nca_fs_info_str;
g_ncaFsSectionsMenuElements[idx]->child_menu = NULL;
g_ncaFsSectionsMenuElements[idx]->userdata = cur_nca_fs_ctx;
idx++;
}
g_ncaFsSectionsMenu.elements = g_ncaFsSectionsMenuElements;
}
NX_INLINE bool useUsbHost(void) NX_INLINE bool useUsbHost(void)
{ {
return (g_storageMenuElementOption.selected == 1); return (g_storageMenuElementOption.selected == 1);

View file

@ -436,6 +436,7 @@ struct _NcaContext {
u8 format_version; ///< NcaVersion. u8 format_version; ///< NcaVersion.
u8 content_type; ///< NcmContentType. Retrieved from NcmContentInfo. u8 content_type; ///< NcmContentType. Retrieved from NcmContentInfo.
u64 content_size; ///< Retrieved from NcmContentInfo. u64 content_size; ///< Retrieved from NcmContentInfo.
char content_size_str[0x10];
u8 key_generation; ///< NcaKeyGeneration. Retrieved from the decrypted header. u8 key_generation; ///< NcaKeyGeneration. Retrieved from the decrypted header.
u8 id_offset; ///< Retrieved from NcmContentInfo. u8 id_offset; ///< Retrieved from NcmContentInfo.
u32 title_version; u32 title_version;

View file

@ -203,7 +203,9 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type,
out->content_type = content_info->content_type; out->content_type = content_info->content_type;
out->id_offset = content_info->id_offset; out->id_offset = content_info->id_offset;
out->title_version = title_version; out->title_version = title_version;
ncmContentInfoSizeToU64(content_info, &(out->content_size)); ncmContentInfoSizeToU64(content_info, &(out->content_size));
utilsGenerateFormattedSizeString((double)out->content_size, out->content_size_str, sizeof(out->content_size_str));
if (out->content_size < NCA_FULL_HEADER_LENGTH) if (out->content_size < NCA_FULL_HEADER_LENGTH)
{ {

View file

@ -651,7 +651,15 @@ void utilsGenerateFormattedSizeString(double size, char *dst, size_t dst_size)
if (size >= pow(1024.0, i + 1) && (i + 1) < g_sizeSuffixesCount) continue; if (size >= pow(1024.0, i + 1) && (i + 1) < g_sizeSuffixesCount) continue;
size /= pow(1024.0, i); size /= pow(1024.0, i);
snprintf(dst, dst_size, "%.2F %s", size, g_sizeSuffixes[i]);
if (i == 0)
{
/* Don't display decimal places if we're dealing with plain bytes. */
snprintf(dst, dst_size, "%.0f %s", size, g_sizeSuffixes[i]);
} else {
snprintf(dst, dst_size, "%.2f %s", size, g_sizeSuffixes[i]);
}
break; break;
} }
} }