There is no point making '--register' functions stdcall so just
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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, 1, 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   }
246   else
247   {
248     ERR( ": semaphore error %ld\n", GetLastError() );
249     return FALSE;
250   }
251
252   SetLastError( ERROR_SUCCESS );
253
254   DPLAYX_AquireSemaphore();
255
256   hDplayxSharedMem = CreateFileMappingA( INVALID_HANDLE_VALUE,
257                                          &s_attrib,
258                                          PAGE_READWRITE | SEC_COMMIT,
259                                          0,
260                                          dwTotalSharedSize,
261                                          lpszDplayxFileMapping );
262
263   if( GetLastError() == ERROR_SUCCESS )
264   {
265     TRACE( "File mapped %p created\n", hDplayxSharedMem );
266   }
267   else if ( GetLastError() == ERROR_ALREADY_EXISTS )
268   {
269     TRACE( "Found FileMapping handle %p\n", hDplayxSharedMem );
270   }
271   else
272   {
273     ERR( ": unable to create shared memory (%ld)\n", GetLastError() );
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     return FALSE;
286   }
287   else
288   {
289     if( lpDesiredMemoryMapStart == lpSharedStaticData )
290     {
291       TRACE( "File mapped to %p\n", lpSharedStaticData );
292     }
293     else
294     {
295       /* Presently the shared data structures use pointers. If the
296        * files are no mapped into the same area, the pointers will no
297        * longer make any sense :(
298        * FIXME: In the future make the shared data structures have some
299        *        sort of fixup to make them independent between data spaces.
300        *        This will also require a rework of the session data stuff.
301        */
302       ERR( "File mapped to %p (not %p). Expect failure\n",
303             lpSharedStaticData, lpDesiredMemoryMapStart );
304     }
305   }
306
307   /* Dynamic area starts just after the static area */
308   lpMemArea = (LPVOID)((BYTE*)lpSharedStaticData + dwStaticSharedSize);
309
310   /* FIXME: Crude hack */
311   lobbyData   = (DPLAYX_LOBBYDATA*)lpSharedStaticData;
312   sessionData = (DPSESSIONDESC2*)((BYTE*)lpSharedStaticData + (dwStaticSharedSize/2));
313
314   /* Initialize shared data segments. */
315   if( bInitializeSharedMemory )
316   {
317     UINT i;
318
319     TRACE( "Initializing shared memory\n" );
320
321     /* Set all lobbies to be "empty" */
322     for( i=0; i < numSupportedLobbies; i++ )
323     {
324       DPLAYX_InitializeLobbyDataEntry( &lobbyData[ i ] );
325     }
326
327     /* Set all sessions to be "empty" */
328     for( i=0; i < numSupportedSessions; i++ )
329     {
330       sessionData[i].dwSize = 0;
331     }
332
333     /* Zero out the dynmaic area */
334     ZeroMemory( lpMemArea, dwDynamicSharedSize );
335
336     /* Just for fun sync the whole data area */
337     FlushViewOfFile( lpSharedStaticData, dwTotalSharedSize );
338   }
339
340   DPLAYX_ReleaseSemaphore();
341
342   /* Everything was created correctly. Signal the lobby client that
343    * we started up correctly
344    */
345   if( DPLAYX_GetThisLobbyHandles( &hInformOnStart, NULL, NULL, FALSE ) &&
346       hInformOnStart
347     )
348   {
349     BOOL bSuccess;
350     bSuccess = SetEvent( hInformOnStart );
351     TRACE( "Signalling lobby app start event %p %s\n",
352              hInformOnStart, bSuccess ? "succeed" : "failed" );
353
354     /* Close out handle */
355     DPLAYX_GetThisLobbyHandles( &hInformOnStart, NULL, NULL, TRUE );
356   }
357
358   return TRUE;
359 }
360
361 /***************************************************************************
362  * Called to destroy all global data. This will only be used on the
363  * unloading of the dll
364  ***************************************************************************/
365 BOOL DPLAYX_DestructData(void)
366 {
367   HANDLE hInformOnDeath;
368
369   TRACE( "DPLAYX dll unloaded - destruct called\n" );
370
371   /* If required, inform that this app is dying */
372   if( DPLAYX_GetThisLobbyHandles( NULL, &hInformOnDeath, NULL, FALSE ) &&
373       hInformOnDeath
374     )
375   {
376     BOOL bSuccess;
377     bSuccess = SetEvent( hInformOnDeath );
378     TRACE( "Signalling lobby app death event %p %s\n",
379              hInformOnDeath, bSuccess ? "succeed" : "failed" );
380
381     /* Close out handle */
382     DPLAYX_GetThisLobbyHandles( NULL, &hInformOnDeath, NULL, TRUE );
383   }
384
385   /* DO CLEAN UP (LAST) */
386
387   /* Delete the semaphore */
388   CloseHandle( hDplayxSema );
389
390   /* Delete shared memory file mapping */
391   UnmapViewOfFile( lpSharedStaticData );
392   CloseHandle( hDplayxSharedMem );
393
394   return FALSE;
395 }
396
397
398 void DPLAYX_InitializeLobbyDataEntry( LPDPLAYX_LOBBYDATA lpData )
399 {
400   ZeroMemory( lpData, sizeof( *lpData ) );
401 }
402
403 /* NOTE: This must be called with the semaphore aquired.
404  * TRUE/FALSE with a pointer to it's data returned. Pointer data is
405  * is only valid if TRUE is returned.
406  */
407 BOOL DPLAYX_IsAppIdLobbied( DWORD dwAppID, LPDPLAYX_LOBBYDATA* lplpDplData )
408 {
409   UINT i;
410
411   *lplpDplData = NULL;
412
413   if( dwAppID == 0 )
414   {
415     dwAppID = GetCurrentProcessId();
416     TRACE( "Translated dwAppID == 0 into 0x%08lx\n", dwAppID );
417   }
418
419   for( i=0; i < numSupportedLobbies; i++ )
420   {
421     if( lobbyData[ i ].dwAppID == dwAppID )
422     {
423       /* This process is lobbied */
424       TRACE( "Found 0x%08lx @ %u\n", dwAppID, i );
425       *lplpDplData = &lobbyData[ i ];
426       return TRUE;
427     }
428   }
429
430   return FALSE;
431 }
432
433 /* Reserve a spot for the new appliction. TRUE means success and FALSE failure.  */
434 BOOL DPLAYX_CreateLobbyApplication( DWORD dwAppID )
435 {
436   UINT i;
437
438   /* 0 is the marker for unused application data slots */
439   if( dwAppID == 0 )
440   {
441     return FALSE;
442   }
443
444   DPLAYX_AquireSemaphore();
445
446   /* Find an empty space in the list and insert the data */
447   for( i=0; i < numSupportedLobbies; i++ )
448   {
449     if( lobbyData[ i ].dwAppID == 0 )
450     {
451       /* This process is now lobbied */
452       TRACE( "Setting lobbyData[%u] for (0x%08lx,0x%08lx)\n",
453               i, dwAppID, GetCurrentProcessId() );
454
455       lobbyData[ i ].dwAppID              = dwAppID;
456       lobbyData[ i ].dwAppLaunchedFromID  = GetCurrentProcessId();
457
458       /* FIXME: Where is the best place for this? In interface or here? */
459       lobbyData[ i ].hInformOnAppStart = 0;
460       lobbyData[ i ].hInformOnAppDeath = 0;
461       lobbyData[ i ].hInformOnSettingRead = 0;
462
463       DPLAYX_ReleaseSemaphore();
464       return TRUE;
465     }
466   }
467
468   ERR( "No empty lobbies\n" );
469
470   DPLAYX_ReleaseSemaphore();
471   return FALSE;
472 }
473
474 /* I'm not sure when I'm going to need this, but here it is */
475 BOOL DPLAYX_DestroyLobbyApplication( DWORD dwAppID )
476 {
477   UINT i;
478
479   DPLAYX_AquireSemaphore();
480
481   /* Find an empty space in the list and insert the data */
482   for( i=0; i < numSupportedLobbies; i++ )
483   {
484     if( lobbyData[ i ].dwAppID == dwAppID )
485     {
486       /* FIXME: Should free up anything unused. Tisk tisk :0 */
487       /* Mark this entry unused */
488       TRACE( "Marking lobbyData[%u] unused\n", i );
489       DPLAYX_InitializeLobbyDataEntry( &lobbyData[ i ] );
490
491       DPLAYX_ReleaseSemaphore();
492       return TRUE;
493     }
494   }
495
496   DPLAYX_ReleaseSemaphore();
497   ERR( "Unable to find global entry for application\n" );
498   return FALSE;
499 }
500
501 BOOL DPLAYX_SetLobbyHandles( DWORD dwAppID,
502                              HANDLE hStart, HANDLE hDeath, HANDLE hConnRead )
503 {
504   LPDPLAYX_LOBBYDATA lpLData;
505
506   /* Need to explictly give lobby application. Can't set for yourself */
507   if( dwAppID == 0 )
508   {
509     return FALSE;
510   }
511
512   DPLAYX_AquireSemaphore();
513
514   if( !DPLAYX_IsAppIdLobbied( dwAppID, &lpLData ) )
515   {
516     DPLAYX_ReleaseSemaphore();
517     return FALSE;
518   }
519
520   lpLData->hInformOnAppStart    = hStart;
521   lpLData->hInformOnAppDeath    = hDeath;
522   lpLData->hInformOnSettingRead = hConnRead;
523
524   DPLAYX_ReleaseSemaphore();
525
526   return TRUE;
527 }
528
529 BOOL DPLAYX_GetThisLobbyHandles( LPHANDLE lphStart,
530                                  LPHANDLE lphDeath,
531                                  LPHANDLE lphConnRead,
532                                  BOOL     bClearSetHandles )
533 {
534   LPDPLAYX_LOBBYDATA lpLData;
535
536   DPLAYX_AquireSemaphore();
537
538   if( !DPLAYX_IsAppIdLobbied( 0, &lpLData ) )
539   {
540     DPLAYX_ReleaseSemaphore();
541     return FALSE;
542   }
543
544   if( lphStart != NULL )
545   {
546     if( lpLData->hInformOnAppStart == 0 )
547     {
548       DPLAYX_ReleaseSemaphore();
549       return FALSE;
550     }
551
552     *lphStart = lpLData->hInformOnAppStart;
553
554     if( bClearSetHandles )
555     {
556       CloseHandle( lpLData->hInformOnAppStart );
557       lpLData->hInformOnAppStart = 0;
558     }
559   }
560
561   if( lphDeath != NULL )
562   {
563     if( lpLData->hInformOnAppDeath == 0 )
564     {
565       DPLAYX_ReleaseSemaphore();
566       return FALSE;
567     }
568
569     *lphDeath = lpLData->hInformOnAppDeath;
570
571     if( bClearSetHandles )
572     {
573       CloseHandle( lpLData->hInformOnAppDeath );
574       lpLData->hInformOnAppDeath = 0;
575     }
576   }
577
578   if( lphConnRead != NULL )
579   {
580     if( lpLData->hInformOnSettingRead == 0 )
581     {
582       DPLAYX_ReleaseSemaphore();
583       return FALSE;
584     }
585
586     *lphConnRead = lpLData->hInformOnSettingRead;
587
588     if( bClearSetHandles )
589     {
590       CloseHandle( lpLData->hInformOnSettingRead );
591       lpLData->hInformOnSettingRead = 0;
592     }
593   }
594
595   DPLAYX_ReleaseSemaphore();
596
597   return TRUE;
598 }
599
600
601 HRESULT DPLAYX_GetConnectionSettingsA
602 ( DWORD dwAppID,
603   LPVOID lpData,
604   LPDWORD lpdwDataSize )
605 {
606   LPDPLAYX_LOBBYDATA lpDplData;
607   DWORD              dwRequiredDataSize = 0;
608   HANDLE             hInformOnSettingRead;
609
610   DPLAYX_AquireSemaphore();
611
612   if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
613   {
614     DPLAYX_ReleaseSemaphore();
615
616     TRACE( "Application 0x%08lx is not lobbied\n", dwAppID );
617     return DPERR_NOTLOBBIED;
618   }
619
620   dwRequiredDataSize = DPLAYX_SizeOfLobbyDataA( lpDplData->lpConn );
621
622   /* Do they want to know the required buffer size or is the provided buffer
623    * big enough?
624    */
625   if ( ( lpData == NULL ) ||
626        ( *lpdwDataSize < dwRequiredDataSize )
627      )
628   {
629     DPLAYX_ReleaseSemaphore();
630
631     *lpdwDataSize = DPLAYX_SizeOfLobbyDataA( lpDplData->lpConn );
632
633     return DPERR_BUFFERTOOSMALL;
634   }
635
636   DPLAYX_CopyConnStructA( (LPDPLCONNECTION)lpData, lpDplData->lpConn );
637
638   DPLAYX_ReleaseSemaphore();
639
640   /* They have gotten the information - signal the event if required */
641   if( DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, FALSE ) &&
642       hInformOnSettingRead
643     )
644   {
645     BOOL bSuccess;
646     bSuccess = SetEvent( hInformOnSettingRead );
647     TRACE( "Signalling setting read event %p %s\n",
648              hInformOnSettingRead, bSuccess ? "succeed" : "failed" );
649
650     /* Close out handle */
651     DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, TRUE );
652   }
653
654   return DP_OK;
655 }
656
657 /* Assumption: Enough contiguous space was allocated at dest */
658 void DPLAYX_CopyConnStructA( LPDPLCONNECTION dest, LPDPLCONNECTION src )
659 {
660   BYTE* lpStartOfFreeSpace;
661
662   CopyMemory( dest, src, sizeof( DPLCONNECTION ) );
663
664   lpStartOfFreeSpace = ((BYTE*)dest) + sizeof( DPLCONNECTION );
665
666   /* Copy the LPDPSESSIONDESC2 structure if it exists */
667   if( src->lpSessionDesc )
668   {
669     dest->lpSessionDesc = (LPDPSESSIONDESC2)lpStartOfFreeSpace;
670     lpStartOfFreeSpace += sizeof( DPSESSIONDESC2 );
671     CopyMemory( dest->lpSessionDesc, src->lpSessionDesc, sizeof( DPSESSIONDESC2 ) );
672
673     /* Session names may or may not exist */
674     if( src->lpSessionDesc->u1.lpszSessionNameA )
675     {
676       strcpy( (LPSTR)lpStartOfFreeSpace, src->lpSessionDesc->u1.lpszSessionNameA );
677       dest->lpSessionDesc->u1.lpszSessionNameA = (LPSTR)lpStartOfFreeSpace;
678       lpStartOfFreeSpace +=
679         strlen( (LPSTR)dest->lpSessionDesc->u1.lpszSessionNameA ) + 1;
680     }
681
682     if( src->lpSessionDesc->u2.lpszPasswordA )
683     {
684       strcpy( (LPSTR)lpStartOfFreeSpace, src->lpSessionDesc->u2.lpszPasswordA );
685       dest->lpSessionDesc->u2.lpszPasswordA = (LPSTR)lpStartOfFreeSpace;
686       lpStartOfFreeSpace +=
687         strlen( (LPSTR)dest->lpSessionDesc->u2.lpszPasswordA ) + 1;
688     }
689   }
690
691   /* DPNAME structure is optional */
692   if( src->lpPlayerName )
693   {
694     dest->lpPlayerName = (LPDPNAME)lpStartOfFreeSpace;
695     lpStartOfFreeSpace += sizeof( DPNAME );
696     CopyMemory( dest->lpPlayerName, src->lpPlayerName, sizeof( DPNAME ) );
697
698     if( src->lpPlayerName->u1.lpszShortNameA )
699     {
700       strcpy( (LPSTR)lpStartOfFreeSpace, src->lpPlayerName->u1.lpszShortNameA );
701       dest->lpPlayerName->u1.lpszShortNameA = (LPSTR)lpStartOfFreeSpace;
702       lpStartOfFreeSpace +=
703         strlen( (LPSTR)dest->lpPlayerName->u1.lpszShortNameA ) + 1;
704     }
705
706     if( src->lpPlayerName->u2.lpszLongNameA )
707     {
708       strcpy( (LPSTR)lpStartOfFreeSpace, src->lpPlayerName->u2.lpszLongNameA );
709       dest->lpPlayerName->u2.lpszLongNameA = (LPSTR)lpStartOfFreeSpace;
710       lpStartOfFreeSpace +=
711         strlen( (LPSTR)dest->lpPlayerName->u2.lpszLongName ) + 1 ;
712     }
713
714   }
715
716   /* Copy address if it exists */
717   if( src->lpAddress )
718   {
719     dest->lpAddress = (LPVOID)lpStartOfFreeSpace;
720     CopyMemory( lpStartOfFreeSpace, src->lpAddress, src->dwAddressSize );
721     /* No need to advance lpStartOfFreeSpace as there is no more "dynamic" data */
722   }
723 }
724
725 HRESULT DPLAYX_GetConnectionSettingsW
726 ( DWORD dwAppID,
727   LPVOID lpData,
728   LPDWORD lpdwDataSize )
729 {
730   LPDPLAYX_LOBBYDATA lpDplData;
731   DWORD              dwRequiredDataSize = 0;
732   HANDLE             hInformOnSettingRead;
733
734   DPLAYX_AquireSemaphore();
735
736   if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
737   {
738     DPLAYX_ReleaseSemaphore();
739     return DPERR_NOTLOBBIED;
740   }
741
742   dwRequiredDataSize = DPLAYX_SizeOfLobbyDataW( lpDplData->lpConn );
743
744   /* Do they want to know the required buffer size or is the provided buffer
745    * big enough?
746    */
747   if ( ( lpData == NULL ) ||
748        ( *lpdwDataSize < dwRequiredDataSize )
749      )
750   {
751     DPLAYX_ReleaseSemaphore();
752
753     *lpdwDataSize = DPLAYX_SizeOfLobbyDataW( lpDplData->lpConn );
754
755     return DPERR_BUFFERTOOSMALL;
756   }
757
758   DPLAYX_CopyConnStructW( (LPDPLCONNECTION)lpData, lpDplData->lpConn );
759
760   DPLAYX_ReleaseSemaphore();
761
762   /* They have gotten the information - signal the event if required */
763   if( DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, FALSE ) &&
764       hInformOnSettingRead
765     )
766   {
767     BOOL bSuccess;
768     bSuccess = SetEvent( hInformOnSettingRead );
769     TRACE( "Signalling setting read event %p %s\n",
770              hInformOnSettingRead, bSuccess ? "succeed" : "failed" );
771
772     /* Close out handle */
773     DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, TRUE );
774   }
775
776   return DP_OK;
777 }
778
779 /* Assumption: Enough contiguous space was allocated at dest */
780 void DPLAYX_CopyConnStructW( LPDPLCONNECTION dest, LPDPLCONNECTION src )
781 {
782   BYTE*              lpStartOfFreeSpace;
783
784   CopyMemory( dest, src, sizeof( DPLCONNECTION ) );
785
786   lpStartOfFreeSpace = ( (BYTE*)dest) + sizeof( DPLCONNECTION );
787
788   /* Copy the LPDPSESSIONDESC2 structure if it exists */
789   if( src->lpSessionDesc )
790   {
791     dest->lpSessionDesc = (LPDPSESSIONDESC2)lpStartOfFreeSpace;
792     lpStartOfFreeSpace += sizeof( DPSESSIONDESC2 );
793     CopyMemory( dest->lpSessionDesc, src->lpSessionDesc, sizeof( DPSESSIONDESC2 ) );
794
795     /* Session names may or may not exist */
796     if( src->lpSessionDesc->u1.lpszSessionName )
797     {
798       strcpyW( (LPWSTR)lpStartOfFreeSpace, dest->lpSessionDesc->u1.lpszSessionName );
799       src->lpSessionDesc->u1.lpszSessionName = (LPWSTR)lpStartOfFreeSpace;
800       lpStartOfFreeSpace +=  sizeof(WCHAR) *
801         ( strlenW( (LPWSTR)dest->lpSessionDesc->u1.lpszSessionName ) + 1 );
802     }
803
804     if( src->lpSessionDesc->u2.lpszPassword )
805     {
806       strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpSessionDesc->u2.lpszPassword );
807       dest->lpSessionDesc->u2.lpszPassword = (LPWSTR)lpStartOfFreeSpace;
808       lpStartOfFreeSpace +=  sizeof(WCHAR) *
809         ( strlenW( (LPWSTR)dest->lpSessionDesc->u2.lpszPassword ) + 1 );
810     }
811   }
812
813   /* DPNAME structure is optional */
814   if( src->lpPlayerName )
815   {
816     dest->lpPlayerName = (LPDPNAME)lpStartOfFreeSpace;
817     lpStartOfFreeSpace += sizeof( DPNAME );
818     CopyMemory( dest->lpPlayerName, src->lpPlayerName, sizeof( DPNAME ) );
819
820     if( src->lpPlayerName->u1.lpszShortName )
821     {
822       strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpPlayerName->u1.lpszShortName );
823       dest->lpPlayerName->u1.lpszShortName = (LPWSTR)lpStartOfFreeSpace;
824       lpStartOfFreeSpace +=  sizeof(WCHAR) *
825         ( strlenW( (LPWSTR)dest->lpPlayerName->u1.lpszShortName ) + 1 );
826     }
827
828     if( src->lpPlayerName->u2.lpszLongName )
829     {
830       strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpPlayerName->u2.lpszLongName );
831       dest->lpPlayerName->u2.lpszLongName = (LPWSTR)lpStartOfFreeSpace;
832       lpStartOfFreeSpace +=  sizeof(WCHAR) *
833         ( strlenW( (LPWSTR)dest->lpPlayerName->u2.lpszLongName ) + 1 );
834     }
835
836   }
837
838   /* Copy address if it exists */
839   if( src->lpAddress )
840   {
841     dest->lpAddress = (LPVOID)lpStartOfFreeSpace;
842     CopyMemory( lpStartOfFreeSpace, src->lpAddress, src->dwAddressSize );
843     /* No need to advance lpStartOfFreeSpace as there is no more "dynamic" data */
844   }
845
846 }
847
848 /* Store the structure into the shared data structre. Ensure that allocs for
849  * variable length strings come from the shared data structure.
850  * FIXME: We need to free information as well
851  */
852 HRESULT DPLAYX_SetConnectionSettingsA
853 ( DWORD dwFlags,
854   DWORD dwAppID,
855   LPDPLCONNECTION lpConn )
856 {
857   LPDPLAYX_LOBBYDATA lpDplData;
858
859   /* Parameter check */
860   if( dwFlags || !lpConn )
861   {
862     ERR("invalid parameters.\n");
863     return DPERR_INVALIDPARAMS;
864   }
865
866   /* Store information */
867   if(  lpConn->dwSize != sizeof(DPLCONNECTION) )
868   {
869     ERR(": old/new DPLCONNECTION type? Size=%08lx vs. expected=%ul bytes\n",
870          lpConn->dwSize, sizeof( DPLCONNECTION ) );
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 vs. expected=%u bytes\n",
891          lpConn->lpSessionDesc->dwSize, sizeof( DPSESSIONDESC2 ) );
892
893     return DPERR_INVALIDPARAMS;
894   }
895
896   /* Free the existing memory */
897   DPLAYX_PrivHeapFree( lpDplData->lpConn );
898
899   lpDplData->lpConn = DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY,
900                                             DPLAYX_SizeOfLobbyDataA( lpConn ) );
901
902   DPLAYX_CopyConnStructA( lpDplData->lpConn, lpConn );
903
904
905   DPLAYX_ReleaseSemaphore();
906
907   /* FIXME: Send a message - I think */
908
909   return DP_OK;
910 }
911
912 /* Store the structure into the shared data structre. Ensure that allocs for
913  * variable length strings come from the shared data structure.
914  * FIXME: We need to free information as well
915  */
916 HRESULT DPLAYX_SetConnectionSettingsW
917 ( DWORD dwFlags,
918   DWORD dwAppID,
919   LPDPLCONNECTION lpConn )
920 {
921   LPDPLAYX_LOBBYDATA lpDplData;
922
923   /* Parameter check */
924   if( dwFlags || !lpConn )
925   {
926     ERR("invalid parameters.\n");
927     return DPERR_INVALIDPARAMS;
928   }
929
930   /* Store information */
931   if(  lpConn->dwSize != sizeof(DPLCONNECTION) )
932   {
933     ERR(": old/new DPLCONNECTION type? Size=%lu vs. expected=%u bytes\n",
934          lpConn->dwSize, sizeof( DPLCONNECTION ) );
935
936     return DPERR_INVALIDPARAMS;
937   }
938
939   DPLAYX_AquireSemaphore();
940
941   if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
942   {
943     DPLAYX_ReleaseSemaphore();
944
945     return DPERR_NOTLOBBIED;
946   }
947
948   /* Free the existing memory */
949   DPLAYX_PrivHeapFree( lpDplData->lpConn );
950
951   lpDplData->lpConn = DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY,
952                                             DPLAYX_SizeOfLobbyDataW( lpConn ) );
953
954   DPLAYX_CopyConnStructW( lpDplData->lpConn, lpConn );
955
956
957   DPLAYX_ReleaseSemaphore();
958
959   /* FIXME: Send a message - I think */
960
961   return DP_OK;
962 }
963
964 DWORD DPLAYX_SizeOfLobbyDataA( LPDPLCONNECTION lpConn )
965 {
966   DWORD dwTotalSize = sizeof( DPLCONNECTION );
967
968   /* Just a safety check */
969   if( lpConn == NULL )
970   {
971     ERR( "lpConn is NULL\n" );
972     return 0;
973   }
974
975   if( lpConn->lpSessionDesc != NULL )
976   {
977     dwTotalSize += sizeof( DPSESSIONDESC2 );
978
979     if( lpConn->lpSessionDesc->u1.lpszSessionNameA )
980     {
981       dwTotalSize += strlen( lpConn->lpSessionDesc->u1.lpszSessionNameA ) + 1;
982     }
983
984     if( lpConn->lpSessionDesc->u2.lpszPasswordA )
985     {
986       dwTotalSize += strlen( lpConn->lpSessionDesc->u2.lpszPasswordA ) + 1;
987     }
988   }
989
990   if( lpConn->lpPlayerName != NULL )
991   {
992     dwTotalSize += sizeof( DPNAME );
993
994     if( lpConn->lpPlayerName->u1.lpszShortNameA )
995     {
996       dwTotalSize += strlen( lpConn->lpPlayerName->u1.lpszShortNameA ) + 1;
997     }
998
999     if( lpConn->lpPlayerName->u2.lpszLongNameA )
1000     {
1001       dwTotalSize += strlen( lpConn->lpPlayerName->u2.lpszLongNameA ) + 1;
1002     }
1003
1004   }
1005
1006   dwTotalSize += lpConn->dwAddressSize;
1007
1008   return dwTotalSize;
1009 }
1010
1011 DWORD DPLAYX_SizeOfLobbyDataW( LPDPLCONNECTION lpConn )
1012 {
1013   DWORD dwTotalSize = sizeof( DPLCONNECTION );
1014
1015   /* Just a safety check */
1016   if( lpConn == NULL )
1017   {
1018     ERR( "lpConn is NULL\n" );
1019     return 0;
1020   }
1021
1022   if( lpConn->lpSessionDesc != NULL )
1023   {
1024     dwTotalSize += sizeof( DPSESSIONDESC2 );
1025
1026     if( lpConn->lpSessionDesc->u1.lpszSessionName )
1027     {
1028       dwTotalSize += sizeof( WCHAR ) *
1029         ( strlenW( lpConn->lpSessionDesc->u1.lpszSessionName ) + 1 );
1030     }
1031
1032     if( lpConn->lpSessionDesc->u2.lpszPassword )
1033     {
1034       dwTotalSize += sizeof( WCHAR ) *
1035         ( strlenW( lpConn->lpSessionDesc->u2.lpszPassword ) + 1 );
1036     }
1037   }
1038
1039   if( lpConn->lpPlayerName != NULL )
1040   {
1041     dwTotalSize += sizeof( DPNAME );
1042
1043     if( lpConn->lpPlayerName->u1.lpszShortName )
1044     {
1045       dwTotalSize += sizeof( WCHAR ) *
1046         ( strlenW( lpConn->lpPlayerName->u1.lpszShortName ) + 1 );
1047     }
1048
1049     if( lpConn->lpPlayerName->u2.lpszLongName )
1050     {
1051       dwTotalSize += sizeof( WCHAR ) *
1052         ( strlenW( lpConn->lpPlayerName->u2.lpszLongName ) + 1 );
1053     }
1054
1055   }
1056
1057   dwTotalSize += lpConn->dwAddressSize;
1058
1059   return dwTotalSize;
1060 }
1061
1062
1063
1064 LPDPSESSIONDESC2 DPLAYX_CopyAndAllocateSessionDesc2A( LPCDPSESSIONDESC2 lpSessionSrc )
1065 {
1066    LPDPSESSIONDESC2 lpSessionDest =
1067      (LPDPSESSIONDESC2)HeapAlloc( GetProcessHeap(),
1068                                   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_AquireSemaphore();
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_AquireSemaphore();
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_AquireSemaphore();
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'll just get the last hr passed in printed. This can change
1211          over time if this method is used alot :) */
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%08lx\n", hr );
1352       wsprintfA( szTempStr, "0x%08lx", hr );
1353       return szTempStr;
1354   }
1355 }