/***************************************************************************** * * DIObj.c * * Copyright (c) 1996 - 2000 Microsoft Corporation. All Rights Reserved. * * Abstract: * * The IDirectInput main interface. * * Contents: * * CDIObj_New * *****************************************************************************/ #include "dinputpr.h" /***************************************************************************** * * The sqiffle for this file. * *****************************************************************************/ #define sqfl sqflDi #define DIDEVTYPE_DEVICE_ORDER 1 #define DIDEVTYPE_HID_MOUSE_ORDER 2 #define DIDEVTYPE_HID_KEYBOARD_ORDER 3 #define DIDEVTYPE_MOUSE_ORDER 4 #define DIDEVTYPE_KEYBOARD_ORDER 5 #define DIDEVTYPE_SUPPLEMENTAL_ORDER 6 #define MAX_ORDER (DI8DEVTYPE_MAX - DI8DEVTYPE_MIN + DIDEVTYPE_SUPPLEMENTAL_ORDER + 2) #define INVALID_ORDER (MAX_ORDER + 1) #define MAX_DEVICENUM 32 DIORDERDEV g_DiDevices[MAX_DEVICENUM]; //all devices that are attached int g_nCurDev; /***************************************************************************** * * @doc INTERNAL * * @struct CDIObj | * * The object, from which other things come. * * The A and W versions are simply alternate interfaces on the same * underlying object. * * There really isn't anything interesting in the structure * itself. * * * @field IDirectInputA | diA | * * ANSI DirectInput object (containing vtbl). * * @field IDirectInputW | diW | * * UNICODE DirectInput object (containing vtbl). * * @field IDirectInputJoyConfig *| pdjc | * * Aggregated joystick configuration interface (if created). * * @field BOOL | fCritInited:1 | * * Set if the critical section has been initialized. * * @field CRITICAL_SECTION | crst | * * Critical section that guards thread-sensitive data. *****************************************************************************/ typedef struct CDIObj { /* Supported interfaces */ TFORM(IDirectInput8) TFORM(di); SFORM(IDirectInput8) SFORM(di); DWORD dwVersion; IDirectInputJoyConfig *pdjc; BOOL fCritInited:1; CRITICAL_SECTION crst; } CDIObj, DDI, *PDDI; #define ThisClass CDIObj #define ThisInterface TFORM(IDirectInput8) #define ThisInterfaceA IDirectInput8A #define ThisInterfaceW IDirectInput8W #define ThisInterfaceT IDirectInput8 /***************************************************************************** * * Declare the interfaces we will be providing. * *****************************************************************************/ Primary_Interface(CDIObj, TFORM(ThisInterfaceT)); Secondary_Interface(CDIObj, SFORM(ThisInterfaceT)); /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInput | QueryInterface | * * Gives a client access to other interfaces on an object. * * @cwrap LPDIRECTINPUT | lpDirectInput * * @parm IN REFIID | riid | * * The requested interface's IID. * * @parm OUT LPVOID * | ppvObj | * * Receives a pointer to the obtained interface. * * @returns * * Returns a COM error code. * * @xref OLE documentation for . * *//************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInput | AddRef | * * Increments the reference count for the interface. * * @cwrap LPDIRECTINPUT | lpDirectInput * * @returns * * Returns the object reference count. * * @xref OLE documentation for . * ***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInput | Release | * * Decrements the reference count for the interface. * If the reference count on the object falls to zero, * the object is freed from memory. * * @cwrap LPDIRECTINPUT | lpDirectInput * * @returns * * Returns the object reference count. * * @xref OLE documentation for . * *****************************************************************************/ #ifdef DEBUG Default_QueryInterface(CDIObj) Default_AddRef(CDIObj) Default_Release(CDIObj) #else #define CDIObj_QueryInterface Common_QueryInterface #define CDIObj_AddRef Common_AddRef #define CDIObj_Release Common_Release #endif #define CDIObj_AppFinalize Common_AppFinalize /***************************************************************************** * * @doc INTERNAL * * @mfunc void | CDIObj | EnterCrit | * * Enter the object critical section. * * @doc INTERNAL * * @mfunc void | CDIObj | LeaveCrit | * * Leave the object critical section. * *****************************************************************************/ void INLINE CDIObj_EnterCrit(PDDI this) { EnterCriticalSection(&this->crst); } void INLINE CDIObj_LeaveCrit(PDDI this) { LeaveCriticalSection(&this->crst); } /***************************************************************************** * * @doc INTERNAL * * @mfunc HRESULT | IDirectInput | QIHelper | * * We will dynamically create * and aggregate it with us. * * @parm IN REFIID | riid | * * The requested interface's IID. * * @parm OUT LPVOID * | ppvObj | * * Receives a pointer to the obtained interfacethis->pdix[iobj].dwOfs. * *****************************************************************************/ STDMETHODIMP CDIObj_QIHelper(PDDI this, RIID riid, PPV ppvObj) { HRESULT hres; EnterProcI(CDIObj_QIHelper, (_ "pG", this, riid)); if ( IsEqualIID(riid, &IID_IDirectInputJoyConfig8) ) { *ppvObj = 0; /* In case the New fails */ CDIObj_EnterCrit(this); if ( this->pdjc == 0 ) { hres = CJoyCfg_New((PUNK)this, &IID_IUnknown, (PPV)&this->pdjc); } else { hres = S_OK; } CDIObj_LeaveCrit(this); if ( SUCCEEDED(hres) ) { /* * This QI will addref us if it succeeds. */ hres = OLE_QueryInterface(this->pdjc, riid, ppvObj); } else { this->pdjc = 0; } } else { hres = Common_QIHelper(this, riid, ppvObj); } ExitOleProcPpv(ppvObj); return(hres); } /***************************************************************************** * * @doc INTERNAL * * @func void | CDIObj_Finalize | * * Clean up our instance data. * * @parm PV | pvObj | * * Object being released. Note that it may not have been * completely initialized, so everything should be done * carefully. * *****************************************************************************/ void INTERNAL CDIObj_Finalize(PV pvObj) { PDDI this = pvObj; Invoke_Release(&this->pdjc); if ( this->fCritInited ) { DeleteCriticalSection(&this->crst); } } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInput | CreateDeviceHelper | * * Creates and initializes an instance of a device which is * specified by the GUID and IID. * * @cwrap LPDIRECTINPUT | lpDirectInput * * @parm IN PCGUID | pguid | * * See . * * @parm OUT PPV | ppvObj | * * See . * * @parm IN LPUNKNOWN | punkOuter | * * See . * * @parm IN RIID | riid | * * The interface the application wants to create. This will * be either or . * If the object is aggregated, then this parameter is ignored. * * @returns * * Returns a COM error code. * *****************************************************************************/ STDMETHODIMP CDIObj_CreateDeviceHelper(PDDI this, PCGUID pguid, PPV ppvObj, PUNK punkOuter, RIID riid) { HRESULT hres; EnterProc(CDIObj_CreateDeviceHelper, (_ "pGxG", this, pguid, punkOuter, riid)); /* * CDIDev_New will validate the punkOuter and ppvObj. * * IDirectInputDevice_Initialize will validate the pguid. * * riid is known good (since it came from CDIObj_CreateDeviceW * or CDIObj_CreateDeviceA). */ hres = CDIDev_New(punkOuter, punkOuter ? &IID_IUnknown : riid, ppvObj); if ( SUCCEEDED(hres) && punkOuter == 0 ) { PDID pdid = *ppvObj; hres = IDirectInputDevice_Initialize(pdid, g_hinst, this->dwVersion, pguid); if ( SUCCEEDED(hres) ) { } else { Invoke_Release(ppvObj); } } ExitOleProcPpv(ppvObj); return(hres); } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInput | CreateDevice | * * Creates and initializes an instance of a device which is * specified by the GUID and IID. * * @cwrap LPDIRECTINPUT | lpDirectInput * * @parm REFGUID | rguid | * Identifies the instance of the * device for which the indicated interface * is requested. The method * can be used to determine which instance GUIDs are supported by * the system. * * @parm OUT LPDIRECTINPUTDEVICE * | lplpDirectInputDevice | * Points to where to return * the pointer to the interface, if successful. * * @parm IN LPUNKNOWN | punkOuter | Pointer to controlling unknown * for OLE aggregation, or 0 if the interface is not aggregated. * Most callers will pass 0. * * @comm Calling this function with

= NULL * is equivalent to creating the object via * (&CLSID_DirectInputDevice, NULL, * CLSCTX_INPROC_SERVER,

,

); * then initializing it with . * * Calling this function with

!= NULL * is equivalent to creating the object via * (&CLSID_DirectInputDevice,

, * CLSCTX_INPROC_SERVER, &IID_IUnknown,

). * The aggregated object must be initialized manually. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : The *

parameter is not a valid pointer. * * = : * Out of memory. * * = * The specified interface is not supported by the object. * * = The device instance does not * correspond to a device that is registered with DirectInput. * *****************************************************************************/ STDMETHODIMP CDIObj_CreateDeviceW(PV pdiW, REFGUID rguid, PPDIDW ppdidW, PUNK punkOuter) { HRESULT hres; EnterProcR(IDirectInput8::CreateDevice, (_ "pGp", pdiW, rguid, punkOuter)); if ( SUCCEEDED(hres = hresPvI(pdiW, ThisInterfaceW)) ) { PDDI this = _thisPvNm(pdiW, diW); hres = CDIObj_CreateDeviceHelper(this, rguid, (PPV)ppdidW, punkOuter, &IID_IDirectInputDevice8W); } ExitOleProcPpv(ppdidW); return(hres); } STDMETHODIMP CDIObj_CreateDeviceA(PV pdiA, REFGUID rguid, PPDIDA ppdidA, PUNK punkOuter) { HRESULT hres; EnterProcR(IDirectInput8::CreateDevice, (_ "pGp", pdiA, rguid, punkOuter)); if ( SUCCEEDED(hres = hresPvI(pdiA, ThisInterfaceA)) ) { PDDI this = _thisPvNm(pdiA, diA); hres = CDIObj_CreateDeviceHelper(this, rguid, (PPV)ppdidA, punkOuter, &IID_IDirectInputDevice8A); } ExitOleProcPpv(ppdidA); return(hres); } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | CDIObj_TestDeviceFlags | * * Determines whether the device matches the specified flags. * Phantom devices are treated as not really there. * * @parm PDIDW | pdidW | * * Device to be queried. * * @parm DWORD | edfl | * * Enumeration flags. It is one or more values. * * The bits in the enumeration flags are in two categories.* * * Normal flags are the ones whose presence requires that * the corresponding bit in the device flags also be set. * * Inverted flags () are the ones whose * absence requires that the corresponding bit in the device * flags also be absent. * * By inverting the inclusion flags in both the enumeration * flags and the actual device flags, and then treating the * whole thing as a bunch of normal flags, we get the desired * behavior for the inclusion flags. * * @returns * * if the device meets the criteria. * * if the device does not meet the criteria. * Note that relies on * this specific return value. * * Other error code as appropriate. * *****************************************************************************/ HRESULT EXTERNAL CDIObj_TestDeviceFlags(PDIDW pdidW, DWORD edfl) { HRESULT hres; DIDEVCAPS_DX3 dc; EnterProcI(CDIObj_TestDeviceFlags, (_ "px", pdidW, edfl)); /* * We intentionally use a DIDEVCAPS_DX3 because going for * a full DIDEVCAPS_DX5 requires us to load the force * feedback driver which is pointless for our current * goal. */ dc.dwSize = cbX(dc); hres = IDirectInputDevice_GetCapabilities(pdidW, (PV)&dc); AssertF(dc.dwSize == cbX(dc)); CAssertF(DIEDFL_ATTACHEDONLY == DIDC_ATTACHED); CAssertF(DIEDFL_FORCEFEEDBACK == DIDC_FORCEFEEDBACK); CAssertF(DIEDFL_INCLUDEALIASES == DIDC_ALIAS); CAssertF(DIEDFL_INCLUDEPHANTOMS == DIDC_PHANTOM); CAssertF(DIEDFL_INCLUDEHIDDEN == DIDC_HIDDEN); if ( SUCCEEDED(hres) ) { if ( fHasAllBitsFlFl(dc.dwFlags ^ DIEDFL_INCLUDEMASK, edfl ^ DIEDFL_INCLUDEMASK) ) { hres = S_OK; } else { /* * Note: DX3 and DX5 returned E_DEVICENOTREG for * phantom devices. Now we return S_FALSE. Let's * hope nobody gets upset. */ hres = S_FALSE; } } ExitOleProc(); return(hres); } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInput | EnumDevices | * * Enumerates the DirectInput devices that are attached to * or could be attached to the computer. * * For example, an external game port may support a joystick * or a steering wheel, but only one can be plugged in at a * time. will enumerate both * devices. * * @cwrap LPDIRECTINPUT | lpDirectInput * * @parm DWORD | dwDevType | * * Device type filter. If 0, then all device types are * enumerated. Otherwise, it is either a value, * indicating the device class that should be enumerated or a * value, indicating the device type that should be * enumerated. * * @parm LPDIENUMDEVICESCALLBACK | lpCallback | * Points to an application-defined callback function. * For more information, see the description of the * callback function. * * @parm IN LPVOID | pvRef | * Specifies a 32-bit application-defined * value to be passed to the callback function. This value * may be any 32-bit value; it is prototyped as an * for convenience. * * @parm DWORD | fl | * Optional flags which control the enumeration. The * following flags are defined and may be combined. * * : Enumerate only attached devices. * * : Enumerate only devices which * support force feedback. This flag is new for DirectX 5.0. * * : Include alias devices in the * enumeration. If this flag is not specified, then devices * which are aliases of other devices (indicated by the * flag in the field * of the structure) will be excluded from * the enumeration. This flag is new for DirectX 5.0a. * * : Include phantom devices in the * enumeration. If this flag is not specified, then devices * which are phantoms (indicated by the * flag in the field * of the structure) will be excluded from * the enumeration. This flag is new for DirectX 5.0a. * * The default is * : Enumerate all installed devices. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * Note that if the callback stops the enumeration prematurely, * the enumeration is considered to have succeeded. * * = : The *

