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.
1049 lines
29 KiB
1049 lines
29 KiB
/****************************************************************************
|
|
|
|
MODULE: FFD_SWFF.CPP
|
|
Tab settings: 5 9
|
|
|
|
Copyright 1995, 1996, 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_GetDeviceState
|
|
FFD_PutRawForce
|
|
FFD_DownloadEffect
|
|
FFD_DestroyEffect
|
|
|
|
|
|
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 SW_WHEEL.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
|
|
21-Mar-99 waltw Removed unreferenced FFD_xxx functions
|
|
|
|
****************************************************************************/
|
|
#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 "DPack.h"
|
|
#include "DTrans.h"
|
|
#include "FFDevice.h"
|
|
#include "CritSec.h"
|
|
|
|
static ACKNACK g_AckNack;
|
|
|
|
// 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_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);
|
|
_RPT0(_CRT_WARN, g_cMsg);
|
|
g_CriticalSection.Leave();
|
|
#endif
|
|
return SFERR_DRIVER_ERROR;
|
|
#if 0
|
|
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:
|
|
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;
|
|
|
|
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;
|
|
|
|
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);
|
|
_RPT0(_CRT_WARN, g_cMsg);
|
|
g_CriticalSection.Leave();
|
|
#endif
|
|
return (hRet);
|
|
#endif //0
|
|
}
|
|
|
|
// *** ---------------------------------------------------------------------***
|
|
// 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);
|
|
_RPT0(_CRT_WARN, g_cMsg);
|
|
g_CriticalSection.Leave();
|
|
#endif
|
|
|
|
ASSUME_NOT_NULL(g_pDataPackager);
|
|
ASSUME_NOT_NULL(g_pDataTransmitter);
|
|
if ((g_pDataPackager == NULL) || (g_pDataTransmitter == NULL)) {
|
|
return SFERR_DRIVER_ERROR;
|
|
}
|
|
|
|
// Create a command/data packet - send it of to the stick
|
|
HRESULT hr = g_pDataPackager->DestroyEffect(DnloadID);
|
|
if (hr != SUCCESS) {
|
|
return hr;
|
|
}
|
|
hr = g_pDataTransmitter->Transmit(g_AckNack); // Send it off
|
|
return hr;
|
|
}
|
|
|
|
|
|
// *** ---------------------------------------------------------------------***
|
|
// 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);
|
|
_RPT0(_CRT_WARN, g_cMsg);
|
|
g_CriticalSection.Leave();
|
|
#endif
|
|
if (NULL == g_pJoltMidi) return (SFERR_DRIVER_ERROR);
|
|
|
|
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;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// *** ---------------------------------------------------------------------***
|
|
// 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: 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
|
|
_RPT0(_CRT_WARN, "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;
|
|
}
|
|
|