Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1316 lines
37 KiB

/****************************************************************************
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 <windows.h>
#include <mmsystem.h>
#include <assert.h>
#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; i<pPLParam->m_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; i<nNumEffects; i++)
pPListArray[i] = (DNHANDLE)(((PL_PARAM*)pParam)->m_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;
}