diff --git a/code_templates/nxdt_rw_poc.c b/code_templates/nxdt_rw_poc.c index 38136cf..cc6f30f 100644 --- a/code_templates/nxdt_rw_poc.c +++ b/code_templates/nxdt_rw_poc.c @@ -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"); diff --git a/include/core/title.h b/include/core/title.h index a8aba01..e0a3168 100644 --- a/include/core/title.h +++ b/include/core/title.h @@ -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); diff --git a/source/core/title.c b/source/core/title.c index c0cb848..10700a3 100644 --- a/source/core/title.c +++ b/source/core/title.c @@ -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; }