| /* dplayx.dll global data implementation. | 
 |  * | 
 |  * Copyright 1999,2000 - Peter Hunnisett | 
 |  * | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  * | 
 |  * This library is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 |  * Lesser General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU Lesser General Public | 
 |  * License along with this library; if not, write to the Free Software | 
 |  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
 |  * | 
 |  * | 
 |  * NOTES:  | 
 |  *  o Implementation of all things which are associated with dplay on | 
 |  *    the computer - i.e. shared resources and such. Methods in this | 
 |  *    compilation unit should not call anything outside of this unit | 
 |  *    except base windows services and an interface to start the | 
 |  *    messaging thread. | 
 |  *  o Methods that begin with DPLAYX_ are used for dealing with | 
 |  *    dplayx.dll data which is accessible from all processes. | 
 |  * | 
 |  */ | 
 |  | 
 | #include <stdarg.h> | 
 | #include <string.h> | 
 |  | 
 | #define NONAMELESSUNION | 
 | #define NONAMELESSSTRUCT | 
 | #include "wine/debug.h" | 
 | #include "windef.h" | 
 | #include "winbase.h" | 
 | #include "winerror.h" | 
 | #include "wine/unicode.h" | 
 |  | 
 | #include "wingdi.h" | 
 | #include "winuser.h" | 
 |  | 
 | #include "dplayx_global.h" | 
 | #include "dplayx_messages.h" /* For CreateMessageReceptionThread only */ | 
 |  | 
 | WINE_DEFAULT_DEBUG_CHANNEL(dplay); | 
 |  | 
 | /* FIXME: Need to do all that fun other dll referencing type of stuff */ | 
 |  | 
 | /* Static data for all processes */ | 
 | static const char lpszDplayxSemaName[] = "WINE_DPLAYX_SM"; | 
 | static HANDLE hDplayxSema; | 
 |  | 
 | static const char lpszDplayxFileMapping[] = "WINE_DPLAYX_FM"; | 
 | static HANDLE hDplayxSharedMem; | 
 |  | 
 | static LPVOID lpSharedStaticData = NULL; | 
 |  | 
 |  | 
 | #define DPLAYX_AcquireSemaphore() TRACE( "Waiting for DPLAYX semaphore\n" ); \ | 
 |                                   WaitForSingleObject( hDplayxSema, INFINITE );\ | 
 |                                   TRACE( "Through wait\n" ) | 
 |  | 
 | #define DPLAYX_ReleaseSemaphore() ReleaseSemaphore( hDplayxSema, 1, NULL ); \ | 
 |                                   TRACE( "DPLAYX Semaphore released\n" ) /* FIXME: Is this correct? */ | 
 |  | 
 |  | 
 | /* HACK for simple global data right now */ | 
 | #define dwStaticSharedSize (128 * 1024) /* 128 KBytes */ | 
 | #define dwDynamicSharedSize (512 * 1024) /* 512 KBytes */ | 
 | #define dwTotalSharedSize  ( dwStaticSharedSize + dwDynamicSharedSize ) | 
 |  | 
 |  | 
 | /* FIXME: Is there no easier way? */ | 
 |  | 
 | /* Pretend the entire dynamic area is carved up into 512 byte blocks. | 
 |  * Each block has 4 bytes which are 0 unless used */ | 
 | #define dwBlockSize 512 | 
 | #define dwMaxBlock  (dwDynamicSharedSize/dwBlockSize) | 
 |  | 
 | typedef struct | 
 | { | 
 |   DWORD used; | 
 |   DWORD data[dwBlockSize-sizeof(DWORD)]; | 
 | } DPLAYX_MEM_SLICE; | 
 |  | 
 | static DPLAYX_MEM_SLICE* lpMemArea; | 
 |  | 
 | static void DPLAYX_PrivHeapFree( LPVOID addr ) | 
 | { | 
 |   LPVOID lpAddrStart; | 
 |   DWORD dwBlockUsed; | 
 |  | 
 |   /* Handle getting passed a NULL */ | 
 |   if( addr == NULL ) | 
 |   { | 
 |     return; | 
 |   } | 
 |  | 
 |   lpAddrStart = (char*)addr - sizeof(DWORD); /* Find block header */ | 
 |   dwBlockUsed =  ((BYTE*)lpAddrStart - (BYTE*)lpMemArea)/dwBlockSize; | 
 |  | 
 |   lpMemArea[ dwBlockUsed ].used = 0; | 
 | } | 
 |  | 
 | static LPVOID DPLAYX_PrivHeapAlloc( DWORD flags, DWORD size ) | 
 | { | 
 |   LPVOID lpvArea = NULL; | 
 |   UINT   uBlockUsed; | 
 |  | 
 |   if( size > (dwBlockSize - sizeof(DWORD)) ) | 
 |   { | 
 |     FIXME( "Size exceeded. Request of 0x%08x\n", size ); | 
 |     size = dwBlockSize - sizeof(DWORD); | 
 |   } | 
 |  | 
 |   /* Find blank area */ | 
 |   uBlockUsed = 0; | 
 |   while( ( lpMemArea[ uBlockUsed ].used != 0 ) && ( uBlockUsed <= dwMaxBlock ) ) { uBlockUsed++; } | 
 |  | 
 |   if( uBlockUsed <= dwMaxBlock ) | 
 |   { | 
 |     /* Set the area used */ | 
 |     lpMemArea[ uBlockUsed ].used = 1; | 
 |     lpvArea = lpMemArea[ uBlockUsed ].data; | 
 |   } | 
 |   else | 
 |   { | 
 |     ERR( "No free block found\n" ); | 
 |     return NULL; | 
 |   } | 
 |  | 
 |   if( flags & HEAP_ZERO_MEMORY ) | 
 |   { | 
 |     ZeroMemory( lpvArea, size ); | 
 |   } | 
 |  | 
 |   return lpvArea; | 
 | } | 
 |  | 
 |  | 
 | enum { numSupportedLobbies = 32, numSupportedSessions = 32 }; | 
 | typedef struct tagDPLAYX_LOBBYDATA | 
 | { | 
 |   /* Points to lpConn + block of contiguous extra memory for dynamic parts | 
 |    * of the struct directly following | 
 |    */ | 
 |   LPDPLCONNECTION lpConn; | 
 |  | 
 |   /* Information for dplobby interfaces */ | 
 |   DWORD           dwAppID; | 
 |   DWORD           dwAppLaunchedFromID; | 
 |  | 
 |   /* Should this lobby app send messages to creator at important life | 
 |    * stages | 
 |    */ | 
 |   HANDLE hInformOnAppStart; | 
 |   HANDLE hInformOnAppDeath; | 
 |   HANDLE hInformOnSettingRead; | 
 |  | 
 |   /* Sundries */ | 
 |   BOOL bWaitForConnectionSettings; | 
 |   DWORD dwLobbyMsgThreadId; | 
 |  | 
 |  | 
 | } DPLAYX_LOBBYDATA, *LPDPLAYX_LOBBYDATA; | 
 |  | 
 | static DPLAYX_LOBBYDATA* lobbyData = NULL; | 
 | /* static DPLAYX_LOBBYDATA lobbyData[ numSupportedLobbies ]; */ | 
 |  | 
 | static DPSESSIONDESC2* sessionData = NULL; | 
 | /* static DPSESSIONDESC2* sessionData[ numSupportedSessions ]; */ | 
 |  | 
 |  | 
 | static void DPLAYX_InitializeLobbyDataEntry( LPDPLAYX_LOBBYDATA lpData ) | 
 | { | 
 |   ZeroMemory( lpData, sizeof( *lpData ) ); | 
 | } | 
 |  | 
 | /* NOTE: This must be called with the semaphore acquired. | 
 |  * TRUE/FALSE with a pointer to it's data returned. Pointer data is | 
 |  * is only valid if TRUE is returned. | 
 |  */ | 
 | static BOOL DPLAYX_IsAppIdLobbied( DWORD dwAppID, LPDPLAYX_LOBBYDATA* lplpDplData ) | 
 | { | 
 |   UINT i; | 
 |  | 
 |   *lplpDplData = NULL; | 
 |  | 
 |   if( dwAppID == 0 ) | 
 |   { | 
 |     dwAppID = GetCurrentProcessId(); | 
 |     TRACE( "Translated dwAppID == 0 into 0x%08x\n", dwAppID ); | 
 |   } | 
 |  | 
 |   for( i=0; i < numSupportedLobbies; i++ ) | 
 |   { | 
 |     if( lobbyData[ i ].dwAppID == dwAppID ) | 
 |     { | 
 |       /* This process is lobbied */ | 
 |       TRACE( "Found 0x%08x @ %u\n", dwAppID, i ); | 
 |       *lplpDplData = &lobbyData[ i ]; | 
 |       return TRUE; | 
 |     } | 
 |   } | 
 |  | 
 |   return FALSE; | 
 | } | 
 |  | 
 | /* Reserve a spot for the new application. TRUE means success and FALSE failure.  */ | 
 | BOOL DPLAYX_CreateLobbyApplication( DWORD dwAppID ) | 
 | { | 
 |   UINT i; | 
 |  | 
 |   /* 0 is the marker for unused application data slots */ | 
 |   if( dwAppID == 0 ) | 
 |   { | 
 |     return FALSE; | 
 |   } | 
 |  | 
 |   DPLAYX_AcquireSemaphore(); | 
 |  | 
 |   /* Find an empty space in the list and insert the data */ | 
 |   for( i=0; i < numSupportedLobbies; i++ ) | 
 |   { | 
 |     if( lobbyData[ i ].dwAppID == 0 ) | 
 |     { | 
 |       /* This process is now lobbied */ | 
 |       TRACE( "Setting lobbyData[%u] for (0x%08x,0x%08x)\n", | 
 |               i, dwAppID, GetCurrentProcessId() ); | 
 |  | 
 |       lobbyData[ i ].dwAppID              = dwAppID; | 
 |       lobbyData[ i ].dwAppLaunchedFromID  = GetCurrentProcessId(); | 
 |  | 
 |       /* FIXME: Where is the best place for this? In interface or here? */ | 
 |       lobbyData[ i ].hInformOnAppStart = 0; | 
 |       lobbyData[ i ].hInformOnAppDeath = 0; | 
 |       lobbyData[ i ].hInformOnSettingRead = 0; | 
 |  | 
 |       DPLAYX_ReleaseSemaphore(); | 
 |       return TRUE; | 
 |     } | 
 |   } | 
 |  | 
 |   ERR( "No empty lobbies\n" ); | 
 |  | 
 |   DPLAYX_ReleaseSemaphore(); | 
 |   return FALSE; | 
 | } | 
 |  | 
 | BOOL DPLAYX_SetLobbyHandles( DWORD dwAppID, | 
 |                              HANDLE hStart, HANDLE hDeath, HANDLE hConnRead ) | 
 | { | 
 |   LPDPLAYX_LOBBYDATA lpLData; | 
 |  | 
 |   /* Need to explicitly give lobby application. Can't set for yourself */ | 
 |   if( dwAppID == 0 ) | 
 |   { | 
 |     return FALSE; | 
 |   } | 
 |  | 
 |   DPLAYX_AcquireSemaphore(); | 
 |  | 
 |   if( !DPLAYX_IsAppIdLobbied( dwAppID, &lpLData ) ) | 
 |   { | 
 |     DPLAYX_ReleaseSemaphore(); | 
 |     return FALSE; | 
 |   } | 
 |  | 
 |   lpLData->hInformOnAppStart    = hStart; | 
 |   lpLData->hInformOnAppDeath    = hDeath; | 
 |   lpLData->hInformOnSettingRead = hConnRead; | 
 |  | 
 |   DPLAYX_ReleaseSemaphore(); | 
 |  | 
 |   return TRUE; | 
 | } | 
 |  | 
 | static BOOL DPLAYX_GetThisLobbyHandles( LPHANDLE lphStart, | 
 |                                         LPHANDLE lphDeath, | 
 |                                         LPHANDLE lphConnRead, | 
 |                                         BOOL     bClearSetHandles ) | 
 | { | 
 |   LPDPLAYX_LOBBYDATA lpLData; | 
 |  | 
 |   DPLAYX_AcquireSemaphore(); | 
 |  | 
 |   if( !DPLAYX_IsAppIdLobbied( 0, &lpLData ) ) | 
 |   { | 
 |     DPLAYX_ReleaseSemaphore(); | 
 |     return FALSE; | 
 |   } | 
 |  | 
 |   if( lphStart != NULL ) | 
 |   { | 
 |     if( lpLData->hInformOnAppStart == 0 ) | 
 |     { | 
 |       DPLAYX_ReleaseSemaphore(); | 
 |       return FALSE; | 
 |     } | 
 |  | 
 |     *lphStart = lpLData->hInformOnAppStart; | 
 |  | 
 |     if( bClearSetHandles ) | 
 |     { | 
 |       CloseHandle( lpLData->hInformOnAppStart ); | 
 |       lpLData->hInformOnAppStart = 0; | 
 |     } | 
 |   } | 
 |  | 
 |   if( lphDeath != NULL ) | 
 |   { | 
 |     if( lpLData->hInformOnAppDeath == 0 ) | 
 |     { | 
 |       DPLAYX_ReleaseSemaphore(); | 
 |       return FALSE; | 
 |     } | 
 |  | 
 |     *lphDeath = lpLData->hInformOnAppDeath; | 
 |  | 
 |     if( bClearSetHandles ) | 
 |     { | 
 |       CloseHandle( lpLData->hInformOnAppDeath ); | 
 |       lpLData->hInformOnAppDeath = 0; | 
 |     } | 
 |   } | 
 |  | 
 |   if( lphConnRead != NULL ) | 
 |   { | 
 |     if( lpLData->hInformOnSettingRead == 0 ) | 
 |     { | 
 |       DPLAYX_ReleaseSemaphore(); | 
 |       return FALSE; | 
 |     } | 
 |  | 
 |     *lphConnRead = lpLData->hInformOnSettingRead; | 
 |  | 
 |     if( bClearSetHandles ) | 
 |     { | 
 |       CloseHandle( lpLData->hInformOnSettingRead ); | 
 |       lpLData->hInformOnSettingRead = 0; | 
 |     } | 
 |   } | 
 |  | 
 |   DPLAYX_ReleaseSemaphore(); | 
 |  | 
 |   return TRUE; | 
 | } | 
 |  | 
 | /*************************************************************************** | 
 |  * Called to initialize the global data. This will only be used on the | 
 |  * loading of the dll | 
 |  ***************************************************************************/ | 
 | BOOL DPLAYX_ConstructData(void) | 
 | { | 
 |   SECURITY_ATTRIBUTES s_attrib; | 
 |   BOOL                bInitializeSharedMemory = FALSE; | 
 |   LPVOID              lpDesiredMemoryMapStart = (LPVOID)0x50000000; | 
 |   HANDLE              hInformOnStart; | 
 |  | 
 |   TRACE( "DPLAYX dll loaded - construct called\n" ); | 
 |  | 
 |   /* Create a semaphore to block access to DPLAYX global data structs */ | 
 |  | 
 |   s_attrib.bInheritHandle       = TRUE; | 
 |   s_attrib.lpSecurityDescriptor = NULL; | 
 |   s_attrib.nLength              = sizeof(s_attrib); | 
 |  | 
 |   hDplayxSema = CreateSemaphoreA( &s_attrib, 0, 1, lpszDplayxSemaName ); | 
 |  | 
 |   /* First instance creates the semaphore. Others just use it */ | 
 |   if( GetLastError() == ERROR_SUCCESS ) | 
 |   { | 
 |     TRACE( "Semaphore %p created\n", hDplayxSema ); | 
 |  | 
 |     /* The semaphore creator will also build the shared memory */ | 
 |     bInitializeSharedMemory = TRUE; | 
 |   } | 
 |   else if ( GetLastError() == ERROR_ALREADY_EXISTS ) | 
 |   { | 
 |     TRACE( "Found semaphore handle %p\n", hDplayxSema ); | 
 |     DPLAYX_AcquireSemaphore(); | 
 |   } | 
 |   else | 
 |   { | 
 |     ERR( ": semaphore error %d\n", GetLastError() ); | 
 |     return FALSE; | 
 |   } | 
 |  | 
 |   SetLastError( ERROR_SUCCESS ); | 
 |  | 
 |   hDplayxSharedMem = CreateFileMappingA( INVALID_HANDLE_VALUE, | 
 |                                          &s_attrib, | 
 |                                          PAGE_READWRITE | SEC_COMMIT, | 
 |                                          0, | 
 |                                          dwTotalSharedSize, | 
 |                                          lpszDplayxFileMapping ); | 
 |  | 
 |   if( GetLastError() == ERROR_SUCCESS ) | 
 |   { | 
 |     TRACE( "File mapped %p created\n", hDplayxSharedMem ); | 
 |   } | 
 |   else if ( GetLastError() == ERROR_ALREADY_EXISTS ) | 
 |   { | 
 |     TRACE( "Found FileMapping handle %p\n", hDplayxSharedMem ); | 
 |   } | 
 |   else | 
 |   { | 
 |     ERR( ": unable to create shared memory (%d)\n", GetLastError() ); | 
 |     DPLAYX_ReleaseSemaphore(); | 
 |     return FALSE; | 
 |   } | 
 |  | 
 |   lpSharedStaticData = MapViewOfFileEx( hDplayxSharedMem, | 
 |                                         FILE_MAP_WRITE, | 
 |                                         0, 0, 0, lpDesiredMemoryMapStart ); | 
 |  | 
 |   if( lpSharedStaticData == NULL ) | 
 |   { | 
 |     ERR( ": unable to map static data into process memory space (%d)\n", | 
 |          GetLastError() ); | 
 |     DPLAYX_ReleaseSemaphore(); | 
 |     return FALSE; | 
 |   } | 
 |   else | 
 |   { | 
 |     if( lpDesiredMemoryMapStart == lpSharedStaticData ) | 
 |     { | 
 |       TRACE( "File mapped to %p\n", lpSharedStaticData ); | 
 |     } | 
 |     else | 
 |     { | 
 |       /* Presently the shared data structures use pointers. If the | 
 |        * files are not mapped into the same area, the pointers will no | 
 |        * longer make any sense :( | 
 |        * FIXME: In the future make the shared data structures have some | 
 |        *        sort of fixup to make them independent between data spaces. | 
 |        *        This will also require a rework of the session data stuff. | 
 |        */ | 
 |       ERR( "File mapped to %p (not %p). Expect failure\n", | 
 |             lpSharedStaticData, lpDesiredMemoryMapStart ); | 
 |     } | 
 |   } | 
 |  | 
 |   /* Dynamic area starts just after the static area */ | 
 |   lpMemArea = (LPVOID)((BYTE*)lpSharedStaticData + dwStaticSharedSize); | 
 |  | 
 |   /* FIXME: Crude hack */ | 
 |   lobbyData   = lpSharedStaticData; | 
 |   sessionData = (DPSESSIONDESC2*)((BYTE*)lpSharedStaticData + (dwStaticSharedSize/2)); | 
 |  | 
 |   /* Initialize shared data segments. */ | 
 |   if( bInitializeSharedMemory ) | 
 |   { | 
 |     UINT i; | 
 |  | 
 |     TRACE( "Initializing shared memory\n" ); | 
 |  | 
 |     /* Set all lobbies to be "empty" */ | 
 |     for( i=0; i < numSupportedLobbies; i++ ) | 
 |     { | 
 |       DPLAYX_InitializeLobbyDataEntry( &lobbyData[ i ] ); | 
 |     } | 
 |  | 
 |     /* Set all sessions to be "empty" */ | 
 |     for( i=0; i < numSupportedSessions; i++ ) | 
 |     { | 
 |       sessionData[i].dwSize = 0; | 
 |     } | 
 |  | 
 |     /* Zero out the dynamic area */ | 
 |     ZeroMemory( lpMemArea, dwDynamicSharedSize ); | 
 |  | 
 |     /* Just for fun sync the whole data area */ | 
 |     FlushViewOfFile( lpSharedStaticData, dwTotalSharedSize ); | 
 |   } | 
 |  | 
 |   DPLAYX_ReleaseSemaphore(); | 
 |  | 
 |   /* Everything was created correctly. Signal the lobby client that | 
 |    * we started up correctly | 
 |    */ | 
 |   if( DPLAYX_GetThisLobbyHandles( &hInformOnStart, NULL, NULL, FALSE ) && | 
 |       hInformOnStart | 
 |     ) | 
 |   { | 
 |     BOOL bSuccess; | 
 |     bSuccess = SetEvent( hInformOnStart ); | 
 |     TRACE( "Signalling lobby app start event %p %s\n", | 
 |              hInformOnStart, bSuccess ? "succeed" : "failed" ); | 
 |  | 
 |     /* Close out handle */ | 
 |     DPLAYX_GetThisLobbyHandles( &hInformOnStart, NULL, NULL, TRUE ); | 
 |   } | 
 |  | 
 |   return TRUE; | 
 | } | 
 |  | 
 | /*************************************************************************** | 
 |  * Called to destroy all global data. This will only be used on the | 
 |  * unloading of the dll | 
 |  ***************************************************************************/ | 
 | BOOL DPLAYX_DestructData(void) | 
 | { | 
 |   HANDLE hInformOnDeath; | 
 |  | 
 |   TRACE( "DPLAYX dll unloaded - destruct called\n" ); | 
 |  | 
 |   /* If required, inform that this app is dying */ | 
 |   if( DPLAYX_GetThisLobbyHandles( NULL, &hInformOnDeath, NULL, FALSE ) && | 
 |       hInformOnDeath | 
 |     ) | 
 |   { | 
 |     BOOL bSuccess; | 
 |     bSuccess = SetEvent( hInformOnDeath ); | 
 |     TRACE( "Signalling lobby app death event %p %s\n", | 
 |              hInformOnDeath, bSuccess ? "succeed" : "failed" ); | 
 |  | 
 |     /* Close out handle */ | 
 |     DPLAYX_GetThisLobbyHandles( NULL, &hInformOnDeath, NULL, TRUE ); | 
 |   } | 
 |  | 
 |   /* DO CLEAN UP (LAST) */ | 
 |  | 
 |   /* Delete the semaphore */ | 
 |   CloseHandle( hDplayxSema ); | 
 |  | 
 |   /* Delete shared memory file mapping */ | 
 |   UnmapViewOfFile( lpSharedStaticData ); | 
 |   CloseHandle( hDplayxSharedMem ); | 
 |  | 
 |   return FALSE; | 
 | } | 
 |  | 
 |  | 
 | /* Assumption: Enough contiguous space was allocated at dest */ | 
 | static void DPLAYX_CopyConnStructA( LPDPLCONNECTION dest, const DPLCONNECTION *src ) | 
 | { | 
 |   BYTE* lpStartOfFreeSpace; | 
 |  | 
 |   *dest = *src; | 
 |  | 
 |   lpStartOfFreeSpace = ((BYTE*)dest) + sizeof( DPLCONNECTION ); | 
 |  | 
 |   /* Copy the LPDPSESSIONDESC2 structure if it exists */ | 
 |   if( src->lpSessionDesc ) | 
 |   { | 
 |     dest->lpSessionDesc = (LPDPSESSIONDESC2)lpStartOfFreeSpace; | 
 |     lpStartOfFreeSpace += sizeof( DPSESSIONDESC2 ); | 
 |     *dest->lpSessionDesc = *src->lpSessionDesc; | 
 |  | 
 |     /* Session names may or may not exist */ | 
 |     if( src->lpSessionDesc->u1.lpszSessionNameA ) | 
 |     { | 
 |       strcpy( (LPSTR)lpStartOfFreeSpace, src->lpSessionDesc->u1.lpszSessionNameA ); | 
 |       dest->lpSessionDesc->u1.lpszSessionNameA = (LPSTR)lpStartOfFreeSpace; | 
 |       lpStartOfFreeSpace += | 
 |         strlen( dest->lpSessionDesc->u1.lpszSessionNameA ) + 1; | 
 |     } | 
 |  | 
 |     if( src->lpSessionDesc->u2.lpszPasswordA ) | 
 |     { | 
 |       strcpy( (LPSTR)lpStartOfFreeSpace, src->lpSessionDesc->u2.lpszPasswordA ); | 
 |       dest->lpSessionDesc->u2.lpszPasswordA = (LPSTR)lpStartOfFreeSpace; | 
 |       lpStartOfFreeSpace += | 
 |         strlen( dest->lpSessionDesc->u2.lpszPasswordA ) + 1; | 
 |     } | 
 |   } | 
 |  | 
 |   /* DPNAME structure is optional */ | 
 |   if( src->lpPlayerName ) | 
 |   { | 
 |     dest->lpPlayerName = (LPDPNAME)lpStartOfFreeSpace; | 
 |     lpStartOfFreeSpace += sizeof( DPNAME ); | 
 |     *dest->lpPlayerName = *src->lpPlayerName; | 
 |  | 
 |     if( src->lpPlayerName->u1.lpszShortNameA ) | 
 |     { | 
 |       strcpy( (LPSTR)lpStartOfFreeSpace, src->lpPlayerName->u1.lpszShortNameA ); | 
 |       dest->lpPlayerName->u1.lpszShortNameA = (LPSTR)lpStartOfFreeSpace; | 
 |       lpStartOfFreeSpace += | 
 |         strlen( dest->lpPlayerName->u1.lpszShortNameA ) + 1; | 
 |     } | 
 |  | 
 |     if( src->lpPlayerName->u2.lpszLongNameA ) | 
 |     { | 
 |       strcpy( (LPSTR)lpStartOfFreeSpace, src->lpPlayerName->u2.lpszLongNameA ); | 
 |       dest->lpPlayerName->u2.lpszLongNameA = (LPSTR)lpStartOfFreeSpace; | 
 |       lpStartOfFreeSpace += | 
 |         strlen( (LPSTR)dest->lpPlayerName->u2.lpszLongName ) + 1 ; | 
 |     } | 
 |  | 
 |   } | 
 |  | 
 |   /* Copy address if it exists */ | 
 |   if( src->lpAddress ) | 
 |   { | 
 |     dest->lpAddress = lpStartOfFreeSpace; | 
 |     CopyMemory( lpStartOfFreeSpace, src->lpAddress, src->dwAddressSize ); | 
 |     /* No need to advance lpStartOfFreeSpace as there is no more "dynamic" data */ | 
 |   } | 
 | } | 
 |  | 
 | /* Assumption: Enough contiguous space was allocated at dest */ | 
 | static void DPLAYX_CopyConnStructW( LPDPLCONNECTION dest, const DPLCONNECTION *src ) | 
 | { | 
 |   BYTE*              lpStartOfFreeSpace; | 
 |  | 
 |   *dest = *src; | 
 |  | 
 |   lpStartOfFreeSpace = ( (BYTE*)dest) + sizeof( DPLCONNECTION ); | 
 |  | 
 |   /* Copy the LPDPSESSIONDESC2 structure if it exists */ | 
 |   if( src->lpSessionDesc ) | 
 |   { | 
 |     dest->lpSessionDesc = (LPDPSESSIONDESC2)lpStartOfFreeSpace; | 
 |     lpStartOfFreeSpace += sizeof( DPSESSIONDESC2 ); | 
 |     *dest->lpSessionDesc = *src->lpSessionDesc; | 
 |  | 
 |     /* Session names may or may not exist */ | 
 |     if( src->lpSessionDesc->u1.lpszSessionName ) | 
 |     { | 
 |       strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpSessionDesc->u1.lpszSessionName ); | 
 |       dest->lpSessionDesc->u1.lpszSessionName = (LPWSTR)lpStartOfFreeSpace; | 
 |       lpStartOfFreeSpace +=  sizeof(WCHAR) * | 
 |         ( strlenW( dest->lpSessionDesc->u1.lpszSessionName ) + 1 ); | 
 |     } | 
 |  | 
 |     if( src->lpSessionDesc->u2.lpszPassword ) | 
 |     { | 
 |       strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpSessionDesc->u2.lpszPassword ); | 
 |       dest->lpSessionDesc->u2.lpszPassword = (LPWSTR)lpStartOfFreeSpace; | 
 |       lpStartOfFreeSpace +=  sizeof(WCHAR) * | 
 |         ( strlenW( dest->lpSessionDesc->u2.lpszPassword ) + 1 ); | 
 |     } | 
 |   } | 
 |  | 
 |   /* DPNAME structure is optional */ | 
 |   if( src->lpPlayerName ) | 
 |   { | 
 |     dest->lpPlayerName = (LPDPNAME)lpStartOfFreeSpace; | 
 |     lpStartOfFreeSpace += sizeof( DPNAME ); | 
 |     *dest->lpPlayerName = *src->lpPlayerName; | 
 |  | 
 |     if( src->lpPlayerName->u1.lpszShortName ) | 
 |     { | 
 |       strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpPlayerName->u1.lpszShortName ); | 
 |       dest->lpPlayerName->u1.lpszShortName = (LPWSTR)lpStartOfFreeSpace; | 
 |       lpStartOfFreeSpace +=  sizeof(WCHAR) * | 
 |         ( strlenW( dest->lpPlayerName->u1.lpszShortName ) + 1 ); | 
 |     } | 
 |  | 
 |     if( src->lpPlayerName->u2.lpszLongName ) | 
 |     { | 
 |       strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpPlayerName->u2.lpszLongName ); | 
 |       dest->lpPlayerName->u2.lpszLongName = (LPWSTR)lpStartOfFreeSpace; | 
 |       lpStartOfFreeSpace +=  sizeof(WCHAR) * | 
 |         ( strlenW( dest->lpPlayerName->u2.lpszLongName ) + 1 ); | 
 |     } | 
 |  | 
 |   } | 
 |  | 
 |   /* Copy address if it exists */ | 
 |   if( src->lpAddress ) | 
 |   { | 
 |     dest->lpAddress = lpStartOfFreeSpace; | 
 |     CopyMemory( lpStartOfFreeSpace, src->lpAddress, src->dwAddressSize ); | 
 |     /* No need to advance lpStartOfFreeSpace as there is no more "dynamic" data */ | 
 |   } | 
 |  | 
 | } | 
 |  | 
 | static DWORD DPLAYX_SizeOfLobbyDataA( const DPLCONNECTION *lpConn ) | 
 | { | 
 |   DWORD dwTotalSize = sizeof( DPLCONNECTION ); | 
 |  | 
 |   /* Just a safety check */ | 
 |   if( lpConn == NULL ) | 
 |   { | 
 |     ERR( "lpConn is NULL\n" ); | 
 |     return 0; | 
 |   } | 
 |  | 
 |   if( lpConn->lpSessionDesc != NULL ) | 
 |   { | 
 |     dwTotalSize += sizeof( DPSESSIONDESC2 ); | 
 |  | 
 |     if( lpConn->lpSessionDesc->u1.lpszSessionNameA ) | 
 |     { | 
 |       dwTotalSize += strlen( lpConn->lpSessionDesc->u1.lpszSessionNameA ) + 1; | 
 |     } | 
 |  | 
 |     if( lpConn->lpSessionDesc->u2.lpszPasswordA ) | 
 |     { | 
 |       dwTotalSize += strlen( lpConn->lpSessionDesc->u2.lpszPasswordA ) + 1; | 
 |     } | 
 |   } | 
 |  | 
 |   if( lpConn->lpPlayerName != NULL ) | 
 |   { | 
 |     dwTotalSize += sizeof( DPNAME ); | 
 |  | 
 |     if( lpConn->lpPlayerName->u1.lpszShortNameA ) | 
 |     { | 
 |       dwTotalSize += strlen( lpConn->lpPlayerName->u1.lpszShortNameA ) + 1; | 
 |     } | 
 |  | 
 |     if( lpConn->lpPlayerName->u2.lpszLongNameA ) | 
 |     { | 
 |       dwTotalSize += strlen( lpConn->lpPlayerName->u2.lpszLongNameA ) + 1; | 
 |     } | 
 |  | 
 |   } | 
 |  | 
 |   dwTotalSize += lpConn->dwAddressSize; | 
 |  | 
 |   return dwTotalSize; | 
 | } | 
 |  | 
 | static DWORD DPLAYX_SizeOfLobbyDataW( const DPLCONNECTION *lpConn ) | 
 | { | 
 |   DWORD dwTotalSize = sizeof( DPLCONNECTION ); | 
 |  | 
 |   /* Just a safety check */ | 
 |   if( lpConn == NULL ) | 
 |   { | 
 |     ERR( "lpConn is NULL\n" ); | 
 |     return 0; | 
 |   } | 
 |  | 
 |   if( lpConn->lpSessionDesc != NULL ) | 
 |   { | 
 |     dwTotalSize += sizeof( DPSESSIONDESC2 ); | 
 |  | 
 |     if( lpConn->lpSessionDesc->u1.lpszSessionName ) | 
 |     { | 
 |       dwTotalSize += sizeof( WCHAR ) * | 
 |         ( strlenW( lpConn->lpSessionDesc->u1.lpszSessionName ) + 1 ); | 
 |     } | 
 |  | 
 |     if( lpConn->lpSessionDesc->u2.lpszPassword ) | 
 |     { | 
 |       dwTotalSize += sizeof( WCHAR ) * | 
 |         ( strlenW( lpConn->lpSessionDesc->u2.lpszPassword ) + 1 ); | 
 |     } | 
 |   } | 
 |  | 
 |   if( lpConn->lpPlayerName != NULL ) | 
 |   { | 
 |     dwTotalSize += sizeof( DPNAME ); | 
 |  | 
 |     if( lpConn->lpPlayerName->u1.lpszShortName ) | 
 |     { | 
 |       dwTotalSize += sizeof( WCHAR ) * | 
 |         ( strlenW( lpConn->lpPlayerName->u1.lpszShortName ) + 1 ); | 
 |     } | 
 |  | 
 |     if( lpConn->lpPlayerName->u2.lpszLongName ) | 
 |     { | 
 |       dwTotalSize += sizeof( WCHAR ) * | 
 |         ( strlenW( lpConn->lpPlayerName->u2.lpszLongName ) + 1 ); | 
 |     } | 
 |  | 
 |   } | 
 |  | 
 |   dwTotalSize += lpConn->dwAddressSize; | 
 |  | 
 |   return dwTotalSize; | 
 | } | 
 |  | 
 | HRESULT DPLAYX_GetConnectionSettingsA | 
 | ( DWORD dwAppID, | 
 |   LPVOID lpData, | 
 |   LPDWORD lpdwDataSize ) | 
 | { | 
 |   LPDPLAYX_LOBBYDATA lpDplData; | 
 |   DWORD              dwRequiredDataSize = 0; | 
 |   HANDLE             hInformOnSettingRead; | 
 |  | 
 |   DPLAYX_AcquireSemaphore(); | 
 |  | 
 |   if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) ) | 
 |   { | 
 |     DPLAYX_ReleaseSemaphore(); | 
 |  | 
 |     TRACE( "Application 0x%08x is not lobbied\n", dwAppID ); | 
 |     return DPERR_NOTLOBBIED; | 
 |   } | 
 |  | 
 |   dwRequiredDataSize = DPLAYX_SizeOfLobbyDataA( lpDplData->lpConn ); | 
 |  | 
 |   /* Do they want to know the required buffer size or is the provided buffer | 
 |    * big enough? | 
 |    */ | 
 |   if ( ( lpData == NULL ) || | 
 |        ( *lpdwDataSize < dwRequiredDataSize ) | 
 |      ) | 
 |   { | 
 |     DPLAYX_ReleaseSemaphore(); | 
 |  | 
 |     *lpdwDataSize = DPLAYX_SizeOfLobbyDataA( lpDplData->lpConn ); | 
 |  | 
 |     return DPERR_BUFFERTOOSMALL; | 
 |   } | 
 |  | 
 |   DPLAYX_CopyConnStructA( lpData, lpDplData->lpConn ); | 
 |  | 
 |   DPLAYX_ReleaseSemaphore(); | 
 |  | 
 |   /* They have gotten the information - signal the event if required */ | 
 |   if( DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, FALSE ) && | 
 |       hInformOnSettingRead | 
 |     ) | 
 |   { | 
 |     BOOL bSuccess; | 
 |     bSuccess = SetEvent( hInformOnSettingRead ); | 
 |     TRACE( "Signalling setting read event %p %s\n", | 
 |              hInformOnSettingRead, bSuccess ? "succeed" : "failed" ); | 
 |  | 
 |     /* Close out handle */ | 
 |     DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, TRUE ); | 
 |   } | 
 |  | 
 |   return DP_OK; | 
 | } | 
 |  | 
 | HRESULT DPLAYX_GetConnectionSettingsW | 
 | ( DWORD dwAppID, | 
 |   LPVOID lpData, | 
 |   LPDWORD lpdwDataSize ) | 
 | { | 
 |   LPDPLAYX_LOBBYDATA lpDplData; | 
 |   DWORD              dwRequiredDataSize = 0; | 
 |   HANDLE             hInformOnSettingRead; | 
 |  | 
 |   DPLAYX_AcquireSemaphore(); | 
 |  | 
 |   if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) ) | 
 |   { | 
 |     DPLAYX_ReleaseSemaphore(); | 
 |     return DPERR_NOTLOBBIED; | 
 |   } | 
 |  | 
 |   dwRequiredDataSize = DPLAYX_SizeOfLobbyDataW( lpDplData->lpConn ); | 
 |  | 
 |   /* Do they want to know the required buffer size or is the provided buffer | 
 |    * big enough? | 
 |    */ | 
 |   if ( ( lpData == NULL ) || | 
 |        ( *lpdwDataSize < dwRequiredDataSize ) | 
 |      ) | 
 |   { | 
 |     DPLAYX_ReleaseSemaphore(); | 
 |  | 
 |     *lpdwDataSize = DPLAYX_SizeOfLobbyDataW( lpDplData->lpConn ); | 
 |  | 
 |     return DPERR_BUFFERTOOSMALL; | 
 |   } | 
 |  | 
 |   DPLAYX_CopyConnStructW( lpData, lpDplData->lpConn ); | 
 |  | 
 |   DPLAYX_ReleaseSemaphore(); | 
 |  | 
 |   /* They have gotten the information - signal the event if required */ | 
 |   if( DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, FALSE ) && | 
 |       hInformOnSettingRead | 
 |     ) | 
 |   { | 
 |     BOOL bSuccess; | 
 |     bSuccess = SetEvent( hInformOnSettingRead ); | 
 |     TRACE( "Signalling setting read event %p %s\n", | 
 |              hInformOnSettingRead, bSuccess ? "succeed" : "failed" ); | 
 |  | 
 |     /* Close out handle */ | 
 |     DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, TRUE ); | 
 |   } | 
 |  | 
 |   return DP_OK; | 
 | } | 
 |  | 
 | /* Store the structure into the shared data structure. Ensure that allocs for | 
 |  * variable length strings come from the shared data structure. | 
 |  * FIXME: We need to free information as well. | 
 |  */ | 
 | HRESULT DPLAYX_SetConnectionSettingsA | 
 | ( DWORD dwFlags, | 
 |   DWORD dwAppID, | 
 |   const DPLCONNECTION *lpConn ) | 
 | { | 
 |   LPDPLAYX_LOBBYDATA lpDplData; | 
 |  | 
 |   /* Parameter check */ | 
 |   if( dwFlags || !lpConn ) | 
 |   { | 
 |     ERR("invalid parameters.\n"); | 
 |     return DPERR_INVALIDPARAMS; | 
 |   } | 
 |  | 
 |   /* Store information */ | 
 |   if(  lpConn->dwSize != sizeof(DPLCONNECTION) ) | 
 |   { | 
 |     ERR(": old/new DPLCONNECTION type? Size=%08x\n", lpConn->dwSize ); | 
 |  | 
 |     return DPERR_INVALIDPARAMS; | 
 |   } | 
 |  | 
 |   DPLAYX_AcquireSemaphore(); | 
 |  | 
 |   if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) ) | 
 |   { | 
 |     DPLAYX_ReleaseSemaphore(); | 
 |  | 
 |     return DPERR_NOTLOBBIED; | 
 |   } | 
 |  | 
 |   if(  (!lpConn->lpSessionDesc ) || | 
 |        ( lpConn->lpSessionDesc->dwSize != sizeof( DPSESSIONDESC2 ) ) | 
 |     ) | 
 |   { | 
 |     DPLAYX_ReleaseSemaphore(); | 
 |  | 
 |     ERR("DPSESSIONDESC passed in? Size=%u\n", | 
 |         lpConn->lpSessionDesc?lpConn->lpSessionDesc->dwSize:0 ); | 
 |  | 
 |     return DPERR_INVALIDPARAMS; | 
 |   } | 
 |  | 
 |   /* Free the existing memory */ | 
 |   DPLAYX_PrivHeapFree( lpDplData->lpConn ); | 
 |  | 
 |   lpDplData->lpConn = DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY, | 
 |                                             DPLAYX_SizeOfLobbyDataA( lpConn ) ); | 
 |  | 
 |   DPLAYX_CopyConnStructA( lpDplData->lpConn, lpConn ); | 
 |  | 
 |  | 
 |   DPLAYX_ReleaseSemaphore(); | 
 |  | 
 |   /* FIXME: Send a message - I think */ | 
 |  | 
 |   return DP_OK; | 
 | } | 
 |  | 
 | /* Store the structure into the shared data structure. Ensure that allocs for | 
 |  * variable length strings come from the shared data structure. | 
 |  * FIXME: We need to free information as well | 
 |  */ | 
 | HRESULT DPLAYX_SetConnectionSettingsW | 
 | ( DWORD dwFlags, | 
 |   DWORD dwAppID, | 
 |   const DPLCONNECTION *lpConn ) | 
 | { | 
 |   LPDPLAYX_LOBBYDATA lpDplData; | 
 |  | 
 |   /* Parameter check */ | 
 |   if( dwFlags || !lpConn ) | 
 |   { | 
 |     ERR("invalid parameters.\n"); | 
 |     return DPERR_INVALIDPARAMS; | 
 |   } | 
 |  | 
 |   /* Store information */ | 
 |   if(  lpConn->dwSize != sizeof(DPLCONNECTION) ) | 
 |   { | 
 |     ERR(": old/new DPLCONNECTION type? Size=%u\n", lpConn->dwSize ); | 
 |  | 
 |     return DPERR_INVALIDPARAMS; | 
 |   } | 
 |  | 
 |   DPLAYX_AcquireSemaphore(); | 
 |  | 
 |   if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) ) | 
 |   { | 
 |     DPLAYX_ReleaseSemaphore(); | 
 |  | 
 |     return DPERR_NOTLOBBIED; | 
 |   } | 
 |  | 
 |   /* Free the existing memory */ | 
 |   DPLAYX_PrivHeapFree( lpDplData->lpConn ); | 
 |  | 
 |   lpDplData->lpConn = DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY, | 
 |                                             DPLAYX_SizeOfLobbyDataW( lpConn ) ); | 
 |  | 
 |   DPLAYX_CopyConnStructW( lpDplData->lpConn, lpConn ); | 
 |  | 
 |  | 
 |   DPLAYX_ReleaseSemaphore(); | 
 |  | 
 |   /* FIXME: Send a message - I think */ | 
 |  | 
 |   return DP_OK; | 
 | } | 
 |  | 
 | BOOL DPLAYX_WaitForConnectionSettings( BOOL bWait ) | 
 | { | 
 |   LPDPLAYX_LOBBYDATA lpLobbyData; | 
 |  | 
 |   DPLAYX_AcquireSemaphore(); | 
 |  | 
 |   if( !DPLAYX_IsAppIdLobbied( 0, &lpLobbyData ) ) | 
 |   { | 
 |     DPLAYX_ReleaseSemaphore(); | 
 |     return FALSE; | 
 |   } | 
 |  | 
 |   lpLobbyData->bWaitForConnectionSettings = bWait; | 
 |  | 
 |   DPLAYX_ReleaseSemaphore(); | 
 |  | 
 |   return TRUE; | 
 | } | 
 |  | 
 | BOOL DPLAYX_AnyLobbiesWaitingForConnSettings(void) | 
 | { | 
 |   UINT i; | 
 |   BOOL bFound = FALSE; | 
 |  | 
 |   DPLAYX_AcquireSemaphore(); | 
 |  | 
 |   for( i=0; i < numSupportedLobbies; i++ ) | 
 |   { | 
 |     if( ( lobbyData[ i ].dwAppID != 0 ) &&            /* lobby initialized */ | 
 |         ( lobbyData[ i ].bWaitForConnectionSettings ) /* Waiting */ | 
 |       ) | 
 |     { | 
 |       bFound = TRUE; | 
 |       break; | 
 |     } | 
 |   } | 
 |  | 
 |   DPLAYX_ReleaseSemaphore(); | 
 |  | 
 |   return bFound; | 
 | } | 
 |  | 
 | BOOL DPLAYX_SetLobbyMsgThreadId( DWORD dwAppId, DWORD dwThreadId ) | 
 | { | 
 |   LPDPLAYX_LOBBYDATA lpLobbyData; | 
 |  | 
 |   DPLAYX_AcquireSemaphore(); | 
 |  | 
 |   if( !DPLAYX_IsAppIdLobbied( dwAppId, &lpLobbyData ) ) | 
 |   { | 
 |     DPLAYX_ReleaseSemaphore(); | 
 |     return FALSE; | 
 |   } | 
 |  | 
 |   lpLobbyData->dwLobbyMsgThreadId = dwThreadId; | 
 |  | 
 |   DPLAYX_ReleaseSemaphore(); | 
 |  | 
 |   return TRUE; | 
 | } | 
 |  | 
 | /* NOTE: This is potentially not thread safe. You are not guaranteed to end up | 
 |          with the correct string printed in the case where the HRESULT is not | 
 |          known. You will just get the last hr passed in. This can change | 
 |          over time if this method is used a lot :) */ | 
 | LPCSTR DPLAYX_HresultToString(HRESULT hr) | 
 | { | 
 |   static char szTempStr[12]; | 
 |  | 
 |   switch (hr) | 
 |   { | 
 |     case DP_OK: | 
 |       return "DP_OK"; | 
 |     case DPERR_ALREADYINITIALIZED: | 
 |       return "DPERR_ALREADYINITIALIZED"; | 
 |     case DPERR_ACCESSDENIED: | 
 |       return "DPERR_ACCESSDENIED"; | 
 |     case DPERR_ACTIVEPLAYERS: | 
 |       return "DPERR_ACTIVEPLAYERS"; | 
 |     case DPERR_BUFFERTOOSMALL: | 
 |       return "DPERR_BUFFERTOOSMALL"; | 
 |     case DPERR_CANTADDPLAYER: | 
 |       return "DPERR_CANTADDPLAYER"; | 
 |     case DPERR_CANTCREATEGROUP: | 
 |       return "DPERR_CANTCREATEGROUP"; | 
 |     case DPERR_CANTCREATEPLAYER: | 
 |       return "DPERR_CANTCREATEPLAYER"; | 
 |     case DPERR_CANTCREATESESSION: | 
 |       return "DPERR_CANTCREATESESSION"; | 
 |     case DPERR_CAPSNOTAVAILABLEYET: | 
 |       return "DPERR_CAPSNOTAVAILABLEYET"; | 
 |     case DPERR_EXCEPTION: | 
 |       return "DPERR_EXCEPTION"; | 
 |     case DPERR_GENERIC: | 
 |       return "DPERR_GENERIC"; | 
 |     case DPERR_INVALIDFLAGS: | 
 |       return "DPERR_INVALIDFLAGS"; | 
 |     case DPERR_INVALIDOBJECT: | 
 |       return "DPERR_INVALIDOBJECT"; | 
 |     case DPERR_INVALIDPARAMS: | 
 |       return "DPERR_INVALIDPARAMS"; | 
 |     case DPERR_INVALIDPLAYER: | 
 |       return "DPERR_INVALIDPLAYER"; | 
 |     case DPERR_INVALIDGROUP: | 
 |       return "DPERR_INVALIDGROUP"; | 
 |     case DPERR_NOCAPS: | 
 |       return "DPERR_NOCAPS"; | 
 |     case DPERR_NOCONNECTION: | 
 |       return "DPERR_NOCONNECTION"; | 
 |     case DPERR_OUTOFMEMORY: | 
 |       return "DPERR_OUTOFMEMORY"; | 
 |     case DPERR_NOMESSAGES: | 
 |       return "DPERR_NOMESSAGES"; | 
 |     case DPERR_NONAMESERVERFOUND: | 
 |       return "DPERR_NONAMESERVERFOUND"; | 
 |     case DPERR_NOPLAYERS: | 
 |       return "DPERR_NOPLAYERS"; | 
 |     case DPERR_NOSESSIONS: | 
 |       return "DPERR_NOSESSIONS"; | 
 |     case DPERR_PENDING: | 
 |       return "DPERR_PENDING"; | 
 |     case DPERR_SENDTOOBIG: | 
 |       return "DPERR_SENDTOOBIG"; | 
 |     case DPERR_TIMEOUT: | 
 |       return "DPERR_TIMEOUT"; | 
 |     case DPERR_UNAVAILABLE: | 
 |       return "DPERR_UNAVAILABLE"; | 
 |     case DPERR_UNSUPPORTED: | 
 |       return "DPERR_UNSUPPORTED"; | 
 |     case DPERR_BUSY: | 
 |       return "DPERR_BUSY"; | 
 |     case DPERR_USERCANCEL: | 
 |       return "DPERR_USERCANCEL"; | 
 |     case DPERR_NOINTERFACE: | 
 |       return "DPERR_NOINTERFACE"; | 
 |     case DPERR_CANNOTCREATESERVER: | 
 |       return "DPERR_CANNOTCREATESERVER"; | 
 |     case DPERR_PLAYERLOST: | 
 |       return "DPERR_PLAYERLOST"; | 
 |     case DPERR_SESSIONLOST: | 
 |       return "DPERR_SESSIONLOST"; | 
 |     case DPERR_UNINITIALIZED: | 
 |       return "DPERR_UNINITIALIZED"; | 
 |     case DPERR_NONEWPLAYERS: | 
 |       return "DPERR_NONEWPLAYERS"; | 
 |     case DPERR_INVALIDPASSWORD: | 
 |       return "DPERR_INVALIDPASSWORD"; | 
 |     case DPERR_CONNECTING: | 
 |       return "DPERR_CONNECTING"; | 
 |     case DPERR_CONNECTIONLOST: | 
 |       return "DPERR_CONNECTIONLOST"; | 
 |     case DPERR_UNKNOWNMESSAGE: | 
 |       return "DPERR_UNKNOWNMESSAGE"; | 
 |     case DPERR_CANCELFAILED: | 
 |       return "DPERR_CANCELFAILED"; | 
 |     case DPERR_INVALIDPRIORITY: | 
 |       return "DPERR_INVALIDPRIORITY"; | 
 |     case DPERR_NOTHANDLED: | 
 |       return "DPERR_NOTHANDLED"; | 
 |     case DPERR_CANCELLED: | 
 |       return "DPERR_CANCELLED"; | 
 |     case DPERR_ABORTED: | 
 |       return "DPERR_ABORTED"; | 
 |     case DPERR_BUFFERTOOLARGE: | 
 |       return "DPERR_BUFFERTOOLARGE"; | 
 |     case DPERR_CANTCREATEPROCESS: | 
 |       return "DPERR_CANTCREATEPROCESS"; | 
 |     case DPERR_APPNOTSTARTED: | 
 |       return "DPERR_APPNOTSTARTED"; | 
 |     case DPERR_INVALIDINTERFACE: | 
 |       return "DPERR_INVALIDINTERFACE"; | 
 |     case DPERR_NOSERVICEPROVIDER: | 
 |       return "DPERR_NOSERVICEPROVIDER"; | 
 |     case DPERR_UNKNOWNAPPLICATION: | 
 |       return "DPERR_UNKNOWNAPPLICATION"; | 
 |     case DPERR_NOTLOBBIED: | 
 |       return "DPERR_NOTLOBBIED"; | 
 |     case DPERR_SERVICEPROVIDERLOADED: | 
 |       return "DPERR_SERVICEPROVIDERLOADED"; | 
 |     case DPERR_ALREADYREGISTERED: | 
 |       return "DPERR_ALREADYREGISTERED"; | 
 |     case DPERR_NOTREGISTERED: | 
 |       return "DPERR_NOTREGISTERED"; | 
 |     case DPERR_AUTHENTICATIONFAILED: | 
 |       return "DPERR_AUTHENTICATIONFAILED"; | 
 |     case DPERR_CANTLOADSSPI: | 
 |       return "DPERR_CANTLOADSSPI"; | 
 |     case DPERR_ENCRYPTIONFAILED: | 
 |       return "DPERR_ENCRYPTIONFAILED"; | 
 |     case DPERR_SIGNFAILED: | 
 |       return "DPERR_SIGNFAILED"; | 
 |     case DPERR_CANTLOADSECURITYPACKAGE: | 
 |       return "DPERR_CANTLOADSECURITYPACKAGE"; | 
 |     case DPERR_ENCRYPTIONNOTSUPPORTED: | 
 |       return "DPERR_ENCRYPTIONNOTSUPPORTED"; | 
 |     case DPERR_CANTLOADCAPI: | 
 |       return "DPERR_CANTLOADCAPI"; | 
 |     case DPERR_NOTLOGGEDIN: | 
 |       return "DPERR_NOTLOGGEDIN"; | 
 |     case DPERR_LOGONDENIED: | 
 |       return "DPERR_LOGONDENIED"; | 
 |     default: | 
 |       /* For errors not in the list, return HRESULT as a string | 
 |          This part is not thread safe */ | 
 |       WARN( "Unknown error 0x%08x\n", hr ); | 
 |       wsprintfA( szTempStr, "0x%08lx", hr ); | 
 |       return szTempStr; | 
 |   } | 
 | } |