diff --git a/Makefile b/Makefile index 571f320..c513d72 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ SOURCE_DIRS := source ROMFS_DIR := romfs LIBRARY_DIRS += $(DEVKITPRO)/libctru $(DEVKITPRO)/portlibs/armv6k $(DEVKITPRO)/portlibs/3ds -LIBRARIES += jansson z citro3d ctru +LIBRARIES += curl mbedtls mbedx509 mbedcrypto jansson z citro3d ctru EXTRA_OUTPUT_FILES := servefiles diff --git a/source/core/error.h b/source/core/error.h index a983245..712293b 100644 --- a/source/core/error.h +++ b/source/core/error.h @@ -13,6 +13,10 @@ #define R_APP_HTTP_ERROR_BASE MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 8) #define R_APP_HTTP_ERROR_END (R_APP_HTTP_ERROR_BASE + 600) +#define R_APP_CURL_INIT_FAILED R_APP_HTTP_ERROR_END +#define R_APP_CURL_ERROR_BASE (R_APP_CURL_INIT_FAILED + 1) +#define R_APP_CURL_ERROR_END (R_APP_CURL_ERROR_BASE + 100) + #define R_APP_NOT_IMPLEMENTED MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, RD_NOT_IMPLEMENTED) #define R_APP_OUT_OF_MEMORY MAKERESULT(RL_FATAL, RS_OUTOFRESOURCE, RM_APPLICATION, RD_OUT_OF_MEMORY) #define R_APP_OUT_OF_RANGE MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_APPLICATION, RD_OUT_OF_RANGE) diff --git a/source/core/http.c b/source/core/http.c index 8f555e5..7456ae7 100644 --- a/source/core/http.c +++ b/source/core/http.c @@ -3,6 +3,7 @@ #include #include <3ds.h> +#include #include #include @@ -15,7 +16,8 @@ #define MAKE_HTTP_USER_AGENT(major, minor, micro) MAKE_HTTP_USER_AGENT_(major, minor, micro) #define HTTP_USER_AGENT MAKE_HTTP_USER_AGENT(VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO) -#define HTTP_TIMEOUT 15000000000 +#define HTTP_TIMEOUT_SEC 15 +#define HTTP_TIMEOUT_NS ((u64) HTTP_TIMEOUT_SEC * 1000000000) struct http_context_s { httpcContext httpc; @@ -89,7 +91,7 @@ Result http_open_ranged(http_context* context, const char* url, bool userAgent, && R_SUCCEEDED(res = httpcAddRequestHeaderField(&ctx->httpc, "Accept-Encoding", "gzip, deflate")) && R_SUCCEEDED(res = httpcSetKeepAlive(&ctx->httpc, HTTPC_KEEPALIVE_ENABLED)) && R_SUCCEEDED(res = httpcBeginRequest(&ctx->httpc)) - && R_SUCCEEDED(res = httpcGetResponseStatusCodeTimeout(&ctx->httpc, &response, HTTP_TIMEOUT))) { + && R_SUCCEEDED(res = httpcGetResponseStatusCodeTimeout(&ctx->httpc, &response, HTTP_TIMEOUT_NS))) { if(response == 301 || response == 302 || response == 303) { redirectCount++; @@ -218,7 +220,7 @@ Result http_read(http_context context, u32* bytesRead, void* buffer, u32 size) { u32 lastPos = context->bufferSize; while(res == HTTPC_RESULTCODE_DOWNLOADPENDING && outPos < size) { if((context->bufferSize > 0 - || R_SUCCEEDED(res = httpcReceiveDataTimeout(&context->httpc, &context->buffer[context->bufferSize], sizeof(context->buffer) - context->bufferSize, HTTP_TIMEOUT)) + || R_SUCCEEDED(res = httpcReceiveDataTimeout(&context->httpc, &context->buffer[context->bufferSize], sizeof(context->buffer) - context->bufferSize, HTTP_TIMEOUT_NS)) || res == HTTPC_RESULTCODE_DOWNLOADPENDING)) { Result posRes = 0; u32 currPos = 0; @@ -243,7 +245,7 @@ Result http_read(http_context context, u32* bytesRead, void* buffer, u32 size) { } } else { while(res == HTTPC_RESULTCODE_DOWNLOADPENDING && outPos < size) { - if(R_SUCCEEDED(res = httpcReceiveDataTimeout(&context->httpc, &((u8*) buffer)[outPos], size - outPos, HTTP_TIMEOUT)) || res == HTTPC_RESULTCODE_DOWNLOADPENDING) { + if(R_SUCCEEDED(res = httpcReceiveDataTimeout(&context->httpc, &((u8*) buffer)[outPos], size - outPos, HTTP_TIMEOUT_NS)) || res == HTTPC_RESULTCODE_DOWNLOADPENDING) { Result posRes = 0; u32 currPos = 0; if(R_SUCCEEDED(posRes = httpcGetDownloadSizeState(&context->httpc, &currPos, NULL))) { @@ -267,17 +269,175 @@ Result http_read(http_context context, u32* bytesRead, void* buffer, u32 size) { return res; } -Result http_download(const char* url, u32* downloadedSize, void* buf, size_t size) { +#define R_HTTP_TLS_VERIFY_FAILED 0xD8A0A03C + +#define HTTP_CONTENT_LENGTH_HEADER "Content-Length" + +typedef struct { + u32 bufferSize; + u64* contentLength; + void* userData; + Result (*callback)(void* userData, void* buffer, size_t size); + + void* buf; + u32 pos; + + Result res; +} http_curl_data; + +static size_t http_curl_header_callback(char* buffer, size_t size, size_t nitems, void* userdata) { + http_curl_data* curlData = (http_curl_data*) userdata; + + size_t bytes = size * nitems; + size_t headerNameLen = strlen(HTTP_CONTENT_LENGTH_HEADER); + + if(bytes >= headerNameLen && strncmp(buffer, HTTP_CONTENT_LENGTH_HEADER, headerNameLen) == 0) { + char* separator = strstr(buffer, ": "); + if(separator != NULL) { + char* valueStart = separator + 2; + + char value[32]; + memset(value, '\0', sizeof(value)); + strncpy(value, valueStart, bytes - (valueStart - buffer)); + + if(curlData->contentLength != NULL) { + *(curlData->contentLength) = (u64) atoll(value); + } + } + } + + return size * nitems; +} + +static size_t http_curl_write_callback(char* ptr, size_t size, size_t nmemb, void* userdata) { + http_curl_data* curlData = (http_curl_data*) userdata; + + size_t available = size * nmemb; + while(available > 0) { + size_t remaining = curlData->bufferSize - curlData->pos; + size_t copySize = available < remaining ? available : remaining; + + memcpy((u8*) curlData->buf + curlData->pos, ptr, copySize); + curlData->pos += copySize; + available -= copySize; + + if(curlData->pos == curlData->bufferSize) { + curlData->res = curlData->callback(curlData->userData, curlData->buf, curlData->bufferSize); + curlData->pos = 0; + } + } + + return R_SUCCEEDED(curlData->res) ? size * nmemb : 0; +} + +Result http_download_callback(const char* url, u32 bufferSize, u64* contentLength, void* userData, Result (*callback)(void* userData, void* buffer, size_t size)) { Result res = 0; - http_context context = NULL; - if(R_SUCCEEDED(res = http_open(&context, url, true))) { - res = http_read(context, downloadedSize, buf, size); + void* buf = malloc(bufferSize); + if(buf != NULL) { + http_context context = NULL; + if(R_SUCCEEDED(res = http_open(&context, url, true))) { + u32 dlSize = 0; + if(R_SUCCEEDED(res = http_get_size(context, &dlSize))) { + if(contentLength != NULL) { + *contentLength = dlSize; + } - Result closeRes = http_close(context); - if(R_SUCCEEDED(res)) { - res = closeRes; + u32 total = 0; + u32 currSize = 0; + while(total < dlSize + && R_SUCCEEDED(res = http_read(context, &currSize, buf, bufferSize)) + && R_SUCCEEDED(res = callback(userData, buf, currSize))) { + total += currSize; + } + + Result closeRes = http_close(context); + if(R_SUCCEEDED(res)) { + res = closeRes; + } + } + } else if(res == R_HTTP_TLS_VERIFY_FAILED) { + res = 0; + + CURL* curl = curl_easy_init(); + if(curl != NULL) { + http_curl_data curlData = {bufferSize, contentLength, userData, callback, buf, 0, 0}; + + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_USERAGENT, HTTP_USER_AGENT); + curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, bufferSize); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, HTTP_TIMEOUT_SEC); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http_curl_write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*) &curlData); + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, http_curl_header_callback); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void*) &curlData); + + CURLcode ret = curl_easy_perform(curl); + + if(ret == CURLE_OK && curlData.pos != 0) { + curlData.res = curlData.callback(curlData.userData, curlData.buf, curlData.pos); + curlData.pos = 0; + + if(R_FAILED(curlData.res)) { + ret = CURLE_WRITE_ERROR; + } + } + + if(ret != CURLE_OK) { + if(ret == CURLE_WRITE_ERROR) { + res = curlData.res; + } else { + res = R_APP_CURL_ERROR_BASE + ret; + } + } + + curl_easy_cleanup(curl); + } else { + res = R_APP_CURL_INIT_FAILED; + } } + + free(buf); + } else { + res = R_APP_OUT_OF_MEMORY; + } + + return res; +} + +typedef struct { + void* buf; + size_t size; + + size_t pos; +} http_buffer_data; + +static Result http_download_buffer_callback(void* userData, void* buffer, size_t size) { + http_buffer_data* data = (http_buffer_data*) userData; + + size_t remaining = data->size - data->pos; + size_t copySize = size; + if(copySize > remaining) { + copySize = remaining; + } + + if(copySize > 0) { + memcpy((u8*) data->buf + data->pos, buffer, copySize); + data->pos += copySize; + } + + return 0; +} + + +Result http_download_buffer(const char* url, u32* downloadedSize, void* buf, size_t size) { + http_buffer_data data = {buf, size, 0}; + Result res = http_download_callback(url, size, NULL, &data, http_download_buffer_callback); + + if(R_SUCCEEDED(res)) { + *downloadedSize = data.pos; } return res; @@ -293,7 +453,7 @@ Result http_download_json(const char* url, json_t** json, size_t maxSize) { char* text = (char*) calloc(sizeof(char), maxSize); if(text != NULL) { u32 textSize = 0; - if(R_SUCCEEDED(res = http_download(url, &textSize, text, maxSize))) { + if(R_SUCCEEDED(res = http_download_buffer(url, &textSize, text, maxSize))) { json_error_t error; json_t* parsed = json_loads(text, 0, &error); if(parsed != NULL) { @@ -357,7 +517,7 @@ Result http_download_seed(u64 titleId) { 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 = http_download(url, &downloadedSize, seed, sizeof(seed))) && downloadedSize != sizeof(seed)) { + if(R_SUCCEEDED(res = http_download_buffer(url, &downloadedSize, seed, sizeof(seed))) && downloadedSize != sizeof(seed)) { res = R_APP_BAD_DATA; } } else { diff --git a/source/core/http.h b/source/core/http.h index 48fa86d..abb3cff 100644 --- a/source/core/http.h +++ b/source/core/http.h @@ -9,6 +9,7 @@ Result http_get_size(http_context context, u32* size); Result http_get_file_name(http_context context, char* out, u32 size); Result http_read(http_context context, u32* bytesRead, void* buffer, u32 size); -Result http_download(const char* url, u32* downloadedSize, void* buf, size_t size); +Result http_download_callback(const char* url, u32 bufferSize, u64* contentLength, void* userData, Result (*callback)(void* userData, void* buffer, size_t size)); +Result http_download_buffer(const char* url, u32* downloadedSize, void* buf, size_t size); Result http_download_json(const char* url, json_t** json, size_t maxSize); Result http_download_seed(u64 titleId); \ No newline at end of file diff --git a/source/core/task/dataop.c b/source/core/task/dataop.c index 24fad02..bba719d 100644 --- a/source/core/task/dataop.c +++ b/source/core/task/dataop.c @@ -7,7 +7,7 @@ #include "dataop.h" #include "../core.h" -static Result task_data_op_check_running(data_op_data* data, u32 index, u32* srcHandle, u32* dstHandle) { +static Result task_data_op_check_running(data_op_data* data) { Result res = 0; if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { @@ -15,12 +15,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->suspendTransfer != NULL && R_SUCCEEDED(res)) { - res = data->suspendTransfer(data->data, index, srcHandle, dstHandle); - } - if(data->suspend != NULL && R_SUCCEEDED(res)) { - res = data->suspend(data->data, index); + res = data->suspend(data->data, data->processed); } } @@ -28,11 +24,7 @@ static Result task_data_op_check_running(data_op_data* data, u32 index, u32* src if(suspended) { if(data->restore != NULL && R_SUCCEEDED(res)) { - res = data->restore(data->data, index); - } - - if(data->op == DATAOP_COPY && srcHandle != NULL && dstHandle != NULL && data->restoreTransfer != NULL && R_SUCCEEDED(res)) { - res = data->restoreTransfer(data->data, index, srcHandle, dstHandle); + res = data->restore(data->data, data->processed); } } } @@ -76,7 +68,7 @@ static Result task_data_op_copy(data_op_data* data, u32 index) { bool firstRun = true; while(data->currProcessed < data->currTotal) { - if(R_FAILED(res = task_data_op_check_running(data, data->processed, &srcHandle, &dstHandle))) { + if(R_FAILED(res = task_data_op_check_running(data))) { break; } @@ -145,6 +137,87 @@ static Result task_data_op_copy(data_op_data* data, u32 index) { return res; } +typedef struct { + data_op_data* data; + + u32 index; + + u32 dstHandle; + bool firstRun; + u64 ioStartTime; + u64 lastBytesPerSecondUpdate; + u32 bytesSinceUpdate; +} data_op_download_data; + +static Result task_data_op_download_callback(void* userData, void* buffer, size_t size) { + data_op_download_data* downloadData = (data_op_download_data*) userData; + data_op_data* data = downloadData->data; + + Result res = 0; + + if(R_SUCCEEDED(res = task_data_op_check_running(data))) { + if(downloadData->firstRun) { + downloadData->firstRun = false; + + if(R_FAILED(res = data->openDst(data->data, downloadData->index, buffer, data->currTotal, &downloadData->dstHandle))) { + return res; + } + } + + u32 bytesWritten = 0; + if(R_SUCCEEDED(res = data->writeDst(data->data, downloadData->dstHandle, &bytesWritten, buffer, data->currProcessed, size))) { + data->currProcessed += bytesWritten; + downloadData->bytesSinceUpdate += bytesWritten; + + u64 time = osGetTime(); + u64 elapsed = time - downloadData->lastBytesPerSecondUpdate; + if(elapsed >= 1000) { + data->bytesPerSecond = (u32) (downloadData->bytesSinceUpdate / (elapsed / 1000.0f)); + + if(downloadData->ioStartTime != 0) { + data->estimatedRemainingSeconds = (u32) ((data->currTotal - data->currProcessed) / (data->currProcessed / ((time - downloadData->ioStartTime) / 1000.0f))); + } else { + data->estimatedRemainingSeconds = 0; + } + + if(downloadData->ioStartTime == 0 && data->currProcessed > 0) { + downloadData->ioStartTime = time; + } + + downloadData->bytesSinceUpdate = 0; + downloadData->lastBytesPerSecondUpdate = time; + } + } + } + + return res; +} + +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; + + char url[DOWNLOAD_URL_MAX]; + if(R_SUCCEEDED(res = data->getSrcUrl(data->data, index, url, DOWNLOAD_URL_MAX))) { + data_op_download_data downloadData = {data, index, 0, true, 0, osGetTime(), 0}; + res = http_download_callback(url, data->bufferSize, &data->currTotal, &downloadData, task_data_op_download_callback); + + if(downloadData.dstHandle != 0) { + Result closeDstRes = data->closeDst(data->data, index, res == 0, downloadData.dstHandle); + if(R_SUCCEEDED(res)) { + res = closeDstRes; + } + } + } + + return res; +} + static Result task_data_op_delete(data_op_data* data, u32 index) { return data->delete(data->data, index); } @@ -159,11 +232,14 @@ static void task_data_op_thread(void* arg) { for(data->processed = 0; data->processed < data->total; data->processed++) { Result res = 0; - if(R_SUCCEEDED(res = task_data_op_check_running(data, data->processed, NULL, NULL))) { + if(R_SUCCEEDED(res = task_data_op_check_running(data))) { switch(data->op) { 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; diff --git a/source/core/task/dataop.h b/source/core/task/dataop.h index c44c593..9b033f3 100644 --- a/source/core/task/dataop.h +++ b/source/core/task/dataop.h @@ -8,6 +8,7 @@ typedef struct ui_view_s ui_view; typedef enum data_op_e { DATAOP_COPY, + DATAOP_DOWNLOAD, DATAOP_DELETE } data_op; @@ -19,9 +20,7 @@ typedef struct data_op_data_s { u32 processed; u32 total; - // Copy - bool copyEmpty; - + // Copy/Download u64 currProcessed; u64 currTotal; @@ -30,6 +29,14 @@ typedef struct data_op_data_s { 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); + + // Copy + bool copyEmpty; + Result (*isSrcDirectory)(void* data, u32 index, bool* isDirectory); Result (*makeDstDirectory)(void* data, u32 index); @@ -39,13 +46,8 @@ 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); - 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 (*suspendTransfer)(void* data, u32 index, u32* srcHandle, u32* dstHandle); - Result (*restoreTransfer)(void* data, u32 index, u32* srcHandle, u32* dstHandle); + // Download + Result (*getSrcUrl)(void* data, u32 index, char* url, size_t maxSize); // Delete Result (*delete)(void* data, u32 index); diff --git a/source/core/ui/error.c b/source/core/ui/error.c index 5ac36af..b09d465 100644 --- a/source/core/ui/error.c +++ b/source/core/ui/error.c @@ -4,6 +4,7 @@ #include #include <3ds.h> +#include #include "error.h" #include "prompt.h" @@ -632,6 +633,8 @@ static const char* description_to_string(Result res) { default: return "HTTP: Unknown Response Code"; } + } else if(res >= R_APP_CURL_ERROR_BASE && res < R_APP_CURL_ERROR_END) { + return curl_easy_strerror(res - R_APP_CURL_ERROR_BASE); } break; @@ -750,6 +753,7 @@ ui_view* error_display_res(void* data, void (*drawTop)(ui_view* view, void* data int summary = R_SUMMARY(result); int module = R_MODULE(result); int description = R_DESCRIPTION(result); + snprintf(errorData->fullText, 4096, "%s\nResult code: 0x%08lX\nLevel: %s (%d)\nSummary: %s (%d)\nModule: %s (%d)\nDesc: %s (%d)", textBuf, result, level_to_string(result), level, summary_to_string(result), summary, module_to_string(result), module, description_to_string(result), description); return prompt_display_notify("Error", errorData->fullText, COLOR_TEXT, errorData, error_draw_top, error_onresponse); diff --git a/source/fbi/action/erasetwlsave.c b/source/fbi/action/erasetwlsave.c index af432c0..a19506b 100644 --- a/source/fbi/action/erasetwlsave.c +++ b/source/fbi/action/erasetwlsave.c @@ -66,14 +66,6 @@ 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_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { - return 0; -} - -static Result action_erase_twl_save_restore_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { - return 0; -} - static Result action_erase_twl_save_suspend(void* data, u32 index) { return 0; } @@ -165,9 +157,6 @@ 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.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/fbi/action/exporttwlsave.c b/source/fbi/action/exporttwlsave.c index 6497ce1..32e8bc3 100644 --- a/source/fbi/action/exporttwlsave.c +++ b/source/fbi/action/exporttwlsave.c @@ -88,14 +88,6 @@ 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_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { - return 0; -} - -static Result action_export_twl_save_restore_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { - return 0; -} - static Result action_export_twl_save_suspend(void* data, u32 index) { return 0; } @@ -187,9 +179,6 @@ 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.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/fbi/action/importtwlsave.c b/source/fbi/action/importtwlsave.c index a5722c1..f890d3a 100644 --- a/source/fbi/action/importtwlsave.c +++ b/source/fbi/action/importtwlsave.c @@ -74,14 +74,6 @@ 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_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { - return 0; -} - -static Result action_import_twl_save_restore_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { - return 0; -} - static Result action_import_twl_save_suspend(void* data, u32 index) { return 0; } @@ -173,9 +165,6 @@ 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.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/fbi/action/installcias.c b/source/fbi/action/installcias.c index 2ef0870..f2fff75 100644 --- a/source/fbi/action/installcias.c +++ b/source/fbi/action/installcias.c @@ -171,14 +171,6 @@ 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_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { - return 0; -} - -static Result action_install_cias_restore_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { - return 0; -} - static Result action_install_cias_suspend(void* data, u32 index) { return 0; } @@ -343,9 +335,6 @@ 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.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/fbi/action/installtickets.c b/source/fbi/action/installtickets.c index 07db371..0142f51 100644 --- a/source/fbi/action/installtickets.c +++ b/source/fbi/action/installtickets.c @@ -120,14 +120,6 @@ 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_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { - return 0; -} - -static Result action_install_tickets_restore_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { - return 0; -} - static Result action_install_tickets_suspend(void* data, u32 index) { return 0; } @@ -290,9 +282,6 @@ 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.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/fbi/action/installurl.c b/source/fbi/action/installurl.c index 91511a6..30b56d4 100644 --- a/source/fbi/action/installurl.c +++ b/source/fbi/action/installurl.c @@ -29,7 +29,6 @@ typedef struct { u64 currTitleId; volatile bool n3dsContinue; ticket_info ticketInfo; - http_context currContext; char currPath[FILE_PATH_MAX]; data_op_data installInfo; @@ -77,46 +76,13 @@ static void action_install_url_draw_top(ui_view* view, void* data, float x1, flo } } -static Result action_install_url_is_src_directory(void* data, u32 index, bool* isDirectory) { - *isDirectory = false; - return 0; -} - -static Result action_install_url_make_dst_directory(void* data, u32 index) { - return 0; -} - -static Result action_install_url_open_src(void* data, u32 index, u32* handle) { +static Result action_install_url_get_src_url(void* data, u32 index, char* url, size_t maxSize) { install_url_data* installData = (install_url_data*) data; - Result res = 0; - - if(R_SUCCEEDED(res = http_open(&installData->currContext, installData->urls[index], true))) { - *handle = (u32) installData->currContext; - } - - return res; + strncpy(url, installData->urls[index], maxSize); + return 0; } -static Result action_install_url_close_src(void* data, u32 index, bool succeeded, u32 handle) { - ((install_url_data*) data)->currContext = NULL; - - return http_close((http_context) handle); -} - -static Result action_install_url_get_src_size(void* data, u32 handle, u64* size) { - u32 downloadSize = 0; - Result res = http_get_size((http_context) handle, &downloadSize); - - *size = downloadSize; - return res; -} - -static Result action_install_url_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) { - return http_read((http_context) 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; @@ -185,9 +151,10 @@ static Result action_install_url_open_dst(void* data, u32 index, void* initialRe string_copy(installData->currPath, installData->paths[index], FILE_PATH_MAX); } else { char filename[FILE_NAME_MAX]; - if(R_FAILED(http_get_file_name(installData->currContext, filename, FILE_NAME_MAX))) { + // TODO + //if(R_FAILED(http_get_file_name(installData->currContext, filename, FILE_NAME_MAX))) { string_get_path_file(filename, installData->urls[index], FILE_NAME_MAX); - } + //} char name[FILE_NAME_MAX]; string_get_file_name(name, filename, FILE_NAME_MAX); @@ -268,14 +235,6 @@ 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_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { - return 0; -} - -static Result action_install_url_restore_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { - return 0; -} - static Result action_install_url_suspend(void* data, u32 index) { return 0; } @@ -424,28 +383,18 @@ 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.bufferSize = 128 * 1024; - data->installInfo.copyEmpty = false; data->installInfo.processed = data->installInfo.total; - data->installInfo.isSrcDirectory = action_install_url_is_src_directory; - data->installInfo.makeDstDirectory = action_install_url_make_dst_directory; - - data->installInfo.openSrc = action_install_url_open_src; - data->installInfo.closeSrc = action_install_url_close_src; - data->installInfo.getSrcSize = action_install_url_get_src_size; - data->installInfo.readSrc = action_install_url_read_src; + data->installInfo.getSrcUrl = action_install_url_get_src_url; data->installInfo.openDst = action_install_url_open_dst; data->installInfo.closeDst = action_install_url_close_dst; data->installInfo.writeDst = action_install_url_write_dst; - 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/fbi/action/pastecontents.c b/source/fbi/action/pastecontents.c index 7e303a0..d4bf7fa 100644 --- a/source/fbi/action/pastecontents.c +++ b/source/fbi/action/pastecontents.c @@ -202,14 +202,6 @@ 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_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { - return 0; -} - -static Result action_paste_contents_restore_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { - return 0; -} - static Result action_paste_contents_suspend(void* data, u32 index) { return 0; } @@ -371,9 +363,6 @@ 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.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/fbi/dumpnand.c b/source/fbi/dumpnand.c index 518bff8..c10c560 100644 --- a/source/fbi/dumpnand.c +++ b/source/fbi/dumpnand.c @@ -71,14 +71,6 @@ 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_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { - return 0; -} - -static Result dumpnand_restore_transfer(void* data, u32 index, u32* srcHandle, u32* dstHandle) { - return 0; -} - static Result dumpnand_suspend(void* data, u32 index) { return 0; } @@ -165,9 +157,6 @@ void dumpnand_open() { data->closeDst = dumpnand_close_dst; data->writeDst = dumpnand_write_dst; - data->suspendTransfer = dumpnand_suspend_transfer; - data->restoreTransfer = dumpnand_restore_transfer; - data->suspend = dumpnand_suspend; data->restore = dumpnand_restore; diff --git a/source/fbi/update.c b/source/fbi/update.c index 0312c8e..0d6ee5c 100644 --- a/source/fbi/update.c +++ b/source/fbi/update.c @@ -11,78 +11,49 @@ #include "task/uitask.h" #include "../core/core.h" -typedef struct { - u32 id; - bool cia; -} update_data; - -static void update_finished_all(void* data) { - free(data); -} - static void update_check_update(ui_view* view, void* data, float* progress, char* text) { - update_data* updateData = (update_data*) data; - bool hasUpdate = false; char updateURL[DOWNLOAD_URL_MAX]; Result res = 0; json_t* json = NULL; - if(R_SUCCEEDED(res = http_download_json("https://api.titledb.com/v1/entry?nested=true&only=id" - "&only=cia.id&only=cia.version&only=cia.updated_at" - "&only=tdsx.id&only=tdsx.version&only=tdsx.updated_at" - "&_filters=%7B%22id%22%3A%20%22138%22%7D", &json, 16 * 1024))) { - const char* type = fs_get_3dsx_path() != NULL ? "tdsx" : "cia"; + if(R_SUCCEEDED(res = http_download_json("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"); - json_t* entry = NULL; - json_t* idJson = NULL; - json_t* objs = NULL; - if(json_is_array(json) && json_array_size(json) == 1 - && json_is_object(entry = json_array_get(json, 0)) - && json_is_integer(idJson = json_object_get(entry, "id")) - && json_is_array(objs = json_object_get(entry, type))) { - if(json_array_size(json) > 0) { - updateData->id = (u32) json_integer_value(idJson); - updateData->cia = fs_get_3dsx_path() != NULL; + if(json_is_string(name) && json_is_array(assets)) { + char versionString[16]; + snprintf(versionString, sizeof(versionString), "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO); - u32 id = 0; - u32 latestMajor = 0; - u32 latestMinor = 0; - u32 latestMicro = 0; + if(strncmp(json_string_value(name), versionString, json_string_length(name)) != 0) { + const char* url = NULL; - for(u32 i = 0; i < json_array_size(objs); i++) { - json_t* obj = json_array_get(objs, i); - if(json_is_object(obj)) { - json_t* subIdJson = json_object_get(obj, "id"); - json_t* versionJson = json_object_get(obj, "version"); - if(json_is_integer(subIdJson) && json_is_string(versionJson)) { - u32 subId = (u32) json_integer_value(subIdJson); - const char* version = json_string_value(versionJson); + for(u32 i = 0; i < json_array_size(assets); i++) { + json_t* val = json_array_get(assets, i); + if(json_is_object(val)) { + json_t* assetName = json_object_get(val, "name"); + json_t* assetUrl = json_object_get(val, "browser_download_url"); - u32 major = 0; - u32 minor = 0; - u32 micro = 0; - sscanf(version, "%lu.%lu.%lu", &major, &minor, µ); - - if(major > latestMajor - || (major == latestMajor && minor > latestMinor) - || (major == latestMajor && minor == latestMinor && micro > latestMicro)) { - id = subId; - latestMajor = major; - latestMinor = minor; - latestMicro = micro; + if(json_is_string(assetName) && json_is_string(assetUrl)) { + if(strncmp(json_string_value(assetName), fs_get_3dsx_path() != NULL ? "FBI.3dsx" : "FBI.cia", json_string_length(assetName)) == 0) { + url = json_string_value(assetUrl); + break; + } } } } - } - if(latestMajor > VERSION_MAJOR - || (latestMajor == VERSION_MAJOR && latestMinor > VERSION_MINOR) - || (latestMajor == VERSION_MAJOR && latestMinor == VERSION_MINOR && latestMicro > VERSION_MICRO)) { - snprintf(updateURL, DOWNLOAD_URL_MAX, "https://3ds.titledb.com/v1/%s/%lu/download", type, id); - hasUpdate = true; + if(url != NULL) { + string_copy(updateURL, url, DOWNLOAD_URL_MAX); + hasUpdate = true; + } else { + res = R_APP_BAD_DATA; + } } + } else { + res = R_APP_BAD_DATA; } } else { res = R_APP_BAD_DATA; @@ -95,25 +66,16 @@ static void update_check_update(ui_view* view, void* data, float* progress, char info_destroy(view); if(hasUpdate) { - action_install_url("Update FBI to the latest version?", updateURL, fs_get_3dsx_path(), updateData, NULL, update_finished_all, NULL); + action_install_url("Update FBI to the latest version?", updateURL, fs_get_3dsx_path(), NULL, NULL, NULL, NULL); } else { if(R_FAILED(res)) { error_display_res(NULL, NULL, res, "Failed to check for update."); } else { prompt_display_notify("Success", "No updates available.", COLOR_TEXT, NULL, NULL, NULL); } - - free(updateData); } } void update_open() { - update_data* data = (update_data*) calloc(1, sizeof(update_data)); - if(data == NULL) { - error_display(NULL, NULL, "Failed to allocate update data."); - - return; - } - - info_display("Checking For Updates", "", false, data, update_check_update, NULL); + info_display("Checking For Updates", "", false, NULL, update_check_update, NULL); }