From 63e92536de1765b110e28c3f6024bc02a9c67f83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reimar=20D=C3=B6ffinger?= Date: Sat, 4 Jun 2016 22:05:36 +0200 Subject: [PATCH] Add experimental support for emulating joystick. --- impl11/Makefile | 2 +- impl11/ddraw/config.cpp | 75 ++++++++++++++++++ impl11/ddraw/config.h | 3 + impl11/ddraw/ddraw.cfg | 18 +++++ impl11/ddraw/ddraw.cpp | 2 + impl11/ddraw/ddraw.rc | 8 +- impl11/ddraw/ddraw.vcxproj | 2 + impl11/ddraw/ddraw.vcxproj.filters | 6 ++ impl11/ddraw/joystick.cpp | 121 +++++++++++++++++++++++++++++ impl11/ddraw/joystick.h | 10 +++ 10 files changed, 242 insertions(+), 5 deletions(-) create mode 100755 impl11/ddraw/joystick.cpp create mode 100755 impl11/ddraw/joystick.h diff --git a/impl11/Makefile b/impl11/Makefile index 371fdba3..9d82b49f 100644 --- a/impl11/Makefile +++ b/impl11/Makefile @@ -17,7 +17,7 @@ all: ddraw.dll i686-w64-mingw32-windres -o $@ -i $< ddraw.dll: ddraw/*.cpp ddraw/ddraw.rc.o ddraw/ddraw.def - i686-w64-mingw32-g++ $(WARN_FLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ $^ -lgdiplus -ld3d11 -ldxgi -ldxguid + i686-w64-mingw32-g++ $(WARN_FLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ $^ -lgdiplus -ld3d11 -ldxgi -ldxguid -lwinmm clean: rm -f ddraw.dll ddraw/ddraw.rc.o xwing_ddraw_d3d11_*.zip diff --git a/impl11/ddraw/config.cpp b/impl11/ddraw/config.cpp index b0f77d8f..67628bb4 100644 --- a/impl11/ddraw/config.cpp +++ b/impl11/ddraw/config.cpp @@ -9,6 +9,8 @@ #include #include +#include "joystick.h" + // Wrapper to avoid undefined behaviour due to // sign-extending char to int when passing to isspace. // That would be undefined behaviour and crashes with table @@ -33,6 +35,8 @@ Config::Config() this->Fullscreen = 0; this->Width = 0; this->Height = 0; + this->MouseSensitivity = 0.5f; + this->KbdSensitivity = 1.0f; this->XWAMode = true; int XWAModeInt = -1; int ProcessAffinity = -1; @@ -126,6 +130,18 @@ Config::Config() { this->Height = stoi(value); } + else if (name == "JoystickEmul") + { + this->JoystickEmul = stoi(value); + } + else if (name == "MouseSensitivity") + { + this->MouseSensitivity = stof(value); + } + else if (name == "KbdSensitivity") + { + this->KbdSensitivity = stof(value); + } else if (name == "XWAMode") { XWAModeInt = stoi(value); @@ -155,6 +171,10 @@ Config::Config() bool isXWA = len >= 17 && _stricmp(name + len - 17, "xwingalliance.exe") == 0; bool isXWing = len >= 11 && _stricmp(name + len - 11, "xwing95.exe") == 0; bool isTIE = len >= 9 && _stricmp(name + len - 9, "tie95.exe") == 0; + bool isXvT = len >= 11 && _stricmp(name + len - 11, "z_xvt__.exe") == 0 && + *(const unsigned long long *)0x523aec == 0x54534c2e31505352ull; + bool isBoP = len >= 11 && _stricmp(name + len - 11, "z_xvt__.exe") == 0 && + *(const unsigned long long *)0x5210bc == 0x54534c2e31505352ull; if (XWAModeInt == -1) { @@ -193,6 +213,61 @@ Config::Config() *(unsigned long long *)0x4f084c = 0x00007d00fa000400ull; } + if (this->JoystickEmul != 0 && isXWing) + { + // TODO: How to check if this is a supported binary? + DWORD old, dummy; + VirtualProtect((void *)0x4c31f0, 0x4c3208 - 0x4c31f0, PAGE_READWRITE, &old); + *(unsigned *)0x4c31f0 = reinterpret_cast(emulJoyGetDevCaps); + *(unsigned *)0x4c3200 = reinterpret_cast(emulJoyGetPosEx); + *(unsigned *)0x4c3204 = reinterpret_cast(emulJoyGetNumDevs); + VirtualProtect((void *)0x4c31f0, 0x4c3208 - 0x4c31f0, old, &dummy); + } + + if (this->JoystickEmul != 0 && isTIE) + { + // TODO: How to check if this is a supported binary? + DWORD old, dummy; + VirtualProtect((void *)0x4dc258, 0x4dc264 - 0x4dc258, PAGE_READWRITE, &old); + *(unsigned *)0x4dc258 = reinterpret_cast(emulJoyGetNumDevs); + *(unsigned *)0x4dc25c = reinterpret_cast(emulJoyGetDevCaps); + *(unsigned *)0x4dc260 = reinterpret_cast(emulJoyGetPosEx); + VirtualProtect((void *)0x4dc258, 0x4dc264 - 0x4dc258, old, &dummy); + } + + if (this->JoystickEmul != 0 && isXvT) + { + // TODO: How to check if this is a supported binary? + DWORD old, dummy; + VirtualProtect((void *)0x860704, 0x860714 - 0x860704, PAGE_READWRITE, &old); + *(unsigned *)0x860704 = reinterpret_cast(emulJoyGetDevCaps); + *(unsigned *)0x860708 = reinterpret_cast(emulJoyGetNumDevs); + *(unsigned *)0x860710 = reinterpret_cast(emulJoyGetPosEx); + VirtualProtect((void *)0x860704, 0x860714 - 0x860704, old, &dummy); + } + + if (this->JoystickEmul != 0 && isBoP) + { + // TODO: How to check if this is a supported binary? + DWORD old, dummy; + VirtualProtect((void *)0xbb969c, 0xbb96b0 - 0xbb969c, PAGE_READWRITE, &old); + *(unsigned *)0xbb969c = reinterpret_cast(emulJoyGetNumDevs); + *(unsigned *)0xbb96a8 = reinterpret_cast(emulJoyGetPosEx); + *(unsigned *)0xbb96ac = reinterpret_cast(emulJoyGetDevCaps); + VirtualProtect((void *)0xbb969c, 0xbb96b0 - 0xbb969c, old, &dummy); + } + + if (this->JoystickEmul != 0 && isXWA) + { + // TODO: How to check if this is a supported binary? + DWORD old, dummy; + VirtualProtect((void *)0x5a92a4, 0x5a92b8 - 0x5a92a4, PAGE_READWRITE, &old); + *(unsigned *)0x5a92a4 = reinterpret_cast(emulJoyGetPosEx); + *(unsigned *)0x5a92a8 = reinterpret_cast(emulJoyGetDevCaps); + *(unsigned *)0x5a92b4 = reinterpret_cast(emulJoyGetNumDevs); + VirtualProtect((void *)0x5a92a4, 0x5a92b8 - 0x5a92a4, old, &dummy); + } + if (ProcessAffinity != 0) { DWORD_PTR CurProcessAffinity, SystemAffinity; HANDLE mod = GetCurrentProcess(); diff --git a/impl11/ddraw/config.h b/impl11/ddraw/config.h index 5e04a738..8ef0e2af 100644 --- a/impl11/ddraw/config.h +++ b/impl11/ddraw/config.h @@ -18,6 +18,9 @@ class Config int Fullscreen; int Width; int Height; + int JoystickEmul; + float MouseSensitivity; + float KbdSensitivity; bool XWAMode; float Concourse3DScale; diff --git a/impl11/ddraw/ddraw.cfg b/impl11/ddraw/ddraw.cfg index 0da2355e..541a33b3 100644 --- a/impl11/ddraw/ddraw.cfg +++ b/impl11/ddraw/ddraw.cfg @@ -54,6 +54,24 @@ Fullscreen = 0 Width = 0 Height = 0 +; JoystickEmul = -1 (default, auto-detect) or 1 (force on) or 0 (force off) +; EXPERIMENTAL - behaviour is far from optimal. +; Emulates a joystick using keyboard and mouse. +; For auto-detect only enabled if no joystick is detected. +; It is not possible to use a joystick that is plugged in after start. +; Force off to get back the dialog boxes asking you to plug in the joystick. +JoystickEmul = -1 + +; MouseSensitivity = 0.5 (default) +; How sensitive the mouse is if joystick emulation is enabled. +; Maximum turn speed might only be possible for values >= 1.1 +MouseSensitivity = 0.5 + +; KbdSensitivity = 1.0 (default) +; How sensitive the keyboard is if joystick emulation is enabled. +; Maximum turn speed for 1.0, values above are not useful. +KbdSensitivity = 1.0 + ; ProcessAffinity = -1 (default), 0 (do not change) or mask ; Sets which processors to allow running on ; When set to -1 (or < 0 in general) tries to auto-detect (currently picks second processor) diff --git a/impl11/ddraw/ddraw.cpp b/impl11/ddraw/ddraw.cpp index 6ad52583..3537ebf9 100644 --- a/impl11/ddraw/ddraw.cpp +++ b/impl11/ddraw/ddraw.cpp @@ -5,6 +5,8 @@ #include "DeviceResources.h" #include "DirectDraw.h" +#include "joystick.h" + #pragma comment(lib, "dxguid") #pragma comment(lib, "dxgi") #pragma comment(lib, "d3d11") diff --git a/impl11/ddraw/ddraw.rc b/impl11/ddraw/ddraw.rc index 31877a3f..c5ac1529 100644 --- a/impl11/ddraw/ddraw.rc +++ b/impl11/ddraw/ddraw.rc @@ -48,8 +48,8 @@ END // 1 VERSIONINFO - FILEVERSION 1,4,5 - PRODUCTVERSION 1,4,5 + FILEVERSION 1,4,6 + PRODUCTVERSION 1,4,6 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -66,12 +66,12 @@ BEGIN BEGIN VALUE "CompanyName", "Jérémy Ansel" VALUE "FileDescription", "Direct3D 11 implementation for X-Wing series" - VALUE "FileVersion", "1.4.5" + VALUE "FileVersion", "1.4.6" VALUE "InternalName", "ddraw.dll" VALUE "LegalCopyright", "Copyright (C) Jérémy Ansel 2014, Reimar Döffinger 2015,2016" VALUE "OriginalFilename", "ddraw.dll" VALUE "ProductName", "xwa_ddraw_d3d11" - VALUE "ProductVersion", "1.4.5" + VALUE "ProductVersion", "1.4.6" END END BLOCK "VarFileInfo" diff --git a/impl11/ddraw/ddraw.vcxproj b/impl11/ddraw/ddraw.vcxproj index 2bc98762..51ac6593 100644 --- a/impl11/ddraw/ddraw.vcxproj +++ b/impl11/ddraw/ddraw.vcxproj @@ -117,6 +117,7 @@ + @@ -142,6 +143,7 @@ + diff --git a/impl11/ddraw/ddraw.vcxproj.filters b/impl11/ddraw/ddraw.vcxproj.filters index 0fac34b8..f55326f5 100644 --- a/impl11/ddraw/ddraw.vcxproj.filters +++ b/impl11/ddraw/ddraw.vcxproj.filters @@ -90,6 +90,9 @@ Fichiers sources + + Fichiers sources + @@ -167,6 +170,9 @@ Fichiers d%27en-tête + + Fichiers d%27en-tête + diff --git a/impl11/ddraw/joystick.cpp b/impl11/ddraw/joystick.cpp new file mode 100755 index 00000000..4554ec6e --- /dev/null +++ b/impl11/ddraw/joystick.cpp @@ -0,0 +1,121 @@ +// Copyright (c) 2016 Reimar Döffinger +// Licensed under the MIT license. See LICENSE.txt + +#include "config.h" +#include "common.h" + +#include + +#include "joystick.h" + +#pragma comment(lib, "winmm") + +#undef min +#undef max +#include + +static bool needsJoyEmul() +{ + UINT cnt = joyGetNumDevs(); + for (unsigned i = 0; i < cnt; ++i) + { + JOYINFOEX jie; + memset(&jie, 0, sizeof(jie)); + jie.dwSize = sizeof(jie); + jie.dwFlags = JOY_RETURNALL; + UINT res = joyGetPosEx(0, &jie); + if (res == JOYERR_NOERROR) + return false; + } + return true; +} + +UINT WINAPI emulJoyGetNumDevs(void) +{ + if (g_config.JoystickEmul < 0) { + g_config.JoystickEmul = needsJoyEmul(); + } + if (!g_config.JoystickEmul) { + return joyGetNumDevs(); + } + return 1; +} + +UINT WINAPI emulJoyGetDevCaps(UINT_PTR joy, struct tagJOYCAPSA *pjc, UINT size) +{ + if (!g_config.JoystickEmul) { + return joyGetDevCaps(joy, pjc, size); + } + if (joy != 0) return MMSYSERR_NODRIVER; + if (size != 0x194) return MMSYSERR_INVALPARAM; + memset(pjc, 0, size); + pjc->wXmax = 512; + pjc->wYmax = 512; + pjc->wNumButtons = 5; + pjc->wMaxButtons = 5; + pjc->wNumAxes = 2; + pjc->wMaxAxes = 2; + return JOYERR_NOERROR; +} + +static DWORD lastGetPos; + +UINT WINAPI emulJoyGetPosEx(UINT joy, struct joyinfoex_tag *pji) +{ + if (!g_config.JoystickEmul) { + return joyGetPosEx(joy, pji); + } + if (joy != 0) return MMSYSERR_NODRIVER; + if (pji->dwSize != 0x34) return MMSYSERR_INVALPARAM; + DWORD now = GetTickCount(); + // Assume we started a new game + if ((now - lastGetPos) > 5000) + { + SetCursorPos(240, 240); + GetAsyncKeyState(VK_LBUTTON); + GetAsyncKeyState(VK_RBUTTON); + GetAsyncKeyState(VK_MBUTTON); + GetAsyncKeyState(VK_XBUTTON1); + GetAsyncKeyState(VK_XBUTTON2); + } + lastGetPos = now; + POINT pos; + GetCursorPos(&pos); + pji->dwXpos = static_cast(std::min(256.0f + (pos.x - 240.0f) * g_config.MouseSensitivity, 512.0f)); + pji->dwYpos = static_cast(std::min(256.0f + (pos.y - 240.0f) * g_config.MouseSensitivity, 512.0f)); + pji->dwButtons = 0; + pji->dwButtonNumber = 0; + if (GetAsyncKeyState(VK_LBUTTON)) { + pji->dwButtons |= 1; + ++pji->dwButtonNumber; + } + if (GetAsyncKeyState(VK_RBUTTON)) { + pji->dwButtons |= 2; + ++pji->dwButtonNumber; + } + if (GetAsyncKeyState(VK_MBUTTON)) { + pji->dwButtons |= 4; + ++pji->dwButtonNumber; + } + if (GetAsyncKeyState(VK_XBUTTON1)) { + pji->dwButtons |= 8; + ++pji->dwButtonNumber; + } + if (GetAsyncKeyState(VK_XBUTTON2)) { + pji->dwButtons |= 16; + ++pji->dwButtonNumber; + } + if (GetAsyncKeyState(VK_LEFT) & 0x8000) { + pji->dwXpos = static_cast(std::max(256 - 256 * g_config.KbdSensitivity, 0.0f));; + } + if (GetAsyncKeyState(VK_RIGHT) & 0x8000) { + pji->dwXpos = static_cast(std::min(256 + 256 * g_config.KbdSensitivity, 512.0f));; + } + if (GetAsyncKeyState(VK_DOWN) & 0x8000) { + pji->dwYpos = static_cast(std::max(256 - 256 * g_config.KbdSensitivity, 0.0f));; + } + if (GetAsyncKeyState(VK_UP) & 0x8000) { + pji->dwYpos = static_cast(std::min(256 + 256 * g_config.KbdSensitivity, 512.0f));; + } + return JOYERR_NOERROR; +} \ No newline at end of file diff --git a/impl11/ddraw/joystick.h b/impl11/ddraw/joystick.h new file mode 100755 index 00000000..d9dd0c96 --- /dev/null +++ b/impl11/ddraw/joystick.h @@ -0,0 +1,10 @@ +// Copyright (c) 2016 Reimar Döffinger +// Licensed under the MIT license. See LICENSE.txt + +#pragma once + +#include "common.h" + +extern "C" UINT WINAPI emulJoyGetNumDevs(void); +extern "C" UINT WINAPI emulJoyGetDevCaps(UINT_PTR, struct tagJOYCAPSA *, UINT); +extern "C" UINT WINAPI emulJoyGetPosEx(UINT, struct joyinfoex_tag *);