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