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