mirror of
https://gitlab.com/Theopse/fbi-i18n-zh.git
synced 2025-04-05 19:41:43 +08:00
Remove curl code.
This commit is contained in:
parent
a466e3da13
commit
20a56f89b0
2
Makefile
2
Makefile
@ -46,7 +46,7 @@ endif
|
||||
|
||||
ifeq ($(TARGET),3DS)
|
||||
LIBRARY_DIRS += $(DEVKITPRO)/libctru $(DEVKITPRO)/portlibs/armv6k $(DEVKITPRO)/portlibs/3ds
|
||||
LIBRARIES += jansson curl mbedtls mbedcrypto mbedx509 z citro3d ctru
|
||||
LIBRARIES += jansson z citro3d ctru
|
||||
|
||||
PRODUCT_CODE := CTR-P-CFBI
|
||||
UNIQUE_ID := 0xF8001
|
||||
|
@ -4,7 +4,7 @@ FBI is an open source title manager for the 3DS.
|
||||
|
||||
Download: https://github.com/Steveice10/FBI/releases
|
||||
|
||||
Requires [devkitARM](http://sourceforge.net/projects/devkitpro/files/devkitARM/), [citro3d](https://github.com/fincs/citro3d), and zlib, jansson, mbedtls, and curl from [3ds_portlibs](https://github.com/devkitPro/3ds_portlibs) to build.
|
||||
Requires [devkitARM](http://sourceforge.net/projects/devkitpro/files/devkitARM/), [citro3d](https://github.com/fincs/citro3d), and zlib and jansson from [3ds_portlibs](https://github.com/devkitPro/3ds_portlibs) to build.
|
||||
|
||||
# Credit
|
||||
|
||||
|
@ -13,9 +13,6 @@
|
||||
#define R_APP_HTTP_ERROR_BASE MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 8)
|
||||
#define R_APP_HTTP_ERROR_END (R_APP_HTTP_ERROR_BASE + 600)
|
||||
|
||||
#define R_APP_CURL_INIT_FAILED (R_APP_HTTP_ERROR_END + 1)
|
||||
#define R_APP_CURL_ERROR_BASE (R_APP_CURL_INIT_FAILED + 1)
|
||||
|
||||
#define R_APP_NOT_IMPLEMENTED MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, RD_NOT_IMPLEMENTED)
|
||||
#define R_APP_OUT_OF_MEMORY MAKERESULT(RL_FATAL, RS_OUTOFRESOURCE, RM_APPLICATION, RD_OUT_OF_MEMORY)
|
||||
#define R_APP_OUT_OF_RANGE MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_APPLICATION, RD_OUT_OF_RANGE)
|
||||
|
@ -3,12 +3,18 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <3ds.h>
|
||||
#include <jansson.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "fs.h"
|
||||
#include "error.h"
|
||||
#include "http.h"
|
||||
|
||||
#define HTTPC_TIMEOUT 15000000000
|
||||
#define MAKE_HTTP_USER_AGENT_(major, minor, micro) ("Mozilla/5.0 (Nintendo 3DS; Mobile; rv:10.0) Gecko/20100101 FBI/" #major "." #minor "." #micro)
|
||||
#define MAKE_HTTP_USER_AGENT(major, minor, micro) MAKE_HTTP_USER_AGENT_(major, minor, micro)
|
||||
#define HTTP_USER_AGENT MAKE_HTTP_USER_AGENT(VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO)
|
||||
|
||||
#define HTTP_TIMEOUT 15000000000
|
||||
|
||||
struct http_context_s {
|
||||
httpcContext httpc;
|
||||
@ -53,7 +59,7 @@ Result http_open_ranged(http_context* context, const char* url, bool userAgent,
|
||||
&& R_SUCCEEDED(res = httpcAddRequestHeaderField(&ctx->httpc, "Accept-Encoding", "gzip, deflate"))
|
||||
&& R_SUCCEEDED(res = httpcSetKeepAlive(&ctx->httpc, HTTPC_KEEPALIVE_ENABLED))
|
||||
&& R_SUCCEEDED(res = httpcBeginRequest(&ctx->httpc))
|
||||
&& R_SUCCEEDED(res = httpcGetResponseStatusCodeTimeout(&ctx->httpc, &response, HTTPC_TIMEOUT))) {
|
||||
&& R_SUCCEEDED(res = httpcGetResponseStatusCodeTimeout(&ctx->httpc, &response, HTTP_TIMEOUT))) {
|
||||
if(response == 301 || response == 302 || response == 303) {
|
||||
redirectCount++;
|
||||
|
||||
@ -179,7 +185,7 @@ Result http_read(http_context context, u32* bytesRead, void* buffer, u32 size) {
|
||||
u32 lastPos = context->bufferSize;
|
||||
while(res == HTTPC_RESULTCODE_DOWNLOADPENDING && outPos < size) {
|
||||
if((context->bufferSize > 0
|
||||
|| R_SUCCEEDED(res = httpcReceiveDataTimeout(&context->httpc, &context->buffer[context->bufferSize], sizeof(context->buffer) - context->bufferSize, HTTPC_TIMEOUT))
|
||||
|| R_SUCCEEDED(res = httpcReceiveDataTimeout(&context->httpc, &context->buffer[context->bufferSize], sizeof(context->buffer) - context->bufferSize, HTTP_TIMEOUT))
|
||||
|| res == HTTPC_RESULTCODE_DOWNLOADPENDING)) {
|
||||
Result posRes = 0;
|
||||
u32 currPos = 0;
|
||||
@ -204,7 +210,7 @@ Result http_read(http_context context, u32* bytesRead, void* buffer, u32 size) {
|
||||
}
|
||||
} else {
|
||||
while(res == HTTPC_RESULTCODE_DOWNLOADPENDING && outPos < size) {
|
||||
if(R_SUCCEEDED(res = httpcReceiveDataTimeout(&context->httpc, &((u8*) buffer)[outPos], size - outPos, HTTPC_TIMEOUT)) || res == HTTPC_RESULTCODE_DOWNLOADPENDING) {
|
||||
if(R_SUCCEEDED(res = httpcReceiveDataTimeout(&context->httpc, &((u8*) buffer)[outPos], size - outPos, HTTP_TIMEOUT)) || res == HTTPC_RESULTCODE_DOWNLOADPENDING) {
|
||||
Result posRes = 0;
|
||||
u32 currPos = 0;
|
||||
if(R_SUCCEEDED(posRes = httpcGetDownloadSizeState(&context->httpc, &currPos, NULL))) {
|
||||
@ -225,5 +231,113 @@ Result http_read(http_context context, u32* bytesRead, void* buffer, u32 size) {
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Result http_download(const char* url, u32* downloadedSize, void* buf, size_t size) {
|
||||
Result res = 0;
|
||||
|
||||
http_context context = NULL;
|
||||
if(R_SUCCEEDED(res = http_open(&context, url, true))) {
|
||||
res = http_read(context, downloadedSize, buf, size);
|
||||
|
||||
Result closeRes = http_close(context);
|
||||
if(R_SUCCEEDED(res)) {
|
||||
res = closeRes;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Result http_download_json(const char* url, json_t** json, size_t maxSize) {
|
||||
if(url == NULL || json == NULL) {
|
||||
return R_APP_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
Result res = 0;
|
||||
|
||||
char* text = (char*) calloc(sizeof(char), maxSize);
|
||||
if(text != NULL) {
|
||||
u32 textSize = 0;
|
||||
if(R_SUCCEEDED(res = http_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_APP_PARSE_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
free(text);
|
||||
} else {
|
||||
res = R_APP_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 http_download_seed(u64 titleId) {
|
||||
char pathBuf[64];
|
||||
snprintf(pathBuf, 64, "/fbi/seed/%016llX.dat", titleId);
|
||||
|
||||
Result res = 0;
|
||||
|
||||
FS_Path* fsPath = fs_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);
|
||||
}
|
||||
|
||||
fs_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 = http_download(url, &downloadedSize, seed, sizeof(seed))) && downloadedSize != sizeof(seed)) {
|
||||
res = R_APP_BAD_DATA;
|
||||
}
|
||||
} else {
|
||||
res = R_APP_OUT_OF_RANGE;
|
||||
}
|
||||
}
|
||||
|
||||
if(R_SUCCEEDED(res)) {
|
||||
res = FSUSER_AddSeed(titleId, seed);
|
||||
}
|
||||
} else {
|
||||
res = R_APP_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
@ -1,11 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define MAKE_HTTP_USER_AGENT_(major, minor, micro) ("Mozilla/5.0 (Nintendo 3DS; Mobile; rv:10.0) Gecko/20100101 FBI/" #major "." #minor "." #micro)
|
||||
#define MAKE_HTTP_USER_AGENT(major, minor, micro) MAKE_HTTP_USER_AGENT_(major, minor, micro)
|
||||
#define HTTP_USER_AGENT MAKE_HTTP_USER_AGENT(VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO)
|
||||
|
||||
#define HTTP_CONNECT_TIMEOUT 15
|
||||
|
||||
typedef struct http_context_s* http_context;
|
||||
|
||||
Result http_open(http_context* context, const char* url, bool userAgent);
|
||||
@ -13,4 +7,8 @@ Result http_open_ranged(http_context* context, const char* url, bool userAgent,
|
||||
Result http_close(http_context context);
|
||||
Result http_get_size(http_context context, u32* size);
|
||||
Result http_get_file_name(http_context context, char* out, u32 size);
|
||||
Result http_read(http_context context, u32* bytesRead, void* buffer, u32 size);
|
||||
Result http_read(http_context context, u32* bytesRead, void* buffer, u32 size);
|
||||
|
||||
Result http_download(const char* url, u32* downloadedSize, void* buf, size_t size);
|
||||
Result http_download_json(const char* url, json_t** json, size_t maxSize);
|
||||
Result http_download_seed(u64 titleId);
|
@ -2,7 +2,6 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <3ds.h>
|
||||
#include <curl/curl.h>
|
||||
#include <jansson.h>
|
||||
|
||||
#include "dataop.h"
|
||||
@ -146,329 +145,6 @@ static Result task_data_op_copy(data_op_data* data, u32 index) {
|
||||
return res;
|
||||
}
|
||||
|
||||
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?
|
||||
|
||||
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_APP_HTTP_ERROR_BASE + ret;
|
||||
}
|
||||
} else {
|
||||
res = R_APP_CURL_ERROR_BASE + ret;
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
} else {
|
||||
res = R_APP_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) {
|
||||
#ifdef USE_CURL
|
||||
if(url == NULL || buf == NULL) {
|
||||
return R_APP_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;
|
||||
#else
|
||||
Result res = 0;
|
||||
|
||||
http_context context = NULL;
|
||||
if(R_SUCCEEDED(res = http_open(&context, url, true))) {
|
||||
res = http_read(context, downloadedSize, buf, size);
|
||||
|
||||
Result closeRes = http_close(context);
|
||||
if(R_SUCCEEDED(res)) {
|
||||
res = closeRes;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
Result task_download_json_sync(const char* url, json_t** json, size_t maxSize) {
|
||||
if(url == NULL || json == NULL) {
|
||||
return R_APP_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_APP_PARSE_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
free(text);
|
||||
} else {
|
||||
res = R_APP_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 = fs_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);
|
||||
}
|
||||
|
||||
fs_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_APP_BAD_DATA;
|
||||
}
|
||||
} else {
|
||||
res = R_APP_OUT_OF_RANGE;
|
||||
}
|
||||
}
|
||||
|
||||
if(R_SUCCEEDED(res)) {
|
||||
res = FSUSER_AddSeed(titleId, seed);
|
||||
}
|
||||
} else {
|
||||
res = R_APP_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_APP_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static Result task_data_op_delete(data_op_data* data, u32 index) {
|
||||
return data->delete(data->data, index);
|
||||
}
|
||||
@ -488,9 +164,6 @@ 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;
|
||||
|
@ -8,7 +8,6 @@ typedef struct ui_view_s ui_view;
|
||||
|
||||
typedef enum data_op_e {
|
||||
DATAOP_COPY,
|
||||
DATAOP_DOWNLOAD,
|
||||
DATAOP_DELETE
|
||||
} data_op;
|
||||
|
||||
@ -23,6 +22,14 @@ typedef struct data_op_data_s {
|
||||
// Copy
|
||||
bool copyEmpty;
|
||||
|
||||
u64 currProcessed;
|
||||
u64 currTotal;
|
||||
|
||||
u32 bytesPerSecond;
|
||||
u32 estimatedRemainingSeconds;
|
||||
|
||||
u32 bufferSize;
|
||||
|
||||
Result (*isSrcDirectory)(void* data, u32 index, bool* isDirectory);
|
||||
Result (*makeDstDirectory)(void* data, u32 index);
|
||||
|
||||
@ -32,18 +39,6 @@ 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);
|
||||
|
||||
@ -71,7 +66,4 @@ typedef struct data_op_data_s {
|
||||
volatile bool retryResponse;
|
||||
} data_op_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);
|
@ -4,7 +4,6 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <3ds.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "error.h"
|
||||
#include "prompt.h"
|
||||
@ -503,8 +502,6 @@ static const char* description_to_string(Result res) {
|
||||
return "Bad data";
|
||||
case R_APP_HTTP_TOO_MANY_REDIRECTS:
|
||||
return "Too many redirects";
|
||||
case R_APP_CURL_INIT_FAILED:
|
||||
return "Failed to initialize CURL.";
|
||||
default:
|
||||
if(res >= R_APP_HTTP_ERROR_BASE && res < R_APP_HTTP_ERROR_END) {
|
||||
switch(res - R_APP_HTTP_ERROR_BASE) {
|
||||
@ -637,10 +634,6 @@ static const char* description_to_string(Result res) {
|
||||
}
|
||||
}
|
||||
|
||||
if(res >= R_APP_CURL_ERROR_BASE && res < R_APP_CURL_ERROR_BASE + CURL_LAST) {
|
||||
return curl_easy_strerror((CURLcode) (res - R_APP_CURL_ERROR_BASE));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -10,7 +10,7 @@
|
||||
static void action_import_seed_update(ui_view* view, void* data, float* progress, char* text) {
|
||||
title_info* info = (title_info*) data;
|
||||
|
||||
Result res = task_download_seed_sync(info->titleId);
|
||||
Result res = http_download_seed(info->titleId);
|
||||
|
||||
ui_pop();
|
||||
info_destroy(view);
|
||||
|
@ -183,7 +183,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(fs_get_title_destination(installData->ticket->titleId), 1, false, &installData->ticket->titleId))) {
|
||||
task_download_seed_sync(installData->ticket->titleId);
|
||||
http_download_seed(installData->ticket->titleId);
|
||||
|
||||
if(installData->ticket->titleId == 0x0004013800000002 || installData->ticket->titleId == 0x0004013820000002) {
|
||||
res = AM_InstallFirm(installData->ticket->titleId);
|
||||
|
@ -154,7 +154,7 @@ static Result action_install_cias_close_dst(void* data, u32 index, bool succeede
|
||||
|
||||
Result res = 0;
|
||||
if(R_SUCCEEDED(res = AM_FinishCiaInstall(handle))) {
|
||||
task_download_seed_sync(info->ciaInfo.titleId);
|
||||
http_download_seed(info->ciaInfo.titleId);
|
||||
|
||||
if((info->ciaInfo.titleId & 0xFFFFFFF) == 0x0000002) {
|
||||
res = AM_InstallFirm(info->ciaInfo.titleId);
|
||||
|
@ -247,7 +247,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))) {
|
||||
task_download_seed_sync(installData->currTitleId);
|
||||
http_download_seed(installData->currTitleId);
|
||||
|
||||
if(installData->currTitleId == 0x0004013800000002 || installData->currTitleId == 0x0004013820000002) {
|
||||
res = AM_InstallFirm(installData->currTitleId);
|
||||
@ -460,13 +460,7 @@ void action_install_url(const char* confirmMessage, const char* urls, const char
|
||||
|
||||
data->installInfo.data = data;
|
||||
|
||||
#ifdef USE_CURL
|
||||
data->installInfo.op = DATAOP_DOWNLOAD;
|
||||
|
||||
data->installInfo.downloadUrls = data->urls;
|
||||
#else
|
||||
data->installInfo.op = DATAOP_COPY;
|
||||
#endif
|
||||
|
||||
data->installInfo.bufferSize = 128 * 1024;
|
||||
data->installInfo.copyEmpty = false;
|
||||
|
@ -2,7 +2,6 @@
|
||||
#include <malloc.h>
|
||||
|
||||
#include <3ds.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "../core/clipboard.h"
|
||||
#include "../core/error.h"
|
||||
@ -149,8 +148,6 @@ void init() {
|
||||
|
||||
AM_InitializeExternalTitleDatabase(false);
|
||||
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
|
||||
screen_init();
|
||||
ui_init();
|
||||
task_init();
|
||||
|
@ -151,10 +151,10 @@ static void task_populate_titledb_thread(void* arg) {
|
||||
linked_list_init(&titles);
|
||||
|
||||
json_t* root = NULL;
|
||||
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=updated_at"
|
||||
"&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 = http_download_json("https://api.titledb.com/v1/entry?nested=true"
|
||||
"&only=id&only=name&only=author&only=headline&only=category&only=updated_at"
|
||||
"&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)) {
|
||||
for(u32 i = 0; i < json_array_size(root) && R_SUCCEEDED(res); i++) {
|
||||
@ -307,7 +307,7 @@ static void task_populate_titledb_thread(void* arg) {
|
||||
|
||||
u8 icon[0x1200];
|
||||
u32 iconSize = 0;
|
||||
if(R_SUCCEEDED(task_download_sync(url, &iconSize, &icon, sizeof(icon))) && iconSize == sizeof(icon)) {
|
||||
if(R_SUCCEEDED(http_download(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);
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ static void update_check_update(ui_view* view, void* data, float* progress, char
|
||||
Result res = 0;
|
||||
|
||||
json_t* json = NULL;
|
||||
if(R_SUCCEEDED(res = task_download_json_sync("https://api.titledb.com/v1/entry?nested=true&only=cia.id&only=cia.version&only=tdsx.id&only=tdsx.version&_filters=%7B%22name%22%3A%20%22FBI%22%7D", &json, 16 * 1024))) {
|
||||
if(R_SUCCEEDED(res = http_download_json("https://api.titledb.com/v1/entry?nested=true&only=cia.id&only=cia.version&only=tdsx.id&only=tdsx.version&_filters=%7B%22name%22%3A%20%22FBI%22%7D", &json, 16 * 1024))) {
|
||||
const char* type = fs_get_3dsx_path() != NULL ? "tdsx" : "cia";
|
||||
|
||||
json_t* entry = NULL;
|
||||
|
Loading…
x
Reference in New Issue
Block a user