From 5ccbcf455e437031cb2542db0804a341cc9150cc Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 30 Dec 2012 13:41:48 +1100 Subject: [PATCH 1/4] Added preliminary GameCube Steering Wheel emulation via a PC Force Feedback Steering Wheel. --- Source/Core/Core/CMakeLists.txt | 1 + Source/Core/Core/Core.vcxproj | 2 + Source/Core/Core/Core.vcxproj.filters | 6 + Source/Core/Core/Src/HW/GCPad.cpp | 27 +- Source/Core/Core/Src/HW/GCPad.h | 1 + Source/Core/Core/Src/HW/GCPadEmu.cpp | 7 +- Source/Core/Core/Src/HW/GCPadEmu.h | 2 +- Source/Core/Core/Src/HW/SI_Device.cpp | 5 + .../Core/Src/HW/SI_DeviceGCSteeringWheel.cpp | 301 ++++++++++++++++++ .../Core/Src/HW/SI_DeviceGCSteeringWheel.h | 118 +++++++ Source/Core/Core/Src/NetPlay.cpp | 11 + Source/Core/DolphinWX/Src/ConfigMain.cpp | 13 +- .../DInput/DInputJoystick.cpp | 27 +- 13 files changed, 506 insertions(+), 15 deletions(-) create mode 100644 Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.cpp create mode 100644 Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.h diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 124d7aa879..6ffeeaec3a 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -115,6 +115,7 @@ set(SRCS Src/ActionReplay.cpp Src/HW/SI_Device.cpp Src/HW/SI_DeviceGBA.cpp Src/HW/SI_DeviceGCController.cpp + Src/HW/SI_DeviceGCSteeringWheel.cpp Src/HW/Sram.cpp Src/HW/StreamADPCM.cpp Src/HW/SystemTimers.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index e626ccd033..bd3d97f11b 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -301,6 +301,7 @@ + @@ -503,6 +504,7 @@ + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index ad6db227d5..246dafd972 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -290,6 +290,9 @@ HW %28Flipper/Hollywood%29\SI - Serial Interface + + HW %28Flipper/Hollywood%29\SI - Serial Interface + HW %28Flipper/Hollywood%29\VI - Video Interface @@ -814,6 +817,9 @@ HW %28Flipper/Hollywood%29\SI - Serial Interface + + HW %28Flipper/Hollywood%29\SI - Serial Interface + HW %28Flipper/Hollywood%29\SI - Serial Interface diff --git a/Source/Core/Core/Src/HW/GCPad.cpp b/Source/Core/Core/Src/HW/GCPad.cpp index 4c1b4dabdd..61b666af12 100644 --- a/Source/Core/Core/Src/HW/GCPad.cpp +++ b/Source/Core/Core/Src/HW/GCPad.cpp @@ -101,7 +101,32 @@ void Rumble(u8 _numPAD, unsigned int _uType, unsigned int _uStrength) { // TODO: this has potential to not stop rumble if user is messing with GUI at the perfect time // set rumble - ((GCPad*)g_plugin.controllers[ _numPAD ])->SetOutput( 1 == _uType && _uStrength > 2 ); + if (1 == _uType && _uStrength > 2) + { + ((GCPad*)g_plugin.controllers[ _numPAD ])->SetOutput(255); + } + } +} + +// __________________________________________________________________________________________________ +// Function: Motor +// Purpose: For devices with constant Force feedback +// input: Type - 06 = Motor On, 04 = Motor Off +// Strength - 00 = Left Strong, 127 = Left Weak, 128 = Right Weak, 255 = Right Strong +// output: none +// +void Motor(u8 _numPAD, unsigned int _uType, unsigned int _uStrength) +{ + std::unique_lock lk(g_plugin.controls_lock, std::try_to_lock); + + if (lk.owns_lock()) + { + // TODO: this has potential to not stop rumble if user is messing with GUI at the perfect time + // set rumble + if (_uType == 06) + { + ((GCPad*)g_plugin.controllers[ _numPAD ])->SetOutput(_uStrength); + } } } diff --git a/Source/Core/Core/Src/HW/GCPad.h b/Source/Core/Core/Src/HW/GCPad.h index f7f6af2f20..fb07e82ccc 100644 --- a/Source/Core/Core/Src/HW/GCPad.h +++ b/Source/Core/Core/Src/HW/GCPad.h @@ -32,6 +32,7 @@ InputPlugin *GetPlugin(); void GetStatus(u8 _numPAD, SPADStatus* _pPADStatus); void Rumble(u8 _numPAD, unsigned int _uType, unsigned int _uStrength); +void Motor(u8 _numPAD, unsigned int _uType, unsigned int _uStrength); bool GetMicButton(u8 pad); } diff --git a/Source/Core/Core/Src/HW/GCPadEmu.cpp b/Source/Core/Core/Src/HW/GCPadEmu.cpp index 837c9104cc..5233ed2877 100644 --- a/Source/Core/Core/Src/HW/GCPadEmu.cpp +++ b/Source/Core/Core/Src/HW/GCPadEmu.cpp @@ -130,10 +130,13 @@ void GCPad::GetInput(SPADStatus* const pad) } } -void GCPad::SetOutput(const bool on) +void GCPad::SetOutput(const u8 on) { // only rumble if window has focus or background input is enabled - m_rumble->controls[0]->control_ref->State(on && (Host_RendererHasFocus() || m_options[0].settings[0]->value)); + if (Host_RendererHasFocus() || m_options[0].settings[0]->value) + m_rumble->controls[0]->control_ref->State((float)on / 255); + else + m_rumble->controls[0]->control_ref->State(0); } void GCPad::LoadDefaults(const ControllerInterface& ciface) diff --git a/Source/Core/Core/Src/HW/GCPadEmu.h b/Source/Core/Core/Src/HW/GCPadEmu.h index 5185ff221c..cf0940245e 100644 --- a/Source/Core/Core/Src/HW/GCPadEmu.h +++ b/Source/Core/Core/Src/HW/GCPadEmu.h @@ -28,7 +28,7 @@ public: GCPad(const unsigned int index); void GetInput(SPADStatus* const pad); - void SetOutput(const bool on); + void SetOutput(const u8 on); bool GetMicButton() const; diff --git a/Source/Core/Core/Src/HW/SI_Device.cpp b/Source/Core/Core/Src/HW/SI_Device.cpp index 8abddbe472..062499f16f 100644 --- a/Source/Core/Core/Src/HW/SI_Device.cpp +++ b/Source/Core/Core/Src/HW/SI_Device.cpp @@ -17,6 +17,7 @@ #include "SI_Device.h" #include "SI_DeviceGCController.h" +#include "SI_DeviceGCSteeringWheel.h" #include "SI_DeviceGBA.h" #include "SI_DeviceAMBaseboard.h" @@ -76,6 +77,10 @@ ISIDevice* SIDevice_Create(const SIDevices device, const int port_number) return new CSIDevice_GCController(device, port_number); break; + case SIDEVICE_GC_STEERING: + return new CSIDevice_GCSteeringWheel(device, port_number); + break; + case SIDEVICE_GC_TARUKONGA: return new CSIDevice_TaruKonga(device, port_number); break; diff --git a/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.cpp b/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.cpp new file mode 100644 index 0000000000..113fe186b1 --- /dev/null +++ b/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.cpp @@ -0,0 +1,301 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include + +#include "SI.h" +#include "SI_Device.h" +#include "SI_DeviceGCSteeringWheel.h" + +#include "EXI_Device.h" +#include "EXI_DeviceMic.h" + +#include "GCPad.h" + +#include "../Movie.h" + +#include "../CoreTiming.h" +#include "SystemTimers.h" +#include "ProcessorInterface.h" +#include "../Core.h" + +// --- standard gamecube controller --- +CSIDevice_GCSteeringWheel::CSIDevice_GCSteeringWheel(SIDevices device, int _iDeviceNumber) + : ISIDevice(device, _iDeviceNumber) + , m_TButtonComboStart(0) + , m_TButtonCombo(0) + , m_LastButtonCombo(COMBO_NONE) +{ + memset(&m_Origin, 0, sizeof(SOrigin)); + m_Origin.uCommand = CMD_ORIGIN; + m_Origin.uOriginStickX = 0x80; // center + m_Origin.uOriginStickY = 0x80; + m_Origin.uSubStickStickX = 0x80; + m_Origin.uSubStickStickY = 0x80; + m_Origin.uTrigger_L = 0x1F; // 0-30 is the lower deadzone + m_Origin.uTrigger_R = 0x1F; + + // Dunno if we need to do this, game/lib should set it? + m_Mode = 0x03; +} + +int CSIDevice_GCSteeringWheel::RunBuffer(u8* _pBuffer, int _iLength) +{ + // For debug logging only + ISIDevice::RunBuffer(_pBuffer, _iLength); + + // Read the command + EBufferCommands command = static_cast(_pBuffer[3]); + + // Handle it + switch (command) + { + case CMD_RESET: + *(u32*)&_pBuffer[0] = SI_GC_STEERING; + break; + + case CMD_ORIGIN: + { + INFO_LOG(SERIALINTERFACE, "PAD - Get Origin"); + u8* pCalibration = reinterpret_cast(&m_Origin); + for (int i = 0; i < (int)sizeof(SOrigin); i++) + { + _pBuffer[i ^ 3] = *pCalibration++; + } + } + break; + + // Recalibrate (FiRES: i am not 100 percent sure about this) + case CMD_RECALIBRATE: + { + INFO_LOG(SERIALINTERFACE, "PAD - Recalibrate"); + u8* pCalibration = reinterpret_cast(&m_Origin); + for (int i = 0; i < (int)sizeof(SOrigin); i++) + { + _pBuffer[i ^ 3] = *pCalibration++; + } + } + break; + + // DEFAULT + default: + { + ERROR_LOG(SERIALINTERFACE, "unknown SI command (0x%x)", command); + } + break; + } + + return _iLength; +} + + +// GetData + +// Return true on new data (max 7 Bytes and 6 bits ;) +// [00?SYXBA] [1LRZUDRL] [x] [y] [cx] [cy] [l] [r] +// |\_ ERR_LATCH (error latched - check SISR) +// |_ ERR_STATUS (error on last GetData or SendCmd?) +bool CSIDevice_GCSteeringWheel::GetData(u32& _Hi, u32& _Low) +{ + SPADStatus PadStatus; + memset(&PadStatus, 0, sizeof(PadStatus)); + + Pad::GetStatus(ISIDevice::m_iDeviceNumber, &PadStatus); + Movie::CallInputManip(&PadStatus, ISIDevice::m_iDeviceNumber); + + u32 netValues[2]; + if (NetPlay_GetInput(ISIDevice::m_iDeviceNumber, PadStatus, netValues)) + { + _Hi = netValues[0]; // first 4 bytes + _Low = netValues[1]; // last 4 bytes + return true; + } + + Movie::SetPolledDevice(); + + if(Movie::IsPlayingInput()) + { + Movie::PlayController(&PadStatus, ISIDevice::m_iDeviceNumber); + if(!Movie::IsUsingWiimote(0)) + Movie::InputUpdate(); + } + else if(Movie::IsRecordingInput()) + { + Movie::RecordInput(&PadStatus, ISIDevice::m_iDeviceNumber); + if(!Movie::IsUsingWiimote(0)) + Movie::InputUpdate(); + } + else + Movie::CheckPadStatus(&PadStatus, ISIDevice::m_iDeviceNumber); + + // Thankfully changing mode does not change the high bits ;) + _Hi = (u32)((u8)PadStatus.stickY); + _Hi |= (u32)((u8)PadStatus.stickX << 8); + _Hi |= (u32)((u16)(PadStatus.button | PAD_USE_ORIGIN) << 16); + + // Low bits are packed differently per mode + if (m_Mode == 0 || m_Mode == 5 || m_Mode == 6 || m_Mode == 7) + { + _Low = (u8)(PadStatus.analogB >> 4); // Top 4 bits + _Low |= (u32)((u8)(PadStatus.analogA >> 4) << 4); // Top 4 bits + _Low |= (u32)((u8)(PadStatus.triggerRight >> 4) << 8); // Top 4 bits + _Low |= (u32)((u8)(PadStatus.triggerLeft >> 4) << 12); // Top 4 bits + _Low |= (u32)((u8)(PadStatus.substickY) << 16); // All 8 bits + _Low |= (u32)((u8)(PadStatus.substickX) << 24); // All 8 bits + } + else if (m_Mode == 1) + { + _Low = (u8)(PadStatus.analogB >> 4); // Top 4 bits + _Low |= (u32)((u8)(PadStatus.analogA >> 4) << 4); // Top 4 bits + _Low |= (u32)((u8)PadStatus.triggerRight << 8); // All 8 bits + _Low |= (u32)((u8)PadStatus.triggerLeft << 16); // All 8 bits + _Low |= (u32)((u8)PadStatus.substickY << 24); // Top 4 bits + _Low |= (u32)((u8)PadStatus.substickX << 28); // Top 4 bits + } + else if (m_Mode == 2) + { + _Low = (u8)(PadStatus.analogB); // All 8 bits + _Low |= (u32)((u8)(PadStatus.analogA) << 8); // All 8 bits + _Low |= (u32)((u8)(PadStatus.triggerRight >> 4) << 16); // Top 4 bits + _Low |= (u32)((u8)(PadStatus.triggerLeft >> 4) << 20); // Top 4 bits + _Low |= (u32)((u8)PadStatus.substickY << 24); // Top 4 bits + _Low |= (u32)((u8)PadStatus.substickX << 28); // Top 4 bits + } + else if (m_Mode == 3) + { + // Analog A/B are always 0 + _Low = (u8)PadStatus.triggerRight; // All 8 bits + _Low |= (u32)((u8)PadStatus.triggerLeft << 8); // All 8 bits + _Low |= (u32)((u8)PadStatus.substickY << 16); // All 8 bits + _Low |= (u32)((u8)PadStatus.substickX << 24); // All 8 bits + } + else if (m_Mode == 4) + { + _Low = (u8)(PadStatus.analogB); // All 8 bits + _Low |= (u32)((u8)(PadStatus.analogA) << 8); // All 8 bits + // triggerLeft/Right are always 0 + _Low |= (u32)((u8)PadStatus.substickY << 16); // All 8 bits + _Low |= (u32)((u8)PadStatus.substickX << 24); // All 8 bits + } + + // Keep track of the special button combos (embedded in controller hardware... :( ) + EButtonCombo tempCombo; + if ((PadStatus.button & 0xff00) == (PAD_BUTTON_Y|PAD_BUTTON_X|PAD_BUTTON_START)) + tempCombo = COMBO_ORIGIN; + else if ((PadStatus.button & 0xff00) == (PAD_BUTTON_B|PAD_BUTTON_X|PAD_BUTTON_START)) + tempCombo = COMBO_RESET; + else + tempCombo = COMBO_NONE; + if (tempCombo != m_LastButtonCombo) + { + m_LastButtonCombo = tempCombo; + if (m_LastButtonCombo != COMBO_NONE) + m_TButtonComboStart = CoreTiming::GetTicks(); + } + if (m_LastButtonCombo != COMBO_NONE) + { + m_TButtonCombo = CoreTiming::GetTicks(); + if ((m_TButtonCombo - m_TButtonComboStart) > SystemTimers::GetTicksPerSecond() * 3) + { + if (m_LastButtonCombo == COMBO_RESET) + ProcessorInterface::ResetButton_Tap(); + else if (m_LastButtonCombo == COMBO_ORIGIN) + { + m_Origin.uOriginStickX = PadStatus.stickX; + m_Origin.uOriginStickY = PadStatus.stickY; + m_Origin.uSubStickStickX = PadStatus.substickX; + m_Origin.uSubStickStickY = PadStatus.substickY; + m_Origin.uTrigger_L = PadStatus.triggerLeft; + m_Origin.uTrigger_R = PadStatus.triggerRight; + } + m_LastButtonCombo = COMBO_NONE; + } + } + + return true; +} + + +// SendCommand +void CSIDevice_GCSteeringWheel::SendCommand(u32 _Cmd, u8 _Poll) +{ + UCommand command(_Cmd); + + switch (command.Command) + { + // Costis sent it in some demos :) + case 0x00: + break; + + case CMD_FORCE: + { + unsigned int uStrength = command.Parameter1; // 0 = left strong, 127 = left weak, 128 = right weak, 255 = right strong + unsigned int uType = command.Parameter2; // 06 = motor on, 04 = motor off + + // get the correct pad number that should rumble locally when using netplay + const u8 numPAD = NetPlay_GetPadNum(ISIDevice::m_iDeviceNumber); + + NOTICE_LOG(COMMON, "Type: %d Strength: %d", uType, uStrength); + + if (numPAD < 4) + Pad::Motor(numPAD, uType, uStrength); + + if (!_Poll) + { + m_Mode = command.Parameter2; + INFO_LOG(SERIALINTERFACE, "PAD %i set to mode %i", ISIDevice::m_iDeviceNumber, m_Mode); + } + } + break; + + case CMD_WRITE: + { + unsigned int uType = command.Parameter1; // 0 = stop, 1 = rumble, 2 = stop hard + unsigned int uStrength = command.Parameter2; + + // get the correct pad number that should rumble locally when using netplay + const u8 numPAD = NetPlay_GetPadNum(ISIDevice::m_iDeviceNumber); + + if (numPAD < 4) + Pad::Rumble(numPAD, uType, uStrength); + + if (!_Poll) + { + m_Mode = command.Parameter2; + INFO_LOG(SERIALINTERFACE, "PAD %i set to mode %i", ISIDevice::m_iDeviceNumber, m_Mode); + } + } + break; + + default: + { + ERROR_LOG(SERIALINTERFACE, "unknown direct command (0x%x)", _Cmd); + } + break; + } +} + +// Savestate support +void CSIDevice_GCSteeringWheel::DoState(PointerWrap& p) +{ + p.Do(m_Origin); + p.Do(m_Mode); + p.Do(m_TButtonComboStart); + p.Do(m_TButtonCombo); + p.Do(m_LastButtonCombo); +} diff --git a/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.h b/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.h new file mode 100644 index 0000000000..1814c8d6e4 --- /dev/null +++ b/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.h @@ -0,0 +1,118 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _SI_DEVICEGCSTEERINGWHEEL_H +#define _SI_DEVICEGCSTEERINGWHEEL_H + +#include "SI_Device.h" +#include "GCPadStatus.h" + + +// standard gamecube controller +class CSIDevice_GCSteeringWheel : public ISIDevice +{ +private: + + // Commands + enum EBufferCommands + { + CMD_RESET = 0x00, + CMD_ORIGIN = 0x41, + CMD_RECALIBRATE = 0x42, + }; + + struct SOrigin + { + u8 uCommand;// Maybe should be button bits? + u8 unk_1; // ..and this would be the other half + u8 uOriginStickX; + u8 uOriginStickY; + u8 uSubStickStickX; + u8 uSubStickStickY; + u8 uTrigger_L; + u8 uTrigger_R; + u8 unk_4; + u8 unk_5; + u8 unk_6; + u8 unk_7; + }; + + enum EDirectCommands + { + CMD_FORCE = 0x30, + CMD_WRITE = 0x40 + }; + + union UCommand + { + u32 Hex; + struct + { + u32 Parameter1 : 8; + u32 Parameter2 : 8; + u32 Command : 8; + u32 : 8; + }; + UCommand() {Hex = 0;} + UCommand(u32 _iValue) {Hex = _iValue;} + }; + + enum EButtonCombo + { + COMBO_NONE = 0, + COMBO_ORIGIN, + COMBO_RESET + }; + + // struct to compare input against + // Set on connection and (standard pad only) on button combo + SOrigin m_Origin; + + // PADAnalogMode + u8 m_Mode; + + // Timer to track special button combos: + // y, X, start for 3 seconds updates origin with current status + // Technically, the above is only on standard pad, wavebird does not support it for example + // b, x, start for 3 seconds triggers reset (PI reset button interrupt) + u64 m_TButtonComboStart, m_TButtonCombo; + // Type of button combo from the last/current poll + EButtonCombo m_LastButtonCombo; + +public: + + // Constructor + CSIDevice_GCSteeringWheel(SIDevices device, int _iDeviceNumber); + + // Run the SI Buffer + virtual int RunBuffer(u8* _pBuffer, int _iLength); + + // Send and Receive pad input from network + static bool NetPlay_GetInput(u8 numPAD, SPADStatus status, u32 *PADStatus); + static u8 NetPlay_GetPadNum(u8 numPAD); + + // Return true on new data + virtual bool GetData(u32& _Hi, u32& _Low); + + // Send a command directly + virtual void SendCommand(u32 _Cmd, u8 _Poll); + + // Savestate support + virtual void DoState(PointerWrap& p); +}; + +#endif diff --git a/Source/Core/Core/Src/NetPlay.cpp b/Source/Core/Core/Src/NetPlay.cpp index 1c19f763ec..05331bc6cc 100644 --- a/Source/Core/Core/Src/NetPlay.cpp +++ b/Source/Core/Core/Src/NetPlay.cpp @@ -22,6 +22,7 @@ #include "IPC_HLE/WII_IPC_HLE_WiiMote.h" // for gcpad #include "HW/SI_DeviceGCController.h" +#include "HW/SI_DeviceGCSteeringWheel.h" // for gctime #include "HW/EXI_DeviceIPL.h" // for wiimote/ OSD messages @@ -296,6 +297,11 @@ bool CSIDevice_GCController::NetPlay_GetInput(u8 numPAD, SPADStatus PadStatus, u return false; } +bool CSIDevice_GCSteeringWheel::NetPlay_GetInput(u8 numPAD, SPADStatus PadStatus, u32 *PADStatus) +{ + return CSIDevice_GCController::NetPlay_GetInput(numPAD, PadStatus, PADStatus); +} + // called from ---CPU--- thread // so all players' games get the same time u32 CEXIIPL::NetPlay_GetGCTime() @@ -320,6 +326,11 @@ u8 CSIDevice_GCController::NetPlay_GetPadNum(u8 numPAD) return numPAD; } +u8 CSIDevice_GCSteeringWheel::NetPlay_GetPadNum(u8 numPAD) +{ + return CSIDevice_GCController::NetPlay_GetPadNum(numPAD); +} + // called from ---CPU--- thread // wiimote update / used for frame counting //void CWII_IPC_HLE_Device_usb_oh1_57e_305::NetPlay_WiimoteUpdate(int _number) diff --git a/Source/Core/DolphinWX/Src/ConfigMain.cpp b/Source/Core/DolphinWX/Src/ConfigMain.cpp index 76508daf7c..6e9987b1f4 100644 --- a/Source/Core/DolphinWX/Src/ConfigMain.cpp +++ b/Source/Core/DolphinWX/Src/ConfigMain.cpp @@ -79,6 +79,7 @@ static const wxLanguage langIds[] = #define DEV_DUMMY_STR _trans("Dummy") #define SIDEV_STDCONT_STR _trans("Standard Controller") +#define SIDEV_STEERING_STR _trans("Steering Wheel") #define SIDEV_BONGO_STR _trans("TaruKonga (Bongos)") #define SIDEV_GBA_STR "GBA" #define SIDEV_AM_BB_STR _trans("AM-Baseboard") @@ -391,6 +392,7 @@ void CConfigMain::InitializeGUIValues() wxArrayString SIDevices; SIDevices.Add(_(DEV_NONE_STR)); SIDevices.Add(_(SIDEV_STDCONT_STR)); + SIDevices.Add(_(SIDEV_STEERING_STR)); SIDevices.Add(_(SIDEV_BONGO_STR)); SIDevices.Add(_(SIDEV_GBA_STR)); SIDevices.Add(_(SIDEV_AM_BB_STR)); @@ -443,15 +445,18 @@ void CConfigMain::InitializeGUIValues() case SIDEVICE_GC_CONTROLLER: GCSIDevice[i]->SetStringSelection(SIDevices[1]); break; - case SIDEVICE_GC_TARUKONGA: + case SIDEVICE_GC_STEERING: GCSIDevice[i]->SetStringSelection(SIDevices[2]); break; - case SIDEVICE_GC_GBA: + case SIDEVICE_GC_TARUKONGA: GCSIDevice[i]->SetStringSelection(SIDevices[3]); break; - case SIDEVICE_AM_BASEBOARD: + case SIDEVICE_GC_GBA: GCSIDevice[i]->SetStringSelection(SIDevices[4]); break; + case SIDEVICE_AM_BASEBOARD: + GCSIDevice[i]->SetStringSelection(SIDevices[5]); + break; default: GCSIDevice[i]->SetStringSelection(SIDevices[0]); break; @@ -1053,6 +1058,8 @@ void CConfigMain::ChooseSIDevice(wxString deviceName, int deviceNum) SIDevices tempType; if (!deviceName.compare(WXSTR_TRANS(SIDEV_STDCONT_STR))) tempType = SIDEVICE_GC_CONTROLLER; + else if (!deviceName.compare(WXSTR_TRANS(SIDEV_STEERING_STR))) + tempType = SIDEVICE_GC_STEERING; else if (!deviceName.compare(WXSTR_TRANS(SIDEV_BONGO_STR))) tempType = SIDEVICE_GC_TARUKONGA; else if (!deviceName.compare(wxT(SIDEV_GBA_STR))) diff --git a/Source/Core/InputCommon/Src/ControllerInterface/DInput/DInputJoystick.cpp b/Source/Core/InputCommon/Src/ControllerInterface/DInput/DInputJoystick.cpp index 08d62bcfc1..5f80b6f32b 100644 --- a/Source/Core/InputCommon/Src/ControllerInterface/DInput/DInputJoystick.cpp +++ b/Source/Core/InputCommon/Src/ControllerInterface/DInput/DInputJoystick.cpp @@ -145,7 +145,7 @@ LCleanup: void InitJoystick(IDirectInput8* const idi8, std::vector& devices, HWND hwnd) { std::list joysticks; - idi8->EnumDevices( DI8DEVCLASS_GAMECTRL, DIEnumDevicesCallback, (LPVOID)&joysticks, DIEDFL_ATTACHEDONLY ); + idi8->EnumDevices( DI8DEVCLASS_GAMECTRL, DIEnumDevicesCallback, (LPVOID)&joysticks, DIEDFL_FORCEFEEDBACK | DIEDFL_ATTACHEDONLY ); // this is used to number the joysticks // multiple joysticks with the same name shall get unique ids starting at 0 @@ -254,9 +254,9 @@ Joystick::Joystick( /*const LPCDIDEVICEINSTANCE lpddi, */const LPDIRECTINPUTDEVI for (unsigned int offset = 0; offset < DIJOFS_BUTTON(0) / sizeof(LONG); ++offset) { range.diph.dwObj = offset * sizeof(LONG); - // try to set some nice power of 2 values (8192) - range.lMin = -(1 << 13); - range.lMax = (1 << 13); + // try to set some nice power of 2 values (128) to match the GameCube controls + range.lMin = -(1 << 7); + range.lMax = (1 << 7); m_device->SetProperty(DIPROP_RANGE, &range.diph); // but i guess not all devices support setting range // so i getproperty right afterward incase it didn't set :P @@ -281,17 +281,19 @@ Joystick::Joystick( /*const LPCDIDEVICEINSTANCE lpddi, */const LPDIRECTINPUTDEVI if ( objects.size() ) { // temporary - DWORD rgdwAxes[] = {DIJOFS_X, DIJOFS_Y}; - LONG rglDirection[] = {0, 0}; + DWORD rgdwAxes[2] = {DIJOFS_X, DIJOFS_Y}; + LONG rglDirection[2] = {-200, 0}; DIEFFECT eff; ZeroMemory(&eff, sizeof(eff)); eff.dwSize = sizeof(DIEFFECT); eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; eff.dwDuration = INFINITE; // (4 * DI_SECONDS) + eff.dwSamplePeriod = 0; eff.dwGain = DI_FFNOMINALMAX; eff.dwTriggerButton = DIEB_NOTRIGGER; - eff.cAxes = std::min((DWORD)2, (DWORD)objects.size()); + eff.dwTriggerRepeatInterval = 0; + eff.cAxes = std::min((DWORD)1, (DWORD)objects.size()); eff.rgdwAxes = rgdwAxes; eff.rglDirection = rglDirection; @@ -310,7 +312,12 @@ Joystick::Joystick( /*const LPCDIDEVICEINSTANCE lpddi, */const LPDIRECTINPUTDEVI { // ugly if ladder if (0 == f) + { + DICONSTANTFORCE diCF = {-10000}; + diCF.lMagnitude = DI_FFNOMINALMAX; eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); + eff.lpvTypeSpecificParams = &diCF; + } else if (1 == f) eff.cbTypeSpecificParams = sizeof(DIRAMPFORCE); else @@ -528,7 +535,11 @@ ControlState Joystick::Hat::GetState() const void Joystick::ForceConstant::SetState(const ControlState state) { - const LONG new_val = LONG(10000 * state); + float force = abs(state - 0.5) * 2; + if (state < 0.5) + force = -force; + + const LONG new_val = LONG(10000 * force); LONG &val = params.lMagnitude; if (val != new_val) From 51603f052220b6240ac9c316ab791c3738e260ef Mon Sep 17 00:00:00 2001 From: skidau Date: Tue, 1 Jan 2013 13:49:22 +1100 Subject: [PATCH 2/4] * Implemented working pedal support. * Changed the mapping of the steering wheel to: Main Stick Left/Right = Steer Left/Right Main Stick Up = Accelerate Main Stick Down = Brake * Fixed non-force feedback controllers that were not detected --- .../Core/Src/HW/SI_DeviceGCSteeringWheel.cpp | 23 ++++++++++++++++--- .../Core/Src/HW/SI_DeviceGCSteeringWheel.h | 1 + .../DInput/DInputJoystick.cpp | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.cpp b/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.cpp index 113fe186b1..7783261da9 100644 --- a/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.cpp +++ b/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.cpp @@ -92,6 +92,10 @@ int CSIDevice_GCSteeringWheel::RunBuffer(u8* _pBuffer, int _iLength) } break; + // Seen in F-Zero GX + case CMD_MOTOR_OFF: + break; + // DEFAULT default: { @@ -144,12 +148,12 @@ bool CSIDevice_GCSteeringWheel::GetData(u32& _Hi, u32& _Low) Movie::CheckPadStatus(&PadStatus, ISIDevice::m_iDeviceNumber); // Thankfully changing mode does not change the high bits ;) - _Hi = (u32)((u8)PadStatus.stickY); - _Hi |= (u32)((u8)PadStatus.stickX << 8); + _Hi = (u32)((u8)PadStatus.stickX); // Steering + _Hi |= 0x800; // Pedal connected flag _Hi |= (u32)((u16)(PadStatus.button | PAD_USE_ORIGIN) << 16); // Low bits are packed differently per mode - if (m_Mode == 0 || m_Mode == 5 || m_Mode == 6 || m_Mode == 7) + if (m_Mode == 0 || m_Mode == 5 || m_Mode == 7) { _Low = (u8)(PadStatus.analogB >> 4); // Top 4 bits _Low |= (u32)((u8)(PadStatus.analogA >> 4) << 4); // Top 4 bits @@ -192,6 +196,19 @@ bool CSIDevice_GCSteeringWheel::GetData(u32& _Hi, u32& _Low) _Low |= (u32)((u8)PadStatus.substickY << 16); // All 8 bits _Low |= (u32)((u8)PadStatus.substickX << 24); // All 8 bits } + else if (m_Mode == 6) + { + _Low = (u8)PadStatus.triggerRight; // All 8 bits + _Low |= (u32)((u8)PadStatus.triggerLeft << 8); // All 8 bits + + // The GC Steering Wheel appears to have combined pedals + // (both the Accelerate and Brake pedals are mapped to a single axis) + // We use the stickY axis for the pedals. + if (PadStatus.stickY < 128) + _Low |= (u32)((u8)(255 - ((PadStatus.stickY & 0x7f) * 2)) << 16); // All 8 bits (Brake) + if (PadStatus.stickY >= 128) + _Low |= (u32)((u8)((PadStatus.stickY & 0x7f) * 2) << 24); // All 8 bits (Accelerate) + } // Keep track of the special button combos (embedded in controller hardware... :( ) EButtonCombo tempCombo; diff --git a/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.h b/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.h index 1814c8d6e4..2245765863 100644 --- a/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.h +++ b/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.h @@ -33,6 +33,7 @@ private: CMD_RESET = 0x00, CMD_ORIGIN = 0x41, CMD_RECALIBRATE = 0x42, + CMD_MOTOR_OFF = 0xff, }; struct SOrigin diff --git a/Source/Core/InputCommon/Src/ControllerInterface/DInput/DInputJoystick.cpp b/Source/Core/InputCommon/Src/ControllerInterface/DInput/DInputJoystick.cpp index 5f80b6f32b..b7ac3bf82e 100644 --- a/Source/Core/InputCommon/Src/ControllerInterface/DInput/DInputJoystick.cpp +++ b/Source/Core/InputCommon/Src/ControllerInterface/DInput/DInputJoystick.cpp @@ -145,7 +145,7 @@ LCleanup: void InitJoystick(IDirectInput8* const idi8, std::vector& devices, HWND hwnd) { std::list joysticks; - idi8->EnumDevices( DI8DEVCLASS_GAMECTRL, DIEnumDevicesCallback, (LPVOID)&joysticks, DIEDFL_FORCEFEEDBACK | DIEDFL_ATTACHEDONLY ); + idi8->EnumDevices( DI8DEVCLASS_GAMECTRL, DIEnumDevicesCallback, (LPVOID)&joysticks, DIEDFL_ATTACHEDONLY ); // this is used to number the joysticks // multiple joysticks with the same name shall get unique ids starting at 0 From 9af711b12c8248f3f806725644e93fdf9024bd37 Mon Sep 17 00:00:00 2001 From: Rachel Bryk Date: Mon, 31 Dec 2012 21:57:39 -0500 Subject: [PATCH 3/4] Always count GC wheel inputs too. --- Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.cpp b/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.cpp index 7783261da9..f69ccd2fc1 100644 --- a/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.cpp +++ b/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.cpp @@ -135,14 +135,12 @@ bool CSIDevice_GCSteeringWheel::GetData(u32& _Hi, u32& _Low) if(Movie::IsPlayingInput()) { Movie::PlayController(&PadStatus, ISIDevice::m_iDeviceNumber); - if(!Movie::IsUsingWiimote(0)) - Movie::InputUpdate(); + Movie::InputUpdate(); } else if(Movie::IsRecordingInput()) { Movie::RecordInput(&PadStatus, ISIDevice::m_iDeviceNumber); - if(!Movie::IsUsingWiimote(0)) - Movie::InputUpdate(); + Movie::InputUpdate(); } else Movie::CheckPadStatus(&PadStatus, ISIDevice::m_iDeviceNumber); From 3fd1b4ee83e02de1d9ed8d145ec21b88c16b523c Mon Sep 17 00:00:00 2001 From: skidau Date: Tue, 1 Jan 2013 15:13:18 +1100 Subject: [PATCH 4/4] Removed some debug testing code. --- Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.cpp b/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.cpp index f69ccd2fc1..ade19b758e 100644 --- a/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.cpp +++ b/Source/Core/Core/Src/HW/SI_DeviceGCSteeringWheel.cpp @@ -265,8 +265,6 @@ void CSIDevice_GCSteeringWheel::SendCommand(u32 _Cmd, u8 _Poll) // get the correct pad number that should rumble locally when using netplay const u8 numPAD = NetPlay_GetPadNum(ISIDevice::m_iDeviceNumber); - NOTICE_LOG(COMMON, "Type: %d Strength: %d", uType, uStrength); - if (numPAD < 4) Pad::Motor(numPAD, uType, uStrength);