diff --git a/source/screen.c b/source/screen.c index ad827d7..0c343ab 100644 --- a/source/screen.c +++ b/source/screen.c @@ -134,7 +134,7 @@ void screen_init() { } TGLP_s* glyphInfo = fontGetGlyphInfo(); - glyphSheets = calloc(1, glyphInfo->nSheets * sizeof(C3D_Tex)); + glyphSheets = calloc(glyphInfo->nSheets, sizeof(C3D_Tex)); for(int i = 0; i < glyphInfo->nSheets; i++) { C3D_Tex* tex = &glyphSheets[i]; tex->data = fontGetGlyphSheetTex(i); diff --git a/source/ui/error.c b/source/ui/error.c index 6d34872..d0aa690 100644 --- a/source/ui/error.c +++ b/source/ui/error.c @@ -9,12 +9,6 @@ #include "prompt.h" #include "../screen.h" -typedef struct { - char fullText[4096]; - void* data; - void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2); -} error_data; - static const char* level_to_string(Result res) { switch(R_LEVEL(res)) { case RL_SUCCESS: @@ -546,6 +540,13 @@ static const char* description_to_string(Result res) { } } +typedef struct { + char fullText[4096]; + void* data; + volatile bool* dismissed; + void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2); +} error_data; + static void error_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { error_data* errorData = (error_data*) data; @@ -555,13 +556,20 @@ static void error_draw_top(ui_view* view, void* data, float x1, float y1, float } static void error_onresponse(ui_view* view, void* data, bool response) { + error_data* errorData = (error_data*) data; + + if(errorData->dismissed != NULL) { + *errorData->dismissed = true; + } + prompt_destroy(view); free(data); } -void error_display(void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), const char* text, ...) { +void error_display(volatile bool* dismissed, void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), const char* text, ...) { error_data* errorData = (error_data*) calloc(1, sizeof(error_data)); errorData->data = data; + errorData->dismissed = dismissed; errorData->drawTop = drawTop; va_list list; @@ -572,9 +580,10 @@ void error_display(void* data, void (*drawTop)(ui_view* view, void* data, float ui_push(prompt_create("Error", errorData->fullText, COLOR_TEXT, false, errorData, NULL, error_draw_top, error_onresponse)); } -void error_display_res(void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), Result result, const char* text, ...) { +void error_display_res(volatile bool* dismissed, void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), Result result, const char* text, ...) { error_data* errorData = (error_data*) calloc(1, sizeof(error_data)); errorData->data = data; + errorData->dismissed = dismissed; errorData->drawTop = drawTop; char textBuf[1024]; @@ -592,9 +601,10 @@ void error_display_res(void* data, void (*drawTop)(ui_view* view, void* data, fl ui_push(prompt_create("Error", errorData->fullText, COLOR_TEXT, false, errorData, NULL, error_draw_top, error_onresponse)); } -void error_display_errno(void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), int err, const char* text, ...) { +void error_display_errno(volatile bool* dismissed, void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), int err, const char* text, ...) { error_data* errorData = (error_data*) calloc(1, sizeof(error_data)); errorData->data = data; + errorData->dismissed = dismissed; errorData->drawTop = drawTop; char textBuf[1024]; diff --git a/source/ui/error.h b/source/ui/error.h index a961503..fe1d3ee 100644 --- a/source/ui/error.h +++ b/source/ui/error.h @@ -2,6 +2,6 @@ #include "ui.h" -void error_display(void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), const char* text, ...); -void error_display_res(void* data, void (* drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), Result result, const char* text, ...); -void error_display_errno(void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), int err, const char* text, ...); \ No newline at end of file +void error_display(volatile bool* dismissed, void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), const char* text, ...); +void error_display_res(volatile bool* dismissed, void* data, void (* drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), Result result, const char* text, ...); +void error_display_errno(volatile bool* dismissed, void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), int err, const char* text, ...); \ No newline at end of file diff --git a/source/ui/section/action/copyfiles.c b/source/ui/section/action/copyfiles.c index 508edd3..99fd595 100644 --- a/source/ui/section/action/copyfiles.c +++ b/source/ui/section/action/copyfiles.c @@ -13,7 +13,7 @@ static void action_copy_files_success_onresponse(ui_view* view, void* data, bool void action_copy_contents(file_info* info, bool* populated) { Result res = 0; if(R_FAILED(res = clipboard_set_contents(*info->archive, info->path))) { - error_display_res(info, ui_draw_file_info, res, "Failed to copy contents to clipboard."); + error_display_res(NULL, info, ui_draw_file_info, res, "Failed to copy contents to clipboard."); return; } diff --git a/source/ui/section/action/deleteallpendingtitles.c b/source/ui/section/action/deleteallpendingtitles.c index c53a513..e7d4b89 100644 --- a/source/ui/section/action/deleteallpendingtitles.c +++ b/source/ui/section/action/deleteallpendingtitles.c @@ -1,3 +1,6 @@ +#include +#include + #include <3ds.h> #include "action.h" @@ -5,42 +8,121 @@ #include "../../progressbar.h" #include "../../prompt.h" #include "../../../screen.h" +#include "../task/task.h" -static void action_delete_all_pending_titles_success_onresponse(ui_view* view, void* data, bool response) { - prompt_destroy(view); +typedef struct { + pending_title_info* info; + bool* populated; + + u64* titleIds; + u32 processed; + u32 total; + + u32 sdTotal; + u32 nandTotal; +} delete_pending_titles_data; + +static void action_delete_pending_titles_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { + delete_pending_titles_data* deleteData = (delete_pending_titles_data*) data; + + if(deleteData->info != NULL) { + ui_draw_pending_title_info(view, deleteData->info, x1, y1, x2, y2); + } } -static void action_delete_all_pending_titles_update(ui_view* view, void* data, float* progress, char* progressText) { - Result res = AM_DeleteAllPendingTitles(MEDIATYPE_NAND); - if(R_SUCCEEDED(res)) { - res = AM_DeleteAllPendingTitles(MEDIATYPE_SD); - } +static void action_delete_pending_titles_free_data(delete_pending_titles_data* data) { + free(data->titleIds); + free(data); +} - if(R_FAILED(res)) { +static void action_delete_pending_titles_success_onresponse(ui_view* view, void* data, bool response) { + prompt_destroy(view); + + action_delete_pending_titles_free_data((delete_pending_titles_data*) data); +} + +static void action_delete_pending_titles_update(ui_view* view, void* data, float* progress, char* progressText) { + delete_pending_titles_data* deleteData = (delete_pending_titles_data*) data; + + if(deleteData->processed >= deleteData->total) { progressbar_destroy(view); ui_pop(); - error_display_res(NULL, NULL, res, "Failed to delete pending titles."); - + ui_push(prompt_create("Success", "Pending title(s) deleted.", COLOR_TEXT, false, data, NULL, action_delete_pending_titles_draw_top, action_delete_pending_titles_success_onresponse)); return; } else { - *(bool*) data = false; + Result res = AM_DeletePendingTitle(deleteData->processed >= deleteData->sdTotal ? MEDIATYPE_NAND : MEDIATYPE_SD, deleteData->titleIds[deleteData->processed]); + if(R_FAILED(res)) { + progressbar_destroy(view); + ui_pop(); + + error_display_res(NULL, deleteData->info, deleteData->info != NULL ? ui_draw_pending_title_info : NULL, res, "Failed to delete pending title(s)."); + + action_delete_pending_titles_free_data(deleteData); + + return; + } else { + *deleteData->populated = false; + + deleteData->processed++; + } } - progressbar_destroy(view); - ui_pop(); - - ui_push(prompt_create("Success", "Pending titles deleted.", COLOR_TEXT, false, NULL, NULL, NULL, action_delete_all_pending_titles_success_onresponse)); + *progress = deleteData->total > 0 ? (float) deleteData->processed / (float) deleteData->total : 0; + snprintf(progressText, PROGRESS_TEXT_MAX, "%lu / %lu", deleteData->processed, deleteData->total); } -static void action_delete_all_pending_titles_onresponse(ui_view* view, void* data, bool response) { +static void action_delete_pending_titles_onresponse(ui_view* view, void* data, bool response) { prompt_destroy(view); if(response) { - ui_push(progressbar_create("Deleting Pending Titles", "", data, action_delete_all_pending_titles_update, NULL)); + ui_push(progressbar_create("Deleting Pending Title(s)", "", data, action_delete_pending_titles_update, action_delete_pending_titles_draw_top)); + } else { + action_delete_pending_titles_free_data((delete_pending_titles_data*) data); } } +void action_delete_pending_title(pending_title_info* info, bool* populated) { + delete_pending_titles_data* data = (delete_pending_titles_data*) calloc(1, sizeof(delete_pending_titles_data)); + data->info = info; + data->populated = populated; + data->titleIds = (u64*) calloc(1, sizeof(u64)); + data->titleIds[0] = info->titleId; + data->processed = 0; + data->total = 1; + data->sdTotal = info->mediaType == MEDIATYPE_SD ? 1 : 0; + data->nandTotal = info->mediaType == MEDIATYPE_NAND ? 1 : 0; + + ui_push(prompt_create("Confirmation", "Delete the selected pending title?", COLOR_TEXT, true, data, NULL, action_delete_pending_titles_draw_top, action_delete_pending_titles_onresponse)); +} + void action_delete_all_pending_titles(pending_title_info* info, bool* populated) { - ui_push(prompt_create("Confirmation", "Delete all pending titles?", COLOR_TEXT, true, populated, NULL, NULL, action_delete_all_pending_titles_onresponse)); + delete_pending_titles_data* data = (delete_pending_titles_data*) calloc(1, sizeof(delete_pending_titles_data)); + data->info = NULL; + data->populated = populated; + data->titleIds = NULL; + data->processed = 0; + data->total = 0; + data->sdTotal = 0; + data->nandTotal = 0; + + Result res = 0; + if(R_FAILED(res = AM_GetPendingTitleCount(&data->sdTotal, MEDIATYPE_SD, AM_STATUS_MASK_INSTALLING | AM_STATUS_MASK_AWAITING_FINALIZATION)) || R_FAILED(res = AM_GetPendingTitleCount(&data->nandTotal, MEDIATYPE_NAND, AM_STATUS_MASK_INSTALLING | AM_STATUS_MASK_AWAITING_FINALIZATION))) { + error_display_res(NULL, NULL, NULL, res, "Failed to retrieve pending title count."); + + free(data); + return; + } + + data->total = data->sdTotal + data->nandTotal; + data->titleIds = (u64*) calloc(data->total, sizeof(u64)); + if(R_FAILED(res = AM_GetPendingTitleList(&data->sdTotal, data->sdTotal, MEDIATYPE_SD, AM_STATUS_MASK_INSTALLING | AM_STATUS_MASK_AWAITING_FINALIZATION, data->titleIds)) || R_FAILED(res = AM_GetPendingTitleList(&data->nandTotal, data->nandTotal, MEDIATYPE_NAND, AM_STATUS_MASK_INSTALLING | AM_STATUS_MASK_AWAITING_FINALIZATION, &data->titleIds[data->sdTotal]))) { + error_display_res(NULL, NULL, NULL, res, "Failed to retrieve pending title list."); + + free(data->titleIds); + free(data); + return; + } + + ui_push(prompt_create("Confirmation", "Delete all pending titles?", COLOR_TEXT, true, data, NULL, action_delete_pending_titles_draw_top, action_delete_pending_titles_onresponse)); } \ No newline at end of file diff --git a/source/ui/section/action/deletecontents.c b/source/ui/section/action/deletecontents.c index 8ad1314..64f1b46 100644 --- a/source/ui/section/action/deletecontents.c +++ b/source/ui/section/action/deletecontents.c @@ -17,31 +17,31 @@ typedef struct { u32 processed; u32 total; char** contents; -} delete_dir_contents_data; +} delete_contents_data; -static void action_delete_dir_contents_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - ui_draw_file_info(view, ((delete_dir_contents_data*) data)->base, x1, y1, x2, y2); +static void action_delete_contents_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { + ui_draw_file_info(view, ((delete_contents_data*) data)->base, x1, y1, x2, y2); } -static void action_delete_dir_contents_free_data(delete_dir_contents_data* data) { +static void action_delete_contents_free_data(delete_contents_data* data) { util_free_contents(data->contents, data->total); free(data); } -static void action_delete_dir_contents_done_onresponse(ui_view* view, void* data, bool response) { - action_delete_dir_contents_free_data((delete_dir_contents_data*) data); - +static void action_delete_contents_done_onresponse(ui_view* view, void* data, bool response) { prompt_destroy(view); + + action_delete_contents_free_data((delete_contents_data*) data); } -static void action_delete_dir_contents_update(ui_view* view, void* data, float* progress, char* progressText) { - delete_dir_contents_data* deleteData = (delete_dir_contents_data*) data; +static void action_delete_contents_update(ui_view* view, void* data, float* progress, char* progressText) { + delete_contents_data* deleteData = (delete_contents_data*) data; if(hidKeysDown() & KEY_B) { progressbar_destroy(view); ui_pop(); - ui_push(prompt_create("Failure", "Delete cancelled.", COLOR_TEXT, false, data, NULL, action_delete_dir_contents_draw_top, action_delete_dir_contents_done_onresponse)); + ui_push(prompt_create("Failure", "Delete cancelled.", COLOR_TEXT, false, data, NULL, action_delete_contents_draw_top, action_delete_contents_done_onresponse)); return; } @@ -49,7 +49,8 @@ static void action_delete_dir_contents_update(ui_view* view, void* data, float* progressbar_destroy(view); ui_pop(); - ui_push(prompt_create("Success", "Contents deleted.", COLOR_TEXT, false, data, NULL, action_delete_dir_contents_draw_top, action_delete_dir_contents_done_onresponse)); + ui_push(prompt_create("Success", "Contents deleted.", COLOR_TEXT, false, data, NULL, action_delete_contents_draw_top, action_delete_contents_done_onresponse)); + return; } else { FS_Archive* archive = deleteData->base->archive; char* path = deleteData->contents[deleteData->processed]; @@ -72,13 +73,13 @@ static void action_delete_dir_contents_update(ui_view* view, void* data, float* } if(strlen(path) > 48) { - error_display_res(deleteData->base, ui_draw_file_info, res, "Failed to delete content.\n%.45s...", path); + error_display_res(NULL, deleteData->base, ui_draw_file_info, res, "Failed to delete content.\n%.45s...", path); } else { - error_display_res(deleteData->base, ui_draw_file_info, res, "Failed to delete content.\n%.48s", path); + error_display_res(NULL, deleteData->base, ui_draw_file_info, res, "Failed to delete content.\n%.48s", path); } if(deleteData->processed >= deleteData->total - 1) { - action_delete_dir_contents_free_data(deleteData); + action_delete_contents_free_data(deleteData); progressbar_destroy(view); return; } @@ -87,88 +88,88 @@ static void action_delete_dir_contents_update(ui_view* view, void* data, float* } deleteData->processed++; - - *progress = (float) deleteData->processed / (float) deleteData->total; - snprintf(progressText, PROGRESS_TEXT_MAX, "%lu / %lu", deleteData->processed, deleteData->total); } + + *progress = deleteData->total > 0 ? (float) deleteData->processed / (float) deleteData->total : 0; + snprintf(progressText, PROGRESS_TEXT_MAX, "%lu / %lu", deleteData->processed, deleteData->total); } -static void action_delete_dir_contents_onresponse(ui_view* view, void* data, bool response) { +static void action_delete_contents_onresponse(ui_view* view, void* data, bool response) { prompt_destroy(view); if(response) { - ui_view* progressView = progressbar_create("Deleting Contents", "Press B to cancel.", data, action_delete_dir_contents_update, action_delete_dir_contents_draw_top); - snprintf(progressbar_get_progress_text(progressView), PROGRESS_TEXT_MAX, "0 / %lu", ((delete_dir_contents_data*) data)->total); + ui_view* progressView = progressbar_create("Deleting Contents", "Press B to cancel.", data, action_delete_contents_update, action_delete_contents_draw_top); + snprintf(progressbar_get_progress_text(progressView), PROGRESS_TEXT_MAX, "0 / %lu", ((delete_contents_data*) data)->total); ui_push(progressView); } else { - free(data); + action_delete_contents_free_data((delete_contents_data*) data); } } void action_delete_contents(file_info* info, bool* populated) { - delete_dir_contents_data* data = (delete_dir_contents_data*) calloc(1, sizeof(delete_dir_contents_data)); + delete_contents_data* data = (delete_contents_data*) calloc(1, sizeof(delete_contents_data)); data->base = info; data->populated = populated; data->processed = 0; Result res = 0; if(R_FAILED(res = util_populate_contents(&data->contents, &data->total, info->archive, info->path, true, false, NULL, NULL))) { - error_display_res(info, ui_draw_file_info, res, "Failed to retrieve content list."); + error_display_res(NULL, info, ui_draw_file_info, res, "Failed to retrieve content list."); free(data); return; } - ui_push(prompt_create("Confirmation", "Delete the selected content?", COLOR_TEXT, true, data, NULL, action_delete_dir_contents_draw_top, action_delete_dir_contents_onresponse)); + ui_push(prompt_create("Confirmation", "Delete the selected content?", COLOR_TEXT, true, data, NULL, action_delete_contents_draw_top, action_delete_contents_onresponse)); } void action_delete_dir(file_info* info, bool* populated) { - delete_dir_contents_data* data = (delete_dir_contents_data*) calloc(1, sizeof(delete_dir_contents_data)); + delete_contents_data* data = (delete_contents_data*) calloc(1, sizeof(delete_contents_data)); data->base = info; data->populated = populated; data->processed = 0; Result res = 0; if(R_FAILED(res = util_populate_contents(&data->contents, &data->total, info->archive, info->path, true, false, NULL, NULL))) { - error_display_res(info, ui_draw_file_info, res, "Failed to retrieve content list."); + error_display_res(NULL, info, ui_draw_file_info, res, "Failed to retrieve content list."); free(data); return; } - ui_push(prompt_create("Confirmation", "Delete the current directory?", COLOR_TEXT, true, data, NULL, action_delete_dir_contents_draw_top, action_delete_dir_contents_onresponse)); + ui_push(prompt_create("Confirmation", "Delete the current directory?", COLOR_TEXT, true, data, NULL, action_delete_contents_draw_top, action_delete_contents_onresponse)); } void action_delete_dir_contents(file_info* info, bool* populated) { - delete_dir_contents_data* data = (delete_dir_contents_data*) calloc(1, sizeof(delete_dir_contents_data)); + delete_contents_data* data = (delete_contents_data*) calloc(1, sizeof(delete_contents_data)); data->base = info; data->populated = populated; data->processed = 0; Result res = 0; if(R_FAILED(res = util_populate_contents(&data->contents, &data->total, info->archive, info->path, true, false, info->path, util_filter_not_path))) { - error_display_res(info, ui_draw_file_info, res, "Failed to retrieve content list."); + error_display_res(NULL, info, ui_draw_file_info, res, "Failed to retrieve content list."); free(data); return; } - ui_push(prompt_create("Confirmation", "Delete all contents of the current directory?", COLOR_TEXT, true, data, NULL, action_delete_dir_contents_draw_top, action_delete_dir_contents_onresponse)); + ui_push(prompt_create("Confirmation", "Delete all contents of the current directory?", COLOR_TEXT, true, data, NULL, action_delete_contents_draw_top, action_delete_contents_onresponse)); } void action_delete_dir_cias(file_info* info, bool* populated) { - delete_dir_contents_data* data = (delete_dir_contents_data*) calloc(1, sizeof(delete_dir_contents_data)); + delete_contents_data* data = (delete_contents_data*) calloc(1, sizeof(delete_contents_data)); data->base = info; data->populated = populated; data->processed = 0; Result res = 0; if(R_FAILED(res = util_populate_contents(&data->contents, &data->total, info->archive, info->path, false, false, ".cia", util_filter_file_extension))) { - error_display_res(info, ui_draw_file_info, res, "Failed to retrieve content list."); + error_display_res(NULL, info, ui_draw_file_info, res, "Failed to retrieve content list."); free(data); return; } - ui_push(prompt_create("Confirmation", "Delete all CIAs in the current directory?", COLOR_TEXT, true, data, NULL, action_delete_dir_contents_draw_top, action_delete_dir_contents_onresponse)); + ui_push(prompt_create("Confirmation", "Delete all CIAs in the current directory?", COLOR_TEXT, true, data, NULL, action_delete_contents_draw_top, action_delete_contents_onresponse)); } \ No newline at end of file diff --git a/source/ui/section/action/deleteextsavedata.c b/source/ui/section/action/deleteextsavedata.c index 53af7b6..46ee0de 100644 --- a/source/ui/section/action/deleteextsavedata.c +++ b/source/ui/section/action/deleteextsavedata.c @@ -32,7 +32,7 @@ static void action_delete_ext_save_data_update(ui_view* view, void* data, float* ui_pop(); if(R_FAILED(res)) { - error_display_res(deleteData->info, ui_draw_ext_save_data_info, res, "Failed to delete ext save data."); + error_display_res(NULL, deleteData->info, ui_draw_ext_save_data_info, res, "Failed to delete ext save data."); } else { *deleteData->populated = false; diff --git a/source/ui/section/action/deletependingtitle.c b/source/ui/section/action/deletependingtitle.c deleted file mode 100644 index 9cceffa..0000000 --- a/source/ui/section/action/deletependingtitle.c +++ /dev/null @@ -1,59 +0,0 @@ -#include - -#include <3ds.h> - -#include "action.h" -#include "../../error.h" -#include "../../progressbar.h" -#include "../../prompt.h" -#include "../../../screen.h" - -typedef struct { - pending_title_info* info; - bool* populated; -} delete_pending_title_data; - -static void action_delete_pending_title_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - ui_draw_pending_title_info(view, ((delete_pending_title_data*) data)->info, x1, y1, x2, y2); -} - -static void action_delete_pending_title_success_onresponse(ui_view* view, void* data, bool response) { - prompt_destroy(view); -} - -static void action_delete_pending_title_update(ui_view* view, void* data, float* progress, char* progressText) { - delete_pending_title_data* deleteData = (delete_pending_title_data*) data; - - Result res = AM_DeletePendingTitle(deleteData->info->mediaType, deleteData->info->titleId); - - progressbar_destroy(view); - ui_pop(); - - if(R_FAILED(res)) { - error_display_res(deleteData->info, ui_draw_pending_title_info, res, "Failed to delete pending title."); - } else { - *deleteData->populated = false; - - ui_push(prompt_create("Success", "Pending title deleted.", COLOR_TEXT, false, deleteData->info, NULL, ui_draw_pending_title_info, action_delete_pending_title_success_onresponse)); - } - - free(data); -} - -static void action_delete_pending_title_onresponse(ui_view* view, void* data, bool response) { - prompt_destroy(view); - - if(response) { - ui_push(progressbar_create("Deleting Pending Title", "", data, action_delete_pending_title_update, action_delete_pending_title_draw_top)); - } else { - free(data); - } -} - -void action_delete_pending_title(pending_title_info* info, bool* populated) { - delete_pending_title_data* data = (delete_pending_title_data*) calloc(1, sizeof(delete_pending_title_data)); - data->info = info; - data->populated = populated; - - ui_push(prompt_create("Confirmation", "Delete the selected pending title?", COLOR_TEXT, true, data, NULL, action_delete_pending_title_draw_top, action_delete_pending_title_onresponse)); -} \ No newline at end of file diff --git a/source/ui/section/action/deletesecurevalue.c b/source/ui/section/action/deletesecurevalue.c index 30d8d3a..6d86ebc 100644 --- a/source/ui/section/action/deletesecurevalue.c +++ b/source/ui/section/action/deletesecurevalue.c @@ -24,7 +24,7 @@ static void action_delete_secure_value_update(ui_view* view, void* data, float* ui_pop(); if(R_FAILED(res)) { - error_display_res(info, ui_draw_title_info, res, "Failed to delete secure value."); + error_display_res(NULL, info, ui_draw_title_info, res, "Failed to delete secure value."); } else { ui_push(prompt_create("Success", "Secure value deleted.", COLOR_TEXT, false, info, NULL, ui_draw_title_info, action_delete_secure_value_end_onresponse)); } diff --git a/source/ui/section/action/deletesystemsavedata.c b/source/ui/section/action/deletesystemsavedata.c index 110aabb..40c822d 100644 --- a/source/ui/section/action/deletesystemsavedata.c +++ b/source/ui/section/action/deletesystemsavedata.c @@ -31,7 +31,7 @@ static void action_delete_system_save_data_update(ui_view* view, void* data, flo ui_pop(); if(R_FAILED(res)) { - error_display_res(deleteData->info, ui_draw_system_save_data_info, res, "Failed to delete system save data."); + error_display_res(NULL, deleteData->info, ui_draw_system_save_data_info, res, "Failed to delete system save data."); } else { *deleteData->populated = false; diff --git a/source/ui/section/action/deleteticket.c b/source/ui/section/action/deleteticket.c index 89b7959..d565fe0 100644 --- a/source/ui/section/action/deleteticket.c +++ b/source/ui/section/action/deleteticket.c @@ -30,7 +30,7 @@ static void action_delete_ticket_update(ui_view* view, void* data, float* progre ui_pop(); if(R_FAILED(res)) { - error_display_res(deleteData->info, ui_draw_ticket_info, res, "Failed to delete ticket."); + error_display_res(NULL, deleteData->info, ui_draw_ticket_info, res, "Failed to delete ticket."); } else { *deleteData->populated = false; diff --git a/source/ui/section/action/deletetitle.c b/source/ui/section/action/deletetitle.c index 8d7962e..7a457a1 100644 --- a/source/ui/section/action/deletetitle.c +++ b/source/ui/section/action/deletetitle.c @@ -30,7 +30,7 @@ static void action_delete_title_update(ui_view* view, void* data, float* progres ui_pop(); if(R_FAILED(res)) { - error_display_res(deleteData->info, ui_draw_title_info, res, "Failed to delete title."); + error_display_res(NULL, deleteData->info, ui_draw_title_info, res, "Failed to delete title."); } else { *deleteData->populated = false; diff --git a/source/ui/section/action/exportsecurevalue.c b/source/ui/section/action/exportsecurevalue.c index 8c4d2a8..52e0f70 100644 --- a/source/ui/section/action/exportsecurevalue.c +++ b/source/ui/section/action/exportsecurevalue.c @@ -52,7 +52,7 @@ static void action_export_secure_value_update(ui_view* view, void* data, float* progressbar_destroy(view); ui_pop(); - error_display_res(info, ui_draw_title_info, res, "Failed to export secure value."); + error_display_res(NULL, info, ui_draw_title_info, res, "Failed to export secure value."); return; } diff --git a/source/ui/section/action/importsecurevalue.c b/source/ui/section/action/importsecurevalue.c index d111c5e..3c99de1 100644 --- a/source/ui/section/action/importsecurevalue.c +++ b/source/ui/section/action/importsecurevalue.c @@ -36,7 +36,7 @@ static void action_import_secure_value_update(ui_view* view, void* data, float* progressbar_destroy(view); ui_pop(); - error_display_res(info, ui_draw_title_info, res, "Failed to import secure value."); + error_display_res(NULL, info, ui_draw_title_info, res, "Failed to import secure value."); return; } diff --git a/source/ui/section/action/installcias.c b/source/ui/section/action/installcias.c index 51477ca..aa4bd84 100644 --- a/source/ui/section/action/installcias.c +++ b/source/ui/section/action/installcias.c @@ -16,45 +16,160 @@ typedef struct { file_info* base; bool delete; bool* populated; - Handle currHandle; - bool installStarted; - u64 currProcessed; - u64 currTotal; - u32 processed; - u32 total; char** contents; - install_cia_result installResult; - Handle installCancelEvent; + u64 currTitleId; + + move_data_info installInfo; + Handle cancelEvent; } install_cias_data; -static Result action_install_cias_read(void* data, u32* bytesRead, void* buffer, u32 size) { +Result action_install_cias_is_src_directory(void* data, u32 index, bool* isDirectory) { + *isDirectory = false; + return 0; +} + +Result action_install_cias_make_dst_directory(void* data, u32 index) { + return 0; +} + +Result action_install_cias_open_src(void* data, u32 index, u32* handle) { install_cias_data* installData = (install_cias_data*) data; - u32 read = 0; - Result res = FSFILE_Read(installData->currHandle, &read, installData->currProcessed, buffer, size); + return FSUSER_OpenFile(handle, *installData->base->archive, fsMakePath(PATH_ASCII, installData->contents[index]), FS_OPEN_READ, 0); +} - if(R_SUCCEEDED(res)) { - installData->currProcessed += read; - if(bytesRead != NULL) { - *bytesRead = read; - } +Result action_install_cias_close_src(void* data, u32 index, bool succeeded, u32 handle) { + install_cias_data* installData = (install_cias_data*) data; + + Result res = 0; + if(R_SUCCEEDED(res = FSFILE_Close(handle)) && installData->delete && succeeded) { + FSUSER_DeleteFile(*installData->base->archive, fsMakePath(PATH_ASCII, installData->contents[index])); } return res; } +Result action_install_cias_get_src_size(void* data, u32 handle, u64* size) { + return FSFILE_GetSize(handle, size); +} + +Result action_install_cias_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) { + return FSFILE_Read(handle, bytesRead, offset, buffer, size); +} + +Result action_install_cias_open_dst(void* data, u32 index, void* initialReadBlock, u32* handle) { + install_cias_data* installData = (install_cias_data*) data; + + u8* buffer = (u8*) initialReadBlock; + + u32 headerSize = *(u32*) &buffer[0x00]; + u32 certSize = *(u32*) &buffer[0x08]; + u64 titleId = __builtin_bswap64(*(u64*) &buffer[((headerSize + 0x3F) & ~0x3F) + ((certSize + 0x3F) & ~0x3F) + 0x1DC]); + + FS_MediaType dest = ((titleId >> 32) & 0x8010) != 0 ? MEDIATYPE_NAND : MEDIATYPE_SD; + + u8 n3ds = false; + if(R_SUCCEEDED(APT_CheckNew3DS(&n3ds)) && !n3ds && ((titleId >> 28) & 0xF) == 2) { + return MAKERESULT(RL_PERMANENT, RS_NOTSUPPORTED, RM_APPLICATION, RD_INVALID_COMBINATION); + } + + // Deleting FBI before it reinstalls itself causes issues. + if(((titleId >> 8) & 0xFFFFF) != 0xF8001) { + AM_DeleteTitle(dest, titleId); + AM_DeleteTicket(titleId); + + if(dest == 1) { + AM_QueryAvailableExternalTitleDatabase(NULL); + } + } + + Result res = AM_StartCiaInstall(dest, handle); + if(R_SUCCEEDED(res)) { + installData->currTitleId = titleId; + } + + return res; +} + +Result action_install_cias_close_dst(void* data, u32 index, bool succeeded, u32 handle) { + if(succeeded) { + install_cias_data* installData = (install_cias_data*) data; + + Result res = 0; + if(R_SUCCEEDED(res = AM_FinishCiaInstall(handle))) { + if(installData->currTitleId == 0x0004013800000002 || installData->currTitleId == 0x0004013820000002) { + res = AM_InstallFirm(installData->currTitleId); + } + } + + return res; + } else { + return AM_CancelCIAInstall(handle); + } +} + +Result action_install_cias_write_dst(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size) { + return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0); +} + +bool action_install_cias_result_error(void* data, u32 index, Result res) { + install_cias_data* installData = (install_cias_data*) data; + + if(res == MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_CANCEL_REQUESTED)) { + ui_push(prompt_create("Failure", "Install cancelled.", COLOR_TEXT, false, installData->base, NULL, ui_draw_file_info, NULL)); + return false; + } else { + char* path = installData->contents[index]; + + volatile bool dismissed = false; + if(res == MAKERESULT(RL_PERMANENT, RS_NOTSUPPORTED, RM_APPLICATION, RD_INVALID_COMBINATION)) { + if(strlen(path) > 48) { + error_display(&dismissed, installData->base, ui_draw_file_info, "Failed to install CIA file.\n%.45s...\nAttempted to install N3DS title to O3DS.", path); + } else { + error_display(&dismissed, installData->base, ui_draw_file_info, "Failed to install CIA file.\n%.48s\nAttempted to install N3DS title to O3DS.", path); + } + } else { + if(strlen(path) > 48) { + error_display_res(&dismissed, installData->base, ui_draw_file_info, res, "Failed to install CIA file.\n%.45s...", path); + } else { + error_display_res(&dismissed, installData->base, ui_draw_file_info, res, "Failed to install CIA file.\n%.48s", path); + } + } + + while(!dismissed) { + svcSleepThread(1000000); + } + } + + return index < installData->installInfo.total - 1; +} + +bool action_install_cias_io_error(void* data, u32 index, int err) { + install_cias_data* installData = (install_cias_data*) data; + + char* path = installData->contents[index]; + + volatile bool dismissed = false; + if(strlen(path) > 48) { + error_display_errno(&dismissed, installData->base, ui_draw_file_info, err, "Failed to install CIA file.\n%.45s...", path); + } else { + error_display_errno(&dismissed, installData->base, ui_draw_file_info, err, "Failed to install CIA file.\n%.48s", path); + } + + while(!dismissed) { + svcSleepThread(1000000); + } + + return index < installData->installInfo.total - 1; +} + static void action_install_cias_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { ui_draw_file_info(view, ((install_cias_data*) data)->base, x1, y1, x2, y2); } static void action_install_cias_free_data(install_cias_data* data) { - if(data->currHandle != 0) { - FSFILE_Close(data->currHandle); - data->currHandle = 0; - } - - util_free_contents(data->contents, data->total); + util_free_contents(data->contents, data->installInfo.total); free(data); } @@ -67,147 +182,49 @@ static void action_install_cias_done_onresponse(ui_view* view, void* data, bool static void action_install_cias_update(ui_view* view, void* data, float* progress, char* progressText) { install_cias_data* installData = (install_cias_data*) data; - if(hidKeysDown() & KEY_B) { - svcSignalEvent(installData->installCancelEvent); - while(svcWaitSynchronization(installData->installCancelEvent, 0) == 0) { - svcSleepThread(1000000); + if(installData->installInfo.finished) { + if(installData->delete) { + *installData->populated = false; } - installData->installCancelEvent = 0; - } + ui_pop(); + progressbar_destroy(view); - if(!installData->installStarted || installData->installResult.finished) { - if(installData->currHandle != 0) { - FSFILE_Close(installData->currHandle); - installData->currHandle = 0; - } - - if(installData->installResult.finished) { - char* path = installData->contents[installData->processed]; - - if(installData->installResult.failed) { - if(installData->installResult.cancelled || installData->processed >= installData->total - 1) { - ui_pop(); - progressbar_destroy(view); - } - - if(installData->installResult.cancelled) { - ui_push(prompt_create("Failure", "Install cancelled.", COLOR_TEXT, false, data, NULL, action_install_cias_draw_top, action_install_cias_done_onresponse)); - } else if(installData->installResult.ioerr) { - if(strlen(path) > 48) { - error_display_errno(installData->base, ui_draw_file_info, installData->installResult.ioerrno, "Failed to install CIA file.\n%.45s...", path); - } else { - error_display_errno(installData->base, ui_draw_file_info, installData->installResult.ioerrno, "Failed to install CIA file.\n%.48s", path); - } - } else if(installData->installResult.wrongSystem) { - ui_push(prompt_create("Failure", "Attempted to install to wrong system.", COLOR_TEXT, false, data, NULL, action_install_cias_draw_top, action_install_cias_done_onresponse)); - } else { - if(strlen(path) > 48) { - error_display_res(installData->base, ui_draw_file_info, installData->installResult.result, "Failed to install CIA file.\n%.45s...", path); - } else { - error_display_res(installData->base, ui_draw_file_info, installData->installResult.result, "Failed to install CIA file.\n%.48s", path); - } - } - - if(installData->installResult.cancelled || installData->processed >= installData->total - 1) { - if(!installData->installResult.cancelled && !installData->installResult.wrongSystem) { - action_install_cias_free_data(installData); - } - - return; - } - } else if(installData->delete) { - if(R_SUCCEEDED(FSUSER_DeleteFile(*installData->base->archive, fsMakePath(PATH_ASCII, path))) && installData->base->archive->id == ARCHIVE_USER_SAVEDATA) { - FSUSER_ControlArchive(*installData->base->archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0); - } - - *installData->populated = false; - } - - installData->processed++; - - installData->currProcessed = 0; - installData->currTotal = 0; - - memset(&installData->installResult, 0, sizeof(installData->installResult)); - installData->installCancelEvent = 0; - } - - if(installData->processed >= installData->total) { - ui_pop(); - progressbar_destroy(view); - - ui_push(prompt_create("Success", "Install finished.", COLOR_TEXT, false, data, NULL, action_install_cias_draw_top, action_install_cias_done_onresponse)); - return; + if(installData->installInfo.premature) { + action_install_cias_free_data(installData); } else { - char* path = installData->contents[installData->processed]; - - Result res = 0; - - if(R_SUCCEEDED(res = FSUSER_OpenFile(&installData->currHandle, *installData->base->archive, fsMakePath(PATH_ASCII, path), FS_OPEN_READ, 0))) { - u64 size = 0; - if(R_SUCCEEDED(res = FSFILE_GetSize(installData->currHandle, &size))) { - installData->currTotal = size; - - installData->installCancelEvent = task_install_cia(&installData->installResult, installData->currTotal, installData, action_install_cias_read); - if(installData->installCancelEvent != 0) { - installData->installStarted = true; - } else { - ui_pop(); - progressbar_destroy(view); - - if(strlen(path) > 48) { - error_display_res(installData->base, ui_draw_file_info, res, "Failed to start CIA install.\n%.45s...", path); - } else { - error_display_res(installData->base, ui_draw_file_info, res, "Failed to start CIA install.\n%.48s", path); - } - - action_install_cias_free_data(installData); - - return; - } - } - } - - if(R_FAILED(res)) { - if(installData->currHandle != 0) { - FSFILE_Close(installData->currHandle); - installData->currHandle = 0; - } - - if(installData->processed >= installData->total - 1) { - ui_pop(); - progressbar_destroy(view); - } - - if(strlen(path) > 48) { - error_display_res(installData->base, ui_draw_file_info, res, "Failed to open CIA file.\n%.45s...", path); - } else { - error_display_res(installData->base, ui_draw_file_info, res, "Failed to open CIA file.\n%.48s", path); - } - - if(installData->processed >= installData->total - 1) { - action_install_cias_free_data(installData); - - return; - } - } + ui_push(prompt_create("Success", "Install finished.", COLOR_TEXT, false, data, NULL, action_install_cias_draw_top, action_install_cias_done_onresponse)); } + + return; } - *progress = (float) ((double) installData->currProcessed / (double) installData->currTotal); - snprintf(progressText, PROGRESS_TEXT_MAX, "%lu / %lu\n%.2f MB / %.2f MB", installData->processed, installData->total, installData->currProcessed / 1024.0 / 1024.0, installData->currTotal / 1024.0 / 1024.0); + if(hidKeysDown() & KEY_B) { + svcSignalEvent(installData->cancelEvent); + } + + *progress = installData->installInfo.currTotal != 0 ? (float) ((double) installData->installInfo.currProcessed / (double) installData->installInfo.currTotal) : 0; + snprintf(progressText, PROGRESS_TEXT_MAX, "%lu / %lu\n%.2f MB / %.2f MB", installData->installInfo.processed, installData->installInfo.total, installData->installInfo.currProcessed / 1024.0 / 1024.0, installData->installInfo.currTotal / 1024.0 / 1024.0); } static void action_install_cias_onresponse(ui_view* view, void* data, bool response) { prompt_destroy(view); + install_cias_data* installData = (install_cias_data*) data; + if(response) { - ui_view* progressView = progressbar_create("Installing CIA(s)", "Press B to cancel.", data, action_install_cias_update, action_install_cias_draw_top); - snprintf(progressbar_get_progress_text(progressView), PROGRESS_TEXT_MAX, "0 / %lu", ((install_cias_data*) data)->total); - ui_push(progressView); + installData->cancelEvent = task_move_data(&installData->installInfo); + if(installData->cancelEvent != 0) { + ui_view* progressView = progressbar_create("Installing CIA(s)", "Press B to cancel.", data, action_install_cias_update, action_install_cias_draw_top); + snprintf(progressbar_get_progress_text(progressView), PROGRESS_TEXT_MAX, "0 / %lu", installData->installInfo.total); + ui_push(progressView); + } else { + error_display(NULL, installData->base, ui_draw_file_info, "Failed to initiate CIA installation."); + + action_install_cias_free_data(installData); + } } else { - free(data); + action_install_cias_free_data(installData); } } @@ -216,18 +233,33 @@ static void action_install_cias_internal(file_info* info, bool* populated, bool data->base = info; data->delete = delete; data->populated = populated; - data->currHandle = 0; - data->installStarted = false; - data->currProcessed = 0; - data->currTotal = 0; - data->processed = 0; - memset(&data->installResult, 0, sizeof(data->installResult)); - data->installCancelEvent = 0; + data->currTitleId = 0; + + data->installInfo.data = data; + + data->installInfo.moveEmpty = false; + + data->installInfo.isSrcDirectory = action_install_cias_is_src_directory; + data->installInfo.makeDstDirectory = action_install_cias_make_dst_directory; + + data->installInfo.openSrc = action_install_cias_open_src; + data->installInfo.closeSrc = action_install_cias_close_src; + data->installInfo.getSrcSize = action_install_cias_get_src_size; + data->installInfo.readSrc = action_install_cias_read_src; + + data->installInfo.openDst = action_install_cias_open_dst; + data->installInfo.closeDst = action_install_cias_close_dst; + data->installInfo.writeDst = action_install_cias_write_dst; + + data->installInfo.resultError = action_install_cias_result_error; + data->installInfo.ioError = action_install_cias_io_error; + + data->cancelEvent = 0; Result res = 0; - if(R_FAILED(res = util_populate_contents(&data->contents, &data->total, info->archive, info->path, false, false, ".cia", util_filter_file_extension))) { - error_display_res(info, ui_draw_file_info, res, "Failed to retrieve content list."); + if(R_FAILED(res = util_populate_contents(&data->contents, &data->installInfo.total, info->archive, info->path, false, false, ".cia", util_filter_file_extension))) { + error_display_res(NULL, info, ui_draw_file_info, res, "Failed to retrieve content list."); free(data); return; diff --git a/source/ui/section/action/installtickets.c b/source/ui/section/action/installtickets.c index 6089a21..720a4d0 100644 --- a/source/ui/section/action/installtickets.c +++ b/source/ui/section/action/installtickets.c @@ -14,17 +14,104 @@ typedef struct { file_info* base; - u32 processed; - u32 total; char** contents; + + move_data_info installInfo; + Handle cancelEvent; } install_tickets_data; +Result action_install_tickets_is_src_directory(void* data, u32 index, bool* isDirectory) { + *isDirectory = false; + return 0; +} + +Result action_install_tickets_make_dst_directory(void* data, u32 index) { + return 0; +} + +Result action_install_tickets_open_src(void* data, u32 index, u32* handle) { + install_tickets_data* installData = (install_tickets_data*) data; + + return FSUSER_OpenFile(handle, *installData->base->archive, fsMakePath(PATH_ASCII, installData->contents[index]), FS_OPEN_READ, 0); +} + +Result action_install_tickets_close_src(void* data, u32 index, bool succeeded, u32 handle) { + return FSFILE_Close(handle); +} + +Result action_install_tickets_get_src_size(void* data, u32 handle, u64* size) { + return FSFILE_GetSize(handle, size); +} + +Result action_install_tickets_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) { + return FSFILE_Read(handle, bytesRead, offset, buffer, size); +} + +Result action_install_tickets_open_dst(void* data, u32 index, void* initialReadBlock, u32* handle) { + return AM_InstallTicketBegin(handle); +} + +Result action_install_tickets_close_dst(void* data, u32 index, bool succeeded, u32 handle) { + if(succeeded) { + return AM_InstallTicketFinalize(handle); + } else { + return AM_InstallTicketAbort(handle); + } +} + +Result action_install_tickets_write_dst(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size) { + return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0); +} + +bool action_install_tickets_result_error(void* data, u32 index, Result res) { + install_tickets_data* installData = (install_tickets_data*) data; + + if(res == MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_CANCEL_REQUESTED)) { + ui_push(prompt_create("Failure", "Install cancelled.", COLOR_TEXT, false, installData->base, NULL, ui_draw_file_info, NULL)); + return false; + } else { + char* path = installData->contents[index]; + + volatile bool dismissed = false; + if(strlen(path) > 48) { + error_display_res(&dismissed, installData->base, ui_draw_file_info, res, "Failed to install ticket.\n%.45s...", path); + } else { + error_display_res(&dismissed, installData->base, ui_draw_file_info, res, "Failed to install ticket.\n%.48s", path); + } + + while(!dismissed) { + svcSleepThread(1000000); + } + } + + return index < installData->installInfo.total - 1; +} + +bool action_install_tickets_io_error(void* data, u32 index, int err) { + install_tickets_data* installData = (install_tickets_data*) data; + + char* path = installData->contents[index]; + + volatile bool dismissed = false; + if(strlen(path) > 48) { + error_display_errno(&dismissed, installData->base, ui_draw_file_info, err, "Failed to install ticket.\n%.45s...", path); + } else { + error_display_errno(&dismissed, installData->base, ui_draw_file_info, err, "Failed to install ticket.\n%.48s", path); + } + + while(!dismissed) { + svcSleepThread(1000000); + } + + return index < installData->installInfo.total - 1; +} + static void action_install_tickets_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { ui_draw_file_info(view, ((install_tickets_data*) data)->base, x1, y1, x2, y2); } static void action_install_tickets_free_data(install_tickets_data* data) { - util_free_contents(data->contents, data->total); + util_free_contents(data->contents, data->installInfo.total); free(data); } @@ -37,95 +124,76 @@ static void action_install_tickets_done_onresponse(ui_view* view, void* data, bo static void action_install_tickets_update(ui_view* view, void* data, float* progress, char* progressText) { install_tickets_data* installData = (install_tickets_data*) data; + if(installData->installInfo.finished) { + ui_pop(); + progressbar_destroy(view); + + if(installData->installInfo.premature) { + action_install_tickets_free_data(installData); + } else { + ui_push(prompt_create("Success", "Install finished.", COLOR_TEXT, false, data, NULL, action_install_tickets_draw_top, action_install_tickets_done_onresponse)); + } + + return; + } + if(hidKeysDown() & KEY_B) { - ui_pop(); - progressbar_destroy(view); - - ui_push(prompt_create("Failed", "Install cancelled.", COLOR_TEXT, false, data, NULL, action_install_tickets_draw_top, action_install_tickets_done_onresponse)); - return; + svcSignalEvent(installData->cancelEvent); } - if(installData->processed >= installData->total) { - ui_pop(); - progressbar_destroy(view); - - ui_push(prompt_create("Success", "Install finished.", COLOR_TEXT, false, data, NULL, action_install_tickets_draw_top, action_install_tickets_done_onresponse)); - return; - } else { - char* path = installData->contents[installData->processed]; - - Result res = 0; - - Handle handle = 0; - if(R_SUCCEEDED(res = FSUSER_OpenFile(&handle, *installData->base->archive, fsMakePath(PATH_ASCII, path), FS_OPEN_READ, 0))) { - u64 size = 0; - if(R_SUCCEEDED(res = FSFILE_GetSize(handle, &size))) { - u8* buffer = (u8*) calloc((size_t) size, 1); - if(buffer != NULL) { - u32 bytesRead = 0; - if(R_SUCCEEDED(res = FSFILE_Read(handle, &bytesRead, 0, buffer, (u32) size)) && bytesRead == size) { - Handle ticketHandle; - if(R_SUCCEEDED(res = AM_InstallTicketBegin(&ticketHandle))) { - u32 bytesWritten = 0; - if(R_FAILED(res = FSFILE_Write(ticketHandle, &bytesWritten, 0, buffer, (u32) size, 0)) || bytesWritten != size || R_FAILED(res = AM_InstallTicketFinalize(ticketHandle))) { - AM_InstallTicketAbort(ticketHandle); - } - } - } - - free(buffer); - } - } - - FSFILE_Close(handle); - } - - installData->processed++; - - if(R_FAILED(res)) { - if(installData->processed >= installData->total - 1) { - ui_pop(); - } - - if(strlen(path) > 48) { - error_display_res(installData->base, ui_draw_file_info, res, "Failed to install ticket.\n%.45s...", path); - } else { - error_display_res(installData->base, ui_draw_file_info, res, "Failed to install ticket.\n%.48s", path); - } - - if(installData->processed >= installData->total - 1) { - action_install_tickets_free_data(installData); - progressbar_destroy(view); - - return; - } - } - } - - *progress = (float) ((double) installData->processed / (double) installData->total); - snprintf(progressText, PROGRESS_TEXT_MAX, "%lu / %lu", installData->processed, installData->total); + *progress = installData->installInfo.currTotal != 0 ? (float) ((double) installData->installInfo.currProcessed / (double) installData->installInfo.currTotal) : 0; + snprintf(progressText, PROGRESS_TEXT_MAX, "%lu / %lu\n%.2f MB / %.2f MB", installData->installInfo.processed, installData->installInfo.total, installData->installInfo.currProcessed / 1024.0 / 1024.0, installData->installInfo.currTotal / 1024.0 / 1024.0); } static void action_install_tickets_onresponse(ui_view* view, void* data, bool response) { prompt_destroy(view); + install_tickets_data* installData = (install_tickets_data*) data; + if(response) { - ui_view* progressView = progressbar_create("Installing tickets(s)", "Press B to cancel.", data, action_install_tickets_update, action_install_tickets_draw_top); - snprintf(progressbar_get_progress_text(progressView), PROGRESS_TEXT_MAX, "0 / %lu", ((install_tickets_data*) data)->total); - ui_push(progressView); + installData->cancelEvent = task_move_data(&installData->installInfo); + if(installData->cancelEvent != 0) { + ui_view* progressView = progressbar_create("Installing ticket(s)", "Press B to cancel.", data, action_install_tickets_update, action_install_tickets_draw_top); + snprintf(progressbar_get_progress_text(progressView), PROGRESS_TEXT_MAX, "0 / %lu", installData->installInfo.total); + ui_push(progressView); + } else { + error_display(NULL, installData->base, ui_draw_file_info, "Failed to initiate ticket installation."); + + action_install_tickets_free_data(installData); + } } else { - free(data); + action_install_tickets_free_data(installData); } } void action_install_tickets(file_info* info, bool* populated) { install_tickets_data* data = (install_tickets_data*) calloc(1, sizeof(install_tickets_data)); data->base = info; - data->processed = 0; + + data->installInfo.data = data; + + data->installInfo.moveEmpty = false; + + data->installInfo.isSrcDirectory = action_install_tickets_is_src_directory; + data->installInfo.makeDstDirectory = action_install_tickets_make_dst_directory; + + data->installInfo.openSrc = action_install_tickets_open_src; + data->installInfo.closeSrc = action_install_tickets_close_src; + data->installInfo.getSrcSize = action_install_tickets_get_src_size; + data->installInfo.readSrc = action_install_tickets_read_src; + + data->installInfo.openDst = action_install_tickets_open_dst; + data->installInfo.closeDst = action_install_tickets_close_dst; + data->installInfo.writeDst = action_install_tickets_write_dst; + + data->installInfo.resultError = action_install_tickets_result_error; + data->installInfo.ioError = action_install_tickets_io_error; + + data->cancelEvent = 0; Result res = 0; - if(R_FAILED(res = util_populate_contents(&data->contents, &data->total, info->archive, info->path, false, false, ".tik", util_filter_file_extension))) { - error_display_res(info, ui_draw_file_info, res, "Failed to retrieve content list."); + if(R_FAILED(res = util_populate_contents(&data->contents, &data->installInfo.total, info->archive, info->path, false, false, ".tik", util_filter_file_extension))) { + error_display_res(NULL, info, ui_draw_file_info, res, "Failed to retrieve content list."); free(data); return; diff --git a/source/ui/section/action/launchtitle.c b/source/ui/section/action/launchtitle.c index 96281ac..f633350 100644 --- a/source/ui/section/action/launchtitle.c +++ b/source/ui/section/action/launchtitle.c @@ -33,7 +33,7 @@ static void action_launch_title_update(ui_view* view, void* data, float* progres progressbar_destroy(view); ui_pop(); - error_display_res(info, ui_draw_title_info, res, "Failed to launch title."); + error_display_res(NULL, info, ui_draw_title_info, res, "Failed to launch title."); } } diff --git a/source/ui/section/action/pastefiles.c b/source/ui/section/action/pastefiles.c index 105f498..c14a1d7 100644 --- a/source/ui/section/action/pastefiles.c +++ b/source/ui/section/action/pastefiles.c @@ -11,34 +11,133 @@ #include "../../prompt.h" #include "../../../screen.h" #include "../../../util.h" - -#define PASTE_BUFFER_SIZE (1024 * 512) +#include "../task/task.h" typedef struct { file_info* base; bool* populated; - bool started; - u8* buffer; - Handle currSrc; - Handle currDst; - u64 currProcessed; - u64 currTotal; - u32 processed; - u32 total; char** contents; + + move_data_info pasteInfo; + Handle cancelEvent; } paste_files_data; +static void action_paste_files_get_dst_path(paste_files_data* data, u32 index, char* dstPath) { + char baseDstPath[PATH_MAX]; + if(data->base->isDirectory) { + strncpy(baseDstPath, data->base->path, PATH_MAX); + } else { + util_get_parent_path(baseDstPath, data->base->path, PATH_MAX); + } + + util_get_parent_path(dstPath, clipboard_get_path(), PATH_MAX); + snprintf(dstPath, PATH_MAX, "%s%s", baseDstPath, data->contents[index] + strlen(dstPath)); +} + +Result action_paste_files_is_src_directory(void* data, u32 index, bool* isDirectory) { + paste_files_data* pasteData = (paste_files_data*) data; + + *isDirectory = util_is_dir(pasteData->base->archive, pasteData->contents[index]); + return 0; +} + +Result action_paste_files_make_dst_directory(void* data, u32 index) { + paste_files_data* pasteData = (paste_files_data*) data; + + char dstPath[PATH_MAX]; + action_paste_files_get_dst_path(pasteData, index, dstPath); + + return FSUSER_CreateDirectory(*pasteData->base->archive, fsMakePath(PATH_ASCII, dstPath), 0); +} + +Result action_paste_files_open_src(void* data, u32 index, u32* handle) { + paste_files_data* pasteData = (paste_files_data*) data; + + return FSUSER_OpenFile(handle, *pasteData->base->archive, fsMakePath(PATH_ASCII, pasteData->contents[index]), FS_OPEN_READ, 0); +} + +Result action_paste_files_close_src(void* data, u32 index, bool succeeded, u32 handle) { + return FSFILE_Close(handle); +} + +Result action_paste_files_get_src_size(void* data, u32 handle, u64* size) { + return FSFILE_GetSize(handle, size); +} + +Result action_paste_files_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) { + return FSFILE_Read(handle, bytesRead, offset, buffer, size); +} + +Result action_paste_files_open_dst(void* data, u32 index, void* initialReadBlock, u32* handle) { + paste_files_data* pasteData = (paste_files_data*) data; + + char dstPath[PATH_MAX]; + action_paste_files_get_dst_path(pasteData, index, dstPath); + + return FSUSER_OpenFile(handle, *pasteData->base->archive, fsMakePath(PATH_ASCII, dstPath), FS_OPEN_WRITE | FS_OPEN_CREATE, 0); +} + +Result action_paste_files_close_dst(void* data, u32 index, bool succeeded, u32 handle) { + return FSFILE_Close(handle); +} + +Result action_paste_files_write_dst(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size) { + return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0); +} + +bool action_paste_files_result_error(void* data, u32 index, Result res) { + paste_files_data* pasteData = (paste_files_data*) data; + + if(res == MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_CANCEL_REQUESTED)) { + ui_push(prompt_create("Failure", "Paste cancelled.", COLOR_TEXT, false, pasteData->base, NULL, ui_draw_file_info, NULL)); + return false; + } else { + char* path = pasteData->contents[index]; + + volatile bool dismissed = false; + if(strlen(path) > 48) { + error_display_res(&dismissed, pasteData->base, ui_draw_file_info, res, "Failed to paste content.\n%.45s...", path); + } else { + error_display_res(&dismissed, pasteData->base, ui_draw_file_info, res, "Failed to paste content.\n%.48s", path); + } + + while(!dismissed) { + svcSleepThread(1000000); + } + } + + return index < pasteData->pasteInfo.total - 1; +} + +bool action_paste_files_io_error(void* data, u32 index, int err) { + paste_files_data* pasteData = (paste_files_data*) data; + + char* path = pasteData->contents[index]; + + volatile bool dismissed = false; + if(strlen(path) > 48) { + error_display_errno(&dismissed, pasteData->base, ui_draw_file_info, err, "Failed to paste content.\n%.45s...", path); + } else { + error_display_errno(&dismissed, pasteData->base, ui_draw_file_info, err, "Failed to paste content.\n%.48s", path); + } + + while(!dismissed) { + svcSleepThread(1000000); + } + + return index < pasteData->pasteInfo.total - 1; +} + static void action_paste_files_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { ui_draw_file_info(view, ((paste_files_data*) data)->base, x1, y1, x2, y2); } -static void action_paste_files_failure_onresponse(ui_view* view, void* data, bool response) { +static void action_paste_files_clipboard_empty_onresponse(ui_view* view, void* data, bool response) { prompt_destroy(view); } static void action_paste_files_free_data(paste_files_data* data) { - util_free_contents(data->contents, data->total); - free(data->buffer); + util_free_contents(data->contents, data->pasteInfo.total); free(data); } @@ -51,196 +150,81 @@ static void action_paste_files_done_onresponse(ui_view* view, void* data, bool r static void action_paste_files_update(ui_view* view, void* data, float* progress, char* progressText) { paste_files_data* pasteData = (paste_files_data*) data; - if(hidKeysDown() & KEY_B) { - if(pasteData->currSrc != 0) { - FSFILE_Close(pasteData->currSrc); - pasteData->currSrc = 0; - } + if(pasteData->pasteInfo.finished) { + *pasteData->populated = false; - if(pasteData->currDst != 0) { - FSFILE_Close(pasteData->currDst); - pasteData->currDst = 0; - } - - progressbar_destroy(view); ui_pop(); + progressbar_destroy(view); + + if(pasteData->pasteInfo.premature) { + action_paste_files_free_data(pasteData); + } else { + ui_push(prompt_create("Success", "Contents pasted.", COLOR_TEXT, false, data, NULL, action_paste_files_draw_top, action_paste_files_done_onresponse)); + } - ui_push(prompt_create("Failure", "Paste cancelled.", COLOR_TEXT, false, data, NULL, action_paste_files_draw_top, action_paste_files_done_onresponse)); return; } - if(!pasteData->started || pasteData->currProcessed >= pasteData->currTotal) { - if(pasteData->started) { - if(pasteData->currSrc != 0) { - FSFILE_Close(pasteData->currSrc); - pasteData->currSrc = 0; - } - - if(pasteData->currDst != 0) { - FSFILE_Close(pasteData->currDst); - pasteData->currDst = 0; - } - - if(pasteData->base->archive->id == ARCHIVE_USER_SAVEDATA) { - FSUSER_ControlArchive(*pasteData->base->archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0); - } - - pasteData->currProcessed = 0; - pasteData->currTotal = 0; - - pasteData->processed++; - } - - if(pasteData->processed >= pasteData->total) { - ui_pop(); - progressbar_destroy(view); - - ui_push(prompt_create("Success", "Contents pasted.", COLOR_TEXT, false, data, NULL, action_paste_files_draw_top, action_paste_files_done_onresponse)); - return; - } else { - FS_Archive* srcArchive = clipboard_get_archive(); - char* srcPath = pasteData->contents[pasteData->processed]; - - char baseDstPath[PATH_MAX]; - if(pasteData->base->isDirectory) { - strncpy(baseDstPath, pasteData->base->path, PATH_MAX); - } else { - util_get_parent_path(baseDstPath, pasteData->base->path, PATH_MAX); - } - - FS_Archive* dstArchive = pasteData->base->archive; - char dstPath[PATH_MAX]; - util_get_parent_path(dstPath, clipboard_get_path(), PATH_MAX); - snprintf(dstPath, PATH_MAX, "%s%s", baseDstPath, srcPath + strlen(dstPath)); - - Result res = 0; - - if(util_is_dir(srcArchive, srcPath)) { - res = FSUSER_CreateDirectory(*dstArchive, fsMakePath(PATH_ASCII, dstPath), 0); - } else { - if(R_SUCCEEDED(res = FSUSER_OpenFile(&pasteData->currSrc, *srcArchive, fsMakePath(PATH_ASCII, srcPath), FS_OPEN_READ, 0)) && R_SUCCEEDED(res = FSFILE_GetSize(pasteData->currSrc, &pasteData->currTotal))) { - res = FSUSER_OpenFile(&pasteData->currDst, *dstArchive, fsMakePath(PATH_ASCII, dstPath), FS_OPEN_WRITE | FS_OPEN_CREATE, 0); - } - } - - if(R_FAILED(res)) { - if(pasteData->currSrc != 0) { - FSFILE_Close(pasteData->currSrc); - pasteData->currSrc = 0; - } - - if(pasteData->currDst != 0) { - FSFILE_Close(pasteData->currDst); - pasteData->currDst = 0; - } - - if(pasteData->processed >= pasteData->total - 1) { - ui_pop(); - progressbar_destroy(view); - } - - if(strlen(srcPath) > 48) { - error_display_res(pasteData->base, ui_draw_file_info, res, "Failed to paste content.\n%.45s...", srcPath); - } else { - error_display_res(pasteData->base, ui_draw_file_info, res, "Failed to paste content.\n%.48s", srcPath); - } - - if(pasteData->processed >= pasteData->total - 1) { - action_paste_files_free_data(pasteData); - - return; - } - } else { - *pasteData->populated = false; - - pasteData->started = true; - } - } - } else if(pasteData->currSrc != 0 && pasteData->currDst != 0) { - Result res = 0; - - u32 size = PASTE_BUFFER_SIZE; - if((u64) size > pasteData->currTotal - pasteData->currProcessed) { - size = (u32) (pasteData->currTotal - pasteData->currProcessed); - } - - u32 bytesRead = 0; - if(R_SUCCEEDED(res = FSFILE_Read(pasteData->currSrc, &bytesRead, pasteData->currProcessed, pasteData->buffer, size)) && bytesRead > 0) { - u32 bytesWritten = 0; - if(R_SUCCEEDED(res = FSFILE_Write(pasteData->currDst, &bytesWritten, pasteData->currProcessed, pasteData->buffer, bytesRead, 0))) { - pasteData->currProcessed += bytesWritten; - } - } - - if(R_FAILED(res)) { - if(pasteData->currSrc != 0) { - FSFILE_Close(pasteData->currSrc); - pasteData->currSrc = 0; - } - - if(pasteData->currDst != 0) { - FSFILE_Close(pasteData->currDst); - pasteData->currDst = 0; - } - - if(pasteData->processed >= pasteData->total - 1) { - ui_pop(); - progressbar_destroy(view); - } - - char* srcPath = pasteData->contents[pasteData->processed]; - if(strlen(srcPath) > 48) { - error_display_res(pasteData->base, ui_draw_file_info, res, "Failed to paste content.\n%.45s...", srcPath); - } else { - error_display_res(pasteData->base, ui_draw_file_info, res, "Failed to paste content.\n%.48s", srcPath); - } - - pasteData->currProcessed = pasteData->currTotal; - - if(pasteData->processed >= pasteData->total - 1) { - action_paste_files_free_data(pasteData); - - return; - } - } + if(hidKeysDown() & KEY_B) { + svcSignalEvent(pasteData->cancelEvent); } - *progress = pasteData->currTotal != 0 ? (float) ((double) pasteData->currProcessed / (double) pasteData->currTotal) : 1; - snprintf(progressText, PROGRESS_TEXT_MAX, "%lu / %lu\n%.2f MB / %.2f MB", pasteData->processed, pasteData->total, pasteData->currProcessed / 1024.0 / 1024.0, pasteData->currTotal / 1024.0 / 1024.0); + *progress = pasteData->pasteInfo.currTotal != 0 ? (float) ((double) pasteData->pasteInfo.currProcessed / (double) pasteData->pasteInfo.currTotal) : 0; + snprintf(progressText, PROGRESS_TEXT_MAX, "%lu / %lu\n%.2f MB / %.2f MB", pasteData->pasteInfo.processed, pasteData->pasteInfo.total, pasteData->pasteInfo.currProcessed / 1024.0 / 1024.0, pasteData->pasteInfo.currTotal / 1024.0 / 1024.0); } static void action_paste_files_onresponse(ui_view* view, void* data, bool response) { prompt_destroy(view); + paste_files_data* pasteData = (paste_files_data*) data; if(response) { - ui_view* progressView = progressbar_create("Pasting Contents", "Press B to cancel.", data, action_paste_files_update, action_paste_files_draw_top); - snprintf(progressbar_get_progress_text(progressView), PROGRESS_TEXT_MAX, "0 / %lu", ((paste_files_data*) data)->total); - ui_push(progressView); + pasteData->cancelEvent = task_move_data(&pasteData->pasteInfo); + if(pasteData->cancelEvent != 0) { + ui_view* progressView = progressbar_create("Pasting Contents", "Press B to cancel.", data, action_paste_files_update, action_paste_files_draw_top); + snprintf(progressbar_get_progress_text(progressView), PROGRESS_TEXT_MAX, "0 / %lu", ((paste_files_data*) data)->pasteInfo.total); + ui_push(progressView); + } else { + error_display(NULL, pasteData->base, ui_draw_file_info, "Failed to initiate paste operation."); + } } else { - free(data); + action_paste_files_free_data(pasteData); } } void action_paste_contents(file_info* info, bool* populated) { if(!clipboard_has_contents()) { - ui_push(prompt_create("Failure", "Clipboard empty.", COLOR_TEXT, false, info, NULL, ui_draw_file_info, action_paste_files_failure_onresponse)); + ui_push(prompt_create("Failure", "Clipboard empty.", COLOR_TEXT, false, info, NULL, ui_draw_file_info, action_paste_files_clipboard_empty_onresponse)); return; } paste_files_data* data = (paste_files_data*) calloc(1, sizeof(paste_files_data)); data->base = info; data->populated = populated; - data->started = false; - data->buffer = (u8*) calloc(1, PASTE_BUFFER_SIZE); - data->currSrc = 0; - data->currDst = 0; - data->currProcessed = 0; - data->currTotal = 0; - data->processed = 0; + + data->pasteInfo.data = data; + + data->pasteInfo.moveEmpty = true; + + data->pasteInfo.isSrcDirectory = action_paste_files_is_src_directory; + data->pasteInfo.makeDstDirectory = action_paste_files_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.openDst = action_paste_files_open_dst; + data->pasteInfo.closeDst = action_paste_files_close_dst; + data->pasteInfo.writeDst = action_paste_files_write_dst; + + data->pasteInfo.resultError = action_paste_files_result_error; + data->pasteInfo.ioError = action_paste_files_io_error; + + data->cancelEvent = 0; Result res = 0; - if(R_FAILED(res = util_populate_contents(&data->contents, &data->total, clipboard_get_archive(), clipboard_get_path(), true, true, NULL, NULL))) { - error_display_res(info, ui_draw_file_info, res, "Failed to retrieve content list."); + if(R_FAILED(res = util_populate_contents(&data->contents, &data->pasteInfo.total, clipboard_get_archive(), clipboard_get_path(), true, true, NULL, NULL))) { + error_display_res(NULL, info, ui_draw_file_info, res, "Failed to retrieve content list."); free(data); return; diff --git a/source/ui/section/dumpnand.c b/source/ui/section/dumpnand.c index d3611a0..0379b30 100644 --- a/source/ui/section/dumpnand.c +++ b/source/ui/section/dumpnand.c @@ -3,20 +3,71 @@ #include <3ds.h> +#include "task/task.h" #include "../error.h" #include "../progressbar.h" #include "../prompt.h" #include "../../screen.h" typedef struct { - Handle in; - Handle out; - u64 offset; - u64 size; - - u8 buffer[1024 * 1024]; + move_data_info dumpInfo; + Handle cancelEvent; } dump_nand_data; +Result dumpnand_is_src_directory(void* data, u32 index, bool* isDirectory) { + *isDirectory = false; + return 0; +} + +Result dumpnand_make_dst_directory(void* data, u32 index) { + return 0; +} + +Result dumpnand_open_src(void* data, u32 index, u32* handle) { + FS_Archive wnandArchive = {ARCHIVE_NAND_W_FS, fsMakePath(PATH_EMPTY, "")}; + return FSUSER_OpenFileDirectly(handle, wnandArchive, fsMakePath(PATH_UTF16, u"/"), FS_OPEN_READ, 0); +} + +Result dumpnand_close_src(void* data, u32 index, bool succeeded, u32 handle) { + return FSFILE_Close(handle); +} + +Result dumpnand_get_src_size(void* data, u32 handle, u64* size) { + return FSFILE_GetSize(handle, size); +} + +Result dumpnand_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) { + return FSFILE_Read(handle, bytesRead, offset, buffer, size); +} + +Result dumpnand_open_dst(void* data, u32 index, void* initialReadBlock, u32* handle) { + FS_Archive sdmcArchive = {ARCHIVE_SDMC, {PATH_BINARY, 0, (u8*) ""}}; + return FSUSER_OpenFileDirectly(handle, sdmcArchive, fsMakePath(PATH_ASCII, "/NAND.bin"), FS_OPEN_WRITE | FS_OPEN_CREATE, 0); +} + +Result dumpnand_close_dst(void* data, u32 index, bool succeeded, u32 handle) { + return FSFILE_Close(handle); +} + +Result dumpnand_write_dst(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size) { + return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0); +} + +bool dumpnand_result_error(void* data, u32 index, Result res) { + if(res == MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_CANCEL_REQUESTED)) { + ui_push(prompt_create("Failure", "Dump cancelled.", COLOR_TEXT, false, NULL, NULL, NULL, NULL)); + } else { + error_display_res(NULL, NULL, NULL, res, "Failed to dump NAND."); + } + + return false; +} + +bool dumpnand_io_error(void* data, u32 index, int err) { + error_display_errno(NULL, NULL, NULL, err, "Failed to dump NAND."); + return false; +} + static void dumpnand_done_onresponse(ui_view* view, void* data, bool response) { prompt_destroy(view); } @@ -24,90 +75,25 @@ static void dumpnand_done_onresponse(ui_view* view, void* data, bool response) { static void dumpnand_update(ui_view* view, void* data, float* progress, char* progressText) { dump_nand_data* dumpData = (dump_nand_data*) data; - if(hidKeysDown() & KEY_B) { - if(dumpData->in != 0) { - FSFILE_Close(dumpData->in); - dumpData->in = 0; - } - - if(dumpData->out != 0) { - FSFILE_Close(dumpData->out); - dumpData->out = 0; - } - - progressbar_destroy(view); + if(dumpData->dumpInfo.finished) { ui_pop(); + progressbar_destroy(view); - ui_push(prompt_create("Failure", "Dump cancelled.", COLOR_TEXT, false, data, NULL, NULL, dumpnand_done_onresponse)); + if(!dumpData->dumpInfo.premature) { + ui_push(prompt_create("Success", "NAND dumped.", COLOR_TEXT, false, NULL, NULL, NULL, dumpnand_done_onresponse)); + } - free(data); + free(dumpData); return; } - Result res = 0; - - u32 bytesRead = 0; - u32 bytesWritten = 0; - if(R_SUCCEEDED(res = FSFILE_Read(dumpData->in, &bytesRead, dumpData->offset, dumpData->buffer, sizeof(dumpData->buffer))) && R_SUCCEEDED(res = FSFILE_Write(dumpData->out, &bytesWritten, dumpData->offset, dumpData->buffer, bytesRead, 0)) && bytesRead == bytesWritten) { - dumpData->offset += bytesRead; + if(hidKeysDown() & KEY_B) { + svcSignalEvent(dumpData->cancelEvent); } - if(R_FAILED(res)) { - if(dumpData->in != 0) { - FSFILE_Close(dumpData->in); - dumpData->in = 0; - } - - if(dumpData->out != 0) { - FSFILE_Close(dumpData->out); - dumpData->out = 0; - } - - progressbar_destroy(view); - ui_pop(); - - error_display_res(NULL, NULL, res, "Failed to dump NAND."); - - free(data); - } else if(bytesRead != bytesWritten) { - if(dumpData->in != 0) { - FSFILE_Close(dumpData->in); - dumpData->in = 0; - } - - if(dumpData->out != 0) { - FSFILE_Close(dumpData->out); - dumpData->out = 0; - } - - progressbar_destroy(view); - ui_pop(); - - error_display(NULL, NULL, "Failed to dump NAND: Read/Write size mismatch."); - - free(data); - } else if(dumpData->offset >= dumpData->size) { - if(dumpData->in != 0) { - FSFILE_Close(dumpData->in); - dumpData->in = 0; - } - - if(dumpData->out != 0) { - FSFILE_Close(dumpData->out); - dumpData->out = 0; - } - - progressbar_destroy(view); - ui_pop(); - - ui_push(prompt_create("Success", "NAND dumped.", COLOR_TEXT, false, NULL, NULL, NULL, dumpnand_done_onresponse)); - - free(data); - } else { - *progress = (float) dumpData->offset / (float) dumpData->size; - snprintf(progressText, PROGRESS_TEXT_MAX, "%.2f MB / %.2f MB", dumpData->offset / 1024.0f / 1024.0f, dumpData->size / 1024.0f / 1024.0f); - } + *progress = dumpData->dumpInfo.currTotal != 0 ? (float) ((double) dumpData->dumpInfo.currProcessed / (double) dumpData->dumpInfo.currTotal) : 0; + snprintf(progressText, PROGRESS_TEXT_MAX, "%.2f MB / %.2f MB", dumpData->dumpInfo.currProcessed / 1024.0f / 1024.0f, dumpData->dumpInfo.currTotal / 1024.0f / 1024.0f); } static void dumpnand_onresponse(ui_view* view, void* data, bool response) { @@ -116,32 +102,11 @@ static void dumpnand_onresponse(ui_view* view, void* data, bool response) { if(response) { dump_nand_data* dumpData = (dump_nand_data*) data; - Result res = 0; - - FS_Archive wnandArchive = {ARCHIVE_NAND_W_FS, fsMakePath(PATH_EMPTY, "")}; - if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&dumpData->in, wnandArchive, fsMakePath(PATH_UTF16, u"/"), FS_OPEN_READ, 0)) && R_SUCCEEDED(res = FSFILE_GetSize(dumpData->in, &dumpData->size))) { - FS_Archive sdmcArchive = {ARCHIVE_SDMC, {PATH_BINARY, 0, (u8*) ""}}; - if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&dumpData->out, sdmcArchive, fsMakePath(PATH_ASCII, "/NAND.bin"), FS_OPEN_CREATE | FS_OPEN_WRITE, 0))) { - dumpData->offset = 0; - - ui_push(progressbar_create("Dumping NAND", "Press B to cancel.", data, dumpnand_update, NULL)); - } - } - - if(R_FAILED(res)) { - if(dumpData->in != 0) { - FSFILE_Close(dumpData->in); - dumpData->in = 0; - } - - if(dumpData->out != 0) { - FSFILE_Close(dumpData->out); - dumpData->out = 0; - } - - error_display_res(NULL, NULL, res, "Failed to prepare for NAND dump."); - - free(data); + dumpData->cancelEvent = task_move_data(&dumpData->dumpInfo); + if(dumpData->cancelEvent != 0) { + ui_push(progressbar_create("Dumping NAND", "Press B to cancel.", data, dumpnand_update, NULL)); + } else { + error_display(NULL, NULL, NULL, "Failed to initiate NAND dump."); } } else { free(data); @@ -150,10 +115,29 @@ static void dumpnand_onresponse(ui_view* view, void* data, bool response) { void dump_nand() { dump_nand_data* data = (dump_nand_data*) calloc(1, sizeof(dump_nand_data)); - data->in = 0; - data->out = 0; - data->offset = 0; - data->size = 0; + + data->dumpInfo.data = data; + + data->dumpInfo.moveEmpty = true; + + data->dumpInfo.total = 1; + + data->dumpInfo.isSrcDirectory = dumpnand_is_src_directory; + data->dumpInfo.makeDstDirectory = dumpnand_make_dst_directory; + + data->dumpInfo.openSrc = dumpnand_open_src; + data->dumpInfo.closeSrc = dumpnand_close_src; + data->dumpInfo.getSrcSize = dumpnand_get_src_size; + data->dumpInfo.readSrc = dumpnand_read_src; + + data->dumpInfo.openDst = dumpnand_open_dst; + data->dumpInfo.closeDst = dumpnand_close_dst; + data->dumpInfo.writeDst = dumpnand_write_dst; + + data->dumpInfo.resultError = dumpnand_result_error; + data->dumpInfo.ioError = dumpnand_io_error; + + data->cancelEvent = 0; ui_push(prompt_create("Confirmation", "Dump raw NAND image to the SD card?", COLOR_TEXT, true, data, NULL, NULL, dumpnand_onresponse)); } \ No newline at end of file diff --git a/source/ui/section/files.c b/source/ui/section/files.c index ff5c97c..81f8a80 100644 --- a/source/ui/section/files.c +++ b/source/ui/section/files.c @@ -306,7 +306,7 @@ void files_open(FS_Archive archive) { Result res = 0; if(R_FAILED(res = FSUSER_OpenArchive(&data->archive))) { - error_display_res(NULL, NULL, res, "Failed to open file listing archive."); + error_display_res(NULL, NULL, NULL, res, "Failed to open file listing archive."); if(data->archivePath != NULL) { free(data->archivePath); diff --git a/source/ui/section/networkinstall.c b/source/ui/section/networkinstall.c index 9c5a449..aef0ed0 100644 --- a/source/ui/section/networkinstall.c +++ b/source/ui/section/networkinstall.c @@ -20,14 +20,11 @@ typedef struct { int serverSocket; int clientSocket; - bool installStarted; - u64 currProcessed; - u64 currTotal; - u32 processed; - u32 total; - install_cia_result installResult; - Handle installCancelEvent; + u64 currTitleId; + + move_data_info installInfo; + Handle cancelEvent; } network_install_data; static int recvwait(int sockfd, void* buf, size_t len, int flags) { @@ -51,27 +48,147 @@ static int sendwait(int sockfd, void* buf, size_t len, int flags) { errno = 0; } - return ret < 0 ? ret : (int) read; + return ret < 0 ? ret : (int) written; } -static Result networkinstall_read(void* data, u32* bytesRead, void* buffer, u32 size) { +Result networkinstall_is_src_directory(void* data, u32 index, bool* isDirectory) { + *isDirectory = false; + return 0; +} + +Result networkinstall_make_dst_directory(void* data, u32 index) { + return 0; +} + +Result networkinstall_open_src(void* data, u32 index, u32* handle) { network_install_data* networkInstallData = (network_install_data*) data; - Result res = 0; + u8 ack = 1; + if(sendwait(networkInstallData->clientSocket, &ack, sizeof(ack), 0) < 0) { + return -1; + } + + return 0; +} + +Result networkinstall_close_src(void* data, u32 index, bool succeeded, u32 handle) { + return 0; +} + +Result networkinstall_get_src_size(void* data, u32 handle, u64* size) { + network_install_data* networkInstallData = (network_install_data*) data; + + u64 netSize = 0; + if(recvwait(networkInstallData->clientSocket, &netSize, sizeof(netSize), 0) < 0) { + return -1; + } + + *size = __builtin_bswap64(netSize); + return 0; +} + +Result networkinstall_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) { + network_install_data* networkInstallData = (network_install_data*) data; int ret = 0; - if((ret = recvwait(networkInstallData->clientSocket, buffer, size, 0)) >= 0) { - networkInstallData->currProcessed += ret; - if(bytesRead != NULL) { - *bytesRead = (u32) ret; + if((ret = recvwait(networkInstallData->clientSocket, buffer, size, 0)) < 0) { + return -1; + } + + *bytesRead = (u32) ret; + return 0; +} + +Result networkinstall_open_dst(void* data, u32 index, void* initialReadBlock, u32* handle) { + network_install_data* networkInstallData = (network_install_data*) data; + + u8* buffer = (u8*) initialReadBlock; + + u32 headerSize = *(u32*) &buffer[0x00]; + u32 certSize = *(u32*) &buffer[0x08]; + u64 titleId = __builtin_bswap64(*(u64*) &buffer[((headerSize + 0x3F) & ~0x3F) + ((certSize + 0x3F) & ~0x3F) + 0x1DC]); + + FS_MediaType dest = ((titleId >> 32) & 0x8010) != 0 ? MEDIATYPE_NAND : MEDIATYPE_SD; + + u8 n3ds = false; + if(R_SUCCEEDED(APT_CheckNew3DS(&n3ds)) && !n3ds && ((titleId >> 28) & 0xF) == 2) { + return MAKERESULT(RL_PERMANENT, RS_NOTSUPPORTED, RM_APPLICATION, RD_INVALID_COMBINATION); + } + + // Deleting FBI before it reinstalls itself causes issues. + if(((titleId >> 8) & 0xFFFFF) != 0xF8001) { + AM_DeleteTitle(dest, titleId); + AM_DeleteTicket(titleId); + + if(dest == 1) { + AM_QueryAvailableExternalTitleDatabase(NULL); } - } else { - res = -1; + } + + Result res = AM_StartCiaInstall(dest, handle); + if(R_SUCCEEDED(res)) { + networkInstallData->currTitleId = titleId; } return res; } +Result networkinstall_close_dst(void* data, u32 index, bool succeeded, u32 handle) { + if(succeeded) { + network_install_data* networkInstallData = (network_install_data*) data; + + Result res = 0; + if(R_SUCCEEDED(res = AM_FinishCiaInstall(handle))) { + if(networkInstallData->currTitleId == 0x0004013800000002 || networkInstallData->currTitleId == 0x0004013820000002) { + res = AM_InstallFirm(networkInstallData->currTitleId); + } + } + + return res; + } else { + return AM_CancelCIAInstall(handle); + } +} + +Result networkinstall_write_dst(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size) { + return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0); +} + +bool networkinstall_result_error(void* data, u32 index, Result res) { + network_install_data* networkInstallData = (network_install_data*) data; + + if(res == MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_CANCEL_REQUESTED)) { + ui_push(prompt_create("Failure", "Install cancelled.", COLOR_TEXT, false, NULL, NULL, NULL, NULL)); + return false; + } else { + volatile bool dismissed = false; + if(res == MAKERESULT(RL_PERMANENT, RS_NOTSUPPORTED, RM_APPLICATION, RD_INVALID_COMBINATION)) { + error_display(&dismissed, NULL, NULL, "Failed to install CIA file.\nAttempted to install N3DS title to O3DS."); + } else { + error_display_res(&dismissed, NULL, NULL, res, "Failed to install CIA file."); + } + + while(!dismissed) { + svcSleepThread(1000000); + } + } + + return index < networkInstallData->installInfo.total - 1; +} + +bool networkinstall_io_error(void* data, u32 index, int err) { + network_install_data* networkInstallData = (network_install_data*) data; + + volatile bool dismissed = false; + error_display_errno(&dismissed, NULL, NULL, err, "Failed to install CIA file."); + + while(!dismissed) { + svcSleepThread(1000000); + } + + return index < networkInstallData->installInfo.total - 1; +} + static void networkinstall_done_onresponse(ui_view* view, void* data, bool response) { prompt_destroy(view); } @@ -81,119 +198,50 @@ static void networkinstall_close_client(network_install_data* data) { sendwait(data->clientSocket, &ack, sizeof(ack), 0); close(data->clientSocket); - data->installStarted = false; - data->processed = 0; - data->total = 0; - data->currProcessed = 0; - data->currTotal = 0; - - data->installCancelEvent = 0; - memset(&data->installResult, 0, sizeof(data->installResult)); + data->currTitleId = 0; + data->cancelEvent = 0; } static void networkinstall_install_update(ui_view* view, void* data, float* progress, char* progressText) { network_install_data* networkInstallData = (network_install_data*) data; - if(hidKeysDown() & KEY_B) { - svcSignalEvent(networkInstallData->installCancelEvent); - while(svcWaitSynchronization(networkInstallData->installCancelEvent, 0) == 0) { - svcSleepThread(1000000); - } + if(networkInstallData->installInfo.finished) { + networkinstall_close_client(networkInstallData); - networkInstallData->installCancelEvent = 0; - } - - if(!networkInstallData->installStarted || networkInstallData->installResult.finished) { - if(networkInstallData->installResult.finished) { - if(networkInstallData->installResult.failed) { - ui_pop(); - progressbar_destroy(view); - - if(networkInstallData->installResult.cancelled) { - ui_push(prompt_create("Failure", "Install cancelled.", COLOR_TEXT, false, data, NULL, NULL, networkinstall_done_onresponse)); - } else if(networkInstallData->installResult.ioerr) { - error_display_errno(NULL, NULL, networkInstallData->installResult.ioerrno, "Failed to install CIA file."); - } else if(networkInstallData->installResult.wrongSystem) { - ui_push(prompt_create("Failure", "Attempted to install to wrong system.", COLOR_TEXT, false, data, NULL, NULL, networkinstall_done_onresponse)); - } else { - error_display_res(NULL, NULL, networkInstallData->installResult.result, "Failed to install CIA file."); - } - - networkinstall_close_client(networkInstallData); - - return; - } - - networkInstallData->processed++; - } - - networkInstallData->installStarted = true; - - if(networkInstallData->processed >= networkInstallData->total) { - networkinstall_close_client(networkInstallData); - - ui_pop(); - progressbar_destroy(view); + ui_pop(); + progressbar_destroy(view); + if(!networkInstallData->installInfo.premature) { ui_push(prompt_create("Success", "Install finished.", COLOR_TEXT, false, data, NULL, NULL, networkinstall_done_onresponse)); - return; - } else { - networkInstallData->currProcessed = 0; - networkInstallData->currTotal = 0; - - u8 ack = 1; - if(sendwait(networkInstallData->clientSocket, &ack, sizeof(ack), 0) < 0) { - networkinstall_close_client(networkInstallData); - - ui_pop(); - progressbar_destroy(view); - - error_display_errno(NULL, NULL, errno, "Failed to write CIA accept notification."); - return; - } - - if(recvwait(networkInstallData->clientSocket, &networkInstallData->currTotal, sizeof(networkInstallData->currTotal), 0) < 0) { - networkinstall_close_client(networkInstallData); - - ui_pop(); - progressbar_destroy(view); - - error_display_errno(NULL, NULL, errno, "Failed to read file size."); - return; - } - - networkInstallData->currTotal = __builtin_bswap64(networkInstallData->currTotal); - - if(networkInstallData->currTotal != 0) { - networkInstallData->installCancelEvent = task_install_cia(&networkInstallData->installResult, networkInstallData->currTotal, networkInstallData, networkinstall_read); - if(networkInstallData->installCancelEvent == 0) { - networkinstall_close_client(networkInstallData); - - ui_pop(); - progressbar_destroy(view); - - error_display(NULL, NULL, "Failed to start CIA install."); - return; - } - } else { - networkInstallData->installResult.finished = true; - } } + + return; } - *progress = networkInstallData->currTotal != 0 ? (float) ((double) networkInstallData->currProcessed / (double) networkInstallData->currTotal) : 1; - snprintf(progressText, PROGRESS_TEXT_MAX, "%lu / %lu\n%.2f MB / %.2f MB", networkInstallData->processed, networkInstallData->total, networkInstallData->currProcessed / 1024.0 / 1024.0, networkInstallData->currTotal / 1024.0 / 1024.0); + if(hidKeysDown() & KEY_B) { + svcSignalEvent(networkInstallData->cancelEvent); + } + + *progress = networkInstallData->installInfo.currTotal != 0 ? (float) ((double) networkInstallData->installInfo.currProcessed / (double) networkInstallData->installInfo.currTotal) : 0; + snprintf(progressText, PROGRESS_TEXT_MAX, "%lu / %lu\n%.2f MB / %.2f MB", networkInstallData->installInfo.processed, networkInstallData->installInfo.total, networkInstallData->installInfo.currProcessed / 1024.0 / 1024.0, networkInstallData->installInfo.currTotal / 1024.0 / 1024.0); } static void networkinstall_confirm_onresponse(ui_view* view, void* data, bool response) { - network_install_data* networkInstallData = (network_install_data*) data; - prompt_destroy(view); + network_install_data* networkInstallData = (network_install_data*) data; + if(response) { - ui_view* progressView = progressbar_create("Installing CIA(s)", "Press B to cancel.", data, networkinstall_install_update, NULL); - snprintf(progressbar_get_progress_text(progressView), PROGRESS_TEXT_MAX, "0 / %lu", networkInstallData->total); - ui_push(progressView); + networkInstallData->cancelEvent = task_move_data(&networkInstallData->installInfo); + if(networkInstallData->cancelEvent != 0) { + ui_view* progressView = progressbar_create("Installing CIA(s)", "Press B to cancel.", data, networkinstall_install_update, NULL); + snprintf(progressbar_get_progress_text(progressView), PROGRESS_TEXT_MAX, "0 / %lu", networkInstallData->installInfo.total); + ui_push(progressView); + } else { + error_display(NULL, NULL, NULL, "Failed to initiate CIA installation."); + + networkinstall_close_client(networkInstallData); + } } else { networkinstall_close_client(networkInstallData); } @@ -220,20 +268,19 @@ static void networkinstall_wait_update(ui_view* view, void* data, float bx1, flo int bufSize = 1024 * 32; setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &bufSize, sizeof(bufSize)); - if(recvwait(sock, &networkInstallData->total, sizeof(networkInstallData->total), 0) < 0) { + if(recvwait(sock, &networkInstallData->installInfo.total, sizeof(networkInstallData->installInfo.total), 0) < 0) { close(sock); - error_display_errno(NULL, NULL, errno, "Failed to read file count."); + error_display_errno(NULL, NULL, NULL, errno, "Failed to read file count."); return; } - networkInstallData->processed = 0; - networkInstallData->total = ntohl(networkInstallData->total); + networkInstallData->installInfo.total = ntohl(networkInstallData->installInfo.total); networkInstallData->clientSocket = sock; ui_push(prompt_create("Confirmation", "Install received CIA(s)?", COLOR_TEXT, true, data, NULL, NULL, networkinstall_confirm_onresponse)); } else if(errno != EAGAIN) { - error_display_errno(NULL, NULL, errno, "Failed to open socket."); + error_display_errno(NULL, NULL, NULL, errno, "Failed to open socket."); } } @@ -255,7 +302,7 @@ static void networkinstall_wait_draw_bottom(ui_view* view, void* data, float x1, void networkinstall_open() { int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if(sock < 0) { - error_display_errno(NULL, NULL, errno, "Failed to open server socket."); + error_display_errno(NULL, NULL, NULL, errno, "Failed to open server socket."); return; } @@ -267,7 +314,7 @@ void networkinstall_open() { if(bind(sock, (struct sockaddr*) &server, sizeof(server)) < 0) { close(sock); - error_display_errno(NULL, NULL, errno, "Failed to bind server socket."); + error_display_errno(NULL, NULL, NULL, errno, "Failed to bind server socket."); return; } @@ -276,21 +323,36 @@ void networkinstall_open() { if(listen(sock, 5) < 0) { close(sock); - error_display_errno(NULL, NULL, errno, "Failed to listen on server socket."); + error_display_errno(NULL, NULL, NULL, errno, "Failed to listen on server socket."); return; } network_install_data* data = (network_install_data*) calloc(1, sizeof(network_install_data)); data->serverSocket = sock; data->clientSocket = 0; - data->installStarted = false; - data->currProcessed = 0; - data->currTotal = 0; - data->processed = 0; - data->total = 0; - memset(&data->installResult, 0, sizeof(data->installResult)); - data->installCancelEvent = 0; + data->currTitleId = 0; + + data->installInfo.data = data; + + data->installInfo.moveEmpty = false; + + data->installInfo.isSrcDirectory = networkinstall_is_src_directory; + data->installInfo.makeDstDirectory = networkinstall_make_dst_directory; + + data->installInfo.openSrc = networkinstall_open_src; + data->installInfo.closeSrc = networkinstall_close_src; + data->installInfo.getSrcSize = networkinstall_get_src_size; + data->installInfo.readSrc = networkinstall_read_src; + + data->installInfo.openDst = networkinstall_open_dst; + data->installInfo.closeDst = networkinstall_close_dst; + data->installInfo.writeDst = networkinstall_write_dst; + + data->installInfo.resultError = networkinstall_result_error; + data->installInfo.ioError = networkinstall_io_error; + + data->cancelEvent = 0; ui_view* view = (ui_view*) calloc(1, sizeof(ui_view)); view->name = "Network Install"; diff --git a/source/ui/section/task/installcia.c b/source/ui/section/task/installcia.c deleted file mode 100644 index b3771b7..0000000 --- a/source/ui/section/task/installcia.c +++ /dev/null @@ -1,153 +0,0 @@ -#include -#include - -#include <3ds.h> -#include - -#include "../../list.h" -#include "../../error.h" -#include "task.h" - -typedef struct { - install_cia_result* result; - u64 size; - void* data; - Result (*read)(void* data, u32* bytesRead, void* buffer, u32 size); - - Handle cancelEvent; -} install_cia_data; - -static u32 align(u32 offset, u32 alignment) { - return (offset + (alignment - 1)) & ~(alignment - 1); -} - -static void task_install_cia_thread(void* arg) { - install_cia_data* data = (install_cia_data*) arg; - - memset(data->result, 0, sizeof(install_cia_result)); - - u32 bufferSize = 1024 * 256; - u8* buffer = (u8*) calloc(1, bufferSize); - if(buffer != NULL) { - bool firstBlock = true; - u64 titleId = 0; - - Handle ciaHandle = 0; - - u32 bytesRead = 0; - u32 bytesWritten = 0; - u64 offset = 0; - while(offset < data->size) { - if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { - data->result->cancelled = true; - break; - } - - u32 readSize = bufferSize; - if(data->size - offset < readSize) { - readSize = (u32) (data->size - offset); - } - - Result readRes = 0; - if(R_FAILED(readRes = data->read(data->data, &bytesRead, buffer, readSize))) { - if(readRes == -1) { - data->result->ioerr = true; - data->result->ioerrno = errno; - } else { - data->result->result = readRes; - } - - break; - } - - if(firstBlock) { - firstBlock = false; - - u32 headerSize = *(u32*) &buffer[0x00]; - u32 certSize = *(u32*) &buffer[0x08]; - titleId = __builtin_bswap64(*(u64*) &buffer[align(headerSize, 64) + align(certSize, 64) + 0x1DC]); - - FS_MediaType dest = ((titleId >> 32) & 0x8010) != 0 ? MEDIATYPE_NAND : MEDIATYPE_SD; - - u8 n3ds = false; - if(R_SUCCEEDED(APT_CheckNew3DS(&n3ds)) && !n3ds && ((titleId >> 28) & 0xF) == 2) { - data->result->wrongSystem = true; - break; - } - - // Deleting FBI before it reinstalls itself causes issues. - if(((titleId >> 8) & 0xFFFFF) != 0xF8001) { - AM_DeleteTitle(dest, titleId); - AM_DeleteTicket(titleId); - - if(dest == 1) { - AM_QueryAvailableExternalTitleDatabase(NULL); - } - } - - if(R_FAILED(data->result->result = AM_StartCiaInstall(dest, &ciaHandle))) { - break; - } - } - - if(R_FAILED(data->result->result = FSFILE_Write(ciaHandle, &bytesWritten, offset, buffer, bytesRead, 0))) { - break; - } - - offset += bytesRead; - } - - free(buffer); - - if(ciaHandle != 0) { - if(R_FAILED(data->result->result)) { - AM_CancelCIAInstall(ciaHandle); - } else if(R_SUCCEEDED(data->result->result = AM_FinishCiaInstall(ciaHandle))) { - if(titleId == 0x0004013800000002 || titleId == 0x0004013820000002) { - data->result->result = AM_InstallFirm(titleId); - } - } - } - } else { - data->result->result = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); - } - - if(R_FAILED(data->result->result) || data->result->cancelled || data->result->ioerr || data->result->wrongSystem) { - data->result->failed = true; - } - - data->result->finished = true; - - svcCloseHandle(data->cancelEvent); - free(data); -} - -Handle task_install_cia(install_cia_result* result, u64 size, void* data, Result (*read)(void* data, u32* bytesRead, void* buffer, u32 size)) { - if(result == NULL || size == 0 || read == NULL) { - return 0; - } - - install_cia_data* installData = (install_cia_data*) calloc(1, sizeof(install_cia_data)); - installData->result = result; - installData->size = size; - installData->data = data; - installData->read = read; - - Result eventRes = svcCreateEvent(&installData->cancelEvent, 1); - if(R_FAILED(eventRes)) { - error_display_res(NULL, NULL, eventRes, "Failed to create CIA installation cancel event."); - - free(installData); - return 0; - } - - if(threadCreate(task_install_cia_thread, installData, 0x4000, 0x18, 1, true) == NULL) { - error_display(NULL, NULL, "Failed to create CIA installation thread."); - - svcCloseHandle(installData->cancelEvent); - free(installData); - return 0; - } - - return installData->cancelEvent; -} \ No newline at end of file diff --git a/source/ui/section/task/listextsavedata.c b/source/ui/section/task/listextsavedata.c index 3102600..7587995 100644 --- a/source/ui/section/task/listextsavedata.c +++ b/source/ui/section/task/listextsavedata.c @@ -105,7 +105,7 @@ static void task_populate_ext_save_data_thread(void* arg) { Result res = 0; if(R_FAILED(res = task_populate_ext_save_data_from(data, MEDIATYPE_SD)) || R_FAILED(res = task_populate_ext_save_data_from(data, MEDIATYPE_NAND))) { - error_display_res(NULL, NULL, res, "Failed to load ext save data listing."); + error_display_res(NULL, NULL, NULL, res, "Failed to load ext save data listing."); } svcCloseHandle(data->cancelEvent); @@ -150,14 +150,14 @@ Handle task_populate_ext_save_data(list_item* items, u32* count, u32 max) { Result eventRes = svcCreateEvent(&data->cancelEvent, 1); if(R_FAILED(eventRes)) { - error_display_res(NULL, NULL, eventRes, "Failed to create ext save data list cancel event."); + error_display_res(NULL, NULL, NULL, eventRes, "Failed to create ext save data list cancel event."); free(data); return 0; } if(threadCreate(task_populate_ext_save_data_thread, data, 0x4000, 0x18, 1, true) == NULL) { - error_display(NULL, NULL, "Failed to create ext save data list thread."); + error_display(NULL, NULL, NULL, "Failed to create ext save data list thread."); svcCloseHandle(data->cancelEvent); free(data); diff --git a/source/ui/section/task/listfiles.c b/source/ui/section/task/listfiles.c index 4a74e49..74d5780 100644 --- a/source/ui/section/task/listfiles.c +++ b/source/ui/section/task/listfiles.c @@ -148,7 +148,7 @@ static void task_populate_files_thread(void* arg) { } if(R_FAILED(res)) { - error_display_res(NULL, NULL, res, "Failed to load file listing."); + error_display_res(NULL, NULL, NULL, res, "Failed to load file listing."); } svcCloseHandle(data->cancelEvent); @@ -194,14 +194,14 @@ Handle task_populate_files(list_item* items, u32* count, u32 max, file_info* dir Result eventRes = svcCreateEvent(&data->cancelEvent, 1); if(R_FAILED(eventRes)) { - error_display_res(NULL, NULL, eventRes, "Failed to create file list cancel event."); + error_display_res(NULL, NULL, NULL, eventRes, "Failed to create file list cancel event."); free(data); return 0; } if(threadCreate(task_populate_files_thread, data, 0x4000, 0x18, 1, true) == NULL) { - error_display(NULL, NULL, "Failed to create file list thread."); + error_display(NULL, NULL, NULL, "Failed to create file list thread."); svcCloseHandle(data->cancelEvent); free(data); diff --git a/source/ui/section/task/listpendingtitles.c b/source/ui/section/task/listpendingtitles.c index 9ac72bc..ca788a1 100644 --- a/source/ui/section/task/listpendingtitles.c +++ b/source/ui/section/task/listpendingtitles.c @@ -79,7 +79,7 @@ static void task_populate_pending_titles_thread(void* arg) { Result res = 0; if(R_FAILED(res = task_populate_pending_titles_from(data, MEDIATYPE_SD)) || R_FAILED(res = task_populate_pending_titles_from(data, MEDIATYPE_NAND))) { - error_display_res(NULL, NULL, res, "Failed to load pending title listing."); + error_display_res(NULL, NULL, NULL, res, "Failed to load pending title listing."); } svcCloseHandle(data->cancelEvent); @@ -119,14 +119,14 @@ Handle task_populate_pending_titles(list_item* items, u32* count, u32 max) { Result eventRes = svcCreateEvent(&data->cancelEvent, 1); if(R_FAILED(eventRes)) { - error_display_res(NULL, NULL, eventRes, "Failed to create pending title list cancel event."); + error_display_res(NULL, NULL, NULL, eventRes, "Failed to create pending title list cancel event."); free(data); return 0; } if(threadCreate(task_populate_pending_titles_thread, data, 0x4000, 0x18, 1, true) == NULL) { - error_display(NULL, NULL, "Failed to create pending title list thread."); + error_display(NULL, NULL, NULL, "Failed to create pending title list thread."); svcCloseHandle(data->cancelEvent); free(data); diff --git a/source/ui/section/task/listsystemsavedata.c b/source/ui/section/task/listsystemsavedata.c index 904ead6..c20833f 100644 --- a/source/ui/section/task/listsystemsavedata.c +++ b/source/ui/section/task/listsystemsavedata.c @@ -56,7 +56,7 @@ static void task_populate_system_save_data_thread(void* arg) { } if(R_FAILED(res)) { - error_display_res(NULL, NULL, res, "Failed to load system save data listing."); + error_display_res(NULL, NULL, NULL, res, "Failed to load system save data listing."); } svcCloseHandle(data->cancelEvent); @@ -96,14 +96,14 @@ Handle task_populate_system_save_data(list_item* items, u32* count, u32 max) { Result eventRes = svcCreateEvent(&data->cancelEvent, 1); if(R_FAILED(eventRes)) { - error_display_res(NULL, NULL, eventRes, "Failed to create system save data list cancel event."); + error_display_res(NULL, NULL, NULL, eventRes, "Failed to create system save data list cancel event."); free(data); return 0; } if(threadCreate(task_populate_system_save_data_thread, data, 0x4000, 0x18, 1, true) == NULL) { - error_display(NULL, NULL, "Failed to create system save data list thread."); + error_display(NULL, NULL, NULL, "Failed to create system save data list thread."); svcCloseHandle(data->cancelEvent); free(data); diff --git a/source/ui/section/task/listtickets.c b/source/ui/section/task/listtickets.c index 947f29b..948b12a 100644 --- a/source/ui/section/task/listtickets.c +++ b/source/ui/section/task/listtickets.c @@ -58,7 +58,7 @@ static void task_populate_tickets_thread(void* arg) { } if(R_FAILED(res)) { - error_display_res(NULL, NULL, res, "Failed to load ticket listing."); + error_display_res(NULL, NULL, NULL, res, "Failed to load ticket listing."); } svcCloseHandle(data->cancelEvent); @@ -98,14 +98,14 @@ Handle task_populate_tickets(list_item* items, u32* count, u32 max) { Result eventRes = svcCreateEvent(&data->cancelEvent, 1); if(R_FAILED(eventRes)) { - error_display_res(NULL, NULL, eventRes, "Failed to create ticket list cancel event."); + error_display_res(NULL, NULL, NULL, eventRes, "Failed to create ticket list cancel event."); free(data); return 0; } if(threadCreate(task_populate_tickets_thread, data, 0x4000, 0x18, 1, true) == NULL) { - error_display(NULL, NULL, "Failed to create ticket list thread."); + error_display(NULL, NULL, NULL, "Failed to create ticket list thread."); svcCloseHandle(data->cancelEvent); free(data); diff --git a/source/ui/section/task/listtitles.c b/source/ui/section/task/listtitles.c index 9966acc..348dc1f 100644 --- a/source/ui/section/task/listtitles.c +++ b/source/ui/section/task/listtitles.c @@ -296,7 +296,7 @@ static void task_populate_titles_thread(void* arg) { Result res = 0; if(R_FAILED(res = task_populate_titles_from(data, MEDIATYPE_GAME_CARD, false)) || R_FAILED(res = task_populate_titles_from(data, MEDIATYPE_SD, false)) || R_FAILED(res = task_populate_titles_from(data, MEDIATYPE_NAND, false)) || R_FAILED(res = task_populate_titles_from(data, MEDIATYPE_NAND, true))) { - error_display_res(NULL, NULL, res, "Failed to load title listing."); + error_display_res(NULL, NULL, NULL, res, "Failed to load title listing."); } svcCloseHandle(data->cancelEvent); @@ -341,14 +341,14 @@ Handle task_populate_titles(list_item* items, u32* count, u32 max) { Result eventRes = svcCreateEvent(&data->cancelEvent, 1); if(R_FAILED(eventRes)) { - error_display_res(NULL, NULL, eventRes, "Failed to create title list cancel event."); + error_display_res(NULL, NULL, NULL, eventRes, "Failed to create title list cancel event."); free(data); return 0; } if(threadCreate(task_populate_titles_thread, data, 0x4000, 0x18, 1, true) == NULL) { - error_display(NULL, NULL, "Failed to create title list thread."); + error_display(NULL, NULL, NULL, "Failed to create title list thread."); svcCloseHandle(data->cancelEvent); free(data); diff --git a/source/ui/section/task/movedata.c b/source/ui/section/task/movedata.c new file mode 100644 index 0000000..fb7abaf --- /dev/null +++ b/source/ui/section/task/movedata.c @@ -0,0 +1,166 @@ +#include +#include + +#include <3ds.h> +#include + +#include "../../list.h" +#include "../../error.h" +#include "task.h" + +typedef struct { + move_data_info* info; + + Handle cancelEvent; +} move_data_data; + +static bool task_move_data_item(move_data_data* data, u32 index) { + data->info->currProcessed = 0; + data->info->currTotal = 0; + + Result res = 0; + + bool isDir = false; + if(R_SUCCEEDED(res = data->info->isSrcDirectory(data->info->data, index, &isDir)) && isDir) { + res = data->info->makeDstDirectory(data->info->data, index); + } else { + u32 srcHandle = 0; + if(R_SUCCEEDED(res = data->info->openSrc(data->info->data, index, &srcHandle))) { + if(R_SUCCEEDED(res = data->info->getSrcSize(data->info->data, srcHandle, &data->info->currTotal))) { + if(data->info->currTotal == 0) { + if(data->info->moveEmpty) { + u32 dstHandle = 0; + if(R_SUCCEEDED(res = data->info->openDst(data->info->data, index, NULL, &dstHandle))) { + res = data->info->closeDst(data->info->data, index, true, dstHandle); + } + } + } else { + u32 bufferSize = 1024 * 256; + u8* buffer = (u8*) calloc(1, bufferSize); + if(buffer != NULL) { + u32 dstHandle = 0; + + bool firstRun = true; + while(data->info->currProcessed < data->info->currTotal) { + if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { + res = MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_CANCEL_REQUESTED); + break; + } + + u32 currSize = bufferSize; + if((u64) currSize > data->info->currTotal - data->info->currProcessed) { + currSize = (u32) (data->info->currTotal - data->info->currProcessed); + } + + u32 bytesRead = 0; + u32 bytesWritten = 0; + if(R_FAILED(res = data->info->readSrc(data->info->data, srcHandle, &bytesRead, buffer, data->info->currProcessed, currSize))) { + break; + } + + if(firstRun) { + firstRun = false; + + if(R_FAILED(res = data->info->openDst(data->info->data, index, buffer, &dstHandle))) { + break; + } + } + + if(R_FAILED(res = data->info->writeDst(data->info->data, dstHandle, &bytesWritten, buffer, data->info->currProcessed, currSize))) { + break; + } + + data->info->currProcessed += bytesWritten; + } + + if(dstHandle != 0) { + Result closeDstRes = data->info->closeDst(data->info->data, index, res == 0, dstHandle); + if(R_SUCCEEDED(res)) { + res = closeDstRes; + } + } + + free(buffer); + } else { + res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, RM_APPLICATION, RD_OUT_OF_MEMORY); + } + } + } + + Result closeSrcRes = data->info->closeSrc(data->info->data, index, res == 0, srcHandle); + if(R_SUCCEEDED(res)) { + res = closeSrcRes; + } + } + } + + if(R_FAILED(res)) { + if(res == -1) { + return data->info->ioError(data->info->data, index, errno); + } else { + return data->info->resultError(data->info->data, index, res); + } + } + + return true; +} + +static void task_move_data_thread(void* arg) { + move_data_data* data = (move_data_data*) arg; + + data->info->finished = false; + data->info->premature = false; + + data->info->processed = 0; + + for(data->info->processed = 0; data->info->processed < data->info->total; data->info->processed++) { + if(!task_move_data_item(data, data->info->processed)) { + data->info->premature = true; + break; + } + } + + data->info->finished = true; + + svcCloseHandle(data->cancelEvent); + free(data); +} + +static void task_move_data_reset_info(move_data_info* info) { + info->finished = false; + info->premature = false; + + info->processed = 0; + + info->currProcessed = 0; + info->currTotal = 0; +} + +Handle task_move_data(move_data_info* info) { + if(info == NULL) { + return 0; + } + + task_move_data_reset_info(info); + + move_data_data* installData = (move_data_data*) calloc(1, sizeof(move_data_data)); + installData->info = info; + + Result eventRes = svcCreateEvent(&installData->cancelEvent, 1); + if(R_FAILED(eventRes)) { + error_display_res(NULL, NULL, NULL, eventRes, "Failed to create CIA installation cancel event."); + + free(installData); + return 0; + } + + if(threadCreate(task_move_data_thread, installData, 0x4000, 0x18, 1, true) == NULL) { + error_display(NULL, NULL, NULL, "Failed to create CIA installation thread."); + + svcCloseHandle(installData->cancelEvent); + free(installData); + return 0; + } + + return installData->cancelEvent; +} \ No newline at end of file diff --git a/source/ui/section/task/task.h b/source/ui/section/task/task.h index 822a167..6d58647 100644 --- a/source/ui/section/task/task.h +++ b/source/ui/section/task/task.h @@ -69,15 +69,36 @@ typedef struct { } file_info; typedef struct { - bool finished; - bool failed; - bool cancelled; - bool ioerr; - bool wrongSystem; + void* data; - Result result; - int ioerrno; -} install_cia_result; + bool moveEmpty; + + bool finished; + bool premature; + + u32 processed; + u32 total; + + u64 currProcessed; + u64 currTotal; + + Result (*isSrcDirectory)(void* data, u32 index, bool* isDirectory); + Result (*makeDstDirectory)(void* data, u32 index); + + Result (*openSrc)(void* data, u32 index, u32* handle); + Result (*closeSrc)(void* data, u32 index, bool succeeded, u32 handle); + + Result (*getSrcSize)(void* data, u32 handle, u64* size); + Result (*readSrc)(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size); + + Result (*openDst)(void* data, u32 index, void* initialReadBlock, u32* handle); + Result (*closeDst)(void* data, u32 index, bool succeeded, u32 handle); + + Result (*writeDst)(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size); + + bool (*resultError)(void* data, u32 index, Result res); + bool (*ioError)(void* data, u32 index, int err); +} move_data_info; bool task_is_quit_all(); void task_quit_all(); @@ -88,4 +109,4 @@ Handle task_populate_pending_titles(list_item* items, u32* count, u32 max); Handle task_populate_system_save_data(list_item* items, u32* count, u32 max); Handle task_populate_tickets(list_item* items, u32* count, u32 max); Handle task_populate_titles(list_item* items, u32* count, u32 max); -Handle task_install_cia(install_cia_result* result, u64 size, void* data, Result (*read)(void* data, u32* bytesRead, void* buffer, u32 size)); \ No newline at end of file +Handle task_move_data(move_data_info* info); \ No newline at end of file