dplayx: Merge the IDirectPlay4_EnumSessions helper.
[wine] / dlls / dplayx / dplay.c
1 /* Direct Play 2,3,4 Implementation
2  *
3  * Copyright 1998,1999,2000,2001 - Peter Hunnisett
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19
20 #define COBJMACROS
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <stdarg.h>
25 #include <string.h>
26
27 #define NONAMELESSUNION
28 #define NONAMELESSSTRUCT
29 #include "windef.h"
30 #include "winerror.h"
31 #include "winbase.h"
32 #include "winnt.h"
33 #include "winreg.h"
34 #include "winnls.h"
35 #include "wine/unicode.h"
36 #include "wine/debug.h"
37
38 #include "dpinit.h"
39 #include "dplayx_global.h"
40 #include "name_server.h"
41 #include "dplayx_queue.h"
42 #include "wine/dplaysp.h"
43 #include "dplay_global.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(dplay);
46
47 /* FIXME: Should this be externed? */
48 extern HRESULT DPL_CreateCompoundAddress
49 ( LPCDPCOMPOUNDADDRESSELEMENT lpElements, DWORD dwElementCount,
50   LPVOID lpAddress, LPDWORD lpdwAddressSize, BOOL bAnsiInterface );
51
52
53 /* Local function prototypes */
54 static lpPlayerList DP_FindPlayer( IDirectPlay2AImpl* This, DPID dpid );
55 static lpPlayerData DP_CreatePlayer( IDirectPlay2Impl* iface, LPDPID lpid,
56                                      LPDPNAME lpName, DWORD dwFlags,
57                                      HANDLE hEvent, BOOL bAnsi );
58 static BOOL DP_CopyDPNAMEStruct( LPDPNAME lpDst, const DPNAME *lpSrc, BOOL bAnsi );
59 static void DP_SetPlayerData( lpPlayerData lpPData, DWORD dwFlags,
60                               LPVOID lpData, DWORD dwDataSize );
61
62 static lpGroupData DP_CreateGroup( IDirectPlay2AImpl* iface, const DPID *lpid,
63                                    const DPNAME *lpName, DWORD dwFlags,
64                                    DPID idParent, BOOL bAnsi );
65 static void DP_SetGroupData( lpGroupData lpGData, DWORD dwFlags,
66                              LPVOID lpData, DWORD dwDataSize );
67 static void DP_DeleteDPNameStruct( LPDPNAME lpDPName );
68 static void DP_DeletePlayer( IDirectPlay2Impl* This, DPID dpid );
69 static BOOL CALLBACK cbDeletePlayerFromAllGroups( DPID dpId,
70                                                   DWORD dwPlayerType,
71                                                   LPCDPNAME lpName,
72                                                   DWORD dwFlags,
73                                                   LPVOID lpContext );
74 static lpGroupData DP_FindAnyGroup( IDirectPlay2AImpl* This, DPID dpid );
75 static BOOL CALLBACK cbRemoveGroupOrPlayer( DPID dpId, DWORD dwPlayerType,
76                                             LPCDPNAME lpName, DWORD dwFlags,
77                                             LPVOID lpContext );
78 static void DP_DeleteGroup( IDirectPlay2Impl* This, DPID dpid );
79
80 /* Helper methods for player/group interfaces */
81 static HRESULT DP_IF_CreatePlayer
82           ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, LPDPID lpidPlayer,
83             LPDPNAME lpPlayerName, HANDLE hEvent, LPVOID lpData,
84             DWORD dwDataSize, DWORD dwFlags, BOOL bAnsi );
85 static HRESULT DP_IF_DestroyGroup
86           ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idGroup, BOOL bAnsi );
87 static HRESULT DP_IF_DestroyPlayer
88           ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idPlayer, BOOL bAnsi );
89 static HRESULT DP_IF_GetGroupName
90           ( IDirectPlay2Impl* This, DPID idGroup, LPVOID lpData,
91             LPDWORD lpdwDataSize, BOOL bAnsi );
92 static HRESULT DP_IF_GetPlayerName
93           ( IDirectPlay2Impl* This, DPID idPlayer, LPVOID lpData,
94             LPDWORD lpdwDataSize, BOOL bAnsi );
95 static HRESULT DP_IF_SetGroupName
96           ( IDirectPlay2Impl* This, DPID idGroup, LPDPNAME lpGroupName,
97             DWORD dwFlags, BOOL bAnsi );
98 static HRESULT DP_IF_SetPlayerName
99           ( IDirectPlay2Impl* This, DPID idPlayer, LPDPNAME lpPlayerName,
100             DWORD dwFlags, BOOL bAnsi );
101 static HRESULT DP_IF_CreateGroup
102           ( IDirectPlay2AImpl* This, LPVOID lpMsgHdr, LPDPID lpidGroup,
103             LPDPNAME lpGroupName, LPVOID lpData, DWORD dwDataSize,
104             DWORD dwFlags, BOOL bAnsi );
105 static HRESULT DP_IF_CreateGroupInGroup
106           ( IDirectPlay3Impl* This, LPVOID lpMsgHdr, DPID idParentGroup,
107             LPDPID lpidGroup, LPDPNAME lpGroupName, LPVOID lpData,
108             DWORD dwDataSize, DWORD dwFlags, BOOL bAnsi );
109 static HRESULT DP_SetSessionDesc
110           ( IDirectPlay2Impl* This, LPCDPSESSIONDESC2 lpSessDesc,
111             DWORD dwFlags, BOOL bInitial, BOOL bAnsi  );
112 static HRESULT DP_SecureOpen
113           ( IDirectPlay2Impl* This, LPCDPSESSIONDESC2 lpsd, DWORD dwFlags,
114             LPCDPSECURITYDESC lpSecurity, LPCDPCREDENTIALS lpCredentials,
115             BOOL bAnsi );
116 static HRESULT DP_SendEx
117           ( IDirectPlay2Impl* This, DPID idFrom, DPID idTo, DWORD dwFlags,
118             LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout,
119             LPVOID lpContext, LPDWORD lpdwMsgID, BOOL bAnsi );
120 static HRESULT DP_IF_Receive
121           ( IDirectPlay2Impl* This, LPDPID lpidFrom, LPDPID lpidTo,
122             DWORD dwFlags, LPVOID lpData, LPDWORD lpdwDataSize, BOOL bAnsi );
123 static HRESULT DP_IF_GetMessageQueue
124           ( IDirectPlay4Impl* This, DPID idFrom, DPID idTo, DWORD dwFlags,
125             LPDWORD lpdwNumMsgs, LPDWORD lpdwNumBytes, BOOL bAnsi );
126 static HRESULT DP_SP_SendEx
127           ( IDirectPlay2Impl* This, DWORD dwFlags,
128             LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout,
129             LPVOID lpContext, LPDWORD lpdwMsgID );
130 static HRESULT DP_IF_CancelMessage
131           ( IDirectPlay4Impl* This, DWORD dwMsgID, DWORD dwFlags,
132             DWORD dwMinPriority, DWORD dwMaxPriority, BOOL bAnsi );
133 static BOOL CALLBACK cbDPCreateEnumConnections( LPCGUID lpguidSP,
134     LPVOID lpConnection, DWORD dwConnectionSize, LPCDPNAME lpName,
135     DWORD dwFlags, LPVOID lpContext );
136 static BOOL DP_BuildSPCompoundAddr( LPGUID lpcSpGuid, LPVOID* lplpAddrBuf,
137                                     LPDWORD lpdwBufSize );
138
139
140
141 static inline DPID DP_NextObjectId(void);
142 static DPID DP_GetRemoteNextObjectId(void);
143
144 static DWORD DP_CalcSessionDescSize( LPCDPSESSIONDESC2 lpSessDesc, BOOL bAnsi );
145 static void DP_CopySessionDesc( LPDPSESSIONDESC2 destSessionDesc,
146                                 LPCDPSESSIONDESC2 srcSessDesc, BOOL bAnsi );
147
148
149 static HMODULE DP_LoadSP( LPCGUID lpcGuid, LPSPINITDATA lpSpData, LPBOOL lpbIsDpSp );
150 static HRESULT DP_InitializeDPSP( IDirectPlay3Impl* This, HMODULE hServiceProvider );
151 static HRESULT DP_InitializeDPLSP( IDirectPlay3Impl* This, HMODULE hServiceProvider );
152
153
154
155
156
157
158 #define DPID_NOPARENT_GROUP 0 /* Magic number to indicate no parent of group */
159 #define DPID_SYSTEM_GROUP DPID_NOPARENT_GROUP /* If system group is supported
160                                                  we don't have to change much */
161 #define DPID_NAME_SERVER 0x19a9d65b  /* Don't ask me why */
162
163 /* Strip out dwFlag values which cannot be sent in the CREATEGROUP msg */
164 #define DPMSG_CREATEGROUP_DWFLAGS(x) ( (x) & DPGROUP_HIDDEN )
165
166 /* Strip out all dwFlags values for CREATEPLAYER msg */
167 #define DPMSG_CREATEPLAYER_DWFLAGS(x) 0
168
169 static LONG kludgePlayerGroupId = 1000;
170
171
172 static inline IDirectPlayImpl *impl_from_IDirectPlay4A( IDirectPlay4A *iface )
173 {
174     return CONTAINING_RECORD( iface, IDirectPlayImpl, IDirectPlay4A_iface );
175 }
176
177 static inline IDirectPlayImpl *impl_from_IDirectPlay4( IDirectPlay4 *iface )
178 {
179     return CONTAINING_RECORD( iface, IDirectPlayImpl, IDirectPlay4_iface );
180 }
181
182 static BOOL DP_CreateDirectPlay2( LPVOID lpDP )
183 {
184   IDirectPlay2AImpl *This = lpDP;
185
186   This->dp2 = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *(This->dp2) ) );
187   if ( This->dp2 == NULL )
188   {
189     return FALSE;
190   }
191
192   This->dp2->bConnectionOpen = FALSE;
193
194   This->dp2->hEnumSessionThread = INVALID_HANDLE_VALUE;
195   This->dp2->dwEnumSessionLock = 0;
196
197   This->dp2->bHostInterface = FALSE;
198
199   DPQ_INIT(This->dp2->receiveMsgs);
200   DPQ_INIT(This->dp2->sendMsgs);
201   DPQ_INIT(This->dp2->repliesExpected);
202
203   if( !NS_InitializeSessionCache( &This->dp2->lpNameServerData ) )
204   {
205     /* FIXME: Memory leak */
206     return FALSE;
207   }
208
209   /* Provide an initial session desc with nothing in it */
210   This->dp2->lpSessionDesc = HeapAlloc( GetProcessHeap(),
211                                         HEAP_ZERO_MEMORY,
212                                         sizeof( *This->dp2->lpSessionDesc ) );
213   if( This->dp2->lpSessionDesc == NULL )
214   {
215     /* FIXME: Memory leak */
216     return FALSE;
217   }
218   This->dp2->lpSessionDesc->dwSize = sizeof( *This->dp2->lpSessionDesc );
219
220   /* We are emulating a dp 6 implementation */
221   This->dp2->spData.dwSPVersion = DPSP_MAJORVERSION;
222
223   This->dp2->spData.lpCB = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
224                                       sizeof( *This->dp2->spData.lpCB ) );
225   This->dp2->spData.lpCB->dwSize = sizeof( *This->dp2->spData.lpCB );
226   This->dp2->spData.lpCB->dwVersion = DPSP_MAJORVERSION;
227
228   /* This is the pointer to the service provider */
229   if( FAILED( DPSP_CreateInterface( &IID_IDirectPlaySP,
230                                     (LPVOID*)&This->dp2->spData.lpISP, This ) )
231     )
232   {
233     /* FIXME: Memory leak */
234     return FALSE;
235   }
236
237   /* Setup lobby provider information */
238   This->dp2->dplspData.dwSPVersion = DPSP_MAJORVERSION;
239   This->dp2->dplspData.lpCB = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
240                                          sizeof( *This->dp2->dplspData.lpCB ) );
241   This->dp2->dplspData.lpCB->dwSize = sizeof(  *This->dp2->dplspData.lpCB );
242
243   if( FAILED( DPLSP_CreateInterface( &IID_IDPLobbySP,
244                                      (LPVOID*)&This->dp2->dplspData.lpISP, This ) )
245     )
246   {
247     /* FIXME: Memory leak */
248     return FALSE;
249   }
250
251   return TRUE;
252 }
253
254 /* Definition of the global function in dplayx_queue.h. #
255  * FIXME: Would it be better to have a dplayx_queue.c for this function? */
256 DPQ_DECL_DELETECB( cbDeleteElemFromHeap, LPVOID )
257 {
258   HeapFree( GetProcessHeap(), 0, elem );
259 }
260
261 static BOOL DP_DestroyDirectPlay2( LPVOID lpDP )
262 {
263   IDirectPlay2AImpl *This = lpDP;
264
265   if( This->dp2->hEnumSessionThread != INVALID_HANDLE_VALUE )
266   {
267     TerminateThread( This->dp2->hEnumSessionThread, 0 );
268     CloseHandle( This->dp2->hEnumSessionThread );
269   }
270
271   /* Finish with the SP - have it shutdown */
272   if( This->dp2->spData.lpCB->ShutdownEx )
273   {
274     DPSP_SHUTDOWNDATA data;
275
276     TRACE( "Calling SP ShutdownEx\n" );
277
278     data.lpISP = This->dp2->spData.lpISP;
279
280     (*This->dp2->spData.lpCB->ShutdownEx)( &data );
281   }
282   else if (This->dp2->spData.lpCB->Shutdown ) /* obsolete interface */
283   {
284     TRACE( "Calling obsolete SP Shutdown\n" );
285     (*This->dp2->spData.lpCB->Shutdown)();
286   }
287
288   /* Unload the SP (if it exists) */
289   if( This->dp2->hServiceProvider != 0 )
290   {
291     FreeLibrary( This->dp2->hServiceProvider );
292   }
293
294   /* Unload the Lobby Provider (if it exists) */
295   if( This->dp2->hDPLobbyProvider != 0 )
296   {
297     FreeLibrary( This->dp2->hDPLobbyProvider );
298   }
299
300   /* FIXME: Need to delete receive and send msgs queue contents */
301
302   NS_DeleteSessionCache( This->dp2->lpNameServerData );
303
304   HeapFree( GetProcessHeap(), 0, This->dp2->lpSessionDesc );
305
306   IDirectPlaySP_Release( This->dp2->spData.lpISP );
307
308   /* Delete the contents */
309   HeapFree( GetProcessHeap(), 0, This->dp2 );
310
311   return TRUE;
312 }
313
314 static void dplay_destroy(IDirectPlayImpl *obj)
315 {
316      DP_DestroyDirectPlay2( obj );
317      obj->lock.DebugInfo->Spare[0] = 0;
318      DeleteCriticalSection( &obj->lock );
319      HeapFree( GetProcessHeap(), 0, obj );
320 }
321
322 static inline DPID DP_NextObjectId(void)
323 {
324   return (DPID)InterlockedIncrement( &kludgePlayerGroupId );
325 }
326
327 /* *lplpReply will be non NULL iff there is something to reply */
328 HRESULT DP_HandleMessage( IDirectPlay2Impl* This, LPCVOID lpcMessageBody,
329                           DWORD  dwMessageBodySize, LPCVOID lpcMessageHeader,
330                           WORD wCommandId, WORD wVersion,
331                           LPVOID* lplpReply, LPDWORD lpdwMsgSize )
332 {
333   TRACE( "(%p)->(%p,0x%08x,%p,%u,%u)\n",
334          This, lpcMessageBody, dwMessageBodySize, lpcMessageHeader, wCommandId,
335          wVersion );
336
337   switch( wCommandId )
338   {
339     /* Name server needs to handle this request */
340     case DPMSGCMD_ENUMSESSIONSREQUEST:
341       /* Reply expected */
342       NS_ReplyToEnumSessionsRequest( lpcMessageBody, lplpReply, lpdwMsgSize, This );
343       break;
344
345     /* Name server needs to handle this request */
346     case DPMSGCMD_ENUMSESSIONSREPLY:
347       /* No reply expected */
348       NS_AddRemoteComputerAsNameServer( lpcMessageHeader,
349                                         This->dp2->spData.dwSPHeaderSize,
350                                         lpcMessageBody,
351                                         This->dp2->lpNameServerData );
352       break;
353
354     case DPMSGCMD_REQUESTNEWPLAYERID:
355     {
356       LPCDPMSG_REQUESTNEWPLAYERID lpcMsg = lpcMessageBody;
357
358       LPDPMSG_NEWPLAYERIDREPLY lpReply;
359
360       *lpdwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpReply );
361
362       *lplpReply = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, *lpdwMsgSize );
363
364       FIXME( "Ignoring dwFlags 0x%08x in request msg\n",
365              lpcMsg->dwFlags );
366
367       /* Setup the reply */
368       lpReply = (LPDPMSG_NEWPLAYERIDREPLY)( (BYTE*)(*lplpReply) +
369                                             This->dp2->spData.dwSPHeaderSize );
370
371       lpReply->envelope.dwMagic    = DPMSGMAGIC_DPLAYMSG;
372       lpReply->envelope.wCommandId = DPMSGCMD_NEWPLAYERIDREPLY;
373       lpReply->envelope.wVersion   = DPMSGVER_DP6;
374
375       lpReply->dpidNewPlayerId = DP_NextObjectId();
376
377       TRACE( "Allocating new playerid 0x%08x from remote request\n",
378              lpReply->dpidNewPlayerId );
379       break;
380     }
381
382     case DPMSGCMD_GETNAMETABLEREPLY:
383     case DPMSGCMD_NEWPLAYERIDREPLY:
384 #if 0
385       if( wCommandId == DPMSGCMD_NEWPLAYERIDREPLY )
386         DebugBreak();
387 #endif
388       DP_MSG_ReplyReceived( This, wCommandId, lpcMessageBody, dwMessageBodySize );
389       break;
390
391 #if 1
392     case DPMSGCMD_JUSTENVELOPE:
393       TRACE( "GOT THE SELF MESSAGE: %p -> 0x%08x\n", lpcMessageHeader, ((const DWORD *)lpcMessageHeader)[1] );
394       NS_SetLocalAddr( This->dp2->lpNameServerData, lpcMessageHeader, 20 );
395       DP_MSG_ReplyReceived( This, wCommandId, lpcMessageBody, dwMessageBodySize );
396 #endif
397
398     case DPMSGCMD_FORWARDADDPLAYER:
399 #if 0
400       DebugBreak();
401 #endif
402 #if 1
403       TRACE( "Sending message to self to get my addr\n" );
404       DP_MSG_ToSelf( This, 1 ); /* This is a hack right now */
405 #endif
406       break;
407
408     case DPMSGCMD_FORWARDADDPLAYERNACK:
409       DP_MSG_ErrorReceived( This, wCommandId, lpcMessageBody, dwMessageBodySize );
410       break;
411
412     default:
413       FIXME( "Unknown wCommandId %u. Ignoring message\n", wCommandId );
414       DebugBreak();
415       break;
416   }
417
418   /* FIXME: There is code in dplaysp.c to handle dplay commands. Move to here. */
419
420   return DP_OK;
421 }
422
423
424 static HRESULT WINAPI IDirectPlay4AImpl_QueryInterface( IDirectPlay4A *iface, REFIID riid,
425         void **ppv )
426 {
427     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
428     return IDirectPlayX_QueryInterface( &This->IDirectPlay4_iface, riid, ppv );
429 }
430
431 static HRESULT WINAPI IDirectPlay4Impl_QueryInterface( IDirectPlay4 *iface, REFIID riid,
432         void **ppv )
433 {
434     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
435
436     if ( IsEqualGUID( &IID_IUnknown, riid ) )
437     {
438         TRACE( "(%p)->(IID_IUnknown %p)\n", This, ppv );
439         *ppv = &This->IDirectPlay4A_iface;
440     }
441     else if ( IsEqualGUID( &IID_IDirectPlay2A, riid ) )
442     {
443         TRACE( "(%p)->(IID_IDirectPlay2A %p)\n", This, ppv );
444         *ppv = &This->IDirectPlay4A_iface;
445     }
446     else if ( IsEqualGUID( &IID_IDirectPlay2, riid ) )
447     {
448         TRACE( "(%p)->(IID_IDirectPlay2 %p)\n", This, ppv );
449         *ppv = &This->IDirectPlay4_iface;
450     }
451     else if ( IsEqualGUID( &IID_IDirectPlay3A, riid ) )
452     {
453         TRACE( "(%p)->(IID_IDirectPlay3A %p)\n", This, ppv );
454         *ppv = &This->IDirectPlay4A_iface;
455     }
456     else if ( IsEqualGUID( &IID_IDirectPlay3, riid ) )
457     {
458         TRACE( "(%p)->(IID_IDirectPlay3 %p)\n", This, ppv );
459         *ppv = &This->IDirectPlay4_iface;
460     }
461     else if ( IsEqualGUID( &IID_IDirectPlay4A, riid ) )
462     {
463         TRACE( "(%p)->(IID_IDirectPlay4A %p)\n", This, ppv );
464         *ppv = &This->IDirectPlay4A_iface;
465     }
466     else if ( IsEqualGUID( &IID_IDirectPlay4, riid ) )
467     {
468         TRACE( "(%p)->(IID_IDirectPlay4 %p)\n", This, ppv );
469         *ppv = &This->IDirectPlay4_iface;
470     }
471     else
472     {
473         WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
474         *ppv = NULL;
475         return E_NOINTERFACE;
476     }
477
478     IUnknown_AddRef((IUnknown*)*ppv);
479     return S_OK;
480 }
481
482 static ULONG WINAPI IDirectPlay4AImpl_AddRef(IDirectPlay4A *iface)
483 {
484     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
485     ULONG ref = InterlockedIncrement( &This->ref4A );
486
487     TRACE( "(%p) ref4A=%d\n", This, ref );
488
489     if ( ref == 1 )
490         InterlockedIncrement( &This->numIfaces );
491
492     return ref;
493 }
494
495 static ULONG WINAPI IDirectPlay4Impl_AddRef(IDirectPlay4 *iface)
496 {
497     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
498     ULONG ref = InterlockedIncrement( &This->ref4 );
499
500     TRACE( "(%p) ref4=%d\n", This, ref );
501
502     if ( ref == 1 )
503         InterlockedIncrement( &This->numIfaces );
504
505     return ref;
506 }
507
508 static ULONG WINAPI IDirectPlay4AImpl_Release(IDirectPlay4A *iface)
509 {
510     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
511     ULONG ref = InterlockedDecrement( &This->ref4A );
512
513     TRACE( "(%p) ref4A=%d\n", This, ref );
514
515     if ( !ref && !InterlockedDecrement( &This->numIfaces ) )
516         dplay_destroy( This );
517
518     return ref;
519 }
520
521 static ULONG WINAPI IDirectPlay4Impl_Release(IDirectPlay4 *iface)
522 {
523     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
524     ULONG ref = InterlockedDecrement( &This->ref4 );
525
526     TRACE( "(%p) ref4=%d\n", This, ref );
527
528     if ( !ref && !InterlockedDecrement( &This->numIfaces ) )
529         dplay_destroy( This );
530
531     return ref;
532 }
533
534 static HRESULT WINAPI IDirectPlay4AImpl_AddPlayerToGroup( IDirectPlay4A *iface, DPID group,
535         DPID player )
536 {
537     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
538     return IDirectPlayX_AddPlayerToGroup( &This->IDirectPlay4_iface, group, player );
539 }
540
541 static HRESULT WINAPI IDirectPlay4Impl_AddPlayerToGroup( IDirectPlay4 *iface, DPID group,
542         DPID player )
543 {
544     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
545     lpGroupData  gdata;
546     lpPlayerList plist;
547     lpPlayerList newplist;
548
549     TRACE( "(%p)->(0x%08x,0x%08x)\n", This, group, player );
550
551     if ( This->dp2->connectionInitialized == NO_PROVIDER )
552         return DPERR_UNINITIALIZED;
553
554     /* Find the group */
555     if ( ( gdata = DP_FindAnyGroup( This, group ) ) == NULL )
556         return DPERR_INVALIDGROUP;
557
558     /* Find the player */
559     if ( ( plist = DP_FindPlayer( This, player ) ) == NULL )
560         return DPERR_INVALIDPLAYER;
561
562     /* Create a player list (ie "shortcut" ) */
563     newplist = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *newplist ) );
564     if ( !newplist )
565         return DPERR_CANTADDPLAYER;
566
567     /* Add the shortcut */
568     plist->lpPData->uRef++;
569     newplist->lpPData = plist->lpPData;
570
571     /* Add the player to the list of players for this group */
572     DPQ_INSERT(gdata->players, newplist, players);
573
574     /* Let the SP know that we've added a player to the group */
575     if ( This->dp2->spData.lpCB->AddPlayerToGroup )
576     {
577         DPSP_ADDPLAYERTOGROUPDATA data;
578
579         TRACE( "Calling SP AddPlayerToGroup\n" );
580
581         data.idPlayer = player;
582         data.idGroup  = group;
583         data.lpISP    = This->dp2->spData.lpISP;
584
585         (*This->dp2->spData.lpCB->AddPlayerToGroup)( &data );
586     }
587
588     /* Inform all other peers of the addition of player to the group. If there are
589      * no peers keep this event quiet.
590      * Also, if this event was the result of another machine sending it to us,
591      * don't bother rebroadcasting it.
592      */
593     if ( This->dp2->lpSessionDesc &&
594             ( This->dp2->lpSessionDesc->dwFlags & DPSESSION_MULTICASTSERVER ) )
595     {
596         DPMSG_ADDPLAYERTOGROUP msg;
597         msg.dwType = DPSYS_ADDPLAYERTOGROUP;
598
599         msg.dpIdGroup  = group;
600         msg.dpIdPlayer = player;
601
602         /* FIXME: Correct to just use send effectively? */
603         /* FIXME: Should size include data w/ message or just message "header" */
604         /* FIXME: Check return code */
605         IDirectPlayX_SendEx( iface, DPID_SERVERPLAYER, DPID_ALLPLAYERS, 0, &msg, sizeof( msg ),
606                 0, 0, NULL, NULL );
607     }
608
609     return DP_OK;
610 }
611
612 static HRESULT WINAPI IDirectPlay4AImpl_Close( IDirectPlay4A *iface )
613 {
614     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
615     return IDirectPlayX_Close( &This->IDirectPlay4_iface);
616 }
617
618 static HRESULT WINAPI IDirectPlay4Impl_Close( IDirectPlay4 *iface )
619 {
620     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
621     HRESULT hr = DP_OK;
622
623     TRACE( "(%p)", This );
624
625     /* FIXME: Need to find a new host I assume (how?) */
626     /* FIXME: Need to destroy all local groups */
627     /* FIXME: Need to migrate all remotely visible players to the new host */
628
629     /* Invoke the SP callback to inform of session close */
630     if( This->dp2->spData.lpCB->CloseEx )
631     {
632         DPSP_CLOSEDATA data;
633
634         TRACE( "Calling SP CloseEx\n" );
635         data.lpISP = This->dp2->spData.lpISP;
636         hr = (*This->dp2->spData.lpCB->CloseEx)( &data );
637     }
638     else if ( This->dp2->spData.lpCB->Close ) /* Try obsolete version */
639     {
640         TRACE( "Calling SP Close (obsolete interface)\n" );
641         hr = (*This->dp2->spData.lpCB->Close)();
642     }
643
644     return hr;
645 }
646
647 static
648 lpGroupData DP_CreateGroup( IDirectPlay2AImpl* This, const DPID *lpid,
649                             const DPNAME *lpName, DWORD dwFlags,
650                             DPID idParent, BOOL bAnsi )
651 {
652   lpGroupData lpGData;
653
654   /* Allocate the new space and add to end of high level group list */
655   lpGData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *lpGData ) );
656
657   if( lpGData == NULL )
658   {
659     return NULL;
660   }
661
662   DPQ_INIT(lpGData->groups);
663   DPQ_INIT(lpGData->players);
664
665   /* Set the desired player ID - no sanity checking to see if it exists */
666   lpGData->dpid = *lpid;
667
668   DP_CopyDPNAMEStruct( &lpGData->name, lpName, bAnsi );
669
670   /* FIXME: Should we check that the parent exists? */
671   lpGData->parent  = idParent;
672
673   /* FIXME: Should we validate the dwFlags? */
674   lpGData->dwFlags = dwFlags;
675
676   TRACE( "Created group id 0x%08x\n", *lpid );
677
678   return lpGData;
679 }
680
681 /* This method assumes that all links to it are already deleted */
682 static void
683 DP_DeleteGroup( IDirectPlay2Impl* This, DPID dpid )
684 {
685   lpGroupList lpGList;
686
687   TRACE( "(%p)->(0x%08x)\n", This, dpid );
688
689   DPQ_REMOVE_ENTRY( This->dp2->lpSysGroup->groups, groups, lpGData->dpid, ==, dpid, lpGList );
690
691   if( lpGList == NULL )
692   {
693     ERR( "DPID 0x%08x not found\n", dpid );
694     return;
695   }
696
697   if( --(lpGList->lpGData->uRef) )
698   {
699     FIXME( "Why is this not the last reference to group?\n" );
700     DebugBreak();
701   }
702
703   /* Delete player */
704   DP_DeleteDPNameStruct( &lpGList->lpGData->name );
705   HeapFree( GetProcessHeap(), 0, lpGList->lpGData );
706
707   /* Remove and Delete Player List object */
708   HeapFree( GetProcessHeap(), 0, lpGList );
709
710 }
711
712 static lpGroupData DP_FindAnyGroup( IDirectPlay2AImpl* This, DPID dpid )
713 {
714   lpGroupList lpGroups;
715
716   TRACE( "(%p)->(0x%08x)\n", This, dpid );
717
718   if( dpid == DPID_SYSTEM_GROUP )
719   {
720     return This->dp2->lpSysGroup;
721   }
722   else
723   {
724     DPQ_FIND_ENTRY( This->dp2->lpSysGroup->groups, groups, lpGData->dpid, ==, dpid, lpGroups );
725   }
726
727   if( lpGroups == NULL )
728   {
729     return NULL;
730   }
731
732   return lpGroups->lpGData;
733 }
734
735 static HRESULT DP_IF_CreateGroup
736           ( IDirectPlay2AImpl* This, LPVOID lpMsgHdr, LPDPID lpidGroup,
737             LPDPNAME lpGroupName, LPVOID lpData, DWORD dwDataSize,
738             DWORD dwFlags, BOOL bAnsi )
739 {
740   lpGroupData lpGData;
741
742   TRACE( "(%p)->(%p,%p,%p,%p,0x%08x,0x%08x,%u)\n",
743          This, lpMsgHdr, lpidGroup, lpGroupName, lpData, dwDataSize,
744          dwFlags, bAnsi );
745
746   if( This->dp2->connectionInitialized == NO_PROVIDER )
747   {
748     return DPERR_UNINITIALIZED;
749   }
750
751   /* If the name is not specified, we must provide one */
752   if( DPID_UNKNOWN == *lpidGroup )
753   {
754     /* If we are the name server, we decide on the group ids. If not, we
755      * must ask for one before attempting a creation.
756      */
757     if( This->dp2->bHostInterface )
758     {
759       *lpidGroup = DP_NextObjectId();
760     }
761     else
762     {
763       *lpidGroup = DP_GetRemoteNextObjectId();
764     }
765   }
766
767   lpGData = DP_CreateGroup( This, lpidGroup, lpGroupName, dwFlags,
768                             DPID_NOPARENT_GROUP, bAnsi );
769
770   if( lpGData == NULL )
771   {
772     return DPERR_CANTADDPLAYER; /* yes player not group */
773   }
774
775   if( DPID_SYSTEM_GROUP == *lpidGroup )
776   {
777     This->dp2->lpSysGroup = lpGData;
778     TRACE( "Inserting system group\n" );
779   }
780   else
781   {
782     /* Insert into the system group */
783     lpGroupList lpGroup = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *lpGroup ) );
784     lpGroup->lpGData = lpGData;
785
786     DPQ_INSERT( This->dp2->lpSysGroup->groups, lpGroup, groups );
787   }
788
789   /* Something is now referencing this data */
790   lpGData->uRef++;
791
792   /* Set all the important stuff for the group */
793   DP_SetGroupData( lpGData, DPSET_REMOTE, lpData, dwDataSize );
794
795   /* FIXME: We should only create the system group if GetCaps returns
796    *        DPCAPS_GROUPOPTIMIZED.
797    */
798
799   /* Let the SP know that we've created this group */
800   if( This->dp2->spData.lpCB->CreateGroup )
801   {
802     DPSP_CREATEGROUPDATA data;
803     DWORD dwCreateFlags = 0;
804
805     TRACE( "Calling SP CreateGroup\n" );
806
807     if( *lpidGroup == DPID_NOPARENT_GROUP )
808       dwCreateFlags |= DPLAYI_GROUP_SYSGROUP;
809
810     if( lpMsgHdr == NULL )
811       dwCreateFlags |= DPLAYI_PLAYER_PLAYERLOCAL;
812
813     if( dwFlags & DPGROUP_HIDDEN )
814       dwCreateFlags |= DPLAYI_GROUP_HIDDEN;
815
816     data.idGroup           = *lpidGroup;
817     data.dwFlags           = dwCreateFlags;
818     data.lpSPMessageHeader = lpMsgHdr;
819     data.lpISP             = This->dp2->spData.lpISP;
820
821     (*This->dp2->spData.lpCB->CreateGroup)( &data );
822   }
823
824   /* Inform all other peers of the creation of a new group. If there are
825    * no peers keep this event quiet.
826    * Also if this message was sent to us, don't rebroadcast.
827    */
828   if( ( lpMsgHdr == NULL ) &&
829       This->dp2->lpSessionDesc &&
830       ( This->dp2->lpSessionDesc->dwFlags & DPSESSION_MULTICASTSERVER ) )
831   {
832     DPMSG_CREATEPLAYERORGROUP msg;
833     msg.dwType = DPSYS_CREATEPLAYERORGROUP;
834
835     msg.dwPlayerType     = DPPLAYERTYPE_GROUP;
836     msg.dpId             = *lpidGroup;
837     msg.dwCurrentPlayers = 0; /* FIXME: Incorrect? */
838     msg.lpData           = lpData;
839     msg.dwDataSize       = dwDataSize;
840     msg.dpnName          = *lpGroupName;
841     msg.dpIdParent       = DPID_NOPARENT_GROUP;
842     msg.dwFlags          = DPMSG_CREATEGROUP_DWFLAGS( dwFlags );
843
844     /* FIXME: Correct to just use send effectively? */
845     /* FIXME: Should size include data w/ message or just message "header" */
846     /* FIXME: Check return code */
847     DP_SendEx( This, DPID_SERVERPLAYER, DPID_ALLPLAYERS, 0, &msg, sizeof( msg ),
848                0, 0, NULL, NULL, bAnsi );
849   }
850
851   return DP_OK;
852 }
853
854 static HRESULT WINAPI IDirectPlay4AImpl_CreateGroup( IDirectPlay4A *iface, DPID *lpidGroup,
855         DPNAME *lpGroupName, void *lpData, DWORD dwDataSize, DWORD dwFlags )
856 {
857     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
858
859     *lpidGroup = DPID_UNKNOWN;
860
861     return DP_IF_CreateGroup( This, NULL, lpidGroup, lpGroupName, lpData, dwDataSize, dwFlags,
862             TRUE );
863 }
864
865 static HRESULT WINAPI DirectPlay2WImpl_CreateGroup
866           ( LPDIRECTPLAY2 iface, LPDPID lpidGroup, LPDPNAME lpGroupName,
867             LPVOID lpData, DWORD dwDataSize, DWORD dwFlags )
868 {
869   *lpidGroup = DPID_UNKNOWN;
870
871   return DP_IF_CreateGroup( (IDirectPlay2AImpl*)iface, NULL, lpidGroup,
872                             lpGroupName, lpData, dwDataSize, dwFlags, FALSE );
873 }
874
875
876 static void
877 DP_SetGroupData( lpGroupData lpGData, DWORD dwFlags,
878                  LPVOID lpData, DWORD dwDataSize )
879 {
880   /* Clear out the data with this player */
881   if( dwFlags & DPSET_LOCAL )
882   {
883     if ( lpGData->dwLocalDataSize != 0 )
884     {
885       HeapFree( GetProcessHeap(), 0, lpGData->lpLocalData );
886       lpGData->lpLocalData = NULL;
887       lpGData->dwLocalDataSize = 0;
888     }
889   }
890   else
891   {
892     if( lpGData->dwRemoteDataSize != 0 )
893     {
894       HeapFree( GetProcessHeap(), 0, lpGData->lpRemoteData );
895       lpGData->lpRemoteData = NULL;
896       lpGData->dwRemoteDataSize = 0;
897     }
898   }
899
900   /* Reallocate for new data */
901   if( lpData != NULL )
902   {
903     if( dwFlags & DPSET_LOCAL )
904     {
905       lpGData->lpLocalData     = lpData;
906       lpGData->dwLocalDataSize = dwDataSize;
907     }
908     else
909     {
910       lpGData->lpRemoteData = HeapAlloc( GetProcessHeap(), 0, dwDataSize );
911       CopyMemory( lpGData->lpRemoteData, lpData, dwDataSize );
912       lpGData->dwRemoteDataSize = dwDataSize;
913     }
914   }
915
916 }
917
918 /* This function will just create the storage for the new player.  */
919 static
920 lpPlayerData DP_CreatePlayer( IDirectPlay2Impl* This, LPDPID lpid,
921                               LPDPNAME lpName, DWORD dwFlags,
922                               HANDLE hEvent, BOOL bAnsi )
923 {
924   lpPlayerData lpPData;
925
926   TRACE( "(%p)->(%p,%p,%u)\n", This, lpid, lpName, bAnsi );
927
928   /* Allocate the storage for the player and associate it with list element */
929   lpPData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *lpPData ) );
930   if( lpPData == NULL )
931   {
932     return NULL;
933   }
934
935   /* Set the desired player ID */
936   lpPData->dpid = *lpid;
937
938   DP_CopyDPNAMEStruct( &lpPData->name, lpName, bAnsi );
939
940   lpPData->dwFlags = dwFlags;
941
942   /* If we were given an event handle, duplicate it */
943   if( hEvent != 0 )
944   {
945     if( !DuplicateHandle( GetCurrentProcess(), hEvent,
946                           GetCurrentProcess(), &lpPData->hEvent,
947                           0, FALSE, DUPLICATE_SAME_ACCESS )
948       )
949     {
950       /* FIXME: Memory leak */
951       ERR( "Can't duplicate player msg handle %p\n", hEvent );
952     }
953   }
954
955   /* Initialize the SP data section */
956   lpPData->lpSPPlayerData = DPSP_CreateSPPlayerData();
957
958   TRACE( "Created player id 0x%08x\n", *lpid );
959
960   if( ~dwFlags & DPLAYI_PLAYER_SYSPLAYER )
961     This->dp2->lpSessionDesc->dwCurrentPlayers++;
962
963   return lpPData;
964 }
965
966 /* Delete the contents of the DPNAME struct */
967 static void
968 DP_DeleteDPNameStruct( LPDPNAME lpDPName )
969 {
970   HeapFree( GetProcessHeap(), HEAP_ZERO_MEMORY, lpDPName->u1.lpszShortNameA );
971   HeapFree( GetProcessHeap(), HEAP_ZERO_MEMORY, lpDPName->u2.lpszLongNameA );
972 }
973
974 /* This method assumes that all links to it are already deleted */
975 static void
976 DP_DeletePlayer( IDirectPlay2Impl* This, DPID dpid )
977 {
978   lpPlayerList lpPList;
979
980   TRACE( "(%p)->(0x%08x)\n", This, dpid );
981
982   DPQ_REMOVE_ENTRY( This->dp2->lpSysGroup->players, players, lpPData->dpid, ==, dpid, lpPList );
983
984   if( lpPList == NULL )
985   {
986     ERR( "DPID 0x%08x not found\n", dpid );
987     return;
988   }
989
990   /* Verify that this is the last reference to the data */
991   if( --(lpPList->lpPData->uRef) )
992   {
993     FIXME( "Why is this not the last reference to player?\n" );
994     DebugBreak();
995   }
996
997   /* Delete player */
998   DP_DeleteDPNameStruct( &lpPList->lpPData->name );
999
1000   CloseHandle( lpPList->lpPData->hEvent );
1001   HeapFree( GetProcessHeap(), 0, lpPList->lpPData );
1002
1003   /* Delete Player List object */
1004   HeapFree( GetProcessHeap(), 0, lpPList );
1005 }
1006
1007 static lpPlayerList DP_FindPlayer( IDirectPlay2AImpl* This, DPID dpid )
1008 {
1009   lpPlayerList lpPlayers;
1010
1011   TRACE( "(%p)->(0x%08x)\n", This, dpid );
1012
1013   if(This->dp2->lpSysGroup == NULL)
1014     return NULL;
1015
1016   DPQ_FIND_ENTRY( This->dp2->lpSysGroup->players, players, lpPData->dpid, ==, dpid, lpPlayers );
1017
1018   return lpPlayers;
1019 }
1020
1021 /* Basic area for Dst must already be allocated */
1022 static BOOL DP_CopyDPNAMEStruct( LPDPNAME lpDst, const DPNAME *lpSrc, BOOL bAnsi )
1023 {
1024   if( lpSrc == NULL )
1025   {
1026     ZeroMemory( lpDst, sizeof( *lpDst ) );
1027     lpDst->dwSize = sizeof( *lpDst );
1028     return TRUE;
1029   }
1030
1031   if( lpSrc->dwSize != sizeof( *lpSrc) )
1032   {
1033     return FALSE;
1034   }
1035
1036   /* Delete any existing pointers */
1037   HeapFree( GetProcessHeap(), 0, lpDst->u1.lpszShortNameA );
1038   HeapFree( GetProcessHeap(), 0, lpDst->u2.lpszLongNameA );
1039
1040   /* Copy as required */
1041   CopyMemory( lpDst, lpSrc, lpSrc->dwSize );
1042
1043   if( bAnsi )
1044   {
1045     if( lpSrc->u1.lpszShortNameA )
1046     {
1047         lpDst->u1.lpszShortNameA = HeapAlloc( GetProcessHeap(), 0,
1048                                              strlen(lpSrc->u1.lpszShortNameA)+1 );
1049         strcpy( lpDst->u1.lpszShortNameA, lpSrc->u1.lpszShortNameA );
1050     }
1051     if( lpSrc->u2.lpszLongNameA )
1052     {
1053         lpDst->u2.lpszLongNameA = HeapAlloc( GetProcessHeap(), 0,
1054                                               strlen(lpSrc->u2.lpszLongNameA)+1 );
1055         strcpy( lpDst->u2.lpszLongNameA, lpSrc->u2.lpszLongNameA );
1056     }
1057   }
1058   else
1059   {
1060     if( lpSrc->u1.lpszShortNameA )
1061     {
1062         lpDst->u1.lpszShortName = HeapAlloc( GetProcessHeap(), 0,
1063                                               (strlenW(lpSrc->u1.lpszShortName)+1)*sizeof(WCHAR) );
1064         strcpyW( lpDst->u1.lpszShortName, lpSrc->u1.lpszShortName );
1065     }
1066     if( lpSrc->u2.lpszLongNameA )
1067     {
1068         lpDst->u2.lpszLongName = HeapAlloc( GetProcessHeap(), 0,
1069                                              (strlenW(lpSrc->u2.lpszLongName)+1)*sizeof(WCHAR) );
1070         strcpyW( lpDst->u2.lpszLongName, lpSrc->u2.lpszLongName );
1071     }
1072   }
1073
1074   return TRUE;
1075 }
1076
1077 static void
1078 DP_SetPlayerData( lpPlayerData lpPData, DWORD dwFlags,
1079                   LPVOID lpData, DWORD dwDataSize )
1080 {
1081   /* Clear out the data with this player */
1082   if( dwFlags & DPSET_LOCAL )
1083   {
1084     if ( lpPData->dwLocalDataSize != 0 )
1085     {
1086       HeapFree( GetProcessHeap(), 0, lpPData->lpLocalData );
1087       lpPData->lpLocalData = NULL;
1088       lpPData->dwLocalDataSize = 0;
1089     }
1090   }
1091   else
1092   {
1093     if( lpPData->dwRemoteDataSize != 0 )
1094     {
1095       HeapFree( GetProcessHeap(), 0, lpPData->lpRemoteData );
1096       lpPData->lpRemoteData = NULL;
1097       lpPData->dwRemoteDataSize = 0;
1098     }
1099   }
1100
1101   /* Reallocate for new data */
1102   if( lpData != NULL )
1103   {
1104
1105     if( dwFlags & DPSET_LOCAL )
1106     {
1107       lpPData->lpLocalData     = lpData;
1108       lpPData->dwLocalDataSize = dwDataSize;
1109     }
1110     else
1111     {
1112       lpPData->lpRemoteData = HeapAlloc( GetProcessHeap(), 0, dwDataSize );
1113       CopyMemory( lpPData->lpRemoteData, lpData, dwDataSize );
1114       lpPData->dwRemoteDataSize = dwDataSize;
1115     }
1116   }
1117
1118 }
1119
1120 static HRESULT DP_IF_CreatePlayer
1121 ( IDirectPlay2Impl* This,
1122   LPVOID lpMsgHdr, /* NULL for local creation, non NULL for remote creation */
1123   LPDPID lpidPlayer,
1124   LPDPNAME lpPlayerName,
1125   HANDLE hEvent,
1126   LPVOID lpData,
1127   DWORD dwDataSize,
1128   DWORD dwFlags,
1129   BOOL bAnsi )
1130 {
1131   HRESULT hr = DP_OK;
1132   lpPlayerData lpPData;
1133   lpPlayerList lpPList;
1134   DWORD dwCreateFlags = 0;
1135
1136   TRACE( "(%p)->(%p,%p,%p,%p,0x%08x,0x%08x,%u)\n",
1137          This, lpidPlayer, lpPlayerName, hEvent, lpData,
1138          dwDataSize, dwFlags, bAnsi );
1139   if( This->dp2->connectionInitialized == NO_PROVIDER )
1140   {
1141     return DPERR_UNINITIALIZED;
1142   }
1143
1144   if( dwFlags == 0 )
1145   {
1146     dwFlags = DPPLAYER_SPECTATOR;
1147   }
1148
1149   if( lpidPlayer == NULL )
1150   {
1151     return DPERR_INVALIDPARAMS;
1152   }
1153
1154
1155   /* Determine the creation flags for the player. These will be passed
1156    * to the name server if requesting a player id and to the SP when
1157    * informing it of the player creation
1158    */
1159   {
1160     if( dwFlags & DPPLAYER_SERVERPLAYER )
1161     {
1162       if( *lpidPlayer == DPID_SERVERPLAYER )
1163       {
1164         /* Server player for the host interface */
1165         dwCreateFlags |= DPLAYI_PLAYER_APPSERVER;
1166       }
1167       else if( *lpidPlayer == DPID_NAME_SERVER )
1168       {
1169         /* Name server - master of everything */
1170         dwCreateFlags |= (DPLAYI_PLAYER_NAMESRVR|DPLAYI_PLAYER_SYSPLAYER);
1171       }
1172       else
1173       {
1174         /* Server player for a non host interface */
1175         dwCreateFlags |= DPLAYI_PLAYER_SYSPLAYER;
1176       }
1177     }
1178
1179     if( lpMsgHdr == NULL )
1180       dwCreateFlags |= DPLAYI_PLAYER_PLAYERLOCAL;
1181   }
1182
1183   /* Verify we know how to handle all the flags */
1184   if( !( ( dwFlags & DPPLAYER_SERVERPLAYER ) ||
1185          ( dwFlags & DPPLAYER_SPECTATOR )
1186        )
1187     )
1188   {
1189     /* Assume non fatal failure */
1190     ERR( "unknown dwFlags = 0x%08x\n", dwFlags );
1191   }
1192
1193   /* If the name is not specified, we must provide one */
1194   if( *lpidPlayer == DPID_UNKNOWN )
1195   {
1196     /* If we are the session master, we dish out the group/player ids */
1197     if( This->dp2->bHostInterface )
1198     {
1199       *lpidPlayer = DP_NextObjectId();
1200     }
1201     else
1202     {
1203       hr = DP_MSG_SendRequestPlayerId( This, dwCreateFlags, lpidPlayer );
1204
1205       if( FAILED(hr) )
1206       {
1207         ERR( "Request for ID failed: %s\n", DPLAYX_HresultToString( hr ) );
1208         return hr;
1209       }
1210     }
1211   }
1212   else
1213   {
1214     /* FIXME: Would be nice to perhaps verify that we don't already have
1215      *        this player.
1216      */
1217   }
1218
1219   /* We pass creation flags, so we can distinguish sysplayers and not count them in the current
1220      player total */
1221   lpPData = DP_CreatePlayer( This, lpidPlayer, lpPlayerName, dwCreateFlags,
1222                              hEvent, bAnsi );
1223
1224   if( lpPData == NULL )
1225   {
1226     return DPERR_CANTADDPLAYER;
1227   }
1228
1229   /* Create the list object and link it in */
1230   lpPList = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *lpPList ) );
1231   if( lpPList == NULL )
1232   {
1233     FIXME( "Memory leak\n" );
1234     return DPERR_CANTADDPLAYER;
1235   }
1236
1237   lpPData->uRef = 1;
1238   lpPList->lpPData = lpPData;
1239
1240   /* Add the player to the system group */
1241   DPQ_INSERT( This->dp2->lpSysGroup->players, lpPList, players );
1242
1243   /* Update the information and send it to all players in the session */
1244   DP_SetPlayerData( lpPData, DPSET_REMOTE, lpData, dwDataSize );
1245
1246   /* Let the SP know that we've created this player */
1247   if( This->dp2->spData.lpCB->CreatePlayer )
1248   {
1249     DPSP_CREATEPLAYERDATA data;
1250
1251     data.idPlayer          = *lpidPlayer;
1252     data.dwFlags           = dwCreateFlags;
1253     data.lpSPMessageHeader = lpMsgHdr;
1254     data.lpISP             = This->dp2->spData.lpISP;
1255
1256     TRACE( "Calling SP CreatePlayer 0x%08x: dwFlags: 0x%08x lpMsgHdr: %p\n",
1257            *lpidPlayer, data.dwFlags, data.lpSPMessageHeader );
1258
1259     hr = (*This->dp2->spData.lpCB->CreatePlayer)( &data );
1260   }
1261
1262   if( FAILED(hr) )
1263   {
1264     ERR( "Failed to create player with sp: %s\n", DPLAYX_HresultToString(hr) );
1265     return hr;
1266   }
1267
1268   /* Now let the SP know that this player is a member of the system group */
1269   if( This->dp2->spData.lpCB->AddPlayerToGroup )
1270   {
1271     DPSP_ADDPLAYERTOGROUPDATA data;
1272
1273     data.idPlayer = *lpidPlayer;
1274     data.idGroup  = DPID_SYSTEM_GROUP;
1275     data.lpISP    = This->dp2->spData.lpISP;
1276
1277     TRACE( "Calling SP AddPlayerToGroup (sys group)\n" );
1278
1279     hr = (*This->dp2->spData.lpCB->AddPlayerToGroup)( &data );
1280   }
1281
1282   if( FAILED(hr) )
1283   {
1284     ERR( "Failed to add player to sys group with sp: %s\n",
1285          DPLAYX_HresultToString(hr) );
1286     return hr;
1287   }
1288
1289 #if 1
1290   if( This->dp2->bHostInterface == FALSE )
1291   {
1292     /* Let the name server know about the creation of this player */
1293     /* FIXME: Is this only to be done for the creation of a server player or
1294      *        is this used for regular players? If only for server players, move
1295      *        this call to DP_SecureOpen(...);
1296      */
1297 #if 0
1298     TRACE( "Sending message to self to get my addr\n" );
1299     DP_MSG_ToSelf( This, *lpidPlayer ); /* This is a hack right now */
1300 #endif
1301
1302     hr = DP_MSG_ForwardPlayerCreation( This, *lpidPlayer);
1303   }
1304 #else
1305   /* Inform all other peers of the creation of a new player. If there are
1306    * no peers keep this quiet.
1307    * Also, if this was a remote event, no need to rebroadcast it.
1308    */
1309   if( ( lpMsgHdr == NULL ) &&
1310       This->dp2->lpSessionDesc &&
1311       ( This->dp2->lpSessionDesc->dwFlags & DPSESSION_MULTICASTSERVER ) )
1312   {
1313     DPMSG_CREATEPLAYERORGROUP msg;
1314     msg.dwType = DPSYS_CREATEPLAYERORGROUP;
1315
1316     msg.dwPlayerType     = DPPLAYERTYPE_PLAYER;
1317     msg.dpId             = *lpidPlayer;
1318     msg.dwCurrentPlayers = 0; /* FIXME: Incorrect */
1319     msg.lpData           = lpData;
1320     msg.dwDataSize       = dwDataSize;
1321     msg.dpnName          = *lpPlayerName;
1322     msg.dpIdParent       = DPID_NOPARENT_GROUP;
1323     msg.dwFlags          = DPMSG_CREATEPLAYER_DWFLAGS( dwFlags );
1324
1325     /* FIXME: Correct to just use send effectively? */
1326     /* FIXME: Should size include data w/ message or just message "header" */
1327     /* FIXME: Check return code */
1328     hr = DP_SendEx( This, DPID_SERVERPLAYER, DPID_ALLPLAYERS, 0, &msg,
1329                     sizeof( msg ), 0, 0, NULL, NULL, bAnsi );
1330   }
1331 #endif
1332
1333   return hr;
1334 }
1335
1336 static HRESULT WINAPI IDirectPlay4AImpl_CreatePlayer( IDirectPlay4A *iface, DPID *lpidPlayer,
1337         DPNAME *lpPlayerName, HANDLE hEvent, void *lpData, DWORD dwDataSize, DWORD dwFlags )
1338 {
1339   IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
1340
1341   if( lpidPlayer == NULL )
1342   {
1343     return DPERR_INVALIDPARAMS;
1344   }
1345
1346   if( dwFlags & DPPLAYER_SERVERPLAYER )
1347   {
1348     *lpidPlayer = DPID_SERVERPLAYER;
1349   }
1350   else
1351   {
1352     *lpidPlayer = DPID_UNKNOWN;
1353   }
1354
1355   return DP_IF_CreatePlayer( This, NULL, lpidPlayer, lpPlayerName, hEvent,
1356                            lpData, dwDataSize, dwFlags, TRUE );
1357 }
1358
1359 static HRESULT WINAPI DirectPlay2WImpl_CreatePlayer
1360           ( LPDIRECTPLAY2 iface, LPDPID lpidPlayer, LPDPNAME lpPlayerName,
1361             HANDLE hEvent, LPVOID lpData, DWORD dwDataSize, DWORD dwFlags )
1362 {
1363   IDirectPlay2Impl *This = (IDirectPlay2Impl *)iface;
1364
1365   if( lpidPlayer == NULL )
1366   {
1367     return DPERR_INVALIDPARAMS;
1368   }
1369
1370   if( dwFlags & DPPLAYER_SERVERPLAYER )
1371   {
1372     *lpidPlayer = DPID_SERVERPLAYER;
1373   }
1374   else
1375   {
1376     *lpidPlayer = DPID_UNKNOWN;
1377   }
1378
1379   return DP_IF_CreatePlayer( This, NULL, lpidPlayer, lpPlayerName, hEvent,
1380                            lpData, dwDataSize, dwFlags, FALSE );
1381 }
1382
1383 static DPID DP_GetRemoteNextObjectId(void)
1384 {
1385   FIXME( ":stub\n" );
1386
1387   /* Hack solution */
1388   return DP_NextObjectId();
1389 }
1390
1391 static HRESULT WINAPI IDirectPlay4AImpl_DeletePlayerFromGroup( IDirectPlay4A *iface, DPID group,
1392         DPID player )
1393 {
1394     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
1395     return IDirectPlayX_DeletePlayerFromGroup( &This->IDirectPlay4_iface, group, player );
1396 }
1397
1398 static HRESULT WINAPI IDirectPlay4Impl_DeletePlayerFromGroup( IDirectPlay4 *iface, DPID group,
1399         DPID player )
1400 {
1401     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
1402     HRESULT hr = DP_OK;
1403
1404     lpGroupData  gdata;
1405     lpPlayerList plist;
1406
1407     TRACE( "(%p)->(0x%08x,0x%08x)\n", This, group, player );
1408
1409     /* Find the group */
1410     if ( ( gdata = DP_FindAnyGroup( This, group ) ) == NULL )
1411         return DPERR_INVALIDGROUP;
1412
1413     /* Find the player */
1414     if ( ( plist = DP_FindPlayer( This, player ) ) == NULL )
1415         return DPERR_INVALIDPLAYER;
1416
1417     /* Remove the player shortcut from the group */
1418     DPQ_REMOVE_ENTRY( gdata->players, players, lpPData->dpid, ==, player, plist );
1419
1420     if ( !plist )
1421         return DPERR_INVALIDPLAYER;
1422
1423     /* One less reference */
1424     plist->lpPData->uRef--;
1425
1426     /* Delete the Player List element */
1427     HeapFree( GetProcessHeap(), 0, plist );
1428
1429     /* Inform the SP if they care */
1430     if ( This->dp2->spData.lpCB->RemovePlayerFromGroup )
1431     {
1432         DPSP_REMOVEPLAYERFROMGROUPDATA data;
1433
1434         TRACE( "Calling SP RemovePlayerFromGroup\n" );
1435         data.idPlayer = player;
1436         data.idGroup = group;
1437         data.lpISP = This->dp2->spData.lpISP;
1438         hr = (*This->dp2->spData.lpCB->RemovePlayerFromGroup)( &data );
1439     }
1440
1441     /* Need to send a DELETEPLAYERFROMGROUP message */
1442     FIXME( "Need to send a message\n" );
1443
1444     return hr;
1445 }
1446
1447 typedef struct _DPRGOPContext
1448 {
1449   IDirectPlay3Impl* This;
1450   BOOL              bAnsi;
1451   DPID              idGroup;
1452 } DPRGOPContext, *lpDPRGOPContext;
1453
1454 static BOOL CALLBACK
1455 cbRemoveGroupOrPlayer(
1456     DPID            dpId,
1457     DWORD           dwPlayerType,
1458     LPCDPNAME       lpName,
1459     DWORD           dwFlags,
1460     LPVOID          lpContext )
1461 {
1462   lpDPRGOPContext lpCtxt = (lpDPRGOPContext)lpContext;
1463
1464   TRACE( "Removing element:0x%08x (type:0x%08x) from element:0x%08x\n",
1465            dpId, dwPlayerType, lpCtxt->idGroup );
1466
1467   if( dwPlayerType == DPPLAYERTYPE_GROUP )
1468   {
1469     if ( FAILED( IDirectPlayX_DeleteGroupFromGroup( &lpCtxt->This->IDirectPlay4_iface,
1470                     lpCtxt->idGroup, dpId ) ) )
1471       ERR( "Unable to delete group 0x%08x from group 0x%08x\n", dpId, lpCtxt->idGroup );
1472   }
1473   else if ( FAILED( IDirectPlayX_DeletePlayerFromGroup( &lpCtxt->This->IDirectPlay4_iface,
1474                                                         lpCtxt->idGroup, dpId ) ) )
1475       ERR( "Unable to delete player 0x%08x from grp 0x%08x\n", dpId, lpCtxt->idGroup );
1476
1477   return TRUE; /* Continue enumeration */
1478 }
1479
1480 static HRESULT DP_IF_DestroyGroup
1481           ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idGroup, BOOL bAnsi )
1482 {
1483   lpGroupData lpGData;
1484   DPRGOPContext context;
1485
1486   FIXME( "(%p)->(%p,0x%08x,%u): semi stub\n",
1487          This, lpMsgHdr, idGroup, bAnsi );
1488
1489   /* Find the group */
1490   if( ( lpGData = DP_FindAnyGroup( This, idGroup ) ) == NULL )
1491   {
1492     return DPERR_INVALIDPLAYER; /* yes player */
1493   }
1494
1495   context.This    = (IDirectPlay3Impl*)This;
1496   context.bAnsi   = bAnsi;
1497   context.idGroup = idGroup;
1498
1499   /* Remove all players that this group has */
1500   IDirectPlayX_EnumGroupPlayers( &This->IDirectPlay4_iface, idGroup, NULL, cbRemoveGroupOrPlayer,
1501           &context, 0 );
1502
1503   /* Remove all links to groups that this group has since this is dp3 */
1504   IDirectPlayX_EnumGroupsInGroup( &This->IDirectPlay4_iface, idGroup, NULL, cbRemoveGroupOrPlayer,
1505           (void*)&context, 0 );
1506
1507   /* Remove this group from the parent group - if it has one */
1508   if( ( idGroup != DPID_SYSTEM_GROUP ) && ( lpGData->parent != DPID_SYSTEM_GROUP ) )
1509     IDirectPlayX_DeleteGroupFromGroup( &This->IDirectPlay4_iface, lpGData->parent, idGroup );
1510
1511   /* Now delete this group data and list from the system group */
1512   DP_DeleteGroup( This, idGroup );
1513
1514   /* Let the SP know that we've destroyed this group */
1515   if( This->dp2->spData.lpCB->DeleteGroup )
1516   {
1517     DPSP_DELETEGROUPDATA data;
1518
1519     FIXME( "data.dwFlags is incorrect\n" );
1520
1521     data.idGroup = idGroup;
1522     data.dwFlags = 0;
1523     data.lpISP   = This->dp2->spData.lpISP;
1524
1525     (*This->dp2->spData.lpCB->DeleteGroup)( &data );
1526   }
1527
1528   FIXME( "Send out a DESTORYPLAYERORGROUP message\n" );
1529
1530   return DP_OK;
1531 }
1532
1533 static HRESULT WINAPI IDirectPlay4AImpl_DestroyGroup( IDirectPlay4A *iface, DPID idGroup )
1534 {
1535     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
1536     return DP_IF_DestroyGroup( This, NULL, idGroup, TRUE );
1537 }
1538
1539 static HRESULT WINAPI DirectPlay2WImpl_DestroyGroup
1540           ( LPDIRECTPLAY2 iface, DPID idGroup )
1541 {
1542   IDirectPlay2Impl *This = (IDirectPlay2Impl *)iface;
1543   return DP_IF_DestroyGroup( This, NULL, idGroup, FALSE );
1544 }
1545
1546 typedef struct _DPFAGContext
1547 {
1548   IDirectPlay2Impl* This;
1549   DPID              idPlayer;
1550   BOOL              bAnsi;
1551 } DPFAGContext, *lpDPFAGContext;
1552
1553 static HRESULT DP_IF_DestroyPlayer
1554           ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idPlayer, BOOL bAnsi )
1555 {
1556   DPFAGContext cbContext;
1557
1558   FIXME( "(%p)->(%p,0x%08x,%u): semi stub\n",
1559          This, lpMsgHdr, idPlayer, bAnsi );
1560
1561   if( This->dp2->connectionInitialized == NO_PROVIDER )
1562   {
1563     return DPERR_UNINITIALIZED;
1564   }
1565
1566   if( DP_FindPlayer( This, idPlayer ) == NULL )
1567   {
1568     return DPERR_INVALIDPLAYER;
1569   }
1570
1571   /* FIXME: If the player is remote, we must be the host to delete this */
1572
1573   cbContext.This     = This;
1574   cbContext.idPlayer = idPlayer;
1575   cbContext.bAnsi    = bAnsi;
1576
1577   /* Find each group and call DeletePlayerFromGroup if the player is a
1578      member of the group */
1579   IDirectPlayX_EnumGroups( &This->IDirectPlay4_iface, NULL, cbDeletePlayerFromAllGroups, &cbContext,
1580           DPENUMGROUPS_ALL );
1581
1582   /* Now delete player and player list from the sys group */
1583   DP_DeletePlayer( This, idPlayer );
1584
1585   /* Let the SP know that we've destroyed this group */
1586   if( This->dp2->spData.lpCB->DeletePlayer )
1587   {
1588     DPSP_DELETEPLAYERDATA data;
1589
1590     FIXME( "data.dwFlags is incorrect\n" );
1591
1592     data.idPlayer = idPlayer;
1593     data.dwFlags = 0;
1594     data.lpISP   = This->dp2->spData.lpISP;
1595
1596     (*This->dp2->spData.lpCB->DeletePlayer)( &data );
1597   }
1598
1599   FIXME( "Send a DELETEPLAYERORGROUP msg\n" );
1600
1601   return DP_OK;
1602 }
1603
1604 static BOOL CALLBACK
1605 cbDeletePlayerFromAllGroups(
1606     DPID            dpId,
1607     DWORD           dwPlayerType,
1608     LPCDPNAME       lpName,
1609     DWORD           dwFlags,
1610     LPVOID          lpContext )
1611 {
1612   lpDPFAGContext lpCtxt = (lpDPFAGContext)lpContext;
1613
1614   if( dwPlayerType == DPPLAYERTYPE_GROUP )
1615   {
1616     IDirectPlayX_DeletePlayerFromGroup( &lpCtxt->This->IDirectPlay4_iface, dpId, lpCtxt->idPlayer );
1617
1618     /* Enumerate all groups in this group since this will normally only
1619      * be called for top level groups
1620      */
1621     IDirectPlayX_EnumGroupsInGroup( &lpCtxt->This->IDirectPlay4_iface, dpId, NULL,
1622             cbDeletePlayerFromAllGroups, lpContext, DPENUMGROUPS_ALL);
1623
1624   }
1625   else
1626   {
1627     ERR( "Group callback has dwPlayerType = 0x%08x\n", dwPlayerType );
1628   }
1629
1630   return TRUE;
1631 }
1632
1633 static HRESULT WINAPI IDirectPlay4AImpl_DestroyPlayer( IDirectPlay4A *iface, DPID idPlayer )
1634 {
1635     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
1636     return DP_IF_DestroyPlayer( This, NULL, idPlayer, TRUE );
1637 }
1638
1639 static HRESULT WINAPI DirectPlay2WImpl_DestroyPlayer
1640           ( LPDIRECTPLAY2 iface, DPID idPlayer )
1641 {
1642   IDirectPlay2Impl *This = (IDirectPlay2Impl *)iface;
1643   return DP_IF_DestroyPlayer( This, NULL, idPlayer, FALSE );
1644 }
1645
1646 static HRESULT WINAPI IDirectPlay4AImpl_EnumGroupPlayers( IDirectPlay4A *iface, DPID group,
1647         GUID *instance, LPDPENUMPLAYERSCALLBACK2 enumplayercb, void *context, DWORD flags )
1648 {
1649     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
1650     return IDirectPlayX_EnumGroupPlayers( &This->IDirectPlay4_iface, group, instance, enumplayercb,
1651             context, flags );
1652 }
1653
1654 static HRESULT WINAPI IDirectPlay4Impl_EnumGroupPlayers( IDirectPlay4 *iface, DPID group,
1655         GUID *instance, LPDPENUMPLAYERSCALLBACK2 enumplayercb, void *context, DWORD flags )
1656 {
1657     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
1658     lpGroupData  gdata;
1659     lpPlayerList plist;
1660
1661     FIXME( "(%p)->(0x%08x,%p,%p,%p,0x%08x): semi stub\n", This, group, instance, enumplayercb,
1662            context, flags );
1663
1664     if ( This->dp2->connectionInitialized == NO_PROVIDER )
1665         return DPERR_UNINITIALIZED;
1666
1667     /* Find the group */
1668     if ( ( gdata = DP_FindAnyGroup( This, group ) ) == NULL )
1669         return DPERR_INVALIDGROUP;
1670
1671     if ( DPQ_IS_EMPTY( gdata->players ) )
1672         return DP_OK;
1673
1674     /* Walk the players in this group */
1675     for( plist = DPQ_FIRST( gdata->players ); ; plist = DPQ_NEXT( plist->players ) )
1676     {
1677         /* We do not enum the name server or app server as they are of no
1678          * consequence to the end user.
1679          */
1680         if ( ( plist->lpPData->dpid != DPID_NAME_SERVER ) &&
1681                 ( plist->lpPData->dpid != DPID_SERVERPLAYER ) )
1682         {
1683             /* FIXME: Need to add stuff for flags checking */
1684             if ( !enumplayercb( plist->lpPData->dpid, DPPLAYERTYPE_PLAYER,
1685                         &plist->lpPData->name, plist->lpPData->dwFlags, context ) )
1686               /* User requested break */
1687               return DP_OK;
1688         }
1689
1690         if ( DPQ_IS_ENDOFLIST( plist->players ) )
1691             break;
1692     }
1693     return DP_OK;
1694 }
1695
1696 /* NOTE: This only enumerates top level groups (created with CreateGroup) */
1697 static HRESULT WINAPI IDirectPlay4AImpl_EnumGroups( IDirectPlay4A *iface, GUID *instance,
1698         LPDPENUMPLAYERSCALLBACK2 enumplayercb, void *context, DWORD flags )
1699 {
1700     return IDirectPlayX_EnumGroupsInGroup( iface, DPID_SYSTEM_GROUP, instance, enumplayercb,
1701             context, flags );
1702 }
1703
1704 static HRESULT WINAPI IDirectPlay4Impl_EnumGroups ( IDirectPlay4 *iface, GUID *instance,
1705         LPDPENUMPLAYERSCALLBACK2 enumplayercb, void *context, DWORD flags )
1706 {
1707     return IDirectPlayX_EnumGroupsInGroup( iface, DPID_SYSTEM_GROUP, instance, enumplayercb,
1708             context, flags );
1709 }
1710
1711 static HRESULT WINAPI IDirectPlay4AImpl_EnumPlayers( IDirectPlay4A *iface, GUID *instance,
1712         LPDPENUMPLAYERSCALLBACK2 enumplayercb, void *context, DWORD flags )
1713 {
1714     return IDirectPlayX_EnumGroupPlayers( iface, DPID_SYSTEM_GROUP, instance, enumplayercb,
1715             context, flags );
1716 }
1717
1718 static HRESULT WINAPI IDirectPlay4Impl_EnumPlayers( IDirectPlay4 *iface, GUID *instance,
1719         LPDPENUMPLAYERSCALLBACK2 enumplayercb, void *context, DWORD flags )
1720 {
1721     return IDirectPlayX_EnumGroupPlayers( iface, DPID_SYSTEM_GROUP, instance, enumplayercb,
1722             context, flags );
1723 }
1724
1725 /* This function should call the registered callback function that the user
1726    passed into EnumSessions for each entry available.
1727  */
1728 static void DP_InvokeEnumSessionCallbacks
1729        ( LPDPENUMSESSIONSCALLBACK2 lpEnumSessionsCallback2,
1730          LPVOID lpNSInfo,
1731          DWORD dwTimeout,
1732          LPVOID lpContext )
1733 {
1734   LPDPSESSIONDESC2 lpSessionDesc;
1735
1736   FIXME( ": not checking for conditions\n" );
1737
1738   /* Not sure if this should be pruning but it's convenient */
1739   NS_PruneSessionCache( lpNSInfo );
1740
1741   NS_ResetSessionEnumeration( lpNSInfo );
1742
1743   /* Enumerate all sessions */
1744   /* FIXME: Need to indicate ANSI */
1745   while( (lpSessionDesc = NS_WalkSessions( lpNSInfo ) ) != NULL )
1746   {
1747     TRACE( "EnumSessionsCallback2 invoked\n" );
1748     if( !lpEnumSessionsCallback2( lpSessionDesc, &dwTimeout, 0, lpContext ) )
1749     {
1750       return;
1751     }
1752   }
1753
1754   /* Invoke one last time to indicate that there is no more to come */
1755   lpEnumSessionsCallback2( NULL, &dwTimeout, DPESC_TIMEDOUT, lpContext );
1756 }
1757
1758 static DWORD CALLBACK DP_EnumSessionsSendAsyncRequestThread( LPVOID lpContext )
1759 {
1760   EnumSessionAsyncCallbackData* data = lpContext;
1761   HANDLE hSuicideRequest = data->hSuicideRequest;
1762   DWORD dwTimeout = data->dwTimeout;
1763
1764   TRACE( "Thread started with timeout = 0x%08x\n", dwTimeout );
1765
1766   for( ;; )
1767   {
1768     HRESULT hr;
1769
1770     /* Sleep up to dwTimeout waiting for request to terminate thread */
1771     if( WaitForSingleObject( hSuicideRequest, dwTimeout ) == WAIT_OBJECT_0 )
1772     {
1773       TRACE( "Thread terminating on terminate request\n" );
1774       break;
1775     }
1776
1777     /* Now resend the enum request */
1778     hr = NS_SendSessionRequestBroadcast( &data->requestGuid,
1779                                          data->dwEnumSessionFlags,
1780                                          data->lpSpData );
1781
1782     if( FAILED(hr) )
1783     {
1784       ERR( "Enum broadcase request failed: %s\n", DPLAYX_HresultToString(hr) );
1785       /* FIXME: Should we kill this thread? How to inform the main thread? */
1786     }
1787
1788   }
1789
1790   TRACE( "Thread terminating\n" );
1791
1792   /* Clean up the thread data */
1793   CloseHandle( hSuicideRequest );
1794   HeapFree( GetProcessHeap(), 0, lpContext );
1795
1796   /* FIXME: Need to have some notification to main app thread that this is
1797    *        dead. It would serve two purposes. 1) allow sync on termination
1798    *        so that we don't actually send something to ourselves when we
1799    *        become name server (race condition) and 2) so that if we die
1800    *        abnormally something else will be able to tell.
1801    */
1802
1803   return 1;
1804 }
1805
1806 static void DP_KillEnumSessionThread( IDirectPlay2Impl* This )
1807 {
1808   /* Does a thread exist? If so we were doing an async enum session */
1809   if( This->dp2->hEnumSessionThread != INVALID_HANDLE_VALUE )
1810   {
1811     TRACE( "Killing EnumSession thread %p\n",
1812            This->dp2->hEnumSessionThread );
1813
1814     /* Request that the thread kill itself nicely */
1815     SetEvent( This->dp2->hKillEnumSessionThreadEvent );
1816     CloseHandle( This->dp2->hKillEnumSessionThreadEvent );
1817
1818     /* We no longer need to know about the thread */
1819     CloseHandle( This->dp2->hEnumSessionThread );
1820
1821     This->dp2->hEnumSessionThread = INVALID_HANDLE_VALUE;
1822   }
1823 }
1824
1825 static HRESULT WINAPI IDirectPlay4AImpl_EnumSessions( IDirectPlay4A *iface, DPSESSIONDESC2 *sdesc,
1826         DWORD timeout, LPDPENUMSESSIONSCALLBACK2 enumsessioncb, void *context, DWORD flags )
1827 {
1828     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
1829     return IDirectPlayX_EnumSessions( &This->IDirectPlay4_iface, sdesc, timeout, enumsessioncb,
1830             context, flags );
1831 }
1832
1833 static HRESULT WINAPI IDirectPlay4Impl_EnumSessions( IDirectPlay4 *iface, DPSESSIONDESC2 *sdesc,
1834         DWORD timeout, LPDPENUMSESSIONSCALLBACK2 enumsessioncb, void *context, DWORD flags )
1835 {
1836     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
1837     void *connection;
1838     DWORD  size;
1839     HRESULT hr = DP_OK;
1840
1841     TRACE( "(%p)->(%p,0x%08x,%p,%p,0x%08x)\n", This, sdesc, timeout, enumsessioncb,
1842             context, flags );
1843
1844     if ( This->dp2->connectionInitialized == NO_PROVIDER )
1845         return DPERR_UNINITIALIZED;
1846
1847     /* Can't enumerate if the interface is already open */
1848     if ( This->dp2->bConnectionOpen )
1849         return DPERR_GENERIC;
1850
1851     /* The loading of a lobby provider _seems_ to require a backdoor loading
1852      * of the service provider to also associate with this DP object. This is
1853      * because the app doesn't seem to have to call EnumConnections and
1854      * InitializeConnection for the SP before calling this method. As such
1855      * we'll do their dirty work for them with a quick hack so as to always
1856      * load the TCP/IP service provider.
1857      *
1858      * The correct solution would seem to involve creating a dialog box which
1859      * contains the possible SPs. These dialog boxes most likely follow SDK
1860      * examples.
1861      */
1862     if ( This->dp2->bDPLSPInitialized && !This->dp2->bSPInitialized )
1863     {
1864         WARN( "Hack providing TCP/IP SP for lobby provider activated\n" );
1865
1866         if ( !DP_BuildSPCompoundAddr( (GUID*)&DPSPGUID_TCPIP, &connection, &size ) )
1867         {
1868             ERR( "Can't build compound addr\n" );
1869             return DPERR_GENERIC;
1870         }
1871
1872         hr = IDirectPlayX_InitializeConnection( &This->IDirectPlay4_iface, connection, 0 );
1873         if ( FAILED(hr) )
1874             return hr;
1875
1876         HeapFree( GetProcessHeap(), 0, connection );
1877         This->dp2->bSPInitialized = TRUE;
1878     }
1879
1880
1881     /* Use the service provider default? */
1882     if ( !timeout )
1883     {
1884         DPCAPS caps;
1885         caps.dwSize = sizeof( caps );
1886
1887         IDirectPlayX_GetCaps( &This->IDirectPlay4_iface, &caps, 0 );
1888         timeout = caps.dwTimeout;
1889         if ( !timeout )
1890             timeout = DPMSG_WAIT_5_SECS; /* Provide the TCP/IP default */
1891     }
1892
1893     if ( flags & DPENUMSESSIONS_STOPASYNC )
1894     {
1895         DP_KillEnumSessionThread( This );
1896         return hr;
1897     }
1898
1899     if ( flags & DPENUMSESSIONS_ASYNC )
1900     {
1901         /* Enumerate everything presently in the local session cache */
1902         DP_InvokeEnumSessionCallbacks( enumsessioncb, This->dp2->lpNameServerData, timeout,
1903                 context );
1904
1905         if ( This->dp2->dwEnumSessionLock )
1906             return DPERR_CONNECTING;
1907
1908         /* See if we've already created a thread to service this interface */
1909         if ( This->dp2->hEnumSessionThread == INVALID_HANDLE_VALUE )
1910         {
1911             DWORD tid;
1912             This->dp2->dwEnumSessionLock++;
1913
1914             /* Send the first enum request inline since the user may cancel a dialog
1915              * if one is presented. Also, may also have a connecting return code.
1916              */
1917             hr = NS_SendSessionRequestBroadcast( &sdesc->guidApplication, flags,
1918                     &This->dp2->spData );
1919
1920             if ( SUCCEEDED(hr) )
1921             {
1922                 EnumSessionAsyncCallbackData* data = HeapAlloc( GetProcessHeap(),
1923                         HEAP_ZERO_MEMORY, sizeof( *data ) );
1924                 /* FIXME: need to kill the thread on object deletion */
1925                 data->lpSpData  = &This->dp2->spData;
1926                 data->requestGuid = sdesc->guidApplication;
1927                 data->dwEnumSessionFlags = flags;
1928                 data->dwTimeout = timeout;
1929
1930                 This->dp2->hKillEnumSessionThreadEvent = CreateEventW( NULL, TRUE, FALSE, NULL );
1931                 if ( !DuplicateHandle( GetCurrentProcess(), This->dp2->hKillEnumSessionThreadEvent,
1932                             GetCurrentProcess(), &data->hSuicideRequest, 0, FALSE,
1933                             DUPLICATE_SAME_ACCESS ) )
1934                     ERR( "Can't duplicate thread killing handle\n" );
1935
1936                 TRACE( ": creating EnumSessionsRequest thread\n" );
1937                 This->dp2->hEnumSessionThread = CreateThread( NULL, 0,
1938                         DP_EnumSessionsSendAsyncRequestThread, data, 0, &tid );
1939             }
1940             This->dp2->dwEnumSessionLock--;
1941         }
1942     }
1943     else
1944     {
1945         /* Invalidate the session cache for the interface */
1946         NS_InvalidateSessionCache( This->dp2->lpNameServerData );
1947         /* Send the broadcast for session enumeration */
1948         hr = NS_SendSessionRequestBroadcast( &sdesc->guidApplication, flags, &This->dp2->spData );
1949         SleepEx( timeout, FALSE );
1950         DP_InvokeEnumSessionCallbacks( enumsessioncb, This->dp2->lpNameServerData, timeout,
1951                 context );
1952     }
1953
1954     return hr;
1955 }
1956
1957 static HRESULT WINAPI IDirectPlay4AImpl_GetCaps( IDirectPlay4A *iface, DPCAPS *caps, DWORD flags )
1958 {
1959     return IDirectPlayX_GetPlayerCaps( iface, DPID_ALLPLAYERS, caps, flags );
1960 }
1961
1962 static HRESULT WINAPI IDirectPlay4Impl_GetCaps( IDirectPlay4 *iface, DPCAPS *caps, DWORD flags )
1963 {
1964     return IDirectPlayX_GetPlayerCaps( iface, DPID_ALLPLAYERS, caps, flags );
1965 }
1966
1967 static HRESULT WINAPI IDirectPlay4AImpl_GetGroupData( IDirectPlay4A *iface, DPID group,
1968         void *data, DWORD *size, DWORD flags )
1969 {
1970     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
1971     return IDirectPlayX_GetGroupData( &This->IDirectPlay4_iface, group, data, size, flags );
1972 }
1973
1974 static HRESULT WINAPI IDirectPlay4Impl_GetGroupData( IDirectPlay4 *iface, DPID group,
1975         void *data, DWORD *size, DWORD flags )
1976 {
1977     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
1978     lpGroupData gdata;
1979     DWORD bufsize;
1980     void *src;
1981
1982     TRACE( "(%p)->(0x%08x,%p,%p,0x%08x)\n", This, group, data, size, flags );
1983
1984     if ( ( gdata = DP_FindAnyGroup( This, group ) ) == NULL )
1985         return DPERR_INVALIDGROUP;
1986
1987     /* How much buffer is required? */
1988     if ( flags & DPSET_LOCAL )
1989     {
1990         bufsize = gdata->dwLocalDataSize;
1991         src = gdata->lpLocalData;
1992     }
1993     else
1994     {
1995         bufsize = gdata->dwRemoteDataSize;
1996         src = gdata->lpRemoteData;
1997     }
1998
1999     /* Is the user requesting to know how big a buffer is required? */
2000     if ( !data || *size < bufsize )
2001     {
2002         *size = bufsize;
2003         return DPERR_BUFFERTOOSMALL;
2004     }
2005
2006     CopyMemory( data, src, bufsize );
2007
2008     return DP_OK;
2009 }
2010
2011 static HRESULT DP_IF_GetGroupName
2012           ( IDirectPlay2Impl* This, DPID idGroup, LPVOID lpData,
2013             LPDWORD lpdwDataSize, BOOL bAnsi )
2014 {
2015   lpGroupData lpGData;
2016   LPDPNAME    lpName = lpData;
2017   DWORD       dwRequiredDataSize;
2018
2019   FIXME("(%p)->(0x%08x,%p,%p,%u) ANSI ignored\n",
2020           This, idGroup, lpData, lpdwDataSize, bAnsi );
2021
2022   if( ( lpGData = DP_FindAnyGroup( This, idGroup ) ) == NULL )
2023   {
2024     return DPERR_INVALIDGROUP;
2025   }
2026
2027   dwRequiredDataSize = lpGData->name.dwSize;
2028
2029   if( lpGData->name.u1.lpszShortNameA )
2030   {
2031     dwRequiredDataSize += strlen( lpGData->name.u1.lpszShortNameA ) + 1;
2032   }
2033
2034   if( lpGData->name.u2.lpszLongNameA )
2035   {
2036     dwRequiredDataSize += strlen( lpGData->name.u2.lpszLongNameA ) + 1;
2037   }
2038
2039   if( ( lpData == NULL ) ||
2040       ( *lpdwDataSize < dwRequiredDataSize )
2041     )
2042   {
2043     *lpdwDataSize = dwRequiredDataSize;
2044     return DPERR_BUFFERTOOSMALL;
2045   }
2046
2047   /* Copy the structure */
2048   CopyMemory( lpName, &lpGData->name, lpGData->name.dwSize );
2049
2050   if( lpGData->name.u1.lpszShortNameA )
2051   {
2052     strcpy( ((char*)lpName)+lpGData->name.dwSize,
2053             lpGData->name.u1.lpszShortNameA );
2054   }
2055   else
2056   {
2057     lpName->u1.lpszShortNameA = NULL;
2058   }
2059
2060   if( lpGData->name.u1.lpszShortNameA )
2061   {
2062     strcpy( ((char*)lpName)+lpGData->name.dwSize,
2063             lpGData->name.u2.lpszLongNameA );
2064   }
2065   else
2066   {
2067     lpName->u2.lpszLongNameA = NULL;
2068   }
2069
2070   return DP_OK;
2071 }
2072
2073 static HRESULT WINAPI IDirectPlay4AImpl_GetGroupName( IDirectPlay4A *iface, DPID idGroup,
2074         void *lpData, DWORD *lpdwDataSize )
2075 {
2076     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
2077     return DP_IF_GetGroupName( This, idGroup, lpData, lpdwDataSize, TRUE );
2078 }
2079
2080 static HRESULT WINAPI DirectPlay2WImpl_GetGroupName
2081           ( LPDIRECTPLAY2 iface, DPID idGroup, LPVOID lpData,
2082             LPDWORD lpdwDataSize )
2083 {
2084   IDirectPlay2Impl *This = (IDirectPlay2Impl *)iface;
2085   return DP_IF_GetGroupName( This, idGroup, lpData, lpdwDataSize, FALSE );
2086 }
2087
2088 static HRESULT WINAPI IDirectPlay4AImpl_GetMessageCount( IDirectPlay4A *iface, DPID player,
2089         DWORD *count )
2090 {
2091     return IDirectPlayX_GetMessageQueue( iface, 0, player, DPMESSAGEQUEUE_RECEIVE, count, NULL );
2092 }
2093
2094 static HRESULT WINAPI IDirectPlay4Impl_GetMessageCount( IDirectPlay4 *iface, DPID player,
2095         DWORD *count )
2096 {
2097     return IDirectPlayX_GetMessageQueue( iface, 0, player, DPMESSAGEQUEUE_RECEIVE, count, NULL );
2098 }
2099
2100 static HRESULT WINAPI IDirectPlay4AImpl_GetPlayerAddress( IDirectPlay4A *iface, DPID player,
2101         void *data, DWORD *size )
2102 {
2103     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
2104     FIXME("(%p)->(0x%08x,%p,%p): stub\n", This, player, data, size );
2105     return DP_OK;
2106 }
2107
2108 static HRESULT WINAPI IDirectPlay4Impl_GetPlayerAddress( IDirectPlay4 *iface, DPID player,
2109         void *data, DWORD *size )
2110 {
2111     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
2112     FIXME( "(%p)->(0x%08x,%p,%p): stub\n", This, player, data, size );
2113     return DP_OK;
2114 }
2115
2116 static HRESULT WINAPI IDirectPlay4AImpl_GetPlayerCaps( IDirectPlay4A *iface, DPID player,
2117         DPCAPS *caps, DWORD flags )
2118 {
2119     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
2120     return IDirectPlayX_GetPlayerCaps( &This->IDirectPlay4_iface, player, caps, flags );
2121 }
2122
2123 static HRESULT WINAPI IDirectPlay4Impl_GetPlayerCaps( IDirectPlay4 *iface, DPID player,
2124         DPCAPS *caps, DWORD flags )
2125 {
2126     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
2127     DPSP_GETCAPSDATA data;
2128
2129     TRACE( "(%p)->(0x%08x,%p,0x%08x)\n", This, player, caps, flags);
2130
2131     if ( This->dp2->connectionInitialized == NO_PROVIDER )
2132         return DPERR_UNINITIALIZED;
2133
2134     /* Query the service provider */
2135     data.idPlayer = player;
2136     data.dwFlags = flags;
2137     data.lpCaps = caps;
2138     data.lpISP = This->dp2->spData.lpISP;
2139
2140     return (*This->dp2->spData.lpCB->GetCaps)( &data );
2141 }
2142
2143 static HRESULT WINAPI IDirectPlay4AImpl_GetPlayerData( IDirectPlay4A *iface, DPID player,
2144         void *data, DWORD *size, DWORD flags )
2145 {
2146     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
2147     return IDirectPlayX_GetPlayerData( &This->IDirectPlay4_iface, player, data, size, flags );
2148 }
2149
2150 static HRESULT WINAPI IDirectPlay4Impl_GetPlayerData( IDirectPlay4 *iface, DPID player,
2151         void *data, DWORD *size, DWORD flags )
2152 {
2153     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
2154     lpPlayerList plist;
2155     DWORD bufsize;
2156     void *src;
2157
2158     TRACE( "(%p)->(0x%08x,%p,%p,0x%08x)\n", This, player, data, size, flags );
2159
2160     if ( This->dp2->connectionInitialized == NO_PROVIDER )
2161         return DPERR_UNINITIALIZED;
2162
2163     if ( ( plist = DP_FindPlayer( This, player ) ) == NULL )
2164         return DPERR_INVALIDPLAYER;
2165
2166     if ( flags & DPSET_LOCAL )
2167     {
2168         bufsize = plist->lpPData->dwLocalDataSize;
2169         src = plist->lpPData->lpLocalData;
2170     }
2171     else
2172     {
2173         bufsize = plist->lpPData->dwRemoteDataSize;
2174         src = plist->lpPData->lpRemoteData;
2175     }
2176
2177     /* Is the user requesting to know how big a buffer is required? */
2178     if ( !data || *size < bufsize )
2179     {
2180         *size = bufsize;
2181         return DPERR_BUFFERTOOSMALL;
2182     }
2183
2184     CopyMemory( data, src, bufsize );
2185
2186     return DP_OK;
2187 }
2188
2189 static HRESULT DP_IF_GetPlayerName
2190           ( IDirectPlay2Impl* This, DPID idPlayer, LPVOID lpData,
2191             LPDWORD lpdwDataSize, BOOL bAnsi )
2192 {
2193   lpPlayerList lpPList;
2194   LPDPNAME    lpName = lpData;
2195   DWORD       dwRequiredDataSize;
2196
2197   FIXME( "(%p)->(0x%08x,%p,%p,%u): ANSI\n",
2198          This, idPlayer, lpData, lpdwDataSize, bAnsi );
2199
2200   if( This->dp2->connectionInitialized == NO_PROVIDER )
2201   {
2202     return DPERR_UNINITIALIZED;
2203   }
2204
2205   if( ( lpPList = DP_FindPlayer( This, idPlayer ) ) == NULL )
2206   {
2207     return DPERR_INVALIDPLAYER;
2208   }
2209
2210   dwRequiredDataSize = lpPList->lpPData->name.dwSize;
2211
2212   if( lpPList->lpPData->name.u1.lpszShortNameA )
2213   {
2214     dwRequiredDataSize += strlen( lpPList->lpPData->name.u1.lpszShortNameA ) + 1;
2215   }
2216
2217   if( lpPList->lpPData->name.u2.lpszLongNameA )
2218   {
2219     dwRequiredDataSize += strlen( lpPList->lpPData->name.u2.lpszLongNameA ) + 1;
2220   }
2221
2222   if( ( lpData == NULL ) ||
2223       ( *lpdwDataSize < dwRequiredDataSize )
2224     )
2225   {
2226     *lpdwDataSize = dwRequiredDataSize;
2227     return DPERR_BUFFERTOOSMALL;
2228   }
2229
2230   /* Copy the structure */
2231   CopyMemory( lpName, &lpPList->lpPData->name, lpPList->lpPData->name.dwSize );
2232
2233   if( lpPList->lpPData->name.u1.lpszShortNameA )
2234   {
2235     strcpy( ((char*)lpName)+lpPList->lpPData->name.dwSize,
2236             lpPList->lpPData->name.u1.lpszShortNameA );
2237   }
2238   else
2239   {
2240     lpName->u1.lpszShortNameA = NULL;
2241   }
2242
2243   if( lpPList->lpPData->name.u1.lpszShortNameA )
2244   {
2245     strcpy( ((char*)lpName)+lpPList->lpPData->name.dwSize,
2246             lpPList->lpPData->name.u2.lpszLongNameA );
2247   }
2248   else
2249   {
2250     lpName->u2.lpszLongNameA = NULL;
2251   }
2252
2253   return DP_OK;
2254 }
2255
2256 static HRESULT WINAPI IDirectPlay4AImpl_GetPlayerName( IDirectPlay4A *iface, DPID idPlayer,
2257         void *lpData, DWORD *lpdwDataSize )
2258 {
2259     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
2260     return DP_IF_GetPlayerName( This, idPlayer, lpData, lpdwDataSize, TRUE );
2261 }
2262
2263 static HRESULT WINAPI DirectPlay2WImpl_GetPlayerName
2264           ( LPDIRECTPLAY2 iface, DPID idPlayer, LPVOID lpData,
2265             LPDWORD lpdwDataSize )
2266 {
2267   IDirectPlay2Impl *This = (IDirectPlay2Impl *)iface;
2268   return DP_IF_GetPlayerName( This, idPlayer, lpData, lpdwDataSize, FALSE );
2269 }
2270
2271 static HRESULT DP_GetSessionDesc
2272           ( IDirectPlay2Impl* This, LPVOID lpData, LPDWORD lpdwDataSize,
2273             BOOL bAnsi )
2274 {
2275   DWORD dwRequiredSize;
2276
2277   TRACE( "(%p)->(%p,%p,%u)\n", This, lpData, lpdwDataSize, bAnsi );
2278
2279   if( This->dp2->connectionInitialized == NO_PROVIDER )
2280   {
2281     return DPERR_UNINITIALIZED;
2282   }
2283
2284   if( ( lpData == NULL ) && ( lpdwDataSize == NULL ) )
2285   {
2286     return DPERR_INVALIDPARAMS;
2287   }
2288
2289   /* FIXME: Get from This->dp2->lpSessionDesc */
2290   dwRequiredSize = DP_CalcSessionDescSize( This->dp2->lpSessionDesc, bAnsi );
2291
2292   if ( ( lpData == NULL ) ||
2293        ( *lpdwDataSize < dwRequiredSize )
2294      )
2295   {
2296     *lpdwDataSize = dwRequiredSize;
2297     return DPERR_BUFFERTOOSMALL;
2298   }
2299
2300   DP_CopySessionDesc( lpData, This->dp2->lpSessionDesc, bAnsi );
2301
2302   return DP_OK;
2303 }
2304
2305 static HRESULT WINAPI IDirectPlay4AImpl_GetSessionDesc( IDirectPlay4A *iface, void *lpData,
2306         DWORD *lpdwDataSize )
2307 {
2308     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
2309     return DP_GetSessionDesc( This, lpData, lpdwDataSize, TRUE );
2310 }
2311
2312 static HRESULT WINAPI DirectPlay2WImpl_GetSessionDesc
2313           ( LPDIRECTPLAY2 iface, LPVOID lpData, LPDWORD lpdwDataSize )
2314 {
2315   IDirectPlay2Impl *This = (IDirectPlay2Impl *)iface;
2316   return DP_GetSessionDesc( This, lpData, lpdwDataSize, TRUE );
2317 }
2318
2319 /* Intended only for COM compatibility. Always returns an error. */
2320 static HRESULT WINAPI IDirectPlay4AImpl_Initialize( IDirectPlay4A *iface, GUID *guid )
2321 {
2322     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
2323     TRACE("(%p)->(%p): no-op\n", This, guid );
2324     return DPERR_ALREADYINITIALIZED;
2325 }
2326
2327 static HRESULT WINAPI IDirectPlay4Impl_Initialize( IDirectPlay4 *iface, GUID *guid )
2328 {
2329     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
2330     TRACE( "(%p)->(%p): no-op\n", This, guid );
2331     return DPERR_ALREADYINITIALIZED;
2332 }
2333
2334
2335 static HRESULT DP_SecureOpen
2336           ( IDirectPlay2Impl* This, LPCDPSESSIONDESC2 lpsd, DWORD dwFlags,
2337             LPCDPSECURITYDESC lpSecurity, LPCDPCREDENTIALS lpCredentials,
2338             BOOL bAnsi )
2339 {
2340   HRESULT hr = DP_OK;
2341
2342   FIXME( "(%p)->(%p,0x%08x,%p,%p): partial stub\n",
2343          This, lpsd, dwFlags, lpSecurity, lpCredentials );
2344
2345   if( This->dp2->connectionInitialized == NO_PROVIDER )
2346   {
2347     return DPERR_UNINITIALIZED;
2348   }
2349
2350   if( lpsd->dwSize != sizeof(DPSESSIONDESC2) )
2351   {
2352     TRACE( ": rejecting invalid dpsd size (%d).\n", lpsd->dwSize );
2353     return DPERR_INVALIDPARAMS;
2354   }
2355
2356   if( This->dp2->bConnectionOpen )
2357   {
2358     TRACE( ": rejecting already open connection.\n" );
2359     return DPERR_ALREADYINITIALIZED;
2360   }
2361
2362   /* If we're enumerating, kill the thread */
2363   DP_KillEnumSessionThread( This );
2364
2365   if( dwFlags & DPOPEN_CREATE )
2366   {
2367     /* Rightoo - this computer is the host and the local computer needs to be
2368        the name server so that others can join this session */
2369     NS_SetLocalComputerAsNameServer( lpsd, This->dp2->lpNameServerData );
2370
2371     This->dp2->bHostInterface = TRUE;
2372
2373     hr = DP_SetSessionDesc( This, lpsd, 0, TRUE, bAnsi );
2374     if( FAILED( hr ) )
2375     {
2376       ERR( "Unable to set session desc: %s\n", DPLAYX_HresultToString( hr ) );
2377       return hr;
2378     }
2379   }
2380
2381   /* Invoke the conditional callback for the service provider */
2382   if( This->dp2->spData.lpCB->Open )
2383   {
2384     DPSP_OPENDATA data;
2385
2386     FIXME( "Not all data fields are correct. Need new parameter\n" );
2387
2388     data.bCreate           = (dwFlags & DPOPEN_CREATE ) != 0;
2389     data.lpSPMessageHeader = (dwFlags & DPOPEN_CREATE ) ? NULL
2390                                                         : NS_GetNSAddr( This->dp2->lpNameServerData );
2391     data.lpISP             = This->dp2->spData.lpISP;
2392     data.bReturnStatus     = (dwFlags & DPOPEN_RETURNSTATUS) != 0;
2393     data.dwOpenFlags       = dwFlags;
2394     data.dwSessionFlags    = This->dp2->lpSessionDesc->dwFlags;
2395
2396     hr = (*This->dp2->spData.lpCB->Open)(&data);
2397     if( FAILED( hr ) )
2398     {
2399       ERR( "Unable to open session: %s\n", DPLAYX_HresultToString( hr ) );
2400       return hr;
2401     }
2402   }
2403
2404   {
2405     /* Create the system group of which everything is a part of */
2406     DPID systemGroup = DPID_SYSTEM_GROUP;
2407
2408     hr = DP_IF_CreateGroup( This, NULL, &systemGroup, NULL,
2409                             NULL, 0, 0, TRUE );
2410
2411   }
2412
2413   if( dwFlags & DPOPEN_JOIN )
2414   {
2415     DPID dpidServerId = DPID_UNKNOWN;
2416
2417     /* Create the server player for this interface. This way we can receive
2418      * messages for this session.
2419      */
2420     /* FIXME: I suppose that we should be setting an event for a receive
2421      *        type of thing. That way the messaging thread could know to wake
2422      *        up. DPlay would then trigger the hEvent for the player the
2423      *        message is directed to.
2424      */
2425     hr = DP_IF_CreatePlayer( This, NULL, &dpidServerId, NULL, 0, NULL,
2426                              0,
2427                              DPPLAYER_SERVERPLAYER | DPPLAYER_LOCAL , bAnsi );
2428
2429   }
2430   else if( dwFlags & DPOPEN_CREATE )
2431   {
2432     DPID dpidNameServerId = DPID_NAME_SERVER;
2433
2434     hr = DP_IF_CreatePlayer( This, NULL, &dpidNameServerId, NULL, 0, NULL,
2435                              0, DPPLAYER_SERVERPLAYER, bAnsi );
2436   }
2437
2438   if( FAILED(hr) )
2439   {
2440     ERR( "Couldn't create name server/system player: %s\n",
2441          DPLAYX_HresultToString(hr) );
2442   }
2443
2444   return hr;
2445 }
2446
2447 static HRESULT WINAPI IDirectPlay4AImpl_Open( IDirectPlay4A *iface, DPSESSIONDESC2 *sdesc,
2448         DWORD flags )
2449 {
2450     return IDirectPlayX_SecureOpen( iface, sdesc, flags, NULL, NULL );
2451 }
2452
2453 static HRESULT WINAPI IDirectPlay4Impl_Open( IDirectPlay4 *iface, DPSESSIONDESC2 *sdesc,
2454         DWORD flags )
2455 {
2456     return IDirectPlayX_SecureOpen( iface, sdesc, flags, NULL, NULL );
2457 }
2458
2459 static HRESULT DP_IF_Receive
2460           ( IDirectPlay2Impl* This, LPDPID lpidFrom, LPDPID lpidTo,
2461             DWORD dwFlags, LPVOID lpData, LPDWORD lpdwDataSize, BOOL bAnsi )
2462 {
2463   LPDPMSG lpMsg = NULL;
2464
2465   FIXME( "(%p)->(%p,%p,0x%08x,%p,%p,%u): stub\n",
2466          This, lpidFrom, lpidTo, dwFlags, lpData, lpdwDataSize, bAnsi );
2467
2468   if( This->dp2->connectionInitialized == NO_PROVIDER )
2469   {
2470     return DPERR_UNINITIALIZED;
2471   }
2472
2473   if( dwFlags == 0 )
2474   {
2475     dwFlags = DPRECEIVE_ALL;
2476   }
2477
2478   /* If the lpData is NULL, we must be peeking the message */
2479   if(  ( lpData == NULL ) &&
2480       !( dwFlags & DPRECEIVE_PEEK )
2481     )
2482   {
2483     return DPERR_INVALIDPARAMS;
2484   }
2485
2486   if( dwFlags & DPRECEIVE_ALL )
2487   {
2488     lpMsg = This->dp2->receiveMsgs.lpQHFirst;
2489
2490     if( !( dwFlags & DPRECEIVE_PEEK ) )
2491     {
2492       FIXME( "Remove from queue\n" );
2493     }
2494   }
2495   else if( ( dwFlags & DPRECEIVE_TOPLAYER ) ||
2496            ( dwFlags & DPRECEIVE_FROMPLAYER )
2497          )
2498   {
2499     FIXME( "Find matching message 0x%08x\n", dwFlags );
2500   }
2501   else
2502   {
2503     ERR( "Hmmm..dwFlags 0x%08x\n", dwFlags );
2504   }
2505
2506   if( lpMsg == NULL )
2507   {
2508     return DPERR_NOMESSAGES;
2509   }
2510
2511   /* Copy into the provided buffer */
2512   if (lpData) CopyMemory( lpData, lpMsg->msg, *lpdwDataSize );
2513
2514   return DP_OK;
2515 }
2516
2517 static HRESULT WINAPI IDirectPlay4AImpl_Receive( IDirectPlay4A *iface, DPID *lpidFrom,
2518         DPID *lpidTo, DWORD dwFlags, void *lpData, DWORD *lpdwDataSize )
2519 {
2520     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
2521     return DP_IF_Receive( This, lpidFrom, lpidTo, dwFlags, lpData, lpdwDataSize, TRUE );
2522 }
2523
2524 static HRESULT WINAPI DirectPlay2WImpl_Receive
2525           ( LPDIRECTPLAY2 iface, LPDPID lpidFrom, LPDPID lpidTo,
2526             DWORD dwFlags, LPVOID lpData, LPDWORD lpdwDataSize )
2527 {
2528   IDirectPlay2Impl *This = (IDirectPlay2Impl *)iface;
2529   return DP_IF_Receive( This, lpidFrom, lpidTo, dwFlags,
2530                         lpData, lpdwDataSize, FALSE );
2531 }
2532
2533 static HRESULT WINAPI IDirectPlay4AImpl_Send( IDirectPlay4A *iface, DPID from, DPID to,
2534         DWORD flags, void *data, DWORD size )
2535 {
2536     return IDirectPlayX_SendEx( iface, from, to, flags, data, size, 0, 0, NULL, NULL );
2537 }
2538
2539 static HRESULT WINAPI IDirectPlay4Impl_Send( IDirectPlay4 *iface, DPID from, DPID to,
2540         DWORD flags, void *data, DWORD size )
2541 {
2542     return IDirectPlayX_SendEx( iface, from, to, flags, data, size, 0, 0, NULL, NULL );
2543 }
2544
2545 static HRESULT WINAPI IDirectPlay4AImpl_SetGroupData( IDirectPlay4A *iface, DPID group, void *data,
2546         DWORD size, DWORD flags )
2547 {
2548     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
2549     return IDirectPlayX_SetGroupData( &This->IDirectPlay4_iface, group, data, size, flags );
2550 }
2551
2552 static HRESULT WINAPI IDirectPlay4Impl_SetGroupData( IDirectPlay4 *iface, DPID group, void *data,
2553         DWORD size, DWORD flags )
2554 {
2555     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
2556     lpGroupData gdata;
2557
2558     TRACE( "(%p)->(0x%08x,%p,0x%08x,0x%08x)\n", This, group, data, size, flags );
2559
2560     /* Parameter check */
2561     if ( !data && size )
2562         return DPERR_INVALIDPARAMS;
2563
2564     /* Find the pointer to the data for this player */
2565     if ( ( gdata = DP_FindAnyGroup( This, group ) ) == NULL )
2566         return DPERR_INVALIDOBJECT;
2567
2568     if ( !(flags & DPSET_LOCAL) )
2569     {
2570         FIXME( "Was this group created by this interface?\n" );
2571         /* FIXME: If this is a remote update need to allow it but not
2572          *        send a message.
2573          */
2574     }
2575
2576     DP_SetGroupData( gdata, flags, data, size );
2577
2578     /* FIXME: Only send a message if this group is local to the session otherwise
2579      * it will have been rejected above
2580      */
2581     if ( !(flags & DPSET_LOCAL) )
2582         FIXME( "Send msg?\n" );
2583
2584     return DP_OK;
2585 }
2586
2587 static HRESULT DP_IF_SetGroupName
2588           ( IDirectPlay2Impl* This, DPID idGroup, LPDPNAME lpGroupName,
2589             DWORD dwFlags, BOOL bAnsi )
2590 {
2591   lpGroupData lpGData;
2592
2593   TRACE( "(%p)->(0x%08x,%p,0x%08x,%u)\n", This, idGroup,
2594          lpGroupName, dwFlags, bAnsi );
2595
2596   if( ( lpGData = DP_FindAnyGroup( This, idGroup ) ) == NULL )
2597   {
2598     return DPERR_INVALIDGROUP;
2599   }
2600
2601   DP_CopyDPNAMEStruct( &lpGData->name, lpGroupName, bAnsi );
2602
2603   /* Should send a DPMSG_SETPLAYERORGROUPNAME message */
2604   FIXME( "Message not sent and dwFlags ignored\n" );
2605
2606   return DP_OK;
2607 }
2608
2609 static HRESULT WINAPI IDirectPlay4AImpl_SetGroupName( IDirectPlay4A *iface, DPID idGroup,
2610         DPNAME *lpGroupName, DWORD dwFlags )
2611 {
2612     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
2613     return DP_IF_SetGroupName( This, idGroup, lpGroupName, dwFlags, TRUE );
2614 }
2615
2616 static HRESULT WINAPI DirectPlay2WImpl_SetGroupName
2617           ( LPDIRECTPLAY2 iface, DPID idGroup, LPDPNAME lpGroupName,
2618             DWORD dwFlags )
2619 {
2620   IDirectPlay2Impl *This = (IDirectPlay2Impl *)iface;
2621   return DP_IF_SetGroupName( This, idGroup, lpGroupName, dwFlags, FALSE );
2622 }
2623
2624 static HRESULT WINAPI IDirectPlay4AImpl_SetPlayerData( IDirectPlay4A *iface, DPID player,
2625         void *data, DWORD size, DWORD flags )
2626 {
2627     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
2628     return IDirectPlayX_SetPlayerData( &This->IDirectPlay4_iface, player, data, size, flags );
2629 }
2630
2631 static HRESULT WINAPI IDirectPlay4Impl_SetPlayerData( IDirectPlay4 *iface, DPID player,
2632         void *data, DWORD size, DWORD flags )
2633 {
2634     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
2635     lpPlayerList plist;
2636
2637     TRACE( "(%p)->(0x%08x,%p,0x%08x,0x%08x)\n", This, player, data, size, flags );
2638
2639     if ( This->dp2->connectionInitialized == NO_PROVIDER )
2640         return DPERR_UNINITIALIZED;
2641
2642     /* Parameter check */
2643     if ( !data && size )
2644         return DPERR_INVALIDPARAMS;
2645
2646     /* Find the pointer to the data for this player */
2647     if ( (plist = DP_FindPlayer( This, player )) == NULL )
2648         return DPERR_INVALIDPLAYER;
2649
2650     if ( !(flags & DPSET_LOCAL) )
2651     {
2652         FIXME( "Was this group created by this interface?\n" );
2653         /* FIXME: If this is a remote update need to allow it but not
2654          *        send a message.
2655          */
2656     }
2657
2658     DP_SetPlayerData( plist->lpPData, flags, data, size );
2659
2660     if ( !(flags & DPSET_LOCAL) )
2661         FIXME( "Send msg?\n" );
2662
2663     return DP_OK;
2664 }
2665
2666 static HRESULT DP_IF_SetPlayerName
2667           ( IDirectPlay2Impl* This, DPID idPlayer, LPDPNAME lpPlayerName,
2668             DWORD dwFlags, BOOL bAnsi )
2669 {
2670   lpPlayerList lpPList;
2671
2672   TRACE( "(%p)->(0x%08x,%p,0x%08x,%u)\n",
2673          This, idPlayer, lpPlayerName, dwFlags, bAnsi );
2674
2675   if( This->dp2->connectionInitialized == NO_PROVIDER )
2676   {
2677     return DPERR_UNINITIALIZED;
2678   }
2679
2680   if( ( lpPList = DP_FindPlayer( This, idPlayer ) ) == NULL )
2681   {
2682     return DPERR_INVALIDGROUP;
2683   }
2684
2685   DP_CopyDPNAMEStruct( &lpPList->lpPData->name, lpPlayerName, bAnsi );
2686
2687   /* Should send a DPMSG_SETPLAYERORGROUPNAME message */
2688   FIXME( "Message not sent and dwFlags ignored\n" );
2689
2690   return DP_OK;
2691 }
2692
2693 static HRESULT WINAPI IDirectPlay4AImpl_SetPlayerName( IDirectPlay4A *iface, DPID idPlayer,
2694         DPNAME *lpPlayerName, DWORD dwFlags )
2695 {
2696     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
2697     return DP_IF_SetPlayerName( This, idPlayer, lpPlayerName, dwFlags, TRUE );
2698 }
2699
2700 static HRESULT WINAPI DirectPlay2WImpl_SetPlayerName
2701           ( LPDIRECTPLAY2 iface, DPID idPlayer, LPDPNAME lpPlayerName,
2702             DWORD dwFlags )
2703 {
2704   IDirectPlay2Impl *This = (IDirectPlay2Impl *)iface;
2705   return DP_IF_SetPlayerName( This, idPlayer, lpPlayerName, dwFlags, FALSE );
2706 }
2707
2708 static HRESULT DP_SetSessionDesc
2709           ( IDirectPlay2Impl* This, LPCDPSESSIONDESC2 lpSessDesc,
2710             DWORD dwFlags, BOOL bInitial, BOOL bAnsi  )
2711 {
2712   DWORD            dwRequiredSize;
2713   LPDPSESSIONDESC2 lpTempSessDesc;
2714
2715   TRACE( "(%p)->(%p,0x%08x,%u,%u)\n",
2716          This, lpSessDesc, dwFlags, bInitial, bAnsi );
2717
2718   if( This->dp2->connectionInitialized == NO_PROVIDER )
2719   {
2720     return DPERR_UNINITIALIZED;
2721   }
2722
2723   if( dwFlags )
2724   {
2725     return DPERR_INVALIDPARAMS;
2726   }
2727
2728   /* Only the host is allowed to update the session desc */
2729   if( !This->dp2->bHostInterface )
2730   {
2731     return DPERR_ACCESSDENIED;
2732   }
2733
2734   /* FIXME: Copy into This->dp2->lpSessionDesc */
2735   dwRequiredSize = DP_CalcSessionDescSize( lpSessDesc, bAnsi );
2736   lpTempSessDesc = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize );
2737
2738   if( lpTempSessDesc == NULL )
2739   {
2740     return DPERR_OUTOFMEMORY;
2741   }
2742
2743   /* Free the old */
2744   HeapFree( GetProcessHeap(), 0, This->dp2->lpSessionDesc );
2745
2746   This->dp2->lpSessionDesc = lpTempSessDesc;
2747   /* Set the new */
2748   DP_CopySessionDesc( This->dp2->lpSessionDesc, lpSessDesc, bAnsi );
2749   if( bInitial )
2750   {
2751     /*Initializing session GUID*/
2752     CoCreateGuid( &(This->dp2->lpSessionDesc->guidInstance) );
2753   }
2754   /* If this is an external invocation of the interface, we should be
2755    * letting everyone know that things have changed. Otherwise this is
2756    * just an initialization and it doesn't need to be propagated.
2757    */
2758   if( !bInitial )
2759   {
2760     FIXME( "Need to send a DPMSG_SETSESSIONDESC msg to everyone\n" );
2761   }
2762
2763   return DP_OK;
2764 }
2765
2766 static HRESULT WINAPI IDirectPlay4AImpl_SetSessionDesc( IDirectPlay4A *iface,
2767         DPSESSIONDESC2 *lpSessDesc, DWORD dwFlags )
2768 {
2769     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
2770     return DP_SetSessionDesc( This, lpSessDesc, dwFlags, FALSE, TRUE );
2771 }
2772
2773 static HRESULT WINAPI DirectPlay2WImpl_SetSessionDesc
2774           ( LPDIRECTPLAY2 iface, LPDPSESSIONDESC2 lpSessDesc, DWORD dwFlags )
2775 {
2776   IDirectPlay2Impl *This = (IDirectPlay2Impl *)iface;
2777   return DP_SetSessionDesc( This, lpSessDesc, dwFlags, FALSE, TRUE );
2778 }
2779
2780 /* FIXME: See about merging some of this stuff with dplayx_global.c stuff */
2781 static DWORD DP_CalcSessionDescSize( LPCDPSESSIONDESC2 lpSessDesc, BOOL bAnsi )
2782 {
2783   DWORD dwSize = 0;
2784
2785   if( lpSessDesc == NULL )
2786   {
2787     /* Hmmm..don't need any size? */
2788     ERR( "NULL lpSessDesc\n" );
2789     return dwSize;
2790   }
2791
2792   dwSize += sizeof( *lpSessDesc );
2793
2794   if( bAnsi )
2795   {
2796     if( lpSessDesc->u1.lpszSessionNameA )
2797     {
2798       dwSize += lstrlenA( lpSessDesc->u1.lpszSessionNameA ) + 1;
2799     }
2800
2801     if( lpSessDesc->u2.lpszPasswordA )
2802     {
2803       dwSize += lstrlenA( lpSessDesc->u2.lpszPasswordA ) + 1;
2804     }
2805   }
2806   else /* UNICODE */
2807   {
2808     if( lpSessDesc->u1.lpszSessionName )
2809     {
2810       dwSize += sizeof( WCHAR ) *
2811         ( lstrlenW( lpSessDesc->u1.lpszSessionName ) + 1 );
2812     }
2813
2814     if( lpSessDesc->u2.lpszPassword )
2815     {
2816       dwSize += sizeof( WCHAR ) *
2817         ( lstrlenW( lpSessDesc->u2.lpszPassword ) + 1 );
2818     }
2819   }
2820
2821   return dwSize;
2822 }
2823
2824 /* Assumes that contiguous buffers are already allocated. */
2825 static void DP_CopySessionDesc( LPDPSESSIONDESC2 lpSessionDest,
2826                                 LPCDPSESSIONDESC2 lpSessionSrc, BOOL bAnsi )
2827 {
2828   BYTE* lpStartOfFreeSpace;
2829
2830   if( lpSessionDest == NULL )
2831   {
2832     ERR( "NULL lpSessionDest\n" );
2833     return;
2834   }
2835
2836   CopyMemory( lpSessionDest, lpSessionSrc, sizeof( *lpSessionSrc ) );
2837
2838   lpStartOfFreeSpace = ((BYTE*)lpSessionDest) + sizeof( *lpSessionSrc );
2839
2840   if( bAnsi )
2841   {
2842     if( lpSessionSrc->u1.lpszSessionNameA )
2843     {
2844       lstrcpyA( (LPSTR)lpStartOfFreeSpace,
2845                 lpSessionDest->u1.lpszSessionNameA );
2846       lpSessionDest->u1.lpszSessionNameA = (LPSTR)lpStartOfFreeSpace;
2847       lpStartOfFreeSpace +=
2848         lstrlenA( lpSessionDest->u1.lpszSessionNameA ) + 1;
2849     }
2850
2851     if( lpSessionSrc->u2.lpszPasswordA )
2852     {
2853       lstrcpyA( (LPSTR)lpStartOfFreeSpace,
2854                 lpSessionDest->u2.lpszPasswordA );
2855       lpSessionDest->u2.lpszPasswordA = (LPSTR)lpStartOfFreeSpace;
2856     }
2857   }
2858   else /* UNICODE */
2859   {
2860     if( lpSessionSrc->u1.lpszSessionName )
2861     {
2862       lstrcpyW( (LPWSTR)lpStartOfFreeSpace,
2863                 lpSessionDest->u1.lpszSessionName );
2864       lpSessionDest->u1.lpszSessionName = (LPWSTR)lpStartOfFreeSpace;
2865       lpStartOfFreeSpace += sizeof(WCHAR) *
2866         ( lstrlenW( lpSessionDest->u1.lpszSessionName ) + 1 );
2867     }
2868
2869     if( lpSessionSrc->u2.lpszPassword )
2870     {
2871       lstrcpyW( (LPWSTR)lpStartOfFreeSpace,
2872                 lpSessionDest->u2.lpszPassword );
2873       lpSessionDest->u2.lpszPassword = (LPWSTR)lpStartOfFreeSpace;
2874     }
2875   }
2876 }
2877
2878 static HRESULT WINAPI IDirectPlay4AImpl_AddGroupToGroup( IDirectPlay4A *iface, DPID parent,
2879         DPID group )
2880 {
2881     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
2882     return IDirectPlayX_AddGroupToGroup( &This->IDirectPlay4_iface, parent, group );
2883 }
2884
2885 static HRESULT WINAPI IDirectPlay4Impl_AddGroupToGroup( IDirectPlay4 *iface, DPID parent,
2886         DPID group )
2887 {
2888     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
2889     lpGroupData gdata;
2890     lpGroupList glist;
2891
2892     TRACE( "(%p)->(0x%08x,0x%08x)\n", This, parent, group );
2893
2894     if ( This->dp2->connectionInitialized == NO_PROVIDER )
2895         return DPERR_UNINITIALIZED;
2896
2897     if ( !DP_FindAnyGroup(This, parent ) )
2898         return DPERR_INVALIDGROUP;
2899
2900     if ( ( gdata = DP_FindAnyGroup(This, group ) ) == NULL )
2901         return DPERR_INVALIDGROUP;
2902
2903     /* Create a player list (ie "shortcut" ) */
2904     glist = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *glist ) );
2905     if ( !glist )
2906         return DPERR_CANTADDPLAYER;
2907
2908     /* Add the shortcut */
2909     gdata->uRef++;
2910     glist->lpGData = gdata;
2911
2912     /* Add the player to the list of players for this group */
2913     DPQ_INSERT( gdata->groups, glist, groups );
2914
2915     /* Send a ADDGROUPTOGROUP message */
2916     FIXME( "Not sending message\n" );
2917
2918     return DP_OK;
2919 }
2920
2921 static HRESULT DP_IF_CreateGroupInGroup
2922           ( IDirectPlay3Impl* This, LPVOID lpMsgHdr, DPID idParentGroup,
2923             LPDPID lpidGroup, LPDPNAME lpGroupName, LPVOID lpData,
2924             DWORD dwDataSize, DWORD dwFlags, BOOL bAnsi )
2925 {
2926   lpGroupData lpGParentData;
2927   lpGroupList lpGList;
2928   lpGroupData lpGData;
2929
2930   TRACE( "(%p)->(0x%08x,%p,%p,%p,0x%08x,0x%08x,%u)\n",
2931          This, idParentGroup, lpidGroup, lpGroupName, lpData,
2932          dwDataSize, dwFlags, bAnsi );
2933
2934   if( This->dp2->connectionInitialized == NO_PROVIDER )
2935   {
2936     return DPERR_UNINITIALIZED;
2937   }
2938
2939   /* Verify that the specified parent is valid */
2940   if( ( lpGParentData = DP_FindAnyGroup( (IDirectPlay2AImpl*)This,
2941                                          idParentGroup ) ) == NULL
2942     )
2943   {
2944     return DPERR_INVALIDGROUP;
2945   }
2946
2947   lpGData = DP_CreateGroup( (IDirectPlay2AImpl*)This, lpidGroup, lpGroupName,
2948                             dwFlags, idParentGroup, bAnsi );
2949
2950   if( lpGData == NULL )
2951   {
2952     return DPERR_CANTADDPLAYER; /* yes player not group */
2953   }
2954
2955   /* Something else is referencing this data */
2956   lpGData->uRef++;
2957
2958   DP_SetGroupData( lpGData, DPSET_REMOTE, lpData, dwDataSize );
2959
2960   /* The list has now been inserted into the interface group list. We now
2961      need to put a "shortcut" to this group in the parent group */
2962   lpGList = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *lpGList ) );
2963   if( lpGList == NULL )
2964   {
2965     FIXME( "Memory leak\n" );
2966     return DPERR_CANTADDPLAYER; /* yes player not group */
2967   }
2968
2969   lpGList->lpGData = lpGData;
2970
2971   DPQ_INSERT( lpGParentData->groups, lpGList, groups );
2972
2973   /* Let the SP know that we've created this group */
2974   if( This->dp2->spData.lpCB->CreateGroup )
2975   {
2976     DPSP_CREATEGROUPDATA data;
2977
2978     TRACE( "Calling SP CreateGroup\n" );
2979
2980     data.idGroup           = *lpidGroup;
2981     data.dwFlags           = dwFlags;
2982     data.lpSPMessageHeader = lpMsgHdr;
2983     data.lpISP             = This->dp2->spData.lpISP;
2984
2985     (*This->dp2->spData.lpCB->CreateGroup)( &data );
2986   }
2987
2988   /* Inform all other peers of the creation of a new group. If there are
2989    * no peers keep this quiet.
2990    */
2991   if( This->dp2->lpSessionDesc &&
2992       ( This->dp2->lpSessionDesc->dwFlags & DPSESSION_MULTICASTSERVER ) )
2993   {
2994     DPMSG_CREATEPLAYERORGROUP msg;
2995
2996     msg.dwType = DPSYS_CREATEPLAYERORGROUP;
2997     msg.dwPlayerType = DPPLAYERTYPE_GROUP;
2998     msg.dpId = *lpidGroup;
2999     msg.dwCurrentPlayers = idParentGroup; /* FIXME: Incorrect? */
3000     msg.lpData = lpData;
3001     msg.dwDataSize = dwDataSize;
3002     msg.dpnName = *lpGroupName;
3003
3004     /* FIXME: Correct to just use send effectively? */
3005     /* FIXME: Should size include data w/ message or just message "header" */
3006     /* FIXME: Check return code */
3007     DP_SendEx( (IDirectPlay2Impl*)This,
3008                DPID_SERVERPLAYER, DPID_ALLPLAYERS, 0, &msg, sizeof( msg ),
3009                0, 0, NULL, NULL, bAnsi );
3010   }
3011
3012   return DP_OK;
3013 }
3014
3015 static HRESULT WINAPI IDirectPlay4AImpl_CreateGroupInGroup( IDirectPlay4A *iface,
3016         DPID idParentGroup, DPID *lpidGroup, DPNAME *lpGroupName, void *lpData, DWORD dwDataSize,
3017         DWORD dwFlags )
3018 {
3019     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
3020
3021     *lpidGroup = DPID_UNKNOWN;
3022
3023     return DP_IF_CreateGroupInGroup( This, NULL, idParentGroup, lpidGroup, lpGroupName, lpData,
3024             dwDataSize, dwFlags, TRUE );
3025 }
3026
3027 static HRESULT WINAPI DirectPlay3WImpl_CreateGroupInGroup
3028           ( LPDIRECTPLAY3 iface, DPID idParentGroup, LPDPID lpidGroup,
3029             LPDPNAME lpGroupName, LPVOID lpData, DWORD dwDataSize,
3030             DWORD dwFlags )
3031 {
3032   IDirectPlay3Impl *This = (IDirectPlay3Impl *)iface;
3033
3034   *lpidGroup = DPID_UNKNOWN;
3035
3036   return DP_IF_CreateGroupInGroup( This, NULL, idParentGroup, lpidGroup,
3037                                    lpGroupName, lpData, dwDataSize,
3038                                    dwFlags, FALSE );
3039 }
3040
3041 static HRESULT WINAPI IDirectPlay4AImpl_DeleteGroupFromGroup( IDirectPlay4A *iface, DPID parent,
3042         DPID group )
3043 {
3044     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
3045     return IDirectPlayX_DeleteGroupFromGroup( &This->IDirectPlay4_iface, parent, group );
3046 }
3047
3048 static HRESULT WINAPI IDirectPlay4Impl_DeleteGroupFromGroup( IDirectPlay4 *iface, DPID parent,
3049         DPID group )
3050 {
3051     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
3052     lpGroupList glist;
3053     lpGroupData parentdata;
3054
3055     TRACE("(%p)->(0x%08x,0x%08x)\n", This, parent, group );
3056
3057     /* Is the parent group valid? */
3058     if ( ( parentdata = DP_FindAnyGroup(This, parent ) ) == NULL )
3059         return DPERR_INVALIDGROUP;
3060
3061     /* Remove the group from the parent group queue */
3062     DPQ_REMOVE_ENTRY( parentdata->groups, groups, lpGData->dpid, ==, group, glist );
3063
3064     if ( glist == NULL )
3065         return DPERR_INVALIDGROUP;
3066
3067     /* Decrement the ref count */
3068     glist->lpGData->uRef--;
3069
3070     /* Free up the list item */
3071     HeapFree( GetProcessHeap(), 0, glist );
3072
3073     /* Should send a DELETEGROUPFROMGROUP message */
3074     FIXME( "message not sent\n" );
3075
3076     return DP_OK;
3077 }
3078
3079 static BOOL DP_BuildSPCompoundAddr( LPGUID lpcSpGuid, LPVOID* lplpAddrBuf,
3080                                     LPDWORD lpdwBufSize )
3081 {
3082   DPCOMPOUNDADDRESSELEMENT dpCompoundAddress;
3083   HRESULT                  hr;
3084
3085   dpCompoundAddress.dwDataSize = sizeof( GUID );
3086   dpCompoundAddress.guidDataType = DPAID_ServiceProvider;
3087   dpCompoundAddress.lpData = lpcSpGuid;
3088
3089   *lplpAddrBuf = NULL;
3090   *lpdwBufSize = 0;
3091
3092   hr = DPL_CreateCompoundAddress( &dpCompoundAddress, 1, *lplpAddrBuf,
3093                                   lpdwBufSize, TRUE );
3094
3095   if( hr != DPERR_BUFFERTOOSMALL )
3096   {
3097     ERR( "can't get buffer size: %s\n", DPLAYX_HresultToString( hr ) );
3098     return FALSE;
3099   }
3100
3101   /* Now allocate the buffer */
3102   *lplpAddrBuf = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
3103                             *lpdwBufSize );
3104
3105   hr = DPL_CreateCompoundAddress( &dpCompoundAddress, 1, *lplpAddrBuf,
3106                                   lpdwBufSize, TRUE );
3107   if( FAILED(hr) )
3108   {
3109     ERR( "can't create address: %s\n", DPLAYX_HresultToString( hr ) );
3110     return FALSE;
3111   }
3112
3113   return TRUE;
3114 }
3115
3116 static HRESULT WINAPI IDirectPlay4AImpl_EnumConnections( IDirectPlay4A *iface,
3117         const GUID *lpguidApplication, LPDPENUMCONNECTIONSCALLBACK lpEnumCallback, void *lpContext,
3118         DWORD dwFlags )
3119 {
3120   IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
3121   TRACE("(%p)->(%p,%p,%p,0x%08x)\n", This, lpguidApplication, lpEnumCallback, lpContext, dwFlags );
3122
3123   /* A default dwFlags (0) is backwards compatible -- DPCONNECTION_DIRECTPLAY */
3124   if( dwFlags == 0 )
3125   {
3126     dwFlags = DPCONNECTION_DIRECTPLAY;
3127   }
3128
3129   if( ! ( ( dwFlags & DPCONNECTION_DIRECTPLAY ) ||
3130           ( dwFlags & DPCONNECTION_DIRECTPLAYLOBBY ) )
3131     )
3132   {
3133     return DPERR_INVALIDFLAGS;
3134   }
3135
3136   if( !lpEnumCallback )
3137   {
3138      return DPERR_INVALIDPARAMS;
3139   }
3140
3141   /* Enumerate DirectPlay service providers */
3142   if( dwFlags & DPCONNECTION_DIRECTPLAY )
3143   {
3144     HKEY hkResult;
3145     LPCSTR searchSubKey    = "SOFTWARE\\Microsoft\\DirectPlay\\Service Providers";
3146     LPCSTR guidDataSubKey  = "Guid";
3147     char subKeyName[51];
3148     DWORD dwIndex, sizeOfSubKeyName=50;
3149     FILETIME filetime;
3150
3151     /* Need to loop over the service providers in the registry */
3152     if( RegOpenKeyExA( HKEY_LOCAL_MACHINE, searchSubKey,
3153                          0, KEY_READ, &hkResult ) != ERROR_SUCCESS )
3154     {
3155       /* Hmmm. Does this mean that there are no service providers? */
3156       ERR(": no service providers?\n");
3157       return DP_OK;
3158     }
3159
3160
3161     /* Traverse all the service providers we have available */
3162     for( dwIndex=0;
3163          RegEnumKeyExA( hkResult, dwIndex, subKeyName, &sizeOfSubKeyName,
3164                         NULL, NULL, NULL, &filetime ) != ERROR_NO_MORE_ITEMS;
3165          ++dwIndex, sizeOfSubKeyName=51 )
3166     {
3167
3168       HKEY     hkServiceProvider;
3169       GUID     serviceProviderGUID;
3170       DWORD    returnTypeGUID, sizeOfReturnBuffer = 50;
3171       char     returnBuffer[51];
3172       WCHAR    buff[51];
3173       DPNAME   dpName;
3174       BOOL     bBuildPass;
3175
3176       LPVOID                   lpAddressBuffer = NULL;
3177       DWORD                    dwAddressBufferSize = 0;
3178
3179       TRACE(" this time through: %s\n", subKeyName );
3180
3181       /* Get a handle for this particular service provider */
3182       if( RegOpenKeyExA( hkResult, subKeyName, 0, KEY_READ,
3183                          &hkServiceProvider ) != ERROR_SUCCESS )
3184       {
3185          ERR(": what the heck is going on?\n" );
3186          continue;
3187       }
3188
3189       if( RegQueryValueExA( hkServiceProvider, guidDataSubKey,
3190                             NULL, &returnTypeGUID, (LPBYTE)returnBuffer,
3191                             &sizeOfReturnBuffer ) != ERROR_SUCCESS )
3192       {
3193         ERR(": missing GUID registry data members\n" );
3194         RegCloseKey(hkServiceProvider);
3195         continue;
3196       }
3197       RegCloseKey(hkServiceProvider);
3198
3199       /* FIXME: Check return types to ensure we're interpreting data right */
3200       MultiByteToWideChar( CP_ACP, 0, returnBuffer, -1, buff, sizeof(buff)/sizeof(WCHAR) );
3201       CLSIDFromString( buff, &serviceProviderGUID );
3202       /* FIXME: Have I got a memory leak on the serviceProviderGUID? */
3203
3204       /* Fill in the DPNAME struct for the service provider */
3205       dpName.dwSize             = sizeof( dpName );
3206       dpName.dwFlags            = 0;
3207       dpName.u1.lpszShortNameA = subKeyName;
3208       dpName.u2.lpszLongNameA  = NULL;
3209
3210       /* Create the compound address for the service provider.
3211        * NOTE: This is a gruesome architectural scar right now.  DP
3212        * uses DPL and DPL uses DP.  Nasty stuff. This may be why the
3213        * native dll just gets around this little bit by allocating an
3214        * 80 byte buffer which isn't even filled with a valid compound
3215        * address. Oh well. Creating a proper compound address is the
3216        * way to go anyways despite this method taking slightly more
3217        * heap space and realtime :) */
3218
3219       bBuildPass = DP_BuildSPCompoundAddr( &serviceProviderGUID,
3220                                            &lpAddressBuffer,
3221                                            &dwAddressBufferSize );
3222       if( !bBuildPass )
3223       {
3224         ERR( "Can't build compound addr\n" );
3225         return DPERR_GENERIC;
3226       }
3227
3228       /* The enumeration will return FALSE if we are not to continue */
3229       if( !lpEnumCallback( &serviceProviderGUID, lpAddressBuffer, dwAddressBufferSize,
3230                            &dpName, dwFlags, lpContext ) )
3231       {
3232          return DP_OK;
3233       }
3234     }
3235   }
3236
3237   /* Enumerate DirectPlayLobby service providers */
3238   if( dwFlags & DPCONNECTION_DIRECTPLAYLOBBY )
3239   {
3240     HKEY hkResult;
3241     LPCSTR searchSubKey    = "SOFTWARE\\Microsoft\\DirectPlay\\Lobby Providers";
3242     LPCSTR guidDataSubKey  = "Guid";
3243     char subKeyName[51];
3244     DWORD dwIndex, sizeOfSubKeyName=50;
3245     FILETIME filetime;
3246
3247     /* Need to loop over the service providers in the registry */
3248     if( RegOpenKeyExA( HKEY_LOCAL_MACHINE, searchSubKey,
3249                          0, KEY_READ, &hkResult ) != ERROR_SUCCESS )
3250     {
3251       /* Hmmm. Does this mean that there are no service providers? */
3252       ERR(": no service providers?\n");
3253       return DP_OK;
3254     }
3255
3256
3257     /* Traverse all the lobby providers we have available */
3258     for( dwIndex=0;
3259          RegEnumKeyExA( hkResult, dwIndex, subKeyName, &sizeOfSubKeyName,
3260                         NULL, NULL, NULL, &filetime ) != ERROR_NO_MORE_ITEMS;
3261          ++dwIndex, sizeOfSubKeyName=51 )
3262     {
3263
3264       HKEY     hkServiceProvider;
3265       GUID     serviceProviderGUID;
3266       DWORD    returnTypeGUID, sizeOfReturnBuffer = 50;
3267       char     returnBuffer[51];
3268       WCHAR    buff[51];
3269       DPNAME   dpName;
3270       HRESULT  hr;
3271
3272       DPCOMPOUNDADDRESSELEMENT dpCompoundAddress;
3273       LPVOID                   lpAddressBuffer = NULL;
3274       DWORD                    dwAddressBufferSize = 0;
3275
3276       TRACE(" this time through: %s\n", subKeyName );
3277
3278       /* Get a handle for this particular service provider */
3279       if( RegOpenKeyExA( hkResult, subKeyName, 0, KEY_READ,
3280                          &hkServiceProvider ) != ERROR_SUCCESS )
3281       {
3282          ERR(": what the heck is going on?\n" );
3283          continue;
3284       }
3285
3286       if( RegQueryValueExA( hkServiceProvider, guidDataSubKey,
3287                             NULL, &returnTypeGUID, (LPBYTE)returnBuffer,
3288                             &sizeOfReturnBuffer ) != ERROR_SUCCESS )
3289       {
3290         ERR(": missing GUID registry data members\n" );
3291         RegCloseKey(hkServiceProvider);
3292         continue;
3293       }
3294       RegCloseKey(hkServiceProvider);
3295
3296       /* FIXME: Check return types to ensure we're interpreting data right */
3297       MultiByteToWideChar( CP_ACP, 0, returnBuffer, -1, buff, sizeof(buff)/sizeof(WCHAR) );
3298       CLSIDFromString( buff, &serviceProviderGUID );
3299       /* FIXME: Have I got a memory leak on the serviceProviderGUID? */
3300
3301       /* Fill in the DPNAME struct for the service provider */
3302       dpName.dwSize             = sizeof( dpName );
3303       dpName.dwFlags            = 0;
3304       dpName.u1.lpszShortNameA = subKeyName;
3305       dpName.u2.lpszLongNameA  = NULL;
3306
3307       /* Create the compound address for the service provider.
3308          NOTE: This is a gruesome architectural scar right now. DP uses DPL and DPL uses DP
3309                nast stuff. This may be why the native dll just gets around this little bit by
3310                allocating an 80 byte buffer which isn't even a filled with a valid compound
3311                address. Oh well. Creating a proper compound address is the way to go anyways
3312                despite this method taking slightly more heap space and realtime :) */
3313
3314       dpCompoundAddress.guidDataType = DPAID_LobbyProvider;
3315       dpCompoundAddress.dwDataSize   = sizeof( GUID );
3316       dpCompoundAddress.lpData       = &serviceProviderGUID;
3317
3318       if( ( hr = DPL_CreateCompoundAddress( &dpCompoundAddress, 1, lpAddressBuffer,
3319                                      &dwAddressBufferSize, TRUE ) ) != DPERR_BUFFERTOOSMALL )
3320       {
3321         ERR( "can't get buffer size: %s\n", DPLAYX_HresultToString( hr ) );
3322         return hr;
3323       }
3324
3325       /* Now allocate the buffer */
3326       lpAddressBuffer = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwAddressBufferSize );
3327
3328       if( ( hr = DPL_CreateCompoundAddress( &dpCompoundAddress, 1, lpAddressBuffer,
3329                                      &dwAddressBufferSize, TRUE ) ) != DP_OK )
3330       {
3331         ERR( "can't create address: %s\n", DPLAYX_HresultToString( hr ) );
3332         HeapFree( GetProcessHeap(), 0, lpAddressBuffer );
3333         return hr;
3334       }
3335
3336       /* The enumeration will return FALSE if we are not to continue */
3337       if( !lpEnumCallback( &serviceProviderGUID, lpAddressBuffer, dwAddressBufferSize,
3338                            &dpName, dwFlags, lpContext ) )
3339       {
3340          HeapFree( GetProcessHeap(), 0, lpAddressBuffer );
3341          return DP_OK;
3342       }
3343       HeapFree( GetProcessHeap(), 0, lpAddressBuffer );
3344     }
3345   }
3346
3347   return DP_OK;
3348 }
3349
3350 static HRESULT WINAPI IDirectPlay4Impl_EnumConnections( IDirectPlay4 *iface,
3351         const GUID *application, LPDPENUMCONNECTIONSCALLBACK enumcb, void *context, DWORD flags )
3352 {
3353     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
3354     FIXME( "(%p)->(%p,%p,%p,0x%08x): stub\n", This, application, enumcb, context, flags );
3355     return DP_OK;
3356 }
3357
3358 static HRESULT WINAPI IDirectPlay4AImpl_EnumGroupsInGroup( IDirectPlay4A *iface, DPID group,
3359         GUID *instance, LPDPENUMPLAYERSCALLBACK2 enumplayercb, void *context, DWORD flags )
3360 {
3361     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
3362     return IDirectPlayX_EnumGroupsInGroup( &This->IDirectPlay4_iface, group, instance,
3363             enumplayercb, context, flags );
3364 }
3365
3366 static HRESULT WINAPI IDirectPlay4Impl_EnumGroupsInGroup( IDirectPlay4 *iface, DPID group,
3367         GUID *instance, LPDPENUMPLAYERSCALLBACK2 enumplayercb, void *context, DWORD flags )
3368 {
3369     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
3370     lpGroupList glist;
3371     lpGroupData gdata;
3372
3373     FIXME( "(%p)->(0x%08x,%p,%p,%p,0x%08x): semi stub\n", This, group, instance, enumplayercb,
3374             context, flags );
3375
3376     if ( This->dp2->connectionInitialized == NO_PROVIDER )
3377         return DPERR_UNINITIALIZED;
3378
3379     if ( ( gdata = DP_FindAnyGroup(This, group ) ) == NULL )
3380         return DPERR_INVALIDGROUP;
3381
3382     if ( DPQ_IS_EMPTY( gdata->groups ) )
3383         return DP_OK;
3384
3385
3386     for( glist = DPQ_FIRST( gdata->groups ); ; glist = DPQ_NEXT( glist->groups ) )
3387     {
3388         /* FIXME: Should check flags for match here */
3389         if ( !(*enumplayercb)( glist->lpGData->dpid, DPPLAYERTYPE_GROUP, &glist->lpGData->name,
3390                     flags, context ) )
3391             return DP_OK; /* User requested break */
3392
3393         if ( DPQ_IS_ENDOFLIST( glist->groups ) )
3394             break;
3395     }
3396
3397     return DP_OK;
3398 }
3399
3400 static HRESULT WINAPI IDirectPlay4AImpl_GetGroupConnectionSettings( IDirectPlay4A *iface,
3401         DWORD flags, DPID group, void *data, DWORD *size )
3402 {
3403     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
3404     FIXME("(%p)->(0x%08x,0x%08x,%p,%p): stub\n", This, flags, group, data, size );
3405     return DP_OK;
3406 }
3407
3408 static HRESULT WINAPI IDirectPlay4Impl_GetGroupConnectionSettings( IDirectPlay4 *iface, DWORD flags,
3409         DPID group, void *data, DWORD *size )
3410 {
3411     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
3412     FIXME( "(%p)->(0x%08x,0x%08x,%p,%p): stub\n", This, flags, group, data, size );
3413     return DP_OK;
3414 }
3415
3416 static BOOL CALLBACK DP_GetSpLpGuidFromCompoundAddress(
3417     REFGUID         guidDataType,
3418     DWORD           dwDataSize,
3419     LPCVOID         lpData,
3420     LPVOID          lpContext )
3421 {
3422   /* Looking for the GUID of the provider to load */
3423   if( ( IsEqualGUID( guidDataType, &DPAID_ServiceProvider ) ) ||
3424       ( IsEqualGUID( guidDataType, &DPAID_LobbyProvider ) )
3425     )
3426   {
3427     TRACE( "Found SP/LP (%s) %s (data size = 0x%08x)\n",
3428            debugstr_guid( guidDataType ), debugstr_guid( lpData ), dwDataSize );
3429
3430     if( dwDataSize != sizeof( GUID ) )
3431     {
3432       ERR( "Invalid sp/lp guid size 0x%08x\n", dwDataSize );
3433     }
3434
3435     memcpy( lpContext, lpData, dwDataSize );
3436
3437     /* There shouldn't be more than 1 GUID/compound address */
3438     return FALSE;
3439   }
3440
3441   /* Still waiting for what we want */
3442   return TRUE;
3443 }
3444
3445
3446 /* Find and perform a LoadLibrary on the requested SP or LP GUID */
3447 static HMODULE DP_LoadSP( LPCGUID lpcGuid, LPSPINITDATA lpSpData, LPBOOL lpbIsDpSp )
3448 {
3449   UINT i;
3450   LPCSTR spSubKey         = "SOFTWARE\\Microsoft\\DirectPlay\\Service Providers";
3451   LPCSTR lpSubKey         = "SOFTWARE\\Microsoft\\DirectPlay\\Lobby Providers";
3452   LPCSTR guidDataSubKey   = "Guid";
3453   LPCSTR majVerDataSubKey = "dwReserved1";
3454   LPCSTR minVerDataSubKey = "dwReserved2";
3455   LPCSTR pathSubKey       = "Path";
3456
3457   TRACE( " request to load %s\n", debugstr_guid( lpcGuid ) );
3458
3459   /* FIXME: Cloned code with a quick hack. */
3460   for( i=0; i<2; i++ )
3461   {
3462     HKEY hkResult;
3463     LPCSTR searchSubKey;
3464     char subKeyName[51];
3465     DWORD dwIndex, sizeOfSubKeyName=50;
3466     FILETIME filetime;
3467
3468     (i == 0) ? (searchSubKey = spSubKey ) : (searchSubKey = lpSubKey );
3469     *lpbIsDpSp = (i == 0) ? TRUE : FALSE;
3470
3471
3472     /* Need to loop over the service providers in the registry */
3473     if( RegOpenKeyExA( HKEY_LOCAL_MACHINE, searchSubKey,
3474                          0, KEY_READ, &hkResult ) != ERROR_SUCCESS )
3475     {
3476       /* Hmmm. Does this mean that there are no service providers? */
3477       ERR(": no service providers?\n");
3478       return 0;
3479     }
3480
3481     /* Traverse all the service providers we have available */
3482     for( dwIndex=0;
3483          RegEnumKeyExA( hkResult, dwIndex, subKeyName, &sizeOfSubKeyName,
3484                         NULL, NULL, NULL, &filetime ) != ERROR_NO_MORE_ITEMS;
3485          ++dwIndex, sizeOfSubKeyName=51 )
3486     {
3487
3488       HKEY     hkServiceProvider;
3489       GUID     serviceProviderGUID;
3490       DWORD    returnType, sizeOfReturnBuffer = 255;
3491       char     returnBuffer[256];
3492       WCHAR    buff[51];
3493       DWORD    dwTemp, len;
3494
3495       TRACE(" this time through: %s\n", subKeyName );
3496
3497       /* Get a handle for this particular service provider */
3498       if( RegOpenKeyExA( hkResult, subKeyName, 0, KEY_READ,
3499                          &hkServiceProvider ) != ERROR_SUCCESS )
3500       {
3501          ERR(": what the heck is going on?\n" );
3502          continue;
3503       }
3504
3505       if( RegQueryValueExA( hkServiceProvider, guidDataSubKey,
3506                             NULL, &returnType, (LPBYTE)returnBuffer,
3507                             &sizeOfReturnBuffer ) != ERROR_SUCCESS )
3508       {
3509         ERR(": missing GUID registry data members\n" );
3510         continue;
3511       }
3512
3513       /* FIXME: Check return types to ensure we're interpreting data right */
3514       MultiByteToWideChar( CP_ACP, 0, returnBuffer, -1, buff, sizeof(buff)/sizeof(WCHAR) );
3515       CLSIDFromString( buff, &serviceProviderGUID );
3516       /* FIXME: Have I got a memory leak on the serviceProviderGUID? */
3517
3518       /* Determine if this is the Service Provider that the user asked for */
3519       if( !IsEqualGUID( &serviceProviderGUID, lpcGuid ) )
3520       {
3521         continue;
3522       }
3523
3524       if( i == 0 ) /* DP SP */
3525       {
3526         len = MultiByteToWideChar( CP_ACP, 0, subKeyName, -1, NULL, 0 );
3527         lpSpData->lpszName = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3528         MultiByteToWideChar( CP_ACP, 0, subKeyName, -1, lpSpData->lpszName, len );
3529       }
3530
3531       sizeOfReturnBuffer = 255;
3532
3533       /* Get dwReserved1 */
3534       if( RegQueryValueExA( hkServiceProvider, majVerDataSubKey,
3535                             NULL, &returnType, (LPBYTE)returnBuffer,
3536                             &sizeOfReturnBuffer ) != ERROR_SUCCESS )
3537       {
3538          ERR(": missing dwReserved1 registry data members\n") ;
3539          continue;
3540       }
3541
3542       if( i == 0 )
3543           memcpy( &lpSpData->dwReserved1, returnBuffer, sizeof(lpSpData->dwReserved1) );
3544
3545       sizeOfReturnBuffer = 255;
3546
3547       /* Get dwReserved2 */
3548       if( RegQueryValueExA( hkServiceProvider, minVerDataSubKey,
3549                             NULL, &returnType, (LPBYTE)returnBuffer,
3550                             &sizeOfReturnBuffer ) != ERROR_SUCCESS )
3551       {
3552          ERR(": missing dwReserved1 registry data members\n") ;
3553          continue;
3554       }
3555
3556       if( i == 0 )
3557           memcpy( &lpSpData->dwReserved2, returnBuffer, sizeof(lpSpData->dwReserved2) );
3558
3559       sizeOfReturnBuffer = 255;
3560
3561       /* Get the path for this service provider */
3562       if( ( dwTemp = RegQueryValueExA( hkServiceProvider, pathSubKey,
3563                             NULL, NULL, (LPBYTE)returnBuffer,
3564                             &sizeOfReturnBuffer ) ) != ERROR_SUCCESS )
3565       {
3566         ERR(": missing PATH registry data members: 0x%08x\n", dwTemp );
3567         continue;
3568       }
3569
3570       TRACE( "Loading %s\n", returnBuffer );
3571       return LoadLibraryA( returnBuffer );
3572     }
3573   }
3574
3575   return 0;
3576 }
3577
3578 static
3579 HRESULT DP_InitializeDPSP( IDirectPlay3Impl* This, HMODULE hServiceProvider )
3580 {
3581   HRESULT hr;
3582   LPDPSP_SPINIT SPInit;
3583
3584   /* Initialize the service provider by calling SPInit */
3585   SPInit = (LPDPSP_SPINIT)GetProcAddress( hServiceProvider, "SPInit" );
3586
3587   if( SPInit == NULL )
3588   {
3589     ERR( "Service provider doesn't provide SPInit interface?\n" );
3590     FreeLibrary( hServiceProvider );
3591     return DPERR_UNAVAILABLE;
3592   }
3593
3594   TRACE( "Calling SPInit (DP SP entry point)\n" );
3595
3596   hr = (*SPInit)( &This->dp2->spData );
3597
3598   if( FAILED(hr) )
3599   {
3600     ERR( "DP SP Initialization failed: %s\n", DPLAYX_HresultToString(hr) );
3601     FreeLibrary( hServiceProvider );
3602     return hr;
3603   }
3604
3605   /* FIXME: Need to verify the sanity of the returned callback table
3606    *        using IsBadCodePtr */
3607   This->dp2->bSPInitialized = TRUE;
3608
3609   /* This interface is now initialized as a DP object */
3610   This->dp2->connectionInitialized = DP_SERVICE_PROVIDER;
3611
3612   /* Store the handle of the module so that we can unload it later */
3613   This->dp2->hServiceProvider = hServiceProvider;
3614
3615   return hr;
3616 }
3617
3618 static
3619 HRESULT DP_InitializeDPLSP( IDirectPlay3Impl* This, HMODULE hLobbyProvider )
3620 {
3621   HRESULT hr;
3622   LPSP_INIT DPLSPInit;
3623
3624   /* Initialize the service provider by calling SPInit */
3625   DPLSPInit = (LPSP_INIT)GetProcAddress( hLobbyProvider, "DPLSPInit" );
3626
3627   if( DPLSPInit == NULL )
3628   {
3629     ERR( "Service provider doesn't provide DPLSPInit interface?\n" );
3630     FreeLibrary( hLobbyProvider );
3631     return DPERR_UNAVAILABLE;
3632   }
3633
3634   TRACE( "Calling DPLSPInit (DPL SP entry point)\n" );
3635
3636   hr = (*DPLSPInit)( &This->dp2->dplspData );
3637
3638   if( FAILED(hr) )
3639   {
3640     ERR( "DPL SP Initialization failed: %s\n", DPLAYX_HresultToString(hr) );
3641     FreeLibrary( hLobbyProvider );
3642     return hr;
3643   }
3644
3645   /* FIXME: Need to verify the sanity of the returned callback table
3646    *        using IsBadCodePtr */
3647
3648   This->dp2->bDPLSPInitialized = TRUE;
3649
3650   /* This interface is now initialized as a lobby object */
3651   This->dp2->connectionInitialized = DP_LOBBY_PROVIDER;
3652
3653   /* Store the handle of the module so that we can unload it later */
3654   This->dp2->hDPLobbyProvider = hLobbyProvider;
3655
3656   return hr;
3657 }
3658
3659 static HRESULT WINAPI IDirectPlay4AImpl_InitializeConnection( IDirectPlay4A *iface,
3660         void *connection, DWORD flags )
3661 {
3662     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
3663     return IDirectPlayX_InitializeConnection( &This->IDirectPlay4_iface, connection, flags );
3664 }
3665
3666 static HRESULT WINAPI IDirectPlay4Impl_InitializeConnection( IDirectPlay4 *iface,
3667         void *connection, DWORD flags )
3668 {
3669     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
3670     HMODULE servprov;
3671     GUID sp;
3672     const DWORD size = 80; /* FIXME: Need to calculate it correctly */
3673     BOOL is_dp_sp; /* TRUE if Direct Play SP, FALSE if Direct Play Lobby SP */
3674     HRESULT hr;
3675
3676     TRACE( "(%p)->(%p,0x%08x)\n", This, connection, flags );
3677
3678     if ( !connection )
3679         return DPERR_INVALIDPARAMS;
3680
3681     if ( flags )
3682         return DPERR_INVALIDFLAGS;
3683
3684     if ( This->dp2->connectionInitialized != NO_PROVIDER )
3685         return DPERR_ALREADYINITIALIZED;
3686
3687     /* Find out what the requested SP is and how large this buffer is */
3688     hr = DPL_EnumAddress( DP_GetSpLpGuidFromCompoundAddress, connection, size, &sp );
3689
3690     if ( FAILED(hr) )
3691     {
3692         ERR( "Invalid compound address?\n" );
3693         return DPERR_UNAVAILABLE;
3694     }
3695
3696     /* Load the service provider */
3697     servprov = DP_LoadSP( &sp, &This->dp2->spData, &is_dp_sp );
3698
3699     if ( !servprov )
3700     {
3701         ERR( "Unable to load service provider %s\n", debugstr_guid(&sp) );
3702         return DPERR_UNAVAILABLE;
3703     }
3704
3705     if ( is_dp_sp )
3706     {
3707          /* Fill in what we can of the Service Provider required information.
3708           * The rest was be done in DP_LoadSP
3709           */
3710          This->dp2->spData.lpAddress = connection;
3711          This->dp2->spData.dwAddressSize = size;
3712          This->dp2->spData.lpGuid = &sp;
3713          hr = DP_InitializeDPSP( This, servprov );
3714     }
3715     else
3716     {
3717          This->dp2->dplspData.lpAddress = connection;
3718          hr = DP_InitializeDPLSP( This, servprov );
3719     }
3720
3721     if ( FAILED(hr) )
3722         return hr;
3723
3724     return DP_OK;
3725 }
3726
3727 static HRESULT WINAPI IDirectPlay4AImpl_SecureOpen( IDirectPlay4A *iface,
3728         const DPSESSIONDESC2 *lpsd, DWORD dwFlags, const DPSECURITYDESC *lpSecurity,
3729         const DPCREDENTIALS *lpCredentials )
3730 {
3731     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
3732     return DP_SecureOpen( This, lpsd, dwFlags, lpSecurity, lpCredentials, TRUE );
3733 }
3734
3735 static HRESULT WINAPI DirectPlay3WImpl_SecureOpen
3736           ( LPDIRECTPLAY3 iface, LPCDPSESSIONDESC2 lpsd, DWORD dwFlags,
3737             LPCDPSECURITYDESC lpSecurity, LPCDPCREDENTIALS lpCredentials )
3738 {
3739   IDirectPlay2Impl *This = (IDirectPlay2Impl *)iface; /* Yes a dp 2 interface */
3740   return DP_SecureOpen( This, lpsd, dwFlags, lpSecurity, lpCredentials, FALSE );
3741 }
3742
3743 static HRESULT WINAPI IDirectPlay4AImpl_SendChatMessage( IDirectPlay4A *iface, DPID from,
3744         DPID to, DWORD flags, DPCHAT *chatmsg )
3745 {
3746     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
3747     FIXME("(%p)->(0x%08x,0x%08x,0x%08x,%p): stub\n", This, from, to, flags, chatmsg );
3748     return DP_OK;
3749 }
3750
3751 static HRESULT WINAPI IDirectPlay4Impl_SendChatMessage( IDirectPlay4 *iface, DPID from, DPID to,
3752         DWORD flags, DPCHAT *chatmsg )
3753 {
3754     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
3755     FIXME( "(%p)->(0x%08x,0x%08x,0x%08x,%p): stub\n", This, from, to, flags, chatmsg );
3756     return DP_OK;
3757 }
3758
3759 static HRESULT WINAPI IDirectPlay4AImpl_SetGroupConnectionSettings( IDirectPlay4A *iface,
3760         DWORD flags, DPID group, DPLCONNECTION *connection )
3761 {
3762     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
3763     FIXME("(%p)->(0x%08x,0x%08x,%p): stub\n", This, flags, group, connection );
3764     return DP_OK;
3765 }
3766
3767 static HRESULT WINAPI IDirectPlay4Impl_SetGroupConnectionSettings( IDirectPlay4 *iface, DWORD flags,
3768         DPID group, DPLCONNECTION *connection )
3769 {
3770     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
3771     FIXME( "(%p)->(0x%08x,0x%08x,%p): stub\n", This, flags, group, connection );
3772     return DP_OK;
3773 }
3774
3775 static HRESULT WINAPI IDirectPlay4AImpl_StartSession( IDirectPlay4A *iface, DWORD dwFlags,
3776         DPID idGroup )
3777 {
3778   IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
3779   FIXME("(%p)->(0x%08x,0x%08x): stub\n", This, dwFlags, idGroup );
3780   return DP_OK;
3781 }
3782
3783 static HRESULT WINAPI IDirectPlay4Impl_StartSession( IDirectPlay4 *iface, DWORD flags, DPID group )
3784 {
3785     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
3786     FIXME( "(%p)->(0x%08x,0x%08x): stub\n", This, flags, group );
3787     return DP_OK;
3788 }
3789
3790 static HRESULT WINAPI IDirectPlay4AImpl_GetGroupFlags( IDirectPlay4A *iface, DPID idGroup,
3791         DWORD *lpdwFlags )
3792 {
3793   IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
3794   FIXME("(%p)->(0x%08x,%p): stub\n", This, idGroup, lpdwFlags );
3795   return DP_OK;
3796 }
3797
3798 static HRESULT WINAPI IDirectPlay4Impl_GetGroupFlags( IDirectPlay4 *iface, DPID group,
3799         DWORD *flags )
3800 {
3801     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
3802     FIXME( "(%p)->(0x%08x,%p): stub\n", This, group, flags );
3803     return DP_OK;
3804 }
3805
3806 static HRESULT WINAPI IDirectPlay4AImpl_GetGroupParent( IDirectPlay4A *iface, DPID group,
3807         DPID *parent )
3808 {
3809     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
3810     return IDirectPlayX_GetGroupParent( &This->IDirectPlay4_iface, group, parent );
3811 }
3812
3813 static HRESULT WINAPI IDirectPlay4Impl_GetGroupParent( IDirectPlay4 *iface, DPID group,
3814         DPID *parent )
3815 {
3816     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
3817     lpGroupData gdata;
3818
3819     TRACE( "(%p)->(0x%08x,%p)\n", This, group, parent );
3820
3821     if ( ( gdata = DP_FindAnyGroup( This, group ) ) == NULL )
3822         return DPERR_INVALIDGROUP;
3823
3824     *parent = gdata->dpid;
3825
3826     return DP_OK;
3827 }
3828
3829 static HRESULT WINAPI IDirectPlay4AImpl_GetPlayerAccount( IDirectPlay4A *iface, DPID player,
3830         DWORD flags, void *data, DWORD *size )
3831 {
3832     IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
3833     FIXME("(%p)->(0x%08x,0x%08x,%p,%p): stub\n", This, player, flags, data, size );
3834     return DP_OK;
3835 }
3836
3837 static HRESULT WINAPI IDirectPlay4Impl_GetPlayerAccount( IDirectPlay4 *iface, DPID player,
3838         DWORD flags, void *data, DWORD *size )
3839 {
3840     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
3841     FIXME( "(%p)->(0x%08x,0x%08x,%p,%p): stub\n", This, player, flags, data, size );
3842     return DP_OK;
3843 }
3844
3845 static HRESULT WINAPI IDirectPlay4AImpl_GetPlayerFlags( IDirectPlay4A *iface, DPID idPlayer,
3846         DWORD *lpdwFlags )
3847 {
3848   IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
3849   FIXME("(%p)->(0x%08x,%p): stub\n", This, idPlayer, lpdwFlags );
3850   return DP_OK;
3851 }
3852
3853 static HRESULT WINAPI IDirectPlay4Impl_GetPlayerFlags( IDirectPlay4 *iface, DPID player,
3854         DWORD *flags )
3855 {
3856     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
3857     FIXME( "(%p)->(0x%08x,%p): stub\n", This, player, flags );
3858     return DP_OK;
3859 }
3860
3861 static HRESULT WINAPI DirectPlay4AImpl_GetGroupOwner
3862           ( LPDIRECTPLAY4A iface, DPID idGroup, LPDPID lpidGroupOwner )
3863 {
3864   IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
3865   FIXME("(%p)->(0x%08x,%p): stub\n", This, idGroup, lpidGroupOwner );
3866   return DP_OK;
3867 }
3868
3869 static HRESULT WINAPI IDirectPlay4Impl_GetGroupOwner( IDirectPlay4 *iface, DPID group,
3870         DPID *owner )
3871 {
3872     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
3873     FIXME( "(%p)->(0x%08x,%p): stub\n", This, group, owner );
3874     return DP_OK;
3875 }
3876
3877 static HRESULT WINAPI DirectPlay4AImpl_SetGroupOwner
3878           ( LPDIRECTPLAY4A iface, DPID idGroup , DPID idGroupOwner )
3879 {
3880   IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
3881   FIXME("(%p)->(0x%08x,0x%08x): stub\n", This, idGroup, idGroupOwner );
3882   return DP_OK;
3883 }
3884
3885 static HRESULT WINAPI IDirectPlay4Impl_SetGroupOwner( IDirectPlay4 *iface, DPID group ,
3886         DPID owner )
3887 {
3888     IDirectPlayImpl *This = impl_from_IDirectPlay4( iface );
3889     FIXME( "(%p)->(0x%08x,0x%08x): stub\n", This, group, owner );
3890     return DP_OK;
3891 }
3892
3893 static HRESULT DP_SendEx
3894           ( IDirectPlay2Impl* This, DPID idFrom, DPID idTo, DWORD dwFlags,
3895             LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout,
3896             LPVOID lpContext, LPDWORD lpdwMsgID, BOOL bAnsi )
3897 {
3898   BOOL         bValidDestination = FALSE;
3899
3900   FIXME( "(%p)->(0x%08x,0x%08x,0x%08x,%p,0x%08x,0x%08x,0x%08x,%p,%p,%u)"
3901          ": stub\n",
3902          This, idFrom, idTo, dwFlags, lpData, dwDataSize, dwPriority,
3903          dwTimeout, lpContext, lpdwMsgID, bAnsi );
3904
3905   if( This->dp2->connectionInitialized == NO_PROVIDER )
3906   {
3907     return DPERR_UNINITIALIZED;
3908   }
3909
3910   /* FIXME: Add parameter checking */
3911   /* FIXME: First call to this needs to acquire a message id which will be
3912    *        used for multiple sends
3913    */
3914
3915   /* NOTE: Can't send messages to yourself - this will be trapped in receive */
3916
3917   /* Verify that the message is being sent from a valid local player. The
3918    * from player may be anonymous DPID_UNKNOWN
3919    */
3920   if( idFrom != DPID_UNKNOWN )
3921   {
3922     if( DP_FindPlayer( This, idFrom ) == NULL )
3923     {
3924       WARN( "INFO: Invalid from player 0x%08x\n", idFrom );
3925       return DPERR_INVALIDPLAYER;
3926     }
3927   }
3928
3929   /* Verify that the message is being sent to a valid player, group or to
3930    * everyone. If it's valid, send it to those players.
3931    */
3932   if( idTo == DPID_ALLPLAYERS )
3933   {
3934     bValidDestination = TRUE;
3935
3936     /* See if SP has the ability to multicast. If so, use it */
3937     if( This->dp2->spData.lpCB->SendToGroupEx )
3938     {
3939       FIXME( "Use group sendex to group 0\n" );
3940     }
3941     else if( This->dp2->spData.lpCB->SendToGroup ) /* obsolete interface */
3942     {
3943       FIXME( "Use obsolete group send to group 0\n" );
3944     }
3945     else /* No multicast, multiplicate */
3946     {
3947       /* Send to all players we know about */
3948       FIXME( "Send to all players using EnumPlayersInGroup\n" );
3949     }
3950   }
3951
3952   if( ( !bValidDestination ) &&
3953       ( DP_FindPlayer( This, idTo ) != NULL )
3954     )
3955   {
3956     /* Have the service provider send this message */
3957     /* FIXME: Could optimize for local interface sends */
3958     return DP_SP_SendEx( This, dwFlags, lpData, dwDataSize, dwPriority,
3959                          dwTimeout, lpContext, lpdwMsgID );
3960   }
3961
3962   if( ( !bValidDestination ) &&
3963       ( DP_FindAnyGroup( This, idTo ) != NULL )
3964     )
3965   {
3966     bValidDestination = TRUE;
3967
3968     /* See if SP has the ability to multicast. If so, use it */
3969     if( This->dp2->spData.lpCB->SendToGroupEx )
3970     {
3971       FIXME( "Use group sendex\n" );
3972     }
3973     else if( This->dp2->spData.lpCB->SendToGroup ) /* obsolete interface */
3974     {
3975       FIXME( "Use obsolete group send to group\n" );
3976     }
3977     else /* No multicast, multiplicate */
3978     {
3979       FIXME( "Send to all players using EnumPlayersInGroup\n" );
3980     }
3981
3982 #if 0
3983     if( bExpectReply )
3984     {
3985       DWORD dwWaitReturn;
3986
3987       This->dp2->hReplyEvent = CreateEventW( NULL, FALSE, FALSE, NULL );
3988
3989       dwWaitReturn = WaitForSingleObject( hReplyEvent, dwTimeout );
3990       if( dwWaitReturn != WAIT_OBJECT_0 )
3991       {
3992         ERR( "Wait failed 0x%08lx\n", dwWaitReturn );
3993       }
3994     }
3995 #endif
3996   }
3997
3998   if( !bValidDestination )
3999   {
4000     return DPERR_INVALIDPLAYER;
4001   }
4002   else
4003   {
4004     /* FIXME: Should return what the send returned */
4005     return DP_OK;
4006   }
4007 }
4008
4009
4010 static HRESULT WINAPI DirectPlay4AImpl_SendEx
4011           ( LPDIRECTPLAY4A iface, DPID idFrom, DPID idTo, DWORD dwFlags,
4012             LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout,
4013             LPVOID lpContext, LPDWORD lpdwMsgID )
4014 {
4015   IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
4016   return DP_SendEx( This, idFrom, idTo, dwFlags, lpData, dwDataSize,
4017                     dwPriority, dwTimeout, lpContext, lpdwMsgID, TRUE );
4018 }
4019
4020 static HRESULT WINAPI DirectPlay4WImpl_SendEx
4021           ( LPDIRECTPLAY4 iface, DPID idFrom, DPID idTo, DWORD dwFlags,
4022             LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout,
4023             LPVOID lpContext, LPDWORD lpdwMsgID )
4024 {
4025   IDirectPlay2Impl *This = (IDirectPlay2Impl *)iface; /* yes downcast to 2 */
4026   return DP_SendEx( This, idFrom, idTo, dwFlags, lpData, dwDataSize,
4027                     dwPriority, dwTimeout, lpContext, lpdwMsgID, FALSE );
4028 }
4029
4030 static HRESULT DP_SP_SendEx
4031           ( IDirectPlay2Impl* This, DWORD dwFlags,
4032             LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout,
4033             LPVOID lpContext, LPDWORD lpdwMsgID )
4034 {
4035   LPDPMSG lpMElem;
4036
4037   FIXME( ": stub\n" );
4038
4039   /* FIXME: This queuing should only be for async messages */
4040
4041   lpMElem = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *lpMElem ) );
4042   lpMElem->msg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwDataSize );
4043
4044   CopyMemory( lpMElem->msg, lpData, dwDataSize );
4045
4046   /* FIXME: Need to queue based on priority */
4047   DPQ_INSERT( This->dp2->sendMsgs, lpMElem, msgs );
4048
4049   return DP_OK;
4050 }
4051
4052 static HRESULT DP_IF_GetMessageQueue
4053           ( IDirectPlay4Impl* This, DPID idFrom, DPID idTo, DWORD dwFlags,
4054             LPDWORD lpdwNumMsgs, LPDWORD lpdwNumBytes, BOOL bAnsi )
4055 {
4056   HRESULT hr = DP_OK;
4057
4058   FIXME( "(%p)->(0x%08x,0x%08x,0x%08x,%p,%p,%u): semi stub\n",
4059          This, idFrom, idTo, dwFlags, lpdwNumMsgs, lpdwNumBytes, bAnsi );
4060
4061   /* FIXME: Do we need to do idFrom and idTo sanity checking here? */
4062   /* FIXME: What about sends which are not immediate? */
4063
4064   if( This->dp2->spData.lpCB->GetMessageQueue )
4065   {
4066     DPSP_GETMESSAGEQUEUEDATA data;
4067
4068     FIXME( "Calling SP GetMessageQueue - is it right?\n" );
4069
4070     /* FIXME: None of this is documented :( */
4071
4072     data.lpISP        = This->dp2->spData.lpISP;
4073     data.dwFlags      = dwFlags;
4074     data.idFrom       = idFrom;
4075     data.idTo         = idTo;
4076     data.lpdwNumMsgs  = lpdwNumMsgs;
4077     data.lpdwNumBytes = lpdwNumBytes;
4078
4079     hr = (*This->dp2->spData.lpCB->GetMessageQueue)( &data );
4080   }
4081   else
4082   {
4083     FIXME( "No SP for GetMessageQueue - fake some data\n" );
4084   }
4085
4086   return hr;
4087 }
4088
4089 static HRESULT WINAPI DirectPlay4AImpl_GetMessageQueue
4090           ( LPDIRECTPLAY4A iface, DPID idFrom, DPID idTo, DWORD dwFlags,
4091             LPDWORD lpdwNumMsgs, LPDWORD lpdwNumBytes )
4092 {
4093   IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
4094   return DP_IF_GetMessageQueue( This, idFrom, idTo, dwFlags, lpdwNumMsgs,
4095                                 lpdwNumBytes, TRUE );
4096 }
4097
4098 static HRESULT WINAPI DirectPlay4WImpl_GetMessageQueue
4099           ( LPDIRECTPLAY4 iface, DPID idFrom, DPID idTo, DWORD dwFlags,
4100             LPDWORD lpdwNumMsgs, LPDWORD lpdwNumBytes )
4101 {
4102   IDirectPlay4Impl *This = (IDirectPlay4Impl *)iface;
4103   return DP_IF_GetMessageQueue( This, idFrom, idTo, dwFlags, lpdwNumMsgs,
4104                                 lpdwNumBytes, FALSE );
4105 }
4106
4107 static HRESULT DP_IF_CancelMessage
4108           ( IDirectPlay4Impl* This, DWORD dwMsgID, DWORD dwFlags,
4109             DWORD dwMinPriority, DWORD dwMaxPriority, BOOL bAnsi )
4110 {
4111   HRESULT hr = DP_OK;
4112
4113   FIXME( "(%p)->(0x%08x,0x%08x,%u): semi stub\n",
4114          This, dwMsgID, dwFlags, bAnsi );
4115
4116   if( This->dp2->spData.lpCB->Cancel )
4117   {
4118     DPSP_CANCELDATA data;
4119
4120     TRACE( "Calling SP Cancel\n" );
4121
4122     /* FIXME: Undocumented callback */
4123
4124     data.lpISP          = This->dp2->spData.lpISP;
4125     data.dwFlags        = dwFlags;
4126     data.lprglpvSPMsgID = NULL;
4127     data.cSPMsgID       = dwMsgID;
4128     data.dwMinPriority  = dwMinPriority;
4129     data.dwMaxPriority  = dwMaxPriority;
4130
4131     hr = (*This->dp2->spData.lpCB->Cancel)( &data );
4132   }
4133   else
4134   {
4135     FIXME( "SP doesn't implement Cancel\n" );
4136   }
4137
4138   return hr;
4139 }
4140
4141 static HRESULT WINAPI DirectPlay4AImpl_CancelMessage
4142           ( LPDIRECTPLAY4A iface, DWORD dwMsgID, DWORD dwFlags )
4143 {
4144   IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
4145
4146   if( dwFlags != 0 )
4147   {
4148     return DPERR_INVALIDFLAGS;
4149   }
4150
4151   if( dwMsgID == 0 )
4152   {
4153     dwFlags |= DPCANCELSEND_ALL;
4154   }
4155
4156   return DP_IF_CancelMessage( This, dwMsgID, dwFlags, 0, 0, TRUE );
4157 }
4158
4159 static HRESULT WINAPI DirectPlay4WImpl_CancelMessage
4160           ( LPDIRECTPLAY4 iface, DWORD dwMsgID, DWORD dwFlags )
4161 {
4162   IDirectPlay4Impl *This = (IDirectPlay4Impl *)iface;
4163
4164   if( dwFlags != 0 )
4165   {
4166     return DPERR_INVALIDFLAGS;
4167   }
4168
4169   if( dwMsgID == 0 )
4170   {
4171     dwFlags |= DPCANCELSEND_ALL;
4172   }
4173
4174   return DP_IF_CancelMessage( This, dwMsgID, dwFlags, 0, 0, FALSE );
4175 }
4176
4177 static HRESULT WINAPI DirectPlay4AImpl_CancelPriority
4178           ( LPDIRECTPLAY4A iface, DWORD dwMinPriority, DWORD dwMaxPriority,
4179             DWORD dwFlags )
4180 {
4181   IDirectPlayImpl *This = impl_from_IDirectPlay4A( iface );
4182
4183   if( dwFlags != 0 )
4184   {
4185     return DPERR_INVALIDFLAGS;
4186   }
4187
4188   return DP_IF_CancelMessage( This, 0, DPCANCELSEND_PRIORITY, dwMinPriority,
4189                               dwMaxPriority, TRUE );
4190 }
4191
4192 static HRESULT WINAPI DirectPlay4WImpl_CancelPriority
4193           ( LPDIRECTPLAY4 iface, DWORD dwMinPriority, DWORD dwMaxPriority,
4194             DWORD dwFlags )
4195 {
4196   IDirectPlay4Impl *This = (IDirectPlay4Impl *)iface;
4197
4198   if( dwFlags != 0 )
4199   {
4200     return DPERR_INVALIDFLAGS;
4201   }
4202
4203   return DP_IF_CancelMessage( This, 0, DPCANCELSEND_PRIORITY, dwMinPriority,
4204                               dwMaxPriority, FALSE );
4205 }
4206
4207 /* Note: Hack so we can reuse the old functions without compiler warnings */
4208 # define XCAST(fun)     (void*)
4209 static const IDirectPlay4Vtbl dp4_vt =
4210 {
4211     IDirectPlay4Impl_QueryInterface,
4212     IDirectPlay4Impl_AddRef,
4213     IDirectPlay4Impl_Release,
4214     IDirectPlay4Impl_AddPlayerToGroup,
4215     IDirectPlay4Impl_Close,
4216   XCAST(CreateGroup)DirectPlay2WImpl_CreateGroup,
4217   XCAST(CreatePlayer)DirectPlay2WImpl_CreatePlayer,
4218     IDirectPlay4Impl_DeletePlayerFromGroup,
4219   XCAST(DestroyGroup)DirectPlay2WImpl_DestroyGroup,
4220   XCAST(DestroyPlayer)DirectPlay2WImpl_DestroyPlayer,
4221     IDirectPlay4Impl_EnumGroupPlayers,
4222     IDirectPlay4Impl_EnumGroups,
4223     IDirectPlay4Impl_EnumPlayers,
4224     IDirectPlay4Impl_EnumSessions,
4225     IDirectPlay4Impl_GetCaps,
4226     IDirectPlay4Impl_GetGroupData,
4227   XCAST(GetGroupName)DirectPlay2WImpl_GetGroupName,
4228     IDirectPlay4Impl_GetMessageCount,
4229     IDirectPlay4Impl_GetPlayerAddress,
4230     IDirectPlay4Impl_GetPlayerCaps,
4231     IDirectPlay4Impl_GetPlayerData,
4232   XCAST(GetPlayerName)DirectPlay2WImpl_GetPlayerName,
4233   XCAST(GetSessionDesc)DirectPlay2WImpl_GetSessionDesc,
4234     IDirectPlay4Impl_Initialize,
4235     IDirectPlay4Impl_Open,
4236   XCAST(Receive)DirectPlay2WImpl_Receive,
4237     IDirectPlay4Impl_Send,
4238     IDirectPlay4Impl_SetGroupData,
4239   XCAST(SetGroupName)DirectPlay2WImpl_SetGroupName,
4240     IDirectPlay4Impl_SetPlayerData,
4241   XCAST(SetPlayerName)DirectPlay2WImpl_SetPlayerName,
4242   XCAST(SetSessionDesc)DirectPlay2WImpl_SetSessionDesc,
4243     IDirectPlay4Impl_AddGroupToGroup,
4244   XCAST(CreateGroupInGroup)DirectPlay3WImpl_CreateGroupInGroup,
4245     IDirectPlay4Impl_DeleteGroupFromGroup,
4246     IDirectPlay4Impl_EnumConnections,
4247     IDirectPlay4Impl_EnumGroupsInGroup,
4248     IDirectPlay4Impl_GetGroupConnectionSettings,
4249     IDirectPlay4Impl_InitializeConnection,
4250   XCAST(SecureOpen)DirectPlay3WImpl_SecureOpen,
4251     IDirectPlay4Impl_SendChatMessage,
4252     IDirectPlay4Impl_SetGroupConnectionSettings,
4253     IDirectPlay4Impl_StartSession,
4254     IDirectPlay4Impl_GetGroupFlags,
4255     IDirectPlay4Impl_GetGroupParent,
4256     IDirectPlay4Impl_GetPlayerAccount,
4257     IDirectPlay4Impl_GetPlayerFlags,
4258     IDirectPlay4Impl_GetGroupOwner,
4259     IDirectPlay4Impl_SetGroupOwner,
4260   DirectPlay4WImpl_SendEx,
4261   DirectPlay4WImpl_GetMessageQueue,
4262   DirectPlay4WImpl_CancelMessage,
4263   DirectPlay4WImpl_CancelPriority
4264 };
4265 #undef XCAST
4266
4267 static const IDirectPlay4Vtbl dp4A_vt =
4268 {
4269     IDirectPlay4AImpl_QueryInterface,
4270     IDirectPlay4AImpl_AddRef,
4271     IDirectPlay4AImpl_Release,
4272     IDirectPlay4AImpl_AddPlayerToGroup,
4273     IDirectPlay4AImpl_Close,
4274     IDirectPlay4AImpl_CreateGroup,
4275     IDirectPlay4AImpl_CreatePlayer,
4276     IDirectPlay4AImpl_DeletePlayerFromGroup,
4277     IDirectPlay4AImpl_DestroyGroup,
4278     IDirectPlay4AImpl_DestroyPlayer,
4279     IDirectPlay4AImpl_EnumGroupPlayers,
4280     IDirectPlay4AImpl_EnumGroups,
4281     IDirectPlay4AImpl_EnumPlayers,
4282     IDirectPlay4AImpl_EnumSessions,
4283     IDirectPlay4AImpl_GetCaps,
4284     IDirectPlay4AImpl_GetGroupData,
4285     IDirectPlay4AImpl_GetGroupName,
4286     IDirectPlay4AImpl_GetMessageCount,
4287     IDirectPlay4AImpl_GetPlayerAddress,
4288     IDirectPlay4AImpl_GetPlayerCaps,
4289     IDirectPlay4AImpl_GetPlayerData,
4290     IDirectPlay4AImpl_GetPlayerName,
4291     IDirectPlay4AImpl_GetSessionDesc,
4292     IDirectPlay4AImpl_Initialize,
4293     IDirectPlay4AImpl_Open,
4294     IDirectPlay4AImpl_Receive,
4295     IDirectPlay4AImpl_Send,
4296     IDirectPlay4AImpl_SetGroupData,
4297     IDirectPlay4AImpl_SetGroupName,
4298     IDirectPlay4AImpl_SetPlayerData,
4299     IDirectPlay4AImpl_SetPlayerName,
4300     IDirectPlay4AImpl_SetSessionDesc,
4301     IDirectPlay4AImpl_AddGroupToGroup,
4302     IDirectPlay4AImpl_CreateGroupInGroup,
4303     IDirectPlay4AImpl_DeleteGroupFromGroup,
4304     IDirectPlay4AImpl_EnumConnections,
4305     IDirectPlay4AImpl_EnumGroupsInGroup,
4306     IDirectPlay4AImpl_GetGroupConnectionSettings,
4307     IDirectPlay4AImpl_InitializeConnection,
4308     IDirectPlay4AImpl_SecureOpen,
4309     IDirectPlay4AImpl_SendChatMessage,
4310     IDirectPlay4AImpl_SetGroupConnectionSettings,
4311     IDirectPlay4AImpl_StartSession,
4312     IDirectPlay4AImpl_GetGroupFlags,
4313     IDirectPlay4AImpl_GetGroupParent,
4314     IDirectPlay4AImpl_GetPlayerAccount,
4315     IDirectPlay4AImpl_GetPlayerFlags,
4316
4317   DirectPlay4AImpl_GetGroupOwner,
4318   DirectPlay4AImpl_SetGroupOwner,
4319   DirectPlay4AImpl_SendEx,
4320   DirectPlay4AImpl_GetMessageQueue,
4321   DirectPlay4AImpl_CancelMessage,
4322   DirectPlay4AImpl_CancelPriority
4323 };
4324
4325 HRESULT dplay_create( REFIID riid, void **ppv )
4326 {
4327     IDirectPlayImpl *obj;
4328     HRESULT hr;
4329
4330     TRACE( "(%s, %p)\n", debugstr_guid( riid ), ppv );
4331
4332     *ppv = NULL;
4333     obj = HeapAlloc( GetProcessHeap(), 0, sizeof( *obj ) );
4334     if ( !obj )
4335         return DPERR_OUTOFMEMORY;
4336
4337     obj->IDirectPlay4A_iface.lpVtbl = &dp4A_vt;
4338     obj->IDirectPlay4_iface.lpVtbl = &dp4_vt;
4339     obj->numIfaces = 1;
4340     obj->ref4A = 1;
4341     obj->ref4 = 0;
4342
4343     InitializeCriticalSection( &obj->lock );
4344     obj->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": IDirectPlayImpl.lock");
4345
4346     if ( DP_CreateDirectPlay2( obj ) )
4347         hr = IDirectPlayX_QueryInterface( &obj->IDirectPlay4A_iface, riid, ppv );
4348     else
4349         hr = DPERR_NOMEMORY;
4350     IDirectPlayX_Release( &obj->IDirectPlay4A_iface );
4351
4352     return hr;
4353 }
4354
4355
4356 HRESULT DP_GetSPPlayerData( IDirectPlay2Impl* lpDP,
4357                             DPID idPlayer,
4358                             LPVOID* lplpData )
4359 {
4360   lpPlayerList lpPlayer = DP_FindPlayer( lpDP, idPlayer );
4361
4362   if( lpPlayer == NULL )
4363   {
4364     return DPERR_INVALIDPLAYER;
4365   }
4366
4367   *lplpData = lpPlayer->lpPData->lpSPPlayerData;
4368
4369   return DP_OK;
4370 }
4371
4372 HRESULT DP_SetSPPlayerData( IDirectPlay2Impl* lpDP,
4373                             DPID idPlayer,
4374                             LPVOID lpData )
4375 {
4376   lpPlayerList lpPlayer = DP_FindPlayer( lpDP, idPlayer );
4377
4378   if( lpPlayer == NULL )
4379   {
4380     return DPERR_INVALIDPLAYER;
4381   }
4382
4383   lpPlayer->lpPData->lpSPPlayerData = lpData;
4384
4385   return DP_OK;
4386 }
4387
4388 /***************************************************************************
4389  *  DirectPlayEnumerateAW
4390  *
4391  *  The pointer to the structure lpContext will be filled with the
4392  *  appropriate data for each service offered by the OS. These services are
4393  *  not necessarily available on this particular machine but are defined
4394  *  as simple service providers under the "Service Providers" registry key.
4395  *  This structure is then passed to lpEnumCallback for each of the different
4396  *  services.
4397  *
4398  *  This API is useful only for applications written using DirectX3 or
4399  *  worse. It is superseded by IDirectPlay3::EnumConnections which also
4400  *  gives information on the actual connections.
4401  *
4402  * defn of a service provider:
4403  * A dynamic-link library used by DirectPlay to communicate over a network.
4404  * The service provider contains all the network-specific code required
4405  * to send and receive messages. Online services and network operators can
4406  * supply service providers to use specialized hardware, protocols, communications
4407  * media, and network resources.
4408  *
4409  */
4410 static HRESULT DirectPlayEnumerateAW(LPDPENUMDPCALLBACKA lpEnumCallbackA,
4411                                      LPDPENUMDPCALLBACKW lpEnumCallbackW,
4412                                      LPVOID lpContext)
4413 {
4414     HKEY   hkResult;
4415     static const WCHAR searchSubKey[] = {
4416         'S', 'O', 'F', 'T', 'W', 'A', 'R', 'E', '\\',
4417         'M', 'i', 'c', 'r', 'o', 's', 'o', 'f', 't', '\\',
4418         'D', 'i', 'r', 'e', 'c', 't', 'P', 'l', 'a', 'y', '\\',
4419         'S', 'e', 'r', 'v', 'i', 'c', 'e', ' ', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r', 's', 0 };
4420     static const WCHAR guidKey[] = { 'G', 'u', 'i', 'd', 0 };
4421     static const WCHAR descW[] = { 'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', 'W', 0 };
4422     
4423     DWORD  dwIndex;
4424     FILETIME filetime;
4425
4426     char  *descriptionA = NULL;
4427     DWORD max_sizeOfDescriptionA = 0;
4428     WCHAR *descriptionW = NULL;
4429     DWORD max_sizeOfDescriptionW = 0;
4430     
4431     if (!lpEnumCallbackA && !lpEnumCallbackW)
4432     {
4433         return DPERR_INVALIDPARAMS;
4434     }
4435     
4436     /* Need to loop over the service providers in the registry */
4437     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, searchSubKey,
4438                       0, KEY_READ, &hkResult) != ERROR_SUCCESS)
4439     {
4440         /* Hmmm. Does this mean that there are no service providers? */
4441         ERR(": no service provider key in the registry - check your Wine installation !!!\n");
4442         return DPERR_GENERIC;
4443     }
4444     
4445     /* Traverse all the service providers we have available */
4446     dwIndex = 0;
4447     while (1)
4448     {
4449         WCHAR subKeyName[255]; /* 255 is the maximum key size according to MSDN */
4450         DWORD sizeOfSubKeyName = sizeof(subKeyName) / sizeof(WCHAR);
4451         HKEY  hkServiceProvider;
4452         GUID  serviceProviderGUID;
4453         WCHAR guidKeyContent[(2 * 16) + 1 + 6 /* This corresponds to '{....-..-..-..-......}' */ ];
4454         DWORD sizeOfGuidKeyContent = sizeof(guidKeyContent);
4455         LONG  ret_value;
4456         
4457         ret_value = RegEnumKeyExW(hkResult, dwIndex, subKeyName, &sizeOfSubKeyName,
4458                                   NULL, NULL, NULL, &filetime);
4459         if (ret_value == ERROR_NO_MORE_ITEMS)
4460             break;
4461         else if (ret_value != ERROR_SUCCESS)
4462         {
4463             ERR(": could not enumerate on service provider key.\n");
4464             return DPERR_EXCEPTION;
4465         }
4466         TRACE(" this time through sub-key %s.\n", debugstr_w(subKeyName));
4467         
4468         /* Open the key for this service provider */
4469         if (RegOpenKeyExW(hkResult, subKeyName, 0, KEY_READ, &hkServiceProvider) != ERROR_SUCCESS)
4470         {
4471             ERR(": could not open registry key for service provider %s.\n", debugstr_w(subKeyName));
4472             continue;
4473         }
4474         
4475         /* Get the GUID from the registry */
4476         if (RegQueryValueExW(hkServiceProvider, guidKey,
4477                              NULL, NULL, (LPBYTE) guidKeyContent, &sizeOfGuidKeyContent) != ERROR_SUCCESS)
4478         {
4479             ERR(": missing GUID registry data member for service provider %s.\n", debugstr_w(subKeyName));
4480             continue;
4481         }
4482         if (sizeOfGuidKeyContent != sizeof(guidKeyContent))
4483         {
4484             ERR(": invalid format for the GUID registry data member for service provider %s (%s).\n", debugstr_w(subKeyName), debugstr_w(guidKeyContent));
4485             continue;
4486         }
4487         CLSIDFromString(guidKeyContent, &serviceProviderGUID );
4488         
4489         /* The enumeration will return FALSE if we are not to continue.
4490          *
4491          * Note: on my windows box, major / minor version is 6 / 0 for all service providers
4492          *       and have no relation to any of the two dwReserved1 and dwReserved2 keys.
4493          *       I think that it simply means that they are in-line with DirectX 6.0
4494          */
4495         if (lpEnumCallbackA)
4496         {
4497             DWORD sizeOfDescription = 0;
4498             
4499             /* Note that this is the A case of this function, so use the A variant to get the description string */
4500             if (RegQueryValueExA(hkServiceProvider, "DescriptionA",
4501                                  NULL, NULL, NULL, &sizeOfDescription) != ERROR_SUCCESS)
4502             {
4503                 ERR(": missing 'DescriptionA' registry data member for service provider %s.\n", debugstr_w(subKeyName));
4504                 continue;
4505             }
4506             if (sizeOfDescription > max_sizeOfDescriptionA)
4507             {
4508                 HeapFree(GetProcessHeap(), 0, descriptionA);
4509                 max_sizeOfDescriptionA = sizeOfDescription;
4510             }
4511             descriptionA = HeapAlloc(GetProcessHeap(), 0, sizeOfDescription);
4512             RegQueryValueExA(hkServiceProvider, "DescriptionA",
4513                              NULL, NULL, (LPBYTE) descriptionA, &sizeOfDescription);
4514             
4515             if (!lpEnumCallbackA(&serviceProviderGUID, descriptionA, 6, 0, lpContext))
4516                 goto end;
4517         }
4518         else
4519         {
4520             DWORD sizeOfDescription = 0;
4521             
4522             if (RegQueryValueExW(hkServiceProvider, descW,
4523                                  NULL, NULL, NULL, &sizeOfDescription) != ERROR_SUCCESS)
4524             {
4525                 ERR(": missing 'DescriptionW' registry data member for service provider %s.\n", debugstr_w(subKeyName));
4526                 continue;
4527             }
4528             if (sizeOfDescription > max_sizeOfDescriptionW)
4529             {
4530                 HeapFree(GetProcessHeap(), 0, descriptionW);
4531                 max_sizeOfDescriptionW = sizeOfDescription;
4532             }
4533             descriptionW = HeapAlloc(GetProcessHeap(), 0, sizeOfDescription);
4534             RegQueryValueExW(hkServiceProvider, descW,
4535                              NULL, NULL, (LPBYTE) descriptionW, &sizeOfDescription);
4536
4537             if (!lpEnumCallbackW(&serviceProviderGUID, descriptionW, 6, 0, lpContext))
4538                 goto end;
4539         }
4540       
4541       dwIndex++;
4542   }
4543
4544  end:
4545     HeapFree(GetProcessHeap(), 0, descriptionA);
4546     HeapFree(GetProcessHeap(), 0, descriptionW);
4547     
4548     return DP_OK;
4549 }
4550
4551 /***************************************************************************
4552  *  DirectPlayEnumerate  [DPLAYX.9]
4553  *  DirectPlayEnumerateA [DPLAYX.2]
4554  */
4555 HRESULT WINAPI DirectPlayEnumerateA(LPDPENUMDPCALLBACKA lpEnumCallback, LPVOID lpContext )
4556 {
4557     TRACE("(%p,%p)\n", lpEnumCallback, lpContext);
4558     
4559     return DirectPlayEnumerateAW(lpEnumCallback, NULL, lpContext);
4560 }
4561
4562 /***************************************************************************
4563  *  DirectPlayEnumerateW [DPLAYX.3]
4564  */
4565 HRESULT WINAPI DirectPlayEnumerateW(LPDPENUMDPCALLBACKW lpEnumCallback, LPVOID lpContext )
4566 {
4567     TRACE("(%p,%p)\n", lpEnumCallback, lpContext);
4568     
4569     return DirectPlayEnumerateAW(NULL, lpEnumCallback, lpContext);
4570 }
4571
4572 typedef struct tagCreateEnum
4573 {
4574   LPVOID  lpConn;
4575   LPCGUID lpGuid;
4576 } CreateEnumData, *lpCreateEnumData;
4577
4578 /* Find and copy the matching connection for the SP guid */
4579 static BOOL CALLBACK cbDPCreateEnumConnections(
4580     LPCGUID     lpguidSP,
4581     LPVOID      lpConnection,
4582     DWORD       dwConnectionSize,
4583     LPCDPNAME   lpName,
4584     DWORD       dwFlags,
4585     LPVOID      lpContext)
4586 {
4587   lpCreateEnumData lpData = (lpCreateEnumData)lpContext;
4588
4589   if( IsEqualGUID( lpguidSP, lpData->lpGuid ) )
4590   {
4591     TRACE( "Found SP entry with guid %s\n", debugstr_guid(lpData->lpGuid) );
4592
4593     lpData->lpConn = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
4594                                 dwConnectionSize );
4595     CopyMemory( lpData->lpConn, lpConnection, dwConnectionSize );
4596
4597     /* Found the record that we were looking for */
4598     return FALSE;
4599   }
4600
4601   /* Haven't found what were looking for yet */
4602   return TRUE;
4603 }
4604
4605
4606 /***************************************************************************
4607  *  DirectPlayCreate [DPLAYX.1]
4608  *
4609  */
4610 HRESULT WINAPI DirectPlayCreate
4611 ( LPGUID lpGUID, LPDIRECTPLAY *lplpDP, IUnknown *pUnk )
4612 {
4613   HRESULT hr;
4614   LPDIRECTPLAY3A lpDP3A;
4615   CreateEnumData cbData;
4616
4617   TRACE( "lpGUID=%s lplpDP=%p pUnk=%p\n", debugstr_guid(lpGUID), lplpDP, pUnk );
4618
4619   if( pUnk != NULL )
4620   {
4621     return CLASS_E_NOAGGREGATION;
4622   }
4623
4624   if( (lplpDP == NULL) || (lpGUID == NULL) )
4625   {
4626     return DPERR_INVALIDPARAMS;
4627   }
4628
4629   /* Create an IDirectPlay object. We don't support that so we'll cheat and
4630      give them an IDirectPlay2A object and hope that doesn't cause problems */
4631   if ( dplay_create( &IID_IDirectPlay2A, (void**)lplpDP ) != DP_OK )
4632     return DPERR_UNAVAILABLE;
4633
4634   if( IsEqualGUID( &GUID_NULL, lpGUID ) )
4635   {
4636     /* The GUID_NULL means don't bind a service provider. Just return the
4637        interface as is */
4638     return DP_OK;
4639   }
4640
4641   /* Bind the desired service provider since lpGUID is non NULL */
4642   TRACE( "Service Provider binding for %s\n", debugstr_guid(lpGUID) );
4643
4644   /* We're going to use a DP3 interface */
4645   hr = IDirectPlayX_QueryInterface( *lplpDP, &IID_IDirectPlay3A,
4646                                     (LPVOID*)&lpDP3A );
4647   if( FAILED(hr) )
4648   {
4649     ERR( "Failed to get DP3 interface: %s\n", DPLAYX_HresultToString(hr) );
4650     return hr;
4651   }
4652
4653   cbData.lpConn = NULL;
4654   cbData.lpGuid = lpGUID;
4655
4656   /* We were given a service provider, find info about it... */
4657   hr = IDirectPlayX_EnumConnections( lpDP3A, NULL, cbDPCreateEnumConnections,
4658                                      &cbData, DPCONNECTION_DIRECTPLAY );
4659   if( ( FAILED(hr) ) ||
4660       ( cbData.lpConn == NULL )
4661     )
4662   {
4663     ERR( "Failed to get Enum for SP: %s\n", DPLAYX_HresultToString(hr) );
4664     IDirectPlayX_Release( lpDP3A );
4665     return DPERR_UNAVAILABLE;
4666   }
4667
4668   /* Initialize the service provider */
4669   hr = IDirectPlayX_InitializeConnection( lpDP3A, cbData.lpConn, 0 );
4670   if( FAILED(hr) )
4671   {
4672     ERR( "Failed to Initialize SP: %s\n", DPLAYX_HresultToString(hr) );
4673     HeapFree( GetProcessHeap(), 0, cbData.lpConn );
4674     IDirectPlayX_Release( lpDP3A );
4675     return hr;
4676   }
4677
4678   /* Release our version of the interface now that we're done with it */
4679   IDirectPlayX_Release( lpDP3A );
4680   HeapFree( GetProcessHeap(), 0, cbData.lpConn );
4681
4682   return DP_OK;
4683 }