988 lines
38 KiB
C++
988 lines
38 KiB
C++
|
#include <winsock2.h>
|
||
|
|
||
|
#include "control.h"
|
||
|
|
||
|
#include <csignal>
|
||
|
|
||
|
#include <psapi.h>
|
||
|
|
||
|
#include "acio/acio.h"
|
||
|
#include "api/controller.h"
|
||
|
#include "avs/core.h"
|
||
|
#include "avs/ea3.h"
|
||
|
#include "avs/game.h"
|
||
|
#include "build/resource.h"
|
||
|
#include "cfg/analog.h"
|
||
|
#include "cfg/button.h"
|
||
|
#include "external/imgui/imgui_memory_editor.h"
|
||
|
#include "games/io.h"
|
||
|
#include "games/iidx/io.h"
|
||
|
#include "games/shared/lcdhandle.h"
|
||
|
#include "hooks/graphics/graphics.h"
|
||
|
#include "launcher/launcher.h"
|
||
|
#include "launcher/shutdown.h"
|
||
|
#include "misc/eamuse.h"
|
||
|
#include "rawinput/rawinput.h"
|
||
|
#include "util/cpuutils.h"
|
||
|
#include "util/memutils.h"
|
||
|
#include "util/netutils.h"
|
||
|
#include "util/libutils.h"
|
||
|
#include "util/peb.h"
|
||
|
#include "util/resutils.h"
|
||
|
#include "util/utils.h"
|
||
|
#include "touch/touch.h"
|
||
|
|
||
|
#include "acio_status_buffers.h"
|
||
|
#include "eadev.h"
|
||
|
#include "wnd_manager.h"
|
||
|
#include "midi.h"
|
||
|
|
||
|
namespace overlay::windows {
|
||
|
|
||
|
Control::Control(SpiceOverlay *overlay) : Window(overlay) {
|
||
|
this->title = "SpiceTools Control";
|
||
|
this->flags = ImGuiWindowFlags_AlwaysAutoResize;
|
||
|
this->toggle_button = games::OverlayButtons::ToggleControl;
|
||
|
this->init_pos = ImVec2(10, 10);
|
||
|
this->size_min.x = 300;
|
||
|
}
|
||
|
|
||
|
Control::~Control() {
|
||
|
}
|
||
|
|
||
|
void Control::build_content() {
|
||
|
top_row_buttons();
|
||
|
img_gui_view();
|
||
|
|
||
|
ImGui::Separator();
|
||
|
|
||
|
avs_info_view();
|
||
|
acio_view();
|
||
|
cpu_view();
|
||
|
graphics_view();
|
||
|
buttons_view();
|
||
|
analogs_view();
|
||
|
lights_view();
|
||
|
cards_view();
|
||
|
coin_view();
|
||
|
control_view();
|
||
|
api_view();
|
||
|
raw_input_view();
|
||
|
touch_view();
|
||
|
lcd_view();
|
||
|
about_view();
|
||
|
ddr_timing_view();
|
||
|
iidx_effectors_view();
|
||
|
}
|
||
|
|
||
|
void Control::top_row_buttons() {
|
||
|
|
||
|
// memory editor button
|
||
|
ImGui::SetNextItemWidth(-1.f);
|
||
|
if (ImGui::Button("Memory Editor")) {
|
||
|
this->memory_editor_open = true;
|
||
|
}
|
||
|
|
||
|
// memory editor window
|
||
|
if (this->memory_editor_open) {
|
||
|
static MemoryEditor memory_editor = MemoryEditor();
|
||
|
ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_Once);
|
||
|
if (ImGui::Begin("Memory Editor", &this->memory_editor_open)) {
|
||
|
|
||
|
// draw filter
|
||
|
if (this->memory_editor_filter.Draw("Filter")) {
|
||
|
memory_editor_modules.clear();
|
||
|
memory_editor_names.clear();
|
||
|
memory_editor_selection = -1;
|
||
|
}
|
||
|
|
||
|
// obtain modules
|
||
|
if (memory_editor_modules.empty()) {
|
||
|
peb::obtain_modules(&memory_editor_modules);
|
||
|
|
||
|
// extract names for combobox
|
||
|
for (size_t i = 0; i < memory_editor_modules.size();) {
|
||
|
auto s = memory_editor_modules[i].first.c_str();
|
||
|
|
||
|
// check if passes filter
|
||
|
if (memory_editor_filter.PassFilter(s)) {
|
||
|
memory_editor_names.emplace_back(s);
|
||
|
i++;
|
||
|
} else {
|
||
|
memory_editor_modules.erase(memory_editor_modules.begin() + i);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// draw combo box
|
||
|
ImGui::Combo("DLL Selection",
|
||
|
&memory_editor_selection,
|
||
|
&memory_editor_names[0],
|
||
|
static_cast<int>(memory_editor_names.size()));
|
||
|
ImGui::Separator();
|
||
|
if (memory_editor_selection >= 0) {
|
||
|
HMODULE module = memory_editor_modules[memory_editor_selection].second;
|
||
|
|
||
|
// get module information
|
||
|
MODULEINFO module_info{};
|
||
|
if (GetModuleInformation(
|
||
|
GetCurrentProcess(),
|
||
|
module,
|
||
|
&module_info,
|
||
|
sizeof(MODULEINFO))) {
|
||
|
|
||
|
/*
|
||
|
* unprotect memory
|
||
|
* small hack: don't reset the mode since multiple pages with different modes are affected
|
||
|
* they'd get overridden by the original mode of the first page
|
||
|
*/
|
||
|
memutils::VProtectGuard guard(
|
||
|
module_info.lpBaseOfDll,
|
||
|
module_info.SizeOfImage,
|
||
|
PAGE_EXECUTE_READWRITE,
|
||
|
false);
|
||
|
|
||
|
// draw memory editor
|
||
|
memory_editor.DrawContents(
|
||
|
module_info.lpBaseOfDll,
|
||
|
module_info.SizeOfImage,
|
||
|
(size_t) module_info.lpBaseOfDll);
|
||
|
} else {
|
||
|
ImGui::Text("Could not get module information");
|
||
|
}
|
||
|
} else {
|
||
|
ImGui::Text("Please select a module");
|
||
|
}
|
||
|
}
|
||
|
ImGui::End();
|
||
|
}
|
||
|
|
||
|
// EA-Dev
|
||
|
ImGui::SameLine();
|
||
|
if (ImGui::Button("EA-Dev")) {
|
||
|
this->children.emplace_back(new EADevWindow(this->overlay));
|
||
|
}
|
||
|
|
||
|
// Window Manager
|
||
|
ImGui::SameLine();
|
||
|
if (ImGui::Button("Window Manager")) {
|
||
|
this->children.emplace_back(new WndManagerWindow(this->overlay));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Control::img_gui_view() {
|
||
|
if (ImGui::CollapsingHeader("ImGui")) {
|
||
|
|
||
|
// display size
|
||
|
ImGui::Text("Display Size: %dx%d",
|
||
|
static_cast<int>(ImGui::GetIO().DisplaySize.x),
|
||
|
static_cast<int>(ImGui::GetIO().DisplaySize.y));
|
||
|
|
||
|
// removed for size (along with setting IMGUI_DISABLE_DEMO_WINDOWS
|
||
|
// and IMGUI_DISABLE_DEBUG_TOOLS) - saves about 300kb in each
|
||
|
// binary
|
||
|
|
||
|
// metrics button
|
||
|
// this->metrics_open |= ImGui::Button("Metrics Window");
|
||
|
// if (this->metrics_open) {
|
||
|
// ImGui::ShowMetricsWindow(&this->metrics_open);
|
||
|
// }
|
||
|
|
||
|
// demo button
|
||
|
// ImGui::SameLine();
|
||
|
// if (ImGui::Button("Demo Window")) {
|
||
|
// this->demo_open = true;
|
||
|
// }
|
||
|
// if (this->demo_open) {
|
||
|
// ImGui::ShowDemoWindow(&this->demo_open);
|
||
|
// }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Control::avs_info_view() {
|
||
|
if (ImGui::CollapsingHeader("AVS")) {
|
||
|
|
||
|
// game
|
||
|
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
|
||
|
if (ImGui::TreeNode("Game")) {
|
||
|
ImGui::BulletText("DLL Name: %s", avs::game::DLL_NAME.c_str());
|
||
|
ImGui::BulletText("Identifier: %s", avs::game::get_identifier().c_str());
|
||
|
ImGui::TreePop();
|
||
|
}
|
||
|
|
||
|
// core
|
||
|
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
|
||
|
if (ImGui::TreeNode("Core")) {
|
||
|
ImGui::BulletText("DLL Name: %s", avs::core::DLL_NAME.c_str());
|
||
|
ImGui::BulletText("Version: %s", avs::core::VERSION_STR.c_str());
|
||
|
ImGui::BulletText("%s", fmt::format("Heap Size: {}{}",
|
||
|
(uint64_t) avs::core::HEAP_SIZE,
|
||
|
avs::core::DEFAULT_HEAP_SIZE_SET ? " (Default)" : "").c_str());
|
||
|
ImGui::BulletText("Log Path: %s", avs::core::LOG_PATH.c_str());
|
||
|
ImGui::BulletText("Config Path: %s", avs::core::CFG_PATH.c_str());
|
||
|
ImGui::TreePop();
|
||
|
}
|
||
|
|
||
|
// EA3
|
||
|
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
|
||
|
if (ImGui::TreeNode("EA3")) {
|
||
|
ImGui::BulletText("DLL Name: %s", avs::ea3::DLL_NAME.c_str());
|
||
|
ImGui::BulletText("Version: %s", avs::ea3::VERSION_STR.c_str());
|
||
|
ImGui::BulletText("Config Path: %s", avs::ea3::CFG_PATH.c_str());
|
||
|
ImGui::BulletText("App Path: %s", avs::ea3::APP_PATH.c_str());
|
||
|
ImGui::BulletText("Services: %s", avs::ea3::EA3_BOOT_URL.c_str());
|
||
|
ImGui::TreePop();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Control::acio_view() {
|
||
|
if (ImGui::CollapsingHeader("ACIO")) {
|
||
|
ImGui::Columns(4, "acio_columns");
|
||
|
ImGui::Separator();
|
||
|
ImGui::Text("Name");
|
||
|
ImGui::NextColumn();
|
||
|
ImGui::Text("Hook");
|
||
|
ImGui::NextColumn();
|
||
|
ImGui::Text("Attached");
|
||
|
ImGui::NextColumn();
|
||
|
ImGui::NextColumn();
|
||
|
ImGui::Separator();
|
||
|
for (auto &module : acio::MODULES) {
|
||
|
ImGui::PushID((void *) module);
|
||
|
ImGui::Text("%s", module->name.c_str());
|
||
|
ImGui::NextColumn();
|
||
|
ImGui::Text("%s", acio::hook_mode_str(module->hook_mode));
|
||
|
ImGui::NextColumn();
|
||
|
ImGui::Text("%s", module->attached ? "true" : "false");
|
||
|
ImGui::NextColumn();
|
||
|
if (module->status_buffer && module->status_buffer_size) {
|
||
|
if (ImGui::Button("Status")) {
|
||
|
this->children.emplace_back(new ACIOStatusBuffers(overlay, module));
|
||
|
}
|
||
|
}
|
||
|
ImGui::NextColumn();
|
||
|
ImGui::Separator();
|
||
|
ImGui::PopID();
|
||
|
}
|
||
|
ImGui::Columns(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Control::cpu_view() {
|
||
|
auto cpu_load_values = cpuutils::get_load();
|
||
|
if (cpu_load_values.size() && ImGui::CollapsingHeader("CPU")) {
|
||
|
|
||
|
// print detected cores
|
||
|
ImGui::BulletText("Detected cores: %i", (int) std::thread::hardware_concurrency());
|
||
|
|
||
|
// make sure the temporary buffer has enough space
|
||
|
while (this->cpu_values.size() < cpu_load_values.size()) {
|
||
|
this->cpu_values.emplace_back(0.f);
|
||
|
}
|
||
|
|
||
|
// iterate cores
|
||
|
for (size_t cpu = 0; cpu < cpu_load_values.size(); cpu++) {
|
||
|
|
||
|
// update average
|
||
|
auto avg_load = MIN(MAX(this->cpu_values[cpu] +
|
||
|
(cpu_load_values[cpu] - this->cpu_values[cpu]) * ImGui::GetIO().DeltaTime, 0), 100);
|
||
|
this->cpu_values[cpu] = avg_load;
|
||
|
|
||
|
// draw content
|
||
|
ImGui::BulletText("CPU #%i:", (int) cpu + 1);
|
||
|
ImGui::SameLine();
|
||
|
ImGui::ProgressBar(avg_load * 0.01f, ImVec2(64, 0));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Control::graphics_view() {
|
||
|
if (ImGui::CollapsingHeader("Graphics")) {
|
||
|
|
||
|
// screenshot button
|
||
|
if (ImGui::Button("Take Screenshot")) {
|
||
|
graphics_screenshot_trigger();
|
||
|
}
|
||
|
|
||
|
// graphics information
|
||
|
ImGui::BulletText("D3D9 Adapter ID: %lu",
|
||
|
overlay->adapter_identifier.DeviceId);
|
||
|
ImGui::BulletText("D3D9 Adapter Name: %s",
|
||
|
overlay->adapter_identifier.DeviceName);
|
||
|
ImGui::BulletText("D3D9 Adapter Revision: %lu",
|
||
|
overlay->adapter_identifier.Revision);
|
||
|
ImGui::BulletText("D3D9 Adapter SubSys ID: %lu",
|
||
|
overlay->adapter_identifier.SubSysId);
|
||
|
ImGui::BulletText("D3D9 Adapter Vendor ID: %lu",
|
||
|
overlay->adapter_identifier.VendorId);
|
||
|
ImGui::BulletText("D3D9 Adapter WQHL Level: %lu",
|
||
|
overlay->adapter_identifier.WHQLLevel);
|
||
|
ImGui::BulletText("D3D9 Adapter Driver: %s",
|
||
|
overlay->adapter_identifier.Driver);
|
||
|
ImGui::BulletText("%s", fmt::format("D3D9 Adapter Driver Version: {}",
|
||
|
overlay->adapter_identifier.DriverVersion.QuadPart).c_str());
|
||
|
ImGui::BulletText("D3D9 Adapter Description: %s",
|
||
|
overlay->adapter_identifier.Description);
|
||
|
ImGui::BulletText("D3D9 Adapter GUID: %s",
|
||
|
guid2s(overlay->adapter_identifier.DeviceIdentifier).c_str());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Control::buttons_view() {
|
||
|
auto buttons = games::get_buttons(eamuse_get_game());
|
||
|
if (buttons && !buttons->empty() && ImGui::CollapsingHeader("Buttons")) {
|
||
|
|
||
|
// print each button state
|
||
|
for (auto &button : *buttons) {
|
||
|
|
||
|
// state
|
||
|
float state = GameAPI::Buttons::getVelocity(RI_MGR, button);
|
||
|
ImGui::ProgressBar(state, ImVec2(32.f, 0));
|
||
|
|
||
|
// mouse down handler
|
||
|
if (ImGui::IsItemHovered()) {
|
||
|
if (ImGui::IsAnyMouseDown()) {
|
||
|
button.override_state = GameAPI::Buttons::BUTTON_PRESSED;
|
||
|
button.override_velocity = 1.f;
|
||
|
button.override_enabled = true;
|
||
|
} else {
|
||
|
button.override_enabled = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// mark overridden items
|
||
|
if (button.override_enabled) {
|
||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 0.f, 1.f));
|
||
|
}
|
||
|
|
||
|
// text
|
||
|
ImGui::SameLine(0.f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||
|
if (RI_MGR && button.isSet()) {
|
||
|
ImGui::Text("%s [%s]",
|
||
|
button.getName().c_str(),
|
||
|
button.getDisplayString(RI_MGR.get()).c_str());
|
||
|
} else {
|
||
|
ImGui::Text("%s", button.getName().c_str());
|
||
|
}
|
||
|
|
||
|
// pop override color
|
||
|
if (button.override_enabled) {
|
||
|
ImGui::PopStyleColor();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Control::analogs_view() {
|
||
|
auto analogs = games::get_analogs(eamuse_get_game());
|
||
|
if (analogs && !analogs->empty() && ImGui::CollapsingHeader("Analogs")) {
|
||
|
|
||
|
// print each button state
|
||
|
for (auto &analog : *analogs) {
|
||
|
|
||
|
// state
|
||
|
float state = GameAPI::Analogs::getState(RI_MGR, analog);
|
||
|
ImGui::ProgressBar(state, ImVec2(32.f, 0));
|
||
|
|
||
|
// mouse down handler
|
||
|
if (ImGui::IsItemHovered()) {
|
||
|
if (ImGui::IsAnyMouseDown()) {
|
||
|
analog.override_state = 1.f;
|
||
|
analog.override_enabled = true;
|
||
|
} else {
|
||
|
analog.override_enabled = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// mark overridden items
|
||
|
if (analog.override_enabled) {
|
||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 0.f, 1.f));
|
||
|
}
|
||
|
|
||
|
// text
|
||
|
ImGui::SameLine(0.f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||
|
if (RI_MGR && analog.isSet()) {
|
||
|
ImGui::Text("%s [%s]",
|
||
|
analog.getName().c_str(),
|
||
|
analog.getDisplayString(RI_MGR.get()).c_str());
|
||
|
} else {
|
||
|
ImGui::Text("%s", analog.getName().c_str());
|
||
|
}
|
||
|
|
||
|
// pop override color
|
||
|
if (analog.override_enabled) {
|
||
|
ImGui::PopStyleColor();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Control::lights_view() {
|
||
|
auto lights = games::get_lights(eamuse_get_game());
|
||
|
if (lights && !lights->empty() && ImGui::CollapsingHeader("Lights")) {
|
||
|
|
||
|
// print each button state
|
||
|
for (auto &light : *lights) {
|
||
|
|
||
|
// state
|
||
|
float state = GameAPI::Lights::readLight(RI_MGR, light);
|
||
|
ImGui::ProgressBar(state, ImVec2(32.f, 0));
|
||
|
|
||
|
// mouse down handler
|
||
|
if (ImGui::IsItemHovered()) {
|
||
|
if (ImGui::IsAnyMouseDown()) {
|
||
|
light.override_state = 1.f;
|
||
|
light.override_enabled = true;
|
||
|
} else {
|
||
|
light.override_enabled = false;
|
||
|
}
|
||
|
GameAPI::Lights::writeLight(RI_MGR, light, light.last_state);
|
||
|
RI_MGR->devices_flush_output();
|
||
|
}
|
||
|
|
||
|
// mark overridden items
|
||
|
if (light.override_enabled) {
|
||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 0.f, 1.f));
|
||
|
}
|
||
|
|
||
|
// text
|
||
|
ImGui::SameLine(0.f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||
|
if (RI_MGR && light.isSet()) {
|
||
|
ImGui::Text("%s [%s]",
|
||
|
light.getName().c_str(),
|
||
|
light.getDisplayString(RI_MGR.get()).c_str());
|
||
|
} else {
|
||
|
ImGui::Text("%s", light.getName().c_str());
|
||
|
}
|
||
|
|
||
|
// pop override color
|
||
|
if (light.override_enabled) {
|
||
|
ImGui::PopStyleColor();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Control::cards_view() {
|
||
|
if (ImGui::CollapsingHeader("Cards")) {
|
||
|
ImGui::InputTextWithHint("Card ID", "E0040123456789AB",
|
||
|
this->card_input,
|
||
|
std::size(this->card_input),
|
||
|
ImGuiInputTextFlags_CharsHexadecimal
|
||
|
| ImGuiInputTextFlags_CharsUppercase);
|
||
|
if (strlen(this->card_input) < 16) {
|
||
|
ImGui::Text("Please enter your card identifier...");
|
||
|
} else {
|
||
|
if (ImGui::Button("Insert P1")) {
|
||
|
uint8_t card_data[8];
|
||
|
if (hex2bin(this->card_input, card_data)) {
|
||
|
eamuse_card_insert(0, card_data);
|
||
|
}
|
||
|
}
|
||
|
if (eamuse_get_game_keypads() > 1) {
|
||
|
ImGui::SameLine();
|
||
|
if (ImGui::Button("Insert P2")) {
|
||
|
uint8_t card_data[8];
|
||
|
if (hex2bin(this->card_input, card_data)) {
|
||
|
eamuse_card_insert(1, card_data);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Control::coin_view() {
|
||
|
if (ImGui::CollapsingHeader("Coins")) {
|
||
|
auto coinstock = eamuse_coin_get_stock();
|
||
|
ImGui::Text("Blocker: %s", eamuse_coin_get_block() ? "closed" : "open");
|
||
|
ImGui::Text("Coinstock: %i", coinstock);
|
||
|
ImGui::Separator();
|
||
|
if (ImGui::Button("Add Coin")) {
|
||
|
eamuse_coin_add();
|
||
|
}
|
||
|
if (coinstock != 0) {
|
||
|
ImGui::SameLine();
|
||
|
if (ImGui::Button("Consume")) {
|
||
|
eamuse_coin_consume_stock();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Control::control_view() {
|
||
|
if (ImGui::CollapsingHeader("Control")) {
|
||
|
|
||
|
// launcher utils
|
||
|
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
|
||
|
if (ImGui::TreeNode("Launcher Utils")) {
|
||
|
if (ImGui::Button("Restart")) {
|
||
|
launcher::restart();
|
||
|
}
|
||
|
if (ImGui::Button("Terminate")) {
|
||
|
launcher::shutdown(0);
|
||
|
}
|
||
|
ImGui::TreePop();
|
||
|
}
|
||
|
|
||
|
// signal triggers
|
||
|
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
|
||
|
if (ImGui::TreeNode("Signal Triggers")) {
|
||
|
if (ImGui::Button("Raise SIGABRT")) {
|
||
|
::raise(SIGABRT);
|
||
|
}
|
||
|
if (ImGui::Button("Raise SIGFPE")) {
|
||
|
::raise(SIGFPE);
|
||
|
}
|
||
|
if (ImGui::Button("Raise SIGILL")) {
|
||
|
::raise(SIGILL);
|
||
|
}
|
||
|
if (ImGui::Button("Raise SIGINT")) {
|
||
|
::raise(SIGINT);
|
||
|
}
|
||
|
if (ImGui::Button("Raise SIGSEGV")) {
|
||
|
::raise(SIGSEGV);
|
||
|
}
|
||
|
if (ImGui::Button("Raise SIGTERM")) {
|
||
|
::raise(SIGTERM);
|
||
|
}
|
||
|
ImGui::TreePop();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Control::api_view() {
|
||
|
if (API_CONTROLLER != nullptr && ImGui::CollapsingHeader("API")) {
|
||
|
std::vector<api::ClientState> client_states;
|
||
|
API_CONTROLLER->obtain_client_states(&client_states);
|
||
|
|
||
|
// show ip addresses
|
||
|
auto ip_addresses = netutils::get_local_addresses();
|
||
|
if (!ip_addresses.empty()) {
|
||
|
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
|
||
|
if (ImGui::TreeNode("Local IP-Addresses")) {
|
||
|
for (auto &adr : ip_addresses) {
|
||
|
ImGui::BulletText("%s", adr.c_str());
|
||
|
}
|
||
|
ImGui::TreePop();
|
||
|
ImGui::Separator();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// client count
|
||
|
ImGui::Text("Connected clients: %u", (unsigned int) client_states.size());
|
||
|
|
||
|
// iterate clients
|
||
|
for (auto &client : client_states) {
|
||
|
auto address = API_CONTROLLER->get_ip_address(client.address);
|
||
|
if (ImGui::TreeNode(("Client @ " + address).c_str())) {
|
||
|
if (client.password.empty()) {
|
||
|
ImGui::Text("No password set.");
|
||
|
} else {
|
||
|
ImGui::Text("Password set.");
|
||
|
}
|
||
|
if (ImGui::TreeNode("Modules")) {
|
||
|
for (auto &module : client.modules) {
|
||
|
if (ImGui::TreeNode(module->name.c_str())) {
|
||
|
ImGui::Text("Password force: %i", module->password_force);
|
||
|
ImGui::TreePop();
|
||
|
}
|
||
|
}
|
||
|
ImGui::TreePop();
|
||
|
}
|
||
|
ImGui::TreePop();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Control::raw_input_view() {
|
||
|
if (RI_MGR != nullptr && ImGui::CollapsingHeader("RawInput")) {
|
||
|
|
||
|
// midi control
|
||
|
if (ImGui::Button("MIDI-Control")) {
|
||
|
this->children.push_back(new MIDIWindow(this->overlay));
|
||
|
}
|
||
|
|
||
|
// device count
|
||
|
auto devices = RI_MGR->devices_get();
|
||
|
ImGui::Text("Devices detected: %u", (unsigned int) devices.size());
|
||
|
|
||
|
// iterate devices
|
||
|
for (auto &device : devices) {
|
||
|
if (ImGui::TreeNode(("#" + to_string(device.id) + ": " + device.desc).c_str())) {
|
||
|
ImGui::Text("GUID: %s", device.info.guid_str.c_str());
|
||
|
ImGui::Text("Output: %i", device.output_enabled);
|
||
|
if (device.input_hz > 0 || device.input_hz_max > 0) {
|
||
|
ImGui::Text("Input rate (cur): %.2fHz", device.input_hz);
|
||
|
ImGui::Text("Input rate (max): %.2fHz", device.input_hz_max);
|
||
|
}
|
||
|
switch (device.type) {
|
||
|
case rawinput::MOUSE: {
|
||
|
auto mouse = device.mouseInfo;
|
||
|
ImGui::Text("Type: Mouse");
|
||
|
ImGui::Text("X: %ld", mouse->pos_x);
|
||
|
ImGui::Text("Y: %ld", mouse->pos_y);
|
||
|
ImGui::Text("Wheel: %ld", mouse->pos_wheel);
|
||
|
|
||
|
// keys
|
||
|
std::stringstream keys;
|
||
|
keys << "[";
|
||
|
for (auto key : mouse->key_states) {
|
||
|
keys << (key ? "1," : "0,");
|
||
|
}
|
||
|
keys << "]";
|
||
|
ImGui::Text("Keys: %s", keys.str().c_str());
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case rawinput::KEYBOARD: {
|
||
|
auto keyboard = device.keyboardInfo;
|
||
|
ImGui::Text("Type: Keyboard");
|
||
|
|
||
|
// keys
|
||
|
std::stringstream keys;
|
||
|
keys << "[";
|
||
|
for (size_t i = 0; i < std::size(keyboard->key_states); i++) {
|
||
|
if (keyboard->key_states[i]) {
|
||
|
keys << i << ",";
|
||
|
}
|
||
|
}
|
||
|
keys << "]";
|
||
|
ImGui::Text("Keys: %s", keys.str().c_str());
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case rawinput::HID: {
|
||
|
auto hid = device.hidInfo;
|
||
|
ImGui::Text("Type: HID");
|
||
|
ImGui::Text("VID: %04X", hid->attributes.VendorID);
|
||
|
ImGui::Text("PID: %04X", hid->attributes.ProductID);
|
||
|
ImGui::Text("VER: %i", hid->attributes.VersionNumber);
|
||
|
switch (hid->driver) {
|
||
|
case rawinput::HIDDriver::PacDrive:
|
||
|
ImGui::Text("Driver: PacDrive");
|
||
|
break;
|
||
|
default:
|
||
|
ImGui::Text("Driver: Default");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// button states
|
||
|
if (!hid->button_states.empty() && ImGui::TreeNode("Button States")) {
|
||
|
size_t button_cap_name = 0;
|
||
|
for (auto state_list : hid->button_states) {
|
||
|
for (auto state : state_list) {
|
||
|
ImGui::Text("%s: %i",
|
||
|
hid->button_caps_names[button_cap_name++].c_str(),
|
||
|
state ? 1 : 0);
|
||
|
}
|
||
|
}
|
||
|
ImGui::TreePop();
|
||
|
}
|
||
|
|
||
|
// button output states
|
||
|
if (!hid->button_output_states.empty() && ImGui::TreeNode("Button Output States")) {
|
||
|
size_t button_output_cap_name = 0;
|
||
|
for (auto state_list : hid->button_output_states) {
|
||
|
for (auto state : state_list) {
|
||
|
ImGui::Text("%s: %i",
|
||
|
hid->button_output_caps_names[button_output_cap_name++].c_str(),
|
||
|
state ? 1 : 0);
|
||
|
}
|
||
|
}
|
||
|
ImGui::TreePop();
|
||
|
}
|
||
|
|
||
|
// analog states
|
||
|
if (!hid->value_states.empty() && ImGui::TreeNode("Analog States")) {
|
||
|
size_t value_cap_name = 0;
|
||
|
for (auto analog_state : hid->value_states) {
|
||
|
ImGui::Text("%s: %.2f",
|
||
|
hid->value_caps_names[value_cap_name++].c_str(),
|
||
|
analog_state);
|
||
|
}
|
||
|
ImGui::TreePop();
|
||
|
}
|
||
|
|
||
|
// analog output states
|
||
|
if (!hid->value_output_states.empty() && ImGui::TreeNode("Analog Output States")) {
|
||
|
size_t value_output_cap_name = 0;
|
||
|
for (auto analog_state : hid->value_output_states) {
|
||
|
ImGui::Text("%s: %.2f",
|
||
|
hid->value_output_caps_names[value_output_cap_name++].c_str(),
|
||
|
analog_state);
|
||
|
}
|
||
|
ImGui::TreePop();
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case rawinput::MIDI: {
|
||
|
ImGui::Text("Type: MIDI");
|
||
|
break;
|
||
|
}
|
||
|
case rawinput::SEXTET_OUTPUT: {
|
||
|
ImGui::Text("Type: Sextet");
|
||
|
break;
|
||
|
}
|
||
|
case rawinput::PIUIO_DEVICE: {
|
||
|
ImGui::Text("Type: PIUIO");
|
||
|
break;
|
||
|
}
|
||
|
case rawinput::DESTROYED: {
|
||
|
ImGui::Text("Disconnected.");
|
||
|
break;
|
||
|
}
|
||
|
case rawinput::UNKNOWN:
|
||
|
default:
|
||
|
ImGui::Text("Type: Unknown");
|
||
|
}
|
||
|
ImGui::TreePop();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Control::touch_view() {
|
||
|
if (ImGui::CollapsingHeader("Touch")) {
|
||
|
|
||
|
// status
|
||
|
ImGui::Text("Status: %s", is_touch_available() ? "available" : "unavailable");
|
||
|
|
||
|
// touch points
|
||
|
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
|
||
|
if (ImGui::TreeNode("Touch Points")) {
|
||
|
|
||
|
// get touch points
|
||
|
std::vector<TouchPoint> touch_points;
|
||
|
touch_get_points(touch_points);
|
||
|
for (auto &tp : touch_points) {
|
||
|
|
||
|
// draw touch point
|
||
|
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
|
||
|
if (ImGui::TreeNode((void *) (size_t) tp.id, "TP #%lu", tp.id)) {
|
||
|
ImGui::Text("X: %ld", tp.x);
|
||
|
ImGui::Text("Y: %ld", tp.y);
|
||
|
ImGui::TreePop();
|
||
|
}
|
||
|
}
|
||
|
ImGui::TreePop();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Control::lcd_view() {
|
||
|
if (games::shared::LCD_ENABLED && ImGui::CollapsingHeader("LCD")) {
|
||
|
ImGui::Text("Enabled: %s", games::shared::LCD_ENABLED ? "true" : "false");
|
||
|
ImGui::Text("CSM: %s", games::shared::LCD_CSM.c_str());
|
||
|
ImGui::Text("BRI: %i", games::shared::LCD_BRI);
|
||
|
ImGui::Text("CON: %i", games::shared::LCD_CON);
|
||
|
ImGui::Text("RED: %i", games::shared::LCD_RED);
|
||
|
ImGui::Text("GREEN: %i", games::shared::LCD_GREEN);
|
||
|
ImGui::Text("BLUE: %i", games::shared::LCD_BLUE);
|
||
|
ImGui::Text("BL: %i", games::shared::LCD_BL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Control::about_view() {
|
||
|
if (ImGui::CollapsingHeader("About")) {
|
||
|
if (ImGui::TreeNode("Changelog")) {
|
||
|
ImGui::Separator();
|
||
|
if (ImGui::BeginChild("changelog", ImVec2(400, 400))) {
|
||
|
ImGui::TextUnformatted(resutil::load_file_string(IDR_CHANGELOG).c_str());
|
||
|
}
|
||
|
ImGui::EndChild();
|
||
|
}
|
||
|
if (ImGui::TreeNode("Licenses")) {
|
||
|
ImGui::Separator();
|
||
|
if (ImGui::BeginChild("changelog", ImVec2(400, 400), false,
|
||
|
ImGuiWindowFlags_HorizontalScrollbar
|
||
|
| ImGuiWindowFlags_AlwaysHorizontalScrollbar)) {
|
||
|
ImGui::TextUnformatted(resutil::load_file_string(IDR_LICENSES).c_str());
|
||
|
}
|
||
|
ImGui::EndChild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Control::ddr_timing_view() {
|
||
|
if (avs::game::is_model("MDX") && ImGui::CollapsingHeader("DDR Timing")) {
|
||
|
|
||
|
// patches
|
||
|
struct ddr_patch {
|
||
|
const char *ext;
|
||
|
const char *name;
|
||
|
const char *format;
|
||
|
int min;
|
||
|
int max;
|
||
|
size_t offset;
|
||
|
intptr_t offset_ptr = 0;
|
||
|
};
|
||
|
|
||
|
static struct ddr_patch PATCHES[] = {
|
||
|
|
||
|
// patches for MDX-001-2019042200
|
||
|
{ "2019042200", "Sound Offset", "%d ms", 0, 1000, 0x1CCC5 },
|
||
|
{ "2019042200", "Render Offset", "%d ms", 0, 1000, 0x1CD0A },
|
||
|
{ "2019042200", "Input Offset", "%d ms", 0, 1000, 0x1CCE5 },
|
||
|
{ "2019042200", "Bomb Offset", "%d frames", 0, 10, 0x1CCC0 },
|
||
|
{ "2019042200", "SSQ Offset", "%d ms", -1000, 1000, 0x1CCCA },
|
||
|
{ "2019042200", "Cabinet Type", "%d", 0, 6, 0x1CDAE },
|
||
|
};
|
||
|
|
||
|
// check if patches available
|
||
|
bool patches_available = false;
|
||
|
for (auto &patch : PATCHES) {
|
||
|
if (avs::game::is_ext(patch.ext)) {
|
||
|
patches_available = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// show message if no patches available
|
||
|
if (!patches_available) {
|
||
|
ImGui::Text("No offsets known for this version.");
|
||
|
} else {
|
||
|
|
||
|
// iterate patches
|
||
|
for (auto &patch : PATCHES) {
|
||
|
if (avs::game::is_ext(patch.ext)) {
|
||
|
|
||
|
// check if pointer is uninitialized
|
||
|
if (patch.offset_ptr == 0) {
|
||
|
|
||
|
// get module information
|
||
|
auto dll_path = MODULE_PATH / "gamemdx.dll";
|
||
|
|
||
|
// get dll_module
|
||
|
auto dll_module = libutils::try_module(dll_path);
|
||
|
if (!dll_module) {
|
||
|
// no fatal error, might just not be loaded yet
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// get module information
|
||
|
MODULEINFO dll_module_info {};
|
||
|
if (GetModuleInformation(
|
||
|
GetCurrentProcess(),
|
||
|
dll_module,
|
||
|
&dll_module_info,
|
||
|
sizeof(MODULEINFO)))
|
||
|
{
|
||
|
// convert offset to RVA
|
||
|
auto rva = libutils::offset2rva(dll_path, patch.offset);
|
||
|
if (rva && rva != ~0) {
|
||
|
|
||
|
// get data pointer
|
||
|
patch.offset_ptr = reinterpret_cast<intptr_t>(dll_module_info.lpBaseOfDll) + rva;
|
||
|
} else {
|
||
|
|
||
|
// invalidate
|
||
|
patch.offset_ptr = -1;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
// invalidate
|
||
|
patch.offset_ptr = -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// check if pointer is valid
|
||
|
if (patch.offset_ptr != -1) {
|
||
|
auto *value_ptr = reinterpret_cast<uint16_t *>(patch.offset_ptr);
|
||
|
|
||
|
// draw drag widget
|
||
|
int value = *value_ptr;
|
||
|
ImGui::DragInt(patch.name, &value, 0.2f, patch.min, patch.max, patch.format);
|
||
|
|
||
|
// write value back
|
||
|
if (value != *value_ptr) {
|
||
|
memutils::VProtectGuard guard(value_ptr, sizeof(uint16_t));
|
||
|
*value_ptr = value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Control::iidx_effectors_view() {
|
||
|
if (avs::game::is_model("LDJ") && ImGui::CollapsingHeader("IIDX Effectors")) {
|
||
|
|
||
|
// effector analog entries
|
||
|
static const std::map<size_t, const char *> ANALOG_ENTRIES {
|
||
|
{ games::iidx::Analogs::VEFX, "VEFX" },
|
||
|
{ games::iidx::Analogs::LowEQ, "LoEQ" },
|
||
|
{ games::iidx::Analogs::HiEQ, "HiEQ" },
|
||
|
{ games::iidx::Analogs::Filter, "Flt" },
|
||
|
{ games::iidx::Analogs::PlayVolume, "Vol" },
|
||
|
};
|
||
|
|
||
|
// iterate analogs
|
||
|
float hue = 0.f;
|
||
|
bool overridden = false;
|
||
|
static auto analogs = games::get_analogs(eamuse_get_game());
|
||
|
for (auto &[index, name] : ANALOG_ENTRIES) {
|
||
|
|
||
|
// safety check
|
||
|
if (index >= analogs->size()) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// get analog
|
||
|
auto &analog = (*analogs)[index];
|
||
|
overridden |= analog.override_enabled;
|
||
|
|
||
|
// push id and style
|
||
|
ImGui::PushID((void *) name);
|
||
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4) ImColor::HSV(hue, 0.5f, 0.5f));
|
||
|
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, (ImVec4) ImColor::HSV(hue, 0.6f, 0.5f));
|
||
|
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, (ImVec4) ImColor::HSV(hue, 0.7f, 0.5f));
|
||
|
ImGui::PushStyleColor(ImGuiCol_SliderGrab, (ImVec4) ImColor::HSV(hue, 0.9f, 0.9f));
|
||
|
if (analog.override_enabled) {
|
||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.f, 1.f, 0.f, 1.f));
|
||
|
} else {
|
||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.8f, 0.8f, 1.f));
|
||
|
}
|
||
|
|
||
|
// vertical slider
|
||
|
auto new_state = analog.override_enabled ? analog.override_state
|
||
|
: GameAPI::Analogs::getState(RI_MGR, analog);
|
||
|
if (hue > 0.f) {
|
||
|
ImGui::SameLine();
|
||
|
}
|
||
|
ImGui::VSliderFloat("##v", ImVec2(32, 160), &new_state, 0.f, 1.f, name);
|
||
|
if (new_state != analog.override_state) {
|
||
|
analog.override_state = new_state;
|
||
|
analog.override_enabled = true;
|
||
|
}
|
||
|
|
||
|
// pop id and style
|
||
|
ImGui::PopStyleColor(5);
|
||
|
ImGui::PopID();
|
||
|
|
||
|
// rainbow
|
||
|
hue += 1.f / 7;
|
||
|
}
|
||
|
|
||
|
// reset button
|
||
|
if (overridden) {
|
||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.f, 1.f, 0.f, 1.f));
|
||
|
if (ImGui::Button("Reset")) {
|
||
|
for (auto &[index, name] : ANALOG_ENTRIES) {
|
||
|
if (index < analogs->size()) {
|
||
|
(*analogs)[index].override_enabled = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
ImGui::PopStyleColor();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|