diff --git a/.gitignore b/.gitignore index 4f07294..d2d3c66 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .idea -.vs +cmake-build-debug CMakeLists.txt build diff --git a/buildtools b/buildtools index 73d9327..b2fe2bf 160000 --- a/buildtools +++ b/buildtools @@ -1 +1 @@ -Subproject commit 73d9327d4227e01c85c6404d7cee747d34e24881 +Subproject commit b2fe2bfbbf072a3bb479a8f73428452f67e4edf2 diff --git a/source/ui/section/action/action.h b/source/ui/section/action/action.h index efe1915..35d24d6 100644 --- a/source/ui/section/action/action.h +++ b/source/ui/section/action/action.h @@ -32,6 +32,7 @@ void action_delete_pending_title(linked_list* items, list_item* selected); void action_delete_all_pending_titles(linked_list* items, list_item* selected); void action_delete_ticket(linked_list* items, list_item* selected); +void action_delete_tickets_unused(linked_list* items, list_item* selected); void action_install_cdn(linked_list* items, list_item* selected); void action_install_cdn_noprompt(volatile bool* done, ticket_info* info, bool finishedPrompt); diff --git a/source/ui/section/action/deletecontents.c b/source/ui/section/action/deletecontents.c index 08fee11..6bd3d7a 100644 --- a/source/ui/section/action/deletecontents.c +++ b/source/ui/section/action/deletecontents.c @@ -152,6 +152,47 @@ static void action_delete_onresponse(ui_view* view, void* data, bool response) { } } +typedef struct { + delete_data* deleteData; + + const char* message; + + populate_files_data popData; +} delete_loading_data; + +static void action_delete_loading_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { + action_delete_draw_top(view, ((delete_loading_data*) data)->deleteData, x1, y1, x2, y2); +} + +static void action_delete_loading_update(ui_view* view, void* data, float* progress, char* text) { + delete_loading_data* loadingData = (delete_loading_data*) data; + + if(loadingData->popData.finished) { + ui_pop(); + info_destroy(view); + + if(R_SUCCEEDED(loadingData->popData.result)) { + loadingData->deleteData->deleteInfo.total = linked_list_size(&loadingData->deleteData->contents); + loadingData->deleteData->deleteInfo.processed = loadingData->deleteData->deleteInfo.total; + + prompt_display("Confirmation", loadingData->message, COLOR_TEXT, true, loadingData->deleteData, action_delete_draw_top, action_delete_onresponse); + } else { + error_display_res(NULL, NULL, loadingData->popData.result, "Failed to populate content list."); + + action_delete_free_data(loadingData->deleteData); + } + + free(loadingData); + return; + } + + if((hidKeysDown() & KEY_B) && !loadingData->popData.finished) { + svcSignalEvent(loadingData->popData.cancelEvent); + } + + snprintf(text, PROGRESS_TEXT_MAX, "Fetching content list..."); +} + static void action_delete_internal(linked_list* items, list_item* selected, const char* message, bool recursive, bool includeBase, bool ciasOnly, bool ticketsOnly) { delete_data* data = (delete_data*) calloc(1, sizeof(delete_data)); if(data == NULL) { @@ -180,40 +221,35 @@ static void action_delete_internal(linked_list* items, list_item* selected, cons linked_list_init(&data->contents); - populate_files_data popData; - memset(&popData, 0, sizeof(popData)); + delete_loading_data* loadingData = (delete_loading_data*) calloc(1, sizeof(delete_loading_data)); + if(loadingData == NULL) { + error_display(NULL, NULL, "Failed to allocate loading data."); - popData.items = &data->contents; - popData.archive = data->archive; - strncpy(popData.path, data->path, FILE_PATH_MAX); - popData.recursive = recursive; - popData.includeBase = includeBase; - popData.filter = ciasOnly ? util_filter_cias : ticketsOnly ? util_filter_tickets : NULL; - popData.filterData = NULL; + action_delete_free_data(data); + return; + } - Result listRes = task_populate_files(&popData); + loadingData->deleteData = data; + loadingData->message = message; + + loadingData->popData.items = &data->contents; + loadingData->popData.archive = data->archive; + strncpy(loadingData->popData.path, data->path, FILE_PATH_MAX); + loadingData->popData.recursive = recursive; + loadingData->popData.includeBase = includeBase; + loadingData->popData.filter = ciasOnly ? util_filter_cias : ticketsOnly ? util_filter_tickets : NULL; + loadingData->popData.filterData = NULL; + + Result listRes = task_populate_files(&loadingData->popData); if(R_FAILED(listRes)) { error_display_res(NULL, NULL, listRes, "Failed to initiate content list population."); + free(loadingData); action_delete_free_data(data); return; } - while(!popData.finished) { - svcSleepThread(1000000); - } - - if(R_FAILED(popData.result)) { - error_display_res(NULL, NULL, popData.result, "Failed to populate content list."); - - action_delete_free_data(data); - return; - } - - data->deleteInfo.total = linked_list_size(&data->contents); - data->deleteInfo.processed = data->deleteInfo.total; - - prompt_display("Confirmation", message, COLOR_TEXT, true, data, action_delete_draw_top, action_delete_onresponse); + info_display("Loading", "Press B to cancel.", false, loadingData, action_delete_loading_update, action_delete_loading_draw_top); } void action_delete_file(linked_list* items, list_item* selected) { diff --git a/source/ui/section/action/deletependingtitles.c b/source/ui/section/action/deletependingtitles.c index a2a4419..7c11860 100644 --- a/source/ui/section/action/deletependingtitles.c +++ b/source/ui/section/action/deletependingtitles.c @@ -19,6 +19,7 @@ typedef struct { list_item* selected; linked_list contents; + bool all; data_op_data deleteInfo; } delete_pending_titles_data; @@ -83,7 +84,10 @@ static bool action_delete_pending_titles_error(void* data, u32 index, Result res } static void action_delete_pending_titles_free_data(delete_pending_titles_data* data) { - task_clear_pending_titles(&data->contents); + if(data->all) { + task_clear_pending_titles(&data->contents); + } + linked_list_destroy(&data->contents); free(data); } @@ -129,6 +133,47 @@ static void action_delete_pending_titles_onresponse(ui_view* view, void* data, b } } +typedef struct { + delete_pending_titles_data* deleteData; + + const char* message; + + populate_pending_titles_data popData; +} delete_pending_titles_loading_data; + +static void action_delete_pending_titles_loading_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { + action_delete_pending_titles_draw_top(view, ((delete_pending_titles_loading_data*) data)->deleteData, x1, y1, x2, y2); +} + +static void action_delete_pending_titles_loading_update(ui_view* view, void* data, float* progress, char* text) { + delete_pending_titles_loading_data* loadingData = (delete_pending_titles_loading_data*) data; + + if(loadingData->popData.finished) { + ui_pop(); + info_destroy(view); + + if(R_SUCCEEDED(loadingData->popData.result)) { + loadingData->deleteData->deleteInfo.total = linked_list_size(&loadingData->deleteData->contents); + loadingData->deleteData->deleteInfo.processed = loadingData->deleteData->deleteInfo.total; + + prompt_display("Confirmation", loadingData->message, COLOR_TEXT, true, loadingData->deleteData, action_delete_pending_titles_draw_top, action_delete_pending_titles_onresponse); + } else { + error_display_res(NULL, NULL, loadingData->popData.result, "Failed to populate pending title list."); + + action_delete_pending_titles_free_data(loadingData->deleteData); + } + + free(loadingData); + return; + } + + if((hidKeysDown() & KEY_B) && !loadingData->popData.finished) { + svcSignalEvent(loadingData->popData.cancelEvent); + } + + snprintf(text, PROGRESS_TEXT_MAX, "Fetching pending title list..."); +} + void action_delete_pending_titles(linked_list* items, list_item* selected, const char* message, bool all) { delete_pending_titles_data* data = (delete_pending_titles_data*) calloc(1, sizeof(delete_pending_titles_data)); if(data == NULL) { @@ -140,6 +185,8 @@ void action_delete_pending_titles(linked_list* items, list_item* selected, const data->items = items; data->selected = selected; + data->all = all; + data->deleteInfo.data = data; data->deleteInfo.op = DATAOP_DELETE; @@ -156,35 +203,37 @@ void action_delete_pending_titles(linked_list* items, list_item* selected, const linked_list_init(&data->contents); if(all) { - populate_pending_titles_data popData; - popData.items = &data->contents; + delete_pending_titles_loading_data* loadingData = (delete_pending_titles_loading_data*) calloc(1, sizeof(delete_pending_titles_loading_data)); + if(loadingData == NULL) { + error_display(NULL, NULL, "Failed to allocate loading data."); - Result listRes = task_populate_pending_titles(&popData); + action_delete_pending_titles_free_data(data); + return; + } + + loadingData->deleteData = data; + loadingData->message = message; + + loadingData->popData.items = &data->contents; + + Result listRes = task_populate_pending_titles(&loadingData->popData); if(R_FAILED(listRes)) { error_display_res(NULL, NULL, listRes, "Failed to initiate pending title list population."); + free(loadingData); action_delete_pending_titles_free_data(data); return; } - while(!popData.finished) { - svcSleepThread(1000000); - } - - if(R_FAILED(popData.result)) { - error_display_res(NULL, NULL, popData.result, "Failed to populate pending title list."); - - action_delete_pending_titles_free_data(data); - return; - } + info_display("Loading", "Press B to cancel.", false, loadingData, action_delete_pending_titles_loading_update, action_delete_pending_titles_loading_draw_top); } else { linked_list_add(&data->contents, selected); + + data->deleteInfo.total = 1; + data->deleteInfo.processed = data->deleteInfo.total; + + prompt_display("Confirmation", message, COLOR_TEXT, true, data, action_delete_pending_titles_draw_top, action_delete_pending_titles_onresponse); } - - data->deleteInfo.total = linked_list_size(&data->contents); - data->deleteInfo.processed = data->deleteInfo.total; - - prompt_display("Confirmation", message, COLOR_TEXT, true, data, !all ? action_delete_pending_titles_draw_top : NULL, action_delete_pending_titles_onresponse); } void action_delete_pending_title(linked_list* items, list_item* selected) { diff --git a/source/ui/section/action/deleteticket.c b/source/ui/section/action/deleteticket.c deleted file mode 100644 index 066e683..0000000 --- a/source/ui/section/action/deleteticket.c +++ /dev/null @@ -1,66 +0,0 @@ -#include - -#include <3ds.h> - -#include "action.h" -#include "../task/task.h" -#include "../../error.h" -#include "../../info.h" -#include "../../list.h" -#include "../../prompt.h" -#include "../../ui.h" -#include "../../../core/linkedlist.h" -#include "../../../core/screen.h" - -typedef struct { - linked_list* items; - list_item* selected; -} delete_ticket_data; - -static void action_delete_ticket_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - ui_draw_ticket_info(view, ((delete_ticket_data*) data)->selected->data, x1, y1, x2, y2); -} - -static void action_delete_ticket_update(ui_view* view, void* data, float* progress, char* text) { - delete_ticket_data* deleteData = (delete_ticket_data*) data; - - ticket_info* info = (ticket_info*) deleteData->selected->data; - - Result res = AM_DeleteTicket(info->titleId); - - ui_pop(); - info_destroy(view); - - if(R_FAILED(res)) { - error_display_res(info, ui_draw_ticket_info, res, "Failed to delete ticket."); - } else { - linked_list_remove(deleteData->items, deleteData->selected); - task_free_ticket(deleteData->selected); - - prompt_display("Success", "Ticket deleted.", COLOR_TEXT, false, NULL, NULL, NULL); - } - - free(data); -} - -static void action_delete_ticket_onresponse(ui_view* view, void* data, bool response) { - if(response) { - info_display("Deleting Ticket", "", false, data, action_delete_ticket_update, action_delete_ticket_draw_top); - } else { - free(data); - } -} - -void action_delete_ticket(linked_list* items, list_item* selected) { - delete_ticket_data* data = (delete_ticket_data*) calloc(1, sizeof(delete_ticket_data)); - if(data == NULL) { - error_display(NULL, NULL, "Failed to allocate delete ticket data."); - - return; - } - - data->items = items; - data->selected = selected; - - prompt_display("Confirmation", "Delete the selected ticket?", COLOR_TEXT, true, data, action_delete_ticket_draw_top, action_delete_ticket_onresponse); -} \ No newline at end of file diff --git a/source/ui/section/action/deletetickets.c b/source/ui/section/action/deletetickets.c new file mode 100644 index 0000000..2c46b7e --- /dev/null +++ b/source/ui/section/action/deletetickets.c @@ -0,0 +1,256 @@ +#include +#include +#include + +#include <3ds.h> + +#include "action.h" +#include "../task/task.h" +#include "../../error.h" +#include "../../info.h" +#include "../../list.h" +#include "../../prompt.h" +#include "../../ui.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" +#include "../../../core/util.h" + +typedef struct { + linked_list* items; + bool unused; + + linked_list contents; + + data_op_data deleteInfo; +} delete_tickets_data; + +static void action_delete_tickets_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { + delete_tickets_data* deleteData = (delete_tickets_data*) data; + + u32 curr = deleteData->deleteInfo.processed; + if(curr < deleteData->deleteInfo.total) { + ui_draw_ticket_info(view, ((list_item*) linked_list_get(&deleteData->contents, curr))->data, x1, y1, x2, y2); + } +} + +static Result action_delete_tickets_delete(void* data, u32 index) { + delete_tickets_data* deleteData = (delete_tickets_data*) data; + + Result res = 0; + + u64 titleId = ((ticket_info*) ((list_item*) linked_list_get(&deleteData->contents, index))->data)->titleId; + if(R_SUCCEEDED(res = AM_DeleteTicket(titleId))) { + linked_list_iter iter; + linked_list_iterate(deleteData->items, &iter); + + while(linked_list_iter_has_next(&iter)) { + list_item* item = (list_item*) linked_list_iter_next(&iter); + ticket_info* currInfo = (ticket_info*) item->data; + + if(currInfo->titleId == titleId) { + linked_list_iter_remove(&iter); + task_free_ticket(item); + } + } + } + + return res; +} + +static Result action_delete_tickets_suspend(void* data, u32 index) { + return 0; +} + +static Result action_delete_tickets_restore(void* data, u32 index) { + return 0; +} + +static bool action_delete_tickets_error(void* data, u32 index, Result res) { + delete_tickets_data* deleteData = (delete_tickets_data*) data; + + if(res == R_FBI_CANCELLED) { + prompt_display("Failure", "Delete cancelled.", COLOR_TEXT, false, NULL, NULL, NULL); + return false; + } else { + ui_view* view = error_display_res(data, action_delete_tickets_draw_top, res, "Failed to delete ticket(s)."); + if(view != NULL) { + svcWaitSynchronization(view->active, U64_MAX); + } + } + + return index < deleteData->deleteInfo.total - 1; +} + +static void action_delete_tickets_free_data(delete_tickets_data* data) { + if(data->unused) { + task_clear_tickets(&data->contents); + } + + linked_list_destroy(&data->contents); + free(data); +} + +static void action_delete_tickets_update(ui_view* view, void* data, float* progress, char* text) { + delete_tickets_data* deleteData = (delete_tickets_data*) data; + + if(deleteData->deleteInfo.finished) { + ui_pop(); + info_destroy(view); + + if(R_SUCCEEDED(deleteData->deleteInfo.result)) { + prompt_display("Success", "Ticket(s) deleted.", COLOR_TEXT, false, NULL, NULL, NULL); + } + + action_delete_tickets_free_data(deleteData); + + return; + } + + if((hidKeysDown() & KEY_B) && !deleteData->deleteInfo.finished) { + svcSignalEvent(deleteData->deleteInfo.cancelEvent); + } + + *progress = deleteData->deleteInfo.total > 0 ? (float) deleteData->deleteInfo.processed / (float) deleteData->deleteInfo.total : 0; + snprintf(text, PROGRESS_TEXT_MAX, "%lu / %lu", deleteData->deleteInfo.processed, deleteData->deleteInfo.total); +} + +static void action_delete_tickets_onresponse(ui_view* view, void* data, bool response) { + delete_tickets_data* deleteData = (delete_tickets_data*) data; + + if(response) { + Result res = task_data_op(&deleteData->deleteInfo); + if(R_SUCCEEDED(res)) { + info_display("Deleting", "Press B to cancel.", true, data, action_delete_tickets_update, action_delete_tickets_draw_top); + } else { + error_display_res(NULL, NULL, res, "Failed to initiate delete operation."); + + action_delete_tickets_free_data(deleteData); + } + } else { + action_delete_tickets_free_data(deleteData); + } +} + +typedef struct { + delete_tickets_data* deleteData; + + const char* message; + + populate_tickets_data popData; +} delete_tickets_loading_data; + +static void action_delete_tickets_loading_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { + action_delete_tickets_draw_top(view, ((delete_tickets_loading_data*) data)->deleteData, x1, y1, x2, y2); +} + +static void action_delete_tickets_loading_update(ui_view* view, void* data, float* progress, char* text) { + delete_tickets_loading_data* loadingData = (delete_tickets_loading_data*) data; + + if(loadingData->popData.finished) { + ui_pop(); + info_destroy(view); + + if(R_SUCCEEDED(loadingData->popData.result)) { + linked_list_iter iter; + linked_list_iterate(&loadingData->deleteData->contents, &iter); + while(linked_list_iter_has_next(&iter)) { + list_item* item = (list_item*) linked_list_iter_next(&iter); + ticket_info* info = (ticket_info*) item->data; + + AM_TitleEntry entry; + for(FS_MediaType mediaType = MEDIATYPE_NAND; mediaType != MEDIATYPE_GAME_CARD; mediaType++) { + if(R_SUCCEEDED(AM_GetTitleInfo(mediaType, 1, &info->titleId, &entry))) { + linked_list_iter_remove(&iter); + break; + } + } + } + + loadingData->deleteData->deleteInfo.total = linked_list_size(&loadingData->deleteData->contents); + loadingData->deleteData->deleteInfo.processed = loadingData->deleteData->deleteInfo.total; + + prompt_display("Confirmation", loadingData->message, COLOR_TEXT, true, loadingData->deleteData, action_delete_tickets_draw_top, action_delete_tickets_onresponse); + } else { + error_display_res(NULL, NULL, loadingData->popData.result, "Failed to populate ticket list."); + + action_delete_tickets_free_data(loadingData->deleteData); + } + + free(loadingData); + return; + } + + if((hidKeysDown() & KEY_B) && !loadingData->popData.finished) { + svcSignalEvent(loadingData->popData.cancelEvent); + } + + snprintf(text, PROGRESS_TEXT_MAX, "Fetching ticket list..."); +} + +static void action_delete_tickets_internal(linked_list* items, list_item* selected, const char* message, bool unused) { + delete_tickets_data* data = (delete_tickets_data*) calloc(1, sizeof(delete_tickets_data)); + if(data == NULL) { + error_display(NULL, NULL, "Failed to allocate delete data."); + + return; + } + + data->items = items; + data->unused = unused; + + data->deleteInfo.data = data; + + data->deleteInfo.op = DATAOP_DELETE; + + data->deleteInfo.delete = action_delete_tickets_delete; + + data->deleteInfo.suspend = action_delete_tickets_suspend; + data->deleteInfo.restore = action_delete_tickets_restore; + + data->deleteInfo.error = action_delete_tickets_error; + + data->deleteInfo.finished = false; + + linked_list_init(&data->contents); + + if(unused) { + delete_tickets_loading_data* loadingData = (delete_tickets_loading_data*) calloc(1, sizeof(delete_tickets_loading_data)); + if(loadingData == NULL) { + error_display(NULL, NULL, "Failed to allocate loading data."); + + action_delete_tickets_free_data(data); + return; + } + + loadingData->deleteData = data; + loadingData->message = message; + + loadingData->popData.items = &data->contents; + + Result listRes = task_populate_tickets(&loadingData->popData); + if(R_FAILED(listRes)) { + error_display_res(NULL, NULL, listRes, "Failed to initiate ticket list population."); + + free(loadingData); + action_delete_tickets_free_data(data); + return; + } + + info_display("Loading", "Press B to cancel.", false, loadingData, action_delete_tickets_loading_update, action_delete_tickets_loading_draw_top); + } else { + linked_list_add(&data->contents, selected); + + data->deleteInfo.total = 1; + data->deleteInfo.processed = data->deleteInfo.total; + + prompt_display("Confirmation", message, COLOR_TEXT, true, data, action_delete_tickets_draw_top, action_delete_tickets_onresponse); + } +} + +void action_delete_ticket(linked_list* items, list_item* selected) { + action_delete_tickets_internal(items, selected, "Delete the selected ticket?", false); +} + +void action_delete_tickets_unused(linked_list* items, list_item* selected) { + action_delete_tickets_internal(items, selected, "Delete all unused tickets?", true); +} diff --git a/source/ui/section/action/installcias.c b/source/ui/section/action/installcias.c index d240618..7773d33 100644 --- a/source/ui/section/action/installcias.c +++ b/source/ui/section/action/installcias.c @@ -260,6 +260,47 @@ static void action_install_cias_onresponse(ui_view* view, void* data, bool respo } } +typedef struct { + install_cias_data* installData; + + const char* message; + + populate_files_data popData; +} install_cias_loading_data; + +static void action_install_cias_loading_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { + action_install_cias_draw_top(view, ((install_cias_loading_data*) data)->installData, x1, y1, x2, y2); +} + +static void action_install_cias_loading_update(ui_view* view, void* data, float* progress, char* text) { + install_cias_loading_data* loadingData = (install_cias_loading_data*) data; + + if(loadingData->popData.finished) { + ui_pop(); + info_destroy(view); + + if(R_SUCCEEDED(loadingData->popData.result)) { + loadingData->installData->installInfo.total = linked_list_size(&loadingData->installData->contents); + loadingData->installData->installInfo.processed = loadingData->installData->installInfo.total; + + prompt_display("Confirmation", loadingData->message, COLOR_TEXT, true, loadingData->installData, action_install_cias_draw_top, action_install_cias_onresponse); + } else { + error_display_res(NULL, NULL, loadingData->popData.result, "Failed to populate CIA list."); + + action_install_cias_free_data(loadingData->installData); + } + + free(loadingData); + return; + } + + if((hidKeysDown() & KEY_B) && !loadingData->popData.finished) { + svcSignalEvent(loadingData->popData.cancelEvent); + } + + snprintf(text, PROGRESS_TEXT_MAX, "Fetching CIA list..."); +} + static void action_install_cias_internal(linked_list* items, list_item* selected, const char* message, bool delete) { install_cias_data* data = (install_cias_data*) calloc(1, sizeof(install_cias_data)); if(data == NULL) { @@ -307,40 +348,35 @@ static void action_install_cias_internal(linked_list* items, list_item* selected linked_list_init(&data->contents); - populate_files_data popData; - memset(&popData, 0, sizeof(popData)); + install_cias_loading_data* loadingData = (install_cias_loading_data*) calloc(1, sizeof(install_cias_loading_data)); + if(loadingData == NULL) { + error_display(NULL, NULL, "Failed to allocate loading data."); - popData.items = &data->contents; - popData.archive = data->target->archive; - strncpy(popData.path, data->target->path, FILE_PATH_MAX); - popData.recursive = false; - popData.includeBase = !(data->target->attributes & FS_ATTRIBUTE_DIRECTORY); - popData.filter = util_filter_cias; - popData.filterData = NULL; + action_install_cias_free_data(data); + return; + } - Result listRes = task_populate_files(&popData); + loadingData->installData = data; + loadingData->message = message; + + loadingData->popData.items = &data->contents; + loadingData->popData.archive = data->target->archive; + strncpy(loadingData->popData.path, data->target->path, FILE_PATH_MAX); + loadingData->popData.recursive = false; + loadingData->popData.includeBase = !(data->target->attributes & FS_ATTRIBUTE_DIRECTORY); + loadingData->popData.filter = util_filter_cias; + loadingData->popData.filterData = NULL; + + Result listRes = task_populate_files(&loadingData->popData); if(R_FAILED(listRes)) { error_display_res(NULL, NULL, listRes, "Failed to initiate CIA list population."); + free(loadingData); action_install_cias_free_data(data); return; } - while(!popData.finished) { - svcSleepThread(1000000); - } - - if(R_FAILED(popData.result)) { - error_display_res(NULL, NULL, popData.result, "Failed to populate CIA list."); - - action_install_cias_free_data(data); - return; - } - - data->installInfo.total = linked_list_size(&data->contents); - data->installInfo.processed = data->installInfo.total; - - prompt_display("Confirmation", message, COLOR_TEXT, true, data, action_install_cias_draw_top, action_install_cias_onresponse); + info_display("Loading", "Press B to cancel.", false, loadingData, action_install_cias_loading_update, action_install_cias_loading_draw_top); } void action_install_cia(linked_list* items, list_item* selected) { diff --git a/source/ui/section/action/installtickets.c b/source/ui/section/action/installtickets.c index e7b0afc..9b70770 100644 --- a/source/ui/section/action/installtickets.c +++ b/source/ui/section/action/installtickets.c @@ -226,6 +226,47 @@ static void action_install_tickets_onresponse(ui_view* view, void* data, bool re } } +typedef struct { + install_tickets_data* installData; + + const char* message; + + populate_files_data popData; +} install_tickets_loading_data; + +static void action_install_tickets_loading_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { + action_install_tickets_draw_top(view, ((install_tickets_loading_data*) data)->installData, x1, y1, x2, y2); +} + +static void action_install_tickets_loading_update(ui_view* view, void* data, float* progress, char* text) { + install_tickets_loading_data* loadingData = (install_tickets_loading_data*) data; + + if(loadingData->popData.finished) { + ui_pop(); + info_destroy(view); + + if(R_SUCCEEDED(loadingData->popData.result)) { + loadingData->installData->installInfo.total = linked_list_size(&loadingData->installData->contents); + loadingData->installData->installInfo.processed = loadingData->installData->installInfo.total; + + prompt_display("Confirmation", loadingData->message, COLOR_TEXT, true, loadingData->installData, action_install_tickets_draw_top, action_install_tickets_onresponse); + } else { + error_display_res(NULL, NULL, loadingData->popData.result, "Failed to populate ticket list."); + + action_install_tickets_free_data(loadingData->installData); + } + + free(loadingData); + return; + } + + if((hidKeysDown() & KEY_B) && !loadingData->popData.finished) { + svcSignalEvent(loadingData->popData.cancelEvent); + } + + snprintf(text, PROGRESS_TEXT_MAX, "Fetching ticket list..."); +} + static void action_install_tickets_internal(linked_list* items, list_item* selected, const char* message, bool delete) { install_tickets_data* data = (install_tickets_data*) calloc(1, sizeof(install_tickets_data)); if(data == NULL) { @@ -270,40 +311,35 @@ static void action_install_tickets_internal(linked_list* items, list_item* selec linked_list_init(&data->contents); - populate_files_data popData; - memset(&popData, 0, sizeof(popData)); + install_tickets_loading_data* loadingData = (install_tickets_loading_data*) calloc(1, sizeof(install_tickets_loading_data)); + if(loadingData == NULL) { + error_display(NULL, NULL, "Failed to allocate loading data."); - popData.items = &data->contents; - popData.archive = data->target->archive; - strncpy(popData.path, data->target->path, FILE_PATH_MAX); - popData.recursive = false; - popData.includeBase = !(data->target->attributes & FS_ATTRIBUTE_DIRECTORY); - popData.filter = util_filter_tickets; - popData.filterData = NULL; + action_install_tickets_free_data(data); + return; + } - Result listRes = task_populate_files(&popData); + loadingData->installData = data; + loadingData->message = message; + + loadingData->popData.items = &data->contents; + loadingData->popData.archive = data->target->archive; + strncpy(loadingData->popData.path, data->target->path, FILE_PATH_MAX); + loadingData->popData.recursive = false; + loadingData->popData.includeBase = !(data->target->attributes & FS_ATTRIBUTE_DIRECTORY); + loadingData->popData.filter = util_filter_tickets; + loadingData->popData.filterData = NULL; + + Result listRes = task_populate_files(&loadingData->popData); if(R_FAILED(listRes)) { - error_display_res(NULL, NULL, listRes, "Failed to initiate ticket file list population."); + error_display_res(NULL, NULL, listRes, "Failed to initiate ticket list population."); + free(loadingData); action_install_tickets_free_data(data); return; } - while(!popData.finished) { - svcSleepThread(1000000); - } - - if(R_FAILED(popData.result)) { - error_display_res(NULL, NULL, popData.result, "Failed to populate ticket file list."); - - action_install_tickets_free_data(data); - return; - } - - data->installInfo.total = linked_list_size(&data->contents); - data->installInfo.processed = data->installInfo.total; - - prompt_display("Confirmation", message, COLOR_TEXT, true, data, action_install_tickets_draw_top, action_install_tickets_onresponse); + info_display("Loading", "Press B to cancel.", false, loadingData, action_install_tickets_loading_update, action_install_tickets_loading_draw_top); } void action_install_ticket(linked_list* items, list_item* selected) { diff --git a/source/ui/section/action/pastefiles.c b/source/ui/section/action/pastecontents.c similarity index 56% rename from source/ui/section/action/pastefiles.c rename to source/ui/section/action/pastecontents.c index 12efaa9..783af94 100644 --- a/source/ui/section/action/pastefiles.c +++ b/source/ui/section/action/pastecontents.c @@ -23,10 +23,10 @@ typedef struct { linked_list contents; data_op_data pasteInfo; -} paste_files_data; +} paste_contents_data; -static void action_paste_files_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - paste_files_data* pasteData = (paste_files_data*) data; +static void action_paste_contents_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { + paste_contents_data* pasteData = (paste_contents_data*) data; u32 curr = pasteData->pasteInfo.processed; if(curr < pasteData->pasteInfo.total) { @@ -36,7 +36,7 @@ static void action_paste_files_draw_top(ui_view* view, void* data, float x1, flo } } -static void action_paste_files_get_dst_path(paste_files_data* data, u32 index, char* dstPath) { +static void action_paste_contents_get_dst_path(paste_contents_data* data, u32 index, char* dstPath) { char baseSrcPath[FILE_PATH_MAX]; if(clipboard_is_contents_only()) { strncpy(baseSrcPath, clipboard_get_path(), FILE_PATH_MAX); @@ -54,22 +54,22 @@ static void action_paste_files_get_dst_path(paste_files_data* data, u32 index, c snprintf(dstPath, FILE_PATH_MAX, "%s%s", baseDstPath, ((file_info*) ((list_item*) linked_list_get(&data->contents, index))->data)->path + strlen(baseSrcPath)); } -static Result action_paste_files_is_src_directory(void* data, u32 index, bool* isDirectory) { - paste_files_data* pasteData = (paste_files_data*) data; +static Result action_paste_contents_is_src_directory(void* data, u32 index, bool* isDirectory) { + paste_contents_data* pasteData = (paste_contents_data*) data; *isDirectory = (bool) (((file_info*) ((list_item*) linked_list_get(&pasteData->contents, index))->data)->attributes & FS_ATTRIBUTE_DIRECTORY); return 0; } -static Result action_paste_files_make_dst_directory(void* data, u32 index) { - paste_files_data* pasteData = (paste_files_data*) data; +static Result action_paste_contents_make_dst_directory(void* data, u32 index) { + paste_contents_data* pasteData = (paste_contents_data*) data; Result res = 0; u32 attributes = ((file_info*) ((list_item*) linked_list_get(&pasteData->contents, index))->data)->attributes; char dstPath[FILE_PATH_MAX]; - action_paste_files_get_dst_path(pasteData, index, dstPath); + action_paste_contents_get_dst_path(pasteData, index, dstPath); FS_Path* fsPath = util_make_path_utf8(dstPath); if(fsPath != NULL) { @@ -107,8 +107,8 @@ static Result action_paste_files_make_dst_directory(void* data, u32 index) { return res; } -static Result action_paste_files_open_src(void* data, u32 index, u32* handle) { - paste_files_data* pasteData = (paste_files_data*) data; +static Result action_paste_contents_open_src(void* data, u32 index, u32* handle) { + paste_contents_data* pasteData = (paste_contents_data*) data; Result res = 0; @@ -124,25 +124,25 @@ static Result action_paste_files_open_src(void* data, u32 index, u32* handle) { return res; } -static Result action_paste_files_close_src(void* data, u32 index, bool succeeded, u32 handle) { +static Result action_paste_contents_close_src(void* data, u32 index, bool succeeded, u32 handle) { return FSFILE_Close(handle); } -static Result action_paste_files_get_src_size(void* data, u32 handle, u64* size) { +static Result action_paste_contents_get_src_size(void* data, u32 handle, u64* size) { return FSFILE_GetSize(handle, size); } -static Result action_paste_files_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) { +static Result action_paste_contents_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) { return FSFILE_Read(handle, bytesRead, offset, buffer, size); } -static Result action_paste_files_open_dst(void* data, u32 index, void* initialReadBlock, u64 size, u32* handle) { - paste_files_data* pasteData = (paste_files_data*) data; +static Result action_paste_contents_open_dst(void* data, u32 index, void* initialReadBlock, u64 size, u32* handle) { + paste_contents_data* pasteData = (paste_contents_data*) data; Result res = 0; char dstPath[FILE_PATH_MAX]; - action_paste_files_get_dst_path(pasteData, index, dstPath); + action_paste_contents_get_dst_path(pasteData, index, dstPath); FS_Path* fsPath = util_make_path_utf8(dstPath); if(fsPath != NULL) { @@ -177,14 +177,14 @@ static Result action_paste_files_open_dst(void* data, u32 index, void* initialRe return res; } -static Result action_paste_files_close_dst(void* data, u32 index, bool succeeded, u32 handle) { - paste_files_data* pasteData = (paste_files_data*) data; +static Result action_paste_contents_close_dst(void* data, u32 index, bool succeeded, u32 handle) { + paste_contents_data* pasteData = (paste_contents_data*) data; Result res = 0; if(R_SUCCEEDED(res = FSFILE_Close(handle))) { char dstPath[FILE_PATH_MAX]; - action_paste_files_get_dst_path(pasteData, index, dstPath); + action_paste_contents_get_dst_path(pasteData, index, dstPath); char parentPath[FILE_PATH_MAX]; util_get_parent_path(parentPath, dstPath, FILE_PATH_MAX); @@ -207,34 +207,34 @@ static Result action_paste_files_close_dst(void* data, u32 index, bool succeeded return res; } -static Result action_paste_files_write_dst(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size) { +static Result action_paste_contents_write_dst(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size) { return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0); } -static Result action_paste_files_suspend_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result action_paste_contents_suspend_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { return 0; } -static Result action_paste_files_restore_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result action_paste_contents_restore_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { return 0; } -static Result action_paste_files_suspend(void* data, u32 index) { +static Result action_paste_contents_suspend(void* data, u32 index) { return 0; } -static Result action_paste_files_restore(void* data, u32 index) { +static Result action_paste_contents_restore(void* data, u32 index) { return 0; } -static bool action_paste_files_error(void* data, u32 index, Result res) { - paste_files_data* pasteData = (paste_files_data*) data; +static bool action_paste_contents_error(void* data, u32 index, Result res) { + paste_contents_data* pasteData = (paste_contents_data*) data; if(res == R_FBI_CANCELLED) { prompt_display("Failure", "Paste cancelled.", COLOR_TEXT, false, NULL, NULL, NULL); return false; } else { - ui_view* view = error_display_res(data, action_paste_files_draw_top, res, "Failed to paste content."); + ui_view* view = error_display_res(data, action_paste_contents_draw_top, res, "Failed to paste content."); if(view != NULL) { svcWaitSynchronization(view->active, U64_MAX); } @@ -243,14 +243,14 @@ static bool action_paste_files_error(void* data, u32 index, Result res) { return index < pasteData->pasteInfo.total - 1; } -static void action_paste_files_free_data(paste_files_data* data) { +static void action_paste_contents_free_data(paste_contents_data* data) { task_clear_files(&data->contents); linked_list_destroy(&data->contents); free(data); } -static void action_paste_files_update(ui_view* view, void* data, float* progress, char* text) { - paste_files_data* pasteData = (paste_files_data*) data; +static void action_paste_contents_update(ui_view* view, void* data, float* progress, char* text) { + paste_contents_data* pasteData = (paste_contents_data*) data; if(pasteData->pasteInfo.finished) { FSUSER_ControlArchive(pasteData->target->archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0); @@ -264,7 +264,7 @@ static void action_paste_files_update(ui_view* view, void* data, float* progress prompt_display("Success", "Contents pasted.", COLOR_TEXT, false, NULL, NULL, NULL); } - action_paste_files_free_data(pasteData); + action_paste_contents_free_data(pasteData); return; } @@ -277,31 +277,70 @@ static void action_paste_files_update(ui_view* view, void* data, float* progress snprintf(text, PROGRESS_TEXT_MAX, "%lu / %lu\n%.2f %s / %.2f %s", pasteData->pasteInfo.processed, pasteData->pasteInfo.total, util_get_display_size(pasteData->pasteInfo.currProcessed), util_get_display_size_units(pasteData->pasteInfo.currProcessed), util_get_display_size(pasteData->pasteInfo.currTotal), util_get_display_size_units(pasteData->pasteInfo.currTotal)); } -static void action_paste_files_onresponse(ui_view* view, void* data, bool response) { - paste_files_data* pasteData = (paste_files_data*) data; +static void action_paste_contents_onresponse(ui_view* view, void* data, bool response) { + paste_contents_data* pasteData = (paste_contents_data*) data; if(response) { Result res = task_data_op(&pasteData->pasteInfo); if(R_SUCCEEDED(res)) { - info_display("Pasting Contents", "Press B to cancel.", true, data, action_paste_files_update, action_paste_files_draw_top); + info_display("Pasting Contents", "Press B to cancel.", true, data, action_paste_contents_update, action_paste_contents_draw_top); } else { error_display_res(pasteData->target, ui_draw_file_info, res, "Failed to initiate paste operation."); - action_paste_files_free_data(pasteData); + action_paste_contents_free_data(pasteData); } } else { - action_paste_files_free_data(pasteData); + action_paste_contents_free_data(pasteData); } } +typedef struct { + paste_contents_data* pasteData; + + populate_files_data popData; +} paste_contents_loading_data; + +static void action_paste_contents_loading_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { + action_paste_contents_draw_top(view, ((paste_contents_loading_data*) data)->pasteData, x1, y1, x2, y2); +} + +static void action_paste_contents_loading_update(ui_view* view, void* data, float* progress, char* text) { + paste_contents_loading_data* loadingData = (paste_contents_loading_data*) data; + + if(loadingData->popData.finished) { + ui_pop(); + info_destroy(view); + + if(R_SUCCEEDED(loadingData->popData.result)) { + loadingData->pasteData->pasteInfo.total = linked_list_size(&loadingData->pasteData->contents); + loadingData->pasteData->pasteInfo.processed = loadingData->pasteData->pasteInfo.total; + + prompt_display("Confirmation", "Paste clipboard contents to the current directory?", COLOR_TEXT, true, loadingData->pasteData, action_paste_contents_draw_top, action_paste_contents_onresponse); + } else { + error_display_res(NULL, NULL, loadingData->popData.result, "Failed to populate clipboard content list."); + + action_paste_contents_free_data(loadingData->pasteData); + } + + free(loadingData); + return; + } + + if((hidKeysDown() & KEY_B) && !loadingData->popData.finished) { + svcSignalEvent(loadingData->popData.cancelEvent); + } + + snprintf(text, PROGRESS_TEXT_MAX, "Fetching clipboard content list..."); +} + void action_paste_contents(linked_list* items, list_item* selected) { if(!clipboard_has_contents()) { prompt_display("Failure", "Clipboard empty.", COLOR_TEXT, false, NULL, NULL, NULL); return; } - paste_files_data* data = (paste_files_data*) calloc(1, sizeof(paste_files_data)); + paste_contents_data* data = (paste_contents_data*) calloc(1, sizeof(paste_contents_data)); if(data == NULL) { - error_display(NULL, NULL, "Failed to allocate paste files data."); + error_display(NULL, NULL, "Failed to allocate paste contents data."); return; } @@ -316,62 +355,56 @@ void action_paste_contents(linked_list* items, list_item* selected) { data->pasteInfo.copyBufferSize = 256 * 1024; data->pasteInfo.copyEmpty = true; - data->pasteInfo.isSrcDirectory = action_paste_files_is_src_directory; - data->pasteInfo.makeDstDirectory = action_paste_files_make_dst_directory; + data->pasteInfo.isSrcDirectory = action_paste_contents_is_src_directory; + data->pasteInfo.makeDstDirectory = action_paste_contents_make_dst_directory; - data->pasteInfo.openSrc = action_paste_files_open_src; - data->pasteInfo.closeSrc = action_paste_files_close_src; - data->pasteInfo.getSrcSize = action_paste_files_get_src_size; - data->pasteInfo.readSrc = action_paste_files_read_src; + data->pasteInfo.openSrc = action_paste_contents_open_src; + data->pasteInfo.closeSrc = action_paste_contents_close_src; + data->pasteInfo.getSrcSize = action_paste_contents_get_src_size; + data->pasteInfo.readSrc = action_paste_contents_read_src; - data->pasteInfo.openDst = action_paste_files_open_dst; - data->pasteInfo.closeDst = action_paste_files_close_dst; - data->pasteInfo.writeDst = action_paste_files_write_dst; + data->pasteInfo.openDst = action_paste_contents_open_dst; + data->pasteInfo.closeDst = action_paste_contents_close_dst; + data->pasteInfo.writeDst = action_paste_contents_write_dst; - data->pasteInfo.suspendCopy = action_paste_files_suspend_copy; - data->pasteInfo.restoreCopy = action_paste_files_restore_copy; + data->pasteInfo.suspendCopy = action_paste_contents_suspend_copy; + data->pasteInfo.restoreCopy = action_paste_contents_restore_copy; - data->pasteInfo.suspend = action_paste_files_suspend; - data->pasteInfo.restore = action_paste_files_restore; + data->pasteInfo.suspend = action_paste_contents_suspend; + data->pasteInfo.restore = action_paste_contents_restore; - data->pasteInfo.error = action_paste_files_error; + data->pasteInfo.error = action_paste_contents_error; data->pasteInfo.finished = true; linked_list_init(&data->contents); - populate_files_data popData; - memset(&popData, 0, sizeof(popData)); + paste_contents_loading_data* loadingData = (paste_contents_loading_data*) calloc(1, sizeof(paste_contents_loading_data)); + if(loadingData == NULL) { + error_display(NULL, NULL, "Failed to allocate loading data."); - popData.items = &data->contents; - popData.archive = clipboard_get_archive(); - strncpy(popData.path, clipboard_get_path(), FILE_PATH_MAX); - popData.recursive = true; - popData.includeBase = !clipboard_is_contents_only() || !util_is_dir(clipboard_get_archive(), clipboard_get_path()); - popData.filter = NULL; - popData.filterData = NULL; + action_paste_contents_free_data(data); + return; + } - Result listRes = task_populate_files(&popData); + loadingData->pasteData = data; + + loadingData->popData.items = &data->contents; + loadingData->popData.archive = clipboard_get_archive(); + strncpy(loadingData->popData.path, clipboard_get_path(), FILE_PATH_MAX); + loadingData->popData.recursive = true; + loadingData->popData.includeBase = !clipboard_is_contents_only() || !util_is_dir(clipboard_get_archive(), clipboard_get_path()); + loadingData->popData.filter = NULL; + loadingData->popData.filterData = NULL; + + Result listRes = task_populate_files(&loadingData->popData); if(R_FAILED(listRes)) { error_display_res(NULL, NULL, listRes, "Failed to initiate clipboard content list population."); - action_paste_files_free_data(data); + free(loadingData); + action_paste_contents_free_data(data); return; } - while(!popData.finished) { - svcSleepThread(1000000); - } - - if(R_FAILED(popData.result)) { - error_display_res(NULL, NULL, popData.result, "Failed to populate clipboard content list."); - - action_paste_files_free_data(data); - return; - } - - data->pasteInfo.total = linked_list_size(&data->contents); - data->pasteInfo.processed = data->pasteInfo.total; - - prompt_display("Confirmation", "Paste clipboard contents to the current directory?", COLOR_TEXT, true, data, action_paste_files_draw_top, action_paste_files_onresponse); + info_display("Loading", "Press B to cancel.", false, loadingData, action_paste_contents_loading_update, action_paste_contents_loading_draw_top); } \ No newline at end of file diff --git a/source/ui/section/tickets.c b/source/ui/section/tickets.c index c86a2da..75df21e 100644 --- a/source/ui/section/tickets.c +++ b/source/ui/section/tickets.c @@ -14,6 +14,7 @@ static list_item install_from_cdn = {"Install from CDN", COLOR_TEXT, action_install_cdn}; static list_item delete_ticket = {"Delete Ticket", COLOR_TEXT, action_delete_ticket}; +static list_item delete_unused_tickets = {"Delete Unused Tickets", COLOR_TEXT, action_delete_tickets_unused}; typedef struct { populate_tickets_data populateData; @@ -58,6 +59,7 @@ static void tickets_action_update(ui_view* view, void* data, linked_list* items, if(linked_list_size(items) == 0) { linked_list_add(items, &install_from_cdn); linked_list_add(items, &delete_ticket); + linked_list_add(items, &delete_unused_tickets); } }