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