mirror of
https://gitlab.com/Theopse/fbi-i18n-zh.git
synced 2025-04-06 03:58:02 +08:00
Create standard task for moving data.
This commit is contained in:
parent
75f7eb9297
commit
7e421c741a
@ -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);
|
||||
|
@ -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];
|
||||
|
@ -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, ...);
|
||||
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, ...);
|
@ -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;
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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));
|
||||
}
|
@ -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));
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -1,59 +0,0 @@
|
||||
#include <malloc.h>
|
||||
|
||||
#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));
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
@ -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);
|
||||
|
@ -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";
|
||||
|
@ -1,153 +0,0 @@
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <3ds.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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;
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
166
source/ui/section/task/movedata.c
Normal file
166
source/ui/section/task/movedata.c
Normal file
@ -0,0 +1,166 @@
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <3ds.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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;
|
||||
}
|
@ -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));
|
||||
Handle task_move_data(move_data_info* info);
|
Loading…
x
Reference in New Issue
Block a user