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