/***************************************************************************** * * 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; }