quartz: Sign-compare warnings fix.
[wine] / dlls / dplayx / dplayx_global.c
1 /* dplayx.dll global data implementation.
2  *
3  * Copyright 1999,2000 - Peter Hunnisett
4  *
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  *
21  * NOTES: 
22  *  o Implementation of all things which are associated with dplay on
23  *    the computer - i.e. shared resources and such. Methods in this
24  *    compilation unit should not call anything outside of this unit
25  *    except base windows services and an interface to start the
26  *    messaging thread.
27  *  o Methods that begin with DPLAYX_ are used for dealing with
28  *    dplayx.dll data which is accessible from all processes.
29  *
30  */
31
32 #include <stdarg.h>
33 #include <string.h>
34
35 #define NONAMELESSUNION
36 #define NONAMELESSSTRUCT
37 #include "wine/debug.h"
38 #include "windef.h"
39 #include "winbase.h"
40 #include "winerror.h"
41 #include "wine/unicode.h"
42
43 #include "wingdi.h"
44 #include "winuser.h"
45
46 #include "dplayx_global.h"
47 #include "dplayx_messages.h" /* For CreateMessageReceptionThread only */
48
49 WINE_DEFAULT_DEBUG_CHANNEL(dplay);
50
51 /* FIXME: Need to do all that fun other dll referencing type of stuff */
52
53 /* Static data for all processes */
54 static const char lpszDplayxSemaName[] = "WINE_DPLAYX_SM";
55 static HANDLE hDplayxSema;
56
57 static const char lpszDplayxFileMapping[] = "WINE_DPLAYX_FM";
58 static HANDLE hDplayxSharedMem;
59
60 static LPVOID lpSharedStaticData = NULL;
61
62
63 #define DPLAYX_AcquireSemaphore() TRACE( "Waiting for DPLAYX semaphore\n" ); \
64                                   WaitForSingleObject( hDplayxSema, INFINITE );\
65                                   TRACE( "Through wait\n" )
66
67 #define DPLAYX_ReleaseSemaphore() ReleaseSemaphore( hDplayxSema, 1, NULL ); \
68                                   TRACE( "DPLAYX Semaphore released\n" ) /* FIXME: Is this correct? */
69
70
71 /* HACK for simple global data right now */
72 #define dwStaticSharedSize (128 * 1024) /* 128 KBytes */
73 #define dwDynamicSharedSize (512 * 1024) /* 512 KBytes */
74 #define dwTotalSharedSize  ( dwStaticSharedSize + dwDynamicSharedSize )
75
76
77 /* FIXME: Is there no easier way? */
78
79 /* Pretend the entire dynamic area is carved up into 512 byte blocks.
80  * Each block has 4 bytes which are 0 unless used */
81 #define dwBlockSize 512
82 #define dwMaxBlock  (dwDynamicSharedSize/dwBlockSize)
83
84 typedef struct
85 {
86   DWORD used;
87   DWORD data[dwBlockSize-sizeof(DWORD)];
88 } DPLAYX_MEM_SLICE;
89
90 static DPLAYX_MEM_SLICE* lpMemArea;
91
92 void DPLAYX_PrivHeapFree( LPVOID addr );
93 void DPLAYX_PrivHeapFree( LPVOID addr )
94 {
95   LPVOID lpAddrStart;
96   DWORD dwBlockUsed;
97
98   /* Handle getting passed a NULL */
99   if( addr == NULL )
100   {
101     return;
102   }
103
104   lpAddrStart = (char*)addr - sizeof(DWORD); /* Find block header */
105   dwBlockUsed =  ((BYTE*)lpAddrStart - (BYTE*)lpMemArea)/dwBlockSize;
106
107   lpMemArea[ dwBlockUsed ].used = 0;
108 }
109
110 /* FIXME: This should be static, but is being used for a hack right now */
111 LPVOID DPLAYX_PrivHeapAlloc( DWORD flags, DWORD size );
112 LPVOID DPLAYX_PrivHeapAlloc( DWORD flags, DWORD size )
113 {
114   LPVOID lpvArea = NULL;
115   UINT   uBlockUsed;
116
117   if( size > (dwBlockSize - sizeof(DWORD)) )
118   {
119     FIXME( "Size exceeded. Request of 0x%08x\n", size );
120     size = dwBlockSize - sizeof(DWORD);
121   }
122
123   /* Find blank area */
124   uBlockUsed = 0;
125   while( ( lpMemArea[ uBlockUsed ].used != 0 ) && ( uBlockUsed <= dwMaxBlock ) ) { uBlockUsed++; }
126
127   if( uBlockUsed <= dwMaxBlock )
128   {
129     /* Set the area used */
130     lpMemArea[ uBlockUsed ].used = 1;
131     lpvArea = lpMemArea[ uBlockUsed ].data;
132   }
133   else
134   {
135     ERR( "No free block found\n" );
136     return NULL;
137   }
138
139   if( flags & HEAP_ZERO_MEMORY )
140   {
141     ZeroMemory( lpvArea, size );
142   }
143
144   return lpvArea;
145 }
146
147 LPSTR DPLAYX_strdupA( DWORD flags, LPCSTR str );
148 LPSTR DPLAYX_strdupA( DWORD flags, LPCSTR str )
149 {
150   LPSTR p = DPLAYX_PrivHeapAlloc( flags, strlen(str) + 1 );
151   if(p) {
152     strcpy( p, str );
153   }
154   return p;
155 }
156
157 LPWSTR DPLAYX_strdupW( DWORD flags, LPCWSTR str );
158 LPWSTR DPLAYX_strdupW( DWORD flags, LPCWSTR str )
159 {
160   INT len = strlenW(str) + 1;
161   LPWSTR p = DPLAYX_PrivHeapAlloc( flags, len * sizeof(WCHAR) );
162   if(p) {
163     strcpyW( p, str );
164   }
165   return p;
166 }
167
168
169 enum { numSupportedLobbies = 32, numSupportedSessions = 32 };
170 typedef struct tagDPLAYX_LOBBYDATA
171 {
172   /* Points to lpConn + block of contiguous extra memory for dynamic parts
173    * of the struct directly following
174    */
175   LPDPLCONNECTION lpConn;
176
177   /* Information for dplobby interfaces */
178   DWORD           dwAppID;
179   DWORD           dwAppLaunchedFromID;
180
181   /* Should this lobby app send messages to creator at important life
182    * stages
183    */
184   HANDLE hInformOnAppStart;
185   HANDLE hInformOnAppDeath;
186   HANDLE hInformOnSettingRead;
187
188   /* Sundries */
189   BOOL bWaitForConnectionSettings;
190   DWORD dwLobbyMsgThreadId;
191
192
193 } DPLAYX_LOBBYDATA, *LPDPLAYX_LOBBYDATA;
194
195 static DPLAYX_LOBBYDATA* lobbyData = NULL;
196 /* static DPLAYX_LOBBYDATA lobbyData[ numSupportedLobbies ]; */
197
198 static DPSESSIONDESC2* sessionData = NULL;
199 /* static DPSESSIONDESC2* sessionData[ numSupportedSessions ]; */
200
201 /* Function prototypes */
202 static DWORD DPLAYX_SizeOfLobbyDataA( const DPLCONNECTION *lpDplData );
203 static DWORD DPLAYX_SizeOfLobbyDataW( const DPLCONNECTION *lpDplData );
204 static void DPLAYX_CopyConnStructA( LPDPLCONNECTION dest, const DPLCONNECTION *src );
205 static void DPLAYX_CopyConnStructW( LPDPLCONNECTION dest, const DPLCONNECTION *src );
206 static BOOL DPLAYX_IsAppIdLobbied( DWORD dwAppId, LPDPLAYX_LOBBYDATA* dplData );
207 static void DPLAYX_InitializeLobbyDataEntry( LPDPLAYX_LOBBYDATA lpData );
208 static BOOL DPLAYX_CopyIntoSessionDesc2A( LPDPSESSIONDESC2  lpSessionDest,
209                                           LPCDPSESSIONDESC2 lpSessionSrc );
210 static BOOL DPLAYX_GetThisLobbyHandles( LPHANDLE lphStart, LPHANDLE lphDeath,
211                                         LPHANDLE lphConnRead, BOOL bClearSetHandles );
212
213
214
215 /***************************************************************************
216  * Called to initialize the global data. This will only be used on the
217  * loading of the dll
218  ***************************************************************************/
219 BOOL DPLAYX_ConstructData(void)
220 {
221   SECURITY_ATTRIBUTES s_attrib;
222   BOOL                bInitializeSharedMemory = FALSE;
223   LPVOID              lpDesiredMemoryMapStart = (LPVOID)0x50000000;
224   HANDLE              hInformOnStart;
225
226   TRACE( "DPLAYX dll loaded - construct called\n" );
227
228   /* Create a semaphore to block access to DPLAYX global data structs */
229
230   s_attrib.bInheritHandle       = TRUE;
231   s_attrib.lpSecurityDescriptor = NULL;
232   s_attrib.nLength              = sizeof(s_attrib);
233
234   hDplayxSema = CreateSemaphoreA( &s_attrib, 0, 1, lpszDplayxSemaName );
235
236   /* First instance creates the semaphore. Others just use it */
237   if( GetLastError() == ERROR_SUCCESS )
238   {
239     TRACE( "Semaphore %p created\n", hDplayxSema );
240
241     /* The semaphore creator will also build the shared memory */
242     bInitializeSharedMemory = TRUE;
243   }
244   else if ( GetLastError() == ERROR_ALREADY_EXISTS )
245   {
246     TRACE( "Found semaphore handle %p\n", hDplayxSema );
247     DPLAYX_AcquireSemaphore();
248   }
249   else
250   {
251     ERR( ": semaphore error %d\n", GetLastError() );
252     return FALSE;
253   }
254
255   SetLastError( ERROR_SUCCESS );
256
257   hDplayxSharedMem = CreateFileMappingA( INVALID_HANDLE_VALUE,
258                                          &s_attrib,
259                                          PAGE_READWRITE | SEC_COMMIT,
260                                          0,
261                                          dwTotalSharedSize,
262                                          lpszDplayxFileMapping );
263
264   if( GetLastError() == ERROR_SUCCESS )
265   {
266     TRACE( "File mapped %p created\n", hDplayxSharedMem );
267   }
268   else if ( GetLastError() == ERROR_ALREADY_EXISTS )
269   {
270     TRACE( "Found FileMapping handle %p\n", hDplayxSharedMem );
271   }
272   else
273   {
274     ERR( ": unable to create shared memory (%d)\n", GetLastError() );
275     DPLAYX_ReleaseSemaphore();
276     return FALSE;
277   }
278
279   lpSharedStaticData = MapViewOfFileEx( hDplayxSharedMem,
280                                         FILE_MAP_WRITE,
281                                         0, 0, 0, lpDesiredMemoryMapStart );
282
283   if( lpSharedStaticData == NULL )
284   {
285     ERR( ": unable to map static data into process memory space (%d)\n",
286          GetLastError() );
287     DPLAYX_ReleaseSemaphore();
288     return FALSE;
289   }
290   else
291   {
292     if( lpDesiredMemoryMapStart == lpSharedStaticData )
293     {
294       TRACE( "File mapped to %p\n", lpSharedStaticData );
295     }
296     else
297     {
298       /* Presently the shared data structures use pointers. If the
299        * files are no mapped into the same area, the pointers will no
300        * longer make any sense :(
301        * FIXME: In the future make the shared data structures have some
302        *        sort of fixup to make them independent between data spaces.
303        *        This will also require a rework of the session data stuff.
304        */
305       ERR( "File mapped to %p (not %p). Expect failure\n",
306             lpSharedStaticData, lpDesiredMemoryMapStart );
307     }
308   }
309
310   /* Dynamic area starts just after the static area */
311   lpMemArea = (LPVOID)((BYTE*)lpSharedStaticData + dwStaticSharedSize);
312
313   /* FIXME: Crude hack */
314   lobbyData   = (DPLAYX_LOBBYDATA*)lpSharedStaticData;
315   sessionData = (DPSESSIONDESC2*)((BYTE*)lpSharedStaticData + (dwStaticSharedSize/2));
316
317   /* Initialize shared data segments. */
318   if( bInitializeSharedMemory )
319   {
320     UINT i;
321
322     TRACE( "Initializing shared memory\n" );
323
324     /* Set all lobbies to be "empty" */
325     for( i=0; i < numSupportedLobbies; i++ )
326     {
327       DPLAYX_InitializeLobbyDataEntry( &lobbyData[ i ] );
328     }
329
330     /* Set all sessions to be "empty" */
331     for( i=0; i < numSupportedSessions; i++ )
332     {
333       sessionData[i].dwSize = 0;
334     }
335
336     /* Zero out the dynamic area */
337     ZeroMemory( lpMemArea, dwDynamicSharedSize );
338
339     /* Just for fun sync the whole data area */
340     FlushViewOfFile( lpSharedStaticData, dwTotalSharedSize );
341   }
342
343   DPLAYX_ReleaseSemaphore();
344
345   /* Everything was created correctly. Signal the lobby client that
346    * we started up correctly
347    */
348   if( DPLAYX_GetThisLobbyHandles( &hInformOnStart, NULL, NULL, FALSE ) &&
349       hInformOnStart
350     )
351   {
352     BOOL bSuccess;
353     bSuccess = SetEvent( hInformOnStart );
354     TRACE( "Signalling lobby app start event %p %s\n",
355              hInformOnStart, bSuccess ? "succeed" : "failed" );
356
357     /* Close out handle */
358     DPLAYX_GetThisLobbyHandles( &hInformOnStart, NULL, NULL, TRUE );
359   }
360
361   return TRUE;
362 }
363
364 /***************************************************************************
365  * Called to destroy all global data. This will only be used on the
366  * unloading of the dll
367  ***************************************************************************/
368 BOOL DPLAYX_DestructData(void)
369 {
370   HANDLE hInformOnDeath;
371
372   TRACE( "DPLAYX dll unloaded - destruct called\n" );
373
374   /* If required, inform that this app is dying */
375   if( DPLAYX_GetThisLobbyHandles( NULL, &hInformOnDeath, NULL, FALSE ) &&
376       hInformOnDeath
377     )
378   {
379     BOOL bSuccess;
380     bSuccess = SetEvent( hInformOnDeath );
381     TRACE( "Signalling lobby app death event %p %s\n",
382              hInformOnDeath, bSuccess ? "succeed" : "failed" );
383
384     /* Close out handle */
385     DPLAYX_GetThisLobbyHandles( NULL, &hInformOnDeath, NULL, TRUE );
386   }
387
388   /* DO CLEAN UP (LAST) */
389
390   /* Delete the semaphore */
391   CloseHandle( hDplayxSema );
392
393   /* Delete shared memory file mapping */
394   UnmapViewOfFile( lpSharedStaticData );
395   CloseHandle( hDplayxSharedMem );
396
397   return FALSE;
398 }
399
400
401 void DPLAYX_InitializeLobbyDataEntry( LPDPLAYX_LOBBYDATA lpData )
402 {
403   ZeroMemory( lpData, sizeof( *lpData ) );
404 }
405
406 /* NOTE: This must be called with the semaphore acquired.
407  * TRUE/FALSE with a pointer to it's data returned. Pointer data is
408  * is only valid if TRUE is returned.
409  */
410 BOOL DPLAYX_IsAppIdLobbied( DWORD dwAppID, LPDPLAYX_LOBBYDATA* lplpDplData )
411 {
412   UINT i;
413
414   *lplpDplData = NULL;
415
416   if( dwAppID == 0 )
417   {
418     dwAppID = GetCurrentProcessId();
419     TRACE( "Translated dwAppID == 0 into 0x%08x\n", dwAppID );
420   }
421
422   for( i=0; i < numSupportedLobbies; i++ )
423   {
424     if( lobbyData[ i ].dwAppID == dwAppID )
425     {
426       /* This process is lobbied */
427       TRACE( "Found 0x%08x @ %u\n", dwAppID, i );
428       *lplpDplData = &lobbyData[ i ];
429       return TRUE;
430     }
431   }
432
433   return FALSE;
434 }
435
436 /* Reserve a spot for the new application. TRUE means success and FALSE failure.  */
437 BOOL DPLAYX_CreateLobbyApplication( DWORD dwAppID )
438 {
439   UINT i;
440
441   /* 0 is the marker for unused application data slots */
442   if( dwAppID == 0 )
443   {
444     return FALSE;
445   }
446
447   DPLAYX_AcquireSemaphore();
448
449   /* Find an empty space in the list and insert the data */
450   for( i=0; i < numSupportedLobbies; i++ )
451   {
452     if( lobbyData[ i ].dwAppID == 0 )
453     {
454       /* This process is now lobbied */
455       TRACE( "Setting lobbyData[%u] for (0x%08x,0x%08x)\n",
456               i, dwAppID, GetCurrentProcessId() );
457
458       lobbyData[ i ].dwAppID              = dwAppID;
459       lobbyData[ i ].dwAppLaunchedFromID  = GetCurrentProcessId();
460
461       /* FIXME: Where is the best place for this? In interface or here? */
462       lobbyData[ i ].hInformOnAppStart = 0;
463       lobbyData[ i ].hInformOnAppDeath = 0;
464       lobbyData[ i ].hInformOnSettingRead = 0;
465
466       DPLAYX_ReleaseSemaphore();
467       return TRUE;
468     }
469   }
470
471   ERR( "No empty lobbies\n" );
472
473   DPLAYX_ReleaseSemaphore();
474   return FALSE;
475 }
476
477 /* I'm not sure when I'm going to need this, but here it is */
478 BOOL DPLAYX_DestroyLobbyApplication( DWORD dwAppID )
479 {
480   UINT i;
481
482   DPLAYX_AcquireSemaphore();
483
484   /* Find an empty space in the list and insert the data */
485   for( i=0; i < numSupportedLobbies; i++ )
486   {
487     if( lobbyData[ i ].dwAppID == dwAppID )
488     {
489       /* FIXME: Should free up anything unused. Tisk tisk :0 */
490       /* Mark this entry unused */
491       TRACE( "Marking lobbyData[%u] unused\n", i );
492       DPLAYX_InitializeLobbyDataEntry( &lobbyData[ i ] );
493
494       DPLAYX_ReleaseSemaphore();
495       return TRUE;
496     }
497   }
498
499   DPLAYX_ReleaseSemaphore();
500   ERR( "Unable to find global entry for application\n" );
501   return FALSE;
502 }
503
504 BOOL DPLAYX_SetLobbyHandles( DWORD dwAppID,
505                              HANDLE hStart, HANDLE hDeath, HANDLE hConnRead )
506 {
507   LPDPLAYX_LOBBYDATA lpLData;
508
509   /* Need to explicitly give lobby application. Can't set for yourself */
510   if( dwAppID == 0 )
511   {
512     return FALSE;
513   }
514
515   DPLAYX_AcquireSemaphore();
516
517   if( !DPLAYX_IsAppIdLobbied( dwAppID, &lpLData ) )
518   {
519     DPLAYX_ReleaseSemaphore();
520     return FALSE;
521   }
522
523   lpLData->hInformOnAppStart    = hStart;
524   lpLData->hInformOnAppDeath    = hDeath;
525   lpLData->hInformOnSettingRead = hConnRead;
526
527   DPLAYX_ReleaseSemaphore();
528
529   return TRUE;
530 }
531
532 static BOOL DPLAYX_GetThisLobbyHandles( LPHANDLE lphStart,
533                                         LPHANDLE lphDeath,
534                                         LPHANDLE lphConnRead,
535                                         BOOL     bClearSetHandles )
536 {
537   LPDPLAYX_LOBBYDATA lpLData;
538
539   DPLAYX_AcquireSemaphore();
540
541   if( !DPLAYX_IsAppIdLobbied( 0, &lpLData ) )
542   {
543     DPLAYX_ReleaseSemaphore();
544     return FALSE;
545   }
546
547   if( lphStart != NULL )
548   {
549     if( lpLData->hInformOnAppStart == 0 )
550     {
551       DPLAYX_ReleaseSemaphore();
552       return FALSE;
553     }
554
555     *lphStart = lpLData->hInformOnAppStart;
556
557     if( bClearSetHandles )
558     {
559       CloseHandle( lpLData->hInformOnAppStart );
560       lpLData->hInformOnAppStart = 0;
561     }
562   }
563
564   if( lphDeath != NULL )
565   {
566     if( lpLData->hInformOnAppDeath == 0 )
567     {
568       DPLAYX_ReleaseSemaphore();
569       return FALSE;
570     }
571
572     *lphDeath = lpLData->hInformOnAppDeath;
573
574     if( bClearSetHandles )
575     {
576       CloseHandle( lpLData->hInformOnAppDeath );
577       lpLData->hInformOnAppDeath = 0;
578     }
579   }
580
581   if( lphConnRead != NULL )
582   {
583     if( lpLData->hInformOnSettingRead == 0 )
584     {
585       DPLAYX_ReleaseSemaphore();
586       return FALSE;
587     }
588
589     *lphConnRead = lpLData->hInformOnSettingRead;
590
591     if( bClearSetHandles )
592     {
593       CloseHandle( lpLData->hInformOnSettingRead );
594       lpLData->hInformOnSettingRead = 0;
595     }
596   }
597
598   DPLAYX_ReleaseSemaphore();
599
600   return TRUE;
601 }
602
603
604 HRESULT DPLAYX_GetConnectionSettingsA
605 ( DWORD dwAppID,
606   LPVOID lpData,
607   LPDWORD lpdwDataSize )
608 {
609   LPDPLAYX_LOBBYDATA lpDplData;
610   DWORD              dwRequiredDataSize = 0;
611   HANDLE             hInformOnSettingRead;
612
613   DPLAYX_AcquireSemaphore();
614
615   if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
616   {
617     DPLAYX_ReleaseSemaphore();
618
619     TRACE( "Application 0x%08x is not lobbied\n", dwAppID );
620     return DPERR_NOTLOBBIED;
621   }
622
623   dwRequiredDataSize = DPLAYX_SizeOfLobbyDataA( lpDplData->lpConn );
624
625   /* Do they want to know the required buffer size or is the provided buffer
626    * big enough?
627    */
628   if ( ( lpData == NULL ) ||
629        ( *lpdwDataSize < dwRequiredDataSize )
630      )
631   {
632     DPLAYX_ReleaseSemaphore();
633
634     *lpdwDataSize = DPLAYX_SizeOfLobbyDataA( lpDplData->lpConn );
635
636     return DPERR_BUFFERTOOSMALL;
637   }
638
639   DPLAYX_CopyConnStructA( (LPDPLCONNECTION)lpData, lpDplData->lpConn );
640
641   DPLAYX_ReleaseSemaphore();
642
643   /* They have gotten the information - signal the event if required */
644   if( DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, FALSE ) &&
645       hInformOnSettingRead
646     )
647   {
648     BOOL bSuccess;
649     bSuccess = SetEvent( hInformOnSettingRead );
650     TRACE( "Signalling setting read event %p %s\n",
651              hInformOnSettingRead, bSuccess ? "succeed" : "failed" );
652
653     /* Close out handle */
654     DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, TRUE );
655   }
656
657   return DP_OK;
658 }
659
660 /* Assumption: Enough contiguous space was allocated at dest */
661 void DPLAYX_CopyConnStructA( LPDPLCONNECTION dest, const DPLCONNECTION *src )
662 {
663   BYTE* lpStartOfFreeSpace;
664
665   *dest = *src;
666
667   lpStartOfFreeSpace = ((BYTE*)dest) + sizeof( DPLCONNECTION );
668
669   /* Copy the LPDPSESSIONDESC2 structure if it exists */
670   if( src->lpSessionDesc )
671   {
672     dest->lpSessionDesc = (LPDPSESSIONDESC2)lpStartOfFreeSpace;
673     lpStartOfFreeSpace += sizeof( DPSESSIONDESC2 );
674     *dest->lpSessionDesc = *src->lpSessionDesc;
675
676     /* Session names may or may not exist */
677     if( src->lpSessionDesc->u1.lpszSessionNameA )
678     {
679       strcpy( (LPSTR)lpStartOfFreeSpace, src->lpSessionDesc->u1.lpszSessionNameA );
680       dest->lpSessionDesc->u1.lpszSessionNameA = (LPSTR)lpStartOfFreeSpace;
681       lpStartOfFreeSpace +=
682         strlen( dest->lpSessionDesc->u1.lpszSessionNameA ) + 1;
683     }
684
685     if( src->lpSessionDesc->u2.lpszPasswordA )
686     {
687       strcpy( (LPSTR)lpStartOfFreeSpace, src->lpSessionDesc->u2.lpszPasswordA );
688       dest->lpSessionDesc->u2.lpszPasswordA = (LPSTR)lpStartOfFreeSpace;
689       lpStartOfFreeSpace +=
690         strlen( dest->lpSessionDesc->u2.lpszPasswordA ) + 1;
691     }
692   }
693
694   /* DPNAME structure is optional */
695   if( src->lpPlayerName )
696   {
697     dest->lpPlayerName = (LPDPNAME)lpStartOfFreeSpace;
698     lpStartOfFreeSpace += sizeof( DPNAME );
699     *dest->lpPlayerName = *src->lpPlayerName;
700
701     if( src->lpPlayerName->u1.lpszShortNameA )
702     {
703       strcpy( (LPSTR)lpStartOfFreeSpace, src->lpPlayerName->u1.lpszShortNameA );
704       dest->lpPlayerName->u1.lpszShortNameA = (LPSTR)lpStartOfFreeSpace;
705       lpStartOfFreeSpace +=
706         strlen( dest->lpPlayerName->u1.lpszShortNameA ) + 1;
707     }
708
709     if( src->lpPlayerName->u2.lpszLongNameA )
710     {
711       strcpy( (LPSTR)lpStartOfFreeSpace, src->lpPlayerName->u2.lpszLongNameA );
712       dest->lpPlayerName->u2.lpszLongNameA = (LPSTR)lpStartOfFreeSpace;
713       lpStartOfFreeSpace +=
714         strlen( (LPSTR)dest->lpPlayerName->u2.lpszLongName ) + 1 ;
715     }
716
717   }
718
719   /* Copy address if it exists */
720   if( src->lpAddress )
721   {
722     dest->lpAddress = (LPVOID)lpStartOfFreeSpace;
723     CopyMemory( lpStartOfFreeSpace, src->lpAddress, src->dwAddressSize );
724     /* No need to advance lpStartOfFreeSpace as there is no more "dynamic" data */
725   }
726 }
727
728 HRESULT DPLAYX_GetConnectionSettingsW
729 ( DWORD dwAppID,
730   LPVOID lpData,
731   LPDWORD lpdwDataSize )
732 {
733   LPDPLAYX_LOBBYDATA lpDplData;
734   DWORD              dwRequiredDataSize = 0;
735   HANDLE             hInformOnSettingRead;
736
737   DPLAYX_AcquireSemaphore();
738
739   if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
740   {
741     DPLAYX_ReleaseSemaphore();
742     return DPERR_NOTLOBBIED;
743   }
744
745   dwRequiredDataSize = DPLAYX_SizeOfLobbyDataW( lpDplData->lpConn );
746
747   /* Do they want to know the required buffer size or is the provided buffer
748    * big enough?
749    */
750   if ( ( lpData == NULL ) ||
751        ( *lpdwDataSize < dwRequiredDataSize )
752      )
753   {
754     DPLAYX_ReleaseSemaphore();
755
756     *lpdwDataSize = DPLAYX_SizeOfLobbyDataW( lpDplData->lpConn );
757
758     return DPERR_BUFFERTOOSMALL;
759   }
760
761   DPLAYX_CopyConnStructW( (LPDPLCONNECTION)lpData, lpDplData->lpConn );
762
763   DPLAYX_ReleaseSemaphore();
764
765   /* They have gotten the information - signal the event if required */
766   if( DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, FALSE ) &&
767       hInformOnSettingRead
768     )
769   {
770     BOOL bSuccess;
771     bSuccess = SetEvent( hInformOnSettingRead );
772     TRACE( "Signalling setting read event %p %s\n",
773              hInformOnSettingRead, bSuccess ? "succeed" : "failed" );
774
775     /* Close out handle */
776     DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, TRUE );
777   }
778
779   return DP_OK;
780 }
781
782 /* Assumption: Enough contiguous space was allocated at dest */
783 void DPLAYX_CopyConnStructW( LPDPLCONNECTION dest, const DPLCONNECTION *src )
784 {
785   BYTE*              lpStartOfFreeSpace;
786
787   *dest = *src;
788
789   lpStartOfFreeSpace = ( (BYTE*)dest) + sizeof( DPLCONNECTION );
790
791   /* Copy the LPDPSESSIONDESC2 structure if it exists */
792   if( src->lpSessionDesc )
793   {
794     dest->lpSessionDesc = (LPDPSESSIONDESC2)lpStartOfFreeSpace;
795     lpStartOfFreeSpace += sizeof( DPSESSIONDESC2 );
796     *dest->lpSessionDesc = *src->lpSessionDesc;
797
798     /* Session names may or may not exist */
799     if( src->lpSessionDesc->u1.lpszSessionName )
800     {
801       strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpSessionDesc->u1.lpszSessionName );
802       dest->lpSessionDesc->u1.lpszSessionName = (LPWSTR)lpStartOfFreeSpace;
803       lpStartOfFreeSpace +=  sizeof(WCHAR) *
804         ( strlenW( dest->lpSessionDesc->u1.lpszSessionName ) + 1 );
805     }
806
807     if( src->lpSessionDesc->u2.lpszPassword )
808     {
809       strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpSessionDesc->u2.lpszPassword );
810       dest->lpSessionDesc->u2.lpszPassword = (LPWSTR)lpStartOfFreeSpace;
811       lpStartOfFreeSpace +=  sizeof(WCHAR) *
812         ( strlenW( dest->lpSessionDesc->u2.lpszPassword ) + 1 );
813     }
814   }
815
816   /* DPNAME structure is optional */
817   if( src->lpPlayerName )
818   {
819     dest->lpPlayerName = (LPDPNAME)lpStartOfFreeSpace;
820     lpStartOfFreeSpace += sizeof( DPNAME );
821     *dest->lpPlayerName = *src->lpPlayerName;
822
823     if( src->lpPlayerName->u1.lpszShortName )
824     {
825       strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpPlayerName->u1.lpszShortName );
826       dest->lpPlayerName->u1.lpszShortName = (LPWSTR)lpStartOfFreeSpace;
827       lpStartOfFreeSpace +=  sizeof(WCHAR) *
828         ( strlenW( dest->lpPlayerName->u1.lpszShortName ) + 1 );
829     }
830
831     if( src->lpPlayerName->u2.lpszLongName )
832     {
833       strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpPlayerName->u2.lpszLongName );
834       dest->lpPlayerName->u2.lpszLongName = (LPWSTR)lpStartOfFreeSpace;
835       lpStartOfFreeSpace +=  sizeof(WCHAR) *
836         ( strlenW( dest->lpPlayerName->u2.lpszLongName ) + 1 );
837     }
838
839   }
840
841   /* Copy address if it exists */
842   if( src->lpAddress )
843   {
844     dest->lpAddress = (LPVOID)lpStartOfFreeSpace;
845     CopyMemory( lpStartOfFreeSpace, src->lpAddress, src->dwAddressSize );
846     /* No need to advance lpStartOfFreeSpace as there is no more "dynamic" data */
847   }
848
849 }
850
851 /* Store the structure into the shared data structure. Ensure that allocs for
852  * variable length strings come from the shared data structure.
853  * FIXME: We need to free information as well.
854  */
855 HRESULT DPLAYX_SetConnectionSettingsA
856 ( DWORD dwFlags,
857   DWORD dwAppID,
858   const DPLCONNECTION *lpConn )
859 {
860   LPDPLAYX_LOBBYDATA lpDplData;
861
862   /* Parameter check */
863   if( dwFlags || !lpConn )
864   {
865     ERR("invalid parameters.\n");
866     return DPERR_INVALIDPARAMS;
867   }
868
869   /* Store information */
870   if(  lpConn->dwSize != sizeof(DPLCONNECTION) )
871   {
872     ERR(": old/new DPLCONNECTION type? Size=%08x\n", lpConn->dwSize );
873
874     return DPERR_INVALIDPARAMS;
875   }
876
877   DPLAYX_AcquireSemaphore();
878
879   if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
880   {
881     DPLAYX_ReleaseSemaphore();
882
883     return DPERR_NOTLOBBIED;
884   }
885
886   if(  (!lpConn->lpSessionDesc ) ||
887        ( lpConn->lpSessionDesc->dwSize != sizeof( DPSESSIONDESC2 ) )
888     )
889   {
890     DPLAYX_ReleaseSemaphore();
891
892     ERR("DPSESSIONDESC passed in? Size=%u\n",
893         lpConn->lpSessionDesc?lpConn->lpSessionDesc->dwSize:0 );
894
895     return DPERR_INVALIDPARAMS;
896   }
897
898   /* Free the existing memory */
899   DPLAYX_PrivHeapFree( lpDplData->lpConn );
900
901   lpDplData->lpConn = DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY,
902                                             DPLAYX_SizeOfLobbyDataA( lpConn ) );
903
904   DPLAYX_CopyConnStructA( lpDplData->lpConn, lpConn );
905
906
907   DPLAYX_ReleaseSemaphore();
908
909   /* FIXME: Send a message - I think */
910
911   return DP_OK;
912 }
913
914 /* Store the structure into the shared data structure. Ensure that allocs for
915  * variable length strings come from the shared data structure.
916  * FIXME: We need to free information as well
917  */
918 HRESULT DPLAYX_SetConnectionSettingsW
919 ( DWORD dwFlags,
920   DWORD dwAppID,
921   const DPLCONNECTION *lpConn )
922 {
923   LPDPLAYX_LOBBYDATA lpDplData;
924
925   /* Parameter check */
926   if( dwFlags || !lpConn )
927   {
928     ERR("invalid parameters.\n");
929     return DPERR_INVALIDPARAMS;
930   }
931
932   /* Store information */
933   if(  lpConn->dwSize != sizeof(DPLCONNECTION) )
934   {
935     ERR(": old/new DPLCONNECTION type? Size=%u\n", lpConn->dwSize );
936
937     return DPERR_INVALIDPARAMS;
938   }
939
940   DPLAYX_AcquireSemaphore();
941
942   if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
943   {
944     DPLAYX_ReleaseSemaphore();
945
946     return DPERR_NOTLOBBIED;
947   }
948
949   /* Free the existing memory */
950   DPLAYX_PrivHeapFree( lpDplData->lpConn );
951
952   lpDplData->lpConn = DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY,
953                                             DPLAYX_SizeOfLobbyDataW( lpConn ) );
954
955   DPLAYX_CopyConnStructW( lpDplData->lpConn, lpConn );
956
957
958   DPLAYX_ReleaseSemaphore();
959
960   /* FIXME: Send a message - I think */
961
962   return DP_OK;
963 }
964
965 DWORD DPLAYX_SizeOfLobbyDataA( const DPLCONNECTION *lpConn )
966 {
967   DWORD dwTotalSize = sizeof( DPLCONNECTION );
968
969   /* Just a safety check */
970   if( lpConn == NULL )
971   {
972     ERR( "lpConn is NULL\n" );
973     return 0;
974   }
975
976   if( lpConn->lpSessionDesc != NULL )
977   {
978     dwTotalSize += sizeof( DPSESSIONDESC2 );
979
980     if( lpConn->lpSessionDesc->u1.lpszSessionNameA )
981     {
982       dwTotalSize += strlen( lpConn->lpSessionDesc->u1.lpszSessionNameA ) + 1;
983     }
984
985     if( lpConn->lpSessionDesc->u2.lpszPasswordA )
986     {
987       dwTotalSize += strlen( lpConn->lpSessionDesc->u2.lpszPasswordA ) + 1;
988     }
989   }
990
991   if( lpConn->lpPlayerName != NULL )
992   {
993     dwTotalSize += sizeof( DPNAME );
994
995     if( lpConn->lpPlayerName->u1.lpszShortNameA )
996     {
997       dwTotalSize += strlen( lpConn->lpPlayerName->u1.lpszShortNameA ) + 1;
998     }
999
1000     if( lpConn->lpPlayerName->u2.lpszLongNameA )
1001     {
1002       dwTotalSize += strlen( lpConn->lpPlayerName->u2.lpszLongNameA ) + 1;
1003     }
1004
1005   }
1006
1007   dwTotalSize += lpConn->dwAddressSize;
1008
1009   return dwTotalSize;
1010 }
1011
1012 DWORD DPLAYX_SizeOfLobbyDataW( const DPLCONNECTION *lpConn )
1013 {
1014   DWORD dwTotalSize = sizeof( DPLCONNECTION );
1015
1016   /* Just a safety check */
1017   if( lpConn == NULL )
1018   {
1019     ERR( "lpConn is NULL\n" );
1020     return 0;
1021   }
1022
1023   if( lpConn->lpSessionDesc != NULL )
1024   {
1025     dwTotalSize += sizeof( DPSESSIONDESC2 );
1026
1027     if( lpConn->lpSessionDesc->u1.lpszSessionName )
1028     {
1029       dwTotalSize += sizeof( WCHAR ) *
1030         ( strlenW( lpConn->lpSessionDesc->u1.lpszSessionName ) + 1 );
1031     }
1032
1033     if( lpConn->lpSessionDesc->u2.lpszPassword )
1034     {
1035       dwTotalSize += sizeof( WCHAR ) *
1036         ( strlenW( lpConn->lpSessionDesc->u2.lpszPassword ) + 1 );
1037     }
1038   }
1039
1040   if( lpConn->lpPlayerName != NULL )
1041   {
1042     dwTotalSize += sizeof( DPNAME );
1043
1044     if( lpConn->lpPlayerName->u1.lpszShortName )
1045     {
1046       dwTotalSize += sizeof( WCHAR ) *
1047         ( strlenW( lpConn->lpPlayerName->u1.lpszShortName ) + 1 );
1048     }
1049
1050     if( lpConn->lpPlayerName->u2.lpszLongName )
1051     {
1052       dwTotalSize += sizeof( WCHAR ) *
1053         ( strlenW( lpConn->lpPlayerName->u2.lpszLongName ) + 1 );
1054     }
1055
1056   }
1057
1058   dwTotalSize += lpConn->dwAddressSize;
1059
1060   return dwTotalSize;
1061 }
1062
1063
1064
1065 static LPDPSESSIONDESC2 DPLAYX_CopyAndAllocateSessionDesc2A( LPCDPSESSIONDESC2 lpSessionSrc )
1066 {
1067    LPDPSESSIONDESC2 lpSessionDest =
1068      HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *lpSessionSrc ) );
1069    DPLAYX_CopyIntoSessionDesc2A( lpSessionDest, lpSessionSrc );
1070
1071    return lpSessionDest;
1072 }
1073
1074 /* Copy an ANSI session desc structure to the given buffer */
1075 BOOL DPLAYX_CopyIntoSessionDesc2A( LPDPSESSIONDESC2  lpSessionDest,
1076                                    LPCDPSESSIONDESC2 lpSessionSrc )
1077 {
1078   CopyMemory( lpSessionDest, lpSessionSrc, sizeof( *lpSessionSrc ) );
1079
1080   if( lpSessionSrc->u1.lpszSessionNameA )
1081   {
1082       if ((lpSessionDest->u1.lpszSessionNameA = HeapAlloc( GetProcessHeap(), 0,
1083                                                              strlen(lpSessionSrc->u1.lpszSessionNameA)+1 )))
1084           strcpy( lpSessionDest->u1.lpszSessionNameA, lpSessionSrc->u1.lpszSessionNameA );
1085   }
1086   if( lpSessionSrc->u2.lpszPasswordA )
1087   {
1088       if ((lpSessionDest->u2.lpszPasswordA = HeapAlloc( GetProcessHeap(), 0,
1089                                                           strlen(lpSessionSrc->u2.lpszPasswordA)+1 )))
1090           strcpy( lpSessionDest->u2.lpszPasswordA, lpSessionSrc->u2.lpszPasswordA );
1091   }
1092
1093   return TRUE;
1094 }
1095
1096 /* Start the index at 0. index will be updated to equal that which should
1097    be passed back into this function for the next element */
1098 LPDPSESSIONDESC2 DPLAYX_CopyAndAllocateLocalSession( UINT* index )
1099 {
1100   for( ; (*index) < numSupportedSessions; (*index)++ )
1101   {
1102     if( sessionData[(*index)].dwSize != 0 )
1103     {
1104       return DPLAYX_CopyAndAllocateSessionDesc2A( &sessionData[(*index)++] );
1105     }
1106   }
1107
1108   /* No more sessions */
1109   return NULL;
1110 }
1111
1112 /* Start the index at 0. index will be updated to equal that which should
1113    be passed back into this function for the next element */
1114 BOOL DPLAYX_CopyLocalSession( UINT* index, LPDPSESSIONDESC2 lpsd )
1115 {
1116   for( ; (*index) < numSupportedSessions; (*index)++ )
1117   {
1118     if( sessionData[(*index)].dwSize != 0 )
1119     {
1120       return DPLAYX_CopyIntoSessionDesc2A( lpsd, &sessionData[(*index)++] );
1121     }
1122   }
1123
1124   /* No more sessions */
1125   return FALSE;
1126 }
1127
1128 void DPLAYX_SetLocalSession( LPCDPSESSIONDESC2 lpsd )
1129 {
1130   UINT i;
1131
1132   /* FIXME: Is this an error if it exists already? */
1133
1134   /* Crude/wrong implementation for now. Just always add to first empty spot */
1135   for( i=0; i < numSupportedSessions; i++ )
1136   {
1137     /* Is this one empty? */
1138     if( sessionData[i].dwSize == 0 )
1139     {
1140       DPLAYX_CopyIntoSessionDesc2A( &sessionData[i], lpsd );
1141       break;
1142     }
1143   }
1144
1145 }
1146
1147 BOOL DPLAYX_WaitForConnectionSettings( BOOL bWait )
1148 {
1149   LPDPLAYX_LOBBYDATA lpLobbyData;
1150
1151   DPLAYX_AcquireSemaphore();
1152
1153   if( !DPLAYX_IsAppIdLobbied( 0, &lpLobbyData ) )
1154   {
1155     DPLAYX_ReleaseSemaphore();
1156     return FALSE;
1157   }
1158
1159   lpLobbyData->bWaitForConnectionSettings = bWait;
1160
1161   DPLAYX_ReleaseSemaphore();
1162
1163   return TRUE;
1164 }
1165
1166 BOOL DPLAYX_AnyLobbiesWaitingForConnSettings(void)
1167 {
1168   UINT i;
1169   BOOL bFound = FALSE;
1170
1171   DPLAYX_AcquireSemaphore();
1172
1173   for( i=0; i < numSupportedLobbies; i++ )
1174   {
1175     if( ( lobbyData[ i ].dwAppID != 0 ) &&            /* lobby initialized */
1176         ( lobbyData[ i ].bWaitForConnectionSettings ) /* Waiting */
1177       )
1178     {
1179       bFound = TRUE;
1180       break;
1181     }
1182   }
1183
1184   DPLAYX_ReleaseSemaphore();
1185
1186   return bFound;
1187 }
1188
1189 BOOL DPLAYX_SetLobbyMsgThreadId( DWORD dwAppId, DWORD dwThreadId )
1190 {
1191   LPDPLAYX_LOBBYDATA lpLobbyData;
1192
1193   DPLAYX_AcquireSemaphore();
1194
1195   if( !DPLAYX_IsAppIdLobbied( dwAppId, &lpLobbyData ) )
1196   {
1197     DPLAYX_ReleaseSemaphore();
1198     return FALSE;
1199   }
1200
1201   lpLobbyData->dwLobbyMsgThreadId = dwThreadId;
1202
1203   DPLAYX_ReleaseSemaphore();
1204
1205   return TRUE;
1206 }
1207
1208 /* NOTE: This is potentially not thread safe. You are not guaranteed to end up
1209          with the correct string printed in the case where the HRESULT is not
1210          known. You will just get the last hr passed in. This can change
1211          over time if this method is used a lot :) */
1212 LPCSTR DPLAYX_HresultToString(HRESULT hr)
1213 {
1214   static char szTempStr[12];
1215
1216   switch (hr)
1217   {
1218     case DP_OK:
1219       return "DP_OK";
1220     case DPERR_ALREADYINITIALIZED:
1221       return "DPERR_ALREADYINITIALIZED";
1222     case DPERR_ACCESSDENIED:
1223       return "DPERR_ACCESSDENIED";
1224     case DPERR_ACTIVEPLAYERS:
1225       return "DPERR_ACTIVEPLAYERS";
1226     case DPERR_BUFFERTOOSMALL:
1227       return "DPERR_BUFFERTOOSMALL";
1228     case DPERR_CANTADDPLAYER:
1229       return "DPERR_CANTADDPLAYER";
1230     case DPERR_CANTCREATEGROUP:
1231       return "DPERR_CANTCREATEGROUP";
1232     case DPERR_CANTCREATEPLAYER:
1233       return "DPERR_CANTCREATEPLAYER";
1234     case DPERR_CANTCREATESESSION:
1235       return "DPERR_CANTCREATESESSION";
1236     case DPERR_CAPSNOTAVAILABLEYET:
1237       return "DPERR_CAPSNOTAVAILABLEYET";
1238     case DPERR_EXCEPTION:
1239       return "DPERR_EXCEPTION";
1240     case DPERR_GENERIC:
1241       return "DPERR_GENERIC";
1242     case DPERR_INVALIDFLAGS:
1243       return "DPERR_INVALIDFLAGS";
1244     case DPERR_INVALIDOBJECT:
1245       return "DPERR_INVALIDOBJECT";
1246     case DPERR_INVALIDPARAMS:
1247       return "DPERR_INVALIDPARAMS";
1248     case DPERR_INVALIDPLAYER:
1249       return "DPERR_INVALIDPLAYER";
1250     case DPERR_INVALIDGROUP:
1251       return "DPERR_INVALIDGROUP";
1252     case DPERR_NOCAPS:
1253       return "DPERR_NOCAPS";
1254     case DPERR_NOCONNECTION:
1255       return "DPERR_NOCONNECTION";
1256     case DPERR_OUTOFMEMORY:
1257       return "DPERR_OUTOFMEMORY";
1258     case DPERR_NOMESSAGES:
1259       return "DPERR_NOMESSAGES";
1260     case DPERR_NONAMESERVERFOUND:
1261       return "DPERR_NONAMESERVERFOUND";
1262     case DPERR_NOPLAYERS:
1263       return "DPERR_NOPLAYERS";
1264     case DPERR_NOSESSIONS:
1265       return "DPERR_NOSESSIONS";
1266     case DPERR_PENDING:
1267       return "DPERR_PENDING";
1268     case DPERR_SENDTOOBIG:
1269       return "DPERR_SENDTOOBIG";
1270     case DPERR_TIMEOUT:
1271       return "DPERR_TIMEOUT";
1272     case DPERR_UNAVAILABLE:
1273       return "DPERR_UNAVAILABLE";
1274     case DPERR_UNSUPPORTED:
1275       return "DPERR_UNSUPPORTED";
1276     case DPERR_BUSY:
1277       return "DPERR_BUSY";
1278     case DPERR_USERCANCEL:
1279       return "DPERR_USERCANCEL";
1280     case DPERR_NOINTERFACE:
1281       return "DPERR_NOINTERFACE";
1282     case DPERR_CANNOTCREATESERVER:
1283       return "DPERR_CANNOTCREATESERVER";
1284     case DPERR_PLAYERLOST:
1285       return "DPERR_PLAYERLOST";
1286     case DPERR_SESSIONLOST:
1287       return "DPERR_SESSIONLOST";
1288     case DPERR_UNINITIALIZED:
1289       return "DPERR_UNINITIALIZED";
1290     case DPERR_NONEWPLAYERS:
1291       return "DPERR_NONEWPLAYERS";
1292     case DPERR_INVALIDPASSWORD:
1293       return "DPERR_INVALIDPASSWORD";
1294     case DPERR_CONNECTING:
1295       return "DPERR_CONNECTING";
1296     case DPERR_CONNECTIONLOST:
1297       return "DPERR_CONNECTIONLOST";
1298     case DPERR_UNKNOWNMESSAGE:
1299       return "DPERR_UNKNOWNMESSAGE";
1300     case DPERR_CANCELFAILED:
1301       return "DPERR_CANCELFAILED";
1302     case DPERR_INVALIDPRIORITY:
1303       return "DPERR_INVALIDPRIORITY";
1304     case DPERR_NOTHANDLED:
1305       return "DPERR_NOTHANDLED";
1306     case DPERR_CANCELLED:
1307       return "DPERR_CANCELLED";
1308     case DPERR_ABORTED:
1309       return "DPERR_ABORTED";
1310     case DPERR_BUFFERTOOLARGE:
1311       return "DPERR_BUFFERTOOLARGE";
1312     case DPERR_CANTCREATEPROCESS:
1313       return "DPERR_CANTCREATEPROCESS";
1314     case DPERR_APPNOTSTARTED:
1315       return "DPERR_APPNOTSTARTED";
1316     case DPERR_INVALIDINTERFACE:
1317       return "DPERR_INVALIDINTERFACE";
1318     case DPERR_NOSERVICEPROVIDER:
1319       return "DPERR_NOSERVICEPROVIDER";
1320     case DPERR_UNKNOWNAPPLICATION:
1321       return "DPERR_UNKNOWNAPPLICATION";
1322     case DPERR_NOTLOBBIED:
1323       return "DPERR_NOTLOBBIED";
1324     case DPERR_SERVICEPROVIDERLOADED:
1325       return "DPERR_SERVICEPROVIDERLOADED";
1326     case DPERR_ALREADYREGISTERED:
1327       return "DPERR_ALREADYREGISTERED";
1328     case DPERR_NOTREGISTERED:
1329       return "DPERR_NOTREGISTERED";
1330     case DPERR_AUTHENTICATIONFAILED:
1331       return "DPERR_AUTHENTICATIONFAILED";
1332     case DPERR_CANTLOADSSPI:
1333       return "DPERR_CANTLOADSSPI";
1334     case DPERR_ENCRYPTIONFAILED:
1335       return "DPERR_ENCRYPTIONFAILED";
1336     case DPERR_SIGNFAILED:
1337       return "DPERR_SIGNFAILED";
1338     case DPERR_CANTLOADSECURITYPACKAGE:
1339       return "DPERR_CANTLOADSECURITYPACKAGE";
1340     case DPERR_ENCRYPTIONNOTSUPPORTED:
1341       return "DPERR_ENCRYPTIONNOTSUPPORTED";
1342     case DPERR_CANTLOADCAPI:
1343       return "DPERR_CANTLOADCAPI";
1344     case DPERR_NOTLOGGEDIN:
1345       return "DPERR_NOTLOGGEDIN";
1346     case DPERR_LOGONDENIED:
1347       return "DPERR_LOGONDENIED";
1348     default:
1349       /* For errors not in the list, return HRESULT as a string
1350          This part is not thread safe */
1351       WARN( "Unknown error 0x%08x\n", hr );
1352       wsprintfA( szTempStr, "0x%08lx", hr );
1353       return szTempStr;
1354   }
1355 }