WIP: Argument | Shortcut support.

This commit is contained in:
StackZ 2020-11-16 13:28:45 +01:00
commit f08bed5813
15 changed files with 294 additions and 20 deletions

View file

@ -33,7 +33,7 @@
class Store {
public:
Store(const std::string &file);
Store(const std::string &file, const std::string &file2, bool ARGMode = false);
~Store();
void LoadFromFile(const std::string &file);
void loadSheets();
@ -80,6 +80,9 @@ public:
/* Both of these things are used for custom BG support. */
C2D_Image GetStoreImg() const { return this->storeBG; };
bool customBG() const { return this->hasCustomBG; };
/* Return filename of the UniStore. */
std::string GetFileName() const { return this->fileName; };
private:
void SetC2DBGImage();
nlohmann::json storeJson = nullptr;
@ -87,6 +90,7 @@ private:
C2D_Image storeBG = { nullptr };
bool valid = false, hasSheet = false, hasCustomBG = false;
int screenIndex = 0, entry = 0, box = 0, downEntry = 0, downIndex = 0;
std::string fileName = "";
};
#endif

View file

@ -0,0 +1,47 @@
/*
* This file is part of Universal-Updater
* Copyright (C) 2019-2020 Universal-Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <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_ARGUMENT_PARSER_HPP
#define _UNIVERSAL_UPDATER_ARGUMENT_PARSER_HPP
#include "json.hpp"
#include "store.hpp"
#include <string>
class ArgumentParser {
public:
ArgumentParser(const std::string &file, const std::string &entry, int dlIndex);
void Load();
void Execute();
bool GetValid() const { return this->isValid; };
private:
std::unique_ptr<Store> store = nullptr;
bool isValid = false;
std::string file = "", executeEntry = "", entry = "";
int dlIndex = -1, entryIndex = -1;
};
#endif

View file

@ -79,6 +79,10 @@ public:
/* If using custom Font. */
bool customfont() const { return this->v_customFont; };
void customfont(bool v) { this->v_customFont = v; if (!this->changesMade) this->changesMade = true; };
/* The shortcut path. */
std::string shortcut() const { return this->v_shortcutPath; };
void shortcut(const std::string &v) { this->v_shortcutPath = v; if (!this->changesMade) this->changesMade = true; };
private:
/* Mainly helper. */
bool getBool(const std::string &key);
@ -92,7 +96,8 @@ private:
bool changesMade = false;
std::string v_language = "en", v_lastStore = "universal-db.unistore",
v_3dsxPath = "sdmc:/3ds", v_ndsPath = "sdmc:", v_archivePath = "sdmc:";
v_3dsxPath = "sdmc:/3ds", v_ndsPath = "sdmc:", v_archivePath = "sdmc:",
v_shortcutPath = "sdmc:/3ds/Universal-Updater/shortcuts";
bool v_list = false, v_autoUpdate = true, v_metadata = true, v_updateCheck = true, v_showBg = false, v_customFont = false;
};

View file

