diff --git a/source-code/data/plugins/config.ini b/source-code/data/plugins/config.ini index 72fc273..ca3a630 100644 --- a/source-code/data/plugins/config.ini +++ b/source-code/data/plugins/config.ini @@ -80,8 +80,6 @@ No_Timer_Sprite=1 Unlock_Pseudo=0 #Enable the card menu (incomplete, it doesn't bypass the card prompt) Card=0 -#Allow using a real arcade slider attached to COM11 -Hardware_Slider=0 #Enable custom patches Custom_Patches=1 #Prevent data deletion diff --git a/source-code/data/plugins/keyconfig.ini b/source-code/data/plugins/keyconfig.ini index 70fa507..64226b3 100644 --- a/source-code/data/plugins/keyconfig.ini +++ b/source-code/data/plugins/keyconfig.ini @@ -24,6 +24,16 @@ rumble =true # Pause when focus is lost autopause =true +# Allow using slider in menus +# Recommended to enable this and remove MENU_L/R bindings if you have a real slider. +# Also needed to see slider in input test +slider_in_menus =false + +# Use the hardware slider from HORI FT official controller (PS4 version) +# Warning: alpha feature, may not work properly +# Recommended to remove Ds4_L_Stick_* and Ds4_R_Stick_* from button bindings (JVS_LEFT, JVS_RIGHT, MENU_L, MENU_R). +ps4_official_slider =false + # JVS Buttons: JVS_TEST = F1 JVS_SERVICE = F2 diff --git a/source-code/source/plugins/Launcher/framework.h b/source-code/source/plugins/Launcher/framework.h index 8b5d168..a023bce 100644 --- a/source-code/source/plugins/Launcher/framework.h +++ b/source-code/source/plugins/Launcher/framework.h @@ -223,9 +223,6 @@ ConfigOptionBase* optionsArray[] = { new NumericOption(L"xinput_preferred", KEYCONFIG_SECTION, KEYCONFIG_FILE, L"XInput Controller Num:", L"Sets the preferred XInput controller.\nIf unavailable, the next connected controller is used.", 0, 0, 3), new OptionMetaSpacer(8), - new BooleanOption(L"hardware_slider", PATCHES_SECTION, CONFIG_FILE, L"Use Hardware Slider", L"Enable this if using a real arcade slider.\n(set the slider to port COM11)", false, false), - new OptionMetaSpacer(8), - new BooleanOption(L"opengl_patch_a", LAUNCHER_SECTION, CONFIG_FILE, L"OpenGL Patch A", L"Ignores some OpenGL-related errors. Don't use both patches at the same time unless you're know what you're doing.", false, false), new BooleanOption(L"opengl_patch_b", LAUNCHER_SECTION, CONFIG_FILE, L"OpenGL Patch B", L"Ignores some OpenGL-related errors. Don't use both patches at the same time unless you're know what you're doing.", false, false), new OptionMetaSpacer(8), diff --git a/source-code/source/plugins/Patches/dllmain.cpp b/source-code/source/plugins/Patches/dllmain.cpp index f92f23f..127ec3e 100644 --- a/source-code/source/plugins/Patches/dllmain.cpp +++ b/source-code/source/plugins/Patches/dllmain.cpp @@ -787,12 +787,6 @@ void ApplyPatches() { { InjectCode((void*)0x0000000140565E6B, { 0x90, 0x90 }); } - // The original slider update needs to run for hardware sliders to work -- only patch it when using emulation - if (!nHardwareSlider) - { - // Don't update the touch slider state so we can write our own - InjectCode((void*)0x000000014061579B, { 0x90, 0x90, 0x90, 0x8B, 0x42, 0xE0, 0x90, 0x90, 0x90 }); - } // Quick start { if (nQuickStart == 1) // skip the card/guest screen diff --git a/source-code/source/plugins/TLAC/Components/Input/TouchSliderEmulator.cpp b/source-code/source/plugins/TLAC/Components/Input/TouchSliderEmulator.cpp index 9d22c6e..29d9591 100644 --- a/source-code/source/plugins/TLAC/Components/Input/TouchSliderEmulator.cpp +++ b/source-code/source/plugins/TLAC/Components/Input/TouchSliderEmulator.cpp @@ -6,6 +6,7 @@ #include "../../Input/Keyboard/Keyboard.h" #include "../../Input/Bindings/KeyboardBinding.h" #include "../../Input/KeyConfig/Config.h" +#include "../../Input/DirectInput/Ds4/DualShock4.h" #include "../../FileSystem/ConfigFile.h" #include "../../Utilities/Math.h" #include @@ -55,16 +56,36 @@ namespace TLAC::Components FileSystem::ConfigFile configFile(framework::GetModuleDirectory(), KEY_CONFIG_FILE_NAME); configFile.OpenRead(); - Config::BindConfigKeys(configFile.ConfigMap, "LEFT_SIDE_SLIDE_LEFT", *LeftSideSlideLeft, { "Q" }); - Config::BindConfigKeys(configFile.ConfigMap, "LEFT_SIDE_SLIDE_RIGHT", *LeftSideSlideRight, { "E" }); + usePs4OfficialSlider = configFile.GetBooleanValue("ps4_official_slider"); + enableInMenus = configFile.GetBooleanValue("slider_in_menus"); - Config::BindConfigKeys(configFile.ConfigMap, "RIGHT_SIDE_SLIDE_LEFT", *RightSideSlideLeft, { "U" }); - Config::BindConfigKeys(configFile.ConfigMap, "RIGHT_SIDE_SLIDE_RIGHT", *RightSideSlideRight, { "O" }); + if (usePs4OfficialSlider) + { + DualShock4* ds4 = DualShock4::GetInstance(); + if (ds4 == nullptr) + DualShock4::rawMode = true; // set raw mode for future instances if no current instance + else + ds4->SetRawMode(true); // if is a current instance, set raw mode on it (also sets for future instances) + } + else + { + DualShock4* ds4 = DualShock4::GetInstance(); + if (ds4 == nullptr) + DualShock4::rawMode = false; // set raw mode for future instances if no current instance + else + ds4->SetRawMode(false); // if is a current instance, set raw mode on it (also sets for future instances) - float touchSliderEmulationSpeed = configFile.GetFloatValue("touch_slider_emulation_speed"); - - if (touchSliderEmulationSpeed != 0.0f) - sliderSpeed = touchSliderEmulationSpeed; + Config::BindConfigKeys(configFile.ConfigMap, "LEFT_SIDE_SLIDE_LEFT", *LeftSideSlideLeft, { "Q" }); + Config::BindConfigKeys(configFile.ConfigMap, "LEFT_SIDE_SLIDE_RIGHT", *LeftSideSlideRight, { "E" }); + + Config::BindConfigKeys(configFile.ConfigMap, "RIGHT_SIDE_SLIDE_LEFT", *RightSideSlideLeft, { "U" }); + Config::BindConfigKeys(configFile.ConfigMap, "RIGHT_SIDE_SLIDE_RIGHT", *RightSideSlideRight, { "O" }); + + float touchSliderEmulationSpeed = configFile.GetFloatValue("touch_slider_emulation_speed"); + + if (touchSliderEmulationSpeed != 0.0f) + sliderSpeed = touchSliderEmulationSpeed; + } } void TouchSliderEmulator::Update() @@ -74,20 +95,45 @@ namespace TLAC::Components void TouchSliderEmulator::UpdateInput() { - if (!componentsManager->GetUpdateGameInput() || componentsManager->IsDwGuiActive() || !(*(GameState*)CURRENT_GAME_STATE_ADDRESS == GS_GAME && *(SubGameState*)CURRENT_GAME_SUB_STATE_ADDRESS == SUB_GAME_MAIN)) + if (!componentsManager->GetUpdateGameInput() || componentsManager->IsDwGuiActive() || (!enableInMenus && !(*(GameState*)CURRENT_GAME_STATE_ADDRESS == GS_GAME && *(SubGameState*)CURRENT_GAME_SUB_STATE_ADDRESS == SUB_GAME_MAIN))) return; - sliderIncrement = GetElapsedTime() / sliderSpeed; + if (usePs4OfficialSlider) + { + DualShock4* ds4 = DualShock4::GetInstance(); + if (ds4 == nullptr) + return; - constexpr float sensorStep = (1.0f / SLIDER_SENSORS); + Joystick ls = ds4->GetLeftStick(); + Joystick rs = ds4->GetRightStick(); - EmulateSliderInput(LeftSideSlideLeft, LeftSideSlideRight, ContactPoints[0], 0.0f, 0.5f); - EmulateSliderInput(RightSideSlideLeft, RightSideSlideRight, ContactPoints[1], 0.5f + sensorStep, 1.0f + sensorStep); + uint32_t state = 0; - sliderState->ResetSensors(); + // DualShock4 normalises sticks to floats -- this undoes that + //printf("left stick: %9.6f, %9.6f, ", ls.XAxis, ls.YAxis); + state |= (uint8_t)((ls.XAxis + 1.0 + 0.001) * 127.5) ^ 0b10000000; // 0.001 to prevent rounding errors + //printf("%d, ", (int)(state ^ 0b10000000)); + state |= ((uint8_t)((ls.YAxis + 1.0 + 0.001) * 127.5) ^ 0b10000000) << 8; + //printf("%d\n", (int)((state >> 8) ^ 0b10000000)); + state |= ((uint8_t)((rs.XAxis + 1.0 + 0.001) * 127.5) ^ 0b10000000) << 16; + state |= ((uint8_t)((rs.YAxis + 1.0 + 0.001) * 127.5) ^ 0b10000000) << 24; - for (int i = 0; i < CONTACT_POINTS; i++) - ApplyContactPoint(ContactPoints[i], i); + ApplyBitfieldState(state); + } + else + { + sliderIncrement = GetElapsedTime() / sliderSpeed; + + constexpr float sensorStep = (1.0f / SLIDER_SENSORS); + + EmulateSliderInput(LeftSideSlideLeft, LeftSideSlideRight, ContactPoints[0], 0.0f, 0.5f); + EmulateSliderInput(RightSideSlideLeft, RightSideSlideRight, ContactPoints[1], 0.5f + sensorStep, 1.0f + sensorStep); + + sliderState->ResetSensors(); + + for (int i = 0; i < CONTACT_POINTS; i++) + ApplyContactPoint(ContactPoints[i]); + } } void TouchSliderEmulator::OnFocusLost() @@ -120,23 +166,25 @@ namespace TLAC::Components contactPoint.InContact = leftDown || rightDown; } - void TouchSliderEmulator::ApplyContactPoint(ContactPoint &contactPoint, int section) + void TouchSliderEmulator::ApplyContactPoint(ContactPoint& contactPoint) { - sliderState->SectionTouched[section] = contactPoint.InContact; - - int pressure = contactPoint.InContact ? FULL_PRESSURE : NO_PRESSURE; - float position = std::clamp(contactPoint.Position, 0.0f, 1.0f); - if (contactPoint.InContact) { + float position = std::clamp(contactPoint.Position, 0.0f, 1.0f); int sensor = (int)(position * (SLIDER_SENSORS - 1)); - sliderState->SetSensor(sensor, pressure); + sliderState->SetSensor(sensor, FULL_PRESSURE); } + } - constexpr float startRange = -1.0f; - constexpr float endRange = +1.0f; - - sliderState->SectionPositions[section] = contactPoint.InContact ? (ConvertRange(0.0f, 1.0f, startRange, endRange, position)) : 0.0f; + void TouchSliderEmulator::ApplyBitfieldState(uint32_t state) + { + for (int i = 0; i < 32; i++) + { + if (state & (1 << (31 - i))) + sliderState->SetSensor(i, FULL_PRESSURE); + else + sliderState->SetSensor(i, NO_PRESSURE); + } } } \ No newline at end of file diff --git a/source-code/source/plugins/TLAC/Components/Input/TouchSliderEmulator.h b/source-code/source/plugins/TLAC/Components/Input/TouchSliderEmulator.h index d491e12..9c90b1b 100644 --- a/source-code/source/plugins/TLAC/Components/Input/TouchSliderEmulator.h +++ b/source-code/source/plugins/TLAC/Components/Input/TouchSliderEmulator.h @@ -25,6 +25,10 @@ namespace TLAC::Components Input::Binding* RightSideSlideLeft; Input::Binding* RightSideSlideRight; + bool usePs4OfficialSlider; + + bool enableInMenus; + TouchSliderEmulator(); ~TouchSliderEmulator(); @@ -53,7 +57,8 @@ namespace TLAC::Components ContactPoint ContactPoints[CONTACT_POINTS]; void EmulateSliderInput(Input::Binding *leftBinding, Input::Binding *rightBinding, ContactPoint &contactPoint, float start, float end); - void ApplyContactPoint(ContactPoint &contactPoint, int section); + void ApplyContactPoint(ContactPoint &contactPoint); + void ApplyBitfieldState(uint32_t state); }; } diff --git a/source-code/source/plugins/TLAC/Components/Input/TouchSliderState.cpp b/source-code/source/plugins/TLAC/Components/Input/TouchSliderState.cpp index 24039e2..dbee5db 100644 --- a/source-code/source/plugins/TLAC/Components/Input/TouchSliderState.cpp +++ b/source-code/source/plugins/TLAC/Components/Input/TouchSliderState.cpp @@ -6,9 +6,19 @@ namespace TLAC::Components { if (index < 0 || index >= SLIDER_SENSORS) return; - - SensorPressureLevels[index] = value; - SensorTouched[index].IsTouched = value > 0; + + if (SerialState == nullptr) + return; + + uint32_t* ph = SerialState->sliderSerialResponse.sensors[index].pressureHistory; + ph[3] = ph[2]; + ph[2] = ph[1]; + ph[1] = ph[0]; + ph[0] = value; + + SerialState->sliderSerialResponse.scanMode = 1; + SerialState->sliderSerialResponse.scanCount = 1; + SerialState->sliderResponseCnt = 1; } void TouchSliderState::ResetSensors() diff --git a/source-code/source/plugins/TLAC/Components/Input/TouchSliderState.h b/source-code/source/plugins/TLAC/Components/Input/TouchSliderState.h index 3830f9c..8aeaefb 100644 --- a/source-code/source/plugins/TLAC/Components/Input/TouchSliderState.h +++ b/source-code/source/plugins/TLAC/Components/Input/TouchSliderState.h @@ -10,9 +10,37 @@ namespace TLAC::Components { + struct TouchSliderSerialState + { + uint8_t padding[0x1960]; + + int sliderResponseCnt; + struct + { + int scanMode; + int scanCount; + struct + { + uint32_t pressureHistory[4]; // first: current, second: last sample, ... + } sensors[SLIDER_SENSORS]; + } sliderSerialResponse; + + struct + { + int scanMode; + int scanCount; + struct + { + uint32_t pressureHistory[4]; // first: current, second: last sample, ... + } sensors[SLIDER_SENSORS]; + } sliderSerialResponseCopy; // the other one is copied here by the game, feel free to ignore it + }; + struct TouchSliderState { - uint8_t Padding0000[112]; + uint8_t Padding0000[104]; + + TouchSliderSerialState* SerialState; int32_t State; diff --git a/source-code/source/plugins/TLAC/Input/DirectInput/DirectInputDevice.cpp b/source-code/source/plugins/TLAC/Input/DirectInput/DirectInputDevice.cpp index 85dbd12..385ce57 100644 --- a/source-code/source/plugins/TLAC/Input/DirectInput/DirectInputDevice.cpp +++ b/source-code/source/plugins/TLAC/Input/DirectInput/DirectInputDevice.cpp @@ -53,6 +53,45 @@ namespace TLAC::Input return result; } + HRESULT DirectInputDevice::DI_SetRange(LONG min, LONG max) + { + DIPROPRANGE propData; + propData.lMin = min; + propData.lMax = max; + + propData.diph.dwSize = sizeof(DIPROPRANGE); + propData.diph.dwHeaderSize = sizeof(DIPROPHEADER); + propData.diph.dwHow = DIPH_DEVICE; + propData.diph.dwObj = 0; + + HRESULT result = directInputdevice->SetProperty(DIPROP_RANGE, &propData.diph); + return result; + } + + HRESULT DirectInputDevice::DI_SetRawMode(BOOL raw) + { + // extra unacquire/acquire logic so this can be called on an active device + BOOL wasAcquired = DI_Unacquire() == DI_OK; + + DIPROPDWORD propData; + propData.dwData = raw ? DIPROPCALIBRATIONMODE_RAW : DIPROPCALIBRATIONMODE_COOKED; + + propData.diph.dwSize = sizeof(DIPROPDWORD); + propData.diph.dwHeaderSize = sizeof(DIPROPHEADER); + propData.diph.dwHow = DIPH_DEVICE; + propData.diph.dwObj = 0; + + HRESULT result = NULL; + + if (FAILED(result = directInputdevice->SetProperty(DIPROP_CALIBRATIONMODE, &propData.diph))) + return result; + + if (wasAcquired) + result = DI_Acquire(); + + return result; + } + void DirectInputDevice::DI_Dispose() { if (directInputdevice == nullptr) diff --git a/source-code/source/plugins/TLAC/Input/DirectInput/DirectInputDevice.h b/source-code/source/plugins/TLAC/Input/DirectInput/DirectInputDevice.h index 1579413..ff9be40 100644 --- a/source-code/source/plugins/TLAC/Input/DirectInput/DirectInputDevice.h +++ b/source-code/source/plugins/TLAC/Input/DirectInput/DirectInputDevice.h @@ -16,6 +16,8 @@ namespace TLAC::Input HRESULT DI_Release(); HRESULT DI_Poll(); HRESULT DI_GetDeviceState(DWORD size, LPVOID data); + HRESULT DI_SetRange(LONG min, LONG max); + HRESULT DI_SetRawMode(BOOL raw); void DI_Dispose(); }; diff --git a/source-code/source/plugins/TLAC/Input/DirectInput/Ds4/DualShock4.cpp b/source-code/source/plugins/TLAC/Input/DirectInput/Ds4/DualShock4.cpp index 899f56c..2755edd 100644 --- a/source-code/source/plugins/TLAC/Input/DirectInput/Ds4/DualShock4.cpp +++ b/source-code/source/plugins/TLAC/Input/DirectInput/Ds4/DualShock4.cpp @@ -6,6 +6,7 @@ namespace TLAC::Input { DualShock4* DualShock4::instance; + bool DualShock4::rawMode = false; DualShock4::DualShock4() { @@ -53,11 +54,29 @@ namespace TLAC::Input if (FAILED(result = DI_SetDataFormat(&c_dfDIJoystick2))) return false; + if (FAILED(result = DI_SetRange(0, 255))) // use 8-bit data + return false; + + // set raw mode from previous instance + if (rawMode && FAILED(result = DI_SetRawMode(true))) + return false; + result = DI_Acquire(); return true; } + bool DualShock4::SetRawMode(bool raw) + { + HRESULT result = NULL; + if (FAILED(result = DI_SetRawMode(raw))) + return false; + + rawMode = raw; + + return true; + } + bool DualShock4::PollInput() { lastState = currentState; diff --git a/source-code/source/plugins/TLAC/Input/DirectInput/Ds4/DualShock4.h b/source-code/source/plugins/TLAC/Input/DirectInput/Ds4/DualShock4.h index 8245f7e..acdf4db 100644 --- a/source-code/source/plugins/TLAC/Input/DirectInput/Ds4/DualShock4.h +++ b/source-code/source/plugins/TLAC/Input/DirectInput/Ds4/DualShock4.h @@ -31,6 +31,7 @@ namespace TLAC::Input static bool TryInitializeInstance(); bool Initialize(); + bool SetRawMode(bool raw); bool PollInput() override; bool IsDown(Ds4Button button); @@ -48,6 +49,8 @@ namespace TLAC::Input static inline DualShock4* GetInstance() { return instance; }; static inline void DeleteInstance() { delete instance; instance = nullptr; }; + static bool rawMode; // kinda dodgy, used to set raw mode for all instances without waiting for a single one + private: static DualShock4* instance; @@ -58,8 +61,8 @@ namespace TLAC::Input const float joystickThreshold = 0.5f; const float dpadThreshold = 0.5f; - inline float NormalizeTrigger(long value) { return (float)value / USHRT_MAX; }; - inline float NormalizeStick(long value) { return (float)value / USHRT_MAX * 2.0f - 1.0f; }; + inline float NormalizeTrigger(long value) { return (float)value / UCHAR_MAX; }; + inline float NormalizeStick(long value) { return (float)value / UCHAR_MAX * 2.0f - 1.0f; }; inline Joystick NormalizeStick(long x, long y) { return Joystick(NormalizeStick(x), NormalizeStick(y)); }; void UpdateInternalDs4State(Ds4State &state);