mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-12-22 16:42:11 +00:00
poc: add base/patch selector.
Other changes include: * title: add titleGetAddOnContentBaseOrPatchList(); add titleIsValidInfoBlock(); rename titleDuplicateTitleInfo() -> titleDuplicateTitleInfoFull(); add non-linked-list aware titleDuplicateTitleInfo().
This commit is contained in:
parent
c6a84f68de
commit
0e70eb0912
3 changed files with 330 additions and 69 deletions
|
@ -147,6 +147,9 @@ static void switchNcaListTitle(Menu *cur_menu, u32 *element_count, TitleInfo *ti
|
|||
void freeNcaFsSectionsList(void);
|
||||
void updateNcaFsSectionsList(NcaUserData *nca_user_data);
|
||||
|
||||
void freeNcaBasePatchList(void);
|
||||
void updateNcaBasePatchList(TitleUserApplicationData *user_app_data, TitleInfo *title_info, NcaFsSectionContext *nca_fs_ctx);
|
||||
|
||||
NX_INLINE bool useUsbHost(void);
|
||||
|
||||
static bool waitForGameCard(void);
|
||||
|
@ -258,7 +261,7 @@ static MenuElementOption g_storageMenuElementOption = {
|
|||
.selected = 0,
|
||||
.getter_func = &getOutputStorageOption,
|
||||
.setter_func = &setOutputStorageOption,
|
||||
.options = NULL
|
||||
.options = NULL // Dynamically set
|
||||
};
|
||||
|
||||
static MenuElement g_storageMenuElement = {
|
||||
|
@ -615,18 +618,31 @@ static Menu g_ticketMenu = {
|
|||
.elements = g_ticketMenuElements
|
||||
};
|
||||
|
||||
static bool g_ncaMenuRawMode = false;
|
||||
static NcaContext *g_ncaFsSectionsMenuCtx = NULL;
|
||||
static TitleInfo *g_ncaBasePatchTitleInfo = NULL;
|
||||
static char **g_ncaBasePatchOptions = NULL;
|
||||
|
||||
static MenuElementOption g_ncaFsSectionsSubMenuBasePatchElementOption = {
|
||||
.selected = 0,
|
||||
.getter_func = NULL,
|
||||
.setter_func = NULL,
|
||||
.options = NULL // Dynamically set
|
||||
};
|
||||
|
||||
static MenuElement *g_ncaFsSectionsSubMenuElements[] = {
|
||||
&(MenuElement){
|
||||
.str = "start nca fs dump",
|
||||
.child_menu = NULL,
|
||||
.task_func = NULL,
|
||||
.task_func = NULL, // TODO: implement nca fs dump function -- additional sparse/patch checks will go here
|
||||
.element_options = NULL,
|
||||
.userdata = NULL // Dynamically set
|
||||
},
|
||||
// TODO: place base/patch selector here? display selector at runtime?
|
||||
&(MenuElement){
|
||||
.str = "use base/patch title",
|
||||
.child_menu = NULL,
|
||||
.task_func = NULL,
|
||||
.element_options = &g_ncaFsSectionsSubMenuBasePatchElementOption,
|
||||
.userdata = NULL
|
||||
},
|
||||
&(MenuElement){
|
||||
.str = "write section image",
|
||||
.child_menu = NULL,
|
||||
|
@ -663,6 +679,9 @@ static Menu g_ncaFsSectionsSubMenu = {
|
|||
.elements = g_ncaFsSectionsSubMenuElements
|
||||
};
|
||||
|
||||
static bool g_ncaMenuRawMode = false;
|
||||
static NcaContext *g_ncaFsSectionsMenuCtx = NULL;
|
||||
|
||||
static MenuElement **g_ncaFsSectionsMenuElements = NULL;
|
||||
|
||||
// Dynamically populated using g_ncaFsSectionsMenuElements.
|
||||
|
@ -1011,7 +1030,7 @@ int main(int argc, char *argv[])
|
|||
break;
|
||||
}
|
||||
|
||||
svcSleepThread(50000000); // 50 ms
|
||||
svcSleepThread(10000000); // 10 ms
|
||||
}
|
||||
|
||||
if (!g_appletStatus) break;
|
||||
|
@ -1100,8 +1119,7 @@ int main(int argc, char *argv[])
|
|||
NcaFsSectionContext *nca_fs_ctx = selected_element->userdata;
|
||||
if (nca_fs_ctx->enabled)
|
||||
{
|
||||
// TODO: add sparse / patch checks here
|
||||
g_ncaFsSectionsSubMenuElements[0]->userdata = nca_fs_ctx;
|
||||
updateNcaBasePatchList(&user_app_data, title_info, nca_fs_ctx);
|
||||
} else {
|
||||
consolePrint("can't dump an invalid nca fs section!\n");
|
||||
error = true;
|
||||
|
@ -1197,17 +1215,23 @@ int main(int argc, char *argv[])
|
|||
selected_element_options->selected++;
|
||||
if (!selected_element_options->options[selected_element_options->selected]) selected_element_options->selected--;
|
||||
if (selected_element_options->setter_func) selected_element_options->setter_func(selected_element_options->selected);
|
||||
|
||||
/* Point to the next base/patch title. */
|
||||
if (cur_menu->id == MenuId_NcaFsSectionsSubMenu && cur_menu->selected == 1 && g_ncaBasePatchTitleInfo && g_ncaBasePatchTitleInfo->next)
|
||||
g_ncaBasePatchTitleInfo = g_ncaBasePatchTitleInfo->next;
|
||||
} else
|
||||
if ((btn_down & (HidNpadButton_Left | HidNpadButton_StickLLeft | HidNpadButton_StickRLeft)) && selected_element_options)
|
||||
{
|
||||
selected_element_options->selected--;
|
||||
if (selected_element_options->selected == UINT32_MAX) selected_element_options->selected = 0;
|
||||
if (selected_element_options->setter_func) selected_element_options->setter_func(selected_element_options->selected);
|
||||
} else
|
||||
if (btn_down & HidNpadButton_B)
|
||||
{
|
||||
if (!cur_menu->parent) break;
|
||||
|
||||
/* Point to the previous base/patch title. */
|
||||
if (cur_menu->id == MenuId_NcaFsSectionsSubMenu && cur_menu->selected == 1 && g_ncaBasePatchTitleInfo && g_ncaBasePatchTitleInfo->previous)
|
||||
g_ncaBasePatchTitleInfo = g_ncaBasePatchTitleInfo->previous;
|
||||
} else
|
||||
if ((btn_down & HidNpadButton_B) && cur_menu->parent)
|
||||
{
|
||||
if (cur_menu->id == MenuId_UserTitles)
|
||||
{
|
||||
app_metadata = NULL;
|
||||
|
@ -1241,7 +1265,7 @@ int main(int argc, char *argv[])
|
|||
} else
|
||||
if (cur_menu->id == MenuId_NcaFsSectionsSubMenu)
|
||||
{
|
||||
g_ncaFsSectionsSubMenuElements[0]->userdata = NULL;
|
||||
freeNcaBasePatchList();
|
||||
}
|
||||
|
||||
cur_menu->selected = 0;
|
||||
|
@ -1283,7 +1307,7 @@ int main(int argc, char *argv[])
|
|||
break;
|
||||
}
|
||||
|
||||
if (btn_held & (HidNpadButton_StickLDown | HidNpadButton_StickRDown | HidNpadButton_StickLUp | HidNpadButton_StickRUp | HidNpadButton_ZL | HidNpadButton_ZR)) svcSleepThread(50000000); // 50 ms
|
||||
if (btn_held & (HidNpadButton_StickLDown | HidNpadButton_StickRDown | HidNpadButton_StickLUp | HidNpadButton_StickRUp | HidNpadButton_ZL | HidNpadButton_ZR)) svcSleepThread(40000000); // 40 ms
|
||||
}
|
||||
|
||||
end:
|
||||
|
@ -1331,7 +1355,7 @@ static void utilsWaitForButtonPress(u64 flag)
|
|||
{
|
||||
utilsScanPads();
|
||||
if (utilsGetButtonsDown() & flag) break;
|
||||
svcSleepThread(50000000); // 50 ms
|
||||
svcSleepThread(10000000); // 10 ms
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1386,6 +1410,8 @@ void freeStorageList(void)
|
|||
}
|
||||
|
||||
g_umsDeviceCount = 0;
|
||||
|
||||
g_storageMenuElementOption.options = NULL;
|
||||
}
|
||||
|
||||
void updateStorageList(void)
|
||||
|
@ -1414,8 +1440,11 @@ void updateStorageList(void)
|
|||
u64 total = 0, free = 0;
|
||||
char total_str[36] = {0}, free_str[32] = {0};
|
||||
|
||||
g_storageOptions[idx] = calloc(sizeof(char), 0x300);
|
||||
if (!g_storageOptions[idx]) continue;
|
||||
if (!g_storageOptions[idx])
|
||||
{
|
||||
g_storageOptions[idx] = calloc(sizeof(char), 0x300);
|
||||
if (!g_storageOptions[idx]) continue;
|
||||
}
|
||||
|
||||
if (i == 1)
|
||||
{
|
||||
|
@ -1498,8 +1527,11 @@ void updateTitleList(void)
|
|||
{
|
||||
TitleApplicationMetadata *cur_app_metadata = app_metadata[i];
|
||||
|
||||
g_userTitlesMenuElements[idx] = calloc(1, sizeof(MenuElement));
|
||||
if (!g_userTitlesMenuElements[idx]) continue;
|
||||
if (!g_userTitlesMenuElements[idx])
|
||||
{
|
||||
g_userTitlesMenuElements[idx] = calloc(1, sizeof(MenuElement));
|
||||
if (!g_userTitlesMenuElements[idx]) continue;
|
||||
}
|
||||
|
||||
g_userTitlesMenuElements[idx]->str = cur_app_metadata->lang_entry.name;
|
||||
g_userTitlesMenuElements[idx]->child_menu = &g_userTitlesSubMenu;
|
||||
|
@ -1565,8 +1597,11 @@ void updateNcaList(TitleInfo *title_info)
|
|||
u64 nca_size = 0;
|
||||
NcaUserData *nca_user_data = NULL;
|
||||
|
||||
g_ncaMenuElements[idx] = calloc(1, sizeof(MenuElement));
|
||||
if (!g_ncaMenuElements[idx]) continue;
|
||||
if (!g_ncaMenuElements[idx])
|
||||
{
|
||||
g_ncaMenuElements[idx] = calloc(1, sizeof(MenuElement));
|
||||
if (!g_ncaMenuElements[idx]) continue;
|
||||
}
|
||||
|
||||
nca_info_str = calloc(128, sizeof(char));
|
||||
nca_user_data = calloc(1, sizeof(NcaUserData));
|
||||
|
@ -1677,8 +1712,11 @@ void updateNcaFsSectionsList(NcaUserData *nca_user_data)
|
|||
NcaFsSectionContext *cur_nca_fs_ctx = &(g_ncaFsSectionsMenuCtx->fs_ctx[i]);
|
||||
char *nca_fs_info_str = NULL;
|
||||
|
||||
g_ncaFsSectionsMenuElements[idx] = calloc(1, sizeof(MenuElement));
|
||||
if (!g_ncaFsSectionsMenuElements[idx]) continue;
|
||||
if (!g_ncaFsSectionsMenuElements[idx])
|
||||
{
|
||||
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;
|
||||
|
@ -1700,6 +1738,114 @@ void updateNcaFsSectionsList(NcaUserData *nca_user_data)
|
|||
g_ncaFsSectionsMenu.elements = g_ncaFsSectionsMenuElements;
|
||||
}
|
||||
|
||||
void freeNcaBasePatchList(void)
|
||||
{
|
||||
/* Free all previously allocated data. */
|
||||
if (g_ncaBasePatchOptions)
|
||||
{
|
||||
/* Skip the first option. */
|
||||
for(u32 i = 1; g_ncaBasePatchOptions[i]; i++)
|
||||
{
|
||||
free(g_ncaBasePatchOptions[i]);
|
||||
g_ncaBasePatchOptions[i] = NULL;
|
||||
}
|
||||
|
||||
free(g_ncaBasePatchOptions);
|
||||
g_ncaBasePatchOptions = NULL;
|
||||
}
|
||||
|
||||
g_ncaFsSectionsSubMenuBasePatchElementOption.selected = 0;
|
||||
g_ncaFsSectionsSubMenuBasePatchElementOption.options = NULL;
|
||||
|
||||
g_ncaFsSectionsSubMenuElements[0]->userdata = NULL;
|
||||
|
||||
if (g_ncaBasePatchTitleInfo && (g_ncaBasePatchTitleInfo->meta_key.type == NcmContentMetaType_AddOnContent || g_ncaBasePatchTitleInfo->meta_key.type == NcmContentMetaType_DataPatch))
|
||||
{
|
||||
titleFreeTitleInfo(&g_ncaBasePatchTitleInfo);
|
||||
}
|
||||
|
||||
g_ncaBasePatchTitleInfo = NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
u8 title_type = title_info->meta_key.type;
|
||||
u8 content_type = nca_fs_ctx->nca_ctx->content_type;
|
||||
u8 section_type = nca_fs_ctx->section_type;
|
||||
bool unsupported = false;
|
||||
|
||||
/* Free all previously allocated data. */
|
||||
freeNcaBasePatchList();
|
||||
|
||||
/* Only enable base/patch list if we're dealing with supported content types and/or FS section types. */
|
||||
if (content_type != NcmContentType_Meta && content_type != NcmContentType_Control && section_type != NcaFsSectionType_Invalid && section_type != NcaFsSectionType_PartitionFs)
|
||||
{
|
||||
/* Retrieve corresponding TitleInfo linked list for the current title type. */
|
||||
switch(title_type)
|
||||
{
|
||||
case NcmContentMetaType_Application:
|
||||
g_ncaBasePatchTitleInfo = user_app_data->patch_info;
|
||||
break;
|
||||
case NcmContentMetaType_Patch:
|
||||
g_ncaBasePatchTitleInfo = user_app_data->app_info;
|
||||
break;
|
||||
case NcmContentMetaType_AddOnContent:
|
||||
case NcmContentMetaType_DataPatch:
|
||||
g_ncaBasePatchTitleInfo = titleGetAddOnContentBaseOrPatchList(title_info);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
unsupported = true;
|
||||
}
|
||||
|
||||
/* 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
|
||||
|
||||
/* Set first option. */
|
||||
g_ncaBasePatchOptions[0] = (unsupported ? "unsupported for this content/section type" : (elem_count < 2 ? "none available" : "no"));
|
||||
|
||||
/* Generate base/patch strings. */
|
||||
cur_title_info = g_ncaBasePatchTitleInfo;
|
||||
while(cur_title_info)
|
||||
{
|
||||
if (!g_ncaBasePatchOptions[idx])
|
||||
{
|
||||
g_ncaBasePatchOptions[idx] = calloc(sizeof(char), 0x40);
|
||||
if (!g_ncaBasePatchOptions[idx])
|
||||
{
|
||||
cur_title_info = cur_title_info->next;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(g_ncaBasePatchOptions[idx], 0x40, "%s v%u (v%u.%u) (%s)", titleGetNcmContentMetaTypeName(cur_title_info->meta_key.type), \
|
||||
cur_title_info->version.value, cur_title_info->version.application_version.release_ver, cur_title_info->version.application_version.private_ver, \
|
||||
titleGetNcmStorageIdName(cur_title_info->storage_id));
|
||||
|
||||
cur_title_info = cur_title_info->next;
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
g_ncaFsSectionsSubMenuBasePatchElementOption.options = g_ncaBasePatchOptions;
|
||||
|
||||
g_ncaFsSectionsSubMenuElements[0]->userdata = nca_fs_ctx;
|
||||
}
|
||||
|
||||
NX_INLINE bool useUsbHost(void)
|
||||
{
|
||||
return (g_storageMenuElementOption.selected == 1);
|
||||
|
@ -2540,7 +2686,7 @@ static bool saveNintendoSubmissionPackage(void *userdata)
|
|||
utilsCreateThread(&dump_thread, nspThreadFunc, &nsp_thread_data, 2);
|
||||
|
||||
/* Wait until the background thread calculates the NSP size. */
|
||||
while(!nsp_thread_data.total_size && !nsp_thread_data.error) svcSleepThread(50000000); // 50 ms
|
||||
while(!nsp_thread_data.total_size && !nsp_thread_data.error) svcSleepThread(10000000); // 10 ms
|
||||
|
||||
if (nsp_thread_data.error)
|
||||
{
|
||||
|
@ -2602,7 +2748,7 @@ static bool saveNintendoSubmissionPackage(void *userdata)
|
|||
consolePrint("%lu / %lu (%u%%) | Time elapsed: %lu\n", size, nsp_thread_data.total_size, percent, (now - start));
|
||||
consoleRefresh();
|
||||
|
||||
svcSleepThread(50000000); // 50 ms
|
||||
svcSleepThread(10000000); // 10 ms
|
||||
}
|
||||
|
||||
consolePrint("\nwaiting for thread to join\n");
|
||||
|
@ -3409,7 +3555,7 @@ static bool spanDumpThreads(ThreadFunc read_func, ThreadFunc write_func, void *a
|
|||
consolePrint("%lu / %lu (%u%%) | Time elapsed: %lu\n", size, shared_thread_data->total_size, percent, (now - start));
|
||||
consoleRefresh();
|
||||
|
||||
svcSleepThread(50000000); // 50 ms
|
||||
svcSleepThread(10000000); // 10 ms
|
||||
}
|
||||
|
||||
consolePrint("\nwaiting for threads to join\n");
|
||||
|
|
|
@ -123,6 +123,12 @@ bool titleGetUserApplicationData(u64 app_id, TitleUserApplicationData *out);
|
|||
/// Frees data populated by titleGetUserApplicationData().
|
||||
void titleFreeUserApplicationData(TitleUserApplicationData *user_app_data);
|
||||
|
||||
/// Takes an input TitleInfo object with meta type NcmContentMetaType_AddOnContent or NcmContentMetaType_DataPatch.
|
||||
/// Returns a linked list of TitleInfo elements with title IDs matching the corresponding base/patch title ID, depending on the meta type of the input TitleInfo object.
|
||||
/// Particularly useful to display add-on-content base/patch titles related to a specific add-on-content (patch) entry.
|
||||
/// Use titleFreeTitleInfo() to free the returned data.
|
||||
TitleInfo *titleGetAddOnContentBaseOrPatchList(TitleInfo *title_info);
|
||||
|
||||
/// Returns true if orphan titles are available.
|
||||
/// Orphan titles are patches or add-on contents with no NsApplicationControlData available for their parent user application ID.
|
||||
bool titleAreOrphanTitlesAvailable(void);
|
||||
|
@ -158,6 +164,14 @@ const char *titleGetNcmContentMetaTypeName(u8 content_meta_type);
|
|||
|
||||
/// Miscellaneous functions.
|
||||
|
||||
NX_INLINE bool titleIsValidInfoBlock(TitleInfo *title_info)
|
||||
{
|
||||
return (title_info && title_info->storage_id >= NcmStorageId_GameCard && title_info->storage_id <= NcmStorageId_SdCard && title_info->meta_key.id && \
|
||||
((title_info->meta_key.type >= NcmContentMetaType_SystemProgram && title_info->meta_key.type <= NcmContentMetaType_BootImagePackageSafe) || \
|
||||
(title_info->meta_key.type >= NcmContentMetaType_Application && title_info->meta_key.type <= NcmContentMetaType_DataPatch)) && \
|
||||
title_info->content_count && title_info->content_infos);
|
||||
}
|
||||
|
||||
NX_INLINE u64 titleGetPatchIdByApplicationId(u64 app_id)
|
||||
{
|
||||
return (app_id + TITLE_PATCH_ID_OFFSET);
|
||||
|
|
|
@ -557,7 +557,8 @@ static bool titleRefreshGameCardTitleInfo(void);
|
|||
static bool titleIsUserApplicationContentAvailable(u64 app_id);
|
||||
static TitleInfo *_titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id);
|
||||
|
||||
static TitleInfo *titleDuplicateTitleInfo(TitleInfo *title_info, TitleInfo *previous, TitleInfo *next);
|
||||
static TitleInfo *titleDuplicateTitleInfoFull(TitleInfo *title_info, TitleInfo *previous, TitleInfo *next);
|
||||
static TitleInfo *titleDuplicateTitleInfo(TitleInfo *title_info);
|
||||
|
||||
static int titleSystemTitleMetadataEntrySortFunction(const void *a, const void *b);
|
||||
static int titleUserApplicationMetadataEntrySortFunction(const void *a, const void *b);
|
||||
|
@ -787,7 +788,7 @@ TitleInfo *titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id)
|
|||
TitleInfo *title_info = (g_titleInterfaceInit ? _titleGetInfoFromStorageByTitleId(storage_id, title_id) : NULL);
|
||||
if (title_info)
|
||||
{
|
||||
ret = titleDuplicateTitleInfo(title_info, NULL, NULL);
|
||||
ret = titleDuplicateTitleInfoFull(title_info, NULL, NULL);
|
||||
if (!ret) LOG_MSG_ERROR("Failed to duplicate title info for %016lX!", title_id);
|
||||
}
|
||||
}
|
||||
|
@ -847,7 +848,7 @@ bool titleGetUserApplicationData(u64 app_id, TitleUserApplicationData *out)
|
|||
|
||||
#define TITLE_ALLOCATE_USER_APP_DATA(elem, msg, decl) \
|
||||
if (elem##_info && !out->elem##_info) { \
|
||||
out->elem##_info = titleDuplicateTitleInfo(elem##_info, NULL, NULL); \
|
||||
out->elem##_info = titleDuplicateTitleInfoFull(elem##_info, NULL, NULL); \
|
||||
if (!out->elem##_info) { \
|
||||
LOG_MSG_ERROR("Failed to duplicate %s info for %016lX!", msg, app_id); \
|
||||
decl; \
|
||||
|
@ -926,6 +927,73 @@ void titleFreeUserApplicationData(TitleUserApplicationData *user_app_data)
|
|||
titleFreeTitleInfo(&(user_app_data->aoc_patch_info));
|
||||
}
|
||||
|
||||
TitleInfo *titleGetAddOnContentBaseOrPatchList(TitleInfo *title_info)
|
||||
{
|
||||
TitleInfo *out = NULL;
|
||||
bool success = false;
|
||||
|
||||
SCOPED_LOCK(&g_titleMutex)
|
||||
{
|
||||
if (!g_titleInterfaceInit || !titleIsValidInfoBlock(title_info) || (title_info->meta_key.type != NcmContentMetaType_AddOnContent && \
|
||||
title_info->meta_key.type != NcmContentMetaType_DataPatch))
|
||||
{
|
||||
LOG_MSG_ERROR("Invalid parameters!");
|
||||
break;
|
||||
}
|
||||
|
||||
TitleInfo *aoc_info = NULL, *tmp = NULL;
|
||||
u64 ref_tid = title_info->meta_key.id;
|
||||
u64 lookup_tid = (title_info->meta_key.type == NcmContentMetaType_AddOnContent ? titleGetDataPatchIdByAddOnContentId(ref_tid) : titleGetAddOnContentIdByDataPatchId(ref_tid));
|
||||
bool error = false;
|
||||
|
||||
/* Get info for the first add-on content (patch) title matching the lookup title ID. */
|
||||
aoc_info = _titleGetInfoFromStorageByTitleId(NcmStorageId_Any, lookup_tid);
|
||||
if (!aoc_info) break;
|
||||
|
||||
/* Create our own custom linked list using entries that match our lookup title ID. */
|
||||
while(aoc_info)
|
||||
{
|
||||
/* Check if this entry's title ID matches our lookup title ID. */
|
||||
if (aoc_info->meta_key.id != lookup_tid)
|
||||
{
|
||||
aoc_info = aoc_info->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Duplicate current entry. */
|
||||
tmp = titleDuplicateTitleInfo(aoc_info);
|
||||
if (!tmp)
|
||||
{
|
||||
LOG_MSG_ERROR("Failed to duplicate TitleInfo object!");
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update pointer. */
|
||||
if (out)
|
||||
{
|
||||
out->next = tmp;
|
||||
} else {
|
||||
out = tmp;
|
||||
}
|
||||
|
||||
tmp = NULL;
|
||||
|
||||
/* Proceed onto the next entry. */
|
||||
aoc_info = aoc_info->next;
|
||||
}
|
||||
|
||||
if (error) break;
|
||||
|
||||
/* Update flag. */
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (!success && out) titleFreeTitleInfo(&out);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
bool titleAreOrphanTitlesAvailable(void)
|
||||
{
|
||||
bool ret = false;
|
||||
|
@ -956,7 +1024,7 @@ TitleInfo **titleGetOrphanTitles(u32 *out_count)
|
|||
/* Duplicate orphan title info entries. */
|
||||
for(u32 i = 0; i < g_orphanTitleInfoCount; i++)
|
||||
{
|
||||
orphan_info[i] = titleDuplicateTitleInfo(g_orphanTitleInfo[i], NULL, NULL);
|
||||
orphan_info[i] = titleDuplicateTitleInfoFull(g_orphanTitleInfo[i], NULL, NULL);
|
||||
if (!orphan_info[i])
|
||||
{
|
||||
LOG_MSG_ERROR("Failed to duplicate info for orphan title %016lX!", g_orphanTitleInfo[i]->meta_key.id);
|
||||
|
@ -2421,52 +2489,31 @@ static TitleInfo *_titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id)
|
|||
return out;
|
||||
}
|
||||
|
||||
static TitleInfo *titleDuplicateTitleInfo(TitleInfo *title_info, TitleInfo *previous, TitleInfo *next)
|
||||
static TitleInfo *titleDuplicateTitleInfoFull(TitleInfo *title_info, TitleInfo *previous, TitleInfo *next)
|
||||
{
|
||||
if (!title_info || title_info->storage_id < NcmStorageId_GameCard || title_info->storage_id > NcmStorageId_SdCard || !title_info->meta_key.id || \
|
||||
(title_info->meta_key.type > NcmContentMetaType_BootImagePackageSafe && title_info->meta_key.type < NcmContentMetaType_Application) || \
|
||||
title_info->meta_key.type > NcmContentMetaType_DataPatch || !title_info->content_count || !title_info->content_infos)
|
||||
if (!titleIsValidInfoBlock(title_info))
|
||||
{
|
||||
LOG_MSG_ERROR("Invalid parameters!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TitleInfo *title_info_dup = NULL, *tmp1 = NULL, *tmp2 = NULL;
|
||||
NcmContentInfo *content_infos_dup = NULL;
|
||||
bool dup_previous = false, dup_next = false, success = false;
|
||||
|
||||
/* Allocate memory for the new TitleInfo element. */
|
||||
title_info_dup = calloc(1, sizeof(TitleInfo));
|
||||
/* Duplicate TitleInfo object. */
|
||||
title_info_dup = titleDuplicateTitleInfo(title_info);
|
||||
if (!title_info_dup)
|
||||
{
|
||||
LOG_MSG_ERROR("Failed to allocate memory for TitleInfo duplicate!");
|
||||
LOG_MSG_ERROR("Failed to duplicate TitleInfo object!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Copy TitleInfo data. */
|
||||
memcpy(title_info_dup, title_info, sizeof(TitleInfo));
|
||||
title_info_dup->previous = title_info_dup->next = NULL;
|
||||
|
||||
/* Allocate memory for NcmContentInfo elements. */
|
||||
content_infos_dup = calloc(title_info->content_count, sizeof(NcmContentInfo));
|
||||
if (!content_infos_dup)
|
||||
{
|
||||
LOG_MSG_ERROR("Failed to allocate memory for NcmContentInfo duplicates!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Copy NcmContentInfo data. */
|
||||
memcpy(content_infos_dup, title_info->content_infos, title_info->content_count * sizeof(NcmContentInfo));
|
||||
|
||||
/* Update content infos pointer. */
|
||||
title_info_dup->content_infos = content_infos_dup;
|
||||
|
||||
#define TITLE_DUPLICATE_LINKED_LIST(elem, prv, nxt) \
|
||||
if (title_info->elem) { \
|
||||
if (elem) { \
|
||||
title_info_dup->elem = elem; \
|
||||
} else { \
|
||||
title_info_dup->elem = titleDuplicateTitleInfo(title_info->elem, prv, nxt); \
|
||||
title_info_dup->elem = titleDuplicateTitleInfoFull(title_info->elem, prv, nxt); \
|
||||
if (!title_info_dup->elem) goto end; \
|
||||
dup_##elem = true; \
|
||||
} \
|
||||
|
@ -2495,29 +2542,83 @@ static TitleInfo *titleDuplicateTitleInfo(TitleInfo *title_info, TitleInfo *prev
|
|||
end:
|
||||
/* We can't directly use titleFreeTitleInfo() on title_info_dup because some or all of the linked list data may have been provided as function arguments. */
|
||||
/* So we'll take care of freeing data the old fashioned way. */
|
||||
if (!success && title_info_dup)
|
||||
{
|
||||
/* Free content infos pointer. */
|
||||
if (title_info_dup->content_infos) free(title_info_dup->content_infos);
|
||||
|
||||
/* Free previous and next linked lists (if duplicated). */
|
||||
/* We need to take care of not freeing the linked lists right away, either because we may have already freed them, or because they may have been passed as arguments. */
|
||||
/* Furthermore, both the next pointer from the previous sibling and the previous pointer from the next sibling reference our current duplicated entry. */
|
||||
/* To avoid issues, we'll just clear all linked list pointers. */
|
||||
TITLE_FREE_DUPLICATED_LINKED_LIST(previous);
|
||||
TITLE_FREE_DUPLICATED_LINKED_LIST(next);
|
||||
|
||||
/* Free allocated buffer and update return pointer. */
|
||||
free(title_info_dup);
|
||||
title_info_dup = NULL;
|
||||
}
|
||||
|
||||
#undef TITLE_DUPLICATE_LINKED_LIST
|
||||
|
||||
#undef TITLE_FREE_DUPLICATED_LINKED_LIST
|
||||
|
||||
return title_info_dup;
|
||||
}
|
||||
|
||||
static TitleInfo *titleDuplicateTitleInfo(TitleInfo *title_info)
|
||||
{
|
||||
if (!titleIsValidInfoBlock(title_info))
|
||||
{
|
||||
LOG_MSG_ERROR("Invalid parameters!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TitleInfo *title_info_dup = NULL;
|
||||
NcmContentInfo *content_infos_dup = NULL;
|
||||
bool success = false;
|
||||
|
||||
/* Allocate memory for the new TitleInfo element. */
|
||||
title_info_dup = calloc(1, sizeof(TitleInfo));
|
||||
if (!title_info_dup)
|
||||
{
|
||||
LOG_MSG_ERROR("Failed to allocate memory for TitleInfo duplicate!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Copy TitleInfo data. */
|
||||
memcpy(title_info_dup, title_info, sizeof(TitleInfo));
|
||||
title_info_dup->previous = title_info_dup->next = NULL;
|
||||
|
||||
/* Allocate memory for NcmContentInfo elements. */
|
||||
content_infos_dup = calloc(title_info->content_count, sizeof(NcmContentInfo));
|
||||
if (!content_infos_dup)
|
||||
{
|
||||
LOG_MSG_ERROR("Failed to allocate memory for NcmContentInfo duplicates!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Copy NcmContentInfo data. */
|
||||
memcpy(content_infos_dup, title_info->content_infos, title_info->content_count * sizeof(NcmContentInfo));
|
||||
|
||||
/* Update content infos pointer. */
|
||||
title_info_dup->content_infos = content_infos_dup;
|
||||
|
||||
/* Update flag. */
|
||||
success = true;
|
||||
|
||||
end:
|
||||
if (!success)
|
||||
{
|
||||
if (content_infos_dup) free(content_infos_dup);
|
||||
|
||||
if (title_info_dup)
|
||||
{
|
||||
/* Free previous and next linked lists (if duplicated). */
|
||||
/* We need to take care of not freeing the linked lists right away, either because we may have already freed them, or because they may have been passed as arguments. */
|
||||
/* Furthermore, both the next pointer from the previous sibling and the previous pointer from the next sibling reference our current duplicated entry. */
|
||||
/* To avoid issues, we'll just clear all linked list pointers. */
|
||||
TITLE_FREE_DUPLICATED_LINKED_LIST(previous);
|
||||
TITLE_FREE_DUPLICATED_LINKED_LIST(next);
|
||||
|
||||
/* Free allocated buffer and update return pointer. */
|
||||
free(title_info_dup);
|
||||
title_info_dup = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#undef TITLE_DUPLICATE_LINKED_LIST
|
||||
|
||||
#undef TITLE_FREE_DUPLICATED_LINKED_LIST
|
||||
|
||||
return title_info_dup;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue