/**************************************************************************** MODULE: FFD_SWFF.CPP Tab settings: 5 9 Copyright 1995, 1996, 1999, Microsoft Corporation, All Rights Reserved. PURPOSE: FFD (SWForce HAL) API FUNCTIONS: Function prototypes for Force Feedback Joystick interface between the SWForce and the device FFD_PutRawForce FFD_DownloadEffect FFD_DestroyEffect FFD_GetEffectForceValue FFD_PutRawForce VFX functions: Download_VFX CreateEffectFromFile CreateEffectFromBuffer These functionality are not necessarily supported by all Force Feedback devices. For example, if a device does not support built-in synthesis capability, then the entry point DownloadEffect, will return an error code ERROR_NO_SUPPORT. COMMENTS: This module of functions are encapsulated in SWFF_PRO.dll the DirectInput DDI driver Author(s): Name: ---------- ---------------- MEA Manolito E. Adan Revision History: ----------------- Version Date Author Comments 1.0 21-Mar-97 MEA original from SWForce code 12-Mar-99 waltw Removed dead code (mostly FFD_xxx functions) These functions no longer exported in .def ****************************************************************************/ #include #include #include #include "midi.hpp" #include "hau_midi.hpp" #include "math.h" #include "FFD_SWFF.hpp" #include "midi_obj.hpp" #include "CritSec.h" // Force Output range values #define MAX_AMP 2047 #define MIN_AMP -2048 #define FORCE_RANGE ((MAX_AMP - MIN_AMP)/2) extern TCHAR szDeviceName[MAX_SIZE_SNAME]; extern CJoltMidi *g_pJoltMidi; #ifdef _DEBUG extern char g_cMsg[160]; #endif static HRESULT AngleToXY( IN LONG lDirectionAngle2D, IN LONG lValueData, IN ULONG ulAxisMask, IN OUT PLONG pX, IN OUT PLONG pY); // *** ---------------------------------------------------------------------*** // Function: FFD_GetDiagCounters // Purpose: Returns the DIAG_COUNTER structure // Parameters: // PDIAG_COUNTER pDiagCounter - Ptr to DIAG_COUNTER struct. // // Returns: SUCCESS - always successful // // Algorithm: // Comments: // *** ---------------------------------------------------------------------*** HRESULT WINAPI FFD_GetDiagCounters(PDIAG_COUNTER pDiagCounter) { #ifdef _DEBUG g_CriticalSection.Enter(); wsprintf(g_cMsg, "FFD_GetDiagCounters: %s \r\n", &szDeviceName[0]); OutputDebugString(g_cMsg); g_CriticalSection.Leave(); #endif if (NULL == g_pJoltMidi) return (SFERR_DRIVER_ERROR); pDiagCounter = g_pJoltMidi->DiagCounterPtrOf(); return (SUCCESS); } // *** ---------------------------------------------------------------------*** // Function: FFD_PutRawForce // Purpose: Send force object parameters to be played back by the FF device. // Parameters: // IN PFORCE pForce - A pointer to a FORCE data structure // // Returns: SUCCESS if successful, else // SFERR_NO_SUPPORT // // Algorithm: // // Comments: // Note: The Playback mode will be ONCE // // // *** ---------------------------------------------------------------------*** HRESULT WINAPI FFD_PutRawForce( IN PFORCE pForce) { assert(pForce); HRESULT hRet; long lForceDataX, lForceDataY; long lForceData = (long) ( FORCE_RANGE * (float) (pForce->m_ForceValue/100.00)); // If single Axis only, then use the force on that axis. // If X and Y-axis, then use 2D angle // if (AngleToXY(pForce->m_DirectionAngle2D+90, lForceData, pForce->m_AxisMask, // &lForceDataX, &lForceDataY) != SUCCESS) return SFERR_NO_SUPPORT; if (AngleToXY(pForce->m_DirectionAngle2D, lForceData, pForce->m_AxisMask, &lForceDataX, &lForceDataY) != SUCCESS) return SFERR_NO_SUPPORT; #ifdef _DEBUG g_CriticalSection.Enter(); wsprintf(g_cMsg, "Enter: FFD_PutRawForce: %Force=%ld\n",szDeviceName, (LONG) lForceData); OutputDebugString(g_cMsg); g_CriticalSection.Leave(); #endif switch (pForce->m_AxisMask) { case (X_AXIS|Y_AXIS): // use 2D hRet = CMD_Force_Out(lForceDataX, X_AXIS); if (FAILED(hRet)) break; hRet = CMD_Force_Out(lForceDataY, Y_AXIS); break; case X_AXIS: lForceDataX = lForceData; hRet = CMD_Force_Out(lForceDataX, X_AXIS); break; case Y_AXIS: lForceDataY = lForceData; hRet = CMD_Force_Out(lForceDataY, Y_AXIS); break; default: hRet = SFERR_NO_SUPPORT; break; } #ifdef _DEBUG g_CriticalSection.Enter(); wsprintf(g_cMsg, "Exit: FFD_PutRawForce: %Force=%ld\n",szDeviceName, (LONG) lForceData); OutputDebugString(g_cMsg); g_CriticalSection.Leave(); #endif return hRet; } // ---------------------------------------------------------------------------- // Function: FFD_Download // // Purpose: Downloads the specified Effect object UD/BE/SE to the FF device. // Parameters: // IN OUT PDNHANDLE pDnloadD - Ptr to DNHANDLE to store EffectID // IN PEFFECT pEffect - Ptr Common attributes for Effects // IN PENVELOPE pEnvelope - Ptr to an ENVELOPE // IN PVOID pTypeParam - Ptr to a Type specific parameter // IN ULONG ulAction - Type of action desired // // Returns: // SUCCESS - if successful // SFERR_FFDEVICE_MEMORY - no more download RAM available // SFERR_INVALID_PARAM - Invalid parameters // SFERR_NO_SUPPORT - if function is unsupported. // Algorithm: // // Comments: // // ulAction: Type of action desired after downloading // PLAY_STORE - stores in Device only // || the following options: // PLAY_STORE - stores in Device only // || the following options: // PLAY_SOLO - stop other forces playing, make this the only one. // PLAY_SUPERIMPOSE- mix with currently playing device // PLAY_LOOP - Loops for Count times, where count value is in // HIWORD // PLAY_FOREVER - Play forever until told to stop: PLAY_LOOP with 0 // value in HIWORD // ---------------------------------------------------------------------------- HRESULT WINAPI FFD_DownloadEffect( IN OUT PDNHANDLE pDnloadID, IN PEFFECT pEffect, IN PENVELOPE pEnvelope, IN PVOID pTypeParam, IN ULONG ulAction) { #ifdef _DEBUG g_CriticalSection.Enter(); wsprintf(g_cMsg, "Enter: FFD_DownloadEffect. DnloadID= %ld, Type=%ld, SubType=%ld\r\n", *pDnloadID, pEffect->m_Type, pEffect->m_SubType); OutputDebugString(g_cMsg); g_CriticalSection.Leave(); #endif if (NULL == g_pJoltMidi) return (SFERR_DRIVER_ERROR); //REVIEW: Still need to do boundary Assertions, structure size check etc... assert(pDnloadID && pEffect); if (!pDnloadID || !pEffect) return (SFERR_INVALID_PARAM); // If the Effect type is not a BE_DELAY or EF_ROM_EFFECT, // make sure there is a pTypeParam if ((BE_DELAY != pEffect->m_SubType) && (EF_ROM_EFFECT != pEffect->m_Type)) { assert(pTypeParam); if (NULL == pTypeParam) return (SFERR_INVALID_PARAM); } // Don't support PLAY_LOOP for this version if ((ulAction & PLAY_LOOP) || (ulAction & 0xffff0000)) return (SFERR_NO_SUPPORT); // REVIEW: TO increase performance, we should do a parameter mod check // For now, we'll assume all parameters are changed, for dwFlags // otherwise, we should check for: //#define DIEP_ALLPARAMS 0x000000FF - All fields valid //#define DIEP_AXES 0x00000020 - cAxes and rgdwAxes //#define DIEP_DIRECTION 0x00000040 - cAxes and rglDirection //#define DIEP_DURATION 0x00000001 - dwDuration //#define DIEP_ENVELOPE 0x00000080 - lpEnvelope //#define DIEP_GAIN 0x00000004 - dwGain //#define DIEP_NODOWNLOAD 0x80000000 - suppress auto - download //#define DIEP_SAMPLEPERIOD 0x00000002 - dwSamplePeriod //#define DIEP_TRIGGERBUTTON 0x00000008 - dwTriggerButton //#define DIEP_TRIGGERREPEATINTERVAL 0x00000010 - dwTriggerRepeatInterval //#define DIEP_TYPESPECIFICPARAMS 0x00000100 - cbTypeSpecificParams // and lpTypeSpecificParams // Figure out the Common members BYTE bAxisMask = (BYTE) pEffect->m_AxisMask; ULONG ulDuration = pEffect->m_Duration; if (PLAY_FOREVER == (ulAction & PLAY_FOREVER)) ulDuration = 0; // map button 10 to button 9 if(pEffect->m_ButtonPlayMask == 0x0200) pEffect->m_ButtonPlayMask = 0x0100; else if(pEffect->m_ButtonPlayMask == 0x0100) return SFERR_NO_SUPPORT; DWORD dwFlags = DIEP_ALLPARAMS; SE_PARAM seParam = { sizeof(SE_PARAM)}; PBE_SPRING_PARAM pBE_xxx1D; PBE_SPRING_2D_PARAM pBE_xxx2D; BE_XXX BE_xxx; PBE_WALL_PARAM pBE_Wall; // Decode the type of Download to use HRESULT hRet = SFERR_INVALID_PARAM; ULONG ulSubType = pEffect->m_SubType; switch (pEffect->m_Type) { case EF_BEHAVIOR: switch (ulSubType) { case BE_SPRING: // 1D Spring case BE_DAMPER: // 1D Damper case BE_INERTIA: // 1D Inertia case BE_FRICTION: // 1D Friction pBE_xxx1D = (PBE_SPRING_PARAM) pTypeParam; if (X_AXIS == bAxisMask) { BE_xxx.m_XConstant = pBE_xxx1D->m_Kconstant; BE_xxx.m_YConstant = 0; if (ulSubType != BE_FRICTION) BE_xxx.m_Param3 = pBE_xxx1D->m_AxisCenter; BE_xxx.m_Param4= 0; } else { if (Y_AXIS != bAxisMask) break; else { BE_xxx.m_YConstant = pBE_xxx1D->m_Kconstant; BE_xxx.m_XConstant = 0; if (ulSubType != BE_FRICTION) BE_xxx.m_Param4 = pBE_xxx1D->m_AxisCenter; BE_xxx.m_Param3= 0; } } hRet = CMD_Download_BE_XXX(pEffect, NULL, &BE_xxx, (PDNHANDLE) pDnloadID, dwFlags); break; case BE_SPRING_2D: // 2D Spring case BE_DAMPER_2D: // 2D Damperfs case BE_INERTIA_2D: // 2D Inertia case BE_FRICTION_2D: // 2D Friction // Validate AxisMask is for 2D if ( (X_AXIS|Y_AXIS) != bAxisMask) break; pBE_xxx2D = (PBE_SPRING_2D_PARAM) pTypeParam; BE_xxx.m_XConstant = pBE_xxx2D->m_XKconstant; if (ulSubType != BE_FRICTION_2D) { BE_xxx.m_YConstant = pBE_xxx2D->m_YKconstant; BE_xxx.m_Param3 = pBE_xxx2D->m_XAxisCenter; BE_xxx.m_Param4 = pBE_xxx2D->m_YAxisCenter; } else { BE_xxx.m_YConstant = pBE_xxx2D->m_XAxisCenter; BE_xxx.m_Param3 = 0; BE_xxx.m_Param4 = 0; } hRet = CMD_Download_BE_XXX(pEffect, NULL, &BE_xxx, (PDNHANDLE) pDnloadID, dwFlags); break; case BE_WALL: pBE_Wall = (PBE_WALL_PARAM) pTypeParam; if ( (pBE_Wall->m_WallAngle == 0) || (pBE_Wall->m_WallAngle == 90) || (pBE_Wall->m_WallAngle == 180) || (pBE_Wall->m_WallAngle == 270) ) { BE_xxx.m_XConstant = pBE_Wall->m_WallType; BE_xxx.m_YConstant = pBE_Wall->m_WallConstant; BE_xxx.m_Param3 = pBE_Wall->m_WallAngle; BE_xxx.m_Param4 = pBE_Wall->m_WallDistance; hRet = CMD_Download_BE_XXX(pEffect, NULL, &BE_xxx, (PDNHANDLE) pDnloadID, dwFlags); } else hRet = SFERR_NO_SUPPORT; break; case BE_DELAY: if (0 == ulDuration) return (SFERR_INVALID_PARAM); hRet = CMD_Download_NOP_DELAY(ulDuration, pEffect, (PDNHANDLE) pDnloadID); break; default: hRet = SFERR_NO_SUPPORT; break; } break; case EF_USER_DEFINED: hRet = CMD_Download_UD_Waveform(ulDuration, pEffect, ((PUD_PARAM)pTypeParam)->m_NumVectors, ((PUD_PARAM)pTypeParam)->m_pForceData, ulAction, pDnloadID, dwFlags); break; case EF_ROM_EFFECT: // Setup the default parameters for the Effect if (SUCCESS != g_pJoltMidi->SetupROM_Fx(pEffect)) { hRet = SFERR_INVALID_OBJECT; break; } // Map the SE_PARAM // set the frequency seParam.m_Freq = 0; // unused by ROM Effect seParam.m_SampleRate = pEffect->m_ForceOutputRate; seParam.m_MinAmp = -100; seParam.m_MaxAmp = 100; hRet = CMD_Download_SYNTH(pEffect, pEnvelope, &seParam, ulAction, (USHORT *) pDnloadID, dwFlags); break; case EF_SYNTHESIZED: if (0 == ((PSE_PARAM)pTypeParam)->m_SampleRate) ((PSE_PARAM)pTypeParam)->m_SampleRate = DEFAULT_JOLT_FORCE_RATE; if (0 == pEffect->m_ForceOutputRate) pEffect->m_ForceOutputRate = DEFAULT_JOLT_FORCE_RATE; hRet = CMD_Download_SYNTH(pEffect, pEnvelope, (PSE_PARAM) pTypeParam, ulAction, pDnloadID, dwFlags); break; default: hRet = SFERR_INVALID_PARAM; } #ifdef _DEBUG g_CriticalSection.Enter(); wsprintf(g_cMsg, "Exit: FFD_DownloadEffect. DnloadID = %lx, hRet=%lx\r\n", *pDnloadID, hRet); OutputDebugString(g_cMsg); g_CriticalSection.Leave(); #endif return (hRet); } // *** ---------------------------------------------------------------------*** // Function: FFD_DestroyEffect // Purpose: Destroys the Effect from download RAM storage area. // Parameters: // IN EFHANDLE EffectID // an Effect ID // // Returns: SUCCESS if successful command sent, else // SFERR_INVALID_ID // SFERR_NO_SUPPORT // // Algorithm: // // Comments: // The Device's Effect ID and memory is returned to free pool. // // *** ---------------------------------------------------------------------*** HRESULT WINAPI FFD_DestroyEffect( IN DNHANDLE DnloadID) { #ifdef _DEBUG g_CriticalSection.Enter(); wsprintf(g_cMsg, "Enter: FFD_DestroyEffect. DnloadID:%ld\r\n", DnloadID); OutputDebugString(g_cMsg); g_CriticalSection.Leave(); #endif HRESULT hRet = CMD_DestroyEffect(DnloadID); #ifdef _DEBUG g_CriticalSection.Enter(); wsprintf(g_cMsg, "Exit: FFD_DestroyEffect. DnloadID:%ld\r\n", DnloadID); OutputDebugString(g_cMsg); g_CriticalSection.Leave(); #endif return (hRet); } // *** ---------------------------------------------------------------------*** // Function: FFD_VFXProcessEffect // Purpose: Commands FF device to process downloaded Effects // // Parameters: // IN OUT PDNHANDLE pDnloadID // Storage for new Download ID // IN int nNumEffects // Number of Effect IDs in the array // IN ULONG ulProcessMode // Processing mode // IN PDNHANDLE pPListArray// Pointer to an array of Effect IDs // // Returns: SUCCESS - if successful, else // SFERR_INVALID_PARAM // SFERR_NO_SUPPORT // // Algorithm: // // Comments: // The following processing is available: // CONCATENATE: Enew = E1 followed by E2 // SUPERIMPOSE: Enew = E1 (t1) + E2 (t1) + E1 (t2) // + E2 (t2) + . . . E1 (tn) + E2 (tn) // // ulProcessMode: // Processing mode: // CONCATENATE - CONCATENATE // SUPERIMPOSE - Mix or overlay // // pEFHandle: // The array of Effect IDs must be one more than the actual number // of Effect IDs to use. The first entry pEFHandle[0] will be // used to store the new Effect ID created for the CONCATENATE // and SUPERIMPOSE process choice. // // *** ---------------------------------------------------------------------*** HRESULT WINAPI FFD_VFXProcessEffect( IN ULONG ulButtonPlayMask, IN OUT PDNHANDLE pDnloadID, IN int nNumEffects, IN ULONG ulProcessMode, IN PDNHANDLE pPListArray) { #ifdef _DEBUG g_CriticalSection.Enter(); wsprintf(g_cMsg, "FFD_ProcessEffect, DnloadID=%ld\r\n", *pDnloadID); OutputDebugString(g_cMsg); g_CriticalSection.Leave(); #endif assert(pDnloadID && pPListArray); if ((NULL == pDnloadID) || (NULL == pPListArray)) return (SFERR_INVALID_PARAM); assert(nNumEffects > 0 && nNumEffects <= MAX_PLIST_EFFECT_SIZE); if ((nNumEffects > MAX_PLIST_EFFECT_SIZE) || (nNumEffects <= 0)) return (SFERR_INVALID_PARAM); // map button 10 to button 9 if(ulButtonPlayMask == 0x0200) ulButtonPlayMask = 0x0100; else if(ulButtonPlayMask == 0x0100) return SFERR_NO_SUPPORT; HRESULT hRet; hRet = CMD_VFXProcessEffect(ulButtonPlayMask, pDnloadID,nNumEffects, ulProcessMode,pPListArray, PLAY_STORE); return (hRet); } // *** ---------------------------------------------------------------------*** // Function: FFD_GetEffectForceValue // Purpose: Returns the force value at tick time for the Effect // // Parameters: DNHANDLE DnloadID - Effect ID // ULONG ulAxisMask - X_AXIS or Y_AXIS // ULONG ulIndex - Index into the Effect // PLONG pForceValue - Ptr to a LONG variable // // Returns: SUCCESS - if successful, else // SFERR_INVALID_PARAM // SFERR_NO_SUPPORT // // Algorithm: // // Comments: // *** ---------------------------------------------------------------------*** HRESULT FFD_GetEffectForceValue( IN DNHANDLE DnloadID, IN ULONG ulAxisMask, IN ULONG ulIndex, IN OUT PLONG pForceValue) { #ifdef _DEBUG g_CriticalSection.Enter(); wsprintf(g_cMsg, "FFD_GetEffectForceValue, DnloadID=%d\r\n", DnloadID); OutputDebugString(g_cMsg); g_CriticalSection.Leave(); #endif return (SFERR_NO_SUPPORT); } // *** ---------------------------------------------------------------------*** // Function: AngleToXY // Purpose: Computes XY from Angle // Parameters: // IN LONG lDirectionAngle2D - Angle in Degrees // IN LONG lForceValue - Resultant Force // IN ULONG ulAxisMask - Axis to Affect // IN OUT PLONG pX - X-Axis store // IN OUT PLONG pY - Y-Axis store // Returns: pX, pY with valid angle components // // *** ---------------------------------------------------------------------*** HRESULT AngleToXY( IN LONG lDirectionAngle2D, IN LONG lValueData, IN ULONG ulAxisMask, IN OUT PLONG pX, IN OUT PLONG pY) { // If single Axis only, then use the force on that axis. // If X and Y-axis, then use 2D angle // If X, Y, and Z-axis, then use 3D angle // If axis is other than X,Y,Z then no support double Radian; switch (ulAxisMask) { case (X_AXIS|Y_AXIS): // use 2D Radian = xDegrees2Radians(lDirectionAngle2D % 360); #ifdef ORIENTATION_MODE1 *pX = - (long) (lValueData * cos(Radian)); *pY = (long) (lValueData * sin(Radian)); #else *pX = - (long) (lValueData * sin(Radian)); *pY = (long) (lValueData * cos(Radian)); #endif break; case X_AXIS: *pX = lValueData; *pY = 0; break; case Y_AXIS: *pX = 0; *pY = lValueData; break; case (X_AXIS|Y_AXIS|Z_AXIS): // use 3D default: return (SFERR_NO_SUPPORT); break; } return SUCCESS; } // // --- VFX SUPPORT FUNCTIONS // // *** ---------------------------------------------------------------------*** // Function: CreateEffectFromFile // Purpose: Creates an Effect from a RIFF file // Parameters: PSWFORCE pISWForce - Ptr to a SWForce // PPSWEFFECT ppISWEffect - Ptr to a SWEffect // TCHAR* pszFileName - Name of file to open // DWORD dwByteCount - Bytes in block // LPGUID lpGUID - Joystick GUID // // // Returns: SUCCESS - if successful, else // error code // // Algorithm: // // Comments: // // *** ---------------------------------------------------------------------*** HRESULT CreateEffectFromFile( IN LPCTSTR pszFileName, IN ULONG ulAction, IN OUT PDNHANDLE pDnloadID, IN DWORD dwFlags) { #ifdef _DEBUG OutputDebugString("CImpIVFX::CreateEffectFromFile\n"); #endif // parameter checking if ( !(pszFileName && pDnloadID) ) return SFERR_INVALID_PARAM; HMMIO hmmio = NULL; DWORD dwFileSize; DWORD dwFilePos; BYTE* pBuffer = NULL; DWORD dwBytesRead; HRESULT hResult = SUCCESS; MMRESULT mmresult; // open a RIFF file using the buffer MMIOINFO mmioinfo; mmioinfo.dwFlags = 0; mmioinfo.fccIOProc = NULL; mmioinfo.pIOProc = NULL; mmioinfo.wErrorRet = 0; mmioinfo.htask = NULL; mmioinfo.cchBuffer = 0; mmioinfo.pchBuffer = NULL; mmioinfo.pchNext = 0; mmioinfo.pchEndRead = 0; mmioinfo.lBufOffset = 0; mmioinfo.adwInfo[0] = 0; mmioinfo.adwInfo[1] = 0; mmioinfo.adwInfo[3] = 0; mmioinfo.dwReserved1 = 0; mmioinfo.dwReserved2 = 0; mmioinfo.hmmio = NULL; // make a non-constant copy of the string TCHAR szFileNameCopy[256]; strcpy(szFileNameCopy, pszFileName); // open the RIFF file for reading hmmio = mmioOpen(szFileNameCopy, &mmioinfo, MMIO_READ); if(hmmio == NULL) { hResult = MMIOErrorToSFERRor(mmioinfo.wErrorRet); goto cleanup; } // calculate the size of the file dwFileSize = mmioSeek(hmmio, 0, SEEK_END); if(dwFileSize == -1) { hResult = MMIOErrorToSFERRor(MMIOERR_CANNOTSEEK); goto cleanup; } dwFilePos = mmioSeek(hmmio, 0, SEEK_SET); if(dwFilePos == -1) { hResult = MMIOErrorToSFERRor(MMIOERR_CANNOTSEEK); goto cleanup; } // get a chunk of memory big enough to hold it pBuffer = new BYTE[dwFileSize]; if(pBuffer == NULL) { hResult = VFX_ERR_FILE_OUT_OF_MEMORY; goto cleanup; } // read the file into the chunk of memory dwBytesRead = mmioRead(hmmio, (char*)pBuffer, dwFileSize); if(dwBytesRead != dwFileSize) { if(dwBytesRead == 0) hResult = VFX_ERR_FILE_END_OF_FILE; else hResult = MMIOErrorToSFERRor(MMIOERR_CANNOTREAD); goto cleanup; } // close the RIFF file mmresult = mmioClose(hmmio, 0); hmmio = NULL; if(mmresult != MMSYSERR_NOERROR) { hResult = MMIOErrorToSFERRor(mmresult); goto cleanup; } // call CreateEffectFromBuffer hResult = CreateEffectFromBuffer(pBuffer, dwFileSize, ulAction, pDnloadID, dwFlags); // clean up cleanup: // close the RIFF file if(hmmio != NULL) { mmresult = mmioClose(hmmio, 0); if(mmresult != MMSYSERR_NOERROR) { hResult = MMIOErrorToSFERRor(mmresult); } } // de-allocate any allocated memory if(pBuffer != NULL) delete [] pBuffer; // return the error code, which is SUCCESS, unless there was an error return hResult; } // *** ---------------------------------------------------------------------*** // Function: CreateEffectFromBuffer // Purpose: Creates an Effect from a buffer // Parameters: PSWFORCE pISWForce - Ptr to a SWForce // PPSWEFFECT ppISWEffect - Ptr to a SWEffect // PVOID pBuffer - Ptr to a buffer block // DWORD dwByteCount - Bytes in block // LPGUID lpGUID - Joystick GUID // // // Returns: SUCCESS - if successful, else // error code // // Algorithm: // // Comments: // // *** ---------------------------------------------------------------------*** HRESULT CreateEffectFromBuffer( IN PVOID pBuffer, IN DWORD dwByteCount, IN ULONG ulAction, IN OUT PDNHANDLE pDnloadID, IN DWORD dwFlags) { #ifdef _DEBUG OutputDebugString("CImpIVFX::CreateEffectFromBuffer\n"); #endif // parameter checking if ( !(pBuffer && pDnloadID) ) return SFERR_INVALID_PARAM; // variables used in this function #define ID_TABLE_SIZE 50 MMRESULT mmresult; DWORD dwMaxID = 0; // maximum id of effects entered into the following table DNHANDLE rgdwDnloadIDTable[ID_TABLE_SIZE]; DNHANDLE dwCurrentDnloadID = 0; int nNextID = 0; HRESULT hResult = SUCCESS; DWORD dwBytesRead; DWORD dwBytesToRead; BYTE* pParam = NULL; BOOL bDone = FALSE; BOOL bSubEffects = FALSE; DWORD dwID; DWORD c; // cleanup counter variable // debugging variables (to make sure we destroy all but one // created effect on success, and that we destory every // created effect on failure)... #ifdef _DEBUG int nEffectsCreated = 0; int nEffectsDestroyed = 0; BOOL bFunctionSuccessful = FALSE; #endif //_DEBUG // clear effect table (we check it during cleanup... anything // that isn't NULL gets destroyed.) memset(rgdwDnloadIDTable,NULL,sizeof(rgdwDnloadIDTable)); // open a RIFF memory file using the buffer MMIOINFO mmioinfo; mmioinfo.dwFlags = 0; mmioinfo.fccIOProc = FOURCC_MEM; mmioinfo.pIOProc = NULL; mmioinfo.wErrorRet = 0; mmioinfo.htask = NULL; mmioinfo.cchBuffer = dwByteCount; mmioinfo.pchBuffer = (char*)pBuffer; mmioinfo.pchNext = 0; mmioinfo.pchEndRead = 0; mmioinfo.lBufOffset = 0; mmioinfo.adwInfo[0] = 0; mmioinfo.adwInfo[1] = 0; mmioinfo.adwInfo[2] = 0; mmioinfo.dwReserved1 = 0; mmioinfo.dwReserved2 = 0; mmioinfo.hmmio = NULL; HMMIO hmmio; hmmio = mmioOpen(NULL, &mmioinfo, MMIO_READWRITE); if(hmmio == NULL) { hResult = MMIOErrorToSFERRor(mmioinfo.wErrorRet); goto cleanup; } // descend into FORC RIFF MMCKINFO mmckinfoForceEffectRIFF; mmckinfoForceEffectRIFF.fccType = FCC_FORCE_EFFECT_RIFF; mmresult = mmioDescend(hmmio, &mmckinfoForceEffectRIFF, NULL, MMIO_FINDRIFF); if(mmresult != MMSYSERR_NOERROR) { hResult = MMIOErrorToSFERRor(mmresult); goto cleanup; } //! handle loading of GUID chunk when its implemented/testable // descend into trak list MMCKINFO mmckinfoTrackLIST; mmckinfoTrackLIST.fccType = FCC_TRACK_LIST; mmresult = mmioDescend(hmmio, &mmckinfoTrackLIST, &mmckinfoForceEffectRIFF, MMIO_FINDLIST); if(mmresult != MMSYSERR_NOERROR) { hResult = MMIOErrorToSFERRor(mmresult); goto cleanup; } // descend into the first efct list (there has to be at least one effect) MMCKINFO mmckinfoEffectLIST; mmckinfoEffectLIST.fccType = FCC_EFFECT_LIST; mmresult = mmioDescend(hmmio, &mmckinfoEffectLIST, &mmckinfoTrackLIST, MMIO_FINDLIST); if(mmresult != MMSYSERR_NOERROR) { hResult = MMIOErrorToSFERRor(mmresult); goto cleanup; } bDone = FALSE; do { // descend into id chunk MMCKINFO mmckinfoIDCHUNK; mmckinfoIDCHUNK.ckid = FCC_ID_CHUNK; mmresult = mmioDescend(hmmio, &mmckinfoIDCHUNK, &mmckinfoEffectLIST, MMIO_FINDCHUNK); if(mmresult != MMSYSERR_NOERROR) { hResult = MMIOErrorToSFERRor(mmresult); goto cleanup; } // read the id //DWORD dwID; moved to being function global so we can use it near the end dwBytesToRead = sizeof(DWORD); dwBytesRead = mmioRead(hmmio, (char*)&dwID, dwBytesToRead); if(dwBytesRead != dwBytesToRead) { if(dwBytesRead == 0) hResult = VFX_ERR_FILE_END_OF_FILE; else hResult = MMIOErrorToSFERRor(MMIOERR_CANNOTREAD); goto cleanup; } if(dwID >= ID_TABLE_SIZE) { hResult = VFX_ERR_FILE_BAD_FORMAT; goto cleanup; } // ascend from id chunk mmresult = mmioAscend(hmmio, &mmckinfoIDCHUNK, 0); if(mmresult != MMSYSERR_NOERROR) { hResult = MMIOErrorToSFERRor(mmresult); goto cleanup; } // descend into data chunk MMCKINFO mmckinfoDataCHUNK; mmckinfoDataCHUNK.ckid = FCC_DATA_CHUNK; mmresult = mmioDescend(hmmio, &mmckinfoDataCHUNK, &mmckinfoEffectLIST, MMIO_FINDCHUNK); if(mmresult != MMSYSERR_NOERROR) { hResult = MMIOErrorToSFERRor(mmresult); goto cleanup; } // read the effect structure EFFECT effect; dwBytesToRead = sizeof(EFFECT); dwBytesRead = mmioRead(hmmio, (char*)&effect, dwBytesToRead); if(dwBytesRead != dwBytesToRead) { if(dwBytesRead == 0) hResult = VFX_ERR_FILE_END_OF_FILE; else hResult = MMIOErrorToSFERRor(MMIOERR_CANNOTREAD); goto cleanup; } // get the envelope structure ENVELOPE envelope; dwBytesToRead = sizeof(ENVELOPE); dwBytesRead = mmioRead(hmmio, (char*)&envelope, dwBytesToRead); if(dwBytesRead != dwBytesToRead) { if(dwBytesRead == 0) hResult = VFX_ERR_FILE_END_OF_FILE; else hResult = MMIOErrorToSFERRor(MMIOERR_CANNOTREAD); goto cleanup; } // calculate the size of and allocate a param structure if(pParam != NULL) { delete [] pParam; pParam = NULL; } // find cur pos w/o changing it DWORD dwCurrentFilePos = mmioSeek(hmmio, 0, SEEK_CUR); if(dwCurrentFilePos == -1) { hResult = MMIOErrorToSFERRor(MMIOERR_CANNOTSEEK); goto cleanup; } DWORD dwEndOfChunk = mmckinfoDataCHUNK.dwDataOffset + mmckinfoDataCHUNK.cksize; dwBytesToRead = dwEndOfChunk - dwCurrentFilePos; pParam = new BYTE[dwBytesToRead]; if(pParam == NULL) { hResult = VFX_ERR_FILE_OUT_OF_MEMORY; goto cleanup; } // get the param structure dwBytesRead = mmioRead(hmmio, (char*)pParam, dwBytesToRead); if(dwBytesRead != dwBytesToRead) { if(dwBytesRead == 0) hResult = VFX_ERR_FILE_END_OF_FILE; else hResult = MMIOErrorToSFERRor(MMIOERR_CANNOTREAD); goto cleanup; } // ascend the data chunk mmresult = mmioAscend(hmmio, &mmckinfoDataCHUNK, 0); if(mmresult != MMSYSERR_NOERROR) { hResult = MMIOErrorToSFERRor(mmresult); goto cleanup; } // ascend from the efct list mmresult = mmioAscend(hmmio, &mmckinfoEffectLIST, 0); if(mmresult != MMSYSERR_NOERROR) { hResult = MMIOErrorToSFERRor(mmresult); goto cleanup; } // reset subeffects flag bSubEffects = FALSE; // special fix-ups for user-defined if(effect.m_Type == EF_USER_DEFINED && (effect.m_SubType == PL_CONCATENATE || effect.m_SubType == PL_SUPERIMPOSE || effect.m_SubType == UD_WAVEFORM)) { if(effect.m_SubType == UD_WAVEFORM) { // fix the pointer to the force data in the UD_PARAM BYTE* pForceData = pParam + sizeof(UD_PARAM); // - sizeof(LONG*); UD_PARAM* pUDParam = (UD_PARAM*)pParam; pUDParam->m_pForceData = (LONG*)pForceData; // do a sanity check if(pUDParam->m_NumVectors > MAX_UD_PARAM_FORCE_DATA_COUNT) { hResult = VFX_ERR_FILE_BAD_FORMAT; goto cleanup; } } else if(effect.m_SubType == PL_CONCATENATE || effect.m_SubType == PL_SUPERIMPOSE) { // fix the pointer to the PSWEFFECT list in the PL_PARAM BYTE* pProcessList = pParam + sizeof(PL_PARAM); PL_PARAM* pPLParam = (PL_PARAM*)pParam; pPLParam->m_pProcessList = (PPSWEFFECT)pProcessList; // do a sanity check if(pPLParam->m_NumEffects > MAX_PL_PARAM_NUM_EFFECTS) { hResult = VFX_ERR_FILE_BAD_FORMAT; goto cleanup; } // make sure all entries in this process list are valid ULONG i; for (i = 0; i < pPLParam->m_NumEffects; i++) { UINT nThisID = (UINT)pPLParam->m_pProcessList[i]; if(nThisID >= ID_TABLE_SIZE) { hResult = VFX_ERR_FILE_BAD_FORMAT; goto cleanup; } DNHANDLE dwThisDnloadID=rgdwDnloadIDTable[nThisID]; if(dwThisDnloadID == 0) { hResult = VFX_ERR_FILE_BAD_FORMAT; goto cleanup; } } // use the ID table to insert the download ID's for(i=0; im_NumEffects; i++) { UINT nThisID = (UINT)pPLParam->m_pProcessList[i]; DNHANDLE dwThisDnloadID=rgdwDnloadIDTable[nThisID]; pPLParam->m_pProcessList[i] = (IDirectInputEffect*)dwThisDnloadID; // since this effect has been used in a process list, // and it will be destroyed after being used in CreateEffect, // null it's entry in the table so it doesn't get erroneously // redestroyed during cleanup of an error. rgdwDnloadIDTable[nThisID] = NULL; } // we have a process list with sub effects, so set the flag bSubEffects = TRUE; } else { // there are no other UD sub-types hResult = VFX_ERR_FILE_BAD_FORMAT; goto cleanup; } } // download the effect // create the effect //hResult = pISWForce->CreateEffect(&pISWEffect, &effect, // &envelope, pParam); if(effect.m_SubType != PL_CONCATENATE && effect.m_SubType != PL_SUPERIMPOSE) { EFFECT SmallEffect; SmallEffect.m_Bytes = sizeof(EFFECT); SmallEffect.m_Type = effect.m_Type; SmallEffect.m_SubType = effect.m_SubType; SmallEffect.m_AxisMask = effect.m_AxisMask; SmallEffect.m_DirectionAngle2D = effect.m_DirectionAngle2D; SmallEffect.m_DirectionAngle3D = effect.m_DirectionAngle3D; SmallEffect.m_Duration = effect.m_Duration; SmallEffect.m_ForceOutputRate = effect.m_ForceOutputRate; SmallEffect.m_Gain = effect.m_Gain; SmallEffect.m_ButtonPlayMask = effect.m_ButtonPlayMask; *pDnloadID = 0; hResult = FFD_DownloadEffect(pDnloadID, &SmallEffect, &envelope, pParam, ulAction); } else { ULONG ulButtonPlayMask = effect.m_ButtonPlayMask; int nNumEffects = ((PL_PARAM*)pParam)->m_NumEffects; ULONG ulProcessMode = effect.m_SubType; PDNHANDLE pPListArray = new DNHANDLE[ID_TABLE_SIZE]; for(int i=0; im_pProcessList[i]); *pDnloadID = 0; hResult = FFD_VFXProcessEffect(ulButtonPlayMask, pDnloadID, nNumEffects, ulProcessMode,pPListArray); } // moved check for success below... #ifdef _DEBUG if (!FAILED(hResult)) nEffectsCreated++; #endif //_DEBUG // if there were sub effects we need to destroy them, making // their ref counts become 1, so the entire effect can be destroyed // by destroying the root effect. #if 0 if (bSubEffects) { PL_PARAM* pPLParam = (PL_PARAM*)pParam; for (ULONG i = 0; i < pPLParam->m_NumEffects; i++) { ASSERT(pPLParam->m_pProcessList[i] != NULL); pISWForce->DestroyEffect(pPLParam->m_pProcessList[i]); #ifdef _DEBUG nEffectsDestroyed++; #endif //_DEBUG } } #endif // now check for success of CreateEffect, because regardless of // whether or not it succeeded, we -must- have destroyed the subeffects // before continuing, or cleanup will not work properly... if (SUCCESS != hResult) { goto cleanup; } // put the id/DnloadID pair into the map rgdwDnloadIDTable[dwID] = *pDnloadID; //pISWEffect; // keep track of the highest ID in the effect table if (dwID > dwMaxID) dwMaxID = dwID; // try to descend the next efct mmresult = mmioDescend(hmmio, &mmckinfoEffectLIST, &mmckinfoTrackLIST, MMIO_FINDLIST); if(mmresult == MMIOERR_CHUNKNOTFOUND) { // we are at the end of the list bDone = TRUE; } else if(mmresult != MMSYSERR_NOERROR) { hResult = MMIOErrorToSFERRor(mmresult); goto cleanup; } } while(!bDone); // ascend from trak list mmresult = mmioAscend(hmmio, &mmckinfoTrackLIST, 0); if(mmresult != MMSYSERR_NOERROR) { hResult = MMIOErrorToSFERRor(mmresult); goto cleanup; } // ascend from FORCE RIFF mmresult = mmioAscend(hmmio, &mmckinfoForceEffectRIFF, 0); if(mmresult != MMSYSERR_NOERROR) { hResult = MMIOErrorToSFERRor(mmresult); goto cleanup; } // get the return value //*pDnloadID = dwCurrentDnloadID; // clear the final effect's entry in the table so we don't destroy it during cleanup rgdwDnloadIDTable[dwID] = 0; // at this point the entire table should be NULL... make sure of it for (c = 0; c <= dwMaxID; c++) ; #ifdef _DEBUG bFunctionSuccessful = TRUE; #endif //_DEBUG cleanup: // destroy everything in the effect table that isn't NULL for (c = 0; c <= dwMaxID; c++) if (NULL != rgdwDnloadIDTable[c]) { FFD_DestroyEffect(rgdwDnloadIDTable[c]); rgdwDnloadIDTable[c] = 0; #ifdef _DEBUG nEffectsDestroyed++; #endif //_DEBUG } #ifdef _DEBUG // make sure we destroy all but one created effect on success, // and that we destory -every- created effect on failure. if (bFunctionSuccessful) { ;//ASSERT(nEffectsCreated - 1 == nEffectsDestroyed); } else { ;//ASSERT(nEffectsCreated == nEffectsDestroyed); } #endif //_DEBUG // close the memory RIFF file if(hmmio != NULL) { mmresult = mmioClose(hmmio, 0); if(mmresult != MMSYSERR_NOERROR) { hResult = MMIOErrorToSFERRor(mmresult); } } // de-allocate any allocated memory if(pParam != NULL) delete [] pParam; // return the error code, which is SUCCESS, unless there was an error return hResult; } HRESULT MMIOErrorToSFERRor(MMRESULT mmresult) { HRESULT hResult; switch(mmresult) { case MMIOERR_FILENOTFOUND: hResult = VFX_ERR_FILE_NOT_FOUND; break; case MMIOERR_OUTOFMEMORY: hResult = VFX_ERR_FILE_OUT_OF_MEMORY; break; case MMIOERR_CANNOTOPEN: hResult = VFX_ERR_FILE_CANNOT_OPEN; break; case MMIOERR_CANNOTCLOSE: hResult = VFX_ERR_FILE_CANNOT_CLOSE; break; case MMIOERR_CANNOTREAD: hResult = VFX_ERR_FILE_CANNOT_READ; break; case MMIOERR_CANNOTWRITE: hResult = VFX_ERR_FILE_CANNOT_WRITE; break; case MMIOERR_CANNOTSEEK: hResult = VFX_ERR_FILE_CANNOT_SEEK; break; case MMIOERR_CANNOTEXPAND: hResult = VFX_ERR_FILE_UNKNOWN_ERROR; break; case MMIOERR_CHUNKNOTFOUND: hResult = VFX_ERR_FILE_BAD_FORMAT; break; case MMIOERR_UNBUFFERED: hResult = VFX_ERR_FILE_UNKNOWN_ERROR; break; case MMIOERR_PATHNOTFOUND: hResult = VFX_ERR_FILE_NOT_FOUND; break; case MMIOERR_ACCESSDENIED: hResult = VFX_ERR_FILE_ACCESS_DENIED; break; case MMIOERR_SHARINGVIOLATION: hResult = VFX_ERR_FILE_SHARING_VIOLATION; break; case MMIOERR_NETWORKERROR: hResult = VFX_ERR_FILE_NETWORK_ERROR; break; case MMIOERR_TOOMANYOPENFILES: hResult = VFX_ERR_FILE_TOO_MANY_OPEN_FILES; break; case MMIOERR_INVALIDFILE: hResult = VFX_ERR_FILE_INVALID; break; default: hResult = VFX_ERR_FILE_UNKNOWN_ERROR; break; } return hResult; }