944 lines
27 KiB
C
944 lines
27 KiB
C
/*****************************************************************************
|
|
*
|
|
* PidParam.c
|
|
*
|
|
* Copyright (c) 1999 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* Abstract:
|
|
*
|
|
* Download PID parameter block(s) .
|
|
*
|
|
*****************************************************************************/
|
|
#include "pidpr.h"
|
|
|
|
#define sqfl ( sqflParam )
|
|
|
|
//struct to keep in relevant data for g_Custom
|
|
typedef struct PIDCUSTOM
|
|
{
|
|
DWORD DataOffset;
|
|
DWORD cSamples;
|
|
DWORD dwSamplePeriod;
|
|
} PIDCUSTOM, *PPIDCUSTOM;
|
|
|
|
|
|
#pragma BEGIN_CONST_DATA
|
|
|
|
static PIDUSAGE c_rgUsgEnvelope[] =
|
|
{
|
|
MAKE_PIDUSAGE(ATTACK_LEVEL, FIELD_OFFSET(DIENVELOPE,dwAttackLevel) ),
|
|
MAKE_PIDUSAGE(ATTACK_TIME, FIELD_OFFSET(DIENVELOPE,dwAttackTime) ),
|
|
MAKE_PIDUSAGE(FADE_LEVEL, FIELD_OFFSET(DIENVELOPE,dwFadeLevel) ),
|
|
MAKE_PIDUSAGE(FADE_TIME, FIELD_OFFSET(DIENVELOPE,dwFadeTime) ),
|
|
};
|
|
|
|
PIDREPORT g_Envelope =
|
|
{
|
|
HidP_Output,
|
|
HID_USAGE_PAGE_PID,
|
|
HID_USAGE_PID_SET_ENVELOPE_REPORT,
|
|
cbX(DIENVELOPE),
|
|
cA(c_rgUsgEnvelope),
|
|
c_rgUsgEnvelope
|
|
};
|
|
|
|
static PIDUSAGE c_rgUsgCondition[] =
|
|
{
|
|
MAKE_PIDUSAGE(CP_OFFSET, FIELD_OFFSET(DICONDITION, lOffset) ),
|
|
MAKE_PIDUSAGE(POSITIVE_COEFFICIENT, FIELD_OFFSET(DICONDITION, lPositiveCoefficient)),
|
|
MAKE_PIDUSAGE(NEGATIVE_COEFFICIENT, FIELD_OFFSET(DICONDITION, lNegativeCoefficient)),
|
|
MAKE_PIDUSAGE(POSITIVE_SATURATION, FIELD_OFFSET(DICONDITION, dwPositiveSaturation)),
|
|
MAKE_PIDUSAGE(NEGATIVE_SATURATION, FIELD_OFFSET(DICONDITION, dwNegativeSaturation)),
|
|
MAKE_PIDUSAGE(DEAD_BAND, FIELD_OFFSET(DICONDITION, lDeadBand)),
|
|
};
|
|
|
|
PIDREPORT g_Condition =
|
|
{
|
|
HidP_Output,
|
|
HID_USAGE_PAGE_PID,
|
|
HID_USAGE_PID_SET_CONDITION_REPORT,
|
|
cbX(DICONDITION),
|
|
cA(c_rgUsgCondition),
|
|
c_rgUsgCondition
|
|
};
|
|
|
|
static PIDUSAGE c_rgUsgPeriodic[] =
|
|
{
|
|
MAKE_PIDUSAGE(OFFSET, FIELD_OFFSET(DIPERIODIC,lOffset)),
|
|
MAKE_PIDUSAGE(MAGNITUDE, FIELD_OFFSET(DIPERIODIC,dwMagnitude)),
|
|
MAKE_PIDUSAGE(PHASE, FIELD_OFFSET(DIPERIODIC,dwPhase)),
|
|
MAKE_PIDUSAGE(PERIOD, FIELD_OFFSET(DIPERIODIC,dwPeriod)),
|
|
};
|
|
|
|
PIDREPORT g_Periodic =
|
|
{
|
|
HidP_Output,
|
|
HID_USAGE_PAGE_PID,
|
|
HID_USAGE_PID_SET_PERIODIC_REPORT,
|
|
cbX(DIPERIODIC),
|
|
cA(c_rgUsgPeriodic),
|
|
c_rgUsgPeriodic
|
|
};
|
|
|
|
static PIDUSAGE c_rgUsgConstant[] =
|
|
{
|
|
MAKE_PIDUSAGE(MAGNITUDE, FIELD_OFFSET(DICONSTANTFORCE, lMagnitude)),
|
|
};
|
|
|
|
PIDREPORT g_Constant =
|
|
{
|
|
HidP_Output,
|
|
HID_USAGE_PAGE_PID,
|
|
HID_USAGE_PID_SET_CONSTANT_FORCE_REPORT,
|
|
cbX(DICONSTANTFORCE),
|
|
cA(c_rgUsgConstant),
|
|
c_rgUsgConstant
|
|
};
|
|
|
|
|
|
static PIDUSAGE c_rgUsgRamp[] =
|
|
{
|
|
MAKE_PIDUSAGE(RAMP_START, FIELD_OFFSET(DIRAMPFORCE, lStart)),
|
|
MAKE_PIDUSAGE(RAMP_END, FIELD_OFFSET(DIRAMPFORCE, lEnd)),
|
|
};
|
|
|
|
PIDREPORT g_Ramp =
|
|
{
|
|
HidP_Output,
|
|
HID_USAGE_PAGE_PID,
|
|
HID_USAGE_PID_SET_RAMP_FORCE_REPORT,
|
|
cbX(DIRAMPFORCE),
|
|
cA(c_rgUsgRamp),
|
|
c_rgUsgRamp
|
|
};
|
|
|
|
static PIDUSAGE c_rgUsgCustom[]=
|
|
{
|
|
MAKE_PIDUSAGE(CUSTOM_FORCE_DATA_OFFSET, FIELD_OFFSET(PIDCUSTOM, DataOffset)),
|
|
MAKE_PIDUSAGE(SAMPLE_COUNT, FIELD_OFFSET(PIDCUSTOM, cSamples)),
|
|
MAKE_PIDUSAGE(SAMPLE_PERIOD, FIELD_OFFSET(PIDCUSTOM, dwSamplePeriod)),
|
|
};
|
|
|
|
PIDREPORT g_Custom =
|
|
{
|
|
HidP_Output,
|
|
HID_USAGE_PAGE_PID,
|
|
HID_USAGE_PID_SET_CUSTOM_FORCE_REPORT,
|
|
cbX(PIDCUSTOM),
|
|
cA(c_rgUsgCustom),
|
|
c_rgUsgCustom,
|
|
};
|
|
|
|
|
|
static PIDUSAGE c_rgUsgCustomData[]=
|
|
{
|
|
MAKE_PIDUSAGE(CUSTOM_FORCE_DATA_OFFSET, 0x0),
|
|
MAKE_HIDUSAGE(GENERIC, HID_USAGE_GENERIC_BYTE_COUNT, 0x0),
|
|
MAKE_PIDUSAGE(CUSTOM_FORCE_DATA, 0x0 ),
|
|
|
|
};
|
|
|
|
PIDREPORT g_CustomData =
|
|
{
|
|
HidP_Output,
|
|
HID_USAGE_PAGE_PID,
|
|
HID_USAGE_PID_CUSTOM_FORCE_DATA_REPORT,
|
|
cbX(DWORD),
|
|
cA(c_rgUsgCustomData),
|
|
c_rgUsgCustomData,
|
|
};
|
|
|
|
static PIDUSAGE c_rgUsgDirectionAxes[]=
|
|
{
|
|
MAKE_HIDUSAGE(GENERIC, HID_USAGE_GENERIC_X, 0*cbX(ULONG)),
|
|
MAKE_HIDUSAGE(GENERIC, HID_USAGE_GENERIC_Y, 1*cbX(ULONG)),
|
|
MAKE_HIDUSAGE(GENERIC, HID_USAGE_GENERIC_Z, 2*cbX(ULONG)),
|
|
};
|
|
|
|
PIDREPORT g_CustomSample =
|
|
{
|
|
HidP_Output,
|
|
HID_USAGE_PAGE_PID,
|
|
HID_USAGE_PID_DOWNLOAD_FORCE_SAMPLE ,
|
|
cbX(DWORD),
|
|
cA(c_rgUsgDirectionAxes),
|
|
c_rgUsgDirectionAxes,
|
|
};
|
|
|
|
|
|
static PIDUSAGE c_rgUsgParameterOffset[] =
|
|
{
|
|
MAKE_PIDUSAGE(PARAMETER_BLOCK_OFFSET, 0x0 ),
|
|
};
|
|
|
|
static PIDREPORT g_ParameterOffset =
|
|
{
|
|
HidP_Output,
|
|
HID_USAGE_PAGE_PID,
|
|
0x0,
|
|
cbX(DWORD),
|
|
cA(c_rgUsgParameterOffset),
|
|
c_rgUsgParameterOffset
|
|
};
|
|
|
|
#pragma END_CONST_DATA
|
|
|
|
//global variable to keep in relevant data for g_Custom
|
|
PIDCUSTOM g_PidCustom;
|
|
|
|
|
|
STDMETHODIMP
|
|
PID_GetParameterOffset
|
|
(
|
|
IDirectInputEffectDriver *ped,
|
|
DWORD dwEffectIndex,
|
|
UINT uParameter,
|
|
DWORD dwSz,
|
|
PLONG plValue
|
|
)
|
|
{
|
|
|
|
CPidDrv *this = (CPidDrv *)ped;
|
|
HRESULT hres = S_OK;
|
|
USHORT uOffset = (USHORT)-1;
|
|
PEFFECTSTATE pEffectState = PeffectStateFromBlockIndex(this,dwEffectIndex);
|
|
PPIDMEM pMem = &pEffectState->PidMem[uParameter];
|
|
|
|
EnterProcI( PID_GetParameterOffset, (_"xxxxx", ped, dwEffectIndex, uParameter, dwSz, plValue));
|
|
|
|
AssertF(uParameter < this->cMaxParameters);
|
|
|
|
*plValue = 0x0;
|
|
hres = PID_ValidateEffectIndex(ped, dwEffectIndex);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
// We have already allocated memory,
|
|
// Just return the last size
|
|
if( PIDMEM_SIZE(pMem) != 0x0 )
|
|
{
|
|
uOffset = PIDMEM_OFFSET(pMem);
|
|
} else if( dwSz == 0x0 )
|
|
{
|
|
// Logitech device wants parameter blocks to
|
|
// set to -1 if they do not exist
|
|
uOffset = (USHORT)-1;
|
|
} else
|
|
{
|
|
// New Allocation
|
|
PPIDMEM pTmp, pNext;
|
|
UINT nAlloc;
|
|
USHORT uSz;
|
|
PUNITSTATE pUnitState = (PUNITSTATE)(g_pshmem + this->iUnitStateOffset);
|
|
hres = DIERR_OUTOFMEMORY;
|
|
|
|
// Align memory request
|
|
uSz = (USHORT)((dwSz / this->ReportPool.uPoolAlign + 1) * (this->ReportPool.uPoolAlign));
|
|
|
|
AssertF(uSz >= (USHORT)this->ReportPool.uPoolAlign);
|
|
//To be doubly sure.
|
|
uSz = max( uSz, (USHORT)this->ReportPool.uPoolAlign);
|
|
|
|
WaitForSingleObject(g_hmtxShared, INFINITE);
|
|
|
|
for(nAlloc = 0x0, pTmp = &(pUnitState->Guard[0]), pNext = (PPIDMEM)((PUCHAR)pUnitState + pTmp->iNext);
|
|
nAlloc < pUnitState->nAlloc && pTmp != &(pUnitState->Guard[1]);
|
|
nAlloc++, pTmp = pNext, pNext = (PPIDMEM)((PUCHAR)pUnitState + pTmp->iNext))
|
|
{
|
|
|
|
SquirtSqflPtszV(sqfl | sqflVerbose,
|
|
TEXT("%d %x(%x), Next:%x (%x) "),
|
|
nAlloc, pTmp, pTmp->uOfSz, pNext, pNext->uOfSz );
|
|
|
|
AssertF(pNext != NULL );
|
|
// If pNext == pUnitState, it means that the offset is 0.
|
|
// The offset of 0 is invalid.
|
|
AssertF((PUCHAR)pNext != (PUCHAR)pUnitState);
|
|
|
|
// Is there space in the cracks
|
|
if( GET_NEXTOFFSET(pTmp) + uSz < PIDMEM_OFFSET(pNext) )
|
|
{
|
|
pMem->iNext = (PUCHAR)pNext - (PUCHAR)pUnitState;
|
|
pTmp->iNext = (PUCHAR)pMem - (PUCHAR)pUnitState;
|
|
|
|
uOffset = GET_NEXTOFFSET(pTmp) ;
|
|
pMem->uOfSz = PIDMEM_OFSZ(uOffset, uSz);
|
|
|
|
pUnitState->nAlloc++;
|
|
pUnitState->cbAlloc += uSz;
|
|
hres = S_OK;
|
|
|
|
SquirtSqflPtszV(sqfl | sqflVerbose,
|
|
TEXT("%d %p (%x), Next: %p (%x) "),
|
|
nAlloc, pMem, pMem->uOfSz, pNext, pNext->uOfSz );
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
ReleaseMutex(g_hmtxShared);
|
|
}
|
|
}
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
*plValue = (ULONG)uOffset;
|
|
} else
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%s:FAIL Could not allocate %d bytes, UsedMem:%d, Allocs%d"),
|
|
s_tszProc, dwSz, ((PUNITSTATE)(g_pshmem + this->iUnitStateOffset))->cbAlloc, ((PUNITSTATE)(g_pshmem + this->iUnitStateOffset))->nAlloc );
|
|
}
|
|
|
|
|
|
ExitOleProc();
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT
|
|
PID_SendParameterBlock
|
|
(
|
|
IDirectInputEffectDriver *ped,
|
|
DWORD dwEffectIndex,
|
|
DWORD dwMemSz,
|
|
PUINT puParameter,
|
|
PPIDREPORT pPidReport,
|
|
PVOID pvData,
|
|
UINT cbData,
|
|
BOOL bBlocking,
|
|
UINT totalBlocks
|
|
)
|
|
{
|
|
CPidDrv *this = (CPidDrv *)ped;
|
|
HRESULT hres=S_OK;
|
|
|
|
PUCHAR pReport;
|
|
UINT cbReport;
|
|
|
|
EnterProcI( PID_SendParameterBlock, (_"xxxxx", ped, dwEffectIndex, dwMemSz, pPidReport, pvData, cbData));
|
|
|
|
AssertF(pPidReport->HidP_Type == HidP_Output);
|
|
|
|
cbReport = this->cbReport[pPidReport->HidP_Type];
|
|
pReport = this->pReport[pPidReport->HidP_Type];
|
|
|
|
if( *puParameter >= this->cMaxParameters )
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%s:FAIL Only support %d parameter blocks per effect "),
|
|
s_tszProc, *puParameter );
|
|
|
|
hres = E_NOTIMPL;
|
|
}
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
USHORT LinkCollection;
|
|
ZeroBuf(pReport, cbReport);
|
|
|
|
PID_GetLinkCollectionIndex(ped, pPidReport->UsagePage, pPidReport->Collection, 0x0, &LinkCollection);
|
|
|
|
hres = PID_PackValue
|
|
(
|
|
ped,
|
|
pPidReport,
|
|
LinkCollection,
|
|
pvData,
|
|
cbData,
|
|
pReport,
|
|
cbReport
|
|
);
|
|
|
|
// For device managed memory, we need to send the
|
|
// effect index
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
if( this->uDeviceManaged & PID_DEVICEMANAGED )
|
|
{
|
|
// Must be a valid effect ID
|
|
AssertF(dwEffectIndex != 0x0 );
|
|
|
|
/*hres =*/
|
|
|
|
PID_PackValue
|
|
(
|
|
ped,
|
|
&g_BlockIndex,
|
|
LinkCollection,
|
|
&dwEffectIndex,
|
|
cbX(dwEffectIndex),
|
|
pReport,
|
|
cbReport
|
|
);
|
|
|
|
// Send down the paramter block index
|
|
/*hres =*/PID_PackValue
|
|
(
|
|
ped,
|
|
&g_ParameterOffset,
|
|
LinkCollection,
|
|
puParameter,
|
|
cbX(*puParameter),
|
|
pReport,
|
|
cbReport
|
|
);
|
|
} else
|
|
{
|
|
LONG lValue;
|
|
|
|
hres = PID_GetParameterOffset(ped, dwEffectIndex, *puParameter, dwMemSz, &lValue);
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
hres = PID_PackValue
|
|
(
|
|
ped,
|
|
&g_ParameterOffset,
|
|
LinkCollection,
|
|
&lValue,
|
|
cbX(lValue),
|
|
pReport,
|
|
cbReport
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
hres = PID_SendReport(ped, pReport, cbReport, pPidReport->HidP_Type, bBlocking, *puParameter, totalBlocks);
|
|
}
|
|
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
(*puParameter)++;
|
|
}
|
|
}
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* PID_DownloadCustomForceData
|
|
*
|
|
* Download custom force sample data to the device.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
PID_DownloadCustomForceData
|
|
(
|
|
IDirectInputEffectDriver *ped,
|
|
DWORD dwEffectIndex,
|
|
PUINT puParameter,
|
|
LPCDICUSTOMFORCE pCustom,
|
|
LPCDIEFFECT peff
|
|
)
|
|
{
|
|
CPidDrv *this = (CPidDrv *)ped;
|
|
HRESULT hres = S_OK;
|
|
PCHAR pData = NULL;
|
|
PCHAR pBuff = NULL;
|
|
USHORT cbData;
|
|
USHORT nBytes;
|
|
USHORT bitsX;
|
|
USHORT bitsY;
|
|
USHORT bitsZ;
|
|
LPLONG pSample;
|
|
|
|
EnterProcI( PID_DownloadCustomForceData, (_"xxx", ped, dwEffectIndex, pCustom, puParameter ));
|
|
|
|
//zero out g_PidCustom
|
|
g_PidCustom.cSamples = g_PidCustom.DataOffset = g_PidCustom.dwSamplePeriod = 0;
|
|
|
|
//get bytes per sample and allocate the buffer
|
|
bitsX = this->customCaps[0].BitSize;
|
|
bitsY = this->customCaps[1].BitSize;
|
|
bitsZ = this->customCaps[2].BitSize;
|
|
|
|
//byte count must be multiple of 8!
|
|
if ((bitsX%8 != 0) || (bitsY%8 != 0) || (bitsZ%8 != 0))
|
|
{
|
|
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%s:FAIL Download Force Sample report fields that are not multiples of 8 are not supported!\n"),
|
|
s_tszProc );
|
|
hres = E_NOTIMPL;
|
|
}
|
|
|
|
//report count shouldn't be bigger than 1!
|
|
AssertF(this->customCaps[0].ReportCount <= 1);
|
|
AssertF(this->customCaps[1].ReportCount <= 1);
|
|
AssertF(this->customCaps[2].ReportCount <= 1);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
nBytes = (bitsX + bitsY + bitsZ)/8;
|
|
cbData = (USHORT) (pCustom->cSamples * nBytes);
|
|
hres = AllocCbPpv(cbData, &pBuff);
|
|
|
|
if( pBuff != NULL)
|
|
{
|
|
//determine which effect axis corresponds to which report axis
|
|
LONG Offset[3] = {-1, -1, -1};
|
|
int nAxis = 0;
|
|
int nChannel = 0;
|
|
int nSample = 0;
|
|
|
|
for (nChannel = 0; nChannel < (int)pCustom->cChannels, nChannel < (int)peff->cAxes; nChannel ++)
|
|
{
|
|
for (nAxis = 0; nAxis < 3; nAxis ++)
|
|
{
|
|
if (DIGETUSAGE(peff->rgdwAxes[nChannel]) == DIGETUSAGE(g_CustomSample.rgPidUsage[nAxis].dwUsage))
|
|
{
|
|
Offset[nAxis] = nChannel;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
ZeroBuf(pBuff, cbData);
|
|
|
|
pData = pBuff;
|
|
pSample = pCustom->rglForceData;
|
|
|
|
//scale all the samples
|
|
//loop through samples
|
|
for (nSample = 0; nSample < (int)pCustom->cSamples; nSample ++)
|
|
{
|
|
//loop through report axis
|
|
for (nAxis = 0; nAxis < 3; nAxis++)
|
|
{
|
|
LONG lSampleValue = 0;
|
|
|
|
//check if this axis is used
|
|
if (Offset[nAxis] == -1)
|
|
{
|
|
pData += this->customCaps[nAxis].BitSize/8;
|
|
continue;
|
|
}
|
|
|
|
lSampleValue = *(pSample + Offset[nAxis]);
|
|
|
|
|
|
switch (this->customCaps[nAxis].BitSize)
|
|
{
|
|
case 8:
|
|
//8-bit reports
|
|
{
|
|
(*((BYTE*)pData)) = (BYTE)(this->customCaps[nAxis].LogicalMin + ((lSampleValue + DI_FFNOMINALMAX) * (this->customCaps[nAxis].LogicalMax - this->customCaps[nAxis].LogicalMin))/(2*DI_FFNOMINALMAX));
|
|
pData++;
|
|
break;
|
|
}
|
|
case 16:
|
|
//16-bit reports
|
|
{
|
|
|
|
(*((SHORT*)pData)) = (SHORT)(this->customCaps[nAxis].LogicalMin + ((lSampleValue + DI_FFNOMINALMAX) * (this->customCaps[nAxis].LogicalMax - this->customCaps[nAxis].LogicalMin))/(2*DI_FFNOMINALMAX));
|
|
pData++;
|
|
break;
|
|
}
|
|
case 32:
|
|
//assume 32-bit reports as default
|
|
{
|
|
(*((LONG*)pData)) = (LONG)(this->customCaps[nAxis].LogicalMin + ((lSampleValue + DI_FFNOMINALMAX) * (this->customCaps[nAxis].LogicalMax - this->customCaps[nAxis].LogicalMin))/(2*DI_FFNOMINALMAX));
|
|
pData++;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%s:FAIL Download Force Sample report fields that are not 8, 16 or 32 are not supported\n"),
|
|
s_tszProc );
|
|
hres = E_NOTIMPL;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
pSample += pCustom->cChannels;
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
PCHAR pReport;
|
|
UINT cbReport;
|
|
HIDP_REPORT_TYPE HidP_Type = HidP_Output;
|
|
USAGE UsagePage = HID_USAGE_PAGE_PID;
|
|
USAGE UsageData = HID_USAGE_PID_CUSTOM_FORCE_DATA;
|
|
USAGE UsageOffset = HID_USAGE_PID_CUSTOM_FORCE_DATA_OFFSET;
|
|
USHORT LinkCollection = 0x0;
|
|
|
|
cbReport = this->cbReport[g_CustomData.HidP_Type];
|
|
pReport = this->pReport[g_CustomData.HidP_Type];
|
|
|
|
if ((this->customDataCaps.ReportCount > 0) && (this->customDataCaps.BitSize >=8))
|
|
{
|
|
USHORT nOffset = 0;
|
|
LONG lOffset = 0;
|
|
USHORT nIncrement = (this->customDataCaps.ReportCount * this->customDataCaps.BitSize)/8;
|
|
|
|
// For memory managed device allocate enough memory
|
|
// holding the custom force samples
|
|
if( ! (this->uDeviceManaged & PID_DEVICEMANAGED ))
|
|
{
|
|
hres = PID_GetParameterOffset(ped, dwEffectIndex, *puParameter, this->SzPool.uSzCustom, &lOffset);
|
|
}
|
|
|
|
pData = pBuff;
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
|
|
//send data in a loop
|
|
for (nOffset = 0; nOffset < cbData; nOffset += nIncrement)
|
|
{
|
|
//create a new buffer and copy data into it
|
|
PCHAR pIncrement = NULL;
|
|
hres = AllocCbPpv(nIncrement, &pIncrement);
|
|
|
|
if (pIncrement != NULL)
|
|
{
|
|
ZeroBuf(pIncrement, nIncrement);
|
|
memcpy(pIncrement, pData, min((cbData - nOffset), nIncrement));
|
|
|
|
ZeroBuf(pReport, cbReport);
|
|
|
|
//set the byte count
|
|
hres = HidP_SetScaledUsageValue
|
|
(
|
|
HidP_Type,
|
|
HID_USAGE_PAGE_GENERIC,
|
|
LinkCollection,
|
|
HID_USAGE_GENERIC_BYTE_COUNT,
|
|
(LONG)nIncrement,
|
|
this->ppd,
|
|
pReport,
|
|
cbReport
|
|
);
|
|
|
|
|
|
|
|
//set the offset
|
|
hres = HidP_SetScaledUsageValue
|
|
(
|
|
HidP_Type,
|
|
UsagePage,
|
|
0x0,
|
|
//LinkCollection,
|
|
UsageOffset,
|
|
(LONG) (nOffset + lOffset),
|
|
this->ppd,
|
|
pReport,
|
|
cbReport
|
|
);
|
|
|
|
|
|
|
|
//set the data
|
|
hres = HidP_SetUsageValueArray
|
|
(
|
|
HidP_Type, // IN HIDP_REPORT_TYPE ReportType,
|
|
UsagePage, // IN USAGE UsagePage,
|
|
0x0, // IN USHORT LinkCollection, // Optional
|
|
UsageData,
|
|
pIncrement, // IN PCHAR UsageValue,
|
|
nIncrement, // IN USHORT UsageValueByteLength,
|
|
this->ppd, // IN PHIDP_PREPARSED_DATA PreparsedData,
|
|
pReport, // OUT PCHAR Report,
|
|
cbReport // IN ULONG ReportLength
|
|
);
|
|
|
|
|
|
//set the effect index
|
|
PID_PackValue
|
|
(
|
|
ped,
|
|
&g_BlockIndex,
|
|
LinkCollection,
|
|
&dwEffectIndex,
|
|
cbX(dwEffectIndex),
|
|
pReport,
|
|
cbReport
|
|
);
|
|
|
|
|
|
//send the report
|
|
hres = PID_SendReport(ped, pReport, cbReport, HidP_Type, TRUE, 0, 1);
|
|
|
|
pData += nIncrement;
|
|
|
|
FreePpv(&pIncrement);
|
|
}
|
|
}
|
|
|
|
//put data into g_PidCustom
|
|
g_PidCustom.DataOffset = (DWORD)lOffset;
|
|
g_PidCustom.cSamples = pCustom->cSamples;
|
|
//ISSUE-2001/03/29-timgill May need to do real scaling.
|
|
g_PidCustom.dwSamplePeriod = pCustom->dwSamplePeriod/1000; //in milliseconds
|
|
|
|
//and increment puParameter
|
|
(*puParameter)++;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//do nothing
|
|
}
|
|
|
|
FreePpv(&pBuff);
|
|
}
|
|
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
PID_DoParameterBlocks
|
|
(
|
|
IDirectInputEffectDriver *ped,
|
|
DWORD dwId,
|
|
DWORD dwEffectId,
|
|
DWORD dwEffectIndex,
|
|
LPCDIEFFECT peff,
|
|
DWORD dwFlags,
|
|
PUINT puParameter,
|
|
BOOL bBlocking,
|
|
UINT totalBlocks
|
|
)
|
|
{
|
|
CPidDrv *this = (CPidDrv *)ped;
|
|
HRESULT hres = S_OK;
|
|
|
|
EnterProcI( PID_DoParameterBlocks, (_"xxxxxxx", ped, dwId, dwEffectId, dwEffectIndex, peff, dwFlags, puParameter ));
|
|
|
|
if( SUCCEEDED(hres)
|
|
&& (dwFlags & DIEP_TYPESPECIFICPARAMS) )
|
|
{
|
|
AssertF(peff->lpvTypeSpecificParams != NULL);
|
|
AssertF(peff->cbTypeSpecificParams != 0x0 );
|
|
|
|
switch(dwEffectId)
|
|
{
|
|
case PIDMAKEUSAGEDWORD(ET_CONSTANT) :
|
|
{
|
|
DICONSTANTFORCE DiParam;
|
|
AssertF(peff->cbTypeSpecificParams <= cbX(DiParam) );
|
|
memcpy(&DiParam, peff->lpvTypeSpecificParams, cbX(DiParam));
|
|
// Constant Force:
|
|
// Scale the magnitude.
|
|
DiParam.lMagnitude = Clamp(-DI_FFNOMINALMAX, DiParam.lMagnitude, DI_FFNOMINALMAX);
|
|
|
|
PID_ApplyScalingFactors(ped, &g_Constant, &this->DiSConstScale, cbX(this->DiSConstScale), &this->DiSConstOffset, cbX(this->DiSConstOffset), &DiParam, cbX(DiParam) );
|
|
|
|
hres = PID_SendParameterBlock
|
|
(
|
|
ped,
|
|
dwEffectIndex,
|
|
this->SzPool.uSzConstant,
|
|
puParameter,
|
|
&g_Constant,
|
|
&DiParam,
|
|
cbX(DiParam),
|
|
bBlocking,
|
|
totalBlocks
|
|
);
|
|
break;
|
|
}
|
|
|
|
case PIDMAKEUSAGEDWORD(ET_RAMP):
|
|
{
|
|
// Ramp Force
|
|
DIRAMPFORCE DiParam;
|
|
AssertF(peff->cbTypeSpecificParams <= cbX(DiParam) );
|
|
memcpy(&DiParam, peff->lpvTypeSpecificParams, cbX(DiParam));
|
|
|
|
//Scale the magnitude
|
|
DiParam.lStart = Clamp(-DI_FFNOMINALMAX, DiParam.lStart, DI_FFNOMINALMAX);
|
|
DiParam.lEnd = Clamp(-DI_FFNOMINALMAX, DiParam.lEnd, DI_FFNOMINALMAX);
|
|
|
|
|
|
PID_ApplyScalingFactors(ped, &g_Ramp, &this->DiSRampScale, cbX(this->DiSRampScale), &this->DiSRampOffset, cbX(this->DiSRampOffset), &DiParam, cbX(DiParam) );
|
|
hres = PID_SendParameterBlock
|
|
(
|
|
ped,
|
|
dwEffectIndex,
|
|
this->SzPool.uSzRamp,
|
|
puParameter,
|
|
&g_Ramp,
|
|
&DiParam,
|
|
cbX(DiParam),
|
|
bBlocking,
|
|
totalBlocks
|
|
);
|
|
break;
|
|
}
|
|
|
|
case PIDMAKEUSAGEDWORD(ET_SQUARE):
|
|
case PIDMAKEUSAGEDWORD(ET_SINE):
|
|
case PIDMAKEUSAGEDWORD(ET_TRIANGLE):
|
|
case PIDMAKEUSAGEDWORD(ET_SAWTOOTH_UP):
|
|
case PIDMAKEUSAGEDWORD(ET_SAWTOOTH_DOWN):
|
|
{
|
|
DIPERIODIC DiParam;
|
|
AssertF(peff->cbTypeSpecificParams <= cbX(DiParam) );
|
|
memcpy(&DiParam, peff->lpvTypeSpecificParams, cbX(DiParam));
|
|
|
|
//Scale the parameters
|
|
DiParam.dwMagnitude = Clip( DiParam.dwMagnitude, DI_FFNOMINALMAX);
|
|
DiParam.lOffset = Clamp(-DI_FFNOMINALMAX, DiParam.lOffset, DI_FFNOMINALMAX);
|
|
//Wrap the phase around
|
|
DiParam.dwPhase %= (360*DI_DEGREES);
|
|
|
|
PID_ApplyScalingFactors(ped, &g_Periodic, &this->DiSPeriodicScale, cbX(this->DiSPeriodicScale), &this->DiSPeriodicOffset, cbX(this->DiSPeriodicOffset), &DiParam, cbX(DiParam) );
|
|
hres = PID_SendParameterBlock
|
|
(
|
|
ped,
|
|
dwEffectIndex,
|
|
this->SzPool.uSzPeriodic,
|
|
puParameter,
|
|
&g_Periodic,
|
|
&DiParam,
|
|
cbX(DiParam),
|
|
bBlocking,
|
|
totalBlocks
|
|
);
|
|
break;
|
|
}
|
|
case PIDMAKEUSAGEDWORD(ET_SPRING):
|
|
case PIDMAKEUSAGEDWORD(ET_DAMPER):
|
|
case PIDMAKEUSAGEDWORD(ET_INERTIA):
|
|
case PIDMAKEUSAGEDWORD(ET_FRICTION):
|
|
{
|
|
LPDICONDITION lpCondition;
|
|
DWORD nStruct;
|
|
DWORD cStruct = (peff->cbTypeSpecificParams)/sizeof(DICONDITION);
|
|
AssertF(cStruct <= peff->cAxes);
|
|
|
|
for(nStruct = 0x0, lpCondition = (LPDICONDITION)peff->lpvTypeSpecificParams;
|
|
nStruct < cStruct && SUCCEEDED(hres);
|
|
nStruct++, lpCondition++ )
|
|
{
|
|
DICONDITION DiCondition;
|
|
DiCondition = *lpCondition;
|
|
|
|
//Scale the values
|
|
DiCondition.lOffset = Clamp(-DI_FFNOMINALMAX, DiCondition.lOffset, DI_FFNOMINALMAX);
|
|
DiCondition.lPositiveCoefficient = Clamp(-DI_FFNOMINALMAX, DiCondition.lPositiveCoefficient, DI_FFNOMINALMAX);
|
|
DiCondition.lNegativeCoefficient = Clamp(-DI_FFNOMINALMAX, DiCondition.lNegativeCoefficient, DI_FFNOMINALMAX);
|
|
DiCondition.dwPositiveSaturation = Clip( DiCondition.dwPositiveSaturation, DI_FFNOMINALMAX);
|
|
DiCondition.dwNegativeSaturation = Clip( DiCondition.dwNegativeSaturation, DI_FFNOMINALMAX);
|
|
DiCondition.lDeadBand = Clamp(0, DiCondition.lDeadBand, DI_FFNOMINALMAX);
|
|
|
|
PID_ApplyScalingFactors(ped, &g_Condition, &this->DiSCondScale, cbX(this->DiSCondScale), &this->DiSCondOffset, cbX(this->DiSCondOffset), &DiCondition, cbX(DiCondition) );
|
|
hres = PID_SendParameterBlock
|
|
(
|
|
ped,
|
|
dwEffectIndex,
|
|
this->SzPool.uSzCondition,
|
|
puParameter,
|
|
&g_Condition,
|
|
&DiCondition,
|
|
sizeof(DiCondition),
|
|
bBlocking,
|
|
totalBlocks
|
|
);
|
|
}
|
|
|
|
//Conditions can't have envelopes!
|
|
//So if there's a flag indicating an envelope, take it out.
|
|
dwFlags &= ~(DIEP_ENVELOPE);
|
|
|
|
break;
|
|
}
|
|
|
|
case PIDMAKEUSAGEDWORD(ET_CUSTOM):
|
|
{
|
|
// Custom Force
|
|
DICUSTOMFORCE DiParam;
|
|
AssertF(peff->cbTypeSpecificParams <= cbX(DiParam) );
|
|
memcpy(&DiParam, peff->lpvTypeSpecificParams, cbX(DiParam));
|
|
|
|
// Download Custom Force -- always a blocking call
|
|
hres = PID_DownloadCustomForceData(ped, dwEffectIndex, puParameter, &DiParam, peff);
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
// Set custom Effect parameter block header -- always a blocking call
|
|
hres = PID_SendParameterBlock
|
|
(
|
|
ped,
|
|
dwEffectIndex,
|
|
this->SzPool.uSzCustom,
|
|
puParameter,
|
|
&g_Custom,
|
|
&g_PidCustom,
|
|
cbX(DiParam),
|
|
TRUE,
|
|
totalBlocks
|
|
);
|
|
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
|
|
hres = DIERR_PID_USAGENOTFOUND;
|
|
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%s:FAIL Unknown parameter block for dwEffectId(0x%x)"),
|
|
s_tszProc, dwEffectId );
|
|
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
if( SUCCEEDED(hres)
|
|
&& (dwFlags & DIEP_ENVELOPE)
|
|
&& peff->lpEnvelope != NULL )
|
|
{
|
|
DIENVELOPE DiEnv;
|
|
DiEnv = *peff->lpEnvelope;
|
|
|
|
//Scale the values
|
|
DiEnv.dwAttackLevel = Clip(DiEnv.dwAttackLevel, DI_FFNOMINALMAX);
|
|
DiEnv.dwFadeLevel = Clip(DiEnv.dwFadeLevel, DI_FFNOMINALMAX);
|
|
|
|
PID_ApplyScalingFactors(ped, &g_Envelope, &this->DiSEnvScale, cbX(this->DiSEnvScale), &this->DiSEnvOffset, cbX(this->DiSEnvOffset), &DiEnv, DiEnv.dwSize );
|
|
|
|
hres = PID_SendParameterBlock
|
|
(
|
|
ped,
|
|
dwEffectIndex,
|
|
this->SzPool.uSzEnvelope,
|
|
puParameter,
|
|
&g_Envelope,
|
|
&DiEnv,
|
|
DiEnv.dwSize,
|
|
bBlocking,
|
|
totalBlocks
|
|
);
|
|
|
|
}
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
|
|
|