You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1201 lines
35 KiB
1201 lines
35 KiB
//@doc
|
|
/******************************************************
|
|
**
|
|
** @module FFDEVICE.CPP | Implementation file for FFDevice class
|
|
**
|
|
** Description:
|
|
**
|
|
** History:
|
|
** Created 11/17/97 Matthew L. Coill (mlc)
|
|
**
|
|
** 21-Mar-99 waltw Added dwDeviceID to SetFirmwareVersion,
|
|
** InitJoystickParams, StateChange, InitRTCSpring,
|
|
** InitRTCSpring200
|
|
**
|
|
** (c) 1986-1997 Microsoft Corporation. All Rights Reserved.
|
|
******************************************************/
|
|
#include "FFDevice.h"
|
|
#include "Midi_obj.hpp"
|
|
#include "DTrans.h"
|
|
#include "DPack.h"
|
|
#include "joyregst.hpp"
|
|
#include "CritSec.h"
|
|
#include <crt/io.h> // For file routines
|
|
#include <FCNTL.h> // File _open flags
|
|
#include <math.h> // for sin and cos
|
|
|
|
extern CJoltMidi* g_pJoltMidi;
|
|
|
|
//
|
|
// --- VFX Force File defines
|
|
//
|
|
#define FCC_FORCE_EFFECT_RIFF mmioFOURCC('F','O','R','C')
|
|
#define FCC_INFO_LIST mmioFOURCC('I','N','F','O')
|
|
#define FCC_INFO_NAME_CHUNK mmioFOURCC('I','N','A','M')
|
|
#define FCC_INFO_COMMENT_CHUNK mmioFOURCC('I','C','M','T')
|
|
#define FCC_INFO_SOFTWARE_CHUNK mmioFOURCC('I','S','F','T')
|
|
#define FCC_INFO_COPYRIGHT_CHUNK mmioFOURCC('I','C','O','P')
|
|
#define FCC_TARGET_DEVICE_CHUNK mmioFOURCC('t','r','g','t')
|
|
#define FCC_TRACK_LIST mmioFOURCC('t','r','a','k')
|
|
#define FCC_EFFECT_LIST mmioFOURCC('e','f','c','t')
|
|
#define FCC_ID_CHUNK mmioFOURCC('i','d',' ',' ')
|
|
#define FCC_DATA_CHUNK mmioFOURCC('d','a','t','a')
|
|
#define FCC_IMPLICIT_CHUNK mmioFOURCC('i','m','p','l')
|
|
#define FCC_SPLINE_CHUNK mmioFOURCC('s','p','l','n')
|
|
|
|
ForceFeedbackDevice g_ForceFeedbackDevice;
|
|
|
|
#include <errno.h> // For open file errors
|
|
HRESULT LoadBufferFromFile(const char* fileName, PBYTE& pBufferBytes, ULONG& numFileBytes)
|
|
{
|
|
if (pBufferBytes != NULL) {
|
|
ASSUME_NOT_REACHED();
|
|
numFileBytes = 0;
|
|
return VFX_ERR_FILE_OUT_OF_MEMORY;
|
|
}
|
|
|
|
int fHandle = ::_open(fileName, _O_RDONLY | _O_BINARY);
|
|
if (fHandle == -1) {
|
|
numFileBytes = 0;
|
|
switch (errno) {
|
|
case EACCES : return VFX_ERR_FILE_ACCESS_DENIED;
|
|
case EMFILE : return VFX_ERR_FILE_TOO_MANY_OPEN_FILES;
|
|
case ENOENT : return VFX_ERR_FILE_NOT_FOUND;
|
|
}
|
|
return VFX_ERR_FILE_CANNOT_OPEN; // Who knows what went wrong
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
numFileBytes = ::_lseek(fHandle, 0, SEEK_END);
|
|
if (numFileBytes == -1) { // Seek failed
|
|
hr = VFX_ERR_FILE_CANNOT_SEEK;
|
|
} else if (numFileBytes == 0) { // Empty file
|
|
hr = VFX_ERR_FILE_BAD_FORMAT;
|
|
} else {
|
|
pBufferBytes = new BYTE[numFileBytes];
|
|
if (pBufferBytes == NULL) { // Could not allocate memory
|
|
hr = VFX_ERR_FILE_OUT_OF_MEMORY;
|
|
} else {
|
|
if (::_lseek(fHandle, 0, SEEK_SET) == -1) { // Failed seek to begining
|
|
hr = VFX_ERR_FILE_CANNOT_SEEK;
|
|
} else if (::_read(fHandle, pBufferBytes, numFileBytes) == -1) { // Failed to read
|
|
hr = VFX_ERR_FILE_CANNOT_READ;
|
|
}
|
|
if (hr != S_OK) { // Things didn't go well
|
|
delete[] pBufferBytes;
|
|
pBufferBytes = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
::_close(fHandle);
|
|
return hr;
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::ForceFeedbackDevice()
|
|
**
|
|
** @mfunc Constructor.
|
|
**
|
|
******************************************************/
|
|
ForceFeedbackDevice::ForceFeedbackDevice() :
|
|
m_FirmwareAckNackValues(0),
|
|
m_FirmwareVersionMajor(0),
|
|
m_FirmwareVersionMinor(0),
|
|
m_DriverVersionMajor(0),
|
|
m_DriverVersionMinor(0),
|
|
m_SpringOffset(0),
|
|
m_Mapping(0),
|
|
m_DIStateFlags(0),
|
|
m_RawForceX(0),
|
|
m_RawForceY(0)
|
|
{
|
|
m_OSVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
::GetVersionEx(&m_OSVersion);
|
|
|
|
for (int index = 0; index < 14; index++) {
|
|
m_PercentMappings[index] = 100; // Default is 100 percent till I'm told otherwise
|
|
}
|
|
for (index = 0; index < MAX_EFFECT_IDS; index++) {
|
|
m_EffectList[index] = NULL;
|
|
}
|
|
m_SystemEffect = NULL;
|
|
|
|
::memset(&m_Version200State, 0, sizeof(m_Version200State));
|
|
::memset(&m_LastStatusPacket, 0, sizeof(m_LastStatusPacket));
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::~ForceFeedbackDevice()
|
|
**
|
|
** @mfunc Destructor.
|
|
**
|
|
******************************************************/
|
|
ForceFeedbackDevice::~ForceFeedbackDevice()
|
|
{
|
|
BOOL lingerer = FALSE;
|
|
|
|
// Destroy the RTCSpring and SystemEffect if still hanging aroung
|
|
if (m_EffectList[0] != NULL) {
|
|
delete m_EffectList[0];
|
|
m_EffectList[0] = NULL;
|
|
}
|
|
if (m_SystemEffect != NULL) {
|
|
delete m_SystemEffect;
|
|
m_SystemEffect = NULL;
|
|
}
|
|
|
|
// Destroy any lingering effects (of which there should be none)
|
|
for (int index = 0; index < MAX_EFFECT_IDS; index++) {
|
|
if (m_EffectList[index] != NULL) {
|
|
lingerer = TRUE;
|
|
delete m_EffectList[index];
|
|
m_EffectList[index] = NULL;
|
|
}
|
|
}
|
|
|
|
ASSUME(lingerer == FALSE); // Assuming programmer cleaned up thier own mess
|
|
}
|
|
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::DetectHardware()
|
|
**
|
|
** @mfunc DetectHardware.
|
|
**
|
|
******************************************************/
|
|
BOOL ForceFeedbackDevice::DetectHardware()
|
|
{
|
|
if (NULL == g_pJoltMidi)
|
|
return (FALSE);
|
|
else
|
|
return g_pJoltMidi->QueryForJolt();
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::SetFirmwareVersion(DWORD dwDeviceID, DWORD major, DWORD minor)
|
|
**
|
|
** @mfunc SetFirmwareVersion.
|
|
**
|
|
******************************************************/
|
|
void ForceFeedbackDevice::SetFirmwareVersion(DWORD dwDeviceID, DWORD major, DWORD minor)
|
|
{
|
|
m_FirmwareVersionMajor = major;
|
|
m_FirmwareVersionMinor = minor;
|
|
|
|
if (g_pDataPackager != NULL) {
|
|
delete g_pDataPackager;
|
|
g_pDataPackager = NULL;
|
|
}
|
|
|
|
if (m_FirmwareVersionMajor == 1) {
|
|
ASSUME_NOT_REACHED(); // Currently this code only supports wheel - this is a Jolt version
|
|
// g_pDataPackager = new DataPackager100();
|
|
} else { // Till version number is locked down
|
|
g_pDataPackager = new DataPackager200();
|
|
}
|
|
|
|
ASSUME_NOT_NULL(g_pDataPackager);
|
|
|
|
m_FirmwareAckNackValues = GetAckNackMethodFromRegistry(dwDeviceID);
|
|
m_SpringOffset = GetSpringOffsetFromRegistry(dwDeviceID);
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::SetDriverVersion(DWORD major, DWORD minor)
|
|
**
|
|
** @mfunc SetDriverVersion.
|
|
**
|
|
******************************************************/
|
|
void ForceFeedbackDevice::SetDriverVersion(DWORD major, DWORD minor)
|
|
{
|
|
if ((major == 0xFFFFFFFF) && (minor == 0xFFFFFFFF)) { // Check for version 1.0 driver version error
|
|
m_DriverVersionMajor = 1;
|
|
m_DriverVersionMinor = 0;
|
|
} else {
|
|
m_DriverVersionMajor = major;
|
|
m_DriverVersionMinor = minor;
|
|
}
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::GetYMappingPercent(UINT index)
|
|
**
|
|
** @mfunc GetYMappingPercent.
|
|
**
|
|
******************************************************/
|
|
short ForceFeedbackDevice::GetYMappingPercent(UINT index) const
|
|
{
|
|
if (m_Mapping & Y_AXIS) {
|
|
if (index < 14) {
|
|
return m_PercentMappings[index];
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::GetEffect(DWORD effectID) const
|
|
**
|
|
** @mfunc GetEffect.
|
|
**
|
|
******************************************************/
|
|
InternalEffect* ForceFeedbackDevice::GetEffect(DWORD effectID) const
|
|
{
|
|
if (effectID == SYSTEM_EFFECT_ID) { // SystemEffect not stored in array
|
|
return m_SystemEffect;
|
|
}
|
|
|
|
if (effectID == SYSTEM_RTCSPRING_ALIAS_ID) { // Remapping of RTC spring
|
|
return m_EffectList[0];
|
|
}
|
|
|
|
if (effectID == RAW_FORCE_ALIAS) {
|
|
return NULL;
|
|
}
|
|
|
|
// Parameter check
|
|
if (effectID >= MAX_EFFECT_IDS) {
|
|
ASSUME_NOT_REACHED();
|
|
return NULL;
|
|
}
|
|
|
|
return m_EffectList[effectID];
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::RemoveEffect(DWORD effectID) const
|
|
**
|
|
** @mfunc GetEffect.
|
|
**
|
|
******************************************************/
|
|
InternalEffect* ForceFeedbackDevice::RemoveEffect(DWORD effectID)
|
|
{
|
|
// There really is no raw force effect
|
|
if (effectID == RAW_FORCE_ALIAS) {
|
|
return NULL;
|
|
}
|
|
|
|
// Cannot remove system effects
|
|
if ((effectID == SYSTEM_EFFECT_ID) || (effectID == 0) || (effectID == SYSTEM_RTCSPRING_ALIAS_ID)) {
|
|
ASSUME_NOT_REACHED();
|
|
return NULL;
|
|
}
|
|
|
|
// Parameter check
|
|
if (effectID >= MAX_EFFECT_IDS) {
|
|
ASSUME_NOT_REACHED();
|
|
return NULL;
|
|
}
|
|
|
|
InternalEffect* pEffect = m_EffectList[effectID];
|
|
m_EffectList[effectID] = NULL;
|
|
return pEffect;
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::InitRTCSpring()
|
|
**
|
|
** @mfunc InitRTCSpring.
|
|
**
|
|
******************************************************/
|
|
HRESULT ForceFeedbackDevice::InitRTCSpring(DWORD dwDeviceID)
|
|
{
|
|
if (g_pDataPackager == NULL) {
|
|
ASSUME_NOT_REACHED();
|
|
return SFERR_DRIVER_ERROR; // No global data packager
|
|
}
|
|
if (g_pDataTransmitter == NULL) {
|
|
ASSUME_NOT_REACHED();
|
|
return SFERR_DRIVER_ERROR; // No global data transmitter
|
|
}
|
|
|
|
if (GetFirmwareVersionMajor() == 1) {
|
|
return InitRTCSpring1XX(dwDeviceID);
|
|
}
|
|
return InitRTCSpring200(dwDeviceID);
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::InitRTCSpring1XX()
|
|
**
|
|
** @mfunc InitRTCSpring.
|
|
**
|
|
******************************************************/
|
|
HRESULT ForceFeedbackDevice::InitRTCSpring1XX(DWORD dwDeviceID)
|
|
{
|
|
// Sanity Checks
|
|
if (GetEffect(SYSTEM_RTCSPRING_ID) != NULL) {
|
|
ASSUME_NOT_REACHED();
|
|
return SUCCESS; // Already initialized
|
|
}
|
|
|
|
// DIEFFECT structure to fill
|
|
DICONDITION cond[2];
|
|
DIEFFECT rtc;
|
|
rtc.dwSize = sizeof(DIEFFECT);
|
|
rtc.cbTypeSpecificParams = sizeof(DICONDITION) * 2;
|
|
rtc.lpvTypeSpecificParams = cond;
|
|
|
|
// The default RTCSpring (the one on the stick)
|
|
RTCSpring1XX rtcSpring1XX; // Def Parms filled by constructor
|
|
|
|
// Default RTCSpring from the registry
|
|
RTCSPRING_PARAM parms;
|
|
GetSystemParams(dwDeviceID, (SYSTEM_PARAMS*)(&parms));
|
|
cond[0].lPositiveCoefficient = parms.m_XKConstant;
|
|
cond[1].lPositiveCoefficient = parms.m_YKConstant;
|
|
cond[0].lOffset = parms.m_XAxisCenter;
|
|
cond[1].lOffset = parms.m_YAxisCenter;
|
|
cond[0].dwPositiveSaturation = parms.m_XSaturation;
|
|
cond[1].dwPositiveSaturation = parms.m_YSaturation;
|
|
cond[0].lDeadBand = parms.m_XDeadBand;
|
|
cond[1].lDeadBand = parms.m_YDeadBand;
|
|
|
|
// Allocate and create the RTCSpring
|
|
InternalEffect* pNewRTCSpring = InternalEffect::CreateRTCSpring();
|
|
if (pNewRTCSpring == NULL) {
|
|
return SFERR_DRIVER_ERROR;
|
|
}
|
|
if (pNewRTCSpring->Create(rtc) != SUCCESS) {
|
|
delete pNewRTCSpring; // Could not create system RTC Spring
|
|
return SFERR_DRIVER_ERROR;
|
|
}
|
|
|
|
// Replace the stick default with the registry default
|
|
SetEffect(SYSTEM_RTCSPRING_ALIAS_ID, &rtcSpring1XX); // Temporary pointer needed (but we only store temporarily)
|
|
g_pDataPackager->ModifyEffect(rtcSpring1XX, *pNewRTCSpring, 0); // Package relative changes
|
|
SetEffect(SYSTEM_RTCSPRING_ALIAS_ID, pNewRTCSpring); // Replace the old with the new
|
|
|
|
pNewRTCSpring = NULL; // Forgotten, but not gone
|
|
|
|
ACKNACK ackNack;
|
|
return g_pDataTransmitter->Transmit(ackNack); // Send it off
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::InitRTCSpring200()
|
|
**
|
|
** @mfunc InitRTCSpring.
|
|
**
|
|
******************************************************/
|
|
HRESULT ForceFeedbackDevice::InitRTCSpring200(DWORD dwDeviceID)
|
|
{
|
|
// Sanity Checks
|
|
if (GetEffect(ID_RTCSPRING_200) != NULL) {
|
|
ASSUME_NOT_REACHED();
|
|
return SUCCESS; // Already initialized
|
|
}
|
|
|
|
// The temporary spring and the allocated one
|
|
InternalEffect* pNewRTCSpring = NULL;
|
|
|
|
// DIEFFECT structure to fill
|
|
DICONDITION cond[2];
|
|
DIEFFECT rtc;
|
|
rtc.dwSize = sizeof(DIEFFECT);
|
|
rtc.cbTypeSpecificParams = sizeof(DICONDITION) * 2;
|
|
rtc.lpvTypeSpecificParams = cond;
|
|
|
|
|
|
// The default RTCSpring (the one on the stick)
|
|
RTCSpring200 rtcSpring200; // Default values filled in by constructor
|
|
|
|
// Default RTCSpring from the registry
|
|
GetRTCSpringData(dwDeviceID, cond);
|
|
|
|
// Allocate and create the RTCSpring
|
|
pNewRTCSpring = InternalEffect::CreateRTCSpring();
|
|
if (pNewRTCSpring == NULL) {
|
|
return SFERR_DRIVER_ERROR;
|
|
}
|
|
HRESULT createResult = pNewRTCSpring->Create(rtc);
|
|
if (FAILED(createResult)) {
|
|
delete pNewRTCSpring; // Could not create system RTC Spring
|
|
return SFERR_DRIVER_ERROR;
|
|
}
|
|
|
|
// Replace the stick default with the registry default
|
|
SetEffect(SYSTEM_RTCSPRING_ALIAS_ID, &rtcSpring200); // Temporary pointer needed (but we only store temporarily)
|
|
g_pDataPackager->ModifyEffect(rtcSpring200, *pNewRTCSpring, 0); // Package relative changes
|
|
SetEffect(SYSTEM_RTCSPRING_ALIAS_ID, pNewRTCSpring); // Replace the old with the new
|
|
|
|
pNewRTCSpring = NULL; // Forgotten, but not gone
|
|
ACKNACK ackNack;
|
|
HRESULT transmitResult = g_pDataTransmitter->Transmit(ackNack); // Send it off
|
|
if (transmitResult != S_OK) {
|
|
return transmitResult;
|
|
}
|
|
return createResult;
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::InitJoystickParams()
|
|
**
|
|
** @mfunc InitRTCSpring.
|
|
**
|
|
******************************************************/
|
|
HRESULT ForceFeedbackDevice::InitJoystickParams(DWORD dwDeviceID)
|
|
{
|
|
// Sanity Checks
|
|
if (GetEffect(SYSTEM_EFFECT_ID) != NULL) {
|
|
ASSUME_NOT_REACHED();
|
|
return SUCCESS; // Already initialized
|
|
}
|
|
if (g_pDataPackager == NULL) {
|
|
ASSUME_NOT_REACHED();
|
|
return SFERR_DRIVER_ERROR; // No global data packager
|
|
}
|
|
if (g_pDataTransmitter == NULL) {
|
|
ASSUME_NOT_REACHED();
|
|
return SFERR_DRIVER_ERROR; // No global data transmitter
|
|
}
|
|
|
|
// Force Mapping
|
|
m_Mapping = ::GetMapping(dwDeviceID);
|
|
::GetMappingPercents(dwDeviceID, m_PercentMappings, 14);
|
|
|
|
|
|
if (GetFirmwareVersionMajor() == 1) {
|
|
// The default System Effect (the one on the stick)
|
|
SystemEffect1XX systemEffect;
|
|
|
|
// Default System Effect from the registry
|
|
SystemStickData1XX sysData;
|
|
sysData.SetFromRegistry(dwDeviceID);
|
|
|
|
// Put the registry system values into a DIEFFECT
|
|
DIEFFECT systemDIEffect;
|
|
systemDIEffect.dwSize = sizeof(DIEFFECT);
|
|
systemDIEffect.cbTypeSpecificParams = sizeof(SystemStickData1XX);
|
|
systemDIEffect.lpvTypeSpecificParams = &sysData;
|
|
|
|
// Get a system effect (and fill it with our local DIEffect information
|
|
SystemEffect1XX* pSystemEffect = (SystemEffect1XX*)(InternalEffect::CreateSystemEffect());
|
|
if (pSystemEffect == NULL) {
|
|
return SFERR_DRIVER_ERROR;
|
|
}
|
|
if (pSystemEffect->Create(systemDIEffect) != SUCCESS) {
|
|
delete pSystemEffect; // Couldnot create SystemEffect
|
|
return SFERR_DRIVER_ERROR;
|
|
}
|
|
|
|
// Replace the stick default with the registry default
|
|
SetEffect(SYSTEM_EFFECT_ID, &systemEffect); // Temporary pointer (but we only store temporarily)
|
|
g_pDataPackager->ModifyEffect(systemEffect, *pSystemEffect, 0); // Package relative changes
|
|
SetEffect(SYSTEM_EFFECT_ID, pSystemEffect); // Replace the old with the new
|
|
pSystemEffect = NULL; // Forgotten, but not gone
|
|
ACKNACK ackNack;
|
|
return g_pDataTransmitter->Transmit(ackNack); // Send it off
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::StateChange(DWORD newStateFlags)
|
|
**
|
|
** @mfunc StateChange.
|
|
**
|
|
******************************************************/
|
|
void ForceFeedbackDevice::StateChange(DWORD dwDeviceID, DWORD newStateFlag)
|
|
{
|
|
if (newStateFlag == DISFFC_RESET) { // Stick is reset need to remove local copies of user commands
|
|
// Remove all effect from our list
|
|
for (int index = 2; index < MAX_EFFECT_IDS; index++) {
|
|
if (m_EffectList[index] != NULL) {
|
|
delete m_EffectList[index];
|
|
m_EffectList[index] = NULL;
|
|
}
|
|
}
|
|
|
|
// Remove individual axis raw effects
|
|
m_RawForceX = 0;
|
|
m_RawForceY = 0;
|
|
|
|
// Look at the Y mapping, perhaps it changed
|
|
m_Mapping = ::GetMapping(dwDeviceID);
|
|
::GetMappingPercents(dwDeviceID, m_PercentMappings, 14);
|
|
} else if (newStateFlag == DISFFC_STOPALL) {
|
|
m_RawForceX = 0;
|
|
m_RawForceY = 0;
|
|
}
|
|
|
|
m_DIStateFlags = newStateFlag;
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::CreateConditionEffect(DWORD subType, const DIEFFECT& diEffect, HRESULT& hr)
|
|
**
|
|
** @mfunc CreateConditionEffect.
|
|
**
|
|
******************************************************/
|
|
InternalEffect* ForceFeedbackDevice::CreateConditionEffect(DWORD minorType, const DIEFFECT& diEffect, HRESULT& hr)
|
|
{
|
|
InternalEffect* pReturnEffect = NULL;
|
|
switch (minorType) {
|
|
case BE_SPRING:
|
|
case BE_SPRING_2D: {
|
|
pReturnEffect = InternalEffect::CreateSpring();
|
|
break;
|
|
}
|
|
case BE_DAMPER:
|
|
case BE_DAMPER_2D: {
|
|
pReturnEffect = InternalEffect::CreateDamper();
|
|
break;
|
|
}
|
|
case BE_INERTIA:
|
|
case BE_INERTIA_2D: {
|
|
pReturnEffect = InternalEffect::CreateInertia();
|
|
break;
|
|
}
|
|
case BE_FRICTION:
|
|
case BE_FRICTION_2D: {
|
|
pReturnEffect = InternalEffect::CreateFriction();
|
|
break;
|
|
}
|
|
case BE_WALL: {
|
|
pReturnEffect = InternalEffect::CreateWall();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pReturnEffect != NULL) {
|
|
hr = pReturnEffect->Create(diEffect);
|
|
if (FAILED(hr)) {
|
|
delete pReturnEffect;
|
|
}
|
|
}
|
|
return pReturnEffect;
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::CreateRTCSpringEffect(DWORD subType, const DIEFFECT& diEffect)
|
|
**
|
|
** @mfunc CreateRTCSpringEffect.
|
|
**
|
|
******************************************************/
|
|
InternalEffect* ForceFeedbackDevice::CreateRTCSpringEffect(DWORD minorType, const DIEFFECT& diEffect)
|
|
{
|
|
InternalEffect* pEffect = InternalEffect::CreateRTCSpring();
|
|
if (pEffect != NULL) {
|
|
if (pEffect->Create(diEffect) == SUCCESS) {
|
|
return pEffect;
|
|
}
|
|
delete pEffect;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::CreateCustomForceEffect(DWORD subType, const DIEFFECT& diEffect, HRESULT& hr)
|
|
**
|
|
** @mfunc CreateCustomForceEffect.
|
|
**
|
|
******************************************************/
|
|
InternalEffect* ForceFeedbackDevice::CreateCustomForceEffect(DWORD minorType, const DIEFFECT& diEffect, HRESULT& hr)
|
|
{
|
|
InternalEffect* pEffect = InternalEffect::CreateCustomForce();
|
|
if (pEffect != NULL) {
|
|
hr = pEffect->Create(diEffect);
|
|
if (SUCCEEDED(hr)) {
|
|
return pEffect;
|
|
}
|
|
delete pEffect;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::CreatePeriodicEffect(DWORD subType, const DIEFFECT& diEffect, HRESULT& hr)
|
|
**
|
|
** @mfunc CreatePeriodicEffect.
|
|
**
|
|
******************************************************/
|
|
InternalEffect* ForceFeedbackDevice::CreatePeriodicEffect(DWORD minorType, const DIEFFECT& diEffect, HRESULT& hr)
|
|
{
|
|
InternalEffect* pReturnEffect = NULL;
|
|
switch (minorType) {
|
|
case SE_SINE: {
|
|
pReturnEffect = InternalEffect::CreateSine();
|
|
break;
|
|
}
|
|
case SE_SQUAREHIGH: {
|
|
pReturnEffect = InternalEffect::CreateSquare();
|
|
break;
|
|
}
|
|
case SE_TRIANGLEUP: {
|
|
pReturnEffect = InternalEffect::CreateTriangle();
|
|
break;
|
|
}
|
|
case SE_SAWTOOTHUP: {
|
|
pReturnEffect = InternalEffect::CreateSawtoothUp();
|
|
break;
|
|
}
|
|
case SE_SAWTOOTHDOWN: {
|
|
pReturnEffect = InternalEffect::CreateSawtoothDown();
|
|
break;
|
|
}
|
|
case SE_CONSTANT_FORCE: {
|
|
return CreateConstantForceEffect(minorType, diEffect, hr);
|
|
}
|
|
case SE_RAMPUP: {
|
|
return CreateRampForceEffect(minorType, diEffect, hr);
|
|
}
|
|
}
|
|
if (pReturnEffect != NULL) {
|
|
hr = pReturnEffect->Create(diEffect);
|
|
if (FAILED(hr) == FALSE) {
|
|
return pReturnEffect;
|
|
}
|
|
delete pReturnEffect;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::CreateConstantForceEffect(DWORD subType, const DIEFFECT& diEffect, HRESULT& hr)
|
|
**
|
|
** @mfunc CreateConstantForceEffect.
|
|
**
|
|
******************************************************/
|
|
InternalEffect* ForceFeedbackDevice::CreateConstantForceEffect(DWORD minorType, const DIEFFECT& diEffect, HRESULT& hr)
|
|
{
|
|
InternalEffect* pEffect = InternalEffect::CreateConstantForce();
|
|
if (pEffect != NULL) {
|
|
hr = pEffect->Create(diEffect);
|
|
if (FAILED(hr) == FALSE) {
|
|
return pEffect;
|
|
}
|
|
delete pEffect;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::CreateRampForceEffect(DWORD subType, const DIEFFECT& diEffect, HRESULT& hr)
|
|
**
|
|
** @mfunc CreateRampForceEffect.
|
|
**
|
|
******************************************************/
|
|
InternalEffect* ForceFeedbackDevice::CreateRampForceEffect(DWORD minorType, const DIEFFECT& diEffect, HRESULT& hr)
|
|
{
|
|
InternalEffect* pEffect = InternalEffect::CreateRamp();
|
|
if (pEffect != NULL) {
|
|
hr = pEffect->Create(diEffect);
|
|
if (SUCCEEDED(hr)) {
|
|
return pEffect;
|
|
}
|
|
delete pEffect;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::SendRawForce(const DIEFFECT& diEffect)
|
|
**
|
|
** @mfunc SendRawForce.
|
|
**
|
|
******************************************************/
|
|
HRESULT ForceFeedbackDevice::SendRawForce(const DIEFFECT& diEffect, BOOL paramCheck)
|
|
{
|
|
if (diEffect.lpvTypeSpecificParams == NULL) {
|
|
return SFERR_INVALID_PARAM;
|
|
}
|
|
if (diEffect.cbTypeSpecificParams != sizeof(DICONSTANTFORCE)) {
|
|
return SFERR_INVALID_PARAM;
|
|
}
|
|
|
|
// We don't support more than 2 axes, and 0 is probably an error
|
|
if ((diEffect.cAxes > 2) || (diEffect.cAxes == 0)) {
|
|
return SFERR_NO_SUPPORT;
|
|
}
|
|
|
|
// Set up the axis mask
|
|
DWORD axisMask = 0;
|
|
for (unsigned int axisIndex = 0; axisIndex < diEffect.cAxes; axisIndex++) {
|
|
DWORD axisNumber = DIDFT_GETINSTANCE(diEffect.rgdwAxes[axisIndex]);
|
|
axisMask |= 1 << axisNumber;
|
|
}
|
|
BOOL axesReversed = (DIDFT_GETINSTANCE(diEffect.rgdwAxes[0]) == 1);
|
|
|
|
double angle = 0.0;
|
|
// Check coordinate sytems and change to rectangular
|
|
if (diEffect.dwFlags & DIEFF_SPHERICAL) { // We don't support sperical (3 axis force)
|
|
return SFERR_NO_SUPPORT; // .. since got by axis check, programmer goofed up
|
|
}
|
|
if (diEffect.dwFlags & DIEFF_POLAR) {
|
|
if (diEffect.cAxes != 2) { // Polar coordinate must have two axes of data (because DX says so)
|
|
return SFERR_INVALID_PARAM;
|
|
}
|
|
DWORD effectAngle = diEffect.rglDirection[0]; // in [0] even if reversed
|
|
if (axesReversed) { // Indicates (-1, 0) as origin instead of (0, -1)
|
|
effectAngle += 27000;
|
|
}
|
|
effectAngle %= 36000;
|
|
|
|
angle = double(effectAngle)/18000 * 3.14159; // Convert to radians
|
|
m_RawForceX = 0;
|
|
m_RawForceY = 0;
|
|
} else if (diEffect.dwFlags & DIEFF_CARTESIAN) { // Convert to polar (so we can convert to cartesian)
|
|
if (diEffect.cAxes == 1) { // Fairly easy conversion
|
|
if (X_AXIS & axisMask) {
|
|
angle = 3.14159/2; // PI/2
|
|
} else {
|
|
angle = 0.0;
|
|
}
|
|
} else { // Multiple axis cartiesian
|
|
m_RawForceX = 0;
|
|
m_RawForceY = 0;
|
|
|
|
int xDirection = DIDFT_GETINSTANCE(diEffect.rglDirection[0]);
|
|
int yDirection = DIDFT_GETINSTANCE(diEffect.rglDirection[1]);
|
|
if (axesReversed == TRUE) {
|
|
yDirection = xDirection;
|
|
xDirection = DIDFT_GETINSTANCE(diEffect.rglDirection[1]);
|
|
}
|
|
angle = atan2(double(yDirection), double(xDirection));
|
|
}
|
|
} else { // What, is there some other format?
|
|
ASSUME_NOT_REACHED();
|
|
return SFERR_INVALID_PARAM; // Untill someone says otherwise there was an error
|
|
}
|
|
|
|
// Sin^2(a) + Cos^2(a) = 1
|
|
double xProj = ::sin(angle); // DI has 0 degs at (1, 0) not (0, 1)
|
|
double yProj = ::cos(angle);
|
|
xProj *= xProj;
|
|
yProj *= yProj;
|
|
DWORD percentX = DWORD(xProj * 100.0 + 0.05);
|
|
DWORD percentY = DWORD(yProj * 100.0 + 0.05);
|
|
|
|
BOOL truncated = FALSE;
|
|
if (percentX != 0) {
|
|
m_RawForceX = LONG(percentX * (((DICONSTANTFORCE*)(diEffect.lpvTypeSpecificParams))->lMagnitude/100));
|
|
if (m_RawForceX > 10000) {
|
|
m_RawForceX = 10000;
|
|
truncated = TRUE;
|
|
} else if (m_RawForceX < -10000) {
|
|
m_RawForceX = -10000;
|
|
truncated = TRUE;
|
|
}
|
|
}
|
|
if (percentY != 0) {
|
|
m_RawForceY = LONG(percentY * (((DICONSTANTFORCE*)(diEffect.lpvTypeSpecificParams))->lMagnitude/100));
|
|
if (m_RawForceY > 10000) {
|
|
m_RawForceY = 10000;
|
|
truncated = TRUE;
|
|
} else if (m_RawForceY < -10000) {
|
|
m_RawForceY = -10000;
|
|
truncated = TRUE;
|
|
}
|
|
}
|
|
long int mag = m_RawForceX + m_RawForceY * GetYMappingPercent(ET_RAWFORCE_200)/100;
|
|
if (mag > 10000) { // Check for overrun but don't return indication of truncation
|
|
mag = 10000;
|
|
} else if (mag < -10000) {
|
|
mag = -10000;
|
|
}
|
|
if (angle > 3.14159) { // PI
|
|
mag *= -1;
|
|
}
|
|
HRESULT hr = g_pDataPackager->ForceOut(mag, X_AXIS);
|
|
if ((hr != SUCCESS) || (paramCheck == TRUE)) {
|
|
return hr;
|
|
}
|
|
|
|
ACKNACK ackNack;
|
|
g_pDataTransmitter->Transmit(ackNack);
|
|
|
|
if (truncated == TRUE) {
|
|
return DI_TRUNCATED;
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::CreateVFXEffect(const DIEFFECT& diEffect, HRESULT& hr)
|
|
**
|
|
** @mfunc CreateVFXEffect.
|
|
**
|
|
******************************************************/
|
|
InternalEffect* ForceFeedbackDevice::CreateVFXEffect(const DIEFFECT& diEffect, HRESULT& hr)
|
|
{
|
|
/* ULONG m_Bytes; // Size of this structure
|
|
ULONG m_PointerType; // VFX_FILENAME or VFX_BUFFER
|
|
ULONG m_BufferSize; // number of bytes in buffer (if VFX_BUFFER)
|
|
PVOID m_pFileNameOrBuffer; // file name to open
|
|
*/
|
|
if (diEffect.lpvTypeSpecificParams == NULL) {
|
|
return NULL;
|
|
}
|
|
if (diEffect.cbTypeSpecificParams != sizeof(VFX_PARAM)) {
|
|
return NULL;
|
|
}
|
|
|
|
VFX_PARAM* pVFXParms = (VFX_PARAM*)diEffect.lpvTypeSpecificParams;
|
|
BYTE* pEffectBuffer = NULL;
|
|
ULONG numBufferBytes = 0;
|
|
if (pVFXParms->m_PointerType == VFX_FILENAME) { // Create memory buffer from file
|
|
hr = LoadBufferFromFile((const char*)(pVFXParms->m_pFileNameOrBuffer), pEffectBuffer, numBufferBytes);
|
|
} else {
|
|
pEffectBuffer = (BYTE*)(pVFXParms->m_pFileNameOrBuffer);
|
|
numBufferBytes = pVFXParms->m_BufferSize;
|
|
}
|
|
|
|
if ((pEffectBuffer == NULL) || (numBufferBytes == 0)) {
|
|
return NULL;
|
|
}
|
|
|
|
return CreateVFXEffectFromBuffer(diEffect, pEffectBuffer, numBufferBytes, hr);
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::CreateVFXEffectFromBuffer(const DIEFFECT& diEffect, BYTE* pEffectBuffer, ULONG numBufferBytes, HRESULT& hr)
|
|
**
|
|
** @mfunc CreateVFXEffect.
|
|
**
|
|
******************************************************/
|
|
InternalEffect* ForceFeedbackDevice::CreateVFXEffectFromBuffer(const DIEFFECT& diEffect, BYTE* pEffectBuffer, ULONG numBufferBytes, HRESULT& hr)
|
|
{
|
|
if ((pEffectBuffer == NULL) || (numBufferBytes == 0)) {
|
|
ASSUME_NOT_REACHED();
|
|
return NULL;
|
|
}
|
|
|
|
MMIOINFO mmioInfo;
|
|
::memset(&mmioInfo, 0, sizeof(MMIOINFO));
|
|
mmioInfo.fccIOProc = FOURCC_MEM;
|
|
mmioInfo.cchBuffer = numBufferBytes;
|
|
mmioInfo.pchBuffer = (char*)pEffectBuffer;
|
|
|
|
HMMIO hmmio = ::mmioOpen(NULL, &mmioInfo, MMIO_READ);
|
|
if (hmmio == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
BYTE* pEffectParms = NULL;
|
|
DWORD paramSize;
|
|
EFFECT effect; // SW EFFECT structure
|
|
ENVELOPE envelope; // SW ENVELOPE structure
|
|
|
|
try { // Try parsing the RIFF file
|
|
MMRESULT mmResult;
|
|
|
|
// Descend into FORC list
|
|
MMCKINFO forceEffectRiffInfo;
|
|
forceEffectRiffInfo.fccType = FCC_FORCE_EFFECT_RIFF;
|
|
if ((mmResult = ::mmioDescend(hmmio, &forceEffectRiffInfo, NULL, MMIO_FINDRIFF)) != MMSYSERR_NOERROR) {
|
|
throw mmResult;
|
|
}
|
|
|
|
// Descend into TRAK list
|
|
MMCKINFO trakListInfo;
|
|
trakListInfo.fccType = FCC_TRACK_LIST;
|
|
if ((mmResult = ::mmioDescend(hmmio, &trakListInfo, &forceEffectRiffInfo, MMIO_FINDLIST)) != MMSYSERR_NOERROR) {
|
|
throw mmResult;
|
|
}
|
|
|
|
// Descend into first EFCT list
|
|
MMCKINFO effectListInfo;
|
|
effectListInfo.fccType = FCC_EFFECT_LIST;
|
|
if ((mmResult = ::mmioDescend(hmmio, &effectListInfo, &trakListInfo, MMIO_FINDLIST)) != MMSYSERR_NOERROR) {
|
|
throw mmResult;
|
|
}
|
|
|
|
// Descend into the ID chunk (maybe someone has a clue what is in here)
|
|
MMCKINFO idInfo;
|
|
idInfo.ckid = FCC_ID_CHUNK;
|
|
if ((mmResult = ::mmioDescend(hmmio, &idInfo, &effectListInfo, MMIO_FINDCHUNK)) != MMSYSERR_NOERROR) {
|
|
throw mmResult;
|
|
}
|
|
// Find the number of IDs in here (should indicate the number of effect)
|
|
DWORD numEffects = idInfo.cksize/sizeof(DWORD);
|
|
if (numEffects != 1) {
|
|
throw SFERR_NO_SUPPORT;
|
|
}
|
|
// Read the ID chunk
|
|
DWORD id;
|
|
DWORD bytesRead = ::mmioRead(hmmio, (char*)&id, sizeof(DWORD));
|
|
if (bytesRead != sizeof(DWORD)) {
|
|
throw (bytesRead == 0) ? VFX_ERR_FILE_END_OF_FILE : VFX_ERR_FILE_CANNOT_READ;
|
|
}
|
|
// Back out of the ID chunk
|
|
if ((mmResult = ::mmioAscend(hmmio, &idInfo, 0)) != MMSYSERR_NOERROR) {
|
|
throw HRESULT_FROM_WIN32(mmResult);
|
|
}
|
|
|
|
// Descend into the DATA chunk
|
|
MMCKINFO dataInfo;
|
|
dataInfo.ckid = FCC_DATA_CHUNK;
|
|
if ((mmResult = ::mmioDescend(hmmio, &dataInfo, &effectListInfo, MMIO_FINDCHUNK)) != MMSYSERR_NOERROR) {
|
|
throw HRESULT_FROM_WIN32(mmResult);
|
|
}
|
|
// Read the effect structure from this chunk
|
|
bytesRead = ::mmioRead(hmmio, (char*)&effect, sizeof(EFFECT));
|
|
if (bytesRead != sizeof(EFFECT)) {
|
|
throw (bytesRead == 0) ? VFX_ERR_FILE_END_OF_FILE : VFX_ERR_FILE_CANNOT_READ;
|
|
}
|
|
// Read the envelope structure from this chunk
|
|
bytesRead = ::mmioRead(hmmio, (char*)&envelope, sizeof(ENVELOPE));
|
|
if (bytesRead != sizeof(ENVELOPE)) {
|
|
throw (bytesRead == 0) ? VFX_ERR_FILE_END_OF_FILE : VFX_ERR_FILE_CANNOT_READ;
|
|
}
|
|
// Read the parameters in:
|
|
// -- Figure out the paramter size
|
|
DWORD currentFilePos = ::mmioSeek(hmmio, 0, SEEK_CUR);
|
|
if (currentFilePos == -1) {
|
|
throw VFX_ERR_FILE_CANNOT_SEEK;
|
|
}
|
|
paramSize = dataInfo.dwDataOffset + dataInfo.cksize - currentFilePos;
|
|
// -- Allocate space for the parameter
|
|
pEffectParms = new BYTE[paramSize];
|
|
if (pEffectParms == NULL) {
|
|
throw VFX_ERR_FILE_OUT_OF_MEMORY;
|
|
}
|
|
// -- Do the actual reading
|
|
bytesRead = ::mmioRead(hmmio, (char*)pEffectParms, paramSize);
|
|
if (bytesRead != paramSize) {
|
|
throw (bytesRead == 0) ? VFX_ERR_FILE_END_OF_FILE : VFX_ERR_FILE_CANNOT_READ;
|
|
}
|
|
// -- The pointer must be fixed if this is User Defined
|
|
if (effect.m_Type == EF_USER_DEFINED) {
|
|
BYTE* pForceData = pEffectParms + sizeof(UD_PARAM);
|
|
UD_PARAM* pUDParam = (UD_PARAM*)pEffectParms;
|
|
pUDParam->m_pForceData = (LONG*)pForceData;
|
|
}
|
|
} catch (HRESULT thrownError) {
|
|
hr = thrownError;
|
|
::mmioClose(hmmio, 0);
|
|
if (pEffectParms == NULL) { // Did we get an effect?
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
::mmioClose(hmmio, 0); // Close the file
|
|
if (pEffectParms == NULL) {
|
|
ASSUME_NOT_REACHED(); //Exception should have been thrown
|
|
return NULL;
|
|
}
|
|
|
|
InternalEffect* pReturnEffect = InternalEffect::CreateFromVFX(diEffect, effect, envelope, pEffectParms, paramSize, hr);
|
|
|
|
// Cleanup
|
|
delete pEffectParms;
|
|
pEffectParms = NULL;
|
|
|
|
return pReturnEffect;
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::CreateEffect(DWORD& effectID, const DIEFFECT& diEffect)
|
|
**
|
|
** @mfunc CreateEffect.
|
|
**
|
|
******************************************************/
|
|
InternalEffect* ForceFeedbackDevice::CreateEffect(DWORD effectType, const DIEFFECT& diEffect, DWORD& dnloadID, HRESULT& hr, BOOL paramCheck)
|
|
{
|
|
WORD majorType = WORD((effectType >> 16) & 0x0000FFFF);
|
|
WORD minorType = WORD(effectType & 0x0000FFFF);
|
|
|
|
if (majorType == EF_RAW_FORCE) {
|
|
hr = SendRawForce(diEffect, paramCheck);
|
|
if (SUCCEEDED(hr)) {
|
|
dnloadID = RAW_FORCE_ALIAS;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
InternalEffect* pEffect = NULL;
|
|
BOOL isNewEffect = (dnloadID == 0);
|
|
if (isNewEffect) {
|
|
dnloadID = g_ForceFeedbackDevice.GetNextCreationID();
|
|
if (dnloadID == 0) {
|
|
hr = SFERR_OUT_OF_FF_MEMORY;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
hr = SUCCESS;
|
|
|
|
switch (majorType) {
|
|
case EF_BEHAVIOR: {
|
|
pEffect = CreateConditionEffect(minorType, diEffect, hr);
|
|
break;
|
|
}
|
|
case EF_USER_DEFINED: {
|
|
pEffect = CreateCustomForceEffect(minorType, diEffect, hr);
|
|
break;
|
|
}
|
|
case EF_SYNTHESIZED: {
|
|
pEffect = CreatePeriodicEffect(minorType, diEffect, hr);
|
|
break;
|
|
}
|
|
case EF_RTC_SPRING: {
|
|
dnloadID = SYSTEM_RTCSPRING_ALIAS_ID;
|
|
pEffect = CreateRTCSpringEffect(minorType, diEffect);
|
|
break;
|
|
}
|
|
case EF_VFX_EFFECT: { // Visual force VFX Effect!!! Danger Will Robinson!
|
|
pEffect = CreateVFXEffect(diEffect, hr);
|
|
break;
|
|
}
|
|
}
|
|
if (((pEffect == NULL) || (paramCheck == TRUE)) && (isNewEffect == TRUE)) {
|
|
dnloadID = 0;
|
|
}
|
|
|
|
if (pEffect != NULL) {
|
|
if ((isNewEffect == TRUE) && (paramCheck == FALSE)) {
|
|
g_ForceFeedbackDevice.SetEffect(BYTE(dnloadID), pEffect);
|
|
}
|
|
} else if (!FAILED(hr)) {
|
|
hr = SFERR_DRIVER_ERROR;
|
|
}
|
|
return pEffect;
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::GetNextCreationID() const
|
|
**
|
|
** @mfunc GetNextCreationID.
|
|
**
|
|
******************************************************/
|
|
BYTE ForceFeedbackDevice::GetNextCreationID() const
|
|
{
|
|
// Must search straight through (start at 2, 0 is spring, 1 is friction
|
|
for (BYTE emptyID = 2; emptyID < MAX_EFFECT_IDS; emptyID++) {
|
|
if (m_EffectList[emptyID] == NULL) {
|
|
break;
|
|
}
|
|
}
|
|
if (emptyID == MAX_EFFECT_IDS) {
|
|
return 0;
|
|
}
|
|
return emptyID;
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::SetEffect(BYTE globalID, BYTE deviceID, InternalEffect* pEffect)
|
|
**
|
|
** @mfunc SetEffect.
|
|
**
|
|
******************************************************/
|
|
void ForceFeedbackDevice::SetEffect(BYTE globalID, InternalEffect* pEffect)
|
|
{
|
|
if (pEffect == NULL) {
|
|
ASSUME_NOT_REACHED();
|
|
return;
|
|
}
|
|
|
|
if (globalID == SYSTEM_EFFECT_ID) {
|
|
m_SystemEffect = pEffect;
|
|
} else if (globalID == SYSTEM_RTCSPRING_ALIAS_ID) {
|
|
m_EffectList[0] = pEffect;
|
|
if (GetFirmwareVersionMajor() == 1) {
|
|
pEffect->SetGlobalID(SYSTEM_RTCSPRING_ID);
|
|
pEffect->SetDeviceID(SYSTEM_RTCSPRING_ID);
|
|
} else {
|
|
pEffect->SetGlobalID(ID_RTCSPRING_200);
|
|
pEffect->SetDeviceID(ID_RTCSPRING_200);
|
|
}
|
|
return;
|
|
} else if (globalID < MAX_EFFECT_IDS) {
|
|
m_EffectList[globalID] = pEffect;
|
|
} else {
|
|
ASSUME_NOT_REACHED(); // Out of range
|
|
}
|
|
|
|
pEffect->SetGlobalID(globalID);
|
|
}
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::SetDeviceIDFromStatusPacket(DWORD globalID)
|
|
**
|
|
** @mfunc SetDeviceIDFromStatusPacket.
|
|
**
|
|
******************************************************/
|
|
void ForceFeedbackDevice::SetDeviceIDFromStatusPacket(DWORD globalID)
|
|
{
|
|
if (globalID == SYSTEM_EFFECT_ID) {
|
|
return;
|
|
}
|
|
if (globalID == SYSTEM_RTCSPRING_ALIAS_ID) {
|
|
return;
|
|
}
|
|
if (globalID < MAX_EFFECT_IDS) {
|
|
InternalEffect* pEffect = m_EffectList[globalID];
|
|
if (pEffect == NULL) {
|
|
ASSUME_NOT_REACHED(); // There should be an effect here
|
|
return;
|
|
}
|
|
pEffect->SetDeviceID(BYTE(m_LastStatusPacket.dwEffect));
|
|
#ifdef _DEBUG // Check to see if they coincide
|
|
if (pEffect->GetGlobalID() != pEffect->GetDeviceID()) {
|
|
TCHAR buff[256];
|
|
::wsprintf(buff, TEXT("SW_WHEEL.DLL: Global ID (%d) != Download ID (%d)\r\n"), pEffect->GetGlobalID(), pEffect->GetDeviceID());
|
|
_RPT0(_CRT_WARN, buff);
|
|
}
|
|
#endif _DEBUG
|
|
} else {
|
|
ASSUME_NOT_REACHED(); // Out of range
|
|
}
|
|
}
|
|
|
|
|
|
/******************************************************
|
|
**
|
|
** ForceFeedbackDevice::QueryStatus()
|
|
**
|
|
** @mfunc QueryStatus.
|
|
**
|
|
******************************************************/
|
|
HRESULT ForceFeedbackDevice::QueryStatus()
|
|
{
|
|
CriticalLock cl; // This is a critical section
|
|
|
|
// Use Digital Overdrive to get the status packet
|
|
JOYCHANNELSTATUS statusPacket = {sizeof(JOYCHANNELSTATUS)};
|
|
|
|
HRESULT hRet = g_pDriverCommunicator->GetStatus(statusPacket);
|
|
if (hRet == SUCCESS) {
|
|
if (GetFirmwareVersionMajor() == 1) {
|
|
// This is irrelevant till we support jolt
|
|
} else {
|
|
if (sizeof(statusPacket.dwDeviceStatus) == sizeof(m_Version200State)) {
|
|
::memcpy(&m_Version200State, &(statusPacket.dwDeviceStatus), sizeof(statusPacket.dwDeviceStatus));
|
|
} else {
|
|
ASSUME_NOT_REACHED();
|
|
}
|
|
}
|
|
}
|
|
|
|
return hRet;
|
|
}
|