mirror of
https://gitlab.com/Theopse/fbi-i18n-zh.git
synced 2025-04-06 03:58:02 +08:00
Add task for downloading using curl, experiment with running the UI thread on the syscore.
This commit is contained in:
parent
0b1f6cd2fd
commit
1539c55ec5
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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(®ion);
|
||||
|
||||
if(region <= CFG_REGION_TWN) {
|
||||
static const char* regionStrings[] = {"JP", "US", "GB", "GB", "HK", "KR", "TW"};
|
||||
|
||||
char url[128];
|
||||
snprintf(url, 128, "https://kagiya-ctr.cdn.nintendo.net/title/0x%016llX/ext_key?country=%s", titleId, regionStrings[region]);
|
||||
|
||||
u32 downloadedSize = 0;
|
||||
if(R_SUCCEEDED(res = util_download(url, &downloadedSize, seed, sizeof(seed))) && downloadedSize != sizeof(seed)) {
|
||||
res = R_FBI_BAD_DATA;
|
||||
}
|
||||
} else {
|
||||
res = R_FBI_OUT_OF_RANGE;
|
||||
}
|
||||
}
|
||||
|
||||
if(R_SUCCEEDED(res)) {
|
||||
res = FSUSER_AddSeed(titleId, seed);
|
||||
}
|
||||
} else {
|
||||
res = R_FBI_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
FS_MediaType util_get_title_destination(u64 titleId) {
|
||||
u16 platform = (u16) ((titleId >> 48) & 0xFFFF);
|
||||
u16 category = (u16) ((titleId >> 32) & 0xFFFF);
|
||||
@ -563,11 +411,11 @@ void util_escape_file_name(char* out, const char* file, size_t size) {
|
||||
|
||||
#define HTTPC_TIMEOUT 15000000000
|
||||
|
||||
Result util_http_open(httpcContext* context, u32* responseCode, const char* url, bool userAgent) {
|
||||
return util_http_open_ranged(context, responseCode, url, userAgent, 0, 0);
|
||||
Result util_http_open(httpcContext* context, const char* url, bool userAgent) {
|
||||
return util_http_open_ranged(context, url, userAgent, 0, 0);
|
||||
}
|
||||
|
||||
Result util_http_open_ranged(httpcContext* context, u32* responseCode, const char* url, bool userAgent, u32 rangeStart, u32 rangeEnd) {
|
||||
Result util_http_open_ranged(httpcContext* context, const char* url, bool userAgent, u32 rangeStart, u32 rangeEnd) {
|
||||
if(context == NULL || url == NULL) {
|
||||
return R_FBI_INVALID_ARGUMENT;
|
||||
}
|
||||
@ -605,12 +453,8 @@ Result util_http_open_ranged(httpcContext* context, u32* responseCode, const cha
|
||||
} else {
|
||||
resolved = true;
|
||||
|
||||
if(responseCode != NULL) {
|
||||
*responseCode = response;
|
||||
}
|
||||
|
||||
if(response != 200) {
|
||||
res = R_FBI_HTTP_RESPONSE_CODE;
|
||||
res = R_FBI_HTTP_ERROR_BASE + response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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.");
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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(®ion);
|
||||
|
||||
if(region <= CFG_REGION_TWN) {
|
||||
static const char* regionStrings[] = {"JP", "US", "GB", "GB", "HK", "KR", "TW"};
|
||||
|
||||
char url[128];
|
||||
snprintf(url, 128, "https://kagiya-ctr.cdn.nintendo.net/title/0x%016llX/ext_key?country=%s", titleId, regionStrings[region]);
|
||||
|
||||
u32 downloadedSize = 0;
|
||||
if(R_SUCCEEDED(res = task_download_sync(url, &downloadedSize, seed, sizeof(seed))) && downloadedSize != sizeof(seed)) {
|
||||
res = R_FBI_BAD_DATA;
|
||||
}
|
||||
} else {
|
||||
res = R_FBI_OUT_OF_RANGE;
|
||||
}
|
||||
}
|
||||
|
||||
if(R_SUCCEEDED(res)) {
|
||||
res = FSUSER_AddSeed(titleId, seed);
|
||||
}
|
||||
} else {
|
||||
res = R_FBI_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
data_op_data* baseData;
|
||||
u32 index;
|
||||
|
||||
u8* buffer;
|
||||
u32 bufferPos;
|
||||
u32 bufferSize;
|
||||
|
||||
u32 dstHandle;
|
||||
|
||||
u32 bytesWritten;
|
||||
u64 ioStartTime;
|
||||
u64 lastBytesPerSecondUpdate;
|
||||
u32 bytesSinceUpdate;
|
||||
|
||||
Result res;
|
||||
} data_op_download_data;
|
||||
|
||||
static bool task_data_op_download_flush(data_op_download_data* data) {
|
||||
if(data->dstHandle == 0 && R_FAILED(data->res = data->baseData->openDst(data->baseData->data, data->index, data->buffer, data->baseData->currTotal, &data->dstHandle))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 bytesWritten = 0;
|
||||
if(R_FAILED(data->res = data->baseData->writeDst(data->baseData->data, data->dstHandle, &bytesWritten, data->buffer, data->bytesWritten, data->bufferPos))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data->bytesWritten += data->bufferPos;
|
||||
data->bufferPos = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t task_data_op_download_write_callback(char* ptr, size_t size, size_t nmemb, void* userdata) {
|
||||
data_op_download_data* data = (data_op_download_data*) userdata;
|
||||
|
||||
size_t remaining = size * nmemb;
|
||||
while(remaining > 0) {
|
||||
// Buffering is done to provide adequate data to openDst and prevent misaligned size errors from AM.
|
||||
if(data->bufferPos < data->bufferSize) {
|
||||
size_t bufferRemaining = data->bufferSize - data->bufferPos;
|
||||
size_t used = remaining < bufferRemaining ? remaining : bufferRemaining;
|
||||
|
||||
memcpy(&data->buffer[data->bufferPos], ptr, used);
|
||||
|
||||
data->bufferPos += used;
|
||||
remaining -= used;
|
||||
}
|
||||
|
||||
if(data->bufferPos >= data->bufferSize) {
|
||||
// TODO: Pause on suspend/Unpause on restore?
|
||||
u32 srcHandle = 0;
|
||||
if(R_FAILED(data->res = task_data_op_check_running(data->baseData, data->baseData->processed, &srcHandle, &data->dstHandle))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!task_data_op_download_flush(data)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (size * nmemb) - remaining;
|
||||
}
|
||||
|
||||
int task_data_op_download_progress_callback(void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
|
||||
data_op_download_data* data = (data_op_download_data*) clientp;
|
||||
|
||||
data->bytesSinceUpdate += (u64) dlnow - data->baseData->currProcessed;
|
||||
data->baseData->currTotal = (u64) dltotal;
|
||||
data->baseData->currProcessed = (u64) dlnow;
|
||||
|
||||
u64 time = osGetTime();
|
||||
if(data->lastBytesPerSecondUpdate != 0) {
|
||||
u64 elapsed = time - data->lastBytesPerSecondUpdate;
|
||||
if(elapsed >= 1000) {
|
||||
data->baseData->bytesPerSecond = (u32) (data->bytesSinceUpdate / (elapsed / 1000.0f));
|
||||
|
||||
if(data->ioStartTime != 0) {
|
||||
data->baseData->estimatedRemainingSeconds = (u32) ((data->baseData->currTotal - data->baseData->currProcessed) / (data->baseData->currProcessed / ((time - data->ioStartTime) / 1000.0f)));
|
||||
} else {
|
||||
data->baseData->estimatedRemainingSeconds = 0;
|
||||
}
|
||||
|
||||
if(data->ioStartTime == 0 && data->baseData->currProcessed > 0) {
|
||||
data->ioStartTime = time;
|
||||
}
|
||||
|
||||
data->bytesSinceUpdate = 0;
|
||||
data->lastBytesPerSecondUpdate = time;
|
||||
}
|
||||
} else {
|
||||
data->lastBytesPerSecondUpdate = time;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Result task_data_op_download(data_op_data* data, u32 index) {
|
||||
data->currProcessed = 0;
|
||||
data->currTotal = 0;
|
||||
|
||||
data->bytesPerSecond = 0;
|
||||
data->estimatedRemainingSeconds = 0;
|
||||
|
||||
Result res = 0;
|
||||
|
||||
void* buffer = calloc(1, data->bufferSize);
|
||||
if(buffer != NULL) {
|
||||
data_op_download_data downloadData = {data, index, buffer, 0, data->bufferSize, 0, 0, 0, 0, 0, 0};
|
||||
res = task_download_execute(data->downloadUrls[index], &downloadData, task_data_op_download_write_callback, task_data_op_download_progress_callback);
|
||||
|
||||
if(downloadData.res != 0) {
|
||||
res = downloadData.res;
|
||||
}
|
||||
|
||||
if(R_SUCCEEDED(res) && downloadData.bufferPos > 0) {
|
||||
task_data_op_download_flush(&downloadData);
|
||||
}
|
||||
|
||||
if(downloadData.dstHandle != 0) {
|
||||
Result closeDstRes = data->closeDst(data->data, index, res == 0, downloadData.dstHandle);
|
||||
if(R_SUCCEEDED(res)) {
|
||||
res = closeDstRes;
|
||||
}
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
} else {
|
||||
res = R_FBI_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static Result task_data_op_delete(data_op_data* data, u32 index) {
|
||||
return data->delete(data->data, index);
|
||||
}
|
||||
@ -166,6 +482,9 @@ static void task_data_op_thread(void* arg) {
|
||||
case DATAOP_COPY:
|
||||
res = task_data_op_copy(data, data->processed);
|
||||
break;
|
||||
case DATAOP_DOWNLOAD:
|
||||
res = task_data_op_download(data, data->processed);
|
||||
break;
|
||||
case DATAOP_DELETE:
|
||||
res = task_data_op_delete(data, data->processed);
|
||||
break;
|
||||
@ -229,7 +548,7 @@ Result task_data_op(data_op_data* data) {
|
||||
|
||||
Result res = 0;
|
||||
if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY))) {
|
||||
if(threadCreate(task_data_op_thread, data, 0x10000, 0x18, 1, true) == NULL) {
|
||||
if(threadCreate(task_data_op_thread, data, 0x10000, 0x18, 0, true) == NULL) {
|
||||
res = R_FBI_THREAD_CREATE_FAILED;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user