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