Update to spice2x-23-06-08
This commit is contained in:
@@ -313,7 +313,10 @@ void ImGui_ImplSpice_NewFrame() {
|
||||
|
||||
// trigger
|
||||
io.KeysDown[vKey] |= state;
|
||||
if (!KeysDownOld[vKey] && state) {
|
||||
|
||||
// generate character input, but only if WM_CHAR didn't take over the
|
||||
// functionality
|
||||
if (!overlay::USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT && !KeysDownOld[vKey] && state) {
|
||||
UCHAR buf[2];
|
||||
auto ret = ToAscii(
|
||||
static_cast<UINT>(vKey),
|
||||
@@ -323,7 +326,7 @@ void ImGui_ImplSpice_NewFrame() {
|
||||
0);
|
||||
if (ret > 0) {
|
||||
for (int i = 0; i < ret; i++) {
|
||||
overlay::OVERLAY->input_char(buf[i], true);
|
||||
overlay::OVERLAY->input_char(buf[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+162
-45
@@ -3,10 +3,13 @@
|
||||
#include "avs/game.h"
|
||||
#include "cfg/configurator.h"
|
||||
#include "games/io.h"
|
||||
#include "games/iidx/iidx.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "touch/touch.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/resutils.h"
|
||||
#include "build/resource.h"
|
||||
|
||||
#include "imgui/impl_dx9.h"
|
||||
#include "imgui/impl_spice.h"
|
||||
@@ -21,7 +24,13 @@
|
||||
#include "windows/config.h"
|
||||
#include "windows/control.h"
|
||||
#include "windows/fps.h"
|
||||
#include "windows/generic_sub.h"
|
||||
#include "windows/iidx_seg.h"
|
||||
#include "windows/iidx_sub.h"
|
||||
#include "windows/iopanel.h"
|
||||
#include "windows/iopanel_ddr.h"
|
||||
#include "windows/iopanel_gfdm.h"
|
||||
#include "windows/iopanel_iidx.h"
|
||||
#include "windows/sdvx_sub.h"
|
||||
#include "windows/keypad.h"
|
||||
#include "windows/kfcontrol.h"
|
||||
@@ -29,14 +38,25 @@
|
||||
#include "windows/patch_manager.h"
|
||||
#include "windows/vr.h"
|
||||
|
||||
static inline ImVec4 operator*(const ImVec4& lhs, const ImVec4& rhs) \
|
||||
{ return ImVec4(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w); }
|
||||
|
||||
namespace overlay {
|
||||
|
||||
// settings
|
||||
bool ENABLED = true;
|
||||
bool AUTO_SHOW_FPS = false;
|
||||
bool AUTO_SHOW_SUBSCREEN = false;
|
||||
bool AUTO_SHOW_IOPANEL = false;
|
||||
bool AUTO_SHOW_KEYPAD_P1 = false;
|
||||
bool AUTO_SHOW_KEYPAD_P2 = false;
|
||||
|
||||
bool USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT = false;
|
||||
|
||||
// global
|
||||
std::mutex OVERLAY_MUTEX;
|
||||
std::unique_ptr<overlay::SpiceOverlay> OVERLAY = nullptr;
|
||||
ImFont* DSEG_FONT = nullptr;
|
||||
}
|
||||
|
||||
static void *ImGui_Alloc(size_t sz, void *user_data) {
|
||||
@@ -146,37 +166,71 @@ void overlay::SpiceOverlay::init() {
|
||||
style.WindowRounding = 0;
|
||||
}
|
||||
|
||||
// red theme
|
||||
/*
|
||||
// red theme based on:
|
||||
// https://github.com/ocornut/imgui/issues/707#issuecomment-760220280
|
||||
// r, g, b, a
|
||||
ImVec4* colors = ImGui::GetStyle().Colors;
|
||||
colors[ImGuiCol_Border] = ImVec4(0.50f, 0.43f, 0.43f, 0.50f);
|
||||
colors[ImGuiCol_FrameBg] = ImVec4(0.48f, 0.16f, 0.16f, 0.54f);
|
||||
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.98f, 0.26f, 0.26f, 0.40f);
|
||||
colors[ImGuiCol_FrameBgActive] = ImVec4(0.98f, 0.26f, 0.26f, 0.67f);
|
||||
// colors[ImGuiCol_Text] = ImVec4(0.75f, 0.75f, 0.75f, 1.00f);
|
||||
// colors[ImGuiCol_TextDisabled] = ImVec4(0.35f, 0.35f, 0.35f, 1.00f);
|
||||
|
||||
// colors[ImGuiCol_WindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.94f);
|
||||
// colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
// colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
|
||||
|
||||
colors[ImGuiCol_Border] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f);
|
||||
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
colors[ImGuiCol_FrameBg] = ImVec4(0.37f, 0.14f, 0.00f, 0.54f);
|
||||
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.37f, 0.14f, 0.14f, 0.67f);
|
||||
colors[ImGuiCol_FrameBgActive] = ImVec4(0.39f, 0.20f, 0.20f, 0.67f);
|
||||
colors[ImGuiCol_TitleBg] = ImVec4(0.04f, 0.04f, 0.04f, 1.00f);
|
||||
colors[ImGuiCol_TitleBgActive] = ImVec4(0.48f, 0.16f, 0.16f, 1.00f);
|
||||
colors[ImGuiCol_CheckMark] = ImVec4(0.98f, 0.26f, 0.26f, 1.00f);
|
||||
colors[ImGuiCol_SliderGrab] = ImVec4(0.88f, 0.24f, 0.24f, 1.00f);
|
||||
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.98f, 0.26f, 0.26f, 1.00f);
|
||||
colors[ImGuiCol_Button] = ImVec4(0.98f, 0.26f, 0.26f, 0.40f);
|
||||
colors[ImGuiCol_ButtonHovered] = ImVec4(0.98f, 0.26f, 0.26f, 1.00f);
|
||||
colors[ImGuiCol_ButtonActive] = ImVec4(0.98f, 0.06f, 0.06f, 1.00f);
|
||||
colors[ImGuiCol_Header] = ImVec4(0.98f, 0.26f, 0.26f, 0.31f);
|
||||
colors[ImGuiCol_HeaderHovered] = ImVec4(0.98f, 0.26f, 0.26f, 0.80f);
|
||||
colors[ImGuiCol_HeaderActive] = ImVec4(0.98f, 0.26f, 0.26f, 1.00f);
|
||||
colors[ImGuiCol_Separator] = ImVec4(0.50f, 0.43f, 0.43f, 0.50f);
|
||||
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.75f, 0.10f, 0.10f, 0.78f);
|
||||
colors[ImGuiCol_SeparatorActive] = ImVec4(0.75f, 0.10f, 0.10f, 1.00f);
|
||||
colors[ImGuiCol_ResizeGrip] = ImVec4(0.98f, 0.26f, 0.26f, 0.25f);
|
||||
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.98f, 0.26f, 0.26f, 0.67f);
|
||||
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.98f, 0.26f, 0.26f, 0.95f);
|
||||
colors[ImGuiCol_Tab] = ImVec4(0.58f, 0.18f, 0.18f, 0.86f);
|
||||
colors[ImGuiCol_TabHovered] = ImVec4(0.98f, 0.26f, 0.26f, 0.80f);
|
||||
colors[ImGuiCol_TabActive] = ImVec4(0.68f, 0.20f, 0.20f, 1.00f);
|
||||
colors[ImGuiCol_TabUnfocused] = ImVec4(0.15f, 0.07f, 0.07f, 0.97f);
|
||||
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.42f, 0.14f, 0.14f, 1.00f);
|
||||
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.98f, 0.26f, 0.26f, 0.35f);
|
||||
colors[ImGuiCol_NavHighlight] = ImVec4(0.98f, 0.26f, 0.26f, 1.00f);
|
||||
*/
|
||||
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.48f, 0.16f, 0.16f, 1.00f);
|
||||
colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f);
|
||||
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);
|
||||
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
|
||||
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
|
||||
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
|
||||
colors[ImGuiCol_CheckMark] = ImVec4(0.56f, 0.10f, 0.10f, 1.00f);
|
||||
colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 0.19f, 0.19f, 0.40f);
|
||||
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.89f, 0.00f, 0.19f, 1.00f);
|
||||
|
||||
colors[ImGuiCol_Button] = ImVec4(1.00f, 0.19f, 0.19f, 0.40f);
|
||||
colors[ImGuiCol_ButtonHovered] = ImVec4(0.80f, 0.17f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_ButtonActive] = ImVec4(0.89f, 0.00f, 0.19f, 1.00f);
|
||||
|
||||
colors[ImGuiCol_Header] = ImVec4(0.33f, 0.35f, 0.36f, 0.53f);
|
||||
colors[ImGuiCol_HeaderHovered] = ImVec4(0.76f, 0.28f, 0.44f, 0.67f);
|
||||
colors[ImGuiCol_HeaderActive] = ImVec4(0.47f, 0.47f, 0.47f, 0.67f);
|
||||
colors[ImGuiCol_Separator] = ImVec4(0.32f, 0.32f, 0.32f, 1.00f);
|
||||
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.32f, 0.32f, 0.32f, 1.00f);
|
||||
colors[ImGuiCol_SeparatorActive] = ImVec4(0.32f, 0.32f, 0.32f, 1.00f);
|
||||
colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.85f);
|
||||
colors[ImGuiCol_ResizeGripHovered] = ImVec4(1.00f, 1.00f, 1.00f, 0.60f);
|
||||
colors[ImGuiCol_ResizeGripActive] = ImVec4(1.00f, 1.00f, 1.00f, 0.90f);
|
||||
|
||||
colors[ImGuiCol_Tab] = colors[ImGuiCol_Button];
|
||||
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_ButtonHovered];
|
||||
colors[ImGuiCol_TabActive] = colors[ImGuiCol_ButtonActive];
|
||||
colors[ImGuiCol_TabUnfocused] = colors[ImGuiCol_Tab] * ImVec4(1.0f, 1.0f, 1.0f, 0.6f);
|
||||
colors[ImGuiCol_TabUnfocusedActive] = colors[ImGuiCol_TabActive] * ImVec4(1.0f, 1.0f, 1.0f, 0.6f);
|
||||
|
||||
colors[ImGuiCol_DockingPreview] = ImVec4(0.47f, 0.47f, 0.47f, 0.47f);
|
||||
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
|
||||
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
|
||||
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
||||
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_TableHeaderBg] = ImVec4(0.19f, 0.19f, 0.20f, 1.00f);
|
||||
colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);
|
||||
colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);
|
||||
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f);
|
||||
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
|
||||
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
|
||||
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
|
||||
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
|
||||
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
|
||||
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
|
||||
|
||||
// configure IO
|
||||
auto &io = ImGui::GetIO();
|
||||
@@ -219,6 +273,16 @@ void overlay::SpiceOverlay::init() {
|
||||
io.Fonts->AddFontFromFileTTF(R"(C:\Windows\Fonts\arial.ttf)",
|
||||
13.0f, &config, io.Fonts->GetGlyphRangesVietnamese());
|
||||
|
||||
// add special font
|
||||
if (avs::game::is_model("LDJ")) {
|
||||
DWORD size;
|
||||
ImFontConfig config {};
|
||||
config.FontDataOwnedByAtlas = false;
|
||||
auto buffer = resutil::load_file(IDR_DSEGFONT, &size);
|
||||
DSEG_FONT = io.Fonts->AddFontFromMemoryTTF((void *)buffer, size,
|
||||
overlay::windows::IIDX_SEGMENT_FONT_SIZE);
|
||||
}
|
||||
|
||||
// init implementation
|
||||
ImGui_ImplSpice_Init(this->hWnd);
|
||||
switch (this->renderer) {
|
||||
@@ -246,27 +310,88 @@ void overlay::SpiceOverlay::init() {
|
||||
ImGui::Begin("Temp");
|
||||
ImGui::End();
|
||||
|
||||
bool set_overlay_active = false;
|
||||
|
||||
// referenced windows
|
||||
this->window_add(window_fps = new overlay::windows::FPS(this));
|
||||
if (!cfg::CONFIGURATOR_STANDALONE && AUTO_SHOW_FPS) {
|
||||
window_fps->set_active(true);
|
||||
set_overlay_active = true;
|
||||
}
|
||||
|
||||
// add default windows
|
||||
this->window_add(new overlay::windows::Config(this));
|
||||
this->window_add(new overlay::windows::Control(this));
|
||||
this->window_add(new overlay::windows::Log(this));
|
||||
this->window_add(new overlay::windows::CardManager(this));
|
||||
this->window_add(new overlay::windows::ScreenResize(this));
|
||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||
this->window_add(new overlay::windows::ScreenResize(this));
|
||||
}
|
||||
this->window_add(new overlay::windows::PatchManager(this));
|
||||
this->window_add(new overlay::windows::Keypad(this, 0));
|
||||
this->window_add(new overlay::windows::KFControl(this));
|
||||
this->window_add(new overlay::windows::VRWindow(this));
|
||||
|
||||
{
|
||||
const auto keypad_p1 = new overlay::windows::Keypad(this, 0);
|
||||
this->window_add(keypad_p1);
|
||||
if (!cfg::CONFIGURATOR_STANDALONE && AUTO_SHOW_KEYPAD_P1) {
|
||||
keypad_p1->set_active(true);
|
||||
set_overlay_active = true;
|
||||
}
|
||||
}
|
||||
if (eamuse_get_game_keypads() > 1) {
|
||||
this->window_add(new overlay::windows::Keypad(this, 1));
|
||||
const auto keypad_p2 = new overlay::windows::Keypad(this, 1);
|
||||
this->window_add(keypad_p2);
|
||||
if (!cfg::CONFIGURATOR_STANDALONE && AUTO_SHOW_KEYPAD_P2) {
|
||||
keypad_p2->set_active(true);
|
||||
set_overlay_active = true;
|
||||
}
|
||||
}
|
||||
if (avs::game::is_model("LDJ")) {
|
||||
this->window_add(new overlay::windows::IIDXSubScreen(this));
|
||||
|
||||
// IO panel needs to know what game is running
|
||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||
overlay::Window *iopanel = nullptr;
|
||||
if (avs::game::is_model("LDJ")) {
|
||||
iopanel = new overlay::windows::IIDXIOPanel(this);
|
||||
} else if (avs::game::is_model("MDX")) {
|
||||
iopanel = new overlay::windows::DDRIOPanel(this);
|
||||
} else if (avs::game::is_model({"J32", "J33", "K32", "K33", "L32", "L33", "M32"})) {
|
||||
iopanel = new overlay::windows::GitadoraIOPanel(this);
|
||||
} else {
|
||||
iopanel = new overlay::windows::IOPanel(this);
|
||||
}
|
||||
if (iopanel) {
|
||||
this->window_add(iopanel);
|
||||
if (AUTO_SHOW_IOPANEL) {
|
||||
iopanel->set_active(true);
|
||||
set_overlay_active = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (avs::game::is_model("KFC")) {
|
||||
this->window_add(new overlay::windows::SDVXSubScreen(this));
|
||||
|
||||
// subscreens need DirectX, so don't try to initialize them in standalone
|
||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||
overlay::Window *subscreen = nullptr;
|
||||
if (avs::game::is_model("LDJ")) {
|
||||
if (games::iidx::TDJ_MODE) {
|
||||
subscreen = new overlay::windows::IIDXSubScreen(this);
|
||||
} else {
|
||||
subscreen = new overlay::windows::IIDXSegmentDisplay(this);
|
||||
}
|
||||
} else if (avs::game::is_model("KFC")) {
|
||||
subscreen = new overlay::windows::SDVXSubScreen(this);
|
||||
}
|
||||
if (subscreen) {
|
||||
this->window_add(subscreen);
|
||||
if (AUTO_SHOW_SUBSCREEN) {
|
||||
subscreen->set_active(true);
|
||||
set_overlay_active = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (set_overlay_active) {
|
||||
this->set_active(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -533,15 +658,7 @@ void overlay::SpiceOverlay::reset_recreate() {
|
||||
ImGui_ImplDX9_CreateDeviceObjects();
|
||||
}
|
||||
|
||||
void overlay::SpiceOverlay::input_char(unsigned int c, bool rawinput) {
|
||||
|
||||
// disable rawinput characters once we got one from somewhere else (such as a WndProc)
|
||||
if (!rawinput) {
|
||||
this->rawinput_char = false;
|
||||
} else if (!this->rawinput_char) {
|
||||
return;
|
||||
}
|
||||
|
||||
void overlay::SpiceOverlay::input_char(unsigned int c) {
|
||||
// add character to ImGui
|
||||
ImGui::GetIO().AddInputCharacter(c);
|
||||
}
|
||||
|
||||
+27
-2
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <windows.h>
|
||||
#include <d3d9.h>
|
||||
@@ -18,6 +19,12 @@ namespace overlay {
|
||||
|
||||
// settings
|
||||
extern bool ENABLED;
|
||||
extern bool AUTO_SHOW_FPS;
|
||||
extern bool AUTO_SHOW_SUBSCREEN;
|
||||
extern bool AUTO_SHOW_IOPANEL;
|
||||
extern bool AUTO_SHOW_KEYPAD_P1;
|
||||
extern bool AUTO_SHOW_KEYPAD_P2;
|
||||
extern bool USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT;
|
||||
|
||||
class SpiceOverlay {
|
||||
public:
|
||||
@@ -43,7 +50,7 @@ namespace overlay {
|
||||
static bool update_cursor();
|
||||
static void reset_invalidate();
|
||||
static void reset_recreate();
|
||||
void input_char(unsigned int c, bool rawinput = false);
|
||||
void input_char(unsigned int c);
|
||||
|
||||
uint32_t *sw_get_pixel_data(int *width, int *height);
|
||||
|
||||
@@ -60,6 +67,22 @@ namespace overlay {
|
||||
return this->device;
|
||||
}
|
||||
|
||||
bool can_transform_touch_input() {
|
||||
return (this->subscreen_mouse_handler != nullptr);
|
||||
}
|
||||
|
||||
bool transform_touch_point(LONG *x, LONG *y) {
|
||||
if (this->get_active() && this->subscreen_mouse_handler) {
|
||||
return this->subscreen_mouse_handler(x, y);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void set_subscreen_mouse_handler(const std::function<bool(LONG *, LONG *)> &f) {
|
||||
this->subscreen_mouse_handler = f;
|
||||
}
|
||||
|
||||
// renderer
|
||||
OverlayRenderer renderer;
|
||||
float total_elapsed = 0.f;
|
||||
@@ -80,9 +103,10 @@ namespace overlay {
|
||||
std::vector<std::unique_ptr<Window>> windows;
|
||||
Window *window_fps = nullptr;
|
||||
|
||||
std::function<bool(LONG *, LONG *)> subscreen_mouse_handler = nullptr;
|
||||
|
||||
bool active = false;
|
||||
bool toggle_down = false;
|
||||
bool rawinput_char = true;
|
||||
bool hotkey_toggle = false;
|
||||
bool hotkey_toggle_last = false;
|
||||
|
||||
@@ -92,6 +116,7 @@ namespace overlay {
|
||||
// global
|
||||
extern std::mutex OVERLAY_MUTEX;
|
||||
extern std::unique_ptr<overlay::SpiceOverlay> OVERLAY;
|
||||
extern ImFont* DSEG_FONT;
|
||||
|
||||
// synchronized helpers
|
||||
void create_d3d9(HWND hWnd, IDirect3D9 *d3d, IDirect3DDevice9 *device);
|
||||
|
||||
+17
-3
@@ -65,9 +65,9 @@ void overlay::Window::build() {
|
||||
if (!cfg::CONFIGURATOR_STANDALONE && (size_max.x < 0 || size_max.y < 0)) {
|
||||
auto &display_size = ImGui::GetIO().DisplaySize;
|
||||
ImVec2 size_max_auto(display_size.x - 100, display_size.y - 100);
|
||||
ImGui::SetNextWindowSizeConstraints(size_min, size_max_auto);
|
||||
ImGui::SetNextWindowSizeConstraints(size_min, size_max_auto, resize_callback);
|
||||
} else {
|
||||
ImGui::SetNextWindowSizeConstraints(size_min, size_max);
|
||||
ImGui::SetNextWindowSizeConstraints(size_min, size_max, resize_callback);
|
||||
}
|
||||
|
||||
// background alpha
|
||||
@@ -75,6 +75,12 @@ void overlay::Window::build() {
|
||||
ImGui::SetNextWindowBgAlpha(this->bg_alpha);
|
||||
}
|
||||
|
||||
if (this->remove_window_padding) {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
}
|
||||
|
||||
// create window
|
||||
if (ImGui::Begin(
|
||||
(this->title + "###" + to_string(this)).c_str(),
|
||||
@@ -82,7 +88,8 @@ void overlay::Window::build() {
|
||||
this->flags)) {
|
||||
|
||||
// window attributes
|
||||
ImGui::SetWindowPos(this->initial_pos(), ImGuiCond_Once);
|
||||
this->calculate_initial_window();
|
||||
ImGui::SetWindowPos(this->init_pos, ImGuiCond_Once);
|
||||
ImGui::SetWindowSize(this->init_size, ImGuiCond_Once);
|
||||
|
||||
// add content
|
||||
@@ -96,6 +103,13 @@ void overlay::Window::build() {
|
||||
// end window
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (this->remove_window_padding) {
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// add raw content
|
||||
|
||||
+7
-4
@@ -10,9 +10,11 @@ namespace overlay {
|
||||
class Window {
|
||||
public:
|
||||
virtual ~Window();
|
||||
virtual const ImVec2 initial_pos() {
|
||||
return this->init_pos;
|
||||
}
|
||||
|
||||
// an opportunity to calculate initial window position and size within the ImGui
|
||||
// window context, since it may not be possible in the Window constructor
|
||||
virtual void calculate_initial_window() {}
|
||||
|
||||
virtual void build_content() = 0;
|
||||
virtual void update();
|
||||
virtual void after_render() {};
|
||||
@@ -21,7 +23,6 @@ namespace overlay {
|
||||
void toggle_active();
|
||||
void set_active(bool active);
|
||||
bool get_active();
|
||||
|
||||
protected:
|
||||
|
||||
// state
|
||||
@@ -30,7 +31,9 @@ namespace overlay {
|
||||
std::vector<Window*> children;
|
||||
|
||||
// settings
|
||||
bool remove_window_padding = false;
|
||||
bool draws_window = true;
|
||||
ImGuiSizeCallback resize_callback = nullptr;
|
||||
std::string title = "Title";
|
||||
ImGuiWindowFlags flags = 0;
|
||||
size_t toggle_button = ~0u;
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace overlay::windows {
|
||||
// configure editor defaults
|
||||
this->editor = new MemoryEditor();
|
||||
this->editor->OptShowDataPreview = true;
|
||||
this->editor->PreviewDataType = MemoryEditor::DataType::DataType_U16;
|
||||
this->editor->PreviewDataType = ImGuiDataType_U16;
|
||||
}
|
||||
|
||||
ACIOStatusBuffers::~ACIOStatusBuffers() {
|
||||
|
||||
+286
-222
@@ -4,6 +4,7 @@
|
||||
#include <thread>
|
||||
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
#include <commdlg.h>
|
||||
|
||||
#include "build/defs.h"
|
||||
@@ -17,6 +18,7 @@
|
||||
#include "avs/game.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "launcher/shutdown.h"
|
||||
#include "launcher/options.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "overlay/imgui/extensions.h"
|
||||
#include "rawinput/piuio.h"
|
||||
@@ -34,6 +36,8 @@
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
const auto PROJECT_URL = "https://spice2x.github.io";
|
||||
|
||||
Config::Config(overlay::SpiceOverlay *overlay) : Window(overlay) {
|
||||
this->title = "Configuration";
|
||||
this->toggle_button = games::OverlayButtons::ToggleConfig;
|
||||
@@ -238,6 +242,29 @@ namespace overlay::windows {
|
||||
|
||||
// keypad buttons
|
||||
ImGui::TextUnformatted("");
|
||||
if (this->games_selected_name == "Sound Voltex") {
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 8);
|
||||
ImGui::TextColored(
|
||||
ImVec4(1, 0.5f, 0.5f, 1.f),
|
||||
"WARNING: Valkyrie Cabinet I/O will ignore the keypad!");
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 8);
|
||||
ImGui::TextWrapped(
|
||||
"Use Toggle Sub Screen button to show the overlay and use your mouse, "
|
||||
"connect using SpiceCompanion app, or connect a touch screen to enter "
|
||||
"the PIN.");
|
||||
ImGui::TextUnformatted("");
|
||||
} else if (this->games_selected_name == "Beatmania IIDX") {
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 8);
|
||||
ImGui::TextColored(
|
||||
ImVec4(1, 0.5f, 0.5f, 1.f),
|
||||
"WARNING: Lightning Model (TDJ) I/O will ignore the keypad!");
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 8);
|
||||
ImGui::TextWrapped(
|
||||
"Use Toggle Sub Screen button to show the overlay and use your mouse, "
|
||||
"connect using SpiceCompanion app, or connect a touch screen to enter "
|
||||
"the PIN.");
|
||||
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) {
|
||||
@@ -336,39 +363,44 @@ namespace overlay::windows {
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Options")) {
|
||||
// options list
|
||||
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<const char *> 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());
|
||||
}
|
||||
}
|
||||
for (auto category : launcher::get_categories(false)) {
|
||||
this->build_options(options, category);
|
||||
}
|
||||
|
||||
// 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 = "";
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 8);
|
||||
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Advanced Options");
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 8);
|
||||
ImGui::TextWrapped(
|
||||
"Rest of the options have moved to Advanced tab.");
|
||||
ImGui::TextUnformatted("");
|
||||
ImGui::TextUnformatted("");
|
||||
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.");
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Advanced")) {
|
||||
// options list
|
||||
this->build_options(options, category);
|
||||
ImGui::BeginChild("AdvancedOptions", ImVec2(
|
||||
0, ImGui::GetWindowContentRegionMax().y - page_offset), false);
|
||||
auto options = games::get_options(this->games_selected_name);
|
||||
for (auto category : launcher::get_categories(true)) {
|
||||
this->build_options(options, category);
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
// hidden options checkbox
|
||||
@@ -402,12 +434,6 @@ namespace overlay::windows {
|
||||
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")) {
|
||||
@@ -488,10 +514,10 @@ namespace overlay::windows {
|
||||
style_color = true;
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.f));
|
||||
}
|
||||
vertical_align_text_column();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("%s", button_name.c_str());
|
||||
ImGui::NextColumn();
|
||||
vertical_align_text_column();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("%s", button_display.empty() ? "None" : button_display.c_str());
|
||||
ImGui::NextColumn();
|
||||
if (style_color) {
|
||||
@@ -622,8 +648,10 @@ namespace overlay::windows {
|
||||
case rawinput::HID: {
|
||||
auto hid = device->hidInfo;
|
||||
|
||||
// ignore touchscreen button inputs
|
||||
if (!rawinput::touch::is_touchscreen(device)) {
|
||||
// ignore touchscreen and digitizer button inputs
|
||||
// digitizer has funky stuff like "Touch Valid" "Data Valid" always held high
|
||||
if (!rawinput::touch::is_touchscreen(device) &&
|
||||
hid->caps.UsagePage != 0x0D) {
|
||||
|
||||
// button caps
|
||||
auto button_states_list = &hid->button_states;
|
||||
@@ -1179,7 +1207,7 @@ namespace overlay::windows {
|
||||
}
|
||||
ImGui::Text("%s", analog_name.c_str());
|
||||
ImGui::NextColumn();
|
||||
vertical_align_text_column();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("%s", analog_display.empty() ? "None" : analog_display.c_str());
|
||||
ImGui::NextColumn();
|
||||
if (analog_display.empty()) {
|
||||
@@ -1502,7 +1530,7 @@ namespace overlay::windows {
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s", light_name.c_str());
|
||||
ImGui::NextColumn();
|
||||
vertical_align_text_column();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("%s", light_display.empty() ? "None" : light_display.c_str());
|
||||
ImGui::NextColumn();
|
||||
if (light_display.empty()) {
|
||||
@@ -1807,7 +1835,7 @@ namespace overlay::windows {
|
||||
if (!cfg::CONFIGURATOR_STANDALONE && !this->keypads_card_select_browser[player].IsOpened()) {
|
||||
this->keypads_card_select_browser[player].SetTitle("Card Select");
|
||||
this->keypads_card_select_browser[player].SetTypeFilters({".txt", "*"});
|
||||
this->keypads_card_select_browser[player].flags_ |= ImGuiFileBrowserFlags_EnterNewFilename;
|
||||
// this->keypads_card_select_browser[player].flags_ |= ImGuiFileBrowserFlags_EnterNewFilename;
|
||||
this->keypads_card_select_browser[player].Open();
|
||||
}
|
||||
}
|
||||
@@ -1903,218 +1931,258 @@ namespace overlay::windows {
|
||||
}
|
||||
|
||||
void Config::build_options(std::vector<Option> *options, const std::string &category) {
|
||||
int options_count;
|
||||
|
||||
ImGui::Columns(3, "OptionsColumns", true);
|
||||
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Title"); ImGui::NextColumn();
|
||||
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), category.c_str());
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Parameter");
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker("These are the command-line parameters you can use in your .bat file to set the options.\n"
|
||||
"Example: spice.exe -w -ea\n"
|
||||
" spice64.exe -api 1337 -apipass changeme");
|
||||
ImGui::HelpMarker(
|
||||
"These are the command-line parameters you can use in your .bat file to set the options.\n"
|
||||
"Example: spice.exe -w -ea\n"
|
||||
" spice64.exe -api 1337 -apipass changeme");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Setting");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Setting"); ImGui::NextColumn();
|
||||
ImGui::Separator();
|
||||
|
||||
// check if empty
|
||||
if (!options || options->empty()) {
|
||||
ImGui::TextUnformatted("-");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextUnformatted("-");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextUnformatted("-");
|
||||
ImGui::NextColumn();
|
||||
}
|
||||
|
||||
// iterate options
|
||||
if (options) {
|
||||
for (auto &option : *options) {
|
||||
options_count = 0;
|
||||
for (auto &option : *options) {
|
||||
// get option definition
|
||||
auto &definition = option.get_definition();
|
||||
|
||||
// get option definition
|
||||
auto &definition = option.get_definition();
|
||||
// check category
|
||||
if (!category.empty() && definition.category != category) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check category
|
||||
if (!category.empty() && definition.category != category) {
|
||||
// check hidden option
|
||||
if (!this->options_show_hidden && option.value.empty()) {
|
||||
|
||||
// skip hidden entries
|
||||
if (definition.hidden) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check hidden option
|
||||
if (!this->options_show_hidden && option.value.empty()) {
|
||||
|
||||
// skip hidden entries
|
||||
if (definition.hidden) {
|
||||
// check for game exclusivity
|
||||
if (!definition.game_name.empty()) {
|
||||
if (definition.game_name != this->games_selected_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for game exclusivity
|
||||
if (!definition.game_name.empty()) {
|
||||
if (definition.game_name != this->games_selected_name) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// list entry
|
||||
ImGui::PushID(&option);
|
||||
vertical_align_text_column();
|
||||
ImGui::HelpMarker(definition.desc.c_str());
|
||||
ImGui::SameLine();
|
||||
if (option.is_active()) {
|
||||
if (option.disabled) {
|
||||
ImGui::TextColored(ImVec4(1.f, 0.4f, 0.f, 1.f), "%s", definition.title.c_str());
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(1.f, 0.7f, 0.f, 1.f), "%s", definition.title.c_str());
|
||||
}
|
||||
} else if (definition.hidden
|
||||
|| (!definition.game_name.empty() && definition.game_name != this->games_selected_name)) {
|
||||
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.f), "%s", definition.title.c_str());
|
||||
} else if (definition.game_name == this->games_selected_name) {
|
||||
ImGui::TextColored(ImVec4(0.8f, 0, 0.8f, 1.f), "%s", definition.title.c_str());
|
||||
} else {
|
||||
ImGui::Text("%s", definition.title.c_str());
|
||||
}
|
||||
ImGui::NextColumn();
|
||||
vertical_align_text_column();
|
||||
ImGui::Text("-%s", definition.name.c_str());
|
||||
ImGui::NextColumn();
|
||||
options_count += 1;
|
||||
|
||||
// list entry
|
||||
ImGui::PushID(&option);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::HelpMarker(definition.desc.c_str());
|
||||
ImGui::SameLine();
|
||||
if (option.is_active()) {
|
||||
if (option.disabled) {
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
|
||||
ImGui::TextColored(ImVec4(1.f, 0.4f, 0.f, 1.f), "%s", definition.title.c_str());
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(1.f, 0.7f, 0.f, 1.f), "%s", definition.title.c_str());
|
||||
}
|
||||
switch (definition.type) {
|
||||
case OptionType::Bool: {
|
||||
bool state = !option.value.empty();
|
||||
if (ImGui::Checkbox(state ? "Enabled" : "Disabled", &state)) {
|
||||
this->options_dirty = true;
|
||||
option.value = state ? "/ENABLED" : "";
|
||||
::Config::getInstance().updateBinding(games_list[games_selected], option);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OptionType::Integer: {
|
||||
char buffer[512];
|
||||
strncpy(buffer, option.value.c_str(), sizeof(buffer) - 1);
|
||||
buffer[sizeof(buffer) - 1] = '\0';
|
||||
auto digits_filter = [](ImGuiInputTextCallbackData* data) {
|
||||
if ('0' <= data->EventChar && data->EventChar <= '9') {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
|
||||
const char *hint = definition.setting_name.empty() ? "Enter number..."
|
||||
: definition.setting_name.c_str();
|
||||
|
||||
if (ImGui::InputTextWithHint("", hint,
|
||||
buffer, sizeof(buffer) - 1,
|
||||
ImGuiInputTextFlags_CallbackCharFilter, digits_filter)) {
|
||||
this->options_dirty = true;
|
||||
option.value = buffer;
|
||||
::Config::getInstance().updateBinding(games_list[games_selected], option);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OptionType::Text: {
|
||||
char buffer[512];
|
||||
strncpy(buffer, option.value.c_str(), sizeof(buffer) - 1);
|
||||
buffer[sizeof(buffer) - 1] = '\0';
|
||||
|
||||
const char *hint = definition.setting_name.empty() ? "Enter value..."
|
||||
: definition.setting_name.c_str();
|
||||
|
||||
if (ImGui::InputTextWithHint("", hint, buffer, sizeof(buffer) - 1)) {
|
||||
this->options_dirty = true;
|
||||
option.value = buffer;
|
||||
::Config::getInstance().updateBinding(games_list[games_selected], option);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OptionType::Enum: {
|
||||
std::string current_item = option.value_text();
|
||||
for (auto &element : definition.elements) {
|
||||
if (element.first == current_item) {
|
||||
current_item += fmt::format(" ({})", element.second);
|
||||
}
|
||||
}
|
||||
if (current_item.empty()) {
|
||||
current_item = "Default";
|
||||
}
|
||||
if (ImGui::BeginCombo("##combo", current_item.c_str(), 0)) {
|
||||
for (auto &element : definition.elements) {
|
||||
bool selected = current_item == element.first;
|
||||
std::string label = element.first;
|
||||
if (!element.second.empty()) {
|
||||
label += fmt::format(" ({})", element.second);
|
||||
}
|
||||
if (ImGui::Selectable(label.c_str(), selected)) {
|
||||
this->options_dirty = true;
|
||||
option.value = element.first;
|
||||
::Config::getInstance().updateBinding(games_list[games_selected], option);
|
||||
}
|
||||
if (selected) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
ImGui::Text("Unknown option type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// clear button
|
||||
if (!option.disabled && option.is_active() && option.get_definition().type != OptionType::Bool) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Clear")) {
|
||||
} else if (definition.hidden
|
||||
|| (!definition.game_name.empty() && definition.game_name != this->games_selected_name)) {
|
||||
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.f), "%s", definition.title.c_str());
|
||||
} else if (definition.game_name == this->games_selected_name) {
|
||||
ImGui::TextColored(ImVec4(0.8f, 0, 0.8f, 1.f), "%s", definition.title.c_str());
|
||||
} else {
|
||||
ImGui::Text("%s", definition.title.c_str());
|
||||
}
|
||||
ImGui::NextColumn();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("-%s", definition.name.c_str());
|
||||
ImGui::NextColumn();
|
||||
if (option.disabled) {
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
|
||||
}
|
||||
switch (definition.type) {
|
||||
case OptionType::Bool: {
|
||||
bool state = !option.value.empty();
|
||||
if (ImGui::Checkbox(state ? "Enabled" : "Disabled", &state)) {
|
||||
this->options_dirty = true;
|
||||
option.value = "";
|
||||
option.value = state ? "/ENABLED" : "";
|
||||
::Config::getInstance().updateBinding(games_list[games_selected], option);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OptionType::Integer: {
|
||||
char buffer[512];
|
||||
strncpy(buffer, option.value.c_str(), sizeof(buffer) - 1);
|
||||
buffer[sizeof(buffer) - 1] = '\0';
|
||||
auto digits_filter = [](ImGuiInputTextCallbackData* data) {
|
||||
if ('0' <= data->EventChar && data->EventChar <= '9') {
|
||||
return 0;
|
||||
}
|
||||
return 1; // discard
|
||||
};
|
||||
|
||||
// clean up disabled item flags
|
||||
if (option.disabled) {
|
||||
ImGui::PopItemFlag();
|
||||
ImGui::PopStyleVar();
|
||||
const char *hint = definition.setting_name.empty() ? "Enter number..."
|
||||
: definition.setting_name.c_str();
|
||||
|
||||
if (ImGui::InputTextWithHint("", hint,
|
||||
buffer, sizeof(buffer) - 1,
|
||||
ImGuiInputTextFlags_CallbackCharFilter, digits_filter)) {
|
||||
this->options_dirty = true;
|
||||
option.value = buffer;
|
||||
::Config::getInstance().updateBinding(games_list[games_selected], option);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OptionType::Hex: {
|
||||
char buffer[512];
|
||||
strncpy(buffer, option.value.c_str(), sizeof(buffer) - 1);
|
||||
buffer[sizeof(buffer) - 1] = '\0';
|
||||
auto digits_filter = [](ImGuiInputTextCallbackData* data) {
|
||||
if ('0' <= data->EventChar && data->EventChar <= '9') {
|
||||
return 0;
|
||||
}
|
||||
if ('a' <= data->EventChar && data->EventChar <= 'f') {
|
||||
return 0;
|
||||
}
|
||||
if ('A' <= data->EventChar && data->EventChar <= 'F') {
|
||||
return 0;
|
||||
}
|
||||
if (data->EventChar == 'x' || data->EventChar == 'X') {
|
||||
return 0;
|
||||
}
|
||||
return 1; // discard
|
||||
};
|
||||
const char *hint = definition.setting_name.empty() ? "Enter hex..."
|
||||
: definition.setting_name.c_str();
|
||||
|
||||
// disabled help
|
||||
if (option.disabled) {
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker("This option can not be edited because it was overriden by command-line "
|
||||
"options.\n"
|
||||
"Run spicecfg.exe to configure the options and then run spice(64).exe directly.");
|
||||
if (ImGui::InputTextWithHint("", hint,
|
||||
buffer, sizeof(buffer) - 1,
|
||||
ImGuiInputTextFlags_CallbackCharFilter, digits_filter)) {
|
||||
this->options_dirty = true;
|
||||
option.value = buffer;
|
||||
::Config::getInstance().updateBinding(games_list[games_selected], option);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OptionType::Text: {
|
||||
char buffer[512];
|
||||
strncpy(buffer, option.value.c_str(), sizeof(buffer) - 1);
|
||||
buffer[sizeof(buffer) - 1] = '\0';
|
||||
|
||||
// next item
|
||||
ImGui::PopID();
|
||||
ImGui::NextColumn();
|
||||
const char *hint = definition.setting_name.empty() ? "Enter value..."
|
||||
: definition.setting_name.c_str();
|
||||
|
||||
if (ImGui::InputTextWithHint("", hint, buffer, sizeof(buffer) - 1)) {
|
||||
this->options_dirty = true;
|
||||
option.value = buffer;
|
||||
::Config::getInstance().updateBinding(games_list[games_selected], option);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OptionType::Enum: {
|
||||
std::string current_item = option.value_text();
|
||||
for (auto &element : definition.elements) {
|
||||
if (element.first == current_item) {
|
||||
current_item += fmt::format(" ({})", element.second);
|
||||
}
|
||||
}
|
||||
if (current_item.empty()) {
|
||||
current_item = "Default";
|
||||
}
|
||||
if (ImGui::BeginCombo("##combo", current_item.c_str(), 0)) {
|
||||
for (auto &element : definition.elements) {
|
||||
bool selected = current_item == element.first;
|
||||
std::string label = element.first;
|
||||
if (!element.second.empty()) {
|
||||
label += fmt::format(" ({})", element.second);
|
||||
}
|
||||
if (ImGui::Selectable(label.c_str(), selected)) {
|
||||
this->options_dirty = true;
|
||||
option.value = element.first;
|
||||
::Config::getInstance().updateBinding(games_list[games_selected], option);
|
||||
}
|
||||
if (selected) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
ImGui::Text("Unknown option type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// clear button
|
||||
if (!option.disabled && option.is_active() && option.get_definition().type != OptionType::Bool) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Clear")) {
|
||||
this->options_dirty = true;
|
||||
option.value = "";
|
||||
::Config::getInstance().updateBinding(games_list[games_selected], option);
|
||||
}
|
||||
}
|
||||
|
||||
// clean up disabled item flags
|
||||
if (option.disabled) {
|
||||
ImGui::PopItemFlag();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
// disabled help
|
||||
if (option.disabled) {
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker(
|
||||
"This option can not be edited because it was overriden by command-line options.\n"
|
||||
"Run spicecfg.exe to configure the options and then run spice(64).exe directly.");
|
||||
}
|
||||
|
||||
// next item
|
||||
ImGui::PopID();
|
||||
ImGui::NextColumn();
|
||||
}
|
||||
|
||||
// check if empty
|
||||
if (options_count == 0) {
|
||||
ImGui::TextUnformatted("-");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextUnformatted("-");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextUnformatted("-");
|
||||
ImGui::NextColumn();
|
||||
}
|
||||
|
||||
ImGui::Columns(1);
|
||||
ImGui::TextUnformatted("");
|
||||
}
|
||||
|
||||
void Config::build_about() {
|
||||
auto time = get_system_milliseconds();
|
||||
auto value = ((float) cos(time * 0.0005) + 1.f) * 0.5f;
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.8f - value * 0.3f, 1.f, 1.f, 1.f));
|
||||
ImGui::TextUnformatted(std::string(
|
||||
"SpiceTools\r\n"
|
||||
"=========================\r\n" +
|
||||
to_string(VERSION_STRING) + "\r\n\r\n" + to_string(
|
||||
resutil::load_file_string_crlf(IDR_README)) +
|
||||
"\r\n" +
|
||||
"Changelog (Highlights):\r\n" +
|
||||
resutil::load_file_string_crlf(IDR_CHANGELOG)).c_str());
|
||||
ImGui::PopStyleColor();
|
||||
"spice2x (a fork of SpiceTools)\r\n"
|
||||
"=========================\r\n" +
|
||||
to_string(VERSION_STRING)).c_str());
|
||||
|
||||
ImGui::TextUnformatted("");
|
||||
|
||||
if (ImGui::Button(PROJECT_URL)) {
|
||||
launch_url();
|
||||
}
|
||||
|
||||
ImGui::TextUnformatted("");
|
||||
ImGui::TextUnformatted(std::string(
|
||||
resutil::load_file_string_crlf(IDR_README) +
|
||||
"\r\n" +
|
||||
"Changelog (Highlights):\r\n" +
|
||||
resutil::load_file_string_crlf(IDR_CHANGELOG)).c_str());
|
||||
}
|
||||
|
||||
void Config::build_licenses() {
|
||||
auto time = get_system_milliseconds();
|
||||
auto value = ((float) cos(time * 0.0005) + 1.f) * 0.5f;
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 0.9f - value * 0.3f, 1.f));
|
||||
ImGui::TextUnformatted(resutil::load_file_string_crlf(IDR_LICENSES).c_str());
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
void Config::build_launcher() {
|
||||
@@ -2156,15 +2224,11 @@ namespace overlay::windows {
|
||||
"at the same time using pages.");
|
||||
}
|
||||
|
||||
void Config::vertical_align_text_column() {
|
||||
/*
|
||||
* see https://github.com/ocornut/imgui/issues/1284
|
||||
* height of most widgets (like Button) is GetFontSize() + GetStyle().FramePadding.y * 2
|
||||
* height of unformatted text is GetFontSize()
|
||||
* by default, rows are top-aligned
|
||||
* so we add FramePadding.y to the top of unformatted text for vertical centering.
|
||||
*/
|
||||
auto y = ImGui::GetCursorPosY() + ImGui::GetStyle().FramePadding.y;
|
||||
ImGui::SetCursorPosY(y);
|
||||
void Config::launch_url() {
|
||||
// doing this on a separate thread to avoid polluting ImGui context
|
||||
std::thread t([] {
|
||||
ShellExecuteA(NULL, "open", PROJECT_URL, NULL, NULL, SW_SHOWNORMAL);
|
||||
});
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,8 +69,8 @@ namespace overlay::windows {
|
||||
void build_about();
|
||||
void build_licenses();
|
||||
void build_launcher();
|
||||
void launch_url();
|
||||
|
||||
static void build_page_selector(int *page);
|
||||
static void vertical_align_text_column();
|
||||
};
|
||||
}
|
||||
|
||||
+31
-8
@@ -1,29 +1,52 @@
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include "fps.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
FPS::FPS(SpiceOverlay *overlay) : Window(overlay) {
|
||||
this->title = "FPS & Frame Time";
|
||||
this->title = "Stats";
|
||||
this->flags = ImGuiWindowFlags_NoTitleBar
|
||||
| ImGuiWindowFlags_NoCollapse
|
||||
| ImGuiWindowFlags_NoResize
|
||||
| ImGuiWindowFlags_NoCollapse
|
||||
| ImGuiWindowFlags_AlwaysAutoResize
|
||||
| ImGuiWindowFlags_NoDecoration
|
||||
| ImGuiWindowFlags_NoFocusOnAppearing
|
||||
| ImGuiWindowFlags_NoNavFocus
|
||||
| ImGuiWindowFlags_NoNavInputs;
|
||||
| ImGuiWindowFlags_NoNavInputs
|
||||
| ImGuiWindowFlags_NoDocking;
|
||||
this->bg_alpha = 0.4f;
|
||||
this->start_time = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
const ImVec2 FPS::initial_pos() {
|
||||
return ImVec2(ImGui::GetIO().DisplaySize.x - 100, 10);
|
||||
void FPS::calculate_initial_window() {
|
||||
// width is 114x82 px with window decoration, 98x47 for the content
|
||||
this->init_pos = ImVec2(ImGui::GetIO().DisplaySize.x - 120, 8);
|
||||
}
|
||||
|
||||
void FPS::build_content() {
|
||||
|
||||
// frame timers
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
ImGui::Text("FPS: %.1f", io.Framerate);
|
||||
ImGui::Text("FT: %.2fms", 1000 / io.Framerate);
|
||||
ImGui::Text("FPS: %.1f", io.Framerate);
|
||||
// ImGui::Text("FT: %.2fms", 1000 / io.Framerate);
|
||||
|
||||
auto now = std::chrono::system_clock::now();
|
||||
|
||||
// current time
|
||||
{
|
||||
auto now_t = std::chrono::system_clock::to_time_t(now);
|
||||
static CHAR buf[48];
|
||||
std::strftime(buf, sizeof(buf), "Time: %H:%M:%S", std::localtime(&now_t));
|
||||
ImGui::Text(buf);
|
||||
}
|
||||
|
||||
// elapsed time
|
||||
{
|
||||
auto d = now - this->start_time;
|
||||
const auto h = std::chrono::duration_cast<std::chrono::hours>(d);
|
||||
const auto m = std::chrono::duration_cast<std::chrono::minutes>(d - h);
|
||||
const auto s = std::chrono::duration_cast<std::chrono::seconds>(d - h - m);
|
||||
ImGui::Text("Up: %02d:%02d:%02d", (int)h.count(), (int)m.count(), (int)s.count());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include "overlay/window.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
class FPS : public Window {
|
||||
private:
|
||||
std::chrono::system_clock::time_point start_time;
|
||||
|
||||
public:
|
||||
|
||||
FPS(SpiceOverlay *overlay);
|
||||
|
||||
const ImVec2 initial_pos() override;
|
||||
void calculate_initial_window() override;
|
||||
void build_content() override;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
#undef CINTERFACE
|
||||
|
||||
#include "generic_sub.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "games/io.h"
|
||||
#include "hooks/graphics/backends/d3d9/d3d9_backend.h"
|
||||
#include "hooks/graphics/backends/d3d9/d3d9_device.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
// #define OVERLAYDBG 1
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
const ImVec4 YELLOW(1.f, 1.f, 0.f, 1.f);
|
||||
const ImVec4 WHITE(1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
GenericSubScreen::GenericSubScreen(SpiceOverlay *overlay) : Window(overlay), device(overlay->get_device()) {
|
||||
this->remove_window_padding = true;
|
||||
this->flags = ImGuiWindowFlags_NoScrollbar |
|
||||
ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoBackground |
|
||||
ImGuiWindowFlags_NoDocking;
|
||||
|
||||
this->size_max = ImVec2(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y);
|
||||
this->size_min = ImVec2(240, 135 + ImGui::GetFrameHeight());
|
||||
this->init_size = size_min;
|
||||
this->resize_callback = keep_16_by_9;
|
||||
|
||||
this->toggle_button = games::OverlayButtons::ToggleSubScreen;
|
||||
this->texture_width = 0;
|
||||
this->texture_height = 0;
|
||||
|
||||
overlay->set_subscreen_mouse_handler([this](LONG *x, LONG *y) -> bool {
|
||||
// convert to normalized form (relative window coordinates 0.f-1.f)
|
||||
ImVec2 xy(*x, *y);
|
||||
xy.x = (xy.x - overlay_content_top_left.x) / overlay_content_size.x;
|
||||
xy.y = (xy.y - overlay_content_top_left.y) / overlay_content_size.y;
|
||||
|
||||
// x/y can be outside of window
|
||||
if (xy.x < 0.f || 1.f < xy.x || xy.y < 0.f || 1.f < xy.y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// call into child
|
||||
this->touch_transform(xy, x, y);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void GenericSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {}
|
||||
|
||||
void GenericSubScreen::build_content() {
|
||||
this->draw_texture();
|
||||
|
||||
#if OVERLAYDBG
|
||||
if (this->status_message.has_value()) {
|
||||
log_warning("sub::overlay", "{}", this->status_message.value().c_str());
|
||||
}
|
||||
if (this->status_message.has_value()) {
|
||||
ImGui::TextColored(YELLOW, "%s", this->status_message.value().c_str());
|
||||
} else if (this->texture) {
|
||||
ImGui::TextColored(WHITE, "Successfully acquired surface texture");
|
||||
} else {
|
||||
ImGui::TextColored(YELLOW, "Failed to acquire surface texture");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool GenericSubScreen::build_texture(IDirect3DSurface9 *surface, UINT width, UINT height) {
|
||||
HRESULT hr;
|
||||
|
||||
D3DSURFACE_DESC desc {};
|
||||
hr = surface->GetDesc(&desc);
|
||||
if (FAILED(hr)) {
|
||||
this->status_message = fmt::format("Failed to get surface descriptor, hr={}", FMT_HRESULT(hr));
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = this->device->CreateTexture(width, height, 0, desc.Usage, desc.Format,
|
||||
desc.Pool, &this->texture, nullptr);
|
||||
if (FAILED(hr)) {
|
||||
this->status_message = fmt::format("Failed to create render target, hr={}", FMT_HRESULT(hr));
|
||||
return false;
|
||||
}
|
||||
|
||||
this->texture_width = width;
|
||||
this->texture_height = height;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GenericSubScreen::draw_texture() {
|
||||
HRESULT hr;
|
||||
|
||||
auto surface = graphics_d3d9_ldj_get_sub_screen();
|
||||
if (surface == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->draws_window) {
|
||||
// calculate the **content** location and size.
|
||||
// GetContentRegionAvail returns the correct dimension, up to (and including) the resize handle
|
||||
// don't be tempted to change it to some other ImGui routine that accomplishes similar things!
|
||||
overlay_content_top_left = ImGui::GetCursorScreenPos();
|
||||
overlay_content_size = ImGui::GetContentRegionAvail();
|
||||
} else {
|
||||
// no window, full screen
|
||||
overlay_content_top_left = ImVec2(0, 0);
|
||||
overlay_content_size = ImGui::GetIO().DisplaySize;
|
||||
}
|
||||
|
||||
if (this->draws_window &&
|
||||
this->texture &&
|
||||
((UINT)overlay_content_size.x != this->texture_width)) {
|
||||
|
||||
#if OVERLAYDBG
|
||||
log_info("sub::overlay", "resize {} != {} ", overlay_content_size.x, this->texture_width);
|
||||
#endif
|
||||
|
||||
// hack needed for SDVX; resizing the texture results in darker image, so allocate texture
|
||||
// again with the new size when the window is resized
|
||||
this->texture->Release();
|
||||
this->texture = nullptr;
|
||||
this->texture_width = 0;
|
||||
this->texture_height = 0;
|
||||
}
|
||||
|
||||
if (this->texture == nullptr) {
|
||||
if (!this->build_texture(surface, overlay_content_size.x, overlay_content_size.y)) {
|
||||
this->texture = nullptr;
|
||||
this->texture_width = 0;
|
||||
this->texture_height = 0;
|
||||
surface->Release();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
IDirect3DSurface9 *texture_surface = nullptr;
|
||||
hr = this->texture->GetSurfaceLevel(0, &texture_surface);
|
||||
if (FAILED(hr)) {
|
||||
this->status_message = fmt::format("Failed to get texture surface, hr={}", FMT_HRESULT(hr));
|
||||
surface->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
hr = this->device->StretchRect(surface, nullptr, texture_surface, nullptr, D3DTEXF_LINEAR);
|
||||
if (FAILED(hr)) {
|
||||
this->status_message = fmt::format("Failed to copy back buffer contents, hr={}", FMT_HRESULT(hr));
|
||||
surface->Release();
|
||||
texture_surface->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
surface->Release();
|
||||
texture_surface->Release();
|
||||
|
||||
// draw the subscreen (this draws *under* ImGui windows, over the game surface)
|
||||
auto bottom_right = overlay_content_size;
|
||||
bottom_right.x += overlay_content_top_left.x;
|
||||
bottom_right.y += overlay_content_top_left.y;
|
||||
ImGui::GetBackgroundDrawList()->AddImage(
|
||||
reinterpret_cast<void *>(this->texture),
|
||||
overlay_content_top_left,
|
||||
bottom_right);
|
||||
|
||||
if (this->draws_window) {
|
||||
// draw an invisible button so that it swallows mouse input
|
||||
// this is needed to prevent the window from being dragged around
|
||||
// (alternatively io.ConfigWindowsMoveFromTitleBarOnly can be set but that is global)
|
||||
ImGui::InvisibleButton(
|
||||
(this->title + "__InvisibleButton").c_str(),
|
||||
overlay_content_size,
|
||||
ImGuiButtonFlags_None);
|
||||
}
|
||||
|
||||
#if OVERLAYDBG
|
||||
log_info("sub::overlay", "{} / {} = {}", overlay_content_size.x, overlay_content_size.y, overlay_content_size.x / overlay_content_size.y);
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef SPICETOOLS_OVERLAY_WINDOWS_GENERIC_SUB_H
|
||||
#define SPICETOOLS_OVERLAY_WINDOWS_GENERIC_SUB_H
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <windows.h>
|
||||
#include <d3d9.h>
|
||||
|
||||
#include "overlay/window.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
class GenericSubScreen : public Window {
|
||||
public:
|
||||
GenericSubScreen(SpiceOverlay *overlay);
|
||||
|
||||
void build_content() override;
|
||||
|
||||
protected:
|
||||
virtual void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out);
|
||||
ImVec2 overlay_content_top_left;
|
||||
ImVec2 overlay_content_size;
|
||||
|
||||
private:
|
||||
static void keep_16_by_9(ImGuiSizeCallbackData* data) {
|
||||
data->DesiredSize.y = (data->DesiredSize.x * 9.f / 16.f) + ImGui::GetFrameHeight();
|
||||
}
|
||||
|
||||
bool build_texture(IDirect3DSurface9 *surface, UINT width, UINT height);
|
||||
void draw_texture();
|
||||
|
||||
std::optional<std::string> status_message = std::nullopt;
|
||||
|
||||
IDirect3DDevice9 *device = nullptr;
|
||||
IDirect3DTexture9 *texture = nullptr;
|
||||
|
||||
UINT texture_width;
|
||||
UINT texture_height;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // SPICETOOLS_OVERLAY_WINDOWS_GENERIC_SUB_H
|
||||
@@ -0,0 +1,148 @@
|
||||
#include <map>
|
||||
|
||||
#include "iidx_seg.h"
|
||||
#include "games/io.h"
|
||||
#include "games/iidx/iidx.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
uint32_t IIDX_SEGMENT_FONT_SIZE = 64;
|
||||
std::optional<uint32_t> IIDX_SEGMENT_FONT_COLOR = std::nullopt;
|
||||
std::string IIDX_SEGMENT_LOCATION = "bottom";
|
||||
|
||||
static const size_t TICKER_SIZE = 9;
|
||||
static const ImVec4 DARK_GRAY(0.1f, 0.1f, 0.1f, 1.f);
|
||||
static const ImVec4 RED(1.f, 0.f, 0.f, 1.f);
|
||||
static const int PADDING_Y = 8;
|
||||
static const int PADDING_X = 4;
|
||||
|
||||
static const std::map<char, std::pair<char, char>> CHARMAP = {\
|
||||
// period - add a space afterwards (game sends 'm' for period)
|
||||
{'m', {'.', ' '}},
|
||||
// exclamation mark (use ./ to make it look like one)
|
||||
{'!', {'.', '/'}},
|
||||
// font doesn't have tilde so using a dash instead
|
||||
{'~', {'-', '\0'}},
|
||||
};
|
||||
|
||||
IIDXSegmentDisplay::IIDXSegmentDisplay(SpiceOverlay *overlay) : Window(overlay) {
|
||||
if (!DSEG_FONT) {
|
||||
log_fatal("iidx_seg", "DSEG_FONT is null");
|
||||
}
|
||||
|
||||
this->title = "IIDX LED Segment Display";
|
||||
this->toggle_button = games::OverlayButtons::ToggleSubScreen;
|
||||
this->remove_window_padding = true;
|
||||
this->size_max = ImVec2(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y);
|
||||
this->flags = ImGuiWindowFlags_NoTitleBar
|
||||
| ImGuiWindowFlags_NoScrollbar
|
||||
| ImGuiWindowFlags_NoResize
|
||||
| ImGuiWindowFlags_NoNavFocus
|
||||
| ImGuiWindowFlags_NoNavInputs
|
||||
| ImGuiWindowFlags_NoDocking;
|
||||
|
||||
if (IIDX_SEGMENT_FONT_COLOR.has_value()) {
|
||||
const auto rgb = IIDX_SEGMENT_FONT_COLOR.value();
|
||||
const auto rgba = IM_COL32_A_MASK | rgb;
|
||||
this->color = ImGui::ColorConvertU32ToFloat4(rgba);
|
||||
} else {
|
||||
this->color = RED;
|
||||
}
|
||||
}
|
||||
|
||||
void IIDXSegmentDisplay::calculate_initial_window() {
|
||||
// ImGui::CalcTextSize doesn't seem to work here, so manually calculate
|
||||
ImGui::PushFont(DSEG_FONT);
|
||||
this->init_size = ImGui::CalcTextSize("~.~.~.~.~.~.~.~.~.");
|
||||
this->init_size.x += PADDING_X * 2;
|
||||
this->init_size.y += PADDING_Y * 2;
|
||||
ImGui::PopFont();
|
||||
|
||||
// initial horizontal position
|
||||
if (IIDX_SEGMENT_LOCATION.find("left") != std::string::npos) {
|
||||
this->init_pos.x = 0;
|
||||
} else if (IIDX_SEGMENT_LOCATION.find("right") != std::string::npos) {
|
||||
this->init_pos.x = ImGui::GetIO().DisplaySize.x - this->init_size.x;
|
||||
} else {
|
||||
// center
|
||||
this->init_pos.x = ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2;
|
||||
}
|
||||
|
||||
// initial vertical position
|
||||
if (IIDX_SEGMENT_LOCATION.rfind("top", 0) == 0) {
|
||||
this->init_pos.y = 0;
|
||||
} else {
|
||||
// bottom
|
||||
this->init_pos.y = ImGui::GetIO().DisplaySize.y - this->init_size.y;
|
||||
}
|
||||
}
|
||||
|
||||
void IIDXSegmentDisplay::build_content() {
|
||||
char input_ticker[TICKER_SIZE];
|
||||
|
||||
// get ticker content from game
|
||||
games::iidx::IIDX_LED_TICKER_LOCK.lock();
|
||||
memcpy(input_ticker, games::iidx::IIDXIO_LED_TICKER, TICKER_SIZE);
|
||||
games::iidx::IIDX_LED_TICKER_LOCK.unlock();
|
||||
|
||||
// since every input character can result in up to two characters,
|
||||
// need double size for output
|
||||
const size_t TICKER_OUTPUT_SIZE = TICKER_SIZE * 2 + 1;
|
||||
char output_ticker[TICKER_OUTPUT_SIZE];
|
||||
int output_ticker_index = 0;
|
||||
|
||||
// look at each input char and convert into output char(s)
|
||||
for (const auto c : input_ticker) {
|
||||
// see if there is an applicable rule for this input character
|
||||
if (0 == CHARMAP.count(c)) {
|
||||
// if not, copy the first character and keep going
|
||||
output_ticker[output_ticker_index] = c;
|
||||
output_ticker_index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// there is a replacement rule for this input character
|
||||
auto replacements = CHARMAP.at(c);
|
||||
|
||||
// replace the first character...
|
||||
output_ticker[output_ticker_index] = replacements.first;
|
||||
output_ticker_index += 1;
|
||||
|
||||
// and optionally add the second character
|
||||
if (replacements.second != '\0') {
|
||||
output_ticker[output_ticker_index] = replacements.second;
|
||||
output_ticker_index += 1;
|
||||
}
|
||||
}
|
||||
if ((int)TICKER_OUTPUT_SIZE <= output_ticker_index) {
|
||||
log_fatal("iidx_seg", "{} is beyond array bounds", output_ticker_index);
|
||||
}
|
||||
|
||||
// terminating null...
|
||||
output_ticker[output_ticker_index] = '\0';
|
||||
|
||||
// finally, draw UI elements
|
||||
draw_ticker(output_ticker);
|
||||
}
|
||||
|
||||
void IIDXSegmentDisplay::draw_ticker(char *ticker_string) {
|
||||
ImGui::PushFont(DSEG_FONT);
|
||||
|
||||
const auto pos = ImVec2(PADDING_X, PADDING_Y);
|
||||
|
||||
// to imitate LED "off"
|
||||
ImGui::SetCursorPos(pos);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, DARK_GRAY);
|
||||
ImGui::TextUnformatted("~.~.~.~.~.~.~.~.~.");
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
// ... then draw the LED "on" above it
|
||||
ImGui::SetCursorPos(pos);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, this->color);
|
||||
ImGui::TextUnformatted(ticker_string);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::PopFont();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include "overlay/window.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
extern uint32_t IIDX_SEGMENT_FONT_SIZE;
|
||||
extern std::optional<uint32_t> IIDX_SEGMENT_FONT_COLOR;
|
||||
extern std::string IIDX_SEGMENT_LOCATION;
|
||||
|
||||
class IIDXSegmentDisplay : public Window {
|
||||
public:
|
||||
IIDXSegmentDisplay(SpiceOverlay *overlay);
|
||||
void calculate_initial_window() override;
|
||||
void build_content() override;
|
||||
private:
|
||||
ImVec4 color;
|
||||
void draw_ticker(char *ticker_string);
|
||||
};
|
||||
}
|
||||
@@ -1,107 +1,43 @@
|
||||
#undef CINTERFACE
|
||||
|
||||
#include "iidx_sub.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "games/io.h"
|
||||
#include "hooks/graphics/backends/d3d9/d3d9_backend.h"
|
||||
#include "hooks/graphics/backends/d3d9/d3d9_device.h"
|
||||
#include "util/logging.h"
|
||||
#include "games/iidx/iidx.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
const ImVec4 YELLOW(1.f, 1.f, 0.f, 1.f);
|
||||
const ImVec4 WHITE(1.f, 1.f, 1.f, 1.f);
|
||||
IIDXSubScreen::IIDXSubScreen(SpiceOverlay *overlay) : GenericSubScreen(overlay) {
|
||||
this->title = "IIDX Sub Screen";
|
||||
|
||||
IIDXSubScreen::IIDXSubScreen(SpiceOverlay *overlay) : Window(overlay), device(overlay->get_device()) {
|
||||
this->draws_window = false;
|
||||
this->title = "Sub Screen";
|
||||
this->toggle_button = games::OverlayButtons::ToggleSubScreen;
|
||||
|
||||
this->texture_size = ImVec2(0, 0);
|
||||
}
|
||||
|
||||
void IIDXSubScreen::build_content() {
|
||||
this->draw_texture();
|
||||
|
||||
/*
|
||||
if (this->status_message.has_value()) {
|
||||
ImGui::TextColored(YELLOW, "%s", this->status_message.value().c_str());
|
||||
} else if (this->texture) {
|
||||
ImGui::TextColored(WHITE, "Successfully acquired surface texture");
|
||||
} else {
|
||||
ImGui::TextColored(YELLOW, "Failed to acquire surface texture");
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
bool IIDXSubScreen::build_texture(IDirect3DSurface9 *surface) {
|
||||
HRESULT hr;
|
||||
|
||||
D3DSURFACE_DESC desc {};
|
||||
hr = surface->GetDesc(&desc);
|
||||
if (FAILED(hr)) {
|
||||
this->status_message = fmt::format("Failed to get surface descriptor, hr={}", FMT_HRESULT(hr));
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = this->device->CreateTexture(desc.Width, desc.Height, 0, desc.Usage, desc.Format,
|
||||
desc.Pool, &this->texture, nullptr);
|
||||
if (FAILED(hr)) {
|
||||
this->status_message = fmt::format("Failed to create render target, hr={}", FMT_HRESULT(hr));
|
||||
return false;
|
||||
}
|
||||
|
||||
this->texture_size = ImVec2(desc.Width, desc.Height);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void IIDXSubScreen::draw_texture() {
|
||||
HRESULT hr;
|
||||
|
||||
auto surface = graphics_d3d9_ldj_get_sub_screen();
|
||||
if (surface == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->texture == nullptr) {
|
||||
if (!this->build_texture(surface)) {
|
||||
this->texture = nullptr;
|
||||
this->texture_size = ImVec2(0, 0);
|
||||
|
||||
surface->Release();
|
||||
return;
|
||||
float size = 0.5f;
|
||||
if (games::iidx::SUBSCREEN_OVERLAY_SIZE.has_value()) {
|
||||
if (games::iidx::SUBSCREEN_OVERLAY_SIZE.value() == "large") {
|
||||
size = 0.8f;
|
||||
} else if (games::iidx::SUBSCREEN_OVERLAY_SIZE.value() == "small") {
|
||||
size = 0.3f;
|
||||
} else if (games::iidx::SUBSCREEN_OVERLAY_SIZE.value() == "fullscreen") {
|
||||
this->draws_window = false;
|
||||
}
|
||||
}
|
||||
this->init_size = ImVec2(
|
||||
ImGui::GetIO().DisplaySize.x * size,
|
||||
(ImGui::GetIO().DisplaySize.x * size * 9 / 16) + ImGui::GetFrameHeight());
|
||||
|
||||
IDirect3DSurface9 *texture_surface = nullptr;
|
||||
hr = this->texture->GetSurfaceLevel(0, &texture_surface);
|
||||
if (FAILED(hr)) {
|
||||
this->status_message = fmt::format("Failed to get texture surface, hr={}", FMT_HRESULT(hr));
|
||||
this->size_max = ImVec2(
|
||||
ImGui::GetIO().DisplaySize.x - ImGui::GetFrameHeight() * 2,
|
||||
ImGui::GetIO().DisplaySize.y - ImGui::GetFrameHeight() * 2);
|
||||
|
||||
surface->Release();
|
||||
// middle / bottom
|
||||
this->init_pos = ImVec2(
|
||||
ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2,
|
||||
ImGui::GetIO().DisplaySize.y - this->init_size.y - (ImGui::GetFrameHeight() / 2));
|
||||
}
|
||||
|
||||
void IIDXSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {
|
||||
if (!this->get_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = this->device->StretchRect(surface, nullptr, texture_surface, nullptr, D3DTEXF_NONE);
|
||||
if (FAILED(hr)) {
|
||||
this->status_message = fmt::format("Failed to copy back buffer contents, hr={}", FMT_HRESULT(hr));
|
||||
|
||||
surface->Release();
|
||||
texture_surface->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
surface->Release();
|
||||
texture_surface->Release();
|
||||
|
||||
ImGui::GetBackgroundDrawList()->AddImage(
|
||||
reinterpret_cast<void *>(this->texture),
|
||||
ImVec2(0, 0),
|
||||
this->texture_size,
|
||||
ImVec2(0, 0),
|
||||
ImVec2(1, 1));
|
||||
*x_out = xy_in.x * ImGui::GetIO().DisplaySize.x;
|
||||
*y_out = xy_in.y * ImGui::GetIO().DisplaySize.y;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,17 @@
|
||||
#ifndef SPICETOOLS_OVERLAY_WINDOWS_IIDX_SUB_H
|
||||
#define SPICETOOLS_OVERLAY_WINDOWS_IIDX_SUB_H
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <windows.h>
|
||||
#include <d3d9.h>
|
||||
|
||||
#include "overlay/window.h"
|
||||
#include "overlay/windows/generic_sub.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
class IIDXSubScreen : public Window {
|
||||
class IIDXSubScreen : public GenericSubScreen {
|
||||
public:
|
||||
IIDXSubScreen(SpiceOverlay *overlay);
|
||||
|
||||
void build_content() override;
|
||||
|
||||
private:
|
||||
bool build_texture(IDirect3DSurface9 *surface);
|
||||
void draw_texture();
|
||||
|
||||
std::optional<std::string> status_message = std::nullopt;
|
||||
|
||||
IDirect3DDevice9 *device = nullptr;
|
||||
IDirect3DTexture9 *texture = nullptr;
|
||||
ImVec2 texture_size;
|
||||
protected:
|
||||
void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) override;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
#include "iopanel.h"
|
||||
#include "cfg/api.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "games/io.h"
|
||||
#include "misc/eamuse.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
IOPanel::IOPanel(SpiceOverlay *overlay) : Window(overlay) {
|
||||
this->title = "Operator";
|
||||
this->flags = ImGuiWindowFlags_AlwaysAutoResize;
|
||||
|
||||
this->init_size = ImVec2(
|
||||
80,
|
||||
ImGui::GetFrameHeight() +
|
||||
ImGui::GetStyle().WindowPadding.y * 2 +
|
||||
ImGui::GetFrameHeightWithSpacing() * 3 +
|
||||
ImGui::GetFrameHeight()); // title + windows padding + 4 buttons
|
||||
|
||||
this->init_pos =ImVec2(10, ImGui::GetIO().DisplaySize.y - this->init_size.y - 10);
|
||||
this->toggle_button = games::OverlayButtons::ToggleIOPanel;
|
||||
this->find_buttons();
|
||||
}
|
||||
|
||||
void IOPanel::find_buttons() {
|
||||
const auto buttons = games::get_buttons(eamuse_get_game());
|
||||
for (auto &button : *buttons) {
|
||||
// since the array indices are different for each game, use string match instead
|
||||
if (button.getName() == "Service") {
|
||||
this->service_button = &button;
|
||||
} else if (button.getName() == "Test") {
|
||||
this->test_button = &button;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float IOPanel::get_suggested_height() {
|
||||
// height of 4 buttons stacked vertically
|
||||
// (coin, unlock, service, test, plus spacing between them)
|
||||
return ImGui::GetFrameHeightWithSpacing() * 3 + ImGui::GetFrameHeight();
|
||||
}
|
||||
|
||||
void IOPanel::build_content() {
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.2f, 0.1f, 0.1f, 1.f));
|
||||
ImGui::PushID(this);
|
||||
|
||||
ImGui::BeginGroup();
|
||||
this->build_operator_menu();
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
this->build_io_panel();
|
||||
|
||||
ImGui::PopID();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
void IOPanel::build_io_panel() {}
|
||||
|
||||
void IOPanel::build_operator_menu() {
|
||||
const ImVec2 wide(60, 0);
|
||||
const float spacing_x = ImGui::GetStyle().ItemSpacing.y;
|
||||
const ImVec2 tall(
|
||||
ImGui::GetFrameHeight(),
|
||||
ImGui::GetFrameHeightWithSpacing() + ImGui::GetFrameHeight());
|
||||
|
||||
if (ImGui::Button("COIN", ImVec2(wide.x + spacing_x + tall.x, wide.y))) {
|
||||
eamuse_coin_add();
|
||||
}
|
||||
|
||||
ImGui::Checkbox("unlock", &operator_unlocked);
|
||||
ImGui::BeginDisabled(!operator_unlocked);
|
||||
|
||||
this->build_button("SERVICE", wide, this->service_button);
|
||||
this->build_button("TEST", wide, this->test_button);
|
||||
|
||||
// service + test button (for test menu navigation)
|
||||
ImGui::SameLine(0, spacing_x);
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - ImGui::GetFrameHeightWithSpacing());
|
||||
this->build_button("+", tall, this->test_button, this->service_button);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
void IOPanel::build_button(
|
||||
const char *label, const ImVec2 &size, Button *button, Button *button_alt, Light *light) {
|
||||
|
||||
ImGui::BeginDisabled(button == nullptr);
|
||||
|
||||
int pushed = 0;
|
||||
if (light && 0.5f < GameAPI::Lights::readLight(RI_MGR, *light)) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.72f, 0.15f, 0.00f, 0.9f));
|
||||
pushed += 1;
|
||||
}
|
||||
|
||||
ImGui::Button(label, size);
|
||||
|
||||
if (ImGui::IsItemActivated()) {
|
||||
button->override_state = GameAPI::Buttons::BUTTON_PRESSED;
|
||||
button->override_velocity = 1.f;
|
||||
button->override_enabled = true;
|
||||
if (button_alt) {
|
||||
button_alt->override_state = GameAPI::Buttons::BUTTON_PRESSED;
|
||||
button_alt->override_velocity = 1.f;
|
||||
button_alt->override_enabled = true;
|
||||
}
|
||||
} else if (ImGui::IsItemDeactivated()) {
|
||||
button->override_enabled = false;
|
||||
if (button_alt) {
|
||||
button_alt->override_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor(pushed);
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "overlay/window.h"
|
||||
#include "cfg/button.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
class IOPanel : public Window {
|
||||
public:
|
||||
IOPanel(SpiceOverlay *overlay);
|
||||
void build_content() override;
|
||||
|
||||
protected:
|
||||
virtual void build_io_panel();
|
||||
void build_button(
|
||||
const char *label,
|
||||
const ImVec2 &size,
|
||||
Button *button,
|
||||
Button *button_alt = nullptr,
|
||||
Light *light = nullptr);
|
||||
|
||||
float get_suggested_height();
|
||||
|
||||
private:
|
||||
void find_buttons();
|
||||
void build_operator_menu();
|
||||
|
||||
bool operator_unlocked = false;
|
||||
Button *test_button = nullptr;
|
||||
Button *service_button = nullptr;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#include "iopanel_ddr.h"
|
||||
#include "games/io.h"
|
||||
#include "games/ddr/io.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
DDRIOPanel::DDRIOPanel(SpiceOverlay *overlay) : IOPanel(overlay) {
|
||||
this->title = "DDR IO Panel";
|
||||
find_ddr_buttons();
|
||||
}
|
||||
|
||||
void DDRIOPanel::find_ddr_buttons() {
|
||||
const auto buttons = games::get_buttons(eamuse_get_game());
|
||||
const auto lights = games::get_lights(eamuse_get_game());
|
||||
|
||||
// SD cabs don't have lights for these buttons, so just use the HD ones
|
||||
|
||||
this->start[0] = &(*buttons)[games::ddr::Buttons::P1_START];
|
||||
this->up[0] = &(*buttons)[games::ddr::Buttons::P1_MENU_UP];
|
||||
this->down[0] = &(*buttons)[games::ddr::Buttons::P1_MENU_DOWN];
|
||||
this->left[0] = &(*buttons)[games::ddr::Buttons::P1_MENU_LEFT];
|
||||
this->right[0] = &(*buttons)[games::ddr::Buttons::P1_MENU_RIGHT];
|
||||
this->start_light[0] = &(*lights)[games::ddr::Lights::HD_P1_START];
|
||||
this->updown_light[0] = &(*lights)[games::ddr::Lights::HD_P1_UP_DOWN];
|
||||
this->leftright_light[0] = &(*lights)[games::ddr::Lights::HD_P1_LEFT_RIGHT];
|
||||
|
||||
this->start[1] = &(*buttons)[games::ddr::Buttons::P2_START];
|
||||
this->up[1] = &(*buttons)[games::ddr::Buttons::P2_MENU_UP];
|
||||
this->down[1] = &(*buttons)[games::ddr::Buttons::P2_MENU_DOWN];
|
||||
this->left[1] = &(*buttons)[games::ddr::Buttons::P2_MENU_LEFT];
|
||||
this->right[1] = &(*buttons)[games::ddr::Buttons::P2_MENU_RIGHT];
|
||||
this->start_light[1] = &(*lights)[games::ddr::Lights::HD_P2_START];
|
||||
this->updown_light[1] = &(*lights)[games::ddr::Lights::HD_P2_UP_DOWN];
|
||||
this->leftright_light[1] = &(*lights)[games::ddr::Lights::HD_P2_LEFT_RIGHT];
|
||||
}
|
||||
|
||||
void DDRIOPanel::build_io_panel() {
|
||||
ImGui::Dummy(ImVec2(12, 0));
|
||||
|
||||
ImGui::SameLine();
|
||||
this->draw_buttons(0);
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(12, 0));
|
||||
|
||||
ImGui::SameLine();
|
||||
this->draw_buttons(1);
|
||||
}
|
||||
|
||||
void DDRIOPanel::draw_buttons(const int p) {
|
||||
// 2x2
|
||||
const ImVec2 start_button_size(
|
||||
ImGui::GetFrameHeightWithSpacing() + ImGui::GetFrameHeight(),
|
||||
ImGui::GetFrameHeightWithSpacing() + ImGui::GetFrameHeight()
|
||||
);
|
||||
// 2x1
|
||||
const ImVec2 updown_size(start_button_size.x, ImGui::GetFrameHeight());
|
||||
// 1x2
|
||||
const ImVec2 leftright_size(ImGui::GetFrameHeight(), start_button_size.y);
|
||||
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetFrameHeightWithSpacing());
|
||||
this->build_button("<", leftright_size, this->left[p], nullptr, this->leftright_light[p]);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine(0, ImGui::GetStyle().ItemSpacing.y);
|
||||
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
this->build_button("^", updown_size, this->up[p], nullptr, this->updown_light[p]);
|
||||
const char *label;
|
||||
if (p == 0) {
|
||||
label = " 1 P\nStart";
|
||||
} else {
|
||||
label = " 2 P\nStart";
|
||||
}
|
||||
this->build_button(label, start_button_size, this->start[p], nullptr, this->start_light[p]);
|
||||
this->build_button("v", updown_size, this->down[p], nullptr, this->updown_light[p]);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine(0, ImGui::GetStyle().ItemSpacing.y);
|
||||
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetFrameHeightWithSpacing());
|
||||
this->build_button(">", leftright_size, this->right[p], nullptr, this->leftright_light[p]);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "overlay/window.h"
|
||||
#include "overlay/windows/iopanel.h"
|
||||
#include "cfg/button.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
class DDRIOPanel : public IOPanel {
|
||||
public:
|
||||
DDRIOPanel(SpiceOverlay *overlay);
|
||||
|
||||
protected:
|
||||
void build_io_panel() override;
|
||||
|
||||
private:
|
||||
void find_ddr_buttons();
|
||||
void draw_buttons(const int player);
|
||||
|
||||
Button *start[2];
|
||||
Button *up[2];
|
||||
Button *down[2];
|
||||
Button *left[2];
|
||||
Button *right[2];
|
||||
Light *start_light[2];
|
||||
Light *updown_light[2];
|
||||
Light *leftright_light[2];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
#include "iopanel_gfdm.h"
|
||||
#include "avs/game.h"
|
||||
#include "games/io.h"
|
||||
#include "games/gitadora/gitadora.h"
|
||||
#include "games/gitadora/io.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
GitadoraIOPanel::GitadoraIOPanel(SpiceOverlay *overlay) : IOPanel(overlay) {
|
||||
this->title = "GITADORA IO Panel";
|
||||
|
||||
// by default, make a safer assumption that there are two players
|
||||
this->two_players = true;
|
||||
// by default, enable the extra input only available on DX cabs...
|
||||
this->has_guitar_knobs = true;
|
||||
|
||||
// drummania can only have one player, no guitar knobs
|
||||
if (avs::game::is_model({ "J32", "K32", "L32" }) ||
|
||||
(avs::game::is_model("M32") && avs::game::SPEC[0] == 'B')) {
|
||||
this->two_players = false;
|
||||
this->has_guitar_knobs = false;
|
||||
}
|
||||
|
||||
// and cab type 3 (white cab) only has one guitar
|
||||
if (games::gitadora::CAB_TYPE.has_value() && games::gitadora::CAB_TYPE == 3) {
|
||||
this->two_players = false;
|
||||
}
|
||||
|
||||
// disable guitar knobs on known non-DX cabs
|
||||
if (games::gitadora::CAB_TYPE.has_value() &&
|
||||
(games::gitadora::CAB_TYPE == 2 || games::gitadora::CAB_TYPE == 3)) {
|
||||
this->has_guitar_knobs = false;
|
||||
}
|
||||
|
||||
find_gfdm_buttons();
|
||||
}
|
||||
|
||||
void GitadoraIOPanel::find_gfdm_buttons() {
|
||||
const auto buttons = games::get_buttons(eamuse_get_game());
|
||||
|
||||
// device emulation treats drum controls to be the same as guitar 1p
|
||||
|
||||
this->start[0] = &(*buttons)[games::gitadora::Buttons::GuitarP1Start];
|
||||
this->help[0] = &(*buttons)[games::gitadora::Buttons::GuitarP1Help];
|
||||
this->up[0] = &(*buttons)[games::gitadora::Buttons::GuitarP1Up];
|
||||
this->down[0] = &(*buttons)[games::gitadora::Buttons::GuitarP1Down];
|
||||
this->left[0] = &(*buttons)[games::gitadora::Buttons::GuitarP1Left];
|
||||
this->right[0] = &(*buttons)[games::gitadora::Buttons::GuitarP1Right];
|
||||
|
||||
this->start[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Start];
|
||||
this->help[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Help];
|
||||
this->up[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Up];
|
||||
this->down[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Down];
|
||||
this->left[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Left];
|
||||
this->right[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Right];
|
||||
}
|
||||
|
||||
void GitadoraIOPanel::build_io_panel() {
|
||||
ImGui::Dummy(ImVec2(12, 0));
|
||||
|
||||
ImGui::SameLine();
|
||||
this->draw_buttons(0);
|
||||
if (this->has_guitar_knobs) {
|
||||
ImGui::SameLine();
|
||||
this->draw_sliders(0);
|
||||
}
|
||||
|
||||
// draw p2 only if guitar freaks
|
||||
if (this->two_players) {
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(12, 0));
|
||||
ImGui::SameLine();
|
||||
this->draw_buttons(1);
|
||||
if (this->has_guitar_knobs) {
|
||||
ImGui::SameLine();
|
||||
this->draw_sliders(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GitadoraIOPanel::draw_buttons(const int p) {
|
||||
// 2x2
|
||||
const ImVec2 start_button_size(
|
||||
ImGui::GetFrameHeightWithSpacing() + ImGui::GetFrameHeight(),
|
||||
ImGui::GetFrameHeightWithSpacing() + ImGui::GetFrameHeight()
|
||||
);
|
||||
// 2x1
|
||||
const ImVec2 updown_size(start_button_size.x, ImGui::GetFrameHeight());
|
||||
// 1x2
|
||||
const ImVec2 leftright_size(ImGui::GetFrameHeight(), start_button_size.y);
|
||||
// 1x1
|
||||
const ImVec2 tiny_size(ImGui::GetFrameHeight(), ImGui::GetFrameHeight());
|
||||
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetFrameHeightWithSpacing());
|
||||
this->build_button("<", leftright_size, this->left[p]);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine(0, ImGui::GetStyle().ItemSpacing.y);
|
||||
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
this->build_button("^", updown_size, this->up[p]);
|
||||
const char *label;
|
||||
if (this->two_players) {
|
||||
if (p == 0) {
|
||||
label = " 1 P\nStart";
|
||||
} else {
|
||||
label = " 2 P\nStart";
|
||||
}
|
||||
} else {
|
||||
label = "Start";
|
||||
}
|
||||
this->build_button(label, start_button_size, this->start[p]);
|
||||
this->build_button("v", updown_size, this->down[p]);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine(0, ImGui::GetStyle().ItemSpacing.y);
|
||||
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
this->build_button("?", tiny_size, this->help[p]);
|
||||
this->build_button(">", leftright_size, this->right[p]);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
|
||||
void GitadoraIOPanel::draw_sliders(const int p) {
|
||||
const auto index =
|
||||
(p == 0) ?
|
||||
games::gitadora::Analogs::GuitarP1Knob :
|
||||
games::gitadora::Analogs::GuitarP2Knob;
|
||||
|
||||
const ImVec2 slider_size(32, get_suggested_height());
|
||||
|
||||
// get analog
|
||||
const auto analogs = games::get_analogs(eamuse_get_game());
|
||||
auto &analog = (*analogs)[index];
|
||||
|
||||
// vertical slider
|
||||
auto new_state = analog.override_enabled ? analog.override_state
|
||||
: GameAPI::Analogs::getState(RI_MGR, analog);
|
||||
|
||||
ImGui::PushID(index);
|
||||
ImGui::VSliderFloat(
|
||||
"##v",
|
||||
slider_size,
|
||||
&new_state,
|
||||
0.f, 1.f,
|
||||
"Knob",
|
||||
ImGuiSliderFlags_AlwaysClamp);
|
||||
ImGui::PopID();
|
||||
if (new_state != analog.override_state) {
|
||||
analog.override_state = new_state;
|
||||
analog.override_enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "overlay/window.h"
|
||||
#include "overlay/windows/iopanel.h"
|
||||
#include "cfg/button.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
class GitadoraIOPanel : public IOPanel {
|
||||
public:
|
||||
GitadoraIOPanel(SpiceOverlay *overlay);
|
||||
|
||||
protected:
|
||||
void build_io_panel() override;
|
||||
|
||||
private:
|
||||
void find_gfdm_buttons();
|
||||
void draw_buttons(const int player);
|
||||
void draw_sliders(const int player);
|
||||
|
||||
bool two_players;
|
||||
bool has_guitar_knobs;
|
||||
|
||||
Button *start[2];
|
||||
Button *help[2];
|
||||
Button *up[2];
|
||||
Button *down[2];
|
||||
Button *left[2];
|
||||
Button *right[2];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
#include <map>
|
||||
#include "iopanel_iidx.h"
|
||||
#include "games/io.h"
|
||||
#include "games/iidx/iidx.h"
|
||||
#include "games/iidx/io.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
IIDXIOPanel::IIDXIOPanel(SpiceOverlay *overlay) : IOPanel(overlay) {
|
||||
this->title = "IIDX IO Panel";
|
||||
find_iidx_buttons();
|
||||
}
|
||||
|
||||
void IIDXIOPanel::find_iidx_buttons() {
|
||||
const auto buttons = games::get_buttons(eamuse_get_game());
|
||||
const auto lights = games::get_lights(eamuse_get_game());
|
||||
|
||||
this->start_1p = &(*buttons)[games::iidx::Buttons::P1_Start];
|
||||
this->start_1p_light = &(*lights)[games::iidx::Lights::P1_Start];
|
||||
|
||||
this->start_2p = &(*buttons)[games::iidx::Buttons::P2_Start];
|
||||
this->start_2p_light = &(*lights)[games::iidx::Lights::P2_Start];
|
||||
|
||||
this->vefx = &(*buttons)[games::iidx::Buttons::VEFX];
|
||||
this->vefx_light = &(*lights)[games::iidx::Lights::VEFX];
|
||||
|
||||
this->effect_on = &(*buttons)[games::iidx::Buttons::Effect];
|
||||
this->effect_on_light = &(*lights)[games::iidx::Lights::Effect];
|
||||
}
|
||||
|
||||
void IIDXIOPanel::build_io_panel() {
|
||||
ImGui::Dummy(ImVec2(12, 0));
|
||||
|
||||
ImGui::SameLine();
|
||||
this->draw_buttons();
|
||||
|
||||
if (!games::iidx::TDJ_MODE) {
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(12, 0));
|
||||
|
||||
ImGui::SameLine();
|
||||
this->draw_sliders();
|
||||
}
|
||||
}
|
||||
|
||||
void IIDXIOPanel::draw_buttons() {
|
||||
const auto big_button_size = ImVec2(get_suggested_height(), get_suggested_height());
|
||||
|
||||
// width = same as above,
|
||||
// height = 2 rows of buttons
|
||||
const auto mid_button_size = ImVec2(
|
||||
big_button_size.x,
|
||||
ImGui::GetFrameHeight() * 2 + ImGui::GetStyle().ItemSpacing.y
|
||||
);
|
||||
|
||||
// width = half of above,
|
||||
// height = 4 rows of buttons
|
||||
const auto tall_button_size = ImVec2(
|
||||
big_button_size.x / 2,
|
||||
big_button_size.y
|
||||
);
|
||||
|
||||
this->build_button(
|
||||
" 1 P\n"
|
||||
"Start",
|
||||
big_button_size,
|
||||
this->start_1p, nullptr,
|
||||
this->start_1p_light);
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginGroup();
|
||||
this->build_button(
|
||||
"EFFECT",
|
||||
mid_button_size,
|
||||
this->effect_on, nullptr,
|
||||
this->effect_on_light);
|
||||
this->build_button(
|
||||
"VEFX",
|
||||
mid_button_size,
|
||||
this->vefx, nullptr,
|
||||
this->vefx_light);
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
this->build_button(
|
||||
"EFF\n"
|
||||
" +\n"
|
||||
"VFX",
|
||||
tall_button_size,
|
||||
this->effect_on,
|
||||
this->vefx);
|
||||
|
||||
ImGui::SameLine();
|
||||
this->build_button(
|
||||
" 2 P\n"
|
||||
"Start",
|
||||
big_button_size,
|
||||
this->start_2p, nullptr,
|
||||
this->start_2p_light);
|
||||
}
|
||||
|
||||
void IIDXIOPanel::draw_sliders() {
|
||||
// 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, "Fltr" },
|
||||
{ games::iidx::Analogs::PlayVolume, "Vol." },
|
||||
};
|
||||
|
||||
const ImVec2 slider_size(32, get_suggested_height());
|
||||
const auto reset_button_size = ImVec2(16, get_suggested_height());
|
||||
|
||||
// iterate analogs
|
||||
bool overridden = false;
|
||||
const 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);
|
||||
|
||||
// vertical slider
|
||||
auto new_state = analog.override_enabled ? analog.override_state
|
||||
: GameAPI::Analogs::getState(RI_MGR, analog);
|
||||
if (0 < index) {
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
ImGui::VSliderFloat(
|
||||
"##v",
|
||||
slider_size,
|
||||
&new_state,
|
||||
0.f, 1.f,
|
||||
name,
|
||||
ImGuiSliderFlags_AlwaysClamp);
|
||||
|
||||
if (new_state != analog.override_state) {
|
||||
analog.override_state = new_state;
|
||||
analog.override_enabled = true;
|
||||
}
|
||||
|
||||
// pop id and style
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
// reset button
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("r\ne\ns\ne\nt", reset_button_size)) {
|
||||
for (auto &[index, name] : ANALOG_ENTRIES) {
|
||||
if (index < analogs->size()) {
|
||||
(*analogs)[index].override_enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "overlay/window.h"
|
||||
#include "overlay/windows/iopanel.h"
|
||||
#include "cfg/button.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
class IIDXIOPanel : public IOPanel {
|
||||
public:
|
||||
IIDXIOPanel(SpiceOverlay *overlay);
|
||||
|
||||
protected:
|
||||
void build_io_panel() override;
|
||||
|
||||
private:
|
||||
void find_iidx_buttons();
|
||||
void draw_buttons();
|
||||
void draw_sliders();
|
||||
|
||||
Button *start_1p = nullptr;
|
||||
Light *start_1p_light = nullptr;
|
||||
|
||||
Button *start_2p = nullptr;
|
||||
Light *start_2p_light = nullptr;
|
||||
|
||||
Button *effect_on = nullptr;
|
||||
Light *effect_on_light = nullptr;
|
||||
|
||||
Button *vefx = nullptr;
|
||||
Light *vefx_light = nullptr;
|
||||
};
|
||||
}
|
||||
@@ -77,7 +77,7 @@ namespace overlay::windows {
|
||||
}
|
||||
|
||||
// mouse down handler
|
||||
if (ImGui::IsItemHovered() && ImGui::IsAnyMouseDown()) {
|
||||
if (ImGui::IsItemActive()) {
|
||||
eamuse_set_keypad_overrides_overlay(this->unit, button.flag);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
#include <games/io.h>
|
||||
|
||||
#include "screen_resize.h"
|
||||
#include "avs/game.h"
|
||||
#include "cfg/screen_resize.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "overlay/imgui/extensions.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
ScreenResize::ScreenResize(SpiceOverlay *overlay) : Window(overlay) {
|
||||
this->title = "Screen Resize";
|
||||
this->flags = ImGuiWindowFlags_AlwaysAutoResize;
|
||||
this->init_pos = ImVec2(
|
||||
ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2,
|
||||
ImGui::GetIO().DisplaySize.y / 2 - this->init_size.y / 2);
|
||||
this->init_pos = ImVec2(10, 10);
|
||||
this->toggle_button = games::OverlayButtons::ToggleScreenResize;
|
||||
this->toggle_screen_resize = games::OverlayButtons::ScreenResize;
|
||||
}
|
||||
@@ -20,35 +22,173 @@ namespace overlay::windows {
|
||||
ScreenResize::~ScreenResize() {
|
||||
}
|
||||
|
||||
HWND ScreenResize::get_first_window() {
|
||||
if (GRAPHICS_WINDOWS.size() == 0) {
|
||||
return NULL;
|
||||
}
|
||||
return GRAPHICS_WINDOWS[0];
|
||||
}
|
||||
|
||||
void ScreenResize::reset_window() {
|
||||
this->reset_vars_to_default();
|
||||
if (GRAPHICS_WINDOWED) {
|
||||
const auto window = get_first_window();
|
||||
if (window) {
|
||||
graphics_move_resize_window(window);
|
||||
graphics_update_window_style(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenResize::reset_vars_to_default() {
|
||||
cfg::SCREENRESIZE->enable_screen_resize = false;
|
||||
cfg::SCREENRESIZE->enable_linear_filter = true;
|
||||
cfg::SCREENRESIZE->keep_aspect_ratio = true;
|
||||
cfg::SCREENRESIZE->centered = true;
|
||||
cfg::SCREENRESIZE->offset_x = 0;
|
||||
cfg::SCREENRESIZE->offset_y = 0;
|
||||
cfg::SCREENRESIZE->scale_x = 1.f;
|
||||
cfg::SCREENRESIZE->scale_y = 1.f;
|
||||
|
||||
cfg::SCREENRESIZE->enable_window_resize = false;
|
||||
cfg::SCREENRESIZE->window_always_on_top = false;
|
||||
cfg::SCREENRESIZE->window_decoration = cfg::WindowDecorationMode::Default;
|
||||
cfg::SCREENRESIZE->window_offset_x = 0;
|
||||
cfg::SCREENRESIZE->window_offset_y = 0;
|
||||
cfg::SCREENRESIZE->client_keep_aspect_ratio = true;
|
||||
cfg::SCREENRESIZE->client_width = cfg::SCREENRESIZE->init_client_width;
|
||||
cfg::SCREENRESIZE->client_height = cfg::SCREENRESIZE->init_client_height;
|
||||
}
|
||||
|
||||
void ScreenResize::build_content() {
|
||||
ImGui::Text("For: %s", eamuse_get_game().c_str());
|
||||
{
|
||||
int flags = 0;
|
||||
if (!GRAPHICS_WINDOWED || cfg::SCREENRESIZE->enable_screen_resize) {
|
||||
flags |= ImGuiTreeNodeFlags_DefaultOpen;
|
||||
}
|
||||
if (ImGui::TreeNodeEx("Image Resize", flags)) {
|
||||
this->build_fullscreen_config();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::BeginDisabled(!GRAPHICS_WINDOWED);
|
||||
{
|
||||
int flags = 0;
|
||||
if (GRAPHICS_WINDOWED) {
|
||||
flags |= ImGuiTreeNodeFlags_DefaultOpen;
|
||||
}
|
||||
if (ImGui::TreeNodeEx("Window Size", flags)) {
|
||||
build_windowed_config();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
build_footer();
|
||||
}
|
||||
|
||||
void ScreenResize::build_fullscreen_config() {
|
||||
// enable checkbox
|
||||
ImGui::Checkbox("Enable Screen Resize", &cfg::SCREENRESIZE->enable_screen_resize);
|
||||
if (cfg::SCREENRESIZE->enable_screen_resize) {
|
||||
ImGui::Checkbox("Enable", &cfg::SCREENRESIZE->enable_screen_resize);
|
||||
ImGui::BeginDisabled(!cfg::SCREENRESIZE->enable_screen_resize);
|
||||
|
||||
// general settings
|
||||
ImGui::Checkbox("Enable Linear Filter", &cfg::SCREENRESIZE->enable_linear_filter);
|
||||
ImGui::InputInt("X Offset", &cfg::SCREENRESIZE->offset_x);
|
||||
ImGui::InputInt("Y Offset", &cfg::SCREENRESIZE->offset_y);
|
||||
// general settings
|
||||
ImGui::Checkbox("Linear Filter", &cfg::SCREENRESIZE->enable_linear_filter);
|
||||
ImGui::Checkbox("Centered", &cfg::SCREENRESIZE->centered);
|
||||
if (!cfg::SCREENRESIZE->centered) {
|
||||
ImGui::InputInt("X Offset", &cfg::SCREENRESIZE->offset_x);
|
||||
ImGui::InputInt("Y Offset", &cfg::SCREENRESIZE->offset_y);
|
||||
}
|
||||
|
||||
// aspect ratio
|
||||
ImGui::Checkbox("Keep Aspect Ratio", &cfg::SCREENRESIZE->keep_aspect_ratio);
|
||||
if (cfg::SCREENRESIZE->keep_aspect_ratio) {
|
||||
if (ImGui::SliderFloat("Scale", &cfg::SCREENRESIZE->scale_x, 0.65f, 2.0f)) {
|
||||
cfg::SCREENRESIZE->scale_y = cfg::SCREENRESIZE->scale_x;
|
||||
}
|
||||
} else {
|
||||
ImGui::SliderFloat("Width Scale", &cfg::SCREENRESIZE->scale_x, 0.65f, 2.0f);
|
||||
ImGui::SliderFloat("Height Scale", &cfg::SCREENRESIZE->scale_y, 0.65f, 2.0f);
|
||||
}
|
||||
// aspect ratio
|
||||
ImGui::Checkbox("Keep Aspect Ratio", &cfg::SCREENRESIZE->keep_aspect_ratio);
|
||||
if (cfg::SCREENRESIZE->keep_aspect_ratio) {
|
||||
if (ImGui::SliderFloat("Scale", &cfg::SCREENRESIZE->scale_x, 0.65f, 2.0f)) {
|
||||
cfg::SCREENRESIZE->scale_y = cfg::SCREENRESIZE->scale_x;
|
||||
}
|
||||
} else {
|
||||
ImGui::SliderFloat("Width Scale", &cfg::SCREENRESIZE->scale_x, 0.65f, 2.0f);
|
||||
ImGui::SliderFloat("Height Scale", &cfg::SCREENRESIZE->scale_y, 0.65f, 2.0f);
|
||||
}
|
||||
|
||||
// reset button
|
||||
if (ImGui::Button("Reset")) {
|
||||
cfg::SCREENRESIZE->offset_x = 0;
|
||||
cfg::SCREENRESIZE->offset_y = 0;
|
||||
cfg::SCREENRESIZE->scale_x = 1;
|
||||
cfg::SCREENRESIZE->scale_y = 1;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
void ScreenResize::build_windowed_config() {
|
||||
// for now, only supports the first window
|
||||
const auto window = get_first_window();
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::TextUnformatted("Warning: may cause some games to crash.");
|
||||
ImGui::BeginDisabled(graphics_window_change_crashes_game());
|
||||
if (ImGui::Combo(
|
||||
"Window Style",
|
||||
&cfg::SCREENRESIZE->window_decoration,
|
||||
"Default\0Borderless\0Resizable Window\0\0")) {
|
||||
graphics_update_window_style(window);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker(
|
||||
"Change window decoration. Resizable Window may not cause your mouse cursor to change, "
|
||||
"but you can still drag to resize. Disabled for some games due to incompatibility.");
|
||||
|
||||
if (ImGui::Checkbox("Always on Top", &cfg::SCREENRESIZE->window_always_on_top) ) {
|
||||
graphics_update_z_order(window);
|
||||
}
|
||||
ImGui::Checkbox("Keep Aspect Ratio", &cfg::SCREENRESIZE->client_keep_aspect_ratio);
|
||||
ImGui::Checkbox("Manual window move/resize", &cfg::SCREENRESIZE->enable_window_resize);
|
||||
ImGui::BeginDisabled(!cfg::SCREENRESIZE->enable_window_resize);
|
||||
|
||||
bool changed = false;
|
||||
const uint32_t step = 1;
|
||||
const uint32_t step_fast = 10;
|
||||
ImGui::BeginDisabled(cfg::SCREENRESIZE->client_keep_aspect_ratio);
|
||||
changed |= ImGui::InputScalar(
|
||||
"Width",
|
||||
ImGuiDataType_U32,
|
||||
&cfg::SCREENRESIZE->client_width,
|
||||
&step, &step_fast, nullptr,
|
||||
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||
ImGui::EndDisabled();
|
||||
changed |= ImGui::InputScalar(
|
||||
"Height",
|
||||
ImGuiDataType_U32,
|
||||
&cfg::SCREENRESIZE->client_height,
|
||||
&step, &step_fast, nullptr,
|
||||
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||
changed |= ImGui::InputScalar(
|
||||
"X Offset",
|
||||
ImGuiDataType_S32,
|
||||
&cfg::SCREENRESIZE->window_offset_x,
|
||||
&step, &step_fast, nullptr,
|
||||
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||
changed |= ImGui::InputScalar(
|
||||
"Y Offset",
|
||||
ImGuiDataType_S32,
|
||||
&cfg::SCREENRESIZE->window_offset_y,
|
||||
&step, &step_fast, nullptr,
|
||||
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||
if (changed) {
|
||||
if (cfg::SCREENRESIZE->client_keep_aspect_ratio) {
|
||||
cfg::SCREENRESIZE->client_width =
|
||||
cfg::SCREENRESIZE->client_height * cfg::SCREENRESIZE->init_client_aspect_ratio;
|
||||
}
|
||||
graphics_move_resize_window(window);
|
||||
}
|
||||
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
void ScreenResize::build_footer() {
|
||||
// reset button
|
||||
if (ImGui::Button("Reset")) {
|
||||
this->reset_window();
|
||||
}
|
||||
|
||||
// load button
|
||||
|
||||
@@ -15,5 +15,19 @@ namespace overlay::windows {
|
||||
private:
|
||||
size_t toggle_screen_resize = ~0u;
|
||||
bool toggle_screen_resize_state = false;
|
||||
|
||||
void build_fullscreen_config();
|
||||
void build_windowed_config();
|
||||
void build_footer();
|
||||
std::string hwnd_preview(int index, HWND hwnd);
|
||||
HWND get_first_window();
|
||||
|
||||
void reset_window();
|
||||
void reset_vars_to_default();
|
||||
|
||||
static LRESULT CALLBACK screen_resize_wndproc(
|
||||
HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
static void wndproc_wm_sizing(int edge, RECT& rect);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,107 +1,55 @@
|
||||
#undef CINTERFACE
|
||||
|
||||
#include "sdvx_sub.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "games/io.h"
|
||||
#include "hooks/graphics/backends/d3d9/d3d9_backend.h"
|
||||
#include "hooks/graphics/backends/d3d9/d3d9_device.h"
|
||||
#include "util/logging.h"
|
||||
#include "games/sdvx/sdvx.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
const ImVec4 YELLOW(1.f, 1.f, 0.f, 1.f);
|
||||
const ImVec4 WHITE(1.f, 1.f, 1.f, 1.f);
|
||||
SDVXSubScreen::SDVXSubScreen(SpiceOverlay *overlay) : GenericSubScreen(overlay) {
|
||||
this->title = "SDVX Sub Screen";
|
||||
|
||||
SDVXSubScreen::SDVXSubScreen(SpiceOverlay *overlay) : Window(overlay), device(overlay->get_device()) {
|
||||
this->draws_window = false;
|
||||
this->title = "Sub Screen";
|
||||
this->toggle_button = games::OverlayButtons::ToggleSubScreen;
|
||||
const auto padding = ImGui::GetFrameHeight() / 2;
|
||||
|
||||
this->texture_size = ImVec2(0, 0);
|
||||
this->init_size = ImVec2(ImGui::GetIO().DisplaySize.x - (padding * 2), 0.f);
|
||||
this->init_size.y = (this->init_size.x * 9 / 16) + ImGui::GetFrameHeight();
|
||||
|
||||
this->init_pos = ImVec2(ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2, 0);
|
||||
switch (games::sdvx::OVERLAY_POS) {
|
||||
case games::sdvx::SDVX_OVERLAY_TOP:
|
||||
this->init_pos.y = padding;
|
||||
break;
|
||||
case games::sdvx::SDVX_OVERLAY_BOTTOM:
|
||||
this->init_pos.y = ImGui::GetIO().DisplaySize.y - this->init_size.y - padding;
|
||||
break;
|
||||
case games::sdvx::SDVX_OVERLAY_MIDDLE:
|
||||
default:
|
||||
this->init_pos.y = (ImGui::GetIO().DisplaySize.y / 2) - (this->init_size.y / 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SDVXSubScreen::build_content() {
|
||||
this->draw_texture();
|
||||
|
||||
/*
|
||||
if (this->status_message.has_value()) {
|
||||
ImGui::TextColored(YELLOW, "%s", this->status_message.value().c_str());
|
||||
} else if (this->texture) {
|
||||
ImGui::TextColored(WHITE, "Successfully acquired surface texture");
|
||||
} else {
|
||||
ImGui::TextColored(YELLOW, "Failed to acquire surface texture");
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
bool SDVXSubScreen::build_texture(IDirect3DSurface9 *surface) {
|
||||
HRESULT hr;
|
||||
|
||||
D3DSURFACE_DESC desc {};
|
||||
hr = surface->GetDesc(&desc);
|
||||
if (FAILED(hr)) {
|
||||
this->status_message = fmt::format("Failed to get surface descriptor, hr={}", FMT_HRESULT(hr));
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = this->device->CreateTexture(desc.Width, desc.Height, 0, desc.Usage, desc.Format,
|
||||
desc.Pool, &this->texture, nullptr);
|
||||
if (FAILED(hr)) {
|
||||
this->status_message = fmt::format("Failed to create render target, hr={}", FMT_HRESULT(hr));
|
||||
return false;
|
||||
}
|
||||
|
||||
this->texture_size = ImVec2(1080, 608);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDVXSubScreen::draw_texture() {
|
||||
HRESULT hr;
|
||||
|
||||
auto surface = graphics_d3d9_ldj_get_sub_screen();
|
||||
if (surface == nullptr) {
|
||||
void SDVXSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {
|
||||
if (!this->get_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// SDVX Valkyrie cab needs math to make things work
|
||||
// what the game expects from touch screen:
|
||||
// [1080, 0 1920, 1080]
|
||||
// [0, 0 0, 1920]
|
||||
|
||||
if (this->texture == nullptr) {
|
||||
if (!this->build_texture(surface)) {
|
||||
this->texture = nullptr;
|
||||
this->texture_size = ImVec2(0, 0);
|
||||
// actual cursor position given by x_in (on the overlay, normalized):
|
||||
// [0, 0 1, 0]
|
||||
// [0, 1 1, 1]
|
||||
|
||||
surface->Release();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Basically, the game operates on rotated coordinates, but on top of that,
|
||||
// it needs to be adjusted so they match up with the subscreen overlay.
|
||||
|
||||
IDirect3DSurface9 *texture_surface = nullptr;
|
||||
hr = this->texture->GetSurfaceLevel(0, &texture_surface);
|
||||
if (FAILED(hr)) {
|
||||
this->status_message = fmt::format("Failed to get texture surface, hr={}", FMT_HRESULT(hr));
|
||||
// so the math is:
|
||||
// Xin(0, 1) => Yout(0, 1920)
|
||||
// Yin(0, 1) => Xout(1080, 0)
|
||||
|
||||
surface->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
hr = this->device->StretchRect(surface, nullptr, texture_surface, nullptr, D3DTEXF_NONE);
|
||||
if (FAILED(hr)) {
|
||||
this->status_message = fmt::format("Failed to copy back buffer contents, hr={}", FMT_HRESULT(hr));
|
||||
|
||||
surface->Release();
|
||||
texture_surface->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
surface->Release();
|
||||
texture_surface->Release();
|
||||
|
||||
ImGui::GetBackgroundDrawList()->AddImage(
|
||||
reinterpret_cast<void *>(this->texture),
|
||||
ImVec2(0, 0),
|
||||
this->texture_size,
|
||||
ImVec2(0, 0),
|
||||
ImVec2(1, 1));
|
||||
*x_out = ImGui::GetIO().DisplaySize.x * (1.f - xy_in.y);
|
||||
*y_out = ImGui::GetIO().DisplaySize.y * xy_in.x;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,27 +4,18 @@
|
||||
#include <optional>
|
||||
|
||||
#include <windows.h>
|
||||
#include <d3d9.h>
|
||||
|
||||
#include "overlay/window.h"
|
||||
#include "overlay/windows/generic_sub.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
class SDVXSubScreen : public Window {
|
||||
class SDVXSubScreen : public GenericSubScreen {
|
||||
public:
|
||||
SDVXSubScreen(SpiceOverlay *overlay);
|
||||
|
||||
void build_content() override;
|
||||
|
||||
private:
|
||||
bool build_texture(IDirect3DSurface9 *surface);
|
||||
void draw_texture();
|
||||
|
||||
std::optional<std::string> status_message = std::nullopt;
|
||||
|
||||
IDirect3DDevice9 *device = nullptr;
|
||||
IDirect3DTexture9 *texture = nullptr;
|
||||
ImVec2 texture_size;
|
||||
protected:
|
||||
void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) override;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user