From 9646df9c06165005880a69c6b3edfb7fbf07b0a2 Mon Sep 17 00:00:00 2001 From: Ghost0159 Date: Sat, 2 Jul 2022 12:01:34 +0200 Subject: [PATCH] v13.5 --- Makefile | 6 +- README.md | 10 +- include/screens/mainScreen.hpp | 4 +- include/store/store.hpp | 3 +- include/store/storeEntry.hpp | 3 +- include/store/storeUtils.hpp | 5 +- include/utils/config.hpp | 8 +- include/utils/download.hpp | 2 +- include/utils/scriptUtils.hpp | 6 +- ressources/Themes/default/Themes.json | 4 +- romfs/lang/en/app.json | 18 ++- source/init.cpp | 10 +- source/main.cpp | 5 + source/menu/downList.cpp | 20 ++- source/menu/entryInfo.cpp | 13 +- source/menu/markMenu.cpp | 2 +- source/menu/queueMenu.cpp | 5 + source/menu/releaseNotes.cpp | 67 ++++++++- source/menu/searchMenu.cpp | 5 + source/menu/settings.cpp | 4 + source/menu/sortMenu.cpp | 5 + source/qr/qrcode.cpp | 6 +- source/screens/mainScreen.cpp | 24 ++- source/store/store.cpp | 20 +++ source/store/storeEntry.cpp | 1 + source/utils/argumentParser.cpp | 2 +- source/utils/config.cpp | 30 ++-- source/utils/download.cpp | 209 +++++++++++++------------- source/utils/fileBrowse.cpp | 2 +- source/utils/queueSystem.cpp | 7 +- source/utils/scriptUtils.cpp | 74 ++++----- 31 files changed, 358 insertions(+), 222 deletions(-) diff --git a/Makefile b/Makefile index e3e0f67..f71af54 100644 --- a/Makefile +++ b/Makefile @@ -61,7 +61,7 @@ endif # Print new version if changed ifeq (,$(findstring $(GIT_VER), $(shell cat include/version.hpp))) -$(shell printf "#ifndef VERSION_HPP\n#define VERSION_HPP\n\n#define VER_NUMBER \"$(GIT_VER)\"\n\n#endif\n" > include/version.hpp) +$(shell printf "#ifndef VERSION_HPP\n#define VERSION_HPP\n\n#define VER_NUMBER \"$(GIT_VER)\"\n#define GIT_SHA \"$(GIT_SHA)\"\n\n#endif\n" > include/version.hpp) endif #--------------------------------------------------------------------------------- @@ -114,7 +114,7 @@ CFLAGS := -g -Wall -Wno-psabi -O2 -mword-relocations \ -fomit-frame-pointer -ffunction-sections \ $(ARCH) -CFLAGS += $(INCLUDE) -DARM11 -D_3DS -D_GNU_SOURCE=1 +CFLAGS += $(INCLUDE) -D__3DS__ -D_GNU_SOURCE=1 CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17 $(CITRA) @@ -329,4 +329,4 @@ endif cppcheck: @rm -f cppcheck.log @cppcheck . --enable=all $(INCLUDE) -UJSON_CATCH_USER -U_Check_return_ -U_MSC_VER -U_Ret_notnull_ -U__INTEL_COMPILER -U__PGI -U__SUNPRO_CC -UJSON_INTERNAL_CATCH_USER -UJSON_THROW_USER -UJSON_TRY_USER -U__IBMCPP__ -U__SUNPRO_CC -D__GNUC__=9 -D__GNUC_MINOR__=1 -DNULL=nullptr --force 2> cppcheck.log - @echo cppcheck.log file created... \ No newline at end of file + @echo cppcheck.log file created... diff --git a/README.md b/README.md index 52898e3..5e32858 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@
# Ghost-Eshop-Alternative-3DS -A free alternative eShop for the Nintendo 3DS family of systems. +An Alternative eShop for the Nintendo 3DS family of systems. # Download ### Requirements -- Latest versions are made available [here](https://ghosteshop.com/) +- Latest version available [here](https://ghosteshop.com/) -- A Homebrewed Nintendo 3DS(XL)/2DS(XL) +- A Modded Nintendo 3DS(XL)/2DS(XL) -- An SD Card with at least 500 MB to install games (Size may vary depending on the game you choose!) +- An SD Card with at least 500 MB to install games
ScreenShot of Ghost eShop
@@ -26,7 +26,7 @@ ______________________ ##### Setting up your environment: -*To build Ghost-eShop-Homebrew from source, you will need to setup a system with devkitARM, libctru and 3ds-libarchive. Follow devkitPro's [Getting Started](https://devkitpro.org/wiki/Getting_Started) page to install pacman, then run `(sudo dkp-)pacman -S devkitARM libctru 3ds-curl 3ds-libarchive`* +*To build Ghost-Eshop-Alternative-3ds from source, you will need to setup a system with devkitARM, libctru and 3ds-libarchive. Follow devkitPro's [Getting Started](https://devkitpro.org/wiki/Getting_Started) page to install pacman, then run `(sudo dkp-)pacman -S devkitARM libctru 3ds-curl 3ds-libarchive`* Install [Git](https://git-scm.com/downloads) if you haven't already
diff --git a/include/screens/mainScreen.hpp b/include/screens/mainScreen.hpp index 1c7aa99..afe882f 100644 --- a/include/screens/mainScreen.hpp +++ b/include/screens/mainScreen.hpp @@ -51,7 +51,7 @@ class MainScreen : public Screen { void Draw(void) const override; void Logic(u32 hDown, u32 hHeld, touchPosition touch) override; private: - std::vector dwnldList, dwnldSizes; + std::vector dwnldList, dwnldSizes, dwnldTypes; bool initialized = false, fetchDown = false, showMarks = false, showSettings = false, ascending = false, updateFilter = false, screenshotFetch = false, canDisplay = false, isAND = true; @@ -62,7 +62,7 @@ class MainScreen : public Screen { SortType sorttype = SortType::LAST_UPDATED; /* Title, Author, Category, Console. */ - std::vector searchIncludes = { false, false, false, false }, installs = { }; + std::vector searchIncludes = { true, false, false, false }, installs = { }; std::string searchResult = "", screenshotName = ""; C2D_Image Screenshot = { nullptr, nullptr }; diff --git a/include/store/store.hpp b/include/store/store.hpp index f4606b4..9d18757 100644 --- a/include/store/store.hpp +++ b/include/store/store.hpp @@ -55,6 +55,7 @@ class Store { std::string GetAdditionalcontentEntry(int index) const; C2D_Image GetIconEntry(int index) const; std::string GetFileSizes(int index, const std::string &entry) const; + std::string GetFileTypes(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; @@ -85,7 +86,7 @@ class Store { C2D_Image GetStoreImg() const { return this->storeBG; }; bool customBG() const { return this->hasCustomBG; }; - /* Return filename of the eShop. */ + /* Return filename of the UniStore. */ std::string GetFileName() const { return this->fileName; }; private: void SetC2DBGImage(); diff --git a/include/store/storeEntry.hpp b/include/store/storeEntry.hpp index b799843..901a539 100644 --- a/include/store/storeEntry.hpp +++ b/include/store/storeEntry.hpp @@ -53,6 +53,7 @@ class StoreEntry { std::vector GetCategoryFull() const { return this->FullCategory; }; std::vector GetConsoleFull() const { return this->FullConsole; }; std::vector GetSizes() const { return this->Sizes; }; + std::vector GetTypes() const { return this->Types; }; std::vector GetScreenshots() const { return this->Screenshots; }; std::vector GetScreenshotNames() const { return this->ScreenshotNames; }; std::string GetReleaseNotes() const { return this->ReleaseNotes; }; @@ -69,7 +70,7 @@ class StoreEntry { std::string Title, Author, Description, Category, Version, Console, LastUpdated, MarkString, Additional, ReleaseNotes; C2D_Image Icon; int SheetIndex, EntryIndex, Marks; - std::vector FullCategory, FullConsole, Sizes, Screenshots, ScreenshotNames; + std::vector FullCategory, FullConsole, Sizes, Types, Screenshots, ScreenshotNames; bool UpdateAvailable; }; diff --git a/include/store/storeUtils.hpp b/include/store/storeUtils.hpp index 09a2d98..c39d6dd 100644 --- a/include/store/storeUtils.hpp +++ b/include/store/storeUtils.hpp @@ -61,7 +61,7 @@ namespace StoreUtils { /* Download entries. */ void DrawDownList(const std::vector &entries, bool fetch, const std::unique_ptr &entry, const std::vector &sizes, const std::vector &installs); - void DownloadHandle(const std::unique_ptr &entry, const std::vector &entries, int ¤tMenu, const int &lastMode, int &smallDelay, std::vector &installs); + void DownloadHandle(const std::unique_ptr &entry, const std::vector &entries, int ¤tMenu, const int &lastMode, int &smallDelay, std::vector &installs, const std::vector &types); /* Queue System. */ void DrawQueueMenu(const int queueIndex); @@ -91,6 +91,9 @@ namespace StoreUtils { void SortHandle(bool &asc, SortType &st); /* Release Notes. */ + size_t FindSplitPoint(const std::string &str, const std::vector splitters); + void ProcessReleaseNotes(std::string ReleaseNotes); + void DrawReleaseNotes(const int &scrollIndex, const std::unique_ptr &entry); void ReleaseNotesLogic(int &scrollIndex, int &storeMode); diff --git a/include/utils/config.hpp b/include/utils/config.hpp index e851a4d..d6a5265 100644 --- a/include/utils/config.hpp +++ b/include/utils/config.hpp @@ -78,6 +78,10 @@ class Config { bool updatecheck() const { return this->v_updateCheck; }; void updatecheck(bool v) { this->v_updateCheck = v; if (!this->changesMade) this->changesMade = true; }; + /* Check for nightly/release on startup. */ + bool updatenightly() const { return this->v_updateNightly; }; + void updatenightly(bool v) { this->v_updateNightly = v; if (!this->changesMade) this->changesMade = true; }; + /* Ghost eShop Update check on startup. */ bool usebg() const { return this->v_showBg; }; void usebg(bool v) { this->v_showBg = v; if (!this->changesMade) this->changesMade = true; }; @@ -114,10 +118,10 @@ class Config { bool changesMade = false; std::string v_language = "en", v_lastStore = "ghosteshop.unistore", - v_3dsxPath = "sdmc:/3ds", v_ndsPath = "sdmc:", v_archivePath = "sdmc:", + v_3dsxPath = "sdmc:/3ds", v_ndsPath = "sdmc:/roms/nds", v_archivePath = "sdmc:", v_shortcutPath = "sdmc:/3ds/Universal-Updater/shortcuts", v_firmPath = "sdmc:/luma/payloads", v_theme = "Default"; - bool v_list = false, v_autoUpdate = true, v_metadata = true, v_updateCheck = true, + bool v_list = false, v_autoUpdate = true, v_metadata = true, v_updateCheck = true, v_updateNightly = false, v_showBg = false, v_customFont = false, v_changelog = true, v_prompt = true, v_3dsxInFolder = false; }; diff --git a/include/utils/download.hpp b/include/utils/download.hpp index bce1444..ed62753 100644 --- a/include/utils/download.hpp +++ b/include/utils/download.hpp @@ -30,7 +30,7 @@ #include "common.hpp" #define APP_TITLE "Ghost eShop" -#define VERSION_STRING "13.0.0" +#define VERSION_STRING "13.5" enum DownloadError { DL_ERROR_NONE = 0, diff --git a/include/utils/scriptUtils.hpp b/include/utils/scriptUtils.hpp index 6aba65d..45cab04 100644 --- a/include/utils/scriptUtils.hpp +++ b/include/utils/scriptUtils.hpp @@ -45,11 +45,11 @@ enum ScriptState { namespace ScriptUtils { bool matchPattern(const std::string &pattern, const std::string &tested); - Result removeFile(const std::string &file, const std::string &message, bool isARG = false); - void bootTitle(const std::string &TitleID, bool isNAND, const std::string &message, bool isARG = false); + Result removeFile(const std::string &file, bool isARG = false); + void bootTitle(const std::string &TitleID, bool isNAND, bool isARG = false); Result prompt(const std::string &message); Result copyFile(const std::string &source, const std::string &destination, const std::string &message, bool isARG = false); - Result renameFile(const std::string &oldName, const std::string &newName, const std::string &message, bool isARG = false); + Result renameFile(const std::string &oldName, const std::string &newName, bool isARG = false); Result downloadRelease(const std::string &repo, const std::string &file, const std::string &output, bool includePrereleases, const std::string &message, bool isARG = false); Result downloadFile(const std::string &file, const std::string &output, const std::string &message, bool isARG = false); void installFile(const std::string &file, bool updatingSelf, const std::string &message, bool isARG = false); diff --git a/ressources/Themes/default/Themes.json b/ressources/Themes/default/Themes.json index 18f873b..7addd8b 100644 --- a/ressources/Themes/default/Themes.json +++ b/ressources/Themes/default/Themes.json @@ -6,7 +6,7 @@ "BoxInside": "#C05A00", "BoxSelected": "#FF5200", "BoxUnselected": "#000000", - "Description": "Ghost eShop's default theme.\n\nBy: Ghost eShop Team", + "Description": "Ghost eShop's default Theme.\n\nBy: Ghost eShop Team", "DownListPrev": "#9C6000", "EntryBar": "#DF6500", "EntryOutline": "#BF4500", @@ -21,4 +21,4 @@ "SideBarUnselected": "#DF6500", "TextColor": "#FFFFFF" } -} +} \ No newline at end of file diff --git a/romfs/lang/en/app.json b/romfs/lang/en/app.json index 202cc62..00ab0ff 100644 --- a/romfs/lang/en/app.json +++ b/romfs/lang/en/app.json @@ -1,14 +1,14 @@ { "3D_BANNER": "Eden for the 3D banner", "3DSX_IN_FOLDER": "Put 3DSX files in folder", - "3DSX_IN_FOLDER_DESC": "If they are enabled, 3DSX files will be put in a folder with the same name than the last set folder", + "3DSX_IN_FOLDER_DESC": "If enabled, 3DSX files will be put in a folder with the same name instead of directly in the set folder.", "ACTION_CANCELED": "%s canceled!", "ACTION_FAILED": "%s failed!", "ACTION_REQUIRED": "Action required!", "ACTION_SUCCEEDED": "%s succeeded!", "ACTIVE_THEME": "Active Theme", - "ARGUMENT_INVALID": "Invalid argument.\nPlease check the xml file for proper arguments.", "ASCENDING": "Ascending", + "ARGUMENT_INVALID": "Argument invalid.\nPlease check the xml file for proper arguments.", "AUTHOR": "Author", "AUTO_UPDATE_ESHOP": "Auto-update eShop", "AUTO_UPDATE_ESHOP_DESC": "With this, the last used eShop will be updated automatically when launching Ghost eShop.", @@ -38,13 +38,13 @@ "COPY_ERROR": "Copy Error!", "CREATE_SHORTCUT": "Would you like to create a shortcut?", "CREDITS": "Credits", + "CREDITS_BTN": "Credits...", "CURRENT_VERSION": "Current version: ", "CURRENTLY_EXTRACTING": "Currently extracting:", "CUSTOM_FONT": "Use Custom Font", - "CUSTOM_FONT_DESC": "If enabled, 'sdmc:/3ds/GhosteShop/font.bcfnt' will be used instead of the system font, if found.", + "CUSTOM_FONT_DESC": "If enabled, 'sdmc:/3ds/Universal-Updater/font.bcfnt' will be used instead of the system font, if found.", "DELETE_ERROR": "Delete Error!", "DELETE_PROMPT": "Are you sure you want to delete this Directory?", - "DELETE_UNNEEDED_FILE": "Deleting unneeded file...", "DELETING": "Deleting...", "DESCENDING": "Descending", "DIRECTION": "Direction", @@ -79,9 +79,9 @@ "FEATURE_SIDE_EFFECTS": "This Feature may have side effects while the Queue is running.\nAre you sure you want to continue?", "FETCHING_METADATA": "Fetching old metadata...", "FETCHING_RECOMMENDED_ESHOP": "Fetching recommended eShop...", - "FILE_EXTRACTED": "file extracted.", - "FILE_SLASH": "Seems like a '/' is included in file name, which is not supported.\nPlease change 'sheet' of filename", "FILES": "File: %d / %d", + "FILE_EXTRACTED": "file extracted.", + "FILE_SLASH": "Seems like a '/' is included, which is not supported.\nPlease change 'file' to filename only.", "FILES_EXTRACTED": "files extracted.", "FILTER_TO": "Filter to:", "GITHUB": "Full credits can be found on GitHub", @@ -93,7 +93,7 @@ "INSTALL_GHOST_ESHOP": "Installing Ghost eShop...", "INVALID_ESHOP": "Invalid eShop", "KEY_CONTINUE": "Press any key to continue.", - "LANGUAGE": "Language", + "LANGUAGE": "Language...", "LAST_UPDATED": "Last updated", "LIST": "List", "LOADING_SCREENSHOT": "Loading Screenshot...", @@ -130,6 +130,10 @@ "SETTINGS": "Settings", "SHEET_SLASH": "Seems like a '/' is included, which is not supported.\nPlease change 'sheet' to filename only.", "SHORTCUT_CREATED": "Shortcut created!", + "SHORTCUT_COPYING": "Copying %s...", + "SHORTCUT_DOWNLOADING": "Downloading %s...", + "SHORTCUT_EXTRACTING": "Extracting %s...", + "SHORTCUT_INSTALLING": "Installing %s...", "SIZE": "Size", "SORT_BY": "Sort By", "SORTING": "Sorting", diff --git a/source/init.cpp b/source/init.cpp index b697262..deacca5 100644 --- a/source/init.cpp +++ b/source/init.cpp @@ -132,6 +132,11 @@ Result Init::Initialize() { mkdir("sdmc:/3ds/Universal-Updater/stores", 0777); mkdir("sdmc:/3ds/Universal-Updater/shortcuts", 0777); mkdir("sdmc:/3ds/Universal-Updater/GhosteShop", 0777); + mkdir("sdmc:/roms/", 0777); + mkdir("sdmc:/roms/nds/", 0777); + mkdir("sdmc:/_nds/", 0777); + mkdir("sdmc:/_nds/TWiLightMenu/", 0777); + mkdir("sdmc:/_nds/TWiLightMenu/boxart/", 0777); config = std::make_unique(); UIThemes = std::make_unique(); @@ -196,9 +201,8 @@ Result Init::MainLoop() { C3D_FrameEnd(0); if (!exiting) Gui::ScreenLogic(hDown, hHeld, touch, true, false); - - if (exiting) { - if (hDown & KEY_START) fullExit = true; // Make it optionally faster. + else { + if (hidKeysDown() & KEY_START) fullExit = true; // Make it optionally faster. if (fadeAlpha < 255) { fadeAlpha += 4; diff --git a/source/main.cpp b/source/main.cpp index 0417fb9..07e4ebb 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -48,6 +48,11 @@ static void InitForARG() { mkdir("sdmc:/3ds/Universal-Updater/stores", 0777); mkdir("sdmc:/3ds/Universal-Updater/shortcuts", 0777); mkdir("sdmc:/3ds/Universal-Updater/GhosteShop", 0777); + mkdir("sdmc:/roms/", 0777); + mkdir("sdmc:/roms/nds/", 0777); + mkdir("sdmc:/_nds/", 0777); + mkdir("sdmc:/_nds/TWiLightMenu/", 0777); + mkdir("sdmc:/_nds/TWiLightMenu/boxart/", 0777); config = std::make_unique(); UIThemes = std::make_unique(); diff --git a/source/menu/downList.cpp b/source/menu/downList.cpp index 32d4395..d047182 100644 --- a/source/menu/downList.cpp +++ b/source/menu/downList.cpp @@ -35,7 +35,7 @@ #define DOWNLOAD_ENTRIES 7 extern std::string _3dsxPath; -extern bool is3DSX; +extern bool exiting, is3DSX, QueueRuns; extern bool touching(touchPosition touch, Structs::ButtonPos button); static const std::vector downloadBoxes = { { 46, 32, 241, 22 }, @@ -169,13 +169,13 @@ void StoreUtils::DrawDownList(const std::vector &entries, bool fetc int &smallDelay: Reference to the small delay. This helps to not directly press A. std::vector &installs: Reference to the installed states. */ -void StoreUtils::DownloadHandle(const std::unique_ptr &entry, const std::vector &entries, int ¤tMenu, const int &lastMode, int &smallDelay, std::vector &installs) { +void StoreUtils::DownloadHandle(const std::unique_ptr &entry, const std::vector &entries, int ¤tMenu, const int &lastMode, int &smallDelay, std::vector &installs, const std::vector &types) { if (StoreUtils::store && entry) { // Ensure, store & entry is not a nullptr. if (smallDelay > 0) { smallDelay--; } - if ((hDown & (KEY_Y | KEY_START) || (hDown & KEY_TOUCH && touching(touch, downloadBoxes[6]))) && !entries.empty()) { + if ((hDown & (KEY_Y | KEY_SELECT) || (hDown & KEY_TOUCH && touching(touch, downloadBoxes[6]))) && !entries.empty()) { if (is3DSX) { // Only allow if 3DSX. if (StoreUtils::entries.size() <= 0) return; // Smaller than 0 -> No No. @@ -212,7 +212,10 @@ void StoreUtils::DownloadHandle(const std::unique_ptr &entry, const for (int i = 0; i < DOWNLOAD_ENTRIES; i++) { if (touching(touch, downloadBoxes[i])) { if (i + StoreUtils::store->GetDownloadSIndex() < (int)entries.size()) { - if (Msg::promptMsg(Lang::get("EXECUTE_ENTRY") + "\n\n" + entries[i + StoreUtils::store->GetDownloadSIndex()])) { + std::string Msg = Lang::get("EXECUTE_ENTRY") + "\n\n" + entries[i + StoreUtils::store->GetDownloadSIndex()]; + if (types[i + StoreUtils::store->GetDownloadSIndex()] == "nightly") Msg += "\n\n" + Lang::get("NOTE_NIGHTLY"); + else if (types[i + StoreUtils::store->GetDownloadSIndex()] == "prerelease") Msg += "\n\n" + Lang::get("NOTE_PRERELEASE"); + if (Msg::promptMsg(Msg)) { StoreUtils::AddToQueue(entry->GetEntryIndex(), entries[i + StoreUtils::store->GetDownloadSIndex()], entry->GetTitle(), entry->GetLastUpdated()); } } @@ -234,7 +237,10 @@ void StoreUtils::DownloadHandle(const std::unique_ptr &entry, const } if (smallDelay == 0 && hDown & KEY_A && !entries.empty()) { - if (Msg::promptMsg(Lang::get("EXECUTE_ENTRY") + "\n\n" + entries[StoreUtils::store->GetDownloadIndex()])) { + std::string Msg = Lang::get("EXECUTE_ENTRY") + "\n\n" + entries[StoreUtils::store->GetDownloadIndex()]; + if (types[StoreUtils::store->GetDownloadIndex()] == "nightly") Msg += "\n\n" + Lang::get("NOTE_NIGHTLY"); + else if (types[StoreUtils::store->GetDownloadIndex()] == "prerelease") Msg += "\n\n" + Lang::get("NOTE_PRERELEASE"); + if (Msg::promptMsg(Msg)) { StoreUtils::AddToQueue(entry->GetEntryIndex(), entries[StoreUtils::store->GetDownloadIndex()], entry->GetTitle(), entry->GetLastUpdated()); } } @@ -248,6 +254,10 @@ void StoreUtils::DownloadHandle(const std::unique_ptr &entry, const if (hDown & KEY_B) currentMenu = lastMode; // Go back to EntryInfo. + /* Quit UU. */ + if (hDown & KEY_START && !QueueRuns) + exiting = true; + /* Scroll Handle. */ if (StoreUtils::store->GetDownloadIndex() < StoreUtils::store->GetDownloadSIndex()) StoreUtils::store->SetDownloadSIndex(StoreUtils::store->GetDownloadIndex()); else if (StoreUtils::store->GetDownloadIndex() > StoreUtils::store->GetDownloadSIndex() + DOWNLOAD_ENTRIES - 1) StoreUtils::store->SetDownloadSIndex(StoreUtils::store->GetDownloadIndex() - DOWNLOAD_ENTRIES + 1); diff --git a/source/menu/entryInfo.cpp b/source/menu/entryInfo.cpp index a020808..0b8bd87 100644 --- a/source/menu/entryInfo.cpp +++ b/source/menu/entryInfo.cpp @@ -34,7 +34,7 @@ static const Structs::ButtonPos btn = { 45, 215, 24, 24 }; static const Structs::ButtonPos sshot = { 75, 215, 24, 24 }; static const Structs::ButtonPos notes = { 105, 215, 24, 24 }; extern bool checkWifiStatus(); -extern bool QueueRuns; +extern bool exiting, QueueRuns; /* Draw the Entry Info part. @@ -77,7 +77,7 @@ void StoreUtils::DrawEntryInfo(const std::unique_ptr &entry) { */ void StoreUtils::EntryHandle(bool &showMark, bool &fetch, bool &sFetch, int &mode, const std::unique_ptr &entry) { if (entry) { - if ((hDown & KEY_START) || (hDown & KEY_TOUCH && touching(touch, btn))) showMark = true; + if ((hDown & KEY_SELECT) || (hDown & KEY_TOUCH && touching(touch, btn))) showMark = true; if ((hDown & KEY_Y) || (hDown & KEY_TOUCH && touching(touch, sshot))) { if (!entry->GetScreenshots().empty()) { @@ -96,7 +96,14 @@ void StoreUtils::EntryHandle(bool &showMark, bool &fetch, bool &sFetch, int &mod } if ((hDown & KEY_X) || (hDown & KEY_TOUCH && touching(touch, notes))) { - if (entry->GetReleaseNotes() != "") mode = 7; + if (entry->GetReleaseNotes() != "") { + ProcessReleaseNotes(entry->GetReleaseNotes()); + mode = 7; + } } } + + /* Quit UU. */ + if (hDown & KEY_START && !QueueRuns) + exiting = true; } \ No newline at end of file diff --git a/source/menu/markMenu.cpp b/source/menu/markMenu.cpp index 2245b94..d9b0d36 100644 --- a/source/menu/markMenu.cpp +++ b/source/menu/markMenu.cpp @@ -124,5 +124,5 @@ void StoreUtils::MarkHandle(std::unique_ptr &entry, bool &showMark) } - if ((hidKeysDown() & KEY_B || hidKeysDown() & KEY_START) || (hidKeysDown() & KEY_TOUCH && touching(t, markBox[5]))) showMark = false; // Return back to screen. + if ((hidKeysDown() & KEY_B || hidKeysDown() & KEY_SELECT) || (hidKeysDown() & KEY_TOUCH && touching(t, markBox[5]))) showMark = false; // Return back to screen. } \ No newline at end of file diff --git a/source/menu/queueMenu.cpp b/source/menu/queueMenu.cpp index d46583a..18f1cd8 100644 --- a/source/menu/queueMenu.cpp +++ b/source/menu/queueMenu.cpp @@ -36,6 +36,7 @@ extern u32 extractSize, writeOffset; extern u32 installSize, installOffset; extern u32 copyOffset, copySize; extern int filesExtracted, extractFilesCount; +extern bool exiting, QueueRuns; extern curl_off_t downloadTotal; extern curl_off_t downloadNow; @@ -331,4 +332,8 @@ void StoreUtils::QueueMenuHandle(int &queueIndex, int &storeMode) { } if (hDown & KEY_B) storeMode = 0; // Go to EntryInfo. + + /* Quit GE. */ + if (hDown & KEY_START && !QueueRuns) + exiting = true; } \ No newline at end of file diff --git a/source/menu/releaseNotes.cpp b/source/menu/releaseNotes.cpp index 1157692..18d269c 100644 --- a/source/menu/releaseNotes.cpp +++ b/source/menu/releaseNotes.cpp @@ -29,11 +29,62 @@ #include "download.hpp" #include "storeUtils.hpp" +std::vector wrappedNotes; + +size_t StoreUtils::FindSplitPoint(const std::string &str, const std::vector splitters) { + for (const std::string &splitter : splitters) { + size_t pos = str.rfind(splitter); + if (pos != std::string::npos) return pos; + } + + return std::string::npos; +} + +/* Process release notes into lines */ +void StoreUtils::ProcessReleaseNotes(std::string releaseNotes) { + wrappedNotes.clear(); + + size_t splitPos = 0; + do { + splitPos = releaseNotes.find('\n'); + std::string substr = releaseNotes.substr(0, splitPos); + + Gui::clearTextBufs(); + float width = Gui::GetStringWidth(0.5f, substr, font); + + /* If too long to fit on screen, wrap at spaces, slashes, periods, etc. */ + size_t spacePos; + while (width > 390.0f && (spacePos = FindSplitPoint(substr.substr(0, splitPos - 1), {" ", "/", ".", "-", "_", "。", "、", ","})) != std::string::npos) { + splitPos = spacePos; + if (substr[splitPos] != ' ') splitPos++; + + /* Skip to next if UTF-8 multibyte char */ + while ((substr[splitPos] & 0xC0) == 0x80) splitPos++; + + substr = substr.substr(0, splitPos); + Gui::clearTextBufs(); + width = Gui::GetStringWidth(0.5f, substr, font); + } + + wrappedNotes.push_back(substr); + + if (splitPos != std::string::npos) { + if (releaseNotes[splitPos] == ' ' || releaseNotes[splitPos] == '\n') splitPos++; + releaseNotes = releaseNotes.substr(splitPos); + } + } while (splitPos != std::string::npos); +} + void StoreUtils::DrawReleaseNotes(const int &scrollIndex, const std::unique_ptr &entry) { if (entry && StoreUtils::store) { Gui::ScreenDraw(Top); Gui::Draw_Rect(0, 26, 400, 214, UIThemes->BGColor()); - Gui::DrawString(5, 25 - scrollIndex, 0.5f, UIThemes->TextColor(), entry->GetReleaseNotes(), 390, 0, font, C2D_WordWrap); + + float fontHeight = Gui::GetStringHeight(0.5f, "", font); + for (size_t i = 0; (scrollIndex + i) < wrappedNotes.size() && i < (240.0f - 25.0f) / fontHeight; i++) { + Gui::DrawString(5, 25 + i * fontHeight, 0.5f, UIThemes->TextColor(), wrappedNotes[scrollIndex + i], 390, 0, font); + } + Gui::Draw_Rect(0, 0, 400, 25, UIThemes->BarColor()); Gui::Draw_Rect(0, 25, 400, 1, UIThemes->BarOutline()); Gui::DrawStringCentered(0, 1, 0.7f, UIThemes->TextColor(), entry->GetTitle(), 390, 0, font); @@ -55,11 +106,17 @@ void StoreUtils::DrawReleaseNotes(const int &scrollIndex, const std::unique_ptr< int &storeMode: The store mode to properly return back. */ void StoreUtils::ReleaseNotesLogic(int &scrollIndex, int &storeMode) { - if (hRepeat & KEY_DOWN) scrollIndex += Gui::GetStringHeight(0.5f, "", font); + int linesPerScreen = ((240.0f - 25.0f) / Gui::GetStringHeight(0.5f, "", font)); - if (hRepeat & KEY_UP) { - if (scrollIndex > 0) scrollIndex -= Gui::GetStringHeight(0.5f, "", font); - } + if (hRepeat & KEY_DOWN) scrollIndex++; + if (hRepeat & KEY_UP) scrollIndex--; + if (hRepeat & KEY_RIGHT) scrollIndex += linesPerScreen; + if (hRepeat & KEY_LEFT) scrollIndex -= linesPerScreen; + + /* Ensure it doesn't scroll off screen. */ + if (scrollIndex < 0) scrollIndex = 0; + if (scrollIndex > (int)wrappedNotes.size() - linesPerScreen) + scrollIndex = std::max(0, (int)wrappedNotes.size() - linesPerScreen); if (hDown & KEY_B) { scrollIndex = 0; diff --git a/source/menu/searchMenu.cpp b/source/menu/searchMenu.cpp index 21ec448..cc6a5b4 100644 --- a/source/menu/searchMenu.cpp +++ b/source/menu/searchMenu.cpp @@ -29,6 +29,7 @@ #include "storeUtils.hpp" #include "structs.hpp" +extern bool exiting, QueueRuns; extern bool touching(touchPosition touch, Structs::ButtonPos button); static const std::vector SearchMenu = { { 51, 41, 262, 30 }, // Search bar. @@ -232,4 +233,8 @@ void StoreUtils::SearchHandle(std::vector &searchIncludes, std::string &se StoreUtils::SortEntries(ascending, sorttype); } } + + /* Quit UU. */ + if (hDown & KEY_START && !QueueRuns) + exiting = true; } \ No newline at end of file diff --git a/source/menu/settings.cpp b/source/menu/settings.cpp index cbd9003..884348c 100644 --- a/source/menu/settings.cpp +++ b/source/menu/settings.cpp @@ -317,6 +317,10 @@ static void SettingsHandleMain(int &page, bool &dspSettings, int &storeMode, int break; } } + + /* Quit UU. */ + if (hDown & KEY_START && !QueueRuns) + exiting = true; } /* diff --git a/source/menu/sortMenu.cpp b/source/menu/sortMenu.cpp index 9eba71e..4f2c1e8 100644 --- a/source/menu/sortMenu.cpp +++ b/source/menu/sortMenu.cpp @@ -29,6 +29,7 @@ #include "storeUtils.hpp" #include "structs.hpp" +extern bool exiting, QueueRuns; extern bool touching(touchPosition touch, Structs::ButtonPos button); static const std::vector buttons = { @@ -158,4 +159,8 @@ void StoreUtils::SortHandle(bool &asc, SortType &st) { } } } + + /* Quit GE. */ + if (hDown & KEY_START && !QueueRuns) + exiting = true; } \ No newline at end of file diff --git a/source/qr/qrcode.cpp b/source/qr/qrcode.cpp index cc4c678..a091661 100644 --- a/source/qr/qrcode.cpp +++ b/source/qr/qrcode.cpp @@ -402,10 +402,10 @@ void QRCode::handler(std::string &result) { else result = std::string((char *)this->out.data(), this->out.size()); } } - - if (this->selectedStore < this->sPos) this->sPos = this->selectedStore; - else if (this->selectedStore > this->sPos + 6 - 1) this->sPos = this->selectedStore - 6 + 1; } + + if (this->selectedStore < this->sPos) this->sPos = this->selectedStore; + else if (this->selectedStore > this->sPos + 6 - 1) this->sPos = this->selectedStore - 6 + 1; } /* diff --git a/source/screens/mainScreen.cpp b/source/screens/mainScreen.cpp index 8ec5d22..afc5a61 100644 --- a/source/screens/mainScreen.cpp +++ b/source/screens/mainScreen.cpp @@ -44,7 +44,7 @@ extern void DisplayChangelog(); Initialized meta, store and StoreEntry class and: - - Downloads ghosteshop.eshop.. in case nothing exist. + - Downloads ghosteshop.unistore.. in case nothing exist. */ MainScreen::MainScreen() { StoreUtils::meta = std::make_unique(); @@ -82,6 +82,9 @@ MainScreen::MainScreen() { DownloadSpriteSheet("https://cdn.ghosteshop.com/script/icon_3.t3x", "icon_3.t3x"); DownloadSpriteSheet("https://cdn.ghosteshop.com/script/icon_4.t3x", "icon_4.t3x"); DownloadSpriteSheet("https://cdn.ghosteshop.com/script/icon_5.t3x", "icon_5.t3x"); + DownloadEshop("https://cdn.ghosteshop.com/script/ghosteshop-ds.unistore", -1, tmp, true, true); + DownloadSpriteSheet("https://cdn.ghosteshop.com/script/icon-ds.t3x", "icon-ds.t3x"); + DownloadSpriteSheet("https://cdn.ghosteshop.com/script/icon-ds_1.t3x", "icon-ds_1.t3x"); } else { notConnectedMsg(); @@ -94,12 +97,15 @@ MainScreen::MainScreen() { if (checkWifiStatus()) { std::string tmp = ""; // Just a temp. DownloadEshop("https://cdn.ghosteshop.com/script/ghosteshop.unistore", -1, tmp, true, true); - DownloadSpriteSheet("https://cdn.ghosteshop.com/script/icon.t3x", "icon.t3x"); - DownloadSpriteSheet("https://cdn.ghosteshop.com/script/icon_1.t3x", "icon_1.t3x"); - DownloadSpriteSheet("https://cdn.ghosteshop.com/script/icon_2.t3x", "icon_2.t3x"); - DownloadSpriteSheet("https://cdn.ghosteshop.com/script/icon_3.t3x", "icon_3.t3x"); - DownloadSpriteSheet("https://cdn.ghosteshop.com/script/icon_4.t3x", "icon_4.t3x"); - DownloadSpriteSheet("https://cdn.ghosteshop.com/script/icon_5.t3x", "icon_5.t3x"); + DownloadSpriteSheet("https://cdn.ghosteshop.com/script/icon.t3x", "icon.t3x"); + DownloadSpriteSheet("https://cdn.ghosteshop.com/script/icon_1.t3x", "icon_1.t3x"); + DownloadSpriteSheet("https://cdn.ghosteshop.com/script/icon_2.t3x", "icon_2.t3x"); + DownloadSpriteSheet("https://cdn.ghosteshop.com/script/icon_3.t3x", "icon_3.t3x"); + DownloadSpriteSheet("https://cdn.ghosteshop.com/script/icon_4.t3x", "icon_4.t3x"); + DownloadSpriteSheet("https://cdn.ghosteshop.com/script/icon_5.t3x", "icon_5.t3x"); + DownloadEshop("https://cdn.ghosteshop.com/script/ghosteshop-ds.unistore", -1, tmp, true, true); + DownloadSpriteSheet("https://cdn.ghosteshop.com/script/icon-ds.t3x", "icon-ds.t3x"); + DownloadSpriteSheet("https://cdn.ghosteshop.com/script/icon-ds_1.t3x", "icon-ds_1.t3x"); } else { notConnectedMsg(); @@ -245,6 +251,7 @@ void MainScreen::Logic(u32 hDown, u32 hHeld, touchPosition touch) { this->installs.clear(); this->dwnldList.clear(); this->dwnldSizes.clear(); + this->dwnldTypes.clear(); if (StoreUtils::store && StoreUtils::store->GetValid()) { const std::vector installedNames = StoreUtils::meta->GetInstalled(StoreUtils::store->GetEshopTitle(), StoreUtils::entries[StoreUtils::store->GetEntry()]->GetTitle()); @@ -254,6 +261,7 @@ void MainScreen::Logic(u32 hDown, u32 hHeld, touchPosition touch) { if ((int)StoreUtils::entries.size() > StoreUtils::store->GetEntry()) { this->dwnldList = StoreUtils::store->GetDownloadList(StoreUtils::entries[StoreUtils::store->GetEntry()]->GetEntryIndex()); this->dwnldSizes = StoreUtils::entries[StoreUtils::store->GetEntry()]->GetSizes(); + this->dwnldTypes = StoreUtils::entries[StoreUtils::store->GetEntry()]->GetTypes(); for (int i = 0; i < (int)this->dwnldList.size(); i++) { bool good = false; @@ -279,7 +287,7 @@ void MainScreen::Logic(u32 hDown, u32 hHeld, touchPosition touch) { break; case 1: - if (StoreUtils::store && StoreUtils::store->GetValid() && StoreUtils::entries.size() > 0) StoreUtils::DownloadHandle(StoreUtils::entries[StoreUtils::store->GetEntry()], this->dwnldList, storeMode, this->lastMode, this->smallDelay, this->installs); + if (StoreUtils::store && StoreUtils::store->GetValid() && StoreUtils::entries.size() > 0) StoreUtils::DownloadHandle(StoreUtils::entries[StoreUtils::store->GetEntry()], this->dwnldList, storeMode, this->lastMode, this->smallDelay, this->installs, this->dwnldTypes); break; case 2: diff --git a/source/store/store.cpp b/source/store/store.cpp index e230a64..9cc3aa6 100644 --- a/source/store/store.cpp +++ b/source/store/store.cpp @@ -505,6 +505,26 @@ std::string Store::GetFileSizes(int index, const std::string &entry) const { return ""; } +/* + Get file script type for each download entry. + + int index: The index. + const std::string &entry: The entry name. +*/ +std::string Store::GetFileTypes(int index, const std::string &entry) const { + if (!this->valid) return ""; + + if (index > (int)this->storeJson["storeContent"].size() - 1) return ""; + + if (this->storeJson["storeContent"][index].contains(entry) && this->storeJson["storeContent"][index][entry].type() == nlohmann::json::value_t::object) { + if (this->storeJson["storeContent"][index][entry].contains("type") && this->storeJson["storeContent"][index][entry]["type"].is_string()) { + return this->storeJson["storeContent"][index][entry]["type"]; + } + } + + return ""; +} + /* Get Screenshot URL list. diff --git a/source/store/storeEntry.cpp b/source/store/storeEntry.cpp index 319dbd0..05fe79e 100644 --- a/source/store/storeEntry.cpp +++ b/source/store/storeEntry.cpp @@ -61,6 +61,7 @@ StoreEntry::StoreEntry(const std::unique_ptr &store, const std::unique_pt if (!entries.empty()) { for (int i = 0; i < (int)entries.size(); i++) { this->Sizes.push_back( store->GetFileSizes(index, entries[i]) ); + this->Types.push_back( store->GetFileTypes(index, entries[i]) ); } } diff --git a/source/utils/argumentParser.cpp b/source/utils/argumentParser.cpp index 9f44aae..6d8a1d6 100644 --- a/source/utils/argumentParser.cpp +++ b/source/utils/argumentParser.cpp @@ -47,7 +47,7 @@ ArgumentParser::ArgumentParser(const std::string &file, const std::string &entry } /* - Prepare eShop and get valid state. + Prepare UniStore and get valid state. */ void ArgumentParser::Load() { if (access((std::string(_STORE_PATH) + this->file).c_str(), F_OK) != 0) return; diff --git a/source/utils/config.cpp b/source/utils/config.cpp index 4425d30..92afebf 100644 --- a/source/utils/config.cpp +++ b/source/utils/config.cpp @@ -39,51 +39,51 @@ void Config::sysLang() { switch(language) { case 0: - this->language("jp"); // Japonais (日本人), traduit. + this->language("jp"); // Japanese break; case 1: - this->language("en"); // Anglais (English), traduit. + this->language("en"); // English break; case 2: - this->language("fr"); // Français (Français), traduit. + this->language("fr"); // French break; case 3: - this->language("de"); // Allemand (German), traduit. + this->language("de"); // German break; case 4: - this->language("it"); // Italien (Italiano), traduit. + this->language("it"); // Italian break; case 5: - this->language("es"); // Espagnol (Español), traduit. + this->language("es"); // Spanish break; case 6: - this->language("zh-CN"); // Chinois simplifié (简体中文), traduit. + this->language("zh-CN"); // Chinese (Simplified) break; case 7: - this->language("kr"); // Coréen (한국어), traduit. - break; - + this->language("kr"); // Korean + break; + case 8: - this->language("nl"); // Néerlandais (Nederlands), pas totalement traduit. - break; + this->language("nl"); // Nederlands + break; case 9: - this->language("pt"); // Portugais (Português), pas totalement traduit. + this->language("pt"); // Portuguese break; case 10: - this->language("ru"); // Russe (русский), pas totalement traduit. + this->language("ru"); // Russian break; case 11: - this->language("zh-TW"); // chinois traditionnel (繁體中文), traduit. + this->language("zh-TW"); // Chinese (Traditional) break; default: diff --git a/source/utils/download.cpp b/source/utils/download.cpp index f52cca4..dc99cc1 100644 --- a/source/utils/download.cpp +++ b/source/utils/download.cpp @@ -146,7 +146,6 @@ static size_t file_handle_data(char *ptr, size_t size, size_t nmemb, void *userd /* Download a file. - const std::string &url: The download URL. const std::string &path: Where to place the file. */ @@ -154,128 +153,128 @@ Result downloadToFile(const std::string &url, const std::string &path) { Result retcode = 0; bool needToRetry = false; do { - if (!checkWifiStatus()) return -1; // NO WIFI. + if (!checkWifiStatus()) return -1; // NO WIFI. - bool needToDelete = false; - downloadTotal = 1; - downloadNow = 0; - downloadSpeed = 0; + bool needToDelete = false; + downloadTotal = 1; + downloadNow = 0; + downloadSpeed = 0; retcode = 0; - CURLcode curlResult; + CURLcode curlResult; needToRetry = false; - int res; + int res; - printf("Downloading from:\n%s\nto:\n%s\n", url.c_str(), path.c_str()); + printf("Downloading from:\n%s\nto:\n%s\n", url.c_str(), path.c_str()); - void *socubuf = memalign(0x1000, 0x100000); - if (!socubuf) { - retcode = -1; - goto exit; - } + void *socubuf = memalign(0x1000, 0x100000); + if (!socubuf) { + retcode = -1; + goto exit; + } - res = socInit((u32 *)socubuf, 0x100000); - if (R_FAILED(res)) { - retcode = res; - goto exit; - } + res = socInit((u32 *)socubuf, 0x100000); + if (R_FAILED(res)) { + retcode = res; + goto exit; + } - /* make directories. */ - for (char *slashpos = strchr(path.c_str() + 1, '/'); slashpos != NULL; slashpos = strchr(slashpos + 1, '/')) { - char bak = *(slashpos); - *(slashpos) = '\0'; + /* make directories. */ + for (char *slashpos = strchr(path.c_str() + 1, '/'); slashpos != NULL; slashpos = strchr(slashpos + 1, '/')) { + char bak = *(slashpos); + *(slashpos) = '\0'; - mkdir(path.c_str(), 0777); + mkdir(path.c_str(), 0777); - *(slashpos) = bak; - } + *(slashpos) = bak; + } - downfile = fopen(path.c_str(), "wb"); - if (!downfile) { - retcode = -2; - goto exit; - } + downfile = fopen(path.c_str(), "wb"); + if (!downfile) { + retcode = -2; + goto exit; + } - CurlHandle = curl_easy_init(); - curl_easy_setopt(CurlHandle, CURLOPT_BUFFERSIZE, FILE_ALLOC_SIZE); - curl_easy_setopt(CurlHandle, CURLOPT_URL, url.c_str()); - curl_easy_setopt(CurlHandle, CURLOPT_NOPROGRESS, 0L); - curl_easy_setopt(CurlHandle, CURLOPT_USERAGENT, USER_AGENT); - curl_easy_setopt(CurlHandle, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(CurlHandle, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt(CurlHandle, CURLOPT_ACCEPT_ENCODING, "gzip"); - curl_easy_setopt(CurlHandle, CURLOPT_MAXREDIRS, 50L); - curl_easy_setopt(CurlHandle, CURLOPT_XFERINFOFUNCTION, curlProgress); - curl_easy_setopt(CurlHandle, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2TLS); - curl_easy_setopt(CurlHandle, CURLOPT_WRITEFUNCTION, file_handle_data); - curl_easy_setopt(CurlHandle, CURLOPT_SSL_VERIFYPEER, 0L); - curl_easy_setopt(CurlHandle, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(CurlHandle, CURLOPT_STDERR, stdout); - - curlResult = curl_easy_perform(CurlHandle); - curl_easy_cleanup(CurlHandle); - CurlHandle = nullptr; - - if (curlResult != CURLE_OK) { - retcode = -curlResult; - needToDelete = true; + CurlHandle = curl_easy_init(); + curl_easy_setopt(CurlHandle, CURLOPT_BUFFERSIZE, FILE_ALLOC_SIZE); + curl_easy_setopt(CurlHandle, CURLOPT_URL, url.c_str()); + curl_easy_setopt(CurlHandle, CURLOPT_NOPROGRESS, 0L); + curl_easy_setopt(CurlHandle, CURLOPT_USERAGENT, USER_AGENT); + curl_easy_setopt(CurlHandle, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(CurlHandle, CURLOPT_FAILONERROR, 1L); + curl_easy_setopt(CurlHandle, CURLOPT_ACCEPT_ENCODING, "gzip"); + curl_easy_setopt(CurlHandle, CURLOPT_MAXREDIRS, 50L); + curl_easy_setopt(CurlHandle, CURLOPT_XFERINFOFUNCTION, curlProgress); + curl_easy_setopt(CurlHandle, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2TLS); + curl_easy_setopt(CurlHandle, CURLOPT_WRITEFUNCTION, file_handle_data); + curl_easy_setopt(CurlHandle, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(CurlHandle, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(CurlHandle, CURLOPT_STDERR, stdout); + + curlResult = curl_easy_perform(CurlHandle); + curl_easy_cleanup(CurlHandle); + CurlHandle = nullptr; + + if (curlResult != CURLE_OK) { + retcode = -curlResult; + needToDelete = true; needToRetry = true; - goto exit; - } + goto exit; + } - LightEvent_Wait(&waitCommit); - LightEvent_Clear(&waitCommit); + LightEvent_Wait(&waitCommit); + LightEvent_Clear(&waitCommit); - file_toCommit_size = file_buffer_pos; - svcFlushProcessDataCache(CUR_PROCESS_HANDLE, (u32)g_buffers[g_index], file_toCommit_size); - g_index = !g_index; + file_toCommit_size = file_buffer_pos; + svcFlushProcessDataCache(CUR_PROCESS_HANDLE, (u32)g_buffers[g_index], file_toCommit_size); + g_index = !g_index; - if (!filecommit()) { - retcode = -3; - needToDelete = true; - goto exit; - } + if (!filecommit()) { + retcode = -3; + needToDelete = true; + goto exit; + } - fflush(downfile); + fflush(downfile); - exit: - if (fsCommitThread) { - killThread = true; - LightEvent_Signal(&readyToCommit); - threadJoin(fsCommitThread, U64_MAX); - killThread = false; - fsCommitThread = nullptr; - } +exit: + if (fsCommitThread) { + killThread = true; + LightEvent_Signal(&readyToCommit); + threadJoin(fsCommitThread, U64_MAX); + killThread = false; + fsCommitThread = nullptr; + } - socExit(); + socExit(); - if (socubuf) free(socubuf); + if (socubuf) free(socubuf); - if (downfile) { - fclose(downfile); - downfile = nullptr; - } + if (downfile) { + fclose(downfile); + downfile = nullptr; + } - if (g_buffers[0]) { - free(g_buffers[0]); - g_buffers[0] = nullptr; - } + if (g_buffers[0]) { + free(g_buffers[0]); + g_buffers[0] = nullptr; + } - if (g_buffers[1]) { - free(g_buffers[1]); - g_buffers[1] = nullptr; - } + if (g_buffers[1]) { + free(g_buffers[1]); + g_buffers[1] = nullptr; + } - g_index = 0; - file_buffer_pos = 0; - file_toCommit_size = 0; - writeError = false; + g_index = 0; + file_buffer_pos = 0; + file_toCommit_size = 0; + writeError = false; - if (needToDelete) { - if (access(path.c_str(), F_OK) == 0) deleteFile(path.c_str()); // Delete file, cause not fully downloaded. - } + if (needToDelete) { + if (access(path.c_str(), F_OK) == 0) deleteFile(path.c_str()); // Delete file, cause not fully downloaded. + } - if (QueueSystem::CancelCallback) return 0; + if (QueueSystem::CancelCallback) return 0; if (needToRetry) { printf("Download failed, retrying...\n"); @@ -340,7 +339,6 @@ static Result setupContext(CURL *hnd, const char *url) { /* Download a file of a GitHub Release. - const std::string &url: Const Reference to the URL. (https://github.com/Owner/Repo) const std::string &asset: Const Reference to the Asset. (File.filetype) const std::string &path: Const Reference, where to store. (sdmc:/File.filetype) @@ -451,7 +449,11 @@ Result downloadFromRelease(const std::string &url, const std::string &asset, con @return True if Wi-Fi is connected; false if not. */ bool checkWifiStatus(void) { - // return true; // For citra. +#ifdef CITRA + // Citra's Wi-Fi check doesn't work + return true; +#endif + u32 wifiStatus; bool res = false; @@ -470,7 +472,6 @@ void notConnectedMsg(void) { Msg::waitMsg(Lang::get("CONNECT_WIFI")); } /* Return, if an update is available. - const std::string &URL: Const Reference to the URL of the eShop. int revCurrent: The current Revision. (-1 if unused) */ @@ -548,12 +549,11 @@ bool IsUpdateAvailable(const std::string &URL, int revCurrent) { /* Download a eShop and return, if revision is higher than current. - const std::string &URL: Const Reference to the URL of the eShop. int currentRev: Const Reference to the current Revision. (-1 if unused) std::string &fl: Output for the filepath. bool isDownload: If download or updating. - bool isEDB: If ghosteshop.eshop download or not. + bool isEDB: If ghosteshop.unistore download or not. */ bool DownloadEshop(const std::string &URL, int currentRev, std::string &fl, bool isDownload, bool isEDB) { if (isEDB) Msg::DisplayMsg(Lang::get("DOWNLOADING_ESHOP_DB")); @@ -702,7 +702,6 @@ bool DownloadEshop(const std::string &URL, int currentRev, std::string &fl, bool /* Download a SpriteSheet. - const std::string &URL: Const Reference to the SpriteSheet URL. const std::string &file: Const Reference to the filepath. */ @@ -923,7 +922,7 @@ void UpdateAction() { } ScriptUtils::installFile("sdmc:/GhostEshop.cia", false, Lang::get("INSTALL_GHOST_ESHOP"), true); - ScriptUtils::removeFile("sdmc:/GhostEshop.cia", Lang::get("DELETE_UNNEEDED_FILE"), true); + ScriptUtils::removeFile("sdmc:/GhostEshop.cia", true); Msg::waitMsg(Lang::get("UPDATE_DONE")); exiting = true; } @@ -1143,4 +1142,4 @@ std::string GetChangelog() { result_written = 0; return ""; -} +} \ No newline at end of file diff --git a/source/utils/fileBrowse.cpp b/source/utils/fileBrowse.cpp index 5e95d31..5dd05e4 100644 --- a/source/utils/fileBrowse.cpp +++ b/source/utils/fileBrowse.cpp @@ -161,7 +161,7 @@ std::vector GetEshopInfo(const std::string &path) { getDirectoryContents(dirContents, { "unistore" }); for(uint i = 0; i < dirContents.size(); i++) { - /* Make sure to ONLY push .eshop, and no folders. Avoids crashes in that case too. */ + /* Make sure to ONLY push .unistores, and no folders. Avoids crashes in that case too. */ if ((path + dirContents[i].name).find(".unistore") != std::string::npos) { info.push_back( GetInfo(path + dirContents[i].name, dirContents[i].name) ); } diff --git a/source/utils/queueSystem.cpp b/source/utils/queueSystem.cpp index 6d2e8bf..7ab013a 100644 --- a/source/utils/queueSystem.cpp +++ b/source/utils/queueSystem.cpp @@ -228,16 +228,21 @@ void QueueSystem::QueueHandle() { } else if (type == "rmdir") { bool missing = false; std::string directory = "", message = "", promptmsg = ""; + bool required = false; queueEntries[0]->status = QueueStatus::Request; if (queueEntries[0]->obj[i].contains("directory") && queueEntries[0]->obj[i]["directory"].is_string()) { directory = queueEntries[0]->obj[i]["directory"]; } else missing = true; + if (queueEntries[0]->obj[i].contains("required") && queueEntries[0]->obj[i]["required"].is_boolean()) { + required = queueEntries[0]->obj[i]["required"]; + } + promptmsg = Lang::get("DELETE_PROMPT") + "\n" + directory; if (!missing && directory != "") { - if (access(directory.c_str(), F_OK) != 0) ret = DELETE_ERROR; + if (access(directory.c_str(), F_OK) != 0 && required) ret = DELETE_ERROR; else { if (QueueSystem::RequestNeeded == RMDIR_REQUEST) { /* There we already did it. :) */ diff --git a/source/utils/scriptUtils.cpp b/source/utils/scriptUtils.cpp index 2bdd896..c074bea 100644 --- a/source/utils/scriptUtils.cpp +++ b/source/utils/scriptUtils.cpp @@ -48,7 +48,7 @@ bool ScriptUtils::matchPattern(const std::string &pattern, const std::string &te } /* Remove a File. */ -Result ScriptUtils::removeFile(const std::string &file, const std::string &message, bool isARG) { +Result ScriptUtils::removeFile(const std::string &file, bool isARG) { std::string out; out = std::regex_replace(file, std::regex("%ARCHIVE_DEFAULT%"), config->archPath()); out = std::regex_replace(out, std::regex("%3DSX%/(.*)\\.(.*)"), config->_3dsxPath() + (config->_3dsxInFolder() ? "/$1/$1.$2" : "/$1.$2")); @@ -59,13 +59,12 @@ Result ScriptUtils::removeFile(const std::string &file, const std::string &messa Result ret = NONE; if (access(out.c_str(), F_OK) != 0) return DELETE_ERROR; - if (isARG) Msg::DisplayMsg(message); deleteFile(out.c_str()); return ret; } /* Boot a title. */ -void ScriptUtils::bootTitle(const std::string &TitleID, bool isNAND, const std::string &message, bool isARG) { +void ScriptUtils::bootTitle(const std::string &TitleID, bool isNAND, bool isARG) { std::string MSG = Lang::get("BOOT_TITLE") + "\n\n"; if (isNAND) MSG += Lang::get("MEDIATYPE_NAND") + "\n" + TitleID; else MSG += Lang::get("MEDIATYPE_SD") + "\n" + TitleID; @@ -73,7 +72,6 @@ void ScriptUtils::bootTitle(const std::string &TitleID, bool isNAND, const std:: const u64 ID = std::stoull(TitleID, 0, 16); if (isARG) { if (Msg::promptMsg(MSG)) { - Msg::DisplayMsg(message); Title::Launch(ID, isNAND ? MEDIATYPE_NAND : MEDIATYPE_SD); } @@ -135,7 +133,7 @@ Result ScriptUtils::copyFile(const std::string &source, const std::string &desti } /* Rename / Move a file. */ -Result ScriptUtils::renameFile(const std::string &oldName, const std::string &newName, const std::string &message, bool isARG) { +Result ScriptUtils::renameFile(const std::string &oldName, const std::string &newName, bool isARG) { Result ret = NONE; if (access(oldName.c_str(), F_OK) != 0) return MOVE_ERROR; @@ -152,8 +150,6 @@ Result ScriptUtils::renameFile(const std::string &oldName, const std::string &ne _new = std::regex_replace(_new, std::regex("%NDS%"), config->ndsPath()); _new = std::regex_replace(_new, std::regex("%FIRM%"), config->firmPath()); - if (isARG) Msg::DisplayMsg(message); - /* TODO: Kinda avoid that? */ makeDirs(_new.c_str()); rename(old.c_str(), _new.c_str()); @@ -361,7 +357,7 @@ Result ScriptUtils::runFunctions(nlohmann::json storeJson, int selection, const if (type == "deleteFile") { bool missing = false; - std::string file = "", message = ""; + std::string file = ""; if (Script[i].contains("file") && Script[i]["file"].is_string()) { @@ -369,16 +365,12 @@ Result ScriptUtils::runFunctions(nlohmann::json storeJson, int selection, const } else missing = true; - if (Script[i].contains("message") && Script[i]["message"].is_string()) { - message = Script[i]["message"]; - } - - if (!missing) ret = ScriptUtils::removeFile(file, message, true); + if (!missing) ret = ScriptUtils::removeFile(file, true); else ret = SYNTAX_ERROR; } else if (type == "downloadFile") { bool missing = false; - std::string file = "", output = "", message = ""; + std::string file = "", output = ""; if (Script[i].contains("file") && Script[i]["file"].is_string()) { file = Script[i]["file"]; @@ -390,16 +382,15 @@ Result ScriptUtils::runFunctions(nlohmann::json storeJson, int selection, const } else missing = true; - if (Script[i].contains("message") && Script[i]["message"].is_string()) { - message = Script[i]["message"]; - } + char message[256]; + snprintf(message, sizeof(message), Lang::get("SHORTCUT_DOWNLOADING").c_str(), output.substr(output.find_first_of("/") + 1).c_str()); if (!missing) ret = ScriptUtils::downloadFile(file, output, message, true); else ret = SYNTAX_ERROR; } else if (type == "downloadRelease") { bool missing = false, includePrereleases = false; - std::string repo = "", file = "", output = "", message = ""; + std::string repo = "", file = "", output = ""; if (Script[i].contains("repo") && Script[i]["repo"].is_string()) { repo = Script[i]["repo"]; @@ -419,16 +410,15 @@ Result ScriptUtils::runFunctions(nlohmann::json storeJson, int selection, const if (Script[i].contains("includePrereleases") && Script[i]["includePrereleases"].is_boolean()) includePrereleases = Script[i]["includePrereleases"]; - if (Script[i].contains("message") && Script[i]["message"].is_string()) { - message = Script[i]["message"]; - } + char message[256]; + snprintf(message, sizeof(message), Lang::get("SHORTCUT_DOWNLOADING").c_str(), output.substr(output.find_first_of("/") + 1).c_str()); if (!missing) ret = ScriptUtils::downloadRelease(repo, file, output, includePrereleases, message, true); else ret = SYNTAX_ERROR; } else if (type == "extractFile") { bool missing = false; - std::string file = "", input = "", output = "", message = ""; + std::string file = "", input = "", output = ""; if (Script[i].contains("file") && Script[i]["file"].is_string()) { file = Script[i]["file"]; @@ -445,16 +435,15 @@ Result ScriptUtils::runFunctions(nlohmann::json storeJson, int selection, const } else missing = true; - if (Script[i].contains("message") && Script[i]["message"].is_string()) { - message = Script[i]["message"]; - } + char message[256]; + snprintf(message, sizeof(message), Lang::get("SHORTCUT_EXTRACTING").c_str(), file.substr(file.find_first_of("/") + 1).c_str()); if (!missing) ret = ScriptUtils::extractFile(file, input, output, message, true); else ret = SYNTAX_ERROR; } else if (type == "installCia") { bool missing = false, updateSelf = false; - std::string file = "", message = ""; + std::string file = ""; if (Script[i].contains("file") && Script[i]["file"].is_string()) { file = Script[i]["file"]; @@ -465,16 +454,15 @@ Result ScriptUtils::runFunctions(nlohmann::json storeJson, int selection, const updateSelf = Script[i]["updateSelf"]; } - if (Script[i].contains("message") && Script[i]["message"].is_string()) { - message = Script[i]["message"]; - } + char message[256]; + snprintf(message, sizeof(message), Lang::get("SHORTCUT_INSTALLING").c_str(), file.substr(file.find_first_of("/") + 1).c_str()); if (!missing) ScriptUtils::installFile(file, updateSelf, message, true); else ret = SYNTAX_ERROR; } else if (type == "mkdir") { bool missing = false; - std::string directory = "", message = ""; + std::string directory = ""; if (Script[i].contains("directory") && Script[i]["directory"].is_string()) { directory = Script[i]["directory"]; @@ -486,16 +474,21 @@ Result ScriptUtils::runFunctions(nlohmann::json storeJson, int selection, const } else if (type == "rmdir") { bool missing = false; - std::string directory = "", message = "", promptmsg = ""; + std::string directory = "", promptmsg = ""; + bool required = false; if (Script[i].contains("directory") && Script[i]["directory"].is_string()) { directory = Script[i]["directory"]; } else missing = true; + if (Script[i].contains("required") && Script[i]["required"].is_boolean()) { + required = Script[i]["required"]; + } + promptmsg = Lang::get("DELETE_PROMPT") + "\n" + directory; if (!missing && directory != "") { - if (access(directory.c_str(), F_OK) != 0) ret = DELETE_ERROR; + if (access(directory.c_str(), F_OK) != 0 && required) ret = DELETE_ERROR; else { if (Msg::promptMsg(promptmsg)) removeDirRecursive(directory.c_str()); } @@ -526,7 +519,7 @@ Result ScriptUtils::runFunctions(nlohmann::json storeJson, int selection, const break; } else if (type == "copy") { - std::string Message = "", source = "", destination = ""; + std::string source = "", destination = ""; bool missing = false; if (Script[i].contains("source") && Script[i]["source"].is_string()) { @@ -539,15 +532,14 @@ Result ScriptUtils::runFunctions(nlohmann::json storeJson, int selection, const } else missing = true; - if (Script[i].contains("message") && Script[i]["message"].is_string()) { - Message = Script[i]["message"]; - } + char message[256]; + snprintf(message, sizeof(message), Lang::get("SHORTCUT_COPYING").c_str(), source.substr(source.find_first_of("/") + 1).c_str()); - if (!missing) ret = ScriptUtils::copyFile(source, destination, Message, true); + if (!missing) ret = ScriptUtils::copyFile(source, destination, message, true); else ret = SYNTAX_ERROR; } else if (type == "move") { - std::string Message = "", oldFile = "", newFile = ""; + std::string oldFile = "", newFile = ""; bool missing = false; if (Script[i].contains("old") && Script[i]["old"].is_string()) { @@ -560,11 +552,7 @@ Result ScriptUtils::runFunctions(nlohmann::json storeJson, int selection, const } else missing = true; - if (Script[i].contains("message") && Script[i]["message"].is_string()) { - Message = Script[i]["message"]; - } - - if (!missing) ret = ScriptUtils::renameFile(oldFile, newFile, Message, true); + if (!missing) ret = ScriptUtils::renameFile(oldFile, newFile, true); else ret = SYNTAX_ERROR; } else if (type == "skip") {