Added GC Steering Wheel emulation.

To set it up, change the Port 1 controller to "Steering Wheel" under the GameCube tab.  This will tell the game that you have a force feedback steering wheel connected.
In the Gamecube Pad Settings, change the Rumble Motor to "Constant".
Configure the controls:

Main Stick Left/Right = Steer Left/Right
Main Stick Up = Accelerate
Main Stick Down = Brake

Thanks to ulao for the device communications info.
This commit is contained in:
skidau 2013-01-04 14:08:09 +11:00
commit 4d6056f146
13 changed files with 519 additions and 14 deletions

View File

@ -115,6 +115,7 @@ set(SRCS Src/ActionReplay.cpp
Src/HW/SI_Device.cpp Src/HW/SI_Device.cpp
Src/HW/SI_DeviceGBA.cpp Src/HW/SI_DeviceGBA.cpp
Src/HW/SI_DeviceGCController.cpp Src/HW/SI_DeviceGCController.cpp
Src/HW/SI_DeviceGCSteeringWheel.cpp
Src/HW/Sram.cpp Src/HW/Sram.cpp
Src/HW/StreamADPCM.cpp Src/HW/StreamADPCM.cpp
Src/HW/SystemTimers.cpp Src/HW/SystemTimers.cpp

View File

@ -301,6 +301,7 @@
<ClCompile Include="Src\HW\SI_DeviceAMBaseboard.cpp" /> <ClCompile Include="Src\HW\SI_DeviceAMBaseboard.cpp" />
<ClCompile Include="Src\HW\SI_DeviceGBA.cpp" /> <ClCompile Include="Src\HW\SI_DeviceGBA.cpp" />
<ClCompile Include="Src\HW\SI_DeviceGCController.cpp" /> <ClCompile Include="Src\HW\SI_DeviceGCController.cpp" />
<ClCompile Include="Src\HW\SI_DeviceGCSteeringWheel.cpp" />
<ClCompile Include="Src\HW\Sram.cpp" /> <ClCompile Include="Src\HW\Sram.cpp" />
<ClCompile Include="Src\HW\StreamADPCM.cpp" /> <ClCompile Include="Src\HW\StreamADPCM.cpp" />
<ClCompile Include="Src\HW\SystemTimers.cpp" /> <ClCompile Include="Src\HW\SystemTimers.cpp" />
@ -503,6 +504,7 @@
<ClInclude Include="Src\HW\SI_DeviceAMBaseboard.h" /> <ClInclude Include="Src\HW\SI_DeviceAMBaseboard.h" />
<ClInclude Include="Src\HW\SI_DeviceGBA.h" /> <ClInclude Include="Src\HW\SI_DeviceGBA.h" />
<ClInclude Include="Src\HW\SI_DeviceGCController.h" /> <ClInclude Include="Src\HW\SI_DeviceGCController.h" />
<ClInclude Include="Src\HW\SI_DeviceGCSteeringWheel.h" />
<ClInclude Include="Src\HW\Sram.h" /> <ClInclude Include="Src\HW\Sram.h" />
<ClInclude Include="Src\HW\StreamADPCM.h" /> <ClInclude Include="Src\HW\StreamADPCM.h" />
<ClInclude Include="Src\HW\SystemTimers.h" /> <ClInclude Include="Src\HW\SystemTimers.h" />

View File

@ -290,6 +290,9 @@
<ClCompile Include="Src\HW\SI_DeviceGCController.cpp"> <ClCompile Include="Src\HW\SI_DeviceGCController.cpp">
<Filter>HW %28Flipper/Hollywood%29\SI - Serial Interface</Filter> <Filter>HW %28Flipper/Hollywood%29\SI - Serial Interface</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Src\HW\SI_DeviceGCSteeringWheel.cpp">
<Filter>HW %28Flipper/Hollywood%29\SI - Serial Interface</Filter>
</ClCompile>
<ClCompile Include="Src\HW\VideoInterface.cpp"> <ClCompile Include="Src\HW\VideoInterface.cpp">
<Filter>HW %28Flipper/Hollywood%29\VI - Video Interface</Filter> <Filter>HW %28Flipper/Hollywood%29\VI - Video Interface</Filter>
</ClCompile> </ClCompile>
@ -814,6 +817,9 @@
<ClInclude Include="Src\HW\SI_DeviceGCController.h"> <ClInclude Include="Src\HW\SI_DeviceGCController.h">
<Filter>HW %28Flipper/Hollywood%29\SI - Serial Interface</Filter> <Filter>HW %28Flipper/Hollywood%29\SI - Serial Interface</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Src\HW\SI_DeviceGCSteeringWheel.h">
<Filter>HW %28Flipper/Hollywood%29\SI - Serial Interface</Filter>
</ClInclude>
<ClInclude Include="Src\HW\SI.h"> <ClInclude Include="Src\HW\SI.h">
<Filter>HW %28Flipper/Hollywood%29\SI - Serial Interface</Filter> <Filter>HW %28Flipper/Hollywood%29\SI - Serial Interface</Filter>
</ClInclude> </ClInclude>

View File

@ -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 // TODO: this has potential to not stop rumble if user is messing with GUI at the perfect time
// set rumble // 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<std::recursive_mutex> 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);
}
} }
} }

