From 99e09bd17cf07e5ff2f08732cdd14725a94148b1 Mon Sep 17 00:00:00 2001 From: Stefan Schnitzler <41997317+deadphoenix8091@users.noreply.github.com> Date: Sun, 17 Nov 2019 02:30:30 +0100 Subject: [PATCH] simple visual progress bar (#7) * download files directly to sd instead of a buffer - this fixes running out of memory while downloading large files * simple visual download progressbar --- include/utils/formatting.hpp | 3 + source/download/download.cpp | 1474 ++++++++++++++++++---------------- source/screens/tinyDB.cpp | 20 +- source/utils/formatting.cpp | 19 + 4 files changed, 789 insertions(+), 727 deletions(-) create mode 100644 include/utils/formatting.hpp create mode 100644 source/utils/formatting.cpp diff --git a/include/utils/formatting.hpp b/include/utils/formatting.hpp new file mode 100644 index 0000000..e7d4f5d --- /dev/null +++ b/include/utils/formatting.hpp @@ -0,0 +1,3 @@ +#pragma once + +std::string formatBytes(int bytes); \ No newline at end of file diff --git a/source/download/download.cpp b/source/download/download.cpp index f96751e..d4b2bd2 100644 --- a/source/download/download.cpp +++ b/source/download/download.cpp @@ -1,710 +1,766 @@ -/* -* This file is part of Universal-Updater -* Copyright (C) 2019 VoltZ, Epicpkmn11, Flame, RocketRobz, TotallyNotGuy -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* Additional Terms 7.b and 7.c of GPLv3 apply to this file: -* * Requiring preservation of specified reasonable legal notices or -* author attributions in that material or in the Appropriate Legal -* Notices displayed by works containing it. -* * Prohibiting misrepresentation of the origin of that material, -* or requiring that modified versions of such material be marked in -* reasonable ways as different from the original version. -*/ - -#include "gui.hpp" - -#include "download/download.hpp" - -#include "lang/lang.hpp" - -#include "screens/screenCommon.hpp" - -#include "utils/config.hpp" -#include "utils/extract.hpp" -#include "utils/fileBrowse.h" -#include "utils/thread.hpp" - -#include -#include -#include - -extern "C" { - #include "utils/cia.h" -} - -#define USER_AGENT APP_TITLE "-" VERSION_STRING - -static char* result_buf = NULL; -static size_t result_sz = 0; -static Handle* result_fileHandle = nullptr; -static size_t result_written = 0; -std::vector _topText; -std::string jsonName; - -extern bool downloadNightlies; -extern int filesExtracted; -extern std::string extractingFile; - -char progressBarMsg[128] = ""; -bool showProgressBar = false; -bool progressBarType = 0; // 0 = Download | 1 = Extract - - -// following function is from -// https://github.com/angelsl/libctrfgh/blob/master/curl_test/src/main.c -static size_t handle_data(char* ptr, size_t size, size_t nmemb, void* userdata) -{ - (void) userdata; - const size_t bsz = size*nmemb; - - if (result_sz == 0 || !result_buf) - { - result_sz = 0x1000; - result_buf = (char*)malloc(result_sz); - } - - bool need_realloc = false; - while (result_written + bsz > result_sz) - { - result_sz <<= 1; - need_realloc = true; - } - - if (need_realloc) - { - char *new_buf = (char*)realloc(result_buf, result_sz); - if (!new_buf) - { - return 0; - } - result_buf = new_buf; - } - - if (!result_buf) - { - return 0; - } - - memcpy(result_buf + result_written, ptr, bsz); - result_written += bsz; - return bsz; -} - -static size_t handle_data_to_file(char* ptr, size_t size, size_t nmemb, void* userdata) -{ - (void) userdata; - const size_t bsz = size*nmemb; - - u32 bytesWritten = 0; - FSFILE_Write(*result_fileHandle, &bytesWritten, result_written, ptr, bsz, 0); - - result_written += bsz; - return bsz; -} - -static Result setupContext(CURL *hnd, const char * url) -{ - curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, 102400L); - curl_easy_setopt(hnd, CURLOPT_URL, url); - curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L); - curl_easy_setopt(hnd, CURLOPT_USERAGENT, USER_AGENT); - curl_easy_setopt(hnd, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L); - curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2TLS); - curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, handle_data); - curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L); - curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(hnd, CURLOPT_STDERR, stdout); - - return 0; -} - -static Result setupContextForDirectToFileDownload(CURL *hnd, const char * url) -{ - Result ret = setupContext(hnd, url); - if (ret != 0) { - return ret; - } - - curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, handle_data_to_file); - - return 0; -} - -Result downloadToFile(std::string url, std::string path) -{ - Result ret = 0; - printf("Downloading from:\n%s\nto:\n%s\n", url.c_str(), path.c_str()); - - void *socubuf = memalign(0x1000, 0x100000); - if (!socubuf) - { - return -1; - } - - ret = socInit((u32*)socubuf, 0x100000); - if (R_FAILED(ret)) - { - free(socubuf); - return ret; - } - - Handle fileHandle; - - ret = openFile(&fileHandle, path.c_str(), true); - if (R_FAILED(ret)) { - printf("Error: couldn't open file to write.\n"); - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_sz = 0; - result_written = 0; - return DL_ERROR_WRITEFILE; - } - - result_fileHandle = &fileHandle; - - CURL *hnd = curl_easy_init(); - ret = setupContextForDirectToFileDownload(hnd, url.c_str()); - if (ret != 0) { - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_fileHandle = nullptr; - result_sz = 0; - result_written = 0; - FSFILE_Close(fileHandle); - return ret; - } - - u64 startTime = osGetTime(); - - CURLcode cres = curl_easy_perform(hnd); - curl_easy_cleanup(hnd); - - if (cres != CURLE_OK) { - printf("Error in:\ncurl\n"); - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_fileHandle = nullptr; - result_sz = 0; - result_written = 0; - FSFILE_Close(fileHandle); - return -1; - } - - u64 endTime = osGetTime(); - u64 totalTime = endTime - startTime; - printf("Download took %llu milliseconds.\n", totalTime); - - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_fileHandle = nullptr; - result_sz = 0; - result_written = 0; - FSFILE_Close(fileHandle); - return 0; -} - -Result downloadFromRelease(std::string url, std::string asset, std::string path, bool includePrereleases) -{ - Result ret = 0; - void *socubuf = memalign(0x1000, 0x100000); - if (!socubuf) - { - return -1; - } - - ret = socInit((u32*)socubuf, 0x100000); - if (R_FAILED(ret)) - { - free(socubuf); - return ret; - } - - std::regex parseUrl("github\\.com\\/(.+)\\/(.+)"); - std::smatch result; - regex_search(url, result, parseUrl); - - std::string repoOwner = result[1].str(), repoName = result[2].str(); - - std::stringstream apiurlStream; - apiurlStream << "https://api.github.com/repos/" << repoOwner << "/" << repoName << (includePrereleases ? "/releases" : "/releases/latest"); - std::string apiurl = apiurlStream.str(); - - printf("Downloading latest release from repo:\n%s\nby:\n%s\n", repoName.c_str(), repoOwner.c_str()); - printf("Crafted API url:\n%s\n", apiurl.c_str()); - - CURL *hnd = curl_easy_init(); - ret = setupContext(hnd, apiurl.c_str()); - if (ret != 0) { - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_sz = 0; - result_written = 0; - return ret; - } - - CURLcode cres = curl_easy_perform(hnd); - curl_easy_cleanup(hnd); - char* newbuf = (char*)realloc(result_buf, result_written + 1); - result_buf = newbuf; - result_buf[result_written] = 0; //nullbyte to end it as a proper C style string - - if (cres != CURLE_OK) { - printf("Error in:\ncurl\n"); - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_sz = 0; - result_written = 0; - return -1; - } - - printf("Looking for asset with matching name:\n%s\n", asset.c_str()); - std::string assetUrl; - json parsedAPI = json::parse(result_buf); - if(includePrereleases) parsedAPI = parsedAPI[0]; - if (parsedAPI["assets"].is_array()) { - for (auto jsonAsset : parsedAPI["assets"]) { - if (jsonAsset.is_object() && jsonAsset["name"].is_string() && jsonAsset["browser_download_url"].is_string()) { - std::string assetName = jsonAsset["name"]; - if (matchPattern(asset, assetName)) { - assetUrl = jsonAsset["browser_download_url"]; - break; - } - } - } - } - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_sz = 0; - result_written = 0; - - if (assetUrl.empty()) - ret = DL_ERROR_GIT; - else - ret = downloadToFile(assetUrl, path); - - return ret; -} - -/** - * Check Wi-Fi status. - * @return True if Wi-Fi is connected; false if not. - */ -bool checkWifiStatus(void) { - u32 wifiStatus; - bool res = false; - - if (R_SUCCEEDED(ACU_GetWifiStatus(&wifiStatus)) && wifiStatus) { - res = true; - } - - return res; -} - -void downloadFailed(void) { - DisplayMsg(Lang::get("DOWNLOAD_FAILED")); - for (int i = 0; i < 60*2; i++) { - gspWaitForVBlank(); - } -} - -void notImplemented(void) { - DisplayMsg(Lang::get("NOT_IMPLEMENTED")); - for (int i = 0; i < 60*2; i++) { - gspWaitForVBlank(); - } -} - -void doneMsg(void) { - DisplayMsg(Lang::get("DONE")); - for (int i = 0; i < 60*2; i++) { - gspWaitForVBlank(); - } -} - -void notConnectedMsg(void) { - DisplayMsg(Lang::get("CONNECT_WIFI")); - for (int i = 0; i < 60*2; i++) { - gspWaitForVBlank(); - } -} - -std::string getLatestRelease(std::string repo, std::string item) -{ - Result ret = 0; - void *socubuf = memalign(0x1000, 0x100000); - if (!socubuf) - { - return ""; - } - - ret = socInit((u32*)socubuf, 0x100000); - if (R_FAILED(ret)) - { - free(socubuf); - return ""; - } - - std::stringstream apiurlStream; - apiurlStream << "https://api.github.com/repos/" << repo << "/releases/latest"; - std::string apiurl = apiurlStream.str(); - - CURL *hnd = curl_easy_init(); - ret = setupContext(hnd, apiurl.c_str()); - if (ret != 0) { - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_sz = 0; - result_written = 0; - return ""; - } - - CURLcode cres = curl_easy_perform(hnd); - curl_easy_cleanup(hnd); - char* newbuf = (char*)realloc(result_buf, result_written + 1); - result_buf = newbuf; - result_buf[result_written] = 0; //nullbyte to end it as a proper C style string - - if (cres != CURLE_OK) { - printf("Error in:\ncurl\n"); - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_sz = 0; - result_written = 0; - return ""; - } - - std::string jsonItem; - json parsedAPI = json::parse(result_buf); - if (parsedAPI[item].is_string()) { - jsonItem = parsedAPI[item]; - } - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_sz = 0; - result_written = 0; - - return jsonItem; -} - -std::string getLatestCommit(std::string repo, std::string item) -{ - Result ret = 0; - void *socubuf = memalign(0x1000, 0x100000); - if (!socubuf) - { - return ""; - } - - ret = socInit((u32*)socubuf, 0x100000); - if (R_FAILED(ret)) - { - free(socubuf); - return ""; - } - - std::stringstream apiurlStream; - apiurlStream << "https://api.github.com/repos/" << repo << "/commits/master"; - std::string apiurl = apiurlStream.str(); - - CURL *hnd = curl_easy_init(); - ret = setupContext(hnd, apiurl.c_str()); - if (ret != 0) { - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_sz = 0; - result_written = 0; - return ""; - } - - CURLcode cres = curl_easy_perform(hnd); - curl_easy_cleanup(hnd); - char* newbuf = (char*)realloc(result_buf, result_written + 1); - result_buf = newbuf; - result_buf[result_written] = 0; //nullbyte to end it as a proper C style string - - if (cres != CURLE_OK) { - printf("Error in:\ncurl\n"); - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_sz = 0; - result_written = 0; - return ""; - } - - std::string jsonItem; - json parsedAPI = json::parse(result_buf); - if (parsedAPI[item].is_string()) { - jsonItem = parsedAPI[item]; - } - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_sz = 0; - result_written = 0; - - return jsonItem; -} - -std::string getLatestCommit(std::string repo, std::string array, std::string item) -{ - Result ret = 0; - void *socubuf = memalign(0x1000, 0x100000); - if (!socubuf) - { - return ""; - } - - ret = socInit((u32*)socubuf, 0x100000); - if (R_FAILED(ret)) - { - free(socubuf); - return ""; - } - - std::stringstream apiurlStream; - apiurlStream << "https://api.github.com/repos/" << repo << "/commits/master"; - std::string apiurl = apiurlStream.str(); - - CURL *hnd = curl_easy_init(); - ret = setupContext(hnd, apiurl.c_str()); - if (ret != 0) { - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_sz = 0; - result_written = 0; - return ""; - } - - CURLcode cres = curl_easy_perform(hnd); - curl_easy_cleanup(hnd); - char* newbuf = (char*)realloc(result_buf, result_written + 1); - result_buf = newbuf; - result_buf[result_written] = 0; //nullbyte to end it as a proper C style string - - if (cres != CURLE_OK) { - printf("Error in:\ncurl\n"); - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_sz = 0; - result_written = 0; - return ""; - } - - std::string jsonItem; - json parsedAPI = json::parse(result_buf); - if (parsedAPI[array][item].is_string()) { - jsonItem = parsedAPI[array][item]; - } - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_sz = 0; - result_written = 0; - - return jsonItem; -} - -std::vector getThemeList(std::string repo, std::string path) -{ - Result ret = 0; - void *socubuf = memalign(0x1000, 0x100000); - std::vector emptyVector; - if (!socubuf) - { - return emptyVector; - } - - ret = socInit((u32*)socubuf, 0x100000); - if (R_FAILED(ret)) - { - free(socubuf); - return emptyVector; - } - - std::stringstream apiurlStream; - apiurlStream << "https://api.github.com/repos/" << repo << "/contents/" << path; - std::string apiurl = apiurlStream.str(); - - CURL *hnd = curl_easy_init(); - ret = setupContext(hnd, apiurl.c_str()); - if (ret != 0) { - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_sz = 0; - result_written = 0; - return emptyVector; - } - - CURLcode cres = curl_easy_perform(hnd); - curl_easy_cleanup(hnd); - char* newbuf = (char*)realloc(result_buf, result_written + 1); - result_buf = newbuf; - result_buf[result_written] = 0; //nullbyte to end it as a proper C style string - - if (cres != CURLE_OK) { - printf("Error in:\ncurl\n"); - socExit(); - free(result_buf); - free(socubuf); - result_buf = NULL; - result_sz = 0; - result_written = 0; - - return emptyVector; - } - - std::vector jsonItems; - json parsedAPI = json::parse(result_buf); - for(uint i=0;i themeContents = getThemeList("Universal-Team/extras", path); - for(uint i=0;i. +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#include "gui.hpp" + +#include "download/download.hpp" + +#include "lang/lang.hpp" + +#include "screens/screenCommon.hpp" + +#include "utils/config.hpp" +#include "utils/extract.hpp" +#include "utils/fileBrowse.h" +#include "utils/thread.hpp" +#include "utils/formatting.hpp" + +#include +#include +#include +#include + +extern "C" { + #include "utils/cia.h" +} + +#define USER_AGENT APP_TITLE "-" VERSION_STRING + +static char* result_buf = NULL; +static size_t result_sz = 0; +static Handle* result_fileHandle = nullptr; +static size_t result_written = 0; +std::vector _topText; +std::string jsonName; + +extern bool downloadNightlies; +extern int filesExtracted; +extern std::string extractingFile; + +char progressBarMsg[128] = ""; +bool showProgressBar = false; +bool progressBarType = 0; // 0 = Download | 1 = Extract + +#define TIME_IN_US 1 +#define TIMETYPE curl_off_t +#define TIMEOPT CURLINFO_TOTAL_TIME_T +#define MINIMAL_PROGRESS_FUNCTIONALITY_INTERVAL 3000000 + +// following function is from +// https://github.com/angelsl/libctrfgh/blob/master/curl_test/src/main.c +static size_t handle_data(char* ptr, size_t size, size_t nmemb, void* userdata) +{ + (void) userdata; + const size_t bsz = size*nmemb; + + if (result_sz == 0 || !result_buf) + { + result_sz = 0x1000; + result_buf = (char*)malloc(result_sz); + } + + bool need_realloc = false; + while (result_written + bsz > result_sz) + { + result_sz <<= 1; + need_realloc = true; + } + + if (need_realloc) + { + char *new_buf = (char*)realloc(result_buf, result_sz); + if (!new_buf) + { + return 0; + } + result_buf = new_buf; + } + + if (!result_buf) + { + return 0; + } + + memcpy(result_buf + result_written, ptr, bsz); + result_written += bsz; + return bsz; +} + +static size_t handle_data_to_file(char* ptr, size_t size, size_t nmemb, void* userdata) +{ + (void) userdata; + const size_t bsz = size*nmemb; + + u32 bytesWritten = 0; + FSFILE_Write(*result_fileHandle, &bytesWritten, result_written, ptr, bsz, 0); + + result_written += bsz; + return bsz; +} + +curl_off_t downloadTotal = 1; //Dont initialize with 0 to avoid division by zero later +curl_off_t downloadNow = 0; + +static int curlProgress(CURL *hnd, + curl_off_t dltotal, curl_off_t dlnow, + curl_off_t ultotal, curl_off_t ulnow) +{ + downloadTotal = dltotal; + downloadNow = dlnow; + + return 0; +} + +static Result setupContext(CURL *hnd, const char * url) +{ + downloadTotal = 1; + downloadNow = 0; + curl_easy_setopt(hnd, CURLOPT_XFERINFOFUNCTION, curlProgress); + + curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, 102400L); + curl_easy_setopt(hnd, CURLOPT_URL, url); + curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 0L); + curl_easy_setopt(hnd, CURLOPT_USERAGENT, USER_AGENT); + curl_easy_setopt(hnd, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L); + curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2TLS); + curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, handle_data); + curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(hnd, CURLOPT_STDERR, stdout); + + return 0; +} + +static Result setupContextForDirectToFileDownload(CURL *hnd, const char * url) +{ + Result ret = setupContext(hnd, url); + if (ret != 0) { + return ret; + } + + curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, handle_data_to_file); + + return 0; +} + +Result downloadToFile(std::string url, std::string path) +{ + Result ret = 0; + printf("Downloading from:\n%s\nto:\n%s\n", url.c_str(), path.c_str()); + + void *socubuf = memalign(0x1000, 0x100000); + if (!socubuf) + { + return -1; + } + + ret = socInit((u32*)socubuf, 0x100000); + if (R_FAILED(ret)) + { + free(socubuf); + return ret; + } + + Handle fileHandle; + + ret = openFile(&fileHandle, path.c_str(), true); + if (R_FAILED(ret)) { + printf("Error: couldn't open file to write.\n"); + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_sz = 0; + result_written = 0; + return DL_ERROR_WRITEFILE; + } + + result_fileHandle = &fileHandle; + + CURL *hnd = curl_easy_init(); + ret = setupContextForDirectToFileDownload(hnd, url.c_str()); + if (ret != 0) { + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_fileHandle = nullptr; + result_sz = 0; + result_written = 0; + FSFILE_Close(fileHandle); + return ret; + } + + u64 startTime = osGetTime(); + + CURLcode cres = curl_easy_perform(hnd); + curl_easy_cleanup(hnd); + + if (cres != CURLE_OK) { + printf("Error in:\ncurl\n"); + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_fileHandle = nullptr; + result_sz = 0; + result_written = 0; + FSFILE_Close(fileHandle); + return -1; + } + + u64 endTime = osGetTime(); + u64 totalTime = endTime - startTime; + printf("Download took %llu milliseconds.\n", totalTime); + + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_fileHandle = nullptr; + result_sz = 0; + result_written = 0; + FSFILE_Close(fileHandle); + return 0; +} + +Result downloadFromRelease(std::string url, std::string asset, std::string path, bool includePrereleases) +{ + Result ret = 0; + void *socubuf = memalign(0x1000, 0x100000); + if (!socubuf) + { + return -1; + } + + ret = socInit((u32*)socubuf, 0x100000); + if (R_FAILED(ret)) + { + free(socubuf); + return ret; + } + + std::regex parseUrl("github\\.com\\/(.+)\\/(.+)"); + std::smatch result; + regex_search(url, result, parseUrl); + + std::string repoOwner = result[1].str(), repoName = result[2].str(); + + std::stringstream apiurlStream; + apiurlStream << "https://api.github.com/repos/" << repoOwner << "/" << repoName << (includePrereleases ? "/releases" : "/releases/latest"); + std::string apiurl = apiurlStream.str(); + + printf("Downloading latest release from repo:\n%s\nby:\n%s\n", repoName.c_str(), repoOwner.c_str()); + printf("Crafted API url:\n%s\n", apiurl.c_str()); + + CURL *hnd = curl_easy_init(); + ret = setupContext(hnd, apiurl.c_str()); + if (ret != 0) { + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_sz = 0; + result_written = 0; + return ret; + } + + CURLcode cres = curl_easy_perform(hnd); + curl_easy_cleanup(hnd); + char* newbuf = (char*)realloc(result_buf, result_written + 1); + result_buf = newbuf; + result_buf[result_written] = 0; //nullbyte to end it as a proper C style string + + if (cres != CURLE_OK) { + printf("Error in:\ncurl\n"); + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_sz = 0; + result_written = 0; + return -1; + } + + printf("Looking for asset with matching name:\n%s\n", asset.c_str()); + std::string assetUrl; + json parsedAPI = json::parse(result_buf); + if(includePrereleases) parsedAPI = parsedAPI[0]; + if (parsedAPI["assets"].is_array()) { + for (auto jsonAsset : parsedAPI["assets"]) { + if (jsonAsset.is_object() && jsonAsset["name"].is_string() && jsonAsset["browser_download_url"].is_string()) { + std::string assetName = jsonAsset["name"]; + if (matchPattern(asset, assetName)) { + assetUrl = jsonAsset["browser_download_url"]; + break; + } + } + } + } + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_sz = 0; + result_written = 0; + + if (assetUrl.empty()) + ret = DL_ERROR_GIT; + else + ret = downloadToFile(assetUrl, path); + + return ret; +} + +/** + * Check Wi-Fi status. + * @return True if Wi-Fi is connected; false if not. + */ +bool checkWifiStatus(void) { + u32 wifiStatus; + bool res = false; + + if (R_SUCCEEDED(ACU_GetWifiStatus(&wifiStatus)) && wifiStatus) { + res = true; + } + + return res; +} + +void downloadFailed(void) { + DisplayMsg(Lang::get("DOWNLOAD_FAILED")); + for (int i = 0; i < 60*2; i++) { + gspWaitForVBlank(); + } +} + +void notImplemented(void) { + DisplayMsg(Lang::get("NOT_IMPLEMENTED")); + for (int i = 0; i < 60*2; i++) { + gspWaitForVBlank(); + } +} + +void doneMsg(void) { + DisplayMsg(Lang::get("DONE")); + for (int i = 0; i < 60*2; i++) { + gspWaitForVBlank(); + } +} + +void notConnectedMsg(void) { + DisplayMsg(Lang::get("CONNECT_WIFI")); + for (int i = 0; i < 60*2; i++) { + gspWaitForVBlank(); + } +} + +std::string getLatestRelease(std::string repo, std::string item) +{ + Result ret = 0; + void *socubuf = memalign(0x1000, 0x100000); + if (!socubuf) + { + return ""; + } + + ret = socInit((u32*)socubuf, 0x100000); + if (R_FAILED(ret)) + { + free(socubuf); + return ""; + } + + std::stringstream apiurlStream; + apiurlStream << "https://api.github.com/repos/" << repo << "/releases/latest"; + std::string apiurl = apiurlStream.str(); + + CURL *hnd = curl_easy_init(); + ret = setupContext(hnd, apiurl.c_str()); + if (ret != 0) { + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_sz = 0; + result_written = 0; + return ""; + } + + CURLcode cres = curl_easy_perform(hnd); + curl_easy_cleanup(hnd); + char* newbuf = (char*)realloc(result_buf, result_written + 1); + result_buf = newbuf; + result_buf[result_written] = 0; //nullbyte to end it as a proper C style string + + if (cres != CURLE_OK) { + printf("Error in:\ncurl\n"); + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_sz = 0; + result_written = 0; + return ""; + } + + std::string jsonItem; + json parsedAPI = json::parse(result_buf); + if (parsedAPI[item].is_string()) { + jsonItem = parsedAPI[item]; + } + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_sz = 0; + result_written = 0; + + return jsonItem; +} + +std::string getLatestCommit(std::string repo, std::string item) +{ + Result ret = 0; + void *socubuf = memalign(0x1000, 0x100000); + if (!socubuf) + { + return ""; + } + + ret = socInit((u32*)socubuf, 0x100000); + if (R_FAILED(ret)) + { + free(socubuf); + return ""; + } + + std::stringstream apiurlStream; + apiurlStream << "https://api.github.com/repos/" << repo << "/commits/master"; + std::string apiurl = apiurlStream.str(); + + CURL *hnd = curl_easy_init(); + ret = setupContext(hnd, apiurl.c_str()); + if (ret != 0) { + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_sz = 0; + result_written = 0; + return ""; + } + + CURLcode cres = curl_easy_perform(hnd); + curl_easy_cleanup(hnd); + char* newbuf = (char*)realloc(result_buf, result_written + 1); + result_buf = newbuf; + result_buf[result_written] = 0; //nullbyte to end it as a proper C style string + + if (cres != CURLE_OK) { + printf("Error in:\ncurl\n"); + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_sz = 0; + result_written = 0; + return ""; + } + + std::string jsonItem; + json parsedAPI = json::parse(result_buf); + if (parsedAPI[item].is_string()) { + jsonItem = parsedAPI[item]; + } + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_sz = 0; + result_written = 0; + + return jsonItem; +} + +std::string getLatestCommit(std::string repo, std::string array, std::string item) +{ + Result ret = 0; + void *socubuf = memalign(0x1000, 0x100000); + if (!socubuf) + { + return ""; + } + + ret = socInit((u32*)socubuf, 0x100000); + if (R_FAILED(ret)) + { + free(socubuf); + return ""; + } + + std::stringstream apiurlStream; + apiurlStream << "https://api.github.com/repos/" << repo << "/commits/master"; + std::string apiurl = apiurlStream.str(); + + CURL *hnd = curl_easy_init(); + ret = setupContext(hnd, apiurl.c_str()); + if (ret != 0) { + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_sz = 0; + result_written = 0; + return ""; + } + + CURLcode cres = curl_easy_perform(hnd); + curl_easy_cleanup(hnd); + char* newbuf = (char*)realloc(result_buf, result_written + 1); + result_buf = newbuf; + result_buf[result_written] = 0; //nullbyte to end it as a proper C style string + + if (cres != CURLE_OK) { + printf("Error in:\ncurl\n"); + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_sz = 0; + result_written = 0; + return ""; + } + + std::string jsonItem; + json parsedAPI = json::parse(result_buf); + if (parsedAPI[array][item].is_string()) { + jsonItem = parsedAPI[array][item]; + } + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_sz = 0; + result_written = 0; + + return jsonItem; +} + +std::vector getThemeList(std::string repo, std::string path) +{ + Result ret = 0; + void *socubuf = memalign(0x1000, 0x100000); + std::vector emptyVector; + if (!socubuf) + { + return emptyVector; + } + + ret = socInit((u32*)socubuf, 0x100000); + if (R_FAILED(ret)) + { + free(socubuf); + return emptyVector; + } + + std::stringstream apiurlStream; + apiurlStream << "https://api.github.com/repos/" << repo << "/contents/" << path; + std::string apiurl = apiurlStream.str(); + + CURL *hnd = curl_easy_init(); + ret = setupContext(hnd, apiurl.c_str()); + if (ret != 0) { + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_sz = 0; + result_written = 0; + return emptyVector; + } + + CURLcode cres = curl_easy_perform(hnd); + curl_easy_cleanup(hnd); + char* newbuf = (char*)realloc(result_buf, result_written + 1); + result_buf = newbuf; + result_buf[result_written] = 0; //nullbyte to end it as a proper C style string + + if (cres != CURLE_OK) { + printf("Error in:\ncurl\n"); + socExit(); + free(result_buf); + free(socubuf); + result_buf = NULL; + result_sz = 0; + result_written = 0; + + return emptyVector; + } + + std::vector jsonItems; + json parsedAPI = json::parse(result_buf); + for(uint i=0;i themeContents = getThemeList("Universal-Team/extras", path); + for(uint i=0;i parseObjects() { std::vector tinyDBList; -// adapted from GM9i's byte parsing. -std::string parseBytes(int bytes) { - char out[32]; - if(bytes == 1) - snprintf(out, sizeof(out), "%d Byte", bytes); - else if(bytes < 1024) - snprintf(out, sizeof(out), "%d Bytes", bytes); - else if(bytes < 1024 * 1024) - snprintf(out, sizeof(out), "%.1f KB", (float)bytes / 1024); - else if (bytes < 1024 * 1024 * 1024) - snprintf(out, sizeof(out), "%.1f MB", (float)bytes / 1024 / 1024); - else - snprintf(out, sizeof(out), "%.1f GB", (float)bytes / 1024 / 1024 / 1024); - - return out; -} - TinyDB::TinyDB() { DisplayMsg(Lang::get("TINYDB_DOWNLOADING")); downloadToFile("https://tinydb.eiphax.tech/api/universal-updater.json?raw=true", tinyDBFile); @@ -103,7 +87,7 @@ void TinyDB::Draw(void) const { Gui::DrawStringCentered(0, 95, 0.6f, Config::TxtColor, Lang::get("RELEASE_TAG") + std::string(tinyDBJson[selectedOption]["info"]["releaseTag"]), 400); Gui::DrawStringCentered(0, 125, 0.6f, Config::TxtColor, Lang::get("RELEASE_ID") + std::string(tinyDBJson[selectedOption]["info"]["releaseId"]), 400); Gui::DrawStringCentered(0, 155, 0.6f, Config::TxtColor, Lang::get("TITLE_ID") + std::string(tinyDBJson[selectedOption]["info"]["titleid"]), 400); - Gui::DrawStringCentered(0, 185, 0.6f, Config::TxtColor, Lang::get("FILE_SIZE") + parseBytes(int64_t(tinyDBJson[selectedOption]["info"]["fileSize"])), 400); + Gui::DrawStringCentered(0, 185, 0.6f, Config::TxtColor, Lang::get("FILE_SIZE") + formatBytes(int64_t(tinyDBJson[selectedOption]["info"]["fileSize"])), 400); Gui::DrawStringCentered(0, 2, 0.7f, Config::TxtColor, "TinyDB", 400); std::string entryAmount = std::to_string(selection+1) + " / " + std::to_string(tinyDBList.size()); Gui::DrawString(397-Gui::GetStringWidth(0.6f, entryAmount), 237-Gui::GetStringHeight(0.6f, entryAmount), 0.6f, Config::TxtColor, entryAmount); diff --git a/source/utils/formatting.cpp b/source/utils/formatting.cpp new file mode 100644 index 0000000..d9aaee6 --- /dev/null +++ b/source/utils/formatting.cpp @@ -0,0 +1,19 @@ +#include +#include "utils/formatting.hpp" + +// adapted from GM9i's byte parsing. +std::string formatBytes(int bytes) { + char out[32]; + if(bytes == 1) + snprintf(out, sizeof(out), "%d Byte", bytes); + else if(bytes < 1024) + snprintf(out, sizeof(out), "%d Bytes", bytes); + else if(bytes < 1024 * 1024) + snprintf(out, sizeof(out), "%.1f KB", (float)bytes / 1024); + else if (bytes < 1024 * 1024 * 1024) + snprintf(out, sizeof(out), "%.1f MB", (float)bytes / 1024 / 1024); + else + snprintf(out, sizeof(out), "%.1f GB", (float)bytes / 1024 / 1024 / 1024); + + return out; +} \ No newline at end of file