diff --git a/source/core/task/capturecam.c b/source/core/task/capturecam.c index b7a1e45..a35780f 100644 --- a/source/core/task/capturecam.c +++ b/source/core/task/capturecam.c @@ -6,6 +6,7 @@ #include "capturecam.h" #include "task.h" #include "../util.h" +#include "../../libs/quirc/quirc.h" #define EVENT_CANCEL 0 #define EVENT_RECV 1 @@ -13,8 +14,17 @@ #define EVENT_COUNT 3 +typedef struct { + capture_cam_data* data; + + struct quirc* qrContext; +} capture_cam_internal_data; + static void task_capture_cam_thread(void* arg) { - capture_cam_data* data = (capture_cam_data*) arg; + capture_cam_internal_data* internalData = (capture_cam_internal_data*) arg; + capture_cam_data* data = internalData->data; + + data->qrReady = false; Handle events[EVENT_COUNT] = {0}; events[EVENT_CANCEL] = data->cancelEvent; @@ -63,6 +73,37 @@ static void task_capture_cam_thread(void* arg) { GSPGPU_FlushDataCache(data->buffer, bufferSize); svcReleaseMutex(data->mutex); + if(data->scanQR) { + int w = 0; + int h = 0; + uint8_t* qrBuf = quirc_begin(internalData->qrContext, &w, &h); + + for(int x = 0; x < w; x++) { + for(int y = 0; y < h; y++) { + u16 px = buffer[y * data->width + x]; + qrBuf[y * w + x] = (u8) (((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3); + } + } + + quirc_end(internalData->qrContext); + + int qrCount = quirc_count(internalData->qrContext); + for(int i = 0; i < qrCount; i++) { + struct quirc_code qrCode; + quirc_extract(internalData->qrContext, i, &qrCode); + + struct quirc_data qrData; + if(quirc_decode(&qrCode, &qrData) == 0) { + svcWaitSynchronization(data->mutex, U64_MAX); + + data->qrReady = true; + memcpy(data->qrData, qrData.payload, sizeof(data->qrData)); + + svcReleaseMutex(data->mutex); + } + } + } + res = CAMU_SetReceiving(&events[EVENT_RECV], buffer, PORT_CAM1, bufferSize, (s16) transferUnit); break; case EVENT_BUFFER_ERROR: @@ -109,6 +150,9 @@ static void task_capture_cam_thread(void* arg) { } } + quirc_destroy(internalData->qrContext); + free(internalData); + svcCloseHandle(data->mutex); data->result = res; @@ -120,22 +164,43 @@ Result task_capture_cam(capture_cam_data* data) { return R_FBI_INVALID_ARGUMENT; } + capture_cam_internal_data* internalData = (capture_cam_internal_data*) calloc(1, sizeof(capture_cam_internal_data)); + if(internalData == NULL) { + return R_FBI_OUT_OF_MEMORY; + } + data->mutex = 0; data->finished = false; data->result = 0; data->cancelEvent = 0; + internalData->data = data; + Result res = 0; - if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY)) && R_SUCCEEDED(res = svcCreateMutex(&data->mutex, false))) { - if(threadCreate(task_capture_cam_thread, data, 0x10000, 0x1A, 1, true) == NULL) { - res = R_FBI_THREAD_CREATE_FAILED; + + internalData->qrContext = quirc_new(); + if(internalData->qrContext != NULL && quirc_resize(internalData->qrContext, data->width, data->height) == 0) { + if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY)) && R_SUCCEEDED(res = svcCreateMutex(&data->mutex, false))) { + if(threadCreate(task_capture_cam_thread, internalData, 0x10000, 0x1A, 0, true) == NULL) { + res = R_FBI_THREAD_CREATE_FAILED; + } } + } else { + res = R_FBI_QR_INIT_FAILED; } + if(R_FAILED(res)) { data->finished = true; + if(internalData->qrContext != NULL) { + quirc_destroy(internalData->qrContext); + internalData->qrContext = NULL; + } + + free(internalData); + if(data->cancelEvent != 0) { svcCloseHandle(data->cancelEvent); data->cancelEvent = 0; diff --git a/source/core/task/capturecam.h b/source/core/task/capturecam.h index 23e2a05..adbb075 100644 --- a/source/core/task/capturecam.h +++ b/source/core/task/capturecam.h @@ -1,5 +1,7 @@ #pragma once +#define CAMERA_QR_DATA_MAX 8896 + typedef enum capture_cam_camera_e { CAMERA_OUTER, CAMERA_INNER @@ -10,6 +12,10 @@ typedef struct capture_cam_data_s { s16 width; s16 height; capture_cam_camera camera; + bool scanQR; + + bool qrReady; + u8 qrData[CAMERA_QR_DATA_MAX]; Handle mutex; diff --git a/source/core/util.c b/source/core/util.c index 238597a..7eefa41 100644 --- a/source/core/util.c +++ b/source/core/util.c @@ -5,8 +5,6 @@ #include #include <3ds.h> -#include -#include #include "linkedlist.h" #include "util.h" @@ -268,156 +266,6 @@ bool util_is_string_empty(const char* str) { return true; } -typedef struct { - u8* buf; - u32 size; - - u32 pos; -} download_data; - -static size_t util_download_write_callback(void* contents, size_t size, size_t nmemb, void* userp) { - download_data* data = (download_data*) userp; - - size_t realSize = size * nmemb; - size_t remaining = data->size - data->pos; - size_t copy = realSize < remaining ? realSize : remaining; - - memcpy(&data->buf[data->pos], contents, copy); - data->pos += copy; - - return copy; -} - -Result util_download(const char* url, u32* downloadedSize, void* buf, size_t size) { - if(url == NULL || buf == NULL) { - return R_FBI_INVALID_ARGUMENT; - } - - Result res = 0; - - CURL* curl = curl_easy_init(); - if(curl != NULL) { - download_data readData = {buf, size, 0}; - - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, util_download_write_callback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*) &readData); - curl_easy_setopt(curl, CURLOPT_USERAGENT, HTTP_USER_AGENT); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true); - curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""); - curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT); - - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false); // TODO: Certificates? - - CURLcode ret = curl_easy_perform(curl); - if(ret == CURLE_OK) { - if(downloadedSize != NULL) { - *downloadedSize = readData.pos; - } - } else { - res = R_FBI_CURL_ERORR_BASE + ret; - } - - curl_easy_cleanup(curl); - } else { - res = R_FBI_CURL_INIT_FAILED; - } - - return res; -} - -Result util_download_json(const char* url, json_t** json, size_t maxSize) { - if(url == NULL || json == NULL) { - return R_FBI_INVALID_ARGUMENT; - } - - Result res = 0; - - char* text = (char*) calloc(sizeof(char), maxSize); - if(text != NULL) { - u32 textSize = 0; - if(R_SUCCEEDED(res = util_download(url, &textSize, text, maxSize))) { - json_error_t error; - json_t* parsed = json_loads(text, 0, &error); - if(parsed != NULL) { - *json = parsed; - } else { - res = R_FBI_PARSE_FAILED; - } - } - - free(text); - } else { - res = R_FBI_OUT_OF_MEMORY; - } - - return res; -} - -static Result FSUSER_AddSeed(u64 titleId, const void* seed) { - u32 *cmdbuf = getThreadCommandBuffer(); - - cmdbuf[0] = 0x087A0180; - cmdbuf[1] = (u32) (titleId & 0xFFFFFFFF); - cmdbuf[2] = (u32) (titleId >> 32); - memcpy(&cmdbuf[3], seed, 16); - - Result ret = 0; - if(R_FAILED(ret = svcSendSyncRequest(*fsGetSessionHandle()))) return ret; - - ret = cmdbuf[1]; - return ret; -} - -Result util_import_seed(u32* responseCode, u64 titleId) { - char pathBuf[64]; - snprintf(pathBuf, 64, "/fbi/seed/%016llX.dat", titleId); - - Result res = 0; - - FS_Path* fsPath = util_make_path_utf8(pathBuf); - if(fsPath != NULL) { - u8 seed[16]; - - Handle fileHandle = 0; - if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&fileHandle, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), *fsPath, FS_OPEN_READ, 0))) { - u32 bytesRead = 0; - res = FSFILE_Read(fileHandle, &bytesRead, 0, seed, sizeof(seed)); - - FSFILE_Close(fileHandle); - } - - util_free_path_utf8(fsPath); - - if(R_FAILED(res)) { - u8 region = CFG_REGION_USA; - CFGU_SecureInfoGetRegion(®ion); - - if(region <= CFG_REGION_TWN) { - static const char* regionStrings[] = {"JP", "US", "GB", "GB", "HK", "KR", "TW"}; - - char url[128]; - snprintf(url, 128, "https://kagiya-ctr.cdn.nintendo.net/title/0x%016llX/ext_key?country=%s", titleId, regionStrings[region]); - - u32 downloadedSize = 0; - if(R_SUCCEEDED(res = util_download(url, &downloadedSize, seed, sizeof(seed))) && downloadedSize != sizeof(seed)) { - res = R_FBI_BAD_DATA; - } - } else { - res = R_FBI_OUT_OF_RANGE; - } - } - - if(R_SUCCEEDED(res)) { - res = FSUSER_AddSeed(titleId, seed); - } - } else { - res = R_FBI_OUT_OF_MEMORY; - } - - return res; -} - FS_MediaType util_get_title_destination(u64 titleId) { u16 platform = (u16) ((titleId >> 48) & 0xFFFF); u16 category = (u16) ((titleId >> 32) & 0xFFFF); @@ -563,11 +411,11 @@ void util_escape_file_name(char* out, const char* file, size_t size) { #define HTTPC_TIMEOUT 15000000000 -Result util_http_open(httpcContext* context, u32* responseCode, const char* url, bool userAgent) { - return util_http_open_ranged(context, responseCode, url, userAgent, 0, 0); +Result util_http_open(httpcContext* context, const char* url, bool userAgent) { + return util_http_open_ranged(context, url, userAgent, 0, 0); } -Result util_http_open_ranged(httpcContext* context, u32* responseCode, const char* url, bool userAgent, u32 rangeStart, u32 rangeEnd) { +Result util_http_open_ranged(httpcContext* context, const char* url, bool userAgent, u32 rangeStart, u32 rangeEnd) { if(context == NULL || url == NULL) { return R_FBI_INVALID_ARGUMENT; } @@ -605,12 +453,8 @@ Result util_http_open_ranged(httpcContext* context, u32* responseCode, const cha } else { resolved = true; - if(responseCode != NULL) { - *responseCode = response; - } - if(response != 200) { - res = R_FBI_HTTP_RESPONSE_CODE; + res = R_FBI_HTTP_ERROR_BASE + response; } } } diff --git a/source/core/util.h b/source/core/util.h index 2fea5c2..2d8f50a 100644 --- a/source/core/util.h +++ b/source/core/util.h @@ -3,17 +3,20 @@ typedef struct json_t json_t; // Errors -#define R_FBI_CANCELLED MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, 1) -#define R_FBI_HTTP_RESPONSE_CODE MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 2) +#define R_FBI_INVALID_ARGUMENT MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_APPLICATION, 1) +#define R_FBI_CANCELLED MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, 2) #define R_FBI_WRONG_SYSTEM MAKERESULT(RL_PERMANENT, RS_NOTSUPPORTED, RM_APPLICATION, 3) -#define R_FBI_INVALID_ARGUMENT MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_APPLICATION, 4) -#define R_FBI_THREAD_CREATE_FAILED MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 5) -#define R_FBI_PARSE_FAILED MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 6) -#define R_FBI_BAD_DATA MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 7) -#define R_FBI_TOO_MANY_REDIRECTS MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 8) - +#define R_FBI_THREAD_CREATE_FAILED MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 4) +#define R_FBI_PARSE_FAILED MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 5) +#define R_FBI_BAD_DATA MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 6) +#define R_FBI_TOO_MANY_REDIRECTS MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 7) +#define R_FBI_QR_INIT_FAILED MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 8) #define R_FBI_CURL_INIT_FAILED MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 9) -#define R_FBI_CURL_ERORR_BASE MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 10) + +#define R_FBI_HTTP_ERROR_BASE MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 10) +#define R_FBI_HTTP_ERROR_END (R_FBI_HTTP_ERROR_BASE + 600) + +#define R_FBI_CURL_ERROR_BASE (R_FBI_HTTP_ERROR_END + 1) #define R_FBI_NOT_IMPLEMENTED MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, RD_NOT_IMPLEMENTED) #define R_FBI_OUT_OF_MEMORY MAKERESULT(RL_FATAL, RS_OUTOFRESOURCE, RM_APPLICATION, RD_OUT_OF_MEMORY) @@ -60,17 +63,11 @@ bool util_filter_cias(void* data, const char* name, u32 attributes); bool util_filter_tickets(void* data, const char* name, u32 attributes); // Titles -Result util_import_seed(u32* responseCode, u64 titleId); - FS_MediaType util_get_title_destination(u64 titleId); -// Download -Result util_download(const char* url, u32* downloadedSize, void* buf, size_t size); -Result util_download_json(const char* url, json_t** json, size_t maxSize); - // HTTP -Result util_http_open(httpcContext* context, u32* responseCode, const char* url, bool userAgent); -Result util_http_open_ranged(httpcContext* context, u32* responseCode, const char* url, bool userAgent, u32 rangeStart, u32 rangeEnd); +Result util_http_open(httpcContext* context, const char* url, bool userAgent); +Result util_http_open_ranged(httpcContext* context, const char* url, bool userAgent, u32 rangeStart, u32 rangeEnd); Result util_http_get_size(httpcContext* context, u32* size); Result util_http_get_file_name(httpcContext* context, char* out, u32 size); Result util_http_read(httpcContext* context, u32* bytesRead, void* buffer, u32 size); diff --git a/source/main.c b/source/main.c index 5661e03..ad5e6b8 100644 --- a/source/main.c +++ b/source/main.c @@ -114,11 +114,7 @@ Result init_services() { return res; } -static u32 old_time_limit = UINT32_MAX; - -#include -#include -#include +//static u32 old_time_limit = UINT32_MAX; FILE* dbg; @@ -144,17 +140,17 @@ void init() { } } - osSetSpeedupEnable(true); - - APT_GetAppCpuTimeLimit(&old_time_limit); + /*APT_GetAppCpuTimeLimit(&old_time_limit); Result cpuRes = APT_SetAppCpuTimeLimit(30); if(R_FAILED(cpuRes)) { util_panic("Failed to set syscore CPU time limit: %08lX", cpuRes); return; - } + }*/ AM_InitializeExternalTitleDatabase(false); + dbg = fopen("sdmc:/debug.txt", "wb"); + curl_global_init(CURL_GLOBAL_ALL); screen_init(); @@ -164,17 +160,17 @@ void init() { } void cleanup() { + fclose(dbg); + clipboard_clear(); task_exit(); ui_exit(); screen_exit(); - if(old_time_limit != UINT32_MAX) { + /*if(old_time_limit != UINT32_MAX) { APT_SetAppCpuTimeLimit(old_time_limit); - } - - osSetSpeedupEnable(false); + }*/ cleanup_services(); @@ -183,17 +179,44 @@ void cleanup() { gfxExit(); } -int main(int argc, const char* argv[]) { - if(argc > 0 && envIsHomebrew()) { - util_set_3dsx_path(argv[0]); - } - +static void main_thread(void* arg) { init(); mainmenu_open(); while(aptMainLoop() && ui_update()); cleanup(); +} + +int main(int argc, const char* argv[]) { + if(argc > 0 && envIsHomebrew()) { + util_set_3dsx_path(argv[0]); + } + + osSetSpeedupEnable(true); + + u32 oldTimeLimit = UINT32_MAX; + APT_GetAppCpuTimeLimit(&oldTimeLimit); + + Result cpuRes = APT_SetAppCpuTimeLimit(30); + if(R_FAILED(cpuRes)) { + util_panic("Failed to set syscore CPU time limit: %08lX", cpuRes); + return 0; + } + + Thread mainThread = threadCreate(main_thread, NULL, 0x10000, 0x18, 1, true); + if(mainThread == NULL) { + util_panic("Failed to start main thread."); + return 0; + } + + threadJoin(mainThread, U64_MAX); + + if(oldTimeLimit != UINT32_MAX) { + APT_SetAppCpuTimeLimit(oldTimeLimit); + } + + osSetSpeedupEnable(false); return 0; } \ No newline at end of file diff --git a/source/ui/error.c b/source/ui/error.c index ad61169..889c6ca 100644 --- a/source/ui/error.c +++ b/source/ui/error.c @@ -489,14 +489,12 @@ static const char* description_to_string(Result res) { break; case RM_APPLICATION: switch(res) { - case R_FBI_CANCELLED: - return "Operation cancelled"; - case R_FBI_HTTP_RESPONSE_CODE: - return "HTTP request returned error"; - case R_FBI_WRONG_SYSTEM: - return "Attempted to install an N3DS title on an O3DS"; case R_FBI_INVALID_ARGUMENT: return "Invalid argument"; + case R_FBI_CANCELLED: + return "Operation cancelled"; + case R_FBI_WRONG_SYSTEM: + return "Attempted to install an N3DS title on an O3DS"; case R_FBI_THREAD_CREATE_FAILED: return "Thread creation failed"; case R_FBI_PARSE_FAILED: @@ -505,11 +503,144 @@ static const char* description_to_string(Result res) { return "Bad data"; case R_FBI_TOO_MANY_REDIRECTS: return "Too many redirects"; + case R_FBI_QR_INIT_FAILED: + return "Failed to initialize QR code parser."; case R_FBI_CURL_INIT_FAILED: return "Failed to initialize CURL."; default: - if(res >= R_FBI_CURL_ERORR_BASE && res < R_FBI_CURL_ERORR_BASE + CURL_LAST) { - return curl_easy_strerror((CURLcode) (res - R_FBI_CURL_ERORR_BASE)); + if(res >= R_FBI_HTTP_ERROR_BASE && res < R_FBI_HTTP_ERROR_END) { + switch(res - R_FBI_HTTP_ERROR_BASE) { + case 100: + return "HTTP 100: Continue"; + case 101: + return "HTTP 101: Switching Protocols"; + case 102: + return "HTTP 102: Processing"; + case 103: + return "HTTP 103: Early Hints"; + case 200: + return "HTTP 200: OK"; + case 201: + return "HTTP 201: Created"; + case 202: + return "HTTP 202: Accepted"; + case 203: + return "HTTP 203: Non-Authoritative Information"; + case 204: + return "HTTP 204: No Content"; + case 205: + return "HTTP 205: Reset Content"; + case 206: + return "HTTP 206: Partial Content"; + case 207: + return "HTTP 207: Multi-Status"; + case 208: + return "HTTP 208: Already Reported"; + case 226: + return "HTTP 226: IM Used"; + case 300: + return "HTTP 300: Multiple Choices"; + case 301: + return "HTTP 301: Moved Permanently"; + case 302: + return "HTTP 302: Found"; + case 303: + return "HTTP 303: See Other"; + case 304: + return "HTTP 304: Not Modified"; + case 305: + return "HTTP 305: Use Proxy"; + case 306: + return "HTTP 306: Switch Proxy"; + case 307: + return "HTTP 307: Temporary Redirect"; + case 308: + return "HTTP 308: Permanent Redirect"; + case 400: + return "HTTP 400: Bad Request"; + case 401: + return "HTTP 401: Unauthorized"; + case 402: + return "HTTP 402: Payment Required"; + case 403: + return "HTTP 403: Forbidden"; + case 404: + return "HTTP 404: Not Found"; + case 405: + return "HTTP 405: Method Not Allowed"; + case 406: + return "HTTP 406: Not Acceptable"; + case 407: + return "HTTP 407: Proxy Authentication Required"; + case 408: + return "HTTP 408: Request Timeout"; + case 409: + return "HTTP 409: Conflict"; + case 410: + return "HTTP 410: Gone"; + case 411: + return "HTTP 411: Length Required"; + case 412: + return "HTTP 412: Precondition Failed"; + case 413: + return "HTTP 413: Payload Too Large"; + case 414: + return "HTTP 414: URI Too Long"; + case 415: + return "HTTP 415: Unsupported Media Type"; + case 416: + return "HTTP 416: Range Not Satisfiable"; + case 417: + return "HTTP 417: Expectation Failed"; + case 418: + return "HTTP 418: I'm a teapot"; + case 421: + return "HTTP 421: Misdirected Request"; + case 422: + return "HTTP 422: Unprocessable Entity"; + case 423: + return "HTTP 423: Locked"; + case 424: + return "HTTP 424: Failed Dependency"; + case 426: + return "HTTP 426: Upgrade Required"; + case 428: + return "HTTP 428: Precondition Required"; + case 429: + return "HTTP 429: Too Many Requests"; + case 431: + return "HTTP 431: Request Header Fields Too Large"; + case 451: + return "HTTP 451: Unavailable For Legal Reasons"; + case 500: + return "HTTP 500: Internal Server Error"; + case 501: + return "HTTP 501: Not Implemented"; + case 502: + return "HTTP 502: Bad Gateway"; + case 503: + return "HTTP 503: Service Unavailable"; + case 504: + return "HTTP 504: Gateway Timeout"; + case 505: + return "HTTP 505: HTTP Version Not Specified"; + case 506: + return "HTTP 506: Variant Also Negotiates"; + case 507: + return "HTTP 507: Insufficient Storage"; + case 508: + return "HTTP 508: Loop Detected"; + case 510: + return "HTTP 510: Not Extended"; + case 511: + return "HTTP 511: Network Authentication Required"; + default: + return "HTTP: Unknown Response Code"; + } + } + + if(res >= R_FBI_CURL_ERROR_BASE && res < R_FBI_CURL_ERROR_BASE + CURL_LAST) { + return curl_easy_strerror((CURLcode) (res - R_FBI_CURL_ERROR_BASE)); } break; diff --git a/source/ui/section/action/action.h b/source/ui/section/action/action.h index ea8e8f8..b3589ce 100644 --- a/source/ui/section/action/action.h +++ b/source/ui/section/action/action.h @@ -5,7 +5,6 @@ typedef struct linked_list_s linked_list; typedef struct list_item_s list_item; typedef struct ui_view_s ui_view; -#define INSTALL_URL_MAX 1024 #define INSTALL_URLS_MAX 128 void action_browse_boss_ext_save_data(linked_list* items, list_item* selected); diff --git a/source/ui/section/action/erasetwlsave.c b/source/ui/section/action/erasetwlsave.c index eb02a04..646c74d 100644 --- a/source/ui/section/action/erasetwlsave.c +++ b/source/ui/section/action/erasetwlsave.c @@ -74,11 +74,11 @@ static Result action_erase_twl_save_write_dst(void* data, u32 handle, u32* bytes return spi_write_save(bytesWritten, buffer, (u32) offset, size); } -static Result action_erase_twl_save_suspend_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result action_erase_twl_save_suspend_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { return 0; } -static Result action_erase_twl_save_restore_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result action_erase_twl_save_restore_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { return 0; } @@ -121,8 +121,8 @@ static void action_erase_twl_save_update(ui_view* view, void* data, float* progr ui_get_display_size_units(eraseData->eraseInfo.currProcessed), ui_get_display_size(eraseData->eraseInfo.currTotal), ui_get_display_size_units(eraseData->eraseInfo.currTotal), - ui_get_display_size(eraseData->eraseInfo.copyBytesPerSecond), - ui_get_display_size_units(eraseData->eraseInfo.copyBytesPerSecond), + ui_get_display_size(eraseData->eraseInfo.bytesPerSecond), + ui_get_display_size_units(eraseData->eraseInfo.bytesPerSecond), ui_get_display_eta(eraseData->eraseInfo.estimatedRemainingSeconds)); } @@ -156,7 +156,7 @@ void action_erase_twl_save(linked_list* items, list_item* selected) { data->eraseInfo.op = DATAOP_COPY; - data->eraseInfo.copyBufferSize = 16 * 1024; + data->eraseInfo.bufferSize = 16 * 1024; data->eraseInfo.copyEmpty = true; data->eraseInfo.total = 1; @@ -173,8 +173,8 @@ void action_erase_twl_save(linked_list* items, list_item* selected) { data->eraseInfo.closeDst = action_erase_twl_save_close_dst; data->eraseInfo.writeDst = action_erase_twl_save_write_dst; - data->eraseInfo.suspendCopy = action_erase_twl_save_suspend_copy; - data->eraseInfo.restoreCopy = action_erase_twl_save_restore_copy; + data->eraseInfo.suspendTransfer = action_erase_twl_save_suspend_transfer; + data->eraseInfo.restoreTransfer = action_erase_twl_save_restore_transfer; data->eraseInfo.suspend = action_erase_twl_save_suspend; data->eraseInfo.restore = action_erase_twl_save_restore; diff --git a/source/ui/section/action/exporttwlsave.c b/source/ui/section/action/exporttwlsave.c index c7c7ccd..dc57509 100644 --- a/source/ui/section/action/exporttwlsave.c +++ b/source/ui/section/action/exporttwlsave.c @@ -96,11 +96,11 @@ static Result action_export_twl_save_write_dst(void* data, u32 handle, u32* byte return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0); } -static Result action_export_twl_save_suspend_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result action_export_twl_save_suspend_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { return 0; } -static Result action_export_twl_save_restore_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result action_export_twl_save_restore_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { return 0; } @@ -143,8 +143,8 @@ static void action_export_twl_save_update(ui_view* view, void* data, float* prog ui_get_display_size_units(exportData->exportInfo.currProcessed), ui_get_display_size(exportData->exportInfo.currTotal), ui_get_display_size_units(exportData->exportInfo.currTotal), - ui_get_display_size(exportData->exportInfo.copyBytesPerSecond), - ui_get_display_size_units(exportData->exportInfo.copyBytesPerSecond), + ui_get_display_size(exportData->exportInfo.bytesPerSecond), + ui_get_display_size_units(exportData->exportInfo.bytesPerSecond), ui_get_display_eta(exportData->exportInfo.estimatedRemainingSeconds)); } @@ -178,7 +178,7 @@ void action_export_twl_save(linked_list* items, list_item* selected) { data->exportInfo.op = DATAOP_COPY; - data->exportInfo.copyBufferSize = 128 * 1024; + data->exportInfo.bufferSize = 128 * 1024; data->exportInfo.copyEmpty = true; data->exportInfo.total = 1; @@ -195,8 +195,8 @@ void action_export_twl_save(linked_list* items, list_item* selected) { data->exportInfo.closeDst = action_export_twl_save_close_dst; data->exportInfo.writeDst = action_export_twl_save_write_dst; - data->exportInfo.suspendCopy = action_export_twl_save_suspend_copy; - data->exportInfo.restoreCopy = action_export_twl_save_restore_copy; + data->exportInfo.suspendTransfer = action_export_twl_save_suspend_transfer; + data->exportInfo.restoreTransfer = action_export_twl_save_restore_transfer; data->exportInfo.suspend = action_export_twl_save_suspend; data->exportInfo.restore = action_export_twl_save_restore; diff --git a/source/ui/section/action/importseed.c b/source/ui/section/action/importseed.c index 0a3c95a..af81664 100644 --- a/source/ui/section/action/importseed.c +++ b/source/ui/section/action/importseed.c @@ -17,16 +17,13 @@ static void action_import_seed_update(ui_view* view, void* data, float* progress, char* text) { title_info* info = (title_info*) data; - u32 responseCode = 0; - Result res = util_import_seed(&responseCode, info->titleId); + Result res = task_download_seed_sync(info->titleId); ui_pop(); info_destroy(view); if(R_SUCCEEDED(res)) { prompt_display_notify("Success", "Seed imported.", COLOR_TEXT, info, ui_draw_title_info, NULL); - } else if(res == R_FBI_HTTP_RESPONSE_CODE) { - error_display(NULL, NULL, "Failed to import seed.\nHTTP server returned response code %d", responseCode); } else { error_display_res(info, ui_draw_title_info, res, "Failed to import seed."); } diff --git a/source/ui/section/action/importtwlsave.c b/source/ui/section/action/importtwlsave.c index d051a07..ae597ab 100644 --- a/source/ui/section/action/importtwlsave.c +++ b/source/ui/section/action/importtwlsave.c @@ -82,11 +82,11 @@ static Result action_import_twl_save_write_dst(void* data, u32 handle, u32* byte return spi_write_save(bytesWritten, buffer, (u32) offset, size); } -static Result action_import_twl_save_suspend_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result action_import_twl_save_suspend_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { return 0; } -static Result action_import_twl_save_restore_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result action_import_twl_save_restore_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { return 0; } @@ -129,8 +129,8 @@ static void action_import_twl_save_update(ui_view* view, void* data, float* prog ui_get_display_size_units(importData->importInfo.currProcessed), ui_get_display_size(importData->importInfo.currTotal), ui_get_display_size_units(importData->importInfo.currTotal), - ui_get_display_size(importData->importInfo.copyBytesPerSecond), - ui_get_display_size_units(importData->importInfo.copyBytesPerSecond), + ui_get_display_size(importData->importInfo.bytesPerSecond), + ui_get_display_size_units(importData->importInfo.bytesPerSecond), ui_get_display_eta(importData->importInfo.estimatedRemainingSeconds)); } @@ -164,7 +164,7 @@ void action_import_twl_save(linked_list* items, list_item* selected) { data->importInfo.op = DATAOP_COPY; - data->importInfo.copyBufferSize = 16 * 1024; + data->importInfo.bufferSize = 16 * 1024; data->importInfo.copyEmpty = true; data->importInfo.total = 1; @@ -181,8 +181,8 @@ void action_import_twl_save(linked_list* items, list_item* selected) { data->importInfo.closeDst = action_import_twl_save_close_dst; data->importInfo.writeDst = action_import_twl_save_write_dst; - data->importInfo.suspendCopy = action_import_twl_save_suspend_copy; - data->importInfo.restoreCopy = action_import_twl_save_restore_copy; + data->importInfo.suspendTransfer = action_import_twl_save_suspend_transfer; + data->importInfo.restoreTransfer = action_import_twl_save_restore_transfer; data->importInfo.suspend = action_import_twl_save_suspend; data->importInfo.restore = action_import_twl_save_restore; diff --git a/source/ui/section/action/installcdn.c b/source/ui/section/action/installcdn.c index febfa83..8c65cf1 100644 --- a/source/ui/section/action/installcdn.c +++ b/source/ui/section/action/installcdn.c @@ -31,8 +31,6 @@ typedef struct { u16 contentIndices[CONTENTS_MAX]; u32 contentIds[CONTENTS_MAX]; - u32 responseCode; - data_op_data installInfo; } install_cdn_data; @@ -63,7 +61,7 @@ static Result action_install_cdn_open_src(void* data, u32 index, u32* handle) { snprintf(url, 256, "http://ccs.cdn.c.shop.nintendowifi.net/ccs/download/%016llX/%08lX", installData->ticket->titleId, installData->contentIds[index - 1]); } - if(R_SUCCEEDED(res = util_http_open(context, &installData->responseCode, url, false))) { + if(R_SUCCEEDED(res = util_http_open(context, url, false))) { *handle = (u32) context; } else { free(context); @@ -147,7 +145,7 @@ static Result action_install_cdn_write_dst(void* data, u32 handle, u32* bytesWri return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0); } -static Result action_install_cdn_suspend_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result action_install_cdn_suspend_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { if(index > 0 && *dstHandle != 0) { return AM_InstallContentStop(*dstHandle); } else { @@ -155,7 +153,7 @@ static Result action_install_cdn_suspend_copy(void* data, u32 index, u32* srcHan } } -static Result action_install_cdn_restore_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result action_install_cdn_restore_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { install_cdn_data* installData = (install_cdn_data*) data; if(index > 0 && *dstHandle != 0) { @@ -178,12 +176,7 @@ static Result action_install_cdn_restore(void* data, u32 index) { bool action_install_cdn_error(void* data, u32 index, Result res, ui_view** errorView) { install_cdn_data* installData = (install_cdn_data*) data; - const char* itemType = index == 0 ? "TMD" : "content"; - if(res == R_FBI_HTTP_RESPONSE_CODE) { - *errorView = error_display(installData->ticket, ui_draw_ticket_info, "Failed to install %s from CDN.\nHTTP server returned response code %d", itemType, installData->responseCode); - } else { - *errorView = error_display_res(installData->ticket, ui_draw_ticket_info, res, "Failed to install %s from CDN.", itemType); - } + *errorView = error_display_res(installData->ticket, ui_draw_ticket_info, res, "Failed to install %s from CDN.", index == 0 ? "TMD" : "content"); return false; } @@ -212,7 +205,7 @@ static void action_install_cdn_update(ui_view* view, void* data, float* progress if(R_SUCCEEDED(installData->installInfo.result)) { if(R_SUCCEEDED(res = AM_InstallTitleFinish()) && R_SUCCEEDED(res = AM_CommitImportTitles(util_get_title_destination(installData->ticket->titleId), 1, false, &installData->ticket->titleId))) { - util_import_seed(NULL, installData->ticket->titleId); + task_download_seed_sync(installData->ticket->titleId); if(installData->ticket->titleId == 0x0004013800000002 || installData->ticket->titleId == 0x0004013820000002) { res = AM_InstallFirm(installData->ticket->titleId); @@ -251,8 +244,8 @@ static void action_install_cdn_update(ui_view* view, void* data, float* progress ui_get_display_size_units(installData->installInfo.currProcessed), ui_get_display_size(installData->installInfo.currTotal), ui_get_display_size_units(installData->installInfo.currTotal), - ui_get_display_size(installData->installInfo.copyBytesPerSecond), - ui_get_display_size_units(installData->installInfo.copyBytesPerSecond), + ui_get_display_size(installData->installInfo.bytesPerSecond), + ui_get_display_size_units(installData->installInfo.bytesPerSecond), ui_get_display_eta(installData->installInfo.estimatedRemainingSeconds)); } @@ -323,13 +316,11 @@ void action_install_cdn_noprompt_internal(volatile bool* done, ticket_info* info memset(data->contentIndices, 0, sizeof(data->contentIndices)); memset(data->contentIds, 0, sizeof(data->contentIds)); - data->responseCode = 0; - data->installInfo.data = data; data->installInfo.op = DATAOP_COPY; - data->installInfo.copyBufferSize = 128 * 1024; + data->installInfo.bufferSize = 128 * 1024; data->installInfo.copyEmpty = false; data->installInfo.total = 1; @@ -346,8 +337,8 @@ void action_install_cdn_noprompt_internal(volatile bool* done, ticket_info* info data->installInfo.closeDst = action_install_cdn_close_dst; data->installInfo.writeDst = action_install_cdn_write_dst; - data->installInfo.suspendCopy = action_install_cdn_suspend_copy; - data->installInfo.restoreCopy = action_install_cdn_restore_copy; + data->installInfo.suspendTransfer = action_install_cdn_suspend_transfer; + data->installInfo.restoreTransfer = action_install_cdn_restore_transfer; data->installInfo.suspend = action_install_cdn_suspend; data->installInfo.restore = action_install_cdn_restore; diff --git a/source/ui/section/action/installcias.c b/source/ui/section/action/installcias.c index 8b4b4a2..55bbec9 100644 --- a/source/ui/section/action/installcias.c +++ b/source/ui/section/action/installcias.c @@ -161,7 +161,7 @@ static Result action_install_cias_close_dst(void* data, u32 index, bool succeede Result res = 0; if(R_SUCCEEDED(res = AM_FinishCiaInstall(handle))) { - util_import_seed(NULL, info->ciaInfo.titleId); + task_download_seed_sync(info->ciaInfo.titleId); if((info->ciaInfo.titleId & 0xFFFFFFF) == 0x0000002) { res = AM_InstallFirm(info->ciaInfo.titleId); @@ -178,11 +178,11 @@ static Result action_install_cias_write_dst(void* data, u32 handle, u32* bytesWr return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0); } -static Result action_install_cias_suspend_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result action_install_cias_suspend_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { return 0; } -static Result action_install_cias_restore_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result action_install_cias_restore_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { return 0; } @@ -242,8 +242,8 @@ static void action_install_cias_update(ui_view* view, void* data, float* progres ui_get_display_size_units(installData->installInfo.currProcessed), ui_get_display_size(installData->installInfo.currTotal), ui_get_display_size_units(installData->installInfo.currTotal), - ui_get_display_size(installData->installInfo.copyBytesPerSecond), - ui_get_display_size_units(installData->installInfo.copyBytesPerSecond), + ui_get_display_size(installData->installInfo.bytesPerSecond), + ui_get_display_size_units(installData->installInfo.bytesPerSecond), ui_get_display_eta(installData->installInfo.estimatedRemainingSeconds)); } @@ -334,7 +334,7 @@ static void action_install_cias_internal(linked_list* items, list_item* selected data->installInfo.op = DATAOP_COPY; - data->installInfo.copyBufferSize = 256 * 1024; + data->installInfo.bufferSize = 256 * 1024; data->installInfo.copyEmpty = false; data->installInfo.isSrcDirectory = action_install_cias_is_src_directory; @@ -349,8 +349,8 @@ static void action_install_cias_internal(linked_list* items, list_item* selected data->installInfo.closeDst = action_install_cias_close_dst; data->installInfo.writeDst = action_install_cias_write_dst; - data->installInfo.suspendCopy = action_install_cias_suspend_copy; - data->installInfo.restoreCopy = action_install_cias_restore_copy; + data->installInfo.suspendTransfer = action_install_cias_suspend_transfer; + data->installInfo.restoreTransfer = action_install_cias_restore_transfer; data->installInfo.suspend = action_install_cias_suspend; data->installInfo.restore = action_install_cias_restore; diff --git a/source/ui/section/action/installtickets.c b/source/ui/section/action/installtickets.c index a1770f1..c62180a 100644 --- a/source/ui/section/action/installtickets.c +++ b/source/ui/section/action/installtickets.c @@ -141,11 +141,11 @@ static Result action_install_tickets_write_dst(void* data, u32 handle, u32* byte return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0); } -static Result action_install_tickets_suspend_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result action_install_tickets_suspend_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { return 0; } -static Result action_install_tickets_restore_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result action_install_tickets_restore_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { return 0; } @@ -205,8 +205,8 @@ static void action_install_tickets_update(ui_view* view, void* data, float* prog ui_get_display_size_units(installData->installInfo.currProcessed), ui_get_display_size(installData->installInfo.currTotal), ui_get_display_size_units(installData->installInfo.currTotal), - ui_get_display_size(installData->installInfo.copyBytesPerSecond), - ui_get_display_size_units(installData->installInfo.copyBytesPerSecond), + ui_get_display_size(installData->installInfo.bytesPerSecond), + ui_get_display_size_units(installData->installInfo.bytesPerSecond), ui_get_display_eta(installData->installInfo.estimatedRemainingSeconds)); } @@ -308,7 +308,7 @@ static void action_install_tickets_internal(linked_list* items, list_item* selec data->installInfo.op = DATAOP_COPY; - data->installInfo.copyBufferSize = 256 * 1024; + data->installInfo.bufferSize = 256 * 1024; data->installInfo.copyEmpty = false; data->installInfo.isSrcDirectory = action_install_tickets_is_src_directory; @@ -323,8 +323,8 @@ static void action_install_tickets_internal(linked_list* items, list_item* selec data->installInfo.closeDst = action_install_tickets_close_dst; data->installInfo.writeDst = action_install_tickets_write_dst; - data->installInfo.suspendCopy = action_install_tickets_suspend_copy; - data->installInfo.restoreCopy = action_install_tickets_restore_copy; + data->installInfo.suspendTransfer = action_install_tickets_suspend_transfer; + data->installInfo.restoreTransfer = action_install_tickets_restore_transfer; data->installInfo.suspend = action_install_tickets_suspend; data->installInfo.restore = action_install_tickets_restore; diff --git a/source/ui/section/action/installtitledb.c b/source/ui/section/action/installtitledb.c index c1d1fad..93d9cbc 100644 --- a/source/ui/section/action/installtitledb.c +++ b/source/ui/section/action/installtitledb.c @@ -48,9 +48,9 @@ void action_install_titledb(linked_list* items, list_item* selected, bool cia) { char url[64]; char path3dsx[FILE_PATH_MAX]; if(data->cia) { - snprintf(url, INSTALL_URL_MAX, "https://3ds.titledb.com/v1/cia/%lu/download", info->cia.id); + snprintf(url, DOWNLOAD_URL_MAX, "https://3ds.titledb.com/v1/cia/%lu/download", info->cia.id); } else { - snprintf(url, INSTALL_URL_MAX, "https://3ds.titledb.com/v1/tdsx/%lu/download", info->tdsx.id); + snprintf(url, DOWNLOAD_URL_MAX, "https://3ds.titledb.com/v1/tdsx/%lu/download", info->tdsx.id); char name[FILE_NAME_MAX]; util_escape_file_name(name, info->meta.shortDescription, sizeof(name)); diff --git a/source/ui/section/action/installurl.c b/source/ui/section/action/installurl.c index 4e52f74..cddc126 100644 --- a/source/ui/section/action/installurl.c +++ b/source/ui/section/action/installurl.c @@ -23,7 +23,7 @@ typedef enum content_type_e { } content_type; typedef struct { - char urls[INSTALL_URLS_MAX][INSTALL_URL_MAX]; + char urls[INSTALL_URLS_MAX][DOWNLOAD_URL_MAX]; char path3dsx[FILE_PATH_MAX]; @@ -35,7 +35,6 @@ typedef struct { bool selectCdnVersion; bool cdnDecided; - u32 responseCode; content_type contentType; u64 currTitleId; volatile bool n3dsContinue; @@ -116,7 +115,7 @@ static Result action_install_url_open_src(void* data, u32 index, u32* handle) { httpcContext* context = (httpcContext*) calloc(1, sizeof(httpcContext)); if(context != NULL) { - if(R_SUCCEEDED(res = util_http_open(context, &installData->responseCode, installData->urls[index], true))) { + if(R_SUCCEEDED(res = util_http_open(context, installData->urls[index], true))) { *handle = (u32) context; installData->currContext = context; @@ -148,12 +147,12 @@ static Result action_install_url_read_src(void* data, u32 handle, u32* bytesRead return util_http_read((httpcContext*) handle, bytesRead, buffer, size); } + static Result action_install_url_open_dst(void* data, u32 index, void* initialReadBlock, u64 size, u32* handle) { install_url_data* installData = (install_url_data*) data; Result res = 0; - installData->responseCode = 0; installData->contentType = CONTENT_CIA; installData->currTitleId = 0; installData->n3dsContinue = false; @@ -262,7 +261,7 @@ static Result action_install_url_close_dst(void* data, u32 index, bool succeeded if(succeeded) { if(installData->contentType == CONTENT_CIA) { if(R_SUCCEEDED(res = AM_FinishCiaInstall(handle))) { - util_import_seed(NULL, installData->currTitleId); + task_download_seed_sync(installData->currTitleId); if(installData->currTitleId == 0x0004013800000002 || installData->currTitleId == 0x0004013820000002) { res = AM_InstallFirm(installData->currTitleId); @@ -311,11 +310,11 @@ static Result action_install_url_write_dst(void* data, u32 handle, u32* bytesWri return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0); } -static Result action_install_url_suspend_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result action_install_url_suspend_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { return 0; } -static Result action_install_url_restore_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result action_install_url_restore_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { return 0; } @@ -332,18 +331,10 @@ static bool action_install_url_error(void* data, u32 index, Result res, ui_view* if(res != R_FBI_WRONG_SYSTEM) { char* url = installData->urls[index]; - if(res == R_FBI_HTTP_RESPONSE_CODE) { - if(strlen(url) > 38) { - *errorView = error_display(data, action_install_url_draw_top, "Failed to install from URL.\n%.35s...\nHTTP server returned response code %d", url, installData->responseCode); - } else { - *errorView = error_display(data, action_install_url_draw_top, "Failed to install from URL.\n%.38s\nHTTP server returned response code %d", url, installData->responseCode); - } + if(strlen(url) > 38) { + *errorView = error_display_res(data, action_install_url_draw_top, res, "Failed to install from URL.\n%.35s...", url); } else { - if(strlen(url) > 38) { - *errorView = error_display_res(data, action_install_url_draw_top, res, "Failed to install from URL.\n%.35s...", url); - } else { - *errorView = error_display_res(data, action_install_url_draw_top, res, "Failed to install from URL.\n%.38s", url); - } + *errorView = error_display_res(data, action_install_url_draw_top, res, "Failed to install from URL.\n%.38s", url); } } @@ -376,8 +367,8 @@ static void action_install_url_install_update(ui_view* view, void* data, float* ui_get_display_size_units(installData->installInfo.currProcessed), ui_get_display_size(installData->installInfo.currTotal), ui_get_display_size_units(installData->installInfo.currTotal), - ui_get_display_size(installData->installInfo.copyBytesPerSecond), - ui_get_display_size_units(installData->installInfo.copyBytesPerSecond), + ui_get_display_size(installData->installInfo.bytesPerSecond), + ui_get_display_size_units(installData->installInfo.bytesPerSecond), ui_get_display_eta(installData->installInfo.estimatedRemainingSeconds)); } @@ -421,15 +412,15 @@ void action_install_url(const char* confirmMessage, const char* urls, const char u32 len = currEnd - currStart; if((len < 7 || strncmp(currStart, "http://", 7) != 0) && (len < 8 || strncmp(currStart, "https://", 8) != 0)) { - if(len > INSTALL_URL_MAX - 7) { - len = INSTALL_URL_MAX - 7; + if(len > DOWNLOAD_URL_MAX - 7) { + len = DOWNLOAD_URL_MAX - 7; } strncpy(data->urls[data->installInfo.total], "http://", 7); strncpy(&data->urls[data->installInfo.total][7], currStart, len); } else { - if(len > INSTALL_URL_MAX) { - len = INSTALL_URL_MAX; + if(len > DOWNLOAD_URL_MAX) { + len = DOWNLOAD_URL_MAX; } strncpy(data->urls[data->installInfo.total], currStart, len); @@ -452,7 +443,6 @@ void action_install_url(const char* confirmMessage, const char* urls, const char data->selectCdnVersion = false; data->cdnDecided = false; - data->responseCode = 0; data->contentType = CONTENT_CIA; data->currTitleId = 0; data->n3dsContinue = false; @@ -461,9 +451,11 @@ void action_install_url(const char* confirmMessage, const char* urls, const char data->installInfo.data = data; - data->installInfo.op = DATAOP_COPY; + data->installInfo.op = DATAOP_DOWNLOAD; - data->installInfo.copyBufferSize = 128 * 1024; + data->installInfo.downloadUrls = data->urls; + + data->installInfo.bufferSize = 128 * 1024; data->installInfo.copyEmpty = false; data->installInfo.processed = data->installInfo.total; @@ -480,8 +472,8 @@ void action_install_url(const char* confirmMessage, const char* urls, const char data->installInfo.closeDst = action_install_url_close_dst; data->installInfo.writeDst = action_install_url_write_dst; - data->installInfo.suspendCopy = action_install_url_suspend_copy; - data->installInfo.restoreCopy = action_install_url_restore_copy; + data->installInfo.suspendTransfer = action_install_url_suspend_transfer; + data->installInfo.restoreTransfer = action_install_url_restore_transfer; data->installInfo.suspend = action_install_url_suspend; data->installInfo.restore = action_install_url_restore; diff --git a/source/ui/section/action/pastecontents.c b/source/ui/section/action/pastecontents.c index 9370ee1..fa08222 100644 --- a/source/ui/section/action/pastecontents.c +++ b/source/ui/section/action/pastecontents.c @@ -210,11 +210,11 @@ static Result action_paste_contents_write_dst(void* data, u32 handle, u32* bytes return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0); } -static Result action_paste_contents_suspend_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result action_paste_contents_suspend_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { return 0; } -static Result action_paste_contents_restore_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result action_paste_contents_restore_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { return 0; } @@ -274,8 +274,8 @@ static void action_paste_contents_update(ui_view* view, void* data, float* progr ui_get_display_size_units(pasteData->pasteInfo.currProcessed), ui_get_display_size(pasteData->pasteInfo.currTotal), ui_get_display_size_units(pasteData->pasteInfo.currTotal), - ui_get_display_size(pasteData->pasteInfo.copyBytesPerSecond), - ui_get_display_size_units(pasteData->pasteInfo.copyBytesPerSecond), + ui_get_display_size(pasteData->pasteInfo.bytesPerSecond), + ui_get_display_size_units(pasteData->pasteInfo.bytesPerSecond), ui_get_display_eta(pasteData->pasteInfo.estimatedRemainingSeconds)); } @@ -364,7 +364,7 @@ void action_paste_contents(linked_list* items, list_item* selected) { data->pasteInfo.op = DATAOP_COPY; - data->pasteInfo.copyBufferSize = 256 * 1024; + data->pasteInfo.bufferSize = 256 * 1024; data->pasteInfo.copyEmpty = true; data->pasteInfo.isSrcDirectory = action_paste_contents_is_src_directory; @@ -379,8 +379,8 @@ void action_paste_contents(linked_list* items, list_item* selected) { data->pasteInfo.closeDst = action_paste_contents_close_dst; data->pasteInfo.writeDst = action_paste_contents_write_dst; - data->pasteInfo.suspendCopy = action_paste_contents_suspend_copy; - data->pasteInfo.restoreCopy = action_paste_contents_restore_copy; + data->pasteInfo.suspendTransfer = action_paste_contents_suspend_transfer; + data->pasteInfo.restoreTransfer = action_paste_contents_restore_transfer; data->pasteInfo.suspend = action_paste_contents_suspend; data->pasteInfo.restore = action_paste_contents_restore; diff --git a/source/ui/section/dumpnand.c b/source/ui/section/dumpnand.c index 764ffcc..6005f3b 100644 --- a/source/ui/section/dumpnand.c +++ b/source/ui/section/dumpnand.c @@ -76,11 +76,11 @@ static Result dumpnand_write_dst(void* data, u32 handle, u32* bytesWritten, void return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0); } -static Result dumpnand_suspend_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result dumpnand_suspend_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { return 0; } -static Result dumpnand_restore_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result dumpnand_restore_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { return 0; } @@ -121,7 +121,7 @@ static void dumpnand_update(ui_view* view, void* data, float* progress, char* te snprintf(text, PROGRESS_TEXT_MAX, "%.2f %s / %.2f %s\n%.2f %s/s, ETA %s", ui_get_display_size(dumpData->currProcessed), ui_get_display_size_units(dumpData->currProcessed), ui_get_display_size(dumpData->currTotal), ui_get_display_size_units(dumpData->currTotal), - ui_get_display_size(dumpData->copyBytesPerSecond), ui_get_display_size_units(dumpData->copyBytesPerSecond), + ui_get_display_size(dumpData->bytesPerSecond), ui_get_display_size_units(dumpData->bytesPerSecond), ui_get_display_eta(dumpData->estimatedRemainingSeconds)); } @@ -153,7 +153,7 @@ void dumpnand_open() { data->op = DATAOP_COPY; - data->copyBufferSize = 256 * 1024; + data->bufferSize = 256 * 1024; data->copyEmpty = true; data->total = 1; @@ -170,8 +170,8 @@ void dumpnand_open() { data->closeDst = dumpnand_close_dst; data->writeDst = dumpnand_write_dst; - data->suspendCopy = dumpnand_suspend_copy; - data->restoreCopy = dumpnand_restore_copy; + data->suspendTransfer = dumpnand_suspend_transfer; + data->restoreTransfer = dumpnand_restore_transfer; data->suspend = dumpnand_suspend; data->restore = dumpnand_restore; diff --git a/source/ui/section/remoteinstall.c b/source/ui/section/remoteinstall.c index 2915250..ecb162b 100644 --- a/source/ui/section/remoteinstall.c +++ b/source/ui/section/remoteinstall.c @@ -10,6 +10,7 @@ #include "section.h" #include "action/action.h" +#include "task/uitask.h" #include "../error.h" #include "../info.h" #include "../kbd.h" @@ -158,7 +159,7 @@ static void remoteinstall_network_update(ui_view* view, void* data, float* progr } size = ntohl(size); - if(size >= INSTALL_URL_MAX * INSTALL_URLS_MAX) { + if(size >= DOWNLOAD_URL_MAX * INSTALL_URLS_MAX) { error_display(NULL, NULL, "Payload too large."); remoteinstall_network_close_client(data); @@ -250,7 +251,6 @@ static void remoteinstall_receive_urls_network() { #define QR_IMAGE_HEIGHT 240 typedef struct { - struct quirc* qrContext; u32 tex; bool capturing; @@ -285,11 +285,6 @@ static void remoteinstall_qr_free_data(remoteinstall_qr_data* data) { data->tex = 0; } - if(data->qrContext != NULL) { - quirc_destroy(data->qrContext); - data->qrContext = NULL; - } - free(data); } @@ -353,39 +348,17 @@ static void remoteinstall_qr_update(ui_view* view, void* data, float* progress, return; } - int w = 0; - int h = 0; - uint8_t* qrBuf = quirc_begin(installData->qrContext, &w, &h); - svcWaitSynchronization(installData->captureInfo.mutex, U64_MAX); - for(int x = 0; x < w; x++) { - for(int y = 0; y < h; y++) { - u16 px = installData->captureInfo.buffer[y * QR_IMAGE_WIDTH + x]; - qrBuf[y * w + x] = (u8) (((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3); - } - } + if(installData->captureInfo.qrReady) { + remoteinstall_set_last_urls((const char*) installData->captureInfo.qrData); + action_install_url("Install from the scanned QR code?", (const char*) installData->captureInfo.qrData, NULL, NULL, NULL, NULL); + installData->captureInfo.qrReady = false; + svcReleaseMutex(installData->captureInfo.mutex); - svcReleaseMutex(installData->captureInfo.mutex); - - quirc_end(installData->qrContext); - - int qrCount = quirc_count(installData->qrContext); - for(int i = 0; i < qrCount; i++) { - struct quirc_code qrCode; - quirc_extract(installData->qrContext, i, &qrCode); - - struct quirc_data qrData; - quirc_decode_error_t err = quirc_decode(&qrCode, &qrData); - - if(err == 0) { - remoteinstall_qr_stop_capture(installData); - - remoteinstall_set_last_urls((const char*) qrData.payload); - - action_install_url("Install from the scanned QR code?", (const char*) qrData.payload, NULL, NULL, NULL, NULL); - return; - } + remoteinstall_qr_stop_capture(installData); + } else { + svcReleaseMutex(installData->captureInfo.mutex); } snprintf(text, PROGRESS_TEXT_MAX, "Waiting for QR code..."); @@ -408,23 +381,10 @@ static void remoteinstall_scan_qr_code() { data->captureInfo.camera = CAMERA_OUTER; + data->captureInfo.scanQR = true; + data->captureInfo.finished = true; - data->qrContext = quirc_new(); - if(data->qrContext == NULL) { - error_display(NULL, NULL, "Failed to create QR context."); - - remoteinstall_qr_free_data(data); - return; - } - - if(quirc_resize(data->qrContext, QR_IMAGE_WIDTH, QR_IMAGE_HEIGHT) != 0) { - error_display(NULL, NULL, "Failed to resize QR context."); - - remoteinstall_qr_free_data(data); - return; - } - data->captureInfo.buffer = (u16*) calloc(1, QR_IMAGE_WIDTH * QR_IMAGE_HEIGHT * sizeof(u16)); if(data->captureInfo.buffer == NULL) { error_display(NULL, NULL, "Failed to create image buffer."); @@ -447,13 +407,13 @@ static void remoteinstall_manually_enter_urls_onresponse(ui_view* view, void* da } static void remoteinstall_manually_enter_urls() { - kbd_display("Enter URL(s)", "", SWKBD_TYPE_NORMAL, SWKBD_MULTILINE, SWKBD_NOTEMPTY_NOTBLANK, INSTALL_URL_MAX * INSTALL_URLS_MAX, NULL, remoteinstall_manually_enter_urls_onresponse); + kbd_display("Enter URL(s)", "", SWKBD_TYPE_NORMAL, SWKBD_MULTILINE, SWKBD_NOTEMPTY_NOTBLANK, DOWNLOAD_URL_MAX * INSTALL_URLS_MAX, NULL, remoteinstall_manually_enter_urls_onresponse); } static void remoteinstall_repeat_last_request() { - char* textBuf = (char*) calloc(1, INSTALL_URL_MAX * INSTALL_URLS_MAX); + char* textBuf = (char*) calloc(1, DOWNLOAD_URL_MAX * INSTALL_URLS_MAX); if(textBuf != NULL) { - if(remoteinstall_get_last_urls(textBuf, INSTALL_URL_MAX * INSTALL_URLS_MAX)) { + if(remoteinstall_get_last_urls(textBuf, DOWNLOAD_URL_MAX * INSTALL_URLS_MAX)) { action_install_url("Install from the last requested URL(s)?", textBuf, NULL, NULL, NULL, NULL); } else { prompt_display_notify("Failure", "No previously requested URL(s) could be found.", COLOR_TEXT, NULL, NULL, NULL); diff --git a/source/ui/section/task/dataop.c b/source/ui/section/task/dataop.c index 2319587..8f5d3d9 100644 --- a/source/ui/section/task/dataop.c +++ b/source/ui/section/task/dataop.c @@ -2,6 +2,8 @@ #include #include <3ds.h> +#include +#include #include "uitask.h" #include "../../prompt.h" @@ -19,8 +21,8 @@ static Result task_data_op_check_running(data_op_data* data, u32 index, u32* src } else { bool suspended = svcWaitSynchronization(task_get_suspend_event(), 0) != 0; if(suspended) { - if(data->op == DATAOP_COPY && srcHandle != NULL && dstHandle != NULL && data->suspendCopy != NULL && R_SUCCEEDED(res)) { - res = data->suspendCopy(data->data, index, srcHandle, dstHandle); + if(data->op == DATAOP_COPY && srcHandle != NULL && dstHandle != NULL && data->suspendTransfer != NULL && R_SUCCEEDED(res)) { + res = data->suspendTransfer(data->data, index, srcHandle, dstHandle); } if(data->suspend != NULL && R_SUCCEEDED(res)) { @@ -35,8 +37,8 @@ static Result task_data_op_check_running(data_op_data* data, u32 index, u32* src res = data->restore(data->data, index); } - if(data->op == DATAOP_COPY && srcHandle != NULL && dstHandle != NULL && data->restoreCopy != NULL && R_SUCCEEDED(res)) { - res = data->restoreCopy(data->data, index, srcHandle, dstHandle); + if(data->op == DATAOP_COPY && srcHandle != NULL && dstHandle != NULL && data->restoreTransfer != NULL && R_SUCCEEDED(res)) { + res = data->restoreTransfer(data->data, index, srcHandle, dstHandle); } } } @@ -48,7 +50,8 @@ static Result task_data_op_copy(data_op_data* data, u32 index) { data->currProcessed = 0; data->currTotal = 0; - data->copyBytesPerSecond = 0; + data->bytesPerSecond = 0; + data->estimatedRemainingSeconds = 0; Result res = 0; @@ -69,7 +72,7 @@ static Result task_data_op_copy(data_op_data* data, u32 index) { res = R_FBI_BAD_DATA; } } else { - u8* buffer = (u8*) calloc(1, data->copyBufferSize); + u8* buffer = (u8*) calloc(1, data->bufferSize); if(buffer != NULL) { u32 dstHandle = 0; @@ -84,7 +87,7 @@ static Result task_data_op_copy(data_op_data* data, u32 index) { } u32 bytesRead = 0; - if(R_FAILED(res = data->readSrc(data->data, srcHandle, &bytesRead, buffer, data->currProcessed, data->copyBufferSize))) { + if(R_FAILED(res = data->readSrc(data->data, srcHandle, &bytesRead, buffer, data->currProcessed, data->bufferSize))) { break; } @@ -107,15 +110,16 @@ static Result task_data_op_copy(data_op_data* data, u32 index) { u64 time = osGetTime(); u64 elapsed = time - lastBytesPerSecondUpdate; if(elapsed >= 1000) { - data->copyBytesPerSecond = (u32) (bytesSinceUpdate / (elapsed / 1000.0f)); + data->bytesPerSecond = (u32) (bytesSinceUpdate / (elapsed / 1000.0f)); - if (ioStartTime != 0) { - data->estimatedRemainingSeconds = (u32) ((data->currTotal - data->currProcessed) / (data->currProcessed / ((time - ioStartTime) / 1000.0f))); + if(ioStartTime != 0) { + data->estimatedRemainingSeconds = (u32) ((data->currTotal - data->currProcessed) / (data->currProcessed / ((time - ioStartTime) / 1000.0f))); } else { - data->estimatedRemainingSeconds = 0; + data->estimatedRemainingSeconds = 0; } - if (ioStartTime == 0 && data->currProcessed > 0) { - ioStartTime = time; + + if(ioStartTime == 0 && data->currProcessed > 0) { + ioStartTime = time; } bytesSinceUpdate = 0; @@ -147,6 +151,318 @@ static Result task_data_op_copy(data_op_data* data, u32 index) { return res; } +extern FILE* dbg; + +static Result task_download_execute(const char* url, void* data, size_t write_callback(char* ptr, size_t size, size_t nmemb, void* userdata), + int progress_callback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)) { + Result res = 0; + + CURL* curl = curl_easy_init(); + if(curl != NULL) { + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, data); + curl_easy_setopt(curl, CURLOPT_USERAGENT, HTTP_USER_AGENT); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true); + curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT); + + if(progress_callback != NULL) { + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); + curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback); + curl_easy_setopt(curl, CURLOPT_XFERINFODATA, data); + } + + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false); // TODO: Certificates? + + curl_easy_setopt(curl, CURLOPT_VERBOSE, true); + curl_easy_setopt(curl, CURLOPT_STDERR, dbg); + + CURLcode ret = curl_easy_perform(curl); + if(ret == CURLE_OK) { + long responseCode; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode); + + if(responseCode >= 400) { + return R_FBI_HTTP_ERROR_BASE + ret; + } + } else { + res = R_FBI_CURL_ERROR_BASE + ret; + } + + curl_easy_cleanup(curl); + } else { + res = R_FBI_CURL_INIT_FAILED; + } + + return res; +} + +typedef struct { + u8* buf; + u32 size; + + u32 pos; +} download_sync_data; + +static size_t task_download_sync_write_callback(char* ptr, size_t size, size_t nmemb, void* userdata) { + download_sync_data* data = (download_sync_data*) userdata; + + size_t realSize = size * nmemb; + size_t remaining = data->size - data->pos; + size_t copy = realSize < remaining ? realSize : remaining; + + memcpy(&data->buf[data->pos], ptr, copy); + data->pos += copy; + + return copy; +} + +Result task_download_sync(const char* url, u32* downloadedSize, void* buf, size_t size) { + if(url == NULL || buf == NULL) { + return R_FBI_INVALID_ARGUMENT; + } + + Result res = 0; + + download_sync_data readData = {buf, size, 0}; + if(R_SUCCEEDED(res = task_download_execute(url, &readData, task_download_sync_write_callback, NULL))) { + if(downloadedSize != NULL) { + *downloadedSize = readData.pos; + } + } + + return res; +} + +Result task_download_json_sync(const char* url, json_t** json, size_t maxSize) { + if(url == NULL || json == NULL) { + return R_FBI_INVALID_ARGUMENT; + } + + Result res = 0; + + char* text = (char*) calloc(sizeof(char), maxSize); + if(text != NULL) { + u32 textSize = 0; + if(R_SUCCEEDED(res = task_download_sync(url, &textSize, text, maxSize))) { + json_error_t error; + json_t* parsed = json_loads(text, 0, &error); + if(parsed != NULL) { + *json = parsed; + } else { + res = R_FBI_PARSE_FAILED; + } + } + + free(text); + } else { + res = R_FBI_OUT_OF_MEMORY; + } + + return res; +} + +static Result FSUSER_AddSeed(u64 titleId, const void* seed) { + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x087A0180; + cmdbuf[1] = (u32) (titleId & 0xFFFFFFFF); + cmdbuf[2] = (u32) (titleId >> 32); + memcpy(&cmdbuf[3], seed, 16); + + Result ret = 0; + if(R_FAILED(ret = svcSendSyncRequest(*fsGetSessionHandle()))) return ret; + + ret = cmdbuf[1]; + return ret; +} + +Result task_download_seed_sync(u64 titleId) { + char pathBuf[64]; + snprintf(pathBuf, 64, "/fbi/seed/%016llX.dat", titleId); + + Result res = 0; + + FS_Path* fsPath = util_make_path_utf8(pathBuf); + if(fsPath != NULL) { + u8 seed[16]; + + Handle fileHandle = 0; + if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&fileHandle, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), *fsPath, FS_OPEN_READ, 0))) { + u32 bytesRead = 0; + res = FSFILE_Read(fileHandle, &bytesRead, 0, seed, sizeof(seed)); + + FSFILE_Close(fileHandle); + } + + util_free_path_utf8(fsPath); + + if(R_FAILED(res)) { + u8 region = CFG_REGION_USA; + CFGU_SecureInfoGetRegion(®ion); + + if(region <= CFG_REGION_TWN) { + static const char* regionStrings[] = {"JP", "US", "GB", "GB", "HK", "KR", "TW"}; + + char url[128]; + snprintf(url, 128, "https://kagiya-ctr.cdn.nintendo.net/title/0x%016llX/ext_key?country=%s", titleId, regionStrings[region]); + + u32 downloadedSize = 0; + if(R_SUCCEEDED(res = task_download_sync(url, &downloadedSize, seed, sizeof(seed))) && downloadedSize != sizeof(seed)) { + res = R_FBI_BAD_DATA; + } + } else { + res = R_FBI_OUT_OF_RANGE; + } + } + + if(R_SUCCEEDED(res)) { + res = FSUSER_AddSeed(titleId, seed); + } + } else { + res = R_FBI_OUT_OF_MEMORY; + } + + return res; +} + +typedef struct { + data_op_data* baseData; + u32 index; + + u8* buffer; + u32 bufferPos; + u32 bufferSize; + + u32 dstHandle; + + u32 bytesWritten; + u64 ioStartTime; + u64 lastBytesPerSecondUpdate; + u32 bytesSinceUpdate; + + Result res; +} data_op_download_data; + +static bool task_data_op_download_flush(data_op_download_data* data) { + if(data->dstHandle == 0 && R_FAILED(data->res = data->baseData->openDst(data->baseData->data, data->index, data->buffer, data->baseData->currTotal, &data->dstHandle))) { + return false; + } + + u32 bytesWritten = 0; + if(R_FAILED(data->res = data->baseData->writeDst(data->baseData->data, data->dstHandle, &bytesWritten, data->buffer, data->bytesWritten, data->bufferPos))) { + return false; + } + + data->bytesWritten += data->bufferPos; + data->bufferPos = 0; + + return true; +} + +static size_t task_data_op_download_write_callback(char* ptr, size_t size, size_t nmemb, void* userdata) { + data_op_download_data* data = (data_op_download_data*) userdata; + + size_t remaining = size * nmemb; + while(remaining > 0) { + // Buffering is done to provide adequate data to openDst and prevent misaligned size errors from AM. + if(data->bufferPos < data->bufferSize) { + size_t bufferRemaining = data->bufferSize - data->bufferPos; + size_t used = remaining < bufferRemaining ? remaining : bufferRemaining; + + memcpy(&data->buffer[data->bufferPos], ptr, used); + + data->bufferPos += used; + remaining -= used; + } + + if(data->bufferPos >= data->bufferSize) { + // TODO: Pause on suspend/Unpause on restore? + u32 srcHandle = 0; + if(R_FAILED(data->res = task_data_op_check_running(data->baseData, data->baseData->processed, &srcHandle, &data->dstHandle))) { + return 0; + } + + if(!task_data_op_download_flush(data)) { + break; + } + } + } + + return (size * nmemb) - remaining; +} + +int task_data_op_download_progress_callback(void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { + data_op_download_data* data = (data_op_download_data*) clientp; + + data->bytesSinceUpdate += (u64) dlnow - data->baseData->currProcessed; + data->baseData->currTotal = (u64) dltotal; + data->baseData->currProcessed = (u64) dlnow; + + u64 time = osGetTime(); + if(data->lastBytesPerSecondUpdate != 0) { + u64 elapsed = time - data->lastBytesPerSecondUpdate; + if(elapsed >= 1000) { + data->baseData->bytesPerSecond = (u32) (data->bytesSinceUpdate / (elapsed / 1000.0f)); + + if(data->ioStartTime != 0) { + data->baseData->estimatedRemainingSeconds = (u32) ((data->baseData->currTotal - data->baseData->currProcessed) / (data->baseData->currProcessed / ((time - data->ioStartTime) / 1000.0f))); + } else { + data->baseData->estimatedRemainingSeconds = 0; + } + + if(data->ioStartTime == 0 && data->baseData->currProcessed > 0) { + data->ioStartTime = time; + } + + data->bytesSinceUpdate = 0; + data->lastBytesPerSecondUpdate = time; + } + } else { + data->lastBytesPerSecondUpdate = time; + } + + return 0; +} + +static Result task_data_op_download(data_op_data* data, u32 index) { + data->currProcessed = 0; + data->currTotal = 0; + + data->bytesPerSecond = 0; + data->estimatedRemainingSeconds = 0; + + Result res = 0; + + void* buffer = calloc(1, data->bufferSize); + if(buffer != NULL) { + data_op_download_data downloadData = {data, index, buffer, 0, data->bufferSize, 0, 0, 0, 0, 0, 0}; + res = task_download_execute(data->downloadUrls[index], &downloadData, task_data_op_download_write_callback, task_data_op_download_progress_callback); + + if(downloadData.res != 0) { + res = downloadData.res; + } + + if(R_SUCCEEDED(res) && downloadData.bufferPos > 0) { + task_data_op_download_flush(&downloadData); + } + + if(downloadData.dstHandle != 0) { + Result closeDstRes = data->closeDst(data->data, index, res == 0, downloadData.dstHandle); + if(R_SUCCEEDED(res)) { + res = closeDstRes; + } + } + + free(buffer); + } else { + res = R_FBI_OUT_OF_MEMORY; + } + + return res; +} + static Result task_data_op_delete(data_op_data* data, u32 index) { return data->delete(data->data, index); } @@ -166,6 +482,9 @@ static void task_data_op_thread(void* arg) { case DATAOP_COPY: res = task_data_op_copy(data, data->processed); break; + case DATAOP_DOWNLOAD: + res = task_data_op_download(data, data->processed); + break; case DATAOP_DELETE: res = task_data_op_delete(data, data->processed); break; @@ -229,7 +548,7 @@ Result task_data_op(data_op_data* data) { Result res = 0; if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY))) { - if(threadCreate(task_data_op_thread, data, 0x10000, 0x18, 1, true) == NULL) { + if(threadCreate(task_data_op_thread, data, 0x10000, 0x18, 0, true) == NULL) { res = R_FBI_THREAD_CREATE_FAILED; } } diff --git a/source/ui/section/task/listextsavedata.c b/source/ui/section/task/listextsavedata.c index bb164f3..694eaf2 100644 --- a/source/ui/section/task/listextsavedata.c +++ b/source/ui/section/task/listextsavedata.c @@ -160,7 +160,7 @@ Result task_populate_ext_save_data(populate_ext_save_data_data* data) { Result res = 0; if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY))) { - if(threadCreate(task_populate_ext_save_data_thread, data, 0x10000, 0x19, 1, true) == NULL) { + if(threadCreate(task_populate_ext_save_data_thread, data, 0x10000, 0x19, 0, true) == NULL) { res = R_FBI_THREAD_CREATE_FAILED; } } diff --git a/source/ui/section/task/listfiles.c b/source/ui/section/task/listfiles.c index d5d1718..27cfeaa 100644 --- a/source/ui/section/task/listfiles.c +++ b/source/ui/section/task/listfiles.c @@ -317,7 +317,7 @@ Result task_populate_files(populate_files_data* data) { Result res = 0; if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY))) { - if(threadCreate(task_populate_files_thread, data, 0x10000, 0x19, 1, true) == NULL) { + if(threadCreate(task_populate_files_thread, data, 0x10000, 0x19, 0, true) == NULL) { res = R_FBI_THREAD_CREATE_FAILED; } } diff --git a/source/ui/section/task/listpendingtitles.c b/source/ui/section/task/listpendingtitles.c index 5617ca2..13041ba 100644 --- a/source/ui/section/task/listpendingtitles.c +++ b/source/ui/section/task/listpendingtitles.c @@ -138,7 +138,7 @@ Result task_populate_pending_titles(populate_pending_titles_data* data) { Result res = 0; if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY))) { - if(threadCreate(task_populate_pending_titles_thread, data, 0x10000, 0x19, 1, true) == NULL) { + if(threadCreate(task_populate_pending_titles_thread, data, 0x10000, 0x19, 0, true) == NULL) { res = R_FBI_THREAD_CREATE_FAILED; } } diff --git a/source/ui/section/task/listsystemsavedata.c b/source/ui/section/task/listsystemsavedata.c index cfeec7f..cc95854 100644 --- a/source/ui/section/task/listsystemsavedata.c +++ b/source/ui/section/task/listsystemsavedata.c @@ -106,7 +106,7 @@ Result task_populate_system_save_data(populate_system_save_data_data* data) { Result res = 0; if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY))) { - if(threadCreate(task_populate_system_save_data_thread, data, 0x10000, 0x19, 1, true) == NULL) { + if(threadCreate(task_populate_system_save_data_thread, data, 0x10000, 0x19, 0, true) == NULL) { res = R_FBI_THREAD_CREATE_FAILED; } } diff --git a/source/ui/section/task/listtickets.c b/source/ui/section/task/listtickets.c index d1259f5..28b4437 100644 --- a/source/ui/section/task/listtickets.c +++ b/source/ui/section/task/listtickets.c @@ -133,7 +133,7 @@ Result task_populate_tickets(populate_tickets_data* data) { Result res = 0; if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY))) { - if(threadCreate(task_populate_tickets_thread, data, 0x10000, 0x19, 1, true) == NULL) { + if(threadCreate(task_populate_tickets_thread, data, 0x10000, 0x19, 0, true) == NULL) { res = R_FBI_THREAD_CREATE_FAILED; } } diff --git a/source/ui/section/task/listtitledb.c b/source/ui/section/task/listtitledb.c index 1a734c2..4335540 100644 --- a/source/ui/section/task/listtitledb.c +++ b/source/ui/section/task/listtitledb.c @@ -70,10 +70,10 @@ static void task_populate_titledb_thread(void* arg) { Result res = 0; json_t* root = NULL; - if(R_SUCCEEDED(res = util_download_json("https://api.titledb.com/v1/entry?nested=true" - "&only=id&only=name&only=author&only=headline&only=category" - "&only=cia.id&only=cia.updated_at&only=cia.version&only=cia.size&only=cia.titleid" - "&only=tdsx.id&only=tdsx.updated_at&only=tdsx.version&only=tdsx.size&only=tdsx.smdh.id", + if(R_SUCCEEDED(res = task_download_json_sync("https://api.titledb.com/v1/entry?nested=true" + "&only=id&only=name&only=author&only=headline&only=category" + "&only=cia.id&only=cia.updated_at&only=cia.version&only=cia.size&only=cia.titleid" + "&only=tdsx.id&only=tdsx.updated_at&only=tdsx.version&only=tdsx.size&only=tdsx.smdh.id", &root, 1024 * 1024))) { if(json_is_array(root)) { linked_list titles; @@ -187,6 +187,11 @@ static void task_populate_titledb_thread(void* arg) { while(linked_list_iter_has_next(&iter)) { svcWaitSynchronization(task_get_pause_event(), U64_MAX); + + Handle events[2] = {data->resumeEvent, data->cancelEvent}; + s32 index = 0; + svcWaitSynchronizationN(&index, events, 2, false, U64_MAX); + if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { break; } @@ -205,13 +210,14 @@ static void task_populate_titledb_thread(void* arg) { u8 icon[0x1200]; u32 iconSize = 0; - if(R_SUCCEEDED(util_download(url, &iconSize, &icon, sizeof(icon))) && iconSize == sizeof(icon)) { + if(R_SUCCEEDED(task_download_sync(url, &iconSize, &icon, sizeof(icon))) && iconSize == sizeof(icon)) { titledbInfo->meta.texture = screen_allocate_free_texture(); screen_load_texture_tiled(titledbInfo->meta.texture, icon, sizeof(icon), 48, 48, GPU_RGB565, false); } } } + svcCloseHandle(data->resumeEvent); svcCloseHandle(data->cancelEvent); data->result = res; @@ -266,8 +272,12 @@ Result task_populate_titledb(populate_titledb_data* data) { Result res = 0; if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY))) { - if(threadCreate(task_populate_titledb_thread, data, 0x10000, 0x19, 1, true) == NULL) { - res = R_FBI_THREAD_CREATE_FAILED; + if(R_SUCCEEDED(res = svcCreateEvent(&data->resumeEvent, RESET_STICKY))) { + svcSignalEvent(data->resumeEvent); + + if(threadCreate(task_populate_titledb_thread, data, 0x10000, 0x19, 0, true) == NULL) { + res = R_FBI_THREAD_CREATE_FAILED; + } } } @@ -275,6 +285,11 @@ Result task_populate_titledb(populate_titledb_data* data) { data->itemsListed = true; data->finished = true; + if(data->resumeEvent != 0) { + svcCloseHandle(data->resumeEvent); + data->resumeEvent = 0; + } + if(data->cancelEvent != 0) { svcCloseHandle(data->cancelEvent); data->cancelEvent = 0; diff --git a/source/ui/section/task/listtitles.c b/source/ui/section/task/listtitles.c index d0f692f..e1a3023 100644 --- a/source/ui/section/task/listtitles.c +++ b/source/ui/section/task/listtitles.c @@ -338,7 +338,7 @@ Result task_populate_titles(populate_titles_data* data) { Result res = 0; if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY))) { - if(threadCreate(task_populate_titles_thread, data, 0x10000, 0x19, 1, true) == NULL) { + if(threadCreate(task_populate_titles_thread, data, 0x10000, 0x19, 0, true) == NULL) { res = R_FBI_THREAD_CREATE_FAILED; } } diff --git a/source/ui/section/task/uitask.h b/source/ui/section/task/uitask.h index d40d4a1..5a2666e 100644 --- a/source/ui/section/task/uitask.h +++ b/source/ui/section/task/uitask.h @@ -6,6 +6,10 @@ #define FILE_PATH_MAX 512 #endif +#define DOWNLOAD_URL_MAX 1024 + +typedef struct json_t json_t; + typedef struct linked_list_s linked_list; typedef struct list_item_s list_item; typedef struct ui_view_s ui_view; @@ -117,6 +121,7 @@ typedef struct titledb_info_s { typedef enum data_op_e { DATAOP_COPY, + DATAOP_DOWNLOAD, DATAOP_DELETE } data_op; @@ -125,18 +130,11 @@ typedef struct data_op_data_s { data_op op; - // Copy - u32 copyBufferSize; - bool copyEmpty; - - u32 copyBytesPerSecond; - u32 estimatedRemainingSeconds; - u32 processed; u32 total; - u64 currProcessed; - u64 currTotal; + // Copy + bool copyEmpty; Result (*isSrcDirectory)(void* data, u32 index, bool* isDirectory); Result (*makeDstDirectory)(void* data, u32 index); @@ -147,13 +145,25 @@ typedef struct data_op_data_s { Result (*getSrcSize)(void* data, u32 handle, u64* size); Result (*readSrc)(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size); + // Download + char (*downloadUrls)[DOWNLOAD_URL_MAX]; + + // Copy/Download + u64 currProcessed; + u64 currTotal; + + u32 bytesPerSecond; + u32 estimatedRemainingSeconds; + + u32 bufferSize; + Result (*openDst)(void* data, u32 index, void* initialReadBlock, u64 size, 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); - Result (*suspendCopy)(void* data, u32 index, u32* srcHandle, u32* dstHandle); - Result (*restoreCopy)(void* data, u32 index, u32* srcHandle, u32* dstHandle); + Result (*suspendTransfer)(void* data, u32 index, u32* srcHandle, u32* dstHandle); + Result (*restoreTransfer)(void* data, u32 index, u32* srcHandle, u32* dstHandle); // Delete Result (*delete)(void* data, u32 index); @@ -246,8 +256,12 @@ typedef struct populate_titledb_data_s { volatile bool finished; Result result; Handle cancelEvent; + Handle resumeEvent; } populate_titledb_data; +Result task_download_sync(const char* url, u32* downloadedSize, void* buf, size_t size); +Result task_download_json_sync(const char* url, json_t** json, size_t maxSize); +Result task_download_seed_sync(u64 titleId); Result task_data_op(data_op_data* data); void task_free_ext_save_data(list_item* item); diff --git a/source/ui/section/titledb.c b/source/ui/section/titledb.c index 7e63ed9..37296da 100644 --- a/source/ui/section/titledb.c +++ b/source/ui/section/titledb.c @@ -184,6 +184,8 @@ static void titledb_draw_top(ui_view* view, void* data, float x1, float y1, floa static void titledb_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { titledb_data* listData = (titledb_data*) data; + svcSignalEvent(listData->populateData.resumeEvent); + if(hidKeysDown() & KEY_B) { if(!listData->populateData.finished) { svcSignalEvent(listData->populateData.cancelEvent); @@ -225,6 +227,8 @@ static void titledb_update(ui_view* view, void* data, linked_list* items, list_i } if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { + svcClearEvent(listData->populateData.resumeEvent); + titledb_entry_open(items, selected); return; } diff --git a/source/ui/section/update.c b/source/ui/section/update.c index d46ef46..160274a 100644 --- a/source/ui/section/update.c +++ b/source/ui/section/update.c @@ -7,6 +7,7 @@ #include "section.h" #include "action/action.h" +#include "task/uitask.h" #include "../error.h" #include "../info.h" #include "../prompt.h" @@ -17,13 +18,12 @@ static void update_check_update(ui_view* view, void* data, float* progress, char* text) { bool hasUpdate = false; - char updateURL[INSTALL_URL_MAX]; + char updateURL[DOWNLOAD_URL_MAX]; Result res = 0; - u32 responseCode = 0; json_t* json = NULL; - if(R_SUCCEEDED(res = util_download_json("https://api.github.com/repos/Steveice10/FBI/releases/latest", &json, 16 * 1024))) { + if(R_SUCCEEDED(res = task_download_json_sync("https://api.github.com/repos/Steveice10/FBI/releases/latest", &json, 16 * 1024))) { if(json_is_object(json)) { json_t* name = json_object_get(json, "name"); json_t* assets = json_object_get(json, "assets"); @@ -51,7 +51,7 @@ static void update_check_update(ui_view* view, void* data, float* progress, char } if(url != NULL) { - strncpy(updateURL, url, INSTALL_URL_MAX); + strncpy(updateURL, url, DOWNLOAD_URL_MAX); hasUpdate = true; } else { res = R_FBI_BAD_DATA; @@ -74,11 +74,7 @@ static void update_check_update(ui_view* view, void* data, float* progress, char action_install_url("Update FBI to the latest version?", updateURL, util_get_3dsx_path(), NULL, NULL, NULL); } else { if(R_FAILED(res)) { - if(res == R_FBI_HTTP_RESPONSE_CODE) { - error_display(NULL, NULL, "Failed to check for update.\nHTTP server returned response code %d", responseCode); - } else { - error_display_res(NULL, NULL, res, "Failed to check for update."); - } + error_display_res(NULL, NULL, res, "Failed to check for update."); } else { prompt_display_notify("Success", "No updates available.", COLOR_TEXT, NULL, NULL, NULL); }