parameter contains invalid flags, or the callback * procedure returned an invalid status code. * * @cb BOOL CALLBACK | DIEnumDevicesProc | * * An application-defined callback function that receives * DirectInput devices as a result of a call to the * method. * * @parm IN LPDIDEVICEINSTANCE | lpddi | * * Structure that describes the device instance. * * * @parm IN OUT LPVOID | pvRef | * Specifies the application-defined value given in the * function. * * @returns * * Returns to continue the enumeration * or to stop the enumeration. * *//************************************************************************** * * In DEBUG/RDEBUG, if the callback returns a bogus value, raise * a validation exception. * *****************************************************************************/ HRESULT INLINE CDIObj_EnumDevices_IsValidTypeFilter(DWORD dwDevType) { HRESULT hres; /* * First make sure the type mask is okay. */ if( ( GET_DIDEVICE_TYPE( dwDevType ) < DI8DEVCLASS_MAX ) || ( ( GET_DIDEVICE_TYPE( dwDevType ) >= DI8DEVTYPE_MIN ) && ( GET_DIDEVICE_TYPE( dwDevType ) < DI8DEVTYPE_MAX ) ) ) { /* * Now make sure attribute masks are okay. */ if ( dwDevType & DIDEVTYPE_ENUMMASK & ~DIDEVTYPE_ENUMVALID ) { RPF("IDirectInput::EnumDevices: Invalid dwDevType"); hres = E_INVALIDARG; } else { hres = S_OK; } } else { RPF("IDirectInput::EnumDevices: Invalid dwDevType"); hres = E_INVALIDARG; } return(hres); } STDMETHODIMP CDIObj_EnumDevicesW(PV pdiW, DWORD dwDevType, LPDIENUMDEVICESCALLBACKW pec, LPVOID pvRef, DWORD fl) { HRESULT hres; EnterProcR(IDirectInput8::EnumDevices, (_ "pxppx", pdiW, dwDevType, pec, pvRef, fl)); if ( SUCCEEDED(hres = hresPvI(pdiW, ThisInterfaceW)) && SUCCEEDED(hres = hresFullValidPfn(pec, 2)) && SUCCEEDED(hres = CDIObj_EnumDevices_IsValidTypeFilter(dwDevType)) && SUCCEEDED(hres = hresFullValidFl(fl, DIEDFL_VALID, 4)) ) { PDDI this = _thisPvNm(pdiW, diW); if ( SUCCEEDED(hres = hresValidInstanceVer(g_hinst, this->dwVersion)) ) { CDIDEnum *pde; hres = CDIDEnum_New(&this->diW, dwDevType, fl, this->dwVersion, &pde); if ( SUCCEEDED(hres) ) { DIDEVICEINSTANCEW ddiW; ddiW.dwSize = cbX(ddiW); while ( (hres = CDIDEnum_Next(pde, &ddiW)) == S_OK ) { BOOL fRc; /* * WARNING! "goto" here! Make sure that nothing * is held while we call the callback. */ fRc = Callback(pec, &ddiW, pvRef); switch ( fRc ) { case DIENUM_STOP: goto enumdoneok; case DIENUM_CONTINUE: break; default: RPF("%s: Invalid return value from callback", s_szProc); ValidationException(); break; } } AssertF(hres == S_FALSE); enumdoneok:; CDIDEnum_Release(pde); hres = S_OK; } } } ExitOleProcR(); return(hres); } BOOL INTERNAL CDIObj_InternalDeviceEnumProcW(LPDIDEVICEINSTANCEW pddiW, LPDIRECTINPUTDEVICE8W pdid8W, LPVOID pv); STDMETHODIMP CDIObj_InternalEnumDevicesW(PV pdiW, DWORD dwDevType, LPVOID pvRef, DWORD fl) { HRESULT hres; CDIDEnum *pde; PDDI this = _thisPvNm(pdiW, diW); hres = CDIDEnum_New(&this->diW, dwDevType, fl, this->dwVersion, &pde); if ( SUCCEEDED(hres) ) { DIDEVICEINSTANCEW ddiW; LPDIRECTINPUTDEVICE8W pdid8W; ddiW.dwSize = cbX(ddiW); while ( (hres = CDIDEnum_InternalNext(pde, &ddiW, &pdid8W)) == S_OK ) { BOOL fRc; fRc = CDIObj_InternalDeviceEnumProcW(&ddiW, pdid8W, pvRef); switch ( fRc ) { case DIENUM_STOP: goto enumdoneok; case DIENUM_CONTINUE: break; default: ValidationException(); break; } } AssertF(hres == S_FALSE); enumdoneok:; CDIDEnum_Release(pde); hres = S_OK; } return(hres); } /***************************************************************************** * * @doc INTERNAL * * @func BOOL | CDIObj_EnumDevicesCallbackA | * * Wrapper function for * which translates the UNICODE parameters to ANSI. * * @parm IN LPCDIDECICEINSTANCEW | pdiW | * * Same as . * * @parm IN OUT PV | pvRef | * * Pointer to which describes * the original callback. * * @returns * * Returns whatever the original callback returned. * *****************************************************************************/ typedef struct ENUMDEVICESINFO { LPDIENUMDEVICESCALLBACKA pecA; PV pvRef; } ENUMDEVICESINFO, *PENUMDEVICESINFO; BOOL CALLBACK CDIObj_EnumDevicesCallback(LPCDIDEVICEINSTANCEW pdiW, PV pvRef) { PENUMDEVICESINFO pedi = pvRef; BOOL fRc; DIDEVICEINSTANCEA diA; EnterProc(CDIObj_EnumDevicesCallback, (_ "GGxWWp", &pdiW->guidInstance, &pdiW->guidProduct, &pdiW->dwDevType, pdiW->tszProductName, pdiW->tszInstanceName, pvRef)); diA.dwSize = cbX(diA); DeviceInfoWToA(&diA, pdiW); fRc = pedi->pecA(&diA, pedi->pvRef); ExitProcX(fRc); return(fRc); } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputA | EnumDevices | * * ANSI version of . * We wrap the operation. * * @parm IN LPGUID | lpGUIDDeviceType | * Same as . * * @parm LPDIENUMDEVICESCALLBACKA | lpCallbackA | * Same as , except ANSI. * * @parm IN LPVOID | pvRef | * Same as . * * @parm DWORD | fl | * Same as . * *****************************************************************************/ STDMETHODIMP CDIObj_EnumDevicesA(PV pdiA, DWORD dwDevType, LPDIENUMDEVICESCALLBACKA pec, LPVOID pvRef, DWORD fl) { HRESULT hres; EnterProcR(IDirectInput8::EnumDevices, (_ "pxppx", pdiA, dwDevType, pec, pvRef, fl)); /* * EnumDevicesW will validate the rest. */ if ( SUCCEEDED(hres = hresPvI(pdiA, ThisInterfaceA)) && SUCCEEDED(hres = hresFullValidPfn(pec, 1)) ) { ENUMDEVICESINFO edi = { pec, pvRef}; PDDI this = _thisPvNm(pdiA, diA); hres = CDIObj_EnumDevicesW(&this->diW, dwDevType, CDIObj_EnumDevicesCallback, &edi, fl); } ExitOleProcR(); return(hres); } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInput | GetDeviceStatus | * * Determine whether a device is currently attached. * * @cwrap LPDIRECTINPUT | lpDirectInput * * @parm REFGUID | rguid | * * Identifies the instance of the * device whose status is being checked. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The device is attached. * * = : The device is not * attached. * * : DirectInput could not determine * whether the device is attached. * * = : The * device does not exist. * *****************************************************************************/ STDMETHODIMP CDIObj_GetDeviceStatus(PV pdi, REFGUID rguid _THAT) { HRESULT hres; EnterProcR(IDirectInput8::GetDeviceStatus, (_ "pG", pdi, rguid)); if ( SUCCEEDED(hres = hresPvT(pdi)) ) { PDDI this = _thisPv(pdi); PDIDW pdidW; hres = IDirectInput_CreateDevice(&this->diW, rguid, (PV)&pdidW, 0); if ( SUCCEEDED(hres) ) { hres = CDIObj_TestDeviceFlags(pdidW, DIEDFL_ATTACHEDONLY); OLE_Release(pdidW); } } ExitOleProc(); return(hres); } #ifdef XDEBUG CSET_STUBS(GetDeviceStatus, (PV pdi, REFGUID rguid), (pdi, rguid THAT_)) #else #define CDIObj_GetDeviceStatusA CDIObj_GetDeviceStatus #define CDIObj_GetDeviceStatusW CDIObj_GetDeviceStatus #endif #ifdef DO_THE_IMPOSSIBLE /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInput | SetAttachedDevice | * * Informs DirectInput that a new device has been attached * to the system by the user. This is useful when an application * asks the user to attach a currently installed device but does * not want to launch the DirectInput control panel. * * DirectInput needs to be informed that the device has * been attached for internal bookkeeping purposes. * * @cwrap LPDIRECTINPUT | lpDirectInput * * @parm IN LPDIRECTINPUTDEVICE | lpDIDevice | * * Identifies the device which has been attached. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The device is attached. * * @devnote * * This method is not implemented in the current release * of DirectInput. * * This won't work. We need to receive a port, too. * And how can the app create a

