Compare commits

..

2 Commits

Author SHA1 Message Date
bicarus 408ed17521 cfg: turn a handful of parameter parsing errors into fatal (#554)
For parameters that require two values (`width, height` or `x, y`), if
the string fails to parse, turn into runtime failure.
2026-02-13 03:22:20 -08:00
bicarus f4050e9adb graphics: custom resolution for subscreen (#553)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Allow user to specify a custom resolution for the sub monitor, in
addition to the existing refresh rate option

This would allow practically any touch monitor (including ones with
weird resolution / aspect ratio / refresh rate) to be used with
IIDX/SDVX.

## Testing
Tested with various resolution / refresh rate combinations on IIDX /
SDVX.
2026-02-12 17:51:38 -08:00
10 changed files with 89 additions and 49 deletions
@@ -945,26 +945,37 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
for (size_t i = 0; i < num_adapters; i++) { for (size_t i = 0; i < num_adapters; i++) {
auto *params = &pPresentationParameters[i]; auto *params = &pPresentationParameters[i];
if (!GRAPHICS_WINDOWED && i == 0) { if (!GRAPHICS_WINDOWED){
GRAPHICS_FS_ORIGINAL_WIDTH = params->BackBufferWidth; if (i == 0) {
GRAPHICS_FS_ORIGINAL_HEIGHT = params->BackBufferHeight; GRAPHICS_FS_ORIGINAL_WIDTH = params->BackBufferWidth;
log_misc("graphics::d3d9", "original resolution: {}x{}", GRAPHICS_FS_ORIGINAL_WIDTH, GRAPHICS_FS_ORIGINAL_HEIGHT); GRAPHICS_FS_ORIGINAL_HEIGHT = params->BackBufferHeight;
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) { log_misc("graphics::d3d9", "original resolution: {}x{}", GRAPHICS_FS_ORIGINAL_WIDTH, GRAPHICS_FS_ORIGINAL_HEIGHT);
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
log_misc(
"graphics::d3d9",
"use custom resolution {}x{} => {}x{}",
params->BackBufferWidth, params->BackBufferHeight,
GRAPHICS_FS_CUSTOM_RESOLUTION.value().first,
GRAPHICS_FS_CUSTOM_RESOLUTION.value().second);
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
log_misc(
"graphics::d3d9",
"swap full orientation {}x{} => {}x{}",
params->BackBufferWidth, params->BackBufferHeight,
params->BackBufferHeight, params->BackBufferWidth);
std::swap(params->BackBufferWidth, params->BackBufferHeight);
}
} else if (i == 1 && GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.has_value()) {
log_misc( log_misc(
"graphics::d3d9", "graphics::d3d9",
"use custom resolution {}x{} => {}x{}", "use custom sub resolution {}x{} => {}x{}",
params->BackBufferWidth, params->BackBufferHeight, params->BackBufferWidth, params->BackBufferHeight,
GRAPHICS_FS_CUSTOM_RESOLUTION.value().first, GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().first,
GRAPHICS_FS_CUSTOM_RESOLUTION.value().second); GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().second);
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first; params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().first;
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second; params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().second;
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
log_misc(
"graphics::d3d9",
"swap full orientation {}x{} => {}x{}",
params->BackBufferWidth, params->BackBufferHeight,
params->BackBufferHeight, params->BackBufferWidth);
std::swap(params->BackBufferWidth, params->BackBufferHeight);
} }
} }
@@ -992,12 +1003,17 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
for (size_t i = 0; i < num_adapters; i++) { for (size_t i = 0; i < num_adapters; i++) {
auto *fullscreen_display_mode = &pFullscreenDisplayMode[i]; auto *fullscreen_display_mode = &pFullscreenDisplayMode[i];
if (!GRAPHICS_WINDOWED && i == 0) { if (!GRAPHICS_WINDOWED) {
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) { if (i == 0) {
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first; if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second; fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
} else if (GRAPHICS_FS_ORIENTATION_SWAP) { fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
std::swap(fullscreen_display_mode->Width, fullscreen_display_mode->Height); } else if (GRAPHICS_FS_ORIENTATION_SWAP) {
std::swap(fullscreen_display_mode->Width, fullscreen_display_mode->Height);
}
} else if (i == 1 && GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.has_value()) {
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().first;
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().second;
} }
} }
+1
View File
@@ -71,6 +71,7 @@ bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME = false;
bool SUBSCREEN_FORCE_REDRAW = false; bool SUBSCREEN_FORCE_REDRAW = false;
bool D3D9_DEVICE_HOOK_DISABLE = false; bool D3D9_DEVICE_HOOK_DISABLE = false;
std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION; std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION_SUB;
bool GRAPHICS_FS_ORIENTATION_SWAP = false; bool GRAPHICS_FS_ORIENTATION_SWAP = false;
uint32_t GRAPHICS_FS_ORIGINAL_WIDTH = 0; uint32_t GRAPHICS_FS_ORIGINAL_WIDTH = 0;
uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT = 0; uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT = 0;
+1
View File
@@ -37,6 +37,7 @@ extern std::optional<int> GRAPHICS_FORCE_VSYNC_BUFFER;
extern bool GRAPHICS_FORCE_SINGLE_ADAPTER; extern bool GRAPHICS_FORCE_SINGLE_ADAPTER;
extern bool GRAPHICS_PREVENT_SECONDARY_WINDOW; extern bool GRAPHICS_PREVENT_SECONDARY_WINDOW;
extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION; extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION_SUB;
extern bool GRAPHICS_FS_ORIENTATION_SWAP; extern bool GRAPHICS_FS_ORIENTATION_SWAP;
extern uint32_t GRAPHICS_FS_ORIGINAL_WIDTH; extern uint32_t GRAPHICS_FS_ORIGINAL_WIDTH;
extern uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT; extern uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT;
@@ -173,7 +173,7 @@ void graphics_load_windowed_parameters() {
cfg::SCREENRESIZE->window_offset_x = result.first; cfg::SCREENRESIZE->window_offset_x = result.first;
cfg::SCREENRESIZE->window_offset_y = result.second; cfg::SCREENRESIZE->window_offset_y = result.second;
} else { } else {
log_warning("graphics-windowed", "failed to parse -windowpos"); log_fatal("graphics-windowed", "failed to parse -windowpos");
} }
} }
@@ -202,7 +202,7 @@ void graphics_load_windowed_subscreen_parameters() {
GRAPHICS_WSUB_WIDTH = result.first; GRAPHICS_WSUB_WIDTH = result.first;
GRAPHICS_WSUB_HEIGHT = result.second; GRAPHICS_WSUB_HEIGHT = result.second;
} else { } else {
log_warning("graphics-windowed", "failed to parse -wsubsize"); log_fatal("graphics-windowed", "failed to parse -iidxwsubsize / -sdvxwsubsize");
} }
} }
@@ -216,7 +216,7 @@ void graphics_load_windowed_subscreen_parameters() {
GRAPHICS_WSUB_X = result.first; GRAPHICS_WSUB_X = result.first;
GRAPHICS_WSUB_Y = result.second; GRAPHICS_WSUB_Y = result.second;
} else { } else {
log_warning("graphics-windowed", "failed to parse -wsubpos"); log_fatal("graphics-windowed", "failed to parse -iidxwsubpos / -sdvxwsubpos");
} }
} }
} }
+13 -4
View File
@@ -1083,8 +1083,8 @@ int main_implementation(int argc, char *argv[]) {
std::pair<uint32_t, uint32_t> result; std::pair<uint32_t, uint32_t> result;
if (parse_width_height(options[launcher::Options::spice2x_WindowSize].value_text(), result)) { if (parse_width_height(options[launcher::Options::spice2x_WindowSize].value_text(), result)) {
GRAPHICS_WINDOW_SIZE = result; GRAPHICS_WINDOW_SIZE = result;
} else { } else if (!cfg_run && !cfg::CONFIGURATOR_STANDALONE) {
log_warning("launcher", "failed to parse -windowsize"); log_fatal("launcher", "failed to parse -windowsize");
} }
} }
if (options[launcher::Options::spice2x_WindowPosition].is_active()) { if (options[launcher::Options::spice2x_WindowPosition].is_active()) {
@@ -1401,8 +1401,17 @@ int main_implementation(int argc, char *argv[]) {
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)) {
GRAPHICS_FS_CUSTOM_RESOLUTION = result; GRAPHICS_FS_CUSTOM_RESOLUTION = result;
} else { } else if (!cfg_run && !cfg::CONFIGURATOR_STANDALONE) {
log_warning("launcher", "failed to parse -forceres"); log_fatal("launcher", "failed to parse -forceres");
}
}
if (options[launcher::Options::FullscreenSubResolution].is_active()) {
std::pair<uint32_t, uint32_t> result;
if (parse_width_height(options[launcher::Options::FullscreenSubResolution].value_text(), result)) {
GRAPHICS_FS_CUSTOM_RESOLUTION_SUB = result;
} else if (!cfg_run && !cfg::CONFIGURATOR_STANDALONE) {
log_fatal("launcher", "failed to parse -forceressub");
} }
} }
+27 -15
View File
@@ -256,9 +256,21 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.hidden = true, .hidden = true,
.category = "Graphics (Full Screen)" .category = "Graphics (Full Screen)"
}, },
{
// FullscreenSubResolution
.title = "Force FS Subscreen Resolution (EXPERIMENTAL)",
.name = "forceressub",
.desc =
"Override fullscreen resolution requested by the game for second monitor, "
"useful if you have a sub monitor that is not quite exactly what the game expects.\n\n"
"WARNING: experimental as we have not done extensive testing to see if this causes desyncs.",
.type = OptionType::Text,
.setting_name = "1280,720",
.category = "Graphics (Full Screen)"
},
{ {
// FullscreenSubRefreshRate // FullscreenSubRefreshRate
.title = "Force Submonitor Refresh Rate (EXPERIMENTAL)", .title = "Force FS Subscreen Refresh Rate (EXPERIMENTAL)",
.name = "graphics-force-refresh-sub", .name = "graphics-force-refresh-sub",
.desc = .desc =
"Override fullscreen refresh rate requested by the game for second monitor, " "Override fullscreen refresh rate requested by the game for second monitor, "
@@ -814,7 +826,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options", .category = "Game Options",
}, },
{ {
.title = "SDVX Native Touch Handling", .title = "SDVX FS Subscreen Native Touch Handling",
.name = "sdvxnativetouch", .name = "sdvxnativetouch",
.desc = "Disables touch hooks and lets the game access a touch screen directly. " .desc = "Disables touch hooks and lets the game access a touch screen directly. "
"Requires a touch screen to be connected as a secondary monitor. " "Requires a touch screen to be connected as a secondary monitor. "
@@ -824,6 +836,18 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.game_name = "Sound Voltex", .game_name = "Sound Voltex",
.category = "Game Options (Advanced)", .category = "Game Options (Advanced)",
}, },
{
// spice2x_SDVXSubRedraw
.title = "SDVX FS Subscreen Force Redraw",
.name = "sp2x-sdvxsubredraw",
.display_name = "sdvxsubredraw",
.aliases= "sdvxsubredraw",
.desc = "Check if submonitor in fullscreen mode doesn't update every frame; "
"this option forces subscreen to redraw every frame.",
.type = OptionType::Bool,
.game_name = "Sound Voltex",
.category = "Game Options (Advanced)",
},
{ {
// spice2x_SDVXDigitalKnobSensitivity // spice2x_SDVXDigitalKnobSensitivity
.title = "SDVX Digital Knob Sensitivity", .title = "SDVX Digital Knob Sensitivity",
@@ -884,18 +908,6 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
{"bottomright", "for landscape"}, {"bottomright", "for landscape"},
}, },
}, },
{
// spice2x_SDVXSubRedraw
.title = "SDVX Subscreen Force Redraw",
.name = "sp2x-sdvxsubredraw",
.display_name = "sdvxsubredraw",
.aliases= "sdvxsubredraw",
.desc = "Check if second monitor for subscreen doesn't update every frame; "
"forces subscreen to redraw every frame, only needed for newer EG.",
.type = OptionType::Bool,
.game_name = "Sound Voltex",
.category = "Game Options (Advanced)",
},
{ {
.title = "Force Load DDR Module", .title = "Force Load DDR Module",
.name = "ddr", .name = "ddr",
@@ -2251,7 +2263,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
}, },
{ {
// spice2x_IIDXNativeTouch // spice2x_IIDXNativeTouch
.title = "IIDX Native Touch Handling", .title = "IIDX TDJ Subscreen Native Touch Handling",
.name = "sp2x-iidxnativetouch", .name = "sp2x-iidxnativetouch",
.display_name = "iidxnativetouch", .display_name = "iidxnativetouch",
.aliases= "iidxnativetouch", .aliases= "iidxnativetouch",
+2 -1
View File
@@ -31,6 +31,7 @@ namespace launcher {
GraphicsForceRefresh, GraphicsForceRefresh,
FullscreenResolution, FullscreenResolution,
FullscreenOrientationFlip, FullscreenOrientationFlip,
FullscreenSubResolution,
FullscreenSubRefreshRate, FullscreenSubRefreshRate,
Graphics9On12, Graphics9On12,
spice2x_Dx9On12, spice2x_Dx9On12,
@@ -85,11 +86,11 @@ namespace launcher {
SDVXPrinterJPGQuality, SDVXPrinterJPGQuality,
SDVXDisableCameras, SDVXDisableCameras,
SDVXNativeTouch, SDVXNativeTouch,
spice2x_SDVXSubRedraw,
spice2x_SDVXDigitalKnobSensitivity, spice2x_SDVXDigitalKnobSensitivity,
SDVXDigitalKnobSocd, SDVXDigitalKnobSocd,
spice2x_SDVXAsioDriver, spice2x_SDVXAsioDriver,
spice2x_SDVXSubPos, spice2x_SDVXSubPos,
spice2x_SDVXSubRedraw,
LoadDDRModule, LoadDDRModule,
DDR43Mode, DDR43Mode,
DDRSkipCodecRegisteration, DDRSkipCodecRegisteration,
+1 -1
View File
@@ -13,7 +13,7 @@ namespace overlay::windows {
if (GRAPHICS_IIDX_WSUB) { if (GRAPHICS_IIDX_WSUB) {
this->disabled_message = this->disabled_message =
"Close this overlay and use the second window.\n" "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."; "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";
+1 -1
View File
@@ -120,7 +120,7 @@ namespace overlay::windows {
"Lightning Model cabinets (TDJ) do not have any keypads; they use the subscreen.\n\n" "Lightning Model cabinets (TDJ) do not have any keypads; they use the subscreen.\n\n"
"Fullscreen mode: bind a key in Overlay tab, and press it in game to show the subscreen, " "Fullscreen mode: bind a key in Overlay tab, and press it in game to show the subscreen, "
"then use your mouse to click. Page Up button is the default binding.\n\n" "then use your mouse to click. Page Up button is the default binding.\n\n"
"Windowed mode: look for the second window in the taskbar.\n\n" "Windowed mode: look for the second window in the taskbar (or ALT+TAB).\n\n"
"Windowed mode with -iidxnosub: bring up the subscreen overlay (default Page Up).\n\n" "Windowed mode with -iidxnosub: bring up the subscreen overlay (default Page Up).\n\n"
); );
+1 -1
View File
@@ -18,7 +18,7 @@ namespace overlay::windows {
if (GRAPHICS_PREVENT_SECONDARY_WINDOW) { if (GRAPHICS_PREVENT_SECONDARY_WINDOW) {
this->disabled_message = "Subscreen has been disabled by the user (-sdvxnosub)."; this->disabled_message = "Subscreen has been disabled by the user (-sdvxnosub).";
} else { } else {
this->disabled_message = "Overlay unavailable in windowed mode! Use the second window instead."; this->disabled_message = "Overlay unavailable in windowed mode! Use the second window instead. (ALT+TAB)";
} }
} }