Compare commits

...

1 Commits

Author SHA1 Message Date
bicarus dc82c980b1 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.
2026-02-15 19:49:07 -08:00
7 changed files with 47 additions and 9 deletions
+4 -1
View File
@@ -82,6 +82,7 @@ namespace games::iidx {
bool IIDXIO_LED_TICKER_READONLY = false; bool IIDXIO_LED_TICKER_READONLY = false;
std::mutex IIDX_LED_TICKER_LOCK; std::mutex IIDX_LED_TICKER_LOCK;
bool IIDX_TDJ_MONITOR_WARNING = false; bool IIDX_TDJ_MONITOR_WARNING = false;
iidx_aio_emulation_state CURRENT_IO_EMULATION_STATE = iidx_aio_emulation_state::unknown;
// io // io
static bool HAS_LIBAIO; // this is how we detect iidx27+ 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 // 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; GRAPHICS_IIDX_WSUB = true;
} }
@@ -934,6 +935,8 @@ namespace games::iidx {
return; return;
} }
CURRENT_IO_EMULATION_STATE = state;
// log_warning below could be turned into log_fatal in the future once we gain confidence // 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 // that this isn't triggering when it's not supposed to
if (state == iidx_aio_emulation_state::bi2a_com2 && TDJ_MODE) { if (state == iidx_aio_emulation_state::bi2a_com2 && TDJ_MODE) {
+7 -4
View File
@@ -11,6 +11,12 @@
namespace games::iidx { namespace games::iidx {
enum class iidx_aio_emulation_state {
unknown,
bi2a_com2,
bi2x_hook
};
// settings // settings
extern bool FLIP_CAMS; extern bool FLIP_CAMS;
@@ -38,6 +44,7 @@ namespace games::iidx {
constexpr int IIDX_TAPELED_TOTAL = 17; constexpr int IIDX_TAPELED_TOTAL = 17;
// data mapping // data mapping
extern tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL]; extern tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL];
extern iidx_aio_emulation_state CURRENT_IO_EMULATION_STATE;
class IIDXGame : public games::Game { class IIDXGame : public games::Game {
public: public:
@@ -64,9 +71,5 @@ namespace games::iidx {
bool is_tdj_fhd(); bool is_tdj_fhd();
void apply_audio_hacks(); void apply_audio_hacks();
enum class iidx_aio_emulation_state {
bi2a_com2,
bi2x_hook
};
void update_io_emulation_state(iidx_aio_emulation_state state); void update_io_emulation_state(iidx_aio_emulation_state state);
} }
@@ -85,10 +85,12 @@ namespace overlay::windows {
} }
void GenericSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {} void GenericSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {}
void GenericSubScreen::check_for_errors() {}
void GenericSubScreen::build_content() { void GenericSubScreen::build_content() {
this->flags |= ImGuiWindowFlags_NoBackground; this->flags |= ImGuiWindowFlags_NoBackground;
check_for_errors();
if (this->disabled_message.has_value()) { if (this->disabled_message.has_value()) {
this->draws_window = true; this->draws_window = true;
this->flags &= ~ImGuiWindowFlags_NoBackground; this->flags &= ~ImGuiWindowFlags_NoBackground;
@@ -31,6 +31,7 @@ namespace overlay::windows {
protected: protected:
virtual void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out); 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_top_left;
ImVec2 overlay_content_size; ImVec2 overlay_content_size;
std::optional<std::string> disabled_message = std::nullopt; std::optional<std::string> disabled_message = std::nullopt;
+18
View File
@@ -14,6 +14,7 @@ namespace overlay::windows {
static const size_t TICKER_SIZE = 9; static const size_t TICKER_SIZE = 9;
static const ImVec4 DARK_GRAY(0.1f, 0.1f, 0.1f, 1.f); 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 ImVec4 RED(1.f, 0.f, 0.f, 1.f);
static const float PADDING_Y = 8.f; static const float PADDING_Y = 8.f;
static const float PADDING_X = 4.f; static const float PADDING_X = 4.f;
@@ -91,6 +92,23 @@ namespace overlay::windows {
} }
void IIDXSegmentDisplay::build_content() { 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]; char input_ticker[TICKER_SIZE];
// get ticker content from game // get ticker content from game
+14 -4
View File
@@ -12,11 +12,9 @@ namespace overlay::windows {
this->title = "IIDX TDJ Subscreen"; this->title = "IIDX TDJ Subscreen";
if (GRAPHICS_IIDX_WSUB) { if (GRAPHICS_IIDX_WSUB) {
this->disabled_message = this->disabled_message = "Close this overlay and use the second window. (try ALT+TAB)";
"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.";
} else if (games::iidx::IIDX_TDJ_MONITOR_WARNING) { } 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; float size = 0.5f;
@@ -44,6 +42,18 @@ namespace overlay::windows {
ImGui::GetIO().DisplaySize.y - this->init_size.y - (ImGui::GetFrameHeight() / 2)); 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) { void IIDXSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {
if (!this->get_active()) { if (!this->get_active()) {
return; return;
+1
View File
@@ -12,6 +12,7 @@ namespace overlay::windows {
protected: protected:
void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) override; void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) override;
void check_for_errors() override;
}; };
} }