diff --git a/README.md b/README.md index 76a408a..e6c429c 100644 --- a/README.md +++ b/README.md @@ -49,4 +49,5 @@ To build Universal-Updater from source, you will need to setup devkitARM with li - [dlbeer](https://github.com/dlbeer) - Original developer of [quirc](https://github.com/dlbeer/quirc) - [FlagBrew](https://github.com/FlagBrew): Original QR Code Scanner code - [Icons8](https://icons8.com/): Icon Designer -- [PabloMK7](https://github.com/mariohackandglitch): Download Code Improvements +- [lvandeve](https://github.com/lvandeve): For [LodePNG](https://github.com/lvandeve/lodepng) +- [PabloMK7](https://github.com/mariohackandglitch): Download Code Improvements \ No newline at end of file diff --git a/assets/gfx/sprites.t3s b/assets/gfx/sprites.t3s index 65ca72c..d66ffb4 100644 --- a/assets/gfx/sprites.t3s +++ b/assets/gfx/sprites.t3s @@ -7,8 +7,11 @@ sprites/checked.png sprites/delete.png sprites/download.png sprites/info.png +sprites/keyboard.png +sprites/list.png sprites/noIcon.png sprites/qr_code.png +sprites/screenshot.png sprites/search.png sprites/settings.png sprites/shortcut.png diff --git a/assets/gfx/sprites/keyboard.png b/assets/gfx/sprites/keyboard.png new file mode 100644 index 0000000..8840359 Binary files /dev/null and b/assets/gfx/sprites/keyboard.png differ diff --git a/assets/gfx/sprites/list.png b/assets/gfx/sprites/list.png new file mode 100644 index 0000000..829a032 Binary files /dev/null and b/assets/gfx/sprites/list.png differ diff --git a/assets/gfx/sprites/qr_code.png b/assets/gfx/sprites/qr_code.png index ea9ffe9..872f464 100644 Binary files a/assets/gfx/sprites/qr_code.png and b/assets/gfx/sprites/qr_code.png differ diff --git a/assets/gfx/sprites/screenshot.png b/assets/gfx/sprites/screenshot.png new file mode 100644 index 0000000..43a53f8 Binary files /dev/null and b/assets/gfx/sprites/screenshot.png differ diff --git a/include/qr/qrcode.hpp b/include/qr/qrcode.hpp index dfd776e..4ba578b 100644 --- a/include/qr/qrcode.hpp +++ b/include/qr/qrcode.hpp @@ -67,16 +67,10 @@ public: void drawThread(); void captureThread(); - void handler(std::vector& out); + void handler(std::string &result); bool done() const { return this->finished; }; bool cancelled() const { return this->cancel; }; - bool Mode() const { return this->mode; }; - void Info(bool v) { this->displayInfo = v; }; - - int selectedStore = 0, sPos = 0; - std::vector stores = { }; - bool FromList = false; - uint8_t timeout = 30; + void List(bool v) { this->displayList = v; }; private: void buffToImage(); void finish(); @@ -90,9 +84,10 @@ private: std::atomic finished = false; bool capturing = false; bool cancel = false; - bool mode = true; // True -> Camera, False -> URL. - bool displayInfo = false; - + bool displayList = false; + int selectedStore = 0, sPos = 0; + std::vector stores = { }; + std::vector out; }; /* diff --git a/include/screens/mainScreen.hpp b/include/screens/mainScreen.hpp index dda3f03..5f95a40 100644 --- a/include/screens/mainScreen.hpp +++ b/include/screens/mainScreen.hpp @@ -55,7 +55,7 @@ private: std::vector dwnldList, dwnldSizes; bool initialized = false, fetchDown = false, showMarks = false, showSettings = false, - ascending = false, updateFilter = false, screenshotFetch = false; + 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; diff --git a/include/store/storeUtils.hpp b/include/store/storeUtils.hpp index c122f0e..52812c3 100644 --- a/include/store/storeUtils.hpp +++ b/include/store/storeUtils.hpp @@ -71,8 +71,8 @@ namespace StoreUtils { void DrawCredits(); /* Screenshot menu. */ - void DrawScreenshotMenu(const C2D_Image &img, const int sIndex, const bool sFetch, const int screenshotSize, const std::string &name, const int zoom); - void ScreenshotMenu(C2D_Image &img, int &sIndex, bool &sFetch, int &storeMode, const int screenshotSize, int &zoom); + void DrawScreenshotMenu(const C2D_Image &img, const int sIndex, const bool sFetch, const int screenshotSize, const std::string &name, const int zoom, const bool canDisplay); + void ScreenshotMenu(C2D_Image &img, int &sIndex, bool &sFetch, int &storeMode, const int screenshotSize, int &zoom, bool &canDisplay); /* Settings. */ void DrawSettings(int page, int selection, int sPos); diff --git a/include/utils/sound.hpp b/include/utils/sound.hpp new file mode 100644 index 0000000..c1f8796 --- /dev/null +++ b/include/utils/sound.hpp @@ -0,0 +1,47 @@ +/* +* 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. +*/ + +#ifndef _UNIVERSAL_UPDATER_SOUND_HPP +#define _UNIVERSAL_UPDATER_SOUND_HPP + +#include <3ds.h> +#include + +class Sound { +public: + Sound(const std::string &path, const int channel = 1, const bool toloop = true); + ~Sound(); + void play(); + void stop(); +private: + u32 dataSize; + bool good = true; + ndspWaveBuf waveBuf; + u8 *data = nullptr; + int chnl; +}; + +#endif \ No newline at end of file diff --git a/romfs/lang/en/app.json b/romfs/lang/en/app.json index 2ebd84d..d99552b 100644 --- a/romfs/lang/en/app.json +++ b/romfs/lang/en/app.json @@ -9,7 +9,6 @@ "AUTO_UPDATE_UU": "Auto-update Universal-Updater", "AUTO_UPDATE_UU_DESC": "When enabled, Universal-Updater will check for updates every time it's opened.", "AVAILABLE_DOWNLOADS": "Available Downloads", - "AVAILABLE_UNISTORES": "Available UniStores", "BOOT_TITLE": "Would you like to boot this title?", "CATEGORY": "Category", "CHANGE_3DSX_PATH": "Change 3DSX path", @@ -57,8 +56,8 @@ "ENTRIES": "Entries", "EXECUTE_ENTRY": "Would you like to execute this entry?", "EXIT_APP": "Exit Universal-Updater", - "FETCHING_AVAILABLE_UNISTORES": "Fetching available UniStores...", "FETCHING_METADATA": "Fetching old metadata...", + "FETCHING_RECOMMENDED_UNISTORES": "Fetching recommended UniStores...", "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.", @@ -84,8 +83,10 @@ "NO_LICENSE": "No License", "NO_SCREENSHOTS_AVAILABLE": "No Screenshots available", "NOT_IMPLEMENTED": "Not Implemented Yet", + "RECOMMENDED_UNISTORES": "Recommended UniStores", "REVISION": "Revision", "SCREENSHOT": "Screenshot %d / %d", + "SCREENSHOT_COULD_NOT_LOAD": "Screenshot could not be loaded.", "SCREENSHOT_INSTRUCTIONS": "Press  to change and  to zoom", "SEARCH_FILTERS": "Search and Filters", "SELECT_DIR": "Select a directory", diff --git a/source/init.cpp b/source/init.cpp index 49841a2..413f73a 100644 --- a/source/init.cpp +++ b/source/init.cpp @@ -28,6 +28,7 @@ #include "download.hpp" #include "init.hpp" #include "mainScreen.hpp" +#include "sound.hpp" #include #include @@ -36,6 +37,8 @@ bool exiting = false, is3DSX = false, needUnloadFont = false; C2D_SpriteSheet sprites; int fadeAlpha = 0; u32 old_time_limit; +std::unique_ptr Music = nullptr; +bool dspfirmFound = false; /* Set, if 3DSX or CIA. @@ -46,6 +49,32 @@ static void getCurrentUsage(){ is3DSX = (id != 0x0004000004391700); } +/* + Init Music. +*/ +static void InitMusic() { + if (access("sdmc:/3ds/dspfirm.cdc", F_OK) == 0) { // Ensure dspfirm dump exist. + if (access("sdmc:/3ds/Universal-Updater/music.wav", F_OK) == 0) { // Ensure music.wav exist. + dspfirmFound = true; + ndspInit(); + Music = std::make_unique("sdmc:/3ds/Universal-Updater/music.wav"); + + Music->play(); + } + } +} + +/* + Exit Music. +*/ +static void ExitMusic() { + if (dspfirmFound) { + Music->stop(); + Music = nullptr; + ndspExit(); + } +} + /* If button Position pressed -> Do something. @@ -121,6 +150,7 @@ Result Init::Initialize() { if (exiting) return -1; // In case the update was successful. Gui::setScreen(std::make_unique(), false, false); + InitMusic(); return 0; } @@ -172,6 +202,7 @@ Result Init::Exit() { Gui::exit(); Gui::unloadSheet(sprites); UnloadFont(); + ExitMusic(); gfxExit(); cfguExit(); config->save(); diff --git a/source/overlays/credits.cpp b/source/overlays/credits.cpp index 208f506..c0eba36 100644 --- a/source/overlays/credits.cpp +++ b/source/overlays/credits.cpp @@ -47,8 +47,9 @@ void Overlays::ShowCredits() { Gui::DrawString(10, 70, 0.5f, TEXT_COLOR, "- dlbeer", 0, 0, font); Gui::DrawString(10, 90, 0.5f, TEXT_COLOR, "- FlagBrew", 0, 0, font); Gui::DrawString(10, 110, 0.5f, TEXT_COLOR, "- https://icons8.com/", 0, 0, font); - Gui::DrawString(10, 130, 0.5f, TEXT_COLOR, "- PabloMK7", 0, 0, font); - Gui::DrawString(10, 150, 0.5f, TEXT_COLOR, Lang::get("CONTRIBUTOR_TRANSLATORS"), 210, 0, font); + Gui::DrawString(10, 130, 0.5f, TEXT_COLOR, "- Ivandeve", 0, 0, font); + Gui::DrawString(10, 150, 0.5f, TEXT_COLOR, "- PabloMK7", 0, 0, font); + Gui::DrawString(10, 170, 0.5f, TEXT_COLOR, Lang::get("CONTRIBUTOR_TRANSLATORS"), 210, 0, font); Gui::DrawString(10, 197, 0.5f, TEXT_COLOR, Lang::get("GITHUB"), 390, 0, font); Gui::Draw_Rect(0, 215, 400, 25, BAR_COLOR); diff --git a/source/overlays/storeSelect.cpp b/source/overlays/storeSelect.cpp index 5256032..27f1734 100644 --- a/source/overlays/storeSelect.cpp +++ b/source/overlays/storeSelect.cpp @@ -45,11 +45,9 @@ static const std::vector mainButtons = { { 10, 154, 300, 22 }, { 10, 184, 300, 22 }, - /* Add, Delete, Info.. */ - { 92, 215, 16, 16 }, - { 136, 215, 16, 16 }, - { 180, 215, 16, 16 }, - { 224, 215, 16, 16 }, + { 112, 215, 16, 16 }, // Delete. + { 154, 215, 16, 16 }, // Update. + { 200, 215, 16, 16 }, // Add. { 4, 0, 24, 24 } // Back. }; @@ -265,7 +263,7 @@ void Overlays::SelectStore(std::unique_ptr &store, std::vector &store, std::vector &store, std::vector _UNISTORE_VERSION) Msg::waitMsg(Lang::get("UNISTORE_TOO_NEW")); else { + config->lastStore(info[selection].FileName); store = std::make_unique(_STORE_PATH + info[selection].FileName, info[selection].FileName); StoreUtils::ResetAll(store, meta, entries); - config->lastStore(info[selection].FileName); StoreUtils::SortEntries(false, SortType::LAST_UPDATED, entries); doOut = true; } @@ -339,9 +336,10 @@ void Overlays::SelectStore(std::unique_ptr &store, std::vector _UNISTORE_VERSION) Msg::waitMsg(Lang::get("UNISTORE_TOO_NEW")); else { + config->lastStore(info[i + sPos].FileName); store = std::make_unique(_STORE_PATH + info[i + sPos].FileName, info[i + sPos].FileName); StoreUtils::ResetAll(store, meta, entries); - config->lastStore(info[i + sPos].FileName); + StoreUtils::SortEntries(false, SortType::LAST_UPDATED, entries); doOut = true; } @@ -395,8 +393,6 @@ void Overlays::SelectStore(std::unique_ptr &store, std::vector mainButtons = { { 10, 94, 300, 22 }, { 10, 124, 300, 22 }, { 10, 154, 300, 22 }, - { 10, 184, 300, 22 } + { 10, 184, 300, 22 }, + + { 5, 215, 24, 24 }, // QR Code / List. + { 35, 215, 24, 24 }, // Keyboard. + { 4, 0, 24, 24 } // Back. }; extern bool touching(touchPosition touch, Structs::ButtonPos button); @@ -84,6 +88,8 @@ QRCode::QRCode() { quirc_resize(this->qrData, 400, 240); if (checkWifiStatus()) this->stores = FetchStores(); // Fetching Stores here. + + if (this->stores.size() > 0) this->displayList = true; } /* @@ -140,39 +146,49 @@ void QRCode::drawThread() { C2D_TargetClear(Top, TRANSPARENT); C2D_TargetClear(Bottom, TRANSPARENT); - if (!this->displayInfo) { + if (!this->displayList) { this->buffToImage(); // Fetch image. Gui::ScreenDraw(Top); C2D_DrawImageAt(this->image, 0, 0, 0.5, nullptr, 1.0f, 1.0f); + GFX::DrawBottom(); + Gui::Draw_Rect(0, 0, 320, 25, ENTRY_BAR_COLOR); + Gui::Draw_Rect(0, 25, 320, 1, ENTRY_BAR_OUTL_COLOR); + } else { GFX::DrawTop(); Gui::DrawStringCentered(0, 1, 0.7, TEXT_COLOR, Lang::get("STORE_INFO"), 390, 0, font); - Gui::DrawStringCentered(0, 30, 0.7f, TEXT_COLOR, this->stores[this->selectedStore].Title, 390, 0, font); - Gui::DrawStringCentered(0, 50, 0.6f, TEXT_COLOR, this->stores[this->selectedStore].Author, 380, 0, font); - if (this->stores[this->selectedStore].Description != "") { - /* "\n\n" breaks C2D_WordWrap, so check here. */ - if (!(this->stores[this->selectedStore].Description.find("\n\n") != std::string::npos)) { - Gui::DrawStringCentered(0, 90, 0.5f, TEXT_COLOR, this->stores[this->selectedStore].Description, 380, 130, font, C2D_WordWrap); + if (this->stores.size() > 0) { + Gui::DrawStringCentered(0, 30, 0.7f, TEXT_COLOR, this->stores[this->selectedStore].Title, 390, 0, font); + Gui::DrawStringCentered(0, 50, 0.6f, TEXT_COLOR, this->stores[this->selectedStore].Author, 380, 0, font); - } else { - Gui::DrawStringCentered(0, 90, 0.5f, TEXT_COLOR, this->stores[this->selectedStore].Description, 380, 130, font); + if (this->stores[this->selectedStore].Description != "") { + /* "\n\n" breaks C2D_WordWrap, so check here. */ + if (!(this->stores[this->selectedStore].Description.find("\n\n") != std::string::npos)) { + Gui::DrawStringCentered(0, 90, 0.5f, TEXT_COLOR, this->stores[this->selectedStore].Description, 380, 130, font, C2D_WordWrap); + + } else { + Gui::DrawStringCentered(0, 90, 0.5f, TEXT_COLOR, this->stores[this->selectedStore].Description, 380, 130, font); + } } } + + GFX::DrawBottom(); + Gui::Draw_Rect(0, 0, 320, 25, ENTRY_BAR_COLOR); + Gui::Draw_Rect(0, 25, 320, 1, ENTRY_BAR_OUTL_COLOR); + Gui::DrawStringCentered(0, 2, 0.6, TEXT_COLOR, Lang::get("RECOMMENDED_UNISTORES"), 310, 0, font); + + for(int i = 0; i < 6 && i < (int)this->stores.size(); i++) { + if (this->sPos + i == this->selectedStore) GFX::DrawBox(mainButtons[i].x, mainButtons[i].y, mainButtons[i].w, mainButtons[i].h, false); + + Gui::DrawStringCentered(10 - 160 + (300 / 2), mainButtons[i].y + 4, 0.45f, TEXT_COLOR, this->stores[this->sPos + i].Title, 295, 0, font); + } } - GFX::DrawBottom(); - Gui::Draw_Rect(0, 0, 320, 25, ENTRY_BAR_COLOR); - Gui::Draw_Rect(0, 25, 320, 1, ENTRY_BAR_OUTL_COLOR); - Gui::DrawStringCentered(0, 2, 0.6, TEXT_COLOR, Lang::get("AVAILABLE_UNISTORES"), 310, 0, font); - - for(int i = 0; i < 6 && i < (int)this->stores.size(); i++) { - if (this->sPos + i == this->selectedStore) GFX::DrawBox(mainButtons[i].x, mainButtons[i].y, mainButtons[i].w, mainButtons[i].h, false); - - Gui::DrawStringCentered(10 - 160 + (300 / 2), mainButtons[i].y + 4, 0.45f, TEXT_COLOR, this->stores[this->sPos + i].Title, 295, 0, font); - } - + GFX::DrawSprite((this->displayList ? sprites_qr_code_idx : sprites_list_idx), mainButtons[6].x, mainButtons[6].y); + if (this->displayList) GFX::DrawSprite(sprites_keyboard_idx, mainButtons[7].x, mainButtons[7].y); + GFX::DrawSprite(sprites_arrow_idx, mainButtons[8].x, mainButtons[8].y); C3D_FrameEnd(0); } @@ -192,6 +208,7 @@ void QRCode::captureThread() { CAMU_SetSize(SELECT_OUT1, SIZE_CTR_TOP_LCD, CONTEXT_A); CAMU_SetOutputFormat(SELECT_OUT1, OUTPUT_RGB_565, CONTEXT_A); CAMU_SetFrameRate(SELECT_OUT1, FRAME_RATE_30); + CAMU_SetNoiseFilter(SELECT_OUT1, true); CAMU_SetAutoExposure(SELECT_OUT1, true); CAMU_SetAutoWhiteBalance(SELECT_OUT1, true); @@ -275,41 +292,44 @@ void captureHelper(void *arg) { /* Handle the capture. - - std::vector &out: The Reference, where to output the decoded result. */ -void QRCode::handler(std::vector &out) { +void QRCode::handler(std::string &result) { hidScanInput(); touchPosition t; hidTouchRead(&t); u32 keyDown = hidKeysDown(); u32 keyRepeat = hidKeysDownRepeat(); - if (keyDown & KEY_B) { - this->cancel = true; + if ((keyDown & KEY_B) || (keyDown & KEY_TOUCH && touching(t, mainButtons[8]))) { + result = ""; + this->finished = true; this->finish(); return; } - if (this->displayInfo) { - if (this->timeout == 0) { // hidKeysDown() is pretty buggy, hence try to do it a timeout way. - if (keyDown & KEY_SELECT) { - this->timeout = 10; - keyDown = 0; - this->displayInfo = false; + /* Keyboard. */ + if (keyDown & KEY_TOUCH && touching(t, mainButtons[7])) { + if (this->displayList) { + const std::string temp = Input::setkbdString(150, Lang::get("ENTER_URL"), { }); + + if (temp != "") { + result = temp; + this->finished = true; + this->finish(); + return; } } + } + + if (this->displayList) { + gspWaitForVBlank(); + + if ((keyDown & KEY_SELECT) || (keyDown & KEY_TOUCH && touching(t, mainButtons[6]))) { + keyDown = 0; + this->displayList = false; + } - } else { if (this->stores.size() > 0) { - if (this->timeout == 0) { - if (keyDown & KEY_SELECT) { - this->timeout = 30; - keyDown = 0; - this->displayInfo = true; - } - } - if (keyRepeat & KEY_DOWN) { if (this->selectedStore < (int)this->stores.size() - 1) this->selectedStore++; else this->selectedStore = 0; @@ -321,7 +341,8 @@ void QRCode::handler(std::vector &out) { } if (keyDown & KEY_A) { - this->FromList = true; + result = this->stores[this->selectedStore].URL; + this->finished = true; this->finish(); return; } @@ -330,8 +351,8 @@ void QRCode::handler(std::vector &out) { for (int i = 0; i < 6; i++) { if (touching(t, mainButtons[i])) { if (i + this->sPos < (int)this->stores.size()) { - this->selectedStore = i + this->sPos; - this->FromList = true; + result = this->stores[i + this->sPos].URL; + this->finished = true; this->finish(); return; } @@ -340,8 +361,16 @@ void QRCode::handler(std::vector &out) { } } + } else { + if (this->stores.size() > 0) { + if ((keyDown & KEY_SELECT) || (keyDown & KEY_TOUCH && touching(t, mainButtons[6]))) { + keyDown = 0; + this->displayList = true; + } + } + if (!this->capturing) { - /* create camera draw thread. */ + /* create camera capture thread. */ if (threadCreate((ThreadFunc)&captureHelper, this, 0x10000, 0x1A, 1, true) != NULL) this->capturing = true; else { this->finish(); @@ -372,50 +401,34 @@ void QRCode::handler(std::vector &out) { if (!quirc_decode(&code, &scan_data)) { this->finish(); - out.resize(scan_data.payload_len); - std::copy(scan_data.payload, scan_data.payload + scan_data.payload_len, out.begin()); + this->out.resize(scan_data.payload_len); + std::copy(scan_data.payload, scan_data.payload + scan_data.payload_len, this->out.begin()); + + /* From scanned stuff. */ + if (this->out.empty()) result = ""; + + /* If Terminator, do -1. */ + if (this->out.back() == '\0') result = std::string((char *)this->out.data(), this->out.size() - 1); + 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->timeout > 0) this->timeout--; } /* The Store Add QR Code handle and such. */ std::string QR_Scanner::StoreHandle() { - std::vector data = { }; + std::string result = ""; std::unique_ptr qrData = std::make_unique(); aptSetHomeAllowed(false); // Block the Home key. - threadCreate((ThreadFunc)&drawHelper, qrData.get(), 0x10000, 0x1A, 1, true); - while (!qrData->done()) qrData->handler(data); // Handle. + while (!qrData->done()) qrData->handler(result); // Handle. aptSetHomeAllowed(true); // Re-Allow it. - /* Selected from list. */ - if (qrData->FromList) { - return qrData->stores[qrData->selectedStore].URL; - } - - /* False means Keyboard. */ - if (!qrData->Mode()) { - const std::string out = Input::setkbdString(150, Lang::get("ENTER_URL"), { }); - return out; - - } else { - /* From scanned stuff. */ - if (data.empty()) return ""; - - /* If Terminator, do -1. */ - if (data.back() == '\0') return std::string((char *)data.data(), data.size() - 1); - - else return std::string((char *)data.data(), data.size()); - } - - return ""; + return result; } \ No newline at end of file diff --git a/source/screens/mainScreen.cpp b/source/screens/mainScreen.cpp index 445e1ce..e6b502d 100644 --- a/source/screens/mainScreen.cpp +++ b/source/screens/mainScreen.cpp @@ -107,7 +107,7 @@ MainScreen::MainScreen() { void MainScreen::Draw(void) const { if (this->storeMode == 5) { /* Screenshot Menu. */ - StoreUtils::DrawScreenshotMenu(this->Screenshot, this->screenshotIndex, this->screenshotFetch, this->sSize, this->screenshotName, this->zoom); + StoreUtils::DrawScreenshotMenu(this->Screenshot, this->screenshotIndex, this->screenshotFetch, this->sSize, this->screenshotName, this->zoom, this->canDisplay); return; } @@ -180,13 +180,15 @@ void MainScreen::Logic(u32 hDown, u32 hHeld, touchPosition touch) { if (this->screenshotIndex < this->sSize) { if (this->sSize > 0) { this->Screenshot = FetchScreenshot(this->entries[this->store->GetEntry()]->GetScreenshots()[this->screenshotIndex]); + if (this->Screenshot.tex) this->canDisplay = true; + else this->canDisplay = false; } } this->screenshotFetch = false; } - StoreUtils::ScreenshotMenu(this->Screenshot, this->screenshotIndex, this->screenshotFetch, this->storeMode, this->sSize, this->zoom); + StoreUtils::ScreenshotMenu(this->Screenshot, this->screenshotIndex, this->screenshotFetch, this->storeMode, this->sSize, this->zoom, this->canDisplay); return; } diff --git a/source/store/entryInfo.cpp b/source/store/entryInfo.cpp index 79c7b74..4082b8d 100644 --- a/source/store/entryInfo.cpp +++ b/source/store/entryInfo.cpp @@ -28,7 +28,9 @@ #include "structs.hpp" extern bool touching(touchPosition touch, Structs::ButtonPos button); -static const Structs::ButtonPos btn = { 53, 215, 20, 20 }; +static const Structs::ButtonPos btn = { 53, 215, 24, 24 }; +static const Structs::ButtonPos sshot = { 83, 215, 24, 24 }; +extern bool checkWifiStatus(); /* Draw the Entry Info part. @@ -61,7 +63,8 @@ void StoreUtils::DrawEntryInfo(const std::unique_ptr &store, const std::u Gui::DrawString(61, 190, 0.45, TEXT_COLOR, Lang::get("LICENSE") + ": " + entry->GetLicense(), 240, 0, font); GFX::DrawBox(btn.x, btn.y, btn.w, btn.h, false); - Gui::DrawString(btn.x + 3, btn.y, 0.6f, TEXT_COLOR, "★", 0, 0, font); + GFX::DrawSprite(sprites_screenshot_idx, sshot.x, sshot.y); + Gui::DrawString(btn.x + 5, btn.y + 2, 0.6f, TEXT_COLOR, "★", 0, 0, font); } } @@ -74,12 +77,16 @@ void StoreUtils::DrawEntryInfo(const std::unique_ptr &store, const std::u bool &showMark: Reference to showMark.. to show the mark menu. bool &fetch: Reference to fetch, so we know, if we need to fetch, when accessing download list. + bool &sFetch: Reference to the screenshot fetch. + int &mode: Reference to the Store mode. */ void StoreUtils::EntryHandle(bool &showMark, bool &fetch, bool &sFetch, int &mode) { if ((hDown & KEY_START) || (hDown & KEY_TOUCH && touching(touch, btn))) showMark = true; - if (hDown & KEY_SELECT) { - sFetch = true; - mode = 5; + if ((hDown & KEY_SELECT) || (hDown & KEY_TOUCH && touching(touch, sshot))) { + if (checkWifiStatus()) { + sFetch = true; + mode = 5; + } } } \ No newline at end of file diff --git a/source/store/markMenu.cpp b/source/store/markMenu.cpp index c1a8e6d..43691b7 100644 --- a/source/store/markMenu.cpp +++ b/source/store/markMenu.cpp @@ -35,7 +35,7 @@ static const std::vector markBox = { { 196, 94, 52, 52 }, { 258, 94, 52, 52 }, - { 53, 215, 20, 20 } + { 53, 215, 24, 24 } }; /* @@ -68,7 +68,7 @@ void StoreUtils::DisplayMarkBox(int marks) { Gui::DrawString(markBox[4].x + 15, markBox[4].y + 11, 0.9, TEXT_COLOR, "♠", 0, 0, font); GFX::DrawBox(markBox[5].x, markBox[5].y, markBox[5].w, markBox[5].h, false); - Gui::DrawString(markBox[5].x + 3, markBox[5].y, 0.6f, TEXT_COLOR, "★", 0, 0, font); + Gui::DrawString(markBox[5].x + 5, markBox[5].y + 2, 0.6f, TEXT_COLOR, "★", 0, 0, font); } /* diff --git a/source/store/screenshotMenu.cpp b/source/store/screenshotMenu.cpp index 384c778..64e524e 100644 --- a/source/store/screenshotMenu.cpp +++ b/source/store/screenshotMenu.cpp @@ -28,6 +28,7 @@ #include "structs.hpp" extern bool touching(touchPosition touch, Structs::ButtonPos button); +extern bool checkWifiStatus(); /* Draw the Screenshot menu. @@ -38,56 +39,73 @@ extern bool touching(touchPosition touch, Structs::ButtonPos button); const int screenshotSize: The screenshot amount. const std::string &name: The name of the screenshot. const int zoom: The zoom level, zoom out, 1x scale, or zoom in. + const bool canDisplay: If can display, or not. */ -void StoreUtils::DrawScreenshotMenu(const C2D_Image &img, const int sIndex, const bool sFetch, const int screenshotSize, const std::string &name, const int zoom) { +void StoreUtils::DrawScreenshotMenu(const C2D_Image &img, const int sIndex, const bool sFetch, const int screenshotSize, const std::string &name, const int zoom, const bool canDisplay) { Gui::ScreenDraw(Top); Gui::Draw_Rect(0, 0, 400, 240, BG_COLOR); - if (screenshotSize > 0) { - float scale = 1.0f; - - if (zoom == 0) { - scale = std::min(1.0f, std::min(400.0f / img.subtex->width, 240.0f / img.subtex->height)); - if (img.tex) C2D_DrawImageAt(img, (400 - img.subtex->width * scale) / 2, (240 - img.subtex->height * scale) / 2, 0.5f, nullptr, scale, scale); - } else { - // Create new C2D_Image with smaller subtex - C2D_Image top = img; - if (img.subtex->height > 240) - top.subtex = new Tex3DS_SubTexture({img.subtex->width, (u16)(img.subtex->height / 2), img.subtex->left, img.subtex->top, img.subtex->right, 1.0f - (img.subtex->height / 2 / 512.0f)}); - - // If zoom == 2, then zoom in to fit the screen - if (zoom == 2) - scale = std::min(400.0f / top.subtex->width, 240.0f / top.subtex->height); - - if (top.tex) C2D_DrawImageAt(top, (400 - top.subtex->width * scale) / 2, (240 - top.subtex->height * scale) / 2, 0.5f, nullptr, scale, scale); - - // Only delete if new - if (top.subtex->height > 240) - delete top.subtex; - } + if (!canDisplay) { GFX::DrawBottom(); - - /* Bottom. */ - if (zoom > 0 && img.subtex->height * scale > 240) { - C2D_Image bottom = img; - bottom.subtex = new Tex3DS_SubTexture({img.subtex->width, (u16)(img.subtex->height / 2), img.subtex->left, img.subtex->bottom + (img.subtex->height / 2 / 512.0f), img.subtex->right, img.subtex->bottom}); - if (bottom.tex) C2D_DrawImageAt(bottom, (320 - bottom.subtex->width * scale) / 2, (240 - bottom.subtex->height * scale) / 2, 0.5f, nullptr, scale, scale); - delete bottom.subtex; + if (screenshotSize > 0) { // if texture is nullptr AND screenshot size is larger than 0. + Gui::DrawStringCentered(0, 2, 0.6f, WHITE, Lang::get("SCREENSHOT_COULD_NOT_LOAD"), 310); } else { - Gui::Draw_Rect(0, 215, 320, 25, BAR_COLOR); - Gui::Draw_Rect(0, 214, 320, 1, BAR_OUTL_COLOR); - Gui::DrawStringCentered(0, 220, 0.5f, TEXT_COLOR, Lang::get("SCREENSHOT_INSTRUCTIONS"), 310, 0, font); - - char screenshots[0x100]; - snprintf(screenshots, sizeof(screenshots), Lang::get("SCREENSHOT").c_str(), sIndex + 1, screenshotSize); - Gui::DrawStringCentered(0, 2, 0.6f, WHITE, screenshots, 310, 0, font); - Gui::DrawStringCentered(0, 40, 0.6f, WHITE, name, 310, 0, font); + Gui::DrawStringCentered(0, 2, 0.6f, WHITE, Lang::get("NO_SCREENSHOTS_AVAILABLE"), 310); } - } else { - GFX::DrawBottom(); - Gui::DrawStringCentered(0, 2, 0.6f, WHITE, Lang::get("NO_SCREENSHOTS_AVAILABLE"), 310); + return; + } + + if (!sFetch) { // Only, if not fetch. This avoids a small flicker of the old screenshot on entries without screenshots. + if (screenshotSize > 0) { + float scale = 1.0f; + + if (zoom == 0) { + scale = std::min(1.0f, std::min(400.0f / img.subtex->width, 240.0f / img.subtex->height)); + if (img.tex) C2D_DrawImageAt(img, (400 - img.subtex->width * scale) / 2, (240 - img.subtex->height * scale) / 2, 0.5f, nullptr, scale, scale); + + } else { + // Create new C2D_Image with smaller subtex + C2D_Image top = img; + if (img.subtex->height > 240) + top.subtex = new Tex3DS_SubTexture({img.subtex->width, (u16)(img.subtex->height / 2), img.subtex->left, img.subtex->top, img.subtex->right, 1.0f - (img.subtex->height / 2 / 512.0f)}); + + // If zoom == 2, then zoom in to fit the screen + if (zoom == 2) + scale = std::min(400.0f / top.subtex->width, 240.0f / top.subtex->height); + + if (top.tex) C2D_DrawImageAt(top, (400 - top.subtex->width * scale) / 2, (240 - top.subtex->height * scale) / 2, 0.5f, nullptr, scale, scale); + + // Only delete if new + if (top.subtex->height > 240) + delete top.subtex; + } + + GFX::DrawBottom(); + + /* Bottom. */ + if (zoom > 0 && img.subtex->height * scale > 240) { + C2D_Image bottom = img; + bottom.subtex = new Tex3DS_SubTexture({img.subtex->width, (u16)(img.subtex->height / 2), img.subtex->left, img.subtex->bottom + (img.subtex->height / 2 / 512.0f), img.subtex->right, img.subtex->bottom}); + if (bottom.tex) C2D_DrawImageAt(bottom, (320 - bottom.subtex->width * scale) / 2, (240 - bottom.subtex->height * scale) / 2, 0.5f, nullptr, scale, scale); + delete bottom.subtex; + + } else { + Gui::Draw_Rect(0, 215, 320, 25, BAR_COLOR); + Gui::Draw_Rect(0, 214, 320, 1, BAR_OUTL_COLOR); + Gui::DrawStringCentered(0, 220, 0.5f, TEXT_COLOR, Lang::get("SCREENSHOT_INSTRUCTIONS"), 310, 0, font); + + char screenshots[0x100]; + snprintf(screenshots, sizeof(screenshots), Lang::get("SCREENSHOT").c_str(), sIndex + 1, screenshotSize); + Gui::DrawStringCentered(0, 2, 0.6f, WHITE, screenshots, 310, 0, font); + Gui::DrawStringCentered(0, 40, 0.6f, WHITE, name, 310, 0, font); + } + + } else { + GFX::DrawBottom(); + Gui::DrawStringCentered(0, 2, 0.6f, WHITE, Lang::get("NO_SCREENSHOTS_AVAILABLE"), 310); + } } } @@ -99,18 +117,22 @@ void StoreUtils::DrawScreenshotMenu(const C2D_Image &img, const int sIndex, cons bool &sFetch: If fetching screenshots or not. const int screenshotSize: The screenshot amount. int &zoom: The zoom level, zoom out, 1x scale, or zoom in. + bool &canDisplay: If can display or not. */ -void StoreUtils::ScreenshotMenu(C2D_Image &img, int &sIndex, bool &sFetch, int &storeMode, const int screenshotSize, int &zoom) { +void StoreUtils::ScreenshotMenu(C2D_Image &img, int &sIndex, bool &sFetch, int &storeMode, const int screenshotSize, int &zoom, bool &canDisplay) { if (hDown & KEY_B) { - zoom = false; + canDisplay = false; + zoom = 0; sIndex = 0; storeMode = 0; // Go back to EntryInfo. } - if (hDown & KEY_RIGHT) { - if (sIndex < screenshotSize - 1) { - sIndex++; - sFetch = true; + if ((hDown & KEY_RIGHT) || (hDown & KEY_R)) { + if (checkWifiStatus()) { + if (sIndex < screenshotSize - 1) { + sIndex++; + sFetch = true; + } } } @@ -118,10 +140,12 @@ void StoreUtils::ScreenshotMenu(C2D_Image &img, int &sIndex, bool &sFetch, int & if (hDown & KEY_UP && zoom < 2) zoom++; - if (hDown & KEY_LEFT) { - if (sIndex > 0) { - sIndex--; - sFetch = true; + if ((hDown & KEY_LEFT) || (hDown & KEY_L)) { + if (checkWifiStatus()) { + if (sIndex > 0) { + sIndex--; + sFetch = true; + } } } } \ No newline at end of file diff --git a/source/utils/config.cpp b/source/utils/config.cpp index ca8e867..d005cc3 100644 --- a/source/utils/config.cpp +++ b/source/utils/config.cpp @@ -112,8 +112,10 @@ Config::Config() { /* Let us create a new one. */ if (!this->json.contains("Version")) this->initialize(); + if (!this->json.contains("Language")) this->sysLang(); else this->language(this->getString("Language")); + if (this->json.contains("LastStore")) this->lastStore(this->getString("LastStore")); if (this->json.contains("List")) this->list(this->getBool("List")); if (this->json.contains("AutoUpdate")) this->autoupdate(this->getBool("AutoUpdate")); @@ -160,20 +162,20 @@ void Config::save() { bool Config::getBool(const std::string &key) { if (!this->json.contains(key)) return false; - return this->json.at(key).get_ref(); + return this->json.at(key).get_ref(); } void Config::setBool(const std::string &key, bool v) { this->json[key] = v; }; int Config::getInt(const std::string &key) { if (!this->json.contains(key)) return 0; - return this->json.at(key).get_ref(); + return this->json.at(key).get_ref(); } void Config::setInt(const std::string &key, int v) { this->json[key] = v; }; std::string Config::getString(const std::string &key) { if (!this->json.contains(key)) return ""; - return this->json.at(key).get_ref(); + return this->json.at(key).get_ref(); } void Config::setString(const std::string &key, const std::string &v) { this->json[key] = v; }; \ No newline at end of file diff --git a/source/utils/download.cpp b/source/utils/download.cpp index ddcb335..4bbc1da 100644 --- a/source/utils/download.cpp +++ b/source/utils/download.cpp @@ -864,7 +864,7 @@ static StoreList fetch(const std::string &entry, nlohmann::json &js) { Fetch Store list for available UniStores. */ std::vector FetchStores() { - Msg::DisplayMsg(Lang::get("FETCHING_AVAILABLE_UNISTORES")); + Msg::DisplayMsg(Lang::get("FETCHING_RECOMMENDED_UNISTORES")); std::vector stores = { }; Result ret = 0; diff --git a/source/utils/sound.cpp b/source/utils/sound.cpp new file mode 100644 index 0000000..bfcf3ae --- /dev/null +++ b/source/utils/sound.cpp @@ -0,0 +1,166 @@ +/* +* 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 "sound.hpp" +#include +#include +#include +#include + +/* + Reference: http://yannesposito.com/Scratch/en/blog/2010-10-14-Fun-with-wav/ +*/ +typedef struct _WavHeader { + char magic[4]; // "RIFF" + u32 totallength; // Total file length, minus 8. + char wavefmt[8]; // Should be "WAVEfmt ". + u32 format; // 16 for PCM format. + u16 pcm; // 1 for PCM format. + u16 channels; // Channels. + u32 frequency; // Sampling frequency. + u32 bytes_per_second; + u16 bytes_by_capture; + u16 bits_per_sample; + char data[4]; // "data". + u32 bytes_in_data; +} WavHeader; +static_assert(sizeof(WavHeader) == 44, "WavHeader size is not 44 bytes."); + +#define _MAX_SIZE 10485760 // 10 MiB. + +/* + Initialize the sound. + + const std::string &path: Path to the file to play. + const int channel: The channel to use. 1 by default. + const bool toloop: If should loop or not. True by default. +*/ +Sound::Sound(const std::string &path, const int channel, const bool toloop) { + ndspSetOutputMode(NDSP_OUTPUT_MONO); + ndspSetOutputCount(2); // Amount of buffers. + + /* Reading wav file. */ + FILE *file = fopen(path.c_str(), "rb"); + + if (!file) { + printf("Could not open the WAV file: %s.\n", path.c_str()); + this->good = false; + return; + } + + WavHeader wavHeader; + size_t read = fread(&wavHeader, 1, sizeof(wavHeader), file); + if (read != sizeof(wavHeader)) { + /* Short read. */ + printf("WAV file header is too short: %s.\n", path.c_str()); + fclose(file); + this->good = false; + return; + } + + /* Verify the header. */ + static const char RIFF_magic[4] = { 'R','I','F','F' }; + if (memcmp(wavHeader.magic, RIFF_magic, sizeof(wavHeader.magic)) != 0) { + /* Incorrect magic number. */ + printf("Wrong file format.\n"); + fclose(file); + this->good = false; + return; + } + + if (wavHeader.totallength == 0 || + (wavHeader.channels != 1 && wavHeader.channels != 2) || + (wavHeader.bits_per_sample != 8 && wavHeader.bits_per_sample != 16)) { + /* Unsupported WAV file. */ + printf("Corrupted wav file.\n"); + fclose(file); + this->good = false; + return; + } + + /* Get the file size. */ + fseek(file, 0, SEEK_END); + this->dataSize = ftell(file) - sizeof(wavHeader); + + if (this->dataSize > _MAX_SIZE) { + fclose(file); + this->good = false; + return; + } + + /* Allocating and reading samples. */ + this->data = reinterpret_cast(linearAlloc(this->dataSize)); + fseek(file, 44, SEEK_SET); + fread(this->data, 1, this->dataSize, file); + fclose(file); + + //if (wavHeader.bits_per_sample == 16) this->dataSize /= 2; // Not sure.. if that is actually needed at all. + + this->chnl = channel; + ndspChnReset(this->chnl); + ndspChnSetInterp(this->chnl, NDSP_INTERP_NONE); + ndspChnSetRate(this->chnl, float(wavHeader.frequency)); + ndspChnSetFormat(this->chnl, ((wavHeader.bits_per_sample == 8) ? NDSP_FORMAT_MONO_PCM8 : NDSP_FORMAT_MONO_PCM16)); + + /* Create and play a wav buffer. */ + memset(&this->waveBuf, 0, sizeof(this->waveBuf)); + + this->waveBuf.data_vaddr = reinterpret_cast(this->data); + this->waveBuf.nsamples = this->dataSize / (wavHeader.bits_per_sample >> 3); + this->waveBuf.looping = toloop; + this->waveBuf.status = NDSP_WBUF_FREE; +} + +Sound::~Sound() { + if (this->good) { + this->waveBuf.data_vaddr = 0; + this->waveBuf.nsamples = 0; + this->waveBuf.looping = false; + this->waveBuf.status = 0; + ndspChnWaveBufClear(this->chnl); + + if (this->data) linearFree(this->data); + } +} + +/* + Plays the sound. +*/ +void Sound::play() { + if (!this->data || !this->good) return; + + DSP_FlushDataCache(this->data, this->dataSize); + ndspChnWaveBufAdd(this->chnl, &this->waveBuf); +} + +/* + Stops the sound. +*/ +void Sound::stop() { + if (!this->data || !this->good) return; + + ndspChnWaveBufClear(this->chnl); +} \ No newline at end of file