@ -1,5 +1,6 @@
{
"ASCENDING": "Ascending",
"ARGUMENT_INVALID": "Argument invalid.\nPlease check the xml file for proper arguments.",
"AUTHOR": "Author",
"AUTO_UPDATE_SETTINGS": "Auto-Update Settings",
"AUTO_UPDATE_SETTINGS_BTN": "Auto-update settings...",
@ -13,6 +14,7 @@
"CHANGE_3DSX_PATH": "Change 3DSX path",
"CHANGE_ARCHIVE_PATH": "Change archive path",
"CHANGE_NDS_PATH": "Change NDS path",
"CHANGE_SHORTCUT_PATH": "Change shortcut path",
"CHECK_UNISTORE_UPDATES": "Checking for UniStore updates...",
"CHECK_UU_UPDATES": "Checking for Universal-Updater updates...",
"CONFIRM_OR_CANCEL": "Press \uE000 to confirm, \uE001 to cancel.",
@ -20,6 +22,7 @@
"CONSOLE": "Console",
"CONTRIBUTOR_TRANSLATORS": "- All Translators & Contributors",
"COPY_ERROR": "Copy Error!",
"CREATE_SHORTCUT": "Would you like to create a shortcut?",
"CREDITS": "Credits",
"CURRENT_VERSION": "Current version: ",
"CURRENTLY_EXTRACTING": "Currently extracting:\n",
@ -45,7 +48,10 @@
"DOWNLOADING_UNISTORE": "Downloading UniStore...",
"ENABLE_AUTOUPDATE_UNISTORE": "Enable auto-update UniStore on boot",
"ENABLE_UPDATE_CHECK": "Enable self-updating",
"ENTER_DESC_SHORTCUT": "Enter the shortcut description.",
"ENTER_SEARCH": "Enter what you like to search.",
"ENTER_SHORTCUT_FILENAME": "Enter the shortcut filename (without extension).",
"ENTER_TITLE_SHORTCUT": "Enter the shortcut title.",
"ENTER_URL": "Enter the URL of the UniStore.",
"ENTRIES": "Entries",
"EXECUTE_ENTRY": "Would you like to execute this entry?",
@ -83,6 +89,7 @@
"SELECT_UNISTORE_2": "Select a UniStore",
"SETTINGS": "Settings",
"SHEET_SLASH": "Seems like a '/' is included, which is not supported.\nPlease change 'sheet' to filename only.",
"SHORTCUT_CREATED": "Shortcut created!",
"SORT_BY": "Sort By",
"SORTING": "Sorting",
"START_SELECT": "Press START to select the current folder",

View file

@ -75,6 +75,7 @@ bool Msg::promptMsg(const std::string &promptMsg) {
Gui::clearTextBufs();
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
C2D_TargetClear(Top, TRANSPARENT);
C2D_TargetClear(Bottom, TRANSPARENT);
GFX::DrawTop();
Gui::Draw_Rect(0, 215, 400, 25, BAR_COLOR);
@ -82,6 +83,7 @@ bool Msg::promptMsg(const std::string &promptMsg) {
Gui::DrawStringCentered(0, (240 - Gui::GetStringHeight(0.6f, promptMsg)) / 2, 0.6f, TEXT_COLOR, promptMsg, 395, 0, font);
Gui::DrawStringCentered(0, 218, 0.6f, TEXT_COLOR, Lang::get("CONFIRM_OR_CANCEL"), 390, 0, font);
GFX::DrawBottom();
C3D_FrameEnd(0);
for (int i = 0; i < 3; i++) gspWaitForVBlank();

View file

@ -103,6 +103,7 @@ Result Init::Initialize() {
mkdir("sdmc:/3ds", 0777);
mkdir("sdmc:/3ds/Universal-Updater", 0777);
mkdir("sdmc:/3ds/Universal-Updater/stores", 0777);
mkdir("sdmc:/3ds/Universal-Updater/shortcuts", 0777);
config = std::make_unique<Config>();
Lang::load(config->language());

View file

@ -62,7 +62,7 @@ std::string Input::setkbdString(uint maxLength, const std::string &Text, const s
}
}
SwkbdButton ret = swkbdInputText(&state, temp, maxLength);
SwkbdButton ret = swkbdInputText(&state, temp, sizeof(temp));
temp[maxLength] = '\0';
return (ret == SWKBD_BUTTON_CONFIRM ? temp : "");

View file

@ -24,13 +24,69 @@
* reasonable ways as different from the original version.
*/
#include "argumentParser.hpp"
#include "common.hpp"
#include "init.hpp"
#include <dirent.h>
#include <string>
#define ARG_AMOUNT 4 // In case for more args, change this. It must be ARG amount + 1, because of 3DSX Path.
std::string _3dsxPath = "";
/*
ARG Init.
*/
static void InitForARG() {
gfxInitDefault();
romfsInit();
Gui::init();
amInit();
acInit();
/* Create Directories, if missing. */
mkdir("sdmc:/3ds", 0777);
mkdir("sdmc:/3ds/Universal-Updater", 0777);
mkdir("sdmc:/3ds/Universal-Updater/stores", 0777);
mkdir("sdmc:/3ds/Universal-Updater/shortcuts", 0777);
config = std::make_unique<Config>();
Lang::load(config->language());
Init::LoadFont();
osSetSpeedupEnable(true); // Enable speed-up for New 3DS users.
}
/*
ARG Exit.
*/
static Result ExitForARG() {
Gui::exit();
Init::UnloadFont();
gfxExit();
cfguExit();
acExit();
amExit();
romfsExit();
return 0;
}
int main(int argc, char *argv[]) {
if (argc > 0) _3dsxPath = argv[0];
/* 4 --> Argument mode. */
if (argc == ARG_AMOUNT) {
InitForARG();
const std::string file = argv[1];
const std::string entry = argv[2];
int dlIndex = atoi(argv[3]);
std::unique_ptr<ArgumentParser> arg = std::make_unique<ArgumentParser>(file, entry, dlIndex);
if (arg->GetValid()) arg->Execute(); // Execute, if valid.
else Msg::waitMsg(Lang::get("ARGUMENT_INVALID"));
return ExitForARG();
}
return Init::MainLoop();
}

View file

@ -318,7 +318,7 @@ void Overlays::SelectStore(std::unique_ptr<Store> &store, std::vector<std::uniqu
else if (info[selection].Version < 3) Msg::waitMsg(Lang::get("UNISTORE_TOO_OLD"));
else if (info[selection].Version > 3) Msg::waitMsg(Lang::get("UNISTORE_TOO_NEW"));
else {
store = std::make_unique<Store>(_STORE_PATH + info[selection].FileName);
store = std::make_unique<Store>(_STORE_PATH + info[selection].FileName, info[selection].FileName);
StoreUtils::ResetAll(store, meta, entries);
config->lastStore(info[selection].FileName);
StoreUtils::SortEntries(false, SortType::LAST_UPDATED, entries);
@ -340,7 +340,7 @@ void Overlays::SelectStore(std::unique_ptr<Store> &store, std::vector<std::uniqu
else if (info[i + sPos].Version < 3) Msg::waitMsg(Lang::get("UNISTORE_TOO_OLD"));
else if (info[i + sPos].Version > 3) Msg::waitMsg(Lang::get("UNISTORE_TOO_NEW"));
else {
store = std::make_unique<Store>(_STORE_PATH + info[i + sPos].FileName);
store = std::make_unique<Store>(_STORE_PATH + info[i + sPos].FileName, info[i + sPos].FileName);
StoreUtils::ResetAll(store, meta, entries);
config->lastStore(info[i + sPos].FileName);
doOut = true;

View file

@ -94,7 +94,7 @@ MainScreen::MainScreen() {
}
}
this->store = std::make_unique<Store>(_STORE_PATH + config->lastStore());
this->store = std::make_unique<Store>(_STORE_PATH + config->lastStore(), config->lastStore());
StoreUtils::ResetAll(this->store, this->meta, this->entries);
StoreUtils::SortEntries(false, SortType::LAST_UPDATED, this->entries);
};

View file

@ -24,11 +24,15 @@
* reasonable ways as different from the original version.
*/
#include "keyboard.hpp"
#include "scriptUtils.hpp"
#include "storeUtils.hpp"
#include "structs.hpp"
#include <fstream>
#define DOWNLOAD_ENTRIES 7
extern std::string _3dsxPath;
extern bool is3DSX;
extern bool touching(touchPosition touch, Structs::ButtonPos button);
static const std::vector<Structs::ButtonPos> downloadBoxes = {
{ 54, 32, 262, 22 },
@ -40,6 +44,45 @@ static const std::vector<Structs::ButtonPos> downloadBoxes = {
{ 54, 212, 262, 22 }
};
/*
With this, we can create a shortcut. ;P
const std::string &entryName: The name of the Entry. AKA: The Title Name.
int index: The Download index.
const std::string &unistoreName: The name of the UniStore filename.
const std::string &author: The author of the app.
*/
static void CreateShortcut(const std::string &entryName, int index, const std::string &unistoreName, const std::string &author) {
std::string sName = Input::setkbdString(30, Lang::get("ENTER_SHORTCUT_FILENAME"), {});
if (sName == "") sName = "tmp";
std::ofstream out(config->shortcut() + "/" + sName + ".xml", std::ios::binary);
out << "<shortcut>" << std::endl;
/* Executable. */
const std::string executable = _3dsxPath.substr(5, _3dsxPath.size()); // It must be '/3ds/...'.
out << " <executable>" << executable << "</executable>" << std::endl;
/* Arguments. */
out << " <arg>\"" << unistoreName << "\" \"" << entryName << "\" \"" << std::to_string(index) << "\"" << "</arg>" << std::endl;
/* Title. */
const std::string title = Input::setkbdString(30, Lang::get("ENTER_TITLE_SHORTCUT"), {});
if (title != "") out << " <name>" << title << "</name>" << std::endl;
else out << " <name>" << entryName << "</name>" << std::endl;
/* Description. */
const std::string desc = Input::setkbdString(50, Lang::get("ENTER_DESC_SHORTCUT"), {});
if (desc != "") out << " <description>" << desc << "</description>" << std::endl;
else out << " <description>" << entryName << "</description>" << std::endl;
/* Author and end. */
out << " <author>" << author << "</author>" << std::endl;
out << "</shortcut>" << std::endl;
out.close();
}
/*
Draw the Download Entries part.
@ -87,6 +130,15 @@ void StoreUtils::DownloadHandle(const std::unique_ptr<Store> &store, const std::
smallDelay--;
}
if ((hDown & KEY_Y) || (hDown & KEY_START)) {
if (is3DSX) { // Only allow if 3DSX.
if (Msg::promptMsg(Lang::get("CREATE_SHORTCUT"))) {
CreateShortcut(entry->GetTitle(), store->GetDownloadIndex(), store->GetFileName(), entry->GetAuthor());
Msg::waitMsg(Lang::get("SHORTCUT_CREATED"));
}
}
}
if (hRepeat & KEY_DOWN) {
if (entries.size() <= 0) return; // Smaller *than* 0 -> Invalid.
@ -122,9 +174,7 @@ void StoreUtils::DownloadHandle(const std::unique_ptr<Store> &store, const std::
for (int i = 0; i < DOWNLOAD_ENTRIES; i++) {
if (touching(touch, downloadBoxes[i])) {
if (i + store->GetDownloadSIndex() < (int)entries.size()) {
const std::string tmp = Lang::get("EXECUTE_ENTRY") + "\n\n" + entries[i + store->GetDownloadSIndex()];
if (Msg::promptMsg(tmp)) {
if (Msg::promptMsg(Lang::get("EXECUTE_ENTRY") + "\n\n" + entries[i + store->GetDownloadSIndex()])) {
ScriptUtils::runFunctions(store->GetJson(), entry->GetEntryIndex(), entries[i + store->GetDownloadSIndex()]);
if (meta) meta->SetUpdated(store->GetUniStoreTitle(), entry->GetTitle(), entry->GetLastUpdated());
entry->SetUpdateAvl(false);
@ -137,8 +187,7 @@ void StoreUtils::DownloadHandle(const std::unique_ptr<Store> &store, const std::
if (smallDelay == 0 && hDown & KEY_A) {
if (entries.size() <= 0) return; // Smaller *than* 0 -> Invalid.
const std::string tmp = Lang::get("EXECUTE_ENTRY") + "\n\n" + entries[store->GetDownloadIndex()];
if (Msg::promptMsg(tmp)) {
if (Msg::promptMsg(Lang::get("EXECUTE_ENTRY") + "\n\n" + entries[store->GetDownloadIndex()])) {
ScriptUtils::runFunctions(store->GetJson(), entry->GetEntryIndex(), entries[store->GetDownloadIndex()]);
if (meta) meta->SetUpdated(store->GetUniStoreTitle(), entry->GetTitle(), entry->GetLastUpdated());
entry->SetUpdateAvl(false);

View file

@ -63,7 +63,7 @@ static const Structs::ButtonPos back = { 52, 0, 24, 24 }; // Back arrow for dire
static const std::vector<std::string> mainStrings = { "LANGUAGE", "SELECT_UNISTORE", "AUTO_UPDATE_SETTINGS_BTN", "GUI_SETTINGS_BTN", "DIRECTORY_SETTINGS_BTN", "CREDITS", "EXIT_APP" };
static const std::vector<std::string> dirStrings = { "CHANGE_3DSX_PATH", "CHANGE_NDS_PATH", "CHANGE_ARCHIVE_PATH" };
static const std::vector<std::string> dirStrings = { "CHANGE_3DSX_PATH", "CHANGE_NDS_PATH", "CHANGE_ARCHIVE_PATH", "CHANGE_SHORTCUT_PATH" };
/* Note: Украïнська is spelled using a latin i with dieresis to work in the system font */
static const std::vector<std::string> languages = { "Bruh", "Dansk", "Deutsch", "English", "Español", "Français", "Italiano", "Lietuvių", "Magyar", "Polski", "Português", "Português (Brasil)", "Русский", "Украïнська", "日本語" };
@ -115,7 +115,7 @@ static void DrawSettingsDir(int selection) {
GFX::DrawSprite(sprites_arrow_idx, back.x, back.y);
Gui::DrawStringCentered(32, 2, 0.6, TEXT_COLOR, Lang::get("DIRECTORY_SETTINGS"), 240, 0, font);
for (int i = 0; i < 3; i++) {
for (int i = 0; i < 4; i++) {
if (i == selection) GFX::DrawBox(mainButtons[i].x, mainButtons[i].y, mainButtons[i].w, mainButtons[i].h, false);
Gui::DrawStringCentered(30, mainButtons[i].y + 4, 0.45f, TEXT_COLOR, Lang::get(dirStrings[i]), 255, 0, font);
}
@ -293,18 +293,18 @@ static void SettingsHandleDir(int &page, int &selection, const std::unique_ptr<S
}
if (hRepeat & KEY_DOWN) {
if (selection < 2) selection++;
if (selection < 3) selection++;
else selection = 0;
}
if (hRepeat & KEY_UP) {
if (selection > 0) selection--;
else selection = dirStrings.size()-1;
else selection = dirStrings.size() - 1;
}
if (hRepeat & KEY_RIGHT) {
if (selection + 8 < (int)dirStrings.size()-1) selection += 8;
else selection = dirStrings.size()-1;
if (selection + 8 < (int)dirStrings.size() - 1) selection += 8;
else selection = dirStrings.size() - 1;
}
if (hRepeat & KEY_LEFT) {
@ -328,6 +328,10 @@ static void SettingsHandleDir(int &page, int &selection, const std::unique_ptr<S
} else if (touching(touch, mainButtons[2])) {
const std::string path = Overlays::SelectDir(config->archPath(), Lang::get("SELECT_DIR"), store);
if (path != "") config->archPath(path);
} else if (touching(touch, mainButtons[3])) {
const std::string path = Overlays::SelectDir(config->shortcut(), Lang::get("SELECT_DIR"), store);
if (path != "") config->shortcut(path);
}
}
@ -349,6 +353,11 @@ static void SettingsHandleDir(int &page, int &selection, const std::unique_ptr<S
path = Overlays::SelectDir(config->archPath(), Lang::get("SELECT_DIR"), store);
if (path != "") config->archPath(path);
break;
case 3:
path = Overlays::SelectDir(config->shortcut(), Lang::get("SELECT_DIR"), store);
if (path != "") config->shortcut(path);
break;
}
}
}

View file

@ -39,10 +39,19 @@ static bool firstStart = true;
Initialize a store.
const std::string &file: The UniStore file.
const std::string &file2: The UniStore file.. without full path.
bool ARGMode: If Argument mode.
*/
Store::Store(const std::string &file) {
Store::Store(const std::string &file, const std::string &file2, bool ARGMode) {
this->fileName = file2;
if (!ARGMode) {
this->update(file);
this->SetC2DBGImage();
} else {
this->LoadFromFile(file);
}
};
/*

View file

@ -0,0 +1,83 @@
/*
* 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 "argumentParser.hpp"
#include "common.hpp"
#include "scriptUtils.hpp"
#include <unistd.h>
/*
The constructor of the Argument Parser.
const std::string &file: Const Reference to the file.
const std::string &entry: Const Reference to the Entry Title name.
int dlIndex: The Download index.
*/
ArgumentParser::ArgumentParser(const std::string &file, const std::string &entry, int dlIndex) {
if (dlIndex != -1 || file != "") {
this->file = file;
this->entry = entry;
this->dlIndex = dlIndex;
this->Load();
}
}
/*
Prepare UniStore and get valid state.
*/
void ArgumentParser::Load() {
if (access((std::string(_STORE_PATH) + this->file).c_str(), F_OK) != 0) return;
this->store = std::make_unique<Store>(_STORE_PATH + this->file, this->file, true);
if (!this->store->GetValid()) return;
for (int i = 0; i < this->store->GetStoreSize(); i++) {
if (this->store->GetTitleEntry(i) == this->entry) {
this->entryIndex = i;
const std::vector<std::string> dlList = this->store->GetDownloadList(this->entryIndex);
if (dlList.empty()) return;
if ((int)dlList.size() >= this->dlIndex) {
this->executeEntry = dlList[this->dlIndex];
this->isValid = true;
return;
}
}
}
}
/*
Execute the Argument's entry, if valid.
*/
void ArgumentParser::Execute() {
if (this->isValid) {
if (Msg::promptMsg(Lang::get("EXECUTE_ENTRY") + "\n\n" + this->executeEntry)) {
ScriptUtils::runFunctions(this->store->GetJson(), this->entryIndex, this->executeEntry);
}
}
}

View file

@ -124,6 +124,7 @@ Config::Config() {
if (this->json.contains("UpdateCheck")) this->updatecheck(this->getBool("UpdateCheck"));
if (this->json.contains("UseBG")) this->usebg(this->getBool("UseBG"));
if (this->json.contains("CustomFont")) this->customfont(this->getBool("CustomFont"));
if (this->json.contains("Shortcut_Path")) this->shortcut(this->getString("Shortcut_Path"));
this->changesMade = false; // No changes made yet.
}
@ -146,6 +147,7 @@ void Config::save() {
this->setBool("UpdateCheck", this->updatecheck());
this->setBool("UseBG", this->usebg());
this->setBool("CustomFont", this->customfont());
this->setString("Shortcut_Path", this->shortcut());
/* Write changes to file. */
const std::string dump = this->json.dump(1, '\t');