/***************************************************************************** * * DIUtil.c * * Copyright (c) 1996 - 2000 Microsoft Corporation. All Rights Reserved. * * Abstract: * * Misc helper functions. * * Contents: * * * *****************************************************************************/ #include "dinputpr.h" /***************************************************************************** * * The sqiffle for this file. * *****************************************************************************/ #define sqfl sqflUtil /***************************************************************************** * * @doc INTERNAL * * @func LPCTSTR | _ParseHex | * * Parse a hex string encoding cb bytes (at most 4), then expect * the tchDelim to appear afterwards. If tchDelim is 0, then no * delimiter is expected. * * Store the result into the indicated LPBYTE (using only the * size requested), updating it, and return a pointer to the * next unparsed character, or 0 on error. * * If the incoming pointer is also 0, then return 0 immediately. * * @parm IN LPCTSTR | ptsz | * * The string to parse. * * @parm IN OUT LPBYTE * | ppb | * * Pointer to the address of the destination buffer. * * @parm IN int | cb | * * The size in bytes of the buffer. * * @parm IN TCHAR | tchDelim | * * The delimiter charater to end the sequence or zero if none is * expected. * * @returns * * Returns a pointer to the next unparsed character, or 0 on error. * * @comm * Stolen from TweakUI. * * Prefix takes a strong dislike to this function, reporting that * all callers could use uninitialized memory when the function * succeeds. * The problem appears to be that Prefix is unable to determine that * if the source string can successfully be read, the destination is * always completely filled (the whole passed destination size) with * the binary value of the source string. Since all callers always * pass the size of the variable to which the destination buffer * pointer points, the memory is always completely initialized but * it seems reasonable that Prefix would raise a warning. * *****************************************************************************/ LPCTSTR INTERNAL _ParseHex(LPCTSTR ptsz, LPBYTE *ppb, int cb, TCHAR tchDelim) { if(ptsz) { int i = cb * 2; DWORD dwParse = 0; do { DWORD uch; uch = (TBYTE)*ptsz - TEXT('0'); if(uch < 10) { /* a decimal digit */ } else { uch = (*ptsz | 0x20) - TEXT('a'); if(uch < 6) { /* a hex digit */ uch += 10; } else { return 0; /* Parse error */ } } dwParse = (dwParse << 4) + uch; ptsz++; } while(--i); if(tchDelim && *ptsz++ != tchDelim) return 0; /* Parse error */ for(i = 0; i < cb; i++) { (*ppb)[i] = ((LPBYTE)&dwParse)[i]; } *ppb += cb; } return ptsz; } /***************************************************************************** * * @doc INTERNAL * * @func BOOL | ParseGUID | * * Take a string and convert it into a GUID, return success/failure. * * @parm OUT LPGUID | lpGUID | * * Receives the parsed GUID on success. * * @parm IN LPCTSTR | ptsz | * * The string to parse. The format is * * { dword - word - word * - byte byte * - byte byte byte * byte byte byte } * * @returns * * Returns zero if