View File

@ -32,6 +32,7 @@ InputPlugin *GetPlugin();
void GetStatus(u8 _numPAD, SPADStatus* _pPADStatus); void GetStatus(u8 _numPAD, SPADStatus* _pPADStatus);
void Rumble(u8 _numPAD, unsigned int _uType, unsigned int _uStrength); void Rumble(u8 _numPAD, unsigned int _uType, unsigned int _uStrength);
void Motor(u8 _numPAD, unsigned int _uType, unsigned int _uStrength);
bool GetMicButton(u8 pad); bool GetMicButton(u8 pad);
} }

View File

@ -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 // 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) void GCPad::LoadDefaults(const ControllerInterface& ciface)

View File

@ -28,7 +28,7 @@ public:
GCPad(const unsigned int index); GCPad(const unsigned int index);
void GetInput(SPADStatus* const pad); void GetInput(SPADStatus* const pad);
void SetOutput(const bool on); void SetOutput(const u8 on);
bool GetMicButton() const; bool GetMicButton() const;

View File

@ -17,6 +17,7 @@
#include "SI_Device.h" #include "SI_Device.h"
#include "SI_DeviceGCController.h" #include "SI_DeviceGCController.h"
#include "SI_DeviceGCSteeringWheel.h"
#include "SI_DeviceGBA.h" #include "SI_DeviceGBA.h"
#include "SI_DeviceAMBaseboard.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); return new CSIDevice_GCController(device, port_number);
break; break;
case SIDEVICE_GC_STEERING:
return new CSIDevice_GCSteeringWheel(device, port_number);
break;
case SIDEVICE_GC_TARUKONGA: case SIDEVICE_GC_TARUKONGA:
return new CSIDevice_TaruKonga(device, port_number); return new CSIDevice_TaruKonga(device, port_number);
break; break;

View File

@ -0,0 +1,314 @@
// 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 <stdio.h>
#include <stdlib.h>
#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<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)
{
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);
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)
{
// 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);
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);
}

View File

@ -0,0 +1,119 @@
// 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,
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);
// 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

View File

