diff --git a/app/banner.bin b/app/banner.bin new file mode 100644 index 0000000..3078df3 Binary files /dev/null and b/app/banner.bin differ diff --git a/app/build-cia.rsf b/app/build-cia.rsf index 07a34ee..0c462a3 100644 --- a/app/build-cia.rsf +++ b/app/build-cia.rsf @@ -1,6 +1,6 @@ BasicInfo: - Title : "LeafEdit" - ProductCode : "CTR-H-LEDT" + Title : "UNIV-UPDATER" + ProductCode : "CTR-H-UVUP" Logo : Homebrew # Nintendo / Licensed / Distributed / iQue / iQueForSystem RomFs: @@ -8,7 +8,7 @@ RomFs: TitleInfo: Category : Application - UniqueId : 0x43921 + UniqueId : 0x43917 Option: UseOnSD : true # true if App is to be installed to SD diff --git a/app/icon.bin b/app/icon.bin new file mode 100644 index 0000000..60e1367 Binary files /dev/null and b/app/icon.bin differ diff --git a/app/icon.png b/app/icon.png index 7acec0d..8ce27c0 100644 Binary files a/app/icon.png and b/app/icon.png differ diff --git a/include/gui.hpp b/include/gui.hpp index 2188e3a..7ef2dd7 100644 --- a/include/gui.hpp +++ b/include/gui.hpp @@ -38,9 +38,9 @@ #include #include -#define BarColor C2D_Color32(0, 102, 204, 255) -#define TopBGColor C2D_Color32(51, 153, 255, 255) -#define BottomBGColor C2D_Color32(0, 64, 128, 255) +#define BarColor C2D_Color32(57, 84, 114, 255) +#define TopBGColor C2D_Color32(96, 168, 192, 255) +#define BottomBGColor C2D_Color32(38, 44, 77, 255) #define BLACK C2D_Color32(0, 0, 0, 255) #define WHITE C2D_Color32(255, 255, 255, 255) #define TextColor C2D_Color32(102, 179, 255, 255) diff --git a/include/screens/mainMenu.hpp b/include/screens/mainMenu.hpp index ebac8e3..3b17df2 100644 --- a/include/screens/mainMenu.hpp +++ b/include/screens/mainMenu.hpp @@ -28,13 +28,19 @@ #include "screens/screen.hpp" +#include "utils/structs.hpp" + +#include class MainMenu : public Screen { public: void Draw(void) const override; void Logic(u32 hDown, u32 hHeld, touchPosition touch) override; - private: + std::vector mainButtons = { + {90, 40, 140, 35, -1}, // ScriptList. + {90, 160, 140, 35, -1}, // Settings? + }; }; #endif \ No newline at end of file diff --git a/include/screens/screenCommon.hpp b/include/screens/screenCommon.hpp index da3e3d5..6931d31 100644 --- a/include/screens/screenCommon.hpp +++ b/include/screens/screenCommon.hpp @@ -29,6 +29,8 @@ #include "gui.hpp" +#include "lang/lang.hpp" + extern C3D_RenderTarget* top; extern C3D_RenderTarget* bottom; diff --git a/include/screens/scriptlist.hpp b/include/screens/scriptlist.hpp new file mode 100644 index 0000000..2685949 --- /dev/null +++ b/include/screens/scriptlist.hpp @@ -0,0 +1,49 @@ +/* +* This file is part of Universal-Updater +* Copyright (C) 2019 VoltZ, Epicpkmn11, Flame, RocketRobz, TotallyNotGuy +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ +#ifndef SCRIPTLIST_HPP +#define SCRIPTLIST_HPP + +#include "screens/screen.hpp" +#include "screens/screenCommon.hpp" + +#include "utils/fileBrowse.h" + +class ScriptList : public Screen +{ +public: + void Draw(void) const override; + void Logic(u32 hDown, u32 hHeld, touchPosition touch) override; + ScriptList(); + +private: + std::vector dirContents; + uint selectedFile = 0; + int keyRepeatDelay = 0; + int fastMode = false; + bool dirChanged = true; +}; + +#endif \ No newline at end of file diff --git a/include/utils/common.hpp b/include/utils/common.hpp index 63760e7..e1b19bc 100644 --- a/include/utils/common.hpp +++ b/include/utils/common.hpp @@ -58,4 +58,5 @@ using json = nlohmann::json; extern char * arg0; -#define WORKING_DIR "/3ds/" \ No newline at end of file +#define WORKING_DIR "/3ds/" +#define SCRIPTS_PATH "/3ds/Universal-Updater/scripts/" // The Scripts will be here. \ No newline at end of file diff --git a/include/utils/parse.hpp b/include/utils/parse.hpp new file mode 100644 index 0000000..6944212 --- /dev/null +++ b/include/utils/parse.hpp @@ -0,0 +1,7 @@ +#ifndef PARSE_HPP +#define PARSE_HPP + +void parse(std::string fileName); +std::string get(nlohmann::json json, const std::string &key, const std::string &key2); + +#endif \ No newline at end of file diff --git a/include/utils/structs.hpp b/include/utils/structs.hpp new file mode 100644 index 0000000..bbf09e3 --- /dev/null +++ b/include/utils/structs.hpp @@ -0,0 +1,45 @@ +/* +* This file is part of Universal-Updater +* Copyright (C) 2019 VoltZ, Epicpkmn11, Flame, RocketRobz, TotallyNotGuy +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#ifndef STRUCTS_HPP +#define STRUCTS_HPP + +#include + +class Structs +{ +public: + struct ButtonPos { + int x; + int y; + int w; + int h; + int link; + }; +private: +}; + +#endif \ No newline at end of file diff --git a/romfs/lang/en/app.json b/romfs/lang/en/app.json index ccf88c2..cf235aa 100644 --- a/romfs/lang/en/app.json +++ b/romfs/lang/en/app.json @@ -12,5 +12,8 @@ "DOWNLOAD_NDSBOOTSTRAP_NIGHTLY": "Downloading nds-bootstrap...\n(Nightly)", "EXTRACT_NDSBOOTSTRAP_NIGHTLY": "Extracting nds-bootstrap...\n(Nightly)", "DOWNLOAD_NDSBOOTSTRAP_RELEASE": "Downloading nds-bootstrap...\n(Release)", - "EXTRACT_NDSBOOTSTRAP_RELEASE": "Extracting nds-bootstrap...\n(Release)" + "EXTRACT_NDSBOOTSTRAP_RELEASE": "Extracting nds-bootstrap...\n(Release)", + + "SCRIPTLIST": "Scriptlist", + "SETTINGS": "Settings" } diff --git a/source/main.cpp b/source/main.cpp index 6231d6d..8db30c2 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -31,13 +31,23 @@ #include "screens/mainMenu.hpp" #include "screens/screenCommon.hpp" +#include "utils/structs.hpp" + #include <3ds.h> -#include +#include bool exiting = false; touchPosition touch; +// If button Position pressed -> Do something. +bool touching(touchPosition touch, Structs::ButtonPos button) { + if (touch.px >= button.x && touch.px <= (button.x + button.w) && touch.py >= button.y && touch.py <= (button.y + button.h)) + return true; + else + return false; +} + int main() { gfxInitDefault(); @@ -46,6 +56,10 @@ int main() sdmcInit(); cfguInit(); Lang::load(1); + // Create Folder if missing. + mkdir("sdmc:/3ds", 0777); + mkdir("sdmc:/3ds/Universal-Updater", 0777); + mkdir("sdmc:/3ds/Universal-Updater/scripts", 0777); Gui::setScreen(std::make_unique()); osSetSpeedupEnable(true); // Enable speed-up for New 3DS users diff --git a/source/screens/mainMenu.cpp b/source/screens/mainMenu.cpp index be7e90d..ee27ae5 100644 --- a/source/screens/mainMenu.cpp +++ b/source/screens/mainMenu.cpp @@ -24,17 +24,23 @@ * reasonable ways as different from the original version. */ -#include "download.hpp" #include "screens/mainMenu.hpp" -#include "screens/screenCommon.hpp" +#include "screens/scriptlist.hpp" extern bool exiting; +extern bool touching(touchPosition touch, Structs::ButtonPos button); void MainMenu::Draw(void) const { Gui::DrawTop(); Gui::DrawStringCentered(0, 2, 0.7f, TextColor, "Universal-Updater", 400); Gui::DrawString(395-Gui::GetStringWidth(0.72f, VERSION_STRING), 218, 0.72f, WHITE, VERSION_STRING); Gui::DrawBottom(); + + // Draw 2 'Buttons'. + Gui::Draw_Rect(mainButtons[0].x, mainButtons[0].y, mainButtons[0].w, mainButtons[0].h, TopBGColor); + Gui::DrawString((320-Gui::GetStringWidth(0.6f, Lang::get("SCRIPTLIST")))/2, mainButtons[0].y+10, 0.6f, WHITE, Lang::get("SCRIPTLIST"), 140); + Gui::Draw_Rect(mainButtons[1].x, mainButtons[1].y, mainButtons[1].w, mainButtons[1].h, TopBGColor); + Gui::DrawString((320-Gui::GetStringWidth(0.6f, Lang::get("SETTINGS")))/2, mainButtons[1].y+10, 0.6f, WHITE, Lang::get("SETTINGS"), 140); } void MainMenu::Logic(u32 hDown, u32 hHeld, touchPosition touch) { @@ -42,7 +48,9 @@ void MainMenu::Logic(u32 hDown, u32 hHeld, touchPosition touch) { exiting = true; } - if (hDown & KEY_X) { - updateBootstrap(true); + if (hDown & KEY_TOUCH) { + if (touching(touch, mainButtons[0])) { + Gui::setScreen(std::make_unique()); + } } } \ No newline at end of file diff --git a/source/screens/scriptlist.cpp b/source/screens/scriptlist.cpp new file mode 100644 index 0000000..33aff38 --- /dev/null +++ b/source/screens/scriptlist.cpp @@ -0,0 +1,89 @@ +/* +* This file is part of Universal-Updater +* Copyright (C) 2019 VoltZ, Epicpkmn11, Flame, RocketRobz, TotallyNotGuy +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#include "download/download.hpp" + +#include "screens/mainMenu.hpp" +#include "screens/scriptlist.hpp" + +#include "utils/parse.hpp" + +#include +#include +#include + +#define ENTRIES_PER_SCREEN 3 + +struct Info { + std::string title; + std::string description; +}; + +Info parseInfo(std::string fileName) { + FILE* file = fopen(fileName.c_str(), "rt"); + if(!file) { + printf("File not found\n"); + fclose(file); + return {"", ""}; + } + nlohmann::json json = nlohmann::json::parse(file, nullptr, false); + fclose(file); + + Info info; + info.title = get(json, "info", "title"); + info.description = get(json, "info", "description"); + return info; +} + +std::vector fileInfo; + +ScriptList::ScriptList() { + dirContents.clear(); + chdir(SCRIPTS_PATH); + getDirectoryContents(dirContents); + for(uint i=0;i +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +int file_count = 0; + +extern bool continueNdsScan; +extern uint selectedFile; +extern int keyRepeatDelay; +extern bool dirChanged; +extern std::vector dirContents; + + +/** + * Get the title ID. + * @param ndsFile DS ROM image. + * @param buf Output buffer for title ID. (Must be at least 4 characters.) + * @return 0 on success; non-zero on error. + */ +int grabTID(FILE *ndsFile, char *buf) { + fseek(ndsFile, offsetof(sNDSHeadertitlecodeonly, gameCode), SEEK_SET); + size_t read = fread(buf, 1, 4, ndsFile); + return !(read == 4); +} + +void findNdsFiles(vector& dirContents) { + struct stat st; + DIR *pdir = opendir("."); + + if (pdir == NULL) { + DisplayMsg("Unable to open the directory."); + for(int i=0;i<120;i++) + gspWaitForVBlank(); + } else { + while (continueNdsScan) + { + DirEntry dirEntry; + + struct dirent* pent = readdir(pdir); + if (pent == NULL) break; + + stat(pent->d_name, &st); + dirEntry.name = pent->d_name; + char scanningMessage[512]; + snprintf(scanningMessage, sizeof(scanningMessage), "Scanning SD card for DS roms...\n\n(Press B to cancel)\n\n\n\n\n\n\n\n\n%s", dirEntry.name.c_str()); + DisplayMsg(scanningMessage); + dirEntry.isDirectory = (st.st_mode & S_IFDIR) ? true : false; + if(!(dirEntry.isDirectory) && dirEntry.name.length() >= 3) { + if (strcasecmp(dirEntry.name.substr(dirEntry.name.length()-3, 3).c_str(), "nds") == 0) { + // Get game's TID + FILE *f_nds_file = fopen(dirEntry.name.c_str(), "rb"); + // char game_TID[5]; + grabTID(f_nds_file, dirEntry.tid); + dirEntry.tid[4] = 0; + fclose(f_nds_file); + + // dirEntry.tid = game_TID; + + dirContents.push_back(dirEntry); + file_count++; + } + } else if (dirEntry.isDirectory + && dirEntry.name.compare(".") != 0 + && dirEntry.name.compare("_nds") != 0 + && dirEntry.name.compare("3ds") != 0 + && dirEntry.name.compare("DCIM") != 0 + && dirEntry.name.compare("gm9") != 0 + && dirEntry.name.compare("luma") != 0 + && dirEntry.name.compare("Nintendo 3DS") != 0 + && dirEntry.name.compare("private") != 0 + && dirEntry.name.compare("retroarch") != 0) { + chdir(dirEntry.name.c_str()); + findNdsFiles(dirContents); + chdir(".."); + } + } + closedir(pdir); + } +} + +off_t getFileSize(const char *fileName) +{ + FILE* fp = fopen(fileName, "rb"); + off_t fsize = 0; + if (fp) { + fseek(fp, 0, SEEK_END); + fsize = ftell(fp); // Get source file's size + fseek(fp, 0, SEEK_SET); + } + fclose(fp); + + return fsize; +} + +bool nameEndsWith(const std::string& name, const std::vector extensionList) { + if(name.substr(0, 2) == "._") return false; + + if(name.size() == 0) return false; + + if(extensionList.size() == 0) return true; + + for(int i = 0; i <(int)extensionList.size(); i++) { + const std::string ext = extensionList.at(i); + if(strcasecmp(name.c_str() + name.size() - ext.size(), ext.c_str()) == 0) return true; + } + return false; +} + +bool dirEntryPredicate(const DirEntry& lhs, const DirEntry& rhs) { + if(!lhs.isDirectory && rhs.isDirectory) { + return false; + } + if(lhs.isDirectory && !rhs.isDirectory) { + return true; + } + return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0; +} + +void getDirectoryContents(std::vector& dirContents, const std::vector extensionList) { + struct stat st; + + dirContents.clear(); + + DIR *pdir = opendir("."); + + if(pdir == NULL) { + DisplayMsg("Unable to open the directory."); + for(int i=0;i<120;i++) gspWaitForVBlank(); + } else { + while(true) { + DirEntry dirEntry; + + struct dirent* pent = readdir(pdir); + if(pent == NULL) break; + + stat(pent->d_name, &st); + dirEntry.name = pent->d_name; + dirEntry.isDirectory = (st.st_mode & S_IFDIR) ? true : false; + + if(dirEntry.name.compare(".") != 0 && (dirEntry.isDirectory || nameEndsWith(dirEntry.name, extensionList))) { + dirContents.push_back(dirEntry); + } + } + closedir(pdir); + } + sort(dirContents.begin(), dirContents.end(), dirEntryPredicate); +} + +void getDirectoryContents(std::vector& dirContents) { + getDirectoryContents(dirContents, {}); +} \ No newline at end of file diff --git a/source/utils/parse.cpp b/source/utils/parse.cpp new file mode 100644 index 0000000..3db0c28 --- /dev/null +++ b/source/utils/parse.cpp @@ -0,0 +1,70 @@ +#include +#include "json.hpp" + + +std::string get(nlohmann::json json, const std::string &key, const std::string &key2) { + if(!json.contains(key)) return "MISSING: " + key; + if(!json.at(key).is_object()) return "NOT OBJECT: " + key; + + if(!json.at(key).contains(key2)) return "MISSING: " + key + "." + key2; + if(!json.at(key).at(key2).is_string()) return "NOT STRING: " + key + "." + key2; + + return json.at(key).at(key2).get_ref(); +} + +void parse(std::string fileName) { + FILE* file = fopen(fileName.c_str(), "rt"); + if(!file) { + printf("File not found\n"); + fclose(file); + return; + } + nlohmann::json json = nlohmann::json::parse(file, nullptr, false); + fclose(file); + + std::string title = get(json, "info", "title"); + std::string description = get(json, "info", "description"); + + printf("%s\n", title.c_str()); + printf("%s\n", description.c_str()); + printf("------------------------------------------------------------------------------------------\n"); + + printf("Options:\n"); + for(auto it = json.begin();it != json.end(); it++) { + if(it.key() != "info") { + printf("> %s\n", it.key().c_str()); + } + } + printf("------------------------------------------------------------------------------------------\n"); + + char choise[16]; + while(!json.contains(choise)) { + printf("Choose an option: "); + scanf("%s", choise); + } + + printf("Parsing %s...\n", choise); + for(int i=0;i