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>
This commit is contained in:
Pk11
2020-12-02 14:23:15 -06:00
committed by GitHub
parent 2e2acf819e
commit 78d0dad604
38 changed files with 9213 additions and 106 deletions
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -37,7 +37,7 @@ To build Universal-Updater from source, you will need to setup devkitARM with li
<details><summary>Screenshots</summary>
![](https://github.com/Universal-Team/Universal-Updater/blob/master/screenshots/Credits.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/screenshots/DirectorySelection.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/screenshots/DownloadList.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/screenshots/EntryInfo.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/screenshots/LanguageSelection.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/screenshots/ListStyle.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/screenshots/MarkMenu.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/screenshots/SearchMenu.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/screenshots/SettingsMenu.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/screenshots/SortMenu.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/screenshots/StoreSelection.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/screenshots/AutoUpdateSettings.png)![](https://github.com/Universal-Team/Universal-Updater/blob/master/screenshots/DirectorySettings.png)![](https://github.com/Universal-Team/Universal-Updater/blob/master/screenshots/GUISettings.png)
![](https://github.com/Universal-Team/Universal-Updater/blob/master/resources/Credits.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/resources/DirectorySelection.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/resources/DownloadList.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/resources/EntryInfo.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/resources/LanguageSelection.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/resources/ListStyle.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/resources/MarkMenu.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/resources/SearchMenu.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/resources/SettingsMenu.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/resources/SortMenu.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/resources/StoreSelection.png) ![](https://github.com/Universal-Team/Universal-Updater/blob/master/resources/AutoUpdateSettings.png)![](https://github.com/Universal-Team/Universal-Updater/blob/master/resources/DirectorySettings.png)![](https://github.com/Universal-Team/Universal-Updater/blob/master/resources/GUISettings.png)
</details>
+13 -5
View File
@@ -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
+11 -3
View File
@@ -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
+2
View File
@@ -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;
+3 -1
View File
@@ -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;
};
+5 -1
View File
@@ -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);
+9
View File
@@ -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
File diff suppressed because it is too large Load Diff
+39
View File
@@ -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

+38
View File
@@ -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."
}
}
+6
View File
@@ -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",
+4 -18
View File
@@ -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;
+164 -58
View File
@@ -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 "";
}
+39 -1
View File
@@ -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:
+1 -1
View File
@@ -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);
+6 -1
View File
@@ -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;
}
}
+127
View File
@@ -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;
}
}
}
+14 -15
View File
@@ -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());
+54
View File
@@ -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;
}
+3
View File
@@ -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);
}
+141
View File
@@ -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;
}
File diff suppressed because it is too large Load Diff
+91
View File
@@ -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;
}