in the * first place for a device that does not exist? * I guess I just don't understand. * *****************************************************************************/ STDMETHODIMP CDIObj_SetAttachedDevice(PV pdi, PV pdid _THAT) { HRESULT hres; EnterProcR(IDirectInput8::SetAttachedDevice, (_ "pp", pdi, pdid)); if ( SUCCEEDED(hres = hresPvT(pdi)) ) { PDDI this = _thisPv(pdi); hres = E_NOTIMPL; } ExitOleProc(); return(hres); } #ifdef XDEBUG CSET_STUBS(SetAttachedDevice, (PV pdi, PV pdid), (pdi, pdid THAT_)) #else #define CDIObj_SetAttachedDeviceA CDIObj_SetAttachedDevice #define CDIObj_SetAttachedDeviceW CDIObj_SetAttachedDevice #endif #endif /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInput | RunControlPanel | * * Run the DirectInput control panel so that the user can * install a new input device or modify the setup. * * This function will not run third-party control panels. * * @cwrap LPDIRECTINPUT | lpDirectInput * * @parm IN HWND | hwndOwner | * * Identifies the window handle that will be used as the * parent window for subsequent UI. NULL is a valid parameter, * indicating that there is no parent window. * * @parm DWORD | dwFlags | * * No flags are currently defined. This parameter "must" be * zero. * * @returns * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The device is attached. * * @devnote * * The

is eventually going to allow * to request a modal control panel. * *****************************************************************************/ #pragma BEGIN_CONST_DATA STDMETHODIMP CDIObj_RunControlPanel(PV pdi, HWND hwndOwner, DWORD fl _THAT) { HRESULT hres; EnterProcR(IDirectInput8::RunControlPanel, (_ "pxx", pdi, hwndOwner, fl)); if ( SUCCEEDED(hres = hresPvT(pdi)) && SUCCEEDED(hres = hresFullValidHwnd0(hwndOwner, 1)) && SUCCEEDED(hres = hresFullValidFl(fl, DIRCP_VALID, 2)) ) { PDDI this = _thisPv(pdi); if ( SUCCEEDED(hres = hresValidInstanceVer(g_hinst, this->dwVersion)) ) { /* * We used to run "directx.cpl,@0,3" but directx.cpl is not * redistributable; it comes only with the SDK. So we just * run the system control panel. */ hres = hresRunControlPanel(TEXT("")); } } ExitOleProc(); return(hres); } #ifdef XDEBUG CSET_STUBS(RunControlPanel, (PV pdi, HWND hwndOwner, DWORD fl), (pdi, hwndOwner, fl THAT_)) #else #define CDIObj_RunControlPanelA CDIObj_RunControlPanel #define CDIObj_RunControlPanelW CDIObj_RunControlPanel #endif /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInput | Initialize | * * Initialize a DirectInput object. * * The method automatically * initializes the DirectInput object device after creating it. * Applications normally do not need to call this function. * * @cwrap LPDIRECTINPUT | lpDirectInput * * @parm IN HINSTANCE | hinst | * * Instance handle of the application or DLL that is creating * the DirectInput object. * * See the section titled "Initialization and Versions" * for more information. * * @parm DWORD | dwVersion | * * Version number of the dinput.h header file that was used. * * See the section titled "Initialization and Versions" * for more information. * * @returns * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The device is attached. * * : The application * requires a newer version of DirectInput. * * : The application * was written for an unsupported prerelease version * of DirectInput. * *****************************************************************************/ STDMETHODIMP CDIObj_Initialize(PV pdi, HINSTANCE hinst, DWORD dwVersion _THAT) { HRESULT hres; EnterProcR(IDirectInput8::Initialize, (_ "pxx", pdi, hinst, dwVersion)); if ( SUCCEEDED(hres = hresPvT(pdi)) ) { PDDI this = _thisPv(pdi); if ( SUCCEEDED(hres = hresValidInstanceVer(hinst, dwVersion)) ) { this->dwVersion = dwVersion; } } #ifndef DX_FINAL_RELEASE { #pragma message("BETA EXPIRATION TIME BOMB! Remove for final build!") SYSTEMTIME st; GetSystemTime(&st); if ( st.wYear > DX_EXPIRE_YEAR || ((st.wYear == DX_EXPIRE_YEAR) && (MAKELONG(st.wDay, st.wMonth) > MAKELONG(DX_EXPIRE_DAY, DX_EXPIRE_MONTH))) ) { MessageBox(0, DX_EXPIRE_TEXT, TEXT("Microsoft DirectInput"), MB_OK); } } #endif ExitOleProc(); return(hres); } #ifdef XDEBUG CSET_STUBS(Initialize, (PV pdi, HINSTANCE hinst, DWORD dwVersion), (pdi, hinst, dwVersion THAT_)) #else #define CDIObj_InitializeA CDIObj_Initialize #define CDIObj_InitializeW CDIObj_Initialize #endif /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | CDIObj_FindDeviceInternal | * * The worker function for * which works only for HID devices. * * For more details, see . * * @parm LPCTSTR | ptszName | * * The name of the device relative to the class . * * @parm OUT LPGUID | pguidOut | * * Pointer to a which receives the instance * for the device, if the device is found. * *****************************************************************************/ HRESULT EXTERNAL CDIObj_FindDeviceInternal(LPCTSTR ptszName, LPGUID pguidOut) { HRESULT hres; /* * Look twice. If it's not found the first time, * then refresh the cache and try again in case * it was for a device that was recently added. * (In fact, it will likely be a device that was * recently added, because FindDevice is usually * called in response to a Plug and Play event.) */ hres = hresFindHIDDeviceInterface(ptszName, pguidOut); if ( FAILED(hres) ) { DIHid_BuildHidList(TRUE); hres = hresFindHIDDeviceInterface(ptszName, pguidOut); } return(hres); } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInput2 | FindDevice | * * Obtain the instance for a device given * its class and an opaque name. * * This method can be used by applications which register * for Plug and Play notifications and are notified by * Plug and Play that a new device has been added * to the system. The Plug and Play notification will * be in the form of a class and a device name. * The application can pass the and name to * this method to obtain the instance for * the device, which can then be passed to * or * . * * @cwrap LPDIRECTINPUT2 | lpDirectInput2 * * @parm REFGUID | rguidClass | * * Class identifying the device class * for the device the application wishes to locate. * * The application obtains the class from the * Plug and Play device arrival notification. * * @parm LPCTSTR | ptszName | * * The name of the device relative to the class . * * The application obtains the class name from the * Plug and Play device arrival notification. * * @parm OUT LPGUID | pguidInstance | * * Pointer to a which receives the instance * for the device, if the device is found. * * @returns * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The device was found, and its * instance has been stored in

