Source code of Windows XP (NT5)
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

  1. /*****************************************************************************
  2. *
  3. * DIDevDf.c
  4. *
  5. * Copyright (c) 1996 - 2000 Microsoft Corporation. All Rights Reserved.
  6. *
  7. * Abstract:
  8. *
  9. * The part of IDirectInputDevice that worries about
  10. * data formats and reading device data.
  11. *
  12. *****************************************************************************/
  13. #include "dinputpr.h"
  14. #include "didev.h"
  15. #undef sqfl
  16. #define sqfl sqflDf
  17. int INTERNAL
  18. CDIDev_OffsetToIobj(PDD this, DWORD dwOfs);
  19. /*****************************************************************************
  20. *
  21. * @doc INTERNAL
  22. *
  23. * @func HRESULT | CDIDev_GetAbsDeviceState |
  24. *
  25. * Get the absolute device state.
  26. *
  27. * @parm OUT LPVOID | pvData |
  28. *
  29. * Application-provided output buffer.
  30. *
  31. *****************************************************************************/
  32. STDMETHODIMP
  33. CDIDev_GetAbsDeviceState(PDD this, LPVOID pvData)
  34. {
  35. return this->pdcb->lpVtbl->GetDeviceState(this->pdcb, pvData);
  36. }
  37. /*****************************************************************************
  38. *
  39. * @doc INTERNAL
  40. *
  41. * @func HRESULT | CDIDev_GetRelDeviceState |
  42. *
  43. * Get the relative device state.
  44. *
  45. * @parm OUT LPVOID | pvData |
  46. *
  47. * Application-provided output buffer.
  48. *
  49. *****************************************************************************/
  50. STDMETHODIMP
  51. CDIDev_GetRelDeviceState(PDD this, LPVOID pvData)
  52. {
  53. HRESULT hres;
  54. hres = this->pdcb->lpVtbl->GetDeviceState(this->pdcb, pvData);
  55. if ( SUCCEEDED(hres) ) {
  56. UINT iaxis;
  57. AssertF(fLimpFF(this->cAxes, this->pvLastBuffer && this->rgdwAxesOfs));
  58. /*
  59. * For each axis, replace the app's buffer with the delta,
  60. * and save the old value.
  61. */
  62. for ( iaxis = 0; iaxis < this->cAxes; iaxis++ ) {
  63. LONG UNALIGNED *plApp = pvAddPvCb(pvData, this->rgdwAxesOfs[iaxis]);
  64. LONG UNALIGNED *plLast = pvAddPvCb(this->pvLastBuffer,
  65. this->rgdwAxesOfs[iaxis]);
  66. LONG lNew = *plApp;
  67. *plApp -= *plLast;
  68. *plLast = lNew;
  69. }
  70. hres = S_OK;
  71. }
  72. return hres;
  73. }
  74. /*****************************************************************************
  75. *
  76. * @doc INTERNAL
  77. *
  78. * @func HRESULT | CDIDev_GetDeviceStateSlow |
  79. *
  80. * Obtains data from the DirectInput device the slow way.
  81. *
  82. * Read the data into the private buffer, then copy it
  83. * bit by bit into the application's buffer.
  84. *
  85. * @parm OUT LPVOID | lpvData |
  86. *
  87. * Application-provided output buffer.
  88. *
  89. *****************************************************************************/
  90. STDMETHODIMP
  91. CDIDev_GetDeviceStateSlow(PDD this, LPVOID pvData)
  92. {
  93. HRESULT hres;
  94. EnterProcR(IDirectInputDevice8::GetDeviceStateSlow, (_ "pp", this, pvData));
  95. AssertF(this->diopt == dioptNone);
  96. AssertF(this->pvBuffer);
  97. AssertF(this->pdcb);
  98. hres = this->GetDeviceState(this, this->pvBuffer);
  99. if ( SUCCEEDED(hres) ) {
  100. int iobj;
  101. ZeroMemory(pvData, this->dwDataSize);
  102. for ( iobj = this->df.dwNumObjs; --iobj >= 0; ) {
  103. if ( this->pdix[iobj].dwOfs != 0xFFFFFFFF ) { /* Data was requested */
  104. DWORD UNALIGNED *pdwOut = pvAddPvCb(pvData, this->pdix[iobj].dwOfs);
  105. DWORD UNALIGNED *pdwIn = pvAddPvCb(this->pvBuffer, this->df.rgodf[iobj].dwOfs);
  106. if ( this->df.rgodf[iobj].dwType & DIDFT_DWORDOBJS ) {
  107. *pdwOut = *pdwIn;
  108. } else {
  109. *(LPBYTE)pdwOut = *(LPBYTE)pdwIn;
  110. }
  111. }
  112. }
  113. }
  114. ExitOleProc();
  115. return hres;
  116. }
  117. /*****************************************************************************
  118. *
  119. * @doc INTERNAL
  120. *
  121. * @func HRESULT | CDIDev_GetDeviceStateMatched |
  122. *
  123. * Obtains data from the DirectInput device in the case
  124. * where the data formats are matched.
  125. *
  126. * Read the data into the private buffer, then block copy it
  127. * into the application's buffer.
  128. *
  129. * @parm OUT LPVOID | lpvData |
  130. *
  131. * Application-provided output buffer.
  132. *
  133. *****************************************************************************/
  134. STDMETHODIMP
  135. CDIDev_GetDeviceStateMatched(PDD this, LPVOID pvData)
  136. {
  137. HRESULT hres;
  138. EnterProcR(IDirectInputDevice8::GetDeviceStateMatched, (_ "pp", this, pvData));
  139. AssertF(this->diopt == dioptMatch);
  140. AssertF(this->pvBuffer);
  141. AssertF(this->pdcb);
  142. hres = this->GetDeviceState(this, this->pvBuffer);
  143. if ( SUCCEEDED(hres) ) {
  144. /*
  145. * To keep keyboard clients happy: Zero out the fore and aft.
  146. * No need to optimize the perfect match case, because that
  147. * gets a different optimization level.
  148. */
  149. ZeroMemory(pvData, this->dwDataSize);
  150. memcpy(pvAddPvCb(pvData, this->ibDelta + this->ibMin),
  151. pvAddPvCb(this->pvBuffer, this->ibMin), this->cbMatch);
  152. }
  153. ExitOleProc();
  154. return hres;
  155. }
  156. /*****************************************************************************
  157. *
  158. * @doc INTERNAL
  159. *
  160. * @func HRESULT | CDIDev_GetDeviceStateDirect |
  161. *
  162. * Obtains data from the DirectInput device in the case
  163. * where we can read the data directly into the client buffer.
  164. *
  165. * @parm OUT LPVOID | lpvData |
  166. *
  167. * Application-provided output buffer.
  168. *
  169. *****************************************************************************/
  170. STDMETHODIMP
  171. CDIDev_GetDeviceStateDirect(PDD this, LPVOID pvData)
  172. {
  173. HRESULT hres;
  174. EnterProcR(IDirectInputDevice8::GetDeviceStateDirect, (_ "pp", this, pvData));
  175. AssertF(this->diopt == dioptDirect);
  176. AssertF(!this->pvBuffer);
  177. AssertF(this->pdcb);
  178. /*
  179. * To keep keyboard clients happy: Zero out the fore and aft.
  180. */
  181. ZeroBuf(pvData, this->dwDataSize);
  182. hres = this->GetDeviceState(this, pvAddPvCb(pvData, this->ibDelta));
  183. ExitOleProc();
  184. return hres;
  185. }
  186. /*****************************************************************************
  187. *
  188. * @doc INTERNAL
  189. *
  190. * @func HRESULT | CDIDev_GetDeviceStateEqual |
  191. *
  192. * Obtains data from the DirectInput device in the case
  193. * where the two data formats are completely identical.
  194. *
  195. * @parm OUT LPVOID | lpvData |
  196. *
  197. * Application-provided output buffer.
  198. *
  199. *****************************************************************************/
  200. STDMETHODIMP
  201. CDIDev_GetDeviceStateEqual(PDD this, LPVOID pvData)
  202. {
  203. HRESULT hres;
  204. EnterProcR(IEqualInputDevice::GetDeviceStateEqual, (_ "pp", this, pvData));
  205. AssertF(this->diopt == dioptEqual);
  206. AssertF(this->ibDelta == 0);
  207. AssertF(this->dwDataSize == this->df.dwDataSize);
  208. AssertF(!this->pvBuffer);
  209. AssertF(this->pdcb);
  210. /*
  211. * Note that this->ibMin is not necessarily zero if the device
  212. * data format doesn't begin at zero (which keyboards don't).
  213. */
  214. hres = this->GetDeviceState(this, pvData);
  215. ExitOleProc();
  216. return hres;
  217. }
  218. /*****************************************************************************
  219. *
  220. * @doc INTERNAL
  221. *
  222. * @method BOOL | CDIDev | IsMatchingGUID |
  223. *
  224. * Helper function that checks if a <t GUID> counts as
  225. * a match when parsing the data format.
  226. *
  227. * @parm PCGUID | pguidSrc |
  228. *
  229. * The <t GUID> to check.
  230. *
  231. * @parm PCGUID | pguidDst |
  232. *
  233. * The <t GUID> it should match.
  234. *
  235. * @returns
  236. *
  237. * Nonzero if this counts as a success.
  238. *
  239. *****************************************************************************/
  240. #pragma BEGIN_CONST_DATA
  241. GUID GUID_Null; /* A zero-filled guid */
  242. #pragma END_CONST_DATA
  243. BOOL INLINE
  244. CDIDev_IsMatchingGUID(PDD this, PCGUID pguidSrc, PCGUID pguidDst)
  245. {
  246. UNREFERENCED_PARAMETER( this );
  247. return IsEqualGUID(pguidSrc, &GUID_Null) ||
  248. IsEqualGUID(pguidSrc, pguidDst);
  249. }
  250. /*****************************************************************************
  251. *
  252. * @doc INTERNAL
  253. *
  254. * @method BOOL | CDIDev | IsMatchingUsage |
  255. *
  256. * Helper function that checks if a <f DIMAKEUSAGEDWORD>
  257. * counts as a match when parsing the data format.
  258. *
  259. * @parm DWORD | dwUsage |
  260. *
  261. * The <f DIMAKEUSAGEDWORD> to check.
  262. *
  263. * @parm int | iobj |
  264. *
  265. * The index of hte object to check for a match.
  266. *
  267. * @returns
  268. *
  269. * Nonzero if this counts as a success.
  270. *
  271. *****************************************************************************/
  272. BOOL INLINE
  273. CDIDev_IsMatchingUsage(PDD this, DWORD dwUsage, int iobj)
  274. {
  275. AssertF(this->pdcb);
  276. return dwUsage == this->pdcb->lpVtbl->GetUsage(this->pdcb, iobj);
  277. }
  278. /*****************************************************************************
  279. *
  280. * @doc INTERNAL
  281. *
  282. * @method int | CDIDev | FindDeviceObjectFormat |
  283. *
  284. * Search the device object format table for the one that
  285. * matches the guid in question.
  286. *
  287. * @parm PCODF | podf |
  288. *
  289. * The object to locate. If the <e DIOBJECTDATAFORMAT.rguid>
  290. * is null, then the field is a wildcard.
  291. *
  292. * If the <e DIOBJECTDATAFORMAT.dwType> specifies
  293. * <c DIDFT_ANYINSTANCE>, then any instance will be accepted.
  294. *
  295. * @parm PDIXLAT | pdix |
  296. *
  297. * The partial translation table so far. This is used to find
  298. * an empty slot in case of wildcards.
  299. *
  300. * @returns
  301. *
  302. * Returns the index of the object that matches, or -1 if
  303. * the object is not supported by the device.
  304. *
  305. * Someday: Should fall back to best match if types don't match.
  306. *
  307. *****************************************************************************/
  308. int INTERNAL
  309. CDIDev_FindDeviceObjectFormat(PDD this, PCODF podf, PDIXLAT pdix)
  310. {
  311. PCODF podfD; /* The format in the device */
  312. UINT iobj;
  313. /*
  314. * We must count upwards, so that first-fit chooses the smallest one.
  315. */
  316. for ( iobj = 0; iobj < this->df.dwNumObjs; iobj++ ) {
  317. podfD = &this->df.rgodf[iobj];
  318. if (
  319. /*
  320. * Type needs to match.
  321. *
  322. * Note that works for output-only actuators:
  323. * Since you cannot read from an output-only
  324. * actuator, you can't put it in a data format.
  325. *
  326. */
  327. (podf->dwType & DIDFT_TYPEVALID & podfD->dwType)
  328. /*
  329. * Attributes need to match.
  330. */
  331. && fHasAllBitsFlFl(podfD->dwType, podf->dwType & DIDFT_ATTRVALID)
  332. /*
  333. * Slot needs to be empty.
  334. */
  335. && pdix[iobj].dwOfs == 0xFFFFFFFF
  336. /*
  337. * "If there is a guid/usage, it must match."
  338. *
  339. * If pguid is NULL, then the match is vacuous.
  340. *
  341. * If DIDOI_GUIDISUSAGE is clear, then pguid points to
  342. * a real GUID. GUID_NULL means "Don't care" and matches
  343. * anything. Otherwise, it must match the actual GUID.
  344. *
  345. * If DIDOI_GUIDISUSAGE is set, then pguid is really
  346. * a DIMAKEUSAGEDWORD of the usage and usage page,
  347. * which we compare against the same in the object.
  348. */
  349. && (podf->pguid == 0 ||
  350. ((podf->dwFlags & DIDOI_GUIDISUSAGE) ?
  351. CDIDev_IsMatchingUsage(this, (DWORD)(UINT_PTR)podf->pguid, iobj) :
  352. CDIDev_IsMatchingGUID(this, podf->pguid, podfD->pguid)))
  353. /*
  354. * If there is an instance number, it must match.
  355. */
  356. && fLimpFF((podf->dwType & DIDFT_ANYINSTANCE) !=
  357. DIDFT_ANYINSTANCE,
  358. fEqualMaskFlFl(DIDFT_ANYINSTANCE,
  359. podf->dwType, podfD->dwType))
  360. /*
  361. * If there is an aspect, it must match.
  362. *
  363. * If the device data format doesn't specify an aspect,
  364. * then that counts as a free match too.
  365. */
  366. && fLimpFF((podf->dwFlags & DIDOI_ASPECTMASK) &&
  367. (podfD->dwFlags & DIDOI_ASPECTMASK),
  368. fEqualMaskFlFl(DIDOI_ASPECTMASK,
  369. podf->dwFlags, podfD->dwFlags))
  370. ) { /* Criterion matches, woo-hoo */
  371. return iobj;
  372. }
  373. }
  374. return -1;
  375. }
  376. /*****************************************************************************
  377. *
  378. * @doc INTERNAL
  379. *
  380. * @method HRESULT | CDIDev | ParseDataFormat |
  381. *
  382. * Parse the data format passed by the application and
  383. * convert it into a format that we can use to translate
  384. * the device data into application data.
  385. *
  386. * @parm IN LPDIDATAFORMAT | lpdf |
  387. *
  388. * Points to a structure that describes the format of the data
  389. * the DirectInputDevice should return.
  390. *
  391. * @returns
  392. *
  393. * Returns a COM error code. The following error codes are
  394. * intended to be illustrative and not necessarily comprehensive.
  395. *
  396. * <c DI_OK> = <c S_OK>: The operation completed successfully.
  397. *
  398. * <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
  399. * <p lpvData> parameter is not a valid pointer.
  400. *
  401. * <c DIERR_ACQUIRED>: Cannot change the data format while the
  402. * device is acquired.
  403. *
  404. *
  405. *****************************************************************************/
  406. STDMETHODIMP
  407. CDIDev_ParseDataFormat(PDD this, const DIDATAFORMAT *lpdf)
  408. {
  409. PDIXLAT pdix;
  410. // Prefix: Whistler 45081
  411. PINT rgiobj = NULL;
  412. HRESULT hres;
  413. DIPROPDWORD dipdw;
  414. VXDDATAFORMAT vdf;
  415. #ifdef DEBUG
  416. EnterProc(CDIDev_ParseDataFormat, (_ "pp", this, lpdf));
  417. #else
  418. EnterProcR(IDirectInputDevice8::SetDataFormat, (_ "pp", this, lpdf));
  419. #endif
  420. /*
  421. * Caller should've nuked the old translation table.
  422. */
  423. AssertF(this->pdix == 0);
  424. AssertF(this->rgiobj == 0);
  425. AssertF(this->cdwPOV == 0);
  426. vdf.cbData = this->df.dwDataSize;
  427. vdf.pDfOfs = 0;
  428. if ( SUCCEEDED(hres = AllocCbPpv(cbCxX(this->df.dwNumObjs, DIXLAT), &pdix)) &&
  429. SUCCEEDED(hres = AllocCbPpv(cbCdw(this->df.dwDataSize), &vdf.pDfOfs)) &&
  430. SUCCEEDED(hres = AllocCbPpv(cbCdw(lpdf->dwDataSize), &rgiobj)) &&
  431. SUCCEEDED(hres =
  432. ReallocCbPpv(cbCdw(lpdf->dwNumObjs), &this->rgdwPOV)) ) {
  433. UINT iobj;
  434. /*
  435. * Pre-init all the translation tags to -1,
  436. * which means "not in use"
  437. */
  438. memset(pdix, 0xFF, cbCxX(this->df.dwNumObjs, DIXLAT));
  439. memset(vdf.pDfOfs, 0xFF, cbCdw(this->df.dwDataSize));
  440. memset(rgiobj, 0xFF, cbCdw(lpdf->dwDataSize));
  441. SquirtSqflPtszV(sqflDf | sqflVerbose, TEXT("Begin parse data format"));
  442. for ( iobj = 0; iobj < lpdf->dwNumObjs; iobj++ ) {
  443. PCODF podf = &lpdf->rgodf[iobj];
  444. SquirtSqflPtszV(sqflDf | sqflVerbose, TEXT("Object %2d: offset %08x"),
  445. iobj, podf->dwOfs);
  446. /*
  447. * Note that the podf->dwOfs < lpdf->dwDataSize test is safe
  448. * even for DWORD objects, since we also check that both
  449. * values are DWORD multiples.
  450. */
  451. if ( ((podf->dwFlags & DIDOI_GUIDISUSAGE) ||
  452. fLimpFF(podf->pguid,
  453. SUCCEEDED(hres = hresFullValidGuid(podf->pguid, 1)))) &&
  454. podf->dwOfs < lpdf->dwDataSize ) {
  455. int iobjDev = CDIDev_FindDeviceObjectFormat(this, podf, pdix);
  456. if ( iobjDev != -1 ) {
  457. PCODF podfFound = &this->df.rgodf[iobjDev];
  458. if ( podfFound->dwType & DIDFT_DWORDOBJS ) {
  459. if ( (podf->dwOfs & 3) == 0 ) {
  460. } else {
  461. RPF("%s: Dword objects must be aligned", s_szProc);
  462. goto fail;
  463. }
  464. }
  465. pdix[iobjDev].dwOfs = podf->dwOfs;
  466. rgiobj[podf->dwOfs] = iobjDev;
  467. vdf.pDfOfs[podfFound->dwOfs] = iobjDev;
  468. if ( podfFound->dwFlags & DIDOI_POLLED ) {
  469. this->fPolledDataFormat = TRUE;
  470. }
  471. dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  472. dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  473. dipdw.diph.dwObj = podfFound->dwType;
  474. dipdw.diph.dwHow = DIPH_BYID;
  475. dipdw.dwData = 0x1; // Enable this report ID
  476. hres = CDIDev_RealSetProperty(this, DIPROP_ENABLEREPORTID, &dipdw.diph);
  477. if ( hres == E_NOTIMPL )
  478. {
  479. hres = S_OK;
  480. }
  481. else if( FAILED( hres ) )
  482. {
  483. SquirtSqflPtszV(sqflDf | sqflError,
  484. TEXT("Could not set DIPROP_ENABLEREPORTID for offset %d"),
  485. iobj);
  486. }
  487. } else if ( podf->dwType & DIDFT_OPTIONAL ) {
  488. SquirtSqflPtszV(sqflDf | sqflVerbose,
  489. TEXT("Object %2d: Skipped (optional)"),
  490. iobj);
  491. /*
  492. * We need to remember where the failed POVs live
  493. * so we can neutralize them in GetDeviceState().
  494. */
  495. if ( podf->dwType & DIDFT_POV ) {
  496. AssertF(this->cdwPOV < lpdf->dwNumObjs);
  497. this->rgdwPOV[this->cdwPOV++] = podf->dwOfs;
  498. }
  499. } else {
  500. RPF("%s: Format not compatible with device", s_szProc);
  501. goto fail;
  502. }
  503. } else {
  504. if ( podf->dwOfs >= lpdf->dwDataSize ) {
  505. RPF("%s: rgodf[%d].dwOfs of 0x%08x out of range in data format",
  506. s_szProc, iobj, podf->dwOfs );
  507. }
  508. fail:;
  509. hres = E_INVALIDARG;
  510. goto done;
  511. }
  512. }
  513. #ifdef DEBUG
  514. /*
  515. * Double-check the lookup tables just to preserve our sanity.
  516. */
  517. {
  518. UINT dwOfs;
  519. for ( dwOfs = 0; dwOfs < lpdf->dwDataSize; dwOfs++ ) {
  520. if ( rgiobj[dwOfs] >= 0 ) {
  521. AssertF(pdix[rgiobj[dwOfs]].dwOfs == dwOfs);
  522. } else {
  523. AssertF(rgiobj[dwOfs] == -1);
  524. }
  525. }
  526. }
  527. #endif
  528. /*
  529. * Shrink the "failed POV" array to its actual size.
  530. * The shrink "should" always succeed. Note also that
  531. * even if it fails, we're okay; we just waste a little
  532. * memory.
  533. */
  534. hres = ReallocCbPpv(cbCdw(this->cdwPOV), &this->rgdwPOV);
  535. AssertF(SUCCEEDED(hres));
  536. /*
  537. * If we are using cooked data, then we actually hand the
  538. * device driver a different translation table which
  539. * combines the offset and dwDevType so data cooking can
  540. * happen safely.
  541. */
  542. vdf.pvi = this->pvi;
  543. if ( fLimpFF(this->pvi,
  544. SUCCEEDED(hres = Hel_SetDataFormat(&vdf))) ) {
  545. this->pdix = pdix;
  546. pdix = 0;
  547. this->rgiobj = rgiobj;
  548. rgiobj = 0;
  549. this->dwDataSize = lpdf->dwDataSize;
  550. hres = S_OK;
  551. } else {
  552. AssertF(FAILED(hres));
  553. }
  554. } else {
  555. /* Out of memory */
  556. }
  557. done:;
  558. FreePpv(&pdix);
  559. FreePpv(&rgiobj);
  560. FreePpv(&vdf.pDfOfs);
  561. ExitOleProc();
  562. return hres;
  563. }
  564. /*****************************************************************************
  565. *
  566. * @doc INTERNAL
  567. *
  568. * @method HRESULT | CDIDev | OptimizeDataFormat |
  569. *
  570. * Study the parsed data format to determine whether we can
  571. * used an optimized <mf CDIDev::GetDeviceState> to obtain
  572. * the data more quickly.
  573. *
  574. * The data format is considered optimized if it matches the
  575. * device data format, modulo possible shifting due to insertion
  576. * of bonus fields at the beginning or end, and modulo missing
  577. * fields.
  578. *
  579. * The data format is considered fully-optimized if it
  580. * optimized, and no shifting is necessary, and the structure size
  581. * is exactly the same. This means the buffer can be passed
  582. * straight through to the driver.
  583. *
  584. *
  585. *****************************************************************************/
  586. HRESULT INTERNAL
  587. CDIDev_OptimizeDataFormat(PDD this)
  588. {
  589. int ib;
  590. DWORD ibMax; /* One past highest match point */
  591. DWORD ibMin; /* Lowest match point */
  592. int iobj;
  593. DWORD dwDataSize;
  594. HRESULT hres;
  595. EnterProc(CDIDev_OptimizeDataFormat, (_ "p", this));
  596. ib = (int)0x8000000; /* Not yet known */
  597. ibMin = 0xFFFFFFFF;
  598. ibMax = 0;
  599. for ( iobj = this->df.dwNumObjs; --iobj >= 0; ) {
  600. DWORD ibMaxThis;
  601. if ( this->pdix[iobj].dwOfs != 0xFFFFFFFF ) { /* Data was requested */
  602. int ibExpected = (int)(this->pdix[iobj].dwOfs -
  603. this->df.rgodf[iobj].dwOfs);
  604. if ( fLimpFF(ib != (int)0x8000000, ib == ibExpected) ) {
  605. ib = ibExpected;
  606. } else {
  607. SquirtSqflPtszV(sqfl | sqflMajor,
  608. TEXT("IDirectInputDevice: Optimization level 0, translation needed") );
  609. this->diopt = dioptNone;
  610. this->GetState = CDIDev_GetDeviceStateSlow;
  611. goto done;
  612. }
  613. if ( ibMin > this->df.rgodf[iobj].dwOfs ) {
  614. ibMin = this->df.rgodf[iobj].dwOfs;
  615. }
  616. if ( this->df.rgodf[iobj].dwType & DIDFT_DWORDOBJS ) {
  617. ibMaxThis = this->df.rgodf[iobj].dwOfs + sizeof(DWORD);
  618. } else {
  619. ibMaxThis = this->df.rgodf[iobj].dwOfs + sizeof(BYTE);
  620. }
  621. if ( ibMax < ibMaxThis ) {
  622. ibMax = ibMaxThis;
  623. }
  624. }
  625. }
  626. /*
  627. * Make sure we actually found something.
  628. */
  629. if ( ib != (int)0x8000000 ) { /* Data format is matched */
  630. AssertF(ibMin < ibMax);
  631. AssertF(ib + (int)ibMin >= 0);
  632. AssertF(ib + ibMax <= this->dwDataSize);
  633. this->ibDelta = ib;
  634. this->ibMin = ibMin;
  635. this->cbMatch = ibMax - ibMin;
  636. if ( ib >= 0 && ib + this->df.dwDataSize <= this->dwDataSize ) {
  637. /* We can go direct */
  638. if ( ib == 0 && this->dwDataSize == this->df.dwDataSize ) {
  639. /* Data formats are equal! */
  640. this->diopt = dioptEqual;
  641. this->GetState = CDIDev_GetDeviceStateEqual;
  642. SquirtSqflPtszV(sqfl | sqflMajor,
  643. TEXT("IDirectInputDevice: Optimization level 3, full speed ahead!") );
  644. } else {
  645. this->diopt = dioptDirect;
  646. this->GetState = CDIDev_GetDeviceStateDirect;
  647. SquirtSqflPtszV(sqfl | sqflMajor,
  648. TEXT("IDirectInputDevice: Optimization level 2, direct access") );
  649. }
  650. } else {
  651. SquirtSqflPtszV(sqfl | sqflMajor,
  652. TEXT("IDirectInputDevice: Optimization level 1, okay") );
  653. this->diopt = dioptMatch;
  654. this->GetState = CDIDev_GetDeviceStateMatched;
  655. }
  656. } else { /* No data in data format! */
  657. RPF("IDirectInputDevice: Null data format; if that's what you want...");
  658. this->diopt = dioptNone;
  659. this->GetState = CDIDev_GetDeviceStateSlow;
  660. }
  661. done:;
  662. if ( this->diopt >= dioptDirect ) { /* Can go direct; don't need buf */
  663. dwDataSize = 0;
  664. } else {
  665. dwDataSize = this->df.dwDataSize;
  666. }
  667. hres = ReallocCbPpv(dwDataSize, &this->pvBuffer);
  668. if ( SUCCEEDED(hres) ) {
  669. AssertF(this->GetState);
  670. } else {
  671. FreePpv(&this->pdix);
  672. D(this->GetState = 0);
  673. }
  674. return hres;
  675. }
  676. /*****************************************************************************
  677. *
  678. * @doc EXTERNAL
  679. *
  680. * @method HRESULT | IDirectInputDevice | SetDataFormat |
  681. *
  682. * Set the data format for the DirectInput device.
  683. *
  684. * The data format must be set before the device can be
  685. * acquired.
  686. *
  687. * It is necessary to set the data format only once.
  688. *
  689. * The data format may not be changed while the device
  690. * is acquired.
  691. *
  692. * If the attempt to set the data format fails, all data
  693. * format information is lost, and a valid data format
  694. * must be set before the device may be acquired.
  695. *
  696. * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
  697. *
  698. * @parm IN LPDIDATAFORMAT | lpdf |
  699. *
  700. * Points to a structure that describes the format of the data
  701. * the DirectInputDevice should return.
  702. *
  703. * @returns
  704. *
  705. * Returns a COM error code. The following error codes are
  706. * intended to be illustrative and not necessarily comprehensive.
  707. *
  708. * <c DI_OK> = <c S_OK>: The operation completed successfully.
  709. *
  710. * <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
  711. * <p lpvData> parameter is not a valid pointer.
  712. *
  713. * <c DIERR_ACQUIRED>: Cannot change the data format while the
  714. * device is acquired.
  715. *
  716. *
  717. *****************************************************************************/
  718. HRESULT INLINE
  719. CDIDev_SetDataFormat_IsValidDataSize(LPCDIDATAFORMAT lpdf)
  720. {
  721. HRESULT hres;
  722. if ( lpdf->dwDataSize % 4 == 0 ) {
  723. hres = S_OK;
  724. } else {
  725. RPF("IDirectInputDevice::SetDataFormat: "
  726. "dwDataSize must be a multiple of 4");
  727. hres = E_INVALIDARG;
  728. }
  729. return hres;
  730. }
  731. HRESULT INLINE
  732. CDIDev_SetDataFormat_IsValidObjectSize(LPCDIDATAFORMAT lpdf)
  733. {
  734. HRESULT hres;
  735. if ( lpdf->dwObjSize == cbX(ODF) ) {
  736. hres = S_OK;
  737. } else {
  738. RPF("IDirectInputDevice::SetDataFormat: Invalid dwObjSize");
  739. hres = E_INVALIDARG;
  740. }
  741. return hres;
  742. }
  743. STDMETHODIMP
  744. CDIDev_SetDataFormat(PV pdd, LPCDIDATAFORMAT lpdf _THAT)
  745. {
  746. HRESULT hres;
  747. EnterProcR(IDirectInputDevice8::SetDataFormat, (_ "pp", pdd, lpdf));
  748. if ( SUCCEEDED(hres = hresPvT(pdd)) &&
  749. SUCCEEDED(hres = hresFullValidReadPxCb(lpdf, DIDATAFORMAT, 1)) &&
  750. SUCCEEDED(hres = hresFullValidFl(lpdf->dwFlags, DIDF_VALID, 1)) &&
  751. SUCCEEDED(hres = CDIDev_SetDataFormat_IsValidDataSize(lpdf)) &&
  752. SUCCEEDED(hres = CDIDev_SetDataFormat_IsValidObjectSize(lpdf)) &&
  753. SUCCEEDED(hres = hresFullValidReadPvCb(lpdf->rgodf,
  754. cbCxX(lpdf->dwNumObjs, ODF), 1)) ) {
  755. PDD this = _thisPv(pdd);
  756. /*
  757. * Must protect with the critical section to prevent two people
  758. * from changing the format simultaneously, or one person from
  759. * changing the data format while somebody else is reading data.
  760. */
  761. CDIDev_EnterCrit(this);
  762. AssertF( this->dwVersion );
  763. if ( !this->fAcquired ) {
  764. DIPROPDWORD dipdw;
  765. /*
  766. * Nuke the old data format stuff before proceeding.
  767. */
  768. FreePpv(&this->pdix);
  769. FreePpv(&this->rgiobj);
  770. this->cdwPOV = 0;
  771. D(this->GetState = 0);
  772. this->fPolledDataFormat = FALSE;
  773. /*
  774. * Wipe out the report IDs
  775. */
  776. dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  777. dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  778. dipdw.diph.dwObj = 0x0;
  779. dipdw.diph.dwHow = DIPH_DEVICE;
  780. dipdw.dwData = 0; // Nuke all knowledge of reportId's
  781. hres = CDIDev_RealSetProperty(this, DIPROP_ENABLEREPORTID, &dipdw.diph);
  782. if ( SUCCEEDED(hres) || hres == E_NOTIMPL )
  783. {
  784. hres = CDIDev_ParseDataFormat(this, lpdf);
  785. if ( SUCCEEDED(hres) ) {
  786. hres = CDIDev_OptimizeDataFormat(this);
  787. /*
  788. * Now set the axis mode, as a convenience.
  789. */
  790. CAssertF(DIDF_VALID == (DIDF_RELAXIS | DIDF_ABSAXIS));
  791. switch ( lpdf->dwFlags ) {
  792. case 0:
  793. hres = S_OK;
  794. goto axisdone;
  795. case DIDF_RELAXIS:
  796. dipdw.dwData = DIPROPAXISMODE_REL;
  797. break;
  798. case DIDF_ABSAXIS:
  799. dipdw.dwData = DIPROPAXISMODE_ABS;
  800. break;
  801. default:
  802. RPF("%s: Cannot combine DIDF_RELAXIS with DIDF_ABSAXIS",
  803. s_szProc);
  804. hres = E_INVALIDARG;
  805. goto axisdone;
  806. }
  807. dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  808. dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  809. dipdw.diph.dwObj = 0;
  810. dipdw.diph.dwHow = DIPH_DEVICE;
  811. hres = CDIDev_RealSetProperty(this, DIPROP_AXISMODE, &dipdw.diph);
  812. if ( SUCCEEDED(hres) ) {
  813. hres = S_OK;
  814. }
  815. }
  816. } else
  817. {
  818. SquirtSqflPtszV(sqflDf,
  819. TEXT("Could not set DIPROP_ENABLEREPORTID to 0x0"));
  820. }
  821. axisdone:;
  822. } else { /* Already acquired */
  823. hres = DIERR_ACQUIRED;
  824. }
  825. CDIDev_LeaveCrit(this);
  826. }
  827. ExitOleProcR();
  828. return hres;
  829. }
  830. #define BEGIN_CONST_DATA data_seg(".text", "CODE")
  831. BYTE c_SemTypeToDFType[8] = { 0, 0,
  832. DIDFT_GETTYPE(DIDFT_ABSAXIS), DIDFT_GETTYPE(DIDFT_RELAXIS),
  833. DIDFT_GETTYPE(DIDFT_BUTTON), 0,
  834. DIDFT_GETTYPE(DIDFT_POV), 0 };
  835. #define END_CONST_DATA data_seg(".data", "DATA")
  836. /*****************************************************************************
  837. *
  838. * @doc INTERNAL
  839. *
  840. * @struct DEVICETOUSER |
  841. *
  842. * Structure to hold a device to user assignment.
  843. *
  844. * @field GUID | guidDevice |
  845. *
  846. * The device.
  847. *
  848. * @field WCHAR | wszOwner[MAX_PATH] |
  849. *
  850. * Name of the user who currently owns the device.
  851. *
  852. *****************************************************************************/
  853. typedef struct _DEVICETOUSER
  854. {
  855. GUID guidDevice;
  856. WCHAR wszOwner[MAX_PATH];
  857. } DEVICETOUSER, *PDEVICETOUSER;
  858. #define DELTA_DTO_COUNT 4
  859. // ISSUE-2001/03/29-MarcAnd CMap usage is inconsistant
  860. // CMap code should be split out into separate source file
  861. /*****************************************************************************
  862. *
  863. * @doc INTERNAL
  864. *
  865. * @func HRESULT | CMap_SetDeviceUserName |
  866. *
  867. * Set the passed device to be associated with the passed user name.
  868. * If the device is already associated with a user or with no user
  869. * that association is replaced with the new one. If the device is
  870. * not yet in the array, it is added. Memory is allocated as
  871. * necessary to increase the array size.
  872. *
  873. * @parm REFGUID | guidDevInst |
  874. *
  875. * A pointer to the device instance GUID to be added/modified.
  876. *
  877. * @parm LPCWSTR | wszOwner |
  878. *
  879. * A UNICODE user name.
  880. *
  881. * @returns
  882. *
  883. * <c S_OK> if the name was set
  884. * <c DI_NOEFFECT> if nothing was set because nothing needed to be
  885. * A memory allocation error if such occured.
  886. *
  887. *****************************************************************************/
  888. HRESULT CMap_SetDeviceUserName
  889. (
  890. REFGUID guidDevInst,
  891. LPCWSTR wszOwner
  892. )
  893. {
  894. HRESULT hres = S_OK;
  895. PDEVICETOUSER pdto;
  896. CAssertF( DELTA_DTO_COUNT > 1 );
  897. AssertF( !IsEqualGUID( guidDevInst, &GUID_Null ) );
  898. AssertF( !InCrit() );
  899. DllEnterCrit();
  900. /*
  901. * Note g_pdto will be NULL until the first name is set
  902. * however, g_cdtoMax will be zero so this loop should
  903. * fall through to allocate more memory.
  904. */
  905. for( pdto = g_pdto; pdto < &g_pdto[ g_cdtoMax ]; pdto++ )
  906. {
  907. if( IsEqualGUID( guidDevInst, &pdto->guidDevice ) )
  908. {
  909. break;
  910. }
  911. }
  912. if( !wszOwner )
  913. {
  914. if( pdto < &g_pdto[ g_cdtoMax ] )
  915. {
  916. ZeroMemory( &pdto->guidDevice, cbX( pdto->guidDevice ) );
  917. g_cdto--;
  918. AssertF( g_cdto >= 0 );
  919. }
  920. else
  921. {
  922. /*
  923. * Could not find device, since we were removing, don't worry
  924. */
  925. hres = DI_NOEFFECT;
  926. }
  927. }
  928. else
  929. {
  930. if( pdto >= &g_pdto[ g_cdtoMax ] )
  931. {
  932. /*
  933. * Need to add a new entry
  934. */
  935. if( g_cdto == g_cdtoMax )
  936. {
  937. /*
  938. * If all entries are used try to get more
  939. */
  940. hres = ReallocCbPpv( ( g_cdtoMax + DELTA_DTO_COUNT ) * cbX( *g_pdto ), &g_pdto );
  941. if( FAILED( hres ) )
  942. {
  943. goto exit_SetDeviceUserName;
  944. }
  945. g_cdtoMax += DELTA_DTO_COUNT;
  946. /*
  947. * The first new element is sure to be free
  948. */
  949. pdto = &g_pdto[ g_cdto ];
  950. }
  951. else
  952. {
  953. /*
  954. * There's an empty one in the array somewhere
  955. */
  956. for( pdto = g_pdto; pdto < &g_pdto[ g_cdtoMax ]; pdto++ )
  957. {
  958. if( IsEqualGUID( &GUID_Null, &pdto->guidDevice ) )
  959. {
  960. break;
  961. }
  962. }
  963. }
  964. pdto->guidDevice = *guidDevInst;
  965. }
  966. g_cdto++;
  967. AssertF( pdto < &g_pdto[ g_cdtoMax ] );
  968. AssertF( lstrlenW( wszOwner ) < cbX( pdto->wszOwner ) );
  969. #ifdef WINNT
  970. lstrcpyW( pdto->wszOwner, wszOwner );
  971. #else
  972. memcpy( pdto->wszOwner, wszOwner, ( 1 + lstrlenW( wszOwner ) ) * cbX( *wszOwner ) );
  973. #endif
  974. }
  975. exit_SetDeviceUserName:;
  976. DllLeaveCrit();
  977. return hres;
  978. }
  979. /*****************************************************************************
  980. *
  981. * @doc INTERNAL
  982. *
  983. * @func HRESULT | CMap_IsNewDeviceUserName |
  984. *
  985. * Searches the array of device to user associations for the passed
  986. * device GUID. If the device GUID is matched the name is tested
  987. * to see if it is the same as the passed name.
  988. *
  989. * @parm REFGUID | guidDevInst |
  990. *
  991. * A pointer to the device instance GUID to be tested.
  992. *
  993. * @parm LPCWSTR | wszOwner |
  994. *
  995. * A UNICODE user name to be tested.
  996. *
  997. * @returns
  998. *
  999. * <c FALSE> if a the device was found and the user matches
  1000. * <c TRUE> if not
  1001. *
  1002. *****************************************************************************/
  1003. HRESULT CMap_IsNewDeviceUserName
  1004. (
  1005. REFGUID guidDevInst,
  1006. LPWSTR wszTest
  1007. )
  1008. {
  1009. BOOL fres = TRUE;
  1010. PDEVICETOUSER pdto;
  1011. AssertF( !IsEqualGUID( guidDevInst, &GUID_Null ) );
  1012. AssertF( wszTest != L'\0' );
  1013. AssertF( !InCrit() );
  1014. DllEnterCrit();
  1015. if( g_pdto )
  1016. {
  1017. for( pdto = g_pdto; pdto < &g_pdto[ g_cdtoMax ]; pdto++ )
  1018. {
  1019. if( IsEqualGUID( guidDevInst, &pdto->guidDevice ) )
  1020. {
  1021. #ifdef WINNT
  1022. if( !lstrcmpW( wszTest, pdto->wszOwner ) )
  1023. #else
  1024. if( ( pdto->wszOwner[0] != L'\0' )
  1025. &&( !memcmp( wszTest, pdto->wszOwner,
  1026. lstrlenW( wszTest ) * cbX( *wszTest ) ) ) )
  1027. #endif
  1028. {
  1029. fres = FALSE;
  1030. }
  1031. break;
  1032. }
  1033. }
  1034. }
  1035. DllLeaveCrit();
  1036. return fres;
  1037. }
  1038. /*****************************************************************************
  1039. *
  1040. * @doc INTERNAL
  1041. *
  1042. * @func HRESULT | CMap_GetDeviceUserName |
  1043. *
  1044. * Searches the array of device to user associations for the passed
  1045. * device GUID. If the device GUID is matched and has an associated
  1046. * user name, that is copied into wszOwner. If the GUID is not
  1047. * matched or the match has a NULL string associated with it,
  1048. * wszOwner is set to a NULL string.
  1049. *
  1050. * @parm REFGUID | guidDevInst |
  1051. *
  1052. * A pointer to the device instance GUID to be added/modified.
  1053. *
  1054. * @parm LPCWSTR | wszOwner |
  1055. *
  1056. * A UNICODE user name.
  1057. *
  1058. * @returns
  1059. *
  1060. * <c S_OK> if a user name has been copied into wszOwner
  1061. * <c DI_NOEFFECT> if wszOwner has been set to a NULL string
  1062. *
  1063. *****************************************************************************/
  1064. HRESULT CMap_GetDeviceUserName
  1065. (
  1066. REFGUID guidDevInst,
  1067. LPWSTR wszOwner
  1068. )
  1069. {
  1070. HRESULT hres = DI_NOEFFECT;
  1071. PDEVICETOUSER pdto;
  1072. AssertF( !IsEqualGUID( guidDevInst, &GUID_Null ) );
  1073. AssertF( !InCrit() );
  1074. /*
  1075. * Assume nothing is found
  1076. */
  1077. wszOwner[0] = L'\0';
  1078. DllEnterCrit();
  1079. if( g_pdto )
  1080. {
  1081. for( pdto = g_pdto; pdto < &g_pdto[ g_cdtoMax ]; pdto++ )
  1082. {
  1083. if( IsEqualGUID( guidDevInst, &pdto->guidDevice ) )
  1084. {
  1085. if( pdto->wszOwner[0] != L'\0' )
  1086. {
  1087. #ifdef WINNT
  1088. lstrcpyW( wszOwner, pdto->wszOwner );
  1089. #else
  1090. memcpy( wszOwner, pdto->wszOwner,
  1091. ( 1 + lstrlenW( pdto->wszOwner ) ) * cbX( *wszOwner ) );
  1092. #endif
  1093. hres = S_OK;
  1094. }
  1095. break;
  1096. }
  1097. }
  1098. }
  1099. DllLeaveCrit();
  1100. return hres;
  1101. }
  1102. /*****************************************************************************
  1103. *
  1104. * @doc INTERNAL
  1105. *
  1106. * @method HRESULT | CMap | ValidateActionMapSemantics |
  1107. *
  1108. * Validate the semantics in an action map for overall sanity.
  1109. *
  1110. * @parm LPDIACTIONFORMATW | pActionFormat |
  1111. *
  1112. * Actions to map.
  1113. *
  1114. * @parm DWORD | dwFlags |
  1115. *
  1116. * Flags to modify the validation behavior.
  1117. * Currently these are the <c DIDBAM_*> flags as the only
  1118. * <mf IDirectInputDevice::SetActionMap> flag is the default
  1119. * <c DIDSAM_DEFAULT>.
  1120. *
  1121. * @returns
  1122. *
  1123. * <c DI_OK> = <c S_OK>: The operation completed successfully.
  1124. *
  1125. *
  1126. *****************************************************************************/
  1127. int __stdcall CompareActions
  1128. (
  1129. PV pv1,
  1130. PV pv2
  1131. )
  1132. {
  1133. int iRes;
  1134. LPDIACTION pAct1 = (LPDIACTION)pv1;
  1135. LPDIACTION pAct2 = (LPDIACTION)pv2;
  1136. iRes = memcmp( &pAct1->guidInstance, &pAct2->guidInstance, cbX(pAct1->guidInstance) );
  1137. if( !iRes )
  1138. {
  1139. if( pAct1->dwFlags & DIA_APPMAPPED )
  1140. {
  1141. if( pAct2->dwFlags & DIA_APPMAPPED )
  1142. {
  1143. iRes = pAct1->dwObjID - pAct2->dwObjID;
  1144. }
  1145. else
  1146. {
  1147. iRes = -1;
  1148. }
  1149. }
  1150. else
  1151. {
  1152. if( pAct2->dwFlags & DIA_APPMAPPED )
  1153. {
  1154. iRes = 1;
  1155. }
  1156. else
  1157. {
  1158. iRes = pAct1->dwSemantic - pAct2->dwSemantic;
  1159. }
  1160. }
  1161. }
  1162. return iRes;
  1163. }
  1164. STDMETHODIMP
  1165. CMap_ValidateActionMapSemantics
  1166. (
  1167. LPDIACTIONFORMATW paf,
  1168. DWORD dwFlags
  1169. )
  1170. {
  1171. HRESULT hres;
  1172. LPDIACTIONW pAction;
  1173. LPDIACTIONW *pWorkSpace;
  1174. LPDIACTIONW *pCurr;
  1175. LPDIACTIONW *pLast;
  1176. EnterProcI(IDirectInputDeviceCallback::CMap::ValidateActionMapSemantics,
  1177. (_ "px", paf, dwFlags ));
  1178. /*
  1179. * Create a pointer array to the actions, sort it then look for duplicates
  1180. */
  1181. if( SUCCEEDED( hres = AllocCbPpv( cbCxX(paf->dwNumActions,PV), &pWorkSpace) ) )
  1182. {
  1183. /*
  1184. * Fill work space from action array discarding unmappable elements
  1185. */
  1186. pCurr = pWorkSpace;
  1187. for( pAction = paf->rgoAction; pAction < &paf->rgoAction[paf->dwNumActions]; pAction++ )
  1188. {
  1189. if( dwFlags & ( DIDBAM_INITIALIZE | DIDBAM_HWDEFAULTS ) )
  1190. {
  1191. pAction->dwFlags = 0;
  1192. pAction->guidInstance = GUID_Null;
  1193. pAction->dwHow = 0;
  1194. }
  1195. else
  1196. {
  1197. if( pAction->dwFlags & ~DIA_VALID )
  1198. {
  1199. RPF( "ERROR Invalid action: rgoAction[%d].dwFlags 0x%08x",
  1200. pAction - paf->rgoAction, pAction->dwFlags & ~DIA_VALID );
  1201. pAction->dwHow = DIAH_ERROR;
  1202. hres = DIERR_INVALIDPARAM;
  1203. goto free_and_exit;
  1204. }
  1205. if( pAction->dwFlags & DIA_APPNOMAP )
  1206. {
  1207. continue;
  1208. }
  1209. if( dwFlags & DIDBAM_PRESERVE )
  1210. {
  1211. switch( pAction->dwHow )
  1212. {
  1213. case DIAH_UNMAPPED:
  1214. break;
  1215. case DIAH_USERCONFIG:
  1216. case DIAH_APPREQUESTED:
  1217. case DIAH_HWAPP:
  1218. case DIAH_HWDEFAULT:
  1219. case DIAH_OTHERAPP:
  1220. case DIAH_DEFAULT:
  1221. if( IsEqualGUID( &pAction->guidInstance, &GUID_Null ) )
  1222. {
  1223. RPF("ERROR Invalid action: rgoAction[%d].dwHow is mapped as 0x%08x but has no device",
  1224. pAction - paf->rgoAction, pAction->dwHow );
  1225. hres = DIERR_INVALIDPARAM;
  1226. goto free_and_exit;
  1227. }
  1228. break;
  1229. case DIAH_ERROR:
  1230. RPF("ERROR Invalid action: rgoAction[%d].dwHow has DIAH_ERROR",
  1231. pAction - paf->rgoAction );
  1232. hres = DIERR_INVALIDPARAM;
  1233. goto free_and_exit;
  1234. default:
  1235. if( pAction->dwHow & ~DIAH_VALID )
  1236. {
  1237. RPF("ERROR Invalid action: rgoAction[%d].dwHow has invalid flags 0x%08x",
  1238. pAction - paf->rgoAction, pAction->dwHow & ~DIAH_VALID );
  1239. }
  1240. else
  1241. {
  1242. RPF("ERROR Invalid action: rgoAction[%d].dwHow has invalid combination of map flags 0x%08x",
  1243. pAction - paf->rgoAction, pAction->dwHow & ~DIAH_VALID );
  1244. }
  1245. pAction->dwHow = DIAH_ERROR;
  1246. hres = DIERR_INVALIDPARAM;
  1247. goto free_and_exit;
  1248. }
  1249. }
  1250. else
  1251. {
  1252. if(!( pAction->dwFlags & DIA_APPMAPPED ) )
  1253. {
  1254. pAction->guidInstance = GUID_Null;
  1255. }
  1256. pAction->dwHow = 0;
  1257. }
  1258. }
  1259. if( ( pAction->dwSemantic ^ paf->dwGenre ) & DISEM_GENRE_MASK )
  1260. {
  1261. switch( DISEM_GENRE_GET( pAction->dwSemantic ) )
  1262. {
  1263. case DISEM_GENRE_GET( DIPHYSICAL_KEYBOARD ):
  1264. case DISEM_GENRE_GET( DIPHYSICAL_MOUSE ):
  1265. case DISEM_GENRE_GET( DIPHYSICAL_VOICE ):
  1266. case DISEM_GENRE_GET( DISEMGENRE_ANY ):
  1267. break;
  1268. default:
  1269. RPF("ERROR Invalid action: rgoAction[%d].dwSemantic 0x%08x for genre 0x%08x",
  1270. pAction - paf->rgoAction, pAction->dwSemantic, paf->dwGenre );
  1271. pAction->dwHow = DIAH_ERROR;
  1272. hres = DIERR_INVALIDPARAM;
  1273. goto free_and_exit;
  1274. }
  1275. }
  1276. /*
  1277. * Note, the SEM_FLAGS are not tested, this is only to save time
  1278. * as nothing depends upon their value. This could be added.
  1279. * Semantic index 0xFF used to mean ANY so don't allow it any more
  1280. */
  1281. if(!( pAction->dwFlags & DIA_APPMAPPED )
  1282. && ( ( pAction->dwSemantic & ~DISEM_VALID )
  1283. || ( DISEM_INDEX_GET( pAction->dwSemantic ) == 0xFF )
  1284. ||!c_SemTypeToDFType[ DISEM_TYPEANDMODE_GET( pAction->dwSemantic ) ] ) )
  1285. {
  1286. RPF("ERROR Invalid action: rgoAction[%d].dwSemantic 0x%08x is invalid",
  1287. pAction - paf->rgoAction, pAction->dwSemantic );
  1288. pAction->dwHow = DIAH_ERROR;
  1289. hres = DIERR_INVALIDPARAM;
  1290. goto free_and_exit;
  1291. }
  1292. *pCurr = pAction;
  1293. pCurr++;
  1294. }
  1295. if( pCurr == pWorkSpace )
  1296. {
  1297. SquirtSqflPtszV(sqflDf | sqflBenign,
  1298. TEXT("Action map contains no mappable actions") );
  1299. hres = S_FALSE;
  1300. }
  1301. else
  1302. {
  1303. hres = S_OK;
  1304. pLast = pCurr - 1;
  1305. ptrPartialQSort( (PV)pWorkSpace, (PV)pLast, &CompareActions );
  1306. ptrInsertSort( (PV)pWorkSpace, (PV)pLast, &CompareActions );
  1307. /*
  1308. * Now we have an ordered list, see there are duplicate actions.
  1309. */
  1310. for( pCurr = pWorkSpace + 1; pCurr <= pLast; pCurr++ )
  1311. {
  1312. if( !CompareActions( *(pCurr-1), *pCurr )
  1313. && !( (*pCurr)->dwFlags & DIA_APPMAPPED ) )
  1314. {
  1315. RPF( "ERROR Invalid DIACTIONFORMAT: rgoAction contains duplicates" );
  1316. hres = DIERR_INVALIDPARAM;
  1317. #ifndef XDEBUG
  1318. /*
  1319. * In retail, any bad is bad. In debug report how bad.
  1320. */
  1321. break;
  1322. #else
  1323. SquirtSqflPtszV(sqflDf | sqflError,
  1324. TEXT("Actions %d and %d are the same"),
  1325. *(pCurr-1) - paf->rgoAction, *pCurr - paf->rgoAction );
  1326. #endif
  1327. }
  1328. }
  1329. }
  1330. free_and_exit:;
  1331. FreePv( pWorkSpace );
  1332. }
  1333. ExitOleProcR();
  1334. return hres;
  1335. }
  1336. /*****************************************************************************
  1337. *
  1338. * @doc INTERNAL
  1339. *
  1340. * @func HRESULT | CMap_TestSysObject |
  1341. *
  1342. * Test the passed object to see if it is a reasonable thing to exist
  1343. * on a mouse or keyboard depending on the physical genre.
  1344. *
  1345. * @parm DWORD | dwPhysicalGenre |
  1346. *
  1347. * Mouse, keyboard or voice as DIPHYSICAL_*
  1348. *
  1349. * @parm DWORD | dwObject |
  1350. *
  1351. * The object in question
  1352. *
  1353. * @returns
  1354. *
  1355. * <c S_OK> if the object could be expected on the class of device
  1356. * or <c DIERR_INVALIDPARAM> if not
  1357. *
  1358. *****************************************************************************/
  1359. HRESULT CMap_TestSysObject
  1360. (
  1361. DWORD dwPhysicalGenre,
  1362. DWORD dwObject
  1363. )
  1364. {
  1365. HRESULT hres = S_OK;
  1366. if( dwPhysicalGenre == DIPHYSICAL_KEYBOARD )
  1367. {
  1368. /*
  1369. * Anything but a button with an 8 bit offset is invalid.
  1370. */
  1371. if( ( dwObject & DIDFT_BUTTON )
  1372. && ( ( DIDFT_GETINSTANCE( dwObject ) & 0xFF00 ) == 0 ) )
  1373. {
  1374. SquirtSqflPtszV(sqflDf | sqflBenign,
  1375. TEXT("Key 0x%02 not defined on this keyboard (id)"),
  1376. dwObject );
  1377. }
  1378. else
  1379. {
  1380. SquirtSqflPtszV(sqflDf | sqflError,
  1381. TEXT("Object type 0x%08 invalid for keyboard (id)"),
  1382. dwObject );
  1383. hres = DIERR_INVALIDPARAM;
  1384. }
  1385. }
  1386. else if( dwPhysicalGenre == DIPHYSICAL_MOUSE )
  1387. {
  1388. /*
  1389. * Allow buttons 1 to 8 and axes 1 to 3
  1390. */
  1391. if( ( dwObject & DIDFT_PSHBUTTON )
  1392. && ( DIDFT_GETINSTANCE( dwObject ) < 8 ) )
  1393. {
  1394. SquirtSqflPtszV(sqflDf | sqflBenign,
  1395. TEXT("Button %d not defined on this mouse (id)"),
  1396. DIDFT_GETINSTANCE( dwObject ) );
  1397. }
  1398. else if( ( dwObject & DIDFT_AXIS )
  1399. && ( DIDFT_GETINSTANCE( dwObject ) < 3 ) )
  1400. {
  1401. SquirtSqflPtszV(sqflDf | sqflBenign,
  1402. TEXT("Axis %d not defined on this mouse (id)"),
  1403. DIDFT_GETINSTANCE( dwObject ) );
  1404. }
  1405. else
  1406. {
  1407. SquirtSqflPtszV(sqflDf | sqflError,
  1408. TEXT("Bad control object 0x%08x for mouse (id)"),
  1409. dwObject );
  1410. hres = DIERR_INVALIDPARAM;
  1411. }
  1412. }
  1413. else if( dwPhysicalGenre == DIPHYSICAL_VOICE )
  1414. {
  1415. if( dwObject & DIDFT_PSHBUTTON )
  1416. {
  1417. switch( DIDFT_GETINSTANCE( dwObject ) )
  1418. {
  1419. case DIVOICE_CHANNEL1:
  1420. case DIVOICE_CHANNEL2:
  1421. case DIVOICE_CHANNEL3:
  1422. case DIVOICE_CHANNEL4:
  1423. case DIVOICE_CHANNEL5:
  1424. case DIVOICE_CHANNEL6:
  1425. case DIVOICE_CHANNEL7:
  1426. case DIVOICE_CHANNEL8:
  1427. case DIVOICE_TEAM:
  1428. case DIVOICE_ALL:
  1429. case DIVOICE_RECORDMUTE:
  1430. case DIVOICE_PLAYBACKMUTE:
  1431. case DIVOICE_TRANSMIT:
  1432. case DIVOICE_VOICECOMMAND:
  1433. SquirtSqflPtszV(sqflDf | sqflBenign,
  1434. TEXT("Button %d not defined on this comms device (id)"),
  1435. DIDFT_GETINSTANCE( dwObject ) );
  1436. break;
  1437. default:
  1438. SquirtSqflPtszV(sqflDf | sqflError,
  1439. TEXT("Bad control object 0x%08x for comms device (id)"),
  1440. dwObject );
  1441. hres = DIERR_INVALIDPARAM;
  1442. }
  1443. }
  1444. else
  1445. {
  1446. SquirtSqflPtszV(sqflDf | sqflError,
  1447. TEXT("Comms control object 0x%08x not a button (id)"),
  1448. dwObject );
  1449. hres = DIERR_INVALIDPARAM;
  1450. }
  1451. }
  1452. else
  1453. {
  1454. AssertF( !"Physical genre not keyboard, mouse or voice (id)" );
  1455. }
  1456. return hres;
  1457. }
  1458. /*****************************************************************************
  1459. *
  1460. * @doc INTERNAL
  1461. *
  1462. * @func HRESULT | CMap_TestSysOffset |
  1463. *
  1464. * Test the passed offset to see if it is a reasonable one for the
  1465. * default data format of the physical genre.
  1466. *
  1467. * @parm DWORD | dwPhysicalGenre |
  1468. *
  1469. * Mouse, keyboard or voice as DIPHYSICAL_*
  1470. *
  1471. * @parm DWORD | dwOffset |
  1472. *
  1473. * The offset in question
  1474. *
  1475. * @returns
  1476. *
  1477. * <c S_OK> if the offset could be expected on the class of device
  1478. * or <c DIERR_INVALIDPARAM> if not
  1479. *
  1480. *****************************************************************************/
  1481. HRESULT CMap_TestSysOffset
  1482. (
  1483. DWORD dwPhysicalGenre,
  1484. DWORD dwOffset
  1485. )
  1486. {
  1487. HRESULT hres = S_OK;
  1488. if( dwPhysicalGenre == DIPHYSICAL_KEYBOARD )
  1489. {
  1490. /*
  1491. * Anything but a button with an 8 bit offset is invalid.
  1492. */
  1493. if( dwOffset <= 0xFF )
  1494. {
  1495. SquirtSqflPtszV(sqflDf | sqflBenign,
  1496. TEXT("Key 0x%02 not defined on this keyboard (ofs)"),
  1497. dwOffset );
  1498. }
  1499. else
  1500. {
  1501. SquirtSqflPtszV(sqflDf | sqflError,
  1502. TEXT("Key offset 0x%08 invalid for keyboard (ofs)"),
  1503. dwOffset );
  1504. hres = DIERR_INVALIDPARAM;
  1505. }
  1506. }
  1507. else if( dwPhysicalGenre == DIPHYSICAL_MOUSE )
  1508. {
  1509. CAssertF( DIMOFS_X == 0 );
  1510. if( dwOffset > DIMOFS_BUTTON7 )
  1511. {
  1512. SquirtSqflPtszV(sqflDf | sqflError,
  1513. TEXT("Bad control offset 0x%08x for mouse (ofs)"),
  1514. dwOffset );
  1515. hres = DIERR_INVALIDPARAM;
  1516. }
  1517. else
  1518. {
  1519. /*
  1520. * Allow buttons 1 to 8
  1521. */
  1522. if( dwOffset >= DIMOFS_BUTTON0 )
  1523. {
  1524. SquirtSqflPtszV(sqflDf | sqflBenign,
  1525. TEXT("Button %d not defined on this mouse (ofs)"),
  1526. dwOffset - DIMOFS_BUTTON0 );
  1527. }
  1528. else
  1529. {
  1530. SquirtSqflPtszV(sqflDf | sqflBenign,
  1531. TEXT("Axis %d not defined on this mouse (ofs)"),
  1532. (dwOffset - DIMOFS_X)>>2 );
  1533. }
  1534. }
  1535. }
  1536. else
  1537. {
  1538. AssertF( !"Physical genre not keyboard, mouse or voice" );
  1539. }
  1540. return hres;
  1541. }
  1542. /*****************************************************************************
  1543. *
  1544. * @doc INTERNAL
  1545. *
  1546. * @method HRESULT | CMap | DeviceValidateActionMap |
  1547. *
  1548. * Validate an action map for a device.
  1549. *
  1550. * @parm LPDIACTIONFORMATW | pActionFormat |
  1551. *
  1552. * Actions to map.
  1553. *
  1554. * @parm DWORD | dwDevGenre |
  1555. *
  1556. * Device genre to match or zero for device that are not physical
  1557. * devices.
  1558. *
  1559. * @parm REFGUID | guidInstace |
  1560. *
  1561. * Instance guid of device to match.
  1562. *
  1563. * @parm LPCDIDATAFORMAT | dfDev |
  1564. *
  1565. * Ponter to device data format.
  1566. *
  1567. * @parm DWORD | dwFlags |
  1568. *
  1569. * A valid combination of <c DVAM_*> flags to describe optional
  1570. * validation behavior.
  1571. *
  1572. * @returns
  1573. *
  1574. * <c DI_OK> = <c S_OK>: The operation completed successfully.
  1575. *
  1576. *
  1577. *****************************************************************************/
  1578. #define DVAM_DEFAULT 0x00000000
  1579. #define DVAM_GETEXACTMAPPINGS 0x00000001
  1580. STDMETHODIMP
  1581. CMap_DeviceValidateActionMap
  1582. (
  1583. PV pdd,
  1584. LPDIACTIONFORMATW paf,
  1585. DWORD dwFlags,
  1586. PDWORD pdwOut
  1587. )
  1588. {
  1589. HRESULT hres;
  1590. LPDIDATAFORMAT dfDev;
  1591. DWORD dwPhysicalGenre;
  1592. LPDIACTIONW pAction;
  1593. PBYTE pMatch;
  1594. UINT idxObj;
  1595. BOOL bHasMap = FALSE;
  1596. BOOL bNewMap = FALSE;
  1597. DWORD dwCommsType = 0;
  1598. PDD this;
  1599. EnterProcI(IDirectInputDeviceCallback::CMap::DeviceValidateActionMap,
  1600. (_ "ppx", pdd, paf, dwFlags));
  1601. this = _thisPv(pdd);
  1602. /*
  1603. * Note, hres is tested before anything is done with dwPhysicalGenre
  1604. */
  1605. switch( GET_DIDEVICE_TYPE(this->dc3.dwDevType) )
  1606. {
  1607. case DI8DEVTYPE_MOUSE:
  1608. dwPhysicalGenre = DIPHYSICAL_MOUSE;
  1609. break;
  1610. case DI8DEVTYPE_KEYBOARD:
  1611. dwPhysicalGenre = DIPHYSICAL_KEYBOARD;
  1612. break;
  1613. case DI8DEVTYPE_DEVICECTRL:
  1614. if( ( GET_DIDEVICE_SUBTYPE( this->dc3.dwDevType ) == DI8DEVTYPEDEVICECTRL_COMMSSELECTION_HARDWIRED )
  1615. || ( GET_DIDEVICE_SUBTYPE( this->dc3.dwDevType ) == DI8DEVTYPEDEVICECTRL_COMMSSELECTION ) )
  1616. {
  1617. dwCommsType = GET_DIDEVICE_SUBTYPE( this->dc3.dwDevType );
  1618. dwPhysicalGenre = DIPHYSICAL_VOICE;
  1619. }
  1620. break;
  1621. default:
  1622. dwPhysicalGenre = 0;
  1623. break;
  1624. }
  1625. if( SUCCEEDED( hres = this->pdcb->lpVtbl->GetDataFormat(this->pdcb, &dfDev) )
  1626. && SUCCEEDED( hres = AllocCbPpv( dfDev->dwNumObjs, &pMatch) ) )
  1627. {
  1628. enum eMap
  1629. {
  1630. eMapTestMatches,
  1631. eMapDeviceExact,
  1632. eMapClassExact,
  1633. eMapEnd
  1634. } iMap, ValidationLimit;
  1635. /*
  1636. * Set the limit on iterations depending on the checks required
  1637. */
  1638. ValidationLimit = ( dwFlags & DVAM_GETEXACTMAPPINGS )
  1639. ? ( dwPhysicalGenre ) ? eMapClassExact : eMapDeviceExact
  1640. : eMapTestMatches;
  1641. for( iMap = eMapTestMatches; SUCCEEDED( hres ) && ( iMap <= ValidationLimit ); iMap++ )
  1642. {
  1643. for( pAction = paf->rgoAction;
  1644. SUCCEEDED( hres ) && ( pAction < &paf->rgoAction[paf->dwNumActions] );
  1645. pAction++ )
  1646. {
  1647. DWORD dwObject;
  1648. if( pAction->dwFlags & DIA_APPNOMAP )
  1649. {
  1650. continue;
  1651. }
  1652. /*
  1653. * If we are mapping exact matches this time, only care
  1654. * about unmapped actions with app mappings.
  1655. */
  1656. if( ( iMap != eMapTestMatches )
  1657. && (!( pAction->dwFlags & DIA_APPMAPPED )
  1658. || ( pAction->dwHow & DIAH_MAPMASK ) ) )
  1659. {
  1660. continue;
  1661. }
  1662. switch( iMap )
  1663. {
  1664. case eMapTestMatches:
  1665. /*
  1666. * These flags have already been validated during semantic
  1667. * validation so just assert them on the first iteration.
  1668. */
  1669. AssertF( ( pAction->dwHow & ~DIAH_VALID ) == 0 );
  1670. AssertF( ( pAction->dwHow & DIAH_ERROR ) == 0 );
  1671. AssertF( ( pAction->dwFlags & ~DIA_VALID ) == 0 );
  1672. /*
  1673. * Only care about pre-existing matches
  1674. */
  1675. if( !( pAction->dwHow & DIAH_MAPMASK ) )
  1676. {
  1677. continue;
  1678. }
  1679. /*
  1680. * Fall through for a GUID match
  1681. */
  1682. case eMapDeviceExact:
  1683. if( !IsEqualGUID( &pAction->guidInstance, &this->guid ) )
  1684. {
  1685. continue;
  1686. }
  1687. break;
  1688. case eMapClassExact:
  1689. if( ( DISEM_GENRE_GET( pAction->dwSemantic ) != DISEM_GENRE_GET( dwPhysicalGenre ) ) )
  1690. {
  1691. continue;
  1692. }
  1693. break;
  1694. default:
  1695. AssertF( !"Invalid iMap" );
  1696. }
  1697. if( ( iMap != eMapTestMatches )
  1698. && ( dwCommsType == DI8DEVTYPEDEVICECTRL_COMMSSELECTION_HARDWIRED ) )
  1699. {
  1700. if( !( pAction->dwHow & DIAH_MAPMASK ) )
  1701. {
  1702. RPF( "ERROR: rgoAction[%d] is trying to map on a hardwired device", pAction - paf->rgoAction );
  1703. hres = DIERR_INVALIDPARAM;
  1704. }
  1705. else if( pAction->dwFlags & DIA_APPMAPPED )
  1706. {
  1707. RPF( "ERROR: rgoAction[%d] is trying to app map on a hardwired device", pAction - paf->rgoAction );
  1708. hres = DIERR_INVALIDPARAM;
  1709. }
  1710. }
  1711. else
  1712. {
  1713. dwObject = pAction->dwObjID;
  1714. /*
  1715. * Now try to find the object.
  1716. * Note, we can't rely on the application data format goo
  1717. * because a data format has probably not been set.
  1718. */
  1719. for( idxObj = 0; idxObj < dfDev->dwNumObjs; idxObj++ )
  1720. {
  1721. /*
  1722. * Ignore FF flags so a FF device can be matched for non-FF
  1723. */
  1724. if( ( ( dwObject ^ dfDev->rgodf[idxObj].dwType )
  1725. &~( DIDFT_FFACTUATOR | DIDFT_FFEFFECTTRIGGER ) ) == 0 )
  1726. {
  1727. break;
  1728. }
  1729. }
  1730. if( idxObj < dfDev->dwNumObjs )
  1731. {
  1732. /*
  1733. * Application mapped controls don't need to have
  1734. * semantics so we cannot test them.
  1735. */
  1736. if( pAction->dwFlags & DIA_APPMAPPED )
  1737. {
  1738. if( pMatch[idxObj] )
  1739. {
  1740. RPF( "ERROR: rgoAction[%d] maps to control 0x%08x which is already mapped",
  1741. pAction - paf->rgoAction, pAction->dwObjID );
  1742. hres = DIERR_INVALIDPARAM;
  1743. break;
  1744. }
  1745. else
  1746. {
  1747. if(!( pAction->dwFlags & DIA_FORCEFEEDBACK )
  1748. || ( dfDev->rgodf[idxObj].dwType & ( DIDFT_FFACTUATOR | DIDFT_FFEFFECTTRIGGER ) ) )
  1749. {
  1750. pMatch[idxObj] = TRUE;
  1751. SquirtSqflPtszV(sqflDf | sqflVerbose,
  1752. TEXT("rgoAction[%d] application mapped to object 0x%08x"),
  1753. pAction - paf->rgoAction, dwObject );
  1754. bHasMap = TRUE;
  1755. if( iMap != eMapTestMatches )
  1756. {
  1757. bNewMap = TRUE;
  1758. pAction->dwHow = DIAH_APPREQUESTED;
  1759. pAction->dwObjID = dwObject;
  1760. if( iMap == eMapClassExact )
  1761. {
  1762. pAction->guidInstance = this->guid;
  1763. }
  1764. }
  1765. }
  1766. else
  1767. {
  1768. RPF( "ERROR: rgoAction[%d] need force feedback but object 0x%08x has none",
  1769. pAction - paf->rgoAction, dwObject );
  1770. hres = DIERR_INVALIDPARAM;
  1771. break;
  1772. }
  1773. }
  1774. }
  1775. else
  1776. {
  1777. AssertF( iMap == eMapTestMatches );
  1778. /*
  1779. * Check the object type matches the semantic
  1780. */
  1781. if( ( c_SemTypeToDFType[ DISEM_TYPEANDMODE_GET( pAction->dwSemantic ) ]
  1782. & DIDFT_GETTYPE( dwObject ) ) )
  1783. {
  1784. if( pMatch[idxObj] )
  1785. {
  1786. RPF( "ERROR: rgoAction[%d] pre-mapped to control 0x%08x which is already mapped",
  1787. pAction - paf->rgoAction, pAction->dwObjID );
  1788. hres = DIERR_INVALIDPARAM;
  1789. break;
  1790. }
  1791. else
  1792. {
  1793. pMatch[idxObj] = TRUE;
  1794. bHasMap = TRUE;
  1795. SquirtSqflPtszV(sqflDf | sqflVerbose,
  1796. TEXT("rgoAction[%d] mapping verifed to object 0x%08x"),
  1797. pAction - paf->rgoAction, dwObject );
  1798. continue;
  1799. }
  1800. }
  1801. else
  1802. {
  1803. RPF( "ERROR: rgoAction[%d] has object type 0x%08x but semantic type 0x%08x",
  1804. pAction - paf->rgoAction, DIDFT_GETTYPE( dwObject ),
  1805. c_SemTypeToDFType[ DISEM_TYPEANDMODE_GET( pAction->dwSemantic ) ] );
  1806. hres = DIERR_INVALIDPARAM;
  1807. break;
  1808. }
  1809. }
  1810. }
  1811. else
  1812. {
  1813. switch( iMap )
  1814. {
  1815. case eMapTestMatches:
  1816. RPF( "ERROR: rgoAction[%d] was mapped (how:0x%08x) to undefined object 0x%08x",
  1817. pAction - paf->rgoAction, pAction->dwHow, pAction->dwObjID );
  1818. hres = DIERR_INVALIDPARAM;
  1819. break;
  1820. case eMapDeviceExact:
  1821. RPF( "ERROR: rgoAction[%d] has application map to undefined object 0x%08x",
  1822. pAction - paf->rgoAction, pAction->dwObjID );
  1823. hres = DIERR_INVALIDPARAM;
  1824. break;
  1825. case eMapClassExact:
  1826. /*
  1827. * If a device class was specified and no match was
  1828. * found it is only an error if the object is
  1829. * invalid for the class.
  1830. */
  1831. hres = CMap_TestSysObject( dwPhysicalGenre, pAction->dwObjID );
  1832. if( FAILED( hres ) )
  1833. {
  1834. RPF( "ERROR: rgoAction[%d] was mapped to object 0x%08x, not valid for device",
  1835. pAction - paf->rgoAction, pAction->dwObjID );
  1836. }
  1837. else
  1838. {
  1839. continue; /* Don't break the loop */
  1840. }
  1841. }
  1842. break;
  1843. }
  1844. }
  1845. }
  1846. if( FAILED( hres ) )
  1847. {
  1848. pAction->dwHow = DIAH_ERROR;
  1849. AssertF( hres == DIERR_INVALIDPARAM );
  1850. break;
  1851. }
  1852. }
  1853. FreePv( pMatch );
  1854. }
  1855. if( SUCCEEDED( hres ) )
  1856. {
  1857. AssertF( hres == S_OK );
  1858. if( dwCommsType == DI8DEVTYPEDEVICECTRL_COMMSSELECTION_HARDWIRED )
  1859. {
  1860. hres = DI_WRITEPROTECT;
  1861. }
  1862. else if(dwFlags == DVAM_DEFAULT)
  1863. {
  1864. //in default case we want to know if there are ANY mappings
  1865. if (!bHasMap)
  1866. hres = DI_NOEFFECT;
  1867. }
  1868. else if(!bNewMap)
  1869. {
  1870. //in non-default case we are interested in ANY NEW mappings
  1871. hres = DI_NOEFFECT;
  1872. }
  1873. }
  1874. *pdwOut = dwCommsType;
  1875. ExitOleProcR();
  1876. return hres;
  1877. }
  1878. /*****************************************************************************
  1879. *
  1880. * @doc INTERNAL
  1881. *
  1882. * @method HRESULT | CMap | BuildDefaultDevActionMap |
  1883. *
  1884. * Get default action map from the action format non system devices.
  1885. *
  1886. * @parm LPDIACTIONFORMATW | pActionFormat |
  1887. *
  1888. * Actions to map.
  1889. *
  1890. * @parm DWORD | dwFlags |
  1891. *
  1892. * Flags used to indicate mapping preferences.
  1893. *
  1894. * @parm REFGUID | guidInstace |
  1895. *
  1896. * Instance guid of device to match.
  1897. *
  1898. * @parm PDIDOBJDEFSEM | rgObjSem |
  1899. *
  1900. * Array of default device object to semantic mappings.
  1901. *
  1902. * @parm DWORD | dwNumAxes |
  1903. *
  1904. * Number of axes in the rgObjSem.
  1905. *
  1906. * @parm DWORD | dwNumPOVs |
  1907. *
  1908. * Number of POVs in the rgObjSem.
  1909. *
  1910. * @parm DWORD | dwNumAxes |
  1911. *
  1912. * Number of buttons in the rgObjSem.
  1913. *
  1914. * @returns
  1915. *
  1916. * <c DI_OK> = <c S_OK>: The operation completed successfully.
  1917. *
  1918. *
  1919. *****************************************************************************/
  1920. STDMETHODIMP CMap_BuildDefaultDevActionMap
  1921. (
  1922. LPDIACTIONFORMATW paf,
  1923. DWORD dwFlags,
  1924. REFGUID guidDevInst,
  1925. PDIDOBJDEFSEM rgObjSem,
  1926. DWORD dwNumAxes,
  1927. DWORD dwNumPOVs,
  1928. DWORD dwNumButtons
  1929. )
  1930. {
  1931. #define IS_OBJECT_USED( dwSem ) ( DISEM_RES_GET( dwSem ) )
  1932. #define MARK_OBJECT_AS_USED( dwSem ) ( dwSem |= DISEM_RES_SET( 1 ) )
  1933. HRESULT hres = S_OK;
  1934. BOOL fSomethingMapped = FALSE;
  1935. PDIDOBJDEFSEM pSemNextPOV;
  1936. PDIDOBJDEFSEM pSemButtons;
  1937. LPDIACTIONW pAction;
  1938. enum eMap
  1939. {
  1940. eMapDeviceSemantic,
  1941. eMapDeviceCompat,
  1942. eMapGenericSemantic,
  1943. eMapGenericCompat,
  1944. eMapEnd
  1945. } iMap;
  1946. EnterProcI(IDirectInputDeviceCallback::CMap::BuildDefaultDevActionMap,
  1947. (_ "pxGpxxx", paf, dwFlags, guidDevInst,
  1948. rgObjSem, dwNumAxes, dwNumPOVs, dwNumButtons ));
  1949. pSemNextPOV = &rgObjSem[dwNumAxes];
  1950. pSemButtons = &rgObjSem[dwNumAxes+dwNumPOVs];
  1951. /*
  1952. * Make an initial pass through to mark already mapped actions
  1953. */
  1954. for( pAction = paf->rgoAction; pAction < &paf->rgoAction[paf->dwNumActions]; pAction++ )
  1955. {
  1956. PDIDOBJDEFSEM pSemExact;
  1957. /*
  1958. * These flags have already been validated during semantic validation,
  1959. */
  1960. AssertF( ( pAction->dwHow & ~DIAH_VALID ) == 0 );
  1961. AssertF( ( pAction->dwHow & DIAH_ERROR ) == 0 );
  1962. AssertF( ( pAction->dwFlags & ~DIA_VALID ) == 0 );
  1963. if( ( pAction->dwFlags & DIA_APPNOMAP )
  1964. ||!( pAction->dwHow & DIAH_MAPMASK )
  1965. ||!IsEqualGUID( &pAction->guidInstance, guidDevInst ) )
  1966. {
  1967. continue;
  1968. }
  1969. /*
  1970. * This object has already been validated so mark it as
  1971. * in use so we don't try to reuse it.
  1972. * No errors should be detected here so use asserts not retail tests
  1973. */
  1974. AssertF( pAction->dwObjID != 0xFFFFFFFF );
  1975. /*
  1976. * Find the object
  1977. */
  1978. for( pSemExact = rgObjSem;
  1979. pSemExact < &rgObjSem[dwNumAxes+dwNumPOVs+dwNumButtons];
  1980. pSemExact++ )
  1981. {
  1982. if( ( ( pSemExact->dwID ^ pAction->dwObjID )
  1983. &~( DIDFT_FFACTUATOR | DIDFT_FFEFFECTTRIGGER ) ) == 0 )
  1984. {
  1985. AssertF( !IS_OBJECT_USED( pSemExact->dwSemantic ) );
  1986. AssertF( DISEM_TYPE_GET( pAction->dwSemantic )
  1987. == DISEM_TYPE_GET( pSemExact->dwSemantic ) );
  1988. MARK_OBJECT_AS_USED( pSemExact->dwSemantic );
  1989. break;
  1990. }
  1991. }
  1992. /*
  1993. * ISSUE-2001/03/29-timgill There should always be an exact action match
  1994. * May need to use tests for duplicates and unmatched controls
  1995. */
  1996. AssertF( pSemExact < &rgObjSem[dwNumAxes+dwNumPOVs+dwNumButtons] );
  1997. }
  1998. for( iMap=0; iMap<eMapEnd; iMap++ )
  1999. {
  2000. for( pAction = paf->rgoAction; pAction < &paf->rgoAction[paf->dwNumActions]; pAction++ )
  2001. {
  2002. /*
  2003. * Do trivial tests first
  2004. */
  2005. if( pAction->dwHow & DIAH_MAPMASK )
  2006. {
  2007. continue;
  2008. }
  2009. if( pAction->dwFlags & DIA_APPNOMAP )
  2010. {
  2011. continue;
  2012. }
  2013. switch( iMap )
  2014. {
  2015. case eMapDeviceSemantic:
  2016. case eMapGenericSemantic:
  2017. if( DISEM_GENRE_GET( pAction->dwSemantic ) == DISEM_GENRE_GET( DISEMGENRE_ANY ) )
  2018. {
  2019. continue; /* No semantic mapping requested */
  2020. }
  2021. if( ( DISEM_TYPE_GET( pAction->dwSemantic ) == DISEM_TYPE_GET( DISEM_TYPE_BUTTON ) )
  2022. && ( DISEM_FLAGS_GET( pAction->dwSemantic ) != 0 ) )
  2023. {
  2024. continue; /* Don't touch link buttons now */
  2025. }
  2026. break;
  2027. case eMapDeviceCompat:
  2028. case eMapGenericCompat:
  2029. if( ( DISEM_GENRE_GET( pAction->dwSemantic ) != DISEM_GENRE_GET( DISEMGENRE_ANY ) )
  2030. && ( DISEM_TYPE_GET( pAction->dwSemantic ) == DISEM_TYPE_GET( DISEM_TYPE_AXIS ) ) )
  2031. {
  2032. continue; /* No generic mapping requested or taken by default */
  2033. }
  2034. break;
  2035. }
  2036. /*
  2037. * Next test that this device suits this action (for this pass)
  2038. */
  2039. if( iMap <= eMapDeviceCompat )
  2040. {
  2041. if( !IsEqualGUID( &pAction->guidInstance, guidDevInst ) )
  2042. {
  2043. continue;
  2044. }
  2045. }
  2046. else if( !IsEqualGUID( &pAction->guidInstance, &GUID_Null) )
  2047. {
  2048. continue;
  2049. }
  2050. else if( ( DISEM_GENRE_GET( pAction->dwSemantic ) == DISEM_GENRE_GET( DIPHYSICAL_MOUSE ) )
  2051. || ( DISEM_GENRE_GET( pAction->dwSemantic ) == DISEM_GENRE_GET( DIPHYSICAL_KEYBOARD ) )
  2052. || ( DISEM_GENRE_GET( pAction->dwSemantic ) == DISEM_GENRE_GET( DIPHYSICAL_VOICE ) ) )
  2053. {
  2054. continue;
  2055. }
  2056. /*
  2057. * Only actions which may be matched on this device get this far.
  2058. */
  2059. switch( iMap )
  2060. {
  2061. case eMapDeviceSemantic:
  2062. case eMapGenericSemantic:
  2063. /*
  2064. * See if a matching control is available.
  2065. */
  2066. switch( DISEM_TYPE_GET( pAction->dwSemantic ) )
  2067. {
  2068. case DISEM_TYPE_GET( DISEM_TYPE_AXIS ):
  2069. {
  2070. DWORD dwAxisType = DISEM_FLAGS_GET( pAction->dwSemantic );
  2071. DWORD dwSemObjType;
  2072. UINT uAxisIdx;
  2073. /*
  2074. * Set up a mask of the type of object we need to find
  2075. */
  2076. dwSemObjType = c_SemTypeToDFType[ DISEM_TYPEANDMODE_GET( pAction->dwSemantic ) ];
  2077. if( pAction->dwFlags & DIA_FORCEFEEDBACK )
  2078. {
  2079. dwSemObjType |= DIDFT_FFACTUATOR;
  2080. }
  2081. for( uAxisIdx = 0; uAxisIdx < dwNumAxes; uAxisIdx++ )
  2082. {
  2083. if( ( dwAxisType == DISEM_FLAGS_GET( rgObjSem[uAxisIdx].dwSemantic ) )
  2084. && ( ( dwSemObjType & rgObjSem[uAxisIdx].dwID ) == dwSemObjType )
  2085. && ( !IS_OBJECT_USED( rgObjSem[uAxisIdx].dwSemantic ) ) )
  2086. {
  2087. pAction->dwObjID = rgObjSem[uAxisIdx].dwID;
  2088. MARK_OBJECT_AS_USED( rgObjSem[uAxisIdx].dwSemantic );
  2089. break;
  2090. }
  2091. }
  2092. if( uAxisIdx >= dwNumAxes )
  2093. {
  2094. continue; /* No matching left */
  2095. }
  2096. break;
  2097. }
  2098. case DISEM_TYPE_GET( DISEM_TYPE_POV ):
  2099. /*
  2100. * Note, no control of POV ordering
  2101. */
  2102. if( ( pSemNextPOV < pSemButtons )
  2103. &&!( pAction->dwFlags & DIA_FORCEFEEDBACK ) )
  2104. {
  2105. pAction->dwObjID = pSemNextPOV->dwID;
  2106. MARK_OBJECT_AS_USED( pSemNextPOV->dwSemantic );
  2107. pSemNextPOV++;
  2108. }
  2109. else
  2110. {
  2111. if( pAction->dwFlags & DIA_FORCEFEEDBACK )
  2112. {
  2113. SquirtSqflPtszV(sqflDf | sqflBenign,
  2114. TEXT( "Not mapping force feedback semantic hat switch (huh?), rgoAction[%d]"),
  2115. pAction - paf->rgoAction );
  2116. }
  2117. continue; /* None left */
  2118. }
  2119. break;
  2120. case DISEM_TYPE_GET( DISEM_TYPE_BUTTON ):
  2121. {
  2122. DWORD dwButtonIdx;
  2123. dwButtonIdx = DISEM_INDEX_GET( pAction->dwSemantic );
  2124. if( dwButtonIdx >= DIAS_INDEX_SPECIAL )
  2125. {
  2126. /*
  2127. * 0xFF used to mean DIBUTTON_ANY.
  2128. * To avoid changing other special indices, still
  2129. * use 0xFF as the base number.
  2130. */
  2131. dwButtonIdx = dwNumButtons - ( 0xFF - dwButtonIdx );
  2132. }
  2133. else
  2134. {
  2135. dwButtonIdx--;
  2136. }
  2137. if( ( dwButtonIdx >= dwNumButtons )
  2138. || ( IS_OBJECT_USED( pSemButtons[dwButtonIdx].dwSemantic ) )
  2139. || ( ( pAction->dwFlags & DIA_FORCEFEEDBACK )
  2140. &&!( pSemButtons[dwButtonIdx].dwID & DIDFT_FFEFFECTTRIGGER ) ) )
  2141. {
  2142. continue; /* No match, no harm */
  2143. }
  2144. else
  2145. {
  2146. pAction->dwObjID = pSemButtons[dwButtonIdx].dwID;
  2147. MARK_OBJECT_AS_USED( pSemButtons[dwButtonIdx].dwSemantic );
  2148. }
  2149. break;
  2150. }
  2151. default:
  2152. RPF( "ERROR Invalid action: rgoAction[%d].dwSemantic 0x%08x",
  2153. pAction - paf->rgoAction, pAction->dwSemantic );
  2154. pAction->dwHow = DIAH_ERROR;
  2155. hres = DIERR_INVALIDPARAM;
  2156. goto error_exit;
  2157. }
  2158. pAction->dwHow = DIAH_DEFAULT;
  2159. break;
  2160. case eMapDeviceCompat:
  2161. case eMapGenericCompat:
  2162. if( ( DISEM_TYPE_GET( pAction->dwSemantic ) == DISEM_TYPE_GET( DISEM_TYPE_BUTTON ) )
  2163. && ( DISEM_FLAGS_GET( pAction->dwSemantic ) != 0 ) )
  2164. {
  2165. LPDIACTIONW pActionTest;
  2166. PDIDOBJDEFSEM pSemTest;
  2167. DWORD dwSemMask;
  2168. /*
  2169. * See if the axis or POV has been mapped
  2170. * Note, there may be no linked object
  2171. */
  2172. if( ( pAction->dwSemantic & DISEM_FLAGS_MASK ) == DISEM_FLAGS_P )
  2173. {
  2174. dwSemMask = DISEM_GROUP_MASK;
  2175. }
  2176. else
  2177. {
  2178. dwSemMask = ( DISEM_FLAGS_MASK | DISEM_GROUP_MASK );
  2179. }
  2180. for( pActionTest = paf->rgoAction; pActionTest < &paf->rgoAction[paf->dwNumActions]; pActionTest++ )
  2181. {
  2182. /*
  2183. * Find the axis or POV for which this button is a fallback
  2184. * Ignore buttons as they are just other fallbacks for the same action
  2185. * Don't do fallbacks for ANY* axes or POVs.
  2186. */
  2187. if( ( DISEM_TYPE_GET( pActionTest->dwSemantic ) != DISEM_TYPE_GET( DISEM_TYPE_BUTTON ) )
  2188. && ( DISEM_GENRE_GET( pAction->dwSemantic ) != DISEM_GENRE_GET( DISEMGENRE_ANY ) )
  2189. && ( ( ( pActionTest->dwSemantic ^ pAction->dwSemantic ) & dwSemMask ) == 0 ) )
  2190. {
  2191. break;
  2192. }
  2193. }
  2194. if( ( pActionTest < &paf->rgoAction[paf->dwNumActions] )
  2195. && ( pActionTest->dwHow & DIAH_MAPMASK ) )
  2196. {
  2197. continue; /* Don't need a fallback */
  2198. }
  2199. /*
  2200. * Find a button
  2201. */
  2202. for( pSemTest = pSemButtons; pSemTest < &pSemButtons[dwNumButtons]; pSemTest++ )
  2203. {
  2204. if( !IS_OBJECT_USED( pSemTest->dwSemantic ) )
  2205. {
  2206. if( ( pAction->dwFlags & DIA_FORCEFEEDBACK )
  2207. &&!( pSemTest->dwID & DIDFT_FFEFFECTTRIGGER ) )
  2208. {
  2209. continue;
  2210. }
  2211. pAction->dwObjID = pSemTest->dwID;
  2212. MARK_OBJECT_AS_USED( pSemTest->dwSemantic );
  2213. break;
  2214. }
  2215. }
  2216. if( pSemTest == &pSemButtons[dwNumButtons] )
  2217. {
  2218. continue; /* None left */
  2219. }
  2220. pAction->dwHow = DIAH_DEFAULT;
  2221. }
  2222. else
  2223. {
  2224. PDIDOBJDEFSEM pSemTest;
  2225. PDIDOBJDEFSEM pSemBound;
  2226. int iDirection = 1;
  2227. DWORD dwSemObjType;
  2228. /*
  2229. * Set up a mask of the type of object we need to find to
  2230. * filter out axes of the wrong mode and non-FF if needed
  2231. */
  2232. dwSemObjType = c_SemTypeToDFType[ DISEM_TYPEANDMODE_GET( pAction->dwSemantic ) ];
  2233. if( pAction->dwFlags & DIA_FORCEFEEDBACK )
  2234. {
  2235. dwSemObjType |= DIDFT_FFACTUATOR | DIDFT_FFEFFECTTRIGGER;
  2236. }
  2237. /*
  2238. * Search the available controls for a match
  2239. */
  2240. switch( DISEM_TYPE_GET( pAction->dwSemantic ) )
  2241. {
  2242. case DISEM_TYPE_GET( DISEM_TYPE_AXIS ):
  2243. pSemTest = rgObjSem;
  2244. pSemBound = &rgObjSem[dwNumAxes];
  2245. /*
  2246. * Filter out axes of the wrong mode and FF caps
  2247. */
  2248. dwSemObjType &= DIDFT_FFACTUATOR | DIDFT_AXIS;
  2249. break;
  2250. case DISEM_TYPE_GET( DISEM_TYPE_POV ):
  2251. if( pAction->dwFlags & DIA_FORCEFEEDBACK )
  2252. {
  2253. SquirtSqflPtszV(sqflDf | sqflBenign,
  2254. TEXT( "Not mapping force feedback compatible hat switch (huh?), rgoAction[%d]"),
  2255. pAction - paf->rgoAction );
  2256. continue;
  2257. }
  2258. pSemTest = pSemNextPOV;
  2259. pSemBound = pSemButtons;
  2260. /*
  2261. * Don't filter POVs any more
  2262. */
  2263. dwSemObjType = 0;
  2264. break;
  2265. case DISEM_TYPE_GET( DISEM_TYPE_BUTTON ):
  2266. /*
  2267. * Note a DIBUTTON_ANY with instance DIAS_INDEX_SPECIAL
  2268. * or greater can be mapped from the end.
  2269. */
  2270. if( DISEM_INDEX_GET( pAction->dwSemantic ) >= DIAS_INDEX_SPECIAL )
  2271. {
  2272. /*
  2273. * For buttons selected from the end, find the last available
  2274. */
  2275. iDirection = -1;
  2276. pSemTest = &rgObjSem[dwNumAxes + dwNumPOVs + dwNumButtons - 1];
  2277. pSemBound = pSemButtons - 1;
  2278. }
  2279. else
  2280. {
  2281. pSemTest = pSemButtons;
  2282. pSemBound = &rgObjSem[dwNumAxes + dwNumPOVs + dwNumButtons];
  2283. }
  2284. /*
  2285. * Filter triggers but just in case, do not distinguish
  2286. * between types of buttons (toggle/push).
  2287. */
  2288. dwSemObjType &= DIDFT_FFEFFECTTRIGGER;
  2289. break;
  2290. }
  2291. while( pSemTest != pSemBound )
  2292. {
  2293. if( !IS_OBJECT_USED( pSemTest->dwSemantic )
  2294. && ( ( dwSemObjType & pSemTest->dwID ) == dwSemObjType )
  2295. && ( ( DISEM_FLAGS_GET( pAction->dwSemantic ) == 0 )
  2296. ||( DISEM_FLAGS_GET( pAction->dwSemantic ) == DISEM_FLAGS_GET( pSemTest->dwSemantic ) ) ) )
  2297. {
  2298. pAction->dwObjID = pSemTest->dwID;
  2299. MARK_OBJECT_AS_USED( pSemTest->dwSemantic );
  2300. break;
  2301. }
  2302. pSemTest += iDirection;
  2303. }
  2304. if( pSemTest == pSemBound )
  2305. {
  2306. continue; /* None left */
  2307. }
  2308. pAction->dwHow = DIAH_DEFAULT;
  2309. }
  2310. break;
  2311. #ifdef XDEBUG
  2312. default:
  2313. AssertF(0);
  2314. #endif
  2315. }
  2316. SquirtSqflPtszV(sqflDf | sqflVerbose,
  2317. TEXT( "Match for action %d is object 0x%08x and how 0x%08x"),
  2318. pAction - paf->rgoAction, pAction->dwObjID, pAction->dwHow );
  2319. /*
  2320. * If we get this far, we have a match.
  2321. * The control object id and dwHow field should have been set
  2322. */
  2323. AssertF( ( pAction->dwHow == DIAH_DEFAULT ) || ( pAction->dwHow == DIAH_APPREQUESTED ) );
  2324. pAction->guidInstance = *guidDevInst;
  2325. SquirtSqflPtszV(sqflDf | sqflVerbose,
  2326. TEXT("Action %d mapped to object id 0x%08x"),
  2327. pAction - paf->rgoAction, pAction->dwObjID );
  2328. fSomethingMapped = TRUE;
  2329. } /* Action loop */
  2330. } /* Match loop */
  2331. /*
  2332. * The result should always be S_OK
  2333. */
  2334. AssertF( hres == S_OK );
  2335. /*
  2336. * If nothing was mapped, let the caller know
  2337. */
  2338. if( !fSomethingMapped )
  2339. {
  2340. hres = DI_NOEFFECT;
  2341. }
  2342. error_exit:;
  2343. ExitOleProc();
  2344. return hres;
  2345. #undef IS_OBJECT_USED
  2346. #undef MARK_OBJECT_AS_USED
  2347. }
  2348. /*****************************************************************************
  2349. *
  2350. * @doc INTERNAL
  2351. *
  2352. * @method HRESULT | CMap | BuildDefaultSysActionMap |
  2353. *
  2354. * Build default action map from the action format for mouse or
  2355. * keyboard devices.
  2356. *
  2357. * @parm LPDIACTIONFORMATW | paf |
  2358. *
  2359. * Actions to map.
  2360. *
  2361. * @parm DWORD | dwFlags |
  2362. *
  2363. * Flags used to indicate mapping preferences.
  2364. *
  2365. * @parm DWORD | dwPhysicalGenre |
  2366. *
  2367. * Device genre to match.
  2368. *
  2369. * @parm REFGUID | guidDevInst |
  2370. *
  2371. * Instance guid of device to match.
  2372. *
  2373. * @parm LPDIDATAFORMAT | dfDev |
  2374. *
  2375. * Internal data format of device.
  2376. *
  2377. * @parm DWORD | dwButtonZeroInst |
  2378. *
  2379. * For mice only, the instance number of the first button.
  2380. *
  2381. * @returns
  2382. *
  2383. * <c DI_OK> = <c S_OK>: The operation completed successfully.
  2384. *
  2385. *
  2386. *****************************************************************************/
  2387. STDMETHODIMP
  2388. CMap_BuildDefaultSysActionMap
  2389. (
  2390. LPDIACTIONFORMATW paf,
  2391. DWORD dwFlags,
  2392. DWORD dwPhysicalGenre,
  2393. REFGUID guidDevInst,
  2394. LPDIDATAFORMAT dfDev,
  2395. DWORD dwButtonZeroInst
  2396. )
  2397. {
  2398. HRESULT hres;
  2399. PBYTE pMatch;
  2400. UINT idxAxis;
  2401. UINT idxButton;
  2402. EnterProcI(IDirectInputDeviceCallback::CMap::BuildDefaultSysActionMap,
  2403. (_ "pxxGpu", paf, dwFlags, dwPhysicalGenre, guidDevInst, dfDev, dwButtonZeroInst ));
  2404. idxButton = 0;
  2405. if( dwPhysicalGenre == DIPHYSICAL_KEYBOARD )
  2406. {
  2407. idxAxis = dfDev->dwNumObjs;
  2408. }
  2409. else
  2410. {
  2411. idxAxis = 0;
  2412. AssertF( dwPhysicalGenre == DIPHYSICAL_MOUSE );
  2413. }
  2414. if( SUCCEEDED( hres = AllocCbPpv( dfDev->dwNumObjs, &pMatch) ) )
  2415. {
  2416. BOOL fSomethingMapped = FALSE;
  2417. enum eMap
  2418. {
  2419. eMapPrevious,
  2420. eMapDeviceSemantic,
  2421. eMapClassSemantic,
  2422. eMapEnd
  2423. } iMap;
  2424. for( iMap=0; iMap<eMapEnd; iMap++ )
  2425. {
  2426. LPDIACTIONW pAction;
  2427. for( pAction = paf->rgoAction; pAction < &paf->rgoAction[paf->dwNumActions]; pAction++ )
  2428. {
  2429. DWORD dwObject;
  2430. UINT idxObj;
  2431. dwObject = (DWORD)-1;
  2432. /*
  2433. * These flags have already been validated during semantic validation,
  2434. */
  2435. AssertF( ( pAction->dwHow & ~DIAH_VALID ) == 0 );
  2436. AssertF( ( pAction->dwHow & DIAH_ERROR ) == 0 );
  2437. AssertF( ( pAction->dwFlags & ~DIA_VALID ) == 0 );
  2438. if( pAction->dwFlags & DIA_APPNOMAP )
  2439. {
  2440. continue;
  2441. }
  2442. if( iMap == eMapPrevious )
  2443. {
  2444. if( !( pAction->dwHow & DIAH_MAPMASK ) )
  2445. {
  2446. continue;
  2447. }
  2448. }
  2449. else
  2450. {
  2451. if( pAction->dwHow & DIAH_MAPMASK )
  2452. {
  2453. continue;
  2454. }
  2455. }
  2456. if( iMap < eMapClassSemantic )
  2457. {
  2458. if( !IsEqualGUID( &pAction->guidInstance, guidDevInst ) )
  2459. {
  2460. continue;
  2461. }
  2462. /*
  2463. * Check that the physical genre is compatible with this device
  2464. */
  2465. if( DISEM_GENRE_GET( pAction->dwSemantic ) != DISEM_GENRE_GET( dwPhysicalGenre ) )
  2466. {
  2467. SquirtSqflPtszV(sqflDf | sqflError,
  2468. TEXT("Device specified for action does not match physical genre"));
  2469. break;
  2470. }
  2471. }
  2472. else
  2473. if( !IsEqualGUID( &pAction->guidInstance, &GUID_Null)
  2474. || ( DISEM_GENRE_GET( pAction->dwSemantic ) != DISEM_GENRE_GET( dwPhysicalGenre ) ) )
  2475. {
  2476. continue;
  2477. }
  2478. if( iMap == eMapPrevious )
  2479. {
  2480. /*
  2481. * This match has already been validated
  2482. */
  2483. SquirtSqflPtszV(sqflDf | sqflVerbose,
  2484. TEXT("Action %d already mapped by 0x%08x to object 0x%08x"),
  2485. pAction - paf->rgoAction, pAction->dwHow, pAction->dwObjID );
  2486. AssertF( pAction->dwObjID != 0xFFFFFFFF );
  2487. /*
  2488. * Find the object index
  2489. */
  2490. for( idxObj = 0; idxObj < dfDev->dwNumObjs; idxObj++ )
  2491. {
  2492. if( ( dfDev->rgodf[idxObj].dwType & DIDFT_FINDMASK ) == ( pAction->dwObjID & DIDFT_FINDMASK ) )
  2493. {
  2494. break;
  2495. }
  2496. }
  2497. if( idxObj < dfDev->dwNumObjs )
  2498. {
  2499. /*
  2500. * Validation should have caught duplicates
  2501. */
  2502. AssertF( !pMatch[idxObj] );
  2503. pMatch[idxObj] = TRUE;
  2504. /*
  2505. * Nothing else to do since we're just counting previous matches
  2506. */
  2507. continue;
  2508. }
  2509. else
  2510. {
  2511. SquirtSqflPtszV(sqflDf | sqflError,
  2512. TEXT("Action %d previously mapped by 0x%08x to unknown object 0x%08x"),
  2513. pAction - paf->rgoAction, pAction->dwHow, pAction->dwObjID );
  2514. }
  2515. }
  2516. else
  2517. {
  2518. DWORD dwSemObjType = c_SemTypeToDFType[ DISEM_TYPEANDMODE_GET( pAction->dwSemantic ) ];
  2519. AssertF( ( iMap == eMapDeviceSemantic ) || ( iMap == eMapClassSemantic ) );
  2520. /*
  2521. * System devices have index of the semantic = object default offset
  2522. * use that to find the object index
  2523. */
  2524. for( idxObj = 0; idxObj < dfDev->dwNumObjs; idxObj++ )
  2525. {
  2526. /*
  2527. * Test that this is an appropriate input type.
  2528. */
  2529. if(!( dfDev->rgodf[idxObj].dwType & dwSemObjType )
  2530. || ( dfDev->rgodf[idxObj].dwType & DIDFT_NODATA ) )
  2531. {
  2532. continue;
  2533. }
  2534. /*
  2535. * All keyboards currently use the same (default)
  2536. * data format so the index can be used directly
  2537. * to match the semantic.
  2538. */
  2539. if( dwPhysicalGenre == DIPHYSICAL_KEYBOARD )
  2540. {
  2541. if( dfDev->rgodf[idxObj].dwOfs != DISEM_INDEX_GET( pAction->dwSemantic ) )
  2542. {
  2543. continue;
  2544. }
  2545. }
  2546. else
  2547. {
  2548. /*
  2549. * Mice are more awkward as HID mice data
  2550. * formats depend on the device so use the
  2551. * dwType instead.
  2552. */
  2553. if( dwSemObjType & DIDFT_BUTTON )
  2554. {
  2555. /*
  2556. * A matching button is offset by the
  2557. * caller supplied button zero instance as
  2558. * the default button zero is instance 3.
  2559. */
  2560. if( DIDFT_GETINSTANCE( dfDev->rgodf[idxObj].dwType ) - (BYTE)dwButtonZeroInst
  2561. != DISEM_INDEX_GET( pAction->dwSemantic ) - DIMOFS_BUTTON0 )
  2562. {
  2563. continue;
  2564. }
  2565. }
  2566. else
  2567. {
  2568. /*
  2569. * All mice have axis instances: x=0, y=1, z=2
  2570. */
  2571. AssertF( dwSemObjType & DIDFT_AXIS );
  2572. if( ( DIDFT_GETINSTANCE( dfDev->rgodf[idxObj].dwType ) << 2 )
  2573. != DISEM_INDEX_GET( pAction->dwSemantic ) )
  2574. {
  2575. continue;
  2576. }
  2577. }
  2578. }
  2579. /*
  2580. * A semantic match has been found
  2581. */
  2582. if( pMatch[idxObj] )
  2583. {
  2584. SquirtSqflPtszV(sqflDf | sqflError,
  2585. TEXT("Action %d maps to already mapped object 0x%08x"),
  2586. pAction - paf->rgoAction, pAction->dwObjID );
  2587. }
  2588. else
  2589. {
  2590. if(!( pAction->dwFlags & DIA_FORCEFEEDBACK )
  2591. || ( dfDev->rgodf[idxObj].dwType & ( DIDFT_FFACTUATOR | DIDFT_FFEFFECTTRIGGER ) ) )
  2592. {
  2593. /*
  2594. * If the game needs FF, only map FF objects
  2595. */
  2596. dwObject = dfDev->rgodf[idxObj].dwType;
  2597. }
  2598. }
  2599. break;
  2600. }
  2601. if( dwObject == (DWORD)-1 )
  2602. {
  2603. if( ( iMap == eMapClassSemantic )
  2604. && SUCCEEDED( CMap_TestSysOffset( dwPhysicalGenre,
  2605. DISEM_INDEX_GET( pAction->dwSemantic ) ) ) )
  2606. {
  2607. /*
  2608. * Don't worry that this device is less capable than some
  2609. */
  2610. continue;
  2611. }
  2612. }
  2613. }
  2614. /*
  2615. * If we get this far, we either have a possible match or the
  2616. * action is invalid. Since we could still find errors, look
  2617. * at matches first.
  2618. */
  2619. if( dwObject != -1 )
  2620. {
  2621. if( idxObj < dfDev->dwNumObjs )
  2622. {
  2623. if( iMap == eMapPrevious )
  2624. {
  2625. /*
  2626. * Validation should have caught duplicates
  2627. */
  2628. AssertF( !pMatch[idxObj] );
  2629. pMatch[idxObj] = TRUE;
  2630. continue;
  2631. }
  2632. else
  2633. {
  2634. if( pMatch[idxObj] )
  2635. {
  2636. SquirtSqflPtszV(sqflDf | sqflError,
  2637. TEXT("Object specified more than once on device"));
  2638. dwObject = (DWORD)-1;
  2639. }
  2640. }
  2641. }
  2642. else
  2643. {
  2644. hres = CMap_TestSysObject( dwPhysicalGenre, dwObject );
  2645. if( SUCCEEDED( hres ) )
  2646. {
  2647. /*
  2648. * Not an unreasonable request so just carry on
  2649. */
  2650. continue;
  2651. }
  2652. else
  2653. {
  2654. dwObject = (DWORD)-1;
  2655. }
  2656. }
  2657. }
  2658. /*
  2659. * We have either a valid object or an error
  2660. */
  2661. if( dwObject != (DWORD)-1 )
  2662. {
  2663. pAction->dwHow = DIAH_DEFAULT;
  2664. pAction->dwObjID = dwObject;
  2665. pAction->guidInstance = *guidDevInst;
  2666. SquirtSqflPtszV(sqflDf | sqflVerbose,
  2667. TEXT("Action %d mapped to object index 0x%08x type 0x%08x"),
  2668. pAction - paf->rgoAction, idxObj, dwObject );
  2669. pMatch[idxObj] = TRUE;
  2670. fSomethingMapped = TRUE;
  2671. }
  2672. else
  2673. {
  2674. /*
  2675. * Mark this action as invalid and quit.
  2676. */
  2677. pAction->dwHow = DIAH_ERROR;
  2678. RPF("ERROR BuildActionMap: arg %d: rgoAction[%d] invalid", 1, pAction - paf->rgoAction );
  2679. RPF( "Semantic 0x%08x", pAction->dwSemantic );
  2680. hres = DIERR_INVALIDPARAM;
  2681. goto free_and_exit;
  2682. }
  2683. } /* Action loop */
  2684. } /* Match loop */
  2685. /*
  2686. * The result should always be a successful memory allocation (S_OK)
  2687. */
  2688. AssertF( hres == S_OK );
  2689. /*
  2690. * If nothing was mapped, let the caller know
  2691. */
  2692. if( !fSomethingMapped )
  2693. {
  2694. hres = DI_NOEFFECT;
  2695. }
  2696. free_and_exit:;
  2697. FreePv( pMatch );
  2698. }
  2699. ExitOleProc();
  2700. return hres;
  2701. }
  2702. /*****************************************************************************
  2703. *
  2704. * @doc INTERNAL
  2705. *
  2706. * @method HRESULT | CMap | ActionMap_IsValidMapObject |
  2707. *
  2708. * Utility function to check a DIACTIONFORMAT structure for validity.
  2709. *
  2710. * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
  2711. *
  2712. * @parm LPDIACTIONFORMAT | paf |
  2713. *
  2714. * Points to a structure that describes the actions needed by the
  2715. * application.
  2716. *
  2717. * @returns
  2718. *
  2719. * Returns a COM error code. The following error codes are
  2720. * intended to be illustrative and not necessarily comprehensive.
  2721. *
  2722. * <c DI_OK> = <c S_OK>: The structure is valid.
  2723. *
  2724. * <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The structure is
  2725. * not valid.
  2726. *
  2727. *****************************************************************************/
  2728. HRESULT
  2729. CDIDev_ActionMap_IsValidMapObject
  2730. (
  2731. LPDIACTIONFORMATW paf
  2732. #ifdef XDEBUG
  2733. comma
  2734. LPCSTR pszProc
  2735. comma
  2736. UINT argnum
  2737. #endif
  2738. )
  2739. {
  2740. HRESULT hres = E_INVALIDARG;
  2741. /*
  2742. * Assert that the structures are the same until the final element
  2743. */
  2744. #if defined(_WIN64)
  2745. CAssertF( ( ( cbX( DIACTIONFORMATW ) - cbX( ((LPDIACTIONFORMATW)0)->tszActionMap ) )
  2746. - ( cbX( DIACTIONFORMATA ) - cbX( ((LPDIACTIONFORMATA)0)->tszActionMap ) )
  2747. < MAX_NATURAL_ALIGNMENT ) );
  2748. #else
  2749. CAssertF( ( cbX( DIACTIONFORMATW ) - cbX( ((LPDIACTIONFORMATW)0)->tszActionMap ) )
  2750. == ( cbX( DIACTIONFORMATA ) - cbX( ((LPDIACTIONFORMATA)0)->tszActionMap ) ) );
  2751. #endif
  2752. CAssertF( FIELD_OFFSET( DIACTIONFORMATW, tszActionMap )
  2753. == FIELD_OFFSET( DIACTIONFORMATA, tszActionMap ) );
  2754. CAssertF( cbX(DIACTIONA) == cbX(DIACTIONW) );
  2755. if( FAILED(hresFullValidWriteNoScramblePvCb_(paf, MAKELONG( cbX(DIACTIONFORMATA), cbX(DIACTIONFORMATW) ), pszProc, argnum)) )
  2756. {
  2757. }
  2758. else if( paf->dwActionSize != cbX(DIACTION) )
  2759. {
  2760. D( RPF("IDirectInputDevice::%s: Invalid DIACTIONFORMAT.dwActionSize 0x%08x",
  2761. pszProc, paf->dwActionSize ); )
  2762. }
  2763. else if( paf->dwDataSize != (paf->dwNumActions * cbX( ((LPDIDEVICEOBJECTDATA)0)->dwData ) ) )
  2764. {
  2765. D( RPF("IDirectInputDevice::%s: DIACTIONFORMAT.dwDataSize 0x%08x not valid for DIACTIONFORMAT.dwNumActions 0x%08x",
  2766. pszProc, paf->dwDataSize ); )
  2767. }
  2768. else if( IsEqualGUID(&paf->guidActionMap, &GUID_Null) )
  2769. {
  2770. D( RPF("IDirectInputDevice::%s: DIACTIONFORMAT.guidActionMap is a NULL GUID", pszProc ); )
  2771. }
  2772. else if( !DISEM_VIRTUAL_GET( paf->dwGenre ) )
  2773. {
  2774. D( RPF("IDirectInputDevice::%s: Invalid (1) DIACTIONFORMAT.dwGenre 0x%08x", pszProc, paf->dwGenre ); )
  2775. }
  2776. else if( DISEM_GENRE_GET( paf->dwGenre ) > DISEM_MAX_GENRE )
  2777. {
  2778. D( RPF("IDirectInputDevice::%s: Invalid (2) DIACTIONFORMAT.dwGenre 0x%08x", pszProc, paf->dwGenre ); )
  2779. }
  2780. else if( ( paf->lAxisMin | paf->lAxisMax ) && ( paf->lAxisMin > paf->lAxisMax ) )
  2781. {
  2782. D( RPF("IDirectInputDevice::%s: Invalid DIACTIONFORMAT.lAxisMin 0x%08x for lAxisMax 0x%08x",
  2783. pszProc, paf->lAxisMin, paf->lAxisMax ); )
  2784. }
  2785. else if( !paf->dwNumActions )
  2786. {
  2787. D( RPF("IDirectInputDevice::%s: DIACTIONFORMAT.dwNumActions is zero", pszProc ); )
  2788. }
  2789. else if( paf->dwNumActions & 0xFF000000 )
  2790. {
  2791. D( RPF("IDirectInputDevice::%s: DIACTIONFORMAT.dwNumActions of 0x%08x is unreasonable", paf->dwNumActions, pszProc ); )
  2792. }
  2793. else if( !paf->rgoAction )
  2794. {
  2795. D( RPF("IDirectInputDevice::%s: DIACTIONFORMAT.rgoAction is NULL", pszProc ); )
  2796. }
  2797. else if( FAILED( hresFullValidWriteNoScramblePvCb_(paf->rgoAction,
  2798. cbX(*paf->rgoAction) * paf->dwNumActions, pszProc, argnum ) ) )
  2799. {
  2800. }
  2801. else
  2802. {
  2803. hres = S_OK;
  2804. }
  2805. /*
  2806. * Warning only tests go here. Only test if everything else was OK.
  2807. */
  2808. #ifdef XDEBUG
  2809. if( SUCCEEDED( hres ) )
  2810. {
  2811. }
  2812. #endif
  2813. return hres;
  2814. }
  2815. /*****************************************************************************
  2816. *
  2817. * @doc INTERNAL
  2818. *
  2819. * @func DWORD | BitwiseReflect |
  2820. *
  2821. * Reflect the bottom bits of a value.
  2822. * Note, this could easily be optimized but it is only used once.
  2823. *
  2824. * @parm IN DWORD | dwValue |
  2825. *
  2826. * The value to be reflected.
  2827. *
  2828. * @parm IN int | iBottom |
  2829. *
  2830. * The number of bits to be reflected.
  2831. *
  2832. * @returns
  2833. * Returns the value dwValue with the bottom iBottom bits reflected
  2834. *
  2835. *****************************************************************************/
  2836. DWORD BitwiseReflect
  2837. (
  2838. DWORD dwValue,
  2839. int iBottom
  2840. )
  2841. {
  2842. int BitIdx;
  2843. DWORD dwTemp = dwValue;
  2844. #define BITMASK(X) (1L << (X))
  2845. for( BitIdx = 0; BitIdx < iBottom; BitIdx++ )
  2846. {
  2847. if( dwTemp & 1L )
  2848. dwValue |= BITMASK( ( iBottom - 1 ) - BitIdx );
  2849. else
  2850. dwValue &= ~BITMASK( ( iBottom - 1 ) - BitIdx );
  2851. dwTemp >>= 1;
  2852. }
  2853. return dwValue;
  2854. #undef BITMASK
  2855. }
  2856. /*****************************************************************************
  2857. *
  2858. * @doc INTERNAL
  2859. *
  2860. * @func HRESULT | CMap_InitializeCRCTable |
  2861. *
  2862. * If needed create and initialize the global table used for CRCs.
  2863. *
  2864. * Allocate memory for the 256 DWORD array and generate a set of
  2865. * values used to calculate a Cyclic Redundancy Check.
  2866. * The algorithm used is supposedly the same as Ethernet's CRC-32.
  2867. *
  2868. * @returns
  2869. * Returns one of the following a COM error codes:
  2870. *
  2871. * <c DI_OK> = <c S_OK>: The operation completed successfully.
  2872. * <c DI_NOEFFECT> = <c S_FALSE>: The table already existed.
  2873. *
  2874. * <c E_OUTOFMEMORY>: insufficient memory available for the table.
  2875. * The GetMapCRC function does not check that the table has been
  2876. * initialized so failure of this function should not allow
  2877. * processing to proceed into any function that uses GetMapCRC.
  2878. *
  2879. *****************************************************************************/
  2880. #define CRC_TABLE_SIZE 256
  2881. #define CRCWIDTH 32
  2882. #define POLY 0x04C11DB7
  2883. HRESULT CMap_InitializeCRCTable( void )
  2884. {
  2885. HRESULT hres;
  2886. int TableIdx;
  2887. if( g_rgdwCRCTable )
  2888. {
  2889. hres = S_FALSE;
  2890. }
  2891. else
  2892. {
  2893. hres = AllocCbPpv( CRC_TABLE_SIZE * cbX( *g_rgdwCRCTable ), &g_rgdwCRCTable );
  2894. if( SUCCEEDED( hres ) )
  2895. {
  2896. /*
  2897. * Special case the first element so it gets a non-zero value
  2898. */
  2899. g_rgdwCRCTable[0] = POLY;
  2900. for( TableIdx = 1; TableIdx < CRC_TABLE_SIZE; TableIdx++ )
  2901. {
  2902. int BitIdx;
  2903. DWORD dwR;
  2904. dwR = BitwiseReflect( TableIdx, 8 ) << ( CRCWIDTH - 8 );
  2905. for( BitIdx = 0; BitIdx < 8; BitIdx++ )
  2906. {
  2907. if( dwR & 0x80000000 )
  2908. dwR = ( dwR << 1 ) ^ POLY;
  2909. else
  2910. dwR <<= 1;
  2911. }
  2912. g_rgdwCRCTable[TableIdx] = BitwiseReflect( dwR, CRCWIDTH );
  2913. /*
  2914. * We do not want a value of zero or identical values to be
  2915. * generated.
  2916. */
  2917. AssertF( g_rgdwCRCTable[TableIdx] );
  2918. AssertF( g_rgdwCRCTable[TableIdx] != g_rgdwCRCTable[TableIdx-1] );
  2919. }
  2920. }
  2921. }
  2922. return hres;
  2923. }
  2924. #undef CRCWIDTH
  2925. /*****************************************************************************
  2926. *
  2927. * @doc INTERNAL
  2928. *
  2929. * @func DWORD | GetMapCRC |
  2930. *
  2931. * Calculate a CRC based on the passed DIACTIONFORMAT contents.
  2932. * The algorithm used is based on "CRC-32" which is supposedly what
  2933. * Ethernet uses, however some changes have been made to suit the
  2934. * specific use.
  2935. *
  2936. * @parm IN LPDIACTIONFORMAT | lpaf |
  2937. *
  2938. * Points to a structure that describes the actions to be mapped
  2939. * for which a CRC is to be generated.
  2940. *
  2941. * @returns
  2942. *
  2943. * The 32 bit CRC as a DWORD value.
  2944. *
  2945. *****************************************************************************/
  2946. DWORD GetMapCRC
  2947. (
  2948. LPDIACTIONFORMATW paf,
  2949. REFGUID guidInst
  2950. )
  2951. {
  2952. DWORD dwCRC;
  2953. PBYTE pBuffer;
  2954. LPCDIACTIONW pAction;
  2955. /*
  2956. * It is the caller's responsibility to make sure g_rgdwCRCTable has
  2957. * been allocated and initialized.
  2958. */
  2959. AssertF( g_rgdwCRCTable );
  2960. /*
  2961. * Initialize to dwNumActions to avoid having to CRC it
  2962. */
  2963. dwCRC = paf->dwNumActions;
  2964. /* Assert that the action map guid and the genre can be tested in series */
  2965. CAssertF( FIELD_OFFSET( DIACTIONFORMATW, dwGenre )
  2966. == FIELD_OFFSET( DIACTIONFORMATW, guidActionMap ) + cbX( paf->guidActionMap ) );
  2967. for( pBuffer = ((PBYTE)&paf->guidActionMap) + cbX( paf->guidActionMap ) + cbX( paf->dwGenre );
  2968. pBuffer >= ((PBYTE)&paf->guidActionMap); pBuffer-- )
  2969. {
  2970. dwCRC = g_rgdwCRCTable[( LOBYTE(dwCRC) ^ *pBuffer )] ^ (dwCRC >> 8);
  2971. }
  2972. /* Assert that the device instance guid and object ID can be tested in series */
  2973. CAssertF( FIELD_OFFSET( DIACTIONW, dwObjID )
  2974. == FIELD_OFFSET( DIACTIONW, guidInstance ) + cbX( pAction->guidInstance ) );
  2975. for( pAction = paf->rgoAction; pAction < &paf->rgoAction[paf->dwNumActions]; pAction++ )
  2976. {
  2977. /*
  2978. * Make sure this action is really relevant before including it in
  2979. * the CRC. It is assumed that any change in which actions are
  2980. * included will be picked up in the resultant CRC.
  2981. */
  2982. if( IsEqualGUID( &pAction->guidInstance, guidInst )
  2983. && ( pAction->dwHow & DIAH_MAPMASK )
  2984. && ( ( pAction->dwFlags & DIA_APPNOMAP ) == 0 ) )
  2985. {
  2986. /*
  2987. * Besides which actions are taken into account, the only fields
  2988. * need to be verified are those that would change a SetActionMap.
  2989. * Although flags such as DIA_FORCEFEEDBACK could alter the
  2990. * mappings they do not change a SetActionMap.
  2991. */
  2992. for( pBuffer = ((PBYTE)&pAction->guidInstance) + cbX( pAction->guidInstance ) + cbX( pAction->dwObjID );
  2993. pBuffer >= ((PBYTE)&pAction->guidInstance); pBuffer-- )
  2994. {
  2995. dwCRC = g_rgdwCRCTable[( LOBYTE(dwCRC) ^ *pBuffer )] ^ (dwCRC >> 8);
  2996. }
  2997. }
  2998. }
  2999. return dwCRC;
  3000. }
  3001. /*****************************************************************************
  3002. *
  3003. * @doc INTERNAL
  3004. *
  3005. * @method HRESULT | IDirectInputDevice | CDIDev_BuildActionMapCore |
  3006. *
  3007. * Worker function for BuildActionMapA and BuildActionMapW.
  3008. * This does the real work once the external entry points have done
  3009. * dialect specific validation and set up.
  3010. *
  3011. *****************************************************************************/
  3012. STDMETHODIMP CDIDev_BuildActionMapCore
  3013. (
  3014. PDD this,
  3015. LPDIACTIONFORMATW paf,
  3016. LPCWSTR lpwszUserName,
  3017. DWORD dwFlags
  3018. )
  3019. {
  3020. HRESULT hres;
  3021. EnterProcI(CDIDev_BuildActionMapCore, (_ "pWx", paf, lpwszUserName, dwFlags ));
  3022. // This application uses the mapper
  3023. AhAppRegister(this->dwVersion, 0x1);
  3024. switch( dwFlags & ( DIDBAM_PRESERVE | DIDBAM_INITIALIZE | DIDBAM_HWDEFAULTS ) )
  3025. {
  3026. case DIDBAM_DEFAULT:
  3027. case DIDBAM_PRESERVE:
  3028. case DIDBAM_INITIALIZE:
  3029. case DIDBAM_HWDEFAULTS:
  3030. hres = S_OK;
  3031. break;
  3032. default:
  3033. RPF("ERROR %s: arg %d: Must not combine "
  3034. "DIDBAM_PRESERVE, DIDBAM_INITIALIZE and DIDBAM_HWDEFAULTS", s_szProc, 3);
  3035. hres = E_INVALIDARG;
  3036. }
  3037. if( SUCCEEDED(hres)
  3038. && SUCCEEDED(hres = hresFullValidFl(dwFlags, DIDBAM_VALID, 3))
  3039. && SUCCEEDED(hres = CMap_ValidateActionMapSemantics( paf, dwFlags ) ) )
  3040. {
  3041. LPDIPROPSTRING pdipMapFile;
  3042. if( SUCCEEDED( hres = AllocCbPpv(cbX(*pdipMapFile), &pdipMapFile) ) )
  3043. {
  3044. HRESULT hresExactMaps = E_FAIL;
  3045. PWCHAR pwszMapFile;
  3046. DWORD dwCommsType;
  3047. if( dwFlags & DIDBAM_HWDEFAULTS )
  3048. {
  3049. hres = CMap_DeviceValidateActionMap( (PV)this, paf, DVAM_DEFAULT, &dwCommsType );
  3050. hresExactMaps = hres;
  3051. if( hres == S_OK )
  3052. {
  3053. hresExactMaps = DI_NOEFFECT;
  3054. }
  3055. }
  3056. else
  3057. {
  3058. /*
  3059. * Do a generic test and map of any exact device matches
  3060. */
  3061. hres = CMap_DeviceValidateActionMap( (PV)this, paf, DVAM_GETEXACTMAPPINGS, &dwCommsType );
  3062. if( SUCCEEDED( hres ) )
  3063. {
  3064. /*
  3065. * Save the exact mapping result to combine with the result
  3066. * of semantic mapping.
  3067. */
  3068. hresExactMaps = hres;
  3069. }
  3070. }
  3071. if( SUCCEEDED( hres ) )
  3072. {
  3073. pdipMapFile->diph.dwSize = cbX(DIPROPSTRING);
  3074. pdipMapFile->diph.dwHeaderSize = cbX(DIPROPHEADER);
  3075. pdipMapFile->diph.dwObj = 0;
  3076. pdipMapFile->diph.dwHow = DIPH_DEVICE;
  3077. /*
  3078. * Try to get a configured mapping.
  3079. * If there is no IHV file there may still be a user file
  3080. */
  3081. hres = CDIDev_GetPropertyW( &this->ddW, DIPROP_MAPFILE, &pdipMapFile->diph );
  3082. pwszMapFile = SUCCEEDED( hres ) ? pdipMapFile->wsz : NULL;
  3083. if( dwCommsType )
  3084. {
  3085. /*
  3086. * Communications control device
  3087. */
  3088. if( !pwszMapFile )
  3089. {
  3090. /*
  3091. * If there's no IHV mapping there's nothing to add.
  3092. */
  3093. hres = DI_NOEFFECT;
  3094. }
  3095. else
  3096. {
  3097. /*
  3098. * Modify the genre over the call to the mapper so
  3099. * that we get physical genre mappings from the IHV
  3100. * file if no user mappings are available
  3101. */
  3102. DWORD dwAppGenre = paf->dwGenre;
  3103. paf->dwGenre = DIPHYSICAL_VOICE;
  3104. hres = this->pMS->lpVtbl->GetActionMap(this->pMS, &this->guid, pwszMapFile,
  3105. (LPDIACTIONFORMATW)paf, lpwszUserName, NULL, dwFlags );
  3106. /*
  3107. * ISSUE-2001/03/29-timgill Only want the timestamp for hardcoded devices
  3108. * ->Read again for mappings
  3109. */
  3110. if( ( dwCommsType == DI8DEVTYPEDEVICECTRL_COMMSSELECTION_HARDWIRED )
  3111. &&!( dwFlags & DIDBAM_HWDEFAULTS ) )
  3112. {
  3113. DWORD dwLowTime;
  3114. DWORD dwHighTime;
  3115. dwLowTime = paf->ftTimeStamp.dwLowDateTime;
  3116. dwHighTime = paf->ftTimeStamp.dwHighDateTime;
  3117. hres = this->pMS->lpVtbl->GetActionMap(this->pMS, &this->guid, pwszMapFile,
  3118. (LPDIACTIONFORMATW)paf, lpwszUserName, NULL, DIDBAM_HWDEFAULTS );
  3119. paf->ftTimeStamp.dwLowDateTime = dwLowTime;
  3120. paf->ftTimeStamp.dwHighDateTime = dwHighTime;
  3121. }
  3122. paf->dwGenre = dwAppGenre;
  3123. if( SUCCEEDED( hres ) )
  3124. {
  3125. if( hres == S_NOMAP )
  3126. {
  3127. /*
  3128. * Make sure we do not attempt defaults and
  3129. * the exact match return code gets back to the
  3130. * caller
  3131. */
  3132. hres = DI_NOEFFECT;
  3133. }
  3134. }
  3135. else
  3136. {
  3137. /*
  3138. * If there was an error, there are no defaults
  3139. * so quit now.
  3140. */
  3141. if( ( HRESULT_FACILITY( hres ) == FACILITY_ITF )
  3142. && ( HRESULT_CODE( hres ) > 0x0600 ) )
  3143. {
  3144. AssertF( HRESULT_CODE( hres ) < 0x0680 );
  3145. RPF( "Internal GetActionMap error 0x%08x for hardwired device", hres );
  3146. hres = DIERR_MAPFILEFAIL;
  3147. }
  3148. goto FreeAndExitCDIDev_BuildActionMapCore;
  3149. }
  3150. }
  3151. }
  3152. else if( !pwszMapFile && ( dwFlags & DIDBAM_HWDEFAULTS ) )
  3153. {
  3154. /*
  3155. * Make sure we get defaults
  3156. */
  3157. SquirtSqflPtszV(sqflDf | sqflBenign,
  3158. TEXT("Failed to GetProperty DIPROP_MAPFILE 0x%08x, default will be generated"), hres );
  3159. hres = S_NOMAP;
  3160. }
  3161. else
  3162. {
  3163. hres = this->pMS->lpVtbl->GetActionMap(this->pMS, &this->guid, pwszMapFile,
  3164. (LPDIACTIONFORMATW)paf, lpwszUserName, NULL, dwFlags);
  3165. if( ( paf->ftTimeStamp.dwHighDateTime == DIAFTS_UNUSEDDEVICEHIGH )
  3166. && ( paf->ftTimeStamp.dwLowDateTime == DIAFTS_UNUSEDDEVICELOW )
  3167. && SUCCEEDED( hres ) )
  3168. {
  3169. /*
  3170. * If the device has never been used, the saved
  3171. * mappings are either the IHV defaults or cooked up
  3172. * defaults. Unfortunately, DIMap will have marked
  3173. * them as DIAH_USERCONFIG. Some day DIMap should
  3174. * either return the true flags or return without
  3175. * changing the flags, until then, reset all the
  3176. * flags and then do a second request for defaults.
  3177. */
  3178. LPDIACTIONW pAction;
  3179. for( pAction = paf->rgoAction; pAction < &paf->rgoAction[paf->dwNumActions]; pAction++ )
  3180. {
  3181. if( ( ( pAction->dwFlags & ( DIA_APPMAPPED | DIA_APPNOMAP ) ) == 0 )
  3182. && ( pAction->dwHow & DIAH_USERCONFIG )
  3183. && IsEqualGUID( &pAction->guidInstance, &this->guid ) )
  3184. {
  3185. pAction->dwHow = DIAH_UNMAPPED;
  3186. }
  3187. }
  3188. hres = this->pMS->lpVtbl->GetActionMap(this->pMS, &this->guid, pwszMapFile,
  3189. (LPDIACTIONFORMATW)paf, lpwszUserName, NULL, DIDBAM_HWDEFAULTS);
  3190. /*
  3191. * Make sure the timestamps are still set for unused.
  3192. */
  3193. paf->ftTimeStamp.dwLowDateTime = DIAFTS_UNUSEDDEVICELOW;
  3194. paf->ftTimeStamp.dwHighDateTime = DIAFTS_UNUSEDDEVICEHIGH;
  3195. }
  3196. if( FAILED( hres ) )
  3197. {
  3198. if( ( HRESULT_FACILITY( hres ) == FACILITY_ITF )
  3199. && ( HRESULT_CODE( hres ) > 0x0600 ) )
  3200. {
  3201. AssertF( HRESULT_CODE( hres ) < 0x0680 );
  3202. RPF( "Internal GetActionMap error 0x%08x for configurable device", hres );
  3203. hres = DIERR_MAPFILEFAIL;
  3204. }
  3205. }
  3206. }
  3207. }
  3208. if( SUCCEEDED( hresExactMaps ) )
  3209. {
  3210. /*
  3211. * If we took an IHV mapping, do a default mapping on top.
  3212. * This allows IHVs to only map objects that are special
  3213. * leaving other objects to be used for whatever semantics
  3214. * match.
  3215. * Some day we should have a return code that indicates which
  3216. * type of mapping DIMap produced, until then, search the
  3217. * mappings for a HWDefault.
  3218. */
  3219. if( SUCCEEDED( hres ) && ( hres != S_NOMAP )
  3220. && ( dwCommsType != DI8DEVTYPEDEVICECTRL_COMMSSELECTION_HARDWIRED ) )
  3221. {
  3222. LPDIACTIONW pAction;
  3223. for( pAction = paf->rgoAction; pAction < &paf->rgoAction[paf->dwNumActions]; pAction++ )
  3224. {
  3225. if( ( ( pAction->dwFlags & ( DIA_APPMAPPED | DIA_APPNOMAP ) ) == 0 )
  3226. && ( pAction->dwHow & DIAH_HWDEFAULT )
  3227. && IsEqualGUID( &pAction->guidInstance, &this->guid ) )
  3228. {
  3229. hres = S_NOMAP;
  3230. break;
  3231. }
  3232. }
  3233. }
  3234. if( FAILED( hres ) || ( hres == S_NOMAP ) )
  3235. {
  3236. hres = this->pdcb->lpVtbl->BuildDefaultActionMap( this->pdcb, paf, dwFlags, &this->guid );
  3237. if( SUCCEEDED( hres ) )
  3238. {
  3239. paf->ftTimeStamp.dwLowDateTime = DIAFTS_NEWDEVICELOW;
  3240. paf->ftTimeStamp.dwHighDateTime = DIAFTS_NEWDEVICEHIGH;
  3241. SquirtSqflPtszV(sqflDf | sqflVerbose, TEXT("Default action map used"));
  3242. }
  3243. else
  3244. {
  3245. SquirtSqflPtszV(sqflDf | sqflError, TEXT("Default action map failed"));
  3246. }
  3247. }
  3248. else
  3249. {
  3250. hres = CMap_DeviceValidateActionMap( (PV)this, paf, DVAM_DEFAULT, &dwCommsType );
  3251. if( SUCCEEDED( hres ) )
  3252. {
  3253. SquirtSqflPtszV(sqflDf | sqflVerbose, TEXT("Action map validated"));
  3254. }
  3255. else
  3256. {
  3257. RPF( "Initially valid action map invalidated by mapper!" );
  3258. }
  3259. }
  3260. /*
  3261. * If no semantics mapped, return the exact map result.
  3262. */
  3263. if( hres == DI_NOEFFECT )
  3264. {
  3265. hres = hresExactMaps;
  3266. }
  3267. if( dwFlags & DIDBAM_HWDEFAULTS )
  3268. {
  3269. /*
  3270. * Timestamps are meaningless for hardware defaults
  3271. * so just make sure the values are consistent.
  3272. */
  3273. paf->ftTimeStamp.dwLowDateTime = DIAFTS_UNUSEDDEVICELOW;
  3274. paf->ftTimeStamp.dwHighDateTime = DIAFTS_UNUSEDDEVICEHIGH;
  3275. }
  3276. else if( ( paf->ftTimeStamp.dwHighDateTime == DIAFTS_NEWDEVICEHIGH )
  3277. && ( paf->ftTimeStamp.dwLowDateTime == DIAFTS_NEWDEVICELOW )
  3278. && SUCCEEDED( hres ) )
  3279. {
  3280. if( FAILED( this->pMS->lpVtbl->SaveActionMap( this->pMS, &this->guid,
  3281. pwszMapFile, paf, lpwszUserName, dwFlags ) ) )
  3282. {
  3283. SquirtSqflPtszV(sqflDf | sqflBenign,
  3284. TEXT("Failed to save action map on first use 0x%08x"), hres );
  3285. paf->ftTimeStamp.dwLowDateTime = DIAFTS_UNUSEDDEVICELOW;
  3286. paf->ftTimeStamp.dwHighDateTime = DIAFTS_UNUSEDDEVICEHIGH;
  3287. /*
  3288. * Don't return an internal DIMap result, convert it to a published one
  3289. */
  3290. if( ( HRESULT_FACILITY( hres ) == FACILITY_ITF )
  3291. && ( HRESULT_CODE( hres ) > 0x0600 ) )
  3292. {
  3293. AssertF( HRESULT_CODE( hres ) < 0x0680 );
  3294. hres = DIERR_MAPFILEFAIL;
  3295. }
  3296. }
  3297. }
  3298. }
  3299. else
  3300. {
  3301. SquirtSqflPtszV(sqflDf | sqflError, TEXT("Invalid mappings in action array"));
  3302. }
  3303. FreeAndExitCDIDev_BuildActionMapCore:;
  3304. FreePv( pdipMapFile );
  3305. if( SUCCEEDED( hres ) )
  3306. {
  3307. paf->dwCRC = GetMapCRC( paf, &this->guid );
  3308. }
  3309. }
  3310. else
  3311. {
  3312. /*
  3313. * Note, we could try to do a default map even though we could not
  3314. * allocate space for the file name property but if allocations
  3315. * are failing we're better of quitting ASAP.
  3316. */
  3317. SquirtSqflPtszV(sqflDf | sqflError, TEXT("Mem allocation failure") );
  3318. }
  3319. }
  3320. #if 0
  3321. {
  3322. LPDIACTIONW pAction;
  3323. RPF( "Action map leaving build" );
  3324. // RPF( "Act# Semantic Flags Object How App Data" );
  3325. RPF( "A# Semantic Device Object How" );
  3326. for( pAction = paf->rgoAction; pAction < &paf->rgoAction[paf->dwNumActions]; pAction++ )
  3327. {
  3328. RPF( "%02d %08x {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X} %08x %08x",
  3329. pAction - paf->rgoAction,
  3330. pAction->dwSemantic,
  3331. pAction->guidInstance.Data1, pAction->guidInstance.Data2, pAction->guidInstance.Data3,
  3332. pAction->guidInstance.Data4[0], pAction->guidInstance.Data4[1],
  3333. pAction->guidInstance.Data4[2], pAction->guidInstance.Data4[3],
  3334. pAction->guidInstance.Data4[4], pAction->guidInstance.Data4[5],
  3335. pAction->guidInstance.Data4[6], pAction->guidInstance.Data4[7],
  3336. pAction->dwObjID,
  3337. pAction->dwHow,
  3338. pAction->uAppData );
  3339. // RPF( "%02d %08x %08x %08x %08x %08x",
  3340. // pAction - paf->rgoAction,
  3341. // pAction->dwSemantic,
  3342. // pAction->dwFlags,
  3343. // pAction->dwObjID,
  3344. // pAction->dwHow,
  3345. // pAction->uAppData );
  3346. }
  3347. RPF( "--" );
  3348. }
  3349. #endif
  3350. ExitOleProc();
  3351. return hres;
  3352. }
  3353. /*****************************************************************************
  3354. *
  3355. * @doc EXTERNAL
  3356. *
  3357. * @method HRESULT | IDirectInputDevice | BuildActionMap |
  3358. *
  3359. * Obtains the mapping of actions described in the <t DIACTIONFORMAT>
  3360. * to controls for this device.
  3361. * Information about user preferences and hardware manufacturer
  3362. * provided defaults is used to create the association between game
  3363. * actions and device controls.
  3364. *
  3365. * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
  3366. *
  3367. * @parm LPDIACTIONFORMAT | paf |
  3368. *
  3369. * Points to a structure that describes the actions needed by the
  3370. * application.
  3371. *
  3372. * @parm LPCSTR | lpszUserName |
  3373. *
  3374. * Name of user for whom mapping is requested. This may be a NULL
  3375. * pointer in which case the current user is assumed.
  3376. *
  3377. * @parm DWORD | dwFlags |
  3378. *
  3379. * Flags used to control the mapping. Must be a valid combination
  3380. * of the <c DIDBAM_*> flags.
  3381. *
  3382. * @returns
  3383. *
  3384. * Returns a COM error code. The following error codes are
  3385. * intended to be illustrative and not necessarily comprehensive.
  3386. *
  3387. * <c DI_OK> = <c S_OK>: The operation completed successfully.
  3388. *
  3389. * <c DI_NOEFFECT> = <c S_OK>: The operation completed successfully
  3390. * but no actions were mapped.
  3391. *
  3392. * <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: A parameter is invalid.
  3393. *
  3394. *****************************************************************************/
  3395. STDMETHODIMP CDIDev_BuildActionMapW
  3396. (
  3397. PV pdidW,
  3398. LPDIACTIONFORMATW paf,
  3399. LPCWSTR lpwszUserName,
  3400. DWORD dwFlags
  3401. )
  3402. {
  3403. HRESULT hres;
  3404. EnterProcR(IDirectInputDevice8W::BuildActionMap, (_ "ppWx", pdidW, paf, lpwszUserName, dwFlags));
  3405. if( SUCCEEDED(hres = hresPvW( pdidW ) )
  3406. && SUCCEEDED(hres = CDIDev_ActionMap_IsValidMapObject( paf D(comma s_szProc comma 1) ) ) )
  3407. {
  3408. if( paf->dwSize != cbX(DIACTIONFORMATW) )
  3409. {
  3410. D( RPF("IDirectInputDevice::%s: Invalid DIACTIONFORMATW.dwSize 0x%08x",
  3411. s_szProc, paf->dwSize ); )
  3412. hres = E_INVALIDARG;
  3413. }
  3414. else
  3415. {
  3416. LPWSTR pwszGoodUserName;
  3417. hres = GetWideUserName( NULL, lpwszUserName, &pwszGoodUserName );
  3418. if( SUCCEEDED( hres ) )
  3419. {
  3420. hres = CDIDev_BuildActionMapCore( _thisPvNm(pdidW, ddW), paf, lpwszUserName, dwFlags );
  3421. if( !lpwszUserName )
  3422. {
  3423. FreePv( pwszGoodUserName );
  3424. }
  3425. }
  3426. }
  3427. }
  3428. ExitOleProc();
  3429. return hres;
  3430. }
  3431. /*****************************************************************************
  3432. *
  3433. * @doc INTERNAL
  3434. *
  3435. * @method HRESULT | IDirectInputDevice | BuildActionMapA |
  3436. *
  3437. * ANSI version of BuildActionMap.
  3438. *
  3439. *****************************************************************************/
  3440. STDMETHODIMP CDIDev_BuildActionMapA
  3441. (
  3442. PV pdidA,
  3443. LPDIACTIONFORMATA pafA,
  3444. LPCSTR lpszUserName,
  3445. DWORD dwFlags
  3446. )
  3447. {
  3448. HRESULT hres;
  3449. EnterProcR(IDirectInputDevice8A::BuildActionMap, (_ "ppAx", pdidA, pafA, lpszUserName, dwFlags));
  3450. if( SUCCEEDED(hres = hresPvA( pdidA ) )
  3451. && SUCCEEDED(hres = CDIDev_ActionMap_IsValidMapObject( (LPDIACTIONFORMATW)pafA D(comma s_szProc comma 1) ) ) )
  3452. {
  3453. if( pafA->dwSize != cbX(DIACTIONFORMATA) )
  3454. {
  3455. D( RPF("IDirectInputDevice::%s: Invalid DIACTIONFORMATA.dwSize 0x%08x",
  3456. s_szProc, pafA->dwSize ); )
  3457. hres = E_INVALIDARG;
  3458. }
  3459. else
  3460. {
  3461. LPWSTR pwszGoodUserName;
  3462. hres = GetWideUserName( lpszUserName, NULL, &pwszGoodUserName );
  3463. if( SUCCEEDED( hres ) )
  3464. {
  3465. /*
  3466. * For the sake of the mapper DLLs validation set the size to
  3467. * the UNICODE version. If we ever send this to an external
  3468. * component we should do this differently.
  3469. */
  3470. pafA->dwSize = cbX(DIACTIONFORMATW);
  3471. hres = CDIDev_BuildActionMapCore( _thisPvNm(pdidA, ddA), (LPDIACTIONFORMATW)pafA, pwszGoodUserName, dwFlags );
  3472. pafA->dwSize = cbX(DIACTIONFORMATA);
  3473. FreePv( pwszGoodUserName );
  3474. }
  3475. }
  3476. }
  3477. ExitOleProc();
  3478. return hres;
  3479. }
  3480. /*****************************************************************************
  3481. *
  3482. * @doc INTERNAL
  3483. *
  3484. * @method HRESULT | IDirectInputDevice | CDIDev_SaveActionMap |
  3485. *
  3486. * Worker function for SetActionMapA and SetActionMapW.
  3487. * Used to save an actions map.
  3488. *
  3489. *****************************************************************************/
  3490. STDMETHODIMP CDIDev_SaveActionMap
  3491. (
  3492. PDD this,
  3493. LPDIACTIONFORMATW paf,
  3494. LPWSTR lpwszUserName,
  3495. DWORD dwFlags
  3496. )
  3497. {
  3498. HRESULT hres;
  3499. LPDIPROPSTRING pdipMapFile;
  3500. EnterProcI(CDIDev_SaveActionMap, (_ "pWAx", paf, lpwszUserName, dwFlags));
  3501. if( SUCCEEDED( hres = AllocCbPpv(cbX(*pdipMapFile), &pdipMapFile) ) )
  3502. {
  3503. DWORD dwLowTime;
  3504. DWORD dwHighTime;
  3505. // Save user's pass-in timestamps
  3506. dwLowTime = paf->ftTimeStamp.dwLowDateTime;
  3507. dwHighTime = paf->ftTimeStamp.dwHighDateTime;
  3508. pdipMapFile->diph.dwSize = cbX(DIPROPSTRING);
  3509. pdipMapFile->diph.dwHeaderSize = cbX(DIPROPHEADER);
  3510. pdipMapFile->diph.dwObj = 0;
  3511. pdipMapFile->diph.dwHow = DIPH_DEVICE;
  3512. paf->ftTimeStamp.dwLowDateTime = DIAFTS_UNUSEDDEVICELOW;
  3513. paf->ftTimeStamp.dwHighDateTime = DIAFTS_UNUSEDDEVICEHIGH;
  3514. hres = CDIDev_GetPropertyW( &this->ddW, DIPROP_MAPFILE, &pdipMapFile->diph );
  3515. hres = this->pMS->lpVtbl->SaveActionMap( this->pMS, &this->guid,
  3516. /* No map file if the GetProperty failed */
  3517. (SUCCEEDED( hres )) ? pdipMapFile->wsz : NULL,
  3518. paf, lpwszUserName, dwFlags );
  3519. // restore user's pass-in timestamps
  3520. paf->ftTimeStamp.dwLowDateTime = dwLowTime;
  3521. paf->ftTimeStamp.dwHighDateTime = dwHighTime;
  3522. FreePv( pdipMapFile );
  3523. }
  3524. ExitOleProc();
  3525. return hres;
  3526. }
  3527. /*****************************************************************************
  3528. *
  3529. * @doc INTERNAL
  3530. *
  3531. * @method HRESULT | CDIDev | ParseActionFormat |
  3532. *
  3533. * Parse the action format passed by the application and
  3534. * convert it into a format that we can use to translate
  3535. * the device data into application data.
  3536. *
  3537. * @parm IN LPDIACTIONFORMAT | lpaf |
  3538. *
  3539. * Points to a structure that describes the actions to be mapped
  3540. * to this device.
  3541. *
  3542. * @returns
  3543. *
  3544. * Returns a COM error code. The following error codes are
  3545. * intended to be illustrative and not necessarily comprehensive.
  3546. *
  3547. * <c DI_OK> = <c S_OK>: The operation completed successfully.
  3548. *
  3549. * <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
  3550. * <p lpvData> parameter is not a valid pointer.
  3551. *
  3552. *
  3553. *****************************************************************************/
  3554. STDMETHODIMP
  3555. CDIDev_ParseActionFormat
  3556. (
  3557. PDD this,
  3558. LPDIACTIONFORMATW paf
  3559. )
  3560. {
  3561. PDIXLAT pdix;
  3562. PINT rgiobj;
  3563. VXDDATAFORMAT vdf;
  3564. HRESULT hres;
  3565. #ifdef DEBUG
  3566. EnterProc(CDIDev_ParseActionFormat, (_ "pp", this, paf));
  3567. #endif
  3568. /*
  3569. * Caller should've nuked the old translation table.
  3570. */
  3571. AssertF(this->pdix == 0);
  3572. AssertF(this->rgiobj == 0);
  3573. if( paf->dwDataSize != paf->dwNumActions * cbX( ((LPDIDEVICEOBJECTDATA)0)->dwData ) )
  3574. {
  3575. SquirtSqflPtszV(sqflDf | sqflError,
  3576. TEXT("Incorrect dwDataSize (0x%08X) for dwNumActions (0x%08X)"),
  3577. paf->dwDataSize, paf->dwNumActions );
  3578. hres = E_INVALIDARG;
  3579. goto done_without_free;
  3580. }
  3581. vdf.cbData = this->df.dwDataSize;
  3582. vdf.pDfOfs = 0;
  3583. rgiobj = 0;
  3584. if( SUCCEEDED(hres = AllocCbPpv(cbCxX(this->df.dwNumObjs, DIXLAT), &pdix))
  3585. && SUCCEEDED(hres = AllocCbPpv(cbCdw(this->df.dwDataSize), &vdf.pDfOfs))
  3586. && SUCCEEDED(hres = AllocCbPpv(cbCdw(paf->dwDataSize), &rgiobj)) )
  3587. {
  3588. LPCDIACTIONW pAction;
  3589. DIPROPDWORD dipdw;
  3590. DIPROPRANGE diprange;
  3591. DWORD dwAxisMode = DIPROPAXISMODE_REL;
  3592. BOOL fSomethingMapped = FALSE;
  3593. BOOL fAxisModeKnown = FALSE;
  3594. /*
  3595. * Pre-init all the translation tags to -1,
  3596. * which means "not in use"
  3597. */
  3598. memset(pdix, 0xFF, cbCxX(this->df.dwNumObjs, DIXLAT));
  3599. memset(vdf.pDfOfs, 0xFF, cbCdw(this->df.dwDataSize));
  3600. memset(rgiobj, 0xFF, cbCdw(paf->dwDataSize) );
  3601. /*
  3602. * Set up the property invariants
  3603. */
  3604. dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  3605. dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  3606. dipdw.diph.dwHow = DIPH_BYID;
  3607. diprange.diph.dwSize = sizeof(DIPROPRANGE);
  3608. diprange.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  3609. diprange.diph.dwHow = DIPH_BYID;
  3610. diprange.lMin = paf->lAxisMin;
  3611. diprange.lMax = paf->lAxisMax;
  3612. for( pAction = paf->rgoAction; pAction < &paf->rgoAction[paf->dwNumActions]; pAction++ )
  3613. {
  3614. if( IsEqualGUID( &pAction->guidInstance, &this->guid ) )
  3615. {
  3616. /*
  3617. * These flags have already been validated but it does not hurt to assert
  3618. */
  3619. AssertF( ( pAction->dwHow & ~DIAH_VALID ) == 0 );
  3620. AssertF( ( pAction->dwHow & DIAH_ERROR ) == 0 );
  3621. AssertF( ( pAction->dwFlags & ~DIA_VALID ) == 0 );
  3622. if( ( pAction->dwHow & DIAH_MAPMASK )
  3623. && ( ( pAction->dwFlags & DIA_APPNOMAP ) == 0 ) )
  3624. {
  3625. int iobjDev = -1;
  3626. PCODF podfFound;
  3627. for( podfFound = this->df.rgodf; podfFound < &this->df.rgodf[this->df.dwNumObjs]; podfFound++ )
  3628. {
  3629. iobjDev++;
  3630. /*
  3631. * Look for an exact type flags match
  3632. */
  3633. if( podfFound->dwType == pAction->dwObjID )
  3634. {
  3635. break;
  3636. }
  3637. }
  3638. if( podfFound < &this->df.rgodf[this->df.dwNumObjs] )
  3639. {
  3640. DWORD dwAppOffset = (DWORD)(pAction - paf->rgoAction) * cbX( ((LPDIDEVICEOBJECTDATA)0)->dwData );
  3641. fSomethingMapped = TRUE;
  3642. vdf.pDfOfs[podfFound->dwOfs] = iobjDev;
  3643. rgiobj[dwAppOffset] = iobjDev;
  3644. pdix[iobjDev].dwOfs = dwAppOffset;
  3645. pdix[iobjDev].uAppData = pAction->uAppData;
  3646. if ( podfFound->dwFlags & DIDOI_POLLED ) {
  3647. this->fPolledDataFormat = TRUE;
  3648. }
  3649. dipdw.diph.dwObj = podfFound->dwType;
  3650. dipdw.dwData = 0x1; // Enable this report ID
  3651. hres = CDIDev_RealSetProperty(this, DIPROP_ENABLEREPORTID, &dipdw.diph);
  3652. if ( hres == E_NOTIMPL )
  3653. {
  3654. hres = S_OK;
  3655. }
  3656. else if( FAILED( hres ) )
  3657. {
  3658. SquirtSqflPtszV(sqflDf | sqflError,
  3659. TEXT("Could not set DIPROP_ENABLEREPORTID for object 0x%08x, error 0x%08x"),
  3660. pAction->dwObjID, hres);
  3661. /*
  3662. * Ouch! Can't carry on or the error will be lost so quit
  3663. */
  3664. break;
  3665. }
  3666. /*
  3667. * Set the default axis parameters
  3668. */
  3669. if( DISEM_TYPE_GET( pAction->dwSemantic ) == DISEM_TYPE_GET( DISEM_TYPE_AXIS ) )
  3670. {
  3671. if( podfFound->dwType & DIDFT_ABSAXIS )
  3672. {
  3673. if( !fAxisModeKnown )
  3674. {
  3675. fAxisModeKnown = TRUE;
  3676. dwAxisMode = DIPROPAXISMODE_ABS;
  3677. }
  3678. if( !( pAction->dwFlags & DIA_NORANGE )
  3679. && ( diprange.lMin | diprange.lMax ) )
  3680. {
  3681. diprange.diph.dwObj = podfFound->dwType;
  3682. hres = CDIDev_RealSetProperty(this, DIPROP_RANGE, &diprange.diph);
  3683. if( FAILED( hres ) )
  3684. {
  3685. SquirtSqflPtszV(sqflDf | sqflBenign,
  3686. TEXT("failed (0x%08x) to set range on mapped axis action"),
  3687. hres );
  3688. }
  3689. /*
  3690. * Ranges cannot be set on natively relative
  3691. * axes so don't worry what the result is.
  3692. */
  3693. hres = S_OK;
  3694. }
  3695. }
  3696. else
  3697. {
  3698. /*
  3699. * dwAxisMode is initialized to DIPROPAXISMODE_REL
  3700. * so that this code path is the same as the one
  3701. * for DIDF_ABSAXIS as far as it goes. Compilers
  3702. * are good at delaying branches to remove such
  3703. * duplication.
  3704. */
  3705. if( !fAxisModeKnown )
  3706. {
  3707. fAxisModeKnown = TRUE;
  3708. }
  3709. }
  3710. }
  3711. }
  3712. else
  3713. {
  3714. SquirtSqflPtszV(sqflDf | sqflError,
  3715. TEXT("mapped action format contains invalid object 0x%08x"),
  3716. pAction->dwObjID );
  3717. hres = E_INVALIDARG;
  3718. goto done;
  3719. }
  3720. }
  3721. }
  3722. #ifdef XDEBUG
  3723. else
  3724. {
  3725. if( ( pAction->dwHow & ~DIAH_VALID )
  3726. || ( pAction->dwHow & DIAH_ERROR )
  3727. || ( pAction->dwFlags & ~DIA_VALID ) )
  3728. {
  3729. SquirtSqflPtszV(sqflDf | sqflBenign,
  3730. TEXT("action format contains invalid object 0x%08x"),
  3731. pAction->dwObjID );
  3732. RPF("rgoAction[%d].dwHow 0x%08x or rgoAction[%d].dwFlags 0x%08x is invalid",
  3733. pAction - paf->rgoAction, pAction->dwHow,
  3734. pAction - paf->rgoAction, pAction->dwFlags );
  3735. }
  3736. }
  3737. #endif
  3738. }
  3739. if( !fSomethingMapped )
  3740. {
  3741. SquirtSqflPtszV(sqflDf | sqflBenign, TEXT("No actions mapped") );
  3742. hres = DI_NOEFFECT;
  3743. goto done;
  3744. }
  3745. #ifdef DEBUG
  3746. /*
  3747. * Double-check the lookup tables just to preserve our sanity.
  3748. */
  3749. {
  3750. UINT dwOfs;
  3751. for ( dwOfs = 0; dwOfs < paf->dwNumActions; dwOfs++ )
  3752. {
  3753. if ( rgiobj[dwOfs] >= 0 ) {
  3754. AssertF(pdix[rgiobj[dwOfs]].dwOfs == dwOfs);
  3755. } else {
  3756. AssertF(rgiobj[dwOfs] == -1);
  3757. }
  3758. }
  3759. }
  3760. #endif
  3761. vdf.pvi = this->pvi;
  3762. if ( fLimpFF(this->pvi,
  3763. SUCCEEDED(hres = Hel_SetDataFormat(&vdf))) ) {
  3764. this->pdix = pdix;
  3765. pdix = 0;
  3766. this->rgiobj = rgiobj;
  3767. rgiobj = 0;
  3768. this->dwDataSize = paf->dwDataSize;
  3769. /*
  3770. * Now that the lower level knows what it's dealing with set
  3771. * the default buffer size.
  3772. */
  3773. dipdw.diph.dwObj = 0;
  3774. dipdw.diph.dwHow = DIPH_DEVICE;
  3775. dipdw.dwData = paf->dwBufferSize;
  3776. hres = CDIDev_RealSetProperty(this, DIPROP_BUFFERSIZE, &dipdw.diph);
  3777. if( SUCCEEDED( hres ) )
  3778. {
  3779. if( fAxisModeKnown )
  3780. {
  3781. AssertF( ( dwAxisMode == DIPROPAXISMODE_REL ) || ( dwAxisMode == DIPROPAXISMODE_ABS ) );
  3782. dipdw.dwData = dwAxisMode;
  3783. D( hres = )
  3784. CDIDev_RealSetProperty(this, DIPROP_AXISMODE, &dipdw.diph);
  3785. AssertF( SUCCEEDED( hres ) );
  3786. }
  3787. /*
  3788. * Complete success, whatever (success) SetProperty returns
  3789. * assume that DIPROP_AXISMODE will never fail from here.
  3790. */
  3791. hres = S_OK;
  3792. }
  3793. else
  3794. {
  3795. SquirtSqflPtszV(sqflDf | sqflError,
  3796. TEXT("failed (0x%08x) to set buffer size 0x%0x8 for device"),
  3797. hres );
  3798. hres = E_INVALIDARG;
  3799. }
  3800. } else {
  3801. AssertF(FAILED(hres));
  3802. }
  3803. } else {
  3804. /* Out of memory */
  3805. }
  3806. done:;
  3807. FreePpv(&pdix);
  3808. FreePpv(&rgiobj);
  3809. FreePpv(&vdf.pDfOfs);
  3810. done_without_free:;
  3811. #ifdef DEBUG
  3812. ExitOleProc();
  3813. #endif
  3814. return hres;
  3815. }
  3816. /*****************************************************************************
  3817. *
  3818. * @doc INTERNAL
  3819. *
  3820. * @method HRESULT | IDirectInputDevice | CDIDev_SetDataFormatFromMap |
  3821. *
  3822. * Worker function for SetActionMapA and SetActionMapW.
  3823. * Used to set a data format based on passed mapped actions.
  3824. *
  3825. *****************************************************************************/
  3826. STDMETHODIMP
  3827. CDIDev_SetDataFormatFromMap
  3828. (
  3829. PDD this,
  3830. LPDIACTIONFORMATW paf
  3831. )
  3832. {
  3833. HRESULT hres;
  3834. EnterProcI(CDIDev_SetDataFormatFromMap, (_ "p", paf));
  3835. if( !paf->dwBufferSize )
  3836. {
  3837. SquirtSqflPtszV(sqflDf | sqflVerbose,
  3838. TEXT("%S: zero DIACTIONFORMAT.dwBufferSize, may need to set yourself"),
  3839. s_szProc );
  3840. }
  3841. if( paf->dwBufferSize > DEVICE_MAXBUFFERSIZE )
  3842. {
  3843. RPF("IDirectInputDevice::%s: DIACTIONFORMAT.dwBufferSize of 0x%08x is very large",
  3844. s_szProc, paf->dwBufferSize );
  3845. }
  3846. /*
  3847. * Must protect with the critical section to prevent two people
  3848. * from changing the format simultaneously, or one person from
  3849. * changing the data format while somebody else is reading data.
  3850. */
  3851. CDIDev_EnterCrit(this);
  3852. if( !this->fAcquired )
  3853. {
  3854. DIPROPDWORD dipdw;
  3855. /*
  3856. * Nuke the old data format stuff before proceeding.
  3857. * Include the "failed POV" array as we can't fail and continue.
  3858. */
  3859. FreePpv(&this->pdix);
  3860. FreePpv(&this->rgiobj);
  3861. FreePpv(&this->rgdwPOV);
  3862. this->cdwPOV = 0;
  3863. D(this->GetState = 0);
  3864. this->fPolledDataFormat = FALSE;
  3865. /*
  3866. * Wipe out the report IDs
  3867. */
  3868. dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  3869. dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  3870. dipdw.diph.dwObj = 0x0;
  3871. dipdw.diph.dwHow = DIPH_DEVICE;
  3872. dipdw.dwData = 0; // Nuke all knowledge of reportId's
  3873. hres = CDIDev_RealSetProperty(this, DIPROP_ENABLEREPORTID, &dipdw.diph);
  3874. if( SUCCEEDED(hres) || hres == E_NOTIMPL )
  3875. {
  3876. hres = CDIDev_ParseActionFormat(this, paf);
  3877. /*
  3878. * If other success codes are implemented, this check should
  3879. * be modified to ( SUCCEEDED( hres ) && ( hres != DI_NOEFFECT ) )
  3880. */
  3881. AssertF( ( hres == S_OK ) || ( hres == DI_NOEFFECT ) || FAILED( hres ) );
  3882. if( hres == S_OK )
  3883. {
  3884. hres = CDIDev_OptimizeDataFormat(this);
  3885. }
  3886. }
  3887. else
  3888. {
  3889. SquirtSqflPtszV(sqflDf | sqflVerbose,
  3890. TEXT("Could not set DIPROP_ENABLEREPORTID to 0x0"));
  3891. }
  3892. }
  3893. else
  3894. { /* Already acquired */
  3895. hres = DIERR_ACQUIRED;
  3896. }
  3897. CDIDev_LeaveCrit(this);
  3898. ExitOleProcR();
  3899. return hres;
  3900. }
  3901. /*****************************************************************************
  3902. *
  3903. * @doc INTERNAL
  3904. *
  3905. * @method HRESULT | IDirectInputDevice | SetActionMapCore |
  3906. *
  3907. * Worker function, does all the real work for SetActionMapW and
  3908. * SetActionMapA. See SetActionMapW for details.
  3909. *
  3910. *****************************************************************************/
  3911. STDMETHODIMP CDIDev_SetActionMapCore
  3912. (
  3913. PDD this,
  3914. LPDIACTIONFORMATW paf,
  3915. LPWSTR lpwszUserName,
  3916. LPSTR lpszUserName,
  3917. DWORD dwFlags
  3918. )
  3919. {
  3920. HRESULT hres;
  3921. EnterProcI(IDirectInputDevice8::SetActionMapCore, (_ "pxWA", paf, dwFlags, lpwszUserName, lpszUserName ));
  3922. if( SUCCEEDED( hres = hresFullValidFl( dwFlags, DIDSAM_VALID, 3 ) ) )
  3923. {
  3924. if( dwFlags & DIDSAM_NOUSER )
  3925. {
  3926. if( dwFlags & ~DIDSAM_NOUSER )
  3927. {
  3928. RPF( "IDirectInputDevice8::SetActionMap: Invalid dwFlags 0x%08x, cannot use DIDSAM_NOUSER with other flags" );
  3929. hres = E_INVALIDARG;
  3930. }
  3931. else
  3932. {
  3933. hres = CMap_SetDeviceUserName( &this->guid, NULL );
  3934. }
  3935. }
  3936. else
  3937. {
  3938. DWORD dwCRC;
  3939. LPWSTR pwszGoodUserName;
  3940. hres = GetWideUserName( lpszUserName, lpwszUserName, &pwszGoodUserName );
  3941. if( SUCCEEDED( hres ) )
  3942. {
  3943. dwCRC = GetMapCRC( paf, &this->guid );
  3944. if( ( paf->dwCRC != dwCRC )
  3945. || CMap_IsNewDeviceUserName( &this->guid, pwszGoodUserName ) )
  3946. {
  3947. /*
  3948. * Set the force save flag so we only have to test one bit later
  3949. */
  3950. dwFlags |= DIDSAM_FORCESAVE;
  3951. }
  3952. if( dwFlags & DIDSAM_FORCESAVE )
  3953. {
  3954. hres = CMap_ValidateActionMapSemantics( paf, DIDBAM_PRESERVE );
  3955. if( SUCCEEDED( hres ) )
  3956. {
  3957. DWORD dwDummy;
  3958. hres = CMap_DeviceValidateActionMap( (PV)this, paf, DVAM_DEFAULT, &dwDummy );
  3959. if( FAILED( hres ) )
  3960. {
  3961. SquirtSqflPtszV(sqflDf | sqflError, TEXT("Action map invalid on SetActionMap"));
  3962. }
  3963. else if( ( hres == DI_WRITEPROTECT ) && ( paf->dwCRC != dwCRC ) )
  3964. {
  3965. RPF( "Refusing changed mappings for hardcoded device" );
  3966. hres = DIERR_INVALIDPARAM;
  3967. }
  3968. }
  3969. else
  3970. {
  3971. SquirtSqflPtszV(sqflDf | sqflError, TEXT("Action map invalid on SetActionMap"));
  3972. }
  3973. }
  3974. if( SUCCEEDED( hres ) )
  3975. {
  3976. if( SUCCEEDED( hres = CMap_SetDeviceUserName( &this->guid, pwszGoodUserName ) )
  3977. && SUCCEEDED( hres = CDIDev_SetDataFormatFromMap( this, paf ) ) )
  3978. {
  3979. if( dwFlags & DIDSAM_FORCESAVE )
  3980. {
  3981. hres = CDIDev_SaveActionMap( this, paf, pwszGoodUserName, dwFlags );
  3982. if( SUCCEEDED( hres ) )
  3983. {
  3984. //We don't remap success code anywhere so
  3985. //assert it is what we expected
  3986. AssertF(hres==S_OK);
  3987. paf->dwCRC = dwCRC;
  3988. }
  3989. else
  3990. {
  3991. RPF( "Ignoring internal SaveActionMap error 0x%08x", hres );
  3992. hres = DI_SETTINGSNOTSAVED;
  3993. }
  3994. }
  3995. }
  3996. }
  3997. if( !lpwszUserName )
  3998. {
  3999. /*
  4000. * Free either the default name or the ANSI translation
  4001. */
  4002. FreePv( pwszGoodUserName );
  4003. }
  4004. }
  4005. }
  4006. }
  4007. ExitOleProc();
  4008. return hres;
  4009. }
  4010. /*****************************************************************************
  4011. *
  4012. * @doc EXTERNAL
  4013. *
  4014. * @method HRESULT | IDirectInputDevice | SetActionMap |
  4015. *
  4016. * Set the data format for the DirectInput device from
  4017. * an action map for the passed application and user.
  4018. *
  4019. * If the action map has been changed (as determined by a CRC check)
  4020. * this latest map is saved after it has been applied.
  4021. *
  4022. * The data format must be set before the device can be
  4023. * acquired.
  4024. *
  4025. * It is necessary to set the data format only once.
  4026. *
  4027. * The data format may not be changed while the device
  4028. * is acquired.
  4029. *
  4030. * If the attempt to set the data format fails, all data
  4031. * format information is lost, and a valid data format
  4032. * must be set before the device may be acquired.
  4033. *
  4034. * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
  4035. *
  4036. * @parm LPDIACTIONFORMAT | paf |
  4037. *
  4038. * Points to a structure that describes the actions needed by the
  4039. * application.
  4040. *
  4041. * @parm LPCTSTR | lptszUserName |
  4042. *
  4043. * Name of user for whom mapping is being set. This may be a NULL
  4044. * pointer in which case the current user is assumed.
  4045. *
  4046. * @parm DWORD | dwFlags |
  4047. *
  4048. * Flags used to control how the mapping should be set.
  4049. * Must be a valid combination of the <c DIDSAM_*> flags.
  4050. *
  4051. * @returns
  4052. *
  4053. * Returns a COM error code. The following error codes are
  4054. * intended to be illustrative and not necessarily comprehensive.
  4055. *
  4056. * <c DI_OK> = <c S_OK>: The operation completed successfully.
  4057. *
  4058. * <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: A parameter is invalid.
  4059. *
  4060. * <c DIERR_ACQUIRED>: Cannot apply an action map while the device
  4061. * is acquired.
  4062. *
  4063. *****************************************************************************/
  4064. STDMETHODIMP CDIDev_SetActionMapW
  4065. (
  4066. PV pdidW,
  4067. LPDIACTIONFORMATW pafW,
  4068. LPCWSTR lpwszUserName,
  4069. DWORD dwFlags
  4070. )
  4071. {
  4072. HRESULT hres;
  4073. EnterProcR(IDirectInputDevice8W::SetActionMap, (_ "ppWx", pdidW, pafW, lpwszUserName, dwFlags));
  4074. if( ( SUCCEEDED( hres = hresPvW( pdidW ) ) )
  4075. && ( SUCCEEDED( hres = CDIDev_ActionMap_IsValidMapObject( pafW D(comma s_szProc comma 1) ) ) ) )
  4076. {
  4077. if( pafW->dwSize != cbX(DIACTIONFORMATW) )
  4078. {
  4079. D( RPF("IDirectInputDevice::%s: Invalid DIACTIONFORMAT.dwSize 0x%08x",
  4080. s_szProc, pafW->dwSize ); )
  4081. hres = E_INVALIDARG;
  4082. }
  4083. else
  4084. {
  4085. hres = CDIDev_SetActionMapCore( _thisPvNm( pdidW, ddW ),
  4086. pafW, (LPWSTR)lpwszUserName, NULL, dwFlags );
  4087. }
  4088. }
  4089. ExitOleProc();
  4090. return hres;
  4091. }
  4092. /*****************************************************************************
  4093. *
  4094. * @doc INTERNAL
  4095. *
  4096. * @method HRESULT | IDirectInputDevice | SetActionMapA |
  4097. *
  4098. * ANSI version of SetActionMap, see SetActionMapW for details.
  4099. *
  4100. *
  4101. *****************************************************************************/
  4102. STDMETHODIMP CDIDev_SetActionMapA
  4103. (
  4104. PV pdidA,
  4105. LPDIACTIONFORMATA pafA,
  4106. LPCSTR lpszUserName,
  4107. DWORD dwFlags
  4108. )
  4109. {
  4110. HRESULT hres;
  4111. EnterProcR(IDirectInputDevice8A::SetActionMap, (_ "ppAx", pdidA, pafA, lpszUserName, dwFlags));
  4112. if( ( SUCCEEDED( hres = hresPvA( pdidA ) ) )
  4113. && ( SUCCEEDED( hres = CDIDev_ActionMap_IsValidMapObject(
  4114. (LPDIACTIONFORMATW)pafA D(comma s_szProc comma 1) ) ) ) )
  4115. {
  4116. if( pafA->dwSize != cbX(DIACTIONFORMATA) )
  4117. {
  4118. D( RPF("IDirectInputDevice::%s: Invalid DIACTIONFORMAT.dwSize 0x%08x",
  4119. s_szProc, pafA->dwSize ); )
  4120. hres = E_INVALIDARG;
  4121. }
  4122. else
  4123. {
  4124. /*
  4125. * For the sake of the mapper DLLs validation set the size to
  4126. * the UNICODE version. If we ever send this to an external
  4127. * component we should do this differently.
  4128. */
  4129. pafA->dwSize = cbX(DIACTIONFORMATW);
  4130. /*
  4131. * Note, the ANSI user name is passed on as there may be no need
  4132. * to translate it. CDIDev_SetActionMapCore deals with this.
  4133. */
  4134. hres = CDIDev_SetActionMapCore( _thisPvNm( pdidA, ddA ),
  4135. (LPDIACTIONFORMATW)pafA, NULL, (LPSTR)lpszUserName, dwFlags );
  4136. pafA->dwSize = cbX(DIACTIONFORMATA);
  4137. }
  4138. }
  4139. ExitOleProc();
  4140. return hres;
  4141. }
  4142. /*****************************************************************************
  4143. *
  4144. * @doc EXTERNAL
  4145. *
  4146. * @method HRESULT | IDirectInputDevice | GetImageInfo |
  4147. *
  4148. * Retrieves device image information for use in displaying a
  4149. * configuration UI for a single device.
  4150. *
  4151. * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
  4152. *
  4153. * @parm LPDIDEVICEIMAGEINFOHEADER | pih |
  4154. *
  4155. * Pointer to structure into which the info is retrieved.
  4156. *
  4157. * @returns
  4158. *
  4159. * Returns a COM error code. The following error codes are
  4160. * intended to be illustrative and not necessarily comprehensive.
  4161. *
  4162. * <c DI_OK> = <c S_OK>: The operation completed successfully.
  4163. *
  4164. * <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: A parameter is invalid.
  4165. *
  4166. * <c DIERR_NOTFOUND>: The device has no image information.
  4167. *
  4168. *****************************************************************************/
  4169. STDMETHODIMP CDIDev_GetImageInfoCore
  4170. (
  4171. PDD this,
  4172. LPDIDEVICEIMAGEINFOHEADERW piih
  4173. )
  4174. {
  4175. HRESULT hres;
  4176. EnterProcI(CDIDev_GetImageInfoCore, (_ "p", piih));
  4177. if( piih->dwSize != cbX( *piih ) )
  4178. {
  4179. D( RPF("IDirectInputDevice::%s: Invalid DIDEVICEIMAGEINFOHEADER.dwSize 0x%08x",
  4180. s_szProc, piih->dwSize ); )
  4181. hres = E_INVALIDARG;
  4182. }
  4183. else if( ( piih->lprgImageInfoArray )
  4184. && ( piih->dwBufferSize < piih->dwSizeImageInfo ) )
  4185. {
  4186. D( RPF("IDirectInputDevice::%s: Invalid DIDEVICEIMAGEINFOHEADER.dwBufferSize 0x%08x",
  4187. s_szProc, piih->dwBufferSize ); )
  4188. hres = E_INVALIDARG;
  4189. }
  4190. else if( piih->dwBufferSize && !piih->lprgImageInfoArray )
  4191. {
  4192. D( RPF("IDirectInputDevice::%s: Invalid DIDEVICEIMAGEINFOHEADERW has dwBufferSize 0x%08x but NULL lprgImageInfoArray",
  4193. s_szProc, piih->dwBufferSize ); )
  4194. hres = E_INVALIDARG;
  4195. }
  4196. else
  4197. {
  4198. LPDIPROPSTRING pdipMapFile;
  4199. if( SUCCEEDED( hres = AllocCbPpv(cbX(*pdipMapFile), &pdipMapFile) ) )
  4200. {
  4201. pdipMapFile->diph.dwSize = cbX(DIPROPSTRING);
  4202. pdipMapFile->diph.dwHeaderSize = cbX(DIPROPHEADER);
  4203. pdipMapFile->diph.dwObj = 0;
  4204. pdipMapFile->diph.dwHow = DIPH_DEVICE;
  4205. /*
  4206. * Must have an IHV file for image info
  4207. */
  4208. hres = CDIDev_GetPropertyW( &this->ddW, DIPROP_MAPFILE, &pdipMapFile->diph );
  4209. if( SUCCEEDED( hres ) )
  4210. {
  4211. /*
  4212. * ISSUE-2001/03/29-timgill workaraound code needs to be removed
  4213. * Initializing this to zero works around most of 34453
  4214. * Remove when that is fixed in DIMap.dll
  4215. */
  4216. piih->dwBufferUsed = 0;
  4217. hres = this->pMS->lpVtbl->GetImageInfo( this->pMS, &this->guid, pdipMapFile->wsz, piih );
  4218. if( SUCCEEDED( hres ) )
  4219. {
  4220. piih->dwcButtons = this->dc3.dwButtons;
  4221. piih->dwcAxes = this->dc3.dwAxes;
  4222. piih->dwcPOVs = this->dc3.dwPOVs;
  4223. AssertF( ( piih->dwBufferSize == 0 )
  4224. || ( piih->dwBufferSize >= piih->dwBufferUsed ) );
  4225. }
  4226. else
  4227. {
  4228. /*
  4229. * Use the same return code for all internal DIMap errors
  4230. */
  4231. if( ( HRESULT_FACILITY( hres ) == FACILITY_ITF )
  4232. && ( HRESULT_CODE( hres ) > 0x0600 ) )
  4233. {
  4234. AssertF( HRESULT_CODE( hres ) < 0x0680 );
  4235. RPF( "Internal GetImageInfo error 0x%08x", hres );
  4236. hres = DIERR_MAPFILEFAIL;
  4237. }
  4238. }
  4239. }
  4240. else
  4241. {
  4242. /*
  4243. * Use the same return code for all forms of not found
  4244. */
  4245. hres = DIERR_NOTFOUND;
  4246. }
  4247. FreePv( pdipMapFile );
  4248. }
  4249. }
  4250. #ifdef DEBUG
  4251. ExitOleProc();
  4252. #endif
  4253. return hres;
  4254. }
  4255. STDMETHODIMP CDIDev_GetImageInfoW
  4256. (
  4257. PV pdidW,
  4258. LPDIDEVICEIMAGEINFOHEADERW piih
  4259. )
  4260. {
  4261. HRESULT hres;
  4262. EnterProcR(IDirectInputDevice8W::GetImageInfo, (_ "pp", pdidW, piih));
  4263. if( SUCCEEDED(hres = hresPvW( pdidW ) )
  4264. && SUCCEEDED(hres = hresFullValidWriteNoScramblePxCb( piih, *piih, 1 ) ) )
  4265. {
  4266. ScrambleBuf( &piih->dwcViews, cbX( piih->dwcViews ) );
  4267. ScrambleBuf( &piih->dwcButtons, cbX( piih->dwcButtons ) );
  4268. ScrambleBuf( &piih->dwcAxes, cbX( piih->dwcAxes ) );
  4269. ScrambleBuf( &piih->dwBufferUsed, cbX( piih->dwBufferUsed ) );
  4270. if( piih->dwSizeImageInfo != cbX( *piih->lprgImageInfoArray ) )
  4271. {
  4272. D( RPF("IDirectInputDevice::%s: Invalid DIDEVICEIMAGEINFOHEADERW.dwSizeImageInfo 0x%08x",
  4273. s_szProc, piih->dwSizeImageInfo ); )
  4274. hres = E_INVALIDARG;
  4275. }
  4276. else if( SUCCEEDED(hres = hresFullValidWriteLargePvCb(
  4277. piih->lprgImageInfoArray, piih->dwBufferSize, 1 ) ) )
  4278. {
  4279. PDD this;
  4280. this = _thisPvNm(pdidW, ddW);
  4281. hres = CDIDev_GetImageInfoCore( this, piih );
  4282. }
  4283. }
  4284. ExitOleProc();
  4285. return hres;
  4286. }
  4287. STDMETHODIMP CDIDev_GetImageInfoA
  4288. (
  4289. PV pdidA,
  4290. LPDIDEVICEIMAGEINFOHEADERA piih
  4291. )
  4292. {
  4293. HRESULT hres;
  4294. EnterProcR(IDirectInputDevice8A::GetImageInfo, (_ "pp", pdidA, piih));
  4295. if( SUCCEEDED(hres = hresPvA( pdidA ) )
  4296. && SUCCEEDED(hres = hresFullValidWriteNoScramblePxCb( piih, *piih, 1 ) ) )
  4297. {
  4298. ScrambleBuf( &piih->dwcViews, cbX( piih->dwcViews ) );
  4299. ScrambleBuf( &piih->dwcButtons, cbX( piih->dwcButtons ) );
  4300. ScrambleBuf( &piih->dwcAxes, cbX( piih->dwcAxes ) );
  4301. ScrambleBuf( &piih->dwBufferUsed, cbX( piih->dwBufferUsed ) );
  4302. if( piih->dwSizeImageInfo != cbX( *piih->lprgImageInfoArray ) )
  4303. {
  4304. D( RPF("IDirectInputDevice::%s: Invalid DIDEVICEIMAGEINFOHEADERA.dwSizeImageInfo 0x%08x",
  4305. s_szProc, piih->dwSizeImageInfo ); )
  4306. hres = E_INVALIDARG;
  4307. }
  4308. else if( SUCCEEDED(hres = hresFullValidWriteLargePvCb(
  4309. piih->lprgImageInfoArray, piih->dwBufferSize, 1 ) ) )
  4310. {
  4311. PDD this;
  4312. DIDEVICEIMAGEINFOHEADERW ihPrivate;
  4313. ihPrivate.dwSize = cbX( ihPrivate );
  4314. ihPrivate.dwSizeImageInfo = cbX( *ihPrivate.lprgImageInfoArray );
  4315. ihPrivate.dwBufferSize = cbX( *ihPrivate.lprgImageInfoArray )
  4316. * ( piih->dwBufferSize / cbX( *piih->lprgImageInfoArray ) );
  4317. hres = AllocCbPpv( ihPrivate.dwBufferSize, &ihPrivate.lprgImageInfoArray );
  4318. if( SUCCEEDED( hres ) )
  4319. {
  4320. this = _thisPvNm(pdidA, ddA);
  4321. hres = CDIDev_GetImageInfoCore( this, &ihPrivate );
  4322. if( SUCCEEDED( hres ) )
  4323. {
  4324. LPDIDEVICEIMAGEINFOW piiW;
  4325. LPDIDEVICEIMAGEINFOA piiA = piih->lprgImageInfoArray;
  4326. CAssertF( cbX( *piiA ) - cbX( piiA->tszImagePath ) == cbX( *piiW ) - cbX( piiW->tszImagePath ) );
  4327. CAssertF( FIELD_OFFSET( DIDEVICEIMAGEINFOA, tszImagePath ) == 0 );
  4328. CAssertF( FIELD_OFFSET( DIDEVICEIMAGEINFOW, tszImagePath ) == 0 );
  4329. CAssertF( FIELD_OFFSET( DIDEVICEIMAGEINFOW, dwFlags ) == cbX( piiW->tszImagePath ) );
  4330. if(ihPrivate.lprgImageInfoArray)
  4331. {
  4332. for( piiW = ihPrivate.lprgImageInfoArray;
  4333. (PBYTE)piiW < (PBYTE)ihPrivate.lprgImageInfoArray + ihPrivate.dwBufferUsed;
  4334. piiW++ )
  4335. {
  4336. UToA( piiA->tszImagePath, cbX( piiA->tszImagePath ), piiW->tszImagePath );
  4337. memcpy( (PV)&piiA->dwFlags, (PV)&piiW->dwFlags,
  4338. cbX( *piiA ) - cbX( piiA->tszImagePath ) );
  4339. piiA++;
  4340. }
  4341. }
  4342. piih->dwBufferUsed = cbX( *piih->lprgImageInfoArray )
  4343. * ( ihPrivate.dwBufferUsed / cbX( *ihPrivate.lprgImageInfoArray ) );
  4344. piih->dwcViews = ihPrivate.dwcViews;
  4345. piih->dwcButtons = ihPrivate.dwcButtons;
  4346. piih->dwcAxes = ihPrivate.dwcAxes;
  4347. piih->dwcPOVs = ihPrivate.dwcPOVs;
  4348. }
  4349. FreePv( ihPrivate.lprgImageInfoArray );
  4350. }
  4351. }
  4352. }
  4353. ExitOleProc();
  4354. return hres;
  4355. }
  4356. /*****************************************************************************
  4357. *
  4358. * @doc EXTERNAL
  4359. *
  4360. * @method HRESULT | IDirectInputDevice | GetDeviceState |
  4361. *
  4362. * Obtains instantaneous data from the DirectInput device.
  4363. *
  4364. * Before device data can be obtained, the data format must
  4365. * be set via <mf IDirectInputDevice::SetDataFormat>, and
  4366. * the device must be acquired via
  4367. * <mf IDirectInputDevice::Acquire>.
  4368. *
  4369. * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
  4370. *
  4371. * @parm DWORD | cbData |
  4372. *
  4373. * The size of the buffer pointed to by <p lpvData>, in bytes.
  4374. *
  4375. * @parm OUT LPVOID | lpvData |
  4376. *
  4377. * Points to a structure that receives the current state
  4378. * of the device.
  4379. * The format of the data is established by a prior call
  4380. * to <mf IDirectInputDevice::SetDataFormat>.
  4381. *
  4382. * @returns
  4383. *
  4384. * Returns a COM error code. The following error codes are
  4385. * intended to be illustrative and not necessarily comprehensive.
  4386. *
  4387. * <c DI_OK> = <c S_OK>: The operation completed successfully.
  4388. *
  4389. * <c E_PENDING>: The device does not have data yet.
  4390. * Some devices (such as USB joysticks) require a delay
  4391. * between the time the device is turned on and the time
  4392. * the device begins sending data. During this "warm-up" time,
  4393. * <mf IDirectInputDevice::GetDeviceState> will return
  4394. * <c E_PENDING>. When data becomes available, the event
  4395. * notification handle will be signalled.
  4396. *
  4397. * <c DIERR_NOTACQUIRED>: The device is not acquired.
  4398. *
  4399. * <c DIERR_INPUTLOST>: Access to the device has been
  4400. * interrupted. The application should re-acquire the
  4401. * device.
  4402. *
  4403. * <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
  4404. * <p lpvData> parameter is not a valid pointer or
  4405. * the <p cbData> parameter does not match the data size
  4406. * set by a previous call to <mf IDirectInputDevice::SetDataFormat>.
  4407. *
  4408. *****************************************************************************/
  4409. extern STDMETHODIMP CDIDev_Acquire(PV pdd _THAT);
  4410. STDMETHODIMP
  4411. CDIDev_GetDeviceState(PV pdd, DWORD cbDataSize, LPVOID pvData _THAT)
  4412. {
  4413. HRESULT hres;
  4414. PDD this;
  4415. EnterProcR(IDirectInputDevice8::GetDeviceState, (_ "pp", pdd, pvData));
  4416. /*
  4417. * Note that we do not validate the parameters.
  4418. * The reason is that GetDeviceState is an inner loop function,
  4419. * so it should be as fast as possible.
  4420. */
  4421. #ifdef XDEBUG
  4422. hresPvT(pdd);
  4423. hresFullValidWritePvCb(pvData, cbDataSize, 1);
  4424. #endif
  4425. this = _thisPv(pdd);
  4426. /*
  4427. * Must protect with the critical section to prevent somebody from
  4428. * unacquiring while we're reading.
  4429. */
  4430. CDIDev_EnterCrit(this);
  4431. /*
  4432. * Reacquire is not allowed until after Win98 SE, see OSR Bug # 89958
  4433. */
  4434. if ( this->diHacks.fReacquire &&
  4435. !this->fAcquired && (this->fOnceAcquired || this->fOnceForcedUnacquired) )
  4436. {
  4437. if ( SUCCEEDED( CDIDev_Acquire(pdd THAT_) ) ) {
  4438. // 7/18/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  4439. RPF(" DirectInput: Auto acquired (0x%p)", pdd);
  4440. }
  4441. }
  4442. #ifdef WINNT
  4443. if( this->fUnacquiredWhenIconic && !IsIconic(this->hwnd) ) {
  4444. if ( SUCCEEDED( CDIDev_Acquire(pdd THAT_) ) ) {
  4445. this->fUnacquiredWhenIconic = 0;
  4446. RPF(" DirectInput: Auto acquired device (0x%p) after being iconic. ", pdd);
  4447. }
  4448. }
  4449. #endif
  4450. if ( this->fAcquired ) {
  4451. AssertF(this->pdix); /* Acquire shouldn't let you get this far */
  4452. AssertF(this->GetState);
  4453. AssertF(this->GetDeviceState);
  4454. AssertF(this->pdcb);
  4455. if ( this->dwDataSize == cbDataSize ) {
  4456. #ifndef DEBUG_STICKY
  4457. hres = this->GetState(this, pvData);
  4458. #else
  4459. PBYTE pbDbg;
  4460. TCHAR tszDbg[80];
  4461. hres = this->GetState(this, pvData);
  4462. for( pbDbg=(PBYTE)pvData; pbDbg<((PBYTE)pvData+cbDataSize); pbDbg++ )
  4463. {
  4464. if( *pbDbg )
  4465. {
  4466. wsprintf( tszDbg, TEXT("GotState @ 0x%02x, 0x%02x\r\n"), pbDbg-(PBYTE)pvData, *pbDbg );
  4467. OutputDebugString( tszDbg );
  4468. }
  4469. }
  4470. #endif /* DEBUG_STICKY */
  4471. if ( SUCCEEDED(hres) ) {
  4472. UINT idw;
  4473. AssertF(hres == S_OK);
  4474. /*
  4475. * Icky POV hack for apps that don't check if they have
  4476. * a POV before reading from it.
  4477. */
  4478. for ( idw = 0; idw < this->cdwPOV; idw++ ) {
  4479. DWORD UNALIGNED *pdw = pvAddPvCb(pvData, this->rgdwPOV[idw]);
  4480. *pdw = JOY_POVCENTERED;
  4481. }
  4482. hres = S_OK;
  4483. } else if ( hres == DIERR_INPUTLOST ) {
  4484. RPF("%s: Input lost", s_szProc);
  4485. CDIDev_InternalUnacquire(this);
  4486. hres = DIERR_INPUTLOST;
  4487. }
  4488. } else {
  4489. RPF("ERROR %s: arg %d: invalid value", s_szProc, 1);
  4490. hres = E_INVALIDARG;
  4491. }
  4492. } else {
  4493. hres = this->hresNotAcquired;
  4494. }
  4495. if ( FAILED(hres) ) {
  4496. ScrambleBuf(pvData, cbDataSize);
  4497. }
  4498. CDIDev_LeaveCrit(this);
  4499. ExitOleProc();
  4500. return hres;
  4501. }
  4502. /*****************************************************************************
  4503. *
  4504. * @doc INTERNAL
  4505. *
  4506. * @method void | IDirectInputDevice | CookDeviceData |
  4507. *
  4508. * Cook device data that was recently obtained from the
  4509. * device buffer.
  4510. *
  4511. * Right now, only the joystick device requires cooking,
  4512. * and nobody in their right mind uses buffered joystick
  4513. * data, and the joystick has only a few objects, so we
  4514. * can afford to be slow on this.
  4515. *
  4516. * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
  4517. *
  4518. * @parm DWORD | cdod |
  4519. *
  4520. * Number of objects to cook.
  4521. *
  4522. * @parm LPDIDEVICEOBJECTDATA | rgdod |
  4523. *
  4524. * Array of object data to cook. The dwOfs are really
  4525. * device object indexes (relative to the device format).
  4526. * After calling the callback, we convert them into
  4527. * application data format offsets.
  4528. *
  4529. * @returns
  4530. *
  4531. * None.
  4532. *
  4533. *****************************************************************************/
  4534. void INTERNAL
  4535. CDIDev_CookDeviceData(PDD this, DWORD cdod, LPDIDEVICEOBJECTDATA rgdod)
  4536. {
  4537. EnterProc(IDirectInputDevice8::CookDeviceData,
  4538. (_ "pxp", this, cdod, rgdod));
  4539. AssertF(this->fCook);
  4540. /*
  4541. * Relative data does not need to be cooked by the callback.
  4542. */
  4543. if( ( this->pvi->fl & VIFL_RELATIVE ) == 0 )
  4544. {
  4545. this->pdcb->lpVtbl->CookDeviceData(this->pdcb, cdod, rgdod);
  4546. }
  4547. /*
  4548. * Step through array converting to application data format offsets
  4549. * including adding the uAppData
  4550. */
  4551. for( ; cdod; cdod--,rgdod++ )
  4552. {
  4553. rgdod->uAppData = this->pdix[rgdod->dwOfs].uAppData;
  4554. rgdod->dwOfs = this->pdix[rgdod->dwOfs].dwOfs;
  4555. }
  4556. ExitProc();
  4557. }
  4558. /*****************************************************************************
  4559. *
  4560. * @doc INTERNAL
  4561. *
  4562. * @struct SOMEDEVICEDATA |
  4563. *
  4564. * Instance data used by <mf IDirectInputDevice::GetSomeDeviceData>.
  4565. *
  4566. * @field DWORD | celtIn |
  4567. *
  4568. * Number of elements remaining in output buffer.
  4569. *
  4570. * @field PDOD | rgdod |
  4571. *
  4572. * Output buffer for data elements, or <c NULL> if
  4573. * elements should be discarded.
  4574. *
  4575. * @field DWORD | celtOut |
  4576. *
  4577. * Number of elements actually copied (so far).
  4578. *
  4579. *****************************************************************************/
  4580. typedef struct SOMEDEVICEDATA {
  4581. DWORD celtIn;
  4582. PDOD rgdod;
  4583. DWORD celtOut;
  4584. } SOMEDEVICEDATA, *PSOMEDEVICEDATA;
  4585. /*****************************************************************************
  4586. *
  4587. * @doc INTERNAL
  4588. *
  4589. * @method PDOD | IDirectInputDevice | GetSomeDeviceData |
  4590. *
  4591. * Obtains a small amount of
  4592. * buffered data from the DirectInput device.
  4593. *
  4594. * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
  4595. *
  4596. * @parm PDOD | pdod |
  4597. *
  4598. * First element to copy.
  4599. *
  4600. * @parm DWORD | celt |
  4601. *
  4602. * Maximum number of elements to copy.
  4603. *
  4604. * @parm PSOMEDEVICEDATA | psdd |
  4605. *
  4606. * Structure describing the state of the ongoing
  4607. * <mf IDirectInputDevice::GetDeviceData>.
  4608. *
  4609. * @returns
  4610. *
  4611. * Returns a pointer to the first uncopied item.
  4612. *
  4613. *****************************************************************************/
  4614. LPDIDEVICEOBJECTDATA_DX3 INTERNAL
  4615. CDIDev_GetSomeDeviceData
  4616. (
  4617. PDD this,
  4618. LPDIDEVICEOBJECTDATA_DX3 pdod,
  4619. DWORD celt,
  4620. PSOMEDEVICEDATA psdd
  4621. )
  4622. {
  4623. #ifdef XDEBUG
  4624. DWORD cCopied;
  4625. #endif
  4626. EnterProc(IDirectInputDevice8::GetSomeDeviceData,
  4627. (_ "ppxx", this, pdod, celt, psdd->celtIn));
  4628. /*
  4629. * Copy as many elements as fit, but not more than exist
  4630. * in the output buffer.
  4631. */
  4632. if ( celt > psdd->celtIn ) {
  4633. celt = psdd->celtIn;
  4634. }
  4635. #ifdef XDEBUG
  4636. cCopied = celt;
  4637. #endif
  4638. /*
  4639. * Copy the elements (if requested) and update the state.
  4640. * Note that celt might be zero.
  4641. */
  4642. psdd->celtOut += celt;
  4643. psdd->celtIn -= celt;
  4644. if( psdd->rgdod )
  4645. {
  4646. LPDIDEVICEOBJECTDATA pdod8;
  4647. pdod8 = psdd->rgdod;
  4648. if( this->fCook )
  4649. {
  4650. /*
  4651. * For a cooked device, leave the offset untranslated so that it
  4652. * can be used to find the appropriate calibration data.
  4653. */
  4654. for( ; celt ; celt-- )
  4655. {
  4656. pdod8->dwOfs = pdod->dwOfs;
  4657. pdod8->dwData = pdod->dwData;
  4658. pdod8->dwTimeStamp = pdod->dwTimeStamp;
  4659. pdod8->dwSequence = pdod->dwSequence;
  4660. pdod++;
  4661. pdod8++;
  4662. }
  4663. }
  4664. else
  4665. {
  4666. for( ; celt ; celt-- )
  4667. {
  4668. pdod8->dwOfs = this->pdix[pdod->dwOfs].dwOfs;
  4669. pdod8->uAppData = this->pdix[pdod->dwOfs].uAppData;
  4670. pdod8->dwData = pdod->dwData;
  4671. pdod8->dwTimeStamp = pdod->dwTimeStamp;
  4672. pdod8->dwSequence = pdod->dwSequence;
  4673. pdod++;
  4674. pdod8++;
  4675. }
  4676. }
  4677. psdd->rgdod = pdod8;
  4678. }
  4679. else
  4680. {
  4681. pdod += celt;
  4682. }
  4683. if ( pdod == this->pvi->pEnd ) {
  4684. pdod = this->pvi->pBuffer;
  4685. }
  4686. ExitProcX(cCopied);
  4687. return pdod;
  4688. }
  4689. /*
  4690. * Keep GetDeviceData, SendDeviceData and Poll in sqflDev
  4691. */
  4692. #undef sqfl
  4693. #define sqfl sqflDev
  4694. /*****************************************************************************
  4695. *
  4696. * @doc EXTERNAL
  4697. *
  4698. * @method HRESULT | IDirectInputDevice | GetDeviceData |
  4699. *
  4700. * Obtains buffered data from the DirectInput device.
  4701. *
  4702. * DirectInput devices are, by default, unbuffered. To
  4703. * turn on buffering, you must set the buffer size
  4704. * via <mf IDirectInputDevice::SetProperty>, setting the
  4705. * <c DIPROP_BUFFERSIZE> property to the desired size
  4706. * of the input buffer.
  4707. *
  4708. * Before device data can be obtained, the data format must
  4709. * be set via <mf IDirectInputDevice::SetDataFormat>, and
  4710. * the device must be acquired via
  4711. * <mf IDirectInputDevice::Acquire>.
  4712. *
  4713. * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
  4714. *
  4715. * @parm DWORD | cbObjectData |
  4716. *
  4717. * The size of a single <t DIDEVICEOBJECTDATA> structure in bytes.
  4718. *
  4719. * @parm OUT LPDIDEVICEOBJECTDATA | rgdod |
  4720. *
  4721. * Array of <t DIDEVICEOBJECTDATA> structures to receive
  4722. * the buffered data. It must consist of
  4723. * *<p pdwInOut> elements.
  4724. *
  4725. * If this parameter is <c NULL>, then the buffered data is
  4726. * not stored anywhere, but all other side-effects take place.
  4727. *
  4728. * @parm INOUT LPDWORD | pdwInOut |
  4729. *
  4730. * On entry, contains the number of elements in the array
  4731. * pointed to by <p rgdod>. On exit, contains the number
  4732. * of elements actually obtained.
  4733. *
  4734. * @parm DWORD | fl |
  4735. *
  4736. * Flags which control the manner in which data is obtained.
  4737. * It may be zero or more of the following flags:
  4738. *
  4739. * <c DIGDD_PEEK>: Do not remove the items from the buffer.
  4740. * A subsequent <mf IDirectInputDevice::GetDeviceData> will
  4741. * read the same data. Normally, data is removed from the
  4742. * buffer after it is read.
  4743. *
  4744. ;begin_internal dx4
  4745. * <c DIGDD_RESIDUAL>: Read data from the device buffer
  4746. * even if the device is not acquired. Normally, attempting
  4747. * to read device data from an unacquired device will return
  4748. * <c DIERR_NOTACQUIRED> or <c DIERR_INPUTLOST>.
  4749. ;end_internal dx4
  4750. *
  4751. * @returns
  4752. *
  4753. * <c DI_OK> = <c S_OK>: All data were retrieved
  4754. * successfully. Note that the application needs to check
  4755. * the output value of *<p pdwInOut> to determine whether
  4756. * and how much data was retrieved: The value may be zero,
  4757. * indicating that the buffer was empty.
  4758. *
  4759. * <c DI_BUFFEROVERFLOW> = <c S_FALSE>: Some data
  4760. * were retrieved successfully, but some data were lost
  4761. * because the device's buffer size was not large enough.
  4762. * The application should retrieve buffered data more frequently
  4763. * or increase the device buffer size. This status code is
  4764. * returned only on the first <mf IDirectInput::GetDeviceData>
  4765. * call after the buffer has overflowed. Note that this is
  4766. * a success status code.
  4767. *
  4768. * <c DIERR_NOTACQUIRED>: The device is not acquired.
  4769. *
  4770. * <c DIERR_INPUTLOST>: Access to the device has been
  4771. * interrupted. The application should re-acquire the
  4772. * device.
  4773. *
  4774. * <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: One or more
  4775. * parameters was invalid. A common cause for this is
  4776. * neglecting to set a buffer size.
  4777. *
  4778. * <c DIERR_NOTBUFFERED>: The device is not buffered.
  4779. * Set the <c DIPROP_BUFFERSIZE> property to enable buffering.
  4780. *
  4781. * @ex
  4782. *
  4783. * The following sample reads up to ten buffered data elements,
  4784. * removing them from the device buffer as they are read.
  4785. *
  4786. * |
  4787. *
  4788. * DIDEVICEOBJECTDATA rgdod[10];
  4789. * DWORD dwItems = 10;
  4790. * hres = IDirectInputDevice_GetDeviceData(
  4791. * pdid,
  4792. * sizeof(DIDEVICEOBJECTDATA),
  4793. * rgdod,
  4794. * &dwItems,
  4795. * 0);
  4796. * if (SUCCEEDED(hres)) {
  4797. * // Buffer successfully flushed.
  4798. * // dwItems = number of elements flushed
  4799. * if (hres == DI_BUFFEROVERFLOW) {
  4800. * // Buffer had overflowed.
  4801. * }
  4802. * }
  4803. *
  4804. *
  4805. *
  4806. *
  4807. * @ex
  4808. *
  4809. * If you pass <c NULL> for the <p rgdod> and request an
  4810. * infinite number of items, this has the effect of flushing
  4811. * the buffer and returning the number of items that were
  4812. * flushed.
  4813. *
  4814. * |
  4815. *
  4816. * dwItems = INFINITE;
  4817. * hres = IDirectInputDevice_GetDeviceData(
  4818. * pdid,
  4819. * sizeof(DIDEVICEOBJECTDATA),
  4820. * NULL,
  4821. * &dwItems,
  4822. * 0);
  4823. * if (SUCCEEDED(hres)) {
  4824. * // Buffer successfully flushed.
  4825. * // dwItems = number of elements flushed
  4826. * if (hres == DI_BUFFEROVERFLOW) {
  4827. * // Buffer had overflowed.
  4828. * }
  4829. * }
  4830. *
  4831. * @ex
  4832. *
  4833. * If you pass <c NULL> for the <p rgdod>, request an
  4834. * infinite number of items, and ask that the data not be
  4835. * removed from the device buffer, this has the effect of
  4836. * querying for the number of elements in the device buffer.
  4837. *
  4838. * |
  4839. *
  4840. * dwItems = INFINITE;
  4841. * hres = IDirectInputDevice_GetDeviceData(
  4842. * pdid,
  4843. * sizeof(DIDEVICEOBJECTDATA),
  4844. * NULL,
  4845. * &dwItems,
  4846. * DIGDD_PEEK);
  4847. * if (SUCCEEDED(hres)) {
  4848. * // dwItems = number of elements in buffer
  4849. * if (hres == DI_BUFFEROVERFLOW) {
  4850. * // Buffer overflow occurred; not all data
  4851. * // were successfully captured.
  4852. * }
  4853. * }
  4854. *
  4855. * @ex
  4856. *
  4857. * If you pass <c NULL> for the <p rgdod> and request zero
  4858. * items, this has the effect of querying whether buffer
  4859. * overflow has occurred.
  4860. *
  4861. * |
  4862. *
  4863. * dwItems = 0;
  4864. * hres = IDirectInputDevice_GetDeviceData(
  4865. * pdid,
  4866. * sizeof(DIDEVICEOBJECTDATA),
  4867. * NULL,
  4868. * &dwItems,
  4869. * 0);
  4870. * if (hres == DI_BUFFEROVERFLOW) {
  4871. * // Buffer overflow occurred
  4872. * }
  4873. *
  4874. *
  4875. *//**************************************************************************
  4876. *
  4877. * When reading this code, the following pictures will come in handy.
  4878. *
  4879. *
  4880. * Buffer not wrapped.
  4881. *
  4882. * pBuffer pEnd
  4883. * | |
  4884. * v v
  4885. * +----+----+----+----+----+----+----+----+----+----+----+
  4886. * | | | | | | | | | | | |
  4887. * | | | |data|data|data|data|data| | | |
  4888. * | | | | | | | | | | | |
  4889. * +----+----+----+----+----+----+----+----+----+----+----+
  4890. * ^ ^
  4891. * | |
  4892. * pTail pHead
  4893. *
  4894. *
  4895. * Buffer wrapped.
  4896. *
  4897. * pBuffer pEnd
  4898. * | |
  4899. * v v
  4900. * +----+----+----+----+----+----+----+----+----+----+----+
  4901. * | | | | | | | | | | | |
  4902. * |data|data| | | | | | |data|data|data|
  4903. * | | | | | | | | | | | |
  4904. * +----+----+----+----+----+----+----+----+----+----+----+
  4905. * ^ ^
  4906. * | |
  4907. * pHead pTail
  4908. *
  4909. *
  4910. * Boundary wrap case.
  4911. *
  4912. *
  4913. * pBuffer pEnd
  4914. * | |
  4915. * v v
  4916. * +----+----+----+----+----+----+----+----+----+----+----+
  4917. * | | | | | | | | | | | |
  4918. * | | | | | | |data|data|data|data|data|
  4919. * | | | | | | | | | | | |
  4920. * +----+----+----+----+----+----+----+----+----+----+----+
  4921. * ^ ^
  4922. * | |
  4923. * pHead pTail
  4924. *
  4925. *
  4926. * Note! At no point is pTail == pEnd or pHead == pEnd.
  4927. *
  4928. *****************************************************************************/
  4929. STDMETHODIMP
  4930. CDIDev_GetDeviceData(PV pdd, DWORD cbdod, PDOD rgdod,
  4931. LPDWORD pdwInOut, DWORD fl _THAT)
  4932. {
  4933. HRESULT hres;
  4934. PDD this;
  4935. SOMEDEVICEDATA sdd;
  4936. EnterProcR(IDirectInputDevice8::GetDeviceData,
  4937. (_ "pxpxx", pdd, cbdod, rgdod,
  4938. IsBadReadPtr(pdwInOut, cbX(DWORD)) ? 0 : *pdwInOut, fl));
  4939. /*
  4940. * Note that we do not validate the parameters.
  4941. * The reason is that GetDeviceData is an inner loop function,
  4942. * so it should be as fast as possible.
  4943. *
  4944. * Note also that it is legal to get device data after the device
  4945. * has been unacquired. This lets you "turn on the faucet" for
  4946. * a short period of time, and then parse the data out later.
  4947. */
  4948. this = _thisPv(pdd);
  4949. #ifdef XDEBUG
  4950. hresPvT(pdd);
  4951. if ( IsBadWritePtr(pdwInOut, cbX(*pdwInOut)) ) {
  4952. RPF("ERROR %s: arg %d: invalid value; crash soon", s_szProc, 3);
  4953. }
  4954. #endif
  4955. if( cbdod == cbX(DOD) )
  4956. {
  4957. #ifdef XDEBUG
  4958. /*
  4959. * Only check the buffer if the size is correct otherwise
  4960. * we can smash the stack of the caller when we've already
  4961. * detected the error.
  4962. */
  4963. if ( rgdod ) {
  4964. hresFullValidWritePvCb(rgdod, *pdwInOut * cbdod, 2);
  4965. }
  4966. #endif
  4967. /*
  4968. * Must protect with the critical section to prevent somebody from
  4969. * acquiring/unacquiring or changing the data format or calling
  4970. * another GetDeviceData while we're reading. (We must be serialized.)
  4971. */
  4972. CDIDev_EnterCrit(this);
  4973. AssertF(CDIDev_IsConsistent(this));
  4974. if ( SUCCEEDED(hres = hresFullValidFl(fl, DIGDD_VALID, 4)) ) {
  4975. if ( this->celtBuf ) {
  4976. /*
  4977. * Don't try to read more than there possibly could be.
  4978. * This avoids overflow conditions in case celtIn is
  4979. * some absurdly huge number.
  4980. */
  4981. sdd.celtIn = *pdwInOut;
  4982. sdd.celtOut = 0;
  4983. if ( sdd.celtIn > this->celtBuf ) {
  4984. sdd.celtIn = this->celtBuf;
  4985. }
  4986. sdd.rgdod = rgdod;
  4987. /*
  4988. * For this version of DirectInput, we do not allow
  4989. * callbacks to implement their own GetDeviceData.
  4990. */
  4991. if ( this->pvi ) {
  4992. /*
  4993. * Reacquire is not allowed until after Win98 SE, see OSR Bug # 89958
  4994. */
  4995. if ( this->diHacks.fReacquire &&
  4996. !this->fAcquired && (this->fOnceAcquired || this->fOnceForcedUnacquired) )
  4997. {
  4998. if ( SUCCEEDED( CDIDev_Acquire(pdd THAT_) ) ) {
  4999. // 7/18/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  5000. RPF(" DirectInput: Auto acquired device (0x%p)", pdd);
  5001. }
  5002. }
  5003. #ifdef WINNT
  5004. if( this->fUnacquiredWhenIconic && !IsIconic(this->hwnd) ) {
  5005. if ( SUCCEEDED( CDIDev_Acquire(pdd THAT_) ) ) {
  5006. this->fUnacquiredWhenIconic = 0;
  5007. RPF(" DirectInput: Auto acquired device (0x%p) after being iconic. ", pdd);
  5008. }
  5009. }
  5010. #endif
  5011. if ( (this->fAcquired && (this->pvi->fl & VIFL_ACQUIRED)) ||
  5012. (fl & DIGDD_RESIDUAL) ) {
  5013. /*
  5014. * The device object data from the driver is always a
  5015. * DX3 (<DX8) structure
  5016. */
  5017. LPDIDEVICEOBJECTDATA_DX3 pdod, pdodHead;
  5018. DWORD celt;
  5019. /*
  5020. * Snapshot the value of pdodHead, because it can
  5021. * change asynchronously. The other fields won't
  5022. * change unless we ask for them to be changed.
  5023. */
  5024. pdodHead = this->pvi->pHead;
  5025. /*
  5026. * Throughout, pdod points to the first unprocessed
  5027. * element.
  5028. */
  5029. pdod = this->pvi->pTail;
  5030. /*
  5031. * If we are wrapped, handle the initial run.
  5032. */
  5033. if ( pdodHead < this->pvi->pTail ) {
  5034. celt = (DWORD)(this->pvi->pEnd - this->pvi->pTail);
  5035. AssertF(celt);
  5036. pdod = CDIDev_GetSomeDeviceData( this, pdod, celt, &sdd );
  5037. }
  5038. /*
  5039. * Now handle the glob from pdod to pdodHead.
  5040. * Remember, pvi->pdodHead may have changed
  5041. * behind our back; use the cached value to
  5042. * ensure consistency. (If we miss data,
  5043. * it'll show up later.)
  5044. */
  5045. AssertF(fLimpFF(sdd.celtIn, pdodHead >= pdod));
  5046. celt = (DWORD)(pdodHead - pdod);
  5047. if ( celt ) {
  5048. pdod = CDIDev_GetSomeDeviceData( this, pdod, celt, &sdd );
  5049. }
  5050. *pdwInOut = sdd.celtOut;
  5051. if ( !(fl & DIGDD_PEEK) ) {
  5052. this->pvi->pTail = pdod;
  5053. }
  5054. if ( rgdod && sdd.celtOut && this->fCook ) {
  5055. CDIDev_CookDeviceData(this, sdd.celtOut, rgdod );
  5056. }
  5057. CAssertF(S_OK == 0);
  5058. CAssertF(DI_BUFFEROVERFLOW == 1);
  5059. hres = (HRESULT)(UINT_PTR)pvExchangePpvPv(&this->pvi->fOverflow, 0);
  5060. #ifdef DEBUG_STICKY
  5061. if( hres == 1 )
  5062. {
  5063. OutputDebugString( TEXT( "Device buffer overflowed\r\n" ) );
  5064. }
  5065. if( sdd.celtOut )
  5066. {
  5067. PDOD pdoddbg;
  5068. TCHAR tszDbg[80];
  5069. wsprintf( tszDbg, TEXT("GotData %d elements: "), sdd.celtOut );
  5070. OutputDebugString( tszDbg );
  5071. for( pdoddbg=rgdod; pdoddbg<&rgdod[sdd.celtOut]; pdoddbg++ )
  5072. {
  5073. wsprintf( tszDbg, TEXT("0x%02x:x0x%08x "), pdoddbg->dwOfs, pdoddbg->dwData );
  5074. OutputDebugString( tszDbg );
  5075. }
  5076. OutputDebugString( TEXT("\r\n") );
  5077. }
  5078. #endif /* DEBUG_STICKY */
  5079. } else if (this->fAcquired && !(this->pvi->fl & VIFL_ACQUIRED)) {
  5080. RPF("ERROR %s - %s", s_szProc, "input lost");
  5081. hres = DIERR_INPUTLOST;
  5082. CDIDev_InternalUnacquire(this);
  5083. } else {
  5084. RPF("ERROR %s: %s", s_szProc,
  5085. this->hresNotAcquired == DIERR_NOTACQUIRED
  5086. ? "Not acquired" : "Input lost");
  5087. hres = this->hresNotAcquired;
  5088. }
  5089. } else { /* Don't support device-side GetData yet */
  5090. hres = E_NOTIMPL;
  5091. }
  5092. } else { /* Device is not buffered */
  5093. #ifdef XDEBUG
  5094. if ( !this->fNotifiedNotBuffered ) {
  5095. this->fNotifiedNotBuffered = 1;
  5096. RPF("ERROR %s: arg %d: device is not buffered", s_szProc, 0);
  5097. }
  5098. #endif
  5099. hres = DIERR_NOTBUFFERED;
  5100. }
  5101. }
  5102. }
  5103. else
  5104. {
  5105. if( cbdod == cbX(DIDEVICEOBJECTDATA_DX3) )
  5106. {
  5107. RPF("ERROR %s: arg %d: old size, invalid for DX8", s_szProc, 1);
  5108. }
  5109. else
  5110. {
  5111. RPF("ERROR %s: arg %d: invalid value", s_szProc, 1);
  5112. }
  5113. hres = E_INVALIDARG;
  5114. }
  5115. CDIDev_LeaveCrit(this);
  5116. return hres;
  5117. }
  5118. /*****************************************************************************
  5119. *
  5120. * @doc EXTERNAL
  5121. *
  5122. * @method HRESULT | IDirectInputDevice8 | Poll |
  5123. *
  5124. * Retrieves data from polled objects on a DirectInput device.
  5125. * If the device does not require polling, then calling this
  5126. * method has no effect. If a device that requires polling
  5127. * is not polled periodically, no new data will be received
  5128. * from the device.
  5129. *
  5130. * Before a device data can be polled, the data format must
  5131. * be set via <mf IDirectInputDevice::SetDataFormat>, and
  5132. * the device must be acquired via
  5133. * <mf IDirectInputDevice::Acquire>.
  5134. *
  5135. * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
  5136. *
  5137. * @returns
  5138. *
  5139. * Returns a COM error code. The following error codes are
  5140. * intended to be illustrative and not necessarily comprehensive.
  5141. *
  5142. * <c DI_OK> = <c S_OK>: The operation completed successfully.
  5143. *
  5144. * <c DI_NOEFFECT> = <c S_FALSE>: The device does not require
  5145. * polling.
  5146. *
  5147. * <c DIERR_INPUTLOST>: Access to the device has been
  5148. * interrupted. The application should re-acquire the
  5149. * device.
  5150. *
  5151. * <c DIERR_NOTACQUIRED>: The device is not acquired.
  5152. *
  5153. *****************************************************************************/
  5154. STDMETHODIMP
  5155. CDIDev_Poll(PV pdd _THAT)
  5156. {
  5157. HRESULT hres;
  5158. PDD this;
  5159. EnterProcR(IDirectInputDevice8::Poll, (_ "p", pdd));
  5160. /*
  5161. * Note that we do not validate the parameters.
  5162. * The reason is that Poll is an inner loop function,
  5163. * so it should be as fast as possible.
  5164. */
  5165. #ifdef XDEBUG
  5166. hresPvT(pdd);
  5167. #endif
  5168. this = _thisPv(pdd);
  5169. /*
  5170. * Fast out: If the device doesn't require polling,
  5171. * then don't bother with the critical section or other validation.
  5172. */
  5173. if ( this->fPolledDataFormat ) {
  5174. /*
  5175. * Must protect with the critical section to prevent somebody from
  5176. * unacquiring while we're polling.
  5177. */
  5178. CDIDev_EnterCrit(this);
  5179. if ( this->fAcquired ) {
  5180. hres = this->pdcb->lpVtbl->Poll(this->pdcb);
  5181. } else {
  5182. hres = this->hresNotAcquired;
  5183. }
  5184. CDIDev_LeaveCrit(this);
  5185. } else {
  5186. if ( this->fAcquired ) {
  5187. hres = S_FALSE;
  5188. } else {
  5189. hres = this->hresNotAcquired;
  5190. }
  5191. }
  5192. /*
  5193. * Failing polls are really annoying so don't use ExitOleProc
  5194. */
  5195. if( FAILED( hres ) )
  5196. {
  5197. SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("IDirectInputDevice::Poll failed 0x%08x"), hres );
  5198. }
  5199. ExitProc();
  5200. return hres;
  5201. }
  5202. /*****************************************************************************
  5203. *
  5204. * @doc EXTERNAL
  5205. *
  5206. * @method HRESULT | IDirectInputDevice8 | SendDeviceData |
  5207. *
  5208. * Sends data to the device.
  5209. *
  5210. * Before device data can be sent to a device,
  5211. * the device must be acquired via
  5212. * <mf IDirectInputDevice::Acquire>.
  5213. *
  5214. * Note that no guarantees
  5215. * are made on the order in which the individual data
  5216. * elements are sent. However, data sent by
  5217. * successive calls to
  5218. * <mf IDirectInputDevice8::SendDeviceData>
  5219. * will not be interleaved.
  5220. * Furthermore, if multiple pieces of
  5221. * data are sent to the same object, it is unspecified
  5222. * which actual piece of data is sent.
  5223. *
  5224. * Consider, for example, a device which can be sent
  5225. * data in packets, each packet describing two pieces
  5226. * of information, call them A and B. Suppose the
  5227. * application attempts to send three data elements,
  5228. * "B = 2", "A = 1", and "B = 0".
  5229. *
  5230. * The actual device will be sent a single packet.
  5231. * The "A" field of the packet will contain the value 1,
  5232. * and the "B" field of the packet will be either 2 or 0.
  5233. *
  5234. * If the application wishes the data to be sent to the
  5235. * device exactly as specified, then three calls to
  5236. * <mf IDirectInputDevice8::SendDeviceData> should be
  5237. * performed, each call sending one data element.
  5238. *
  5239. * In response to the first call,
  5240. * the device will be sent a packet where the "A" field
  5241. * is blank and the "B" field contains the value 2.
  5242. *
  5243. * In response to the second call,
  5244. * the device will be sent a packet where the "A" field
  5245. * contains the value 1, and the "B" field is blank.
  5246. *
  5247. * Finally, in response to the third call,
  5248. * the device will be sent a packet where the "A" field
  5249. * is blank and the "B" field contains the value 0.
  5250. *
  5251. *
  5252. *
  5253. * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
  5254. *
  5255. * @parm DWORD | cbObjectData |
  5256. *
  5257. * The size of a single <t DIDEVICEOBJECTDATA> structure in bytes.
  5258. *
  5259. * @parm IN LPCDIDEVICEOBJECTDATA | rgdod |
  5260. *
  5261. * Array of <t DIDEVICEOBJECTDATA> structures containing
  5262. * the data to send to the device. It must consist of
  5263. * *<p pdwInOut> elements.
  5264. *
  5265. * <y Note>: The <e DIDEVICEOBJECTDATA.dwOfs> field of
  5266. * the <t DIDEVICEOBJECTDATA> structure must contain the
  5267. * device object identifier (as obtained from the
  5268. * <e DIDEVICEOBJECTINSTANCE.dwType> field of the
  5269. * <t DIDEVICEOBJECTINSTANCE> sturcture) for the device
  5270. * object at which the data is directed.
  5271. *
  5272. * Furthermore, the <e DIDEVICEOBJECTDATA.dwTimeStamp>
  5273. * <e DIDEVICEOBJECTDATA.dwSequence> and
  5274. * <e DIDEVICEOBJECTDATA.uAppData> fields are
  5275. * reserved for future use and must be zero.
  5276. *
  5277. * @parm INOUT LPDWORD | pdwInOut |
  5278. *
  5279. * On entry, contains the number of elements in the array
  5280. * pointed to by <p rgdod>. On exit, contains the number
  5281. * of elements actually sent to the device.
  5282. *
  5283. * @parm DWORD | fl |
  5284. *
  5285. * Flags which control the manner in which data is sent.
  5286. * It may consist of zero or more of the following flags:
  5287. *
  5288. * <c DISDD_CONTINUE>: If this flag is set, then
  5289. * the device data sent will be overlaid upon the previously
  5290. * sent device data. Otherwise, the device data sent
  5291. * will start from scratch.
  5292. *
  5293. * For example, suppose a device supports two button outputs,
  5294. * call them A and B.
  5295. * If an application first calls
  5296. * <mf IDirectInputDevice8::SendDeviceData> passing
  5297. * "button A pressed", then
  5298. * a packet of the form "A pressed, B not pressed" will be
  5299. * sent to the device.
  5300. * If an application then calls
  5301. * <mf IDirectInputDevice8::SendDeviceData> passing
  5302. * "button B pressed" and the <c DISDD_CONTINUE> flag, then
  5303. * a packet of the form "A pressed, B pressed" will be
  5304. * sent to the device.
  5305. * However, if the application had not passed the
  5306. * <c DISDD_CONTINUE> flag, then the packet sent to the device
  5307. * would have been "A not pressed, B pressed".
  5308. *
  5309. *
  5310. * @returns
  5311. *
  5312. * Returns a COM error code. The following error codes are
  5313. * intended to be illustrative and not necessarily comprehensive.
  5314. *
  5315. * <c DI_OK> = <c S_OK>: The operation completed successfully.
  5316. *
  5317. * <c DIERR_INPUTLOST>: Access to the device has been
  5318. * interrupted. The application should re-acquire the
  5319. * device.
  5320. *
  5321. * <c DIERR_NOTACQUIRED>: The device is not acquired.
  5322. *
  5323. *****************************************************************************/
  5324. STDMETHODIMP
  5325. CDIDev_SendDeviceData(PV pdd, DWORD cbdod, LPCDIDEVICEOBJECTDATA rgdod,
  5326. LPDWORD pdwInOut, DWORD fl _THAT)
  5327. {
  5328. HRESULT hres;
  5329. PDD this;
  5330. EnterProcR(IDirectInputDevice8::SendDeviceData,
  5331. (_ "pxpxx", pdd, cbdod, rgdod,
  5332. IsBadReadPtr(pdwInOut, cbX(DWORD)) ? 0 : *pdwInOut, fl));
  5333. /*
  5334. * Note that parameter validation is limited as SendDeviceData is
  5335. * intended to be an inner loop function. In practice SendDeviceData is
  5336. * rarely used so speed is not that important.
  5337. */
  5338. #ifdef XDEBUG
  5339. hresPvT(pdd);
  5340. if ( IsBadWritePtr(pdwInOut, cbX(*pdwInOut)) ) {
  5341. RPF("ERROR %s: arg %d: invalid value; crash soon", s_szProc, 3);
  5342. }
  5343. hresFullValidReadPvCb(rgdod, cbX(*pdwInOut) * cbdod, 2);
  5344. #endif
  5345. this = _thisPv(pdd);
  5346. /*
  5347. * Must protect with the critical section to prevent somebody from
  5348. * unacquiring while we're sending data.
  5349. */
  5350. CDIDev_EnterCrit(this);
  5351. if ( SUCCEEDED(hres = hresFullValidFl(fl, DISDD_VALID, 4)) ) {
  5352. if( cbdod == cbX(DOD) )
  5353. {
  5354. #ifdef XDEBUG
  5355. UINT iod;
  5356. LPCDIDEVICEOBJECTDATA pcdod;
  5357. for ( iod = 0, pcdod=rgdod; iod < *pdwInOut; iod++ ) {
  5358. if ( pcdod->dwTimeStamp ) {
  5359. RPF("%s: ERROR: dwTimeStamp must be zero", s_szProc);
  5360. }
  5361. if ( pcdod->dwSequence ) {
  5362. RPF("%s: ERROR: dwSequence must be zero", s_szProc);
  5363. }
  5364. if( pcdod->uAppData ){
  5365. RPF("%s: ERROR: uAppData must be zero", s_szProc);
  5366. }
  5367. pcdod++;
  5368. }
  5369. #endif
  5370. if ( this->fAcquired ) {
  5371. UINT iod;
  5372. LPDIDEVICEOBJECTDATA pdodCopy;
  5373. hres = AllocCbPpv( cbCxX( *pdwInOut, *pdodCopy ), &pdodCopy );
  5374. if( SUCCEEDED( hres ) )
  5375. {
  5376. LPDIDEVICEOBJECTDATA pdod;
  5377. memcpy( pdodCopy, rgdod, cbCxX( *pdwInOut, *pdodCopy ) );
  5378. for( iod=0, pdod=pdodCopy; iod < *pdwInOut; iod++ )
  5379. {
  5380. int iobj = CDIDev_OffsetToIobj(this, pdod->dwOfs);
  5381. pdod->dwOfs = this->df.rgodf[iobj].dwType;
  5382. pdod++;
  5383. }
  5384. hres = this->pdcb->lpVtbl->SendDeviceData(this->pdcb, cbdod,
  5385. pdodCopy, pdwInOut, fl );
  5386. FreePv( pdodCopy );
  5387. }
  5388. } else {
  5389. hres = this->hresNotAcquired;
  5390. }
  5391. } else {
  5392. RPF("ERROR %s: arg %d: invalid value", s_szProc, 1);
  5393. }
  5394. }
  5395. CDIDev_LeaveCrit(this);
  5396. ExitOleProc();
  5397. return hres;
  5398. }
  5399. #undef sqfl
  5400. #define sqfl sqflDf
  5401. /*****************************************************************************
  5402. *
  5403. * @doc INTERNAL
  5404. *
  5405. * @func BOOL | CDIDev_CheckId |
  5406. *
  5407. * Verify that the item has the appropriate type.
  5408. *
  5409. * @parm DWORD | dwId |
  5410. *
  5411. * ID to locate.
  5412. *
  5413. * @parm UINT | fl |
  5414. *
  5415. * Bitmask of flags for things to validate.
  5416. *
  5417. * The <c DEVCO_TYPEMASK> fields describe what type
  5418. * of object we should be locating.
  5419. *
  5420. * The <c DEVCO_ATTRMASK> fields describe the attribute
  5421. * bits that are required.
  5422. *
  5423. *****************************************************************************/
  5424. BOOL INLINE
  5425. CDIDev_CheckId(DWORD dwId, DWORD fl)
  5426. {
  5427. CAssertF(DIDFT_ATTRMASK == DEVCO_ATTRMASK);
  5428. return(dwId & fl & DEVCO_TYPEMASK) &&
  5429. fHasAllBitsFlFl(dwId, fl & DIDFT_ATTRMASK);
  5430. }
  5431. /*****************************************************************************
  5432. *
  5433. * @doc INTERNAL
  5434. *
  5435. * @method int | CDIDev | IdToIobj |
  5436. *
  5437. * Locate an item which matches the specified ID.
  5438. *
  5439. * @cwrap PDD | this
  5440. *
  5441. * @parm DWORD | dwId |
  5442. *
  5443. * ID to locate.
  5444. *
  5445. * @returns
  5446. *
  5447. * Returns the index of the object found, or -1 on error.
  5448. *
  5449. *****************************************************************************/
  5450. int INTERNAL
  5451. CDIDev_IdToIobj(PDD this, DWORD dwId)
  5452. {
  5453. int iobj;
  5454. /* Someday: Perf: Should have xlat table */
  5455. for ( iobj = this->df.dwNumObjs; --iobj >= 0; ) {
  5456. PODF podf = &this->df.rgodf[iobj];
  5457. if ( DIDFT_FINDMATCH(podf->dwType, dwId) ) {
  5458. goto done;
  5459. }
  5460. }
  5461. iobj = -1;
  5462. done:;
  5463. return iobj;
  5464. }
  5465. #if 0
  5466. /*****************************************************************************
  5467. *
  5468. * @doc INTERNAL
  5469. *
  5470. * @method HRESULT | CDIDev | IdToId |
  5471. *
  5472. * Convert a single <t DWORD> from an ID to an ID.
  5473. *
  5474. * This is clearly a very simple operation.
  5475. *
  5476. * It's all validation.
  5477. *
  5478. * @cwrap PDD | this
  5479. *
  5480. * @parm LPDWORD | pdw |
  5481. *
  5482. * Single item to convert.
  5483. *
  5484. * @parm UINT | fl |
  5485. *
  5486. * Bitmask of flags that govern the conversion.
  5487. * The function should look only at
  5488. * <c DEVCO_AXIS> or <c DEVCO_BUTTON>.
  5489. *
  5490. *****************************************************************************/
  5491. HRESULT INTERNAL
  5492. CDIDev_IdToId(PDD this, LPDWORD pdw, UINT fl)
  5493. {
  5494. HRESULT hres;
  5495. int iobj;
  5496. iobj = CDIDev_FindId(this, *pdw, fl);
  5497. if ( iobj >= 0 ) {
  5498. *pdw = this->df.rgodf[iobj].dwType;
  5499. hres = S_OK;
  5500. } else {
  5501. hres = E_INVALIDARG;
  5502. }
  5503. return hres;
  5504. }
  5505. #endif
  5506. /*****************************************************************************
  5507. *
  5508. * @doc INTERNAL
  5509. *
  5510. * @method int | CDIDev | OffsetToIobj |
  5511. *
  5512. * Convert a single <t DWORD> from an offset to an object index.
  5513. *
  5514. * @cwrap PDD | this
  5515. *
  5516. * @parm DWORD | dwOfs |
  5517. *
  5518. * Offset to convert.
  5519. *
  5520. *****************************************************************************/
  5521. int INTERNAL
  5522. CDIDev_OffsetToIobj(PDD this, DWORD dwOfs)
  5523. {
  5524. int iobj;
  5525. AssertF(this->pdix);
  5526. AssertF(this->rgiobj);
  5527. if ( dwOfs < this->dwDataSize ) {
  5528. iobj = this->rgiobj[dwOfs];
  5529. if ( iobj >= 0 ) {
  5530. AssertF(this->pdix[iobj].dwOfs == dwOfs);
  5531. } else {
  5532. AssertF(iobj == -1);
  5533. }
  5534. } else {
  5535. iobj = -1;
  5536. }
  5537. return iobj;
  5538. }
  5539. /*****************************************************************************
  5540. *
  5541. * @doc INTERNAL
  5542. *
  5543. * @method int | CDIDev | IobjToId |
  5544. *
  5545. * Convert an object index to an ID.
  5546. *
  5547. * @cwrap PDD | this
  5548. *
  5549. * @parm int | iobj |
  5550. *
  5551. * Single item to convert.
  5552. *
  5553. *****************************************************************************/
  5554. DWORD INLINE
  5555. CDIDev_IobjToId(PDD this, int iobj)
  5556. {
  5557. AssertF((DWORD)iobj < this->df.dwNumObjs);
  5558. return this->df.rgodf[iobj].dwType;
  5559. }
  5560. /*****************************************************************************
  5561. *
  5562. * @doc INTERNAL
  5563. *
  5564. * @method int | CDIDev | IobjToOffset |
  5565. *
  5566. * Convert an object index to an offset.
  5567. *
  5568. * @cwrap PDD | this
  5569. *
  5570. * @parm int | iobj |
  5571. *
  5572. * Single item to convert.
  5573. *
  5574. *****************************************************************************/
  5575. DWORD INLINE
  5576. CDIDev_IobjToOffset(PDD this, int iobj)
  5577. {
  5578. AssertF((DWORD)iobj < this->df.dwNumObjs);
  5579. AssertF(this->pdix);
  5580. return this->pdix[iobj].dwOfs;
  5581. }
  5582. /*****************************************************************************
  5583. *
  5584. * @doc INTERNAL
  5585. *
  5586. * @method HRESULT | CDIDev | ConvertObjects |
  5587. *
  5588. * Convert between the ways of talking about device gizmos.
  5589. *
  5590. * Since this is used only by the force feedback subsystem,
  5591. * we also barf if the device found does not support
  5592. * force feedback.
  5593. *
  5594. * @cwrap PDD | this
  5595. *
  5596. * @parm UINT | cdw |
  5597. *
  5598. * Number of elements to convert (in-place).
  5599. *
  5600. * @parm LPDWORD | rgdw |
  5601. *
  5602. * Array of elements to convert.
  5603. *
  5604. * @parm UINT | fl |
  5605. *
  5606. * Flags that describe how to do the conversion.
  5607. *
  5608. * <c DEVCO_AXIS> or <c DEVCO_BUTTON> indicate whether
  5609. * the item being converted is an axis or button.
  5610. *
  5611. * <c DEVCO_FROM*> specifies what the existing value is.
  5612. *
  5613. * <c DEVCO_TO*> specifies what the new values should be.
  5614. *
  5615. *****************************************************************************/
  5616. STDMETHODIMP
  5617. CDIDev_ConvertObjects(PDD this, UINT cdw, LPDWORD rgdw, UINT fl)
  5618. {
  5619. HRESULT hres;
  5620. /*
  5621. * Don't let somebody change the data format while we're
  5622. * looking at it.
  5623. */
  5624. CDIDev_EnterCrit(this);
  5625. AssertF((fl & ~DEVCO_VALID) == 0);
  5626. if ( fLimpFF(fl & (DEVCO_FROMOFFSET | DEVCO_TOOFFSET),
  5627. this->pdix && this->rgiobj) ) {
  5628. UINT idw;
  5629. for ( idw = 0; idw < cdw; idw++ ) {
  5630. /*
  5631. * Convert from its source to an object index,
  5632. * validate the object index, then convert to
  5633. * the target.
  5634. */
  5635. int iobj;
  5636. switch ( fl & DEVCO_FROMMASK ) {
  5637. default:
  5638. AssertF(0); /* Huh? */
  5639. case DEVCO_FROMID:
  5640. iobj = CDIDev_IdToIobj(this, rgdw[idw]);
  5641. break;
  5642. case DEVCO_FROMOFFSET:
  5643. iobj = CDIDev_OffsetToIobj(this, rgdw[idw]);
  5644. break;
  5645. }
  5646. if ( iobj < 0 ) {
  5647. hres = E_INVALIDARG; /* Invalid object */
  5648. goto done;
  5649. }
  5650. AssertF((DWORD)iobj < this->df.dwNumObjs);
  5651. if ( !CDIDev_CheckId(this->df.rgodf[iobj].dwType, fl) ) {
  5652. hres = E_INVALIDARG; /* Bad attributes */
  5653. goto done;
  5654. }
  5655. switch ( fl & DEVCO_TOMASK ) {
  5656. default:
  5657. AssertF(0); /* Huh? */
  5658. case DEVCO_TOID:
  5659. rgdw[idw] = CDIDev_IobjToId(this, iobj);
  5660. break;
  5661. case DEVCO_TOOFFSET:
  5662. rgdw[idw] = CDIDev_IobjToOffset(this, iobj);
  5663. break;
  5664. }
  5665. }
  5666. hres = S_OK;
  5667. done:;
  5668. } else {
  5669. RPF("ERROR: Must have a data format to use offsets");
  5670. hres = E_INVALIDARG;
  5671. }
  5672. CDIDev_LeaveCrit(this);
  5673. return hres;
  5674. }