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