UniStore list and screenshots (#54)
* Initial push. * Fix png loading * Remove unneeded casts * Push my progress. * Improve screenshot display * Hopefully last commit here before merge? Co-authored-by: StackZ <47382115+SuperSaiyajinStackZ@users.noreply.github.com>
@@ -2,7 +2,7 @@ name: Build Universal-Updater
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore: [translation, full-rewrite]
|
||||
branches-ignore: [translation, full-rewrite, PNG]
|
||||
paths-ignore:
|
||||
- 'README.md'
|
||||
pull_request:
|
||||
|
||||
@@ -37,7 +37,7 @@ To build Universal-Updater from source, you will need to setup devkitARM with li
|
||||
|
||||
<details><summary>Screenshots</summary>
|
||||
|
||||
           
|
||||
           
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
@@ -68,8 +68,15 @@ public:
|
||||
void drawThread();
|
||||
void captureThread();
|
||||
void handler(std::vector<u8>& out);
|
||||
bool done() const { return finished; };
|
||||
bool cancelled() const { return cancel; };
|
||||
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<StoreList> stores = { };
|
||||
bool FromList = false;
|
||||
uint8_t timeout = 30;
|
||||
private:
|
||||
void buffToImage();
|
||||
void finish();
|
||||
@@ -83,15 +90,16 @@ private:
|
||||
std::atomic<bool> finished = false;
|
||||
bool capturing = false;
|
||||
bool cancel = false;
|
||||
bool mode = true; // True -> Camera, False -> URL.
|
||||
bool displayInfo = false;
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
This is, what should get called.
|
||||
*/
|
||||
namespace QR_Scanner {
|
||||
/* Empty == cancelled. */
|
||||
std::vector<u8> scan();
|
||||
std::string GetQRURL();
|
||||
std::string StoreHandle();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -40,6 +40,7 @@
|
||||
2: Search + Favorites.
|
||||
3: Sorting.
|
||||
4: Settings / Credits(?).
|
||||
5: Screenshot Menu.
|
||||
*/
|
||||
|
||||
class MainScreen : public Screen {
|
||||
@@ -52,13 +53,20 @@ private:
|
||||
std::unique_ptr<Meta> meta = nullptr;
|
||||
std::vector<std::unique_ptr<StoreEntry>> entries;
|
||||
std::vector<std::string> dwnldList, dwnldSizes;
|
||||
bool initialized = false, fetchDown = false, showMarks = false, showSettings = false, ascending = false, updateFilter = false;
|
||||
int storeMode = 0, marks = 0, markIndex = 0, sPage = 0, lMode = 0, sSelection = 0, lastMode = 0, smallDelay = 0, sPos = 0;
|
||||
|
||||
bool initialized = false, fetchDown = false, showMarks = false, showSettings = false,
|
||||
ascending = false, updateFilter = false, screenshotFetch = 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;
|
||||
|
||||
SortType sorttype = SortType::LAST_UPDATED;
|
||||
|
||||
/* Title, Author, Category, Console. */
|
||||
std::vector<bool> searchIncludes = { false, false, false, false };
|
||||
std::string searchResult = "";
|
||||
std::string searchResult = "", screenshotName = "";
|
||||
|
||||
C2D_Image Screenshot = { nullptr, nullptr };
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -55,6 +55,8 @@ public:
|
||||
std::string GetLicenseEntry(int index) const;
|
||||
C2D_Image GetIconEntry(int index) const;
|
||||
std::string GetFileSizes(int index, const std::string &entry) const;
|
||||
std::vector<std::string> GetScreenshotList(int index) const;
|
||||
std::vector<std::string> GetScreenshotNames(int index) const;
|
||||
|
||||
std::vector<std::string> GetDownloadList(int index) const;
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ public:
|
||||
std::vector<std::string> GetCategoryFull() const { return this->FullCategory; };
|
||||
std::vector<std::string> GetConsoleFull() const { return this->FullConsole; };
|
||||
std::vector<std::string> GetSizes() const { return this->Sizes; };
|
||||
std::vector<std::string> GetScreenshots() const { return this->Screenshots; };
|
||||
std::vector<std::string> GetScreenshotNames() const { return this->ScreenshotNames; };
|
||||
|
||||
bool GetUpdateAvl() const { return this->UpdateAvailable; };
|
||||
void SetUpdateAvl(bool v) { this->UpdateAvailable = v; };
|
||||
@@ -66,7 +68,7 @@ private:
|
||||
std::string Title, Author, Description, Category, Version, Console, LastUpdated, License, MarkString;
|
||||
C2D_Image Icon;
|
||||
int SheetIndex, EntryIndex, Marks;
|
||||
std::vector<std::string> FullCategory, FullConsole, Sizes;
|
||||
std::vector<std::string> FullCategory, FullConsole, Sizes, Screenshots, ScreenshotNames;
|
||||
bool UpdateAvailable;
|
||||
};
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace StoreUtils {
|
||||
|
||||
/* Entry Info. */
|
||||
void DrawEntryInfo(const std::unique_ptr<Store> &store, const std::unique_ptr<StoreEntry> &entry);
|
||||
void EntryHandle(bool &showMark, bool &fetch);
|
||||
void EntryHandle(bool &showMark, bool &fetch, bool &sFetch, int &mode);
|
||||
|
||||
/* Side Menu. */
|
||||
void DrawSideMenu(int currentMenu);
|
||||
@@ -70,6 +70,10 @@ namespace StoreUtils {
|
||||
/* Credits. */
|
||||
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);
|
||||
|
||||
/* Settings. */
|
||||
void DrawSettings(int page, int selection, int sPos);
|
||||
void SettingsHandle(int &page, bool &dspSettings, int &storeMode, int &selection, std::unique_ptr<Store> &store, std::vector<std::unique_ptr<StoreEntry>> &entries, std::unique_ptr<Meta> &meta, int &sPos);
|
||||
|
||||
@@ -41,6 +41,13 @@ enum DownloadError {
|
||||
DL_CANCEL, // No clue if that's needed tho.
|
||||
};
|
||||
|
||||
struct StoreList {
|
||||
std::string Title;
|
||||
std::string Author;
|
||||
std::string URL;
|
||||
std::string Description;
|
||||
};
|
||||
|
||||
Result downloadToFile(const std::string &url, const std::string &path);
|
||||
Result downloadFromRelease(const std::string &url, const std::string &asset, const std::string &path, bool includePrereleases);
|
||||
|
||||
@@ -70,5 +77,7 @@ bool DownloadUniStore(const std::string &URL, int currentRev, std::string &fl, b
|
||||
bool DownloadSpriteSheet(const std::string &URL, const std::string &file);
|
||||
bool IsUUUpdateAvailable();
|
||||
void UpdateAction();
|
||||
std::vector<StoreList> FetchStores();
|
||||
C2D_Image FetchScreenshot(const std::string &URL);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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_SCREENSHOT_HPP
|
||||
#define _UNIVERSAL_UPDATER_SCREENSHOT_HPP
|
||||
|
||||
#include <citro2d.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Screenshot {
|
||||
C2D_Image Convert(const std::string &filename);
|
||||
C2D_Image ConvertFromBuffer(const std::vector<u8> &buffer);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"Stack-Store": {
|
||||
"title": "Stack-Store",
|
||||
"author": "SuperSaiyajinStackZ",
|
||||
"url": "https://github.com/SuperSaiyajinStackZ/Stack-Store/raw/master/unistore/Stack-Store.unistore",
|
||||
"description": "Here you can find stuff, i am working on.\nFrom StackGames up to Save Editors and Utilities!\nEnjoy browsing through my UniStore called Stack-Store! ~SuperSaiyajinStackZ"
|
||||
},
|
||||
"TWiLight Menu++ Skins": {
|
||||
"title": "TWiLight Menu++ Skins",
|
||||
"author": "DS-Homebrew",
|
||||
"url": "https://github.com/DS-Homebrew/twlmenu-extras/raw/master/unistore/twlmenu-skins.unistore",
|
||||
"description": "A collection of skins for TWiLight Menu++\nfrom DS-Homebrew/twlmenu-extras on GitHub\n\n(The 'Console' is the theme in TWiLight)"
|
||||
},
|
||||
"3DEins-Sets": {
|
||||
"title": "3DEins-Sets",
|
||||
"author": "SuperSaiyajinStackZ",
|
||||
"url": "https://github.com/SuperSaiyajinStackZ/3DEins-3DZwei-Sets/raw/master/unistore/3DEins-Sets.unistore",
|
||||
"description": "You can find CardSets for 3DEins on this store.\nThis store is made by SuperSaiyajinStackZ.\nCardsets are hosted by SuperSaiyajinStackZ as well."
|
||||
},
|
||||
"3DZwei-Sets": {
|
||||
"title": "3DZwei-Sets",
|
||||
"author": "SuperSaiyajinStackZ",
|
||||
"url": "https://github.com/SuperSaiyajinStackZ/3DEins-3DZwei-Sets/raw/master/unistore/3DZwei-Sets.unistore",
|
||||
"description": "You can find CardSets for 3DZwei on this store.\nThis store is made by SuperSaiyajinStackZ.\nCardsets are hosted by SuperSaiyajinStackZ as well."
|
||||
},
|
||||
"Universal-DB": {
|
||||
"title": "Universal-DB",
|
||||
"author": "Universal-Team",
|
||||
"url": "https://github.com/Universal-Team/db/raw/master/unistore/universal-db.unistore",
|
||||
"description": "Universal-DB - An online database of 3DS and DS homebrew"
|
||||
},
|
||||
"LinuxCat's Store": {
|
||||
"title": "LinuxCat's Store",
|
||||
"author": "LinuxCat",
|
||||
"url": "https://github.com/L-i-n-u-x-C-a-t/LinuxCat-s-Store/raw/master/unistore/linuxcat-store.unistore",
|
||||
"description": "A store where everything is not made by me but most of it is."
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
"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",
|
||||
@@ -56,6 +57,7 @@
|
||||
"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...",
|
||||
"FILE_EXTRACTED": "file extracted.",
|
||||
"FILE_SLASH": "Seems like a '/' is included, which is not supported.\nPlease change 'file' to filename only.",
|
||||
@@ -80,8 +82,11 @@
|
||||
"NO": "No",
|
||||
"NO_DOWNLOADS_AVAILABLE": "No downloads available",
|
||||
"NO_LICENSE": "No License",
|
||||
"NO_SCREENSHOTS_AVAILABLE": "No Screenshots available",
|
||||
"NOT_IMPLEMENTED": "Not Implemented Yet",
|
||||
"REVISION": "Revision",
|
||||
"SCREENSHOT": "Screenshot %d / %d",
|
||||
"SCREENSHOT_INSTRUCTIONS": "Press to change and to zoom",
|
||||
"SEARCH_FILTERS": "Search and Filters",
|
||||
"SELECT_DIR": "Select a directory",
|
||||
"SELECT_LANG": "Choose the language",
|
||||
@@ -94,6 +99,7 @@
|
||||
"SORT_BY": "Sort By",
|
||||
"SORTING": "Sorting",
|
||||
"START_SELECT": "Press START to select the current folder",
|
||||
"STORE_INFO": "Store Info",
|
||||
"SYNTAX_ERROR": "Syntax Error!",
|
||||
"TITLE": "Title",
|
||||
"TOP_STYLE": "Top Style",
|
||||
|
||||
@@ -96,14 +96,12 @@ static void DeleteStore(const std::string &file) {
|
||||
|
||||
/*
|
||||
Download a Store.. including the SpriteSheets, if found.
|
||||
|
||||
bool Cam: if cam should be used.
|
||||
*/
|
||||
static bool DownloadStore(bool Cam = true) {
|
||||
static bool DownloadStore() {
|
||||
bool doSheet = false;
|
||||
std::string file = "";
|
||||
|
||||
const std::string URL = Cam ? QR_Scanner::GetQRURL() : Input::setkbdString(150, Lang::get("ENTER_URL"), { });
|
||||
const std::string URL = QR_Scanner::StoreHandle();
|
||||
if (URL != "") doSheet = DownloadUniStore(URL, -1, file, true);
|
||||
|
||||
if (doSheet) {
|
||||
@@ -149,6 +147,7 @@ static bool DownloadStore(bool Cam = true) {
|
||||
}
|
||||
}
|
||||
|
||||
hidScanInput(); // Re-Scan.
|
||||
return doSheet;
|
||||
}
|
||||
|
||||
@@ -382,21 +381,8 @@ void Overlays::SelectStore(std::unique_ptr<Store> &store, std::vector<std::uniqu
|
||||
else if (selection > sPos + 6 - 1) sPos = selection - 6 + 1;
|
||||
}
|
||||
|
||||
/* UniStore URL Download. */
|
||||
/* UniStore QR Code / URL Download. */
|
||||
if ((hidKeysDown() & KEY_Y) || (hidKeysDown() & KEY_TOUCH && touching(touch, mainButtons[8]))) {
|
||||
if (checkWifiStatus()) {
|
||||
if (DownloadStore(false)) {
|
||||
selection = 0;
|
||||
info = GetUniStoreInfo(_STORE_PATH);
|
||||
}
|
||||
|
||||
} else {
|
||||
notConnectedMsg();
|
||||
}
|
||||
}
|
||||
|
||||
/* UniStore QR Code Download. */
|
||||
if ((hidKeysDown() & KEY_SELECT) || (hidKeysDown() & KEY_TOUCH && touching(touch, mainButtons[9]))) {
|
||||
if (checkWifiStatus()) {
|
||||
if (DownloadStore()) {
|
||||
selection = 0;
|
||||
|
||||
@@ -50,9 +50,22 @@
|
||||
* reasonable ways as different from the original version.
|
||||
*/
|
||||
|
||||
#include "common.hpp"
|
||||
#include "download.hpp"
|
||||
#include "keyboard.hpp"
|
||||
#include "qrcode.hpp"
|
||||
#include <cstring>
|
||||
|
||||
static const std::vector<Structs::ButtonPos> mainButtons = {
|
||||
{ 10, 34, 300, 22 },
|
||||
{ 10, 64, 300, 22 },
|
||||
{ 10, 94, 300, 22 },
|
||||
{ 10, 124, 300, 22 },
|
||||
{ 10, 154, 300, 22 },
|
||||
{ 10, 184, 300, 22 }
|
||||
};
|
||||
extern bool touching(touchPosition touch, Structs::ButtonPos button);
|
||||
|
||||
/*
|
||||
Initialize everything needed for the camera.
|
||||
*/
|
||||
@@ -69,6 +82,8 @@ QRCode::QRCode() {
|
||||
LightLock_Init(&this->imageLock);
|
||||
svcCreateEvent(&this->exitEvent, RESET_STICKY);
|
||||
quirc_resize(this->qrData, 400, 240);
|
||||
|
||||
if (checkWifiStatus()) this->stores = FetchStores(); // Fetching Stores here.
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -125,10 +140,39 @@ void QRCode::drawThread() {
|
||||
C2D_TargetClear(Top, TRANSPARENT);
|
||||
C2D_TargetClear(Bottom, TRANSPARENT);
|
||||
|
||||
this->buffToImage(); // Fetch image.
|
||||
Gui::ScreenDraw(Top);
|
||||
C2D_DrawImageAt(this->image, 0, 0, 0.5, nullptr, 1.0f, 1.0f);
|
||||
if (!this->displayInfo) {
|
||||
this->buffToImage(); // Fetch image.
|
||||
Gui::ScreenDraw(Top);
|
||||
C2D_DrawImageAt(this->image, 0, 0, 0.5, nullptr, 1.0f, 1.0f);
|
||||
|
||||
} 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);
|
||||
|
||||
} 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("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);
|
||||
}
|
||||
|
||||
C3D_FrameEnd(0);
|
||||
}
|
||||
|
||||
@@ -236,80 +280,142 @@ void captureHelper(void *arg) {
|
||||
*/
|
||||
void QRCode::handler(std::vector<u8> &out) {
|
||||
hidScanInput();
|
||||
touchPosition t;
|
||||
hidTouchRead(&t);
|
||||
u32 keyDown = hidKeysDown();
|
||||
u32 keyRepeat = hidKeysDownRepeat();
|
||||
|
||||
if (hidKeysDown() & KEY_B) { // Cancel with B.
|
||||
if (keyDown & KEY_B) {
|
||||
this->cancel = true;
|
||||
this->finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->capturing) {
|
||||
/* create camera draw thread. */
|
||||
if (threadCreate((ThreadFunc)&captureHelper, this, 0x10000, 0x1A, 1, true) != NULL) capturing = true;
|
||||
else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
} 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;
|
||||
}
|
||||
|
||||
if (keyRepeat & KEY_UP) {
|
||||
if (this->selectedStore > 0) this->selectedStore--;
|
||||
else this->selectedStore = (int)this->stores.size() - 1;
|
||||
}
|
||||
|
||||
if (keyDown & KEY_A) {
|
||||
this->FromList = true;
|
||||
this->finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (keyDown & KEY_TOUCH) {
|
||||
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;
|
||||
this->finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->capturing) {
|
||||
/* create camera draw thread. */
|
||||
if (threadCreate((ThreadFunc)&captureHelper, this, 0x10000, 0x1A, 1, true) != NULL) this->capturing = true;
|
||||
else {
|
||||
this->finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->done()) return;
|
||||
|
||||
int w, h;
|
||||
u8 *image = (u8 *)quirc_begin(this->qrData, &w, &h);
|
||||
LightLock_Lock(&bufferLock);
|
||||
|
||||
for (ssize_t x = 0; x < w; x++) {
|
||||
for (ssize_t y = 0; y < h; y++) {
|
||||
u16 px = this->cameraBuffer[y * 400 + x];
|
||||
image[y * w + x] = (u8)(((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3);
|
||||
}
|
||||
}
|
||||
|
||||
LightLock_Unlock(&this->bufferLock);
|
||||
quirc_end(this->qrData);
|
||||
|
||||
if (quirc_count(this->qrData) > 0) {
|
||||
struct quirc_code code;
|
||||
struct quirc_data scan_data;
|
||||
quirc_extract(this->qrData, 0, &code);
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
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->done()) return;
|
||||
|
||||
int w, h;
|
||||
u8 *image = (u8 *)quirc_begin(this->qrData, &w, &h);
|
||||
LightLock_Lock(&bufferLock);
|
||||
|
||||
for (ssize_t x = 0; x < w; x++) {
|
||||
for (ssize_t y = 0; y < h; y++) {
|
||||
u16 px = this->cameraBuffer[y * 400 + x];
|
||||
image[y * w + x] = (u8)(((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3);
|
||||
}
|
||||
}
|
||||
|
||||
LightLock_Unlock(&this->bufferLock);
|
||||
quirc_end(this->qrData);
|
||||
|
||||
if (quirc_count(this->qrData) > 0) {
|
||||
struct quirc_code code;
|
||||
struct quirc_data scan_data;
|
||||
quirc_extract(this->qrData, 0, &code);
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
if (this->timeout > 0) this->timeout--;
|
||||
}
|
||||
|
||||
/*
|
||||
Return a vector of u8's from the QR Code.
|
||||
The Store Add QR Code handle and such.
|
||||
*/
|
||||
std::vector<u8> QR_Scanner::scan() {
|
||||
std::vector<u8> out = { };
|
||||
std::string QR_Scanner::StoreHandle() {
|
||||
std::vector<u8> data = { };
|
||||
|
||||
std::unique_ptr<QRCode> qrData = std::make_unique<QRCode>();
|
||||
aptSetHomeAllowed(false); // Block the Home key.
|
||||
|
||||
threadCreate((ThreadFunc)&drawHelper, qrData.get(), 0x10000, 0x1A, 1, true);
|
||||
while (!qrData->done()) qrData->handler(out); // Handle.
|
||||
|
||||
while (!qrData->done()) qrData->handler(data); // Handle.
|
||||
aptSetHomeAllowed(true); // Re-Allow it.
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
Return the URL from the QR Code.
|
||||
*/
|
||||
std::string QR_Scanner::GetQRURL() {
|
||||
std::vector<u8> qrData = QR_Scanner::scan();
|
||||
|
||||
if (qrData.empty()) return ""; // Because it is empty, return "".
|
||||
|
||||
if (qrData.back() == '\0') { // If Terminator, do -1.
|
||||
return std::string((char *)qrData.data(), qrData.size() - 1);
|
||||
|
||||
} else {
|
||||
return std::string((char *)qrData.data(), qrData.size());
|
||||
/* Selected from list. */
|
||||
if (qrData->FromList) {
|
||||
return qrData->stores[qrData->selectedStore].URL;
|
||||
}
|
||||
|
||||
return "";
|
||||
/* 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 "";
|
||||
}
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "download.hpp"
|
||||
#include "fileBrowse.hpp"
|
||||
#include "mainScreen.hpp"
|
||||
#include "screenshot.hpp"
|
||||
#include "storeUtils.hpp"
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -104,6 +105,12 @@ MainScreen::MainScreen() {
|
||||
MainScreen Main Draw.
|
||||
*/
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
Gui::ScreenDraw(Top);
|
||||
Gui::Draw_Rect(0, 0, 400, 25, BAR_COLOR);
|
||||
Gui::Draw_Rect(0, 25, 400, 1, BAR_OUTL_COLOR);
|
||||
@@ -152,6 +159,37 @@ void MainScreen::Draw(void) const {
|
||||
MainScreen Logic.
|
||||
*/
|
||||
void MainScreen::Logic(u32 hDown, u32 hHeld, touchPosition touch) {
|
||||
if (this->storeMode == 5) {
|
||||
if (this->screenshotFetch) {
|
||||
/* Delete Texture first. */
|
||||
if (this->Screenshot.tex) {
|
||||
C3D_TexDelete(this->Screenshot.tex);
|
||||
this->Screenshot.tex = nullptr;
|
||||
this->Screenshot.subtex = nullptr;
|
||||
}
|
||||
|
||||
this->screenshotName = "";
|
||||
|
||||
if (this->screenshotIndex < (int)this->entries[this->store->GetEntry()]->GetScreenshotNames().size()) {
|
||||
this->screenshotName = this->entries[this->store->GetEntry()]->GetScreenshotNames()[this->screenshotIndex];
|
||||
}
|
||||
|
||||
this->sSize = 0;
|
||||
this->sSize = this->entries[this->store->GetEntry()]->GetScreenshots().size();
|
||||
|
||||
if (this->screenshotIndex < this->sSize) {
|
||||
if (this->sSize > 0) {
|
||||
this->Screenshot = FetchScreenshot(this->entries[this->store->GetEntry()]->GetScreenshots()[this->screenshotIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
this->screenshotFetch = false;
|
||||
}
|
||||
|
||||
StoreUtils::ScreenshotMenu(this->Screenshot, this->screenshotIndex, this->screenshotFetch, this->storeMode, this->sSize, this->zoom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->showMarks) StoreUtils::MarkHandle(this->entries[this->store->GetEntry()], this->store, this->showMarks, this->meta);
|
||||
|
||||
if (!this->showMarks) {
|
||||
@@ -181,7 +219,7 @@ void MainScreen::Logic(u32 hDown, u32 hHeld, touchPosition touch) {
|
||||
|
||||
switch(this->storeMode) {
|
||||
case 0:
|
||||
if (this->store && this->store->GetValid()) StoreUtils::EntryHandle(this->showMarks, this->fetchDown);
|
||||
if (this->store && this->store->GetValid()) StoreUtils::EntryHandle(this->showMarks, this->fetchDown, this->screenshotFetch, this->storeMode);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
|
||||
@@ -127,7 +127,7 @@ void StoreUtils::DrawDownList(const std::unique_ptr<Store> &store, const std::ve
|
||||
Gui::DrawStringCentered(54 - 160 + (262 / 2), downloadBoxes[i].y + 4, 0.45f, TEXT_COLOR, entries[(i + store->GetDownloadSIndex())], 260, 0, font);
|
||||
}
|
||||
|
||||
GFX::DrawSprite(sprites_shortcut_idx, downloadBoxes[6].x, downloadBoxes[6].y);
|
||||
if (is3DSX) GFX::DrawSprite(sprites_shortcut_idx, downloadBoxes[6].x, downloadBoxes[6].y);
|
||||
|
||||
} else { // If no downloads available..
|
||||
Gui::DrawStringCentered(54 - 160 + (262 / 2), downloadBoxes[0].y + 4, 0.5f, TEXT_COLOR, Lang::get("NO_DOWNLOADS_AVAILABLE"), 255, 0, font);
|
||||
|
||||
@@ -75,6 +75,11 @@ void StoreUtils::DrawEntryInfo(const std::unique_ptr<Store> &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.
|
||||
*/
|
||||
void StoreUtils::EntryHandle(bool &showMark, bool &fetch) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 "storeUtils.hpp"
|
||||
#include "structs.hpp"
|
||||
|
||||
extern bool touching(touchPosition touch, Structs::ButtonPos button);
|
||||
|
||||
/*
|
||||
Draw the Screenshot menu.
|
||||
|
||||
const C2D_Image &img: The C2D_Image of the screenshot.
|
||||
const int sIndex: The Screenshot index.
|
||||
const bool sFetch: If fetching screenshots or not.
|
||||
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.
|
||||
*/
|
||||
void StoreUtils::DrawScreenshotMenu(const C2D_Image &img, const int sIndex, const bool sFetch, const int screenshotSize, const std::string &name, const int zoom) {
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Screenshot Menu handle.
|
||||
|
||||
C2D_Image &img: The C2D_Image of the screenshot.
|
||||
int &sIndex: The Screenshot index.
|
||||
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.
|
||||
*/
|
||||
void StoreUtils::ScreenshotMenu(C2D_Image &img, int &sIndex, bool &sFetch, int &storeMode, const int screenshotSize, int &zoom) {
|
||||
if (hDown & KEY_B) {
|
||||
zoom = false;
|
||||
sIndex = 0;
|
||||
storeMode = 0; // Go back to EntryInfo.
|
||||
}
|
||||
|
||||
if (hDown & KEY_RIGHT) {
|
||||
if (sIndex < screenshotSize - 1) {
|
||||
sIndex++;
|
||||
sFetch = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hDown & KEY_DOWN && zoom > 0) zoom--;
|
||||
|
||||
if (hDown & KEY_UP && zoom < 2) zoom++;
|
||||
|
||||
if (hDown & KEY_LEFT) {
|
||||
if (sIndex > 0) {
|
||||
sIndex--;
|
||||
sFetch = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,6 @@ static const std::vector<Structs::ButtonPos> langButtons = {
|
||||
};
|
||||
|
||||
static const std::vector<Structs::ButtonPos> toggleAbles = {
|
||||
{ 52, 6, 24, 24 }, // Back arrow.
|
||||
{ 288, 64, 24, 24 },
|
||||
{ 288, 140, 24, 24 }
|
||||
};
|
||||
@@ -125,11 +124,11 @@ static void DrawSettingsDir(int selection) {
|
||||
Draw Auto-Update Settings page.
|
||||
*/
|
||||
static void DrawAutoUpdate(int selection) {
|
||||
Gui::Draw_Rect(48, 0, 272, 36, ENTRY_BAR_COLOR);
|
||||
Gui::Draw_Rect(48, 36, 272, 1, ENTRY_BAR_OUTL_COLOR);
|
||||
GFX::DrawSprite(sprites_arrow_idx, 52, 6);
|
||||
Gui::Draw_Rect(48, 0, 272, 25, ENTRY_BAR_COLOR);
|
||||
Gui::Draw_Rect(48, 25, 272, 1, ENTRY_BAR_OUTL_COLOR);
|
||||
GFX::DrawSprite(sprites_arrow_idx, back.x, back.y);
|
||||
|
||||
Gui::DrawStringCentered(32, 7, 0.6, TEXT_COLOR, Lang::get("AUTO_UPDATE_SETTINGS"), 240, 0, font);
|
||||
Gui::DrawStringCentered(32, 2, 0.6, TEXT_COLOR, Lang::get("AUTO_UPDATE_SETTINGS"), 240, 0, font);
|
||||
|
||||
/* Toggle Boxes. */
|
||||
Gui::Draw_Rect(48, 64, 273, 24, (selection == 0 ? SIDEBAR_UNSELECTED_COLOR : BOX_INSIDE_COLOR));
|
||||
@@ -149,11 +148,11 @@ static void DrawAutoUpdate(int selection) {
|
||||
int selection: The Settings Selection.
|
||||
*/
|
||||
static void DrawGUISettings(int selection) {
|
||||
Gui::Draw_Rect(48, 0, 272, 36, ENTRY_BAR_COLOR);
|
||||
Gui::Draw_Rect(48, 36, 272, 1, ENTRY_BAR_OUTL_COLOR);
|
||||
GFX::DrawSprite(sprites_arrow_idx, 52, 6);
|
||||
Gui::Draw_Rect(48, 0, 272, 25, ENTRY_BAR_COLOR);
|
||||
Gui::Draw_Rect(48, 25, 272, 1, ENTRY_BAR_OUTL_COLOR);
|
||||
GFX::DrawSprite(sprites_arrow_idx, back.x, back.y);
|
||||
|
||||
Gui::DrawStringCentered(32, 7, 0.6, TEXT_COLOR, Lang::get("GUI_SETTINGS"), 240, 0, font);
|
||||
Gui::DrawStringCentered(32, 2, 0.6, TEXT_COLOR, Lang::get("GUI_SETTINGS"), 240, 0, font);
|
||||
|
||||
Gui::Draw_Rect(48, 64, 273, 24, (selection == 0 ? SIDEBAR_UNSELECTED_COLOR : BOX_INSIDE_COLOR));
|
||||
Gui::DrawString(55, 68, 0.5f, TEXT_COLOR, Lang::get("UNISTORE_BG"), 210, 0, font);
|
||||
@@ -388,14 +387,14 @@ static void AutoUpdateLogic(int &page, int &selection) {
|
||||
}
|
||||
|
||||
if (hDown & KEY_TOUCH) {
|
||||
if (touching(touch, toggleAbles[0])) {
|
||||
if (touching(touch, back)) {
|
||||
page = 0;
|
||||
selection = 2;
|
||||
|
||||
} else if (touching(touch, toggleAbles[1])) {
|
||||
} else if (touching(touch, toggleAbles[0])) {
|
||||
config->autoupdate(!config->autoupdate());
|
||||
|
||||
} else if (touching(touch, toggleAbles[2])) {
|
||||
} else if (touching(touch, toggleAbles[1])) {
|
||||
config->updatecheck(!config->updatecheck());
|
||||
}
|
||||
}
|
||||
@@ -438,14 +437,14 @@ static void GUISettingsLogic(int &page, int &selection) {
|
||||
}
|
||||
|
||||
if (hDown & KEY_TOUCH) {
|
||||
if (touching(touch, toggleAbles[0])) {
|
||||
if (touching(touch, back)) {
|
||||
page = 0;
|
||||
selection = 3;
|
||||
|
||||
} else if (touching(touch, toggleAbles[1])) {
|
||||
} else if (touching(touch, toggleAbles[0])) {
|
||||
config->usebg(!config->usebg());
|
||||
|
||||
} else if (touching(touch, toggleAbles[2])) {
|
||||
} else if (touching(touch, toggleAbles[1])) {
|
||||
config->customfont(!config->customfont());
|
||||
|
||||
(config->customfont() ? Init::LoadFont() : Init::UnloadFont());
|
||||
|
||||
@@ -477,6 +477,12 @@ std::vector<std::string> Store::GetDownloadList(int index) const {
|
||||
return temp;
|
||||
}
|
||||
|
||||
/*
|
||||
Get filesizes for each download entry.
|
||||
|
||||
int index: The index.
|
||||
const std::string &entry: The entry name.
|
||||
*/
|
||||
std::string Store::GetFileSizes(int index, const std::string &entry) const {
|
||||
if (!this->valid) return "";
|
||||
|
||||
@@ -489,4 +495,52 @@ std::string Store::GetFileSizes(int index, const std::string &entry) const {
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/*
|
||||
Get Screenshot URL list.
|
||||
|
||||
int index: The Entry Index.
|
||||
*/
|
||||
std::vector<std::string> Store::GetScreenshotList(int index) const {
|
||||
if (!this->valid) return { };
|
||||
|
||||
if (index > (int)this->storeJson["storeContent"].size() - 1) return { };
|
||||
|
||||
std::vector<std::string> screenshots;
|
||||
|
||||
if (this->storeJson["storeContent"][index]["info"].contains("screenshots")) {
|
||||
if (this->storeJson["storeContent"][index]["info"]["screenshots"].is_array()) {
|
||||
for(auto &item : this->storeJson["storeContent"][index]["info"]["screenshots"]) {
|
||||
if (item.is_object() && item.contains("url")) screenshots.push_back(item["url"]);
|
||||
else screenshots.push_back("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return screenshots;
|
||||
}
|
||||
|
||||
/*
|
||||
Get Screenshot names.
|
||||
|
||||
int index: The Entry Index.
|
||||
*/
|
||||
std::vector<std::string> Store::GetScreenshotNames(int index) const {
|
||||
if (!this->valid) return { };
|
||||
|
||||
if (index > (int)this->storeJson["storeContent"].size() - 1) return { };
|
||||
|
||||
std::vector<std::string> screenshotNames;
|
||||
|
||||
if (this->storeJson["storeContent"][index]["info"].contains("screenshots")) {
|
||||
if (this->storeJson["storeContent"][index]["info"]["screenshots"].is_array()) {
|
||||
for(auto &item : this->storeJson["storeContent"][index]["info"]["screenshots"]) {
|
||||
if (item.is_object() && item.contains("description")) screenshotNames.push_back(item["description"]);
|
||||
else screenshotNames.push_back("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return screenshotNames;
|
||||
}
|
||||
@@ -63,4 +63,7 @@ StoreEntry::StoreEntry(const std::unique_ptr<Store> &store, const std::unique_pt
|
||||
this->Sizes.push_back( store->GetFileSizes(index, entries[i]) );
|
||||
}
|
||||
}
|
||||
|
||||
this->Screenshots = store->GetScreenshotList(index);
|
||||
this->ScreenshotNames = store->GetScreenshotNames(index);
|
||||
}
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "files.hpp"
|
||||
#include "json.hpp"
|
||||
#include "lang.hpp"
|
||||
#include "screenshot.hpp"
|
||||
#include "scriptUtils.hpp"
|
||||
#include "stringutils.hpp"
|
||||
|
||||
@@ -846,4 +847,144 @@ void UpdateAction() {
|
||||
Msg::waitMsg(Lang::get("UPDATE_DONE"));
|
||||
exiting = true;
|
||||
}
|
||||
}
|
||||
|
||||
static StoreList fetch(const std::string &entry, nlohmann::json &js) {
|
||||
StoreList store = { "", "", "", "" };
|
||||
if (!js.contains(entry)) return store;
|
||||
|
||||
if (js[entry].contains("title") && js[entry]["title"].is_string()) store.Title = js[entry]["title"];
|
||||
if (js[entry].contains("author") && js[entry]["author"].is_string()) store.Author = js[entry]["author"];
|
||||
if (js[entry].contains("url") && js[entry]["url"].is_string()) store.URL = js[entry]["url"];
|
||||
if (js[entry].contains("description") && js[entry]["description"].is_string()) store.Description = js[entry]["description"];
|
||||
|
||||
return store;
|
||||
}
|
||||
/*
|
||||
Fetch Store list for available UniStores.
|
||||
*/
|
||||
std::vector<StoreList> FetchStores() {
|
||||
Msg::DisplayMsg(Lang::get("FETCHING_AVAILABLE_UNISTORES"));
|
||||
std::vector<StoreList> stores = { };
|
||||
|
||||
Result ret = 0;
|
||||
void *socubuf = memalign(0x1000, 0x100000);
|
||||
if (!socubuf) return stores;
|
||||
|
||||
ret = socInit((u32 *)socubuf, 0x100000);
|
||||
|
||||
if (R_FAILED(ret)) {
|
||||
free(socubuf);
|
||||
return stores;
|
||||
}
|
||||
|
||||
CURL *hnd = curl_easy_init();
|
||||
|
||||
ret = setupContext(hnd, "https://github.com/Universal-Team/Universal-Updater/raw/master/resources/UniStores.json");
|
||||
if (ret != 0) {
|
||||
socExit();
|
||||
free(result_buf);
|
||||
free(socubuf);
|
||||
result_buf = nullptr;
|
||||
result_sz = 0;
|
||||
result_written = 0;
|
||||
return stores;
|
||||
}
|
||||
|
||||
CURLcode cres = curl_easy_perform(hnd);
|
||||
curl_easy_cleanup(hnd);
|
||||
char *newbuf = (char *)realloc(result_buf, result_written + 1);
|
||||
result_buf = newbuf;
|
||||
result_buf[result_written] = 0; // nullbyte to end it as a proper C style string.
|
||||
|
||||
if (cres != CURLE_OK) {
|
||||
printf("Error in:\ncurl\n");
|
||||
socExit();
|
||||
free(result_buf);
|
||||
free(socubuf);
|
||||
result_buf = nullptr;
|
||||
result_sz = 0;
|
||||
result_written = 0;
|
||||
return stores;
|
||||
}
|
||||
|
||||
if (nlohmann::json::accept(result_buf)) {
|
||||
nlohmann::json parsedAPI = nlohmann::json::parse(result_buf);
|
||||
|
||||
for(auto it = parsedAPI.begin(); it != parsedAPI.end(); ++it) {
|
||||
stores.push_back( fetch(it.key(), parsedAPI) );
|
||||
}
|
||||
}
|
||||
|
||||
socExit();
|
||||
free(result_buf);
|
||||
free(socubuf);
|
||||
result_buf = nullptr;
|
||||
result_sz = 0;
|
||||
result_written = 0;
|
||||
|
||||
return stores;
|
||||
}
|
||||
|
||||
C2D_Image FetchScreenshot(const std::string &URL) {
|
||||
if (URL == "") return { };
|
||||
|
||||
C2D_Image img = { };
|
||||
|
||||
Result ret = 0;
|
||||
void *socubuf = memalign(0x1000, 0x100000);
|
||||
if (!socubuf) return img;
|
||||
|
||||
ret = socInit((u32 *)socubuf, 0x100000);
|
||||
|
||||
if (R_FAILED(ret)) {
|
||||
free(socubuf);
|
||||
return img;
|
||||
}
|
||||
|
||||
CURL *hnd = curl_easy_init();
|
||||
|
||||
ret = setupContext(hnd, URL.c_str());
|
||||
if (ret != 0) {
|
||||
socExit();
|
||||
free(result_buf);
|
||||
free(socubuf);
|
||||
result_buf = nullptr;
|
||||
result_sz = 0;
|
||||
result_written = 0;
|
||||
return img;
|
||||
}
|
||||
|
||||
CURLcode cres = curl_easy_perform(hnd);
|
||||
curl_easy_cleanup(hnd);
|
||||
char *newbuf = (char *)realloc(result_buf, result_written + 1);
|
||||
result_buf = newbuf;
|
||||
result_buf[result_written] = 0; // nullbyte to end it as a proper C style string.
|
||||
|
||||
if (cres != CURLE_OK) {
|
||||
printf("Error in:\ncurl\n");
|
||||
socExit();
|
||||
free(result_buf);
|
||||
free(socubuf);
|
||||
result_buf = nullptr;
|
||||
result_sz = 0;
|
||||
result_written = 0;
|
||||
return img;
|
||||
}
|
||||
|
||||
std::vector<u8> buffer;
|
||||
for (int i = 0; i < (int)result_written; i++) {
|
||||
buffer.push_back( result_buf[i] );
|
||||
}
|
||||
|
||||
img = Screenshot::ConvertFromBuffer(buffer);
|
||||
|
||||
socExit();
|
||||
free(result_buf);
|
||||
free(socubuf);
|
||||
result_buf = nullptr;
|
||||
result_sz = 0;
|
||||
result_written = 0;
|
||||
|
||||
return img;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 "lodepng.h"
|
||||
#include "msg.hpp"
|
||||
#include "screenshot.hpp"
|
||||
|
||||
C2D_Image Screenshot::Convert(const std::string &filename) {
|
||||
std::vector<u8> ImageBuffer;
|
||||
unsigned width, height;
|
||||
C2D_Image img;
|
||||
lodepng::decode(ImageBuffer, width, height, filename.c_str());
|
||||
|
||||
img.tex = new C3D_Tex;
|
||||
img.subtex = new Tex3DS_SubTexture({(u16)width, (u16)height, 0.0f, 1.0f, width / 512.0f, 1.0f - (height / 512.0f)});
|
||||
|
||||
C3D_TexInit(img.tex, 512, 512, GPU_RGBA8);
|
||||
C3D_TexSetFilter(img.tex, GPU_LINEAR, GPU_LINEAR);
|
||||
img.tex->border = 0xFFFFFFFF;
|
||||
C3D_TexSetWrap(img.tex, GPU_CLAMP_TO_BORDER, GPU_CLAMP_TO_BORDER);
|
||||
|
||||
for (u32 x = 0; x < width && x < 512; x++) {
|
||||
for (u32 y = 0; y < height && y < 512; y++) {
|
||||
const u32 dstPos = ((((y >> 3) * (512 >> 3) + (x >> 3)) << 6) +
|
||||
((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) |
|
||||
((x & 4) << 2) | ((y & 4) << 3))) * 4;
|
||||
|
||||
const u32 srcPos = (y * width + x) * 4;
|
||||
((uint8_t *)img.tex->data)[dstPos + 1] = ImageBuffer.data()[srcPos + 2];
|
||||
((uint8_t *)img.tex->data)[dstPos + 2] = ImageBuffer.data()[srcPos + 1];
|
||||
((uint8_t *)img.tex->data)[dstPos + 3] = ImageBuffer.data()[srcPos + 0];
|
||||
((uint8_t *)img.tex->data)[dstPos + 4] = ImageBuffer.data()[srcPos + 3];
|
||||
}
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
C2D_Image Screenshot::ConvertFromBuffer(const std::vector<u8> &buffer) {
|
||||
std::vector<u8> ImageBuffer;
|
||||
unsigned width, height;
|
||||
C2D_Image img;
|
||||
lodepng::decode(ImageBuffer, width, height, buffer);
|
||||
|
||||
img.tex = new C3D_Tex;
|
||||
img.subtex = new Tex3DS_SubTexture({(u16)width, (u16)height, 0.0f, 1.0f, width / 512.0f, 1.0f - (height / 512.0f)});
|
||||
|
||||
C3D_TexInit(img.tex, 512, 512, GPU_RGBA8);
|
||||
C3D_TexSetFilter(img.tex, GPU_LINEAR, GPU_LINEAR);
|
||||
img.tex->border = 0xFFFFFFFF;
|
||||
C3D_TexSetWrap(img.tex, GPU_CLAMP_TO_BORDER, GPU_CLAMP_TO_BORDER);
|
||||
|
||||
for (u32 x = 0; x < width && x < 512; x++) {
|
||||
for (u32 y = 0; y < height && y < 512; y++) {
|
||||
const u32 dstPos = ((((y >> 3) * (512 >> 3) + (x >> 3)) << 6) +
|
||||
((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) |
|
||||
((x & 4) << 2) | ((y & 4) << 3))) * 4;
|
||||
|
||||
const u32 srcPos = (y * width + x) * 4;
|
||||
((uint8_t *)img.tex->data)[dstPos + 1] = ImageBuffer.data()[srcPos + 2];
|
||||
((uint8_t *)img.tex->data)[dstPos + 2] = ImageBuffer.data()[srcPos + 1];
|
||||
((uint8_t *)img.tex->data)[dstPos + 3] = ImageBuffer.data()[srcPos + 0];
|
||||
((uint8_t *)img.tex->data)[dstPos + 4] = ImageBuffer.data()[srcPos + 3];
|
||||
}
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||