. * * = The and name do not * correspond to a device that is registered with DirectInput. * For example, they may refer to a storage device rather * than an input device. * *****************************************************************************/ #define cchNameMax MAX_PATH STDMETHODIMP TFORM(CDIObj_FindDevice)(PV pdiT, REFGUID rguid, LPCTSTR ptszName, LPGUID pguidOut) { HRESULT hres; EnterProcR(IDirectInput8::FindDevice, (_ "pGs", pdiT, rguid, ptszName)); if ( SUCCEEDED(hres = TFORM(hresPv)(pdiT)) && SUCCEEDED(hres = hresFullValidGuid(rguid, 1)) && SUCCEEDED(hres = TFORM(hresFullValidReadStr)(ptszName, cchNameMax, 2)) && SUCCEEDED(hres = hresFullValidWritePvCb(pguidOut, cbX(GUID), 3)) ) { if ( IsEqualIID(rguid, &GUID_HIDClass) ) { hres = CDIObj_FindDeviceInternal(ptszName, pguidOut); } else { hres = DIERR_DEVICENOTREG; } } ExitOleProc(); return(hres); } STDMETHODIMP SFORM(CDIObj_FindDevice)(PV pdiS, REFGUID rguid, LPCSSTR psszName, LPGUID pguidOut) { HRESULT hres; TCHAR tsz[cchNameMax]; EnterProcR(IDirectInput8::FindDevice, (_ "pGS", pdiS, rguid, psszName)); /* * TFORM(CDIObj_FindDevice) will validate the rguid and pguidOut. */ if ( SUCCEEDED(hres = SFORM(hresPv)(pdiS)) && SUCCEEDED(hres = SFORM(hresFullValidReadStr)(psszName, cA(tsz), 2)) ) { PDDI this = _thisPvNm(pdiS, SFORM(di)); SToT(tsz, cA(tsz), psszName); hres = TFORM(CDIObj_FindDevice)(&this->TFORM(di), rguid, tsz, pguidOut); } ExitOleProc(); return(hres); } /***************************************************************************** * * @doc INTERNAL * * @mfunc HRESULT | IDirectInput | New | * * Create a new instance of an IDirectInput object. * * @parm IN PUNK | punkOuter | * * Controlling unknown for aggregation. * * @parm IN RIID | riid | * Desired interface to new object. * * @parm OUT PPV | ppvObj | * Output pointer for new object. * * @returns * * Standard OLE . * *****************************************************************************/ STDMETHODIMP CDIObj_New(PUNK punkOuter, RIID riid, PPV ppvObj) { HRESULT hres; EnterProcR(IDirectInput8::CreateInstance, (_ "Gp", riid, ppvObj)); hres = Excl_Init(); if ( SUCCEEDED(hres) ) { /* * Note that we cannot use Common_NewRiid for an object * that aggregates other interfaces! * * The reason is that Common_NewRiid will perform * a QI as part of the initialization, but we cannot handle * the QI until after we've been initialized and are * ready to mess with aggregated goo. */ if ( SUCCEEDED(hres = hresFullValidRiid(riid, 2)) ) { if ( fLimpFF(punkOuter, IsEqualIID(riid, &IID_IUnknown)) ) { hres = Common_New(CDIObj, punkOuter, ppvObj); if ( SUCCEEDED(hres) ) { PDDI this = _thisPv(*ppvObj); this->fCritInited = fInitializeCriticalSection(&this->crst); if ( this->fCritInited ) { /* * Only after the object is ready do we QI for the * requested interface. And the reason is that the * QI might cause us to create an aggregated buddy, * which we can't do until we've been initialized. * * Don't do this extra QI if we are ourselves aggregated, * or we will end up giving the wrong punk to the caller! */ if ( punkOuter == 0 ) { hres = OLE_QueryInterface(this, riid, ppvObj); OLE_Release(this); } if ( FAILED(hres) ) { Invoke_Release(ppvObj); } } else { Common_Unhold(this); *ppvObj = NULL; hres = E_OUTOFMEMORY; } } } else { RPF("CreateDevice: IID must be IID_IUnknown if created for aggregation"); *ppvObj = 0; hres = CLASS_E_NOAGGREGATION; } } } ExitOleProcPpvR(ppvObj); return(hres); } /***************************************************************************** * * @doc INTERNAL * * @func BOOL | CDIObj_IsDeviceUsedByUser | * * To Test whether the device is used by the user as specified by pdm->lpszUserName. * * @parm IN LPDIDEVICEINSTANCEW | pddiW | * * Device Instance. * * @parm IN LPDIMAPPER | pdm | * * pointer to DIMAPPER structure * * @returns * * TRUE: device is used by the user * FALSE: otherwise * *****************************************************************************/ BOOL INTERNAL CDIObj_IsDeviceUsedByUser( LPDIDEVICEINSTANCEW pddiW, LPDIMAPPER pdm ) { HRESULT hres; BOOL fRtn = FALSE; WCHAR wszUserName[UNLEN+1]; hres = CMap_GetDeviceUserName( &pddiW->guidInstance, wszUserName ); if( hres == S_OK ) { DWORD dwLen = 0; dwLen = lstrlenW(pdm->lpszUserName); if(memcmp(pdm->lpszUserName, wszUserName, dwLen*2) == 0) { fRtn = TRUE; } } return(fRtn); } /***************************************************************************** * * @doc INTERNAL * * @func BOOL | CDIObj_IsDeviceAvailable | * * To Test whether the device is still available. * * @parm IN LPDIDEVICEINSTANCEW | pddiW | * * Device Instance. * * @parm IN LPDIMAPPER | pdm | * * pointer to DIMAPPER structure * * @returns * * TRUE: device is available * FALSE: not available * *****************************************************************************/ BOOL INTERNAL CDIObj_IsDeviceAvailable( LPDIDEVICEINSTANCEW pddiW, LPDIMAPPER pdm ) { HRESULT hres; BOOL fAvailable = FALSE; WCHAR wszUserName[UNLEN+1]; hres = CMap_GetDeviceUserName( &pddiW->guidInstance, wszUserName ); if( hres != S_OK ) { fAvailable = TRUE; } return(fAvailable); } /***************************************************************************** * * @doc INTERNAL * * @func BOOL | CDIObj_IsUserConfigured | * * To Test whether the device has been configured by user. * * @parm IN LPDIDEVICEINSTANCEW | pddiW | * * Device Instance. * * @parm IN LPDIMAPPER | pdm | * * pointer to DIMAPPER structure * * @returns * * TRUE: user configured * FALSE: not configured * *****************************************************************************/ BOOL INTERNAL CDIObj_IsUserConfigured( LPDIDEVICEINSTANCEW pddiW, LPDIMAPPER pdm ) { LPDIACTIONFORMATW pdiaf = pdm->pDiActionFormat; BOOL fConfigured = FALSE; DWORD i; for ( i=0; idwNumActions; i++ ) { if ( IsEqualGUID(&pdiaf->rgoAction[i].guidInstance, &pddiW->guidInstance) ) { fConfigured = (pdiaf->rgoAction[i].dwHow & DIAH_USERCONFIG) ? TRUE: FALSE; break; } } return(fConfigured); } /***************************************************************************** * * @doc INTERNAL * * @func int | CDIObj_GetMappedActionNum | * * Get the number of the actions which have been mapped to controls. * * @parm IN LPDIDEVICEINSTANCEW | pddiW | * * Device Instance. * * @parm IN LPDIMAPPER | pdm | * * Pointer to DIMAPPER structure * * @returns * * Number of mapped actions * *****************************************************************************/ int INTERNAL CDIObj_GetMappedActionNum( LPDIDEVICEINSTANCEW pddiW, LPDIMAPPER pdm ) { LPDIACTIONFORMATW pdiaf = pdm->pDiActionFormat; DWORD i; int num = 0; for ( i=0; idwNumActions; i++ ) { if ( IsEqualGUID(&pdiaf->rgoAction[i].guidInstance, &pddiW->guidInstance) ) { if ( pdiaf->rgoAction[i].dwHow & DIAH_MAPMASK ) { num++; } } } return(num); } /***************************************************************************** * * @doc INTERNAL * * @func DWORD | CDIObj_GetMappedPriorities | * * Get the priorities of the mapeed actions. * * @parm IN LPDIDEVICEINSTANCEW | pddiW | * * Device Instance. * * @parm IN LPDIMAPPER | pdm | * * Pointer to DIMAPPER structure * * @returns * * The priorities of mapped actions. Can be DIEDBS_MAPPEDPRI1, * DIEDBS_MAPPEDPRI2, or the OR of both. * *****************************************************************************/ DWORD INTERNAL CDIObj_GetMappedPriorities( LPDIDEVICEINSTANCEW pddiW, LPDIMAPPER pdm ) { LPDIACTIONFORMATW pdiaf = pdm->pDiActionFormat; DWORD i; DWORD pri = 0; for ( i=0; idwNumActions; i++ ) { if( pri == ( DIEDBS_MAPPEDPRI1 | DIEDBS_MAPPEDPRI2 ) ) { break; } if ( IsEqualGUID(&pdiaf->rgoAction[i].guidInstance, &pddiW->guidInstance) && pdiaf->rgoAction[i].dwHow & DIAH_MAPMASK ) { if( DISEM_PRI_GET(pdiaf->rgoAction[i].dwSemantic) == 0 ) { pri |= DIEDBS_MAPPEDPRI1; } else if( DISEM_PRI_GET(pdiaf->rgoAction[i].dwSemantic) == 1 ) { pri |= DIEDBS_MAPPEDPRI2; } } } return(pri); } /***************************************************************************** * * @doc INTERNAL * * @func int | CDIObj_GetDeviceOrder | * * Get the number of the actions which have been mapped to controls. * * @parm IN LPDIDEVICEINSTANCEW | pddiW | * * Device Instance. * * @parm IN LPDIMAPPER | pdm | * * pointer to DIMAPPER structure * * @returns * * The order of the device * * @comm * The order (DWORD) is consisted of three parts: * HIWORD: HIBYTE: the order defined in the genre * LOWBYTE: none, can be used later * LOWORD: HIBYTE - mapped action number * LOWBYTE - Force Feedback (1) or not (0) * *****************************************************************************/ #define GET_MAPPED_ACTION_NUM(x) ((x & 0x0000ff00) >> 8) DWORD INTERNAL CDIObj_GetDeviceOrder( LPDIDEVICEINSTANCEW pddiW, LPDIMAPPER pdm ) { WORD wHighWord, wLowWord; BYTE byDevOrder, byFF, byMappedActions; DWORD dwGenre, dwJoyType; BYTE byOrder; AssertF(pddiW); AssertF(pdm->pDiActionFormat); if ( CDIObj_IsUserConfigured( pddiW, pdm ) ) { byDevOrder = MAX_ORDER; } else { switch( GET_DIDEVICE_TYPE(pddiW->dwDevType) ) { case DI8DEVTYPE_DEVICE: if( !(pdm->dwFlags & DIEDBSFL_NONGAMINGDEVICES) ) { byDevOrder = INVALID_ORDER; } else { byDevOrder = DIDEVTYPE_DEVICE_ORDER; } break; case DI8DEVTYPE_MOUSE: if( pddiW->dwDevType & DIDEVTYPE_HID ) { if( !(pdm->dwFlags & DIEDBSFL_MULTIMICEKEYBOARDS) ) { byDevOrder = INVALID_ORDER; } else { byDevOrder = DIDEVTYPE_HID_MOUSE_ORDER; } } else { byDevOrder = DIDEVTYPE_MOUSE_ORDER; } break; case DI8DEVTYPE_KEYBOARD: if( pddiW->dwDevType & DIDEVTYPE_HID ) { if( !(pdm->dwFlags & DIEDBSFL_MULTIMICEKEYBOARDS) ) { byDevOrder = INVALID_ORDER; } else { byDevOrder = DIDEVTYPE_HID_KEYBOARD_ORDER; } } else { byDevOrder = DIDEVTYPE_KEYBOARD_ORDER; } break; case DI8DEVTYPE_JOYSTICK: case DI8DEVTYPE_GAMEPAD: case DI8DEVTYPE_DRIVING: case DI8DEVTYPE_FLIGHT: case DI8DEVTYPE_1STPERSON: case DI8DEVTYPE_SCREENPOINTER: case DI8DEVTYPE_REMOTE: case DI8DEVTYPE_DEVICECTRL: dwJoyType = GET_DIDEVICE_TYPE(pddiW->dwDevType); dwGenre = DISEM_VIRTUAL_GET(pdm->pDiActionFormat->dwGenre); AssertF(dwGenre <= DISEM_MAX_GENRE); AssertF(dwJoyType < DI8DEVTYPE_MAX); AssertF(dwJoyType != 0); for ( byOrder=DI8DEVTYPE_MIN; byOrderguidFFDriver, &GUID_Null) ? 0 : 1; byMappedActions = (UCHAR) CDIObj_GetMappedActionNum( pddiW, pdm ); wLowWord = MAKEWORD( byFF, byMappedActions ); wHighWord = MAKEWORD( 0, byDevOrder ); return(MAKELONG( wLowWord, wHighWord )); } else { return 0; } } /***************************************************************************** * * CDIObj_DeviceEnumProc * * Device enumeration procedure which is called one for each device. * *****************************************************************************/ BOOL INTERNAL CDIObj_InternalDeviceEnumProcW(LPDIDEVICEINSTANCEW pddiW, LPDIRECTINPUTDEVICE8W pdid8W, LPVOID pv) { LPDIMAPPER pdm = pv; HRESULT hres = S_OK; BOOL fRc = DIENUM_CONTINUE; BOOL fContinue = FALSE; DWORD dwDevOrder; if ( g_nCurDev >= MAX_DEVICENUM ) { fRc = DIENUM_STOP; goto _done; } AssertF(pdid8W); AssertF(pdm->pDiActionFormat); hres = pdid8W->lpVtbl->BuildActionMap(pdid8W, pdm->pDiActionFormat, pdm->lpszUserName, pdm->lpszUserName ? DIDBAM_DEFAULT : DIDBAM_HWDEFAULTS); if ( SUCCEEDED(hres) ) { if ( (pdm->dwFlags & DIEDBSFL_AVAILABLEDEVICES) || (pdm->dwFlags & DIEDBSFL_THISUSER) ) { if( ((pdm->dwFlags & DIEDBSFL_AVAILABLEDEVICES) && CDIObj_IsDeviceAvailable(pddiW,pdm)) || ((pdm->dwFlags & DIEDBSFL_THISUSER) && CDIObj_IsDeviceUsedByUser(pddiW,pdm)) ) { fContinue = TRUE; } } else { fContinue = TRUE; } if( fContinue && ((dwDevOrder = CDIObj_GetDeviceOrder(pddiW, pdm)) != 0) ) { #ifdef DEBUG DWORD dbgRef; #endif #ifdef DEBUG dbgRef = #endif pdid8W->lpVtbl->AddRef(pdid8W); g_DiDevices[g_nCurDev].dwOrder = dwDevOrder; g_DiDevices[g_nCurDev].dwFlags = CDIObj_GetMappedPriorities( pddiW, pdm ); g_DiDevices[g_nCurDev].pdid8W = pdid8W; memcpy( &g_DiDevices[g_nCurDev].ftTimeStamp, &pdm->pDiActionFormat->ftTimeStamp, sizeof(FILETIME) ); memcpy( &g_DiDevices[g_nCurDev].ddiW, pddiW, sizeof(*pddiW) ); g_nCurDev ++; fRc = DIENUM_CONTINUE; } } _done: return(fRc); } /***************************************************************************** * * compare * * Compare func used in shortsort * *****************************************************************************/ int __cdecl compare( const void *arg1, const void *arg2 ) { DWORD dw1 = ((LPDIORDERDEV)arg1)->dwOrder; DWORD dw2 = ((LPDIORDERDEV)arg2)->dwOrder; /* * Compare the device order * If necessary, we can compare seperately: devtype, mapped action number, FF device */ if ( dw1 < dw2 ) { return(1); } else if ( dw1 > dw2 ) { return(-1); } else { return(0); } } void FreeDiActionFormatW(LPDIACTIONFORMATW* lplpDiAfW ) { FreePpv(lplpDiAfW); } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | IsValidMapObjectA | * * Validates a LPDIACTIONFORMATW including strings * * @parm const LPDIACTIONFORMATW | lpDiAfW | * * Original. * * @returns * * Standard OLE . * *****************************************************************************/ HRESULT IsValidMapObjectA ( LPDIACTIONFORMATA paf #ifdef XDEBUG comma LPCSTR pszProc comma UINT argnum #endif ) { HRESULT hres; hres = CDIDev_ActionMap_IsValidMapObject ( (LPDIACTIONFORMATW)paf #ifdef XDEBUG comma pszProc comma argnum #endif ); if( SUCCEEDED( hres ) ) { if( paf->dwSize != cbX(DIACTIONFORMATA) ) { D( RPF("IDirectInputDevice::%s: Invalid DIACTIONFORMATA.dwSize 0x%08x", pszProc, paf->dwSize ); ) hres = E_INVALIDARG; } } if(SUCCEEDED(hres)) { DWORD i; LPDIACTIONA lpDiAA; // Compute the size for each of the text strings in array of DIACTIONs for ( i = 0x0, lpDiAA = paf->rgoAction; i < paf->dwNumActions && SUCCEEDED(hres) ; i++, lpDiAA++ ) { // Handle the NULL ptr case if ( NULL != lpDiAA->lptszActionName ) { hres = hresFullValidReadStrA_(lpDiAA->lptszActionName, MAX_JOYSTRING, pszProc, argnum); } } } return hres; } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | IsValidMapObjectW | * * Validates a LPDIACTIONFORMATW including strings * * @parm const LPDIACTIONFORMATW | lpDiAfW | * * Original. * * @returns * * Standard OLE . * *****************************************************************************/ HRESULT IsValidMapObjectW ( LPDIACTIONFORMATW paf #ifdef XDEBUG comma LPCSTR pszProc comma UINT argnum #endif ) { HRESULT hres; hres = CDIDev_ActionMap_IsValidMapObject ( paf #ifdef XDEBUG comma pszProc comma argnum #endif ); if( SUCCEEDED( hres ) ) { if( paf->dwSize != cbX(DIACTIONFORMATW) ) { D( RPF("IDirectInputDevice::%s: Invalid DIACTIONFORMATW.dwSize 0x%08x", pszProc, paf->dwSize ); ) hres = E_INVALIDARG; } } if(SUCCEEDED(hres)) { DWORD i; LPDIACTIONW lpDiAW; // Compute the size for each of the text strings in array of DIACTIONs for ( i = 0x0, lpDiAW = paf->rgoAction; i < paf->dwNumActions && SUCCEEDED(hres) ; i++, lpDiAW++ ) { // Handle the NULL ptr case if ( NULL != lpDiAW->lptszActionName ) { hres = hresFullValidReadStrW_(lpDiAW->lptszActionName, MAX_JOYSTRING, pszProc, argnum); } } } return hres; } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | DiActionFormatWtoW | * * Copies LPDIACTIONFORMATW to LPDIACTIONFORMATW * * @parm const LPDIACTIONFORMATW | lpDiAfW | * * Original. * * @parm LPDIACTIONFORMATW* | lplpDiAfW | * * Address of a pointer to a that receives the converted * ACTIONFORMAT. * @returns * * Standard OLE . * *****************************************************************************/ HRESULT EXTERNAL DiActionFormatWtoW ( const LPDIACTIONFORMATW lpDiAfW0, LPDIACTIONFORMATW* lplpDiAfW ) { DWORD cbAlloc; PDWORD pdwStrLen0, pdwStrLen; LPDIACTIONFORMATW lpDiAfW; LPDIACTIONW lpDiAW0; LPDIACTIONW lpDiAW; DWORD i; HRESULT hres; EnterProcI(DiActionFormatWtoW, (_ "xx", lpDiAfW0, lplpDiAfW)); // Internal function, no validation *lplpDiAfW = NULL; /* * PREFIX complains (mb:37926 - items 3 & 4) that we could be requesting * a zero byte allocation which would not allocate anything. This is * never the case because CDIDev_ActionMap_IsValidMapObject tests that * dwNumActions is less than 2^24. Assert in debug for extra safety. */ AssertF( (lpDiAfW0->dwNumActions +1) * cbX(*pdwStrLen0) ); hres = AllocCbPpv( (lpDiAfW0->dwNumActions +1) * cbX(*pdwStrLen0) , &pdwStrLen0); if ( SUCCEEDED(hres) ) { pdwStrLen = pdwStrLen0; // Compute the amount of memory required to clone the DIACTIONFORMATA cbAlloc = /* 1: The Action Format array */ lpDiAfW0->dwSize /* 2: Each of the DIACTION arrays */ + lpDiAfW0->dwActionSize * lpDiAfW0->dwNumActions; // Compute the size for each of the text strings in array of DIACTIONs for ( i = 0x0, lpDiAW0 = lpDiAfW0->rgoAction; i < lpDiAfW0->dwNumActions ; i++, lpDiAW0++ ) { // Handle the NULL ptr case if ( !lpDiAW0->lptszActionName ) { *pdwStrLen++ = 0; } else { if ( (UINT_PTR)lpDiAW0->lptszActionName > (UINT_PTR)0xFFFF ) { /* 3: Text string in each DIACTION array*/ // Conversion from A to U, need multiplier *pdwStrLen = cbX(lpDiAW0->lptszActionName[0]) * ( lstrlenW(lpDiAW0->lptszActionName) + 1 ); cbAlloc += *pdwStrLen++; } else { // Use resource strings WCHAR wsz[MAX_PATH]; if (lpDiAfW0->hInstString > 0) { //find out the length of the string *pdwStrLen = LoadStringW(lpDiAfW0->hInstString, lpDiAW0->uResIdString, (LPWSTR) &wsz, MAX_PATH); } else { *pdwStrLen = 0; } cbAlloc += *pdwStrLen++; } } } if ( SUCCEEDED( hres = AllocCbPpv(cbAlloc, &lpDiAfW) ) ) { DWORD dwLen; DWORD cb; pdwStrLen = pdwStrLen0; // 1: Copy the DIACTIONFORMAT *lpDiAfW = *lpDiAfW0; cb = lpDiAfW0->dwSize; // 2: Block copy the DIACTION array lpDiAfW->rgoAction = (LPDIACTIONW)( (char*)lpDiAfW + cb); dwLen = lpDiAfW0->dwActionSize * lpDiAfW0->dwNumActions; memcpy(lpDiAfW->rgoAction, lpDiAfW0->rgoAction, dwLen); cb += dwLen; // 3: ActionName for ( i = 0x0, lpDiAW0=lpDiAfW0->rgoAction, lpDiAW=lpDiAfW->rgoAction; i < lpDiAfW0->dwNumActions ; i++, lpDiAW0++, lpDiAW++ ) { if ( (UINT_PTR)lpDiAW0->lptszActionName > (UINT_PTR)0xFFFF ) { WCHAR* wsz = (WCHAR*) ((char*)lpDiAfW+cb); lpDiAW->lptszActionName = wsz; dwLen = *pdwStrLen++; memcpy(wsz, lpDiAW0->lptszActionName, dwLen); cb += dwLen ; } else { // Handle resource strings // OK for now, as long as UI always uses CloneDiActionFormatW WCHAR* wsz = (WCHAR*) ((char*)lpDiAfW+cb); dwLen = *pdwStrLen++; if ((dwLen != 0) && (LoadStringW(lpDiAfW0->hInstString, lpDiAW0->uResIdString, wsz, dwLen))) { //If we found a length last time there must be a resource module AssertF( lpDiAfW0->hInstString > 0 ); //Found and loaded the string lpDiAW->lptszActionName = wsz; } else { //No hinstance or length 0 or didn't load the string lpDiAW->lptszActionName = NULL; } cb += dwLen; } } // If we have not done something goofy, the memory allocates should match // the memory we used AssertF(cbAlloc == cb ); *lplpDiAfW = lpDiAfW; } FreePpv(&pdwStrLen0); } ExitOleProc(); return(hres); } /***************************************************************************** * * CDIMap_EnumDevicesBySemantics * * Enum Suitable Devices. * *****************************************************************************/ STDMETHODIMP CDIObj_EnumDevicesBySemanticsW( PV pDiW, LPCWSTR lpszUserName, LPDIACTIONFORMATW pDiActionFormat, LPDIENUMDEVICESBYSEMANTICSCBW pecW, LPVOID pvRef, DWORD dwFlags ) { HRESULT hres; EnterProcR(IDirectInput8::EnumDevicesBySemantics, (_ "pppppx", pDiW, lpszUserName, pDiActionFormat, pecW, pvRef, dwFlags)); if ( SUCCEEDED(hres = hresPvI(pDiW, ThisInterfaceW)) && SUCCEEDED(hres = IsValidMapObjectW(pDiActionFormat D(comma s_szProc comma 2))) && (lpszUserName == NULL || SUCCEEDED(hres = hresFullValidReadStrW(lpszUserName, UNLEN+1, 1))) && SUCCEEDED(hres = hresFullValidPfn(pecW, 3)) && SUCCEEDED(hres = hresFullValidFl(dwFlags, DIEDBSFL_VALID, 5)) && SUCCEEDED(hres = CMap_ValidateActionMapSemantics(pDiActionFormat, DIDBAM_PRESERVE)) ) { PDDI this = _thisPvNm(pDiW, diW); if ( SUCCEEDED(hres = hresValidInstanceVer(g_hinst, this->dwVersion)) ) { DIMAPPER dm; LPDIACTIONFORMATW lpDiAfW; if ( SUCCEEDED(hres= DiActionFormatWtoW(pDiActionFormat, &lpDiAfW)) ) { dm.lpszUserName = lpszUserName; dm.pDiActionFormat = lpDiAfW; dm.pecW = pecW; dm.pvRef = pvRef; dm.dwFlags = dwFlags; if( dwFlags == 0 ) { dwFlags |= DIEDFL_ATTACHEDONLY; } dwFlags &= ~DIEDBSFL_AVAILABLEDEVICES; dwFlags &= ~DIEDBSFL_THISUSER; dwFlags &= ~DIEDBSFL_MULTIMICEKEYBOARDS; dwFlags &= ~DIEDBSFL_NONGAMINGDEVICES; ZeroX(g_DiDevices); g_nCurDev = 0; hres = CDIObj_InternalEnumDevicesW( pDiW, 0, //enum all tyeps of devices (LPVOID)&dm, dwFlags //only enum attached devices ); /* * For short array sorting (size <= 8), shortsort is better than qsort. */ if ( SUCCEEDED(hres) && g_nCurDev ) { int ndev; int nNewDev = -1; FILETIME ft = { 0, 0 }; FILETIME ftMostRecent; shortsort( (char *)&g_DiDevices[0], (char *)&g_DiDevices[g_nCurDev-1], sizeof(DIORDERDEV), compare ); SquirtSqflPtszV(sqflDi | sqflVerbose, TEXT("EnumDevicesBySemantics: %d devices enumed"), g_nCurDev ); for ( ndev=0; ndevtszProductName, GET_MAPPED_ACTION_NUM(g_DiDevices[ndev].dwOrder) ); if ( pddiW ) { #ifdef DEBUG DWORD dbgRef; #endif DWORD dwDeviceRemaining = g_nCurDev-ndev-1; AssertF(g_DiDevices[ndev].pdid8W); AssertF(g_DiDevices[ndev].pdid8W->lpVtbl); if( g_DiDevices[ndev].pdid8W && g_DiDevices[ndev].pdid8W->lpVtbl ) { BOOL fRc; fRc = pecW(pddiW, g_DiDevices[ndev].pdid8W, g_DiDevices[ndev].dwFlags, dwDeviceRemaining, pvRef); if( fRc == DIENUM_STOP ) { for ( ; ndevlpVtbl->Release(g_DiDevices[ndev].pdid8W); } break; } #ifdef DEBUG dbgRef = #endif g_DiDevices[ndev].pdid8W->lpVtbl->Release(g_DiDevices[ndev].pdid8W); } } } } } FreeDiActionFormatW(&lpDiAfW); } } } ExitOleProcR(); return(hres); } /***************************************************************************** * * @doc INTERNAL * * @func BOOL | CDIObj_EnumDevicesBySemanticsCallbackA | * * Wrapper function for * which translates the UNICODE parameters to ANSI. * * @parm IN LPCDIDECICEINSTANCEW | pdiW | * * Same as . * * @parm IN OUT PV | pvRef | * * Pointer to which describes * the original callback. * * @returns * * Returns whatever the original callback returned. * *****************************************************************************/ typedef struct ENUMDEVICESBYSEMANTICSINFO { LPDIENUMDEVICESBYSEMANTICSCBA pecA; PV pvRef; } ENUMDEVICESBYSEMANTICSINFO, *PENUMDEVICESBYSEMANTICSINFO; BOOL CALLBACK CDIObj_EnumDevicesBySemanticsCallback(LPCDIDEVICEINSTANCEW pdiW, LPDIRECTINPUTDEVICE8W pdid8W, DWORD dwFlags, DWORD dwDeviceRemaining, PV pvRef) { PENUMDEVICESBYSEMANTICSINFO pesdi = pvRef; BOOL fRc; DIDEVICEINSTANCEA diA; LPDIRECTINPUTDEVICE8A pdid8A = NULL; EnterProc(CDIObj_EnumDevicesBySemanticsCallback, (_ "GGxWWp", &pdiW->guidInstance, &pdiW->guidProduct, &pdiW->dwDevType, pdiW->tszProductName, pdiW->tszInstanceName, pvRef)); diA.dwSize = cbX(diA); DeviceInfoWToA(&diA, pdiW); Device8WTo8A(&pdid8A, pdid8W); fRc = pesdi->pecA(&diA, pdid8A, dwFlags, dwDeviceRemaining, pesdi->pvRef); ExitProcX(fRc); return(fRc); } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInput | EnumDevicesBySemantics | * * Enumerates devices suitable for the application specified * . * * @cwrap LPDIRECTINPUT | lpDirectInput * * ISSUE-2001/03/29-timgill Need to fix auto docs * @parm LPTSTR | lptszActionMap | * * Friendly name for the application. * * @parm REFGUID | rguid | * * Unique GUID to identify the application. * * @returns * * Standard OLE . * *****************************************************************************/ STDMETHODIMP CDIObj_EnumDevicesBySemanticsA ( PV pDiA, LPCSTR lpszUserName, LPDIACTIONFORMATA pDiActionFormat, LPDIENUMDEVICESBYSEMANTICSCBA pecA, LPVOID pvRef, DWORD dwFlags ) { HRESULT hres; EnterProcR(IDirectInput8::EnumDevicesBySemantics, (_ "pppppx", pDiA, lpszUserName, pDiActionFormat, pecA, pvRef, dwFlags)); /* * EnumDevicesBySemanticsW will validate the rest. */ if ( SUCCEEDED(hres = hresPvI(pDiA, ThisInterfaceA)) && SUCCEEDED(hres = IsValidMapObjectA(pDiActionFormat D(comma s_szProc comma 2))) && (lpszUserName == NULL || SUCCEEDED(hres = hresFullValidReadStrA(lpszUserName, UNLEN+1, 1))) && SUCCEEDED(hres = hresFullValidPfn(pecA, 3)) && SUCCEEDED(hres = hresFullValidFl(dwFlags, DIEDBSFL_VALID, 5)) ) { PDDI this = _thisPvNm(pDiA, diA); ENUMDEVICESBYSEMANTICSINFO esdi = { pecA, pvRef}; WCHAR wszUserName[MAX_PATH]; DIACTIONFORMATW diafW; LPDIACTIONW rgoActionW; wszUserName[0] = L'\0'; if( lpszUserName ) { AToU(wszUserName, MAX_PATH, lpszUserName); } /* * Assert that the structure can be copied as: * a) dwSize * b) everything else * c) the app name */ CAssertF( FIELD_OFFSET( DIACTIONFORMATW, dwSize ) == 0 ); CAssertF( FIELD_OFFSET( DIACTIONFORMATA, dwSize ) == 0 ); CAssertF( FIELD_OFFSET( DIACTIONFORMATW, dwSize ) + cbX( ((LPDIACTIONFORMATW)0)->dwSize ) == FIELD_OFFSET( DIACTIONFORMATW, dwActionSize ) ); #if defined(_WIN64) CAssertF( ( ( cbX( DIACTIONFORMATW ) - cbX( ((LPDIACTIONFORMATW)0)->tszActionMap ) ) - ( cbX( DIACTIONFORMATA ) - cbX( ((LPDIACTIONFORMATA)0)->tszActionMap ) ) < MAX_NATURAL_ALIGNMENT ) ); #else CAssertF( ( cbX( DIACTIONFORMATW ) - cbX( ((LPDIACTIONFORMATW)0)->tszActionMap ) ) == ( cbX( DIACTIONFORMATA ) - cbX( ((LPDIACTIONFORMATA)0)->tszActionMap ) ) ); #endif CAssertF( FIELD_OFFSET( DIACTIONFORMATW, tszActionMap ) == FIELD_OFFSET( DIACTIONFORMATA, tszActionMap ) ); CAssertF( cA( ((LPDIACTIONFORMATW)0)->tszActionMap ) == cA( ((LPDIACTIONFORMATA)0)->tszActionMap ) ); //Init diafW fields diafW.dwSize = cbX(DIACTIONFORMATW); memcpy( &diafW.dwActionSize, &pDiActionFormat->dwActionSize, FIELD_OFFSET( DIACTIONFORMATW, tszActionMap ) - FIELD_OFFSET( DIACTIONFORMATW, dwActionSize ) ); AToU(diafW.tszActionMap, cbX(pDiActionFormat->tszActionMap), pDiActionFormat->tszActionMap); if ( SUCCEEDED( hres = AllocCbPpv( cbCxX(pDiActionFormat->dwNumActions, DIACTIONW), &rgoActionW) ) ) { memcpy( rgoActionW, pDiActionFormat->rgoAction, sizeof(DIACTIONA) * pDiActionFormat->dwNumActions ); diafW.rgoAction = rgoActionW; hres = CDIObj_EnumDevicesBySemanticsW(&this->diW, wszUserName, &diafW, CDIObj_EnumDevicesBySemanticsCallback, &esdi, dwFlags); FreePpv(&rgoActionW); } } ExitOleProcR(); return(hres); } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | hresValidSurface | * * Tests the interface pointer for a valid surface of sufficient * dimensions to display the UI and with a supported pixel format. * * @parm IUnknown* | lpUnkDDSTarget | * * Pointer to an interface which must be validated as a COM object * before calling this function. * * @returns * * = : The operation completed successfully. * = : The *

