diff --git a/src/spice2x/CMakeLists.txt b/src/spice2x/CMakeLists.txt index 62941f6..d2713f1 100644 --- a/src/spice2x/CMakeLists.txt +++ b/src/spice2x/CMakeLists.txt @@ -554,6 +554,7 @@ set(SOURCE_FILES ${SOURCE_FILES} overlay/windows/camera_control.cpp overlay/windows/card_manager.cpp overlay/windows/drs_dancefloor.cpp + overlay/windows/gfdm_sub.cpp overlay/windows/screen_resize.cpp overlay/windows/sdvx_sub.cpp overlay/windows/config.cpp diff --git a/src/spice2x/games/gitadora/gitadora.cpp b/src/spice2x/games/gitadora/gitadora.cpp index f580646..e0935c1 100644 --- a/src/spice2x/games/gitadora/gitadora.cpp +++ b/src/spice2x/games/gitadora/gitadora.cpp @@ -5,6 +5,7 @@ #include "cfg/configurator.h" #include "hooks/audio/mme.h" #include "hooks/graphics/graphics.h" +#include "misc/wintouchemu.h" #include "overlay/overlay.h" #include "util/cpuutils.h" #include "util/detour.h" @@ -19,8 +20,10 @@ namespace games::gitadora { // settings bool TWOCHANNEL = false; std::optional CAB_TYPE = std::nullopt; + bool ARENA_SINGLE_WINDOW = false; bool P1_LEFTY = false; bool P2_LEFTY = false; + std::optional SUBSCREEN_OVERLAY_SIZE; /* * Prevent GitaDora from creating folders on F drive @@ -285,6 +288,13 @@ namespace games::gitadora { // volume change prevention hooks::audio::mme::init(avs::game::DLL_INSTANCE); + + // touch hook + if (ARENA_SINGLE_WINDOW) { + wintouchemu::FORCE = true; + wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true; + wintouchemu::hook("GITADORA", avs::game::DLL_INSTANCE); + } return; } diff --git a/src/spice2x/games/gitadora/gitadora.h b/src/spice2x/games/gitadora/gitadora.h index 12e9ad2..a6ebf71 100644 --- a/src/spice2x/games/gitadora/gitadora.h +++ b/src/spice2x/games/gitadora/gitadora.h @@ -10,8 +10,10 @@ namespace games::gitadora { // settings extern bool TWOCHANNEL; extern std::optional CAB_TYPE; + extern bool ARENA_SINGLE_WINDOW; extern bool P1_LEFTY; extern bool P2_LEFTY; + extern std::optional SUBSCREEN_OVERLAY_SIZE; class GitaDoraGame : public games::Game { public: diff --git a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp index dee5c88..16b1658 100644 --- a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp +++ b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp @@ -13,6 +13,7 @@ #include "avs/game.h" #include "cfg/screen_resize.h" +#include "games/gitadora/gitadora.h" #include "games/iidx/iidx.h" #include "games/sdvx/sdvx.h" #include "games/mfc/mfc.h" @@ -1170,9 +1171,15 @@ IDirect3DSurface9 *graphics_d3d9_ldj_get_sub_screen() { } static void graphics_d3d9_ldj_on_present(IDirect3DDevice9 *wrapped_device) { + // iidx/sdvx + int swapchain = 1; + if (games::gitadora::is_arena_model()) { + swapchain = 3; + } + if (!ATTEMPTED_SUB_SWAP_CHAIN_ACQUIRE && SUB_SWAP_CHAIN == nullptr) { ATTEMPTED_SUB_SWAP_CHAIN_ACQUIRE = true; - HRESULT hr = wrapped_device->GetSwapChain(1, &SUB_SWAP_CHAIN); + HRESULT hr = wrapped_device->GetSwapChain(swapchain, &SUB_SWAP_CHAIN); if (FAILED(hr)) { log_warning( "graphics::d3d9", @@ -1383,7 +1390,9 @@ void graphics_d3d9_on_present( // for IIDX TDJ / SDVX UFC, handle subscreen const bool is_vm = games::sdvx::is_valkyrie_model(); const bool is_tdj = avs::game::is_model("LDJ") && games::iidx::TDJ_MODE; - if (is_vm || is_tdj) { + const bool is_gfdm_arena = + games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW; + if (is_vm || is_tdj || is_gfdm_arena) { graphics_d3d9_ldj_on_present(wrapped_device); } diff --git a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp index e6db0d2..ad3550d 100644 --- a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp +++ b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp @@ -4,6 +4,7 @@ #include #include "avs/game.h" +#include "games/gitadora/gitadora.h" #include "hooks/graphics/graphics.h" #include "overlay/overlay.h" #include "util/flags_helper.h" @@ -249,8 +250,16 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateAdditionalSwapChain( WRAP_VERBOSE; HRESULT hr = pReal->CreateAdditionalSwapChain(pPresentationParameters, ppSwapChain); - + bool create_swap_chain = false; if (avs::game::is_model({"LDJ", "KFC"})) { + create_swap_chain = true; + } else if (games::gitadora::is_arena_model() && + games::gitadora::ARENA_SINGLE_WINDOW && + pPresentationParameters->BackBufferWidth == 800) { + create_swap_chain = true; + } + + if (create_swap_chain) { if (SUCCEEDED(hr) && !sub_swapchain) { sub_swapchain = new WrappedIDirect3DSwapChain9(this, *ppSwapChain); sub_swapchain->should_run_hooks = false; @@ -295,7 +304,17 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetSwapChain( return D3D_OK; } + bool swap = false; if (iSwapChain == 1 && avs::game::is_model({"LDJ", "KFC"})) { + swap = true; + } + if (games::gitadora::is_arena_model() && + games::gitadora::ARENA_SINGLE_WINDOW && + iSwapChain == 3) { + swap = true; + } + + if (swap) { if (sub_swapchain) { sub_swapchain->AddRef(); *ppSwapChain = static_cast(sub_swapchain); diff --git a/src/spice2x/hooks/graphics/graphics.cpp b/src/spice2x/hooks/graphics/graphics.cpp index 136eb1b..33f0c61 100644 --- a/src/spice2x/hooks/graphics/graphics.cpp +++ b/src/spice2x/hooks/graphics/graphics.cpp @@ -98,6 +98,7 @@ static decltype(SetCursor) *SetCursor_orig = nullptr; static decltype(SetWindowLongA) *SetWindowLongA_orig = nullptr; static decltype(SetWindowLongW) *SetWindowLongW_orig = nullptr; static decltype(SetWindowPos) *SetWindowPos_orig = nullptr; +static decltype(ShowWindow) *ShowWindow_orig = nullptr; static void reset_window_hook(HWND hWnd) { overlay::destroy(hWnd); @@ -377,7 +378,7 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC bool is_sdvx_sub_window = avs::game::is_model("KFC") && window_name.ends_with(" Sub Screen"); bool is_gfdm_sub_window = games::gitadora::is_arena_model() && window_name.ends_with("SMALL"); - // TDJ windowed mode with subscreen: hide maximize button + // hide maximize button (prevent misaligned touches) if ((is_tdj_sub_window && GRAPHICS_IIDX_WSUB) || is_sdvx_sub_window || is_gfdm_sub_window) { dwStyle &= ~(WS_MAXIMIZEBOX); } @@ -385,7 +386,7 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC if (is_gfdm_sub_window) { dwStyle &= ~(WS_SIZEBOX); } - if ((is_tdj_sub_window || is_sdvx_sub_window || is_gfdm_sub_window) && GRAPHICS_WSUB_BORDERLESS) { + if ((is_tdj_sub_window || is_sdvx_sub_window) && GRAPHICS_WSUB_BORDERLESS) { dwStyle &= ~(WS_OVERLAPPEDWINDOW); } @@ -433,7 +434,8 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC graphics_hook_subscreen_window(SDVX_SUBSCREEN_WINDOW); } - if (is_gfdm_sub_window) { + // only hook touch window if multiple windows are allowed + if (is_gfdm_sub_window && !games::gitadora::ARENA_SINGLE_WINDOW) { GFDM_SUBSCREEN_WINDOW = result; graphics_hook_subscreen_window(GFDM_SUBSCREEN_WINDOW); } @@ -682,6 +684,18 @@ static BOOL WINAPI SetWindowPos_hook(HWND hWnd, HWND hWndInsertAfter, return SetWindowPos_orig(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); } +static BOOL WINAPI ShowWindow_hook(HWND hWnd, int nCmdShow) { + if (games::gitadora::is_arena_model() && + games::gitadora::ARENA_SINGLE_WINDOW && + hWnd != GRAPHICS_WINDOW_MAIN) { + log_info("graphics", "ShowWindow_hook - hiding sub window 0x{}", fmt::ptr(hWnd)); + return true; + } + + // call original + return ShowWindow_orig(hWnd, nCmdShow); +} + static ATOM WINAPI RegisterClassA_hook(const WNDCLASSA *lpWndClass) { // check for null @@ -824,6 +838,7 @@ void graphics_init() { SetWindowLongA_orig = detour::iat_try("SetWindowLongA", SetWindowLongA_hook); SetWindowLongW_orig = detour::iat_try("SetWindowLongW", SetWindowLongW_hook); SetWindowPos_orig = detour::iat_try("SetWindowPos", SetWindowPos_hook); + ShowWindow_orig = detour::iat_try("ShowWindow", ShowWindow_hook); detour::iat_try("MessageBoxA", MessageBoxA_hook); detour::iat_try("MessageBoxExA", MessageBoxExA_hook); diff --git a/src/spice2x/launcher/launcher.cpp b/src/spice2x/launcher/launcher.cpp index fea9b24..3598773 100644 --- a/src/spice2x/launcher/launcher.cpp +++ b/src/spice2x/launcher/launcher.cpp @@ -589,9 +589,15 @@ int main_implementation(int argc, char *argv[]) { if (options[launcher::Options::GitaDoraCabinetType].is_active()) { games::gitadora::CAB_TYPE = options[launcher::Options::GitaDoraCabinetType].value_uint32(); } + if (options[launcher::Options::GitaDoraArenaSingleWindow].value_bool() && GRAPHICS_WINDOWED) { + games::gitadora::ARENA_SINGLE_WINDOW = true; + } if (options[launcher::Options::GitaDoraWailHold].is_active()) { socd::TILT_HOLD_MS = options[launcher::Options::GitaDoraWailHold].value_uint32(); } + if (options[launcher::Options::GitaDoraSubOverlaySize].is_active()) { + games::gitadora::SUBSCREEN_OVERLAY_SIZE = options[launcher::Options::GitaDoraSubOverlaySize].value_text(); + } if (options[launcher::Options::LoadNostalgiaModule].value_bool()) { attach_nostalgia = true; } diff --git a/src/spice2x/launcher/options.cpp b/src/spice2x/launcher/options.cpp index 81c7dff..813e1b4 100644 --- a/src/spice2x/launcher/options.cpp +++ b/src/spice2x/launcher/options.cpp @@ -978,6 +978,15 @@ static const std::vector OPTION_DEFINITIONS = { .category = "Game Options", .elements = {{"1", "DX"}, {"2", "SD"}, {"3", "SD2 - white cab"}}, }, + { + // GitaDoraArenaSingleWindow + .title = "GitaDora Arena Single Window (EXPERIMENTAL)", + .name = "gdaonewindow", + .desc = "For Arena Model, when combined with windowed mode, only show the main game window; enables subscreen overlay", + .type = OptionType::Bool, + .game_name = "GitaDora", + .category = "Game Options", + }, { // GitaDoraLefty .title = "GitaDora Lefty Guitar (for Digital Wailing)", @@ -1011,6 +1020,20 @@ static const std::vector OPTION_DEFINITIONS = { .game_name = "GitaDora", .category = "Game Options", }, + { + // GitaDoraSubOverlaySize + .title = "GitaDora Subscreen Overlay Size", + .name = "gdsubsize", + .desc = "Default size of the subscreen overlay. Default: medium", + .type = OptionType::Enum, + .game_name = "GitaDora", + .category = "Overlay", + .elements = { + {"small", ""}, + {"medium", ""}, + {"large", ""} + }, + }, { .title = "Force Load Jubeat Module", .name = "jb", diff --git a/src/spice2x/launcher/options.h b/src/spice2x/launcher/options.h index dceb464..f0c59c8 100644 --- a/src/spice2x/launcher/options.h +++ b/src/spice2x/launcher/options.h @@ -100,8 +100,10 @@ namespace launcher { LoadGitaDoraModule, GitaDoraTwoChannelAudio, GitaDoraCabinetType, + GitaDoraArenaSingleWindow, GitaDoraLefty, GitaDoraWailHold, + GitaDoraSubOverlaySize, LoadJubeatModule, LoadReflecBeatModule, LoadShogikaiModule, diff --git a/src/spice2x/misc/wintouchemu.cpp b/src/spice2x/misc/wintouchemu.cpp index 1311034..0896ab5 100644 --- a/src/spice2x/misc/wintouchemu.cpp +++ b/src/spice2x/misc/wintouchemu.cpp @@ -10,6 +10,7 @@ #include #include "cfg/screen_resize.h" +#include "games/gitadora/gitadora.h" #include "games/iidx/iidx.h" #include "hooks/graphics/graphics.h" #include "overlay/overlay.h" @@ -390,7 +391,10 @@ namespace wintouchemu { } else if (avs::game::is_model("LDJ") && !GENERIC_SUB_WINDOW_FULLSIZE) { // overlay subscreen in IIDX // use mouse position as ImGui overlay will block the touch window - log_info("wintouchemu", "use mouse cursor API for overlay subscreen"); + log_info("wintouchemu", "use mouse cursor API for ldj overlay subscreen"); + USE_MOUSE = true; + } else if (games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW) { + log_info("wintouchemu", "use mouse cursor API for gitadora overlay subscreen"); USE_MOUSE = true; } else { // create touch window - create overlay if not yet existing at this point diff --git a/src/spice2x/overlay/overlay.cpp b/src/spice2x/overlay/overlay.cpp index 4589225..cb7bacf 100644 --- a/src/spice2x/overlay/overlay.cpp +++ b/src/spice2x/overlay/overlay.cpp @@ -29,6 +29,7 @@ #include "windows/generic_sub.h" #include "windows/iidx_seg.h" #include "windows/iidx_sub.h" +#include "windows/gfdm_sub.h" #include "windows/drs_dancefloor.h" #include "windows/iopanel.h" #include "windows/iopanel_ddr.h" @@ -410,7 +411,10 @@ void overlay::SpiceOverlay::init() { window_sub = new overlay::windows::DRSDanceFloorDisplay(this); } else if (avs::game::is_model("KFC")) { window_sub = new overlay::windows::SDVXSubScreen(this); + } else if (games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW) { + window_sub = new overlay::windows::GitaDoraSubScreen(this); } + if (window_sub) { this->window_add(window_sub); if (AUTO_SHOW_SUBSCREEN) { diff --git a/src/spice2x/overlay/windows/exitprompt.cpp b/src/spice2x/overlay/windows/exitprompt.cpp index 5544f7a..38d6a16 100644 --- a/src/spice2x/overlay/windows/exitprompt.cpp +++ b/src/spice2x/overlay/windows/exitprompt.cpp @@ -3,6 +3,7 @@ #include "misc/eamuse.h" #include "util/logging.h" #include "games/iidx/iidx.h" +#include "games/gitadora/gitadora.h" namespace overlay::windows { @@ -69,6 +70,8 @@ namespace overlay::windows { sub = "Show Valkyrie Subscreen"; } else if (avs::game::is_model("REC")) { sub = "Show DRS Dance Floor"; + } else if (games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW) { + sub = "Show GITADORA Subscreen"; } build_button(this->overlay->window_sub, sub, size, NextItem::NEW_LINE, false); diff --git a/src/spice2x/overlay/windows/gfdm_sub.cpp b/src/spice2x/overlay/windows/gfdm_sub.cpp new file mode 100644 index 0000000..5ce5ad6 --- /dev/null +++ b/src/spice2x/overlay/windows/gfdm_sub.cpp @@ -0,0 +1,54 @@ +#include "avs/game.h" +#include "gfdm_sub.h" +#include "games/gitadora/gitadora.h" +#include "hooks/graphics/graphics.h" +#include "touch/touch.h" + +namespace overlay::windows { + + GitaDoraSubScreen::GitaDoraSubScreen(SpiceOverlay *overlay) : GenericSubScreen(overlay) { + this->title = "GITADORA Subscreen"; + + if (!games::gitadora::is_arena_model()) { + this->disabled_message = "Requires Arena Model!"; + } + + this->resize_callback = keep_10_by_16; + + // medium size - perfect for "layout B" in GW Delta, though in drums it covers the pacemaker + float size = 0.64f; + if (games::gitadora::SUBSCREEN_OVERLAY_SIZE.has_value()) { + if (games::gitadora::SUBSCREEN_OVERLAY_SIZE.value() == "large") { + // enough to not cover the FPS overlay + size = 0.88f; + } else if (games::gitadora::SUBSCREEN_OVERLAY_SIZE.value() == "small") { + size = 0.48f; + } + } + + const float height = ImGui::GetIO().DisplaySize.y * size; + const float width = height * 10 / 16; + this->init_size = ImVec2(width, height + ImGui::GetFrameHeight()); + + // bottom right + this->init_pos = ImVec2( + ImGui::GetIO().DisplaySize.x - ImGui::GetFrameHeight() / 4 - this->init_size.x, + ImGui::GetIO().DisplaySize.y - this->init_size.y - (ImGui::GetFrameHeight() / 4)); + } + + void GitaDoraSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) { + if (!this->get_active()) { + return; + } + + if (GRAPHICS_WINDOWED) { + // Touch needs to be registered on global coords + *x_out = SPICETOUCH_TOUCH_X + xy_in.x * SPICETOUCH_TOUCH_WIDTH; + *y_out = SPICETOUCH_TOUCH_Y + xy_in.y * SPICETOUCH_TOUCH_HEIGHT; + } else { + // Fullscreen mode, scale to game coords + *x_out = xy_in.x * ImGui::GetIO().DisplaySize.x; + *y_out = xy_in.y * ImGui::GetIO().DisplaySize.y; + } + } +} diff --git a/src/spice2x/overlay/windows/gfdm_sub.h b/src/spice2x/overlay/windows/gfdm_sub.h new file mode 100644 index 0000000..7a1dca7 --- /dev/null +++ b/src/spice2x/overlay/windows/gfdm_sub.h @@ -0,0 +1,20 @@ +#pragma once + +#include "overlay/window.h" +#include "overlay/windows/generic_sub.h" + +namespace overlay::windows { + + class GitaDoraSubScreen : public GenericSubScreen { + public: + GitaDoraSubScreen(SpiceOverlay *overlay); + + protected: + void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) override; + + private: + static void keep_10_by_16(ImGuiSizeCallbackData* data) { + data->DesiredSize.y = (data->DesiredSize.x * 16.f / 10.f) + ImGui::GetFrameHeight(); + } + }; +}