is not a valid GUID. * * * @comm * * Stolen from TweakUI. * *****************************************************************************/ BOOL EXTERNAL ParseGUID(LPGUID pguid, LPCTSTR ptsz) { if(lstrlen(ptsz) == ctchGuid - 1 && *ptsz == TEXT('{')) { ptsz++; ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 4, TEXT('-')); ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 2, TEXT('-')); ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 2, TEXT('-')); ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, 0 ); ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, TEXT('-')); ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, 0 ); ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, 0 ); ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, 0 ); ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, 0 ); ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, 0 ); ptsz = _ParseHex(ptsz, (LPBYTE *)&pguid, 1, TEXT('}')); return (BOOL)(UINT_PTR)ptsz; } else { return 0; } } /***************************************************************************** * * @doc INTERNAL * * @func BOOL | ParseVIDPID | * * Take a string formatted as VID_%04&PID_%04. * * @parm OUT PUSHORT | puVID | * * Receives the parsed VID. * * @parm OUT PUSHORT | puPID | * * Receives the parsed PID. * * @parm IN LPCTSTR | ptsz | * * * @returns * * Returns zero on failure. * * * @comm * * Stolen from TweakUI. * *****************************************************************************/ // VID _ XXXX & PID _ YYYY #define ctchVIDPID ( 3 + 1 + 4 + 1 + 3 + 1 + 4 ) #define VID_ TEXT("VID_") #define VID_offset (3+1) #define PID_ TEXT("&PID_") #define PID_offset (3+1+4+1+3+1) BOOL EXTERNAL ParseVIDPID(PUSHORT puVID, PUSHORT puPID , LPCWSTR pwsz) { LPCTSTR ptsz; #ifndef UNICODE TCHAR tsz[MAX_JOYSTRING]; UToT( tsz, cA(tsz), pwsz ); ptsz = tsz; #else ptsz = pwsz; #endif if( _ParseHex(ptsz+VID_offset, (LPBYTE *)&puVID, 2, TEXT('&')) && _ParseHex(ptsz+PID_offset, (LPBYTE *)&puPID, 2, 0) ) { return TRUE; } return FALSE; } /***************************************************************************** * * @doc INTERNAL * * @func void | NameFromGUID | * * Convert a GUID into an ASCII string that will be used * to name it in the global namespace. * * We use the name "DirectInput.{guid}". * * Names are used in the following places: * * names a mutex based on * to gate access to the * shared memory block used to manage exclusive access. * * names a shared memory block based on * to record information * about exclusive access. * * names a mutex based on * to gate access to the * shared memory block used to track joystick effects. * * @parm LPTSTR | ptszBuf | * * Output buffer to receive the converted name. It must * be characters in size. * * @parm PCGUID | pguid | * * The GUID to convert. * * *****************************************************************************/ #pragma BEGIN_CONST_DATA /* Note: If you change this string, you need to change ctchNameGuid to match */ TCHAR c_tszNameFormat[] = TEXT("DirectInput.{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"); #pragma END_CONST_DATA void EXTERNAL NameFromGUID(LPTSTR ptszBuf, PCGUID pguid) { int ctch; ctch = wsprintf(ptszBuf, c_tszNameFormat, pguid->Data1, pguid->Data2, pguid->Data3, pguid->Data4[0], pguid->Data4[1], pguid->Data4[2], pguid->Data4[3], pguid->Data4[4], pguid->Data4[5], pguid->Data4[6], pguid->Data4[7]); AssertF(ctch == ctchNameGuid - 1); } /***************************************************************************** * * @doc INTERNAL * * @func PV | pvFindResource | * * Handy wrapper that finds and loads a resource. * * @parm IN HINSTANCE | hinst | * * Module instance handle. * * @parm DWORD | id | * * Resource identifier. * * @parm LPCTSTR | rt | * * Resource type. * * @returns * * Pointer to resource, or 0. * *****************************************************************************/ PV EXTERNAL pvFindResource(HINSTANCE hinst, DWORD id, LPCTSTR rt) { HANDLE hrsrc; PV pv; hrsrc = FindResource(hinst, (LPTSTR)(LONG_PTR)(id), rt); if(hrsrc) { pv = LoadResource(hinst, hrsrc); } else { pv = 0; } return pv; } #ifndef UNICODE /***************************************************************************** * * @doc INTERNAL * * @func UINT | LoadStringW | * * Implementation of LoadStringW for platforms on which Unicode is * not supported. Does exactly what LoadStringW would've done * if it existed. * * @parm IN HINSTANCE | hinst | * * Module instance handle. * * @parm UINT | ids | * * String id number. * * @parm LPWSTR | pwsz | * * UNICODE output buffer. * * @parm UINT | cwch | * * Size of UNICODE output buffer. * * @returns * * Number of characters copied, not including terminating null. * * @comm * * Since the string is stored in the resource as UNICODE, * we just take it out ourselves. If we go through * , we may end up losing characters due * to character set translation. * *****************************************************************************/ int EXTERNAL LoadStringW(HINSTANCE hinst, UINT ids, LPWSTR pwsz, int cwch) { PWCHAR pwch; AssertF(cwch); ScrambleBuf(pwsz, cbCwch(cwch)); /* * String tables are broken up into "bundles" of 16 strings each. */ pwch = pvFindResource(hinst, 1 + ids / 16, RT_STRING); if(pwch) { /* * Now skip over the strings in the resource until we * hit the one we want. Each entry is a counted string, * just like Pascal. */ for(ids %= 16; ids; ids--) { pwch += *pwch + 1; } cwch = min(*pwch, cwch - 1); memcpy(pwsz, pwch+1, cbCwch(cwch)); /* Copy the goo */ } else { cwch = 0; } pwsz[cwch] = TEXT('\0'); /* Terminate the string */ return cwch; } #endif /***************************************************************************** * * @doc INTERNAL * * @func void | GetNthString | * * Generate a generic numbered object name. * * @parm LPWSTR | pwsz | * * Output buffer of characters. * * @parm UINT | ids | * * String containing number template. * * @parm UINT | ui | * * Button number. * *****************************************************************************/ void EXTERNAL GetNthString(LPWSTR pwsz, UINT ids, UINT ui) { TCHAR tsz[256]; #ifndef UNICODE TCHAR tszOut[MAX_PATH]; #endif LoadString(g_hinst, ids, tsz, cA(tsz)); #ifdef UNICODE wsprintfW(pwsz, tsz, ui); #else wsprintf(tszOut, tsz, ui); TToU(pwsz, MAX_PATH, tszOut); #endif } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | hresRunControlPanel | * * Run the control panel with the specified applet. * * @parm LPCTSTR | ptszApplet | * * Applet name. * * @returns * * if we started the applet. * *****************************************************************************/ #pragma BEGIN_CONST_DATA TCHAR c_tszControlExeS[] = TEXT("control.exe %s"); #pragma END_CONST_DATA HRESULT EXTERNAL hresRunControlPanel(LPCTSTR ptszCpl) { HRESULT hres; STARTUPINFO si; PROCESS_INFORMATION pi; TCHAR tsz[MAX_PATH]; EnterProc(hresRunControlPanel, (_ "s", ptszCpl)); ZeroX(si); si.cb = cbX(si); wsprintf(tsz, c_tszControlExeS, ptszCpl); if(CreateProcess(0, tsz, 0, 0, 0, 0, 0, 0, &si, &pi)) { CloseHandle(pi.hProcess); CloseHandle(pi.hThread); hres = S_OK; } else { hres = hresLe(GetLastError()); } ExitOleProc(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @func void | ObjectInfoWToA | * * Convert a * to a * or a . * * @parm LPDIDIDEVICEOBJECTINSTANCEA | pdoiA | * * Destination. * * @parm LPCDIDIDEVICEOBJECTINSTANCEW | pdoiW | * * Source. * *****************************************************************************/ void EXTERNAL ObjectInfoWToA(LPDIDEVICEOBJECTINSTANCEA pdoiA, LPCDIDEVICEOBJECTINSTANCEW pdoiW) { EnterProc(ObjectInfoWToA, (_ "pp", pdoiA, pdoiW)); AssertF(pdoiW->dwSize == sizeof(DIDEVICEOBJECTINSTANCEW)); AssertF(IsValidSizeDIDEVICEOBJECTINSTANCEA(pdoiA->dwSize)); pdoiA->guidType = pdoiW->guidType; pdoiA->dwOfs = pdoiW->dwOfs; pdoiA->dwType = pdoiW->dwType; pdoiA->dwFlags = pdoiW->dwFlags; UToA(pdoiA->tszName, cA(pdoiA->tszName), pdoiW->tszName); if(pdoiA->dwSize >= cbX(DIDEVICEOBJECTINSTANCE_DX5A)) { pdoiA->dwFFMaxForce = pdoiW->dwFFMaxForce; pdoiA->dwFFForceResolution = pdoiW->dwFFForceResolution; pdoiA->wCollectionNumber = pdoiW->wCollectionNumber; pdoiA->wDesignatorIndex = pdoiW->wDesignatorIndex; pdoiA->wUsagePage = pdoiW->wUsagePage; pdoiA->wUsage = pdoiW->wUsage; pdoiA->dwDimension = pdoiW->dwDimension; pdoiA->wExponent = pdoiW->wExponent; pdoiA->wReportId = pdoiW->wReportId; } ExitProc(); } /***************************************************************************** * * @doc INTERNAL * * @func void | EffectInfoWToA | * * Convert a to a * * @parm LPDIEFFECTINFOA | pdeiA | * * Destination. * * @parm LPCDIEFFECTINFOW | pdeiW | * * Source. * *****************************************************************************/ void EXTERNAL EffectInfoWToA(LPDIEFFECTINFOA pdeiA, LPCDIEFFECTINFOW pdeiW) { EnterProc(EffectInfoWToA, (_ "pp", pdeiA, pdeiW)); AssertF(pdeiW->dwSize == sizeof(DIEFFECTINFOW)); AssertF(pdeiA->dwSize == cbX(*pdeiA)); pdeiA->guid = pdeiW->guid; pdeiA->dwEffType = pdeiW->dwEffType; pdeiA->dwStaticParams = pdeiW->dwStaticParams; pdeiA->dwDynamicParams = pdeiW->dwDynamicParams; UToA(pdeiA->tszName, cA(pdeiA->tszName), pdeiW->tszName); ExitProc(); } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | hresValidInstanceVer | * * Check the and version number received from * an application. * * @parm HINSTANCE | hinst | * * Purported module instance handle. * * @parm DWORD | dwVersion | * * Version the application is asking for. * *****************************************************************************/ HRESULT EXTERNAL hresValidInstanceVer_(HINSTANCE hinst, DWORD dwVersion, LPCSTR s_szProc) { HRESULT hres; TCHAR tszScratch[4]; EnterProcS(hresValidInstanceVer, (_ "xxs", hinst, dwVersion, s_szProc)); /* * You would think that passing a zero-sized buffer to * GetModuleFileName would return the necessary buffer size. * * You would be right. Except that the Win95 validation layer * doesn't realize that this was a valid scenario, so the call * fails in the validation layer and never reached Kernel. * * So we read it into a small scratch buffer. The scratch buffer * must be at least 2 characters; if we passed only 1, then * GetModuleFileName won't be able to write any characters and * will return 0. * * Now it turns out that there's a bug in NT where, if you * pass a buffer size of 4, but the actual name is longer than * 4, it writes 4 characters, PLUS A NULL TERMINATOR, thereby * smashing your stack and making you fault randomly. * * I spent two hours trying to figure that out. * * Therefore, you must pass one *less* than the buffer size * to GetModuleFileName, because it will overwrite your buffer * by one. */ if( ( hinst != 0 ) && GetModuleFileName(hinst, tszScratch, cA(tszScratch) - 1) ) { if(dwVersion == DIRECTINPUT_INTERNAL_VERSION) { hres = S_OK; } else if ( dwVersion == 0 ) { RPF("%s: DinputInput object has not been initialized, or the version is given as 0.", s_szProc); hres = DIERR_NOTINITIALIZED; } else if(dwVersion < DIRECTINPUT_VERSION) { RPF("%s: Incorrect dwVersion(0x%x); program was written with beta SDK. This version 0x%x", s_szProc, dwVersion, DIRECTINPUT_VERSION); hres = DIERR_BETADIRECTINPUTVERSION; } else { RPF("%s: Incorrect dwVersion(0x%x); program needs newer version of dinput. This version 0x%x", s_szProc, dwVersion, DIRECTINPUT_VERSION); hres = DIERR_OLDDIRECTINPUTVERSION; } } else { RPF("%s: Invalid HINSTANCE", s_szProc); hres = E_INVALIDARG; } ExitOleProc(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | DupEventHandle | * * Duplicate an event handle intra-process-ly. If the incoming * handle is NULL, then so is the output handle (and the call * succeeds). * * @parm HANDLE | h | * * Source handle. * * @parm LPHANDLE | phOut | * * Receives output handle. * *****************************************************************************/ HRESULT EXTERNAL DupEventHandle(HANDLE h, LPHANDLE phOut) { HRESULT hres; EnterProc(DupEventHandle, (_ "p", h)); if(h) { HANDLE hProcessMe = GetCurrentProcess(); if(DuplicateHandle(hProcessMe, h, hProcessMe, phOut, EVENT_MODIFY_STATE, 0, 0)) { hres = S_OK; } else { hres = hresLe(GetLastError()); } } else { *phOut = h; hres = S_OK; } ExitOleProc(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @func DWORD | GetWindowPid | * * Simple wrapper that returns the PID of a window. * * Here is also where we do goofy hacks for DOS boxes * on Win95. * * @parm HWND | hwnd | * * Window handle. * * @returns * * PID or 0. * *****************************************************************************/ DWORD EXTERNAL GetWindowPid(HWND hwnd) { DWORD pid; if(IsWindow(hwnd) && GetWindowThreadProcessId(hwnd, &pid) ) { #ifndef WINNT /* * The Winoldap console window belongs to another * process but Win95 lies and says that it belongs * to you but it doesn't. */ if ( GetProp(hwnd, TEXT("flWinOldAp")) != 0 ) { pid = 0; } #endif } else { pid = 0; } return pid; } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | hresDupPtszPptsz | * * OLEish version of strdup. * * @parm LPCTSTR | ptszSrc | * * Source string being duplicated. * * @parm LPTSTR * | pptszDst | * * Receives the duplicated string. * * @returns * * or an error code. * *****************************************************************************/ HRESULT EXTERNAL hresDupPtszPptsz(LPCTSTR ptszSrc, LPTSTR *pptszDst) { HRESULT hres; hres = AllocCbPpv(cbCtch(lstrlen(ptszSrc) + 1), pptszDst); if(SUCCEEDED(hres)) { lstrcpy(*pptszDst, ptszSrc); hres = S_OK; } return hres; } /***************************************************************************** * * @doc INTERNAL * * @func BOOL | fInitializeCriticalSection | * * Initialize the give critical section, returning 0 if an exception * is thrown, else 0. * * @parm LPCRITICAL_SECTION | pCritSec | * * Pointer to an uninitialized critical section. * *****************************************************************************/ BOOL EXTERNAL fInitializeCriticalSection(LPCRITICAL_SECTION pCritSec) { BOOL fres = 1; EnterProc(fInitializeCriticalSection, (_ "" )); AssertF( pCritSec ); __try { InitializeCriticalSection( pCritSec ); } __except( EXCEPTION_EXECUTE_HANDLER ) { fres = 0; } ExitProcF( fres ); return fres; } /***************************************************************************** * * @doc INTERNAL * * @func void | DiCharUpperW | * * This function converts a wide-character string or a single wide-character * to uppercase. Since Win9x doesn't implement CharUpperW, we have to implement * ourselves. * * @parm LPWSTR | pwsz | * * The string to be converted * * @returns * * void * *****************************************************************************/ void EXTERNAL DiCharUpperW(LPWSTR pwsz) { int idx; int iLen = lstrlenW(pwsz); #define DIFF (L'a' - L'A') for( idx=0; idx= L'a') && (pwsz[idx] <= L'z') ){ pwsz[idx] -= DIFF; } } #undef DIFF } /***************************************************************************** * * @doc INTERNAL * * @func void | ptrSwap | * * swaps the pointers pointed to by the two parameters * * @parm void ** | ppA | * * pointer to first pointer * * @parm void ** | ppB | * * pointer to second pointer * *****************************************************************************/ void __inline ptrSwap( PPV ppA, PPV ppB ) { PV pTemp = *ppB; *ppB = *ppA; *ppA = pTemp; } /***************************************************************************** * * @doc INTERNAL * * @func void | ptrPartialQSort | * * Partially sort the passed (sub)set of an array of pointers to * structures such that resultant array is contains sub arrays which * are sorted with respect to each other though locally unsorted. * Once partially sorted, a simple sort may be used to complete the * sort. * * @parm void ** | ppL | * Pointer to lowest element in the (sub)array to sort * * @parm void ** | ppR | * Pointer to highest element in the (sub)array to sort * * @parm COMP_FUNC | fpCompare | * Pointer to function returning analog of strcmp for strings, but * supplied by caller for comparing elements of the array. * *****************************************************************************/ void ptrPartialQSort ( PPV ppL, PPV ppR, COMP_FUNC fpCompare ) { while( ( ppR - ppL ) > 8 ) { /* * First pick a pivot by sorting the first last and middle * values in the sub array. */ { PPV ppMid = ppL + ( ( ppR - ppL ) / 2 ); if( fpCompare( *ppL, *ppMid ) > 0 ) { ptrSwap( ppL, ppMid ); } if( fpCompare( *ppL, *ppR ) > 0 ) { ptrSwap( ppL, ppR ); } if( fpCompare( *ppMid, *ppR ) > 0 ) { ptrSwap( ppMid, ppR ); } /* * Now we have a reasonable chance of a good pivot, move it * out of the way. */ ptrSwap( ppMid, ppR-1 ); } /* * Now sort the remainder into high and low parts */ { PPV ppHi = ppR - 1; PV pPivot = *ppHi; PPV ppLo = ppL; for( ;; ) { while( fpCompare( *(++ppLo), pPivot ) < 0 ); while( fpCompare( *(--ppHi), pPivot ) > 0 ); if( ppLo >= ppHi ) break; ptrSwap( ppLo, ppHi ); } /* * Put the pivot back between the two parts as it must be in * order relative to all the items on each side of it. */ ptrSwap( ppLo, ppR - 1 ); /* * Recurse on the smaller part and continue with the larger */ if( ppLo - ppL > ppR - ppLo ) { /* * Left part is larger */ ptrPartialQSort( ppLo + 1, ppR, fpCompare ); ppR = ppLo - 1; } else { /* * Right part is larger */ ptrPartialQSort( ppL, ppLo - 1, fpCompare ); ppL = ppLo + 1; } } } } /***************************************************************************** * * @doc INTERNAL * * @func void | ptrInsertSort | * * Sort the passed (sub)set of an array of pointers to structures. * This sort is not suitable for large arrays. * * @parm void ** | ppBase | * Pointer to lowest element in the (sub)array to sort * * @parm void ** | ppLast | * Pointer to highest element in the (sub)array to sort * * @parm COMP_FUNC | fpCompare | * Pointer to function returning analog of strcmp for strings, but * supplied by caller for comparing elements of the array. * *****************************************************************************/ void ptrInsertSort ( PPV ppBase, PPV ppLast, COMP_FUNC fpCompare ) { PPV ppOuter; for( ppOuter = ppBase + 1; ppOuter <= ppLast; ppOuter++ ) { PV pTemp = *ppOuter; PPV ppInner; for( ppInner = ppOuter - 1; ppInner >= ppBase; ppInner-- ) { if( fpCompare( pTemp, *ppInner ) > 0 ) { *(ppInner+1) = *ppInner; } else { break; } } *(ppInner+1) = pTemp; } } /***************************************************************************** * * @doc INTERNAL * * @func void | swap | * * swaps the two array elements of size width * * @parm char * | a | * * pointer to first elements to swap * * @parm char * | b | * * pointer to second elements to swap * * @parm unsigned | width | * * width in bytes of each array element * * @returns * * void * *****************************************************************************/ static void __cdecl swap ( char *a, char *b, unsigned width ) { char tmp; if ( a != b ) { /* Do the swap one character at a time to avoid potential alignment problems. */ while ( width-- ) { tmp = *a; *a++ = *b; *b++ = tmp; } } } /***************************************************************************** * * @doc INTERNAL * * @func void | shortsort | * * sorts the sub-array of elements between lo and hi (inclusive) * side effects: sorts in place * assumes that lo is less than hi * * @parm char * | lo | * pointer to low element to sort * * @parm char * | hi | * pointer to high element to sort * * @parm unsigned | width | * width in bytes of each array element * * @parm int (*func)() | comp | * pointer to function returning analog of strcmp for * strings, but supplied by user for comparing the array elements. * it accepts 2 pointers to elements and returns neg if 1 lt 2, 0 if * 1 eq 2, pos if 1 gt 2. * * @returns * * void * *****************************************************************************/ void __cdecl shortsort ( char *lo, char *hi, unsigned width, int (__cdecl *comp)(const void *, const void *) ) { char *p, *max; /* Note: in assertions below, i and j are alway inside original bound of array to sort. */ while (hi > lo) { /* A[i] <= A[j] for i <= j, j > hi */ max = lo; for (p = lo+width; p <= hi; p += width) { /* A[i] <= A[max] for lo <= i < p */ if (comp(p, max) > 0) { max = p; } /* A[i] <= A[max] for lo <= i <= p */ } /* A[i] <= A[max] for lo <= i <= hi */ swap(max, hi, width); /* A[i] <= A[hi] for i <= hi, so A[i] <= A[j] for i <= j, j >= hi */ hi -= width; /* A[i] <= A[j] for i <= j, j > hi, loop top condition established */ } /* A[i] <= A[j] for i <= j, j > lo, which implies A[i] <= A[j] for i < j, so array is sorted */ } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | GetWideUserName | * * Get a wide string for a user. * If both of the IN parameters are NULL, memory is allocated for a * MULTI_SZ string and set to the output pointer. The sting is * filled with the current user name if available, else a localizable * default sting. * If a UNICODE name is supplied, it is validated and assigned to the * output string pointer. * If an ANSI name is supplied, it is validated and memory is * allocated into which the translated SZ UNICODE name is written. * Note it is the caller's responsibility to free the memory in * either of the cases in which is is allocated. * Also note that in the case of an error no memory needs to be * freed. * * @parm LPCSTR | lpszUserName | * * A specific ANSI user name. * * @parm LPCWSTR | lpwszUserName | * * A specific UNICODE user name. * * @parm LPWSTR* | ppwszGoodUserName | * * A pointer to a pointer to the wide user name. * * @returns * * if the output string is valid * or an error code reflecting what went wrong. * *****************************************************************************/ STDMETHODIMP GetWideUserName ( IN LPCSTR lpszUserName, IN LPCWSTR lpwszUserName, OUT LPWSTR *ppwszGoodUserName ) { HRESULT hres = S_OK; EnterProcI(GetWideUserName, (_ "AW", lpszUserName, lpwszUserName )); if( lpwszUserName ) { /* * Just validate and copy the pointer */ if( SUCCEEDED( hres = hresFullValidReadStrW( lpwszUserName, UNLEN+1, 2 ) ) ) { *ppwszGoodUserName = (LPWSTR)lpwszUserName; } } else if( lpszUserName ) { /* * If an ANSI user name has been passed translate it */ if( SUCCEEDED( hres = hresFullValidReadStrA( lpszUserName, UNLEN+1, 1 ) ) ) { int UserNameLen = lstrlenA( lpszUserName ) + 1; hres = AllocCbPpv( cbX(*lpwszUserName) * UserNameLen, ppwszGoodUserName ); if( SUCCEEDED( hres ) ) { AToU( *ppwszGoodUserName, UserNameLen, lpszUserName ); } } } else { DWORD dwUserNameLen = UNLEN + 1; #ifdef WINNT hres = AllocCbPpv( (dwUserNameLen+1) * 2, ppwszGoodUserName ); if( SUCCEEDED( hres ) ) { if( GetUserNameW( *ppwszGoodUserName, &dwUserNameLen ) ) { #else hres = AllocCbPpv( (dwUserNameLen+1) * 3, ppwszGoodUserName ); if( SUCCEEDED( hres ) ) { if( GetUserNameA( (PCHAR)(&((*ppwszGoodUserName)[UNLEN+2])), &dwUserNameLen ) ) { AToU( *ppwszGoodUserName, dwUserNameLen, (PCHAR)(&((*ppwszGoodUserName)[UNLEN+2])) ); #endif /* * We allocated _and_zeroed_ an extra wchar for double * termination. Assert nobody messed it up and then * make sure we don't free the extra. */ AssertF( (*ppwszGoodUserName)[dwUserNameLen] == L'\0' ); dwUserNameLen++; } else { /* * If we felt the need, we could follow this recovery path * only if ( GetLastError() == ERROR_NOT_LOGGED_ON ) and * report some error or follow some alternate recovery * otherwise but unless a problem is found with always using * the default, this seems safer. */ dwUserNameLen = LoadStringW( g_hinst, IDS_DEFAULTUSER, *ppwszGoodUserName, UNLEN+1 ); if( dwUserNameLen ) { /* * Double terminate the string for ConfigureDevices */ dwUserNameLen += 2; (*ppwszGoodUserName)[dwUserNameLen-1] = L'\0'; SquirtSqflPtszV(sqflUtil | sqflBenign, TEXT("Failed to GetUserName, using default") ); } else { SquirtSqflPtszV(sqflUtil | sqflError, TEXT("Failed to GetUserName and default, le = %d"), GetLastError() ); hres = E_FAIL; } } /* * Chances are we have way more space than we need * NOTE, in the failure case, this reallocs to zero thus * freeing the memory. * If we have a valid sting, failure to realloc just means * using more memory that we need to for a little while. * If we don't have a string, ReallocCbPpv cannot fail. */ ReallocCbPpv( dwUserNameLen*2, ppwszGoodUserName ); #ifndef WINNT } } #else /* Reduce bracket matching insanity even though these don't match */ } } #endif #ifdef XDEBUG if( SUCCEEDED( hres ) ) { AssertF( SUCCEEDED( hresFullValidReadStrW( *ppwszGoodUserName, UNLEN+1, 2 ) ) ); } else { /* * If a passed string was invalid, we never allocated any memory so * don't worry about this uninitialized output value for this error. */ if( hres != E_INVALIDARG ) { AssertF( *ppwszGoodUserName == NULL ); } } #endif ExitOleProc(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @func DWORD | GetValidDI8DevType | * * Return a valid type and subtype based on the ones passed, the * number of buttons and axis/pov caps. * * @parm DWORD | dwDevType | * * Value to be checked, only the type and sub type bits are used. * * @parm DWORD | dwNumButtons | * * Number of buttons on device, only used when checking devices mja * * @parm DWORD | dwFlags | * * Flags determining any axis/pov requirements. mja * * @returns * * Zero if the type and subtype are not a valid combination for DX8. * * *****************************************************************************/ #pragma BEGIN_CONST_DATA #define MAKE_DI8TYPE_LIMITS( type ) { type##_MIN, type##_MAX, type##_MIN_BUTTONS, type##_MIN_CAPS }, struct { BYTE SubTypeMin; BYTE SubTypeMax; BYTE MinButtons; BYTE HWCaps; } c_rgSubTypeDetails[] = { { 0, 1, 0, 0 }, /* DI8DEVTYPEDEVICE has no subtype or limits */ MAKE_DI8TYPE_LIMITS( DI8DEVTYPEMOUSE ) MAKE_DI8TYPE_LIMITS( DI8DEVTYPEKEYBOARD ) MAKE_DI8TYPE_LIMITS( DI8DEVTYPEJOYSTICK ) MAKE_DI8TYPE_LIMITS( DI8DEVTYPEGAMEPAD ) MAKE_DI8TYPE_LIMITS( DI8DEVTYPEDRIVING ) MAKE_DI8TYPE_LIMITS( DI8DEVTYPEFLIGHT ) MAKE_DI8TYPE_LIMITS( DI8DEVTYPE1STPERSON ) MAKE_DI8TYPE_LIMITS( DI8DEVTYPEDEVICECTRL ) MAKE_DI8TYPE_LIMITS( DI8DEVTYPESCREENPTR ) MAKE_DI8TYPE_LIMITS( DI8DEVTYPEREMOTE ) MAKE_DI8TYPE_LIMITS( DI8DEVTYPESUPPLEMENTAL ) }; #undef MAKE_DI8TYPE_LIMITS #pragma END_CONST_DATA DWORD EXTERNAL GetValidDI8DevType ( DWORD dwDevType, DWORD dwNumButtons, DWORD dwFlags ) { BYTE bDevTypeIdx = GET_DIDEVICE_TYPE( dwDevType ) - DI8DEVTYPE_MIN; CAssertF( cA( c_rgSubTypeDetails ) == DI8DEVTYPE_MAX - DI8DEVTYPE_MIN ); if( ( (__int8)bDevTypeIdx >= 0 ) && ( bDevTypeIdx < DI8DEVTYPE_MAX - DI8DEVTYPE_MIN ) && ( GET_DIDEVICE_SUBTYPE( dwDevType ) >= c_rgSubTypeDetails[bDevTypeIdx].SubTypeMin ) && ( GET_DIDEVICE_SUBTYPE( dwDevType ) < c_rgSubTypeDetails[bDevTypeIdx].SubTypeMax ) ) { /* * MinButtons of zero means no minimum buttons OR caps */ if( !c_rgSubTypeDetails[bDevTypeIdx].MinButtons || ( ( dwNumButtons >= c_rgSubTypeDetails[bDevTypeIdx].MinButtons ) && ( ( dwFlags & c_rgSubTypeDetails[bDevTypeIdx].HWCaps ) == c_rgSubTypeDetails[bDevTypeIdx].HWCaps ) ) ) { return dwDevType & ( DIDEVTYPE_TYPEMASK | DIDEVTYPE_SUBTYPEMASK ); } else { return ( dwDevType & DIDEVTYPE_TYPEMASK ) | MAKE_DIDEVICE_TYPE( 0, DI8DEVTYPE_LIMITEDGAMESUBTYPE ); } } else { return 0; } } /***************************************************************************** * * @doc INTERNAL * * @func BOOL | DIGetKeyNameTextHelper | * * return key name of a key. * * @parm IN UINT | uiScanCode | * * scan code of the key. * * @parm OUT LPWSTR | lpwszName | * * The buffer that will hold the returned key name. * * @parm int | nSize | * * The length of lpwszName. * * @returns * * TRUE - if successfully get a key name. * FALSE - otherwise * *****************************************************************************/ BOOL DIGetKeyNameTextHelper( UINT uiScanCode, LPWSTR lpwszName, int nSize ) { HKL hkl; DWORD dwThread, dwProcessId; UINT uiVk; BYTE kbuf[256] = ""; int nResult; // we only want to use this method to resolve alpha & punct keys. if( uiScanCode > 0x53 ) { return FALSE; } //Get the active window's thread dwThread=GetWindowThreadProcessId(GetActiveWindow(), &dwProcessId); //Get the active window's keyboard layout hkl=GetKeyboardLayout(dwThread); uiVk = MapVirtualKeyEx( uiScanCode, 3, hkl ); #ifdef WINNT nResult = ToUnicodeEx(uiVk, uiScanCode, kbuf, lpwszName, nSize, 0, hkl); #else nResult = ToAsciiEx(uiVk, uiScanCode, kbuf, lpwszName, 0, hkl); #endif if( (nResult != 1) || (!iswalpha(lpwszName[0]) && !iswpunct(lpwszName[0])) ) { return FALSE; } else { WCHAR wc; wc = towupper( lpwszName[0] ); lpwszName[0] = wc; } return TRUE; } /***************************************************************************** * * @doc INTERNAL * * @func HRESULE | DIGetKeyNameText | * * return key name of a key. * * @parm IN UINT | index | * * the index of g_rgbKbdRMap[]. * * @parm IN DWORD | dwDevType | * * the DevType of the key. * * @parm OUT LPWSTR | lpwszName | * * The buffer that will hold the returned key name. * * @parm int | nSize | * * The length of lpwszName. * * @returns * * S_OK - if successfully get a key name. * DIERR_OBJECTNOTFOUND - otherwise * *****************************************************************************/ HRESULT DIGetKeyNameText( UINT index, DWORD dwDevType, LPWSTR lpwszName, int nSize ) { HRESULT hres; DWORD dwScancode; LONG lp; DWORD dw; BOOL fSpecKey = FALSE; DWORD dwSpecKeys[] = { 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, //number 0x90, 0x99, 0xa0, 0xa1, 0xa2, 0xa4, 0xae, 0xb0, 0xb2, 0xde, 0xdf, 0xe3, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed }; dwScancode = (DWORD)g_rgbKbdRMap[index]; lp = ((dwScancode & 0x7F) << 16) | ((dwScancode & 0x80) << 17); for( dw = 0; dw < cA(dwSpecKeys); dw++ ) { if( dwSpecKeys[dw] == dwScancode ) { fSpecKey = TRUE; break; } } if( !fSpecKey ) { if( !DIGetKeyNameTextHelper(dwScancode, lpwszName, nSize) ) { GetKeyNameTextW(lp, lpwszName, nSize); #ifndef UNICODE if( GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) { CHAR szName[MAX_PATH]; GetKeyNameTextA( lp, szName, cA(szName) ); AToU( lpwszName, cA(szName), szName ); } #endif } } if( lpwszName[0] == TEXT('\0') && (dwScancode != 0x56 && dwScancode != 0x73 && dwScancode != 0x7E ) ) { LoadStringW(g_hinst, IDS_KEYBOARDOBJECT + DIDFT_GETINSTANCE(dwDevType), lpwszName, nSize); } if( lpwszName[0] == L'\0' ) { hres = DIERR_OBJECTNOTFOUND; } else { hres = S_OK; } return hres; } /***************************************************************************** * * @doc INTERNAL * * @func DWORD | DIGetOSVersion | * * Return the OS version on which DInput8.dll is running. * * @returns * * WIN95_OS, WIN98_OS, WINME_OS, WINNT_OS, WINWH_OS, or WIN_UNKNOWN_OS. * *****************************************************************************/ DWORD DIGetOSVersion() { OSVERSIONINFO osVerInfo; DWORD dwVer; if( GetVersion() < 0x80000000 ) { dwVer = WINNT_OS; } else { dwVer = WIN95_OS; //assume Windows 95 for safe } osVerInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); // If GetVersionEx is supported, then get more details. if( GetVersionEx( &osVerInfo ) ) { // Win2K if( osVerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ) { // Whistler: Major = 5 & Build # > 2195 if( osVerInfo.dwMajorVersion == 5 && osVerInfo.dwBuildNumber > 2195 ) { dwVer = WINWH_OS; } else { dwVer = WINNT_OS; } } // Win9X else { if( (HIBYTE(HIWORD(osVerInfo.dwBuildNumber)) == 4) ) { // WinMe: Major = 4, Minor = 90 if( (LOBYTE(HIWORD(osVerInfo.dwBuildNumber)) == 90) ) { dwVer = WINME_OS; } else if ( (LOBYTE(HIWORD(osVerInfo.dwBuildNumber)) > 0) ) { dwVer = WIN98_OS; } else { dwVer = WIN95_OS; } } } } return dwVer; }