Update to spice2x-23-06-08

This commit is contained in:
[ ]
2023-09-20 02:09:17 +09:00
parent 8957d9d446
commit d03cca1fe2
98 changed files with 21957 additions and 4977 deletions
+5 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+1 -1
View File
@@ -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
View File
@@ -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();
}
}
+1 -1
View File
@@ -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
View File
@@ -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());
}
}
}
+5 -1
View File
@@ -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;
};
}
+183
View File
@@ -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
}
}
+42
View File
@@ -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
+148
View File
@@ -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();
}
}
+21
View File
@@ -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);
};
}
+27 -91
View File
@@ -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;
}
}
+4 -17
View File
@@ -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;
};
}
+121
View File
@@ -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();
}
}
+32
View File
@@ -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;
};
}
+95
View File
@@ -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();
}
}
+29
View File
@@ -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];
};
}
+163
View File
@@ -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;
}
}
}
+31
View File
@@ -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];
};
}
+168
View File
@@ -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;
}
}
}
}
}
+33
View File
@@ -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;
};
}
+1 -1
View File
@@ -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);
}
+166 -26
View File
@@ -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
+14
View File
@@ -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);
};
}
+37 -89
View File
@@ -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 -13
View File
@@ -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;
};
}