/***************************************************************************** * * DIDevDf.c * * Copyright (c) 1996 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" 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(IDirectInputDevice::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(IDirectInputDevice::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(IDirectInputDevice::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 counts as * a match when parsing the data format. * * @parm PCGUID | pguidSrc | * * The to check. * * @parm PCGUID | pguidDst | * * The 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) { return IsEqualGUID(pguidSrc, &GUID_Null) || IsEqualGUID(pguidSrc, pguidDst); } /***************************************************************************** * * @doc INTERNAL * * @method BOOL | CDIDev | IsMatchingUsage | * * Helper function that checks if a * counts as a match when parsing the data format. * * @parm DWORD | dwUsage | * * The 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 * is null, then the field is a wildcard. * * If the specifies * , 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. * * Note that we need to be careful how we check, because * DX 3.0 and DX 5.0 uses different masks. (DX 5.0 needs * 16 bits of instance data to accomodate HID devices.) */ && fLimpFF((podf->dwType & this->didftInstance) != this->didftInstance, fEqualMaskFlFl(this->didftInstance, 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. * * = : The operation completed successfully. * * = : The *

parameter is not a valid pointer. * * : 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; DWORD dwDataSize; #ifdef DEBUG EnterProc(CDIDev_ParseDataFormat, (_ "pp", this, lpdf)); #else EnterProcR(IDirectInputDevice::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 the device is cooked, then we stash the client offset * into the high word of the VxD data, so it had better fit into * a word... */ dwDataSize = min(lpdf->dwDataSize, 0x00010000); 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, TEXT("Begin parse data format")); for ( iobj = 0; iobj < lpdf->dwNumObjs; iobj++ ) { PCODF podf = &lpdf->rgodf[iobj]; SquirtSqflPtszV(sqflDf, TEXT("Object %2d: offset %08x"), iobj, podf->dwOfs); /* * Note that the podf->dwOfs < 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 < dwDataSize ) { int iobjDev = CDIDev_FindDeviceObjectFormat(this, podf, pdix); /* Hack for pre DX6 apps that only look for a Z axis, * newer USB devices use GUID_Slider for the same functionality. * */ if ( podf->pguid != 0x0 // Looking for matching GUID && this->dwVersion < 0x600 // Only for Dx version < 0x600 && iobjDev == -1 // Did not find default mapping && IsEqualGUID(podf->pguid, &GUID_ZAxis) ) // Looking for GUID_ZAxis { ODF odf = lpdf->rgodf[iobj]; // Make a copy of the object data format odf.pguid = &GUID_Slider; // Substitute Slider for Z axis iobjDev = CDIDev_FindDeviceObjectFormat(this, &odf, 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; } } if ( this->fCook ) { vdf.pDfOfs[podfFound->dwOfs] = (DWORD)DICOOK_DFOFSFROMOFSID(podf->dwOfs, podfFound->dwType); } else { vdf.pDfOfs[podfFound->dwOfs] = podf->dwOfs; } pdix[iobjDev].dwOfs = podf->dwOfs; rgiobj[podf->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 ) { SquirtSqflPtszV(sqflDf, TEXT("Could not set DIPROP_ENABLEREPORTID for offset %d"), iobj); hres = S_OK; } } else if ( podf->dwType & DIDFT_OPTIONAL ) { SquirtSqflPtszV(sqflDf, 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: Offset out of range in data format", s_szProc); } else if ( podf->dwOfs >= dwDataSize ) { RPF("%s: Data format cannot exceed 64K", s_szProc); } 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; } #ifdef BUGGY_DX7_WINNT /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CDIDev | ParseDataFormatInternal | * * Parse the data format passed by CDIDev_Intialize and * convert it into a format that we can use to translate * the device data into application data. Used on WINNT only. * * @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. * * = : The operation completed successfully. * * = : The *

parameter is not a valid pointer. * * : Cannot change the data format while the * device is acquired. * * @devnotes: * This function is originaly wrote to fix manbug: 41464. * Boarder Zone gets object's data offset before it calls SetDataFormat, * so Dinput returns it internal data offsets. But it uses them as user data * offset, which causes the bug. * To fix it, we call this function in CDIDev_Initialize with c_dfDIJoystick, * which is used by many games. When an application ask for object info, * we check if an user data format has been set, if not, we will assume user * uses c_dfDIJoystick, hence return the data offset based on it. * This function will only be called if application uses dinput (version < 0x700) * && (version != 0x5B2). * *****************************************************************************/ HRESULT CDIDev_ParseDataFormatInternal(PDD this, const DIDATAFORMAT *lpdf) { PDIXLAT pdix; PINT rgiobj = NULL; HRESULT hres; #ifdef DEBUG EnterProc(CDIDev_ParseDataFormat2, (_ "pp", this, lpdf)); #endif AssertF(this->pdix2 == 0); if( SUCCEEDED(hres = AllocCbPpv(cbCxX(this->df.dwNumObjs, DIXLAT), &pdix)) && SUCCEEDED(hres = AllocCbPpv(cbCdw(lpdf->dwDataSize), &rgiobj))) { UINT iobj; /* * Pre-init all the translation tags to -1, * which means "not in use" */ memset(pdix, 0xFF, cbCxX(this->df.dwNumObjs, DIXLAT)); memset(rgiobj, 0xFF, cbCdw(lpdf->dwDataSize)); 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; } else if ( podf->dwType & DIDFT_OPTIONAL ) { //do nothing } 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 this->pdix2 = pdix; pdix = 0; this->rgiobj2 = rgiobj; rgiobj = 0; this->dwDataSize2 = lpdf->dwDataSize; hres = S_OK; } else { /* Out of memory */ hres = ERROR_NOT_ENOUGH_MEMORY; } done:; FreePpv(&pdix); FreePpv(&rgiobj); ExitOleProc(); return hres; } #endif //BUGGY_DX7_WINNT /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CDIDev | OptimizeDataFormat | * * Study the parsed data format to determine whether we can * used an optimized 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 = -1; /* Not yet known */ ibMin = 0xFFFFFFFF; ibMax = 0; /* * ISSUE-2001/03/29-timgill Need to change data sentinel value * -1 is not a valid sentinel; we might validly * get data at an offset of -1. */ 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 != -1, 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 != -1 ) { /* 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. * * = : The operation completed successfully. * * = : The *

parameter is not a valid pointer. * * : 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(IDirectInputDevice::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); #if DIRECTINPUT_VERSION >= 0x04F0 if ( this->dwVersion == 0 ) { RPF("Warning: IDirectInputDevice::Initialize not called; " "assuming version 3.0"); } #endif 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; } /***************************************************************************** * * @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 , and * the device must be acquired via * . * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm DWORD | cbData | * * The size of the buffer pointed to by

, 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 . * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : 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, * will return * . When data becomes available, the event * notification handle will be signalled. * * : The device is not acquired. * * : Access to the device has been * interrupted. The application should re-acquire the * device. * * = : The *

parameter is not a valid pointer or * the

parameter does not match the data size * set by a previous call to . * *****************************************************************************/ extern STDMETHODIMP CDIDev_Acquire(PV pdd _THAT); STDMETHODIMP CDIDev_GetDeviceState(PV pdd, DWORD cbDataSize, LPVOID pvData _THAT) { HRESULT hres; PDD this; EnterProcR(IDirectInputDevice::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 (DIRECTINPUT_VERSION > 0x061A) 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 #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; } #if DIRECTINPUT_VERSION > 0x0300 /***************************************************************************** * * @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 UINT | 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, UINT cdod, PDOD rgdod) { UINT idod; EnterProc(IDirectInputDevice::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); } for ( idod = 0; idod < cdod; idod++ ) { rgdod[idod].dwOfs = DICOOK_OFSFROMDFOFS(rgdod[idod].dwOfs); } ExitProc(); } #endif /***************************************************************************** * * @doc INTERNAL * * @struct SOMEDEVICEDATA | * * Instance data used by . * * @field DWORD | celtIn | * * Number of elements remaining in output buffer. * * @field PDOD | rgdod | * * Output buffer for data elements, or 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 * . * * @returns * * Returns a pointer to the first uncopied item. * *****************************************************************************/ PDOD INTERNAL CDIDev_GetSomeDeviceData(PDD this, PDOD pdod, DWORD celt, PSOMEDEVICEDATA psdd) { EnterProc(IDirectInputDevice::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; } /* * Copy the elements (if requested) and update the state. * Note that celt might be zero; fortunately, memcpy does * the right thing. */ if ( psdd->rgdod ) { memcpy(psdd->rgdod, pdod, cbCxX(celt, DOD)); psdd->rgdod += celt; } psdd->celtOut += celt; psdd->celtIn -= celt; pdod += celt; if ( pdod == this->pvi->pEnd ) { pdod = this->pvi->pBuffer; } ExitProcX(celt); return pdod; } /***************************************************************************** * * @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 , setting the * property to the desired size * of the input buffer. * * Before device data can be obtained, the data format must * be set via , and * the device must be acquired via * . * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm DWORD | cbObjectData | * * The size of a single structure in bytes. * * @parm OUT LPDIDEVICEOBJECTDATA | rgdod | * * Array of structures to receive * the buffered data. It must consist of * *

elements. * * If this parameter is , 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

. 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: * * : Do not remove the items from the buffer. * A subsequent will * read the same data. Normally, data is removed from the * buffer after it is read. * ;begin_internal dx4 * : 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 * or . ;end_internal dx4 * * @returns * * = : All data were retrieved * successfully. Note that the application needs to check * the output value of *

to determine whether * and how much data was retrieved: The value may be zero, * indicating that the buffer was empty. * * = : 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 * call after the buffer has overflowed. Note that this is * a success status code. * * : The device is not acquired. * * : Access to the device has been * interrupted. The application should re-acquire the * device. * * = : One or more * parameters was invalid. A common cause for this is * neglecting to set a buffer size. * * : The device is not buffered. * Set the 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 for the

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 for the

, 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 for the

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(IDirectInputDevice::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. */ #ifdef XDEBUG hresPvT(pdd); if ( IsBadWritePtr(pdwInOut, cbX(*pdwInOut)) ) { RPF("ERROR %s: arg %d: invalid value; crash soon", s_szProc, 3); } if ( rgdod ) { hresFullValidWritePvCb(rgdod, cbCxX(*pdwInOut, DOD), 2); } #endif this = _thisPv(pdd); /* * 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 ( cbdod == cbX(DOD) ) { 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 (DIRECTINPUT_VERSION > 0x061A) 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 #endif if ( (this->fAcquired && (this->pvi->fl & VIFL_ACQUIRED)) || (fl & DIGDD_RESIDUAL) ) { LPDIDEVICEOBJECTDATA 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 DIRECTINPUT_VERSION > 0x0300 if ( rgdod && sdd.celtOut && this->fCook ) { CDIDev_CookDeviceData(this, sdd.celtOut, rgdod); } #endif 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 #if DIRECTINPUT_VERSION > 0x0300 hres = DIERR_NOTBUFFERED; #else hres = E_INVALIDARG; #endif } } else { RPF("ERROR %s: arg %d: invalid value", s_szProc, 1); } } CDIDev_LeaveCrit(this); return hres; } #ifdef IDirectInputDevice2Vtbl /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice2 | 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 , and * the device must be acquired via * . * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : The device does not require * polling. * * : Access to the device has been * interrupted. The application should re-acquire the * device. * * : The device is not acquired. * *****************************************************************************/ STDMETHODIMP CDIDev_Poll(PV pdd _THAT) { HRESULT hres; PDD this; EnterProcR(IDirectInputDevice::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 ) { if( this->dwVersion < 0x05B2 ) { hres = S_OK; } else { hres = S_FALSE; } } else { hres = this->hresNotAcquired; } } ExitOleProc(); return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice2 | SendDeviceData | * * Sends data to the device. * * Before device data can be sent to a device, * the device must be acquired via * . * * Note that no guarantees * are made on the order in which the individual data * elements are sent. However, data sent by * successive calls to * * 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 * 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 structure in bytes. * * @parm IN LPCDIDEVICEOBJECTDATA | rgdod | * * Array of structures containing * the data to send to the device. It must consist of * *

elements. * * : The field of * the structure must contain the * device object identifier (as obtained from the * field of the * sturcture) for the device * object at which the data is directed. * * Furthermore, the * and 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

. 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: * * : 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 * 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 * passing * "button B pressed" and the 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 * 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. * * = : The operation completed successfully. * * : Access to the device has been * interrupted. The application should re-acquire the * device. * * : The device is not acquired. * *****************************************************************************/ STDMETHODIMP CDIDev_SendDeviceData(PV pdd, DWORD cbdod, PCDOD rgdod, LPDWORD pdwInOut, DWORD fl _THAT) { HRESULT hres; PDD this; EnterProcR(IDirectInputDevice::SendDeviceData, (_ "pxpxx", pdd, cbdod, rgdod, IsBadReadPtr(pdwInOut, cbX(DWORD)) ? 0 : *pdwInOut, fl)); /* * Note that we do not validate the parameters. * The reason is that SendDeviceData is an inner loop function, * so it should be as fast as possible. */ #ifdef XDEBUG hresPvT(pdd); if ( IsBadWritePtr(pdwInOut, cbX(*pdwInOut)) ) { RPF("ERROR %s: arg %d: invalid value; crash soon", s_szProc, 3); } hresFullValidReadPvCb(rgdod, cbCxX(*pdwInOut, DOD), 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; for ( iod = 0; iod < *pdwInOut; iod++ ) { if ( rgdod[iod].dwTimeStamp ) { RPF("%s: ERROR: dwTimeStamp must be zero", s_szProc); } if ( rgdod[iod].dwSequence ) { RPF("%s: ERROR: dwSequence must be zero", s_szProc); } } #endif if ( this->fAcquired ) { UINT iod; for ( iod=0; iod < *pdwInOut; iod++ ) { int iobj = CDIDev_OffsetToIobj(this, rgdod[iod].dwOfs); LPDWORD pdw = (LPDWORD)&rgdod[iod].dwOfs; *pdw = this->df.rgodf[iobj].dwType; } hres = this->pdcb->lpVtbl->SendDeviceData(this->pdcb, rgdod, pdwInOut, fl); } else { hres = this->hresNotAcquired; } } else { RPF("ERROR %s: arg %d: invalid value", s_szProc, 1); } } CDIDev_LeaveCrit(this); ExitOleProc(); return hres; } #endif /***************************************************************************** * * @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 fields describe what type * of object we should be locating. * * The 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 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 * or . * *****************************************************************************/ 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 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. * * or indicate whether * the item being converted is an axis or button. * * specifies what the existing value is. * * 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; }