diff --git a/Makefile b/Makefile index eca9461..79eafde 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ DATA := data INCLUDES := include lib/zipper/include /lib/borealis/library/include/borealis/extern/nlohmann APP_TITLE := All-in-One Switch Updater APP_AUTHOR := HamletDuFromage -APP_VERSION := 2.5.2 +APP_VERSION := 2.5.3 TARGET := $(notdir $(CURDIR)) ROMFS := resources diff --git a/include/fs.hpp b/include/fs.hpp index 411cbdc..72f5d6f 100644 --- a/include/fs.hpp +++ b/include/fs.hpp @@ -12,5 +12,6 @@ namespace fs bool copyFile(const std::string& from, const std::string& to); std::string copyFiles(const std::string& path); void createTree(std::string path); - std::set readLineByLine(const std::string& path); + std::set readLineByLine(const std::string& path); + Result getFreeStorageSD(s64& free); } diff --git a/resources/i18n/en-US/menus.json b/resources/i18n/en-US/menus.json index 34fc10f..a97d27b 100644 --- a/resources/i18n/en-US/menus.json +++ b/resources/i18n/en-US/menus.json @@ -159,6 +159,9 @@ "net": { "title": "Internet settings" }, + "errors": { + "unsufficient_storage": "There isn't enough space available on your SD card to perform this operation." + }, "language": { "system_default": "System default", "en-US": "American English (en-US)", diff --git a/source/changelog_page.cpp b/source/changelog_page.cpp index b7faaef..c98ff93 100644 --- a/source/changelog_page.cpp +++ b/source/changelog_page.cpp @@ -154,6 +154,9 @@ ChangelogPage::ChangelogPage() : AppletFrame(true, true) verTitles.push_back("v2.5.2"); changes.push_back("\uE016 Updated Japanese and Traditional Chinese translations (thanks to yyoossk and qazrfv1234).\n\uE016 Updated payload so that it doesn't throw errors if the bootloader folder is missing.\n\uE016 Minor code changes that may or may not result in performace improvements."); + verTitles.push_back("v2.5.3"); + changes.push_back("\uE016 Check available size before downloading and extracting files.\n\uE016 Now displaying a message when the custom payload is running."); + for(int i = verTitles.size() -1 ; i >= 0; i--){ listItem = new brls::ListItem(verTitles[i]); change = changes[i]; diff --git a/source/download.cpp b/source/download.cpp index ff66d75..050433f 100644 --- a/source/download.cpp +++ b/source/download.cpp @@ -1,16 +1,18 @@ #include "download.hpp" #include "utils.hpp" +#include "progress_event.hpp" +#include "fs.hpp" +#include #include #include #include #include #include - #include #include -#include -#include "progress_event.hpp" +namespace i18n = brls::i18n; +using namespace i18n::literals; constexpr const char API_AGENT[] = "HamletDuFromage"; constexpr int _1MiB = 0x100000; @@ -98,6 +100,24 @@ namespace download { return realsize; } + bool checkSize(CURL* curl, const std::string& url) { + curl_off_t dl; + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_USERAGENT, API_AGENT); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); + curl_easy_perform(curl); + auto res = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &dl); + if(!res) { + s64 freeStorage; + if(R_SUCCEEDED(fs::getFreeStorageSD(freeStorage)) && dl * 1.1 > freeStorage) { + return false; + } + } + return true; + } } std::vector downloadFile(const std::string& url, const char* output, int api) @@ -107,6 +127,7 @@ std::vector downloadFile(const std::string& url, const char* outpu ntwrk_struct_t chunk = {0}; time_old = std::chrono::steady_clock::now(); dlold = 0.0f; + bool can_download = true; if (curl) { FILE *fp = fopen(output, "wb"); @@ -116,39 +137,51 @@ std::vector downloadFile(const std::string& url, const char* outpu chunk.data_size = _1MiB; chunk.out = fp; - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_USERAGENT, API_AGENT); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); - - // write calls - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &chunk); - - if (api == OFF) - { - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); - curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, download_progress); + if(*output != 0) { + can_download = checkSize(curl, url); } - curl_easy_perform(curl); - if (fp && chunk.offset) - fwrite(chunk.data, 1, chunk.offset, fp); - curl_easy_cleanup(curl); - ProgressEvent::instance().setStep(ProgressEvent::instance().getMax()); + if(can_download) { + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_USERAGENT, API_AGENT); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(curl, CURLOPT_NOBODY, 0L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &chunk); + + if (api == OFF) + { + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); + curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, download_progress); + } + curl_easy_perform(curl); + + if (fp && chunk.offset && can_download) + fwrite(chunk.data, 1, chunk.offset, fp); + + curl_easy_cleanup(curl); + ProgressEvent::instance().setStep(ProgressEvent::instance().getMax()); + } } } + fclose(chunk.out); + if(!can_download) { + brls::Application::crash("menus/errors/unsufficient_storage"_i18n); + usleep(2000000); + brls::Application::quit(); + return (std::vector){}; + } + if (*output == 0) { std::vector res(chunk.data, chunk.data + chunk.offset); free(chunk.data); - fclose(chunk.out); return res; } else { free(chunk.data); - fclose(chunk.out); return (std::vector){}; } diff --git a/source/extract.cpp b/source/extract.cpp index 654fcb2..2e4fbcb 100644 --- a/source/extract.cpp +++ b/source/extract.cpp @@ -1,4 +1,9 @@ #include "extract.hpp" +#include "progress_event.hpp" +#include "utils.hpp" +#include "download.hpp" +#include "fs.hpp" +#include "main_frame.hpp" #include #include #include @@ -9,10 +14,6 @@ #include #include #include -#include "progress_event.hpp" -#include "utils.hpp" -#include "download.hpp" -#include "fs.hpp" namespace i18n = brls::i18n; using namespace i18n::literals; @@ -23,16 +24,34 @@ namespace extract { bool caselessCompare (const std::string& a, const std::string& b){ return strcasecmp(a.c_str(), b.c_str()) < 0; } + + void preWork(zipper::Unzipper& unzipper, const std::string& workingPath, std::vector& entries) { + chdir(workingPath.c_str()); + entries = unzipper.entries(); + s64 uncompressedSize = 0; + s64 freeStorage; + for (const auto& entry: entries) + uncompressedSize += entry.uncompressedSize; + + if(R_SUCCEEDED(fs::getFreeStorageSD(freeStorage))) { + if(uncompressedSize * 1.1 > freeStorage) { + unzipper.close(); + brls::Application::crash("menus/errors/unsufficient_storage"_i18n); + usleep(2000000); + brls::Application::quit(); + } + } + ProgressEvent::instance().reset(); + ProgressEvent::instance().setStep(1); + ProgressEvent::instance().setTotalSteps(entries.size() + 1); + } } void extract(const std::string& filename, const std::string& workingPath, int overwriteInis){ - ProgressEvent::instance().reset(); - ProgressEvent::instance().setStep(1); - chdir(workingPath.c_str()); - std::set ignoreList = fs::readLineByLine(FILES_IGNORE); zipper::Unzipper unzipper(filename); - std::vector entries = unzipper.entries(); - ProgressEvent::instance().setTotalSteps(entries.size() + 1); + std::vector entries; + preWork(unzipper, workingPath, entries); + std::set ignoreList = fs::readLineByLine(FILES_IGNORE); for (const auto& entry : entries) { if((overwriteInis == 0 && entry.name.substr(entry.name.length() - 4) == ".ini") || find_if(ignoreList.begin(), ignoreList.end(), [&entry](std::string ignored) { @@ -60,14 +79,11 @@ void extract(const std::string& filename, const std::string& workingPath, int o } void extract(const std::string& filename, const std::string& workingPath, const std::string& toExclude){ - ProgressEvent::instance().reset(); - ProgressEvent::instance().setStep(1); - chdir(workingPath.c_str()); + zipper::Unzipper unzipper(filename); + std::vector entries; + preWork(unzipper, workingPath, entries); std::set ignoreList = fs::readLineByLine(FILES_IGNORE); ignoreList.insert(toExclude); - zipper::Unzipper unzipper(filename); - std::vector entries = unzipper.entries(); - ProgressEvent::instance().setTotalSteps(entries.size() + 1); for (const auto& entry : entries) { if(find_if(ignoreList.begin(), ignoreList.end(), [&entry](std::string ignored) { u8 res = ("/" + entry.name).find(ignored); diff --git a/source/fs.cpp b/source/fs.cpp index 44cb395..a841ab1 100644 --- a/source/fs.cpp +++ b/source/fs.cpp @@ -1,9 +1,8 @@ #include "fs.hpp" +#include "constants.hpp" #include #include #include -#include "constants.hpp" - namespace i18n = brls::i18n; using namespace i18n::literals; @@ -101,7 +100,7 @@ namespace fs { return error; } - std::set readLineByLine(const std::string& path){ + std::set readLineByLine(const std::string& path){ std::set titles; std::string str; std::ifstream in(path); @@ -116,4 +115,8 @@ namespace fs { return titles; } + Result getFreeStorageSD(s64& free) { + return nsGetFreeSpaceSize(NcmStorageId_SdCard, &free); + } + } \ No newline at end of file