From d71dcc180785e63cf00a1dd6f56694e1af17747e Mon Sep 17 00:00:00 2001 From: flb Date: Tue, 6 Sep 2022 20:45:26 +0200 Subject: [PATCH] Add Custom Downloads tab --- Makefile | 2 +- README.md | 7 ++-- customPacks.json | 4 -- custom_packs.json | 9 +++++ hide_tabs.json | 1 - include/ams_tab.hpp | 11 +++--- include/constants.hpp | 3 +- include/list_download_tab.hpp | 1 - resources/i18n/en-US/menus.json | 6 ++- source/ams_tab.cpp | 68 +++++++++++++++++++-------------- source/changelog_page.cpp | 4 ++ source/extract.cpp | 21 +++++----- source/main_frame.cpp | 8 ++-- 13 files changed, 83 insertions(+), 62 deletions(-) delete mode 100644 customPacks.json create mode 100644 custom_packs.json diff --git a/Makefile b/Makefile index 4ac18ab..ab207fe 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ DATA := data INCLUDES := include /lib/borealis/library/include/borealis/extern/nlohmann APP_TITLE := All-in-One Switch Updater APP_AUTHOR := HamletDuFromage -APP_VERSION := 2.19.3 +APP_VERSION := 2.20.0 TARGET := $(notdir $(CURDIR)) ROMFS := resources diff --git a/README.md b/README.md index ea75d8e..ba61462 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@

