diff --git a/assets/gfx/sprites.t3s b/assets/gfx/sprites.t3s index d66ffb4..301e1e0 100644 --- a/assets/gfx/sprites.t3s +++ b/assets/gfx/sprites.t3s @@ -10,6 +10,7 @@ sprites/info.png sprites/keyboard.png sprites/list.png sprites/noIcon.png +sprites/notes.png sprites/qr_code.png sprites/screenshot.png sprites/search.png diff --git a/assets/gfx/sprites/notes.png b/assets/gfx/sprites/notes.png new file mode 100644 index 0000000..c11078e Binary files /dev/null and b/assets/gfx/sprites/notes.png differ diff --git a/include/screens/mainScreen.hpp b/include/screens/mainScreen.hpp index 5f95a40..154bf1e 100644 --- a/include/screens/mainScreen.hpp +++ b/include/screens/mainScreen.hpp @@ -41,6 +41,7 @@ 3: Sorting. 4: Settings / Credits(?). 5: Screenshot Menu. + 6: Release Notes. */ class MainScreen : public Screen { @@ -58,7 +59,7 @@ private: ascending = false, updateFilter = false, screenshotFetch = false, canDisplay = false; int storeMode = 0, marks = 0, markIndex = 0, sPage = 0, lMode = 0, sSelection = 0, - lastMode = 0, smallDelay = 0, sPos = 0, screenshotIndex = 0, sSize = 0, zoom = 0; + lastMode = 0, smallDelay = 0, sPos = 0, screenshotIndex = 0, sSize = 0, zoom = 0, scrollIndex = 0; SortType sorttype = SortType::LAST_UPDATED; diff --git a/include/store/store.hpp b/include/store/store.hpp index 91b453f..0c95e55 100644 --- a/include/store/store.hpp +++ b/include/store/store.hpp @@ -57,6 +57,7 @@ public: std::string GetFileSizes(int index, const std::string &entry) const; std::vector GetScreenshotList(int index) const; std::vector GetScreenshotNames(int index) const; + std::string GetReleaseNotes(int index) const; std::vector GetDownloadList(int index) const; diff --git a/include/store/storeEntry.hpp b/include/store/storeEntry.hpp index 43a1508..f04d70b 100644 --- a/include/store/storeEntry.hpp +++ b/include/store/storeEntry.hpp @@ -55,6 +55,7 @@ public: std::vector GetSizes() const { return this->Sizes; }; std::vector GetScreenshots() const { return this->Screenshots; }; std::vector GetScreenshotNames() const { return this->ScreenshotNames; }; + std::string GetReleaseNotes() const { return this->ReleaseNotes; }; bool GetUpdateAvl() const { return this->UpdateAvailable; }; void SetUpdateAvl(bool v) { this->UpdateAvailable = v; }; @@ -65,7 +66,7 @@ public: }; private: - std::string Title, Author, Description, Category, Version, Console, LastUpdated, License, MarkString; + std::string Title, Author, Description, Category, Version, Console, LastUpdated, License, MarkString, ReleaseNotes; C2D_Image Icon; int SheetIndex, EntryIndex, Marks; std::vector FullCategory, FullConsole, Sizes, Screenshots, ScreenshotNames; diff --git a/include/store/storeUtils.hpp b/include/store/storeUtils.hpp index 52812c3..2b2a686 100644 --- a/include/store/storeUtils.hpp +++ b/include/store/storeUtils.hpp @@ -82,6 +82,10 @@ namespace StoreUtils { void DrawSorting(bool asc, SortType st); void SortHandle(std::unique_ptr &store, std::vector> &entries, bool &asc, SortType &st); + /* Release Notes. */ + void DrawReleaseNotes(const int &scrollIndex, const std::unique_ptr &entry, const std::unique_ptr &store); + void ReleaseNotesLogic(int &scrollIndex, const std::unique_ptr &entry, int &storeMode); + bool compareTitleDescending(const std::unique_ptr &a, const std::unique_ptr &b); bool compareTitleAscending(const std::unique_ptr &a, const std::unique_ptr &b); diff --git a/include/utils/config.hpp b/include/utils/config.hpp index 0014304..8b47bd6 100644 --- a/include/utils/config.hpp +++ b/include/utils/config.hpp @@ -83,6 +83,10 @@ public: /* The shortcut path. */ std::string shortcut() const { return this->v_shortcutPath; }; void shortcut(const std::string &v) { this->v_shortcutPath = v; if (!this->changesMade) this->changesMade = true; }; + + /* If displaying changelog. */ + bool changelog() const { return this->v_changelog; }; + void changelog(bool v) { this->v_changelog = v; if (!this->changesMade) this->changesMade = true; }; private: /* Mainly helper. */ bool getBool(const std::string &key); @@ -98,7 +102,9 @@ private: std::string v_language = "en", v_lastStore = "universal-db.unistore", v_3dsxPath = "sdmc:/3ds", v_ndsPath = "sdmc:", v_archivePath = "sdmc:", v_shortcutPath = "sdmc:/3ds/Universal-Updater/shortcuts"; - bool v_list = false, v_autoUpdate = true, v_metadata = true, v_updateCheck = true, v_showBg = false, v_customFont = false; + + bool v_list = false, v_autoUpdate = true, v_metadata = true, v_updateCheck = true, + v_showBg = false, v_customFont = false, v_changelog = true; }; #endif diff --git a/include/utils/download.hpp b/include/utils/download.hpp index d18bcfa..1fe590e 100644 --- a/include/utils/download.hpp +++ b/include/utils/download.hpp @@ -48,6 +48,12 @@ struct StoreList { std::string Description; }; +struct UUUpdate { + bool Available = false; + std::string Notes = ""; + std::string Version = ""; +}; + Result downloadToFile(const std::string &url, const std::string &path); Result downloadFromRelease(const std::string &url, const std::string &asset, const std::string &path, bool includePrereleases); @@ -75,9 +81,10 @@ void doneMsg(void); bool IsUpdateAvailable(const std::string &URL, int revCurrent); bool DownloadUniStore(const std::string &URL, int currentRev, std::string &fl, bool isDownload = false, bool isUDB = false); bool DownloadSpriteSheet(const std::string &URL, const std::string &file); -bool IsUUUpdateAvailable(); +UUUpdate IsUUUpdateAvailable(); void UpdateAction(); std::vector FetchStores(); C2D_Image FetchScreenshot(const std::string &URL); +std::string GetChangelog(); #endif \ No newline at end of file diff --git a/source/init.cpp b/source/init.cpp index 413f73a..fc1354f 100644 --- a/source/init.cpp +++ b/source/init.cpp @@ -127,6 +127,7 @@ Result Init::Initialize() { APT_SetAppCpuTimeLimit(30); // Needed for QR Scanner to work. getCurrentUsage(); aptSetSleepAllowed(false); + hidSetRepeatParameters(20, 8); /* Create Directories, if missing. */ mkdir("sdmc:/3ds", 0777); @@ -143,9 +144,7 @@ Result Init::Initialize() { osSetSpeedupEnable(true); // Enable speed-up for New 3DS users. /* Check here for updates. */ - if (config->updatecheck()) { - if (IsUUUpdateAvailable()) UpdateAction(); - } + if (config->updatecheck()) UpdateAction(); if (exiting) return -1; // In case the update was successful. @@ -161,7 +160,6 @@ Result Init::MainLoop() { bool fullExit = false; if (Initialize() == -1) fullExit = true; - hidSetRepeatParameters(20, 8); /* Loop as long as the status is not fullExit. */ while (aptMainLoop() && !fullExit) { diff --git a/source/screens/mainScreen.cpp b/source/screens/mainScreen.cpp index e6b502d..c020a67 100644 --- a/source/screens/mainScreen.cpp +++ b/source/screens/mainScreen.cpp @@ -35,6 +35,7 @@ extern int fadeAlpha; extern UniStoreInfo GetInfo(const std::string &file, const std::string &fileName); extern void notConnectedMsg(); +extern void DisplayChangelog(); /* MainScreen Constructor. @@ -55,7 +56,7 @@ MainScreen::MainScreen() { /* check version and file here. */ const UniStoreInfo info = GetInfo((std::string(_STORE_PATH) + config->lastStore()), config->lastStore()); - if (info.Version != 3 || info.Version != _UNISTORE_VERSION) { + if (info.Version != 3 && info.Version != _UNISTORE_VERSION) { config->lastStore("universal-db.unistore"); } @@ -99,6 +100,7 @@ MainScreen::MainScreen() { this->store = std::make_unique(_STORE_PATH + config->lastStore(), config->lastStore()); StoreUtils::ResetAll(this->store, this->meta, this->entries); StoreUtils::SortEntries(false, SortType::LAST_UPDATED, this->entries); + DisplayChangelog(); }; /* @@ -111,6 +113,13 @@ void MainScreen::Draw(void) const { return; } + if (this->storeMode == 6) { + /* Release Notes. */ + StoreUtils::DrawReleaseNotes(this->scrollIndex, this->entries[this->store->GetEntry()], this->store); + GFX::DrawBottom(); + return; + } + Gui::ScreenDraw(Top); Gui::Draw_Rect(0, 0, 400, 25, BAR_COLOR); Gui::Draw_Rect(0, 25, 400, 1, BAR_OUTL_COLOR); @@ -159,6 +168,7 @@ void MainScreen::Draw(void) const { MainScreen Logic. */ void MainScreen::Logic(u32 hDown, u32 hHeld, touchPosition touch) { + /* Screenshots Menu. */ if (this->storeMode == 5) { if (this->screenshotFetch) { /* Delete Texture first. */ @@ -192,6 +202,13 @@ void MainScreen::Logic(u32 hDown, u32 hHeld, touchPosition touch) { return; } + /* Release Notes. */ + if (this->storeMode == 6) { + StoreUtils::ReleaseNotesLogic(this->scrollIndex, this->entries[this->store->GetEntry()], this->storeMode); + return; + } + + /* Mark Menu. */ if (this->showMarks) StoreUtils::MarkHandle(this->entries[this->store->GetEntry()], this->store, this->showMarks, this->meta); if (!this->showMarks) { diff --git a/source/store/entryInfo.cpp b/source/store/entryInfo.cpp index 4669535..e8abd1f 100644 --- a/source/store/entryInfo.cpp +++ b/source/store/entryInfo.cpp @@ -30,6 +30,7 @@ extern bool touching(touchPosition touch, Structs::ButtonPos button); static const Structs::ButtonPos btn = { 53, 215, 24, 24 }; static const Structs::ButtonPos sshot = { 83, 215, 24, 24 }; +static const Structs::ButtonPos notes = { 113, 215, 24, 24 }; extern bool checkWifiStatus(); /* @@ -55,6 +56,7 @@ void StoreUtils::DrawEntryInfo(const std::unique_ptr &store, const std::u GFX::DrawBox(btn.x, btn.y, btn.w, btn.h, false); GFX::DrawSprite(sprites_screenshot_idx, sshot.x, sshot.y); + GFX::DrawSprite(sprites_notes_idx, notes.x, notes.y); Gui::DrawString(btn.x + 5, btn.y + 2, 0.6f, TEXT_COLOR, "★", 0, 0, font); } } @@ -80,4 +82,6 @@ void StoreUtils::EntryHandle(bool &showMark, bool &fetch, bool &sFetch, int &mod mode = 5; } } + + if ((hDown & KEY_X) || (hDown & KEY_TOUCH && touching(touch, notes))) mode = 6; } \ No newline at end of file diff --git a/source/store/releaseNotes.cpp b/source/store/releaseNotes.cpp new file mode 100644 index 0000000..3644be5 --- /dev/null +++ b/source/store/releaseNotes.cpp @@ -0,0 +1,137 @@ +/* +* This file is part of Universal-Updater +* Copyright (C) 2019-2020 Universal-Team +* +* 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 "download.hpp" +#include "storeUtils.hpp" + +void StoreUtils::DrawReleaseNotes(const int &scrollIndex, const std::unique_ptr &entry, const std::unique_ptr &store) { + if (entry && store) { + Gui::ScreenDraw(Top); + Gui::Draw_Rect(0, 26, 400, 214, BG_COLOR); + Gui::DrawString(5, 25 - scrollIndex, 0.5f, TEXT_COLOR, entry->GetReleaseNotes(), 390, 0, font, C2D_WordWrap); + Gui::Draw_Rect(0, 0, 400, 25, BAR_COLOR); + Gui::Draw_Rect(0, 25, 400, 1, BAR_OUTL_COLOR); + Gui::DrawStringCentered(0, 1, 0.7f, TEXT_COLOR, entry->GetTitle(), 390, 0, font); + + } else { + Gui::ScreenDraw(Top); + Gui::Draw_Rect(0, 0, 400, 25, BAR_COLOR); + Gui::Draw_Rect(0, 25, 400, 1, BAR_OUTL_COLOR); + Gui::Draw_Rect(0, 26, 400, 214, BG_COLOR); + } +} + +/* + As the name says: Release notes logic. + + int &scrollIndex: The scroll index for the Release Notes text. + const std::unique_ptr &entry: The Store Entry. + int &storeMode: The store mode to properly return back. +*/ +void StoreUtils::ReleaseNotesLogic(int &scrollIndex, const std::unique_ptr &entry, int &storeMode) { + if (entry) { + if (hRepeat & KEY_DOWN) { + if (entry->GetReleaseNotes() != "") { // Only scroll if not "". + const int height = Gui::GetStringHeight(0.5f, "", font); + scrollIndex += height; + } + } + + if (hRepeat & KEY_UP) { + if (entry->GetReleaseNotes() != "") { // Only scroll if not "". + const int height = Gui::GetStringHeight(0.5f, "", font); + if (scrollIndex > 0) scrollIndex -= height; + } + } + + if (hDown & KEY_B) { + scrollIndex = 0; + storeMode = 0; + } + } +} + + +/* + I place it temporarely here for now. + + Display Release changelog for Universal-Updater. +*/ +void DisplayChangelog() { + if (config->changelog()) { + config->changelog(false); + + bool confirmed = false; + const std::string notes = GetChangelog(); + if (notes == "") return; + int scrollIndex = 0; + + while(!confirmed) { + Gui::clearTextBufs(); + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + C2D_TargetClear(Top, C2D_Color32(0, 0, 0, 0)); + C2D_TargetClear(Bottom, C2D_Color32(0, 0, 0, 0)); + + Gui::ScreenDraw(Top); + Gui::Draw_Rect(0, 26, 400, 214, BG_COLOR); + Gui::DrawString(5, 25 - scrollIndex, 0.5f, TEXT_COLOR, notes, 390, 0, font, C2D_WordWrap); + Gui::Draw_Rect(0, 0, 400, 25, BAR_COLOR); + Gui::Draw_Rect(0, 25, 400, 1, BAR_OUTL_COLOR); + Gui::DrawStringCentered(0, 1, 0.7f, TEXT_COLOR, "Universal-Updater", 390, 0, font); + Gui::Draw_Rect(0, 215, 400, 25, BAR_COLOR); + Gui::Draw_Rect(0, 214, 400, 1, BAR_OUTL_COLOR); + Gui::DrawStringCentered(0, 217, 0.7f, TEXT_COLOR, C_V, 390, 0, font); + + GFX::DrawBottom(); + Gui::Draw_Rect(0, 0, 320, 25, BAR_COLOR); + Gui::Draw_Rect(0, 25, 320, 1, BAR_OUTL_COLOR); + C3D_FrameEnd(0); + + hidScanInput(); + touchPosition t; + touchRead(&t); + u32 repeat = hidKeysDownRepeat(); + u32 down = hidKeysDown(); + + /* Scroll Logic. */ + if (repeat & KEY_DOWN) { + if (notes != "") { // Only scroll if not "". + const int height = Gui::GetStringHeight(0.5f, "", font); + scrollIndex += height; + } + } + + if (repeat & KEY_UP) { + if (notes != "") { // Only scroll if not "". + const int height = Gui::GetStringHeight(0.5f, "", font); + if (scrollIndex > 0) scrollIndex -= height; + } + } + + if ((down & KEY_A) || (down & KEY_B) || (down & KEY_START) || (down & KEY_TOUCH)) confirmed = true; + } + } +} \ No newline at end of file diff --git a/source/store/store.cpp b/source/store/store.cpp index 4f39522..6319c49 100644 --- a/source/store/store.cpp +++ b/source/store/store.cpp @@ -543,4 +543,20 @@ std::vector Store::GetScreenshotNames(int index) const { } return screenshotNames; +} + +/* + Get the update notes of an entry. + + int index: The Entry Index. +*/ +std::string Store::GetReleaseNotes(int index) const { + if (!this->valid) return ""; + if (index > (int)this->storeJson["storeContent"].size() - 1) return ""; // Empty. + + if (this->storeJson["storeContent"][index]["info"].contains("releasenotes") && this->storeJson["storeContent"][index]["info"]["releasenotes"].is_string()) { + return this->storeJson["storeContent"][index]["info"]["releasenotes"]; + } + + return ""; } \ No newline at end of file diff --git a/source/store/storeEntry.cpp b/source/store/storeEntry.cpp index 5fd67d0..2a94139 100644 --- a/source/store/storeEntry.cpp +++ b/source/store/storeEntry.cpp @@ -66,4 +66,5 @@ StoreEntry::StoreEntry(const std::unique_ptr &store, const std::unique_pt this->Screenshots = store->GetScreenshotList(index); this->ScreenshotNames = store->GetScreenshotNames(index); + this->ReleaseNotes = store->GetReleaseNotes(index); } \ No newline at end of file diff --git a/source/utils/config.cpp b/source/utils/config.cpp index d005cc3..9e875b7 100644 --- a/source/utils/config.cpp +++ b/source/utils/config.cpp @@ -127,6 +127,7 @@ Config::Config() { if (this->json.contains("UseBG")) this->usebg(this->getBool("UseBG")); if (this->json.contains("CustomFont")) this->customfont(this->getBool("CustomFont")); if (this->json.contains("Shortcut_Path")) this->shortcut(this->getString("Shortcut_Path")); + if (this->json.contains("Display_Changelog")) this->changelog(this->getBool("Display_Changelog")); this->changesMade = false; // No changes made yet. } @@ -150,6 +151,7 @@ void Config::save() { this->setBool("UseBG", this->usebg()); this->setBool("CustomFont", this->customfont()); this->setString("Shortcut_Path", this->shortcut()); + this->setBool("Display_Changelog", this->changelog()); /* Write changes to file. */ const std::string dump = this->json.dump(1, '\t'); diff --git a/source/utils/download.cpp b/source/utils/download.cpp index 4bbc1da..8a44e98 100644 --- a/source/utils/download.cpp +++ b/source/utils/download.cpp @@ -752,20 +752,20 @@ bool DownloadSpriteSheet(const std::string &URL, const std::string &file) { /* Checks for U-U updates. */ -bool IsUUUpdateAvailable() { - if (!checkWifiStatus()) return false; +UUUpdate IsUUUpdateAvailable() { + if (!checkWifiStatus()) return { false, "", "" }; Msg::DisplayMsg(Lang::get("CHECK_UU_UPDATES")); Result ret = 0; void *socubuf = memalign(0x1000, 0x100000); - if (!socubuf) return false; + if (!socubuf) return { false, "", "" }; ret = socInit((u32 *)socubuf, 0x100000); if (R_FAILED(ret)) { free(socubuf); - return false; + return { false, "", "" }; } CURL *hnd = curl_easy_init(); @@ -778,7 +778,7 @@ bool IsUUUpdateAvailable() { result_buf = nullptr; result_sz = 0; result_written = 0; - return false; + return { false, "", "" }; } CURLcode cres = curl_easy_perform(hnd); @@ -795,14 +795,15 @@ bool IsUUUpdateAvailable() { result_buf = nullptr; result_sz = 0; result_written = 0; - return false; + return { false, "", "" }; } if (nlohmann::json::accept(result_buf)) { nlohmann::json parsedAPI = nlohmann::json::parse(result_buf); if (parsedAPI.contains("tag_name") && parsedAPI["tag_name"].is_string()) { - const std::string tag = parsedAPI["tag_name"]; + UUUpdate update = { false, "", "" }; + update.Version = parsedAPI["tag_name"]; socExit(); free(result_buf); @@ -811,7 +812,10 @@ bool IsUUUpdateAvailable() { result_sz = 0; result_written = 0; - return strcasecmp(StringUtils::lower_case(tag).c_str(), StringUtils::lower_case(C_V).c_str()) > 0; + if (parsedAPI["body"].is_string()) update.Notes = parsedAPI["body"]; + update.Notes.erase(remove(update.Notes.begin(), update.Notes.end(), '\r'), update.Notes.end()); // Remove the CRLF \r's. + update.Available = strcasecmp(StringUtils::lower_case(update.Version).c_str(), StringUtils::lower_case(C_V).c_str()) > 0; + return update; } } @@ -822,7 +826,7 @@ bool IsUUUpdateAvailable() { result_sz = 0; result_written = 0; - return false; + return { false, "", "" }; } extern bool is3DSX, exiting; @@ -832,20 +836,72 @@ extern std::string _3dsxPath; Execute U-U update action. */ void UpdateAction() { - if (ScriptUtils::downloadRelease("Universal-Team/Universal-Updater", (is3DSX ? "Universal-Updater.3dsx" : "Universal-Updater.cia"), - (is3DSX ? _3dsxPath : "sdmc:/Universal-Updater.cia"), - false, Lang::get("DONLOADING_UNIVERSAL_UPDATER")) == 0) { + UUUpdate res = IsUUUpdateAvailable(); + if (res.Available) { + bool confirmed = false; + int scrollIndex = 0; - if (is3DSX) { - Msg::waitMsg(Lang::get("UPDATE_DONE")); - exiting = true; - return; + while(!confirmed) { + Gui::clearTextBufs(); + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + C2D_TargetClear(Top, C2D_Color32(0, 0, 0, 0)); + C2D_TargetClear(Bottom, C2D_Color32(0, 0, 0, 0)); + + Gui::ScreenDraw(Top); + Gui::Draw_Rect(0, 26, 400, 214, BG_COLOR); + Gui::DrawString(5, 25 - scrollIndex, 0.5f, TEXT_COLOR, res.Notes, 390, 0, font, C2D_WordWrap); + Gui::Draw_Rect(0, 0, 400, 25, BAR_COLOR); + Gui::Draw_Rect(0, 25, 400, 1, BAR_OUTL_COLOR); + Gui::DrawStringCentered(0, 1, 0.7f, TEXT_COLOR, "Universal-Updater", 390, 0, font); + Gui::Draw_Rect(0, 215, 400, 25, BAR_COLOR); + Gui::Draw_Rect(0, 214, 400, 1, BAR_OUTL_COLOR); + Gui::DrawStringCentered(0, 217, 0.7f, TEXT_COLOR, res.Version, 390, 0, font); + + GFX::DrawBottom(); + Gui::Draw_Rect(0, 0, 320, 25, BAR_COLOR); + Gui::Draw_Rect(0, 25, 320, 1, BAR_OUTL_COLOR); + Gui::DrawStringCentered(0, 1, 0.7f, TEXT_COLOR, Lang::get("UPDATE_AVAILABLE"), 310, 0, font); + C3D_FrameEnd(0); + + hidScanInput(); + touchPosition t; + touchRead(&t); + u32 repeat = hidKeysDownRepeat(); + u32 down = hidKeysDown(); + + /* Scroll Logic. */ + if (repeat & KEY_DOWN) { + if (res.Notes != "") { // Only scroll if not "". + const int height = Gui::GetStringHeight(0.5f, "", font); + scrollIndex += height; + } + } + + if (repeat & KEY_UP) { + if (res.Notes != "") { // Only scroll if not "". + const int height = Gui::GetStringHeight(0.5f, "", font); + if (scrollIndex > 0) scrollIndex -= height; + } + } + + if ((down & KEY_A) || (down & KEY_B) || (down & KEY_START) || (down & KEY_TOUCH)) confirmed = true; } - ScriptUtils::installFile("sdmc:/Universal-Updater.cia", false, Lang::get("INSTALL_UNIVERSAL_UPDATER")); - ScriptUtils::removeFile("sdmc:/Universal-Updater.cia", Lang::get("DELETE_UNNEEDED_FILE")); - Msg::waitMsg(Lang::get("UPDATE_DONE")); - exiting = true; + if (ScriptUtils::downloadRelease("Universal-Team/Universal-Updater", (is3DSX ? "Universal-Updater.3dsx" : "Universal-Updater.cia"), + (is3DSX ? _3dsxPath : "sdmc:/Universal-Updater.cia"), + false, Lang::get("DONLOADING_UNIVERSAL_UPDATER")) == 0) { + + if (is3DSX) { + Msg::waitMsg(Lang::get("UPDATE_DONE")); + exiting = true; + return; + } + + ScriptUtils::installFile("sdmc:/Universal-Updater.cia", false, Lang::get("INSTALL_UNIVERSAL_UPDATER")); + ScriptUtils::removeFile("sdmc:/Universal-Updater.cia", Lang::get("DELETE_UNNEEDED_FILE")); + Msg::waitMsg(Lang::get("UPDATE_DONE")); + exiting = true; + } } } @@ -987,4 +1043,79 @@ C2D_Image FetchScreenshot(const std::string &URL) { result_written = 0; return img; +} + +/* + Return the release changelog. +*/ +std::string GetChangelog() { + if (!checkWifiStatus()) return ""; + + Result ret = 0; + + void *socubuf = memalign(0x1000, 0x100000); + if (!socubuf) return ""; + + ret = socInit((u32 *)socubuf, 0x100000); + + if (R_FAILED(ret)) { + free(socubuf); + return ""; + } + + CURL *hnd = curl_easy_init(); + + ret = setupContext(hnd, "https://api.github.com/repos/Universal-Team/Universal-Updater/releases/latest"); + if (ret != 0) { + socExit(); + free(result_buf); + free(socubuf); + result_buf = nullptr; + 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 = nullptr; + result_sz = 0; + result_written = 0; + return ""; + } + + if (nlohmann::json::accept(result_buf)) { + nlohmann::json parsedAPI = nlohmann::json::parse(result_buf); + + if (parsedAPI.contains("body") && parsedAPI["body"].is_string()) { + socExit(); + free(result_buf); + free(socubuf); + result_buf = nullptr; + result_sz = 0; + result_written = 0; + + std::string notes = parsedAPI["body"]; + notes.erase(remove(notes.begin(), notes.end(), '\r'), notes.end()); // Remove the CRLF \r's. + return notes; + } + } + + socExit(); + free(result_buf); + free(socubuf); + result_buf = nullptr; + result_sz = 0; + result_written = 0; + + return ""; } \ No newline at end of file