#include "config.h" #include #include #include #include #include "build/defs.h" #include "build/resource.h" #include "cfg/config.h" #include "cfg/configurator.h" #include "external/imgui/imgui_internal.h" #include "games/io.h" #include "avs/core.h" #include "avs/ea3.h" #include "avs/game.h" #include "launcher/launcher.h" #include "launcher/shutdown.h" #include "misc/eamuse.h" #include "overlay/imgui/extensions.h" #include "rawinput/piuio.h" #include "rawinput/rawinput.h" #include "rawinput/touch.h" #include "util/fileutils.h" #include "util/logging.h" #include "util/resutils.h" #include "util/time.h" #include "util/utils.h" #ifdef min #undef min #endif namespace overlay::windows { Config::Config(overlay::SpiceOverlay *overlay) : Window(overlay) { this->title = "Configuration"; this->toggle_button = games::OverlayButtons::ToggleConfig; this->init_size = ImVec2(800, 600); this->size_min = ImVec2(100, 200); this->init_pos = ImVec2(0, 0); if (cfg::CONFIGURATOR_STANDALONE && cfg::CONFIGURATOR_TYPE == cfg::ConfigType::Config) { this->active = true; this->flags |= ImGuiWindowFlags_NoResize; this->flags |= ImGuiWindowFlags_NoMove; this->flags |= ImGuiWindowFlags_NoTitleBar; this->flags |= ImGuiWindowFlags_NoCollapse; this->flags |= ImGuiWindowFlags_NoDecoration; } // build game list auto &game_names = games::get_games(); for (auto &game_name : game_names) { this->games_names.push_back(game_name.c_str()); auto &game = this->games_list.emplace_back(game_name); auto buttons = games::get_buttons(game_name); auto analogs = games::get_analogs(game_name); auto lights = games::get_lights(game_name); if (buttons) { for (auto &item : *buttons) { game.addItems(item); } } if (analogs) { for (auto &item : *analogs) { game.addItems(item); } } if (lights) { for (auto &item : *lights) { game.addItems(item); } } // default to currently running game if (!cfg::CONFIGURATOR_STANDALONE && game_name == eamuse_get_game()) { this->games_selected = games_list.size() - 1; this->games_selected_name = game_name; } // standalone configurator should look for file hints if (cfg::CONFIGURATOR_STANDALONE) { auto file_hints = games::get_game_file_hints(game_name); if (file_hints) { for (auto &file_hint : *file_hints) { if (fileutils::file_exists(file_hint) || fileutils::file_exists(std::filesystem::path("modules") / file_hint) || fileutils::file_exists(std::filesystem::path("contents") / file_hint) || fileutils::file_exists(MODULE_PATH / file_hint)) { this->games_selected = games_list.size() - 1; this->games_selected_name = game_name; eamuse_set_game(game_name); } } } } } // configurator fallback to detected game name if (cfg::CONFIGURATOR_STANDALONE && this->games_selected == -1) { for (size_t i = 0; i < games_names.size(); i++) { if (games_names[i] == eamuse_get_game()) { this->games_selected = i; } } } // add games to the config and window auto &config = ::Config::getInstance(); for (auto &game : games_list) { config.addGame(game); if (!config.getStatus()) { log_warning("config", "failure adding game: {}", game.getGameName()); } } // read card numbers read_card(); } Config::~Config() { } void Config::read_card(int player) { // check if a game is selected if (this->games_selected_name.empty()) { return; } // iterate bindings auto bindings = ::Config::getInstance().getKeypadBindings(this->games_selected_name); for (int p = 0; p < 2; ++p) { if (player < 0 || player == p) { // get path std::filesystem::path path; if (!bindings.card_paths[p].empty()) { path = bindings.card_paths[p]; } else { path = p > 0 ? "card1.txt" : "card0.txt"; } // open file std::ifstream f(path); if (!f || !f.is_open()) { this->keypads_card_number[p][0] = 0; continue; } // get file size f.seekg(0, f.end); auto length = (size_t) f.tellg(); f.seekg(0, f.beg); // read file contents f.read(this->keypads_card_number[p], 16); this->keypads_card_number[p][length < 16 ? length : 16] = 0; f.close(); } } } void Config::write_card(int player) { // get path auto bindings = ::Config::getInstance().getKeypadBindings(this->games_selected_name); std::filesystem::path path; if (!bindings.card_paths[player].empty()) { path = bindings.card_paths[player]; } else { path = player > 0 ? "card1.txt" : "card0.txt"; } // write file std::ofstream f(path); if (f) { f.write(this->keypads_card_number[player], strlen(this->keypads_card_number[player])); f.close(); } } void Config::build_content() { // if standalone then fullscreen window if (cfg::CONFIGURATOR_STANDALONE) { ImGui::SetWindowPos(ImVec2(0, 0)); ImGui::SetWindowSize(ImGui::GetIO().DisplaySize); } // game selector int previous_games_selected = this->games_selected; ImGui::PushItemWidth(MAX(1, 463 - MAX(0, 580 - ImGui::GetWindowSize().x))); ImGui::Combo("Game Selection", &this->games_selected, games_names.data(), (int) games_list.size()); ImGui::PopItemWidth(); // remember selected game name if (this->games_selected >= 0 && this->games_selected < (int) games_list.size()) { this->games_selected_name = games_list.at(games_selected).getGameName(); // standalone configurator applies selected game if (cfg::CONFIGURATOR_STANDALONE) { eamuse_set_game(games_selected_name); } } else { // invalid selection this->games_selected_name = ""; } // display launcher if no game is selected if (this->games_selected_name.empty()) { this->build_launcher(); return; } // selected game changed if (previous_games_selected != this->games_selected) { read_card(); } // tab selection if (ImGui::BeginTabBar("Config Tabs", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { int page_offset = cfg::CONFIGURATOR_STANDALONE ? 92 : 110; int page_offset2 = cfg::CONFIGURATOR_STANDALONE ? 69 : 87; if (ImGui::BeginTabItem("Buttons")) { ImGui::BeginChild("Buttons", ImVec2( 0, ImGui::GetWindowContentRegionMax().y - page_offset), false); // game buttons this->build_buttons("Game", games::get_buttons(this->games_selected_name)); // keypad buttons ImGui::TextUnformatted(""); auto keypad_buttons = games::get_buttons_keypads(this->games_selected_name); auto keypad_count = eamuse_get_game_keypads_name(); if (keypad_count == 1) { this->build_buttons("Keypad", keypad_buttons, 0, games::KeypadButtons::Size - 1); } else if (keypad_count >= 2) { this->build_buttons("Keypad", keypad_buttons); } // overlay buttons ImGui::TextUnformatted(""); this->build_buttons("Overlay", games::get_buttons_overlay(this->games_selected_name)); ImGui::EndChild(); // page selector this->build_page_selector(&this->buttons_page); // standalone configurator extras if (cfg::CONFIGURATOR_STANDALONE) { ImGui::SameLine(); ImGui::Checkbox("Enable Overlay in Config", &OVERLAY->hotkeys_enable); } // bind many ImGui::SameLine(); if (ImGui::Checkbox("Bind Many", &buttons_many_active)) { buttons_many_index = -1; buttons_many_delay = 0; } ImGui::SameLine(); ImGui::HelpMarker("Immediately query for the next button after binding one."); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Analogs")) { ImGui::BeginChild("Analogs", ImVec2( 0, ImGui::GetWindowContentRegionMax().y - page_offset2), false); this->build_analogs("Game", games::get_analogs(this->games_selected_name)); ImGui::EndChild(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Lights")) { ImGui::BeginChild("Lights", ImVec2( 0, ImGui::GetWindowContentRegionMax().y - page_offset), false); this->build_lights("Game", games::get_lights(this->games_selected_name)); ImGui::EndChild(); this->build_page_selector(&this->lights_page); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Cards")) { ImGui::BeginChild("Cards", ImVec2( 0, ImGui::GetWindowContentRegionMax().y - page_offset2), false); this->build_cards(); ImGui::EndChild(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Patches")) { // initialization static std::once_flag initialized; static bool failure = false; std::call_once(initialized, [this] { if (cfg::CONFIGURATOR_STANDALONE) { // verify game is set, otherwise set failure flag if (strlen(avs::game::MODEL) != 3 || (strlen(avs::game::DEST) != 1) || (strlen(avs::game::SPEC) != 1) || (strlen(avs::game::REV) != 1) || (strlen(avs::game::EXT) != 10) || (strcmp(avs::game::MODEL, "000") == 0) || (strcmp(avs::game::EXT, "0000000000") == 0)) { failure = true; } } }); // display tab contents ImGui::BeginChild("Patches", ImVec2( 0, ImGui::GetWindowContentRegionMax().y - page_offset2), false); if (failure) { ImGui::TextColored(ImVec4(0.7f, 0.f, 0.f, 1.f), "Unable to detect the game version.\n" "Try to open Patch Manager using the game overlay."); } else { // allocate patch manager if (!patch_manager) { patch_manager.reset(new PatchManager(overlay)); } // display patch manager this->patch_manager->build_content(); } ImGui::EndChild(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Options")) { ImGui::BeginChild("Options", ImVec2( 0, ImGui::GetWindowContentRegionMax().y - page_offset), false); // get options and categories auto options = games::get_options(this->games_selected_name); std::vector categories; categories.push_back("Everything"); if (options) { for (auto &option : *options) { bool found = false; for (auto &category : categories) { if (strcmp(option.get_definition().category.c_str(), category) == 0) { found = true; break; } } if (!found) { categories.push_back(option.get_definition().category.c_str()); } } } // get current category std::string category = ""; if (this->options_category != -1 && this->options_category < (int) categories.size()) { category = categories[this->options_category]; if (category == "Everything") { category = ""; } } // options list this->build_options(options, category); ImGui::EndChild(); // hidden options checkbox ImGui::Checkbox("Show Hidden Options", &this->options_show_hidden); if (!cfg::CONFIGURATOR_STANDALONE && this->options_dirty) { ImGui::SameLine(); if (ImGui::Button("Restart Game")) { launcher::restart(); } ImGui::SameLine(); ImGui::HelpMarker("You need to restart the game to apply the changed settings."); } // reset configuration button ImGui::SameLine(); if (ImGui::Button("Reset Configuration")) { ImGui::OpenPopup("Reset Config"); } if (ImGui::BeginPopupModal("Reset Config", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::TextColored(ImVec4(1, 0.5f, 0.5f, 1.f), "Do you really want to reset your configuration for all games?\n" "Warning: This can't be reverted!"); if (ImGui::Button("Yes")) { ::Config::getInstance().createConfigFile(); launcher::restart(); } ImGui::SameLine(); if (ImGui::Button("Nope")) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } // category selection ImGui::SameLine(); ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.3f); ImGui::Combo("Category", &this->options_category, categories.data(), categories.size()); ImGui::PopItemWidth(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("About")) { ImGui::BeginChild("About", ImVec2( 0, ImGui::GetWindowContentRegionMax().y - page_offset2), false); this->build_about(); ImGui::EndChild(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Licenses")) { ImGui::BeginChild("Licenses", ImVec2( 0, ImGui::GetWindowContentRegionMax().y - page_offset2), false); this->build_licenses(); ImGui::EndChild(); ImGui::EndTabItem(); } ImGui::EndTabBar(); } // disclaimer ImGui::TextColored( ImVec4(1, 0.5f, 0.5f, 1.f), "Do NOT stream or upload game data anywhere public! Support arcades when you can. Thanks."); } void Config::build_buttons(const std::string &name, std::vector