Add task for downloading using curl, experiment with running the UI thread on the syscore.

This commit is contained in:
Steven Smith 2018-02-06 12:04:44 -08:00
parent 0b1f6cd2fd
commit 1539c55ec5
30 changed files with 766 additions and 413 deletions

View File

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

View File

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

View File

@ -5,8 +5,6 @@
#include <string.h>
#include <3ds.h>
#include <curl/curl.h>
#include <jansson.h>
#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(&region);
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;
}
}
}

View File

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

View File

@ -114,11 +114,7 @@ Result init_services() {
return res;
}
static u32 old_time_limit = UINT32_MAX;
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
//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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,6 +2,8 @@
#include <string.h>
#include <3ds.h>
#include <curl/curl.h>
#include <jansson.h>
#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(&region);
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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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