diff --git a/.gitignore b/.gitignore index 8513267..f938e9a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -.vscode/ +#.vscode/ build/ screenshots/ switch/ diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..04f3533 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,24 @@ +{ + "configurations": [ + { + "name": "DKP Aarch64", + "includePath": [ + "${workspaceFolder}/**", + "${DEVKITPRO}/devkitA64/include/**", + "${DEVKITPRO}/devkitA64/aarch64-none-elf/include/**", + "${DEVKITPRO}/libnx/include/**", + "${DEVKITPRO}/portlibs/switch/include/**" + ], + "defines": [ + "DEBUG", + "SWITCH", + "__SWITCH__" + ], + "compilerPath": "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-g++", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..d2e487b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,74 @@ +{ + "files.associations": { + "sstream": "cpp", + "format": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "codecvt": "cpp", + "compare": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "forward_list": "cpp", + "list": "cpp", + "map": "cpp", + "set": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "regex": "cpp", + "string": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "ostream": "cpp", + "ranges": "cpp", + "shared_mutex": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "thread": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp", + "valarray": "cpp", + "buffer": "cpp", + "internet": "cpp", + "socket": "cpp", + "variant": "cpp" + } +} \ No newline at end of file diff --git a/Makefile b/Makefile index b7b8e70..6cf751e 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,7 @@ DATA := data INCLUDES := include lib/zipper/include APP_TITLE := All-in-One Switch Updater APP_AUTHOR := HamletDuFromage -APP_VERSION := 1.3.2 +APP_VERSION := 1.4.0 TARGET := $(notdir $(CURDIR))-v$(APP_VERSION) ROMFS := resources diff --git a/README.md b/README.md index bc6bf30..d7ce974 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,8 @@ Downloads and extracts daily-updated cheat code. The program will only extract c - Change software color scheme of Joy-Cons. Additional color profiles can be found in the releases and should be copied to `config/aio-switch-updater/jc_profiles.json`. - Change software color scheme of Pro controllers (has to be paired as Player 1). Additional color profiles can be found in the releases and should be copied to `config/aio-switch-updater/pc_profiles.json`. - View which of your games got cheat codes from the app. +- Launch the Switch's web browser. +- Edit internet settings (DNS, IP address, MTU, etc). Add you own configs to `config/aio-switch-updater/internet.json`. You can find a template in the root of the repo. ## Screenshots ![ss](https://user-images.githubusercontent.com/61667930/93691403-30fb2e80-fad4-11ea-9701-7992a1de53e0.jpg) diff --git a/include/constants.hpp b/include/constants.hpp index e0e4883..025cd89 100644 --- a/include/constants.hpp +++ b/include/constants.hpp @@ -37,6 +37,7 @@ #define CHEATS_FILENAME "/config/aio-switch-updater/cheats.zip" #define CHEATS_EXCLUDE "/config/aio-switch-updater/exclude.txt" #define FILES_IGNORE "/config/aio-switch-updater/preserve.txt" +#define INTERNET_JSON "/config/aio-switch-updater/internet.json" #define UPDATED_TITLES_PATH "/config/aio-switch-updater/updated.dat" #define CHEATS_VERSION "/config/aio-switch-updater/cheats_version.dat" #define AMS_CONTENTS "/atmosphere/contents/" diff --git a/include/net_page.hpp b/include/net_page.hpp new file mode 100644 index 0000000..f74b9b5 --- /dev/null +++ b/include/net_page.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include "constants.hpp" +#include "main_frame.hpp" +#include +#include +#include +#include + +#define AF_INET 2 + +class NetPage : public brls::AppletFrame +{ + private: + brls::List* list; + brls::Label* label; + std::vector listItems; + brls::ListItem* cancel; + + public: + NetPage(); + std::string ipToString(u8* ip); + int stringToIp(std::string ip, u8* out); + +}; \ No newline at end of file diff --git a/include/tools_tab.hpp b/include/tools_tab.hpp index 2aca468..c9e8d7c 100644 --- a/include/tools_tab.hpp +++ b/include/tools_tab.hpp @@ -10,6 +10,7 @@ #include "language_option_page.hpp" #include "JC_page.hpp" #include "PC_page.hpp" +#include "net_page.hpp" #include "extract.hpp" #include "utils.hpp" //#include "ntcp.hpp" @@ -27,6 +28,8 @@ class ToolsTab : public brls::List brls::ListItem* language; brls::ListItem* cleanUp; brls::ListItem* ntcp; + brls::ListItem* netSettings; + brls::ListItem* browser; brls::StagedAppletFrame* stagedFrame; diff --git a/internet.json b/internet.json new file mode 100644 index 0000000..df3eb52 --- /dev/null +++ b/internet.json @@ -0,0 +1,14 @@ +[ + { + "ip_addr": "10.13.222.222", + "subnet_mask": "255.255.0.0", + "gateway": "10.13.37.1", + "mtu": 1500, + "name": "ACNH lan-play" + }, + { + "dns1": "1.1.1.1", + "dns2": "1.0.0.1", + "name": "Cloudfare DNS" + } +] \ No newline at end of file diff --git a/pc_profiles.json b/pc_profiles.json index 015ee2f..eaf884c 100644 --- a/pc_profiles.json +++ b/pc_profiles.json @@ -3,5 +3,15 @@ "BODY": "2d2d2d", "BTN": "e6e6e6", "name": "Default black" + }, + { + "BODY": "82FF96", + "BTN": "0A1E0A", + "name": "ACNH green" + }, + { + "BODY": "96F5F5", + "BTN": "0A1E28", + "name": "ACNH blue" } ] \ No newline at end of file diff --git a/resources/i18n/en-US/menus.json b/resources/i18n/en-US/menus.json index d8a775f..b75c079 100644 --- a/resources/i18n/en-US/menus.json +++ b/resources/i18n/en-US/menus.json @@ -6,7 +6,7 @@ "app_page.cpp":"", "app_title": "Installed cheats", - "app_label": "The following titles have recieved cheat code updates the last time you used the app. Please note that despite having been downloaded for a game, cheats may not match its current update.", + "app_label": "The following titles have received cheat code updates the last time you used the app. Please note that despite having been downloaded for a game, cheats may not match its current update.", "text_download": "Downloading:\nLatest cheat codes\n\nFrom:\n", "text_download_list": "Download latest cheat codes", "text_title": "Getting cheat codes", @@ -48,6 +48,8 @@ "v1_3_1_text": "\uE016 Small fixes for bugs causing hangs.", "v1_3_2": "v1.3.2", "v1_3_2_text": "\uE016 Added ability to change the internet color of Pro controllers.\n\uE016 Added the ability to preserve specific files when updating.\n\uE016 Added the ability to clean up downloaded archives.", + "v1_4_0": "v1.4.0", + "v1_4_0_text": "\uE016 Added possibility to use pre-set network settings. Add your own in `config/aio-switch-updater/internet.json.\n\uE016 Added web browser.", "Ok_button": "Ok", "cheats_page.cpp":"", @@ -99,6 +101,7 @@ "jc_change": "Changing color. Make sure the Joy-Con are docked. If the process hangs, try docking/undocking the JCs.", "jc_all_": "All done! You may need to dock/undock your Joy-Cons for the change to take effect.", + "pro_con": "Pro-Con color swapper", "pc_you_can" : "You can change the internal color of your Pro controller. Make sure it's set to Player 1.", "pc_color": "Pro controller color swapper", "pc_backing": "Backing up the color profile", @@ -162,6 +165,8 @@ "tool_all_done": " All done!", "tool_changelog": "Changelog", "tool_cleanUp": "Clean up downloaded files", + "tool_net_settings": "Edit internet settings", + "tool_browser": "Web Brower", "utils.cpp":"", "utils_because": "Because of the size of the FW archive, downloading firmwares in Applet Mode is not supported. Please launch the app with full RAM access.", @@ -177,7 +182,12 @@ "reboot_rcm" : "The Switch will now launch reboot to a special payload in order to finalise the install.", "hekate_dialogue" : "Do you want to also download Hekate?\nIf not, the Switch will now launch reboot to a special payload in order to finalise the install.", "Yes" : "Yes", - "No" : "No" + "No" : "No", + + "go_back" : "Go back", + "Confirm_button" : "Confirm", + "Cancel_button" : "Cancel" + } diff --git a/source/PC_page.cpp b/source/PC_page.cpp index 43b55e1..8074b06 100644 --- a/source/PC_page.cpp +++ b/source/PC_page.cpp @@ -4,7 +4,7 @@ namespace i18n = brls::i18n; using namespace i18n::literals; PCPage::PCPage() : AppletFrame(true, true) { - this->setTitle("menus/joy_con"_i18n ); + this->setTitle("menus/pro_con"_i18n ); list = new brls::List(); std::string labelText = "menus/pc_you_can"_i18n; label = new brls::Label(brls::LabelStyle::DESCRIPTION, labelText, true); diff --git a/source/changelog_page.cpp b/source/changelog_page.cpp index 4cd49a0..12d283b 100644 --- a/source/changelog_page.cpp +++ b/source/changelog_page.cpp @@ -58,6 +58,9 @@ ChangelogPage::ChangelogPage() : AppletFrame(true, true) verTitles.push_back("menus/v1_3_2"_i18n ); changes.push_back("menus/v1_3_2_text"_i18n ); + verTitles.push_back("menus/v1_4_0"_i18n ); + changes.push_back("menus/v1_4_0_text"_i18n ); + int nbVersions = verTitles.size(); items.reserve(nbVersions); for(int i = nbVersions -1 ; i >= 0; i--){ diff --git a/source/main.cpp b/source/main.cpp index b7e87c1..f5b7fe6 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,5 +1,5 @@ -#include -#include +//#include +//#include #include #include @@ -10,8 +10,8 @@ #include "utils.hpp" -#include -#include +//#include +//#include #include #include #include @@ -19,12 +19,12 @@ #include #include -#include +#include namespace i18n = brls::i18n; using namespace i18n::literals; -TimeServiceType __nx_time_service_type = TimeServiceType_System; +//TimeServiceType __nx_time_service_type = TimeServiceType_System; int main(int argc, char* argv[]) { diff --git a/source/main_frame.cpp b/source/main_frame.cpp index 4d7443e..946bbca 100644 --- a/source/main_frame.cpp +++ b/source/main_frame.cpp @@ -13,7 +13,7 @@ MainFrame::MainFrame() : TabFrame() this->addTab("menus/main_about"_i18n , new AboutTab()); - this->addSeparator(); + //this->addSeparator(); this->addTab("menus/main_update_ams"_i18n , new AmsTab()); this->addTab("menus/main_update_cfw"_i18n , new ListDownloadTab(cfw)); diff --git a/source/net_page.cpp b/source/net_page.cpp new file mode 100644 index 0000000..e8c9691 --- /dev/null +++ b/source/net_page.cpp @@ -0,0 +1,216 @@ +#include "net_page.hpp" +#include +#include + + + +namespace i18n = brls::i18n; +using namespace i18n::literals; +using json = nlohmann::json; + +NetPage::NetPage() : AppletFrame(true, true) +{ + this->setTitle("menus/net_settings"_i18n ); + + nifmInitialize(NifmServiceType_User); + NifmNetworkProfileData profile; + nifmGetCurrentNetworkProfile (&profile); + nifmExit(); + + int uuid = 0; + for (int j = 0; j < 16; j++) uuid += int(profile.uuid.uuid[j]); + + std::string labelText = ""; + if(uuid){ + labelText = "IP Adress: " + ipToString(profile.ip_setting_data.ip_address_setting.current_addr.addr) + +"\nSubnet Mask: " + ipToString(profile.ip_setting_data.ip_address_setting.subnet_mask.addr) + +"\nGateway: " + ipToString(profile.ip_setting_data.ip_address_setting.gateway.addr) + +"\nMTU: " + std::to_string(unsigned(profile.ip_setting_data.mtu)); + + if(profile.ip_setting_data.dns_setting.is_automatic){ + labelText += "\nDNS: Automatic"; + } + else{ + labelText += "\nPrimary DNS: " + ipToString(profile.ip_setting_data.dns_setting.primary_dns_server.addr) + +"\nSecondary DNS: "+ ipToString(profile.ip_setting_data.dns_setting.secondary_dns_server.addr); + } + } + else{ + labelText = "Please connect to internet to use this feature."; + } + + list = new brls::List(); + label = new brls::Label(brls::LabelStyle::DESCRIPTION, labelText, true); + list->addView(label); + + //ip_addr + //subnet_mask + //gateway + //dns1 + //dns2 + //mtu + //ip_auto + //dns_auto + + if(uuid){ + std::fstream profilesFile; + json profiles; + if(std::filesystem::exists(INTERNET_JSON)){ + profilesFile.open(INTERNET_JSON, std::fstream::in); + profilesFile >> profiles; + profilesFile.close(); + profiles.push_back(json::object({ + {"name", "90DNS (Europe)"}, + {"dns1", "163.172.141.219"}, + {"dns2", "207.246.121.77"} + })); + } + else{ + profiles = {{ + {"name", "90DNS (Europe)"}, + {"dns1", "163.172.141.219"}, + {"dns2", "207.246.121.77"} + }}; + } + + profiles.push_back(json::object({ + {"name", "90DNS (USA)"}, + {"dns1", "207.246.121.77"}, + {"dns2", "163.172.141.219"} + })); + + profiles.push_back(json::object({ + {"name", "Google DNS"}, + {"dns1", "8.8.8.8"}, + {"dns2", "8.8.4.4"} + })); + + profiles.push_back(json::object({ + {"name", "Automatic IP Address"}, + {"ip_auto", true} + })); + + profiles.push_back(json::object({ + {"name", "Automatic DNS"}, + {"dns_auto", true} + })); + + profiles.push_back(json::object({ + {"ip_addr", "10.13.111.111"}, + {"subnet_mask", "255.255.0.0"}, + {"gateway", "10.13.37.1"}, + {"mtu", 1500}, + {"name", "ACNH lan-play"} + })); + + int nbProfiles = profiles.size(); + listItems.reserve(nbProfiles); + + int iter = 0; + for (const auto& p : profiles.items()){ + json values = p.value(); + if(values.find("name") != values.end()) listItems[iter] = new brls::ListItem(values["name"]); + else listItems[iter] = new brls::ListItem("Unnamed"); + listItems[iter]->getClickEvent()->subscribe([&, values](brls::View* view){ + brls::Dialog* dialog = new brls::Dialog(values.dump(0).substr(1, values.dump(0).size() - 2)); + brls::GenericEvent::Callback callbackOk = [&, dialog, values](brls::View* view) { + nifmInitialize(NifmServiceType_Admin); + NifmNetworkProfileData profile; + nifmGetCurrentNetworkProfile (&profile); + unsigned char buf[sizeof(struct in6_addr)]; + if(values.find("ip_addr") != values.end()){ + if(inet_pton(AF_INET, std::string(values["ip_addr"]).c_str(), buf)){ + profile.ip_setting_data.ip_address_setting.is_automatic = u8(0); + //nifmSetNetworkProfile(&profile, &profile.uuid); + stringToIp(std::string(values["ip_addr"]), profile.ip_setting_data.ip_address_setting.current_addr.addr); + } + } + if(values.find("subnet_mask") != values.end()){ + if(inet_pton(AF_INET, std::string(values["subnet_mask"]).c_str(), buf)){ + stringToIp(std::string(values["subnet_mask"]), profile.ip_setting_data.ip_address_setting.subnet_mask.addr); + } + } + if(values.find("gateway") != values.end()){ + if(inet_pton(AF_INET, std::string(values["gateway"]).c_str(), buf)){ + stringToIp(std::string(values["gateway"]), profile.ip_setting_data.ip_address_setting.gateway.addr); + } + } + if(values.find("dns1") != values.end()){ + if(inet_pton(AF_INET, std::string(values["dns1"]).c_str(), buf)){ + profile.ip_setting_data.dns_setting.is_automatic = u8(0); + stringToIp(std::string(values["dns1"]), profile.ip_setting_data.dns_setting.primary_dns_server.addr); + //std::cout << unsigned(profile.ip_setting_data.dns_setting.primary_dns_server.addr[0]) << std::endl; + } + } + if(values.find("dns2") != values.end()){ + if(inet_pton(AF_INET, std::string(values["dns2"]).c_str(), buf)){ + profile.ip_setting_data.dns_setting.is_automatic = u8(0); + stringToIp(std::string(values["dns2"]), profile.ip_setting_data.dns_setting.secondary_dns_server.addr); + } + } + if(values.find("mtu") != values.end()){ + profile.ip_setting_data.mtu = u16(values["mtu"]); + /* try{ + u8 mtu = u8(std::stoi(std::string(values["mtu"]))); + profile.ip_setting_data.mtu = mtu; + } + catch(const std::invalid_argument& ia){} */ + } + if(values.find("ip_auto") != values.end()){ + profile.ip_setting_data.ip_address_setting.is_automatic = u8(values["ip_auto"]); + } + if(values.find("dns_auto") != values.end()){ + profile.ip_setting_data.dns_setting.is_automatic = u8(values["dns_auto"]); + } + + nifmSetNetworkProfile(&profile, &profile.uuid); + nifmSetWirelessCommunicationEnabled(true); + nifmExit(); + usleep(2500000); //Wait to avoid crashes when leaving too fast + dialog->close(); + //brls::Application::pushView(new NetPage()); + }; + brls::GenericEvent::Callback callbackNo = [dialog](brls::View* view) { + dialog->close(); + }; + dialog->addButton("menus/Confirm_button"_i18n , callbackOk); + dialog->addButton("menus/Cancel_button"_i18n , callbackNo); + dialog->setCancelable(false); + dialog->open(); + }); + list->addView(listItems[iter]); + iter++; + } + } + else{ + cancel = new brls::ListItem("menus/go_back"_i18n); + cancel->getClickEvent()->subscribe([&](brls::View* view){ + brls::Application::pushView(new MainFrame()); + }); + list->addView(cancel); + } + this->setContentView(list); +} + +std::string NetPage::ipToString(u8* ip){ + std::string res = ""; + for (size_t i = 0; i < 3; i++){ + res += std::to_string(unsigned(ip[i])); + res += "."; + } + res += std::to_string(unsigned(ip[4])); + return res; +} + +int NetPage::stringToIp(std::string ip, u8 *out){ + size_t start; + size_t end = 0; + int i = 0; + while ((start = ip.find_first_not_of(".", end)) != std::string::npos) + { + end = ip.find(".", start); + out[i] = u8(std::stoi(ip.substr(start, end - start))); + i++; + } + return 0; +} \ No newline at end of file diff --git a/source/tools_tab.cpp b/source/tools_tab.cpp index 227d327..a4979bc 100644 --- a/source/tools_tab.cpp +++ b/source/tools_tab.cpp @@ -54,6 +54,50 @@ ToolsTab::ToolsTab(std::string tag) : brls::List() ntcp->setHeight(LISTITEM_HEIGHT); this->addView(ntcp); */ + netSettings = new brls::ListItem("menus/tool_net_settings"_i18n ); + netSettings->getClickEvent()->subscribe([&](brls::View* view){ + brls::Application::pushView(new NetPage()); + }); + netSettings->setHeight(LISTITEM_HEIGHT); + this->addView(netSettings); + + browser = new brls::ListItem("menus/tool_browser"_i18n ); + browser->getClickEvent()->subscribe([&](brls::View* view){ + Result rc=0; + char url[0xc00] = {0}; + strcpy(url, "https://duckduckgo.com"); + int at = appletGetAppletType(); + std::string error = ""; + if(at == AppletType_Application) { // Running as a title + WebCommonConfig conf; + WebCommonReply out; + rc = webPageCreate(&conf, url); + if (R_FAILED(rc)) + error += "Error starting Browser\nLookup error code for more info " + rc; + webConfigSetJsExtension(&conf, true); + webConfigSetPageCache(&conf, true); + webConfigSetBootLoadingIcon(&conf, true); + webConfigSetWhitelist(&conf, ".*"); + rc = webConfigShow(&conf, &out); + if (R_FAILED(rc)) + error += "Error starting Browser\nLookup error code for more info " + rc; + } else { // Running under applet + error += "Running in applet mode\nPlease launch hbmenu by holding R on an APP (e.g. a game) NOT an applet (e.g. Gallery)"; + } + if(!error.empty()){ + brls::Dialog* dialog = new brls::Dialog(error); + brls::GenericEvent::Callback callback = [dialog](brls::View* view) { + dialog->close(); + }; + dialog->addButton("menus/Ok_button"_i18n , callback); + dialog->setCancelable(true); + dialog->open(); + } + + }); + browser->setHeight(LISTITEM_HEIGHT); + this->addView(browser); + cleanUp = new brls::ListItem("menus/tool_cleanUp"_i18n ); cleanUp->getClickEvent()->subscribe([&](brls::View* view){ std::filesystem::remove(AMS_ZIP_PATH); @@ -116,4 +160,5 @@ ToolsTab::ToolsTab(std::string tag) : brls::List() }); language->setHeight(LISTITEM_HEIGHT); this->addView(language); */ -} \ No newline at end of file +} +