-A Nintendo Switch homebrew app to download and update CFWs, sigpatches, FWs and cheat codes. Supports Atmosphère, ReiNX and SXOS. +A Nintendo Switch homebrew app to download and update CFWs, FWs and cheat codes. Supports Atmosphère, ReiNX and SXOS. Works on **unpatched** (Erista) and **patched** (v2/Mariko) Switches. @@ -27,13 +27,12 @@ Copy the `aio-switch-updater/` directory to `/switch/` on your sdcard. - Update the Atmosphère Switch Custom Firmware. AIO-Switch-Updater uses a custom RCM payload to finalise the install as it can't be performed while HOS is running. - If you would like to preserve additional files or directories, write their path (one line each) in `/config/aio-switch-updater/preserve.txt` and they won't be overwritten when updating. - Place [this file](https://github.com/HamletDuFromage/aio-switch-updater/blob/master/copy_files.txt) in `/config/aio-switch-updater/copy_files.txt` in order to have specific copy operations performed after each download. This is mainly meant for users with trinkets who want payloads automatically copied to a directory. - - A custom Atmosphère url can be entered in [this file](https://github.com/HamletDuFromage/aio-switch-updater/blob/master/customPacks.json). Once moved to `/config/aio-switch-updater/customPacks.json`, it will show on the `Update Atmopshère` menu. This can be used to support third-party packs through AIO-Switch-Updater ### ⬦ Update Hekate/Payload - Download and update Hekate, as well as a selection of RCM payloads -### ⬦ Update Sigpatches -- Dowload sigpatches, which are patches required to launch homebrew .NSPs on the Atmosphère CFW. +### ⬦ Custom Downloads +- A custom Atmosphère url can be entered in [this file](https://github.com/HamletDuFromage/aio-switch-updater/blob/master/custom_packs.json). Once moved to `/config/aio-switch-updater/custom_packs.json`, it will show on the `Custom Download` menu. This can be used to support third-party packs through AIO-Switch-Updater. Non-Atmosphère downloads can also be added in the `misc` category. ### ⬦ Download firmwares - Download firmware files to `/firmware` that can then be installed using DayBreak. diff --git a/customPacks.json b/customPacks.json deleted file mode 100644 index a138a50..0000000 --- a/customPacks.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "Name of pack": "link_to_zip", - "Name of anther pack": "link_to_zip" -} \ No newline at end of file diff --git a/custom_packs.json b/custom_packs.json new file mode 100644 index 0000000..2413b12 --- /dev/null +++ b/custom_packs.json @@ -0,0 +1,9 @@ +{ + "ams": { + "Name of Atmosphère pack": "link_to_zip", + "Name of anther pack": "link_to_zip" + }, + "misc": { + "name of download": "link" + } +} \ No newline at end of file diff --git a/hide_tabs.json b/hide_tabs.json index d2813c0..0392cb2 100644 --- a/hide_tabs.json +++ b/hide_tabs.json @@ -5,7 +5,6 @@ "cfw": true, "cheats": true, "firmwares": true, - "sigpatches": true, "jccolor": true, "pccolor": true, "downloadpayload": true, diff --git a/include/ams_tab.hpp b/include/ams_tab.hpp index a04490a..f288bf9 100644 --- a/include/ams_tab.hpp +++ b/include/ams_tab.hpp @@ -4,23 +4,24 @@ #include #include +#include "constants.hpp" + class AmsTab : public brls::List { private: brls::ListItem* listItem; - brls::Label* description; - int size = 0; bool erista; + contentType type; nlohmann::ordered_json hekate; std::string GetRepoName(const std::string& repo); std::set GetLastDownloadedModules(const std::string& json_path); - void CreateStagedFrames(const std::string& text, const std::string& url, const std::string& operation, bool erista, bool hekate = false, const std::string& text_hekate = "", const std::string& hekate_url = ""); - void CreateDownloadItems(const nlohmann::ordered_json& cfw_links, bool hekate = true); + void CreateStagedFrames(const std::string& text, const std::string& url, bool erista, bool ams = true, bool hekate = false, const std::string& text_hekate = "", const std::string& hekate_url = ""); + void CreateDownloadItems(const nlohmann::ordered_json& cfw_links, bool hekate = true, bool ams = true); nlohmann::ordered_json SortDeepseaModules(const nlohmann::ordered_json& modules); void ShowCustomDeepseaBuilder(nlohmann::ordered_json& modules); public: - AmsTab(const nlohmann::json& nxlinks, const bool erista = true, const bool hideStandardEntries = false); + AmsTab(const nlohmann::json& nxlinks, const bool erista = true, const bool custom = false); }; class UnTogglableListItem : public brls::ToggleListItem diff --git a/include/constants.hpp b/include/constants.hpp index b4768bc..c48b09f 100644 --- a/include/constants.hpp +++ b/include/constants.hpp @@ -18,7 +18,6 @@ constexpr const char APP_FILENAME[] = "/config/aio-switch-updater/app.zip"; constexpr const char NXLINKS_URL[] = "https://raw.githubusercontent.com/HamletDuFromage/nx-links/master/nx-links.json"; -constexpr const char SIGPATCHES_URL[] = "https://raw.githubusercontent.com/HamletDuFromage/nx-links/master/sigpatches.json"; constexpr const char SIGPATCHES_FILENAME[] = "/config/aio-switch-updater/sigpatches.zip"; constexpr const char HEKATE_IPL_PATH[] = "/bootloader/hekate_ipl.ini"; @@ -41,7 +40,7 @@ constexpr const char DEEPSEA_META_JSON[] = "https://builder.teamneptune.net/meta constexpr const char DEEPSEA_BUILD_URL[] = "https://builder.teamneptune.net/build/"; constexpr const char DEEPSEA_PACKAGE_PATH[] = "/config/deepsea/customPackage.json"; -constexpr const char CUSTOM_PACKS_PATH[] = "/config/aio-switch-updater/customPacks.json"; +constexpr const char CUSTOM_PACKS_PATH[] = "/config/aio-switch-updater/custom_packs.json"; constexpr const char CHEATS_URL_TITLES[] = "https://github.com/HamletDuFromage/switch-cheats-db/releases/latest/download/titles.zip"; constexpr const char CHEATS_URL_CONTENTS[] = "https://github.com/HamletDuFromage/switch-cheats-db/releases/latest/download/contents.zip"; diff --git a/include/list_download_tab.hpp b/include/list_download_tab.hpp index a4c03cf..e76be5e 100644 --- a/include/list_download_tab.hpp +++ b/include/list_download_tab.hpp @@ -13,7 +13,6 @@ private: std::string currentCheatsVer = ""; std::string newCheatsVer = ""; contentType type; - int size = 0; void createList(); void createList(contentType type); void createCheatSlipItem(); diff --git a/resources/i18n/en-US/menus.json b/resources/i18n/en-US/menus.json index 1defd78..474dc3a 100644 --- a/resources/i18n/en-US/menus.json +++ b/resources/i18n/en-US/menus.json @@ -127,6 +127,7 @@ "new_update": " - New app update available", "about": "About", "update_ams": "Update Atmosphère", + "custom_downloads": "Custom Downloads", "update_cfw": "Update CFW", "update_bootloaders": "Update bootloaders", "update_sigpatches": "Update sigpatches", @@ -191,7 +192,10 @@ "cant_fetch_deepsea": "Couldn't fetch packages. Open a Github issue if this persists.", "build_your_deepsea": "Build your own custom DeepSea package by selecting packages", "getting_ams": "Getting Atmosphère", - "custom_packs_label": "\u25c6 Here are packs listed in the {} file. Be aware that those are not endorsed by AIO-Switch-Updater so make sure you trust their source." + "custom_packs_label": "\u25c6 Here are packs listed in the {} file. Be aware that those are not endorsed by AIO-Switch-Updater so make sure you trust their source.", + "custom_packs_ams": "\u25c6 Downloads containing the Atmosphère custom firmware.", + "custom_packs_misc": "\u25c6 Downloads containing homebrew and tools.", + "custom_download" : "Custom Download" }, "firmware": { "launch_daybreak": "Do you want to launch Daybreak to install the downloaded sysupdate?" diff --git a/source/ams_tab.cpp b/source/ams_tab.cpp index ea42110..5de61a0 100644 --- a/source/ams_tab.cpp +++ b/source/ams_tab.cpp @@ -15,22 +15,22 @@ namespace i18n = brls::i18n; using namespace i18n::literals; -AmsTab::AmsTab(const nlohmann::json& nxlinks, const bool erista, const bool hideStandardEntries) : brls::List() +AmsTab::AmsTab(const nlohmann::json& nxlinks, const bool erista, const bool custom) : brls::List() { this->erista = erista; this->hekate = util::getValueFromKey(nxlinks, "hekate"); - auto cfws = util::getValueFromKey(nxlinks, "cfws"); - if (!hideStandardEntries) { - this->description = new brls::Label(brls::LabelStyle::DESCRIPTION, "menus/main/ams_text"_i18n + (CurrentCfw::running_cfw == CFW::ams ? "\n" + "menus/ams_update/current_ams"_i18n + CurrentCfw::getAmsInfo() : "") + (erista ? "\n" + "menus/ams_update/erista_rev"_i18n : "\n" + "menus/ams_update/mariko_rev"_i18n), true); - this->addView(description); + if (!custom) { + this->type = contentType::ams_cfw; + auto cfws = util::getValueFromKey(nxlinks, "cfws"); + + this->addView(new brls::Label(brls::LabelStyle::DESCRIPTION, "menus/main/ams_text"_i18n + (CurrentCfw::running_cfw == CFW::ams ? "\n" + "menus/ams_update/current_ams"_i18n + CurrentCfw::getAmsInfo() : "") + (erista ? "\n" + "menus/ams_update/erista_rev"_i18n : "\n" + "menus/ams_update/mariko_rev"_i18n), true)); CreateDownloadItems(util::getValueFromKey(cfws, "Atmosphere")); - description = new brls::Label( + this->addView(new brls::Label( brls::LabelStyle::DESCRIPTION, "menus/ams_update/deepsea_label"_i18n, - true); - this->addView(description); + true)); listItem = new brls::ListItem("menus/ams_update/get_custom_deepsea"_i18n); listItem->setHeight(LISTITEM_HEIGHT); @@ -40,25 +40,36 @@ AmsTab::AmsTab(const nlohmann::json& nxlinks, const bool erista, const bool hide this->ShowCustomDeepseaBuilder(modules); }); this->addView(listItem); - CreateDownloadItems(util::getValueFromKey(cfws, "DeepSea"), false); } - auto custom_pack = fs::parseJsonFile(CUSTOM_PACKS_PATH); - if (custom_pack.size() != 0) { - description = new brls::Label( + else { + auto custom_pack = fs::parseJsonFile(CUSTOM_PACKS_PATH); + this->addView(new brls::Label( brls::LabelStyle::DESCRIPTION, fmt::format("menus/ams_update/custom_packs_label"_i18n, CUSTOM_PACKS_PATH), - true); - this->addView(description); - - CreateDownloadItems(cfws.size() ? custom_pack : nlohmann::ordered_json::object(), true); // TODO: better way to check for availability of the links + true)); + if (custom_pack.contains("ams") && custom_pack["ams"].size() != 0) { + this->type = contentType::ams_cfw; + this->addView(new brls::Label( + brls::LabelStyle::DESCRIPTION, + "menus/ams_update/custom_packs_ams"_i18n, + true)); + CreateDownloadItems(custom_pack["ams"], true); // TODO: check for internet + } + if (custom_pack.contains("misc") && custom_pack["misc"].size() != 0) { + this->type = contentType::bootloaders; + this->addView(new brls::Label( + brls::LabelStyle::DESCRIPTION, + "menus/ams_update/custom_packs_misc"_i18n, + true)); + CreateDownloadItems(custom_pack["misc"], false, false); + } } } -void AmsTab::CreateDownloadItems(const nlohmann::ordered_json& cfw_links, bool hekate) +void AmsTab::CreateDownloadItems(const nlohmann::ordered_json& cfw_links, bool hekate, bool ams) { - std::string operation("menus/ams_update/getting_ams"_i18n); std::vector> links; links = download::getLinksFromJson(cfw_links); if (links.size()) { @@ -71,19 +82,19 @@ void AmsTab::CreateDownloadItems(const nlohmann::ordered_json& cfw_links, bool h std::string text("menus/common/download"_i18n + link.first + "menus/common/from"_i18n + url); listItem = new brls::ListItem(link.first); listItem->setHeight(LISTITEM_HEIGHT); - listItem->getClickEvent()->subscribe([this, text, text_hekate, url, hekate_url, operation, hekate](brls::View* view) { + listItem->getClickEvent()->subscribe([this, text, text_hekate, url, hekate_url, hekate, ams](brls::View* view) { if (!erista && !std::filesystem::exists(MARIKO_PAYLOAD_PATH)) { brls::Application::crash("menus/errors/mariko_payload_missing"_i18n); } else { - CreateStagedFrames(text, url, operation, erista, hekate, text_hekate, hekate_url); + CreateStagedFrames(text, url, erista, ams, hekate, text_hekate, hekate_url); } }); this->addView(listItem); } } else { - description = new brls::Label( + brls::Label* description = new brls::Label( brls::LabelStyle::SMALL, "menus/main/links_not_found"_i18n, true); @@ -92,16 +103,16 @@ void AmsTab::CreateDownloadItems(const nlohmann::ordered_json& cfw_links, bool h } } -void AmsTab::CreateStagedFrames(const std::string& text, const std::string& url, const std::string& operation, bool erista, bool hekate, const std::string& text_hekate, const std::string& hekate_url) +void AmsTab::CreateStagedFrames(const std::string& text, const std::string& url, bool erista, bool ams, bool hekate, const std::string& text_hekate, const std::string& hekate_url) { brls::StagedAppletFrame* stagedFrame = new brls::StagedAppletFrame(); - stagedFrame->setTitle(operation); + stagedFrame->setTitle(this->type == contentType::ams_cfw ? "menus/ams_update/getting_ams"_i18n : "menus/ams_update/custom_download"_i18n); stagedFrame->addStage( new ConfirmPage(stagedFrame, text)); stagedFrame->addStage( - new WorkerPage(stagedFrame, "menus/common/downloading"_i18n, [url]() { util::downloadArchive(url, contentType::ams_cfw); })); + new WorkerPage(stagedFrame, "menus/common/downloading"_i18n, [&, url]() { util::downloadArchive(url, this->type); })); stagedFrame->addStage( - new WorkerPage(stagedFrame, "menus/common/extracting"_i18n, []() { util::extractArchive(contentType::ams_cfw); })); + new WorkerPage(stagedFrame, "menus/common/extracting"_i18n, [&]() { util::extractArchive(this->type); })); if (hekate) { stagedFrame->addStage( new DialoguePage_ams(stagedFrame, text_hekate, erista)); @@ -110,8 +121,10 @@ void AmsTab::CreateStagedFrames(const std::string& text, const std::string& url, stagedFrame->addStage( new WorkerPage(stagedFrame, "menus/common/extracting"_i18n, []() { util::extractArchive(contentType::bootloaders); })); } - stagedFrame->addStage( - new ConfirmPage(stagedFrame, "menus/ams_update/reboot_rcm"_i18n, false, true, erista)); + if (ams) + stagedFrame->addStage(new ConfirmPage(stagedFrame, "menus/ams_update/reboot_rcm"_i18n, false, true, erista)); + else + stagedFrame->addStage(new ConfirmPage(stagedFrame, "menus/common/all_done"_i18n, true)); brls::Application::pushView(stagedFrame); } @@ -207,7 +220,6 @@ void AmsTab::ShowCustomDeepseaBuilder(nlohmann::ordered_json& modules) this->CreateStagedFrames("menus/common/download"_i18n + "Custom DeepSea package" + "menus/common/from"_i18n + request_url, request_url, - "menus/ams_update/get_custom_deepsea"_i18n, this->erista); return true; }); diff --git a/source/changelog_page.cpp b/source/changelog_page.cpp index 73e3635..1c1eda8 100644 --- a/source/changelog_page.cpp +++ b/source/changelog_page.cpp @@ -249,6 +249,10 @@ ChangelogPage::ChangelogPage() : AppletFrame(true, true) verTitles.push_back("v2.19.3"); changes.push_back("\uE016 Fix wrong bid for titles overriden by HBL.\n\uE016 Improve Korean localisation (https://github.com/DDinghoya).\n\uE016 Improve Italian localisation (https://github.com/clamintus)."); + verTitles.push_back("v2.20.0"); + changes.push_back("\uE016 Significantly increase extraction speed (https://github.com/PoloNX).\n\uE016 Create a \"Custom Downloads\" tab that supports user-provided links for Atmosphère packs as well as regular downloads."); + + for (int i = verTitles.size() - 1; i >= 0; i--) { listItem = new brls::ListItem(verTitles[i]); change = changes[i]; diff --git a/source/extract.cpp b/source/extract.cpp index f209bd9..eab3b76 100644 --- a/source/extract.cpp +++ b/source/extract.cpp @@ -53,9 +53,8 @@ namespace extract { return size; // in B } - void preWork(const std::string& archivePath, const std::string& workingPath) + void ensureAvailableStorage(const std::string& archivePath) { - chdir(workingPath.c_str()); s64 uncompressedSize = getUncompressedSize(archivePath); s64 freeStorage; @@ -91,7 +90,7 @@ namespace extract { void extract(const std::string& archivePath, const std::string& workingPath, int overwriteInis, std::function func) { - preWork(archivePath, workingPath); + ensureAvailableStorage(archivePath); unzFile zfile = unzOpen(archivePath.c_str()); unz_global_info gi; @@ -107,28 +106,28 @@ namespace extract { char szFilename[0x301] = ""; unzOpenCurrentFile(zfile); unzGetCurrentFileInfo(zfile, NULL, szFilename, sizeof(szFilename), NULL, 0, NULL, 0); - std::string filename = szFilename; + std::string filename = workingPath + szFilename; if (ProgressEvent::instance().getInterupt()) { unzCloseCurrentFile(zfile); break; } - if (appPath != workingPath + filename) { - if ((overwriteInis == 0 && filename.substr(filename.length() - 4) == ".ini") || std::find_if(ignoreList.begin(), ignoreList.end(), [&filename, &workingPath](std::string ignored) { - u8 res = (workingPath + filename).find(ignored); + if (appPath != filename) { + if ((overwriteInis == 0 && filename.substr(filename.length() - 4) == ".ini") || std::find_if(ignoreList.begin(), ignoreList.end(), [&filename](std::string ignored) { + u8 res = (filename).find(ignored); return (res == 0 || res == 1); }) != ignoreList.end()) { - if (!std::filesystem::exists(workingPath + filename)) { + if (!std::filesystem::exists(filename)) { extractEntry(filename, zfile); } } else { - if ((filename == "atmosphere/package3") || (filename == "atmosphere/stratosphere.romfs")) { + if ((filename == "/atmosphere/package3") || (filename == "/atmosphere/stratosphere.romfs")) { extractEntry(filename + ".aio", zfile); } else { extractEntry(filename, zfile); if (filename.substr(0, 13) == "hekate_ctcaer") { - fs::copyFile(workingPath + filename, UPDATE_BIN_PATH); + fs::copyFile(filename, UPDATE_BIN_PATH); if (CurrentCfw::running_cfw == CFW::ams && util::showDialogBoxBlocking(fmt::format("menus/utils/set_hekate_reboot_payload"_i18n, UPDATE_BIN_PATH, REBOOT_PAYLOAD_PATH), "menus/common/yes"_i18n, "menus/common/no"_i18n) == 0) { fs::copyFile(UPDATE_BIN_PATH, REBOOT_PAYLOAD_PATH); } @@ -235,7 +234,7 @@ namespace extract { void extractCheats(const std::string& archivePath, const std::vector& titles, CFW cfw, const std::string& version, bool extractAll) { - preWork(archivePath, "/"); + ensureAvailableStorage(archivePath); unzFile zfile = unzOpen(archivePath.c_str()); unz_global_info gi; diff --git a/source/main_frame.cpp b/source/main_frame.cpp index de26e9c..1c2bd7b 100644 --- a/source/main_frame.cpp +++ b/source/main_frame.cpp @@ -41,20 +41,20 @@ MainFrame::MainFrame() : TabFrame() this->addTab("menus/main/about"_i18n, new AboutTab()); if (!util::getBoolValue(hideStatus, "atmosphere")) - this->addTab("menus/main/update_ams"_i18n, new AmsTab(nxlinks, erista, util::getBoolValue(hideStatus, "atmosphereentries"))); + this->addTab("menus/main/update_ams"_i18n, new AmsTab(nxlinks, erista, false)); if (!util::getBoolValue(hideStatus, "cfw")) this->addTab("menus/main/update_bootloaders"_i18n, new ListDownloadTab(contentType::bootloaders, nxlinks)); - if (!util::getBoolValue(hideStatus, "sigpatches")) - this->addTab("menus/main/update_sigpatches"_i18n, new ListDownloadTab(contentType::sigpatches, nxlinks)); - if (!util::getBoolValue(hideStatus, "firmwares")) this->addTab("menus/main/download_firmware"_i18n, new ListDownloadTab(contentType::fw, nxlinks)); if (!util::getBoolValue(hideStatus, "cheats")) this->addTab("menus/main/download_cheats"_i18n, new ListDownloadTab(contentType::cheats)); + if (!util::getBoolValue(hideStatus, "custom")) + this->addTab("menus/main/custom_downloads"_i18n, new AmsTab(nxlinks, erista, true)); + if (!util::getBoolValue(hideStatus, "tools")) this->addTab("menus/main/tools"_i18n, new ToolsTab(tag, util::getValueFromKey(nxlinks, "payloads"), erista, hideStatus));