Update to spice2x-25-04-25 (pre-apply)

> broken commit
This commit is contained in:
[ ]
2025-05-07 00:25:31 +09:00
parent 04dee88276
commit c94de456b5
543 changed files with 78491 additions and 91698 deletions
+58 -7
View File
@@ -6,18 +6,69 @@
namespace ImGui {
const auto fg = ImVec4(0.910f, 0.914f, 0.922f, 1.0f);
const auto bg = ImVec4(0.192f, 0.212f, 0.220f, 1.0f);
void HelpTooltip(const char* desc) {
ImGui::PushStyleColor(ImGuiCol_Border, bg);
ImGui::PushStyleColor(ImGuiCol_BorderShadow, bg);
ImGui::PushStyleColor(ImGuiCol_PopupBg, bg);
ImGui::PushStyleColor(ImGuiCol_Text, fg);
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextUnformatted(desc);
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
ImGui::PopStyleColor(4);
}
void HelpMarker(const char* desc) {
ImGui::TextDisabled("(?)");
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextUnformatted(desc);
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
if (ImGui::IsItemHovered()) {
HelpTooltip(desc);
}
}
void WarnTooltip(const char* desc, const char* warn) {
ImGui::PushStyleColor(ImGuiCol_Border, bg);
ImGui::PushStyleColor(ImGuiCol_BorderShadow, bg);
ImGui::PushStyleColor(ImGuiCol_PopupBg, bg);
ImGui::PushStyleColor(ImGuiCol_Text, fg);
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
if (desc && desc[0]) {
ImGui::TextUnformatted(desc);
ImGui::TextUnformatted("");
}
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 0.f, 1.f));
if (warn && warn[0]) {
ImGui::TextUnformatted("WARNING:");
ImGui::TextUnformatted(warn);
}
ImGui::PopStyleColor();
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
ImGui::PopStyleColor(4);
}
void WarnMarker(const char* desc, const char* warn) {
ImGui::PushStyleColor(ImGuiCol_TextDisabled, ImVec4(1.f, 1.f, 0.f, 1.f));
ImGui::TextDisabled("(!)");
ImGui::PopStyleColor();
if (ImGui::IsItemHovered()) {
WarnTooltip(desc, warn);
}
}
void DummyMarker() {
// dummy marker that is the same width as HelpMarker/WarnMarker.
ImGui::Dummy(ImVec2(22, 0));
}
void Knob(float fraction, float size, float thickness, float pos_x, float pos_y) {
// get values
+4
View File
@@ -2,7 +2,11 @@
namespace ImGui {
void HelpTooltip(const char* desc);
void HelpMarker(const char* desc);
void WarnTooltip(const char* desc, const char* warn);
void WarnMarker(const char* desc, const char* warn);
void DummyMarker();
void Knob(float fraction, float size, float thickness = 2.f,
float pos_x = -1.f, float pos_y = -1.f);
}
-360
View File
@@ -1,360 +0,0 @@
// dear imgui: Renderer for DirectX9
// This needs to be used along with a Platform Binding (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2019-05-29: DirectX9: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: DirectX9: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-03-29: Misc: Fixed erroneous assert in ImGui_ImplDX9_InvalidateDeviceObjects().
// 2019-01-16: Misc: Disabled fog before drawing UI's. Fixes issue #2288.
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-06-08: Misc: Extracted imgui_impl_dx9.cpp/.h away from the old combined DX9+Win32 example.
// 2018-06-08: DirectX9: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-05-07: Render: Saving/restoring Transform because they don't seem to be included in the StateBlock. Setting shading mode to Gouraud.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX9_RenderDrawData() in the .h file so you can call it yourself.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
#include "impl_dx9.h"
#include <algorithm>
// DirectX
#include <d3d9.h>
#include "external/imgui/imgui.h"
// allow std::min use
#ifdef min
#undef min
#endif
// DirectX data
static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
static LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
static LPDIRECT3DINDEXBUFFER9 g_pIB = NULL;
static LPDIRECT3DTEXTURE9 g_FontTexture = NULL;
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000;
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)
// Render function.
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
void ImGui_ImplDX9_RenderDrawData(ImDrawData *draw_data) {
// Avoid rendering when minimized
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return;
// Create and grow buffers if needed
if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount) {
if (g_pVB) {
g_pVB->Release();
g_pVB = NULL;
}
g_VertexBufferSize = draw_data->TotalVtxCount + 5000;
if (g_pd3dDevice->CreateVertexBuffer(g_VertexBufferSize * sizeof(ImDrawVert),
D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX,
D3DPOOL_DEFAULT, &g_pVB, NULL) < 0)
return;
}
if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount) {
if (g_pIB) {
g_pIB->Release();
g_pIB = NULL;
}
g_IndexBufferSize = draw_data->TotalIdxCount + 10000;
if (g_pd3dDevice->CreateIndexBuffer(g_IndexBufferSize * sizeof(ImDrawIdx),
D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY,
sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32,
D3DPOOL_DEFAULT, &g_pIB, NULL) < 0)
return;
}
// Backup the DX9 state
IDirect3DStateBlock9 *d3d9_state_block = NULL;
if (g_pd3dDevice->CreateStateBlock(D3DSBT_ALL, &d3d9_state_block) < 0)
return;
// Backup the DX9 transform (DX9 documentation suggests that it is included in the StateBlock but it doesn't appear to)
D3DMATRIX last_world, last_view, last_projection;
g_pd3dDevice->GetTransform(D3DTS_WORLD, &last_world);
g_pd3dDevice->GetTransform(D3DTS_VIEW, &last_view);
g_pd3dDevice->GetTransform(D3DTS_PROJECTION, &last_projection);
// Copy all vertices into a single contiguous buffer
ImDrawVert *vtx_dst;
ImDrawIdx *idx_dst;
if (g_pVB->Lock(0, (UINT) (draw_data->TotalVtxCount * sizeof(ImDrawVert)), (void **) &vtx_dst,
D3DLOCK_DISCARD) < 0)
return;
if (g_pIB->Lock(0, (UINT) (draw_data->TotalIdxCount * sizeof(ImDrawIdx)), (void **) &idx_dst,
D3DLOCK_DISCARD) < 0)
return;
for (int n = 0; n < draw_data->CmdListsCount; n++) {
const ImDrawList *cmd_list = draw_data->CmdLists[n];
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
vtx_dst += cmd_list->VtxBuffer.Size;
idx_dst += cmd_list->IdxBuffer.Size;
}
g_pVB->Unlock();
g_pIB->Unlock();
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(ImDrawVert));
g_pd3dDevice->SetIndices(g_pIB);
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
// Setup viewport
D3DVIEWPORT9 vp;
vp.X = vp.Y = 0;
vp.Width = (DWORD) draw_data->DisplaySize.x;
vp.Height = (DWORD) draw_data->DisplaySize.y;
vp.MinZ = 0.0f;
vp.MaxZ = 1.0f;
g_pd3dDevice->SetViewport(&vp);
g_pd3dDevice->SetPixelShader(nullptr);
g_pd3dDevice->SetVertexShader(nullptr);
D3DCAPS9 caps {};
if (FAILED(g_pd3dDevice->GetDeviceCaps(&caps))) {
caps.NumSimultaneousRTs = 0UL;
}
IDirect3DSurface9 *back_buffer = nullptr;
IDirect3DSurface9 *depth_stencil = nullptr;
IDirect3DSurface9 *render_targets[8];
// save all previous render target state
for (size_t target = 0; target < std::min(8UL, caps.NumSimultaneousRTs); target++) {
if (FAILED(g_pd3dDevice->GetRenderTarget(target, &render_targets[target]))) {
render_targets[target] = nullptr;
}
}
// get the previous depth stencil
if (FAILED(g_pd3dDevice->GetDepthStencilSurface(&depth_stencil))) {
depth_stencil = nullptr;
}
// set the back buffer as the current render target
if (SUCCEEDED(g_pd3dDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &back_buffer))) {
g_pd3dDevice->SetRenderTarget(0, back_buffer);
g_pd3dDevice->SetDepthStencilSurface(nullptr);
for (size_t target = 1; target < std::min(8UL, caps.NumSimultaneousRTs); target++) {
g_pd3dDevice->SetRenderTarget(target, nullptr);
}
} else {
back_buffer = nullptr;
}
// Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient)
g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, false);
g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, false);
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
g_pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, false);
g_pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
g_pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, true);
g_pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
g_pd3dDevice->SetRenderState(D3DRS_FOGENABLE, false);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
// Setup orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
// Being agnostic of whether <d3dx9.h> or <DirectXMath.h> can be used, we aren't relying on D3DXMatrixIdentity()/D3DXMatrixOrthoOffCenterLH() or DirectX::XMMatrixIdentity()/DirectX::XMMatrixOrthographicOffCenterLH()
{
float L = draw_data->DisplayPos.x + 0.5f;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x + 0.5f;
float T = draw_data->DisplayPos.y + 0.5f;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y + 0.5f;
D3DMATRIX mat_identity = {{{1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}}};
D3DMATRIX mat_projection =
{{{
2.0f / (R - L), 0.0f, 0.0f, 0.0f,
0.0f, 2.0f / (T - B), 0.0f, 0.0f,
0.0f, 0.0f, 0.5f, 0.0f,
(L + R) / (L - R), (T + B) / (B - T), 0.5f, 1.0f
}}};
g_pd3dDevice->SetTransform(D3DTS_WORLD, &mat_identity);
g_pd3dDevice->SetTransform(D3DTS_VIEW, &mat_identity);
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_projection);
}
// Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them)
int global_vtx_offset = 0;
int global_idx_offset = 0;
ImVec2 clip_off = draw_data->DisplayPos;
for (int n = 0; n < draw_data->CmdListsCount; n++) {
const ImDrawList *cmd_list = draw_data->CmdLists[n];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) {
const ImDrawCmd *pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL) {
pcmd->UserCallback(cmd_list, pcmd);
} else {
const RECT r = {(LONG) (pcmd->ClipRect.x - clip_off.x), (LONG) (pcmd->ClipRect.y - clip_off.y),
(LONG) (pcmd->ClipRect.z - clip_off.x), (LONG) (pcmd->ClipRect.w - clip_off.y)};
auto texture = reinterpret_cast<IDirect3DBaseTexture9 *>(pcmd->TextureId);
g_pd3dDevice->SetTexture(0, texture);
g_pd3dDevice->SetScissorRect(&r);
g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,
pcmd->VtxOffset + global_vtx_offset, 0,
(UINT) cmd_list->VtxBuffer.Size,
pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount / 3);
}
}
global_idx_offset += cmd_list->IdxBuffer.Size;
global_vtx_offset += cmd_list->VtxBuffer.Size;
}
if (back_buffer) {
back_buffer->Release();
back_buffer = nullptr;
}
// restore previous depth stencil
if (depth_stencil) {
g_pd3dDevice->SetDepthStencilSurface(depth_stencil);
depth_stencil->Release();
depth_stencil = nullptr;
}
// restore all render target state
for (size_t target = 0; target < std::min(8UL, caps.NumSimultaneousRTs); target++) {
auto render_target = render_targets[target];
if (render_target) {
g_pd3dDevice->SetRenderTarget(target, render_target);
render_target->Release();
}
}
// restore the DX9 transform
g_pd3dDevice->SetTransform(D3DTS_WORLD, &last_world);
g_pd3dDevice->SetTransform(D3DTS_VIEW, &last_view);
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &last_projection);
// restore the DX9 state
d3d9_state_block->Apply();
d3d9_state_block->Release();
}
bool ImGui_ImplDX9_Init(IDirect3DDevice9 *device) {
// Setup back-end capabilities flags
auto &io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_dx9";
// We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;
g_pd3dDevice = device;
g_pd3dDevice->AddRef();
return true;
}
void ImGui_ImplDX9_Shutdown() {
ImGui_ImplDX9_InvalidateDeviceObjects();
if (g_pd3dDevice) {
g_pd3dDevice->Release();
g_pd3dDevice = NULL;
}
}
static bool ImGui_ImplDX9_CreateFontsTexture() {
// Build texture atlas
ImGuiIO &io = ImGui::GetIO();
unsigned char *pixels;
int width, height, bytes_per_pixel;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &bytes_per_pixel);
// Upload texture to graphics system
g_FontTexture = NULL;
if (g_pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8,
D3DPOOL_DEFAULT, &g_FontTexture, NULL) < 0)
return false;
D3DLOCKED_RECT tex_locked_rect;
if (g_FontTexture->LockRect(0, &tex_locked_rect, NULL, 0) != D3D_OK)
return false;
for (int y = 0; y < height; y++)
memcpy((unsigned char *) tex_locked_rect.pBits + tex_locked_rect.Pitch * y,
pixels + (width * bytes_per_pixel) * y, (width * bytes_per_pixel));
g_FontTexture->UnlockRect(0);
// Store our identifier
io.Fonts->TexID = (ImTextureID) g_FontTexture;
return true;
}
bool ImGui_ImplDX9_CreateDeviceObjects() {
if (!g_pd3dDevice) {
return false;
}
return ImGui_ImplDX9_CreateFontsTexture();
}
void ImGui_ImplDX9_InvalidateDeviceObjects() {
if (!g_pd3dDevice)
return;
if (g_pVB) {
g_pVB->Release();
g_pVB = NULL;
}
if (g_pIB) {
g_pIB->Release();
g_pIB = NULL;
}
if (g_FontTexture) {
g_FontTexture->Release();
g_FontTexture = NULL;
ImGui::GetIO().Fonts->TexID = NULL;
} // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
}
void ImGui_ImplDX9_NewFrame() {
if (!g_FontTexture) {
ImGui_ImplDX9_CreateDeviceObjects();
}
IDirect3DSwapChain9 *swap_chain = nullptr;
if (SUCCEEDED(g_pd3dDevice->GetSwapChain(0, &swap_chain))) {
auto &io = ImGui::GetIO();
D3DPRESENT_PARAMETERS present_params {};
if (SUCCEEDED(swap_chain->GetPresentParameters(&present_params))) {
if (present_params.BackBufferWidth != 0 && present_params.BackBufferHeight != 0) {
io.DisplaySize.x = static_cast<float>(present_params.BackBufferWidth);
io.DisplaySize.y = static_cast<float>(present_params.BackBufferHeight);
} else {
RECT rect {};
GetClientRect(present_params.hDeviceWindow, &rect);
io.DisplaySize.x = static_cast<float>(rect.right - rect.left);
io.DisplaySize.y = static_cast<float>(rect.bottom - rect.top);
}
}
swap_chain->Release();
}
}
-25
View File
@@ -1,25 +0,0 @@
// dear imgui: Renderer for DirectX9
// This needs to be used along with a Platform Binding (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
#pragma once
#include "external/imgui/imgui.h"
struct IDirect3DDevice9;
IMGUI_IMPL_API bool ImGui_ImplDX9_Init(IDirect3DDevice9 *device);
IMGUI_IMPL_API void ImGui_ImplDX9_Shutdown();
IMGUI_IMPL_API void ImGui_ImplDX9_NewFrame();
IMGUI_IMPL_API void ImGui_ImplDX9_RenderDrawData(ImDrawData *draw_data);
// Use if you want to reset your rendering device without losing ImGui state.
IMGUI_IMPL_API bool ImGui_ImplDX9_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplDX9_InvalidateDeviceObjects();
+52 -34
View File
@@ -2,6 +2,7 @@
#include <windows.h>
#include "cfg/configurator.h"
#include "games/io.h"
#include "launcher/launcher.h"
#include "launcher/superexit.h"
@@ -10,6 +11,17 @@
#include "rawinput/rawinput.h"
#include "touch/touch.h"
#include "util/logging.h"
#include "util/utils.h"
#if !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) || \
!defined(IMGUI_DISABLE_DEFAULT_ALLOCATORS) || \
!defined(IMGUI_USE_BGRA_PACKED_COLOR) || \
!defined(IMGUI_HAS_VIEWPORT) || \
!defined(IMGUI_HAS_DOCK) || \
!defined(IMGUI_DISABLE_DEMO_WINDOWS) || \
defined(IMGUI_DISABLE_DEBUG_TOOLS)
#error "fix imconfig.h after updating imgui version"
#endif
// state
static HWND g_hWnd = nullptr;
@@ -41,6 +53,7 @@ bool ImGui_ImplSpice_Init(HWND hWnd) {
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos;
io.BackendPlatformName = "imgui_impl_spice";
io.ConfigErrorRecoveryEnableTooltip = true;
// keyboard mapping
io.KeyMap[ImGuiKey_Tab] = VK_TAB;
@@ -58,7 +71,7 @@ bool ImGui_ImplSpice_Init(HWND hWnd) {
io.KeyMap[ImGuiKey_Space] = VK_SPACE;
io.KeyMap[ImGuiKey_Enter] = VK_RETURN;
io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE;
io.KeyMap[ImGuiKey_KeyPadEnter] = VK_RETURN;
io.KeyMap[ImGuiKey_KeypadEnter] = VK_RETURN;
io.KeyMap[ImGuiKey_A] = 'A';
io.KeyMap[ImGuiKey_C] = 'C';
io.KeyMap[ImGuiKey_V] = 'V';
@@ -269,8 +282,8 @@ void ImGui_ImplSpice_NewFrame() {
io.KeySuper |= (::GetKeyState(VK_RWIN) & 0x8000) != 0;
// apply windows mouse buttons
io.MouseDown[0] |= (GetAsyncKeyState(VK_LBUTTON)) != 0;
io.MouseDown[1] |= (GetAsyncKeyState(VK_RBUTTON)) != 0;
io.MouseDown[0] |= (get_async_primary_mouse()) != 0;
io.MouseDown[1] |= (get_async_secondary_mouse()) != 0;
io.MouseDown[2] |= (GetAsyncKeyState(VK_MBUTTON)) != 0;
// read new keys state
@@ -284,11 +297,20 @@ void ImGui_ImplSpice_NewFrame() {
auto &mouse = device.mouseInfo;
// mouse button triggers
if (mouse->key_states[rawinput::MOUSEBTN_LEFT]) {
io.MouseDown[0] = true;
}
if (mouse->key_states[rawinput::MOUSEBTN_RIGHT]) {
io.MouseDown[1] = true;
if (GetSystemMetrics(SM_SWAPBUTTON)) {
if (mouse->key_states[rawinput::MOUSEBTN_RIGHT]) {
io.MouseDown[0] = true;
}
if (mouse->key_states[rawinput::MOUSEBTN_LEFT]) {
io.MouseDown[1] = true;
}
} else {
if (mouse->key_states[rawinput::MOUSEBTN_LEFT]) {
io.MouseDown[0] = true;
}
if (mouse->key_states[rawinput::MOUSEBTN_RIGHT]) {
io.MouseDown[1] = true;
}
}
if (mouse->key_states[rawinput::MOUSEBTN_MIDDLE]) {
io.MouseDown[2] = true;
@@ -339,27 +361,6 @@ void ImGui_ImplSpice_NewFrame() {
}
}
// navigator input
auto buttons = games::get_buttons_overlay(eamuse_get_game());
if (buttons && (!overlay::OVERLAY || overlay::OVERLAY->hotkeys_triggered())) {
struct {
size_t index;
Button &btn;
} NAV_MAPPING[] = {
{ ImGuiNavInput_Activate, buttons->at(games::OverlayButtons::NavigatorActivate )},
{ ImGuiNavInput_Cancel, buttons->at(games::OverlayButtons::NavigatorCancel) },
{ ImGuiNavInput_DpadUp, buttons->at(games::OverlayButtons::NavigatorUp) },
{ ImGuiNavInput_DpadDown, buttons->at(games::OverlayButtons::NavigatorDown) },
{ ImGuiNavInput_DpadLeft, buttons->at(games::OverlayButtons::NavigatorLeft) },
{ ImGuiNavInput_DpadRight, buttons->at(games::OverlayButtons::NavigatorRight) },
};
for (auto mapping : NAV_MAPPING) {
if (GameAPI::Buttons::getState(RI_MGR, mapping.btn)) {
io.NavInputs[mapping.index] = 1;
}
}
}
// set mouse wheel
auto mouse_diff = mouse_wheel - mouse_wheel_last;
mouse_wheel_last = mouse_wheel;
@@ -368,10 +369,27 @@ void ImGui_ImplSpice_NewFrame() {
// update OS mouse position
ImGui_ImplSpice_UpdateMousePos();
// update OS mouse cursor with the cursor requested by imgui
ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
if (g_LastMouseCursor != mouse_cursor) {
g_LastMouseCursor = mouse_cursor;
ImGui_ImplSpice_UpdateMouseCursor();
if (cfg::CONFIGURATOR_STANDALONE) {
// if cursor is inside the client area, always set the OS cursor to what ImGui wants
// this is to deal with cases where mouse cursor changes outside the client rect and comes
// back into the window
// i'm sure there might be better ways to deal with this but this works so whatever, right?
RECT client_rect;
if (GetClientRect(g_hWnd, &client_rect)) {
POINT cursor;
if (GetCursorPos(&cursor) && ScreenToClient(g_hWnd, &cursor)) {
if (client_rect.left < cursor.x && cursor.x < client_rect.right &&
client_rect.top < cursor.y && cursor.y < client_rect.bottom) {
ImGui_ImplSpice_UpdateMouseCursor();
}
}
}
} else {
// update OS mouse cursor with the cursor requested by imgui
ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
if (g_LastMouseCursor != mouse_cursor) {
g_LastMouseCursor = mouse_cursor;
ImGui_ImplSpice_UpdateMouseCursor();
}
}
}
+1 -1
View File
@@ -678,7 +678,7 @@ void bind_imgui_painting()
int font_width, font_height;
io.Fonts->GetTexDataAsAlpha8(&tex_data, &font_width, &font_height);
const auto texture = new Texture{tex_data, font_width, font_height};
io.Fonts->TexID = texture;
io.Fonts->TexID = reinterpret_cast<ImTextureID>(texture);
}
static Stats s_stats; // TODO: pass as an argument?
+125 -100
View File
@@ -7,18 +7,19 @@
#include "hooks/graphics/graphics.h"
#include "misc/eamuse.h"
#include "touch/touch.h"
#include "util/fileutils.h"
#include "util/logging.h"
#include "util/resutils.h"
#include "build/resource.h"
#include "imgui/impl_dx9.h"
#include "imgui/impl_spice.h"
#include "imgui/impl_sw.h"
#include "overlay/imgui/impl_dx9.h"
#include "external/imgui/backends/imgui_impl_dx9.h"
#include "overlay/imgui/impl_spice.h"
#include "overlay/imgui/impl_sw.h"
#include "window.h"
#ifdef SPICE64
#include "windows/camera_control.h"
#endif
#include "windows/card_manager.h"
#include "windows/screen_resize.h"
#include "windows/config.h"
@@ -27,16 +28,16 @@
#include "windows/generic_sub.h"
#include "windows/iidx_seg.h"
#include "windows/iidx_sub.h"
#include "windows/drs_dancefloor.h"
#include "windows/iopanel.h"
#include "windows/iopanel_ddr.h"
#include "windows/iopanel_gfdm.h"
#include "windows/iopanel_iidx.h"
#include "windows/sdvx_sub.h"
#include "windows/keypad.h"
#include "windows/kfcontrol.h"
#include "windows/log.h"
#include "windows/patch_manager.h"
#include "windows/vr.h"
#include "windows/exitprompt.cpp"
static inline ImVec4 operator*(const ImVec4& lhs, const ImVec4& rhs) \
{ return ImVec4(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w); }
@@ -53,10 +54,13 @@ namespace overlay {
bool USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT = false;
bool FPS_SHOULD_FLIP = false;
// global
std::mutex OVERLAY_MUTEX;
std::unique_ptr<overlay::SpiceOverlay> OVERLAY = nullptr;
ImFont* DSEG_FONT = nullptr;
bool SHOW_DEBUG_LOG_WINDOW = false;
}
static void *ImGui_Alloc(size_t sz, void *user_data) {
@@ -175,7 +179,7 @@ void overlay::SpiceOverlay::init() {
// colors[ImGuiCol_WindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.94f);
// colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
// colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.f, 0.f, 0.94f);
colors[ImGuiCol_Border] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f);
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
@@ -224,7 +228,7 @@ void overlay::SpiceOverlay::init() {
colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);
colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f);
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.04f);
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
@@ -237,13 +241,20 @@ void overlay::SpiceOverlay::init() {
io.UserData = this;
io.ConfigFlags = ImGuiConfigFlags_NavEnableKeyboard
| ImGuiConfigFlags_NavEnableGamepad
| ImGuiConfigFlags_NavEnableSetMousePos
| ImGuiConfigFlags_DockingEnable
| ImGuiConfigFlags_ViewportsEnable;
if (is_touch_available()) {
| ImGuiConfigFlags_NavEnableSetMousePos;
if (!cfg::CONFIGURATOR_STANDALONE) {
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
}
if (is_touch_available("SpiceOverlay::init")) {
io.ConfigFlags |= ImGuiConfigFlags_IsTouchScreen;
}
// temporarily turn this off as it can cause crashes during font load failures
// turns back on in ImGui_ImplSpice_Init
io.ConfigErrorRecoveryEnableTooltip = false;
io.MouseDrawCursor = !GRAPHICS_SHOW_CURSOR;
// disable config
@@ -258,20 +269,14 @@ void overlay::SpiceOverlay::init() {
// add fallback fonts for missing glyph ranges
ImFontConfig config {};
config.MergeMode = true;
io.Fonts->AddFontFromFileTTF(R"(C:\Windows\Fonts\simsun.ttc)",
13.0f, &config, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
io.Fonts->AddFontFromFileTTF(R"(C:\Windows\Fonts\arial.ttf)",
13.0f, &config, io.Fonts->GetGlyphRangesCyrillic());
io.Fonts->AddFontFromFileTTF(R"(C:\Windows\Fonts\meiryu.ttc)",
13.0f, &config, io.Fonts->GetGlyphRangesJapanese());
io.Fonts->AddFontFromFileTTF(R"(C:\Windows\Fonts\meiryo.ttc)",
13.0f, &config, io.Fonts->GetGlyphRangesJapanese());
io.Fonts->AddFontFromFileTTF(R"(C:\Windows\Fonts\gulim.ttc)",
13.0f, &config, io.Fonts->GetGlyphRangesKorean());
io.Fonts->AddFontFromFileTTF(R"(C:\Windows\Fonts\cordia.ttf)",
13.0f, &config, io.Fonts->GetGlyphRangesThai());
io.Fonts->AddFontFromFileTTF(R"(C:\Windows\Fonts\arial.ttf)",
13.0f, &config, io.Fonts->GetGlyphRangesVietnamese());
add_font("simsun.ttc", &config, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
add_font("arial.ttc", &config, io.Fonts->GetGlyphRangesCyrillic());
add_font("meiryu.ttc", &config, io.Fonts->GetGlyphRangesJapanese());
add_font("meiryo.ttc", &config, io.Fonts->GetGlyphRangesJapanese());
add_font("gulim.ttc", &config, io.Fonts->GetGlyphRangesKorean());
add_font("cordia.ttc", &config, io.Fonts->GetGlyphRangesThai());
add_font("arial.ttc", &config, io.Fonts->GetGlyphRangesVietnamese());
// add special font
if (avs::game::is_model("LDJ")) {
@@ -319,51 +324,56 @@ void overlay::SpiceOverlay::init() {
set_overlay_active = true;
}
this->window_add(window_main_menu = new overlay::windows::ExitPrompt(this));
// add default windows
this->window_add(new overlay::windows::Config(this));
this->window_add(new overlay::windows::Control(this));
this->window_add(new overlay::windows::Log(this));
this->window_add(new overlay::windows::CardManager(this));
this->window_add(window_config = new overlay::windows::Config(this));
this->window_add(window_control = new overlay::windows::Control(this));
this->window_add(window_log = new overlay::windows::Log(this));
#ifdef SPICE64
if (avs::game::is_model("LDJ")) {
this->window_add(window_camera = new overlay::windows::CameraControl(this));
}
#endif
this->window_add(window_cards = new overlay::windows::CardManager(this));
if (!cfg::CONFIGURATOR_STANDALONE) {
this->window_add(new overlay::windows::ScreenResize(this));
this->window_add(window_resize = new overlay::windows::ScreenResize(this));
}
this->window_add(new overlay::windows::PatchManager(this));
this->window_add(new overlay::windows::KFControl(this));
this->window_add(new overlay::windows::VRWindow(this));
{
const auto keypad_p1 = new overlay::windows::Keypad(this, 0);
this->window_add(keypad_p1);
window_keypad1 = new overlay::windows::Keypad(this, 0);
this->window_add(window_keypad1);
if (!cfg::CONFIGURATOR_STANDALONE && AUTO_SHOW_KEYPAD_P1) {
keypad_p1->set_active(true);
window_keypad1->set_active(true);
set_overlay_active = true;
}
}
if (eamuse_get_game_keypads() > 1) {
const auto keypad_p2 = new overlay::windows::Keypad(this, 1);
this->window_add(keypad_p2);
window_keypad2 = new overlay::windows::Keypad(this, 1);
this->window_add(window_keypad2);
if (!cfg::CONFIGURATOR_STANDALONE && AUTO_SHOW_KEYPAD_P2) {
keypad_p2->set_active(true);
window_keypad2->set_active(true);
set_overlay_active = true;
}
}
// IO panel needs to know what game is running
if (!cfg::CONFIGURATOR_STANDALONE) {
overlay::Window *iopanel = nullptr;
window_iopanel = nullptr;
if (avs::game::is_model("LDJ")) {
iopanel = new overlay::windows::IIDXIOPanel(this);
window_iopanel = new overlay::windows::IIDXIOPanel(this);
} else if (avs::game::is_model("MDX")) {
iopanel = new overlay::windows::DDRIOPanel(this);
window_iopanel = new overlay::windows::DDRIOPanel(this);
} else if (avs::game::is_model({"J32", "J33", "K32", "K33", "L32", "L33", "M32"})) {
iopanel = new overlay::windows::GitadoraIOPanel(this);
window_iopanel = new overlay::windows::GitadoraIOPanel(this);
} else {
iopanel = new overlay::windows::IOPanel(this);
window_iopanel = new overlay::windows::IOPanel(this);
}
if (iopanel) {
this->window_add(iopanel);
if (window_iopanel) {
this->window_add(window_iopanel);
if (AUTO_SHOW_IOPANEL) {
iopanel->set_active(true);
window_iopanel->set_active(true);
set_overlay_active = true;
}
}
@@ -371,20 +381,22 @@ void overlay::SpiceOverlay::init() {
// subscreens need DirectX, so don't try to initialize them in standalone
if (!cfg::CONFIGURATOR_STANDALONE) {
overlay::Window *subscreen = nullptr;
window_sub = nullptr;
if (avs::game::is_model("LDJ")) {
if (games::iidx::TDJ_MODE) {
subscreen = new overlay::windows::IIDXSubScreen(this);
window_sub = new overlay::windows::IIDXSubScreen(this);
} else {
subscreen = new overlay::windows::IIDXSegmentDisplay(this);
window_sub = new overlay::windows::IIDXSegmentDisplay(this);
}
} else if (avs::game::is_model("REC")) {
window_sub = new overlay::windows::DRSDanceFloorDisplay(this);
} else if (avs::game::is_model("KFC")) {
subscreen = new overlay::windows::SDVXSubScreen(this);
window_sub = new overlay::windows::SDVXSubScreen(this);
}
if (subscreen) {
this->window_add(subscreen);
if (window_sub) {
this->window_add(window_sub);
if (AUTO_SHOW_SUBSCREEN) {
subscreen->set_active(true);
window_sub->set_active(true);
set_overlay_active = true;
}
}
@@ -441,57 +453,15 @@ void overlay::SpiceOverlay::new_frame() {
}
ImGui::NewFrame();
// animated background
if (cfg::CONFIGURATOR_STANDALONE) {
auto flags = ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoBackground |
ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoFocusOnAppearing |
ImGuiWindowFlags_NoInputs;
if (ImGui::Begin("Background", nullptr, flags)) {
ImGui::SetWindowPos(ImVec2(0, 0));
ImGui::SetWindowSize(ImGui::GetIO().DisplaySize);
static const int EDGES[] = { 3, 4, 5, 6, 18 };
auto &io = ImGui::GetIO();
auto display_x = std::ceil(io.DisplaySize.x);
auto display_y = std::ceil(io.DisplaySize.y);
auto spacing = (display_x + display_y) * 0.1f;
auto max_x = static_cast<int>(display_x / spacing + 1.f);
auto max_y = static_cast<int>(display_y / spacing + 1.f);
auto draw = ImGui::GetWindowDrawList();
for (int i_x = -1; i_x < max_x; i_x++) {
for (int i_y = -1; i_y < max_y; i_y++) {
auto x = static_cast<float>(i_x);
auto y = static_cast<float>(i_y);
draw->AddCircleFilled(
ImVec2(
x * spacing + sinf(total_elapsed * 0.5f + x - y) * spacing * 0.8f,
y * spacing + cosf(total_elapsed * 0.5f - x + y) * spacing * 0.8f
),
spacing * 0.3f + sinf(total_elapsed * 0.2f - x - y) * spacing * 0.05f,
ImColor(
0.1f,
0.1f,
0.1f,
1.f),
EDGES[static_cast<size_t>(i_x + i_y) % std::size(EDGES)]);
}
}
ImGui::End();
}
}
// build windows
for (auto &window : this->windows) {
window->build();
}
if (SHOW_DEBUG_LOG_WINDOW) {
ImGui::ShowDebugLogWindow(&SHOW_DEBUG_LOG_WINDOW);
}
// end frame
ImGui::EndFrame();
}
@@ -557,6 +527,15 @@ void overlay::SpiceOverlay::update() {
}
this->toggle_down = toggle_down_new;
// check main menu
const auto main_menu_down_new = overlay_buttons
&& this->hotkeys_triggered()
&& GameAPI::Buttons::getState(RI_MGR, overlay_buttons->at(games::OverlayButtons::ToggleMainMenu));
if (main_menu_down_new && !this->main_menu_down) {
show_main_menu();
}
this->main_menu_down = main_menu_down_new;
// update windows
for (auto &window : this->windows) {
window->update();
@@ -584,12 +563,40 @@ void overlay::SpiceOverlay::toggle_active(bool overlay_key) {
// invert active state
this->active = !this->active;
// get rid of main menu if it was visible
if (this->window_main_menu) {
this->window_main_menu->set_active(false);
}
// show FPS window if toggled with overlay key
if (overlay_key) {
this->window_fps->set_active(this->active);
}
}
void overlay::SpiceOverlay::show_main_menu() {
if (!this->window_main_menu) {
return;
}
if (this->window_main_menu->get_active()) {
// window already visible - close the window
this->window_main_menu->set_active(false);
return;
}
if (ImGui::IsPopupOpen(0, ImGuiPopupFlags_AnyPopup)) {
return;
}
if (this->get_active()) {
if (!ImGui::IsAnyItemActive() && !ImGui::IsAnyItemFocused()) {
this->window_main_menu->set_active(true);
}
} else {
this->set_active(true);
this->window_main_menu->set_active(true);
}
}
void overlay::SpiceOverlay::set_active(bool new_active) {
// toggle if different
@@ -690,3 +697,21 @@ uint32_t *overlay::SpiceOverlay::sw_get_pixel_data(int *width, int *height) {
*height = this->pixel_data_height;
return &this->pixel_data[0];
}
void overlay::SpiceOverlay::add_font(const char* font, ImFontConfig* config, const ImWchar* glyphs) {
CHAR fonts_dir[MAX_PATH];
ExpandEnvironmentStringsA(R"(%SYSTEMROOT%\Fonts\)", fonts_dir, MAX_PATH);
std::filesystem::path full_path = fonts_dir;
full_path += font;
if (fileutils::file_exists(full_path)) {
log_misc("overlay", "loading font: {}", full_path.string());
ImGui::GetIO().Fonts->AddFontFromFileTTF(
full_path.string().c_str(),
13.0f,
config,
glyphs);
} else {
log_misc("overlay", "font not found: {}", full_path.string());
}
}
+20 -1
View File
@@ -25,6 +25,8 @@ namespace overlay {
extern bool AUTO_SHOW_KEYPAD_P1;
extern bool AUTO_SHOW_KEYPAD_P2;
extern bool USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT;
extern bool FPS_SHOULD_FLIP;
extern bool SHOW_DEBUG_LOG_WINDOW;
class SpiceOverlay {
public:
@@ -33,6 +35,19 @@ namespace overlay {
D3DADAPTER_IDENTIFIER9 adapter_identifier {};
bool hotkeys_enable = true;
// windows
Window *window_fps = nullptr;
Window *window_iopanel = nullptr;
Window *window_config = nullptr;
Window *window_keypad1 = nullptr;
Window *window_keypad2 = nullptr;
Window *window_cards = nullptr;
Window *window_control = nullptr;
Window *window_resize = nullptr;
Window *window_camera = nullptr;
Window *window_sub = nullptr;
Window *window_log = nullptr;
explicit SpiceOverlay(HWND hWnd, IDirect3D9 *d3d, IDirect3DDevice9 *device);
explicit SpiceOverlay(HWND hWnd);
~SpiceOverlay();
@@ -42,6 +57,7 @@ namespace overlay {
void render();
void update();
void toggle_active(bool overlay_key = false);
void show_main_menu();
void set_active(bool active);
bool get_active();
bool has_focus();
@@ -101,16 +117,19 @@ namespace overlay {
size_t pixel_data_height = 0;
std::vector<std::unique_ptr<Window>> windows;
Window *window_fps = nullptr;
Window *window_main_menu = nullptr;
std::function<bool(LONG *, LONG *)> subscreen_mouse_handler = nullptr;
bool active = false;
bool toggle_down = false;
bool main_menu_down = false;
bool hotkey_toggle = false;
bool hotkey_toggle_last = false;
void init();
void add_font(const char* font, ImFontConfig* config, const ImWchar* glyphs);
};
// global
+2 -3
View File
@@ -99,10 +99,9 @@ void overlay::Window::build() {
for (auto &child : this->children) {
child->build();
}
// end window
ImGui::End();
}
// end window
ImGui::End();
if (this->remove_window_padding) {
ImGui::PopStyleVar();
+1
View File
@@ -3,6 +3,7 @@
#include <string>
#include "external/imgui/imgui.h"
#include "external/imgui/misc/cpp/imgui_stdlib.h"
#include "overlay.h"
namespace overlay {
+216
View File
@@ -0,0 +1,216 @@
#include "camera_control.h"
#if SPICE64
#include <games/io.h>
#include <strmif.h>
#include "games/iidx/camera.h"
#include "games/iidx/local_camera.h"
#include "misc/eamuse.h"
#include "util/utils.h"
#include "util/fileutils.h"
#include "util/logging.h"
#include "overlay/imgui/extensions.h"
#include "misc/clipboard.h"
using namespace games::iidx;
namespace overlay::windows {
CameraControl::CameraControl(SpiceOverlay *overlay) : Window(overlay) {
this->title = "IIDX Camera Control";
this->flags |= ImGuiWindowFlags_AlwaysAutoResize;
this->init_pos = ImVec2(40, 40);
this->toggle_button = games::OverlayButtons::ToggleCameraControl;
}
CameraControl::~CameraControl() {
}
void CameraControl::build_content() {
if (!CAMERA_READY) {
ImGui::TextColored(ImVec4(1.f, 1.f, 0.f, 1.f), "%s", "Camera not ready");
return;
}
// camera combo box
auto numCameras = LOCAL_CAMERA_LIST.size();
if (numCameras == 0) {
return;
}
IIDXLocalCamera *selectedCamera = LOCAL_CAMERA_LIST.at(m_selectedCameraIndex);
auto selectedCameraChanged = ImGui::BeginCombo(
"Source", selectedCamera->GetName().c_str()
);
if (selectedCameraChanged) {
for (size_t i = 0; i < numCameras; i++) {
IIDXLocalCamera *cameraItem = LOCAL_CAMERA_LIST.at(i);
const bool is_selected = (m_selectedCameraIndex == (int) i);
if (ImGui::Selectable(cameraItem->GetName().c_str(), is_selected)) {
m_selectedCameraIndex = i;
}
if (is_selected) {
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
selectedCamera = LOCAL_CAMERA_LIST.at(m_selectedCameraIndex);
ImGui::AlignTextToFramePadding();
if (selectedCamera->GetFriendlyName().length() < 22) {
ImGui::Text("%s", selectedCamera->GetFriendlyName().c_str());
} else {
ImGui::Text("%.19s...", selectedCamera->GetFriendlyName().c_str());
}
ImGui::SameLine();
ImGui::HelpMarker(selectedCamera->GetSymLink().c_str());
ImGui::SameLine();
if (ImGui::Button("Copy")) {
const auto s = selectedCamera->GetFriendlyName() + "\n" + selectedCamera->GetSymLink();
clipboard::copy_text(s);
}
// Render parameters
ImGui::Separator();
ImGui::Text("Rendering");
// Media Type Selector
int selectedMediaTypeIndex = selectedCamera->m_selectedMediaTypeIndex;
auto numMediaTypes = selectedCamera->m_mediaTypeInfos.size();
auto selectedMediaTypeIndexChanged = ImGui::BeginCombo(
"Media Type", selectedCamera->m_mediaTypeInfos.at(selectedMediaTypeIndex).description.c_str()
);
if (selectedMediaTypeIndexChanged) {
for (size_t i = 0; i < numMediaTypes; i++) {
const bool is_selected = (selectedMediaTypeIndex == (int) i);
if (ImGui::Selectable(selectedCamera->m_mediaTypeInfos.at(i).description.c_str(), is_selected)) {
selectedMediaTypeIndex = i;
}
if (is_selected) {
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
if (selectedMediaTypeIndexChanged && selectedMediaTypeIndex != selectedCamera->m_selectedMediaTypeIndex) {
selectedCamera->m_useAutoMediaType = false;
selectedCamera->ChangeMediaType(selectedCamera->m_mediaTypeInfos.at(selectedMediaTypeIndex).p_mediaType);
}
// Auto media type
bool isAutoMediaType = selectedCamera->m_useAutoMediaType;
ImGui::SameLine(300);
if (ImGui::Checkbox("Auto##MediaType", &isAutoMediaType)) {
selectedCamera->m_useAutoMediaType = isAutoMediaType;
if (isAutoMediaType) {
selectedCamera->ChangeMediaType(selectedCamera->m_pAutoMediaType);
}
}
// Draw mode
int selectedDrawModeIndex = selectedCamera->m_drawMode;
if (ImGui::BeginCombo("Draw Mode", DRAW_MODE_LABELS[selectedCamera->m_drawMode].c_str())) {
for (size_t i = 0; i < DRAW_MODE_SIZE; i++) {
const bool is_selected = (selectedDrawModeIndex == (int) i);
if (ImGui::Selectable(DRAW_MODE_LABELS[i].c_str(), is_selected)) {
selectedDrawModeIndex = i;
}
if (is_selected) {
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
ImGui::SameLine();
ImGui::HelpMarker(
"Stretch: direct copy from source to destination\n\n"
"Crop: Keep aspect ratio (16:9) and cut off horizontally or vertically\n\n"
"Letterbox: Keep aspect ratio (16:9) and add black space horizontally or vertically\n\n"
"Crop to 4:3: Crop to display as 4:3\n\n"
"Letterbox to 4:3: Like Letterbox, but target 4:3"
);
if (selectedDrawModeIndex != selectedCamera->m_drawMode) {
selectedCamera->m_drawMode = (LocalCameraDrawMode)selectedDrawModeIndex;
selectedCamera->UpdateDrawRect();
}
ImGui::AlignTextToFramePadding();
ImGui::Checkbox("Horizontal Flip", &selectedCamera->m_flipHorizontal);
ImGui::SameLine();
ImGui::Checkbox("Vertical Flip", &selectedCamera->m_flipVertical);
// Camera control parameters
ImGui::Separator();
ImGui::Text("Camera control");
// some high end webcams store settings on its onboard memory, with the user configuring it
// via proprietary software outside of the game, so don't mess with it unless the user
// explicitly wants to change things here
ImGui::Checkbox("Allow manual control", &selectedCamera->m_allowManualControl);
ImGui::BeginDisabled(!selectedCamera->m_allowManualControl);
IAMCameraControl *pCameraControl = selectedCamera->GetCameraControl();
if (pCameraControl) {
for (size_t i = 0; i < CAMERA_CONTROL_PROP_SIZE; i++) {
CameraControlProp prop = {};
selectedCamera->GetCameraControlProp(i, &prop);
auto value = prop.value;
bool isDisabled = (prop.defFlags == 0 || prop.valueFlags & CameraControl_Flags_Auto);
ImGui::BeginDisabled(isDisabled);
int sliderFlag = ImGuiSliderFlags_AlwaysClamp | ImGuiSliderFlags_NoRoundToFormat;
bool isDefAuto = prop.defFlags & CameraControl_Flags_Auto;
if (ImGui::SliderInt(CAMERA_CONTROL_LABELS[i].c_str(), (int*) &value, prop.minValue, prop.maxValue, "%d", sliderFlag)) {
selectedCamera->SetCameraControlProp(i, value, prop.valueFlags);
}
ImGui::EndDisabled();
if (isDefAuto) {
ImGui::SameLine(300);
if (ImGui::CheckboxFlags(("Auto##" + CAMERA_CONTROL_LABELS[i]).c_str(), (int*) &prop.valueFlags, CameraControl_Flags_Auto)) {
selectedCamera->SetCameraControlProp(i, value, prop.valueFlags);
}
}
}
}
ImGui::Separator();
// reset button
if (ImGui::Button("Reset")) {
selectedCamera->ResetCameraControlProps();
}
ImGui::EndDisabled();
// save button
ImGui::SameLine();
if (ImGui::Button("Save")) {
this->config_save();
}
}
void CameraControl::config_save() {
camera_config_save();
}
}
#endif
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#if SPICE64
#include "overlay/window.h"
#include <strmif.h>
namespace overlay::windows {
class CameraControl : public Window {
public:
CameraControl(SpiceOverlay *overlay);
~CameraControl() override;
void build_content() override;
private:
int m_selectedCameraIndex = 0;
bool config_dirty = false;
void config_save();
};
}
#endif
+446 -91
View File
@@ -1,11 +1,15 @@
#include <random>
#include <games/io.h>
#include "card_manager.h"
#include "external/rapidjson/document.h"
#include "external/rapidjson/writer.h"
#include "external/rapidjson/prettywriter.h"
#include "misc/eamuse.h"
#include "misc/clipboard.h"
#include "util/utils.h"
#include "util/fileutils.h"
#include "cfg/configurator.h"
#include "overlay/imgui/extensions.h"
using namespace rapidjson;
@@ -14,104 +18,413 @@ namespace overlay::windows {
CardManager::CardManager(SpiceOverlay *overlay) : Window(overlay) {
this->title = "Card Manager";
this->init_size = ImVec2(300, 200);
this->init_pos = ImVec2(
ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2,
ImGui::GetIO().DisplaySize.y / 2 - this->init_size.y / 2);
this->init_size = ImVec2(420, 420);
if (cfg::CONFIGURATOR_STANDALONE) {
this->init_pos = ImVec2(40, 40);
} else {
this->init_pos = ImVec2(
ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2,
ImGui::GetIO().DisplaySize.y / 2 - this->init_size.y / 2);
}
this->toggle_button = games::OverlayButtons::ToggleCardManager;
this->config_path = std::string(getenv("APPDATA")) + "\\spicetools_card_manager.json";
if (fileutils::file_exists(this->config_path)) {
bool file_exists = false;
this->config_path =
fileutils::get_config_file_path("cardmanager", "spicetools_card_manager.json", &file_exists);
if (file_exists) {
this->config_load();
}
// load -card0 / -card1
// -card0 / -card1 override
{
std::lock_guard<std::mutex> lock(CARD_OVERRIDES_LOCK);
if (!CARD_OVERRIDES[0].empty()) {
const CardEntry card0 = {
.name = "P1 Default (-card0)",
.id = CARD_OVERRIDES[0],
.search_string = "p1 default (-card0)",
.read_only = true,
.color = {0.9f, 0.9f, 0.9f}
};
card_cmd_overrides[0].emplace(card0);
this->loaded_card[0] = card0;
}
if (eamuse_get_game_keypads() > 1 && !CARD_OVERRIDES[1].empty()) {
const CardEntry card1 = {
.name = "P2 Default (-card1)",
.id = CARD_OVERRIDES[1],
.search_string = "p2 default (-card1)",
.read_only = true,
.color = {0.9f, 0.9f, 0.9f}
};
card_cmd_overrides[1].emplace(card1);
this->loaded_card[1] = card1;
}
}
}
CardManager::~CardManager() {
}
void CardManager::build_content() {
// get window size
auto window_size = ImGui::GetWindowSize();
// name field
ImGui::InputTextWithHint("Card Name", "Main Card",
this->name_buffer, std::size(this->name_buffer));
// card field
ImGui::InputTextWithHint("Card ID", "E0040123456789AB",
this->card_buffer,
std::size(this->card_buffer),
ImGuiInputTextFlags_CharsHexadecimal
| ImGuiInputTextFlags_CharsUppercase);
// add card button
if (strlen(this->card_buffer) == 16) {
if (ImGui::Button("Add Card", ImVec2(-1.f, 0.f))) {
// save entry
CardEntry entry {
.name = this->name_buffer,
.id = this->card_buffer
};
this->cards.emplace_back(entry);
this->config_dirty = true;
// clear input fields
memset(this->name_buffer, 0, sizeof(this->name_buffer));
memset(this->card_buffer, 0, sizeof(this->card_buffer));
ImGui::TextColored(ImVec4(1, 0.7f, 0, 1), "Active card overrides");
ImGui::SameLine();
ImGui::HelpMarker(
"Click to insert card now, or press Insert Card key. Auto Card Insert will also use these cards.\n\n"
"If no override is set, pressing Insert Card will read from card0.txt / card1.txt.");
if (ImGui::BeginTable("CardSetTable", eamuse_get_game_keypads() > 1 ? 2 : 1, ImGuiTableFlags_SizingFixedFit)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted("Player 1");
if (eamuse_get_game_keypads() > 1) {
ImGui::TableNextColumn();
ImGui::TextUnformatted("Player 2");
}
} else {
ImGui::Text("Enter card identifier...");
ImGui::TableNextRow();
for (size_t i = 0; i < 2; i++) {
if (eamuse_get_game_keypads() > (int)i) {
ImGui::TableNextColumn();
if (build_card(i) && this->loaded_card[i].has_value()) {
insert_card_over_api(i, this->loaded_card[i].value());
}
}
}
ImGui::EndTable();
}
// cards area
ImGui::Spacing();
ImGui::Spacing();
ImGui::TextColored(ImVec4(1, 0.7f, 0, 1), "Available cards");
build_card_list();
ImGui::Separator();
if (ImGui::BeginChild("cards", ImVec2(0, window_size.y - 128))) {
for (auto &card : this->cards) {
ImGui::Spacing();
build_footer();
// get card name
std::string card_name = card.name;
if (card.name.size() > 0) {
card_name += " - ";
build_card_editor();
}
bool CardManager::build_card(int reader) {
ImGui::PushID(reader);
bool clicked = false;
if (this->loaded_card[reader].has_value()) {
const auto &card = this->loaded_card[reader].value();
const ImVec4 color(card.color[0], card.color[1], card.color[2], 1.f);
float bg_luminance = (0.299f * card.color[0] + 0.587 * card.color[1] + 0.114 * card.color[2]);
// text color
ImVec4 text_color;
if (0.5f < bg_luminance) {
text_color = ImVec4(0.f, 0.f, 0.f, 1.f); // black
} else {
text_color = ImVec4(1.f, 1.f, 1.f, 1.f); // white
}
ImGui::PushStyleColor(ImGuiCol_Button, color);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, color);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, color);
ImGui::PushStyleColor(ImGuiCol_Text, text_color);
if (ImGui::Button(fmt::format(
" {} \n {} {} {} {} ",
card.name.empty() ? "<blank>" : card.name.substr(0, 19),
card.id.substr(0, 4).c_str(),
card.id.substr(4, 4).c_str(),
card.id.substr(8, 4).c_str(),
card.id.substr(12, 4).c_str()
).c_str())) {
clicked = true;
}
ImGui::PopStyleColor(4);
} else {
ImGui::BeginDisabled();
ImGui::Button(" (No override set) \n"
" xxxx xxxx xxxx xxxx ");
ImGui::EndDisabled();
}
ImGui::PopID();
return clicked;
}
void CardManager::open_card_editor() {
if (this->current_card) {
const auto card = this->current_card;
strcpy_s(this->name_buffer, std::size(this->name_buffer), card->name.c_str());
strcpy_s(this->card_buffer, std::size(this->card_buffer), card->id.c_str());
this->color_buffer[0] = card->color[0];
this->color_buffer[1] = card->color[1];
this->color_buffer[2] = card->color[2];
ImGui::OpenPopup("Card Editor");
}
}
void CardManager::build_card_editor() {
// new/edit card popup
if (ImGui::BeginPopupModal("Card Editor", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
// card ID field (only editable for new cards)
ImGui::BeginDisabled(this->current_card);
ImGui::InputTextWithHint("Card ID", "E0040123456789AB",
this->card_buffer,
std::size(this->card_buffer),
ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase);
ImGui::EndDisabled();
if (this->current_card == nullptr) {
ImGui::SameLine();
if (ImGui::Button("Generate")) {
generate_ea_card(this->card_buffer);
}
card_name += card.id;
} else {
ImGui::SameLine();
if (ImGui::Button("Copy")) {
clipboard::copy_text(this->current_card->id);
}
}
// draw entry
ImGui::PushID(&card);
if (ImGui::Selectable(card_name.c_str(), card.selected)) {
// name field
ImGui::InputTextWithHint("Card Name", "Main Card",
this->name_buffer, std::size(this->name_buffer));
// unselect other cards
for (auto &card_disable : this->cards) {
card_disable.selected = false;
// color
ImGui::ColorEdit3("Color", this->color_buffer, ImGuiColorEditFlags_DisplayHex);
ImGui::SameLine();
if (ImGui::Button("Random")) {
generate_random_color();
}
ImGui::Separator();
// add/update button
ImGui::BeginDisabled(strlen(this->card_buffer) != 16);
if (ImGui::Button(this->current_card ? "Update Card" : "Save Card")) {
if (this->current_card) {
// update existing card
this->current_card->name = strtrim(this->name_buffer);
this->current_card->color[0] = this->color_buffer[0];
this->current_card->color[1] = this->color_buffer[1];
this->current_card->color[2] = this->color_buffer[2];
generate_search_string(this->current_card);
// ensure loaded cards are kept up to date
// note: does not handle cases where multiple cards have the same ID
for (size_t i = 0; i < 2; i++) {
if (this->loaded_card[i].has_value() &&
!this->loaded_card[i].value().read_only &&
this->loaded_card[i].value().id == this->current_card->id) {
this->loaded_card[i] = *this->current_card;
break;
}
}
} else {
// create a new card
CardEntry card {
.name = strtrim(this->name_buffer),
.id = std::string(this->card_buffer),
.color = {this->color_buffer[0], this->color_buffer[1], this->color_buffer[2]}
};
generate_search_string(&card);
this->cards.emplace_back(card);
// mark this card as the selected one
card.selected = true;
this->current_card = &this->cards.back();
}
this->config_save();
ImGui::CloseCurrentPopup();
}
ImGui::EndDisabled();
// delete current card button
if (this->current_card) {
ImGui::SameLine();
if (ImGui::Button("Delete Card")) {
std::erase_if(this->cards, [&](CardEntry &card) {
return &card == this->current_card;
});
this->current_card = nullptr;
this->config_save();
ImGui::CloseCurrentPopup();
}
}
ImGui::SameLine();
if (ImGui::Button("Cancel")) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
void CardManager::build_card_selectable(CardEntry &card) {
// generate card name
std::string card_name = "";
if (card.id.length() == 16) {
card_name += card.id.substr(0, 4);
card_name += " ";
card_name += card.id.substr(4, 4);
card_name += " ";
card_name += card.id.substr(8, 4);
card_name += " ";
card_name += card.id.substr(12, 4);
} else {
card_name += card.id;
}
if (!card.name.empty()) {
card_name += " - ";
card_name += card.name;
}
ImGui::PushID(&card);
// color button
ImVec4 color(card.color[0], card.color[1], card.color[2], 1.f);
ImGui::PushStyleColor(ImGuiCol_Button, color);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, color);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, color);
ImGui::SmallButton(" ");
ImGui::PopStyleColor(3);
ImGui::SameLine();
// selectable item
if (ImGui::Selectable(card_name.c_str(), this->current_card == &card)) {
this->current_card = &card;
}
ImGui::PopID();
}
void CardManager::build_card_list() {
// search for card
//
// setting ImGuiInputTextFlags_CallbackCharFilter and pressing escape doesn't cause below
// to return true, making it necessary to provide a callback...
ImGui::SetNextItemWidth(240);
if (ImGui::InputTextWithHint("", "Type here to search..", &this->search_filter)) {
this->current_card = nullptr;
this->search_filter_in_lower_case = strtolower(this->search_filter);
}
if (!this->search_filter.empty()) {
ImGui::SameLine();
if (ImGui::Button("Clear")) {
this->search_filter.clear();
this->search_filter_in_lower_case.clear();
}
}
// toolbar
// set card as p1/p2
for (size_t i = 0; i < 2; i++) {
if (eamuse_get_game_keypads() > (int)i) {
if (i != 0) {
ImGui::SameLine();
}
ImGui::PushID(i);
ImGui::BeginDisabled(this->current_card == nullptr);
if (ImGui::Button(i == 0 ? "Load P1" : "Load P2") && this->current_card) {
this->loaded_card[i] = *this->current_card;
log_info(
"cardmanager",
"[P{}] update override and insert card: {} ({})",
i+1,
this->current_card->id,
this->current_card->name
);
// update override
std::lock_guard<std::mutex> lock(CARD_OVERRIDES_LOCK);
CARD_OVERRIDES[i] = this->current_card->id;
// insert card over api
insert_card_over_api(i, this->loaded_card[i].value());
}
ImGui::EndDisabled();
ImGui::PopID();
}
}
ImGui::EndChild();
// insert P1 button
if (ImGui::Button("Insert P1")) {
auto card = this->cards_get_selected();
uint8_t card_bin[8];
if (card && card->id.length() == 16 && hex2bin(card->id.c_str(), card_bin)) {
eamuse_card_insert(0, card_bin);
}
// edit selected card
ImGui::SameLine();
ImGui::BeginDisabled(this->current_card == nullptr || this->current_card->read_only);
if (ImGui::Button("Edit")) {
open_card_editor();
}
// insert P2 button
if (eamuse_get_game_keypads() > 1) {
// move selected up/down the list
if (this->search_filter.empty()) {
ImGui::SameLine();
if (ImGui::Button("Insert P2")) {
auto card = this->cards_get_selected();
uint8_t card_bin[8];
if (card && card->id.length() == 16 && hex2bin(card->id.c_str(), card_bin)) {
eamuse_card_insert(1, card_bin);
if (ImGui::Button("Move Up")) {
for (auto it = this->cards.begin(); it != this->cards.end(); ++it) {
if (&*it == this->current_card && it != this->cards.begin()) {
std::iter_swap(it, it - 1);
this->current_card = &*(it - 1);
this->config_dirty = true;
break;
}
}
}
ImGui::SameLine();
if (ImGui::Button("Move Down")) {
for (auto it = this->cards.begin(); it != this->cards.end(); ++it) {
if (&*it == this->current_card && (it + 1) != this->cards.end()) {
std::iter_swap(it, it + 1);
this->current_card = &*(it + 1);
this->config_dirty = true;
break;
}
}
}
}
ImGui::EndDisabled();
ImGui::Spacing();
// cards list
// use all available vertical space, minus height footer (a row of buttons and separator)
if (ImGui::BeginChild(
"cards",
ImVec2(0, ImGui::GetContentRegionAvail().y - ImGui::GetFrameHeightWithSpacing() - 8.f))) {
// -card0 / -card1 override
for (size_t i = 0; i < 2; i++) {
if (card_cmd_overrides[i].has_value()) {
build_card_selectable(card_cmd_overrides[i].value());
}
}
// cards from card manager JSON
for (auto &card : this->cards) {
if (!this->search_filter_in_lower_case.empty() && !card.search_string.empty()) {
const bool matched =
card.search_string.find(this->search_filter_in_lower_case) != std::string::npos;
if (!matched) {
continue;
}
}
build_card_selectable(card);
}
}
ImGui::EndChild();
}
void CardManager::build_footer() {
// add new card
if (ImGui::Button("Add New Card")) {
memset(this->name_buffer, 0, sizeof(this->name_buffer));
memset(this->card_buffer, 0, sizeof(this->card_buffer));
generate_random_color();
this->current_card = nullptr;
ImGui::OpenPopup("Card Editor");
}
// save button
@@ -123,24 +436,7 @@ namespace overlay::windows {
}
}
CardEntry *CardManager::cards_get_selected() {
// iterate cards
for (auto &card : this->cards) {
// check if selected and return pointer
if (card.selected) {
return &card;
}
}
// no card selected
return nullptr;
}
void CardManager::config_load() {
log_info("cardmanager", "loading config");
// clear cards
this->cards.clear();
@@ -192,8 +488,21 @@ namespace overlay::windows {
// save entry
CardEntry entry {
.name = name->value.GetString(),
.id = id->value.GetString()
.id = id->value.GetString(),
};
generate_search_string(&entry);
// optional color
auto color = card.FindMember("color");
if (color != doc.MemberEnd() && color->value.IsArray()) {
auto c = color->value.GetArray();
if (c.Size() == 3 && c[0].IsFloat()) {
entry.color[0] = c[0].GetFloat();
entry.color[1] = c[1].GetFloat();
entry.color[2] = c[2].GetFloat();
}
}
this->cards.emplace_back(entry);
} else {
@@ -242,19 +551,65 @@ namespace overlay::windows {
Value card(kObjectType);
card.AddMember("name", StringRef(entry.name.c_str()), doc.GetAllocator());
card.AddMember("id", StringRef(entry.id.c_str()), doc.GetAllocator());
Value color(kArrayType);
color.PushBack(entry.color[0], doc.GetAllocator());
color.PushBack(entry.color[1], doc.GetAllocator());
color.PushBack(entry.color[2], doc.GetAllocator());
card.AddMember("color", color, doc.GetAllocator());
cards.PushBack(card, doc.GetAllocator());
}
// build JSON
// build JSON; using pretty writer so people can manually edit it
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
PrettyWriter<StringBuffer> writer(buffer);
doc.Accept(writer);
// save to file
if (fileutils::text_write(this->config_path, buffer.GetString())) {
if (fileutils::write_config_file("cardmanager", this->config_path, buffer.GetString())) {
this->config_dirty = false;
} else {
log_warning("cardmanager", "unable to save config file to {}", this->config_path);
log_warning("cardmanager", "unable to save config file");
}
}
void CardManager::generate_search_string(CardEntry *card) {
card->search_string = strtolower(card->name) + " " + strtolower(card->id);
}
void CardManager::generate_random_color() {
// these are colors on a hue wheel, starting from red
static const char colors[48][7] = {
"FF0000","FF2000","FF4000","FF6000","FF8000","FFAA00","FFCC00","FFEE00",
"FFFF00","DDFF00","CCFF00","AAFF00","80FF00","60FF00","40FF00","20FF00",
"00FF00","00FF20","00FF40","00FF60","00FF80","00FFAA","00FFCC","00FFDD",
"00FFFF","00DDFF","00CCFF","0099FF","0080FF","0060FF","0040FF","0020FF",
"0000FF","2000FF","4000FF","6000FF","8000FF","AA00FF","CC00FF","DD00FF",
"FF00FF","FF00EE","FF00CC","FF00AA","FF0080","FF0060","FF0040","FF0020"
};
std::random_device rd;
std::mt19937 generator(rd());
std::uniform_int_distribution<> uniform(12, 36);
// skip, ignoring half the hue wheel close to current index
static int index = 0;
index = (index + uniform(generator)) % 48;
const auto hex = colors[index];
uint8_t r, g, b;
std::sscanf(hex, "%02hhx%02hhx%02hhx", &r, &g, &b);
this->color_buffer[0] = r / 255.f;
this->color_buffer[1] = g / 255.f;
this->color_buffer[2] = b / 255.f;
}
void CardManager::insert_card_over_api(int reader, CardEntry &card) {
uint8_t card_bin[8];
if (card.id.length() == 16 && hex2bin(card.id.c_str(), card_bin)) {
eamuse_card_insert(reader, card_bin);
}
}
}
+29 -4
View File
@@ -1,13 +1,18 @@
#pragma once
#include <filesystem>
#include <optional>
#include "overlay/window.h"
namespace overlay::windows {
struct CardEntry {
std::string name = "unnamed";
std::string id = "E004000000000000";
bool selected = false;
std::string id = "E004010000000000";
std::string search_string = "";
bool read_only = false;
float color[3] {};
};
class CardManager : public Window {
@@ -20,14 +25,34 @@ namespace overlay::windows {
private:
std::string config_path;
std::filesystem::path config_path;
bool config_dirty = false;
std::vector<CardEntry> cards;
char name_buffer[65] {};
char card_buffer[17] {};
float color_buffer[3] {};
std::optional<CardEntry> card_cmd_overrides[2];
std::optional<CardEntry> loaded_card[2];
CardEntry *current_card = nullptr;
std::string search_filter = "";
std::string search_filter_in_lower_case = "";
CardEntry *cards_get_selected();
void config_load();
void config_save();
void generate_search_string(CardEntry *card);
void generate_random_color();
bool build_card(int reader);
void open_card_editor();
void build_card_editor();
void build_card_list();
void build_card_selectable(CardEntry &card);
void build_footer();
void insert_card_over_api(int reader, CardEntry &card);
};
}
+2343 -1433
View File
File diff suppressed because it is too large Load Diff
+30 -2
View File
@@ -1,5 +1,7 @@
#pragma once
#include <optional>
#include "cfg/game.h"
#include "overlay/window.h"
#include "rawinput/device.h"
@@ -8,6 +10,21 @@
namespace overlay::windows {
enum class ConfigTab {
CONFIG_TAB_INVALID,
CONFIG_TAB_BUTTONS,
CONFIG_TAB_ANALOGS,
CONFIG_TAB_OVERLAY,
CONFIG_TAB_LIGHTS,
CONFIG_TAB_CARDS,
CONFIG_TAB_PATCHES,
CONFIG_TAB_API,
CONFIG_TAB_OPTIONS,
CONFIG_TAB_ADVANCED,
CONFIG_TAB_DEV,
CONFIG_TAB_SEARCH,
};
class Config : public Window {
private:
@@ -17,15 +34,21 @@ namespace overlay::windows {
std::vector<Game> games_list;
std::vector<const char *> games_names;
// tabs ui
ConfigTab tab_selected = ConfigTab::CONFIG_TAB_INVALID;
// buttons tab
int buttons_page = 0;
bool buttons_keyboard_state[0xFF];
bool buttons_bind_active = false;
bool buttons_many_active = false;
std::string buttons_many_active_section = "";
bool buttons_many_naive = false;
int buttons_many_delay = 0;
int buttons_many_index = -1;
void inc_buttons_many_index(int index_max);
// analogs tab
std::vector<rawinput::Device *> analogs_devices;
int analogs_devices_selected = -1;
@@ -52,6 +75,8 @@ namespace overlay::windows {
bool options_show_hidden = false;
bool options_dirty = false;
int options_category = 0;
std::string search_filter = "";
std::string search_filter_in_lower_case = "";
public:
Config(SpiceOverlay *overlay);
@@ -65,12 +90,15 @@ namespace overlay::windows {
void build_analogs(const std::string &name, std::vector<Analog> *analogs);
void build_lights(const std::string &name, std::vector<Light> *lights);
void build_cards();
void build_options(std::vector<Option> *options, const std::string &category);
void build_options(
std::vector<Option> *options, const std::string &category, const std::string *filter=nullptr);
void build_about();
void build_licenses();
void build_launcher();
void launch_url();
void launch_shell(LPCSTR app, LPCSTR file);
static void build_page_selector(int *page);
void build_menu(int *game_selected);
void shutdown_system(bool force, bool reboot_instead);
};
}
+18 -12
View File
@@ -178,20 +178,26 @@ namespace overlay::windows {
static_cast<int>(ImGui::GetIO().DisplaySize.x),
static_cast<int>(ImGui::GetIO().DisplaySize.y));
// metrics button
this->metrics_open |= ImGui::Button("Metrics Window");
if (this->metrics_open) {
ImGui::ShowMetricsWindow(&this->metrics_open);
if (ImGui::Button("ImGui Debug Log")) {
overlay::SHOW_DEBUG_LOG_WINDOW = !overlay::SHOW_DEBUG_LOG_WINDOW;
}
// removed for size (IMGUI_DISABLE_DEMO_WINDOWS)
// metrics button
// this->metrics_open |= ImGui::Button("Metrics Window");
// if (this->metrics_open) {
// ImGui::ShowMetricsWindow(&this->metrics_open);
// }
// demo button
ImGui::SameLine();
if (ImGui::Button("Demo Window")) {
this->demo_open = true;
}
if (this->demo_open) {
ImGui::ShowDemoWindow(&this->demo_open);
}
// ImGui::SameLine();
// if (ImGui::Button("Demo Window")) {
// this->demo_open = true;
// }
// if (this->demo_open) {
// ImGui::ShowDemoWindow(&this->demo_open);
// }
}
}
@@ -745,7 +751,7 @@ namespace overlay::windows {
if (ImGui::CollapsingHeader("Touch")) {
// status
ImGui::Text("Status: %s", is_touch_available() ? "available" : "unavailable");
ImGui::Text("Status: %s", is_touch_available("Control::touch_view") ? "available" : "unavailable");
// touch points
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
+66
View File
@@ -0,0 +1,66 @@
#include <map>
#include "drs_dancefloor.h"
#include "games/io.h"
#include "games/drs/drs.h"
#include "util/logging.h"
namespace overlay::windows {
DRSDanceFloorDisplay::DRSDanceFloorDisplay(SpiceOverlay *overlay) : Window(overlay) {
this->title = "DANCERUSH Floor";
this->toggle_button = games::OverlayButtons::ToggleSubScreen;
this->remove_window_padding = true;
this->size_max = ImVec2(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y);
this->size_min = ImVec2(100, 128 + ImGui::GetFrameHeight());
this->init_size = size_min;
this->resize_callback = this->keep_aspect_ratio;
this->flags = ImGuiWindowFlags_NoScrollbar
| ImGuiWindowFlags_NoNavFocus
| ImGuiWindowFlags_NoNavInputs
| ImGuiWindowFlags_NoDocking
| ImGuiWindowFlags_NoBackground;
}
void DRSDanceFloorDisplay::calculate_initial_window() {
this->init_size.x = 360;
this->init_size.y = this->init_size.x * DRS_TAPELED_ROWS / DRS_TAPELED_COLS;
// horizontal right
this->init_pos.x = ImGui::GetIO().DisplaySize.x - this->init_size.x;
// vertical center
this->init_pos.y = ImGui::GetIO().DisplaySize.y / 2 - this->init_size.y / 2;
}
void DRSDanceFloorDisplay::build_content() {
const auto draw_list = ImGui::GetWindowDrawList();
const auto canvas_pos = ImGui::GetCursorScreenPos();
const auto canvas_size = ImGui::GetContentRegionAvail();
const float off_x = canvas_pos.x;
const float off_y = canvas_pos.y;
const float scale = std::min(canvas_size.x, canvas_size.y) / DRS_TAPELED_COLS;
for (int x = 0; x < DRS_TAPELED_COLS; x++) {
for (int y = 0; y < DRS_TAPELED_ROWS; y++) {
auto &led = games::drs::DRS_TAPELED[x + y * DRS_TAPELED_COLS];
ImColor color(
((uint8_t)led[0]) / ((float)DRS_TAPELED_MAX_VAL),
((uint8_t)led[1]) / ((float)DRS_TAPELED_MAX_VAL),
((uint8_t)led[2]) / ((float)DRS_TAPELED_MAX_VAL));
// if (x == 0 && y == 0) {
// log_info("drs", "color: {} {} {}", (uint8_t)led[0], (uint8_t)led[1], (uint8_t)led[2]);
// }
ImVec2 p1(x * scale + off_x, y * scale + off_y);
ImVec2 p2((x + 1) * scale + off_x, (y + 1) * scale + off_y);
draw_list->AddRectFilled(p1, p2, color, 0.f);
}
}
}
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include "games/drs/drs.h"
#include "overlay/window.h"
namespace overlay::windows {
class DRSDanceFloorDisplay : public Window {
public:
DRSDanceFloorDisplay(SpiceOverlay *overlay);
void calculate_initial_window() override;
void build_content() override;
private:
static void keep_aspect_ratio(ImGuiSizeCallbackData* data) {
const float ratio = (float)DRS_TAPELED_ROWS / (float)DRS_TAPELED_COLS;
data->DesiredSize.y = (data->DesiredSize.x * ratio) + ImGui::GetFrameHeight();
}
};
}
+139
View File
@@ -0,0 +1,139 @@
#include "exitprompt.h"
#include "misc/eamuse.h"
#include "util/logging.h"
namespace overlay::windows {
ExitPrompt::ExitPrompt(SpiceOverlay *overlay) : Window(overlay) {
this->title = "spice2x";
this->init_size = ImVec2(
(ImGui::GetFontSize() * 14) + (ImGui::GetStyle().ItemSpacing.x * 2),
120);
this->init_pos = ImVec2(
ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2,
10);
this->flags = ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_NoCollapse
| ImGuiWindowFlags_AlwaysAutoResize
| ImGuiWindowFlags_NoDocking;
}
void ExitPrompt::build_button(
Window *window, std::string label, const ImVec2 &size, NextItem next, bool is_toggle) {
if (window == nullptr) {
return;
}
if (ImGui::Button(label.c_str(), size)) {
if (is_toggle) {
window->toggle_active();
} else {
window->set_active(true);
this->set_active(false);
}
}
if (next == NextItem::NEW_LINE) {
ImGui::Spacing();
} else {
ImGui::SameLine();
}
}
void ExitPrompt::build_content() {
const ImVec2 size(ImGui::GetFontSize() * 14, ImGui::GetFontSize() * 1.9f);
const ImVec2 size_half(
(size.x - ImGui::GetStyle().ItemSpacing.x) / 2,
ImGui::GetFontSize() * 1.9f);
const ImVec2 size_third(
(size.x - (ImGui::GetStyle().ItemSpacing.x * 2)) / 3,
ImGui::GetFontSize() * 2.5f);
if (ImGui::Button("Hide overlay", size)) {
overlay::OVERLAY->set_active(false);
}
ImGui::Spacing();
build_button(this->overlay->window_config, "Show Config", size, NextItem::NEW_LINE, false);
build_button(this->overlay->window_sub, "Show Subscreen", size, NextItem::NEW_LINE, false);
ImGui::TextDisabled("Graphics");
build_button(this->overlay->window_camera, "Camera control", size, NextItem::NEW_LINE);
build_button(this->overlay->window_fps, "FPS", size_half, NextItem::SAME_LINE);
build_button(this->overlay->window_resize, "Resize", size_half, NextItem::NEW_LINE);
ImGui::TextDisabled("I/O");
build_button(this->overlay->window_cards, "Card Manager", size, NextItem::NEW_LINE);
if (this->overlay->window_keypad2 == nullptr) {
// 1p only
build_button(this->overlay->window_keypad1, "Keypad", size_half, NextItem::SAME_LINE);
build_button(this->overlay->window_iopanel, "I/O panel", size_half, NextItem::NEW_LINE);
} else {
// 1p and 2p
build_button(this->overlay->window_keypad1, "Keypad\n P1", size_third, NextItem::SAME_LINE);
build_button(this->overlay->window_iopanel, " I/O\npanel", size_third, NextItem::SAME_LINE);
build_button(this->overlay->window_keypad2, "Keypad\n P2", size_third, NextItem::NEW_LINE);
}
ImGui::TextDisabled("Debug");
build_button(this->overlay->window_control, "Control", size_half, NextItem::SAME_LINE);
build_button(this->overlay->window_log, "Log", size_half, NextItem::NEW_LINE);
ImGui::TextDisabled("Windows audio volume");
if (ImGui::Button("-", size_third)) {
INPUT keys = {};
keys.type = INPUT_KEYBOARD;
keys.ki.wVk = VK_VOLUME_DOWN;
SendInput(1, &keys, sizeof(keys));
}
ImGui::SameLine();
if (ImGui::Button("Mute", size_third)) {
INPUT keys = {};
keys.type = INPUT_KEYBOARD;
keys.ki.wVk = VK_VOLUME_MUTE;
SendInput(1, &keys, sizeof(keys));
}
ImGui::SameLine();
if (ImGui::Button("+", size_third)) {
INPUT keys = {};
keys.type = INPUT_KEYBOARD;
keys.ki.wVk = VK_VOLUME_UP;
SendInput(1, &keys, sizeof(keys));
}
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
// quit
if (ImGui::Button("Exit Game", size)) {
ImGui::OpenPopup("spice2x##quitdialog");
}
if (ImGui::BeginPopupModal(
"spice2x##quitdialog",
nullptr,
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoDocking |
ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::TextUnformatted("Exit the game now?");
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
if (ImGui::Button("Exit")) {
log_info("exitprompt", "user chose to quit game...");
launcher::shutdown();
}
ImGui::SameLine();
if (ImGui::Button("Cancel")) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::Spacing();
}
}
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include <chrono>
#include "overlay/window.h"
namespace overlay::windows {
enum class NextItem {
NEW_LINE,
SAME_LINE
};
class ExitPrompt : public Window {
public:
ExitPrompt(SpiceOverlay *overlay);
void build_content() override;
private:
void build_button(Window *window, std::string label, const ImVec2 &size, NextItem next, bool is_toggle=true);
};
}
+2 -1
View File
@@ -20,7 +20,8 @@ namespace overlay::windows {
void FPS::calculate_initial_window() {
// width is 114x82 px with window decoration, 98x47 for the content
this->init_pos = ImVec2(ImGui::GetIO().DisplaySize.x - 120, 8);
int pos_x = overlay::FPS_SHOULD_FLIP ? 8 : ImGui::GetIO().DisplaySize.x - 122;
this->init_pos = ImVec2(pos_x, 8);
}
void FPS::build_content() {
+2
View File
@@ -15,5 +15,7 @@ namespace overlay::windows {
void calculate_initial_window() override;
void build_content() override;
bool should_flip = false;
};
}
+49 -4
View File
@@ -5,10 +5,19 @@
#include <fmt/format.h>
#include "games/io.h"
#include "cfg/screen_resize.h"
#include "hooks/graphics/backends/d3d9/d3d9_backend.h"
#include "hooks/graphics/backends/d3d9/d3d9_device.h"
#include "hooks/graphics/graphics.h"
#include "util/logging.h"
#include "util/utils.h"
#include "touch/touch.h"
int GENERIC_SUB_WINDOW_X = 0;
int GENERIC_SUB_WINDOW_Y = 0;
int GENERIC_SUB_WINDOW_WIDTH = 0;
int GENERIC_SUB_WINDOW_HEIGHT = 0;
bool GENERIC_SUB_WINDOW_FULLSIZE = false;
// #define OVERLAYDBG 1
@@ -19,6 +28,7 @@ namespace overlay::windows {
GenericSubScreen::GenericSubScreen(SpiceOverlay *overlay) : Window(overlay), device(overlay->get_device()) {
this->remove_window_padding = true;
// ImGuiWindowFlags_NoBackground is needed as the background is drawn on top of the subscreen image
this->flags = ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoBackground |
@@ -35,9 +45,34 @@ namespace overlay::windows {
overlay->set_subscreen_mouse_handler([this](LONG *x, LONG *y) -> bool {
// convert to normalized form (relative window coordinates 0.f-1.f)
ImVec2 xy(*x, *y);
xy.x = (xy.x - overlay_content_top_left.x) / overlay_content_size.x;
xy.y = (xy.y - overlay_content_top_left.y) / overlay_content_size.y;
ImVec2 xy;
// log_misc("sub::overlay", "mouse handler {} {}", to_string(*x), to_string(*y));
// log_misc("sub::overlay", "spicetouch coords {} {} {} {}", to_string(SPICETOUCH_TOUCH_X), to_string(SPICETOUCH_TOUCH_Y), to_string(SPICETOUCH_TOUCH_WIDTH), to_string(SPICETOUCH_TOUCH_HEIGHT));
float ratio_x, ratio_y;
if (GRAPHICS_WINDOWED) {
// input coords are relative to spicetouch wnd
ratio_x = (float)*x / SPICETOUCH_TOUCH_WIDTH;
ratio_y = (float)*y / SPICETOUCH_TOUCH_HEIGHT;
} else {
// inputs coords are relative to (0,0) for non-windowed mode
ratio_x = (float)*x / ImGui::GetIO().DisplaySize.x;
ratio_y = (float)*y / ImGui::GetIO().DisplaySize.y;
}
// log_misc("sub::overlay", "game coords {} {}", to_string(ratio_x), to_string(ratio_y));
// transform to subscreen overlay coords
if (!GENERIC_SUB_WINDOW_FULLSIZE) {
ratio_x = (ratio_x * ImGui::GetIO().DisplaySize.x - GENERIC_SUB_WINDOW_X) / GENERIC_SUB_WINDOW_WIDTH;
ratio_y = (ratio_y * ImGui::GetIO().DisplaySize.y - GENERIC_SUB_WINDOW_Y) / GENERIC_SUB_WINDOW_HEIGHT;
// log_misc("sub::overlay", "overlay coords {} {} {} {}", to_string(GENERIC_SUB_WINDOW_X), to_string(GENERIC_SUB_WINDOW_Y), to_string(GENERIC_SUB_WINDOW_WIDTH), to_string(GENERIC_SUB_WINDOW_HEIGHT));
}
xy.x = ratio_x;
xy.y = ratio_y;
// log_misc("sub::overlay", "ratio {} {}", to_string(xy.x), to_string(xy.y));
// x/y can be outside of window
if (xy.x < 0.f || 1.f < xy.x || xy.y < 0.f || 1.f < xy.y) {
@@ -53,6 +88,11 @@ namespace overlay::windows {
void GenericSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {}
void GenericSubScreen::build_content() {
if (this->disabled_message.has_value()) {
this->flags &= ~ImGuiWindowFlags_NoBackground;
ImGui::TextColored(YELLOW, "%s", this->disabled_message.value().c_str());
return;
}
this->draw_texture();
#if OVERLAYDBG
@@ -111,6 +151,11 @@ namespace overlay::windows {
overlay_content_size = ImGui::GetIO().DisplaySize;
}
GENERIC_SUB_WINDOW_X = overlay_content_top_left.x;
GENERIC_SUB_WINDOW_Y = overlay_content_top_left.y;
GENERIC_SUB_WINDOW_WIDTH = overlay_content_size.x;
GENERIC_SUB_WINDOW_HEIGHT = overlay_content_size.y;
if (this->draws_window &&
this->texture &&
((UINT)overlay_content_size.x != this->texture_width)) {
@@ -161,7 +206,7 @@ namespace overlay::windows {
bottom_right.x += overlay_content_top_left.x;
bottom_right.y += overlay_content_top_left.y;
ImGui::GetBackgroundDrawList()->AddImage(
reinterpret_cast<void *>(this->texture),
reinterpret_cast<ImTextureID>(this->texture),
overlay_content_top_left,
bottom_right);
+14
View File
@@ -8,6 +8,19 @@
#include "overlay/window.h"
//=================================================================================================
// Global variable to track the coords of Subscreen.
// Values are in original coord-space (not scaled to Windowed mode size).
//
// For use with touch coords transformation.
//=================================================================================================
extern int GENERIC_SUB_WINDOW_X;
extern int GENERIC_SUB_WINDOW_Y;
extern int GENERIC_SUB_WINDOW_WIDTH;
extern int GENERIC_SUB_WINDOW_HEIGHT;
extern bool GENERIC_SUB_WINDOW_FULLSIZE;
namespace overlay::windows {
class GenericSubScreen : public Window {
@@ -20,6 +33,7 @@ namespace overlay::windows {
virtual void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out);
ImVec2 overlay_content_top_left;
ImVec2 overlay_content_size;
std::optional<std::string> disabled_message = std::nullopt;
private:
static void keep_16_by_9(ImGuiSizeCallbackData* data) {
+20 -2
View File
@@ -1,13 +1,23 @@
#undef CINTERFACE
#include "iidx_sub.h"
#include "cfg/screen_resize.h"
#include "games/iidx/iidx.h"
#include "hooks/graphics/graphics.h"
#include "touch/touch.h"
namespace overlay::windows {
IIDXSubScreen::IIDXSubScreen(SpiceOverlay *overlay) : GenericSubScreen(overlay) {
this->title = "IIDX Sub Screen";
if (GRAPHICS_IIDX_WSUB) {
this->disabled_message =
"Close this overlay and use the second window.\n"
"Or, turn on -iidxnosub to use the overlay instead.";
this->draws_window = false;
}
float size = 0.5f;
if (games::iidx::SUBSCREEN_OVERLAY_SIZE.has_value()) {
if (games::iidx::SUBSCREEN_OVERLAY_SIZE.value() == "large") {
@@ -15,6 +25,7 @@ namespace overlay::windows {
} else if (games::iidx::SUBSCREEN_OVERLAY_SIZE.value() == "small") {
size = 0.3f;
} else if (games::iidx::SUBSCREEN_OVERLAY_SIZE.value() == "fullscreen") {
GENERIC_SUB_WINDOW_FULLSIZE = true;
this->draws_window = false;
}
}
@@ -37,7 +48,14 @@ namespace overlay::windows {
return;
}
*x_out = xy_in.x * ImGui::GetIO().DisplaySize.x;
*y_out = xy_in.y * ImGui::GetIO().DisplaySize.y;
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;
}
}
}
+18 -8
View File
@@ -17,8 +17,7 @@ namespace overlay::windows {
this->has_guitar_knobs = true;
// drummania can only have one player, no guitar knobs
if (avs::game::is_model({ "J32", "K32", "L32" }) ||
(avs::game::is_model("M32") && avs::game::SPEC[0] == 'B')) {
if (games::gitadora::is_drum()) {
this->two_players = false;
this->has_guitar_knobs = false;
}
@@ -39,6 +38,7 @@ namespace overlay::windows {
void GitadoraIOPanel::find_gfdm_buttons() {
const auto buttons = games::get_buttons(eamuse_get_game());
const auto lights = games::get_lights(eamuse_get_game());
// device emulation treats drum controls to be the same as guitar 1p
@@ -49,12 +49,22 @@ namespace overlay::windows {
this->left[0] = &(*buttons)[games::gitadora::Buttons::GuitarP1Left];
this->right[0] = &(*buttons)[games::gitadora::Buttons::GuitarP1Right];
this->start_light[0] = &(*lights)[games::gitadora::Lights::P1MenuStart];
this->help_light[0] = &(*lights)[games::gitadora::Lights::P1MenuHelp];
this->updown_light[0] = &(*lights)[games::gitadora::Lights::P1MenuUpDown];
this->leftright_light[0] = &(*lights)[games::gitadora::Lights::P1MenuLeftRight];
this->start[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Start];
this->help[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Help];
this->up[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Up];
this->down[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Down];
this->left[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Left];
this->right[1] = &(*buttons)[games::gitadora::Buttons::GuitarP2Right];
this->start_light[1] = &(*lights)[games::gitadora::Lights::P2MenuStart];
this->help_light[1] = &(*lights)[games::gitadora::Lights::P2MenuHelp];
this->updown_light[1] = &(*lights)[games::gitadora::Lights::P2MenuUpDown];
this->leftright_light[1] = &(*lights)[games::gitadora::Lights::P2MenuLeftRight];
}
void GitadoraIOPanel::build_io_panel() {
@@ -96,7 +106,7 @@ namespace overlay::windows {
ImGui::BeginGroup();
{
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetFrameHeightWithSpacing());
this->build_button("<", leftright_size, this->left[p]);
this->build_button("<", leftright_size, this->left[p], nullptr, this->leftright_light[p]);
}
ImGui::EndGroup();
@@ -104,7 +114,7 @@ namespace overlay::windows {
ImGui::BeginGroup();
{
this->build_button("^", updown_size, this->up[p]);
this->build_button("^", updown_size, this->up[p], nullptr, this->updown_light[p]);
const char *label;
if (this->two_players) {
if (p == 0) {
@@ -115,8 +125,8 @@ namespace overlay::windows {
} else {
label = "Start";
}
this->build_button(label, start_button_size, this->start[p]);
this->build_button("v", updown_size, this->down[p]);
this->build_button(label, start_button_size, this->start[p], nullptr, this->start_light[p]);
this->build_button("v", updown_size, this->down[p], nullptr, this->updown_light[p]);
}
ImGui::EndGroup();
@@ -124,8 +134,8 @@ namespace overlay::windows {
ImGui::BeginGroup();
{
this->build_button("?", tiny_size, this->help[p]);
this->build_button(">", leftright_size, this->right[p]);
this->build_button("?", tiny_size, this->help[p], nullptr, this->help_light[p]);
this->build_button(">", leftright_size, this->right[p], nullptr, this->leftright_light[p]);
}
ImGui::EndGroup();
}
+5
View File
@@ -27,5 +27,10 @@ namespace overlay::windows {
Button *down[2];
Button *left[2];
Button *right[2];
Light *start_light[2];
Light *help_light[2];
Light *updown_light[2];
Light *leftright_light[2];
};
}
+33
View File
@@ -1,8 +1,11 @@
#include <games/io.h>
#include "keypad.h"
#include "avs/game.h"
#include "games/iidx/iidx.h"
#include "misc/eamuse.h"
#include "util/logging.h"
#include "overlay/imgui/extensions.h"
namespace overlay::windows {
@@ -37,6 +40,14 @@ namespace overlay::windows {
}
void Keypad::build_content() {
if (avs::game::is_model("LDJ") && games::iidx::TDJ_MODE) {
build_tdj_keypad();
} else {
build_keypad();
}
}
void Keypad::build_keypad() {
// buttons
static const struct {
@@ -91,4 +102,26 @@ namespace overlay::windows {
}
}
}
void Keypad::build_tdj_keypad() {
ImGui::AlignTextToFramePadding();
ImGui::TextDisabled("Keypad disabled in TDJ!\nUse subscreen overlay.");
ImGui::SameLine();
ImGui::WarnMarker(
nullptr,
"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, "
"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 with -iidxnosub: bring up the subscreen overlay (default Page Up).\n\n"
);
ImGui::Spacing();
if (ImGui::Button("Insert Card")) {
eamuse_set_keypad_overrides_overlay(this->unit, 1 << EAM_IO_INSERT);
} else {
eamuse_set_keypad_overrides_overlay(this->unit, 0);
}
}
}
+2
View File
@@ -8,6 +8,8 @@ namespace overlay::windows {
private:
size_t unit = 0;
void build_keypad();
void build_tdj_keypad();
public:
File diff suppressed because it is too large Load Diff
-125
View File
@@ -1,125 +0,0 @@
#pragma once
#include "overlay/window.h"
#include <thread>
#include <mutex>
#include <memory>
namespace overlay::windows {
enum class HueFunc : int {
Sine, Cosine, Absolute, Linear,
Count
};
class KFControl : public Window {
public:
KFControl(SpiceOverlay *overlay);
~KFControl() override;
void config_save();
void config_load();
ImVec4 hsv_transform(ImVec4 col,
float hue = 0.f, float sat = 1.f, float val = 1.f);
ImVec4 hue_shift(ImVec4 col, float amp, float per, uint64_t ms);
void build_content() override;
private:
std::string config_path;
int config_profile = 0;
std::unique_ptr<std::thread> worker;
bool worker_running = false;
void worker_start();
void worker_func();
std::mutex worker_m;
void worker_button_check(bool state_new, bool *state_old, int scan,
int profile_switch = -1);
void worker_button_set(int scan, bool state);
void worker_mouse_click(bool state);
void worker_mouse_move(int dx, int dy);
int poll_delay = 1;
float vol_deadzone = 0.003f;
uint64_t vol_timeout = 32;
bool vol_mouse = false;
float vol_mouse_sensitivity = 512;
bool start_click = false;
bool kp_profiles = false;
float vol_sound = 0.f;
float vol_headphone = 0.f;
float vol_external = 0.f;
float vol_woofer = 0.f;
bool vol_mute = false;
char icca_file[512] = "";
uint64_t icca_timeout = 4000;
uint64_t coin_timeout = 250;
ImVec4 light_wing_left_up {};
ImVec4 light_wing_left_low {};
ImVec4 light_wing_right_up {};
ImVec4 light_wing_right_low {};
ImVec4 light_woofer {};
ImVec4 light_controller {};
ImVec4 light_generator {};
bool light_buttons = true;
bool light_hue_preview = false;
bool light_hue_disable = false;
HueFunc light_hue_func = HueFunc::Sine;
float light_wing_left_up_hue_amp = 0.f;
float light_wing_left_up_hue_per = 1.f;
float light_wing_left_low_hue_amp = 0.f;
float light_wing_left_low_hue_per = 1.f;
float light_wing_right_up_hue_amp = 0.f;
float light_wing_right_up_hue_per = 1.f;
float light_wing_right_low_hue_amp = 0.f;
float light_wing_right_low_hue_per = 1.f;
float light_woofer_hue_amp = 0.f;
float light_woofer_hue_per = 1.f;
float light_controller_hue_amp = 0.f;
float light_controller_hue_per = 1.f;
float light_generator_hue_amp = 0.f;
float light_generator_hue_per = 1.f;
/*
* Keyboard Scancodes
* Check: http://kbdlayout.info/kbdus/overview+scancodes
*/
int scan_service = 3;
int scan_test = 4;
int scan_coin_mech = 5;
int scan_bt_a = 32;
int scan_bt_b = 33;
int scan_bt_c = 36;
int scan_bt_d = 37;
int scan_fx_l = 46;
int scan_fx_r = 50;
int scan_start = 2;
int scan_headphone = 0;
int scan_vol_l_left = 17;
int scan_vol_l_right = 18;
int scan_vol_r_left = 24;
int scan_vol_r_right = 25;
int scan_icca = 6;
int scan_coin = 7;
int scan_kp_0 = 0;
int scan_kp_1 = 0;
int scan_kp_2 = 0;
int scan_kp_3 = 0;
int scan_kp_4 = 0;
int scan_kp_5 = 0;
int scan_kp_6 = 0;
int scan_kp_7 = 0;
int scan_kp_8 = 0;
int scan_kp_9 = 0;
int scan_kp_00 = 0;
int scan_kp_decimal = 0;
};
}
+2 -2
View File
@@ -32,7 +32,7 @@ namespace overlay::windows {
name = "SYSTEM";
break;
}
return fmt::format("{} (0x{:2X})", name, cmd);
return fmt::format("{} (0x{:X})", name, cmd);
}
MIDIWindow::MIDIWindow(SpiceOverlay *overlay) : Window(overlay) {
@@ -90,7 +90,7 @@ namespace overlay::windows {
ImGui::NextColumn();
ImGui::Text("%s", midi_cmd_str(data.cmd).c_str());
ImGui::NextColumn();
ImGui::Text("0x%02X - %i", data.ch, data.ch);
ImGui::Text("0x%X - Ch%i", data.ch, data.ch + 1);
ImGui::NextColumn();
ImGui::Text("0x%02X", data.b1);
ImGui::NextColumn();
File diff suppressed because it is too large Load Diff
+97 -8
View File
@@ -1,6 +1,11 @@
#pragma once
#include "overlay/window.h"
#include <map>
#include <functional>
#include <filesystem>
#include <optional>
#include "external/rapidjson/document.h"
namespace overlay::windows {
@@ -8,6 +13,8 @@ namespace overlay::windows {
Unknown,
Memory,
Signature,
Union,
Integer,
};
enum class PatchStatus {
@@ -16,6 +23,14 @@ namespace overlay::windows {
Enabled,
};
enum class PatchUrlStatus {
Valid,
Invalid,
Unapplied,
ValidButNoData,
Partial,
};
struct MemoryPatch {
std::string dll_name = "";
std::shared_ptr<uint8_t[]> data_disabled = nullptr;
@@ -31,25 +46,59 @@ namespace overlay::windows {
struct SignaturePatch {
std::string dll_name = "";
std::string signature = "", replacement = "";
int64_t offset = 0, usage = 0;
uint64_t offset = 0;
int64_t usage = 0;
MemoryPatch to_memory(PatchData *patch);
};
struct UnionPatch {
std::string name = "";
std::string dll_name = "";
std::shared_ptr<uint8_t[]> data = nullptr;
size_t data_len = 0;
uint64_t offset = 0;
uint8_t* data_offset_ptr = nullptr;
bool fatal_error = false;
};
struct NumberPatch {
std::string dll_name = "";
uint64_t data_offset = 0;
uint8_t* data_offset_ptr = nullptr;
int32_t min;
int32_t max;
int32_t value;
size_t size_in_bytes;
bool fatal_error = false;
};
struct PatchData {
bool enabled;
std::string game_code;
int datecode_min, datecode_max;
std::string name, description;
int datecode_min = 0;
int datecode_max = 0;
std::string name, description, caution;
std::string name_in_lower_case = "";
PatchType type;
bool preset;
std::vector<MemoryPatch> patches_memory;
std::vector<UnionPatch> patches_union;
NumberPatch patch_number;
PatchStatus last_status;
bool saved;
std::string hash;
bool unverified = false;
std::string peIdentifier;
std::string error_reason = "";
// for union patch only
std::string selected_union_name = "";
};
extern std::optional<std::string> PATCH_MANAGER_CFG_PATH_OVERRIDE;
std::string get_game_identifier(const std::filesystem::path& dll_path);
class PatchManager : public Window {
public:
@@ -57,27 +106,67 @@ namespace overlay::windows {
~PatchManager() override;
void build_content() override;
void reload_patches(bool apply_patches = false);
void reload_local_patches(bool apply_patches = false);
bool import_remote_patches_to_disk();
bool load_from_patches_json(bool apply_patches);
bool import_remote_patches_for_dll(const std::string& url, const std::string& dll_name);
void hard_apply_patches();
void load_embedded_patches(bool apply_patches);
private:
// configuration
static std::string config_path;
static std::filesystem::path config_path;
static bool config_dirty;
static bool setting_auto_apply;
static std::vector<std::string> setting_auto_apply_list;
static std::vector<std::string> setting_patches_enabled;
static std::map<std::string, std::string> setting_union_patches_enabled;
static std::map<std::string, int64_t> setting_int_patches_enabled;
static std::string patch_url;
static std::string patch_name_filter;
static std::filesystem::path LOCAL_PATCHES_PATH;
static std::string ACTIVE_JSON_FILE;
// patches
static std::vector<PatchData> patches;
static bool patches_initialized;
static bool local_patches_initialized;
void config_load();
void config_save();
void append_patches(std::string &patches_json, bool apply_patches = false);
void append_patches(
std::string &patches_json,
bool apply_patches = false,
std::function<bool(const PatchData&)> filter = std::function<bool(const PatchData&)>(),
std::string pe_identifier_for_patch = "");
void show_patch_tooltip(const PatchData& patch);
};
PatchStatus is_patch_active(PatchData &patch);
bool apply_patch(PatchData &patch, bool active);
int64_t parse_little_endian_int(uint8_t* bytes, size_t size);
void int_to_little_endian_bytes(int64_t value, uint8_t* bytes, size_t size);
std::vector<uint8_t>* find_in_dll_map(
const std::string& dll_name, size_t offset, size_t size);
std::vector<uint8_t>* find_in_dll_map_org(
const std::string& dll_name, size_t offset, size_t size);
bool restore_bytes_from_dll_map_org(
uint8_t* destination, const std::string& dll_name, size_t offset, size_t size);
void create_dll_backup(
std::vector<std::string>& written_list, const std::filesystem::path& dll_path);
std::string fix_up_dll_name(const std::string& dll_name);
uint8_t* get_dll_offset_for_patch_apply(
const std::string& dll_name, const uint64_t data_offset, const size_t size_in_bytes);
uint64_t parse_json_data_offset(
const std::string &patch_name, const rapidjson::Value &value);
void print_auto_apply_status(PatchData &patch);
}
+117 -36
View File
@@ -17,6 +17,10 @@ namespace overlay::windows {
this->init_pos = ImVec2(10, 10);
this->toggle_button = games::OverlayButtons::ToggleScreenResize;
this->toggle_screen_resize = games::OverlayButtons::ScreenResize;
this->toggle_scene[0] = games::OverlayButtons::ScreenResizeScene1;
this->toggle_scene[1] = games::OverlayButtons::ScreenResizeScene2;
this->toggle_scene[2] = games::OverlayButtons::ScreenResizeScene3;
this->toggle_scene[3] = games::OverlayButtons::ScreenResizeScene4;
}
ScreenResize::~ScreenResize() {
@@ -42,13 +46,16 @@ namespace overlay::windows {
void ScreenResize::reset_vars_to_default() {
cfg::SCREENRESIZE->enable_screen_resize = false;
cfg::SCREENRESIZE->screen_resize_current_scene = 0;
cfg::SCREENRESIZE->enable_linear_filter = true;
cfg::SCREENRESIZE->keep_aspect_ratio = true;
cfg::SCREENRESIZE->centered = true;
cfg::SCREENRESIZE->offset_x = 0;
cfg::SCREENRESIZE->offset_y = 0;
cfg::SCREENRESIZE->scale_x = 1.f;
cfg::SCREENRESIZE->scale_y = 1.f;
for (size_t i = 0; i < std::size(cfg::SCREENRESIZE->scene_settings); i++) {
auto& scene = cfg::SCREENRESIZE->scene_settings[i];
scene.keep_aspect_ratio = true;
scene.offset_x = 0;
scene.offset_y = 0;
scene.scale_x = 1.f;
scene.scale_y = 1.f;
}
cfg::SCREENRESIZE->enable_window_resize = false;
cfg::SCREENRESIZE->window_always_on_top = false;
@@ -63,11 +70,7 @@ namespace overlay::windows {
void ScreenResize::build_content() {
ImGui::Text("For: %s", eamuse_get_game().c_str());
{
int flags = 0;
if (!GRAPHICS_WINDOWED || cfg::SCREENRESIZE->enable_screen_resize) {
flags |= ImGuiTreeNodeFlags_DefaultOpen;
}
if (ImGui::TreeNodeEx("Image Resize", flags)) {
if (ImGui::TreeNodeEx("Image Resize", ImGuiTreeNodeFlags_DefaultOpen)) {
this->build_fullscreen_config();
ImGui::TreePop();
}
@@ -93,25 +96,57 @@ namespace overlay::windows {
void ScreenResize::build_fullscreen_config() {
// enable checkbox
ImGui::Checkbox("Enable", &cfg::SCREENRESIZE->enable_screen_resize);
ImGui::SameLine();
ImGui::HelpMarker("Hint: bind a key to Screen Resize for a quick toggle.");
ImGui::BeginDisabled(!cfg::SCREENRESIZE->enable_screen_resize);
ImGui::Checkbox("Linear Filter", &cfg::SCREENRESIZE->enable_linear_filter);
if (ImGui::RadioButton("Scene 1", cfg::SCREENRESIZE->screen_resize_current_scene == 0)) {
cfg::SCREENRESIZE->screen_resize_current_scene = 0;
}
ImGui::SameLine();
if (ImGui::RadioButton("Scene 2", cfg::SCREENRESIZE->screen_resize_current_scene == 1)) {
cfg::SCREENRESIZE->screen_resize_current_scene = 1;
}
ImGui::SameLine();
if (ImGui::RadioButton("Scene 3", cfg::SCREENRESIZE->screen_resize_current_scene == 2)) {
cfg::SCREENRESIZE->screen_resize_current_scene = 2;
}
ImGui::SameLine();
if (ImGui::RadioButton("Scene 4", cfg::SCREENRESIZE->screen_resize_current_scene == 3)) {
cfg::SCREENRESIZE->screen_resize_current_scene = 3;
}
ImGui::SameLine();
ImGui::HelpMarker(
"Hint: bind a key to Screen Resize 1/2/3/4 for quick scene switching. "
"Scene 1 is the default scene activated when starting the game.");
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
// general settings
ImGui::Checkbox("Linear Filter", &cfg::SCREENRESIZE->enable_linear_filter);
ImGui::Checkbox("Centered", &cfg::SCREENRESIZE->centered);
if (!cfg::SCREENRESIZE->centered) {
ImGui::InputInt("X Offset", &cfg::SCREENRESIZE->offset_x);
ImGui::InputInt("Y Offset", &cfg::SCREENRESIZE->offset_y);
}
ImGui::InputInt("X Offset", &scene.offset_x);
ImGui::SameLine();
ImGui::HelpMarker("Hint: ctrl + click on +/- buttons to move quickly.");
ImGui::InputInt("Y Offset", &scene.offset_y);
ImGui::SameLine();
ImGui::HelpMarker("Hint: ctrl + click on +/- buttons to move quickly.");
// aspect ratio
ImGui::Checkbox("Keep Aspect Ratio", &cfg::SCREENRESIZE->keep_aspect_ratio);
if (cfg::SCREENRESIZE->keep_aspect_ratio) {
if (ImGui::SliderFloat("Scale", &cfg::SCREENRESIZE->scale_x, 0.65f, 2.0f)) {
cfg::SCREENRESIZE->scale_y = cfg::SCREENRESIZE->scale_x;
ImGui::Checkbox("Keep Aspect Ratio", &scene.keep_aspect_ratio);
if (scene.keep_aspect_ratio) {
if (ImGui::SliderFloat("Scale", &scene.scale_x, 0.5f, 2.5f)) {
scene.scale_y = scene.scale_x;
}
ImGui::SameLine();
ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value.");
} else {
ImGui::SliderFloat("Width Scale", &cfg::SCREENRESIZE->scale_x, 0.65f, 2.0f);
ImGui::SliderFloat("Height Scale", &cfg::SCREENRESIZE->scale_y, 0.65f, 2.0f);
ImGui::SliderFloat("Width Scale", &scene.scale_x, 0.5f, 2.5f);
ImGui::SameLine();
ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value.");
ImGui::SliderFloat("Height Scale", &scene.scale_y, 0.5f, 2.5f);
ImGui::SameLine();
ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value.");
}
ImGui::EndDisabled();
@@ -141,6 +176,15 @@ namespace overlay::windows {
if (ImGui::Checkbox("Always on Top", &cfg::SCREENRESIZE->window_always_on_top) ) {
graphics_update_z_order(window);
}
ImGui::BeginDisabled();
ImGui::Checkbox("Forced Render Scaling", &GRAPHICS_WINDOW_BACKBUFFER_SCALE);
ImGui::EndDisabled();
ImGui::SameLine();
ImGui::HelpMarker(
"For windowed mode: forcibly set DX9 back buffer dimensions to match window size. "
"Reduces pixelated scaling artifacts. Works great on some games, but completely broken on others.\n\n"
"This can't be changed in-game; instead, set -windowscale option in spicecfg and restart.");
ImGui::Checkbox("Keep Aspect Ratio", &cfg::SCREENRESIZE->client_keep_aspect_ratio);
ImGui::Checkbox("Manual window move/resize", &cfg::SCREENRESIZE->enable_window_resize);
ImGui::BeginDisabled(!cfg::SCREENRESIZE->enable_window_resize);
@@ -149,31 +193,35 @@ namespace overlay::windows {
const uint32_t step = 1;
const uint32_t step_fast = 10;
ImGui::BeginDisabled(cfg::SCREENRESIZE->client_keep_aspect_ratio);
changed |= ImGui::InputScalar(
ImGui::InputScalar(
"Width",
ImGuiDataType_U32,
&cfg::SCREENRESIZE->client_width,
&step, &step_fast, nullptr,
ImGuiInputTextFlags_EnterReturnsTrue);
&step, &step_fast, nullptr);
changed |= ImGui::IsItemDeactivatedAfterEdit();
ImGui::EndDisabled();
changed |= ImGui::InputScalar(
ImGui::InputScalar(
"Height",
ImGuiDataType_U32,
&cfg::SCREENRESIZE->client_height,
&step, &step_fast, nullptr,
ImGuiInputTextFlags_EnterReturnsTrue);
changed |= ImGui::InputScalar(
&step, &step_fast, nullptr);
changed |= ImGui::IsItemDeactivatedAfterEdit();
ImGui::InputScalar(
"X Offset",
ImGuiDataType_S32,
&cfg::SCREENRESIZE->window_offset_x,
&step, &step_fast, nullptr,
ImGuiInputTextFlags_EnterReturnsTrue);
changed |= ImGui::InputScalar(
&step, &step_fast, nullptr);
changed |= ImGui::IsItemDeactivatedAfterEdit();
ImGui::InputScalar(
"Y Offset",
ImGuiDataType_S32,
&cfg::SCREENRESIZE->window_offset_y,
&step, &step_fast, nullptr,
ImGuiInputTextFlags_EnterReturnsTrue);
&step, &step_fast, nullptr);
changed |= ImGui::IsItemDeactivatedAfterEdit();
if (changed) {
if (cfg::SCREENRESIZE->client_keep_aspect_ratio) {
cfg::SCREENRESIZE->client_width =
@@ -206,8 +254,10 @@ namespace overlay::windows {
void ScreenResize::update() {
Window::update();
auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game());
// toggle
if (this->toggle_screen_resize != ~0u) {
auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game());
bool toggle_screen_resize_new = overlay_buttons
&& this->overlay->hotkeys_triggered()
&& GameAPI::Buttons::getState(RI_MGR, overlay_buttons->at(this->toggle_screen_resize));
@@ -217,5 +267,36 @@ namespace overlay::windows {
}
this->toggle_screen_resize_state = toggle_screen_resize_new;
}
// scene switch
auto toggle_scene_state_new = ~0u;
for (size_t i = 0; i < std::size(this->toggle_scene); i++) {
if (this->toggle_scene[i] == ~0u) {
continue;
}
bool scene_switched = overlay_buttons
&& this->overlay->hotkeys_triggered()
&& GameAPI::Buttons::getState(RI_MGR, overlay_buttons->at(this->toggle_scene[i]));
if (scene_switched) {
toggle_scene_state_new = (uint32_t)i;
}
// only detect rising edges of key presses
if (scene_switched && (this->toggle_scene_state != i)) {
if (cfg::SCREENRESIZE->screen_resize_current_scene == (int8_t)i &&
cfg::SCREENRESIZE->enable_screen_resize) {
// this scene is already active, turn scaling off
cfg::SCREENRESIZE->enable_screen_resize = false;
} else {
// switch to scene
cfg::SCREENRESIZE->enable_screen_resize = true;
cfg::SCREENRESIZE->screen_resize_current_scene = i;
}
break;
}
}
// remember if a key was pressed (or nothing pressed) this frame
this->toggle_scene_state = toggle_scene_state_new;
}
}
+3
View File
@@ -16,6 +16,9 @@ namespace overlay::windows {
size_t toggle_screen_resize = ~0u;
bool toggle_screen_resize_state = false;
size_t toggle_scene[4] = { ~0u, ~0u, ~0u, ~0u };
uint32_t toggle_scene_state = ~0u;
void build_fullscreen_config();
void build_windowed_config();
void build_footer();
+39 -2
View File
@@ -1,20 +1,57 @@
#undef CINTERFACE
#include "avs/game.h"
#include "sdvx_sub.h"
#include "games/sdvx/sdvx.h"
#include "hooks/graphics/graphics.h"
namespace overlay::windows {
SDVXSubScreen::SDVXSubScreen(SpiceOverlay *overlay) : GenericSubScreen(overlay) {
this->title = "SDVX Sub Screen";
bool isValkyrieCabinetMode = avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H';
if (!isValkyrieCabinetMode) {
this->disabled_message = "Valkyrie Model mode is not enabled!";
} else if (GRAPHICS_WINDOWED) {
if (GRAPHICS_PREVENT_SECONDARY_WINDOW) {
this->disabled_message = "Subscreen has been disabled by the user (-sdvxnosub).";
} else {
this->disabled_message = "Overlay unavailable in windowed mode! Use the second window instead.";
}
}
const auto padding = ImGui::GetFrameHeight() / 2;
this->init_size = ImVec2(ImGui::GetIO().DisplaySize.x - (padding * 2), 0.f);
this->init_size.y = (this->init_size.x * 9 / 16) + ImGui::GetFrameHeight();
switch (games::sdvx::OVERLAY_POS) {
case games::sdvx::SDVX_OVERLAY_BOTTOM_LEFT:
case games::sdvx::SDVX_OVERLAY_BOTTOM_RIGHT:
this->init_size.x = (ImGui::GetIO().DisplaySize.x - (ImGui::GetIO().DisplaySize.y * 9 / 16)) / 2 - padding;
this->init_size.y = (this->init_size.x * 9 / 16) + ImGui::GetFrameHeight();
break;
case games::sdvx::SDVX_OVERLAY_TOP:
case games::sdvx::SDVX_OVERLAY_BOTTOM:
case games::sdvx::SDVX_OVERLAY_MIDDLE:
default:
this->init_size = ImVec2(ImGui::GetIO().DisplaySize.x - (padding * 2), 0.f);
this->init_size.y = (this->init_size.x * 9 / 16) + ImGui::GetFrameHeight();
if (GRAPHICS_FS_ORIENTATION_SWAP) {
this->init_size.x /= 2;
this->init_size.y /= 2;
}
break;
}
this->init_pos = ImVec2(ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2, 0);
switch (games::sdvx::OVERLAY_POS) {
case games::sdvx::SDVX_OVERLAY_BOTTOM_LEFT:
this->init_pos.x = 0;
this->init_pos.y = ImGui::GetIO().DisplaySize.y - this->init_size.y;
break;
case games::sdvx::SDVX_OVERLAY_BOTTOM_RIGHT:
this->init_pos.x = ImGui::GetIO().DisplaySize.x - this->init_size.x;
this->init_pos.y = ImGui::GetIO().DisplaySize.y - this->init_size.y;
break;
case games::sdvx::SDVX_OVERLAY_TOP:
this->init_pos.y = padding;
break;
-218
View File
@@ -1,218 +0,0 @@
#include "vr.h"
#include "misc/vrutil.h"
#include "util/logging.h"
#include "games/io.h"
#include "games/drs/drs.h"
#include "avs/game.h"
#include "overlay/imgui/extensions.h"
namespace overlay::windows {
VRWindow::VRWindow(SpiceOverlay *overlay) : Window(overlay) {
this->title = "VR";
this->flags = ImGuiWindowFlags_None;
this->toggle_button = games::OverlayButtons::ToggleVRControl;
this->init_size = ImVec2(500, 800);
this->size_min = ImVec2(250, 200);
}
VRWindow::~VRWindow() {
}
void VRWindow::build_content() {
ImGui::BeginTabBar("VRTabBar");
if (ImGui::BeginTabItem("Info")) {
build_info();
ImGui::EndTabItem();
}
if (avs::game::is_model("REC")) {
if (ImGui::BeginTabItem("Dancefloor")) {
build_dancefloor();
}
}
}
void VRWindow::build_info() {
// status
auto status = vrutil::STATUS;
switch (status) {
case vrutil::VRStatus::Disabled:
ImGui::TextColored(
ImVec4(0.4f, 0.4f, 0.4f, 1.f),
"Disabled");
if (ImGui::Button("Start")) {
vrutil::init();
if (avs::game::is_model("REC")) {
games::drs::start_vr();
}
}
break;
case vrutil::VRStatus::Error:
ImGui::TextColored(
ImVec4(0.8f, 0.1f, 0.1f, 1.f),
"Error");
if (ImGui::Button("Restart")) {
vrutil::shutdown();
vrutil::init();
}
break;
case vrutil::VRStatus::Running:
ImGui::TextColored(
ImVec4(0.1f, 0.8f, 0.1f, 1.f),
"Running");
if (ImGui::Button("Stop")) {
vrutil::shutdown();
}
break;
}
// rescan
if (ImGui::Button("Rescan Devices")) {
vrutil::scan(true);
}
// data table header
ImGui::Columns(2);
ImGui::Text("Device");
ImGui::NextColumn();
ImGui::Text("Position");
ImGui::NextColumn();
ImGui::Separator();
// HMD/Left/Right data
vr::TrackedDevicePose_t hmd_pose, left_pose, right_pose;
vr::VRControllerState_t left_state, right_state;
vrutil::get_hmd_pose(&hmd_pose);
vrutil::get_con_pose(vrutil::INDEX_LEFT, &left_pose, &left_state);
vrutil::get_con_pose(vrutil::INDEX_RIGHT, &right_pose, &right_state);
auto hmd_pos = vrutil::get_translation(hmd_pose.mDeviceToAbsoluteTracking);
auto left_pos = vrutil::get_translation(left_pose.mDeviceToAbsoluteTracking);
auto right_pos = vrutil::get_translation(right_pose.mDeviceToAbsoluteTracking);
ImGui::Text("HMD");
ImGui::NextColumn();
ImGui::TextUnformatted(fmt::format(
"X={:3f} Y={:3f} Z={:3f}",
hmd_pos.x, hmd_pos.y, hmd_pos.z).c_str());
ImGui::NextColumn();
ImGui::Text("Left");
ImGui::NextColumn();
ImGui::TextUnformatted(fmt::format(
"X={:3f} Y={:3f} Z={:3f}",
left_pos.x, left_pos.y, left_pos.z).c_str());
ImGui::NextColumn();
ImGui::Text("Right");
ImGui::NextColumn();
ImGui::TextUnformatted(fmt::format(
"X={:3f} Y={:3f} Z={:3f}",
right_pos.x, right_pos.y, right_pos.z).c_str());
ImGui::NextColumn();
}
void VRWindow::build_dancefloor() {
ImGui::Separator();
// settings
ImGui::DragFloat3("Scale", &games::drs::VR_SCALE[0], 0.1f);
ImGui::DragFloat3("Offset", &games::drs::VR_OFFSET[0], 0.1f);
ImGui::DragFloat("Rotation", &games::drs::VR_ROTATION, 0.5f);
for (int i = 0; i < (int) std::size(games::drs::VR_FOOTS); ++i) {
auto &foot = games::drs::VR_FOOTS[i];
ImGui::Separator();
ImGui::PushID(&foot);
ImGui::Text("%s Foot", i == 0 ? "Left" : "Right");
ImGui::InputInt("Device Index", (int*) &foot.index, 1, 1);
ImGui::DragFloat("Length", &foot.length,
0.005f, 0.001f, 1000.f);
ImGui::DragFloat("Size Base", &foot.size_base,
0.005f, 0.001f, 1000.f);
ImGui::DragFloat("Size Scale", &foot.size_scale,
0.005f, 0.001f, 1000.f);
ImGui::DragFloat4("Rotation Quat", &foot.rotation.x, 0.001f, -1, 1);
if (ImGui::Button("Calibrate")) {
vr::TrackedDevicePose_t pose;
vr::VRControllerState_t state;
vrutil::get_con_pose(foot.get_index(), &pose, &state);
foot.length = foot.height + 0.02f;
auto pose_rot = vrutil::get_rotation(pose.mDeviceToAbsoluteTracking.m);
foot.rotation = linalg::qmul(linalg::qinv(pose_rot),
vrutil::get_rotation((float) M_PI * -0.5f, 0, 0));
}
ImGui::SameLine();
ImGui::HelpMarker("Place the controller to the lower part of your leg "
"and press this button to auto calibrate angle and length");
ImGui::PopID();
}
// prepare view
auto draw_list = ImGui::GetWindowDrawList();
auto canvas_pos = ImGui::GetCursorScreenPos();
auto canvas_size = ImGui::GetContentRegionAvail();
float offset_x = canvas_size.x * 0.5f;
float offset_y = canvas_size.y * 0.1f;
float off_x = offset_x + canvas_pos.x;
float off_y = offset_y + canvas_pos.y;
float scale = std::min(canvas_size.x, canvas_size.y) / 60;
// axis
draw_list->AddLine(
ImVec2(canvas_pos.x, off_y),
ImVec2(canvas_pos.x + canvas_size.x, off_y),
ImColor(255, 0, 0, 128));
draw_list->AddLine(
ImVec2(off_x, canvas_pos.y),
ImVec2(off_x, canvas_pos.y + canvas_size.y),
ImColor(0, 255, 0, 128));
// tiles
for (int x = 0; x < 38; x++) {
for (int y = 0; y < 49; y++) {
auto &led = games::drs::DRS_TAPELED[x + y * 38];
ImColor color((int) led[0], (int) led[1], (int) led[2]);
ImVec2 p1((x - 19) * scale + off_x, (y + 0) * scale + off_y);
ImVec2 p2((x - 18) * scale + off_x, (y + 1) * scale + off_y);
draw_list->AddRectFilled(p1, p2, color, 0.f);
}
}
// foots
const float foot_box = 2.f * scale;
for (auto &foot : games::drs::VR_FOOTS) {
vr::TrackedDevicePose_t pose;
vr::VRControllerState_t state;
vrutil::get_con_pose(foot.get_index(), &pose, &state);
if (pose.bPoseIsValid) {
// position
auto pos = vrutil::get_translation(pose.mDeviceToAbsoluteTracking);
pos = foot.to_world(pos);
pos.x -= 19;
pos *= scale;
ImColor color(255, 0, 255);
if (foot.event.type == games::drs::DRS_DOWN
|| (foot.event.type == games::drs::DRS_MOVE)) {
auto size_factor = foot.event.width / (foot.size_base + foot.size_scale);
color = ImColor((int) (size_factor * 127) + 128, 0, 0);
}
ImVec2 p1(pos.x + off_x - foot_box * 0.5f,
pos.y + off_y - foot_box * 0.5f);
ImVec2 p2(pos.x + off_x + foot_box * 0.5f,
pos.y + off_y + foot_box * 0.5f);
draw_list->AddRectFilled(p1, p2, color, 0.f);
// direction
auto direction = -linalg::qzdir(linalg::qmul(
vrutil::get_rotation(pose.mDeviceToAbsoluteTracking.m),
foot.rotation));
direction = linalg::aliases::float3 {
-direction.z, direction.x, direction.y
};
auto end = pos + direction * foot.length * scale;
draw_list->AddLine(
ImVec2(pos.x + off_x, pos.y + off_y),
ImVec2(end.x + off_x, end.y + off_y),
ImColor(0, 255, 0));
}
}
}
}
-17
View File
@@ -1,17 +0,0 @@
#pragma once
#include "overlay/window.h"
namespace overlay::windows {
class VRWindow : public Window {
public:
VRWindow(SpiceOverlay *overlay);
~VRWindow() override;
void build_content() override;
void build_info();
void build_dancefloor();
};
}