mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 21:37:52 -07:00
SI: Clean up controller-like devices with subclassing
This takes the giant mess of controller-like devices (dance mat and steering wheel) down to something more manageable, similar to how the Donkey Konga bongo controller works. Based-on-a-patch-by: comex <comexk@gmail.com>
This commit is contained in:
parent
ce059769f6
commit
6aa1a59ee8
@ -2,250 +2,40 @@
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "Core/Core.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/Movie.h"
|
||||
#include "Core/HW/EXI_Device.h"
|
||||
#include "Core/HW/EXI_DeviceMic.h"
|
||||
#include "Core/HW/GCPad.h"
|
||||
#include "Core/HW/ProcessorInterface.h"
|
||||
#include "Core/HW/SI.h"
|
||||
#include "Core/HW/SI_Device.h"
|
||||
#include "Core/HW/SI_DeviceDanceMat.h"
|
||||
#include "Core/HW/SystemTimers.h"
|
||||
|
||||
// --- Dance mat GameCube controller ---
|
||||
CSIDevice_DanceMat::CSIDevice_DanceMat(SIDevices device, int _iDeviceNumber)
|
||||
: ISIDevice(device, _iDeviceNumber)
|
||||
, m_TButtonComboStart(0)
|
||||
, m_TButtonCombo(0)
|
||||
, m_LastButtonCombo(COMBO_NONE)
|
||||
: CSIDevice_GCController(device, _iDeviceNumber) {}
|
||||
|
||||
u32 CSIDevice_DanceMat::MapPadStatus(const GCPadStatus& pad_status)
|
||||
{
|
||||
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 = 0x00;
|
||||
m_Origin.uTrigger_R = 0x00;
|
||||
|
||||
// Dunno if we need to do this, game/lib should set it?
|
||||
m_Mode = 0x03;
|
||||
}
|
||||
|
||||
int CSIDevice_DanceMat::RunBuffer(u8* _pBuffer, int _iLength)
|
||||
{
|
||||
// For debug logging only
|
||||
ISIDevice::RunBuffer(_pBuffer, _iLength);
|
||||
|
||||
// Read the command
|
||||
EBufferCommands command = static_cast<EBufferCommands>(_pBuffer[3]);
|
||||
|
||||
// Handle it
|
||||
switch (command)
|
||||
{
|
||||
case CMD_RESET:
|
||||
*(u32*)&_pBuffer[0] = SI_DANCEMAT;
|
||||
break;
|
||||
|
||||
case CMD_DIRECT:
|
||||
{
|
||||
INFO_LOG(SERIALINTERFACE, "PAD - Direct (Length: %d)", _iLength);
|
||||
u32 high, low;
|
||||
GetData(high, low);
|
||||
for (int i = 0; i < (_iLength - 1) / 2; i++)
|
||||
{
|
||||
_pBuffer[i + 0] = (high >> (i * 8)) & 0xff;
|
||||
_pBuffer[i + 4] = (low >> (i * 8)) & 0xff;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_ORIGIN:
|
||||
{
|
||||
INFO_LOG(SERIALINTERFACE, "PAD - Get Origin");
|
||||
u8* pCalibration = reinterpret_cast<u8*>(&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<u8*>(&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);
|
||||
PanicAlert("SI: Unknown 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_DanceMat::GetData(u32& _Hi, u32& _Low)
|
||||
{
|
||||
GCPadStatus PadStatus;
|
||||
memset(&PadStatus, 0, sizeof(PadStatus));
|
||||
|
||||
Pad::GetStatus(ISIDevice::m_iDeviceNumber, &PadStatus);
|
||||
Movie::CallGCInputManip(&PadStatus, ISIDevice::m_iDeviceNumber);
|
||||
|
||||
Movie::SetPolledDevice();
|
||||
if (NetPlay_GetInput(ISIDevice::m_iDeviceNumber, &PadStatus))
|
||||
{
|
||||
}
|
||||
else if (Movie::IsPlayingInput())
|
||||
{
|
||||
Movie::PlayController(&PadStatus, ISIDevice::m_iDeviceNumber);
|
||||
Movie::InputUpdate();
|
||||
}
|
||||
else if (Movie::IsRecordingInput())
|
||||
{
|
||||
Movie::RecordInput(&PadStatus, ISIDevice::m_iDeviceNumber);
|
||||
Movie::InputUpdate();
|
||||
}
|
||||
else
|
||||
{
|
||||
Movie::CheckPadStatus(&PadStatus, ISIDevice::m_iDeviceNumber);
|
||||
}
|
||||
|
||||
// Map the dpad to the blue arrows, the buttons to the orange arrows
|
||||
// Z = + button, Start = - button
|
||||
u16 map = 0;
|
||||
if (PadStatus.button & PAD_BUTTON_UP)
|
||||
if (pad_status.button & PAD_BUTTON_UP)
|
||||
map |= 0x1000;
|
||||
if (PadStatus.button & PAD_BUTTON_DOWN)
|
||||
if (pad_status.button & PAD_BUTTON_DOWN)
|
||||
map |= 0x2;
|
||||
if (PadStatus.button & PAD_BUTTON_LEFT)
|
||||
if (pad_status.button & PAD_BUTTON_LEFT)
|
||||
map |= 0x8;
|
||||
if (PadStatus.button & PAD_BUTTON_RIGHT)
|
||||
if (pad_status.button & PAD_BUTTON_RIGHT)
|
||||
map |= 0x4;
|
||||
if (PadStatus.button & PAD_BUTTON_Y)
|
||||
if (pad_status.button & PAD_BUTTON_Y)
|
||||
map |= 0x200;
|
||||
if (PadStatus.button & PAD_BUTTON_A)
|
||||
if (pad_status.button & PAD_BUTTON_A)
|
||||
map |= 0x10;
|
||||
if (PadStatus.button & PAD_BUTTON_B)
|
||||
if (pad_status.button & PAD_BUTTON_B)
|
||||
map |= 0x100;
|
||||
if (PadStatus.button & PAD_BUTTON_X)
|
||||
if (pad_status.button & PAD_BUTTON_X)
|
||||
map |= 0x800;
|
||||
if (PadStatus.button & PAD_TRIGGER_Z)
|
||||
if (pad_status.button & PAD_TRIGGER_Z)
|
||||
map |= 0x400;
|
||||
if (PadStatus.button & PAD_BUTTON_START)
|
||||
if (pad_status.button & PAD_BUTTON_START)
|
||||
map |= 0x1;
|
||||
|
||||
_Hi = (u32)(map << 16) | 0x8080;
|
||||
|
||||
// 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)
|
||||
{
|
||||
// Identifies the dance mat
|
||||
_Low = 0x8080ffff;
|
||||
}
|
||||
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
|
||||
}
|
||||
return true;
|
||||
return (u32)(map << 16) | 0x8080;
|
||||
}
|
||||
|
||||
|
||||
// SendCommand
|
||||
void CSIDevice_DanceMat::SendCommand(u32 _Cmd, u8 _Poll)
|
||||
void CSIDevice_DanceMat::HandleButtonCombos(const GCPadStatus& pad_status)
|
||||
{
|
||||
UCommand command(_Cmd);
|
||||
|
||||
switch (command.Command)
|
||||
{
|
||||
// Costis sent it in some demos :)
|
||||
case 0x00:
|
||||
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_InGamePadToLocalPad(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);
|
||||
PanicAlert("SI: Unknown direct command");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Savestate support
|
||||
void CSIDevice_DanceMat::DoState(PointerWrap& p)
|
||||
{
|
||||
p.Do(m_Origin);
|
||||
p.Do(m_Mode);
|
||||
p.Do(m_TButtonComboStart);
|
||||
p.Do(m_TButtonCombo);
|
||||
p.Do(m_LastButtonCombo);
|
||||
}
|
||||
|
@ -4,98 +4,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Core/HW/SI_Device.h"
|
||||
#include "InputCommon/GCPadStatus.h"
|
||||
#include "Core/HW/SI_DeviceGCController.h"
|
||||
|
||||
// standard GameCube controller
|
||||
class CSIDevice_DanceMat : public ISIDevice
|
||||
class CSIDevice_DanceMat : public CSIDevice_GCController
|
||||
{
|
||||
private:
|
||||
|
||||
// Commands
|
||||
enum EBufferCommands
|
||||
{
|
||||
CMD_RESET = 0x00,
|
||||
CMD_DIRECT = 0x40,
|
||||
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_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_DanceMat(SIDevices device, int _iDeviceNumber);
|
||||
|
||||
// Run the SI Buffer
|
||||
virtual int RunBuffer(u8* _pBuffer, int _iLength) override;
|
||||
|
||||
// Send and Receive pad input from network
|
||||
static bool NetPlay_GetInput(u8 numPAD, GCPadStatus* status);
|
||||
static u8 NetPlay_InGamePadToLocalPad(u8 numPAD);
|
||||
|
||||
// Return true on new data
|
||||
virtual bool GetData(u32& _Hi, u32& _Low) override;
|
||||
|
||||
// Send a command directly
|
||||
virtual void SendCommand(u32 _Cmd, u8 _Poll) override;
|
||||
|
||||
// Savestate support
|
||||
virtual void DoState(PointerWrap& p) override;
|
||||
virtual u32 MapPadStatus(const GCPadStatus& pad_status) override;
|
||||
virtual void HandleButtonCombos(const GCPadStatus& pad_status) override;
|
||||
};
|
||||
|
@ -131,10 +131,7 @@ bool CSIDevice_GCController::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)((u16)(PadStatus.button | PAD_USE_ORIGIN) << 16);
|
||||
_Hi = MapPadStatus(PadStatus);
|
||||
|
||||
// Low bits are packed differently per mode
|
||||
if (m_Mode == 0 || m_Mode == 5 || m_Mode == 6 || m_Mode == 7)
|
||||
@ -181,11 +178,27 @@ bool CSIDevice_GCController::GetData(u32& _Hi, u32& _Low)
|
||||
_Low |= (u32)((u8)PadStatus.substickX << 24); // All 8 bits
|
||||
}
|
||||
|
||||
HandleButtonCombos(PadStatus);
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 CSIDevice_GCController::MapPadStatus(const GCPadStatus& pad_status)
|
||||
{
|
||||
// Thankfully changing mode does not change the high bits ;)
|
||||
u32 _Hi = 0;
|
||||
_Hi = (u32)((u8)pad_status.stickY);
|
||||
_Hi |= (u32)((u8)pad_status.stickX << 8);
|
||||
_Hi |= (u32)((u16)(pad_status.button | PAD_USE_ORIGIN) << 16);
|
||||
return _Hi;
|
||||
}
|
||||
|
||||
void CSIDevice_GCController::HandleButtonCombos(const GCPadStatus& pad_status)
|
||||
{
|
||||
// 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))
|
||||
if ((pad_status.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))
|
||||
else if ((pad_status.button & 0xff00) == (PAD_BUTTON_B|PAD_BUTTON_X|PAD_BUTTON_START))
|
||||
tempCombo = COMBO_RESET;
|
||||
else
|
||||
tempCombo = COMBO_NONE;
|
||||
@ -204,21 +217,18 @@ bool CSIDevice_GCController::GetData(u32& _Hi, u32& _Low)
|
||||
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_Origin.uOriginStickX = pad_status.stickX;
|
||||
m_Origin.uOriginStickY = pad_status.stickY;
|
||||
m_Origin.uSubStickStickX = pad_status.substickX;
|
||||
m_Origin.uSubStickStickY = pad_status.substickY;
|
||||
m_Origin.uTrigger_L = pad_status.triggerLeft;
|
||||
m_Origin.uTrigger_R = pad_status.triggerRight;
|
||||
}
|
||||
m_LastButtonCombo = COMBO_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// SendCommand
|
||||
void CSIDevice_GCController::SendCommand(u32 _Cmd, u8 _Poll)
|
||||
{
|
||||
|
@ -7,11 +7,9 @@
|
||||
#include "Core/HW/SI_Device.h"
|
||||
#include "InputCommon/GCPadStatus.h"
|
||||
|
||||
|
||||
// standard GameCube controller
|
||||
class CSIDevice_GCController : public ISIDevice
|
||||
{
|
||||
private:
|
||||
protected:
|
||||
|
||||
// Commands
|
||||
enum EBufferCommands
|
||||
@ -24,8 +22,8 @@ private:
|
||||
|
||||
struct SOrigin
|
||||
{
|
||||
u8 uCommand;// Maybe should be button bits?
|
||||
u8 unk_1; // ..and this would be the other half
|
||||
u8 uCommand; // Maybe should be button bits?
|
||||
u8 unk_1; // ..and this would be the other half
|
||||
u8 uOriginStickX;
|
||||
u8 uOriginStickY;
|
||||
u8 uSubStickStickX;
|
||||
@ -94,6 +92,9 @@ public:
|
||||
// Return true on new data
|
||||
virtual bool GetData(u32& _Hi, u32& _Low) override;
|
||||
|
||||
virtual u32 MapPadStatus(const GCPadStatus& pad_status);
|
||||
virtual void HandleButtonCombos(const GCPadStatus& pad_status);
|
||||
|
||||
// Send a command directly
|
||||
virtual void SendCommand(u32 _Cmd, u8 _Poll) override;
|
||||
|
||||
|
@ -2,292 +2,36 @@
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
||||
#include "Core/Core.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/Movie.h"
|
||||
#include "Core/HW/EXI_Device.h"
|
||||
#include "Core/HW/EXI_DeviceMic.h"
|
||||
#include "Core/HW/GCPad.h"
|
||||
#include "Core/HW/ProcessorInterface.h"
|
||||
#include "Core/HW/SI.h"
|
||||
#include "Core/HW/SI_Device.h"
|
||||
#include "Core/HW/SI_DeviceGCSteeringWheel.h"
|
||||
#include "Core/HW/SystemTimers.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;
|
||||
: CSIDevice_GCController(device, _iDeviceNumber)
|
||||
{}
|
||||
|
||||
// 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<EBufferCommands>(_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<u8*>(&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<u8*>(&m_Origin);
|
||||
for (int i = 0; i < (int)sizeof(SOrigin); i++)
|
||||
{
|
||||
_pBuffer[i ^ 3] = *pCalibration++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Seen in F-Zero GX
|
||||
case CMD_MOTOR_OFF:
|
||||
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)
|
||||
{
|
||||
GCPadStatus PadStatus;
|
||||
memset(&PadStatus, 0, sizeof(PadStatus));
|
||||
|
||||
Pad::GetStatus(ISIDevice::m_iDeviceNumber, &PadStatus);
|
||||
Movie::CallGCInputManip(&PadStatus, ISIDevice::m_iDeviceNumber);
|
||||
|
||||
Movie::SetPolledDevice();
|
||||
if (NetPlay_GetInput(ISIDevice::m_iDeviceNumber, &PadStatus))
|
||||
{
|
||||
}
|
||||
else if (Movie::IsPlayingInput())
|
||||
{
|
||||
Movie::PlayController(&PadStatus, ISIDevice::m_iDeviceNumber);
|
||||
Movie::InputUpdate();
|
||||
}
|
||||
else if (Movie::IsRecordingInput())
|
||||
{
|
||||
Movie::RecordInput(&PadStatus, ISIDevice::m_iDeviceNumber);
|
||||
Movie::InputUpdate();
|
||||
}
|
||||
else
|
||||
{
|
||||
Movie::CheckPadStatus(&PadStatus, ISIDevice::m_iDeviceNumber);
|
||||
}
|
||||
|
||||
// Thankfully changing mode does not change the high bits ;)
|
||||
_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 == 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
|
||||
}
|
||||
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;
|
||||
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)
|
||||
if (command.Command == CMD_FORCE)
|
||||
{
|
||||
// Costis sent it in some demos :)
|
||||
case 0x00:
|
||||
break;
|
||||
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
|
||||
|
||||
case CMD_FORCE:
|
||||
// get the correct pad number that should rumble locally when using netplay
|
||||
const u8 numPAD = NetPlay_InGamePadToLocalPad(ISIDevice::m_iDeviceNumber);
|
||||
|
||||
if (numPAD < 4)
|
||||
Pad::Motor(numPAD, uType, uStrength);
|
||||
|
||||
if (!_Poll)
|
||||
{
|
||||
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_InGamePadToLocalPad(ISIDevice::m_iDeviceNumber);
|
||||
|
||||
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);
|
||||
}
|
||||
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_InGamePadToLocalPad(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;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CSIDevice_GCController::SendCommand(_Cmd, _Poll);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
@ -4,14 +4,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Core/HW/SI_Device.h"
|
||||
#include "InputCommon/GCPadStatus.h"
|
||||
#include "Core/HW/SI_DeviceGCController.h"
|
||||
|
||||
// standard GameCube controller
|
||||
class CSIDevice_GCSteeringWheel : public ISIDevice
|
||||
class CSIDevice_GCSteeringWheel : public CSIDevice_GCController
|
||||
{
|
||||
private:
|
||||
|
||||
// Commands
|
||||
enum EBufferCommands
|
||||
{
|
||||
@ -21,82 +18,13 @@ private:
|
||||
CMD_MOTOR_OFF = 0xff,
|
||||
};
|
||||
|
||||
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) override;
|
||||
|
||||
// Send and Receive pad input from network
|
||||
static bool NetPlay_GetInput(u8 numPAD, GCPadStatus* status);
|
||||
static u8 NetPlay_InGamePadToLocalPad(u8 numPAD);
|
||||
|
||||
// Return true on new data
|
||||
virtual bool GetData(u32& _Hi, u32& _Low) override;
|
||||
|
||||
// Send a command directly
|
||||
virtual void SendCommand(u32 _Cmd, u8 _Poll) override;
|
||||
|
||||
// Savestate support
|
||||
virtual void DoState(PointerWrap& p) override;
|
||||
virtual void SendCommand(u32 _Cmd, u8 _Poll);
|
||||
};
|
||||
|
@ -835,16 +835,6 @@ bool WiimoteEmu::Wiimote::NetPlay_GetWiimoteData(int wiimote, u8* data, u8 size)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CSIDevice_GCSteeringWheel::NetPlay_GetInput(u8 numPAD, GCPadStatus* PadStatus)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CSIDevice_DanceMat::NetPlay_GetInput(u8 numPAD, GCPadStatus* PadStatus)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// called from ---CPU--- thread
|
||||
// so all players' games get the same time
|
||||
u32 CEXIIPL::NetPlay_GetGCTime()
|
||||
@ -869,16 +859,6 @@ u8 CSIDevice_GCController::NetPlay_InGamePadToLocalPad(u8 numPAD)
|
||||
return numPAD;
|
||||
}
|
||||
|
||||
u8 CSIDevice_GCSteeringWheel::NetPlay_InGamePadToLocalPad(u8 numPAD)
|
||||
{
|
||||
return CSIDevice_GCController::NetPlay_InGamePadToLocalPad(numPAD);
|
||||
}
|
||||
|
||||
u8 CSIDevice_DanceMat::NetPlay_InGamePadToLocalPad(u8 numPAD)
|
||||
{
|
||||
return CSIDevice_GCController::NetPlay_InGamePadToLocalPad(numPAD);
|
||||
}
|
||||
|
||||
bool NetPlay::IsNetPlayRunning()
|
||||
{
|
||||
return netplay_client != nullptr;
|
||||
|
Loading…
Reference in New Issue
Block a user