Create standard task for moving data.

This commit is contained in:
Steven Smith 2016-04-18 18:42:35 -07:00
parent 75f7eb9297
commit 7e421c741a
30 changed files with 1156 additions and 958 deletions

View File

@ -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);

View File

@ -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];

View File

@ -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, ...);

View File

@ -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;
}

View File

@ -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));
}

View File

@ -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));
}

View File

@ -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;

View File

@ -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));
}

View File

@ -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));
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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.");
}
}

View File

@ -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;

View File

@ -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));
}

View File

@ -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);

View File

@ -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";

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View 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;
}

View File

@ -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);