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