From dc82c980b143e2ae7f95f2df2efe6a8433c05ebe Mon Sep 17 00:00:00 2001 From: bicarus <202771338+bicarus-dev@users.noreply.github.com> Date: Sun, 15 Feb 2026 19:49:07 -0800 Subject: [PATCH] iidx: show error message for emulated I/O mismatch in LDJ/TDJ sub overlay (#555) ## Link to GitHub Issue, if one exists #345 ## Description of change When DLL is configured to use TDJ I/O but spice is in LDJ mode, show an error in LED ticker suggesting the user to turn on `-iidxtdj`. When DLL is configured to use LDJ I/O but spice is in TDJ mode, show an error in TDJ overaly suggesting the user to fix the DLL. Fix a small bug that caused a white window to be created when `-w` is enabled even when `-iidxtdj` is not, but DLL is using TDJ I/O. --- src/spice2x/games/iidx/iidx.cpp | 5 ++++- src/spice2x/games/iidx/iidx.h | 11 +++++++---- src/spice2x/overlay/windows/generic_sub.cpp | 2 ++ src/spice2x/overlay/windows/generic_sub.h | 1 + src/spice2x/overlay/windows/iidx_seg.cpp | 18 ++++++++++++++++++ src/spice2x/overlay/windows/iidx_sub.cpp | 18 ++++++++++++++---- src/spice2x/overlay/windows/iidx_sub.h | 1 + 7 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/spice2x/games/iidx/iidx.cpp b/src/spice2x/games/iidx/iidx.cpp index b1f05d4..9fe0d6f 100644 --- a/src/spice2x/games/iidx/iidx.cpp +++ b/src/spice2x/games/iidx/iidx.cpp @@ -82,6 +82,7 @@ namespace games::iidx { bool IIDXIO_LED_TICKER_READONLY = false; std::mutex IIDX_LED_TICKER_LOCK; bool IIDX_TDJ_MONITOR_WARNING = false; + iidx_aio_emulation_state CURRENT_IO_EMULATION_STATE = iidx_aio_emulation_state::unknown; // io static bool HAS_LIBAIO; // this is how we detect iidx27+ @@ -453,7 +454,7 @@ namespace games::iidx { } // windowed subscreen, enabled by default, unless turned off by user - if (GRAPHICS_WINDOWED && !options->at(launcher::Options::spice2x_IIDXNoSub).value_bool()) { + if (GRAPHICS_WINDOWED && !options->at(launcher::Options::spice2x_IIDXNoSub).value_bool() && TDJ_MODE) { GRAPHICS_IIDX_WSUB = true; } @@ -934,6 +935,8 @@ namespace games::iidx { return; } + CURRENT_IO_EMULATION_STATE = state; + // log_warning below could be turned into log_fatal in the future once we gain confidence // that this isn't triggering when it's not supposed to if (state == iidx_aio_emulation_state::bi2a_com2 && TDJ_MODE) { diff --git a/src/spice2x/games/iidx/iidx.h b/src/spice2x/games/iidx/iidx.h index 997d1a7..2178901 100644 --- a/src/spice2x/games/iidx/iidx.h +++ b/src/spice2x/games/iidx/iidx.h @@ -11,6 +11,12 @@ namespace games::iidx { + enum class iidx_aio_emulation_state { + unknown, + bi2a_com2, + bi2x_hook + }; + // settings extern bool FLIP_CAMS; @@ -38,6 +44,7 @@ namespace games::iidx { constexpr int IIDX_TAPELED_TOTAL = 17; // data mapping extern tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL]; + extern iidx_aio_emulation_state CURRENT_IO_EMULATION_STATE; class IIDXGame : public games::Game { public: @@ -64,9 +71,5 @@ namespace games::iidx { bool is_tdj_fhd(); void apply_audio_hacks(); - enum class iidx_aio_emulation_state { - bi2a_com2, - bi2x_hook - }; void update_io_emulation_state(iidx_aio_emulation_state state); } diff --git a/src/spice2x/overlay/windows/generic_sub.cpp b/src/spice2x/overlay/windows/generic_sub.cpp index b20df4d..c5244c6 100644 --- a/src/spice2x/overlay/windows/generic_sub.cpp +++ b/src/spice2x/overlay/windows/generic_sub.cpp @@ -85,10 +85,12 @@ namespace overlay::windows { } void GenericSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {} + void GenericSubScreen::check_for_errors() {} void GenericSubScreen::build_content() { this->flags |= ImGuiWindowFlags_NoBackground; + check_for_errors(); if (this->disabled_message.has_value()) { this->draws_window = true; this->flags &= ~ImGuiWindowFlags_NoBackground; diff --git a/src/spice2x/overlay/windows/generic_sub.h b/src/spice2x/overlay/windows/generic_sub.h index 0ee53f8..ae39bfb 100644 --- a/src/spice2x/overlay/windows/generic_sub.h +++ b/src/spice2x/overlay/windows/generic_sub.h @@ -31,6 +31,7 @@ namespace overlay::windows { protected: virtual void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out); + virtual void check_for_errors(); ImVec2 overlay_content_top_left; ImVec2 overlay_content_size; std::optional disabled_message = std::nullopt; diff --git a/src/spice2x/overlay/windows/iidx_seg.cpp b/src/spice2x/overlay/windows/iidx_seg.cpp index 0cf37e1..02d9708 100644 --- a/src/spice2x/overlay/windows/iidx_seg.cpp +++ b/src/spice2x/overlay/windows/iidx_seg.cpp @@ -14,6 +14,7 @@ namespace overlay::windows { static const size_t TICKER_SIZE = 9; static const ImVec4 DARK_GRAY(0.1f, 0.1f, 0.1f, 1.f); + static const ImVec4 YELLOW(1.f, 1.f, 0.f, 1.f); static const ImVec4 RED(1.f, 0.f, 0.f, 1.f); static const float PADDING_Y = 8.f; static const float PADDING_X = 4.f; @@ -91,6 +92,23 @@ namespace overlay::windows { } void IIDXSegmentDisplay::build_content() { + + // explicitly check if TDJ I/O is enabled to account for the fact that I/O emulation may be + // disabled + if (games::iidx::CURRENT_IO_EMULATION_STATE == games::iidx::iidx_aio_emulation_state::bi2x_hook) { + ImGui::TextUnformatted(""); + ImGui::TextColored( + YELLOW, + "%s", + "Invalid I/O mode; DLL is configured for TDJ I/O!\n" + "Enable -iidxtdj if you want TDJ subscreen overlay."); + ImGui::TextUnformatted(""); + if (ImGui::Button("Close")) { + this->set_active(false); + } + return; + } + char input_ticker[TICKER_SIZE]; // get ticker content from game diff --git a/src/spice2x/overlay/windows/iidx_sub.cpp b/src/spice2x/overlay/windows/iidx_sub.cpp index 4c01684..4f8ea5a 100644 --- a/src/spice2x/overlay/windows/iidx_sub.cpp +++ b/src/spice2x/overlay/windows/iidx_sub.cpp @@ -12,11 +12,9 @@ namespace overlay::windows { this->title = "IIDX TDJ Subscreen"; if (GRAPHICS_IIDX_WSUB) { - this->disabled_message = - "Close this overlay and use the second window (ALT+TAB).\n" - "If you don't see the window, double check your DLL type and apply TDJ I/O patches as needed."; + this->disabled_message = "Close this overlay and use the second window. (try ALT+TAB)"; } else if (games::iidx::IIDX_TDJ_MONITOR_WARNING) { - this->disabled_message = "TDJ mode subscreen overlay is not compatible with -monitor option"; + this->disabled_message = "TDJ mode subscreen overlay is not compatible with -monitor option."; } float size = 0.5f; @@ -44,6 +42,18 @@ namespace overlay::windows { ImGui::GetIO().DisplaySize.y - this->init_size.y - (ImGui::GetFrameHeight() / 2)); } + void IIDXSubScreen::check_for_errors() { + if (games::iidx::CURRENT_IO_EMULATION_STATE == + games::iidx::iidx_aio_emulation_state::bi2a_com2) { + + // explicitly check if LDJ I/O is enabled (as opposed to checking if NOT TDJ I/O) + // to account for the fact that I/O emulation may be disabled + this->disabled_message = + "LDJ I/O detected; TDJ mode subscreen overlay requires TDJ I/O.\n" + "Ensure you apply applicable TDJ I/O patches, or run with TDJ DLL."; + } + } + void IIDXSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) { if (!this->get_active()) { return; diff --git a/src/spice2x/overlay/windows/iidx_sub.h b/src/spice2x/overlay/windows/iidx_sub.h index adb897d..7d77cae 100644 --- a/src/spice2x/overlay/windows/iidx_sub.h +++ b/src/spice2x/overlay/windows/iidx_sub.h @@ -12,6 +12,7 @@ namespace overlay::windows { protected: void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) override; + void check_for_errors() override; }; }