#include <tesla.hpp> #include "gui_main.hpp" #include "gui_help.hpp" #include "value_list_item.hpp" #include "clickable_list_item.hpp" #include "taunt_toggles.hpp" static struct TrainingModpackMenu { int HITBOX_VIS = true; int DI_STATE = NONE; int LEFT_STICK = NONE; ActionFlags MASH_STATE = ActionFlags::None; ActionFlags FOLLOW_UP = ActionFlags::None; LedgeFlags LEDGE_STATE = LedgeFlags::All; TechFlags TECH_STATE = TechFlags::All; int SHIELD_STATE = NONE; DefensiveFlags DEFENSIVE_STATE = DefensiveFlags::All; int OOS_OFFSET = 0; int REACTION_TIME = 0; int MASH_IN_NEUTRAL = false; int FAST_FALL = false; int FAST_FALL_DELAY = 0; int FALLING_AERIALS = false; int FULL_HOP = false; } menu; static int FRAME_ADVANTAGE = 0; u64 pidSmash = 0; static const char* SYSTEM_SETTINGS_FILE = "/atmosphere/config/system_settings.ini"; static const char* TRAINING_MOD_LOG = "/TrainingModpack/training_modpack.log"; static const char* TRAINING_MOD_FRAME_ADV_LOG = "/TrainingModpack/training_modpack_frame_adv.log"; static const char* TRAINING_MOD_CONF = "/TrainingModpack/training_modpack_menu.conf"; static tsl::hlp::ini::IniData readSettings() { /* Open Sd card filesystem. */ FsFileSystem fsSdmc; if(R_FAILED(fsOpenSdCardFileSystem(&fsSdmc))) return {}; tsl::hlp::ScopeGuard fsGuard([&] { fsFsClose(&fsSdmc); }); /* Open config file. */ FsFile fileConfig; if(R_FAILED(fsFsOpenFile(&fsSdmc, SYSTEM_SETTINGS_FILE, FsOpenMode_Read, &fileConfig))) return {}; tsl::hlp::ScopeGuard fileGuard([&] { fsFileClose(&fileConfig); }); /* Get config file size. */ s64 configFileSize; if(R_FAILED(fsFileGetSize(&fileConfig, &configFileSize))) return {}; /* Read and parse config file. */ std::string configFileData(configFileSize, '\0'); u64 readSize; Result rc = fsFileRead(&fileConfig, 0, configFileData.data(), configFileSize, FsReadOption_None, &readSize); if(R_FAILED(rc) || readSize != static_cast<u64>(configFileSize)) return {}; return tsl::hlp::ini::parseIni(configFileData); } static void writeSettings(tsl::hlp::ini::IniData const& iniData) { /* Open Sd card filesystem. */ FsFileSystem fsSdmc; if(R_FAILED(fsOpenSdCardFileSystem(&fsSdmc))) return; tsl::hlp::ScopeGuard fsGuard([&] { fsFsClose(&fsSdmc); }); std::string iniString = tsl::hlp::ini::unparseIni(iniData); fsFsDeleteFile(&fsSdmc, SYSTEM_SETTINGS_FILE); fsFsCreateFile(&fsSdmc, SYSTEM_SETTINGS_FILE, iniString.length(), 0); /* Open config file. */ FsFile fileConfig; if(R_FAILED(fsFsOpenFile(&fsSdmc, SYSTEM_SETTINGS_FILE, FsOpenMode_Write, &fileConfig))) return; tsl::hlp::ScopeGuard fileGuard([&] { fsFileClose(&fileConfig); }); fsFileWrite(&fileConfig, 0, iniString.c_str(), iniString.length(), FsWriteOption_Flush); } static void updateSettings(tsl::hlp::ini::IniData const& changes) { tsl::hlp::ini::IniData iniData = readSettings(); for(auto& section : changes) { for(auto& keyValue : section.second) { iniData[section.first][keyValue.first] = keyValue.second; } } writeSettings(iniData); } GuiMain::GuiMain() { smInitialize(); pminfoInitialize(); pmbmInitialize(); smExit(); pmdmntGetProcessId(&pidSmash, 0x01006A800016E000); Result rc = fsOpenSdCardFileSystem(&this->m_fs); if(R_FAILED(rc)) return; FsFile menuFile; rc = fsFsOpenFile(&this->m_fs, TRAINING_MOD_CONF, FsOpenMode_Read, &menuFile); if(R_FAILED(rc)) return; u64 bytesRead; rc = fsFileRead(&menuFile, 0, static_cast<void*>(&menu), sizeof(menu), FsReadOption_None, &bytesRead); if(R_FAILED(rc)) { fsFileWrite(&menuFile, 0, static_cast<void*>(&menu), sizeof(menu), FsOpenMode_Write); } fsFileClose(&menuFile); } GuiMain::~GuiMain() { smInitialize(); pminfoExit(); pmbmExit(); smExit(); } static char FrameAdvantage[672]; class FrameAdvantageOverlayFrame : public tsl::elm::OverlayFrame { public: FrameAdvantageOverlayFrame(const std::string& title, const std::string& subtitle) : tsl::elm::OverlayFrame(title, subtitle) {} virtual void draw(tsl::gfx::Renderer* renderer) override { renderer->clearScreen(); renderer->drawRect(0, 0, tsl::cfg::FramebufferWidth, 85, a(tsl::style::color::ColorFrameBackground)); renderer->drawString(this->m_title.c_str(), false, 20, 50, 30, a(tsl::style::color::ColorText)); renderer->drawString(this->m_subtitle.c_str(), false, 20, 70, 15, a(tsl::style::color::ColorDescription)); if(this->m_contentElement != nullptr) this->m_contentElement->frame(renderer); } }; class GuiFrameAdvantage : public tsl::Gui { public: GuiFrameAdvantage() { tsl::hlp::requestForeground(false); smInitialize(); pminfoInitialize(); pmbmInitialize(); smExit(); pmdmntGetProcessId(&pidSmash, 0x01006A800016E000); Result rc = fsOpenSdCardFileSystem(&this->m_fs); if(R_FAILED(rc)) return; } ~GuiFrameAdvantage() { smInitialize(); pminfoExit(); pmbmExit(); smExit(); } virtual tsl::elm::Element* createUI() override { snprintf(FrameAdvantage, 256, "Frame Advantage: %d", FRAME_ADVANTAGE); auto rootFrame = new FrameAdvantageOverlayFrame(FrameAdvantage, "\uE0A2 + \uE07B Back"); this->rootFrame = rootFrame; return rootFrame; } virtual void update() override { static u32 counter = 0; if(counter++ % 10 != 0) return; Result rc; Handle debug; if(pidSmash != 0) { rc = svcDebugActiveProcess(&debug, pidSmash); if(R_SUCCEEDED(rc)) { u64 frame_adv_addr = 0; FsFile menuAddrFile; rc = fsFsOpenFile(&this->m_fs, TRAINING_MOD_FRAME_ADV_LOG, FsOpenMode_Read, &menuAddrFile); if(R_FAILED(rc)) { snprintf(FrameAdvantage, sizeof FrameAdvantage, "Failed to open file with error %d", rc); rootFrame->setTitle(FrameAdvantage); svcCloseHandle(debug); return; } char buffer[100]; u64 bytesRead; rc = fsFileRead(&menuAddrFile, 0, buffer, 100, FsReadOption_None, &bytesRead); if(R_FAILED(rc)) { snprintf(FrameAdvantage, sizeof FrameAdvantage, "Failed to read file with error %d", rc); rootFrame->setTitle(FrameAdvantage); svcCloseHandle(debug); return; } fsFileClose(&menuAddrFile); buffer[bytesRead] = '\0'; frame_adv_addr = strtoul(buffer, NULL, 16); if(frame_adv_addr != 0) { rc = svcReadDebugProcessMemory(&FRAME_ADVANTAGE, debug, frame_adv_addr, sizeof(int)); snprintf(FrameAdvantage, sizeof FrameAdvantage, "Frame Advantage: %d", FRAME_ADVANTAGE); rootFrame->setTitle(FrameAdvantage); } svcCloseHandle(debug); } } else { snprintf(FrameAdvantage, sizeof FrameAdvantage, "Smash is not running."); rootFrame->setTitle(FrameAdvantage); } } virtual bool handleInput(u64 keysDown, u64 keysHeld, touchPosition touchInput, JoystickPosition leftJoyStick, JoystickPosition rightJoyStick) override { if(keysHeld & KEY_DLEFT) { if(keysHeld & KEY_X) { tsl::goBack(); tsl::hlp::requestForeground(true); return true; } } // intercept B inputs if(keysDown & KEY_B) { return true; } return false; } FrameAdvantageOverlayFrame* rootFrame; FsFileSystem m_fs; }; namespace { template<typename T> tsl::elm::ListItem* createBitFlagOption(T* option, const std::string& name, const std::string& help) { using FlagType = typename T::Type; auto item = new tsl::elm::ListItem(name); item->setClickListener([name, help, option](u64 keys) -> bool { if(keys & KEY_A) { tsl::changeTo<GuiLambda>([option, name]() -> tsl::elm::Element* { auto toggleList = new tsl::elm::List(); std::vector<tsl::elm::ToggleListItem*> items; for(auto& [flag, str] : detail::EnumArray<FlagType>::values) { items.emplace_back(new BitFlagToggleListItem<FlagType>(str, flag, option)); } auto allOff = new SetToggleListItem({}, items, "None"); auto allOn = new SetToggleListItem(items, {}, "All"); toggleList->addItem(allOn); toggleList->addItem(allOff); for(auto it : items) { toggleList->addItem(it); } auto frame = new tsl::elm::OverlayFrame(name, ""); frame->setContent(toggleList); return frame; }); return true; } if(keys & KEY_Y) { tsl::changeTo<GuiHelp>(name, help); } return false; }); return item; } } // namespace tsl::elm::Element* GuiMain::createUI() { char buffer[256]; snprintf(buffer, 256, "Version %s", VERSION); tsl::elm::OverlayFrame* rootFrame = new tsl::elm::OverlayFrame("Training Modpack", buffer); auto list = new tsl::elm::List(); Result rc; Handle debug; tsl::hlp::ini::IniData iniData = readSettings(); bool ease_nro_restriction = false; for(auto& section : iniData) { for(auto& keyValue : section.second) { if(section.first == "ro") { if(keyValue.first == "ease_nro_restriction") { ease_nro_restriction = (readSettings()["ro"]["ease_nro_restriction"] == "u8!0x1"); } } } } if(!ease_nro_restriction) { tsl::elm::Element* iniShow = new tsl::elm::CustomDrawer([](tsl::gfx::Renderer* renderer, u16 x, u16 y, u16 w, u16 h) { renderer->drawString( "Your config file did not have the \nproper configuration to run the \nTraining Modpack.\n\n\nIt has been automatically \nupdated.\n- atmosphere\n---- config\n-------- system_settings.ini\n\n(enable ease_nro_restriction)\n\n\nPlease reboot your Switch.", false, 50, 225, 20, tsl::Color(255, 255, 255, 255)); }); updateSettings({{"ro", {{"ease_nro_restriction", "u8!0x1"}}}}); rootFrame->setContent(iniShow); return rootFrame; } if(pidSmash != 0) { rc = svcDebugActiveProcess(&debug, pidSmash); if(R_SUCCEEDED(rc)) { svcCloseHandle(debug); ClickableListItem* frameAdvantageItem = new ClickableListItem("Frame Advantage", frame_advantage_items, nullptr, "frameAdvantage", 0, "Frame Advantage", frame_advantage_help); frameAdvantageItem->setClickListener([](std::vector<std::string> values, int* curValue, std::string extdata, int index, std::string title, std::string help) { tsl::changeTo<GuiFrameAdvantage>(); }); frameAdvantageItem->setHelpListener( [](std::string title, std::string help) { tsl::changeTo<GuiHelp>(title, help); }); list->addItem(frameAdvantageItem); ValueListItem* hitboxItem = new ValueListItem("Hitbox Visualization", on_off, &menu.HITBOX_VIS, "hitbox", hitbox_help); list->addItem(hitboxItem); valueListItems.push_back(hitboxItem); ValueListItem* shieldItem = new ValueListItem("Shield Options", shield_items, &menu.SHIELD_STATE, "shield", shield_help); list->addItem(shieldItem); valueListItems.push_back(shieldItem); list->addItem(createBitFlagOption(&menu.MASH_STATE, "Mash Toggles", mash_help)); list->addItem(createBitFlagOption(&menu.FOLLOW_UP, "Followup Toggles", follow_up_help)); ValueListItem* mashNeutralItem = new ValueListItem("Mash In Neutral", on_off, &menu.MASH_IN_NEUTRAL, "mash_neutral", mash_neutral_help); list->addItem(mashNeutralItem); valueListItems.push_back(mashNeutralItem); list->addItem(createBitFlagOption(&menu.LEDGE_STATE, "Ledge Options", ledge_help)); list->addItem(createBitFlagOption(&menu.TECH_STATE, "Tech Options", tech_help)); list->addItem(createBitFlagOption(&menu.DEFENSIVE_STATE, "Defensive Options", defensive_help)); ValueListItem* diItem = new ValueListItem("Set DI", di_items, &menu.DI_STATE, "di", di_help); list->addItem(diItem); valueListItems.push_back(diItem); ValueListItem* leftStickItem = new ValueListItem("Left Stick", di_items, &menu.LEFT_STICK, "leftStick", left_stick_help); list->addItem(leftStickItem); valueListItems.push_back(leftStickItem); ValueListItem* oosOffsetItem = new ValueListItem("OOS Offset", number_list, &menu.OOS_OFFSET, "oos", oos_help); list->addItem(oosOffsetItem); valueListItems.push_back(oosOffsetItem); ValueListItem* reactionTime = new ValueListItem("Reaction Time", number_list_big, &menu.REACTION_TIME, "reaction_time", reaction_time_help); list->addItem(reactionTime); valueListItems.push_back(reactionTime); ValueListItem* fastFallItem = new ValueListItem("Fast Fall", on_off, &menu.FAST_FALL, "fast_fall", ""); list->addItem(fastFallItem); valueListItems.push_back(fastFallItem); ValueListItem* fastFallDelay = new ValueListItem("Fast Fall Delay", number_list_big, &menu.FAST_FALL_DELAY, "fast_fall", "In Frames"); list->addItem(fastFallDelay); valueListItems.push_back(fastFallDelay); ValueListItem* fallingAerialsItem = new ValueListItem("Falling Aerials", on_off, &menu.FALLING_AERIALS, "falling_aerials", ""); list->addItem(fallingAerialsItem); valueListItems.push_back(fallingAerialsItem); ValueListItem* fullHopItem = new ValueListItem("Full Hop", on_off, &menu.FULL_HOP, "full_hop", ""); list->addItem(fullHopItem); valueListItems.push_back(fullHopItem); ClickableListItem* saveStateItem = new ClickableListItem( "Save States", save_state_items, nullptr, "saveStates", 0, "Save States", save_states_help); saveStateItem->setClickListener([](std::vector<std::string> values, int* curValue, std::string extdata, int index, std::string title, std::string help) { tsl::changeTo<GuiHelp>(title, help); }); saveStateItem->setHelpListener([](std::string title, std::string help) { tsl::changeTo<GuiHelp>(title, help); }); list->addItem(saveStateItem); rootFrame->setContent(list); } else { tsl::elm::Element* warning = new tsl::elm::CustomDrawer([](tsl::gfx::Renderer* renderer, u16 x, u16 y, u16 w, u16 h) { renderer->drawString("\uE150", false, 180, 250, 90, tsl::Color(255, 255, 255, 255)); renderer->drawString("Could not debug process memory", false, 110, 340, 25, tsl::Color(255, 255, 255, 255)); }); rootFrame->setContent(warning); } } else { tsl::elm::Element* warning = new tsl::elm::CustomDrawer([](tsl::gfx::Renderer* renderer, u16 x, u16 y, u16 w, u16 h) { renderer->drawString("\uE150", false, 180, 250, 90, tsl::Color(255, 255, 255, 255)); renderer->drawString("Smash not running.", false, 110, 340, 25, tsl::Color(255, 255, 255, 255)); }); rootFrame->setContent(warning); } return rootFrame; } void GuiMain::update() { static u32 counter = 0; if(counter++ % 15 != 0) return; applyChanges(); } void GuiMain::applyChanges() { for(ValueListItem* item : valueListItems) { item->applyChanges(); } Result rc; Handle debug; if(pidSmash != 0) { rc = svcDebugActiveProcess(&debug, pidSmash); if(R_SUCCEEDED(rc)) { u64 menu_addr = 0; FsFile menuAddrFile; rc = fsFsOpenFile(&this->m_fs, TRAINING_MOD_LOG, FsOpenMode_Read, &menuAddrFile); if(R_FAILED(rc)) { svcCloseHandle(debug); return; } char buffer[100]; u64 bytesRead; rc = fsFileRead(&menuAddrFile, 0, buffer, 100, FsReadOption_None, &bytesRead); if(R_FAILED(rc)) { svcCloseHandle(debug); return; } fsFileClose(&menuAddrFile); buffer[bytesRead] = '\0'; menu_addr = strtoul(buffer, NULL, 16); if(menu_addr != 0) { rc = svcWriteDebugProcessMemory(debug, &menu, (u64)menu_addr, sizeof(menu)); } svcCloseHandle(debug); } } FsFile menuFile; fsFsCreateFile(&this->m_fs, TRAINING_MOD_CONF, sizeof(menu), 0); rc = fsFsOpenFile(&this->m_fs, TRAINING_MOD_CONF, FsOpenMode_Write, &menuFile); if(R_FAILED(rc)) { fsFileClose(&menuFile); return; } rc = fsFileWrite(&menuFile, 0, static_cast<void*>(&menu), sizeof(menu), FsOpenMode_Write); if(R_FAILED(rc)) { fsFileClose(&menuFile); return; } fsFileClose(&menuFile); }