mirror of
https://github.com/DarkStore-3DS/DarkStore.git
synced 2026-07-03 00:39:02 +00:00
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:
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-15
@@ -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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user