mirror of https://github.com/tongzx/nt5src
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.
6334 lines
207 KiB
6334 lines
207 KiB
/*****************************************************************************
|
|
*
|
|
* DIDevDf.c
|
|
*
|
|
* Copyright (c) 1996 - 2000 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* Abstract:
|
|
*
|
|
* The part of IDirectInputDevice that worries about
|
|
* data formats and reading device data.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include "dinputpr.h"
|
|
#include "didev.h"
|
|
|
|
#undef sqfl
|
|
#define sqfl sqflDf
|
|
|
|
int INTERNAL
|
|
CDIDev_OffsetToIobj(PDD this, DWORD dwOfs);
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | CDIDev_GetAbsDeviceState |
|
|
*
|
|
* Get the absolute device state.
|
|
*
|
|
* @parm OUT LPVOID | pvData |
|
|
*
|
|
* Application-provided output buffer.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_GetAbsDeviceState(PDD this, LPVOID pvData)
|
|
{
|
|
return this->pdcb->lpVtbl->GetDeviceState(this->pdcb, pvData);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | CDIDev_GetRelDeviceState |
|
|
*
|
|
* Get the relative device state.
|
|
*
|
|
* @parm OUT LPVOID | pvData |
|
|
*
|
|
* Application-provided output buffer.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_GetRelDeviceState(PDD this, LPVOID pvData)
|
|
{
|
|
HRESULT hres;
|
|
|
|
hres = this->pdcb->lpVtbl->GetDeviceState(this->pdcb, pvData);
|
|
if ( SUCCEEDED(hres) ) {
|
|
UINT iaxis;
|
|
AssertF(fLimpFF(this->cAxes, this->pvLastBuffer && this->rgdwAxesOfs));
|
|
|
|
/*
|
|
* For each axis, replace the app's buffer with the delta,
|
|
* and save the old value.
|
|
*/
|
|
for ( iaxis = 0; iaxis < this->cAxes; iaxis++ ) {
|
|
LONG UNALIGNED *plApp = pvAddPvCb(pvData, this->rgdwAxesOfs[iaxis]);
|
|
LONG UNALIGNED *plLast = pvAddPvCb(this->pvLastBuffer,
|
|
this->rgdwAxesOfs[iaxis]);
|
|
LONG lNew = *plApp;
|
|
*plApp -= *plLast;
|
|
*plLast = lNew;
|
|
}
|
|
|
|
hres = S_OK;
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | CDIDev_GetDeviceStateSlow |
|
|
*
|
|
* Obtains data from the DirectInput device the slow way.
|
|
*
|
|
* Read the data into the private buffer, then copy it
|
|
* bit by bit into the application's buffer.
|
|
*
|
|
* @parm OUT LPVOID | lpvData |
|
|
*
|
|
* Application-provided output buffer.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_GetDeviceStateSlow(PDD this, LPVOID pvData)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice8::GetDeviceStateSlow, (_ "pp", this, pvData));
|
|
|
|
AssertF(this->diopt == dioptNone);
|
|
AssertF(this->pvBuffer);
|
|
AssertF(this->pdcb);
|
|
hres = this->GetDeviceState(this, this->pvBuffer);
|
|
if ( SUCCEEDED(hres) ) {
|
|
int iobj;
|
|
ZeroMemory(pvData, this->dwDataSize);
|
|
for ( iobj = this->df.dwNumObjs; --iobj >= 0; ) {
|
|
if ( this->pdix[iobj].dwOfs != 0xFFFFFFFF ) { /* Data was requested */
|
|
DWORD UNALIGNED *pdwOut = pvAddPvCb(pvData, this->pdix[iobj].dwOfs);
|
|
DWORD UNALIGNED *pdwIn = pvAddPvCb(this->pvBuffer, this->df.rgodf[iobj].dwOfs);
|
|
if ( this->df.rgodf[iobj].dwType & DIDFT_DWORDOBJS ) {
|
|
*pdwOut = *pdwIn;
|
|
} else {
|
|
*(LPBYTE)pdwOut = *(LPBYTE)pdwIn;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | CDIDev_GetDeviceStateMatched |
|
|
*
|
|
* Obtains data from the DirectInput device in the case
|
|
* where the data formats are matched.
|
|
*
|
|
* Read the data into the private buffer, then block copy it
|
|
* into the application's buffer.
|
|
*
|
|
* @parm OUT LPVOID | lpvData |
|
|
*
|
|
* Application-provided output buffer.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_GetDeviceStateMatched(PDD this, LPVOID pvData)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice8::GetDeviceStateMatched, (_ "pp", this, pvData));
|
|
|
|
AssertF(this->diopt == dioptMatch);
|
|
AssertF(this->pvBuffer);
|
|
AssertF(this->pdcb);
|
|
hres = this->GetDeviceState(this, this->pvBuffer);
|
|
|
|
if ( SUCCEEDED(hres) ) {
|
|
/*
|
|
* To keep keyboard clients happy: Zero out the fore and aft.
|
|
* No need to optimize the perfect match case, because that
|
|
* gets a different optimization level.
|
|
*/
|
|
ZeroMemory(pvData, this->dwDataSize);
|
|
memcpy(pvAddPvCb(pvData, this->ibDelta + this->ibMin),
|
|
pvAddPvCb(this->pvBuffer, this->ibMin), this->cbMatch);
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | CDIDev_GetDeviceStateDirect |
|
|
*
|
|
* Obtains data from the DirectInput device in the case
|
|
* where we can read the data directly into the client buffer.
|
|
*
|
|
* @parm OUT LPVOID | lpvData |
|
|
*
|
|
* Application-provided output buffer.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_GetDeviceStateDirect(PDD this, LPVOID pvData)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice8::GetDeviceStateDirect, (_ "pp", this, pvData));
|
|
|
|
AssertF(this->diopt == dioptDirect);
|
|
AssertF(!this->pvBuffer);
|
|
AssertF(this->pdcb);
|
|
|
|
/*
|
|
* To keep keyboard clients happy: Zero out the fore and aft.
|
|
*/
|
|
ZeroBuf(pvData, this->dwDataSize);
|
|
hres = this->GetDeviceState(this, pvAddPvCb(pvData, this->ibDelta));
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | CDIDev_GetDeviceStateEqual |
|
|
*
|
|
* Obtains data from the DirectInput device in the case
|
|
* where the two data formats are completely identical.
|
|
*
|
|
* @parm OUT LPVOID | lpvData |
|
|
*
|
|
* Application-provided output buffer.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_GetDeviceStateEqual(PDD this, LPVOID pvData)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IEqualInputDevice::GetDeviceStateEqual, (_ "pp", this, pvData));
|
|
|
|
AssertF(this->diopt == dioptEqual);
|
|
AssertF(this->ibDelta == 0);
|
|
AssertF(this->dwDataSize == this->df.dwDataSize);
|
|
AssertF(!this->pvBuffer);
|
|
AssertF(this->pdcb);
|
|
|
|
/*
|
|
* Note that this->ibMin is not necessarily zero if the device
|
|
* data format doesn't begin at zero (which keyboards don't).
|
|
*/
|
|
hres = this->GetDeviceState(this, pvData);
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method BOOL | CDIDev | IsMatchingGUID |
|
|
*
|
|
* Helper function that checks if a <t GUID> counts as
|
|
* a match when parsing the data format.
|
|
*
|
|
* @parm PCGUID | pguidSrc |
|
|
*
|
|
* The <t GUID> to check.
|
|
*
|
|
* @parm PCGUID | pguidDst |
|
|
*
|
|
* The <t GUID> it should match.
|
|
*
|
|
* @returns
|
|
*
|
|
* Nonzero if this counts as a success.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#pragma BEGIN_CONST_DATA
|
|
|
|
GUID GUID_Null; /* A zero-filled guid */
|
|
|
|
#pragma END_CONST_DATA
|
|
|
|
BOOL INLINE
|
|
CDIDev_IsMatchingGUID(PDD this, PCGUID pguidSrc, PCGUID pguidDst)
|
|
{
|
|
UNREFERENCED_PARAMETER( this );
|
|
|
|
return IsEqualGUID(pguidSrc, &GUID_Null) ||
|
|
IsEqualGUID(pguidSrc, pguidDst);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method BOOL | CDIDev | IsMatchingUsage |
|
|
*
|
|
* Helper function that checks if a <f DIMAKEUSAGEDWORD>
|
|
* counts as a match when parsing the data format.
|
|
*
|
|
* @parm DWORD | dwUsage |
|
|
*
|
|
* The <f DIMAKEUSAGEDWORD> to check.
|
|
*
|
|
* @parm int | iobj |
|
|
*
|
|
* The index of hte object to check for a match.
|
|
*
|
|
* @returns
|
|
*
|
|
* Nonzero if this counts as a success.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
BOOL INLINE
|
|
CDIDev_IsMatchingUsage(PDD this, DWORD dwUsage, int iobj)
|
|
{
|
|
AssertF(this->pdcb);
|
|
|
|
return dwUsage == this->pdcb->lpVtbl->GetUsage(this->pdcb, iobj);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method int | CDIDev | FindDeviceObjectFormat |
|
|
*
|
|
* Search the device object format table for the one that
|
|
* matches the guid in question.
|
|
*
|
|
* @parm PCODF | podf |
|
|
*
|
|
* The object to locate. If the <e DIOBJECTDATAFORMAT.rguid>
|
|
* is null, then the field is a wildcard.
|
|
*
|
|
* If the <e DIOBJECTDATAFORMAT.dwType> specifies
|
|
* <c DIDFT_ANYINSTANCE>, then any instance will be accepted.
|
|
*
|
|
* @parm PDIXLAT | pdix |
|
|
*
|
|
* The partial translation table so far. This is used to find
|
|
* an empty slot in case of wildcards.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns the index of the object that matches, or -1 if
|
|
* the object is not supported by the device.
|
|
*
|
|
* Someday: Should fall back to best match if types don't match.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
int INTERNAL
|
|
CDIDev_FindDeviceObjectFormat(PDD this, PCODF podf, PDIXLAT pdix)
|
|
{
|
|
PCODF podfD; /* The format in the device */
|
|
UINT iobj;
|
|
|
|
/*
|
|
* We must count upwards, so that first-fit chooses the smallest one.
|
|
*/
|
|
for ( iobj = 0; iobj < this->df.dwNumObjs; iobj++ ) {
|
|
podfD = &this->df.rgodf[iobj];
|
|
if (
|
|
|
|
/*
|
|
* Type needs to match.
|
|
*
|
|
* Note that works for output-only actuators:
|
|
* Since you cannot read from an output-only
|
|
* actuator, you can't put it in a data format.
|
|
*
|
|
*/
|
|
(podf->dwType & DIDFT_TYPEVALID & podfD->dwType)
|
|
|
|
/*
|
|
* Attributes need to match.
|
|
*/
|
|
&& fHasAllBitsFlFl(podfD->dwType, podf->dwType & DIDFT_ATTRVALID)
|
|
|
|
/*
|
|
* Slot needs to be empty.
|
|
*/
|
|
&& pdix[iobj].dwOfs == 0xFFFFFFFF
|
|
|
|
/*
|
|
* "If there is a guid/usage, it must match."
|
|
*
|
|
* If pguid is NULL, then the match is vacuous.
|
|
*
|
|
* If DIDOI_GUIDISUSAGE is clear, then pguid points to
|
|
* a real GUID. GUID_NULL means "Don't care" and matches
|
|
* anything. Otherwise, it must match the actual GUID.
|
|
*
|
|
* If DIDOI_GUIDISUSAGE is set, then pguid is really
|
|
* a DIMAKEUSAGEDWORD of the usage and usage page,
|
|
* which we compare against the same in the object.
|
|
*/
|
|
|
|
&& (podf->pguid == 0 ||
|
|
((podf->dwFlags & DIDOI_GUIDISUSAGE) ?
|
|
CDIDev_IsMatchingUsage(this, (DWORD)(UINT_PTR)podf->pguid, iobj) :
|
|
CDIDev_IsMatchingGUID(this, podf->pguid, podfD->pguid)))
|
|
|
|
/*
|
|
* If there is an instance number, it must match.
|
|
*/
|
|
&& fLimpFF((podf->dwType & DIDFT_ANYINSTANCE) !=
|
|
DIDFT_ANYINSTANCE,
|
|
fEqualMaskFlFl(DIDFT_ANYINSTANCE,
|
|
podf->dwType, podfD->dwType))
|
|
|
|
/*
|
|
* If there is an aspect, it must match.
|
|
*
|
|
* If the device data format doesn't specify an aspect,
|
|
* then that counts as a free match too.
|
|
*/
|
|
&& fLimpFF((podf->dwFlags & DIDOI_ASPECTMASK) &&
|
|
(podfD->dwFlags & DIDOI_ASPECTMASK),
|
|
fEqualMaskFlFl(DIDOI_ASPECTMASK,
|
|
podf->dwFlags, podfD->dwFlags))
|
|
|
|
) { /* Criterion matches, woo-hoo */
|
|
return iobj;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev | ParseDataFormat |
|
|
*
|
|
* Parse the data format passed by the application and
|
|
* convert it into a format that we can use to translate
|
|
* the device data into application data.
|
|
*
|
|
* @parm IN LPDIDATAFORMAT | lpdf |
|
|
*
|
|
* Points to a structure that describes the format of the data
|
|
* the DirectInputDevice should return.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
|
|
* <p lpvData> parameter is not a valid pointer.
|
|
*
|
|
* <c DIERR_ACQUIRED>: Cannot change the data format while the
|
|
* device is acquired.
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_ParseDataFormat(PDD this, const DIDATAFORMAT *lpdf)
|
|
{
|
|
PDIXLAT pdix;
|
|
// Prefix: Whistler 45081
|
|
PINT rgiobj = NULL;
|
|
HRESULT hres;
|
|
DIPROPDWORD dipdw;
|
|
VXDDATAFORMAT vdf;
|
|
#ifdef DEBUG
|
|
EnterProc(CDIDev_ParseDataFormat, (_ "pp", this, lpdf));
|
|
#else
|
|
EnterProcR(IDirectInputDevice8::SetDataFormat, (_ "pp", this, lpdf));
|
|
#endif
|
|
|
|
/*
|
|
* Caller should've nuked the old translation table.
|
|
*/
|
|
AssertF(this->pdix == 0);
|
|
AssertF(this->rgiobj == 0);
|
|
AssertF(this->cdwPOV == 0);
|
|
|
|
vdf.cbData = this->df.dwDataSize;
|
|
vdf.pDfOfs = 0;
|
|
|
|
if ( SUCCEEDED(hres = AllocCbPpv(cbCxX(this->df.dwNumObjs, DIXLAT), &pdix)) &&
|
|
SUCCEEDED(hres = AllocCbPpv(cbCdw(this->df.dwDataSize), &vdf.pDfOfs)) &&
|
|
SUCCEEDED(hres = AllocCbPpv(cbCdw(lpdf->dwDataSize), &rgiobj)) &&
|
|
SUCCEEDED(hres =
|
|
ReallocCbPpv(cbCdw(lpdf->dwNumObjs), &this->rgdwPOV)) ) {
|
|
UINT iobj;
|
|
|
|
/*
|
|
* Pre-init all the translation tags to -1,
|
|
* which means "not in use"
|
|
*/
|
|
memset(pdix, 0xFF, cbCxX(this->df.dwNumObjs, DIXLAT));
|
|
memset(vdf.pDfOfs, 0xFF, cbCdw(this->df.dwDataSize));
|
|
memset(rgiobj, 0xFF, cbCdw(lpdf->dwDataSize));
|
|
|
|
SquirtSqflPtszV(sqflDf | sqflVerbose, TEXT("Begin parse data format"));
|
|
|
|
for ( iobj = 0; iobj < lpdf->dwNumObjs; iobj++ ) {
|
|
PCODF podf = &lpdf->rgodf[iobj];
|
|
SquirtSqflPtszV(sqflDf | sqflVerbose, TEXT("Object %2d: offset %08x"),
|
|
iobj, podf->dwOfs);
|
|
|
|
/*
|
|
* Note that the podf->dwOfs < lpdf->dwDataSize test is safe
|
|
* even for DWORD objects, since we also check that both
|
|
* values are DWORD multiples.
|
|
*/
|
|
if ( ((podf->dwFlags & DIDOI_GUIDISUSAGE) ||
|
|
fLimpFF(podf->pguid,
|
|
SUCCEEDED(hres = hresFullValidGuid(podf->pguid, 1)))) &&
|
|
podf->dwOfs < lpdf->dwDataSize ) {
|
|
int iobjDev = CDIDev_FindDeviceObjectFormat(this, podf, pdix);
|
|
|
|
|
|
if ( iobjDev != -1 ) {
|
|
PCODF podfFound = &this->df.rgodf[iobjDev];
|
|
if ( podfFound->dwType & DIDFT_DWORDOBJS ) {
|
|
if ( (podf->dwOfs & 3) == 0 ) {
|
|
} else {
|
|
RPF("%s: Dword objects must be aligned", s_szProc);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
pdix[iobjDev].dwOfs = podf->dwOfs;
|
|
rgiobj[podf->dwOfs] = iobjDev;
|
|
vdf.pDfOfs[podfFound->dwOfs] = iobjDev;
|
|
|
|
if ( podfFound->dwFlags & DIDOI_POLLED ) {
|
|
this->fPolledDataFormat = TRUE;
|
|
}
|
|
|
|
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
|
|
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
|
dipdw.diph.dwObj = podfFound->dwType;
|
|
dipdw.diph.dwHow = DIPH_BYID;
|
|
dipdw.dwData = 0x1; // Enable this report ID
|
|
hres = CDIDev_RealSetProperty(this, DIPROP_ENABLEREPORTID, &dipdw.diph);
|
|
if ( hres == E_NOTIMPL )
|
|
{
|
|
hres = S_OK;
|
|
}
|
|
else if( FAILED( hres ) )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflError,
|
|
TEXT("Could not set DIPROP_ENABLEREPORTID for offset %d"),
|
|
iobj);
|
|
}
|
|
|
|
|
|
} else if ( podf->dwType & DIDFT_OPTIONAL ) {
|
|
SquirtSqflPtszV(sqflDf | sqflVerbose,
|
|
TEXT("Object %2d: Skipped (optional)"),
|
|
iobj);
|
|
/*
|
|
* We need to remember where the failed POVs live
|
|
* so we can neutralize them in GetDeviceState().
|
|
*/
|
|
if ( podf->dwType & DIDFT_POV ) {
|
|
AssertF(this->cdwPOV < lpdf->dwNumObjs);
|
|
this->rgdwPOV[this->cdwPOV++] = podf->dwOfs;
|
|
}
|
|
} else {
|
|
RPF("%s: Format not compatible with device", s_szProc);
|
|
goto fail;
|
|
}
|
|
} else {
|
|
if ( podf->dwOfs >= lpdf->dwDataSize ) {
|
|
RPF("%s: rgodf[%d].dwOfs of 0x%08x out of range in data format",
|
|
s_szProc, iobj, podf->dwOfs );
|
|
}
|
|
fail:;
|
|
hres = E_INVALIDARG;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
/*
|
|
* Double-check the lookup tables just to preserve our sanity.
|
|
*/
|
|
{
|
|
UINT dwOfs;
|
|
|
|
for ( dwOfs = 0; dwOfs < lpdf->dwDataSize; dwOfs++ ) {
|
|
if ( rgiobj[dwOfs] >= 0 ) {
|
|
AssertF(pdix[rgiobj[dwOfs]].dwOfs == dwOfs);
|
|
} else {
|
|
AssertF(rgiobj[dwOfs] == -1);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Shrink the "failed POV" array to its actual size.
|
|
* The shrink "should" always succeed. Note also that
|
|
* even if it fails, we're okay; we just waste a little
|
|
* memory.
|
|
*/
|
|
hres = ReallocCbPpv(cbCdw(this->cdwPOV), &this->rgdwPOV);
|
|
AssertF(SUCCEEDED(hres));
|
|
|
|
/*
|
|
* If we are using cooked data, then we actually hand the
|
|
* device driver a different translation table which
|
|
* combines the offset and dwDevType so data cooking can
|
|
* happen safely.
|
|
*/
|
|
|
|
vdf.pvi = this->pvi;
|
|
|
|
if ( fLimpFF(this->pvi,
|
|
SUCCEEDED(hres = Hel_SetDataFormat(&vdf))) ) {
|
|
this->pdix = pdix;
|
|
pdix = 0;
|
|
this->rgiobj = rgiobj;
|
|
rgiobj = 0;
|
|
this->dwDataSize = lpdf->dwDataSize;
|
|
hres = S_OK;
|
|
} else {
|
|
AssertF(FAILED(hres));
|
|
}
|
|
|
|
} else {
|
|
/* Out of memory */
|
|
}
|
|
|
|
done:;
|
|
FreePpv(&pdix);
|
|
FreePpv(&rgiobj);
|
|
FreePpv(&vdf.pDfOfs);
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev | OptimizeDataFormat |
|
|
*
|
|
* Study the parsed data format to determine whether we can
|
|
* used an optimized <mf CDIDev::GetDeviceState> to obtain
|
|
* the data more quickly.
|
|
*
|
|
* The data format is considered optimized if it matches the
|
|
* device data format, modulo possible shifting due to insertion
|
|
* of bonus fields at the beginning or end, and modulo missing
|
|
* fields.
|
|
*
|
|
* The data format is considered fully-optimized if it
|
|
* optimized, and no shifting is necessary, and the structure size
|
|
* is exactly the same. This means the buffer can be passed
|
|
* straight through to the driver.
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HRESULT INTERNAL
|
|
CDIDev_OptimizeDataFormat(PDD this)
|
|
{
|
|
int ib;
|
|
DWORD ibMax; /* One past highest match point */
|
|
DWORD ibMin; /* Lowest match point */
|
|
int iobj;
|
|
DWORD dwDataSize;
|
|
HRESULT hres;
|
|
EnterProc(CDIDev_OptimizeDataFormat, (_ "p", this));
|
|
|
|
ib = (int)0x8000000; /* Not yet known */
|
|
ibMin = 0xFFFFFFFF;
|
|
ibMax = 0;
|
|
|
|
for ( iobj = this->df.dwNumObjs; --iobj >= 0; ) {
|
|
DWORD ibMaxThis;
|
|
if ( this->pdix[iobj].dwOfs != 0xFFFFFFFF ) { /* Data was requested */
|
|
|
|
int ibExpected = (int)(this->pdix[iobj].dwOfs -
|
|
this->df.rgodf[iobj].dwOfs);
|
|
if ( fLimpFF(ib != (int)0x8000000, ib == ibExpected) ) {
|
|
ib = ibExpected;
|
|
} else {
|
|
SquirtSqflPtszV(sqfl | sqflMajor,
|
|
TEXT("IDirectInputDevice: Optimization level 0, translation needed") );
|
|
this->diopt = dioptNone;
|
|
this->GetState = CDIDev_GetDeviceStateSlow;
|
|
goto done;
|
|
}
|
|
if ( ibMin > this->df.rgodf[iobj].dwOfs ) {
|
|
ibMin = this->df.rgodf[iobj].dwOfs;
|
|
}
|
|
if ( this->df.rgodf[iobj].dwType & DIDFT_DWORDOBJS ) {
|
|
ibMaxThis = this->df.rgodf[iobj].dwOfs + sizeof(DWORD);
|
|
} else {
|
|
ibMaxThis = this->df.rgodf[iobj].dwOfs + sizeof(BYTE);
|
|
}
|
|
if ( ibMax < ibMaxThis ) {
|
|
ibMax = ibMaxThis;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Make sure we actually found something.
|
|
*/
|
|
if ( ib != (int)0x8000000 ) { /* Data format is matched */
|
|
AssertF(ibMin < ibMax);
|
|
AssertF(ib + (int)ibMin >= 0);
|
|
AssertF(ib + ibMax <= this->dwDataSize);
|
|
this->ibDelta = ib;
|
|
this->ibMin = ibMin;
|
|
this->cbMatch = ibMax - ibMin;
|
|
if ( ib >= 0 && ib + this->df.dwDataSize <= this->dwDataSize ) {
|
|
/* We can go direct */
|
|
if ( ib == 0 && this->dwDataSize == this->df.dwDataSize ) {
|
|
/* Data formats are equal! */
|
|
this->diopt = dioptEqual;
|
|
this->GetState = CDIDev_GetDeviceStateEqual;
|
|
SquirtSqflPtszV(sqfl | sqflMajor,
|
|
TEXT("IDirectInputDevice: Optimization level 3, full speed ahead!") );
|
|
} else {
|
|
this->diopt = dioptDirect;
|
|
this->GetState = CDIDev_GetDeviceStateDirect;
|
|
SquirtSqflPtszV(sqfl | sqflMajor,
|
|
TEXT("IDirectInputDevice: Optimization level 2, direct access") );
|
|
}
|
|
} else {
|
|
SquirtSqflPtszV(sqfl | sqflMajor,
|
|
TEXT("IDirectInputDevice: Optimization level 1, okay") );
|
|
this->diopt = dioptMatch;
|
|
this->GetState = CDIDev_GetDeviceStateMatched;
|
|
}
|
|
|
|
} else { /* No data in data format! */
|
|
RPF("IDirectInputDevice: Null data format; if that's what you want...");
|
|
this->diopt = dioptNone;
|
|
this->GetState = CDIDev_GetDeviceStateSlow;
|
|
}
|
|
|
|
done:;
|
|
if ( this->diopt >= dioptDirect ) { /* Can go direct; don't need buf */
|
|
dwDataSize = 0;
|
|
} else {
|
|
dwDataSize = this->df.dwDataSize;
|
|
}
|
|
|
|
hres = ReallocCbPpv(dwDataSize, &this->pvBuffer);
|
|
|
|
if ( SUCCEEDED(hres) ) {
|
|
AssertF(this->GetState);
|
|
} else {
|
|
FreePpv(&this->pdix);
|
|
D(this->GetState = 0);
|
|
}
|
|
|
|
return hres;
|
|
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | SetDataFormat |
|
|
*
|
|
* Set the data format for the DirectInput device.
|
|
*
|
|
* The data format must be set before the device can be
|
|
* acquired.
|
|
*
|
|
* It is necessary to set the data format only once.
|
|
*
|
|
* The data format may not be changed while the device
|
|
* is acquired.
|
|
*
|
|
* If the attempt to set the data format fails, all data
|
|
* format information is lost, and a valid data format
|
|
* must be set before the device may be acquired.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm IN LPDIDATAFORMAT | lpdf |
|
|
*
|
|
* Points to a structure that describes the format of the data
|
|
* the DirectInputDevice should return.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
|
|
* <p lpvData> parameter is not a valid pointer.
|
|
*
|
|
* <c DIERR_ACQUIRED>: Cannot change the data format while the
|
|
* device is acquired.
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HRESULT INLINE
|
|
CDIDev_SetDataFormat_IsValidDataSize(LPCDIDATAFORMAT lpdf)
|
|
{
|
|
HRESULT hres;
|
|
if ( lpdf->dwDataSize % 4 == 0 ) {
|
|
hres = S_OK;
|
|
} else {
|
|
RPF("IDirectInputDevice::SetDataFormat: "
|
|
"dwDataSize must be a multiple of 4");
|
|
hres = E_INVALIDARG;
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
HRESULT INLINE
|
|
CDIDev_SetDataFormat_IsValidObjectSize(LPCDIDATAFORMAT lpdf)
|
|
{
|
|
HRESULT hres;
|
|
if ( lpdf->dwObjSize == cbX(ODF) ) {
|
|
hres = S_OK;
|
|
} else {
|
|
RPF("IDirectInputDevice::SetDataFormat: Invalid dwObjSize");
|
|
hres = E_INVALIDARG;
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CDIDev_SetDataFormat(PV pdd, LPCDIDATAFORMAT lpdf _THAT)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice8::SetDataFormat, (_ "pp", pdd, lpdf));
|
|
|
|
if ( SUCCEEDED(hres = hresPvT(pdd)) &&
|
|
SUCCEEDED(hres = hresFullValidReadPxCb(lpdf, DIDATAFORMAT, 1)) &&
|
|
SUCCEEDED(hres = hresFullValidFl(lpdf->dwFlags, DIDF_VALID, 1)) &&
|
|
SUCCEEDED(hres = CDIDev_SetDataFormat_IsValidDataSize(lpdf)) &&
|
|
SUCCEEDED(hres = CDIDev_SetDataFormat_IsValidObjectSize(lpdf)) &&
|
|
SUCCEEDED(hres = hresFullValidReadPvCb(lpdf->rgodf,
|
|
cbCxX(lpdf->dwNumObjs, ODF), 1)) ) {
|
|
PDD this = _thisPv(pdd);
|
|
|
|
/*
|
|
* Must protect with the critical section to prevent two people
|
|
* from changing the format simultaneously, or one person from
|
|
* changing the data format while somebody else is reading data.
|
|
*/
|
|
CDIDev_EnterCrit(this);
|
|
|
|
AssertF( this->dwVersion );
|
|
|
|
if ( !this->fAcquired ) {
|
|
DIPROPDWORD dipdw;
|
|
|
|
/*
|
|
* Nuke the old data format stuff before proceeding.
|
|
*/
|
|
FreePpv(&this->pdix);
|
|
FreePpv(&this->rgiobj);
|
|
this->cdwPOV = 0;
|
|
D(this->GetState = 0);
|
|
this->fPolledDataFormat = FALSE;
|
|
|
|
/*
|
|
* Wipe out the report IDs
|
|
*/
|
|
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
|
|
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
|
dipdw.diph.dwObj = 0x0;
|
|
dipdw.diph.dwHow = DIPH_DEVICE;
|
|
dipdw.dwData = 0; // Nuke all knowledge of reportId's
|
|
hres = CDIDev_RealSetProperty(this, DIPROP_ENABLEREPORTID, &dipdw.diph);
|
|
if ( SUCCEEDED(hres) || hres == E_NOTIMPL )
|
|
{
|
|
hres = CDIDev_ParseDataFormat(this, lpdf);
|
|
if ( SUCCEEDED(hres) ) {
|
|
hres = CDIDev_OptimizeDataFormat(this);
|
|
|
|
/*
|
|
* Now set the axis mode, as a convenience.
|
|
*/
|
|
CAssertF(DIDF_VALID == (DIDF_RELAXIS | DIDF_ABSAXIS));
|
|
|
|
switch ( lpdf->dwFlags ) {
|
|
case 0:
|
|
hres = S_OK;
|
|
goto axisdone;
|
|
|
|
case DIDF_RELAXIS:
|
|
dipdw.dwData = DIPROPAXISMODE_REL;
|
|
break;
|
|
|
|
case DIDF_ABSAXIS:
|
|
dipdw.dwData = DIPROPAXISMODE_ABS;
|
|
break;
|
|
|
|
default:
|
|
RPF("%s: Cannot combine DIDF_RELAXIS with DIDF_ABSAXIS",
|
|
s_szProc);
|
|
hres = E_INVALIDARG;
|
|
goto axisdone;
|
|
|
|
}
|
|
|
|
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
|
|
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
|
dipdw.diph.dwObj = 0;
|
|
dipdw.diph.dwHow = DIPH_DEVICE;
|
|
|
|
hres = CDIDev_RealSetProperty(this, DIPROP_AXISMODE, &dipdw.diph);
|
|
|
|
if ( SUCCEEDED(hres) ) {
|
|
hres = S_OK;
|
|
}
|
|
|
|
}
|
|
} else
|
|
{
|
|
SquirtSqflPtszV(sqflDf,
|
|
TEXT("Could not set DIPROP_ENABLEREPORTID to 0x0"));
|
|
}
|
|
axisdone:;
|
|
|
|
} else { /* Already acquired */
|
|
hres = DIERR_ACQUIRED;
|
|
}
|
|
CDIDev_LeaveCrit(this);
|
|
}
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
#define BEGIN_CONST_DATA data_seg(".text", "CODE")
|
|
BYTE c_SemTypeToDFType[8] = { 0, 0,
|
|
DIDFT_GETTYPE(DIDFT_ABSAXIS), DIDFT_GETTYPE(DIDFT_RELAXIS),
|
|
DIDFT_GETTYPE(DIDFT_BUTTON), 0,
|
|
DIDFT_GETTYPE(DIDFT_POV), 0 };
|
|
#define END_CONST_DATA data_seg(".data", "DATA")
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @struct DEVICETOUSER |
|
|
*
|
|
* Structure to hold a device to user assignment.
|
|
*
|
|
* @field GUID | guidDevice |
|
|
*
|
|
* The device.
|
|
*
|
|
* @field WCHAR | wszOwner[MAX_PATH] |
|
|
*
|
|
* Name of the user who currently owns the device.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
typedef struct _DEVICETOUSER
|
|
{
|
|
GUID guidDevice;
|
|
WCHAR wszOwner[MAX_PATH];
|
|
} DEVICETOUSER, *PDEVICETOUSER;
|
|
|
|
#define DELTA_DTO_COUNT 4
|
|
|
|
|
|
|
|
// ISSUE-2001/03/29-MarcAnd CMap usage is inconsistant
|
|
// CMap code should be split out into separate source file
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | CMap_SetDeviceUserName |
|
|
*
|
|
* Set the passed device to be associated with the passed user name.
|
|
* If the device is already associated with a user or with no user
|
|
* that association is replaced with the new one. If the device is
|
|
* not yet in the array, it is added. Memory is allocated as
|
|
* necessary to increase the array size.
|
|
*
|
|
* @parm REFGUID | guidDevInst |
|
|
*
|
|
* A pointer to the device instance GUID to be added/modified.
|
|
*
|
|
* @parm LPCWSTR | wszOwner |
|
|
*
|
|
* A UNICODE user name.
|
|
*
|
|
* @returns
|
|
*
|
|
* <c S_OK> if the name was set
|
|
* <c DI_NOEFFECT> if nothing was set because nothing needed to be
|
|
* A memory allocation error if such occured.
|
|
*
|
|
*****************************************************************************/
|
|
HRESULT CMap_SetDeviceUserName
|
|
(
|
|
REFGUID guidDevInst,
|
|
LPCWSTR wszOwner
|
|
)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
PDEVICETOUSER pdto;
|
|
|
|
CAssertF( DELTA_DTO_COUNT > 1 );
|
|
|
|
AssertF( !IsEqualGUID( guidDevInst, &GUID_Null ) );
|
|
AssertF( !InCrit() );
|
|
DllEnterCrit();
|
|
|
|
/*
|
|
* Note g_pdto will be NULL until the first name is set
|
|
* however, g_cdtoMax will be zero so this loop should
|
|
* fall through to allocate more memory.
|
|
*/
|
|
for( pdto = g_pdto; pdto < &g_pdto[ g_cdtoMax ]; pdto++ )
|
|
{
|
|
if( IsEqualGUID( guidDevInst, &pdto->guidDevice ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !wszOwner )
|
|
{
|
|
if( pdto < &g_pdto[ g_cdtoMax ] )
|
|
{
|
|
ZeroMemory( &pdto->guidDevice, cbX( pdto->guidDevice ) );
|
|
g_cdto--;
|
|
AssertF( g_cdto >= 0 );
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Could not find device, since we were removing, don't worry
|
|
*/
|
|
hres = DI_NOEFFECT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( pdto >= &g_pdto[ g_cdtoMax ] )
|
|
{
|
|
/*
|
|
* Need to add a new entry
|
|
*/
|
|
if( g_cdto == g_cdtoMax )
|
|
{
|
|
/*
|
|
* If all entries are used try to get more
|
|
*/
|
|
hres = ReallocCbPpv( ( g_cdtoMax + DELTA_DTO_COUNT ) * cbX( *g_pdto ), &g_pdto );
|
|
if( FAILED( hres ) )
|
|
{
|
|
goto exit_SetDeviceUserName;
|
|
}
|
|
g_cdtoMax += DELTA_DTO_COUNT;
|
|
/*
|
|
* The first new element is sure to be free
|
|
*/
|
|
pdto = &g_pdto[ g_cdto ];
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There's an empty one in the array somewhere
|
|
*/
|
|
for( pdto = g_pdto; pdto < &g_pdto[ g_cdtoMax ]; pdto++ )
|
|
{
|
|
if( IsEqualGUID( &GUID_Null, &pdto->guidDevice ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
pdto->guidDevice = *guidDevInst;
|
|
}
|
|
g_cdto++;
|
|
|
|
AssertF( pdto < &g_pdto[ g_cdtoMax ] );
|
|
AssertF( lstrlenW( wszOwner ) < cbX( pdto->wszOwner ) );
|
|
|
|
#ifdef WINNT
|
|
lstrcpyW( pdto->wszOwner, wszOwner );
|
|
#else
|
|
memcpy( pdto->wszOwner, wszOwner, ( 1 + lstrlenW( wszOwner ) ) * cbX( *wszOwner ) );
|
|
#endif
|
|
}
|
|
|
|
exit_SetDeviceUserName:;
|
|
DllLeaveCrit();
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | CMap_IsNewDeviceUserName |
|
|
*
|
|
* Searches the array of device to user associations for the passed
|
|
* device GUID. If the device GUID is matched the name is tested
|
|
* to see if it is the same as the passed name.
|
|
*
|
|
* @parm REFGUID | guidDevInst |
|
|
*
|
|
* A pointer to the device instance GUID to be tested.
|
|
*
|
|
* @parm LPCWSTR | wszOwner |
|
|
*
|
|
* A UNICODE user name to be tested.
|
|
*
|
|
* @returns
|
|
*
|
|
* <c FALSE> if a the device was found and the user matches
|
|
* <c TRUE> if not
|
|
*
|
|
*****************************************************************************/
|
|
HRESULT CMap_IsNewDeviceUserName
|
|
(
|
|
REFGUID guidDevInst,
|
|
LPWSTR wszTest
|
|
)
|
|
{
|
|
BOOL fres = TRUE;
|
|
PDEVICETOUSER pdto;
|
|
|
|
AssertF( !IsEqualGUID( guidDevInst, &GUID_Null ) );
|
|
AssertF( wszTest != L'\0' );
|
|
AssertF( !InCrit() );
|
|
|
|
DllEnterCrit();
|
|
|
|
if( g_pdto )
|
|
{
|
|
for( pdto = g_pdto; pdto < &g_pdto[ g_cdtoMax ]; pdto++ )
|
|
{
|
|
if( IsEqualGUID( guidDevInst, &pdto->guidDevice ) )
|
|
{
|
|
#ifdef WINNT
|
|
if( !lstrcmpW( wszTest, pdto->wszOwner ) )
|
|
#else
|
|
if( ( pdto->wszOwner[0] != L'\0' )
|
|
&&( !memcmp( wszTest, pdto->wszOwner,
|
|
lstrlenW( wszTest ) * cbX( *wszTest ) ) ) )
|
|
#endif
|
|
{
|
|
fres = FALSE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
DllLeaveCrit();
|
|
|
|
return fres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | CMap_GetDeviceUserName |
|
|
*
|
|
* Searches the array of device to user associations for the passed
|
|
* device GUID. If the device GUID is matched and has an associated
|
|
* user name, that is copied into wszOwner. If the GUID is not
|
|
* matched or the match has a NULL string associated with it,
|
|
* wszOwner is set to a NULL string.
|
|
*
|
|
* @parm REFGUID | guidDevInst |
|
|
*
|
|
* A pointer to the device instance GUID to be added/modified.
|
|
*
|
|
* @parm LPCWSTR | wszOwner |
|
|
*
|
|
* A UNICODE user name.
|
|
*
|
|
* @returns
|
|
*
|
|
* <c S_OK> if a user name has been copied into wszOwner
|
|
* <c DI_NOEFFECT> if wszOwner has been set to a NULL string
|
|
*
|
|
*****************************************************************************/
|
|
HRESULT CMap_GetDeviceUserName
|
|
(
|
|
REFGUID guidDevInst,
|
|
LPWSTR wszOwner
|
|
)
|
|
{
|
|
HRESULT hres = DI_NOEFFECT;
|
|
PDEVICETOUSER pdto;
|
|
|
|
AssertF( !IsEqualGUID( guidDevInst, &GUID_Null ) );
|
|
|
|
AssertF( !InCrit() );
|
|
|
|
/*
|
|
* Assume nothing is found
|
|
*/
|
|
wszOwner[0] = L'\0';
|
|
|
|
DllEnterCrit();
|
|
|
|
if( g_pdto )
|
|
{
|
|
for( pdto = g_pdto; pdto < &g_pdto[ g_cdtoMax ]; pdto++ )
|
|
{
|
|
if( IsEqualGUID( guidDevInst, &pdto->guidDevice ) )
|
|
{
|
|
if( pdto->wszOwner[0] != L'\0' )
|
|
{
|
|
#ifdef WINNT
|
|
lstrcpyW( wszOwner, pdto->wszOwner );
|
|
#else
|
|
memcpy( wszOwner, pdto->wszOwner,
|
|
( 1 + lstrlenW( pdto->wszOwner ) ) * cbX( *wszOwner ) );
|
|
#endif
|
|
hres = S_OK;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
DllLeaveCrit();
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CMap | ValidateActionMapSemantics |
|
|
*
|
|
* Validate the semantics in an action map for overall sanity.
|
|
*
|
|
* @parm LPDIACTIONFORMATW | pActionFormat |
|
|
*
|
|
* Actions to map.
|
|
*
|
|
* @parm DWORD | dwFlags |
|
|
*
|
|
* Flags to modify the validation behavior.
|
|
* Currently these are the <c DIDBAM_*> flags as the only
|
|
* <mf IDirectInputDevice::SetActionMap> flag is the default
|
|
* <c DIDSAM_DEFAULT>.
|
|
*
|
|
* @returns
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
|
|
int __stdcall CompareActions
|
|
(
|
|
PV pv1,
|
|
PV pv2
|
|
)
|
|
{
|
|
int iRes;
|
|
LPDIACTION pAct1 = (LPDIACTION)pv1;
|
|
LPDIACTION pAct2 = (LPDIACTION)pv2;
|
|
|
|
iRes = memcmp( &pAct1->guidInstance, &pAct2->guidInstance, cbX(pAct1->guidInstance) );
|
|
|
|
if( !iRes )
|
|
{
|
|
if( pAct1->dwFlags & DIA_APPMAPPED )
|
|
{
|
|
if( pAct2->dwFlags & DIA_APPMAPPED )
|
|
{
|
|
iRes = pAct1->dwObjID - pAct2->dwObjID;
|
|
}
|
|
else
|
|
{
|
|
iRes = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( pAct2->dwFlags & DIA_APPMAPPED )
|
|
{
|
|
iRes = 1;
|
|
}
|
|
else
|
|
{
|
|
iRes = pAct1->dwSemantic - pAct2->dwSemantic;
|
|
}
|
|
}
|
|
}
|
|
|
|
return iRes;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CMap_ValidateActionMapSemantics
|
|
(
|
|
LPDIACTIONFORMATW paf,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
HRESULT hres;
|
|
|
|
LPDIACTIONW pAction;
|
|
LPDIACTIONW *pWorkSpace;
|
|
LPDIACTIONW *pCurr;
|
|
LPDIACTIONW *pLast;
|
|
|
|
EnterProcI(IDirectInputDeviceCallback::CMap::ValidateActionMapSemantics,
|
|
(_ "px", paf, dwFlags ));
|
|
|
|
|
|
/*
|
|
* Create a pointer array to the actions, sort it then look for duplicates
|
|
*/
|
|
if( SUCCEEDED( hres = AllocCbPpv( cbCxX(paf->dwNumActions,PV), &pWorkSpace) ) )
|
|
{
|
|
/*
|
|
* Fill work space from action array discarding unmappable elements
|
|
*/
|
|
pCurr = pWorkSpace;
|
|
for( pAction = paf->rgoAction; pAction < &paf->rgoAction[paf->dwNumActions]; pAction++ )
|
|
{
|
|
if( dwFlags & ( DIDBAM_INITIALIZE | DIDBAM_HWDEFAULTS ) )
|
|
{
|
|
pAction->dwFlags = 0;
|
|
pAction->guidInstance = GUID_Null;
|
|
pAction->dwHow = 0;
|
|
}
|
|
else
|
|
{
|
|
if( pAction->dwFlags & ~DIA_VALID )
|
|
{
|
|
RPF( "ERROR Invalid action: rgoAction[%d].dwFlags 0x%08x",
|
|
pAction - paf->rgoAction, pAction->dwFlags & ~DIA_VALID );
|
|
pAction->dwHow = DIAH_ERROR;
|
|
hres = DIERR_INVALIDPARAM;
|
|
goto free_and_exit;
|
|
}
|
|
|
|
if( pAction->dwFlags & DIA_APPNOMAP )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( dwFlags & DIDBAM_PRESERVE )
|
|
{
|
|
switch( pAction->dwHow )
|
|
{
|
|
case DIAH_UNMAPPED:
|
|
break;
|
|
case DIAH_USERCONFIG:
|
|
case DIAH_APPREQUESTED:
|
|
case DIAH_HWAPP:
|
|
case DIAH_HWDEFAULT:
|
|
case DIAH_OTHERAPP:
|
|
case DIAH_DEFAULT:
|
|
if( IsEqualGUID( &pAction->guidInstance, &GUID_Null ) )
|
|
{
|
|
RPF("ERROR Invalid action: rgoAction[%d].dwHow is mapped as 0x%08x but has no device",
|
|
pAction - paf->rgoAction, pAction->dwHow );
|
|
hres = DIERR_INVALIDPARAM;
|
|
goto free_and_exit;
|
|
}
|
|
break;
|
|
case DIAH_ERROR:
|
|
RPF("ERROR Invalid action: rgoAction[%d].dwHow has DIAH_ERROR",
|
|
pAction - paf->rgoAction );
|
|
hres = DIERR_INVALIDPARAM;
|
|
goto free_and_exit;
|
|
default:
|
|
if( pAction->dwHow & ~DIAH_VALID )
|
|
{
|
|
RPF("ERROR Invalid action: rgoAction[%d].dwHow has invalid flags 0x%08x",
|
|
pAction - paf->rgoAction, pAction->dwHow & ~DIAH_VALID );
|
|
}
|
|
else
|
|
{
|
|
RPF("ERROR Invalid action: rgoAction[%d].dwHow has invalid combination of map flags 0x%08x",
|
|
pAction - paf->rgoAction, pAction->dwHow & ~DIAH_VALID );
|
|
}
|
|
pAction->dwHow = DIAH_ERROR;
|
|
hres = DIERR_INVALIDPARAM;
|
|
goto free_and_exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!( pAction->dwFlags & DIA_APPMAPPED ) )
|
|
{
|
|
pAction->guidInstance = GUID_Null;
|
|
}
|
|
pAction->dwHow = 0;
|
|
}
|
|
}
|
|
|
|
if( ( pAction->dwSemantic ^ paf->dwGenre ) & DISEM_GENRE_MASK )
|
|
{
|
|
switch( DISEM_GENRE_GET( pAction->dwSemantic ) )
|
|
{
|
|
case DISEM_GENRE_GET( DIPHYSICAL_KEYBOARD ):
|
|
case DISEM_GENRE_GET( DIPHYSICAL_MOUSE ):
|
|
case DISEM_GENRE_GET( DIPHYSICAL_VOICE ):
|
|
case DISEM_GENRE_GET( DISEMGENRE_ANY ):
|
|
break;
|
|
default:
|
|
RPF("ERROR Invalid action: rgoAction[%d].dwSemantic 0x%08x for genre 0x%08x",
|
|
pAction - paf->rgoAction, pAction->dwSemantic, paf->dwGenre );
|
|
pAction->dwHow = DIAH_ERROR;
|
|
hres = DIERR_INVALIDPARAM;
|
|
goto free_and_exit;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Note, the SEM_FLAGS are not tested, this is only to save time
|
|
* as nothing depends upon their value. This could be added.
|
|
* Semantic index 0xFF used to mean ANY so don't allow it any more
|
|
*/
|
|
if(!( pAction->dwFlags & DIA_APPMAPPED )
|
|
&& ( ( pAction->dwSemantic & ~DISEM_VALID )
|
|
|| ( DISEM_INDEX_GET( pAction->dwSemantic ) == 0xFF )
|
|
||!c_SemTypeToDFType[ DISEM_TYPEANDMODE_GET( pAction->dwSemantic ) ] ) )
|
|
{
|
|
RPF("ERROR Invalid action: rgoAction[%d].dwSemantic 0x%08x is invalid",
|
|
pAction - paf->rgoAction, pAction->dwSemantic );
|
|
pAction->dwHow = DIAH_ERROR;
|
|
hres = DIERR_INVALIDPARAM;
|
|
goto free_and_exit;
|
|
}
|
|
|
|
|
|
*pCurr = pAction;
|
|
pCurr++;
|
|
}
|
|
if( pCurr == pWorkSpace )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflBenign,
|
|
TEXT("Action map contains no mappable actions") );
|
|
hres = S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
hres = S_OK;
|
|
|
|
pLast = pCurr - 1;
|
|
|
|
ptrPartialQSort( (PV)pWorkSpace, (PV)pLast, &CompareActions );
|
|
ptrInsertSort( (PV)pWorkSpace, (PV)pLast, &CompareActions );
|
|
|
|
|
|
/*
|
|
* Now we have an ordered list, see there are duplicate actions.
|
|
*/
|
|
|
|
for( pCurr = pWorkSpace + 1; pCurr <= pLast; pCurr++ )
|
|
{
|
|
if( !CompareActions( *(pCurr-1), *pCurr )
|
|
&& !( (*pCurr)->dwFlags & DIA_APPMAPPED ) )
|
|
{
|
|
RPF( "ERROR Invalid DIACTIONFORMAT: rgoAction contains duplicates" );
|
|
hres = DIERR_INVALIDPARAM;
|
|
#ifndef XDEBUG
|
|
/*
|
|
* In retail, any bad is bad. In debug report how bad.
|
|
*/
|
|
break;
|
|
#else
|
|
SquirtSqflPtszV(sqflDf | sqflError,
|
|
TEXT("Actions %d and %d are the same"),
|
|
*(pCurr-1) - paf->rgoAction, *pCurr - paf->rgoAction );
|
|
#endif
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
free_and_exit:;
|
|
FreePv( pWorkSpace );
|
|
}
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | CMap_TestSysObject |
|
|
*
|
|
* Test the passed object to see if it is a reasonable thing to exist
|
|
* on a mouse or keyboard depending on the physical genre.
|
|
*
|
|
* @parm DWORD | dwPhysicalGenre |
|
|
*
|
|
* Mouse, keyboard or voice as DIPHYSICAL_*
|
|
*
|
|
* @parm DWORD | dwObject |
|
|
*
|
|
* The object in question
|
|
*
|
|
* @returns
|
|
*
|
|
* <c S_OK> if the object could be expected on the class of device
|
|
* or <c DIERR_INVALIDPARAM> if not
|
|
*
|
|
*****************************************************************************/
|
|
HRESULT CMap_TestSysObject
|
|
(
|
|
DWORD dwPhysicalGenre,
|
|
DWORD dwObject
|
|
)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
|
|
if( dwPhysicalGenre == DIPHYSICAL_KEYBOARD )
|
|
{
|
|
/*
|
|
* Anything but a button with an 8 bit offset is invalid.
|
|
*/
|
|
if( ( dwObject & DIDFT_BUTTON )
|
|
&& ( ( DIDFT_GETINSTANCE( dwObject ) & 0xFF00 ) == 0 ) )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflBenign,
|
|
TEXT("Key 0x%02 not defined on this keyboard (id)"),
|
|
dwObject );
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflError,
|
|
TEXT("Object type 0x%08 invalid for keyboard (id)"),
|
|
dwObject );
|
|
hres = DIERR_INVALIDPARAM;
|
|
}
|
|
}
|
|
else if( dwPhysicalGenre == DIPHYSICAL_MOUSE )
|
|
{
|
|
/*
|
|
* Allow buttons 1 to 8 and axes 1 to 3
|
|
*/
|
|
if( ( dwObject & DIDFT_PSHBUTTON )
|
|
&& ( DIDFT_GETINSTANCE( dwObject ) < 8 ) )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflBenign,
|
|
TEXT("Button %d not defined on this mouse (id)"),
|
|
DIDFT_GETINSTANCE( dwObject ) );
|
|
}
|
|
else if( ( dwObject & DIDFT_AXIS )
|
|
&& ( DIDFT_GETINSTANCE( dwObject ) < 3 ) )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflBenign,
|
|
TEXT("Axis %d not defined on this mouse (id)"),
|
|
DIDFT_GETINSTANCE( dwObject ) );
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflError,
|
|
TEXT("Bad control object 0x%08x for mouse (id)"),
|
|
dwObject );
|
|
hres = DIERR_INVALIDPARAM;
|
|
}
|
|
}
|
|
else if( dwPhysicalGenre == DIPHYSICAL_VOICE )
|
|
{
|
|
if( dwObject & DIDFT_PSHBUTTON )
|
|
{
|
|
switch( DIDFT_GETINSTANCE( dwObject ) )
|
|
{
|
|
case DIVOICE_CHANNEL1:
|
|
case DIVOICE_CHANNEL2:
|
|
case DIVOICE_CHANNEL3:
|
|
case DIVOICE_CHANNEL4:
|
|
case DIVOICE_CHANNEL5:
|
|
case DIVOICE_CHANNEL6:
|
|
case DIVOICE_CHANNEL7:
|
|
case DIVOICE_CHANNEL8:
|
|
case DIVOICE_TEAM:
|
|
case DIVOICE_ALL:
|
|
case DIVOICE_RECORDMUTE:
|
|
case DIVOICE_PLAYBACKMUTE:
|
|
case DIVOICE_TRANSMIT:
|
|
case DIVOICE_VOICECOMMAND:
|
|
SquirtSqflPtszV(sqflDf | sqflBenign,
|
|
TEXT("Button %d not defined on this comms device (id)"),
|
|
DIDFT_GETINSTANCE( dwObject ) );
|
|
break;
|
|
default:
|
|
SquirtSqflPtszV(sqflDf | sqflError,
|
|
TEXT("Bad control object 0x%08x for comms device (id)"),
|
|
dwObject );
|
|
hres = DIERR_INVALIDPARAM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflError,
|
|
TEXT("Comms control object 0x%08x not a button (id)"),
|
|
dwObject );
|
|
hres = DIERR_INVALIDPARAM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AssertF( !"Physical genre not keyboard, mouse or voice (id)" );
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | CMap_TestSysOffset |
|
|
*
|
|
* Test the passed offset to see if it is a reasonable one for the
|
|
* default data format of the physical genre.
|
|
*
|
|
* @parm DWORD | dwPhysicalGenre |
|
|
*
|
|
* Mouse, keyboard or voice as DIPHYSICAL_*
|
|
*
|
|
* @parm DWORD | dwOffset |
|
|
*
|
|
* The offset in question
|
|
*
|
|
* @returns
|
|
*
|
|
* <c S_OK> if the offset could be expected on the class of device
|
|
* or <c DIERR_INVALIDPARAM> if not
|
|
*
|
|
*****************************************************************************/
|
|
HRESULT CMap_TestSysOffset
|
|
(
|
|
DWORD dwPhysicalGenre,
|
|
DWORD dwOffset
|
|
)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
|
|
if( dwPhysicalGenre == DIPHYSICAL_KEYBOARD )
|
|
{
|
|
/*
|
|
* Anything but a button with an 8 bit offset is invalid.
|
|
*/
|
|
if( dwOffset <= 0xFF )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflBenign,
|
|
TEXT("Key 0x%02 not defined on this keyboard (ofs)"),
|
|
dwOffset );
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflError,
|
|
TEXT("Key offset 0x%08 invalid for keyboard (ofs)"),
|
|
dwOffset );
|
|
hres = DIERR_INVALIDPARAM;
|
|
}
|
|
}
|
|
else if( dwPhysicalGenre == DIPHYSICAL_MOUSE )
|
|
{
|
|
CAssertF( DIMOFS_X == 0 );
|
|
|
|
if( dwOffset > DIMOFS_BUTTON7 )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflError,
|
|
TEXT("Bad control offset 0x%08x for mouse (ofs)"),
|
|
dwOffset );
|
|
hres = DIERR_INVALIDPARAM;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Allow buttons 1 to 8
|
|
*/
|
|
if( dwOffset >= DIMOFS_BUTTON0 )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflBenign,
|
|
TEXT("Button %d not defined on this mouse (ofs)"),
|
|
dwOffset - DIMOFS_BUTTON0 );
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflBenign,
|
|
TEXT("Axis %d not defined on this mouse (ofs)"),
|
|
(dwOffset - DIMOFS_X)>>2 );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AssertF( !"Physical genre not keyboard, mouse or voice" );
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CMap | DeviceValidateActionMap |
|
|
*
|
|
* Validate an action map for a device.
|
|
*
|
|
* @parm LPDIACTIONFORMATW | pActionFormat |
|
|
*
|
|
* Actions to map.
|
|
*
|
|
* @parm DWORD | dwDevGenre |
|
|
*
|
|
* Device genre to match or zero for device that are not physical
|
|
* devices.
|
|
*
|
|
* @parm REFGUID | guidInstace |
|
|
*
|
|
* Instance guid of device to match.
|
|
*
|
|
* @parm LPCDIDATAFORMAT | dfDev |
|
|
*
|
|
* Ponter to device data format.
|
|
*
|
|
* @parm DWORD | dwFlags |
|
|
*
|
|
* A valid combination of <c DVAM_*> flags to describe optional
|
|
* validation behavior.
|
|
*
|
|
* @returns
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#define DVAM_DEFAULT 0x00000000
|
|
#define DVAM_GETEXACTMAPPINGS 0x00000001
|
|
|
|
STDMETHODIMP
|
|
CMap_DeviceValidateActionMap
|
|
(
|
|
PV pdd,
|
|
LPDIACTIONFORMATW paf,
|
|
DWORD dwFlags,
|
|
PDWORD pdwOut
|
|
)
|
|
{
|
|
HRESULT hres;
|
|
LPDIDATAFORMAT dfDev;
|
|
DWORD dwPhysicalGenre;
|
|
LPDIACTIONW pAction;
|
|
PBYTE pMatch;
|
|
UINT idxObj;
|
|
BOOL bHasMap = FALSE;
|
|
BOOL bNewMap = FALSE;
|
|
DWORD dwCommsType = 0;
|
|
PDD this;
|
|
|
|
EnterProcI(IDirectInputDeviceCallback::CMap::DeviceValidateActionMap,
|
|
(_ "ppx", pdd, paf, dwFlags));
|
|
|
|
|
|
this = _thisPv(pdd);
|
|
|
|
/*
|
|
* Note, hres is tested before anything is done with dwPhysicalGenre
|
|
*/
|
|
|
|
switch( GET_DIDEVICE_TYPE(this->dc3.dwDevType) )
|
|
{
|
|
case DI8DEVTYPE_MOUSE:
|
|
dwPhysicalGenre = DIPHYSICAL_MOUSE;
|
|
break;
|
|
case DI8DEVTYPE_KEYBOARD:
|
|
dwPhysicalGenre = DIPHYSICAL_KEYBOARD;
|
|
break;
|
|
case DI8DEVTYPE_DEVICECTRL:
|
|
if( ( GET_DIDEVICE_SUBTYPE( this->dc3.dwDevType ) == DI8DEVTYPEDEVICECTRL_COMMSSELECTION_HARDWIRED )
|
|
|| ( GET_DIDEVICE_SUBTYPE( this->dc3.dwDevType ) == DI8DEVTYPEDEVICECTRL_COMMSSELECTION ) )
|
|
{
|
|
dwCommsType = GET_DIDEVICE_SUBTYPE( this->dc3.dwDevType );
|
|
dwPhysicalGenre = DIPHYSICAL_VOICE;
|
|
}
|
|
break;
|
|
default:
|
|
dwPhysicalGenre = 0;
|
|
break;
|
|
}
|
|
|
|
if( SUCCEEDED( hres = this->pdcb->lpVtbl->GetDataFormat(this->pdcb, &dfDev) )
|
|
&& SUCCEEDED( hres = AllocCbPpv( dfDev->dwNumObjs, &pMatch) ) )
|
|
{
|
|
enum eMap
|
|
{
|
|
eMapTestMatches,
|
|
eMapDeviceExact,
|
|
eMapClassExact,
|
|
eMapEnd
|
|
} iMap, ValidationLimit;
|
|
|
|
/*
|
|
* Set the limit on iterations depending on the checks required
|
|
*/
|
|
ValidationLimit = ( dwFlags & DVAM_GETEXACTMAPPINGS )
|
|
? ( dwPhysicalGenre ) ? eMapClassExact : eMapDeviceExact
|
|
: eMapTestMatches;
|
|
|
|
for( iMap = eMapTestMatches; SUCCEEDED( hres ) && ( iMap <= ValidationLimit ); iMap++ )
|
|
{
|
|
for( pAction = paf->rgoAction;
|
|
SUCCEEDED( hres ) && ( pAction < &paf->rgoAction[paf->dwNumActions] );
|
|
pAction++ )
|
|
{
|
|
DWORD dwObject;
|
|
|
|
if( pAction->dwFlags & DIA_APPNOMAP )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* If we are mapping exact matches this time, only care
|
|
* about unmapped actions with app mappings.
|
|
*/
|
|
if( ( iMap != eMapTestMatches )
|
|
&& (!( pAction->dwFlags & DIA_APPMAPPED )
|
|
|| ( pAction->dwHow & DIAH_MAPMASK ) ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
switch( iMap )
|
|
{
|
|
case eMapTestMatches:
|
|
/*
|
|
* These flags have already been validated during semantic
|
|
* validation so just assert them on the first iteration.
|
|
*/
|
|
AssertF( ( pAction->dwHow & ~DIAH_VALID ) == 0 );
|
|
AssertF( ( pAction->dwHow & DIAH_ERROR ) == 0 );
|
|
AssertF( ( pAction->dwFlags & ~DIA_VALID ) == 0 );
|
|
|
|
/*
|
|
* Only care about pre-existing matches
|
|
*/
|
|
if( !( pAction->dwHow & DIAH_MAPMASK ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Fall through for a GUID match
|
|
*/
|
|
case eMapDeviceExact:
|
|
if( !IsEqualGUID( &pAction->guidInstance, &this->guid ) )
|
|
{
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case eMapClassExact:
|
|
if( ( DISEM_GENRE_GET( pAction->dwSemantic ) != DISEM_GENRE_GET( dwPhysicalGenre ) ) )
|
|
{
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
AssertF( !"Invalid iMap" );
|
|
}
|
|
|
|
if( ( iMap != eMapTestMatches )
|
|
&& ( dwCommsType == DI8DEVTYPEDEVICECTRL_COMMSSELECTION_HARDWIRED ) )
|
|
{
|
|
if( !( pAction->dwHow & DIAH_MAPMASK ) )
|
|
{
|
|
RPF( "ERROR: rgoAction[%d] is trying to map on a hardwired device", pAction - paf->rgoAction );
|
|
hres = DIERR_INVALIDPARAM;
|
|
}
|
|
else if( pAction->dwFlags & DIA_APPMAPPED )
|
|
{
|
|
RPF( "ERROR: rgoAction[%d] is trying to app map on a hardwired device", pAction - paf->rgoAction );
|
|
hres = DIERR_INVALIDPARAM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwObject = pAction->dwObjID;
|
|
|
|
/*
|
|
* Now try to find the object.
|
|
* Note, we can't rely on the application data format goo
|
|
* because a data format has probably not been set.
|
|
*/
|
|
for( idxObj = 0; idxObj < dfDev->dwNumObjs; idxObj++ )
|
|
{
|
|
/*
|
|
* Ignore FF flags so a FF device can be matched for non-FF
|
|
*/
|
|
if( ( ( dwObject ^ dfDev->rgodf[idxObj].dwType )
|
|
&~( DIDFT_FFACTUATOR | DIDFT_FFEFFECTTRIGGER ) ) == 0 )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( idxObj < dfDev->dwNumObjs )
|
|
{
|
|
/*
|
|
* Application mapped controls don't need to have
|
|
* semantics so we cannot test them.
|
|
*/
|
|
if( pAction->dwFlags & DIA_APPMAPPED )
|
|
{
|
|
if( pMatch[idxObj] )
|
|
{
|
|
RPF( "ERROR: rgoAction[%d] maps to control 0x%08x which is already mapped",
|
|
pAction - paf->rgoAction, pAction->dwObjID );
|
|
hres = DIERR_INVALIDPARAM;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if(!( pAction->dwFlags & DIA_FORCEFEEDBACK )
|
|
|| ( dfDev->rgodf[idxObj].dwType & ( DIDFT_FFACTUATOR | DIDFT_FFEFFECTTRIGGER ) ) )
|
|
{
|
|
pMatch[idxObj] = TRUE;
|
|
|
|
SquirtSqflPtszV(sqflDf | sqflVerbose,
|
|
TEXT("rgoAction[%d] application mapped to object 0x%08x"),
|
|
pAction - paf->rgoAction, dwObject );
|
|
|
|
bHasMap = TRUE;
|
|
if( iMap != eMapTestMatches )
|
|
{
|
|
bNewMap = TRUE;
|
|
pAction->dwHow = DIAH_APPREQUESTED;
|
|
pAction->dwObjID = dwObject;
|
|
if( iMap == eMapClassExact )
|
|
{
|
|
pAction->guidInstance = this->guid;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RPF( "ERROR: rgoAction[%d] need force feedback but object 0x%08x has none",
|
|
pAction - paf->rgoAction, dwObject );
|
|
hres = DIERR_INVALIDPARAM;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AssertF( iMap == eMapTestMatches );
|
|
/*
|
|
* Check the object type matches the semantic
|
|
*/
|
|
if( ( c_SemTypeToDFType[ DISEM_TYPEANDMODE_GET( pAction->dwSemantic ) ]
|
|
& DIDFT_GETTYPE( dwObject ) ) )
|
|
{
|
|
if( pMatch[idxObj] )
|
|
{
|
|
RPF( "ERROR: rgoAction[%d] pre-mapped to control 0x%08x which is already mapped",
|
|
pAction - paf->rgoAction, pAction->dwObjID );
|
|
hres = DIERR_INVALIDPARAM;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
pMatch[idxObj] = TRUE;
|
|
bHasMap = TRUE;
|
|
|
|
SquirtSqflPtszV(sqflDf | sqflVerbose,
|
|
TEXT("rgoAction[%d] mapping verifed to object 0x%08x"),
|
|
pAction - paf->rgoAction, dwObject );
|
|
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RPF( "ERROR: rgoAction[%d] has object type 0x%08x but semantic type 0x%08x",
|
|
pAction - paf->rgoAction, DIDFT_GETTYPE( dwObject ),
|
|
c_SemTypeToDFType[ DISEM_TYPEANDMODE_GET( pAction->dwSemantic ) ] );
|
|
hres = DIERR_INVALIDPARAM;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch( iMap )
|
|
{
|
|
case eMapTestMatches:
|
|
RPF( "ERROR: rgoAction[%d] was mapped (how:0x%08x) to undefined object 0x%08x",
|
|
pAction - paf->rgoAction, pAction->dwHow, pAction->dwObjID );
|
|
hres = DIERR_INVALIDPARAM;
|
|
break;
|
|
|
|
case eMapDeviceExact:
|
|
RPF( "ERROR: rgoAction[%d] has application map to undefined object 0x%08x",
|
|
pAction - paf->rgoAction, pAction->dwObjID );
|
|
hres = DIERR_INVALIDPARAM;
|
|
break;
|
|
|
|
case eMapClassExact:
|
|
/*
|
|
* If a device class was specified and no match was
|
|
* found it is only an error if the object is
|
|
* invalid for the class.
|
|
*/
|
|
hres = CMap_TestSysObject( dwPhysicalGenre, pAction->dwObjID );
|
|
if( FAILED( hres ) )
|
|
{
|
|
RPF( "ERROR: rgoAction[%d] was mapped to object 0x%08x, not valid for device",
|
|
pAction - paf->rgoAction, pAction->dwObjID );
|
|
}
|
|
else
|
|
{
|
|
continue; /* Don't break the loop */
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( FAILED( hres ) )
|
|
{
|
|
pAction->dwHow = DIAH_ERROR;
|
|
AssertF( hres == DIERR_INVALIDPARAM );
|
|
break;
|
|
}
|
|
}
|
|
|
|
FreePv( pMatch );
|
|
}
|
|
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
AssertF( hres == S_OK );
|
|
if( dwCommsType == DI8DEVTYPEDEVICECTRL_COMMSSELECTION_HARDWIRED )
|
|
{
|
|
hres = DI_WRITEPROTECT;
|
|
}
|
|
else if(dwFlags == DVAM_DEFAULT)
|
|
{
|
|
//in default case we want to know if there are ANY mappings
|
|
if (!bHasMap)
|
|
hres = DI_NOEFFECT;
|
|
}
|
|
else if(!bNewMap)
|
|
{
|
|
//in non-default case we are interested in ANY NEW mappings
|
|
hres = DI_NOEFFECT;
|
|
}
|
|
}
|
|
|
|
*pdwOut = dwCommsType;
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CMap | BuildDefaultDevActionMap |
|
|
*
|
|
* Get default action map from the action format non system devices.
|
|
*
|
|
* @parm LPDIACTIONFORMATW | pActionFormat |
|
|
*
|
|
* Actions to map.
|
|
*
|
|
* @parm DWORD | dwFlags |
|
|
*
|
|
* Flags used to indicate mapping preferences.
|
|
*
|
|
* @parm REFGUID | guidInstace |
|
|
*
|
|
* Instance guid of device to match.
|
|
*
|
|
* @parm PDIDOBJDEFSEM | rgObjSem |
|
|
*
|
|
* Array of default device object to semantic mappings.
|
|
*
|
|
* @parm DWORD | dwNumAxes |
|
|
*
|
|
* Number of axes in the rgObjSem.
|
|
*
|
|
* @parm DWORD | dwNumPOVs |
|
|
*
|
|
* Number of POVs in the rgObjSem.
|
|
*
|
|
* @parm DWORD | dwNumAxes |
|
|
*
|
|
* Number of buttons in the rgObjSem.
|
|
*
|
|
* @returns
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
STDMETHODIMP CMap_BuildDefaultDevActionMap
|
|
(
|
|
LPDIACTIONFORMATW paf,
|
|
DWORD dwFlags,
|
|
REFGUID guidDevInst,
|
|
PDIDOBJDEFSEM rgObjSem,
|
|
DWORD dwNumAxes,
|
|
DWORD dwNumPOVs,
|
|
DWORD dwNumButtons
|
|
)
|
|
{
|
|
#define IS_OBJECT_USED( dwSem ) ( DISEM_RES_GET( dwSem ) )
|
|
#define MARK_OBJECT_AS_USED( dwSem ) ( dwSem |= DISEM_RES_SET( 1 ) )
|
|
|
|
HRESULT hres = S_OK;
|
|
|
|
BOOL fSomethingMapped = FALSE;
|
|
|
|
PDIDOBJDEFSEM pSemNextPOV;
|
|
PDIDOBJDEFSEM pSemButtons;
|
|
LPDIACTIONW pAction;
|
|
|
|
enum eMap
|
|
{
|
|
eMapDeviceSemantic,
|
|
eMapDeviceCompat,
|
|
eMapGenericSemantic,
|
|
eMapGenericCompat,
|
|
eMapEnd
|
|
} iMap;
|
|
|
|
EnterProcI(IDirectInputDeviceCallback::CMap::BuildDefaultDevActionMap,
|
|
(_ "pxGpxxx", paf, dwFlags, guidDevInst,
|
|
rgObjSem, dwNumAxes, dwNumPOVs, dwNumButtons ));
|
|
|
|
pSemNextPOV = &rgObjSem[dwNumAxes];
|
|
pSemButtons = &rgObjSem[dwNumAxes+dwNumPOVs];
|
|
|
|
/*
|
|
* Make an initial pass through to mark already mapped actions
|
|
*/
|
|
for( pAction = paf->rgoAction; pAction < &paf->rgoAction[paf->dwNumActions]; pAction++ )
|
|
{
|
|
PDIDOBJDEFSEM pSemExact;
|
|
|
|
/*
|
|
* These flags have already been validated during semantic validation,
|
|
*/
|
|
AssertF( ( pAction->dwHow & ~DIAH_VALID ) == 0 );
|
|
AssertF( ( pAction->dwHow & DIAH_ERROR ) == 0 );
|
|
AssertF( ( pAction->dwFlags & ~DIA_VALID ) == 0 );
|
|
|
|
if( ( pAction->dwFlags & DIA_APPNOMAP )
|
|
||!( pAction->dwHow & DIAH_MAPMASK )
|
|
||!IsEqualGUID( &pAction->guidInstance, guidDevInst ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* This object has already been validated so mark it as
|
|
* in use so we don't try to reuse it.
|
|
* No errors should be detected here so use asserts not retail tests
|
|
*/
|
|
AssertF( pAction->dwObjID != 0xFFFFFFFF );
|
|
|
|
/*
|
|
* Find the object
|
|
*/
|
|
for( pSemExact = rgObjSem;
|
|
pSemExact < &rgObjSem[dwNumAxes+dwNumPOVs+dwNumButtons];
|
|
pSemExact++ )
|
|
{
|
|
if( ( ( pSemExact->dwID ^ pAction->dwObjID )
|
|
&~( DIDFT_FFACTUATOR | DIDFT_FFEFFECTTRIGGER ) ) == 0 )
|
|
{
|
|
AssertF( !IS_OBJECT_USED( pSemExact->dwSemantic ) );
|
|
AssertF( DISEM_TYPE_GET( pAction->dwSemantic )
|
|
== DISEM_TYPE_GET( pSemExact->dwSemantic ) );
|
|
MARK_OBJECT_AS_USED( pSemExact->dwSemantic );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ISSUE-2001/03/29-timgill There should always be an exact action match
|
|
* May need to use tests for duplicates and unmatched controls
|
|
*/
|
|
AssertF( pSemExact < &rgObjSem[dwNumAxes+dwNumPOVs+dwNumButtons] );
|
|
}
|
|
|
|
|
|
for( iMap=0; iMap<eMapEnd; iMap++ )
|
|
{
|
|
for( pAction = paf->rgoAction; pAction < &paf->rgoAction[paf->dwNumActions]; pAction++ )
|
|
{
|
|
/*
|
|
* Do trivial tests first
|
|
*/
|
|
if( pAction->dwHow & DIAH_MAPMASK )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( pAction->dwFlags & DIA_APPNOMAP )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
switch( iMap )
|
|
{
|
|
case eMapDeviceSemantic:
|
|
case eMapGenericSemantic:
|
|
if( DISEM_GENRE_GET( pAction->dwSemantic ) == DISEM_GENRE_GET( DISEMGENRE_ANY ) )
|
|
{
|
|
continue; /* No semantic mapping requested */
|
|
}
|
|
if( ( DISEM_TYPE_GET( pAction->dwSemantic ) == DISEM_TYPE_GET( DISEM_TYPE_BUTTON ) )
|
|
&& ( DISEM_FLAGS_GET( pAction->dwSemantic ) != 0 ) )
|
|
{
|
|
continue; /* Don't touch link buttons now */
|
|
}
|
|
break;
|
|
|
|
case eMapDeviceCompat:
|
|
case eMapGenericCompat:
|
|
if( ( DISEM_GENRE_GET( pAction->dwSemantic ) != DISEM_GENRE_GET( DISEMGENRE_ANY ) )
|
|
&& ( DISEM_TYPE_GET( pAction->dwSemantic ) == DISEM_TYPE_GET( DISEM_TYPE_AXIS ) ) )
|
|
{
|
|
continue; /* No generic mapping requested or taken by default */
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
/*
|
|
* Next test that this device suits this action (for this pass)
|
|
*/
|
|
if( iMap <= eMapDeviceCompat )
|
|
{
|
|
if( !IsEqualGUID( &pAction->guidInstance, guidDevInst ) )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else if( !IsEqualGUID( &pAction->guidInstance, &GUID_Null) )
|
|
{
|
|
continue;
|
|
}
|
|
else if( ( DISEM_GENRE_GET( pAction->dwSemantic ) == DISEM_GENRE_GET( DIPHYSICAL_MOUSE ) )
|
|
|| ( DISEM_GENRE_GET( pAction->dwSemantic ) == DISEM_GENRE_GET( DIPHYSICAL_KEYBOARD ) )
|
|
|| ( DISEM_GENRE_GET( pAction->dwSemantic ) == DISEM_GENRE_GET( DIPHYSICAL_VOICE ) ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
|
|
/*
|
|
* Only actions which may be matched on this device get this far.
|
|
*/
|
|
switch( iMap )
|
|
{
|
|
case eMapDeviceSemantic:
|
|
case eMapGenericSemantic:
|
|
/*
|
|
* See if a matching control is available.
|
|
*/
|
|
switch( DISEM_TYPE_GET( pAction->dwSemantic ) )
|
|
{
|
|
case DISEM_TYPE_GET( DISEM_TYPE_AXIS ):
|
|
{
|
|
DWORD dwAxisType = DISEM_FLAGS_GET( pAction->dwSemantic );
|
|
DWORD dwSemObjType;
|
|
UINT uAxisIdx;
|
|
|
|
/*
|
|
* Set up a mask of the type of object we need to find
|
|
*/
|
|
dwSemObjType = c_SemTypeToDFType[ DISEM_TYPEANDMODE_GET( pAction->dwSemantic ) ];
|
|
if( pAction->dwFlags & DIA_FORCEFEEDBACK )
|
|
{
|
|
dwSemObjType |= DIDFT_FFACTUATOR;
|
|
}
|
|
|
|
for( uAxisIdx = 0; uAxisIdx < dwNumAxes; uAxisIdx++ )
|
|
{
|
|
if( ( dwAxisType == DISEM_FLAGS_GET( rgObjSem[uAxisIdx].dwSemantic ) )
|
|
&& ( ( dwSemObjType & rgObjSem[uAxisIdx].dwID ) == dwSemObjType )
|
|
&& ( !IS_OBJECT_USED( rgObjSem[uAxisIdx].dwSemantic ) ) )
|
|
{
|
|
pAction->dwObjID = rgObjSem[uAxisIdx].dwID;
|
|
MARK_OBJECT_AS_USED( rgObjSem[uAxisIdx].dwSemantic );
|
|
break;
|
|
}
|
|
}
|
|
if( uAxisIdx >= dwNumAxes )
|
|
{
|
|
continue; /* No matching left */
|
|
}
|
|
break;
|
|
}
|
|
|
|
case DISEM_TYPE_GET( DISEM_TYPE_POV ):
|
|
/*
|
|
* Note, no control of POV ordering
|
|
*/
|
|
if( ( pSemNextPOV < pSemButtons )
|
|
&&!( pAction->dwFlags & DIA_FORCEFEEDBACK ) )
|
|
{
|
|
pAction->dwObjID = pSemNextPOV->dwID;
|
|
MARK_OBJECT_AS_USED( pSemNextPOV->dwSemantic );
|
|
pSemNextPOV++;
|
|
}
|
|
else
|
|
{
|
|
if( pAction->dwFlags & DIA_FORCEFEEDBACK )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflBenign,
|
|
TEXT( "Not mapping force feedback semantic hat switch (huh?), rgoAction[%d]"),
|
|
pAction - paf->rgoAction );
|
|
}
|
|
continue; /* None left */
|
|
}
|
|
break;
|
|
|
|
case DISEM_TYPE_GET( DISEM_TYPE_BUTTON ):
|
|
{
|
|
DWORD dwButtonIdx;
|
|
dwButtonIdx = DISEM_INDEX_GET( pAction->dwSemantic );
|
|
if( dwButtonIdx >= DIAS_INDEX_SPECIAL )
|
|
{
|
|
/*
|
|
* 0xFF used to mean DIBUTTON_ANY.
|
|
* To avoid changing other special indices, still
|
|
* use 0xFF as the base number.
|
|
*/
|
|
dwButtonIdx = dwNumButtons - ( 0xFF - dwButtonIdx );
|
|
}
|
|
else
|
|
{
|
|
dwButtonIdx--;
|
|
}
|
|
if( ( dwButtonIdx >= dwNumButtons )
|
|
|| ( IS_OBJECT_USED( pSemButtons[dwButtonIdx].dwSemantic ) )
|
|
|| ( ( pAction->dwFlags & DIA_FORCEFEEDBACK )
|
|
&&!( pSemButtons[dwButtonIdx].dwID & DIDFT_FFEFFECTTRIGGER ) ) )
|
|
{
|
|
continue; /* No match, no harm */
|
|
}
|
|
else
|
|
{
|
|
pAction->dwObjID = pSemButtons[dwButtonIdx].dwID;
|
|
MARK_OBJECT_AS_USED( pSemButtons[dwButtonIdx].dwSemantic );
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
RPF( "ERROR Invalid action: rgoAction[%d].dwSemantic 0x%08x",
|
|
pAction - paf->rgoAction, pAction->dwSemantic );
|
|
pAction->dwHow = DIAH_ERROR;
|
|
hres = DIERR_INVALIDPARAM;
|
|
goto error_exit;
|
|
}
|
|
pAction->dwHow = DIAH_DEFAULT;
|
|
break;
|
|
|
|
case eMapDeviceCompat:
|
|
case eMapGenericCompat:
|
|
|
|
if( ( DISEM_TYPE_GET( pAction->dwSemantic ) == DISEM_TYPE_GET( DISEM_TYPE_BUTTON ) )
|
|
&& ( DISEM_FLAGS_GET( pAction->dwSemantic ) != 0 ) )
|
|
{
|
|
LPDIACTIONW pActionTest;
|
|
PDIDOBJDEFSEM pSemTest;
|
|
DWORD dwSemMask;
|
|
|
|
/*
|
|
* See if the axis or POV has been mapped
|
|
* Note, there may be no linked object
|
|
*/
|
|
if( ( pAction->dwSemantic & DISEM_FLAGS_MASK ) == DISEM_FLAGS_P )
|
|
{
|
|
dwSemMask = DISEM_GROUP_MASK;
|
|
}
|
|
else
|
|
{
|
|
dwSemMask = ( DISEM_FLAGS_MASK | DISEM_GROUP_MASK );
|
|
}
|
|
|
|
for( pActionTest = paf->rgoAction; pActionTest < &paf->rgoAction[paf->dwNumActions]; pActionTest++ )
|
|
{
|
|
/*
|
|
* Find the axis or POV for which this button is a fallback
|
|
* Ignore buttons as they are just other fallbacks for the same action
|
|
* Don't do fallbacks for ANY* axes or POVs.
|
|
*/
|
|
if( ( DISEM_TYPE_GET( pActionTest->dwSemantic ) != DISEM_TYPE_GET( DISEM_TYPE_BUTTON ) )
|
|
&& ( DISEM_GENRE_GET( pAction->dwSemantic ) != DISEM_GENRE_GET( DISEMGENRE_ANY ) )
|
|
&& ( ( ( pActionTest->dwSemantic ^ pAction->dwSemantic ) & dwSemMask ) == 0 ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( ( pActionTest < &paf->rgoAction[paf->dwNumActions] )
|
|
&& ( pActionTest->dwHow & DIAH_MAPMASK ) )
|
|
{
|
|
continue; /* Don't need a fallback */
|
|
}
|
|
|
|
/*
|
|
* Find a button
|
|
*/
|
|
for( pSemTest = pSemButtons; pSemTest < &pSemButtons[dwNumButtons]; pSemTest++ )
|
|
{
|
|
if( !IS_OBJECT_USED( pSemTest->dwSemantic ) )
|
|
{
|
|
if( ( pAction->dwFlags & DIA_FORCEFEEDBACK )
|
|
&&!( pSemTest->dwID & DIDFT_FFEFFECTTRIGGER ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
pAction->dwObjID = pSemTest->dwID;
|
|
MARK_OBJECT_AS_USED( pSemTest->dwSemantic );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( pSemTest == &pSemButtons[dwNumButtons] )
|
|
{
|
|
continue; /* None left */
|
|
}
|
|
|
|
pAction->dwHow = DIAH_DEFAULT;
|
|
}
|
|
else
|
|
{
|
|
PDIDOBJDEFSEM pSemTest;
|
|
PDIDOBJDEFSEM pSemBound;
|
|
int iDirection = 1;
|
|
DWORD dwSemObjType;
|
|
|
|
/*
|
|
* Set up a mask of the type of object we need to find to
|
|
* filter out axes of the wrong mode and non-FF if needed
|
|
*/
|
|
dwSemObjType = c_SemTypeToDFType[ DISEM_TYPEANDMODE_GET( pAction->dwSemantic ) ];
|
|
if( pAction->dwFlags & DIA_FORCEFEEDBACK )
|
|
{
|
|
dwSemObjType |= DIDFT_FFACTUATOR | DIDFT_FFEFFECTTRIGGER;
|
|
}
|
|
|
|
/*
|
|
* Search the available controls for a match
|
|
*/
|
|
switch( DISEM_TYPE_GET( pAction->dwSemantic ) )
|
|
{
|
|
case DISEM_TYPE_GET( DISEM_TYPE_AXIS ):
|
|
pSemTest = rgObjSem;
|
|
pSemBound = &rgObjSem[dwNumAxes];
|
|
/*
|
|
* Filter out axes of the wrong mode and FF caps
|
|
*/
|
|
dwSemObjType &= DIDFT_FFACTUATOR | DIDFT_AXIS;
|
|
break;
|
|
|
|
case DISEM_TYPE_GET( DISEM_TYPE_POV ):
|
|
if( pAction->dwFlags & DIA_FORCEFEEDBACK )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflBenign,
|
|
TEXT( "Not mapping force feedback compatible hat switch (huh?), rgoAction[%d]"),
|
|
pAction - paf->rgoAction );
|
|
continue;
|
|
}
|
|
pSemTest = pSemNextPOV;
|
|
pSemBound = pSemButtons;
|
|
/*
|
|
* Don't filter POVs any more
|
|
*/
|
|
dwSemObjType = 0;
|
|
break;
|
|
|
|
case DISEM_TYPE_GET( DISEM_TYPE_BUTTON ):
|
|
/*
|
|
* Note a DIBUTTON_ANY with instance DIAS_INDEX_SPECIAL
|
|
* or greater can be mapped from the end.
|
|
*/
|
|
if( DISEM_INDEX_GET( pAction->dwSemantic ) >= DIAS_INDEX_SPECIAL )
|
|
{
|
|
/*
|
|
* For buttons selected from the end, find the last available
|
|
*/
|
|
iDirection = -1;
|
|
pSemTest = &rgObjSem[dwNumAxes + dwNumPOVs + dwNumButtons - 1];
|
|
pSemBound = pSemButtons - 1;
|
|
}
|
|
else
|
|
{
|
|
pSemTest = pSemButtons;
|
|
pSemBound = &rgObjSem[dwNumAxes + dwNumPOVs + dwNumButtons];
|
|
}
|
|
/*
|
|
* Filter triggers but just in case, do not distinguish
|
|
* between types of buttons (toggle/push).
|
|
*/
|
|
dwSemObjType &= DIDFT_FFEFFECTTRIGGER;
|
|
break;
|
|
}
|
|
|
|
while( pSemTest != pSemBound )
|
|
{
|
|
if( !IS_OBJECT_USED( pSemTest->dwSemantic )
|
|
&& ( ( dwSemObjType & pSemTest->dwID ) == dwSemObjType )
|
|
&& ( ( DISEM_FLAGS_GET( pAction->dwSemantic ) == 0 )
|
|
||( DISEM_FLAGS_GET( pAction->dwSemantic ) == DISEM_FLAGS_GET( pSemTest->dwSemantic ) ) ) )
|
|
{
|
|
pAction->dwObjID = pSemTest->dwID;
|
|
MARK_OBJECT_AS_USED( pSemTest->dwSemantic );
|
|
break;
|
|
}
|
|
pSemTest += iDirection;
|
|
}
|
|
|
|
if( pSemTest == pSemBound )
|
|
{
|
|
continue; /* None left */
|
|
}
|
|
pAction->dwHow = DIAH_DEFAULT;
|
|
}
|
|
break;
|
|
#ifdef XDEBUG
|
|
default:
|
|
AssertF(0);
|
|
#endif
|
|
}
|
|
|
|
SquirtSqflPtszV(sqflDf | sqflVerbose,
|
|
TEXT( "Match for action %d is object 0x%08x and how 0x%08x"),
|
|
pAction - paf->rgoAction, pAction->dwObjID, pAction->dwHow );
|
|
|
|
/*
|
|
* If we get this far, we have a match.
|
|
* The control object id and dwHow field should have been set
|
|
*/
|
|
AssertF( ( pAction->dwHow == DIAH_DEFAULT ) || ( pAction->dwHow == DIAH_APPREQUESTED ) );
|
|
|
|
pAction->guidInstance = *guidDevInst;
|
|
|
|
SquirtSqflPtszV(sqflDf | sqflVerbose,
|
|
TEXT("Action %d mapped to object id 0x%08x"),
|
|
pAction - paf->rgoAction, pAction->dwObjID );
|
|
|
|
fSomethingMapped = TRUE;
|
|
|
|
} /* Action loop */
|
|
} /* Match loop */
|
|
|
|
/*
|
|
* The result should always be S_OK
|
|
*/
|
|
AssertF( hres == S_OK );
|
|
|
|
/*
|
|
* If nothing was mapped, let the caller know
|
|
*/
|
|
if( !fSomethingMapped )
|
|
{
|
|
hres = DI_NOEFFECT;
|
|
}
|
|
|
|
error_exit:;
|
|
ExitOleProc();
|
|
return hres;
|
|
|
|
#undef IS_OBJECT_USED
|
|
#undef MARK_OBJECT_AS_USED
|
|
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CMap | BuildDefaultSysActionMap |
|
|
*
|
|
* Build default action map from the action format for mouse or
|
|
* keyboard devices.
|
|
*
|
|
* @parm LPDIACTIONFORMATW | paf |
|
|
*
|
|
* Actions to map.
|
|
*
|
|
* @parm DWORD | dwFlags |
|
|
*
|
|
* Flags used to indicate mapping preferences.
|
|
*
|
|
* @parm DWORD | dwPhysicalGenre |
|
|
*
|
|
* Device genre to match.
|
|
*
|
|
* @parm REFGUID | guidDevInst |
|
|
*
|
|
* Instance guid of device to match.
|
|
*
|
|
* @parm LPDIDATAFORMAT | dfDev |
|
|
*
|
|
* Internal data format of device.
|
|
*
|
|
* @parm DWORD | dwButtonZeroInst |
|
|
*
|
|
* For mice only, the instance number of the first button.
|
|
*
|
|
* @returns
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CMap_BuildDefaultSysActionMap
|
|
(
|
|
LPDIACTIONFORMATW paf,
|
|
DWORD dwFlags,
|
|
DWORD dwPhysicalGenre,
|
|
REFGUID guidDevInst,
|
|
LPDIDATAFORMAT dfDev,
|
|
DWORD dwButtonZeroInst
|
|
)
|
|
{
|
|
HRESULT hres;
|
|
|
|
PBYTE pMatch;
|
|
UINT idxAxis;
|
|
UINT idxButton;
|
|
|
|
EnterProcI(IDirectInputDeviceCallback::CMap::BuildDefaultSysActionMap,
|
|
(_ "pxxGpu", paf, dwFlags, dwPhysicalGenre, guidDevInst, dfDev, dwButtonZeroInst ));
|
|
|
|
idxButton = 0;
|
|
if( dwPhysicalGenre == DIPHYSICAL_KEYBOARD )
|
|
{
|
|
idxAxis = dfDev->dwNumObjs;
|
|
}
|
|
else
|
|
{
|
|
idxAxis = 0;
|
|
AssertF( dwPhysicalGenre == DIPHYSICAL_MOUSE );
|
|
}
|
|
|
|
if( SUCCEEDED( hres = AllocCbPpv( dfDev->dwNumObjs, &pMatch) ) )
|
|
{
|
|
BOOL fSomethingMapped = FALSE;
|
|
|
|
enum eMap
|
|
{
|
|
eMapPrevious,
|
|
eMapDeviceSemantic,
|
|
eMapClassSemantic,
|
|
eMapEnd
|
|
} iMap;
|
|
|
|
for( iMap=0; iMap<eMapEnd; iMap++ )
|
|
{
|
|
LPDIACTIONW pAction;
|
|
|
|
for( pAction = paf->rgoAction; pAction < &paf->rgoAction[paf->dwNumActions]; pAction++ )
|
|
{
|
|
DWORD dwObject;
|
|
UINT idxObj;
|
|
|
|
dwObject = (DWORD)-1;
|
|
|
|
/*
|
|
* These flags have already been validated during semantic validation,
|
|
*/
|
|
AssertF( ( pAction->dwHow & ~DIAH_VALID ) == 0 );
|
|
AssertF( ( pAction->dwHow & DIAH_ERROR ) == 0 );
|
|
AssertF( ( pAction->dwFlags & ~DIA_VALID ) == 0 );
|
|
|
|
if( pAction->dwFlags & DIA_APPNOMAP )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( iMap == eMapPrevious )
|
|
{
|
|
if( !( pAction->dwHow & DIAH_MAPMASK ) )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( pAction->dwHow & DIAH_MAPMASK )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if( iMap < eMapClassSemantic )
|
|
{
|
|
if( !IsEqualGUID( &pAction->guidInstance, guidDevInst ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
|
|
/*
|
|
* Check that the physical genre is compatible with this device
|
|
*/
|
|
if( DISEM_GENRE_GET( pAction->dwSemantic ) != DISEM_GENRE_GET( dwPhysicalGenre ) )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflError,
|
|
TEXT("Device specified for action does not match physical genre"));
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
if( !IsEqualGUID( &pAction->guidInstance, &GUID_Null)
|
|
|| ( DISEM_GENRE_GET( pAction->dwSemantic ) != DISEM_GENRE_GET( dwPhysicalGenre ) ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( iMap == eMapPrevious )
|
|
{
|
|
/*
|
|
* This match has already been validated
|
|
*/
|
|
SquirtSqflPtszV(sqflDf | sqflVerbose,
|
|
TEXT("Action %d already mapped by 0x%08x to object 0x%08x"),
|
|
pAction - paf->rgoAction, pAction->dwHow, pAction->dwObjID );
|
|
AssertF( pAction->dwObjID != 0xFFFFFFFF );
|
|
|
|
/*
|
|
* Find the object index
|
|
*/
|
|
for( idxObj = 0; idxObj < dfDev->dwNumObjs; idxObj++ )
|
|
{
|
|
if( ( dfDev->rgodf[idxObj].dwType & DIDFT_FINDMASK ) == ( pAction->dwObjID & DIDFT_FINDMASK ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if( idxObj < dfDev->dwNumObjs )
|
|
{
|
|
/*
|
|
* Validation should have caught duplicates
|
|
*/
|
|
AssertF( !pMatch[idxObj] );
|
|
pMatch[idxObj] = TRUE;
|
|
/*
|
|
* Nothing else to do since we're just counting previous matches
|
|
*/
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflError,
|
|
TEXT("Action %d previously mapped by 0x%08x to unknown object 0x%08x"),
|
|
pAction - paf->rgoAction, pAction->dwHow, pAction->dwObjID );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DWORD dwSemObjType = c_SemTypeToDFType[ DISEM_TYPEANDMODE_GET( pAction->dwSemantic ) ];
|
|
|
|
AssertF( ( iMap == eMapDeviceSemantic ) || ( iMap == eMapClassSemantic ) );
|
|
|
|
/*
|
|
* System devices have index of the semantic = object default offset
|
|
* use that to find the object index
|
|
*/
|
|
|
|
for( idxObj = 0; idxObj < dfDev->dwNumObjs; idxObj++ )
|
|
{
|
|
/*
|
|
* Test that this is an appropriate input type.
|
|
*/
|
|
if(!( dfDev->rgodf[idxObj].dwType & dwSemObjType )
|
|
|| ( dfDev->rgodf[idxObj].dwType & DIDFT_NODATA ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* All keyboards currently use the same (default)
|
|
* data format so the index can be used directly
|
|
* to match the semantic.
|
|
*/
|
|
if( dwPhysicalGenre == DIPHYSICAL_KEYBOARD )
|
|
{
|
|
if( dfDev->rgodf[idxObj].dwOfs != DISEM_INDEX_GET( pAction->dwSemantic ) )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Mice are more awkward as HID mice data
|
|
* formats depend on the device so use the
|
|
* dwType instead.
|
|
*/
|
|
if( dwSemObjType & DIDFT_BUTTON )
|
|
{
|
|
/*
|
|
* A matching button is offset by the
|
|
* caller supplied button zero instance as
|
|
* the default button zero is instance 3.
|
|
*/
|
|
if( DIDFT_GETINSTANCE( dfDev->rgodf[idxObj].dwType ) - (BYTE)dwButtonZeroInst
|
|
!= DISEM_INDEX_GET( pAction->dwSemantic ) - DIMOFS_BUTTON0 )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* All mice have axis instances: x=0, y=1, z=2
|
|
*/
|
|
AssertF( dwSemObjType & DIDFT_AXIS );
|
|
if( ( DIDFT_GETINSTANCE( dfDev->rgodf[idxObj].dwType ) << 2 )
|
|
!= DISEM_INDEX_GET( pAction->dwSemantic ) )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* A semantic match has been found
|
|
*/
|
|
if( pMatch[idxObj] )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflError,
|
|
TEXT("Action %d maps to already mapped object 0x%08x"),
|
|
pAction - paf->rgoAction, pAction->dwObjID );
|
|
}
|
|
else
|
|
{
|
|
if(!( pAction->dwFlags & DIA_FORCEFEEDBACK )
|
|
|| ( dfDev->rgodf[idxObj].dwType & ( DIDFT_FFACTUATOR | DIDFT_FFEFFECTTRIGGER ) ) )
|
|
{
|
|
/*
|
|
* If the game needs FF, only map FF objects
|
|
*/
|
|
dwObject = dfDev->rgodf[idxObj].dwType;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if( dwObject == (DWORD)-1 )
|
|
{
|
|
if( ( iMap == eMapClassSemantic )
|
|
&& SUCCEEDED( CMap_TestSysOffset( dwPhysicalGenre,
|
|
DISEM_INDEX_GET( pAction->dwSemantic ) ) ) )
|
|
{
|
|
/*
|
|
* Don't worry that this device is less capable than some
|
|
*/
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we get this far, we either have a possible match or the
|
|
* action is invalid. Since we could still find errors, look
|
|
* at matches first.
|
|
*/
|
|
if( dwObject != -1 )
|
|
{
|
|
if( idxObj < dfDev->dwNumObjs )
|
|
{
|
|
if( iMap == eMapPrevious )
|
|
{
|
|
/*
|
|
* Validation should have caught duplicates
|
|
*/
|
|
AssertF( !pMatch[idxObj] );
|
|
pMatch[idxObj] = TRUE;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if( pMatch[idxObj] )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflError,
|
|
TEXT("Object specified more than once on device"));
|
|
dwObject = (DWORD)-1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hres = CMap_TestSysObject( dwPhysicalGenre, dwObject );
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
/*
|
|
* Not an unreasonable request so just carry on
|
|
*/
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
dwObject = (DWORD)-1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We have either a valid object or an error
|
|
*/
|
|
if( dwObject != (DWORD)-1 )
|
|
{
|
|
pAction->dwHow = DIAH_DEFAULT;
|
|
pAction->dwObjID = dwObject;
|
|
pAction->guidInstance = *guidDevInst;
|
|
|
|
SquirtSqflPtszV(sqflDf | sqflVerbose,
|
|
TEXT("Action %d mapped to object index 0x%08x type 0x%08x"),
|
|
pAction - paf->rgoAction, idxObj, dwObject );
|
|
|
|
pMatch[idxObj] = TRUE;
|
|
fSomethingMapped = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Mark this action as invalid and quit.
|
|
*/
|
|
pAction->dwHow = DIAH_ERROR;
|
|
RPF("ERROR BuildActionMap: arg %d: rgoAction[%d] invalid", 1, pAction - paf->rgoAction );
|
|
RPF( "Semantic 0x%08x", pAction->dwSemantic );
|
|
hres = DIERR_INVALIDPARAM;
|
|
goto free_and_exit;
|
|
}
|
|
} /* Action loop */
|
|
} /* Match loop */
|
|
|
|
/*
|
|
* The result should always be a successful memory allocation (S_OK)
|
|
*/
|
|
AssertF( hres == S_OK );
|
|
|
|
/*
|
|
* If nothing was mapped, let the caller know
|
|
*/
|
|
if( !fSomethingMapped )
|
|
{
|
|
hres = DI_NOEFFECT;
|
|
}
|
|
|
|
free_and_exit:;
|
|
FreePv( pMatch );
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CMap | ActionMap_IsValidMapObject |
|
|
*
|
|
* Utility function to check a DIACTIONFORMAT structure for validity.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm LPDIACTIONFORMAT | paf |
|
|
*
|
|
* Points to a structure that describes the actions needed by the
|
|
* application.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The structure is valid.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The structure is
|
|
* not valid.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HRESULT
|
|
CDIDev_ActionMap_IsValidMapObject
|
|
(
|
|
LPDIACTIONFORMATW paf
|
|
#ifdef XDEBUG
|
|
comma
|
|
LPCSTR pszProc
|
|
comma
|
|
UINT argnum
|
|
#endif
|
|
)
|
|
{
|
|
HRESULT hres = E_INVALIDARG;
|
|
|
|
/*
|
|
* Assert that the structures are the same until the final element
|
|
*/
|
|
#if defined(_WIN64)
|
|
CAssertF( ( ( cbX( DIACTIONFORMATW ) - cbX( ((LPDIACTIONFORMATW)0)->tszActionMap ) )
|
|
- ( cbX( DIACTIONFORMATA ) - cbX( ((LPDIACTIONFORMATA)0)->tszActionMap ) )
|
|
< MAX_NATURAL_ALIGNMENT ) );
|
|
#else
|
|
CAssertF( ( cbX( DIACTIONFORMATW ) - cbX( ((LPDIACTIONFORMATW)0)->tszActionMap ) )
|
|
== ( cbX( DIACTIONFORMATA ) - cbX( ((LPDIACTIONFORMATA)0)->tszActionMap ) ) );
|
|
#endif
|
|
|
|
CAssertF( FIELD_OFFSET( DIACTIONFORMATW, tszActionMap )
|
|
== FIELD_OFFSET( DIACTIONFORMATA, tszActionMap ) );
|
|
|
|
CAssertF( cbX(DIACTIONA) == cbX(DIACTIONW) );
|
|
|
|
if( FAILED(hresFullValidWriteNoScramblePvCb_(paf, MAKELONG( cbX(DIACTIONFORMATA), cbX(DIACTIONFORMATW) ), pszProc, argnum)) )
|
|
{
|
|
}
|
|
else if( paf->dwActionSize != cbX(DIACTION) )
|
|
{
|
|
D( RPF("IDirectInputDevice::%s: Invalid DIACTIONFORMAT.dwActionSize 0x%08x",
|
|
pszProc, paf->dwActionSize ); )
|
|
}
|
|
else if( paf->dwDataSize != (paf->dwNumActions * cbX( ((LPDIDEVICEOBJECTDATA)0)->dwData ) ) )
|
|
{
|
|
D( RPF("IDirectInputDevice::%s: DIACTIONFORMAT.dwDataSize 0x%08x not valid for DIACTIONFORMAT.dwNumActions 0x%08x",
|
|
pszProc, paf->dwDataSize ); )
|
|
}
|
|
else if( IsEqualGUID(&paf->guidActionMap, &GUID_Null) )
|
|
{
|
|
D( RPF("IDirectInputDevice::%s: DIACTIONFORMAT.guidActionMap is a NULL GUID", pszProc ); )
|
|
}
|
|
else if( !DISEM_VIRTUAL_GET( paf->dwGenre ) )
|
|
{
|
|
D( RPF("IDirectInputDevice::%s: Invalid (1) DIACTIONFORMAT.dwGenre 0x%08x", pszProc, paf->dwGenre ); )
|
|
}
|
|
else if( DISEM_GENRE_GET( paf->dwGenre ) > DISEM_MAX_GENRE )
|
|
{
|
|
D( RPF("IDirectInputDevice::%s: Invalid (2) DIACTIONFORMAT.dwGenre 0x%08x", pszProc, paf->dwGenre ); )
|
|
}
|
|
else if( ( paf->lAxisMin | paf->lAxisMax ) && ( paf->lAxisMin > paf->lAxisMax ) )
|
|
{
|
|
D( RPF("IDirectInputDevice::%s: Invalid DIACTIONFORMAT.lAxisMin 0x%08x for lAxisMax 0x%08x",
|
|
pszProc, paf->lAxisMin, paf->lAxisMax ); )
|
|
}
|
|
else if( !paf->dwNumActions )
|
|
{
|
|
D( RPF("IDirectInputDevice::%s: DIACTIONFORMAT.dwNumActions is zero", pszProc ); )
|
|
}
|
|
else if( paf->dwNumActions & 0xFF000000 )
|
|
{
|
|
D( RPF("IDirectInputDevice::%s: DIACTIONFORMAT.dwNumActions of 0x%08x is unreasonable", paf->dwNumActions, pszProc ); )
|
|
}
|
|
else if( !paf->rgoAction )
|
|
{
|
|
D( RPF("IDirectInputDevice::%s: DIACTIONFORMAT.rgoAction is NULL", pszProc ); )
|
|
}
|
|
else if( FAILED( hresFullValidWriteNoScramblePvCb_(paf->rgoAction,
|
|
cbX(*paf->rgoAction) * paf->dwNumActions, pszProc, argnum ) ) )
|
|
{
|
|
}
|
|
else
|
|
{
|
|
hres = S_OK;
|
|
}
|
|
|
|
/*
|
|
* Warning only tests go here. Only test if everything else was OK.
|
|
*/
|
|
#ifdef XDEBUG
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
}
|
|
#endif
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func DWORD | BitwiseReflect |
|
|
*
|
|
* Reflect the bottom bits of a value.
|
|
* Note, this could easily be optimized but it is only used once.
|
|
*
|
|
* @parm IN DWORD | dwValue |
|
|
*
|
|
* The value to be reflected.
|
|
*
|
|
* @parm IN int | iBottom |
|
|
*
|
|
* The number of bits to be reflected.
|
|
*
|
|
* @returns
|
|
* Returns the value dwValue with the bottom iBottom bits reflected
|
|
*
|
|
*****************************************************************************/
|
|
DWORD BitwiseReflect
|
|
(
|
|
DWORD dwValue,
|
|
int iBottom
|
|
)
|
|
{
|
|
int BitIdx;
|
|
DWORD dwTemp = dwValue;
|
|
|
|
#define BITMASK(X) (1L << (X))
|
|
|
|
for( BitIdx = 0; BitIdx < iBottom; BitIdx++ )
|
|
{
|
|
if( dwTemp & 1L )
|
|
dwValue |= BITMASK( ( iBottom - 1 ) - BitIdx );
|
|
else
|
|
dwValue &= ~BITMASK( ( iBottom - 1 ) - BitIdx );
|
|
dwTemp >>= 1;
|
|
}
|
|
return dwValue;
|
|
|
|
#undef BITMASK
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | CMap_InitializeCRCTable |
|
|
*
|
|
* If needed create and initialize the global table used for CRCs.
|
|
*
|
|
* Allocate memory for the 256 DWORD array and generate a set of
|
|
* values used to calculate a Cyclic Redundancy Check.
|
|
* The algorithm used is supposedly the same as Ethernet's CRC-32.
|
|
*
|
|
* @returns
|
|
* Returns one of the following a COM error codes:
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
* <c DI_NOEFFECT> = <c S_FALSE>: The table already existed.
|
|
*
|
|
* <c E_OUTOFMEMORY>: insufficient memory available for the table.
|
|
* The GetMapCRC function does not check that the table has been
|
|
* initialized so failure of this function should not allow
|
|
* processing to proceed into any function that uses GetMapCRC.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#define CRC_TABLE_SIZE 256
|
|
|
|
#define CRCWIDTH 32
|
|
#define POLY 0x04C11DB7
|
|
|
|
HRESULT CMap_InitializeCRCTable( void )
|
|
{
|
|
HRESULT hres;
|
|
int TableIdx;
|
|
|
|
if( g_rgdwCRCTable )
|
|
{
|
|
hres = S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
hres = AllocCbPpv( CRC_TABLE_SIZE * cbX( *g_rgdwCRCTable ), &g_rgdwCRCTable );
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
/*
|
|
* Special case the first element so it gets a non-zero value
|
|
*/
|
|
g_rgdwCRCTable[0] = POLY;
|
|
|
|
for( TableIdx = 1; TableIdx < CRC_TABLE_SIZE; TableIdx++ )
|
|
{
|
|
int BitIdx;
|
|
DWORD dwR;
|
|
|
|
dwR = BitwiseReflect( TableIdx, 8 ) << ( CRCWIDTH - 8 );
|
|
|
|
for( BitIdx = 0; BitIdx < 8; BitIdx++ )
|
|
{
|
|
if( dwR & 0x80000000 )
|
|
dwR = ( dwR << 1 ) ^ POLY;
|
|
else
|
|
dwR <<= 1;
|
|
}
|
|
|
|
g_rgdwCRCTable[TableIdx] = BitwiseReflect( dwR, CRCWIDTH );
|
|
|
|
/*
|
|
* We do not want a value of zero or identical values to be
|
|
* generated.
|
|
*/
|
|
AssertF( g_rgdwCRCTable[TableIdx] );
|
|
AssertF( g_rgdwCRCTable[TableIdx] != g_rgdwCRCTable[TableIdx-1] );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
#undef CRCWIDTH
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func DWORD | GetMapCRC |
|
|
*
|
|
* Calculate a CRC based on the passed DIACTIONFORMAT contents.
|
|
* The algorithm used is based on "CRC-32" which is supposedly what
|
|
* Ethernet uses, however some changes have been made to suit the
|
|
* specific use.
|
|
*
|
|
* @parm IN LPDIACTIONFORMAT | lpaf |
|
|
*
|
|
* Points to a structure that describes the actions to be mapped
|
|
* for which a CRC is to be generated.
|
|
*
|
|
* @returns
|
|
*
|
|
* The 32 bit CRC as a DWORD value.
|
|
*
|
|
*****************************************************************************/
|
|
DWORD GetMapCRC
|
|
(
|
|
LPDIACTIONFORMATW paf,
|
|
REFGUID guidInst
|
|
)
|
|
{
|
|
DWORD dwCRC;
|
|
PBYTE pBuffer;
|
|
LPCDIACTIONW pAction;
|
|
|
|
/*
|
|
* It is the caller's responsibility to make sure g_rgdwCRCTable has
|
|
* been allocated and initialized.
|
|
*/
|
|
AssertF( g_rgdwCRCTable );
|
|
|
|
/*
|
|
* Initialize to dwNumActions to avoid having to CRC it
|
|
*/
|
|
dwCRC = paf->dwNumActions;
|
|
|
|
/* Assert that the action map guid and the genre can be tested in series */
|
|
CAssertF( FIELD_OFFSET( DIACTIONFORMATW, dwGenre )
|
|
== FIELD_OFFSET( DIACTIONFORMATW, guidActionMap ) + cbX( paf->guidActionMap ) );
|
|
|
|
for( pBuffer = ((PBYTE)&paf->guidActionMap) + cbX( paf->guidActionMap ) + cbX( paf->dwGenre );
|
|
pBuffer >= ((PBYTE)&paf->guidActionMap); pBuffer-- )
|
|
{
|
|
dwCRC = g_rgdwCRCTable[( LOBYTE(dwCRC) ^ *pBuffer )] ^ (dwCRC >> 8);
|
|
}
|
|
|
|
/* Assert that the device instance guid and object ID can be tested in series */
|
|
CAssertF( FIELD_OFFSET( DIACTIONW, dwObjID )
|
|
== FIELD_OFFSET( DIACTIONW, guidInstance ) + cbX( pAction->guidInstance ) );
|
|
|
|
for( pAction = paf->rgoAction; pAction < &paf->rgoAction[paf->dwNumActions]; pAction++ )
|
|
{
|
|
/*
|
|
* Make sure this action is really relevant before including it in
|
|
* the CRC. It is assumed that any change in which actions are
|
|
* included will be picked up in the resultant CRC.
|
|
*/
|
|
if( IsEqualGUID( &pAction->guidInstance, guidInst )
|
|
&& ( pAction->dwHow & DIAH_MAPMASK )
|
|
&& ( ( pAction->dwFlags & DIA_APPNOMAP ) == 0 ) )
|
|
{
|
|
/*
|
|
* Besides which actions are taken into account, the only fields
|
|
* need to be verified are those that would change a SetActionMap.
|
|
* Although flags such as DIA_FORCEFEEDBACK could alter the
|
|
* mappings they do not change a SetActionMap.
|
|
*/
|
|
for( pBuffer = ((PBYTE)&pAction->guidInstance) + cbX( pAction->guidInstance ) + cbX( pAction->dwObjID );
|
|
pBuffer >= ((PBYTE)&pAction->guidInstance); pBuffer-- )
|
|
{
|
|
dwCRC = g_rgdwCRCTable[( LOBYTE(dwCRC) ^ *pBuffer )] ^ (dwCRC >> 8);
|
|
}
|
|
}
|
|
}
|
|
|
|
return dwCRC;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | CDIDev_BuildActionMapCore |
|
|
*
|
|
* Worker function for BuildActionMapA and BuildActionMapW.
|
|
* This does the real work once the external entry points have done
|
|
* dialect specific validation and set up.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP CDIDev_BuildActionMapCore
|
|
(
|
|
PDD this,
|
|
LPDIACTIONFORMATW paf,
|
|
LPCWSTR lpwszUserName,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
HRESULT hres;
|
|
|
|
EnterProcI(CDIDev_BuildActionMapCore, (_ "pWx", paf, lpwszUserName, dwFlags ));
|
|
|
|
// This application uses the mapper
|
|
AhAppRegister(this->dwVersion, 0x1);
|
|
|
|
switch( dwFlags & ( DIDBAM_PRESERVE | DIDBAM_INITIALIZE | DIDBAM_HWDEFAULTS ) )
|
|
{
|
|
case DIDBAM_DEFAULT:
|
|
case DIDBAM_PRESERVE:
|
|
case DIDBAM_INITIALIZE:
|
|
case DIDBAM_HWDEFAULTS:
|
|
hres = S_OK;
|
|
break;
|
|
default:
|
|
RPF("ERROR %s: arg %d: Must not combine "
|
|
"DIDBAM_PRESERVE, DIDBAM_INITIALIZE and DIDBAM_HWDEFAULTS", s_szProc, 3);
|
|
hres = E_INVALIDARG;
|
|
}
|
|
|
|
if( SUCCEEDED(hres)
|
|
&& SUCCEEDED(hres = hresFullValidFl(dwFlags, DIDBAM_VALID, 3))
|
|
&& SUCCEEDED(hres = CMap_ValidateActionMapSemantics( paf, dwFlags ) ) )
|
|
{
|
|
LPDIPROPSTRING pdipMapFile;
|
|
|
|
if( SUCCEEDED( hres = AllocCbPpv(cbX(*pdipMapFile), &pdipMapFile) ) )
|
|
{
|
|
HRESULT hresExactMaps = E_FAIL;
|
|
PWCHAR pwszMapFile;
|
|
DWORD dwCommsType;
|
|
|
|
if( dwFlags & DIDBAM_HWDEFAULTS )
|
|
{
|
|
hres = CMap_DeviceValidateActionMap( (PV)this, paf, DVAM_DEFAULT, &dwCommsType );
|
|
hresExactMaps = hres;
|
|
if( hres == S_OK )
|
|
{
|
|
hresExactMaps = DI_NOEFFECT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Do a generic test and map of any exact device matches
|
|
*/
|
|
hres = CMap_DeviceValidateActionMap( (PV)this, paf, DVAM_GETEXACTMAPPINGS, &dwCommsType );
|
|
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
/*
|
|
* Save the exact mapping result to combine with the result
|
|
* of semantic mapping.
|
|
*/
|
|
hresExactMaps = hres;
|
|
}
|
|
}
|
|
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
pdipMapFile->diph.dwSize = cbX(DIPROPSTRING);
|
|
pdipMapFile->diph.dwHeaderSize = cbX(DIPROPHEADER);
|
|
pdipMapFile->diph.dwObj = 0;
|
|
pdipMapFile->diph.dwHow = DIPH_DEVICE;
|
|
|
|
/*
|
|
* Try to get a configured mapping.
|
|
* If there is no IHV file there may still be a user file
|
|
*/
|
|
hres = CDIDev_GetPropertyW( &this->ddW, DIPROP_MAPFILE, &pdipMapFile->diph );
|
|
pwszMapFile = SUCCEEDED( hres ) ? pdipMapFile->wsz : NULL;
|
|
|
|
if( dwCommsType )
|
|
{
|
|
/*
|
|
* Communications control device
|
|
*/
|
|
if( !pwszMapFile )
|
|
{
|
|
/*
|
|
* If there's no IHV mapping there's nothing to add.
|
|
*/
|
|
hres = DI_NOEFFECT;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Modify the genre over the call to the mapper so
|
|
* that we get physical genre mappings from the IHV
|
|
* file if no user mappings are available
|
|
*/
|
|
|
|
DWORD dwAppGenre = paf->dwGenre;
|
|
|
|
paf->dwGenre = DIPHYSICAL_VOICE;
|
|
|
|
hres = this->pMS->lpVtbl->GetActionMap(this->pMS, &this->guid, pwszMapFile,
|
|
(LPDIACTIONFORMATW)paf, lpwszUserName, NULL, dwFlags );
|
|
|
|
/*
|
|
* ISSUE-2001/03/29-timgill Only want the timestamp for hardcoded devices
|
|
* ->Read again for mappings
|
|
*/
|
|
if( ( dwCommsType == DI8DEVTYPEDEVICECTRL_COMMSSELECTION_HARDWIRED )
|
|
&&!( dwFlags & DIDBAM_HWDEFAULTS ) )
|
|
{
|
|
DWORD dwLowTime;
|
|
DWORD dwHighTime;
|
|
|
|
dwLowTime = paf->ftTimeStamp.dwLowDateTime;
|
|
dwHighTime = paf->ftTimeStamp.dwHighDateTime;
|
|
|
|
hres = this->pMS->lpVtbl->GetActionMap(this->pMS, &this->guid, pwszMapFile,
|
|
(LPDIACTIONFORMATW)paf, lpwszUserName, NULL, DIDBAM_HWDEFAULTS );
|
|
|
|
paf->ftTimeStamp.dwLowDateTime = dwLowTime;
|
|
paf->ftTimeStamp.dwHighDateTime = dwHighTime;
|
|
}
|
|
paf->dwGenre = dwAppGenre;
|
|
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
if( hres == S_NOMAP )
|
|
{
|
|
/*
|
|
* Make sure we do not attempt defaults and
|
|
* the exact match return code gets back to the
|
|
* caller
|
|
*/
|
|
hres = DI_NOEFFECT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If there was an error, there are no defaults
|
|
* so quit now.
|
|
*/
|
|
if( ( HRESULT_FACILITY( hres ) == FACILITY_ITF )
|
|
&& ( HRESULT_CODE( hres ) > 0x0600 ) )
|
|
{
|
|
AssertF( HRESULT_CODE( hres ) < 0x0680 );
|
|
RPF( "Internal GetActionMap error 0x%08x for hardwired device", hres );
|
|
hres = DIERR_MAPFILEFAIL;
|
|
}
|
|
goto FreeAndExitCDIDev_BuildActionMapCore;
|
|
}
|
|
}
|
|
}
|
|
else if( !pwszMapFile && ( dwFlags & DIDBAM_HWDEFAULTS ) )
|
|
{
|
|
/*
|
|
* Make sure we get defaults
|
|
*/
|
|
SquirtSqflPtszV(sqflDf | sqflBenign,
|
|
TEXT("Failed to GetProperty DIPROP_MAPFILE 0x%08x, default will be generated"), hres );
|
|
hres = S_NOMAP;
|
|
}
|
|
else
|
|
{
|
|
hres = this->pMS->lpVtbl->GetActionMap(this->pMS, &this->guid, pwszMapFile,
|
|
(LPDIACTIONFORMATW)paf, lpwszUserName, NULL, dwFlags);
|
|
|
|
if( ( paf->ftTimeStamp.dwHighDateTime == DIAFTS_UNUSEDDEVICEHIGH )
|
|
&& ( paf->ftTimeStamp.dwLowDateTime == DIAFTS_UNUSEDDEVICELOW )
|
|
&& SUCCEEDED( hres ) )
|
|
{
|
|
/*
|
|
* If the device has never been used, the saved
|
|
* mappings are either the IHV defaults or cooked up
|
|
* defaults. Unfortunately, DIMap will have marked
|
|
* them as DIAH_USERCONFIG. Some day DIMap should
|
|
* either return the true flags or return without
|
|
* changing the flags, until then, reset all the
|
|
* flags and then do a second request for defaults.
|
|
*/
|
|
LPDIACTIONW pAction;
|
|
|
|
for( pAction = paf->rgoAction; pAction < &paf->rgoAction[paf->dwNumActions]; pAction++ )
|
|
{
|
|
if( ( ( pAction->dwFlags & ( DIA_APPMAPPED | DIA_APPNOMAP ) ) == 0 )
|
|
&& ( pAction->dwHow & DIAH_USERCONFIG )
|
|
&& IsEqualGUID( &pAction->guidInstance, &this->guid ) )
|
|
{
|
|
pAction->dwHow = DIAH_UNMAPPED;
|
|
}
|
|
}
|
|
|
|
hres = this->pMS->lpVtbl->GetActionMap(this->pMS, &this->guid, pwszMapFile,
|
|
(LPDIACTIONFORMATW)paf, lpwszUserName, NULL, DIDBAM_HWDEFAULTS);
|
|
|
|
/*
|
|
* Make sure the timestamps are still set for unused.
|
|
*/
|
|
paf->ftTimeStamp.dwLowDateTime = DIAFTS_UNUSEDDEVICELOW;
|
|
paf->ftTimeStamp.dwHighDateTime = DIAFTS_UNUSEDDEVICEHIGH;
|
|
}
|
|
|
|
if( FAILED( hres ) )
|
|
{
|
|
if( ( HRESULT_FACILITY( hres ) == FACILITY_ITF )
|
|
&& ( HRESULT_CODE( hres ) > 0x0600 ) )
|
|
{
|
|
AssertF( HRESULT_CODE( hres ) < 0x0680 );
|
|
RPF( "Internal GetActionMap error 0x%08x for configurable device", hres );
|
|
hres = DIERR_MAPFILEFAIL;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if( SUCCEEDED( hresExactMaps ) )
|
|
{
|
|
/*
|
|
* If we took an IHV mapping, do a default mapping on top.
|
|
* This allows IHVs to only map objects that are special
|
|
* leaving other objects to be used for whatever semantics
|
|
* match.
|
|
* Some day we should have a return code that indicates which
|
|
* type of mapping DIMap produced, until then, search the
|
|
* mappings for a HWDefault.
|
|
*/
|
|
if( SUCCEEDED( hres ) && ( hres != S_NOMAP )
|
|
&& ( dwCommsType != DI8DEVTYPEDEVICECTRL_COMMSSELECTION_HARDWIRED ) )
|
|
{
|
|
LPDIACTIONW pAction;
|
|
|
|
for( pAction = paf->rgoAction; pAction < &paf->rgoAction[paf->dwNumActions]; pAction++ )
|
|
{
|
|
if( ( ( pAction->dwFlags & ( DIA_APPMAPPED | DIA_APPNOMAP ) ) == 0 )
|
|
&& ( pAction->dwHow & DIAH_HWDEFAULT )
|
|
&& IsEqualGUID( &pAction->guidInstance, &this->guid ) )
|
|
{
|
|
hres = S_NOMAP;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( FAILED( hres ) || ( hres == S_NOMAP ) )
|
|
{
|
|
hres = this->pdcb->lpVtbl->BuildDefaultActionMap( this->pdcb, paf, dwFlags, &this->guid );
|
|
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
paf->ftTimeStamp.dwLowDateTime = DIAFTS_NEWDEVICELOW;
|
|
paf->ftTimeStamp.dwHighDateTime = DIAFTS_NEWDEVICEHIGH;
|
|
SquirtSqflPtszV(sqflDf | sqflVerbose, TEXT("Default action map used"));
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflError, TEXT("Default action map failed"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hres = CMap_DeviceValidateActionMap( (PV)this, paf, DVAM_DEFAULT, &dwCommsType );
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflVerbose, TEXT("Action map validated"));
|
|
}
|
|
else
|
|
{
|
|
RPF( "Initially valid action map invalidated by mapper!" );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If no semantics mapped, return the exact map result.
|
|
*/
|
|
if( hres == DI_NOEFFECT )
|
|
{
|
|
hres = hresExactMaps;
|
|
}
|
|
|
|
if( dwFlags & DIDBAM_HWDEFAULTS )
|
|
{
|
|
/*
|
|
* Timestamps are meaningless for hardware defaults
|
|
* so just make sure the values are consistent.
|
|
*/
|
|
paf->ftTimeStamp.dwLowDateTime = DIAFTS_UNUSEDDEVICELOW;
|
|
paf->ftTimeStamp.dwHighDateTime = DIAFTS_UNUSEDDEVICEHIGH;
|
|
}
|
|
else if( ( paf->ftTimeStamp.dwHighDateTime == DIAFTS_NEWDEVICEHIGH )
|
|
&& ( paf->ftTimeStamp.dwLowDateTime == DIAFTS_NEWDEVICELOW )
|
|
&& SUCCEEDED( hres ) )
|
|
{
|
|
if( FAILED( this->pMS->lpVtbl->SaveActionMap( this->pMS, &this->guid,
|
|
pwszMapFile, paf, lpwszUserName, dwFlags ) ) )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflBenign,
|
|
TEXT("Failed to save action map on first use 0x%08x"), hres );
|
|
paf->ftTimeStamp.dwLowDateTime = DIAFTS_UNUSEDDEVICELOW;
|
|
paf->ftTimeStamp.dwHighDateTime = DIAFTS_UNUSEDDEVICEHIGH;
|
|
/*
|
|
* Don't return an internal DIMap result, convert it to a published one
|
|
*/
|
|
if( ( HRESULT_FACILITY( hres ) == FACILITY_ITF )
|
|
&& ( HRESULT_CODE( hres ) > 0x0600 ) )
|
|
{
|
|
AssertF( HRESULT_CODE( hres ) < 0x0680 );
|
|
hres = DIERR_MAPFILEFAIL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflError, TEXT("Invalid mappings in action array"));
|
|
}
|
|
|
|
FreeAndExitCDIDev_BuildActionMapCore:;
|
|
FreePv( pdipMapFile );
|
|
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
paf->dwCRC = GetMapCRC( paf, &this->guid );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Note, we could try to do a default map even though we could not
|
|
* allocate space for the file name property but if allocations
|
|
* are failing we're better of quitting ASAP.
|
|
*/
|
|
SquirtSqflPtszV(sqflDf | sqflError, TEXT("Mem allocation failure") );
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
{
|
|
LPDIACTIONW pAction;
|
|
|
|
RPF( "Action map leaving build" );
|
|
// RPF( "Act# Semantic Flags Object How App Data" );
|
|
RPF( "A# Semantic Device Object How" );
|
|
for( pAction = paf->rgoAction; pAction < &paf->rgoAction[paf->dwNumActions]; pAction++ )
|
|
{
|
|
RPF( "%02d %08x {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X} %08x %08x",
|
|
pAction - paf->rgoAction,
|
|
pAction->dwSemantic,
|
|
pAction->guidInstance.Data1, pAction->guidInstance.Data2, pAction->guidInstance.Data3,
|
|
pAction->guidInstance.Data4[0], pAction->guidInstance.Data4[1],
|
|
pAction->guidInstance.Data4[2], pAction->guidInstance.Data4[3],
|
|
pAction->guidInstance.Data4[4], pAction->guidInstance.Data4[5],
|
|
pAction->guidInstance.Data4[6], pAction->guidInstance.Data4[7],
|
|
pAction->dwObjID,
|
|
pAction->dwHow,
|
|
pAction->uAppData );
|
|
// RPF( "%02d %08x %08x %08x %08x %08x",
|
|
// pAction - paf->rgoAction,
|
|
// pAction->dwSemantic,
|
|
// pAction->dwFlags,
|
|
// pAction->dwObjID,
|
|
// pAction->dwHow,
|
|
// pAction->uAppData );
|
|
}
|
|
RPF( "--" );
|
|
}
|
|
#endif
|
|
ExitOleProc();
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | BuildActionMap |
|
|
*
|
|
* Obtains the mapping of actions described in the <t DIACTIONFORMAT>
|
|
* to controls for this device.
|
|
* Information about user preferences and hardware manufacturer
|
|
* provided defaults is used to create the association between game
|
|
* actions and device controls.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm LPDIACTIONFORMAT | paf |
|
|
*
|
|
* Points to a structure that describes the actions needed by the
|
|
* application.
|
|
*
|
|
* @parm LPCSTR | lpszUserName |
|
|
*
|
|
* Name of user for whom mapping is requested. This may be a NULL
|
|
* pointer in which case the current user is assumed.
|
|
*
|
|
* @parm DWORD | dwFlags |
|
|
*
|
|
* Flags used to control the mapping. Must be a valid combination
|
|
* of the <c DIDBAM_*> flags.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DI_NOEFFECT> = <c S_OK>: The operation completed successfully
|
|
* but no actions were mapped.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: A parameter is invalid.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP CDIDev_BuildActionMapW
|
|
(
|
|
PV pdidW,
|
|
LPDIACTIONFORMATW paf,
|
|
LPCWSTR lpwszUserName,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
HRESULT hres;
|
|
|
|
EnterProcR(IDirectInputDevice8W::BuildActionMap, (_ "ppWx", pdidW, paf, lpwszUserName, dwFlags));
|
|
|
|
if( SUCCEEDED(hres = hresPvW( pdidW ) )
|
|
&& SUCCEEDED(hres = CDIDev_ActionMap_IsValidMapObject( paf D(comma s_szProc comma 1) ) ) )
|
|
{
|
|
if( paf->dwSize != cbX(DIACTIONFORMATW) )
|
|
{
|
|
D( RPF("IDirectInputDevice::%s: Invalid DIACTIONFORMATW.dwSize 0x%08x",
|
|
s_szProc, paf->dwSize ); )
|
|
hres = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
LPWSTR pwszGoodUserName;
|
|
|
|
hres = GetWideUserName( NULL, lpwszUserName, &pwszGoodUserName );
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
hres = CDIDev_BuildActionMapCore( _thisPvNm(pdidW, ddW), paf, lpwszUserName, dwFlags );
|
|
|
|
if( !lpwszUserName )
|
|
{
|
|
FreePv( pwszGoodUserName );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ExitOleProc();
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | BuildActionMapA |
|
|
*
|
|
* ANSI version of BuildActionMap.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP CDIDev_BuildActionMapA
|
|
(
|
|
PV pdidA,
|
|
LPDIACTIONFORMATA pafA,
|
|
LPCSTR lpszUserName,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
HRESULT hres;
|
|
|
|
EnterProcR(IDirectInputDevice8A::BuildActionMap, (_ "ppAx", pdidA, pafA, lpszUserName, dwFlags));
|
|
|
|
if( SUCCEEDED(hres = hresPvA( pdidA ) )
|
|
&& SUCCEEDED(hres = CDIDev_ActionMap_IsValidMapObject( (LPDIACTIONFORMATW)pafA D(comma s_szProc comma 1) ) ) )
|
|
{
|
|
if( pafA->dwSize != cbX(DIACTIONFORMATA) )
|
|
{
|
|
D( RPF("IDirectInputDevice::%s: Invalid DIACTIONFORMATA.dwSize 0x%08x",
|
|
s_szProc, pafA->dwSize ); )
|
|
hres = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
LPWSTR pwszGoodUserName;
|
|
|
|
hres = GetWideUserName( lpszUserName, NULL, &pwszGoodUserName );
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
/*
|
|
* For the sake of the mapper DLLs validation set the size to
|
|
* the UNICODE version. If we ever send this to an external
|
|
* component we should do this differently.
|
|
*/
|
|
pafA->dwSize = cbX(DIACTIONFORMATW);
|
|
|
|
hres = CDIDev_BuildActionMapCore( _thisPvNm(pdidA, ddA), (LPDIACTIONFORMATW)pafA, pwszGoodUserName, dwFlags );
|
|
|
|
pafA->dwSize = cbX(DIACTIONFORMATA);
|
|
|
|
FreePv( pwszGoodUserName );
|
|
}
|
|
}
|
|
}
|
|
|
|
ExitOleProc();
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | CDIDev_SaveActionMap |
|
|
*
|
|
* Worker function for SetActionMapA and SetActionMapW.
|
|
* Used to save an actions map.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP CDIDev_SaveActionMap
|
|
(
|
|
PDD this,
|
|
LPDIACTIONFORMATW paf,
|
|
LPWSTR lpwszUserName,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
HRESULT hres;
|
|
LPDIPROPSTRING pdipMapFile;
|
|
|
|
EnterProcI(CDIDev_SaveActionMap, (_ "pWAx", paf, lpwszUserName, dwFlags));
|
|
|
|
if( SUCCEEDED( hres = AllocCbPpv(cbX(*pdipMapFile), &pdipMapFile) ) )
|
|
{
|
|
DWORD dwLowTime;
|
|
DWORD dwHighTime;
|
|
|
|
// Save user's pass-in timestamps
|
|
dwLowTime = paf->ftTimeStamp.dwLowDateTime;
|
|
dwHighTime = paf->ftTimeStamp.dwHighDateTime;
|
|
|
|
pdipMapFile->diph.dwSize = cbX(DIPROPSTRING);
|
|
pdipMapFile->diph.dwHeaderSize = cbX(DIPROPHEADER);
|
|
pdipMapFile->diph.dwObj = 0;
|
|
pdipMapFile->diph.dwHow = DIPH_DEVICE;
|
|
|
|
paf->ftTimeStamp.dwLowDateTime = DIAFTS_UNUSEDDEVICELOW;
|
|
paf->ftTimeStamp.dwHighDateTime = DIAFTS_UNUSEDDEVICEHIGH;
|
|
|
|
hres = CDIDev_GetPropertyW( &this->ddW, DIPROP_MAPFILE, &pdipMapFile->diph );
|
|
|
|
hres = this->pMS->lpVtbl->SaveActionMap( this->pMS, &this->guid,
|
|
/* No map file if the GetProperty failed */
|
|
(SUCCEEDED( hres )) ? pdipMapFile->wsz : NULL,
|
|
paf, lpwszUserName, dwFlags );
|
|
|
|
// restore user's pass-in timestamps
|
|
paf->ftTimeStamp.dwLowDateTime = dwLowTime;
|
|
paf->ftTimeStamp.dwHighDateTime = dwHighTime;
|
|
|
|
FreePv( pdipMapFile );
|
|
}
|
|
|
|
ExitOleProc();
|
|
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev | ParseActionFormat |
|
|
*
|
|
* Parse the action format passed by the application and
|
|
* convert it into a format that we can use to translate
|
|
* the device data into application data.
|
|
*
|
|
* @parm IN LPDIACTIONFORMAT | lpaf |
|
|
*
|
|
* Points to a structure that describes the actions to be mapped
|
|
* to this device.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
|
|
* <p lpvData> parameter is not a valid pointer.
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_ParseActionFormat
|
|
(
|
|
PDD this,
|
|
LPDIACTIONFORMATW paf
|
|
)
|
|
{
|
|
PDIXLAT pdix;
|
|
PINT rgiobj;
|
|
VXDDATAFORMAT vdf;
|
|
HRESULT hres;
|
|
|
|
|
|
#ifdef DEBUG
|
|
EnterProc(CDIDev_ParseActionFormat, (_ "pp", this, paf));
|
|
#endif
|
|
|
|
/*
|
|
* Caller should've nuked the old translation table.
|
|
*/
|
|
AssertF(this->pdix == 0);
|
|
AssertF(this->rgiobj == 0);
|
|
|
|
if( paf->dwDataSize != paf->dwNumActions * cbX( ((LPDIDEVICEOBJECTDATA)0)->dwData ) )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflError,
|
|
TEXT("Incorrect dwDataSize (0x%08X) for dwNumActions (0x%08X)"),
|
|
paf->dwDataSize, paf->dwNumActions );
|
|
hres = E_INVALIDARG;
|
|
goto done_without_free;
|
|
}
|
|
|
|
vdf.cbData = this->df.dwDataSize;
|
|
vdf.pDfOfs = 0;
|
|
rgiobj = 0;
|
|
|
|
if( SUCCEEDED(hres = AllocCbPpv(cbCxX(this->df.dwNumObjs, DIXLAT), &pdix))
|
|
&& SUCCEEDED(hres = AllocCbPpv(cbCdw(this->df.dwDataSize), &vdf.pDfOfs))
|
|
&& SUCCEEDED(hres = AllocCbPpv(cbCdw(paf->dwDataSize), &rgiobj)) )
|
|
{
|
|
LPCDIACTIONW pAction;
|
|
DIPROPDWORD dipdw;
|
|
DIPROPRANGE diprange;
|
|
DWORD dwAxisMode = DIPROPAXISMODE_REL;
|
|
BOOL fSomethingMapped = FALSE;
|
|
BOOL fAxisModeKnown = FALSE;
|
|
|
|
/*
|
|
* Pre-init all the translation tags to -1,
|
|
* which means "not in use"
|
|
*/
|
|
memset(pdix, 0xFF, cbCxX(this->df.dwNumObjs, DIXLAT));
|
|
memset(vdf.pDfOfs, 0xFF, cbCdw(this->df.dwDataSize));
|
|
memset(rgiobj, 0xFF, cbCdw(paf->dwDataSize) );
|
|
|
|
/*
|
|
* Set up the property invariants
|
|
*/
|
|
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
|
|
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
|
dipdw.diph.dwHow = DIPH_BYID;
|
|
diprange.diph.dwSize = sizeof(DIPROPRANGE);
|
|
diprange.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
|
diprange.diph.dwHow = DIPH_BYID;
|
|
diprange.lMin = paf->lAxisMin;
|
|
diprange.lMax = paf->lAxisMax;
|
|
|
|
for( pAction = paf->rgoAction; pAction < &paf->rgoAction[paf->dwNumActions]; pAction++ )
|
|
{
|
|
if( IsEqualGUID( &pAction->guidInstance, &this->guid ) )
|
|
{
|
|
/*
|
|
* These flags have already been validated but it does not hurt to assert
|
|
*/
|
|
AssertF( ( pAction->dwHow & ~DIAH_VALID ) == 0 );
|
|
AssertF( ( pAction->dwHow & DIAH_ERROR ) == 0 );
|
|
AssertF( ( pAction->dwFlags & ~DIA_VALID ) == 0 );
|
|
|
|
if( ( pAction->dwHow & DIAH_MAPMASK )
|
|
&& ( ( pAction->dwFlags & DIA_APPNOMAP ) == 0 ) )
|
|
{
|
|
int iobjDev = -1;
|
|
PCODF podfFound;
|
|
|
|
for( podfFound = this->df.rgodf; podfFound < &this->df.rgodf[this->df.dwNumObjs]; podfFound++ )
|
|
{
|
|
iobjDev++;
|
|
/*
|
|
* Look for an exact type flags match
|
|
*/
|
|
if( podfFound->dwType == pAction->dwObjID )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( podfFound < &this->df.rgodf[this->df.dwNumObjs] )
|
|
{
|
|
DWORD dwAppOffset = (DWORD)(pAction - paf->rgoAction) * cbX( ((LPDIDEVICEOBJECTDATA)0)->dwData );
|
|
|
|
fSomethingMapped = TRUE;
|
|
|
|
vdf.pDfOfs[podfFound->dwOfs] = iobjDev;
|
|
rgiobj[dwAppOffset] = iobjDev;
|
|
|
|
pdix[iobjDev].dwOfs = dwAppOffset;
|
|
pdix[iobjDev].uAppData = pAction->uAppData;
|
|
|
|
if ( podfFound->dwFlags & DIDOI_POLLED ) {
|
|
this->fPolledDataFormat = TRUE;
|
|
}
|
|
|
|
dipdw.diph.dwObj = podfFound->dwType;
|
|
dipdw.dwData = 0x1; // Enable this report ID
|
|
hres = CDIDev_RealSetProperty(this, DIPROP_ENABLEREPORTID, &dipdw.diph);
|
|
if ( hres == E_NOTIMPL )
|
|
{
|
|
hres = S_OK;
|
|
}
|
|
else if( FAILED( hres ) )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflError,
|
|
TEXT("Could not set DIPROP_ENABLEREPORTID for object 0x%08x, error 0x%08x"),
|
|
pAction->dwObjID, hres);
|
|
/*
|
|
* Ouch! Can't carry on or the error will be lost so quit
|
|
*/
|
|
break;
|
|
}
|
|
|
|
|
|
/*
|
|
* Set the default axis parameters
|
|
*/
|
|
if( DISEM_TYPE_GET( pAction->dwSemantic ) == DISEM_TYPE_GET( DISEM_TYPE_AXIS ) )
|
|
{
|
|
if( podfFound->dwType & DIDFT_ABSAXIS )
|
|
{
|
|
if( !fAxisModeKnown )
|
|
{
|
|
fAxisModeKnown = TRUE;
|
|
dwAxisMode = DIPROPAXISMODE_ABS;
|
|
}
|
|
|
|
if( !( pAction->dwFlags & DIA_NORANGE )
|
|
&& ( diprange.lMin | diprange.lMax ) )
|
|
{
|
|
diprange.diph.dwObj = podfFound->dwType;
|
|
hres = CDIDev_RealSetProperty(this, DIPROP_RANGE, &diprange.diph);
|
|
if( FAILED( hres ) )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflBenign,
|
|
TEXT("failed (0x%08x) to set range on mapped axis action"),
|
|
hres );
|
|
}
|
|
/*
|
|
* Ranges cannot be set on natively relative
|
|
* axes so don't worry what the result is.
|
|
*/
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* dwAxisMode is initialized to DIPROPAXISMODE_REL
|
|
* so that this code path is the same as the one
|
|
* for DIDF_ABSAXIS as far as it goes. Compilers
|
|
* are good at delaying branches to remove such
|
|
* duplication.
|
|
*/
|
|
if( !fAxisModeKnown )
|
|
{
|
|
fAxisModeKnown = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflError,
|
|
TEXT("mapped action format contains invalid object 0x%08x"),
|
|
pAction->dwObjID );
|
|
hres = E_INVALIDARG;
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
#ifdef XDEBUG
|
|
else
|
|
{
|
|
if( ( pAction->dwHow & ~DIAH_VALID )
|
|
|| ( pAction->dwHow & DIAH_ERROR )
|
|
|| ( pAction->dwFlags & ~DIA_VALID ) )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflBenign,
|
|
TEXT("action format contains invalid object 0x%08x"),
|
|
pAction->dwObjID );
|
|
RPF("rgoAction[%d].dwHow 0x%08x or rgoAction[%d].dwFlags 0x%08x is invalid",
|
|
pAction - paf->rgoAction, pAction->dwHow,
|
|
pAction - paf->rgoAction, pAction->dwFlags );
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if( !fSomethingMapped )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflBenign, TEXT("No actions mapped") );
|
|
hres = DI_NOEFFECT;
|
|
goto done;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
/*
|
|
* Double-check the lookup tables just to preserve our sanity.
|
|
*/
|
|
{
|
|
UINT dwOfs;
|
|
|
|
for ( dwOfs = 0; dwOfs < paf->dwNumActions; dwOfs++ )
|
|
{
|
|
if ( rgiobj[dwOfs] >= 0 ) {
|
|
AssertF(pdix[rgiobj[dwOfs]].dwOfs == dwOfs);
|
|
} else {
|
|
AssertF(rgiobj[dwOfs] == -1);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
vdf.pvi = this->pvi;
|
|
|
|
if ( fLimpFF(this->pvi,
|
|
SUCCEEDED(hres = Hel_SetDataFormat(&vdf))) ) {
|
|
this->pdix = pdix;
|
|
pdix = 0;
|
|
this->rgiobj = rgiobj;
|
|
rgiobj = 0;
|
|
this->dwDataSize = paf->dwDataSize;
|
|
|
|
/*
|
|
* Now that the lower level knows what it's dealing with set
|
|
* the default buffer size.
|
|
*/
|
|
dipdw.diph.dwObj = 0;
|
|
dipdw.diph.dwHow = DIPH_DEVICE;
|
|
dipdw.dwData = paf->dwBufferSize;
|
|
|
|
hres = CDIDev_RealSetProperty(this, DIPROP_BUFFERSIZE, &dipdw.diph);
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
if( fAxisModeKnown )
|
|
{
|
|
AssertF( ( dwAxisMode == DIPROPAXISMODE_REL ) || ( dwAxisMode == DIPROPAXISMODE_ABS ) );
|
|
dipdw.dwData = dwAxisMode;
|
|
D( hres = )
|
|
CDIDev_RealSetProperty(this, DIPROP_AXISMODE, &dipdw.diph);
|
|
AssertF( SUCCEEDED( hres ) );
|
|
}
|
|
|
|
/*
|
|
* Complete success, whatever (success) SetProperty returns
|
|
* assume that DIPROP_AXISMODE will never fail from here.
|
|
*/
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflError,
|
|
TEXT("failed (0x%08x) to set buffer size 0x%0x8 for device"),
|
|
hres );
|
|
hres = E_INVALIDARG;
|
|
}
|
|
} else {
|
|
AssertF(FAILED(hres));
|
|
}
|
|
|
|
} else {
|
|
/* Out of memory */
|
|
}
|
|
|
|
done:;
|
|
FreePpv(&pdix);
|
|
FreePpv(&rgiobj);
|
|
FreePpv(&vdf.pDfOfs);
|
|
|
|
done_without_free:;
|
|
#ifdef DEBUG
|
|
ExitOleProc();
|
|
#endif
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | CDIDev_SetDataFormatFromMap |
|
|
*
|
|
* Worker function for SetActionMapA and SetActionMapW.
|
|
* Used to set a data format based on passed mapped actions.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_SetDataFormatFromMap
|
|
(
|
|
PDD this,
|
|
LPDIACTIONFORMATW paf
|
|
)
|
|
{
|
|
HRESULT hres;
|
|
|
|
EnterProcI(CDIDev_SetDataFormatFromMap, (_ "p", paf));
|
|
|
|
if( !paf->dwBufferSize )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflVerbose,
|
|
TEXT("%S: zero DIACTIONFORMAT.dwBufferSize, may need to set yourself"),
|
|
s_szProc );
|
|
}
|
|
if( paf->dwBufferSize > DEVICE_MAXBUFFERSIZE )
|
|
{
|
|
RPF("IDirectInputDevice::%s: DIACTIONFORMAT.dwBufferSize of 0x%08x is very large",
|
|
s_szProc, paf->dwBufferSize );
|
|
}
|
|
|
|
/*
|
|
* Must protect with the critical section to prevent two people
|
|
* from changing the format simultaneously, or one person from
|
|
* changing the data format while somebody else is reading data.
|
|
*/
|
|
CDIDev_EnterCrit(this);
|
|
|
|
if( !this->fAcquired )
|
|
{
|
|
DIPROPDWORD dipdw;
|
|
|
|
/*
|
|
* Nuke the old data format stuff before proceeding.
|
|
* Include the "failed POV" array as we can't fail and continue.
|
|
*/
|
|
FreePpv(&this->pdix);
|
|
FreePpv(&this->rgiobj);
|
|
FreePpv(&this->rgdwPOV);
|
|
|
|
this->cdwPOV = 0;
|
|
D(this->GetState = 0);
|
|
this->fPolledDataFormat = FALSE;
|
|
|
|
/*
|
|
* Wipe out the report IDs
|
|
*/
|
|
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
|
|
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
|
dipdw.diph.dwObj = 0x0;
|
|
dipdw.diph.dwHow = DIPH_DEVICE;
|
|
dipdw.dwData = 0; // Nuke all knowledge of reportId's
|
|
hres = CDIDev_RealSetProperty(this, DIPROP_ENABLEREPORTID, &dipdw.diph);
|
|
if( SUCCEEDED(hres) || hres == E_NOTIMPL )
|
|
{
|
|
hres = CDIDev_ParseActionFormat(this, paf);
|
|
|
|
/*
|
|
* If other success codes are implemented, this check should
|
|
* be modified to ( SUCCEEDED( hres ) && ( hres != DI_NOEFFECT ) )
|
|
*/
|
|
AssertF( ( hres == S_OK ) || ( hres == DI_NOEFFECT ) || FAILED( hres ) );
|
|
if( hres == S_OK )
|
|
{
|
|
hres = CDIDev_OptimizeDataFormat(this);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflVerbose,
|
|
TEXT("Could not set DIPROP_ENABLEREPORTID to 0x0"));
|
|
}
|
|
}
|
|
else
|
|
{ /* Already acquired */
|
|
hres = DIERR_ACQUIRED;
|
|
}
|
|
CDIDev_LeaveCrit(this);
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | SetActionMapCore |
|
|
*
|
|
* Worker function, does all the real work for SetActionMapW and
|
|
* SetActionMapA. See SetActionMapW for details.
|
|
*
|
|
*****************************************************************************/
|
|
STDMETHODIMP CDIDev_SetActionMapCore
|
|
(
|
|
PDD this,
|
|
LPDIACTIONFORMATW paf,
|
|
LPWSTR lpwszUserName,
|
|
LPSTR lpszUserName,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
HRESULT hres;
|
|
|
|
EnterProcI(IDirectInputDevice8::SetActionMapCore, (_ "pxWA", paf, dwFlags, lpwszUserName, lpszUserName ));
|
|
|
|
if( SUCCEEDED( hres = hresFullValidFl( dwFlags, DIDSAM_VALID, 3 ) ) )
|
|
{
|
|
if( dwFlags & DIDSAM_NOUSER )
|
|
{
|
|
if( dwFlags & ~DIDSAM_NOUSER )
|
|
{
|
|
RPF( "IDirectInputDevice8::SetActionMap: Invalid dwFlags 0x%08x, cannot use DIDSAM_NOUSER with other flags" );
|
|
hres = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
hres = CMap_SetDeviceUserName( &this->guid, NULL );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DWORD dwCRC;
|
|
LPWSTR pwszGoodUserName;
|
|
|
|
hres = GetWideUserName( lpszUserName, lpwszUserName, &pwszGoodUserName );
|
|
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
dwCRC = GetMapCRC( paf, &this->guid );
|
|
|
|
if( ( paf->dwCRC != dwCRC )
|
|
|| CMap_IsNewDeviceUserName( &this->guid, pwszGoodUserName ) )
|
|
{
|
|
/*
|
|
* Set the force save flag so we only have to test one bit later
|
|
*/
|
|
dwFlags |= DIDSAM_FORCESAVE;
|
|
}
|
|
|
|
if( dwFlags & DIDSAM_FORCESAVE )
|
|
{
|
|
hres = CMap_ValidateActionMapSemantics( paf, DIDBAM_PRESERVE );
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
DWORD dwDummy;
|
|
hres = CMap_DeviceValidateActionMap( (PV)this, paf, DVAM_DEFAULT, &dwDummy );
|
|
if( FAILED( hres ) )
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflError, TEXT("Action map invalid on SetActionMap"));
|
|
}
|
|
else if( ( hres == DI_WRITEPROTECT ) && ( paf->dwCRC != dwCRC ) )
|
|
{
|
|
RPF( "Refusing changed mappings for hardcoded device" );
|
|
hres = DIERR_INVALIDPARAM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqflDf | sqflError, TEXT("Action map invalid on SetActionMap"));
|
|
}
|
|
}
|
|
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
if( SUCCEEDED( hres = CMap_SetDeviceUserName( &this->guid, pwszGoodUserName ) )
|
|
&& SUCCEEDED( hres = CDIDev_SetDataFormatFromMap( this, paf ) ) )
|
|
{
|
|
if( dwFlags & DIDSAM_FORCESAVE )
|
|
{
|
|
hres = CDIDev_SaveActionMap( this, paf, pwszGoodUserName, dwFlags );
|
|
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
//We don't remap success code anywhere so
|
|
//assert it is what we expected
|
|
AssertF(hres==S_OK);
|
|
paf->dwCRC = dwCRC;
|
|
}
|
|
else
|
|
{
|
|
RPF( "Ignoring internal SaveActionMap error 0x%08x", hres );
|
|
hres = DI_SETTINGSNOTSAVED;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !lpwszUserName )
|
|
{
|
|
/*
|
|
* Free either the default name or the ANSI translation
|
|
*/
|
|
FreePv( pwszGoodUserName );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ExitOleProc();
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | SetActionMap |
|
|
*
|
|
* Set the data format for the DirectInput device from
|
|
* an action map for the passed application and user.
|
|
*
|
|
* If the action map has been changed (as determined by a CRC check)
|
|
* this latest map is saved after it has been applied.
|
|
*
|
|
* The data format must be set before the device can be
|
|
* acquired.
|
|
*
|
|
* It is necessary to set the data format only once.
|
|
*
|
|
* The data format may not be changed while the device
|
|
* is acquired.
|
|
*
|
|
* If the attempt to set the data format fails, all data
|
|
* format information is lost, and a valid data format
|
|
* must be set before the device may be acquired.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm LPDIACTIONFORMAT | paf |
|
|
*
|
|
* Points to a structure that describes the actions needed by the
|
|
* application.
|
|
*
|
|
* @parm LPCTSTR | lptszUserName |
|
|
*
|
|
* Name of user for whom mapping is being set. This may be a NULL
|
|
* pointer in which case the current user is assumed.
|
|
*
|
|
* @parm DWORD | dwFlags |
|
|
*
|
|
* Flags used to control how the mapping should be set.
|
|
* Must be a valid combination of the <c DIDSAM_*> flags.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: A parameter is invalid.
|
|
*
|
|
* <c DIERR_ACQUIRED>: Cannot apply an action map while the device
|
|
* is acquired.
|
|
*
|
|
*****************************************************************************/
|
|
STDMETHODIMP CDIDev_SetActionMapW
|
|
(
|
|
PV pdidW,
|
|
LPDIACTIONFORMATW pafW,
|
|
LPCWSTR lpwszUserName,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
HRESULT hres;
|
|
|
|
EnterProcR(IDirectInputDevice8W::SetActionMap, (_ "ppWx", pdidW, pafW, lpwszUserName, dwFlags));
|
|
|
|
if( ( SUCCEEDED( hres = hresPvW( pdidW ) ) )
|
|
&& ( SUCCEEDED( hres = CDIDev_ActionMap_IsValidMapObject( pafW D(comma s_szProc comma 1) ) ) ) )
|
|
{
|
|
if( pafW->dwSize != cbX(DIACTIONFORMATW) )
|
|
{
|
|
D( RPF("IDirectInputDevice::%s: Invalid DIACTIONFORMAT.dwSize 0x%08x",
|
|
s_szProc, pafW->dwSize ); )
|
|
hres = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
hres = CDIDev_SetActionMapCore( _thisPvNm( pdidW, ddW ),
|
|
pafW, (LPWSTR)lpwszUserName, NULL, dwFlags );
|
|
}
|
|
}
|
|
|
|
ExitOleProc();
|
|
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | SetActionMapA |
|
|
*
|
|
* ANSI version of SetActionMap, see SetActionMapW for details.
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
STDMETHODIMP CDIDev_SetActionMapA
|
|
(
|
|
PV pdidA,
|
|
LPDIACTIONFORMATA pafA,
|
|
LPCSTR lpszUserName,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
HRESULT hres;
|
|
|
|
EnterProcR(IDirectInputDevice8A::SetActionMap, (_ "ppAx", pdidA, pafA, lpszUserName, dwFlags));
|
|
|
|
if( ( SUCCEEDED( hres = hresPvA( pdidA ) ) )
|
|
&& ( SUCCEEDED( hres = CDIDev_ActionMap_IsValidMapObject(
|
|
(LPDIACTIONFORMATW)pafA D(comma s_szProc comma 1) ) ) ) )
|
|
{
|
|
if( pafA->dwSize != cbX(DIACTIONFORMATA) )
|
|
{
|
|
D( RPF("IDirectInputDevice::%s: Invalid DIACTIONFORMAT.dwSize 0x%08x",
|
|
s_szProc, pafA->dwSize ); )
|
|
hres = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* For the sake of the mapper DLLs validation set the size to
|
|
* the UNICODE version. If we ever send this to an external
|
|
* component we should do this differently.
|
|
*/
|
|
pafA->dwSize = cbX(DIACTIONFORMATW);
|
|
|
|
/*
|
|
* Note, the ANSI user name is passed on as there may be no need
|
|
* to translate it. CDIDev_SetActionMapCore deals with this.
|
|
*/
|
|
hres = CDIDev_SetActionMapCore( _thisPvNm( pdidA, ddA ),
|
|
(LPDIACTIONFORMATW)pafA, NULL, (LPSTR)lpszUserName, dwFlags );
|
|
|
|
pafA->dwSize = cbX(DIACTIONFORMATA);
|
|
}
|
|
}
|
|
|
|
ExitOleProc();
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | GetImageInfo |
|
|
*
|
|
* Retrieves device image information for use in displaying a
|
|
* configuration UI for a single device.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm LPDIDEVICEIMAGEINFOHEADER | pih |
|
|
*
|
|
* Pointer to structure into which the info is retrieved.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: A parameter is invalid.
|
|
*
|
|
* <c DIERR_NOTFOUND>: The device has no image information.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP CDIDev_GetImageInfoCore
|
|
(
|
|
PDD this,
|
|
LPDIDEVICEIMAGEINFOHEADERW piih
|
|
)
|
|
{
|
|
HRESULT hres;
|
|
|
|
EnterProcI(CDIDev_GetImageInfoCore, (_ "p", piih));
|
|
|
|
if( piih->dwSize != cbX( *piih ) )
|
|
{
|
|
D( RPF("IDirectInputDevice::%s: Invalid DIDEVICEIMAGEINFOHEADER.dwSize 0x%08x",
|
|
s_szProc, piih->dwSize ); )
|
|
hres = E_INVALIDARG;
|
|
}
|
|
else if( ( piih->lprgImageInfoArray )
|
|
&& ( piih->dwBufferSize < piih->dwSizeImageInfo ) )
|
|
{
|
|
D( RPF("IDirectInputDevice::%s: Invalid DIDEVICEIMAGEINFOHEADER.dwBufferSize 0x%08x",
|
|
s_szProc, piih->dwBufferSize ); )
|
|
hres = E_INVALIDARG;
|
|
}
|
|
else if( piih->dwBufferSize && !piih->lprgImageInfoArray )
|
|
{
|
|
D( RPF("IDirectInputDevice::%s: Invalid DIDEVICEIMAGEINFOHEADERW has dwBufferSize 0x%08x but NULL lprgImageInfoArray",
|
|
s_szProc, piih->dwBufferSize ); )
|
|
hres = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
LPDIPROPSTRING pdipMapFile;
|
|
|
|
if( SUCCEEDED( hres = AllocCbPpv(cbX(*pdipMapFile), &pdipMapFile) ) )
|
|
{
|
|
pdipMapFile->diph.dwSize = cbX(DIPROPSTRING);
|
|
pdipMapFile->diph.dwHeaderSize = cbX(DIPROPHEADER);
|
|
pdipMapFile->diph.dwObj = 0;
|
|
pdipMapFile->diph.dwHow = DIPH_DEVICE;
|
|
|
|
/*
|
|
* Must have an IHV file for image info
|
|
*/
|
|
hres = CDIDev_GetPropertyW( &this->ddW, DIPROP_MAPFILE, &pdipMapFile->diph );
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
/*
|
|
* ISSUE-2001/03/29-timgill workaraound code needs to be removed
|
|
* Initializing this to zero works around most of 34453
|
|
* Remove when that is fixed in DIMap.dll
|
|
*/
|
|
piih->dwBufferUsed = 0;
|
|
|
|
hres = this->pMS->lpVtbl->GetImageInfo( this->pMS, &this->guid, pdipMapFile->wsz, piih );
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
piih->dwcButtons = this->dc3.dwButtons;
|
|
piih->dwcAxes = this->dc3.dwAxes;
|
|
piih->dwcPOVs = this->dc3.dwPOVs;
|
|
|
|
AssertF( ( piih->dwBufferSize == 0 )
|
|
|| ( piih->dwBufferSize >= piih->dwBufferUsed ) );
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Use the same return code for all internal DIMap errors
|
|
*/
|
|
if( ( HRESULT_FACILITY( hres ) == FACILITY_ITF )
|
|
&& ( HRESULT_CODE( hres ) > 0x0600 ) )
|
|
{
|
|
AssertF( HRESULT_CODE( hres ) < 0x0680 );
|
|
RPF( "Internal GetImageInfo error 0x%08x", hres );
|
|
hres = DIERR_MAPFILEFAIL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Use the same return code for all forms of not found
|
|
*/
|
|
hres = DIERR_NOTFOUND;
|
|
}
|
|
|
|
FreePv( pdipMapFile );
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
ExitOleProc();
|
|
#endif
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDIDev_GetImageInfoW
|
|
(
|
|
PV pdidW,
|
|
LPDIDEVICEIMAGEINFOHEADERW piih
|
|
)
|
|
{
|
|
HRESULT hres;
|
|
|
|
EnterProcR(IDirectInputDevice8W::GetImageInfo, (_ "pp", pdidW, piih));
|
|
|
|
if( SUCCEEDED(hres = hresPvW( pdidW ) )
|
|
&& SUCCEEDED(hres = hresFullValidWriteNoScramblePxCb( piih, *piih, 1 ) ) )
|
|
{
|
|
ScrambleBuf( &piih->dwcViews, cbX( piih->dwcViews ) );
|
|
ScrambleBuf( &piih->dwcButtons, cbX( piih->dwcButtons ) );
|
|
ScrambleBuf( &piih->dwcAxes, cbX( piih->dwcAxes ) );
|
|
ScrambleBuf( &piih->dwBufferUsed, cbX( piih->dwBufferUsed ) );
|
|
|
|
if( piih->dwSizeImageInfo != cbX( *piih->lprgImageInfoArray ) )
|
|
{
|
|
D( RPF("IDirectInputDevice::%s: Invalid DIDEVICEIMAGEINFOHEADERW.dwSizeImageInfo 0x%08x",
|
|
s_szProc, piih->dwSizeImageInfo ); )
|
|
hres = E_INVALIDARG;
|
|
}
|
|
else if( SUCCEEDED(hres = hresFullValidWriteLargePvCb(
|
|
piih->lprgImageInfoArray, piih->dwBufferSize, 1 ) ) )
|
|
{
|
|
PDD this;
|
|
|
|
this = _thisPvNm(pdidW, ddW);
|
|
|
|
hres = CDIDev_GetImageInfoCore( this, piih );
|
|
}
|
|
}
|
|
|
|
ExitOleProc();
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDIDev_GetImageInfoA
|
|
(
|
|
PV pdidA,
|
|
LPDIDEVICEIMAGEINFOHEADERA piih
|
|
)
|
|
{
|
|
HRESULT hres;
|
|
|
|
EnterProcR(IDirectInputDevice8A::GetImageInfo, (_ "pp", pdidA, piih));
|
|
|
|
if( SUCCEEDED(hres = hresPvA( pdidA ) )
|
|
&& SUCCEEDED(hres = hresFullValidWriteNoScramblePxCb( piih, *piih, 1 ) ) )
|
|
{
|
|
ScrambleBuf( &piih->dwcViews, cbX( piih->dwcViews ) );
|
|
ScrambleBuf( &piih->dwcButtons, cbX( piih->dwcButtons ) );
|
|
ScrambleBuf( &piih->dwcAxes, cbX( piih->dwcAxes ) );
|
|
ScrambleBuf( &piih->dwBufferUsed, cbX( piih->dwBufferUsed ) );
|
|
|
|
if( piih->dwSizeImageInfo != cbX( *piih->lprgImageInfoArray ) )
|
|
{
|
|
D( RPF("IDirectInputDevice::%s: Invalid DIDEVICEIMAGEINFOHEADERA.dwSizeImageInfo 0x%08x",
|
|
s_szProc, piih->dwSizeImageInfo ); )
|
|
hres = E_INVALIDARG;
|
|
}
|
|
else if( SUCCEEDED(hres = hresFullValidWriteLargePvCb(
|
|
piih->lprgImageInfoArray, piih->dwBufferSize, 1 ) ) )
|
|
{
|
|
PDD this;
|
|
DIDEVICEIMAGEINFOHEADERW ihPrivate;
|
|
|
|
ihPrivate.dwSize = cbX( ihPrivate );
|
|
ihPrivate.dwSizeImageInfo = cbX( *ihPrivate.lprgImageInfoArray );
|
|
ihPrivate.dwBufferSize = cbX( *ihPrivate.lprgImageInfoArray )
|
|
* ( piih->dwBufferSize / cbX( *piih->lprgImageInfoArray ) );
|
|
|
|
hres = AllocCbPpv( ihPrivate.dwBufferSize, &ihPrivate.lprgImageInfoArray );
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
this = _thisPvNm(pdidA, ddA);
|
|
|
|
hres = CDIDev_GetImageInfoCore( this, &ihPrivate );
|
|
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
LPDIDEVICEIMAGEINFOW piiW;
|
|
LPDIDEVICEIMAGEINFOA piiA = piih->lprgImageInfoArray;
|
|
|
|
CAssertF( cbX( *piiA ) - cbX( piiA->tszImagePath ) == cbX( *piiW ) - cbX( piiW->tszImagePath ) );
|
|
CAssertF( FIELD_OFFSET( DIDEVICEIMAGEINFOA, tszImagePath ) == 0 );
|
|
CAssertF( FIELD_OFFSET( DIDEVICEIMAGEINFOW, tszImagePath ) == 0 );
|
|
CAssertF( FIELD_OFFSET( DIDEVICEIMAGEINFOW, dwFlags ) == cbX( piiW->tszImagePath ) );
|
|
|
|
if(ihPrivate.lprgImageInfoArray)
|
|
{
|
|
for( piiW = ihPrivate.lprgImageInfoArray;
|
|
(PBYTE)piiW < (PBYTE)ihPrivate.lprgImageInfoArray + ihPrivate.dwBufferUsed;
|
|
piiW++ )
|
|
{
|
|
UToA( piiA->tszImagePath, cbX( piiA->tszImagePath ), piiW->tszImagePath );
|
|
memcpy( (PV)&piiA->dwFlags, (PV)&piiW->dwFlags,
|
|
cbX( *piiA ) - cbX( piiA->tszImagePath ) );
|
|
piiA++;
|
|
}
|
|
}
|
|
|
|
piih->dwBufferUsed = cbX( *piih->lprgImageInfoArray )
|
|
* ( ihPrivate.dwBufferUsed / cbX( *ihPrivate.lprgImageInfoArray ) );
|
|
|
|
piih->dwcViews = ihPrivate.dwcViews;
|
|
piih->dwcButtons = ihPrivate.dwcButtons;
|
|
piih->dwcAxes = ihPrivate.dwcAxes;
|
|
piih->dwcPOVs = ihPrivate.dwcPOVs;
|
|
}
|
|
|
|
FreePv( ihPrivate.lprgImageInfoArray );
|
|
}
|
|
}
|
|
}
|
|
ExitOleProc();
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | GetDeviceState |
|
|
*
|
|
* Obtains instantaneous data from the DirectInput device.
|
|
*
|
|
* Before device data can be obtained, the data format must
|
|
* be set via <mf IDirectInputDevice::SetDataFormat>, and
|
|
* the device must be acquired via
|
|
* <mf IDirectInputDevice::Acquire>.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm DWORD | cbData |
|
|
*
|
|
* The size of the buffer pointed to by <p lpvData>, in bytes.
|
|
*
|
|
* @parm OUT LPVOID | lpvData |
|
|
*
|
|
* Points to a structure that receives the current state
|
|
* of the device.
|
|
* The format of the data is established by a prior call
|
|
* to <mf IDirectInputDevice::SetDataFormat>.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c E_PENDING>: The device does not have data yet.
|
|
* Some devices (such as USB joysticks) require a delay
|
|
* between the time the device is turned on and the time
|
|
* the device begins sending data. During this "warm-up" time,
|
|
* <mf IDirectInputDevice::GetDeviceState> will return
|
|
* <c E_PENDING>. When data becomes available, the event
|
|
* notification handle will be signalled.
|
|
*
|
|
* <c DIERR_NOTACQUIRED>: The device is not acquired.
|
|
*
|
|
* <c DIERR_INPUTLOST>: Access to the device has been
|
|
* interrupted. The application should re-acquire the
|
|
* device.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
|
|
* <p lpvData> parameter is not a valid pointer or
|
|
* the <p cbData> parameter does not match the data size
|
|
* set by a previous call to <mf IDirectInputDevice::SetDataFormat>.
|
|
*
|
|
*****************************************************************************/
|
|
extern STDMETHODIMP CDIDev_Acquire(PV pdd _THAT);
|
|
|
|
|
|
STDMETHODIMP
|
|
CDIDev_GetDeviceState(PV pdd, DWORD cbDataSize, LPVOID pvData _THAT)
|
|
{
|
|
HRESULT hres;
|
|
PDD this;
|
|
EnterProcR(IDirectInputDevice8::GetDeviceState, (_ "pp", pdd, pvData));
|
|
|
|
/*
|
|
* Note that we do not validate the parameters.
|
|
* The reason is that GetDeviceState is an inner loop function,
|
|
* so it should be as fast as possible.
|
|
*/
|
|
#ifdef XDEBUG
|
|
hresPvT(pdd);
|
|
hresFullValidWritePvCb(pvData, cbDataSize, 1);
|
|
#endif
|
|
this = _thisPv(pdd);
|
|
|
|
/*
|
|
* Must protect with the critical section to prevent somebody from
|
|
* unacquiring while we're reading.
|
|
*/
|
|
CDIDev_EnterCrit(this);
|
|
|
|
/*
|
|
* Reacquire is not allowed until after Win98 SE, see OSR Bug # 89958
|
|
*/
|
|
if ( this->diHacks.fReacquire &&
|
|
!this->fAcquired && (this->fOnceAcquired || this->fOnceForcedUnacquired) )
|
|
{
|
|
if ( SUCCEEDED( CDIDev_Acquire(pdd THAT_) ) ) {
|
|
// 7/18/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
|
|
RPF(" DirectInput: Auto acquired (0x%p)", pdd);
|
|
}
|
|
}
|
|
|
|
#ifdef WINNT
|
|
if( this->fUnacquiredWhenIconic && !IsIconic(this->hwnd) ) {
|
|
if ( SUCCEEDED( CDIDev_Acquire(pdd THAT_) ) ) {
|
|
this->fUnacquiredWhenIconic = 0;
|
|
RPF(" DirectInput: Auto acquired device (0x%p) after being iconic. ", pdd);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ( this->fAcquired ) {
|
|
AssertF(this->pdix); /* Acquire shouldn't let you get this far */
|
|
AssertF(this->GetState);
|
|
AssertF(this->GetDeviceState);
|
|
AssertF(this->pdcb);
|
|
|
|
if ( this->dwDataSize == cbDataSize ) {
|
|
#ifndef DEBUG_STICKY
|
|
hres = this->GetState(this, pvData);
|
|
#else
|
|
PBYTE pbDbg;
|
|
TCHAR tszDbg[80];
|
|
|
|
hres = this->GetState(this, pvData);
|
|
|
|
for( pbDbg=(PBYTE)pvData; pbDbg<((PBYTE)pvData+cbDataSize); pbDbg++ )
|
|
{
|
|
if( *pbDbg )
|
|
{
|
|
wsprintf( tszDbg, TEXT("GotState @ 0x%02x, 0x%02x\r\n"), pbDbg-(PBYTE)pvData, *pbDbg );
|
|
OutputDebugString( tszDbg );
|
|
}
|
|
}
|
|
#endif /* DEBUG_STICKY */
|
|
|
|
if ( SUCCEEDED(hres) ) {
|
|
UINT idw;
|
|
|
|
AssertF(hres == S_OK);
|
|
/*
|
|
* Icky POV hack for apps that don't check if they have
|
|
* a POV before reading from it.
|
|
*/
|
|
for ( idw = 0; idw < this->cdwPOV; idw++ ) {
|
|
DWORD UNALIGNED *pdw = pvAddPvCb(pvData, this->rgdwPOV[idw]);
|
|
*pdw = JOY_POVCENTERED;
|
|
}
|
|
hres = S_OK;
|
|
} else if ( hres == DIERR_INPUTLOST ) {
|
|
RPF("%s: Input lost", s_szProc);
|
|
CDIDev_InternalUnacquire(this);
|
|
|
|
hres = DIERR_INPUTLOST;
|
|
}
|
|
} else {
|
|
RPF("ERROR %s: arg %d: invalid value", s_szProc, 1);
|
|
hres = E_INVALIDARG;
|
|
}
|
|
} else {
|
|
hres = this->hresNotAcquired;
|
|
}
|
|
|
|
if ( FAILED(hres) ) {
|
|
ScrambleBuf(pvData, cbDataSize);
|
|
}
|
|
|
|
CDIDev_LeaveCrit(this);
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method void | IDirectInputDevice | CookDeviceData |
|
|
*
|
|
* Cook device data that was recently obtained from the
|
|
* device buffer.
|
|
*
|
|
* Right now, only the joystick device requires cooking,
|
|
* and nobody in their right mind uses buffered joystick
|
|
* data, and the joystick has only a few objects, so we
|
|
* can afford to be slow on this.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm DWORD | cdod |
|
|
*
|
|
* Number of objects to cook.
|
|
*
|
|
* @parm LPDIDEVICEOBJECTDATA | rgdod |
|
|
*
|
|
* Array of object data to cook. The dwOfs are really
|
|
* device object indexes (relative to the device format).
|
|
* After calling the callback, we convert them into
|
|
* application data format offsets.
|
|
*
|
|
* @returns
|
|
*
|
|
* None.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void INTERNAL
|
|
CDIDev_CookDeviceData(PDD this, DWORD cdod, LPDIDEVICEOBJECTDATA rgdod)
|
|
{
|
|
EnterProc(IDirectInputDevice8::CookDeviceData,
|
|
(_ "pxp", this, cdod, rgdod));
|
|
|
|
AssertF(this->fCook);
|
|
|
|
/*
|
|
* Relative data does not need to be cooked by the callback.
|
|
*/
|
|
if( ( this->pvi->fl & VIFL_RELATIVE ) == 0 )
|
|
{
|
|
this->pdcb->lpVtbl->CookDeviceData(this->pdcb, cdod, rgdod);
|
|
}
|
|
|
|
/*
|
|
* Step through array converting to application data format offsets
|
|
* including adding the uAppData
|
|
*/
|
|
|
|
for( ; cdod; cdod--,rgdod++ )
|
|
{
|
|
rgdod->uAppData = this->pdix[rgdod->dwOfs].uAppData;
|
|
rgdod->dwOfs = this->pdix[rgdod->dwOfs].dwOfs;
|
|
}
|
|
|
|
ExitProc();
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @struct SOMEDEVICEDATA |
|
|
*
|
|
* Instance data used by <mf IDirectInputDevice::GetSomeDeviceData>.
|
|
*
|
|
* @field DWORD | celtIn |
|
|
*
|
|
* Number of elements remaining in output buffer.
|
|
*
|
|
* @field PDOD | rgdod |
|
|
*
|
|
* Output buffer for data elements, or <c NULL> if
|
|
* elements should be discarded.
|
|
*
|
|
* @field DWORD | celtOut |
|
|
*
|
|
* Number of elements actually copied (so far).
|
|
*
|
|
*****************************************************************************/
|
|
|
|
typedef struct SOMEDEVICEDATA {
|
|
DWORD celtIn;
|
|
PDOD rgdod;
|
|
DWORD celtOut;
|
|
} SOMEDEVICEDATA, *PSOMEDEVICEDATA;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method PDOD | IDirectInputDevice | GetSomeDeviceData |
|
|
*
|
|
* Obtains a small amount of
|
|
* buffered data from the DirectInput device.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm PDOD | pdod |
|
|
*
|
|
* First element to copy.
|
|
*
|
|
* @parm DWORD | celt |
|
|
*
|
|
* Maximum number of elements to copy.
|
|
*
|
|
* @parm PSOMEDEVICEDATA | psdd |
|
|
*
|
|
* Structure describing the state of the ongoing
|
|
* <mf IDirectInputDevice::GetDeviceData>.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a pointer to the first uncopied item.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
LPDIDEVICEOBJECTDATA_DX3 INTERNAL
|
|
CDIDev_GetSomeDeviceData
|
|
(
|
|
PDD this,
|
|
LPDIDEVICEOBJECTDATA_DX3 pdod,
|
|
DWORD celt,
|
|
PSOMEDEVICEDATA psdd
|
|
)
|
|
{
|
|
#ifdef XDEBUG
|
|
DWORD cCopied;
|
|
#endif
|
|
|
|
EnterProc(IDirectInputDevice8::GetSomeDeviceData,
|
|
(_ "ppxx", this, pdod, celt, psdd->celtIn));
|
|
|
|
/*
|
|
* Copy as many elements as fit, but not more than exist
|
|
* in the output buffer.
|
|
*/
|
|
if ( celt > psdd->celtIn ) {
|
|
celt = psdd->celtIn;
|
|
}
|
|
|
|
#ifdef XDEBUG
|
|
cCopied = celt;
|
|
#endif
|
|
/*
|
|
* Copy the elements (if requested) and update the state.
|
|
* Note that celt might be zero.
|
|
*/
|
|
psdd->celtOut += celt;
|
|
psdd->celtIn -= celt;
|
|
|
|
if( psdd->rgdod )
|
|
{
|
|
LPDIDEVICEOBJECTDATA pdod8;
|
|
pdod8 = psdd->rgdod;
|
|
|
|
if( this->fCook )
|
|
{
|
|
/*
|
|
* For a cooked device, leave the offset untranslated so that it
|
|
* can be used to find the appropriate calibration data.
|
|
*/
|
|
for( ; celt ; celt-- )
|
|
{
|
|
pdod8->dwOfs = pdod->dwOfs;
|
|
pdod8->dwData = pdod->dwData;
|
|
pdod8->dwTimeStamp = pdod->dwTimeStamp;
|
|
pdod8->dwSequence = pdod->dwSequence;
|
|
|
|
pdod++;
|
|
pdod8++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( ; celt ; celt-- )
|
|
{
|
|
pdod8->dwOfs = this->pdix[pdod->dwOfs].dwOfs;
|
|
pdod8->uAppData = this->pdix[pdod->dwOfs].uAppData;
|
|
pdod8->dwData = pdod->dwData;
|
|
pdod8->dwTimeStamp = pdod->dwTimeStamp;
|
|
pdod8->dwSequence = pdod->dwSequence;
|
|
|
|
pdod++;
|
|
pdod8++;
|
|
}
|
|
}
|
|
psdd->rgdod = pdod8;
|
|
}
|
|
else
|
|
{
|
|
pdod += celt;
|
|
}
|
|
|
|
|
|
if ( pdod == this->pvi->pEnd ) {
|
|
pdod = this->pvi->pBuffer;
|
|
}
|
|
|
|
ExitProcX(cCopied);
|
|
|
|
return pdod;
|
|
}
|
|
|
|
/*
|
|
* Keep GetDeviceData, SendDeviceData and Poll in sqflDev
|
|
*/
|
|
#undef sqfl
|
|
#define sqfl sqflDev
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | GetDeviceData |
|
|
*
|
|
* Obtains buffered data from the DirectInput device.
|
|
*
|
|
* DirectInput devices are, by default, unbuffered. To
|
|
* turn on buffering, you must set the buffer size
|
|
* via <mf IDirectInputDevice::SetProperty>, setting the
|
|
* <c DIPROP_BUFFERSIZE> property to the desired size
|
|
* of the input buffer.
|
|
*
|
|
* Before device data can be obtained, the data format must
|
|
* be set via <mf IDirectInputDevice::SetDataFormat>, and
|
|
* the device must be acquired via
|
|
* <mf IDirectInputDevice::Acquire>.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm DWORD | cbObjectData |
|
|
*
|
|
* The size of a single <t DIDEVICEOBJECTDATA> structure in bytes.
|
|
*
|
|
* @parm OUT LPDIDEVICEOBJECTDATA | rgdod |
|
|
*
|
|
* Array of <t DIDEVICEOBJECTDATA> structures to receive
|
|
* the buffered data. It must consist of
|
|
* *<p pdwInOut> elements.
|
|
*
|
|
* If this parameter is <c NULL>, then the buffered data is
|
|
* not stored anywhere, but all other side-effects take place.
|
|
*
|
|
* @parm INOUT LPDWORD | pdwInOut |
|
|
*
|
|
* On entry, contains the number of elements in the array
|
|
* pointed to by <p rgdod>. On exit, contains the number
|
|
* of elements actually obtained.
|
|
*
|
|
* @parm DWORD | fl |
|
|
*
|
|
* Flags which control the manner in which data is obtained.
|
|
* It may be zero or more of the following flags:
|
|
*
|
|
* <c DIGDD_PEEK>: Do not remove the items from the buffer.
|
|
* A subsequent <mf IDirectInputDevice::GetDeviceData> will
|
|
* read the same data. Normally, data is removed from the
|
|
* buffer after it is read.
|
|
*
|
|
;begin_internal dx4
|
|
* <c DIGDD_RESIDUAL>: Read data from the device buffer
|
|
* even if the device is not acquired. Normally, attempting
|
|
* to read device data from an unacquired device will return
|
|
* <c DIERR_NOTACQUIRED> or <c DIERR_INPUTLOST>.
|
|
;end_internal dx4
|
|
*
|
|
* @returns
|
|
*
|
|
* <c DI_OK> = <c S_OK>: All data were retrieved
|
|
* successfully. Note that the application needs to check
|
|
* the output value of *<p pdwInOut> to determine whether
|
|
* and how much data was retrieved: The value may be zero,
|
|
* indicating that the buffer was empty.
|
|
*
|
|
* <c DI_BUFFEROVERFLOW> = <c S_FALSE>: Some data
|
|
* were retrieved successfully, but some data were lost
|
|
* because the device's buffer size was not large enough.
|
|
* The application should retrieve buffered data more frequently
|
|
* or increase the device buffer size. This status code is
|
|
* returned only on the first <mf IDirectInput::GetDeviceData>
|
|
* call after the buffer has overflowed. Note that this is
|
|
* a success status code.
|
|
*
|
|
* <c DIERR_NOTACQUIRED>: The device is not acquired.
|
|
*
|
|
* <c DIERR_INPUTLOST>: Access to the device has been
|
|
* interrupted. The application should re-acquire the
|
|
* device.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: One or more
|
|
* parameters was invalid. A common cause for this is
|
|
* neglecting to set a buffer size.
|
|
*
|
|
* <c DIERR_NOTBUFFERED>: The device is not buffered.
|
|
* Set the <c DIPROP_BUFFERSIZE> property to enable buffering.
|
|
*
|
|
* @ex
|
|
*
|
|
* The following sample reads up to ten buffered data elements,
|
|
* removing them from the device buffer as they are read.
|
|
*
|
|
* |
|
|
*
|
|
* DIDEVICEOBJECTDATA rgdod[10];
|
|
* DWORD dwItems = 10;
|
|
* hres = IDirectInputDevice_GetDeviceData(
|
|
* pdid,
|
|
* sizeof(DIDEVICEOBJECTDATA),
|
|
* rgdod,
|
|
* &dwItems,
|
|
* 0);
|
|
* if (SUCCEEDED(hres)) {
|
|
* // Buffer successfully flushed.
|
|
* // dwItems = number of elements flushed
|
|
* if (hres == DI_BUFFEROVERFLOW) {
|
|
* // Buffer had overflowed.
|
|
* }
|
|
* }
|
|
*
|
|
*
|
|
*
|
|
*
|
|
* @ex
|
|
*
|
|
* If you pass <c NULL> for the <p rgdod> and request an
|
|
* infinite number of items, this has the effect of flushing
|
|
* the buffer and returning the number of items that were
|
|
* flushed.
|
|
*
|
|
* |
|
|
*
|
|
* dwItems = INFINITE;
|
|
* hres = IDirectInputDevice_GetDeviceData(
|
|
* pdid,
|
|
* sizeof(DIDEVICEOBJECTDATA),
|
|
* NULL,
|
|
* &dwItems,
|
|
* 0);
|
|
* if (SUCCEEDED(hres)) {
|
|
* // Buffer successfully flushed.
|
|
* // dwItems = number of elements flushed
|
|
* if (hres == DI_BUFFEROVERFLOW) {
|
|
* // Buffer had overflowed.
|
|
* }
|
|
* }
|
|
*
|
|
* @ex
|
|
*
|
|
* If you pass <c NULL> for the <p rgdod>, request an
|
|
* infinite number of items, and ask that the data not be
|
|
* removed from the device buffer, this has the effect of
|
|
* querying for the number of elements in the device buffer.
|
|
*
|
|
* |
|
|
*
|
|
* dwItems = INFINITE;
|
|
* hres = IDirectInputDevice_GetDeviceData(
|
|
* pdid,
|
|
* sizeof(DIDEVICEOBJECTDATA),
|
|
* NULL,
|
|
* &dwItems,
|
|
* DIGDD_PEEK);
|
|
* if (SUCCEEDED(hres)) {
|
|
* // dwItems = number of elements in buffer
|
|
* if (hres == DI_BUFFEROVERFLOW) {
|
|
* // Buffer overflow occurred; not all data
|
|
* // were successfully captured.
|
|
* }
|
|
* }
|
|
*
|
|
* @ex
|
|
*
|
|
* If you pass <c NULL> for the <p rgdod> and request zero
|
|
* items, this has the effect of querying whether buffer
|
|
* overflow has occurred.
|
|
*
|
|
* |
|
|
*
|
|
* dwItems = 0;
|
|
* hres = IDirectInputDevice_GetDeviceData(
|
|
* pdid,
|
|
* sizeof(DIDEVICEOBJECTDATA),
|
|
* NULL,
|
|
* &dwItems,
|
|
* 0);
|
|
* if (hres == DI_BUFFEROVERFLOW) {
|
|
* // Buffer overflow occurred
|
|
* }
|
|
*
|
|
*
|
|
*//**************************************************************************
|
|
*
|
|
* When reading this code, the following pictures will come in handy.
|
|
*
|
|
*
|
|
* Buffer not wrapped.
|
|
*
|
|
* pBuffer pEnd
|
|
* | |
|
|
* v v
|
|
* +----+----+----+----+----+----+----+----+----+----+----+
|
|
* | | | | | | | | | | | |
|
|
* | | | |data|data|data|data|data| | | |
|
|
* | | | | | | | | | | | |
|
|
* +----+----+----+----+----+----+----+----+----+----+----+
|
|
* ^ ^
|
|
* | |
|
|
* pTail pHead
|
|
*
|
|
*
|
|
* Buffer wrapped.
|
|
*
|
|
* pBuffer pEnd
|
|
* | |
|
|
* v v
|
|
* +----+----+----+----+----+----+----+----+----+----+----+
|
|
* | | | | | | | | | | | |
|
|
* |data|data| | | | | | |data|data|data|
|
|
* | | | | | | | | | | | |
|
|
* +----+----+----+----+----+----+----+----+----+----+----+
|
|
* ^ ^
|
|
* | |
|
|
* pHead pTail
|
|
*
|
|
*
|
|
* Boundary wrap case.
|
|
*
|
|
*
|
|
* pBuffer pEnd
|
|
* | |
|
|
* v v
|
|
* +----+----+----+----+----+----+----+----+----+----+----+
|
|
* | | | | | | | | | | | |
|
|
* | | | | | | |data|data|data|data|data|
|
|
* | | | | | | | | | | | |
|
|
* +----+----+----+----+----+----+----+----+----+----+----+
|
|
* ^ ^
|
|
* | |
|
|
* pHead pTail
|
|
*
|
|
*
|
|
* Note! At no point is pTail == pEnd or pHead == pEnd.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_GetDeviceData(PV pdd, DWORD cbdod, PDOD rgdod,
|
|
LPDWORD pdwInOut, DWORD fl _THAT)
|
|
{
|
|
HRESULT hres;
|
|
PDD this;
|
|
SOMEDEVICEDATA sdd;
|
|
EnterProcR(IDirectInputDevice8::GetDeviceData,
|
|
(_ "pxpxx", pdd, cbdod, rgdod,
|
|
IsBadReadPtr(pdwInOut, cbX(DWORD)) ? 0 : *pdwInOut, fl));
|
|
|
|
/*
|
|
* Note that we do not validate the parameters.
|
|
* The reason is that GetDeviceData is an inner loop function,
|
|
* so it should be as fast as possible.
|
|
*
|
|
* Note also that it is legal to get device data after the device
|
|
* has been unacquired. This lets you "turn on the faucet" for
|
|
* a short period of time, and then parse the data out later.
|
|
*/
|
|
this = _thisPv(pdd);
|
|
|
|
#ifdef XDEBUG
|
|
hresPvT(pdd);
|
|
if ( IsBadWritePtr(pdwInOut, cbX(*pdwInOut)) ) {
|
|
RPF("ERROR %s: arg %d: invalid value; crash soon", s_szProc, 3);
|
|
}
|
|
#endif
|
|
if( cbdod == cbX(DOD) )
|
|
{
|
|
#ifdef XDEBUG
|
|
/*
|
|
* Only check the buffer if the size is correct otherwise
|
|
* we can smash the stack of the caller when we've already
|
|
* detected the error.
|
|
*/
|
|
if ( rgdod ) {
|
|
hresFullValidWritePvCb(rgdod, *pdwInOut * cbdod, 2);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Must protect with the critical section to prevent somebody from
|
|
* acquiring/unacquiring or changing the data format or calling
|
|
* another GetDeviceData while we're reading. (We must be serialized.)
|
|
*/
|
|
CDIDev_EnterCrit(this);
|
|
|
|
AssertF(CDIDev_IsConsistent(this));
|
|
|
|
if ( SUCCEEDED(hres = hresFullValidFl(fl, DIGDD_VALID, 4)) ) {
|
|
if ( this->celtBuf ) {
|
|
/*
|
|
* Don't try to read more than there possibly could be.
|
|
* This avoids overflow conditions in case celtIn is
|
|
* some absurdly huge number.
|
|
*/
|
|
sdd.celtIn = *pdwInOut;
|
|
sdd.celtOut = 0;
|
|
if ( sdd.celtIn > this->celtBuf ) {
|
|
sdd.celtIn = this->celtBuf;
|
|
}
|
|
sdd.rgdod = rgdod;
|
|
|
|
|
|
/*
|
|
* For this version of DirectInput, we do not allow
|
|
* callbacks to implement their own GetDeviceData.
|
|
*/
|
|
if ( this->pvi ) {
|
|
|
|
/*
|
|
* Reacquire is not allowed until after Win98 SE, see OSR Bug # 89958
|
|
*/
|
|
if ( this->diHacks.fReacquire &&
|
|
!this->fAcquired && (this->fOnceAcquired || this->fOnceForcedUnacquired) )
|
|
{
|
|
if ( SUCCEEDED( CDIDev_Acquire(pdd THAT_) ) ) {
|
|
// 7/18/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
|
|
RPF(" DirectInput: Auto acquired device (0x%p)", pdd);
|
|
}
|
|
}
|
|
|
|
#ifdef WINNT
|
|
if( this->fUnacquiredWhenIconic && !IsIconic(this->hwnd) ) {
|
|
if ( SUCCEEDED( CDIDev_Acquire(pdd THAT_) ) ) {
|
|
this->fUnacquiredWhenIconic = 0;
|
|
RPF(" DirectInput: Auto acquired device (0x%p) after being iconic. ", pdd);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ( (this->fAcquired && (this->pvi->fl & VIFL_ACQUIRED)) ||
|
|
(fl & DIGDD_RESIDUAL) ) {
|
|
/*
|
|
* The device object data from the driver is always a
|
|
* DX3 (<DX8) structure
|
|
*/
|
|
LPDIDEVICEOBJECTDATA_DX3 pdod, pdodHead;
|
|
DWORD celt;
|
|
|
|
/*
|
|
* Snapshot the value of pdodHead, because it can
|
|
* change asynchronously. The other fields won't
|
|
* change unless we ask for them to be changed.
|
|
*/
|
|
pdodHead = this->pvi->pHead;
|
|
|
|
/*
|
|
* Throughout, pdod points to the first unprocessed
|
|
* element.
|
|
*/
|
|
pdod = this->pvi->pTail;
|
|
|
|
/*
|
|
* If we are wrapped, handle the initial run.
|
|
*/
|
|
if ( pdodHead < this->pvi->pTail ) {
|
|
celt = (DWORD)(this->pvi->pEnd - this->pvi->pTail);
|
|
AssertF(celt);
|
|
|
|
pdod = CDIDev_GetSomeDeviceData( this, pdod, celt, &sdd );
|
|
|
|
}
|
|
|
|
/*
|
|
* Now handle the glob from pdod to pdodHead.
|
|
* Remember, pvi->pdodHead may have changed
|
|
* behind our back; use the cached value to
|
|
* ensure consistency. (If we miss data,
|
|
* it'll show up later.)
|
|
*/
|
|
|
|
AssertF(fLimpFF(sdd.celtIn, pdodHead >= pdod));
|
|
|
|
celt = (DWORD)(pdodHead - pdod);
|
|
if ( celt ) {
|
|
pdod = CDIDev_GetSomeDeviceData( this, pdod, celt, &sdd );
|
|
}
|
|
|
|
*pdwInOut = sdd.celtOut;
|
|
|
|
if ( !(fl & DIGDD_PEEK) ) {
|
|
this->pvi->pTail = pdod;
|
|
}
|
|
|
|
if ( rgdod && sdd.celtOut && this->fCook ) {
|
|
CDIDev_CookDeviceData(this, sdd.celtOut, rgdod );
|
|
}
|
|
|
|
CAssertF(S_OK == 0);
|
|
CAssertF(DI_BUFFEROVERFLOW == 1);
|
|
|
|
hres = (HRESULT)(UINT_PTR)pvExchangePpvPv(&this->pvi->fOverflow, 0);
|
|
#ifdef DEBUG_STICKY
|
|
if( hres == 1 )
|
|
{
|
|
OutputDebugString( TEXT( "Device buffer overflowed\r\n" ) );
|
|
}
|
|
if( sdd.celtOut )
|
|
{
|
|
PDOD pdoddbg;
|
|
TCHAR tszDbg[80];
|
|
|
|
wsprintf( tszDbg, TEXT("GotData %d elements: "), sdd.celtOut );
|
|
OutputDebugString( tszDbg );
|
|
for( pdoddbg=rgdod; pdoddbg<&rgdod[sdd.celtOut]; pdoddbg++ )
|
|
{
|
|
wsprintf( tszDbg, TEXT("0x%02x:x0x%08x "), pdoddbg->dwOfs, pdoddbg->dwData );
|
|
OutputDebugString( tszDbg );
|
|
}
|
|
OutputDebugString( TEXT("\r\n") );
|
|
}
|
|
#endif /* DEBUG_STICKY */
|
|
} else if (this->fAcquired && !(this->pvi->fl & VIFL_ACQUIRED)) {
|
|
RPF("ERROR %s - %s", s_szProc, "input lost");
|
|
hres = DIERR_INPUTLOST;
|
|
CDIDev_InternalUnacquire(this);
|
|
} else {
|
|
RPF("ERROR %s: %s", s_szProc,
|
|
this->hresNotAcquired == DIERR_NOTACQUIRED
|
|
? "Not acquired" : "Input lost");
|
|
hres = this->hresNotAcquired;
|
|
}
|
|
} else { /* Don't support device-side GetData yet */
|
|
hres = E_NOTIMPL;
|
|
}
|
|
} else { /* Device is not buffered */
|
|
#ifdef XDEBUG
|
|
if ( !this->fNotifiedNotBuffered ) {
|
|
this->fNotifiedNotBuffered = 1;
|
|
RPF("ERROR %s: arg %d: device is not buffered", s_szProc, 0);
|
|
}
|
|
#endif
|
|
hres = DIERR_NOTBUFFERED;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( cbdod == cbX(DIDEVICEOBJECTDATA_DX3) )
|
|
{
|
|
RPF("ERROR %s: arg %d: old size, invalid for DX8", s_szProc, 1);
|
|
}
|
|
else
|
|
{
|
|
RPF("ERROR %s: arg %d: invalid value", s_szProc, 1);
|
|
}
|
|
hres = E_INVALIDARG;
|
|
}
|
|
|
|
CDIDev_LeaveCrit(this);
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice8 | Poll |
|
|
*
|
|
* Retrieves data from polled objects on a DirectInput device.
|
|
* If the device does not require polling, then calling this
|
|
* method has no effect. If a device that requires polling
|
|
* is not polled periodically, no new data will be received
|
|
* from the device.
|
|
*
|
|
* Before a device data can be polled, the data format must
|
|
* be set via <mf IDirectInputDevice::SetDataFormat>, and
|
|
* the device must be acquired via
|
|
* <mf IDirectInputDevice::Acquire>.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DI_NOEFFECT> = <c S_FALSE>: The device does not require
|
|
* polling.
|
|
*
|
|
* <c DIERR_INPUTLOST>: Access to the device has been
|
|
* interrupted. The application should re-acquire the
|
|
* device.
|
|
*
|
|
* <c DIERR_NOTACQUIRED>: The device is not acquired.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_Poll(PV pdd _THAT)
|
|
{
|
|
HRESULT hres;
|
|
PDD this;
|
|
EnterProcR(IDirectInputDevice8::Poll, (_ "p", pdd));
|
|
|
|
/*
|
|
* Note that we do not validate the parameters.
|
|
* The reason is that Poll is an inner loop function,
|
|
* so it should be as fast as possible.
|
|
*/
|
|
#ifdef XDEBUG
|
|
hresPvT(pdd);
|
|
#endif
|
|
this = _thisPv(pdd);
|
|
|
|
/*
|
|
* Fast out: If the device doesn't require polling,
|
|
* then don't bother with the critical section or other validation.
|
|
*/
|
|
if ( this->fPolledDataFormat ) {
|
|
/*
|
|
* Must protect with the critical section to prevent somebody from
|
|
* unacquiring while we're polling.
|
|
*/
|
|
CDIDev_EnterCrit(this);
|
|
|
|
if ( this->fAcquired ) {
|
|
hres = this->pdcb->lpVtbl->Poll(this->pdcb);
|
|
|
|
} else {
|
|
hres = this->hresNotAcquired;
|
|
}
|
|
|
|
CDIDev_LeaveCrit(this);
|
|
|
|
} else {
|
|
if ( this->fAcquired ) {
|
|
hres = S_FALSE;
|
|
} else {
|
|
hres = this->hresNotAcquired;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Failing polls are really annoying so don't use ExitOleProc
|
|
*/
|
|
if( FAILED( hres ) )
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("IDirectInputDevice::Poll failed 0x%08x"), hres );
|
|
}
|
|
|
|
ExitProc();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice8 | SendDeviceData |
|
|
*
|
|
* Sends data to the device.
|
|
*
|
|
* Before device data can be sent to a device,
|
|
* the device must be acquired via
|
|
* <mf IDirectInputDevice::Acquire>.
|
|
*
|
|
* Note that no guarantees
|
|
* are made on the order in which the individual data
|
|
* elements are sent. However, data sent by
|
|
* successive calls to
|
|
* <mf IDirectInputDevice8::SendDeviceData>
|
|
* will not be interleaved.
|
|
* Furthermore, if multiple pieces of
|
|
* data are sent to the same object, it is unspecified
|
|
* which actual piece of data is sent.
|
|
*
|
|
* Consider, for example, a device which can be sent
|
|
* data in packets, each packet describing two pieces
|
|
* of information, call them A and B. Suppose the
|
|
* application attempts to send three data elements,
|
|
* "B = 2", "A = 1", and "B = 0".
|
|
*
|
|
* The actual device will be sent a single packet.
|
|
* The "A" field of the packet will contain the value 1,
|
|
* and the "B" field of the packet will be either 2 or 0.
|
|
*
|
|
* If the application wishes the data to be sent to the
|
|
* device exactly as specified, then three calls to
|
|
* <mf IDirectInputDevice8::SendDeviceData> should be
|
|
* performed, each call sending one data element.
|
|
*
|
|
* In response to the first call,
|
|
* the device will be sent a packet where the "A" field
|
|
* is blank and the "B" field contains the value 2.
|
|
*
|
|
* In response to the second call,
|
|
* the device will be sent a packet where the "A" field
|
|
* contains the value 1, and the "B" field is blank.
|
|
*
|
|
* Finally, in response to the third call,
|
|
* the device will be sent a packet where the "A" field
|
|
* is blank and the "B" field contains the value 0.
|
|
*
|
|
*
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm DWORD | cbObjectData |
|
|
*
|
|
* The size of a single <t DIDEVICEOBJECTDATA> structure in bytes.
|
|
*
|
|
* @parm IN LPCDIDEVICEOBJECTDATA | rgdod |
|
|
*
|
|
* Array of <t DIDEVICEOBJECTDATA> structures containing
|
|
* the data to send to the device. It must consist of
|
|
* *<p pdwInOut> elements.
|
|
*
|
|
* <y Note>: The <e DIDEVICEOBJECTDATA.dwOfs> field of
|
|
* the <t DIDEVICEOBJECTDATA> structure must contain the
|
|
* device object identifier (as obtained from the
|
|
* <e DIDEVICEOBJECTINSTANCE.dwType> field of the
|
|
* <t DIDEVICEOBJECTINSTANCE> sturcture) for the device
|
|
* object at which the data is directed.
|
|
*
|
|
* Furthermore, the <e DIDEVICEOBJECTDATA.dwTimeStamp>
|
|
* <e DIDEVICEOBJECTDATA.dwSequence> and
|
|
* <e DIDEVICEOBJECTDATA.uAppData> fields are
|
|
* reserved for future use and must be zero.
|
|
*
|
|
* @parm INOUT LPDWORD | pdwInOut |
|
|
*
|
|
* On entry, contains the number of elements in the array
|
|
* pointed to by <p rgdod>. On exit, contains the number
|
|
* of elements actually sent to the device.
|
|
*
|
|
* @parm DWORD | fl |
|
|
*
|
|
* Flags which control the manner in which data is sent.
|
|
* It may consist of zero or more of the following flags:
|
|
*
|
|
* <c DISDD_CONTINUE>: If this flag is set, then
|
|
* the device data sent will be overlaid upon the previously
|
|
* sent device data. Otherwise, the device data sent
|
|
* will start from scratch.
|
|
*
|
|
* For example, suppose a device supports two button outputs,
|
|
* call them A and B.
|
|
* If an application first calls
|
|
* <mf IDirectInputDevice8::SendDeviceData> passing
|
|
* "button A pressed", then
|
|
* a packet of the form "A pressed, B not pressed" will be
|
|
* sent to the device.
|
|
* If an application then calls
|
|
* <mf IDirectInputDevice8::SendDeviceData> passing
|
|
* "button B pressed" and the <c DISDD_CONTINUE> flag, then
|
|
* a packet of the form "A pressed, B pressed" will be
|
|
* sent to the device.
|
|
* However, if the application had not passed the
|
|
* <c DISDD_CONTINUE> flag, then the packet sent to the device
|
|
* would have been "A not pressed, B pressed".
|
|
*
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DIERR_INPUTLOST>: Access to the device has been
|
|
* interrupted. The application should re-acquire the
|
|
* device.
|
|
*
|
|
* <c DIERR_NOTACQUIRED>: The device is not acquired.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_SendDeviceData(PV pdd, DWORD cbdod, LPCDIDEVICEOBJECTDATA rgdod,
|
|
LPDWORD pdwInOut, DWORD fl _THAT)
|
|
{
|
|
HRESULT hres;
|
|
PDD this;
|
|
EnterProcR(IDirectInputDevice8::SendDeviceData,
|
|
(_ "pxpxx", pdd, cbdod, rgdod,
|
|
IsBadReadPtr(pdwInOut, cbX(DWORD)) ? 0 : *pdwInOut, fl));
|
|
|
|
/*
|
|
* Note that parameter validation is limited as SendDeviceData is
|
|
* intended to be an inner loop function. In practice SendDeviceData is
|
|
* rarely used so speed is not that important.
|
|
*/
|
|
#ifdef XDEBUG
|
|
hresPvT(pdd);
|
|
if ( IsBadWritePtr(pdwInOut, cbX(*pdwInOut)) ) {
|
|
RPF("ERROR %s: arg %d: invalid value; crash soon", s_szProc, 3);
|
|
}
|
|
hresFullValidReadPvCb(rgdod, cbX(*pdwInOut) * cbdod, 2);
|
|
#endif
|
|
|
|
this = _thisPv(pdd);
|
|
|
|
/*
|
|
* Must protect with the critical section to prevent somebody from
|
|
* unacquiring while we're sending data.
|
|
*/
|
|
CDIDev_EnterCrit(this);
|
|
|
|
if ( SUCCEEDED(hres = hresFullValidFl(fl, DISDD_VALID, 4)) ) {
|
|
if( cbdod == cbX(DOD) )
|
|
{
|
|
#ifdef XDEBUG
|
|
UINT iod;
|
|
LPCDIDEVICEOBJECTDATA pcdod;
|
|
|
|
for ( iod = 0, pcdod=rgdod; iod < *pdwInOut; iod++ ) {
|
|
if ( pcdod->dwTimeStamp ) {
|
|
RPF("%s: ERROR: dwTimeStamp must be zero", s_szProc);
|
|
}
|
|
if ( pcdod->dwSequence ) {
|
|
RPF("%s: ERROR: dwSequence must be zero", s_szProc);
|
|
}
|
|
if( pcdod->uAppData ){
|
|
RPF("%s: ERROR: uAppData must be zero", s_szProc);
|
|
}
|
|
pcdod++;
|
|
}
|
|
#endif
|
|
if ( this->fAcquired ) {
|
|
UINT iod;
|
|
LPDIDEVICEOBJECTDATA pdodCopy;
|
|
|
|
hres = AllocCbPpv( cbCxX( *pdwInOut, *pdodCopy ), &pdodCopy );
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
LPDIDEVICEOBJECTDATA pdod;
|
|
memcpy( pdodCopy, rgdod, cbCxX( *pdwInOut, *pdodCopy ) );
|
|
|
|
for( iod=0, pdod=pdodCopy; iod < *pdwInOut; iod++ )
|
|
{
|
|
int iobj = CDIDev_OffsetToIobj(this, pdod->dwOfs);
|
|
pdod->dwOfs = this->df.rgodf[iobj].dwType;
|
|
pdod++;
|
|
}
|
|
|
|
hres = this->pdcb->lpVtbl->SendDeviceData(this->pdcb, cbdod,
|
|
pdodCopy, pdwInOut, fl );
|
|
FreePv( pdodCopy );
|
|
}
|
|
} else {
|
|
hres = this->hresNotAcquired;
|
|
}
|
|
} else {
|
|
RPF("ERROR %s: arg %d: invalid value", s_szProc, 1);
|
|
}
|
|
}
|
|
|
|
CDIDev_LeaveCrit(this);
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
|
|
#undef sqfl
|
|
#define sqfl sqflDf
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func BOOL | CDIDev_CheckId |
|
|
*
|
|
* Verify that the item has the appropriate type.
|
|
*
|
|
* @parm DWORD | dwId |
|
|
*
|
|
* ID to locate.
|
|
*
|
|
* @parm UINT | fl |
|
|
*
|
|
* Bitmask of flags for things to validate.
|
|
*
|
|
* The <c DEVCO_TYPEMASK> fields describe what type
|
|
* of object we should be locating.
|
|
*
|
|
* The <c DEVCO_ATTRMASK> fields describe the attribute
|
|
* bits that are required.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
BOOL INLINE
|
|
CDIDev_CheckId(DWORD dwId, DWORD fl)
|
|
{
|
|
CAssertF(DIDFT_ATTRMASK == DEVCO_ATTRMASK);
|
|
|
|
return(dwId & fl & DEVCO_TYPEMASK) &&
|
|
fHasAllBitsFlFl(dwId, fl & DIDFT_ATTRMASK);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method int | CDIDev | IdToIobj |
|
|
*
|
|
* Locate an item which matches the specified ID.
|
|
*
|
|
* @cwrap PDD | this
|
|
*
|
|
* @parm DWORD | dwId |
|
|
*
|
|
* ID to locate.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns the index of the object found, or -1 on error.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
int INTERNAL
|
|
CDIDev_IdToIobj(PDD this, DWORD dwId)
|
|
{
|
|
int iobj;
|
|
|
|
/* Someday: Perf: Should have xlat table */
|
|
|
|
for ( iobj = this->df.dwNumObjs; --iobj >= 0; ) {
|
|
PODF podf = &this->df.rgodf[iobj];
|
|
if ( DIDFT_FINDMATCH(podf->dwType, dwId) ) {
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
iobj = -1;
|
|
|
|
done:;
|
|
return iobj;
|
|
|
|
}
|
|
|
|
#if 0
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev | IdToId |
|
|
*
|
|
* Convert a single <t DWORD> from an ID to an ID.
|
|
*
|
|
* This is clearly a very simple operation.
|
|
*
|
|
* It's all validation.
|
|
*
|
|
* @cwrap PDD | this
|
|
*
|
|
* @parm LPDWORD | pdw |
|
|
*
|
|
* Single item to convert.
|
|
*
|
|
* @parm UINT | fl |
|
|
*
|
|
* Bitmask of flags that govern the conversion.
|
|
* The function should look only at
|
|
* <c DEVCO_AXIS> or <c DEVCO_BUTTON>.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HRESULT INTERNAL
|
|
CDIDev_IdToId(PDD this, LPDWORD pdw, UINT fl)
|
|
{
|
|
HRESULT hres;
|
|
int iobj;
|
|
|
|
iobj = CDIDev_FindId(this, *pdw, fl);
|
|
if ( iobj >= 0 ) {
|
|
*pdw = this->df.rgodf[iobj].dwType;
|
|
hres = S_OK;
|
|
} else {
|
|
hres = E_INVALIDARG;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method int | CDIDev | OffsetToIobj |
|
|
*
|
|
* Convert a single <t DWORD> from an offset to an object index.
|
|
*
|
|
* @cwrap PDD | this
|
|
*
|
|
* @parm DWORD | dwOfs |
|
|
*
|
|
* Offset to convert.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
int INTERNAL
|
|
CDIDev_OffsetToIobj(PDD this, DWORD dwOfs)
|
|
{
|
|
int iobj;
|
|
|
|
AssertF(this->pdix);
|
|
AssertF(this->rgiobj);
|
|
|
|
if ( dwOfs < this->dwDataSize ) {
|
|
iobj = this->rgiobj[dwOfs];
|
|
if ( iobj >= 0 ) {
|
|
AssertF(this->pdix[iobj].dwOfs == dwOfs);
|
|
} else {
|
|
AssertF(iobj == -1);
|
|
}
|
|
} else {
|
|
iobj = -1;
|
|
}
|
|
|
|
return iobj;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method int | CDIDev | IobjToId |
|
|
*
|
|
* Convert an object index to an ID.
|
|
*
|
|
* @cwrap PDD | this
|
|
*
|
|
* @parm int | iobj |
|
|
*
|
|
* Single item to convert.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
DWORD INLINE
|
|
CDIDev_IobjToId(PDD this, int iobj)
|
|
{
|
|
AssertF((DWORD)iobj < this->df.dwNumObjs);
|
|
|
|
return this->df.rgodf[iobj].dwType;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method int | CDIDev | IobjToOffset |
|
|
*
|
|
* Convert an object index to an offset.
|
|
*
|
|
* @cwrap PDD | this
|
|
*
|
|
* @parm int | iobj |
|
|
*
|
|
* Single item to convert.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
DWORD INLINE
|
|
CDIDev_IobjToOffset(PDD this, int iobj)
|
|
{
|
|
AssertF((DWORD)iobj < this->df.dwNumObjs);
|
|
AssertF(this->pdix);
|
|
|
|
return this->pdix[iobj].dwOfs;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev | ConvertObjects |
|
|
*
|
|
* Convert between the ways of talking about device gizmos.
|
|
*
|
|
* Since this is used only by the force feedback subsystem,
|
|
* we also barf if the device found does not support
|
|
* force feedback.
|
|
*
|
|
* @cwrap PDD | this
|
|
*
|
|
* @parm UINT | cdw |
|
|
*
|
|
* Number of elements to convert (in-place).
|
|
*
|
|
* @parm LPDWORD | rgdw |
|
|
*
|
|
* Array of elements to convert.
|
|
*
|
|
* @parm UINT | fl |
|
|
*
|
|
* Flags that describe how to do the conversion.
|
|
*
|
|
* <c DEVCO_AXIS> or <c DEVCO_BUTTON> indicate whether
|
|
* the item being converted is an axis or button.
|
|
*
|
|
* <c DEVCO_FROM*> specifies what the existing value is.
|
|
*
|
|
* <c DEVCO_TO*> specifies what the new values should be.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_ConvertObjects(PDD this, UINT cdw, LPDWORD rgdw, UINT fl)
|
|
{
|
|
HRESULT hres;
|
|
|
|
/*
|
|
* Don't let somebody change the data format while we're
|
|
* looking at it.
|
|
*/
|
|
CDIDev_EnterCrit(this);
|
|
|
|
AssertF((fl & ~DEVCO_VALID) == 0);
|
|
|
|
if ( fLimpFF(fl & (DEVCO_FROMOFFSET | DEVCO_TOOFFSET),
|
|
this->pdix && this->rgiobj) ) {
|
|
UINT idw;
|
|
|
|
for ( idw = 0; idw < cdw; idw++ ) {
|
|
|
|
/*
|
|
* Convert from its source to an object index,
|
|
* validate the object index, then convert to
|
|
* the target.
|
|
*/
|
|
int iobj;
|
|
|
|
switch ( fl & DEVCO_FROMMASK ) {
|
|
default:
|
|
AssertF(0); /* Huh? */
|
|
case DEVCO_FROMID:
|
|
iobj = CDIDev_IdToIobj(this, rgdw[idw]);
|
|
break;
|
|
|
|
case DEVCO_FROMOFFSET:
|
|
iobj = CDIDev_OffsetToIobj(this, rgdw[idw]);
|
|
break;
|
|
}
|
|
|
|
if ( iobj < 0 ) {
|
|
hres = E_INVALIDARG; /* Invalid object */
|
|
goto done;
|
|
}
|
|
|
|
AssertF((DWORD)iobj < this->df.dwNumObjs);
|
|
|
|
if ( !CDIDev_CheckId(this->df.rgodf[iobj].dwType, fl) ) {
|
|
hres = E_INVALIDARG; /* Bad attributes */
|
|
goto done;
|
|
}
|
|
|
|
switch ( fl & DEVCO_TOMASK ) {
|
|
|
|
default:
|
|
AssertF(0); /* Huh? */
|
|
case DEVCO_TOID:
|
|
rgdw[idw] = CDIDev_IobjToId(this, iobj);
|
|
break;
|
|
|
|
case DEVCO_TOOFFSET:
|
|
rgdw[idw] = CDIDev_IobjToOffset(this, iobj);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
hres = S_OK;
|
|
|
|
done:;
|
|
|
|
} else {
|
|
RPF("ERROR: Must have a data format to use offsets");
|
|
hres = E_INVALIDARG;
|
|
}
|
|
|
|
CDIDev_LeaveCrit(this);
|
|
return hres;
|
|
}
|
|
|
|
|