@ -22,6 +22,7 @@
#include "IPC_HLE/WII_IPC_HLE_WiiMote.h" #include "IPC_HLE/WII_IPC_HLE_WiiMote.h"
// for gcpad // for gcpad
#include "HW/SI_DeviceGCController.h" #include "HW/SI_DeviceGCController.h"
#include "HW/SI_DeviceGCSteeringWheel.h"
// for gctime // for gctime
#include "HW/EXI_DeviceIPL.h" #include "HW/EXI_DeviceIPL.h"
// for wiimote/ OSD messages // for wiimote/ OSD messages
@ -296,6 +297,11 @@ bool CSIDevice_GCController::NetPlay_GetInput(u8 numPAD, SPADStatus PadStatus, u
return false; 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 // called from ---CPU--- thread
// so all players' games get the same time // so all players' games get the same time
u32 CEXIIPL::NetPlay_GetGCTime() u32 CEXIIPL::NetPlay_GetGCTime()
@ -320,6 +326,11 @@ u8 CSIDevice_GCController::NetPlay_GetPadNum(u8 numPAD)
return numPAD; return numPAD;
} }
u8 CSIDevice_GCSteeringWheel::NetPlay_GetPadNum(u8 numPAD)
{
return CSIDevice_GCController::NetPlay_GetPadNum(numPAD);
}
// called from ---CPU--- thread // called from ---CPU--- thread
// wiimote update / used for frame counting // wiimote update / used for frame counting
//void CWII_IPC_HLE_Device_usb_oh1_57e_305::NetPlay_WiimoteUpdate(int _number) //void CWII_IPC_HLE_Device_usb_oh1_57e_305::NetPlay_WiimoteUpdate(int _number)

View File

@ -79,6 +79,7 @@ static const wxLanguage langIds[] =
#define DEV_DUMMY_STR _trans("Dummy") #define DEV_DUMMY_STR _trans("Dummy")
#define SIDEV_STDCONT_STR _trans("Standard Controller") #define SIDEV_STDCONT_STR _trans("Standard Controller")
#define SIDEV_STEERING_STR _trans("Steering Wheel")
#define SIDEV_BONGO_STR _trans("TaruKonga (Bongos)") #define SIDEV_BONGO_STR _trans("TaruKonga (Bongos)")
#define SIDEV_GBA_STR "GBA" #define SIDEV_GBA_STR "GBA"
#define SIDEV_AM_BB_STR _trans("AM-Baseboard") #define SIDEV_AM_BB_STR _trans("AM-Baseboard")
@ -391,6 +392,7 @@ void CConfigMain::InitializeGUIValues()
wxArrayString SIDevices; wxArrayString SIDevices;
SIDevices.Add(_(DEV_NONE_STR)); SIDevices.Add(_(DEV_NONE_STR));
SIDevices.Add(_(SIDEV_STDCONT_STR)); SIDevices.Add(_(SIDEV_STDCONT_STR));
SIDevices.Add(_(SIDEV_STEERING_STR));
SIDevices.Add(_(SIDEV_BONGO_STR)); SIDevices.Add(_(SIDEV_BONGO_STR));
SIDevices.Add(_(SIDEV_GBA_STR)); SIDevices.Add(_(SIDEV_GBA_STR));
SIDevices.Add(_(SIDEV_AM_BB_STR)); SIDevices.Add(_(SIDEV_AM_BB_STR));
@ -443,15 +445,18 @@ void CConfigMain::InitializeGUIValues()
case SIDEVICE_GC_CONTROLLER: case SIDEVICE_GC_CONTROLLER:
GCSIDevice[i]->SetStringSelection(SIDevices[1]); GCSIDevice[i]->SetStringSelection(SIDevices[1]);
break; break;
case SIDEVICE_GC_TARUKONGA: case SIDEVICE_GC_STEERING:
GCSIDevice[i]->SetStringSelection(SIDevices[2]); GCSIDevice[i]->SetStringSelection(SIDevices[2]);
break; break;
case SIDEVICE_GC_GBA: case SIDEVICE_GC_TARUKONGA:
GCSIDevice[i]->SetStringSelection(SIDevices[3]); GCSIDevice[i]->SetStringSelection(SIDevices[3]);
break; break;
case SIDEVICE_AM_BASEBOARD: case SIDEVICE_GC_GBA:
GCSIDevice[i]->SetStringSelection(SIDevices[4]); GCSIDevice[i]->SetStringSelection(SIDevices[4]);
break; break;
case SIDEVICE_AM_BASEBOARD:
GCSIDevice[i]->SetStringSelection(SIDevices[5]);
break;
default: default:
GCSIDevice[i]->SetStringSelection(SIDevices[0]); GCSIDevice[i]->SetStringSelection(SIDevices[0]);
break; break;
@ -1059,6 +1064,8 @@ void CConfigMain::ChooseSIDevice(wxString deviceName, int deviceNum)
SIDevices tempType; SIDevices tempType;
if (!deviceName.compare(WXSTR_TRANS(SIDEV_STDCONT_STR))) if (!deviceName.compare(WXSTR_TRANS(SIDEV_STDCONT_STR)))
tempType = SIDEVICE_GC_CONTROLLER; 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))) else if (!deviceName.compare(WXSTR_TRANS(SIDEV_BONGO_STR)))
tempType = SIDEVICE_GC_TARUKONGA; tempType = SIDEVICE_GC_TARUKONGA;
else if (!deviceName.compare(wxT(SIDEV_GBA_STR))) else if (!deviceName.compare(wxT(SIDEV_GBA_STR)))

