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