parameter is not valid. * A DirectDraw or D3D error if one was returned. * or a Standard OLE . * *****************************************************************************/ HRESULT INLINE hresValidSurface ( IUnknown* lpUnkDDSTarget ) { HRESULT hres; IUnknown* lpSurface = NULL; /* * Short on time, so take short-cuts in validation debug error messages */ #ifdef XDEBUG CHAR s_szProc[] = "IDirectInput8::ConfigureDevices"; #endif #define ArgIS 2 if( SUCCEEDED( hres = lpUnkDDSTarget->lpVtbl->QueryInterface( lpUnkDDSTarget, &IID_IDirect3DSurface8, (LPVOID*)&lpSurface ) ) ) { D3DSURFACE_DESC SurfDesc; hres = ((IDirect3DSurface8*)lpSurface)->lpVtbl->GetDesc( ((IDirect3DSurface8*)lpSurface), &SurfDesc ); if( FAILED( hres ) ) { RPF( "%s: Arg %d: Unable to GetDesc on surface, error 0x%08x", s_szProc, ArgIS, hres ); /* * D3D returns real HRESULTs which can be returned to the caller */ } else { if( ( SurfDesc.Width < 640 ) || ( SurfDesc.Height < 480 ) ) { RPF( "%s: Arg %d: cannot use %d by %d surface", s_szProc, ArgIS, SurfDesc.Width, SurfDesc.Height ); hres = E_INVALIDARG; } else { switch( SurfDesc.Format ) { case D3DFMT_R8G8B8: case D3DFMT_A8R8G8B8: case D3DFMT_X8R8G8B8: case D3DFMT_R5G6B5: case D3DFMT_X1R5G5B5: case D3DFMT_A1R5G5B5: SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("ConfigureDevices: validated %d by %d format %d surface"), SurfDesc.Width, SurfDesc.Height, SurfDesc.Format ); break; default: RPF( "%s: Arg %d: cannot use surface format %d ", s_szProc, ArgIS, SurfDesc.Format ); hres = E_INVALIDARG; break; } } } } else { DDSURFACEDESC2 SurfDesc; SurfDesc.dwSize = cbX( SurfDesc ); if( SUCCEEDED(hres = lpUnkDDSTarget->lpVtbl->QueryInterface( lpUnkDDSTarget, &IID_IDirectDrawSurface7, (LPVOID*) &lpSurface)) ) { hres = ((IDirectDrawSurface7*)lpSurface)->lpVtbl->GetSurfaceDesc( ((IDirectDrawSurface7*)lpSurface), &SurfDesc ); } else if( SUCCEEDED(hres = lpUnkDDSTarget->lpVtbl->QueryInterface( lpUnkDDSTarget, &IID_IDirectDrawSurface4, (LPVOID*) &lpSurface )) ) { hres = ((IDirectDrawSurface4*)lpSurface)->lpVtbl->GetSurfaceDesc( ((IDirectDrawSurface4*)lpSurface), &SurfDesc ); } if( FAILED( hres ) ) { if( lpSurface ) { RPF( "%s: Arg %d: failed GetSurfaceDesc, error 0x%08x", s_szProc, ArgIS, hres ); } else { RPF( "%s: Arg %d: failed QI for supported surface interfaces from %p, error 0x%08x", s_szProc, ArgIS, lpSurface, hres ); } /* * DDraw returns real HRESULTs which can be returned to the caller */ } else if( ( SurfDesc.dwWidth < 640 ) || ( SurfDesc.dwHeight < 480 ) ) { RPF( "%s: Arg %d: cannot use %d by %d surface", s_szProc, ArgIS, SurfDesc.dwWidth, SurfDesc.dwHeight ); hres = E_INVALIDARG; } else { /* * Check for the eqivalent of the DX8 surfaces: * A8R8G8B8, X8R8G8B8, R8G8B8, A1R5G5B5, X1R5G5B5, R5G6B5 */ if( SurfDesc.ddpfPixelFormat.dwFlags & DDPF_RGB ) { if( SurfDesc.ddpfPixelFormat.dwRGBBitCount > 16 ) { AssertF( ( SurfDesc.ddpfPixelFormat.dwRGBBitCount == 32 ) ||( SurfDesc.ddpfPixelFormat.dwRGBBitCount == 24 ) ); /* * All of these must be R8 G8 B8 */ if( ( SurfDesc.ddpfPixelFormat.dwRBitMask == 0x00FF0000 ) && ( SurfDesc.ddpfPixelFormat.dwGBitMask == 0x0000FF00 ) && ( SurfDesc.ddpfPixelFormat.dwBBitMask == 0x000000FF ) ) { SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("ConfigureDevices: validated %d by %d format R8G8B8 %d bit surface"), SurfDesc.dwWidth, SurfDesc.dwHeight, SurfDesc.ddpfPixelFormat.dwRGBBitCount ); } else { RPF( "%s: Arg %d: cannot use surface pixel format", s_szProc, ArgIS ); hres = E_INVALIDARG; } } else { if( SurfDesc.ddpfPixelFormat.dwRGBBitCount == 16 ) { /* * Allow R5 G5 B5 and R5 G6 B5 */ if( ( SurfDesc.ddpfPixelFormat.dwBBitMask == 0x0000001F ) && ( ( SurfDesc.ddpfPixelFormat.dwGBitMask == 0x000003E0 ) && ( SurfDesc.ddpfPixelFormat.dwRBitMask == 0x00007C00 ) ) || ( ( SurfDesc.ddpfPixelFormat.dwGBitMask == 0x000007E0 ) && ( SurfDesc.ddpfPixelFormat.dwRBitMask == 0x0000F800 ) ) ) { SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("ConfigureDevices: validated %d by %d format 16 bit surface"), SurfDesc.dwWidth, SurfDesc.dwHeight ); } else { RPF( "%s: Arg %d: cannot use 16 bit surface pixel format", s_szProc, ArgIS ); hres = E_INVALIDARG; } } else { RPF( "%s: Arg %d: cannot use %d bit surface pixel format", s_szProc, ArgIS, SurfDesc.ddpfPixelFormat.dwRGBBitCount ); hres = E_INVALIDARG; } } } else { RPF( "%s: Arg %d: cannot use non RGB surface, Surface.dwFlags = 0x%08x", s_szProc, ArgIS, SurfDesc.ddpfPixelFormat.dwFlags ); hres = E_INVALIDARG; } } } if( lpSurface != NULL ) { lpSurface->lpVtbl->Release( lpSurface ); } return hres; #undef ArgIS } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInput | ConfigureDevices | * * Configures the devices by attaching mappings provided in * to appropriate device controls. * * @cwrap LPDIRECTINPUT | lpDirectInput * * @parm LPCTSTR | lpctszUserName | * * User Name. * * @parm LPDIACTIONFORMAT | lpDiActionFormat | * * Pointer to the structure containing the action map. * * * @returns * * Standard OLE . * *****************************************************************************/ STDMETHODIMP CDIObj_ConfigureDevicesCore ( PV pDiW, LPDICONFIGUREDEVICESCALLBACK lpdiCallback, LPDICONFIGUREDEVICESPARAMSW lpdiCDParams, DWORD dwFlags, LPVOID pvRefData ) { //the real ConfigureDevices() //some stuff should have been validated already HRESULT hres = S_OK; EnterProcI(IDirectInput8::ConfigureDevicesCore, (_ "pppup", pDiW, lpdiCallback,lpdiCDParams, dwFlags, pvRefData)); if( SUCCEEDED( hres = hresFullValidFl( dwFlags, DICD_VALID, 3 ) ) && SUCCEEDED( hres = hresFullValidReadPvCb( &(lpdiCDParams->dics), sizeof(DICOLORSET), 2 ) ) ) { /* * lpUnkDDSTarget and lpdiCallback are "coupled", because the only * function of the callback is to display the updates to the surface * either they are both NULL, or they are both non-NULL and valid. * otherwise, it is an error. */ if( lpdiCallback == NULL ) { if( lpdiCDParams->lpUnkDDSTarget == NULL ) { hres = S_OK; } else { RPF( "%s: Arg %d or %d: neither or both of callback and surface must be NULL", s_szProc, 1, 2 ); hres = E_INVALIDARG; } } else if( lpdiCDParams->lpUnkDDSTarget == NULL ) { RPF( "%s: Arg %d or %d: neither or both of surface and callback must be NULL", s_szProc, 2, 1 ); hres = E_INVALIDARG; } else if( SUCCEEDED( hres = hresFullValidPfn( lpdiCallback, 1 ) ) && SUCCEEDED( hres = hresFullValidPitf( lpdiCDParams->lpUnkDDSTarget, 2 ) ) ) { hres = hresValidSurface( lpdiCDParams->lpUnkDDSTarget ); } if( SUCCEEDED( hres ) ) { //load the framework HINSTANCE hinst; IDirectInputActionFramework* pDIAcFrame = NULL; TCHAR tszName[ctchNameGuid]; TCHAR tszClsid[ctchGuid]; NameFromGUID(tszName, &CLSID_CDirectInputActionFramework); memcpy(tszClsid, &tszName[ctchNamePrefix], cbX(tszClsid) ); hres = DICoCreateInstance(tszClsid, NULL, &IID_IDIActionFramework, (LPVOID*) & pDIAcFrame, &hinst); if( SUCCEEDED(hres) ) { //for getting default user name, if needed LPWSTR pwszUserName = NULL; //can't pass NULL user name down to the framework -- need to get the default user name if( lpdiCDParams->lptszUserNames == NULL ) { hres = GetWideUserName(NULL, NULL, &pwszUserName); lpdiCDParams->lptszUserNames = pwszUserName; lpdiCDParams->dwcUsers = 1; } if( SUCCEEDED(hres) ) { //call the framework hres = pDIAcFrame->lpVtbl->ConfigureDevices ( pDIAcFrame, lpdiCallback, lpdiCDParams, dwFlags, pvRefData); if( SUCCEEDED(hres) ) { SquirtSqflPtszV( sqfl | sqflVerbose, TEXT("Default Remapping UI returned 0x%08x"), hres ); } else { SquirtSqflPtszV( sqfl | sqflError, TEXT("Default Remapping UI returned error 0x%08x"), hres ); } //release pwsUserName, if we have used it FreePpv(&pwszUserName); } FreeLibrary( hinst ); } } } ExitOleProc(); return hres; } STDMETHODIMP CDIObj_ConfigureDevicesW ( PV pDiW, LPDICONFIGUREDEVICESCALLBACK lpdiCallback, LPDICONFIGUREDEVICESPARAMSW lpdiCDParams, DWORD dwFlags, LPVOID pvRefData ) { HRESULT hres = S_OK; EnterProcR(IDirectInput8::ConfigureDevices, (_ "pppxp", pDiW, lpdiCallback,lpdiCDParams, dwFlags, pvRefData)); //validate all the ptrs if ( (SUCCEEDED(hres = hresPvI(pDiW, ThisInterfaceW)) && (SUCCEEDED(hres = hresFullValidReadPvCb(lpdiCDParams, sizeof(DICONFIGUREDEVICESPARAMSW), 2))) && ((lpdiCDParams->lptszUserNames == NULL) || (SUCCEEDED(hres = hresFullValidReadStrW((LPWSTR)(lpdiCDParams->lptszUserNames), MAX_JOYSTRING * (lpdiCDParams->dwcUsers), 2)))) && (SUCCEEDED(hres = hresFullValidReadPvCb(lpdiCDParams->lprgFormats, lpdiCDParams->dwcFormats*sizeof(DIACTIONFORMATW), 2))))) { if( lpdiCDParams->dwSize != cbX(DICONFIGUREDEVICESPARAMSW) ) { RPF("IDirectInput::%s: Invalid DICONFIGUREDEVICESPARAMSW.dwSize 0x%08x", lpdiCDParams->dwSize ); hres = E_INVALIDARG; } if (SUCCEEDED(hres)) { PDDI this = _thisPvNm(pDiW, diW); //params structure DICONFIGUREDEVICESPARAMSW diconfparamsW; //to translate each DIACTIONFORMAT LPDIACTIONFORMATW* lpDiAfW = NULL; //the cloning array of DIACTIONFORMATs LPDIACTIONFORMATW* lpDAFW = NULL; //to traverse the old array LPDIACTIONFORMATW lpDIFormat; //to traverse the new array LPDIACTIONFORMATW lpDIF; //user names LPWSTR lpUserNames = NULL; //DIACTIONFORMATs cloned DWORD clonedF = 0; //length of user names DWORD strLen = 0; //zero out ZeroMemory(&diconfparamsW, sizeof(DICONFIGUREDEVICESPARAMSW)); //set the size diconfparamsW.dwSize = sizeof(DICONFIGUREDEVICESPARAMSW); //1. Validate and translate each LPDIACTIONFORMAT in the array lpDIFormat = (lpdiCDParams->lprgFormats); //allocate the new array hres = AllocCbPpv(lpdiCDParams->dwcFormats * sizeof(DIACTIONFORMATW), &diconfparamsW.lprgFormats); if (FAILED(hres)) { goto cleanup; } lpDIF = diconfparamsW.lprgFormats; //allocate the cloning array hres = AllocCbPpv(lpdiCDParams->dwcFormats * sizeof(DIACTIONFORMATW), &lpDAFW); if (FAILED(hres)) { goto cleanup; } lpDiAfW = lpDAFW; //clone for (clonedF = 0; clonedF < lpdiCDParams->dwcFormats; clonedF ++) { //validate hres = IsValidMapObjectW(lpDIFormat D(comma s_szProc comma 2)); if (FAILED(hres)) { goto cleanup; } //translate hres = DiActionFormatWtoW(lpDIFormat, lpDiAfW); if (FAILED(hres)) { goto cleanup; } //save *lpDIF = *(*lpDiAfW); //move on lpDIFormat++; lpDiAfW++; lpDIF++; } //if everything went fine, should have cloned all AssertF(clonedF == lpdiCDParams->dwcFormats); //2. Copy the user names if (lpdiCDParams->lptszUserNames != NULL) { DWORD countN; WCHAR* lpName = lpdiCDParams->lptszUserNames; for (countN = 0; countN < lpdiCDParams->dwcUsers; countN ++) { DWORD Len; hres = hresFullValidReadStrW(lpName, MAX_JOYSTRING, 2); if (FAILED(hres)) { goto cleanup; } Len = lstrlenW(lpName); //if length is 0 -- and we haven't reached the correct user count yet - //then it is an error if (Len == 0) { hres = DIERR_INVALIDPARAM; goto cleanup; } //move on to the next user name strLen += Len + 1; lpName += Len + 1; } //if everything went fine, should have traversed all the user names AssertF(countN == lpdiCDParams->dwcUsers); //allocate hres = AllocCbPpv( (strLen + 1) * 2, &lpUserNames ); if (FAILED(hres)) { goto cleanup; } //copy memcpy(lpUserNames, lpdiCDParams->lptszUserNames, strLen*2); diconfparamsW.lptszUserNames = lpUserNames; } //3. Populate the rest of the structure diconfparamsW.dwcUsers = lpdiCDParams->dwcUsers; diconfparamsW.dwcFormats = clonedF; diconfparamsW.hwnd = lpdiCDParams->hwnd; diconfparamsW.dics = lpdiCDParams->dics; diconfparamsW.lpUnkDDSTarget = lpdiCDParams->lpUnkDDSTarget; //4. Call the framework hres = CDIObj_ConfigureDevicesCore ( &this->diW, lpdiCallback, &diconfparamsW, dwFlags, pvRefData); cleanup:; //free the space for the new array FreePpv(&diconfparamsW.lprgFormats); //free as many DIACTIONFORMATs as were created if (lpDAFW) { lpDiAfW = lpDAFW; for (clonedF; clonedF > 0; clonedF--) { FreeDiActionFormatW(lpDiAfW); lpDiAfW++; } //delete the entire block FreePpv(&lpDAFW); } //delete the user names FreePpv(&lpUserNames); } } ExitOleProc(); return(hres); } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | DiActionFormatAtoW | * * Copies LPDIACTIONFORMATA to LPDIACTIONFORMATW * * @parm const LPDIACTIONFORMATA | lpDiAfA | * * Original. * * @parm LPDIACTIONFORMATW* | lplpDiAfW | * * Address of a pointer to a that receives the converted * ACTIONFORMAT. * @returns * * Standard OLE . * *****************************************************************************/ HRESULT EXTERNAL DiActionFormatAtoW ( const LPDIACTIONFORMATA lpDiAfA, LPDIACTIONFORMATW* lplpDiAfW ) { DWORD cbAlloc; PDWORD pdwStrLen, pdwStrLen0; LPDIACTIONFORMATW lpDiAfW; LPDIACTIONA lpDiAA; LPDIACTIONW lpDiAW; DWORD i; HRESULT hres; EnterProcI(DiActionFormatAtoW, (_ "xx", lpDiAfA, lplpDiAfW)); // Internal function, no validation *lplpDiAfW = NULL; /* * PREFIX complains (mb:37926 - item 2) that we could be requesting a * zero byte allocation which would not allocate anything. This is * never the case because CDIDev_ActionMap_IsValidMapObject tests that * dwNumActions is less than 2^24. Assert in debug for extra safety. */ AssertF( (lpDiAfA->dwNumActions +1)*cbX(*pdwStrLen0) ); hres = AllocCbPpv( (lpDiAfA->dwNumActions +1)*cbX(*pdwStrLen0) , &pdwStrLen0); if ( SUCCEEDED(hres) ) { // Compute the amount of memory required to clone the DIACTIONFORMATA cbAlloc = /* 1: The wide form of the Action Format array */ cbX(DIACTIONFORMATW) /* 2: Each of the DIACTION arrays */ + lpDiAfA->dwActionSize * lpDiAfA->dwNumActions; pdwStrLen = pdwStrLen0; // Compute the size for each of the text strings in array of DIACTIONs for ( i = 0x0, lpDiAA = lpDiAfA->rgoAction; i < lpDiAfA->dwNumActions ; i++, lpDiAA++ ) { // Handle the NULL ptr case if ( NULL != lpDiAA->lptszActionName ) { /* 3: Text string in each DIACTION array*/ // Conversion from A to U, need multiplier // ISSUE-2001/03/29-timgill (MarcAnd), A to U conversions are not always 1 to 1. *pdwStrLen = lstrlenA(lpDiAA->lptszActionName) + 1; cbAlloc += cbX(lpDiAW->lptszActionName[0]) * ( *pdwStrLen++ ); } } if ( SUCCEEDED( hres = AllocCbPpv(cbAlloc, &lpDiAfW) ) ) { DWORD dwLen; DWORD cb; pdwStrLen = pdwStrLen0; // 1: Copy the DIACTIONFORMAT /* * Assert that the structure can be copied as: * a) dwSize * b) everything else * c) the app name */ CAssertF( FIELD_OFFSET( DIACTIONFORMATW, dwSize ) == 0 ); CAssertF( FIELD_OFFSET( DIACTIONFORMATA, dwSize ) == 0 ); CAssertF( FIELD_OFFSET( DIACTIONFORMATW, dwSize ) + cbX( ((LPDIACTIONFORMATW)0)->dwSize ) == FIELD_OFFSET( DIACTIONFORMATW, dwActionSize ) ); #if defined(_WIN64) CAssertF( ( ( cbX( DIACTIONFORMATW ) - cbX( ((LPDIACTIONFORMATW)0)->tszActionMap ) ) - ( cbX( DIACTIONFORMATA ) - cbX( ((LPDIACTIONFORMATA)0)->tszActionMap ) ) < MAX_NATURAL_ALIGNMENT ) ); #else CAssertF( ( cbX( DIACTIONFORMATW ) - cbX( ((LPDIACTIONFORMATW)0)->tszActionMap ) ) == ( cbX( DIACTIONFORMATA ) - cbX( ((LPDIACTIONFORMATA)0)->tszActionMap ) ) ); #endif CAssertF( FIELD_OFFSET( DIACTIONFORMATW, tszActionMap ) == FIELD_OFFSET( DIACTIONFORMATA, tszActionMap ) ); CAssertF( cA( ((LPDIACTIONFORMATW)0)->tszActionMap ) == cA( ((LPDIACTIONFORMATA)0)->tszActionMap ) ); // Init counts and lpDiAfW fields dwLen = lpDiAfA->dwActionSize * lpDiAfA->dwNumActions; cb = lpDiAfW->dwSize = cbX(DIACTIONFORMATW); memcpy( &lpDiAfW->dwActionSize, &lpDiAfA->dwActionSize, FIELD_OFFSET( DIACTIONFORMATW, tszActionMap ) - FIELD_OFFSET( DIACTIONFORMATW, dwActionSize ) ); AToU(lpDiAfW->tszActionMap, cbX(lpDiAfA->tszActionMap), lpDiAfA->tszActionMap); // 2: Block copy the DIACTION array CAssertF(cbX(*lpDiAfW->rgoAction) == cbX(*lpDiAfA->rgoAction) ) lpDiAfW->rgoAction = (LPDIACTIONW)( (char*)lpDiAfW + cb); dwLen = lpDiAfA->dwActionSize * lpDiAfA->dwNumActions; memcpy(lpDiAfW->rgoAction, lpDiAfA->rgoAction, dwLen); cb += dwLen; // 3: ActionName // Convert each of the strings in the ACTION array from A to W for ( i = 0x0, lpDiAA=lpDiAfA->rgoAction, lpDiAW=lpDiAfW->rgoAction; i < lpDiAfW->dwNumActions ; i++, lpDiAA++, lpDiAW++ ) { if ( lpDiAA->lptszActionName != NULL ) { WCHAR* wsz = (WCHAR*) ((char*)lpDiAfW+cb); lpDiAW->lptszActionName = wsz; dwLen = (*pdwStrLen++); AToU( wsz, dwLen, lpDiAA->lptszActionName); cb += dwLen * cbX(lpDiAW->lptszActionName[0]) ; } else { // Resource strings are handled in DiActionFormatWtoW // OK for now, as long as UI always uses CloneDiActionFormatW lpDiAW->lptszActionName = NULL; } } // If we have not done something goofy, the memory allocates should match // the memory we used AssertF(cbAlloc == cb ); *lplpDiAfW = lpDiAfW; } FreePpv(&pdwStrLen0); } ExitOleProc(); return(hres); } STDMETHODIMP CDIObj_ConfigureDevicesA ( PV pDiA, LPDICONFIGUREDEVICESCALLBACK lpdiCallback, LPDICONFIGUREDEVICESPARAMSA lpdiCDParams, DWORD dwFlags, LPVOID pvRefData ) { HRESULT hres = S_OK; EnterProcR(IDirectInput8::ConfigureDevices, (_ "pppxp", pDiA, lpdiCallback, lpdiCDParams, dwFlags, pvRefData)); /* * ConfigureDevicesCore will validate the rest. */ if ( (SUCCEEDED(hres = hresPvI(pDiA, ThisInterfaceA)) && (SUCCEEDED(hres = hresFullValidReadPvCb(lpdiCDParams, sizeof(DICONFIGUREDEVICESPARAMSA), 2)) && ((lpdiCDParams->lptszUserNames == NULL) || (SUCCEEDED(hres = hresFullValidReadStrA((LPSTR)(lpdiCDParams->lptszUserNames), MAX_JOYSTRING * (lpdiCDParams->dwcUsers), 2)))) && (SUCCEEDED(hres = hresFullValidReadPvCb(lpdiCDParams->lprgFormats, lpdiCDParams->dwcFormats*sizeof(DIACTIONFORMATA), 2)))))) { if( lpdiCDParams->dwSize != cbX(DICONFIGUREDEVICESPARAMSA) ) { RPF("IDirectInput::%s: Invalid DICONFIGUREDEVICESPARAMSA.dwSize 0x%08x", lpdiCDParams->dwSize ); hres = E_INVALIDARG; } if (SUCCEEDED(hres)) { PDDI this = _thisPvNm(pDiA, diA); //params structure DICONFIGUREDEVICESPARAMSW diconfparamsW; //to translate each DIACTIONFORMAT LPDIACTIONFORMATW* lpDiAfW = NULL; //the new array of DIACTIONFORMATs LPDIACTIONFORMATW* lpDAFW = NULL; //to traverse the old array LPDIACTIONFORMATA lpDIFormat; //to traverse the new array LPDIACTIONFORMATW lpDIF; //to keep the new user name LPWSTR lpUserNames = NULL; //to know how many DIACTIONFORMATS we have cloned successfully DWORD clonedF = 0; //kength of user names string DWORD strLen = 0; //zero out ZeroMemory(&diconfparamsW, sizeof(DICONFIGUREDEVICESPARAMSW)); //set the size diconfparamsW.dwSize = sizeof(DICONFIGUREDEVICESPARAMSW); //1. Validate and translate each LPDIACTIONFORMAT in the array lpDIFormat = (lpdiCDParams->lprgFormats); //allocate the new array hres = AllocCbPpv(lpdiCDParams->dwcFormats * sizeof(DIACTIONFORMATW), &diconfparamsW.lprgFormats); if (FAILED(hres)) { goto cleanup; } lpDIF = diconfparamsW.lprgFormats; //allocate the cloning array hres = AllocCbPpv(lpdiCDParams->dwcFormats * sizeof(DIACTIONFORMATW), &lpDAFW); if (FAILED(hres)) { goto cleanup; } lpDiAfW = lpDAFW; //clone for (clonedF = 0; clonedF < lpdiCDParams->dwcFormats; clonedF ++) { //validate hres = IsValidMapObjectA(lpDIFormat D(comma s_szProc comma 2)); if (FAILED(hres)) { goto cleanup; } //translate hres = DiActionFormatAtoW(lpDIFormat, lpDiAfW); if (FAILED(hres)) { goto cleanup; } //save *lpDIF = *(*lpDiAfW); //move on lpDIFormat++; lpDiAfW++; lpDIF++; } //if everything went fine, should have cloned all AssertF(clonedF == lpdiCDParams->dwcFormats); //2. Copy the user names if (lpdiCDParams->lptszUserNames != NULL) { DWORD countN; DWORD Len; //to traverse new user names WCHAR* lpNameW; CHAR* lpName = lpdiCDParams->lptszUserNames; //go throught all the user names for ( countN = 0; countN < lpdiCDParams->dwcUsers; countN ++) { hres = hresFullValidReadStrA(lpName, MAX_JOYSTRING, 2); if (FAILED(hres)) { goto cleanup; } Len = lstrlenA(lpName); //if length is 0 -- and we haven't reached the correct user count yet - //then it is an error if (Len == 0) { hres = DIERR_INVALIDPARAM; goto cleanup; } //move on to the next user name strLen += Len + 1; lpName += Len + 1; } //if everything went fine, should have traversed all the user names AssertF(countN == lpdiCDParams->dwcUsers); //allocate hres = AllocCbPpv( (strLen + 1) * 2, &lpUserNames ); if (FAILED(hres)) { goto cleanup; } //translate //AToU stops at the first '\0', so we have to go in a loop lpName = lpdiCDParams->lptszUserNames; lpNameW = lpUserNames; //go throught all the user names for ( countN = 0; countN < lpdiCDParams->dwcUsers; countN ++) { Len = lstrlenA(lpName); AToU(lpNameW, Len + 1, lpName); lpName += Len + 1; lpNameW += Len + 1; } //save diconfparamsW.lptszUserNames = lpUserNames; } //3. Populate the rest of the structure diconfparamsW.dwcUsers = lpdiCDParams->dwcUsers; diconfparamsW.dwcFormats = clonedF; diconfparamsW.hwnd = lpdiCDParams->hwnd; diconfparamsW.dics = lpdiCDParams->dics; diconfparamsW.lpUnkDDSTarget = lpdiCDParams->lpUnkDDSTarget; //4.Call the framework hres = CDIObj_ConfigureDevicesCore ( &this->diW, lpdiCallback, &diconfparamsW, dwFlags, pvRefData); cleanup:; //free the sapce for the array FreePpv(&diconfparamsW.lprgFormats); //free as many DIACTIONFORMATs as were created if (lpDAFW) { lpDiAfW = lpDAFW; for (clonedF; clonedF > 0; clonedF--) { FreeDiActionFormatW(lpDiAfW); lpDiAfW++; } //delete the entire block FreePpv(&lpDAFW); } //free the user names, if allocated FreePpv(&lpUserNames); } } ExitOleProc(); return(hres); } /***************************************************************************** * * The long-awaited vtbls and templates * *****************************************************************************/ #pragma BEGIN_CONST_DATA #define CDIObj_Signature 0x504E4944 /* "DINP" */ Interface_Template_Begin(CDIObj) Primary_Interface_Template(CDIObj, TFORM(ThisInterfaceT)) Secondary_Interface_Template(CDIObj, SFORM(ThisInterfaceT)) Interface_Template_End(CDIObj) Primary_Interface_Begin(CDIObj, TFORM(ThisInterfaceT)) TFORM(CDIObj_CreateDevice), TFORM(CDIObj_EnumDevices), TFORM(CDIObj_GetDeviceStatus), TFORM(CDIObj_RunControlPanel), TFORM(CDIObj_Initialize), TFORM(CDIObj_FindDevice), TFORM(CDIObj_EnumDevicesBySemantics), TFORM(CDIObj_ConfigureDevices), Primary_Interface_End(CDIObj, TFORM(ThisInterfaceT)) Secondary_Interface_Begin(CDIObj, SFORM(ThisInterfaceT), SFORM(di)) SFORM(CDIObj_CreateDevice), SFORM(CDIObj_EnumDevices), SFORM(CDIObj_GetDeviceStatus), SFORM(CDIObj_RunControlPanel), SFORM(CDIObj_Initialize), SFORM(CDIObj_FindDevice), SFORM(CDIObj_EnumDevicesBySemantics), SFORM(CDIObj_ConfigureDevices), Secondary_Interface_End(CDIObj, SFORM(ThisInterfaceT), SFORM(di))