View File

@ -254,9 +254,9 @@ Joystick::Joystick( /*const LPCDIDEVICEINSTANCE lpddi, */const LPDIRECTINPUTDEVI
for (unsigned int offset = 0; offset < DIJOFS_BUTTON(0) / sizeof(LONG); ++offset) for (unsigned int offset = 0; offset < DIJOFS_BUTTON(0) / sizeof(LONG); ++offset)
{ {
range.diph.dwObj = offset * sizeof(LONG); range.diph.dwObj = offset * sizeof(LONG);
// try to set some nice power of 2 values (8192) // try to set some nice power of 2 values (128) to match the GameCube controls
range.lMin = -(1 << 13); range.lMin = -(1 << 7);
range.lMax = (1 << 13); range.lMax = (1 << 7);
m_device->SetProperty(DIPROP_RANGE, &range.diph); m_device->SetProperty(DIPROP_RANGE, &range.diph);
// but i guess not all devices support setting range // but i guess not all devices support setting range
// so i getproperty right afterward incase it didn't set :P // 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() ) if ( objects.size() )
{ {
// temporary // temporary
DWORD rgdwAxes[] = {DIJOFS_X, DIJOFS_Y}; DWORD rgdwAxes[2] = {DIJOFS_X, DIJOFS_Y};
LONG rglDirection[] = {0, 0}; LONG rglDirection[2] = {-200, 0};
DIEFFECT eff; DIEFFECT eff;
ZeroMemory(&eff, sizeof(eff)); ZeroMemory(&eff, sizeof(eff));
eff.dwSize = sizeof(DIEFFECT); eff.dwSize = sizeof(DIEFFECT);
eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
eff.dwDuration = INFINITE; // (4 * DI_SECONDS) eff.dwDuration = INFINITE; // (4 * DI_SECONDS)
eff.dwSamplePeriod = 0;
eff.dwGain = DI_FFNOMINALMAX; eff.dwGain = DI_FFNOMINALMAX;
eff.dwTriggerButton = DIEB_NOTRIGGER; 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.rgdwAxes = rgdwAxes;
eff.rglDirection = rglDirection; eff.rglDirection = rglDirection;
@ -310,7 +312,12 @@ Joystick::Joystick( /*const LPCDIDEVICEINSTANCE lpddi, */const LPDIRECTINPUTDEVI
{ {
// ugly if ladder // ugly if ladder
if (0 == f) if (0 == f)
{
DICONSTANTFORCE diCF = {-10000};
diCF.lMagnitude = DI_FFNOMINALMAX;
eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
eff.lpvTypeSpecificParams = &diCF;
}
else if (1 == f) else if (1 == f)
eff.cbTypeSpecificParams = sizeof(DIRAMPFORCE); eff.cbTypeSpecificParams = sizeof(DIRAMPFORCE);
else else
@ -528,7 +535,11 @@ ControlState Joystick::Hat::GetState() const
void Joystick::ForceConstant::SetState(const ControlState state) 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; LONG &val = params.lMagnitude;
if (val != new_val) if (val != new_val)