diff --git a/source/core/util.c b/source/core/util.c index 864487e..f5a7be2 100644 --- a/source/core/util.c +++ b/source/core/util.c @@ -291,9 +291,7 @@ static Result FSUSER_AddSeed(u64 titleId, const void* seed) { return ret; } -static u32 import_response_code = 0; - -Result util_import_seed(u64 titleId) { +Result util_import_seed(u32* responseCode, u64 titleId) { char pathBuf[64]; snprintf(pathBuf, 64, "/fbi/seed/%016llX.dat", titleId); @@ -324,17 +322,14 @@ Result util_import_seed(u64 titleId) { snprintf(url, 128, "https://kagiya-ctr.cdn.nintendo.net/title/0x%016llX/ext_key?country=%s", titleId, regionStrings[region]); httpcContext context; - if(R_SUCCEEDED(res = httpcOpenContext(&context, HTTPC_METHOD_GET, url, 1))) { - if(R_SUCCEEDED(res = httpcSetSSLOpt(&context, SSLCOPT_DisableVerify)) && R_SUCCEEDED(res = httpcBeginRequest(&context)) && R_SUCCEEDED(res = httpcGetResponseStatusCode(&context, &import_response_code))) { - if(import_response_code == 200) { - u32 bytesRead = 0; - res = httpcDownloadData(&context, seed, sizeof(seed), &bytesRead); - } else { - res = R_FBI_HTTP_RESPONSE_CODE; - } - } + if(R_SUCCEEDED(res = util_http_open(&context, responseCode, url, false))) { + u32 bytesRead = 0; + res = util_http_read(&context, &bytesRead, seed, sizeof(seed)); - httpcCloseContext(&context); + Result closeRes = util_http_close(&context); + if(R_SUCCEEDED(res)) { + res = closeRes; + } } } else { res = R_FBI_OUT_OF_RANGE; @@ -351,10 +346,6 @@ Result util_import_seed(u64 titleId) { return res; } -u32 util_get_seed_response_code() { - return import_response_code; -} - FS_MediaType util_get_title_destination(u64 titleId) { u16 platform = (u16) ((titleId >> 48) & 0xFFFF); u16 category = (u16) ((titleId >> 32) & 0xFFFF); @@ -647,4 +638,81 @@ void util_smdh_region_to_string(char* out, u32 region, size_t size) { } } } +} + +static char util_http_redirect_buffer[1024]; + +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_ranged(httpcContext* context, u32* responseCode, const char* url, bool userAgent, u32 rangeStart, u32 rangeEnd) { + if(context == NULL || url == NULL) { + return R_FBI_INVALID_ARGUMENT; + } + + Result res = 0; + + if(R_SUCCEEDED(res = httpcOpenContext(context, HTTPC_METHOD_GET, url, 1))) { + char agent[128]; + snprintf(agent, sizeof(agent), "Mozilla/5.0 (Nintendo 3DS; Mobile; rv:10.0) Gecko/20100101 FBI/%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO); + + char range[64]; + if(rangeEnd > rangeStart) { + snprintf(range, sizeof(range), "%lu-%lu", rangeStart, rangeEnd); + } else { + snprintf(range, sizeof(range), "%lu-", rangeStart); + } + + u32 response = 0; + if(R_SUCCEEDED(res = httpcSetSSLOpt(context, SSLCOPT_DisableVerify)) && (!userAgent || R_SUCCEEDED(res = httpcAddRequestHeaderField(context, "User-Agent", agent))) && (rangeStart == 0 || R_SUCCEEDED(res = httpcAddRequestHeaderField(context, "Range", range))) && R_SUCCEEDED(res = httpcSetKeepAlive(context, HTTPC_KEEPALIVE_ENABLED)) && R_SUCCEEDED(res = httpcBeginRequest(context)) && R_SUCCEEDED(res = httpcGetResponseStatusCode(context, &response))) { + if(response == 301 || response == 302 || response == 303) { + memset(util_http_redirect_buffer, '\0', sizeof(util_http_redirect_buffer)); + if(R_SUCCEEDED(res = httpcGetResponseHeader(context, "Location", util_http_redirect_buffer, sizeof(util_http_redirect_buffer)))) { + httpcCloseContext(context); + + return util_http_open_ranged(context, responseCode, util_http_redirect_buffer, userAgent, rangeStart, rangeEnd); + } + } else { + if(responseCode != NULL) { + *responseCode = response; + } + + if(response != 200) { + res = R_FBI_HTTP_RESPONSE_CODE; + } + } + } + + if(R_FAILED(res)) { + httpcCloseContext(context); + } + } + + return res; +} + +Result util_http_get_size(httpcContext* context, u32* size) { + if(context == NULL || size == NULL) { + return R_FBI_INVALID_ARGUMENT; + } + + return httpcGetDownloadSizeState(context, NULL, size); +} + +Result util_http_read(httpcContext* context, u32* bytesRead, void* buffer, u32 size) { + if(context == NULL || buffer == NULL) { + return R_FBI_INVALID_ARGUMENT; + } + + Result res = httpcDownloadData(context, buffer, size, bytesRead); + return res != HTTPC_RESULTCODE_DOWNLOADPENDING ? res : 0; +} + +Result util_http_close(httpcContext* context) { + if(context == NULL) { + return R_FBI_INVALID_ARGUMENT; + } + + return httpcCloseContext(context); } \ No newline at end of file diff --git a/source/core/util.h b/source/core/util.h index c534b05..6042615 100644 --- a/source/core/util.h +++ b/source/core/util.h @@ -55,8 +55,7 @@ void util_get_parent_path(char* out, const char* path, u32 size); bool util_is_string_empty(const char* str); -Result util_import_seed(u64 titleId); -u32 util_get_seed_response_code(); +Result util_import_seed(u32* responseCode, u64 titleId); FS_MediaType util_get_title_destination(u64 titleId); @@ -84,4 +83,10 @@ const char* util_get_display_size_units(u64 size); void util_escape_file_name(char* out, const char* in, size_t size); -void util_smdh_region_to_string(char* out, u32 region, size_t size); \ No newline at end of file +void util_smdh_region_to_string(char* out, u32 region, size_t size); + +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_get_size(httpcContext* context, u32* size); +Result util_http_read(httpcContext* context, u32* bytesRead, void* buffer, u32 size); +Result util_http_close(httpcContext* context); \ No newline at end of file diff --git a/source/ui/section/action/importseed.c b/source/ui/section/action/importseed.c index 5a2e697..eab664d 100644 --- a/source/ui/section/action/importseed.c +++ b/source/ui/section/action/importseed.c @@ -16,7 +16,8 @@ static void action_import_seed_update(ui_view* view, void* data, float* progress, char* text) { title_info* info = (title_info*) data; - Result res = util_import_seed(info->titleId); + u32 responseCode = 0; + Result res = util_import_seed(&responseCode, info->titleId); ui_pop(); info_destroy(view); @@ -24,7 +25,7 @@ static void action_import_seed_update(ui_view* view, void* data, float* progress if(R_SUCCEEDED(res)) { prompt_display("Success", "Seed imported.", COLOR_TEXT, false, 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", util_get_seed_response_code()); + error_display(NULL, NULL, "Failed to import seed.\nHTTP server returned response code %d", responseCode); } else { error_display_res(info, ui_draw_title_info, res, "Failed to import seed."); } diff --git a/source/ui/section/action/installcdn.c b/source/ui/section/action/installcdn.c index 9ab53be..2e37385 100644 --- a/source/ui/section/action/installcdn.c +++ b/source/ui/section/action/installcdn.c @@ -59,21 +59,9 @@ 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 = httpcOpenContext(context, HTTPC_METHOD_GET, url, 1))) { - if(R_SUCCEEDED(res = httpcSetSSLOpt(context, SSLCOPT_DisableVerify)) && R_SUCCEEDED(res = httpcBeginRequest(context)) && R_SUCCEEDED(res = httpcGetResponseStatusCode(context, &installData->responseCode))) { - if(installData->responseCode == 200) { - *handle = (u32) context; - } else { - res = R_FBI_HTTP_RESPONSE_CODE; - } - } - - if(R_FAILED(res)) { - httpcCloseContext(context); - } - } - - if(R_FAILED(res)) { + if(R_SUCCEEDED(res = util_http_open(context, &installData->responseCode, url, false))) { + *handle = (u32) context; + } else { free(context); } } else { @@ -84,20 +72,19 @@ static Result action_install_cdn_open_src(void* data, u32 index, u32* handle) { } static Result action_install_cdn_close_src(void* data, u32 index, bool succeeded, u32 handle) { - return httpcCloseContext((httpcContext*) handle); + return util_http_close((httpcContext*) handle); } static Result action_install_cdn_get_src_size(void* data, u32 handle, u64* size) { u32 downloadSize = 0; - Result res = httpcGetDownloadSizeState((httpcContext*) handle, NULL, &downloadSize); + Result res = util_http_get_size((httpcContext*) handle, &downloadSize); *size = downloadSize; return res; } static Result action_install_cdn_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) { - Result res = httpcDownloadData((httpcContext*) handle, buffer, size, bytesRead); - return res != HTTPC_RESULTCODE_DOWNLOADPENDING ? res : 0; + return util_http_read((httpcContext*) handle, bytesRead, buffer, size); } static Result action_install_cdn_open_dst(void* data, u32 index, void* initialReadBlock, u64 size, u32* handle) { @@ -222,7 +209,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(installData->ticket->titleId); + util_import_seed(NULL, installData->ticket->titleId); if(installData->ticket->titleId == 0x0004013800000002 || installData->ticket->titleId == 0x0004013820000002) { res = AM_InstallFirm(installData->ticket->titleId); diff --git a/source/ui/section/action/installcias.c b/source/ui/section/action/installcias.c index 70479df..b31f0b2 100644 --- a/source/ui/section/action/installcias.c +++ b/source/ui/section/action/installcias.c @@ -160,7 +160,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(installData->currTitleId); + util_import_seed(NULL, installData->currTitleId); if(installData->currTitleId == 0x0004013800000002 || installData->currTitleId == 0x0004013820000002) { res = AM_InstallFirm(installData->currTitleId); diff --git a/source/ui/section/action/urlinstall.c b/source/ui/section/action/urlinstall.c index 8ba60e5..6e5a001 100644 --- a/source/ui/section/action/urlinstall.c +++ b/source/ui/section/action/urlinstall.c @@ -58,32 +58,9 @@ static Result action_url_install_open_src(void* data, u32 index, u32* handle) { httpcContext* context = (httpcContext*) calloc(1, sizeof(httpcContext)); if(context != NULL) { - if(R_SUCCEEDED(res = httpcOpenContext(context, HTTPC_METHOD_GET, installData->urls[index], 1))) { - char userAgent[128]; - snprintf(userAgent, sizeof(userAgent), "Mozilla/5.0 (Nintendo 3DS; Mobile; rv:10.0) Gecko/20100101 FBI/%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO); - - if(R_SUCCEEDED(res = httpcSetSSLOpt(context, SSLCOPT_DisableVerify)) && R_SUCCEEDED(res = httpcAddRequestHeaderField(context, "User-Agent", userAgent)) && R_SUCCEEDED(res = httpcBeginRequest(context)) && R_SUCCEEDED(res = httpcGetResponseStatusCode(context, &installData->responseCode))) { - if(installData->responseCode == 200) { - *handle = (u32) context; - } else if(installData->responseCode == 301 || installData->responseCode == 302 || installData->responseCode == 303) { - memset(installData->urls[index], '\0', URL_MAX); - if(R_SUCCEEDED(res = httpcGetResponseHeader(context, "Location", installData->urls[index], URL_MAX))) { - httpcCloseContext(context); - free(context); - - return action_url_install_open_src(data, index, handle); - } - } else { - res = R_FBI_HTTP_RESPONSE_CODE; - } - } - - if(R_FAILED(res)) { - httpcCloseContext(context); - } - } - - if(R_FAILED(res)) { + if(R_SUCCEEDED(res = util_http_open(context, &installData->responseCode, installData->urls[index], true))) { + *handle = (u32) context; + } else { free(context); } } else { @@ -94,20 +71,19 @@ static Result action_url_install_open_src(void* data, u32 index, u32* handle) { } static Result action_url_install_close_src(void* data, u32 index, bool succeeded, u32 handle) { - return httpcCloseContext((httpcContext*) handle); + return util_http_close((httpcContext*) handle); } static Result action_url_install_get_src_size(void* data, u32 handle, u64* size) { u32 downloadSize = 0; - Result res = httpcGetDownloadSizeState((httpcContext*) handle, NULL, &downloadSize); + Result res = util_http_get_size((httpcContext*) handle, &downloadSize); *size = downloadSize; return res; } static Result action_url_install_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) { - Result res = httpcDownloadData((httpcContext*) handle, buffer, size, bytesRead); - return res != HTTPC_RESULTCODE_DOWNLOADPENDING ? res : 0; + return util_http_read((httpcContext*) handle, bytesRead, buffer, size); } static Result action_url_install_open_dst(void* data, u32 index, void* initialReadBlock, u64 size, u32* handle) { @@ -190,7 +166,7 @@ static Result action_url_install_close_dst(void* data, u32 index, bool succeeded } } else { if(R_SUCCEEDED(res = AM_FinishCiaInstall(handle))) { - util_import_seed(installData->currTitleId); + util_import_seed(NULL, installData->currTitleId); if(installData->currTitleId == 0x0004013800000002 || installData->currTitleId == 0x0004013820000002) { res = AM_InstallFirm(installData->currTitleId); diff --git a/source/ui/section/task/listtitledb.c b/source/ui/section/task/listtitledb.c index d88ebe1..1556892 100644 --- a/source/ui/section/task/listtitledb.c +++ b/source/ui/section/task/listtitledb.c @@ -17,39 +17,14 @@ static Result task_populate_titledb_download(u32* downloadSize, void* buffer, u32 maxSize, const char* url) { Result res = 0; - if(downloadSize != NULL) { - *downloadSize = 0; - } - httpcContext context; - if(R_SUCCEEDED(res = httpcOpenContext(&context, HTTPC_METHOD_GET, url, 1))) { - char userAgent[128]; - snprintf(userAgent, sizeof(userAgent), "Mozilla/5.0 (Nintendo 3DS; Mobile; rv:10.0) Gecko/20100101 FBI/%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO); + if(R_SUCCEEDED(res = util_http_open(&context, NULL, url, true))) { + res = util_http_read(&context, downloadSize, buffer, maxSize); - u32 responseCode = 0; - if(R_SUCCEEDED(res = httpcSetSSLOpt(&context, SSLCOPT_DisableVerify)) - && R_SUCCEEDED(res = httpcAddRequestHeaderField(&context, "User-Agent", userAgent)) - && R_SUCCEEDED(res = httpcAddRequestHeaderField(&context, "Connection", "Keep-Alive")) - && R_SUCCEEDED(res = httpcBeginRequest(&context)) - && R_SUCCEEDED(res = httpcGetResponseStatusCode(&context, &responseCode))) { - if(responseCode == 200) { - u32 size = 0; - u32 bytesRead = 0; - while(size < maxSize && (res = httpcDownloadData(&context, &((u8*) buffer)[size], maxSize - size < 0x1000 ? maxSize - size : 0x1000, &bytesRead)) == HTTPC_RESULTCODE_DOWNLOADPENDING) { - size += bytesRead; - } - - size += bytesRead; - - if(R_SUCCEEDED(res) && downloadSize != NULL) { - *downloadSize = size; - } - } else { - res = R_FBI_HTTP_RESPONSE_CODE; - } + Result closeRes = util_http_close(&context); + if(R_SUCCEEDED(res)) { + res = closeRes; } - - httpcCloseContext(&context); } return res; diff --git a/source/ui/section/update.c b/source/ui/section/update.c index 66d1f53..668513f 100644 --- a/source/ui/section/update.c +++ b/source/ui/section/update.c @@ -39,28 +39,9 @@ static Result update_open_src(void* data, u32 index, u32* handle) { httpcContext* context = (httpcContext*) calloc(1, sizeof(httpcContext)); if(context != NULL) { - if(R_SUCCEEDED(res = httpcOpenContext(context, HTTPC_METHOD_GET, updateData->url, 1))) { - if(R_SUCCEEDED(res = httpcSetSSLOpt(context, SSLCOPT_DisableVerify)) && R_SUCCEEDED(res = httpcBeginRequest(context)) && R_SUCCEEDED(res = httpcGetResponseStatusCode(context, &updateData->responseCode))) { - if(updateData->responseCode == 200) { - *handle = (u32) context; - } else if(updateData->responseCode == 301 || updateData->responseCode == 302 || updateData->responseCode == 303) { - if(R_SUCCEEDED(res = httpcGetResponseHeader(context, "Location", updateData->url, URL_MAX))) { - httpcCloseContext(context); - free(context); - - return update_open_src(data, index, handle); - } - } else { - res = R_FBI_HTTP_RESPONSE_CODE; - } - } - - if(R_FAILED(res)) { - httpcCloseContext(context); - } - } - - if(R_FAILED(res)) { + if(R_SUCCEEDED(res = util_http_open(context, &updateData->responseCode, updateData->url, true))) { + *handle = (u32) context; + } else { free(context); } } else { @@ -71,20 +52,19 @@ static Result update_open_src(void* data, u32 index, u32* handle) { } static Result update_close_src(void* data, u32 index, bool succeeded, u32 handle) { - return httpcCloseContext((httpcContext*) handle); + return util_http_close((httpcContext*) handle); } static Result update_get_src_size(void* data, u32 handle, u64* size) { u32 downloadSize = 0; - Result res = httpcGetDownloadSizeState((httpcContext*) handle, NULL, &downloadSize); + Result res = util_http_get_size((httpcContext*) handle, &downloadSize); *size = downloadSize; return res; } static Result update_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) { - Result res = httpcDownloadData((httpcContext*) handle, buffer, size, bytesRead); - return res != HTTPC_RESULTCODE_DOWNLOADPENDING ? res : 0; + return util_http_read((httpcContext*) handle, bytesRead, buffer, size); } static Result update_open_dst(void* data, u32 index, void* initialReadBlock, u64 size, u32* handle) { @@ -183,96 +163,87 @@ static void update_check_update(ui_view* view, void* data, float* progress, char u32 responseCode = 0; httpcContext context; - if(R_SUCCEEDED(res = httpcOpenContext(&context, HTTPC_METHOD_GET, "https://api.github.com/repos/Steveice10/FBI/releases/latest", 1))) { - char userAgent[128]; - snprintf(userAgent, sizeof(userAgent), "Mozilla/5.0 (Nintendo 3DS; Mobile; rv:10.0) Gecko/20100101 FBI/%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO); + if(R_SUCCEEDED(res = util_http_open(&context, &responseCode, "https://api.github.com/repos/Steveice10/FBI/releases/latest", true))) { + u32 size = 0; + if(R_SUCCEEDED(res = util_http_get_size(&context, &size))) { + char* jsonText = (char*) calloc(sizeof(char), size); + if(jsonText != NULL) { + u32 bytesRead = 0; + if(R_SUCCEEDED(res = util_http_read(&context, &bytesRead, (u8*) jsonText, size))) { + json_value* json = json_parse(jsonText, size); + if(json != NULL) { + if(json->type == json_object) { + json_value* name = NULL; + json_value* assets = NULL; - if(R_SUCCEEDED(res = httpcSetSSLOpt(&context, SSLCOPT_DisableVerify)) - && R_SUCCEEDED(res = httpcAddRequestHeaderField(&context, "User-Agent", userAgent)) - && R_SUCCEEDED(res = httpcBeginRequest(&context)) - && R_SUCCEEDED(res = httpcGetResponseStatusCode(&context, &responseCode))) { - if(responseCode == 200) { - u32 size = 0; - if(R_SUCCEEDED(res = httpcGetDownloadSizeState(&context, NULL, &size))) { - char* jsonText = (char*) calloc(sizeof(char), size); - if(jsonText != NULL) { - u32 bytesRead = 0; - if(R_SUCCEEDED(res = httpcDownloadData(&context, (u8*) jsonText, size, &bytesRead))) { - json_value* json = json_parse(jsonText, size); - if(json != NULL) { - if(json->type == json_object) { - json_value* name = NULL; - json_value* assets = NULL; + for(u32 i = 0; i < json->u.object.length; i++) { + json_value* val = json->u.object.values[i].value; + if(strncmp(json->u.object.values[i].name, "name", json->u.object.values[i].name_length) == 0 && val->type == json_string) { + name = val; + } else if(strncmp(json->u.object.values[i].name, "assets", json->u.object.values[i].name_length) == 0 && val->type == json_array) { + assets = val; + } + } - for(u32 i = 0; i < json->u.object.length; i++) { - json_value* val = json->u.object.values[i].value; - if(strncmp(json->u.object.values[i].name, "name", json->u.object.values[i].name_length) == 0 && val->type == json_string) { - name = val; - } else if(strncmp(json->u.object.values[i].name, "assets", json->u.object.values[i].name_length) == 0 && val->type == json_array) { - assets = val; - } - } + if(name != NULL && assets != NULL) { + char versionString[16]; + snprintf(versionString, sizeof(versionString), "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO); - if(name != NULL && assets != NULL) { - char versionString[16]; - snprintf(versionString, sizeof(versionString), "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO); + if(strncmp(name->u.string.ptr, versionString, name->u.string.length) != 0) { + char* url = NULL; - if(strncmp(name->u.string.ptr, versionString, name->u.string.length) != 0) { - char* url = NULL; + for(u32 i = 0; i < assets->u.array.length; i++) { + json_value* val = assets->u.array.values[i]; + if(val->type == json_object) { + json_value* assetName = NULL; + json_value* assetUrl = NULL; - for(u32 i = 0; i < assets->u.array.length; i++) { - json_value* val = assets->u.array.values[i]; - if(val->type == json_object) { - json_value* assetName = NULL; - json_value* assetUrl = NULL; - - for(u32 j = 0; j < val->u.object.length; j++) { - json_value* subVal = val->u.object.values[j].value; - if(strncmp(val->u.object.values[j].name, "name", val->u.object.values[j].name_length) == 0 && subVal->type == json_string) { - assetName = subVal; - } else if(strncmp(val->u.object.values[j].name, "browser_download_url", val->u.object.values[j].name_length) == 0 && subVal->type == json_string) { - assetUrl = subVal; - } - } - - if(assetName != NULL && assetUrl != NULL) { - if(strncmp(assetName->u.string.ptr, util_get_3dsx_path() != NULL ? "FBI.3dsx" : "FBI.cia", assetName->u.string.length) == 0) { - url = assetUrl->u.string.ptr; - break; - } - } + for(u32 j = 0; j < val->u.object.length; j++) { + json_value* subVal = val->u.object.values[j].value; + if(strncmp(val->u.object.values[j].name, "name", val->u.object.values[j].name_length) == 0 && subVal->type == json_string) { + assetName = subVal; + } else if(strncmp(val->u.object.values[j].name, "browser_download_url", val->u.object.values[j].name_length) == 0 && subVal->type == json_string) { + assetUrl = subVal; } } - if(url != NULL) { - strncpy(updateData->url, url, URL_MAX); - hasUpdate = true; - } else { - res = R_FBI_BAD_DATA; + if(assetName != NULL && assetUrl != NULL) { + if(strncmp(assetName->u.string.ptr, util_get_3dsx_path() != NULL ? "FBI.3dsx" : "FBI.cia", assetName->u.string.length) == 0) { + url = assetUrl->u.string.ptr; + break; + } } } + } + + if(url != NULL) { + strncpy(updateData->url, url, URL_MAX); + hasUpdate = true; } else { res = R_FBI_BAD_DATA; } - } else { - res = R_FBI_BAD_DATA; } } else { - res = R_FBI_PARSE_FAILED; + res = R_FBI_BAD_DATA; } + } else { + res = R_FBI_BAD_DATA; } - - free(jsonText); } else { - res = R_FBI_OUT_OF_MEMORY; + res = R_FBI_PARSE_FAILED; } } + + free(jsonText); } else { - res = R_FBI_HTTP_RESPONSE_CODE; + res = R_FBI_OUT_OF_MEMORY; } } - httpcCloseContext(&context); + Result closeRes = util_http_close(&context); + if(R_SUCCEEDED(res)) { + res = closeRes; + } } ui_pop();