Compare commits

...

6 Commits

Author SHA1 Message Date
bicarus-dev 61c97ccaab net: ignore -ea if -url is set (#430)
## Link to GitHub Issue, if one exists
#345 

## Description of change
Ignore -ea if -url is provided

## Testing
2025-12-01 16:04:32 -08:00
bicarus-dev 12d6a88c60 iidx: turn off camera access by default (#428)
## Link to GitHub Issue, if one exists
For #345 
Similar idea as https://github.com/spice2x/spice2x.github.io/pull/350

## Description of change
Disable camera access by default by setting `CONNECT_CAMERA` environment
var to 0 before launching. This was one of those "required" things
people clicked on, but now it's done automatically to reduce friction
for new users.

On cab setups (i.e., running without -iidx module), cameras will remain
on, unless user overrides it.

On franken setups (partial I/O) - user will need to specify what they
want via the new option, we can't guess correctly.

## Testing
2025-11-30 16:13:34 -08:00
bicarus-dev 984b41a1ae cfg: fix combo boxes (#429)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Combo boxes for options were not highlighting the currently selected
item. Also, when there were no description provided for an option, it
was still showing the empty `()` in the selected value.

## Testing
2025-11-30 01:59:25 -08:00
duel0213 d06b8bd731 mfc: fix 2025 version touch (#426)
## Link to GitHub Issue, if one exists
n/a

## Description of change
HG cabinet uses wintouch and it requires TOUCHEVENTF_PRIMARY flag in
order to accept touch inputs.
Added wintouchemu on game hook and TOUCHEVENTF_PRIMARY flag on
wintouchemu.
 
## Testing
The game boots into test menu, touch now works. (tested in I/O TEST ->
TOUCH PANEL CHECK with '-s' flag)
TDJ subscreen overlay and nostalgia seems works fine regardless added
TOUCHEVENTF_PRIMARY flag but more testing required.
2025-11-25 17:14:07 -08:00
bicarus-dev 0dad96b876 check for standalone cfg for fatal options 2025-11-23 09:36:38 -08:00
bicarus-dev def296095b reword log message to avoid confusion 2025-11-22 16:45:27 -08:00
13 changed files with 124 additions and 41 deletions
+22 -8
View File
@@ -53,7 +53,7 @@ namespace games::iidx {
// settings // settings
bool FLIP_CAMS = false; bool FLIP_CAMS = false;
bool DISABLE_CAMS = false; std::optional<bool> DISABLE_CAMS;
bool TDJ_CAMERA = false; bool TDJ_CAMERA = false;
bool TDJ_CAMERA_PREFER_16_9 = true; bool TDJ_CAMERA_PREFER_16_9 = true;
bool TDJ_MODE = false; bool TDJ_MODE = false;
@@ -334,11 +334,6 @@ namespace games::iidx {
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true; wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
wintouchemu::hook_title_ends("beatmania IIDX", "main", avs::game::DLL_INSTANCE); wintouchemu::hook_title_ends("beatmania IIDX", "main", avs::game::DLL_INSTANCE);
} }
// prevent crash on TDJ mode without correct DLL
if (!GetModuleHandle("nvcuda.dll")) {
DISABLE_CAMS = true;
}
} }
// insert BI2X hooks // insert BI2X hooks
@@ -399,7 +394,10 @@ namespace games::iidx {
"RegQueryValueExA", RegQueryValueExA_hook, avs::game::DLL_INSTANCE); "RegQueryValueExA", RegQueryValueExA_hook, avs::game::DLL_INSTANCE);
// check if cam hook should be enabled // check if cam hook should be enabled
if (!DISABLE_CAMS) { if (!DISABLE_CAMS.has_value()) {
log_fatal("iidx", "assertion failure - DISABLE_CAMS not set during attach");
}
if (!DISABLE_CAMS.value()) {
init_legacy_camera_hook(FLIP_CAMS); init_legacy_camera_hook(FLIP_CAMS);
} }
@@ -409,15 +407,31 @@ namespace games::iidx {
void IIDXGame::pre_attach() { void IIDXGame::pre_attach() {
Game::pre_attach(); Game::pre_attach();
auto options = games::get_options(eamuse_get_game());
// environment variables must be set before the DLL is loaded as the VC++ runtime copies all // environment variables must be set before the DLL is loaded as the VC++ runtime copies all
// environment variables at startup // environment variables at startup
if (SCREEN_MODE.has_value()) { if (SCREEN_MODE.has_value()) {
log_misc("iidx", "SCREEN_MODE env var set to {}", SCREEN_MODE.value().c_str());
SetEnvironmentVariable("SCREEN_MODE", SCREEN_MODE.value().c_str()); SetEnvironmentVariable("SCREEN_MODE", SCREEN_MODE.value().c_str());
} }
// check for cab camera access for the second time (first time was in launcher.cpp)
// this time, we are inside -iidx module hook, which means the user is likely NOT on a cab
// therefore, start with cams OFF by default, and allow user to forcibly override to ON
if (!games::iidx::DISABLE_CAMS.has_value()) {
games::iidx::DISABLE_CAMS = true;
if (options->at(launcher::Options::IIDXCabCamAccess).is_active() &&
options->at(launcher::Options::IIDXCabCamAccess).value_text() == "on") {
games::iidx::DISABLE_CAMS = false;
}
if (games::iidx::DISABLE_CAMS.value()) {
log_misc("iidx", "CONNECT_CAMERA env var set to 0");
SetEnvironmentVariable("CONNECT_CAMERA", "0");
}
}
// windowed subscreen, enabled by default, unless turned off by user // windowed subscreen, enabled by default, unless turned off by user
auto options = games::get_options(eamuse_get_game());
if (GRAPHICS_WINDOWED && !options->at(launcher::Options::spice2x_IIDXNoSub).value_bool()) { if (GRAPHICS_WINDOWED && !options->at(launcher::Options::spice2x_IIDXNoSub).value_bool()) {
GRAPHICS_IIDX_WSUB = true; GRAPHICS_IIDX_WSUB = true;
} }
+1 -1
View File
@@ -14,7 +14,7 @@ namespace games::iidx {
// settings // settings
extern bool FLIP_CAMS; extern bool FLIP_CAMS;
extern bool DISABLE_CAMS; extern std::optional<bool> DISABLE_CAMS;
extern bool TDJ_CAMERA; extern bool TDJ_CAMERA;
extern bool TDJ_CAMERA_PREFER_16_9; extern bool TDJ_CAMERA_PREFER_16_9;
extern std::optional<std::string> TDJ_CAMERA_OVERRIDE; extern std::optional<std::string> TDJ_CAMERA_OVERRIDE;
+2
View File
@@ -213,6 +213,8 @@ namespace games::iidx {
} }
void init_legacy_camera_hook(bool flip_cams) { void init_legacy_camera_hook(bool flip_cams) {
log_info("iidx::cam", "initializing legacy camera hook");
should_flip_cams = flip_cams; should_flip_cams = flip_cams;
// camera media framework hook // camera media framework hook
+11 -2
View File
@@ -4,6 +4,7 @@
#include "avs/game.h" #include "avs/game.h"
#include "hooks/graphics/graphics.h" #include "hooks/graphics/graphics.h"
#include "misc/eamuse.h" #include "misc/eamuse.h"
#include "misc/wintouchemu.h"
#include "touch/touch.h" #include "touch/touch.h"
#include "util/detour.h" #include "util/detour.h"
#include "util/libutils.h" #include "util/libutils.h"
@@ -32,6 +33,8 @@ namespace games::mfc {
static uint8_t CARD_TYPE = 0; static uint8_t CARD_TYPE = 0;
static uint8_t CARD_UID[8]; static uint8_t CARD_UID[8];
bool HG_MODE = false;
typedef int (__cdecl *inifile_param_num_t)(const char *, int *); typedef int (__cdecl *inifile_param_num_t)(const char *, int *);
static inifile_param_num_t inifile_param_num_real; static inifile_param_num_t inifile_param_num_real;
@@ -50,8 +53,10 @@ namespace games::mfc {
static void __cdecl touch_init(int width, int height) { static void __cdecl touch_init(int width, int height) {
log_info("mfc", "call touch_init(width: {}, height: {})", width, height); log_info("mfc", "call touch_init(width: {}, height: {})", width, height);
// attach touch module if (HG_MODE) {
if (!TOUCH_ATTACHED) { wintouchemu::ADD_TOUCH_FLAG_PRIMARY = true;
wintouchemu::hook("MFC9", avs::game::DLL_INSTANCE);
} else if (!TOUCH_ATTACHED) { // attach touch module
// store touch size specification // store touch size specification
TOUCH_MAX_X = width; TOUCH_MAX_X = width;
@@ -328,6 +333,10 @@ namespace games::mfc {
if (allinone_module == nullptr) { if (allinone_module == nullptr) {
log_misc("mfc", "using system.dll instead of allinone.dll for i/o hooks"); log_misc("mfc", "using system.dll instead of allinone.dll for i/o hooks");
allinone_module = system_module; allinone_module = system_module;
if (avs::game::SPEC[0] == 'F') {
HG_MODE = true;
}
} }
// network fix // network fix
+2
View File
@@ -4,6 +4,8 @@
namespace games::mfc { namespace games::mfc {
extern bool HG_MODE;
struct joystick_state { struct joystick_state {
bool up = false; bool up = false;
bool down = false; bool down = false;
+1 -1
View File
@@ -106,7 +106,7 @@ namespace games::sdvx {
if (ENABLE_COM_PORT_SCAN_HOOK && if (ENABLE_COM_PORT_SCAN_HOOK &&
lpSubKey != nullptr && phkResult != nullptr && lpSubKey != nullptr && phkResult != nullptr &&
_stricmp(lpSubKey, "HARDWARE\\DEVICEMAP\\SERIALCOMM") == 0) { _stricmp(lpSubKey, "HARDWARE\\DEVICEMAP\\SERIALCOMM") == 0) {
log_info("sdvx::io", "failing HKLM\\HARDWARE\\DEVICEMAP\\SERIALCOMM to force COM1 ICCA"); log_info("sdvx::io", "hook access to HKLM\\HARDWARE\\DEVICEMAP\\SERIALCOMM to force COM1 ICCA");
return 2; // ERROR_FILE_NOT_FOUND return 2; // ERROR_FILE_NOT_FOUND
} }
@@ -15,6 +15,7 @@
#include "cfg/screen_resize.h" #include "cfg/screen_resize.h"
#include "games/iidx/iidx.h" #include "games/iidx/iidx.h"
#include "games/sdvx/sdvx.h" #include "games/sdvx/sdvx.h"
#include "games/mfc/mfc.h"
#include "games/io.h" #include "games/io.h"
#include "hooks/graphics/graphics.h" #include "hooks/graphics/graphics.h"
#include "launcher/launcher.h" #include "launcher/launcher.h"
@@ -1360,6 +1361,11 @@ void graphics_d3d9_on_present(
graphics_d3d9_ldj_on_present(wrapped_device); graphics_d3d9_ldj_on_present(wrapped_device);
} }
const bool is_mfc = avs::game::is_model("KK9") && games::mfc::HG_MODE;
if (is_mfc) {
wintouchemu::update();
}
// check screenshot key // check screenshot key
static bool trigger_last = false; static bool trigger_last = false;
auto buttons = games::get_buttons_overlay(eamuse_get_game()); auto buttons = games::get_buttons_overlay(eamuse_get_game());
+27 -25
View File
@@ -3,6 +3,7 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <assert.h>
#include <shlwapi.h> #include <shlwapi.h>
#include <cfg/configurator.h> #include <cfg/configurator.h>
@@ -299,10 +300,14 @@ int main_implementation(int argc, char *argv[]) {
cfg::CONFIGURATOR_TYPE = cfg::ConfigType::Config; cfg::CONFIGURATOR_TYPE = cfg::ConfigType::Config;
cfg_run = true; cfg_run = true;
} }
if (options[launcher::Options::EAmusementEmulation].value_bool()) {
// -ea, but ignored if -url is set
if (options[launcher::Options::EAmusementEmulation].value_bool() &&
!options[launcher::Options::ServiceURL].is_active()) {
avs::ea3::URL_SLASH = 1; avs::ea3::URL_SLASH = 1;
easrv_port = 8080; easrv_port = 8080;
} }
if (options[launcher::Options::SmartEAmusement].value_bool()) { if (options[launcher::Options::SmartEAmusement].value_bool()) {
easrv_smart = true; easrv_smart = true;
} }
@@ -458,17 +463,26 @@ int main_implementation(int argc, char *argv[]) {
if (options[launcher::Options::IIDXCameraOrderFlip].value_bool()) { if (options[launcher::Options::IIDXCameraOrderFlip].value_bool()) {
games::iidx::FLIP_CAMS = true; games::iidx::FLIP_CAMS = true;
} }
// IIDX CONNECT_CAMERA logic here for cases where user is running without -iidx module
// for now, we assume that user may be running on a cab
// (games::iidx::DISABLE_CAMS starts out as false unless user overrides)
// we will check again in IIDX module with a different default
assert(!games::iidx::DISABLE_CAMS.has_value());
if (options[launcher::Options::IIDXDisableCameras].value_bool()) { if (options[launcher::Options::IIDXDisableCameras].value_bool()) {
games::iidx::DISABLE_CAMS = true; games::iidx::DISABLE_CAMS = true;
} }
if (options[launcher::Options::IIDXCabCamAccess].is_active() &&
options[launcher::Options::IIDXCabCamAccess].value_text() == "off") {
games::iidx::DISABLE_CAMS = true;
}
if (options[launcher::Options::IIDXCamHook].value_bool()) { if (options[launcher::Options::IIDXCamHook].value_bool()) {
games::iidx::TDJ_CAMERA = true; games::iidx::TDJ_CAMERA = true;
// Disable legacy behaviour to avoid conflict // Disable legacy behaviour to avoid conflict
games::iidx::DISABLE_CAMS = true; games::iidx::DISABLE_CAMS = true;
} }
if (games::iidx::DISABLE_CAMS) { // CONNECT_CAMERA env var will be set once logging is enabled
SetEnvironmentVariable("CONNECT_CAMERA", "0");
}
if (options[launcher::Options::IIDXCamHookOverride].is_active()) { if (options[launcher::Options::IIDXCamHookOverride].is_active()) {
games::iidx::TDJ_CAMERA_OVERRIDE = options[launcher::Options::IIDXCamHookOverride].value_text(); games::iidx::TDJ_CAMERA_OVERRIDE = options[launcher::Options::IIDXCamHookOverride].value_text();
} }
@@ -1303,25 +1317,6 @@ int main_implementation(int argc, char *argv[]) {
); );
} }
if (options[launcher::Options::EAmusementEmulation].value_bool() &&
options[launcher::Options::ServiceURL].is_active() &&
!cfg::CONFIGURATOR_STANDALONE) {
log_warning(
"launcher",
"BAD EAMUSE SETTINGS ERROR\n\n\n"
"-------------------------------------------------------------------\n"
"WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING\n"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
"A service URL is set **AND** E-Amusement emulation is enabled.\n"
"Either remove the service URL, or disable E-Amusement emulation.\n"
"Otherwise you may experience problems logging in.\n"
"-------------------------------------------------------------------\n\n\n"
);
log_fatal(
"launcher",
"BAD EAMUSE SETTINGS ERROR: both -ea and -url are set");
}
if (options[launcher::Options::FullscreenResolution].is_active()) { if (options[launcher::Options::FullscreenResolution].is_active()) {
std::pair<uint32_t, uint32_t> result; std::pair<uint32_t, uint32_t> result;
if (parse_width_height(options[launcher::Options::FullscreenResolution].value_text(), result)) { if (parse_width_height(options[launcher::Options::FullscreenResolution].value_text(), result)) {
@@ -1333,7 +1328,7 @@ int main_implementation(int argc, char *argv[]) {
// SDVXFullscreenLandscape disabled due to it messing with in-game camera angle // SDVXFullscreenLandscape disabled due to it messing with in-game camera angle
// FullscreenOrientationFlip continues to live on, however. // FullscreenOrientationFlip continues to live on, however.
if (options[launcher::Options::SDVXFullscreenLandscape].value_bool()) { if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !cfg::CONFIGURATOR_STANDALONE) {
log_fatal("launcher", "-sdvxlandscape removed due to a camera bug in SDVX!"); log_fatal("launcher", "-sdvxlandscape removed due to a camera bug in SDVX!");
} }
// if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !GRAPHICS_WINDOWED) { // if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !GRAPHICS_WINDOWED) {
@@ -1348,8 +1343,15 @@ int main_implementation(int argc, char *argv[]) {
GRAPHICS_FS_ORIENTATION_SWAP = true; GRAPHICS_FS_ORIENTATION_SWAP = true;
} }
if (games::iidx::DISABLE_CAMS.has_value() &&
games::iidx::DISABLE_CAMS.value() &&
!cfg::CONFIGURATOR_STANDALONE) {
log_misc("launcher", "CONNECT_CAMERA env var set to 0");
SetEnvironmentVariable("CONNECT_CAMERA", "0");
}
// deleted options // deleted options
if (options[launcher::Options::OpenKFControl].value_bool()) { if (options[launcher::Options::OpenKFControl].value_bool() && !cfg::CONFIGURATOR_STANDALONE) {
log_fatal("launcher", "KFControl has been removed from spice2x; please use an older version."); log_fatal("launcher", "KFControl has been removed from spice2x; please use an older version.");
} }
if (options[launcher::Options::VREnable].value_bool()) { if (options[launcher::Options::VREnable].value_bool()) {
+18 -1
View File
@@ -456,13 +456,30 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options (Advanced)", .category = "Game Options (Advanced)",
}, },
{ {
.title = "IIDX Disable Cameras", // IIDXDisableCameras
.title = "IIDX Disable Cameras (DEPRECATED - no longer needed)",
.name = "iidxdisablecams", .name = "iidxdisablecams",
.desc = "Disables cameras", .desc = "Disables cameras",
.type = OptionType::Bool, .type = OptionType::Bool,
.hidden = true,
.game_name = "Beatmania IIDX", .game_name = "Beatmania IIDX",
.category = "Game Options", .category = "Game Options",
}, },
{
// IIDXCabCamAccess
.title = "IIDX Use Official AC Cams",
.name = "iidxcabcams",
.desc = "For IIDX25+, allow direct access to cameras from real arcade cabinets. "
"Only turn this on if you have OFFICIAL arcade cameras connected to the correct USB ports. Default: auto",
.type = OptionType::Enum,
.game_name = "Beatmania IIDX",
.category = "Game Options (Advanced)",
.elements = {
{"auto", ""},
{"off", ""},
{"on", ""}
},
},
{ {
// IIDXCamHook // IIDXCamHook
.title = "IIDX Cam Hook", .title = "IIDX Cam Hook",
+1
View File
@@ -55,6 +55,7 @@ namespace launcher {
LoadIIDXModule, LoadIIDXModule,
IIDXCameraOrderFlip, IIDXCameraOrderFlip,
IIDXDisableCameras, IIDXDisableCameras,
IIDXCabCamAccess,
IIDXCamHook, IIDXCamHook,
IIDXCamHookRatio, IIDXCamHookRatio,
IIDXCamHookOverride, IIDXCamHookOverride,
+25
View File
@@ -34,6 +34,7 @@ namespace wintouchemu {
bool FORCE = false; bool FORCE = false;
bool INJECT_MOUSE_AS_WM_TOUCH = false; bool INJECT_MOUSE_AS_WM_TOUCH = false;
bool LOG_FPS = false; bool LOG_FPS = false;
bool ADD_TOUCH_FLAG_PRIMARY = false;
static inline bool is_emu_enabled() { static inline bool is_emu_enabled() {
return FORCE || !is_touch_available("wintouchemu::is_emu_enabled") || GRAPHICS_SHOW_CURSOR; return FORCE || !is_touch_available("wintouchemu::is_emu_enabled") || GRAPHICS_SHOW_CURSOR;
@@ -187,16 +188,28 @@ namespace wintouchemu {
switch (touch_event->type) { switch (touch_event->type) {
case TOUCH_DOWN: case TOUCH_DOWN:
if (valid) { if (valid) {
if (ADD_TOUCH_FLAG_PRIMARY) {
touch_input->dwFlags |= TOUCHEVENTF_PRIMARY;
}
touch_input->dwFlags |= TOUCHEVENTF_DOWN; touch_input->dwFlags |= TOUCHEVENTF_DOWN;
} }
break; break;
case TOUCH_MOVE: case TOUCH_MOVE:
if (valid) { if (valid) {
if (ADD_TOUCH_FLAG_PRIMARY) {
touch_input->dwFlags |= TOUCHEVENTF_PRIMARY;
}
touch_input->dwFlags |= TOUCHEVENTF_MOVE; touch_input->dwFlags |= TOUCHEVENTF_MOVE;
} }
break; break;
case TOUCH_UP: case TOUCH_UP:
// don't check valid so that this touch ID can be released // don't check valid so that this touch ID can be released
if (ADD_TOUCH_FLAG_PRIMARY) {
touch_input->dwFlags |= TOUCHEVENTF_PRIMARY;
}
touch_input->dwFlags |= TOUCHEVENTF_UP; touch_input->dwFlags |= TOUCHEVENTF_UP;
break; break;
} }
@@ -238,16 +251,28 @@ namespace wintouchemu {
switch (mouse_state.touch_event) { switch (mouse_state.touch_event) {
case TOUCHEVENTF_DOWN: case TOUCHEVENTF_DOWN:
if (valid) { if (valid) {
if (ADD_TOUCH_FLAG_PRIMARY) {
touch_input->dwFlags |= TOUCHEVENTF_PRIMARY;
}
touch_input->dwFlags |= TOUCHEVENTF_DOWN; touch_input->dwFlags |= TOUCHEVENTF_DOWN;
} }
break; break;
case TOUCHEVENTF_MOVE: case TOUCHEVENTF_MOVE:
if (valid) { if (valid) {
if (ADD_TOUCH_FLAG_PRIMARY) {
touch_input->dwFlags |= TOUCHEVENTF_PRIMARY;
}
touch_input->dwFlags |= TOUCHEVENTF_MOVE; touch_input->dwFlags |= TOUCHEVENTF_MOVE;
} }
break; break;
case TOUCHEVENTF_UP: case TOUCHEVENTF_UP:
// don't check valid so that this touch ID can be released // don't check valid so that this touch ID can be released
if (ADD_TOUCH_FLAG_PRIMARY) {
touch_input->dwFlags |= TOUCHEVENTF_PRIMARY;
}
touch_input->dwFlags |= TOUCHEVENTF_UP; touch_input->dwFlags |= TOUCHEVENTF_UP;
break; break;
} }
+1
View File
@@ -8,6 +8,7 @@ namespace wintouchemu {
extern bool FORCE; extern bool FORCE;
extern bool INJECT_MOUSE_AS_WM_TOUCH; extern bool INJECT_MOUSE_AS_WM_TOUCH;
extern bool LOG_FPS; extern bool LOG_FPS;
extern bool ADD_TOUCH_FLAG_PRIMARY;
void hook(const char *window_title, HMODULE module = nullptr, int delay_in_s=0); void hook(const char *window_title, HMODULE module = nullptr, int delay_in_s=0);
void hook_title_ends( void hook_title_ends(
+5 -1
View File
@@ -2828,19 +2828,23 @@ namespace overlay::windows {
std::string current_item = option.value_text(); std::string current_item = option.value_text();
for (auto &element : definition.elements) { for (auto &element : definition.elements) {
if (element.first == current_item) { if (element.first == current_item) {
if (!element.second.empty()) {
current_item += fmt::format(" ({})", element.second); current_item += fmt::format(" ({})", element.second);
} }
break;
}
} }
if (current_item.empty()) { if (current_item.empty()) {
current_item = "Default"; current_item = "Default";
} }
if (ImGui::BeginCombo("##combo", current_item.c_str(), 0)) { if (ImGui::BeginCombo("##combo", current_item.c_str(), 0)) {
for (auto &element : definition.elements) { for (auto &element : definition.elements) {
bool selected = current_item == element.first;
std::string label = element.first; std::string label = element.first;
if (!element.second.empty()) { if (!element.second.empty()) {
label += fmt::format(" ({})", element.second); label += fmt::format(" ({})", element.second);
} }
bool selected = current_item == label;
if (ImGui::Selectable(label.c_str(), selected)) { if (ImGui::Selectable(label.c_str(), selected)) {
this->options_dirty = true; this->options_dirty = true;
option.value = element.first; option.value = element.first;