Index: trunk/src/DPlayX/dpclassfactory.cpp
===================================================================
--- trunk/src/DPlayX/dpclassfactory.cpp	(revision 4314)
+++ trunk/src/DPlayX/dpclassfactory.cpp	(revision 4314)
@@ -0,0 +1,116 @@
+#include <string.h>
+
+#include <odin.h>
+#define ICOM_CINTERFACE 1
+#define CINTERFACE
+
+#include "wine/obj_base.h"
+#include "winerror.h"
+#include "debugtools.h"
+#include "dpinit.h"
+
+DEFAULT_DEBUG_CHANNEL(dplay)
+
+#define debugstr_guid(a) a
+
+/*******************************************************************************
+ * DirectPlayLobby ClassFactory
+ */
+
+typedef struct
+{
+    /* IUnknown fields */
+    ICOM_VFIELD(IClassFactory);
+    DWORD                       ref;
+} IClassFactoryImpl;
+
+static HRESULT WINAPI
+DP_and_DPL_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
+        ICOM_THIS(IClassFactoryImpl,iface);
+
+        FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
+
+        return E_NOINTERFACE;
+}
+
+static ULONG WINAPI
+DP_and_DPL_AddRef(LPCLASSFACTORY iface) {
+        ICOM_THIS(IClassFactoryImpl,iface);
+        return ++(This->ref);
+}
+
+static ULONG WINAPI DP_and_DPL_Release(LPCLASSFACTORY iface) {
+        ICOM_THIS(IClassFactoryImpl,iface);
+        /* static class (reference starts @ 1), won't ever be freed */
+        return --(This->ref);
+}
+
+static HRESULT WINAPI DP_and_DPL_CreateInstance(
+        LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
+) {
+        ICOM_THIS(IClassFactoryImpl,iface);
+
+        TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
+
+        if ( DPL_CreateInterface( riid, ppobj ) == S_OK )
+        {
+           return S_OK;
+        }
+        else if ( DP_CreateInterface( riid, ppobj ) == S_OK )
+        {
+           return S_OK;
+        }
+
+        return E_NOINTERFACE;
+}
+
+static HRESULT WINAPI DP_and_DPL_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
+        ICOM_THIS(IClassFactoryImpl,iface);
+        FIXME("(%p)->(%d),stub!\n",This,dolock);
+        return S_OK;
+}
+
+static ICOM_VTABLE(IClassFactory) DP_and_DPL_Vtbl = {
+        ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+        DP_and_DPL_QueryInterface,
+        DP_and_DPL_AddRef,
+        DP_and_DPL_Release,
+        DP_and_DPL_CreateInstance,
+        DP_and_DPL_LockServer
+};
+
+static IClassFactoryImpl DP_and_DPL_CF = {&DP_and_DPL_Vtbl, 1 };
+
+
+/*******************************************************************************
+ * DPLAYX_DllGetClassObject [DPLAYX.?]
+ * Retrieves DP or DPL class object from a DLL object
+ *
+ * NOTES
+ *    Docs say returns STDAPI
+ *
+ * PARAMS
+ *    rclsid [I] CLSID for the class object
+ *    riid   [I] Reference to identifier of interface for class object
+ *    ppv    [O] Address of variable to receive interface pointer for riid
+ *
+ * RETURNS
+ *    Success: S_OK
+ *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
+ *             E_UNEXPECTED
+ */
+DWORD WINAPI DPLAYX_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
+{
+    TRACE("(%p,%p,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
+
+    if ( IsEqualCLSID( riid, &IID_IClassFactory ) )
+    {
+        *ppv = (LPVOID)&DP_and_DPL_CF;
+        IClassFactory_AddRef( (IClassFactory*)*ppv );
+
+        return S_OK;
+    }
+
+    ERR("(%p,%p,%p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
+    return CLASS_E_CLASSNOTAVAILABLE;
+}
Index: trunk/src/DPlayX/dpinit.h
===================================================================
--- trunk/src/DPlayX/dpinit.h	(revision 4314)
+++ trunk/src/DPlayX/dpinit.h	(revision 4314)
@@ -0,0 +1,13 @@
+
+#ifndef __WINE_DPINIT_H
+#define __WINE_DPINIT_H
+
+#include "wtypes.h"
+#include "dplay_global.h"
+
+extern HRESULT DP_CreateInterface( REFIID riid, LPVOID* ppvObj );
+extern HRESULT DPL_CreateInterface( REFIID riid, LPVOID* ppvObj );
+extern HRESULT DPSP_CreateInterface( REFIID riid, LPVOID* ppvObj, 
+                                     IDirectPlay2Impl* dp );
+
+#endif
Index: trunk/src/DPlayX/dplay.cpp
===================================================================
--- trunk/src/DPlayX/dplay.cpp	(revision 4314)
+++ trunk/src/DPlayX/dplay.cpp	(revision 4314)
@@ -0,0 +1,5015 @@
+/* Direct Play 2,3,4 Implementation
+ *
+ * Copyright 1998,1999,2000 - Peter Hunnisett
+ *
+ * <presently under construction - contact hunnise@nortelnetworks.com>
+ *
+ */
+#include <string.h>
+
+#include <odin.h>
+#define ICOM_CINTERFACE 1
+#define CINTERFACE
+
+#include "windef.h"
+#include "winerror.h"
+#include "winbase.h"
+#include "winnt.h"
+#include "winreg.h"
+#include "dplay.h"
+#include "heap.h"
+#include "heapstring.h"
+#include "debugtools.h"
+
+#include "dpinit.h"
+#include "dplayx_global.h"
+#include "name_server.h"
+#include "dplayx_queue.h"
+#include "dplaysp.h"
+#include "dplay_global.h"
+
+DEFAULT_DEBUG_CHANNEL(dplay)
+
+#define debugstr_guid(a) a
+
+
+/* FIXME: Should this be externed? */
+extern HRESULT DPL_CreateCompoundAddress
+( LPCDPCOMPOUNDADDRESSELEMENT lpElements, DWORD dwElementCount,
+  LPVOID lpAddress, LPDWORD lpdwAddressSize, BOOL bAnsiInterface );
+
+
+/* Local function prototypes */
+static lpPlayerList DP_FindPlayer( IDirectPlay2AImpl* This, DPID dpid );
+static lpPlayerData DP_CreatePlayer( IDirectPlay2Impl* iface, LPDPID lpid,
+                                     LPDPNAME lpName, DWORD dwFlags,
+                                     HANDLE hEvent, BOOL bAnsi );
+static BOOL DP_CopyDPNAMEStruct( LPDPNAME lpDst, LPDPNAME lpSrc, BOOL bAnsi );
+static void DP_SetPlayerData( lpPlayerData lpPData, DWORD dwFlags,
+                              LPVOID lpData, DWORD dwDataSize );
+
+static lpGroupData DP_CreateGroup( IDirectPlay2AImpl* iface, LPDPID lpid,
+                                   LPDPNAME lpName, DWORD dwFlags,
+                                   DPID idParent, BOOL bAnsi );
+static void DP_SetGroupData( lpGroupData lpGData, DWORD dwFlags,
+                             LPVOID lpData, DWORD dwDataSize );
+static void DP_DeleteDPNameStruct( LPDPNAME lpDPName );
+static void DP_DeletePlayer( IDirectPlay2Impl* This, DPID dpid );
+static BOOL CALLBACK cbDeletePlayerFromAllGroups( DPID dpId,
+                                                  DWORD dwPlayerType,
+                                                  LPCDPNAME lpName,
+                                                  DWORD dwFlags,
+                                                  LPVOID lpContext );
+static lpGroupData DP_FindAnyGroup( IDirectPlay2AImpl* This, DPID dpid );
+static BOOL CALLBACK cbRemoveGroupOrPlayer( DPID dpId, DWORD dwPlayerType,
+                                            LPCDPNAME lpName, DWORD dwFlags,
+                                            LPVOID lpContext );
+static void DP_DeleteGroup( IDirectPlay2Impl* This, DPID dpid );
+
+/* Helper methods for player/group interfaces */
+static HRESULT WINAPI DP_IF_DeletePlayerFromGroup
+          ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idGroup,
+            DPID idPlayer, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_CreatePlayer
+          ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, LPDPID lpidPlayer,
+            LPDPNAME lpPlayerName, HANDLE hEvent, LPVOID lpData,
+            DWORD dwDataSize, DWORD dwFlags, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_DestroyGroup
+          ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idGroup, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_DestroyPlayer
+          ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idPlayer, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_EnumGroupPlayers
+          ( IDirectPlay2Impl* This, DPID idGroup, LPGUID lpguidInstance,
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2,
+            LPVOID lpContext, DWORD dwFlags, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_EnumGroups
+          ( IDirectPlay2Impl* This, LPGUID lpguidInstance,
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2,
+            LPVOID lpContext, DWORD dwFlags, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_EnumPlayers
+          ( IDirectPlay2Impl* This, LPGUID lpguidInstance,
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2,
+            LPVOID lpContext, DWORD dwFlags, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_GetGroupData
+          ( IDirectPlay2Impl* This, DPID idGroup, LPVOID lpData,
+            LPDWORD lpdwDataSize, DWORD dwFlags, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_GetGroupName
+          ( IDirectPlay2Impl* This, DPID idGroup, LPVOID lpData,
+            LPDWORD lpdwDataSize, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_GetPlayerData
+          ( IDirectPlay2Impl* This, DPID idPlayer, LPVOID lpData,
+            LPDWORD lpdwDataSize, DWORD dwFlags, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_GetPlayerName
+          ( IDirectPlay2Impl* This, DPID idPlayer, LPVOID lpData,
+            LPDWORD lpdwDataSize, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_SetGroupName
+          ( IDirectPlay2Impl* This, DPID idGroup, LPDPNAME lpGroupName,
+            DWORD dwFlags, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_SetPlayerData
+          ( IDirectPlay2Impl* This, DPID idPlayer, LPVOID lpData,
+            DWORD dwDataSize, DWORD dwFlags, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_SetPlayerName
+          ( IDirectPlay2Impl* This, DPID idPlayer, LPDPNAME lpPlayerName,
+            DWORD dwFlags, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_AddGroupToGroup
+          ( IDirectPlay3Impl* This, DPID idParentGroup, DPID idGroup );
+static HRESULT WINAPI DP_IF_CreateGroup
+          ( IDirectPlay2AImpl* This, LPVOID lpMsgHdr, LPDPID lpidGroup,
+            LPDPNAME lpGroupName, LPVOID lpData, DWORD dwDataSize,
+            DWORD dwFlags, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_CreateGroupInGroup
+          ( IDirectPlay3Impl* This, LPVOID lpMsgHdr, DPID idParentGroup,
+            LPDPID lpidGroup, LPDPNAME lpGroupName, LPVOID lpData,
+            DWORD dwDataSize, DWORD dwFlags, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_AddPlayerToGroup
+          ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idGroup,
+            DPID idPlayer, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_DeleteGroupFromGroup
+          ( IDirectPlay3Impl* This, DPID idParentGroup, DPID idGroup );
+static HRESULT WINAPI DP_SetSessionDesc
+          ( IDirectPlay2Impl* This, LPCDPSESSIONDESC2 lpSessDesc,
+            DWORD dwFlags, BOOL bInitial, BOOL bAnsi  );
+static HRESULT WINAPI DP_SecureOpen
+          ( IDirectPlay2Impl* This, LPCDPSESSIONDESC2 lpsd, DWORD dwFlags,
+            LPCDPSECURITYDESC lpSecurity, LPCDPCREDENTIALS lpCredentials,
+            BOOL bAnsi );
+static HRESULT WINAPI DP_SendEx
+          ( IDirectPlay2Impl* This, DPID idFrom, DPID idTo, DWORD dwFlags,
+            LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout,
+            LPVOID lpContext, LPDWORD lpdwMsgID, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_Receive
+          ( IDirectPlay2Impl* This, LPDPID lpidFrom, LPDPID lpidTo,
+            DWORD dwFlags, LPVOID lpData, LPDWORD lpdwDataSize, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_GetMessageQueue
+          ( IDirectPlay4Impl* This, DPID idFrom, DPID idTo, DWORD dwFlags,
+            LPDWORD lpdwNumMsgs, LPDWORD lpdwNumBytes, BOOL bAnsi );
+static HRESULT WINAPI DP_SP_SendEx
+          ( IDirectPlay2Impl* This, DWORD dwFlags,
+            LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout,
+            LPVOID lpContext, LPDWORD lpdwMsgID );
+static HRESULT WINAPI DP_IF_SetGroupData
+          ( IDirectPlay2Impl* This, DPID idGroup, LPVOID lpData,
+            DWORD dwDataSize, DWORD dwFlags, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_GetPlayerCaps
+          ( IDirectPlay2Impl* This, DPID idPlayer, LPDPCAPS lpDPCaps,
+            DWORD dwFlags );
+static HRESULT WINAPI DP_IF_Close( IDirectPlay2Impl* This, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_CancelMessage
+          ( IDirectPlay4Impl* This, DWORD dwMsgID, DWORD dwFlags,
+            DWORD dwMinPriority, DWORD dwMaxPriority, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_EnumGroupsInGroup
+          ( IDirectPlay3AImpl* This, DPID idGroup, LPGUID lpguidInstance,
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2,
+            LPVOID lpContext, DWORD dwFlags, BOOL bAnsi );
+static HRESULT WINAPI DP_IF_GetGroupParent
+          ( IDirectPlay3AImpl* This, DPID idGroup, LPDPID lpidGroup,
+            BOOL bAnsi );
+static HRESULT WINAPI DP_IF_GetCaps
+          ( IDirectPlay2Impl* This, LPDPCAPS lpDPCaps, DWORD dwFlags );
+static HRESULT WINAPI DP_IF_EnumSessions
+          ( IDirectPlay2Impl* This, LPDPSESSIONDESC2 lpsd, DWORD dwTimeout,
+            LPDPENUMSESSIONSCALLBACK2 lpEnumSessionsCallback2,
+            LPVOID lpContext, DWORD dwFlags, BOOL bAnsi );
+static BOOL CALLBACK cbDPCreateEnumConnections( LPCGUID lpguidSP,
+    LPVOID lpConnection, DWORD dwConnectionSize, LPCDPNAME lpName,
+    DWORD dwFlags, LPVOID lpContext );
+
+
+
+static inline DPID DP_NextObjectId(void);
+static DPID DP_GetRemoteNextObjectId(void);
+
+
+static void DP_CopySessionDesc( LPDPSESSIONDESC2 destSessionDesc,
+                                LPCDPSESSIONDESC2 srcSessDesc, BOOL bAnsi );
+
+
+static HMODULE DP_LoadSP( LPCGUID lpcGuid, LPSPINITDATA lpSpData );
+
+
+
+
+
+#define DPID_NOPARENT_GROUP 0 /* Magic number to indicate no parent of group */
+#define DPID_SYSTEM_GROUP DPID_NOPARENT_GROUP /* If system group is supported
+                                                 we don't have to change much */
+#define DPID_NAME_SERVER 0x19a9d65b  /* Don't ask me why */
+
+/* Strip out dwFlag values which cannot be sent in the CREATEGROUP msg */
+#define DPMSG_CREATEGROUP_DWFLAGS(x) ( (x) & DPGROUP_HIDDEN )
+
+/* Strip out all dwFlags values for CREATEPLAYER msg */
+#define DPMSG_CREATEPLAYER_DWFLAGS(x) 0
+
+static DWORD kludgePlayerGroupId = 1000;
+
+/* ------------------------------------------------------------------ */
+
+
+static BOOL DP_CreateIUnknown( LPVOID lpDP )
+{
+  ICOM_THIS(IDirectPlay2AImpl,lpDP);
+
+  This->unk = (DirectPlayIUnknownData*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                                  sizeof( *(This->unk) ) );
+  if ( This->unk == NULL )
+  {
+    return FALSE;
+  }
+
+  InitializeCriticalSection( &This->unk->DP_lock );
+
+  return TRUE;
+}
+
+static BOOL DP_DestroyIUnknown( LPVOID lpDP )
+{
+  ICOM_THIS(IDirectPlay2AImpl,lpDP);
+
+  DeleteCriticalSection( &This->unk->DP_lock );
+  HeapFree( GetProcessHeap(), 0, This->unk );
+
+  return TRUE;
+}
+
+static BOOL DP_CreateDirectPlay2( LPVOID lpDP )
+{
+  ICOM_THIS(IDirectPlay2AImpl,lpDP);
+
+  This->dp2 = (DirectPlay2Data*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                           sizeof( *(This->dp2) ) );
+  if ( This->dp2 == NULL )
+  {
+    return FALSE;
+  }
+
+  This->dp2->bConnectionOpen = FALSE;
+
+  This->dp2->hEnumSessionThread = INVALID_HANDLE_VALUE;
+
+  This->dp2->bHostInterface = FALSE;
+
+  DPQ_INIT(This->dp2->receiveMsgs);
+  DPQ_INIT(This->dp2->sendMsgs);
+
+  if( !NS_InitializeSessionCache( &This->dp2->lpNameServerData ) )
+  {
+    /* FIXME: Memory leak */
+    return FALSE;
+  }
+
+  /* Provide an initial session desc with nothing in it */
+  This->dp2->lpSessionDesc = (LPDPSESSIONDESC2)HeapAlloc( GetProcessHeap(),
+                               HEAP_ZERO_MEMORY,
+                               sizeof( *This->dp2->lpSessionDesc ) );
+  if( This->dp2->lpSessionDesc == NULL )
+  {
+    /* FIXME: Memory leak */
+    return FALSE;
+  }
+  This->dp2->lpSessionDesc->dwSize = sizeof( *This->dp2->lpSessionDesc );
+
+  /* We are a emulating a dp 6 implementation */
+  This->dp2->spData.dwSPVersion = DPSP_MAJORVERSION;
+
+  This->dp2->spData.lpCB = (LPDPSP_SPCALLBACKS)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                      sizeof( *This->dp2->spData.lpCB ) );
+  This->dp2->spData.lpCB->dwSize = sizeof( *This->dp2->spData.lpCB );
+  This->dp2->spData.lpCB->dwVersion = DPSP_MAJORVERSION;
+
+  /* This is the pointer to the service provider */
+  if( FAILED( DPSP_CreateInterface( &IID_IDirectPlaySP,
+                                    (LPVOID*)&This->dp2->spData.lpISP, This ) )
+    )
+  {
+    /* FIXME: Memory leak */
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/* Definition of the global function in dplayx_queue.h. #
+ * FIXME: Would it be better to have a dplayx_queue.c for this function? */
+DPQ_DECL_DELETECB( cbDeleteElemFromHeap, LPVOID )
+{
+  HeapFree( GetProcessHeap(), 0, elem );
+}
+
+/* Function to delete the list of groups with this interface. Needs to
+ * delete the group and player lists associated with this group as well
+ * as the group data associated with this group. It should not delete
+ * player data as that is shared with the top player list and will be
+ * deleted with that.
+ */
+DPQ_DECL_DELETECB( cbDeleteGroupsElem, lpGroupList );
+DPQ_DECL_DELETECB( cbDeleteGroupsElem, lpGroupList )
+{
+  DPQ_DELETEQ( elem->lpGData->groups, groups,
+               lpGroupList, cbDeleteElemFromHeap );
+  DPQ_DELETEQ( elem->lpGData->players, players,
+               lpPlayerList, cbDeleteElemFromHeap );
+  HeapFree( GetProcessHeap(), 0, elem->lpGData );
+  HeapFree( GetProcessHeap(), 0, elem );
+}
+
+/* Function to delete the list of players with this interface. Needs to
+ * delete the player data for all players as well.
+ */
+DPQ_DECL_DELETECB( cbDeletePlayerElem, lpPlayerList );
+DPQ_DECL_DELETECB( cbDeletePlayerElem, lpPlayerList )
+{
+  HeapFree( GetProcessHeap(), 0, elem->lpPData );
+  HeapFree( GetProcessHeap(), 0, elem );
+}
+
+static BOOL DP_DestroyDirectPlay2( LPVOID lpDP )
+{
+  ICOM_THIS(IDirectPlay2AImpl,lpDP);
+
+  if( This->dp2->hEnumSessionThread != INVALID_HANDLE_VALUE )
+  {
+    TerminateThread( This->dp2->hEnumSessionThread, 0 );
+    CloseHandle( This->dp2->hEnumSessionThread );
+  }
+
+  /* Finish with the SP - have it shutdown */
+  if( This->dp2->spData.lpCB->ShutdownEx )
+  {
+    DPSP_SHUTDOWNDATA data;
+
+    TRACE( "Calling SP ShutdownEx\n" );
+
+    data.lpISP = This->dp2->spData.lpISP;
+
+    (*This->dp2->spData.lpCB->ShutdownEx)( &data );
+  }
+  else if (This->dp2->spData.lpCB->Shutdown ) /* obsolete interface */
+  {
+    TRACE( "Calling obsolete SP Shutdown\n" );
+    (*This->dp2->spData.lpCB->Shutdown)();
+  }
+
+  /* Unload the SP */
+  if( This->dp2->hServiceProvider != 0 )
+  {
+    FreeLibrary( This->dp2->hServiceProvider );
+  }
+
+
+#if 0
+  DPQ_DELETEQ( This->dp2->players, players, lpPlayerList, cbDeletePlayerElem );
+  DPQ_DELETEQ( This->dp2->groups, groups, lpGroupList, cbDeleteGroupsElem );
+#endif
+
+  /* FIXME: Need to delete receive and send msgs queue contents */
+
+  NS_DeleteSessionCache( This->dp2->lpNameServerData );
+
+  HeapFree( GetProcessHeap(), 0, This->dp2->lpSessionDesc );
+
+  IDirectPlaySP_Release( This->dp2->spData.lpISP );
+
+  /* Delete the contents */
+  HeapFree( GetProcessHeap(), 0, This->dp2 );
+
+  return TRUE;
+}
+
+static BOOL DP_CreateDirectPlay3( LPVOID lpDP )
+{
+  ICOM_THIS(IDirectPlay3AImpl,lpDP);
+
+  This->dp3 = (DirectPlay3Data*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                           sizeof( *(This->dp3) ) );
+  if ( This->dp3 == NULL )
+  {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+static BOOL DP_DestroyDirectPlay3( LPVOID lpDP )
+{
+  ICOM_THIS(IDirectPlay3AImpl,lpDP);
+
+  /* Delete the contents */
+  HeapFree( GetProcessHeap(), 0, This->dp3 );
+
+  return TRUE;
+}
+
+static BOOL DP_CreateDirectPlay4( LPVOID lpDP )
+{
+  ICOM_THIS(IDirectPlay4AImpl,lpDP);
+
+  This->dp4 = (DirectPlay4Data*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                           sizeof( *(This->dp4) ) );
+  if ( This->dp4 == NULL )
+  {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+static BOOL DP_DestroyDirectPlay4( LPVOID lpDP )
+{
+  ICOM_THIS(IDirectPlay3AImpl,lpDP);
+
+  /* Delete the contents */
+  HeapFree( GetProcessHeap(), 0, This->dp4 );
+
+  return TRUE;
+}
+
+
+/* Create a new interface */
+extern
+HRESULT DP_CreateInterface
+         ( REFIID riid, LPVOID* ppvObj )
+{
+  TRACE( " for %s\n", debugstr_guid( riid ) );
+
+  *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                       sizeof( IDirectPlay2Impl ) );
+
+  if( *ppvObj == NULL )
+  {
+    return DPERR_OUTOFMEMORY;
+  }
+
+  if( IsEqualGUID( &IID_IDirectPlay2, riid ) )
+  {
+    ICOM_THIS(IDirectPlay2Impl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay2WVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlay2A, riid ) )
+  {
+    ICOM_THIS(IDirectPlay2AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay2AVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlay3, riid ) )
+  {
+    ICOM_THIS(IDirectPlay3Impl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay3WVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlay3A, riid ) )
+  {
+    ICOM_THIS(IDirectPlay3AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay3AVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlay4, riid ) )
+  {
+    ICOM_THIS(IDirectPlay4Impl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay4WVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlay4A, riid ) )
+  {
+    ICOM_THIS(IDirectPlay4AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay4AVT;
+  }
+  else
+  {
+    /* Unsupported interface */
+    HeapFree( GetProcessHeap(), 0, *ppvObj );
+    *ppvObj = NULL;
+
+    return E_NOINTERFACE;
+  }
+
+  /* Initialize it */
+  if ( DP_CreateIUnknown( *ppvObj ) &&
+       DP_CreateDirectPlay2( *ppvObj ) &&
+       DP_CreateDirectPlay3( *ppvObj ) &&
+       DP_CreateDirectPlay4( *ppvObj )
+     )
+  {
+    IDirectPlayX_AddRef( (LPDIRECTPLAY2A)*ppvObj );
+
+    return S_OK;
+  }
+
+  /* Initialize failed, destroy it */
+  DP_DestroyDirectPlay4( *ppvObj );
+  DP_DestroyDirectPlay3( *ppvObj );
+  DP_DestroyDirectPlay2( *ppvObj );
+  DP_DestroyIUnknown( *ppvObj );
+
+  HeapFree( GetProcessHeap(), 0, *ppvObj );
+
+  *ppvObj = NULL;
+  return DPERR_NOMEMORY;
+}
+
+
+/* Direct Play methods */
+
+/* Shared between all dplay types */
+static HRESULT WINAPI DP_QueryInterface
+         ( LPDIRECTPLAY2 iface, REFIID riid, LPVOID* ppvObj )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  TRACE("(%p)->(%s,%p)\n", This, debugstr_guid( riid ), ppvObj );
+
+  *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                       sizeof( IDirectPlay2Impl ) );
+
+  if( *ppvObj == NULL )
+  {
+    return DPERR_OUTOFMEMORY;
+  }
+
+  CopyMemory( *ppvObj, iface, sizeof( IDirectPlay2Impl )  );
+  (*(IDirectPlay2Impl**)ppvObj)->ulInterfaceRef = 0;
+
+  if( IsEqualGUID( &IID_IDirectPlay2, riid ) )
+  {
+    ICOM_THIS(IDirectPlay2Impl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay2WVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlay2A, riid ) )
+  {
+    ICOM_THIS(IDirectPlay2AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay2AVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlay3, riid ) )
+  {
+    ICOM_THIS(IDirectPlay3Impl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay3WVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlay3A, riid ) )
+  {
+    ICOM_THIS(IDirectPlay3AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay3AVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlay4, riid ) )
+  {
+    ICOM_THIS(IDirectPlay4Impl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay4WVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlay4A, riid ) )
+  {
+    ICOM_THIS(IDirectPlay4AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlay4AVT;
+  }
+  else
+  {
+    /* Unsupported interface */
+    HeapFree( GetProcessHeap(), 0, *ppvObj );
+    *ppvObj = NULL;
+
+    return E_NOINTERFACE;
+  }
+
+  IDirectPlayX_AddRef( (LPDIRECTPLAY2)*ppvObj );
+
+  return S_OK;
+}
+
+/* Shared between all dplay types */
+static ULONG WINAPI DP_AddRef
+         ( LPDIRECTPLAY3 iface )
+{
+  ULONG ulInterfaceRefCount, ulObjRefCount;
+  ICOM_THIS(IDirectPlay3Impl,iface);
+
+  ulObjRefCount       = InterlockedIncrement( (LPLONG)&This->unk->ulObjRef );
+  ulInterfaceRefCount = InterlockedIncrement( (LPLONG)&This->ulInterfaceRef );
+
+  TRACE( "ref count incremented to %lu:%lu for %p\n",
+         ulInterfaceRefCount, ulObjRefCount, This );
+
+  return ulObjRefCount;
+}
+
+static ULONG WINAPI DP_Release
+( LPDIRECTPLAY3 iface )
+{
+  ULONG ulInterfaceRefCount, ulObjRefCount;
+
+  ICOM_THIS(IDirectPlay3Impl,iface);
+
+  ulObjRefCount       = InterlockedDecrement( (LPLONG)&This->unk->ulObjRef );
+  ulInterfaceRefCount = InterlockedDecrement( (LPLONG)&This->ulInterfaceRef );
+
+  TRACE( "ref count decremented to %lu:%lu for %p\n",
+         ulInterfaceRefCount, ulObjRefCount, This );
+
+  /* Deallocate if this is the last reference to the object */
+  if( ulObjRefCount == 0 )
+  {
+     /* If we're destroying the object, this must be the last ref
+        of the last interface */
+     DP_DestroyDirectPlay4( This );
+     DP_DestroyDirectPlay3( This );
+     DP_DestroyDirectPlay2( This );
+     DP_DestroyIUnknown( This );
+  }
+
+  /* Deallocate the interface */
+  if( ulInterfaceRefCount == 0 )
+  {
+    HeapFree( GetProcessHeap(), 0, This );
+  }
+
+  return ulObjRefCount;
+}
+
+static inline DPID DP_NextObjectId(void)
+{
+  return (DPID)InterlockedIncrement( (LPLONG)&kludgePlayerGroupId );
+}
+
+/* *lplpReply will be non NULL iff there is something to reply */
+HRESULT DP_HandleMessage( IDirectPlay2Impl* This, LPCVOID lpMessageBody,
+                          DWORD  dwMessageBodySize, LPCVOID lpMessageHeader,
+                          WORD wCommandId, WORD wVersion,
+                          LPVOID* lplpReply, LPDWORD lpdwMsgSize )
+{
+  TRACE( "(%p)->(%p,0x%08lx,%p,%u,%u)\n",
+         This, lpMessageBody, dwMessageBodySize, lpMessageHeader, wCommandId,
+         wVersion );
+
+  switch( wCommandId )
+  {
+    case DPMSGCMD_REQUESTNEWPLAYERID:
+    {
+#if 0
+      LPCDPMSG_REQUESTNEWPLAYERID lpcMsg =
+        (LPCDPMSG_REQUESTNEWPLAYERID)lpMessageBody;
+#endif
+      LPDPMSG_NEWPLAYERIDREPLY lpReply;
+
+      *lpdwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpReply );
+
+      *lplpReply = (LPDPMSG_NEWPLAYERIDREPLY)HeapAlloc( GetProcessHeap(),
+                                                        HEAP_ZERO_MEMORY,
+                                                        *lpdwMsgSize );
+
+      FIXME( "Ignoring dwFlags in msg\n" );
+
+      /* Setup the reply */
+      lpReply = (LPDPMSG_NEWPLAYERIDREPLY)( (BYTE*)(*lplpReply) +
+                                            This->dp2->spData.dwSPHeaderSize );
+
+      lpReply->envelope.dwMagic    = DPMSGMAGIC_DPLAYMSG;
+      lpReply->envelope.wCommandId = DPMSGCMD_NEWPLAYERIDREPLY;
+      lpReply->envelope.wVersion   = DPMSGVER_DP6;
+
+#if 0
+      /* FIXME: Need to know the proper contents of the message! */
+      lpReply->dpidNewPlayerId = DP_NextObjectId();
+#endif
+
+      break;
+    }
+
+    case DPMSGCMD_NEWPLAYERIDREPLY:
+    {
+
+      DebugBreak();
+
+      if( This->dp2->hMsgReceipt )
+      {
+        /* This is a hack only */
+        This->dp2->lpMsgReceived = HeapAlloc( GetProcessHeap(),
+                                              HEAP_ZERO_MEMORY,
+                                              dwMessageBodySize );
+        CopyMemory( This->dp2->lpMsgReceived, lpMessageBody, dwMessageBodySize );
+        SetEvent( This->dp2->hMsgReceipt );
+      }
+      else
+      {
+        ERR( "No receipt event set\n" );
+      }
+
+      break;
+    }
+
+    default:
+    {
+      FIXME( "Unknown wCommandId %u. Ignoring message\n", wCommandId );
+      break;
+    }
+  }
+
+  return DP_OK;
+}
+
+
+static HRESULT WINAPI DP_IF_AddPlayerToGroup
+          ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idGroup,
+            DPID idPlayer, BOOL bAnsi )
+{
+  lpGroupData  lpGData;
+  lpPlayerList lpPList;
+  lpPlayerList lpNewPList;
+
+  TRACE( "(%p)->(%p,0x%08lx,0x%08lx,%u)\n",
+         This, lpMsgHdr, idGroup, idPlayer, bAnsi );
+
+  /* Find the group */
+  if( ( lpGData = DP_FindAnyGroup( This, idGroup ) ) == NULL )
+  {
+    return DPERR_INVALIDGROUP;
+  }
+
+  /* Find the player */
+  if( ( lpPList = DP_FindPlayer( This, idPlayer ) ) == NULL )
+  {
+    return DPERR_INVALIDPLAYER;
+  }
+
+  /* Create a player list (ie "shortcut" ) */
+  lpNewPList = (lpPlayerList)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                        sizeof( *lpNewPList ) );
+  if( lpNewPList == NULL )
+  {
+    return DPERR_CANTADDPLAYER;
+  }
+
+  /* Add the shortcut */
+  lpPList->lpPData->uRef++;
+  lpNewPList->lpPData = lpPList->lpPData;
+
+  /* Add the player to the list of players for this group */
+  DPQ_INSERT(lpGData->players,lpNewPList,players);
+
+  /* Let the SP know that we've added a player to the group */
+  if( This->dp2->spData.lpCB->AddPlayerToGroup )
+  {
+    DPSP_ADDPLAYERTOGROUPDATA data;
+
+    TRACE( "Calling SP AddPlayerToGroup\n" );
+
+    data.idPlayer = idPlayer;
+    data.idGroup  = idGroup;
+    data.lpISP    = This->dp2->spData.lpISP;
+
+    (*This->dp2->spData.lpCB->AddPlayerToGroup)( &data );
+  }
+
+  /* Inform all other peers of the addition of player to the group. If there are
+   * no peers keep this event quiet.
+   * Also, if this event was the result of another machine sending it to us,
+   * don't bother rebroadcasting it.
+   */
+  if( ( lpMsgHdr == NULL ) &&
+      This->dp2->lpSessionDesc &&
+      ( This->dp2->lpSessionDesc->dwFlags & DPSESSION_MULTICASTSERVER ) )
+  {
+    DPMSG_ADDPLAYERTOGROUP msg;
+    msg.dwType = DPSYS_ADDPLAYERTOGROUP;
+
+    msg.dpIdGroup  = idGroup;
+    msg.dpIdPlayer = idPlayer;
+
+    /* FIXME: Correct to just use send effectively? */
+    /* FIXME: Should size include data w/ message or just message "header" */
+    /* FIXME: Check return code */
+    DP_SendEx( This, DPID_SERVERPLAYER, DPID_ALLPLAYERS, 0, &msg, sizeof( msg ),               0, 0, NULL, NULL, bAnsi );
+  }
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_AddPlayerToGroup
+          ( LPDIRECTPLAY2A iface, DPID idGroup, DPID idPlayer )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_AddPlayerToGroup( This, NULL, idGroup, idPlayer, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_AddPlayerToGroup
+          ( LPDIRECTPLAY2 iface, DPID idGroup, DPID idPlayer )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_AddPlayerToGroup( This, NULL, idGroup, idPlayer, FALSE );
+}
+
+static HRESULT WINAPI DP_IF_Close( IDirectPlay2Impl* This, BOOL bAnsi )
+{
+  HRESULT hr = DP_OK;
+
+  TRACE("(%p)->(%u)\n", This, bAnsi );
+
+  /* FIXME: Need to find a new host I assume (how?) */
+  /* FIXME: Need to destroy all local groups */
+  /* FIXME: Need to migrate all remotely visible players to the new host */
+
+  /* Invoke the SP callback to inform of session close */
+  if( This->dp2->spData.lpCB->CloseEx )
+  {
+    DPSP_CLOSEDATA data;
+
+    TRACE( "Calling SP CloseEx\n" );
+
+    data.lpISP = This->dp2->spData.lpISP;
+
+    hr = (*This->dp2->spData.lpCB->CloseEx)( &data );
+
+  }
+  else if ( This->dp2->spData.lpCB->Close ) /* Try obsolete version */
+  {
+    TRACE( "Calling SP Close (obsolete interface)\n" );
+
+    hr = (*This->dp2->spData.lpCB->Close)();
+  }
+
+  return hr;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_Close
+          ( LPDIRECTPLAY2A iface )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_Close( This, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_Close
+          ( LPDIRECTPLAY2 iface )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_Close( This, FALSE );
+}
+
+static
+lpGroupData DP_CreateGroup( IDirectPlay2AImpl* This, LPDPID lpid,
+                            LPDPNAME lpName, DWORD dwFlags,
+                            DPID idParent, BOOL bAnsi )
+{
+  lpGroupData lpGData;
+
+  /* Allocate the new space and add to end of high level group list */
+  lpGData = (lpGroupData) HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                     sizeof( *lpGData ) );
+
+  if( lpGData == NULL )
+  {
+    return NULL;
+  }
+
+  DPQ_INIT(lpGData->groups);
+  DPQ_INIT(lpGData->players);
+
+  /* Set the desired player ID - no sanity checking to see if it exists */
+  lpGData->dpid = *lpid;
+
+  DP_CopyDPNAMEStruct( &lpGData->name, lpName, bAnsi );
+
+  /* FIXME: Should we check that the parent exists? */
+  lpGData->parent  = idParent;
+
+  /* FIXME: Should we validate the dwFlags? */
+  lpGData->dwFlags = dwFlags;
+
+  return lpGData;
+}
+
+/* This method assumes that all links to it are already deleted */
+static void
+DP_DeleteGroup( IDirectPlay2Impl* This, DPID dpid )
+{
+  lpGroupList lpGList;
+
+  TRACE( "(%p)->(0x%08lx)\n", This, dpid );
+
+  DPQ_REMOVE_ENTRY( This->dp2->lpSysGroup->groups, groups, lpGData->dpid, ==, dpid, lpGList );
+
+  if( lpGList == NULL )
+  {
+    ERR( "DPID 0x%08lx not found\n", dpid );
+    return;
+  }
+
+  if( --(lpGList->lpGData->uRef) )
+  {
+    FIXME( "Why is this not the last reference to group?\n" );
+    DebugBreak();
+  }
+
+  /* Delete player */
+  DP_DeleteDPNameStruct( &lpGList->lpGData->name );
+  HeapFree( GetProcessHeap(), 0, lpGList->lpGData );
+
+  /* Remove and Delete Player List object */
+  HeapFree( GetProcessHeap(), 0, lpGList );
+
+}
+
+static lpGroupData DP_FindAnyGroup( IDirectPlay2AImpl* This, DPID dpid )
+{
+  lpGroupList lpGroups;
+
+  TRACE( "(%p)->(0x%08lx)\n", This, dpid );
+
+  if( dpid == DPID_SYSTEM_GROUP )
+  {
+    return This->dp2->lpSysGroup;
+  }
+  else
+  {
+    DPQ_FIND_ENTRY( This->dp2->lpSysGroup->groups, groups, lpGData->dpid, ==, dpid, lpGroups );
+  }
+
+  if( lpGroups == NULL )
+  {
+    return NULL;
+  }
+
+  return lpGroups->lpGData;
+}
+
+static HRESULT WINAPI DP_IF_CreateGroup
+          ( IDirectPlay2AImpl* This, LPVOID lpMsgHdr, LPDPID lpidGroup,
+            LPDPNAME lpGroupName, LPVOID lpData, DWORD dwDataSize,
+            DWORD dwFlags, BOOL bAnsi )
+{
+  lpGroupData lpGData;
+
+  TRACE( "(%p)->(%p,%p,%p,%p,0x%08lx,0x%08lx,%u)\n",
+         This, lpMsgHdr, lpidGroup, lpGroupName, lpData, dwDataSize,
+         dwFlags, bAnsi );
+
+  /* If the name is not specified, we must provide one */
+  if( DPID_UNKNOWN == *lpidGroup )
+  {
+    /* If we are the name server, we decide on the group ids. If not, we
+     * must ask for one before attempting a creation.
+     */
+    if( This->dp2->bHostInterface )
+    {
+      *lpidGroup = DP_NextObjectId();
+    }
+    else
+    {
+      *lpidGroup = DP_GetRemoteNextObjectId();
+    }
+  }
+
+  lpGData = DP_CreateGroup( This, lpidGroup, lpGroupName, dwFlags,
+                            DPID_NOPARENT_GROUP, bAnsi );
+
+  if( lpGData == NULL )
+  {
+    return DPERR_CANTADDPLAYER; /* yes player not group */
+  }
+
+  if( DPID_SYSTEM_GROUP == *lpidGroup )
+  {
+    This->dp2->lpSysGroup = lpGData;
+  }
+  else
+  {
+    /* Insert into the system group */
+    lpGroupList lpGroup = (lpGroupList) HeapAlloc( GetProcessHeap(),
+                                                   HEAP_ZERO_MEMORY,
+                                                   sizeof( *lpGroup ) );
+    lpGroup->lpGData = lpGData;
+
+    DPQ_INSERT( This->dp2->lpSysGroup->groups, lpGroup, groups );
+  }
+
+  /* Something is now referencing this data */
+  lpGData->uRef++;
+
+  /* Set all the important stuff for the group */
+  DP_SetGroupData( lpGData, DPSET_REMOTE, lpData, dwDataSize );
+
+  /* FIXME: We should only create the system group if GetCaps returns
+   *        DPCAPS_GROUPOPTIMIZED.
+   */
+
+  /* Let the SP know that we've created this group */
+  if( This->dp2->spData.lpCB->CreateGroup )
+  {
+    DPSP_CREATEGROUPDATA data;
+    DWORD dwCreateFlags = 0;
+
+    TRACE( "Calling SP CreateGroup\n" );
+
+    if( *lpidGroup == DPID_NOPARENT_GROUP )
+      dwCreateFlags |= DPLAYI_GROUP_SYSGROUP;
+
+    if( lpMsgHdr == NULL )
+      dwCreateFlags |= DPLAYI_PLAYER_PLAYERLOCAL;
+
+    if( dwFlags & DPGROUP_HIDDEN )
+      dwCreateFlags |= DPLAYI_GROUP_HIDDEN;
+
+    data.idGroup           = *lpidGroup;
+    data.dwFlags           = dwCreateFlags;
+    data.lpSPMessageHeader = lpMsgHdr;
+    data.lpISP             = This->dp2->spData.lpISP;
+
+    (*This->dp2->spData.lpCB->CreateGroup)( &data );
+  }
+
+  /* Inform all other peers of the creation of a new group. If there are
+   * no peers keep this event quiet.
+   * Also if this message was sent to us, don't rebroadcast.
+   */
+  if( ( lpMsgHdr == NULL ) &&
+      This->dp2->lpSessionDesc &&
+      ( This->dp2->lpSessionDesc->dwFlags & DPSESSION_MULTICASTSERVER ) )
+  {
+    DPMSG_CREATEPLAYERORGROUP msg;
+    msg.dwType = DPSYS_CREATEPLAYERORGROUP;
+
+    msg.dwPlayerType     = DPPLAYERTYPE_GROUP;
+    msg.dpId             = *lpidGroup;
+    msg.dwCurrentPlayers = 0; /* FIXME: Incorrect? */
+    msg.lpData           = lpData;
+    msg.dwDataSize       = dwDataSize;
+    msg.dpnName          = *lpGroupName;
+    msg.dpIdParent       = DPID_NOPARENT_GROUP;
+    msg.dwFlags          = DPMSG_CREATEGROUP_DWFLAGS( dwFlags );
+
+    /* FIXME: Correct to just use send effectively? */
+    /* FIXME: Should size include data w/ message or just message "header" */
+    /* FIXME: Check return code */
+    DP_SendEx( This, DPID_SERVERPLAYER, DPID_ALLPLAYERS, 0, &msg, sizeof( msg ),
+               0, 0, NULL, NULL, bAnsi );
+  }
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_CreateGroup
+          ( LPDIRECTPLAY2A iface, LPDPID lpidGroup, LPDPNAME lpGroupName,
+            LPVOID lpData, DWORD dwDataSize, DWORD dwFlags )
+{
+  *lpidGroup = DPID_UNKNOWN;
+
+  return DP_IF_CreateGroup( (IDirectPlay2AImpl*)iface, NULL, lpidGroup,
+                            lpGroupName, lpData, dwDataSize, dwFlags, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_CreateGroup
+          ( LPDIRECTPLAY2 iface, LPDPID lpidGroup, LPDPNAME lpGroupName,
+            LPVOID lpData, DWORD dwDataSize, DWORD dwFlags )
+{
+  *lpidGroup = DPID_UNKNOWN;
+
+  return DP_IF_CreateGroup( (IDirectPlay2AImpl*)iface, NULL, lpidGroup,
+                            lpGroupName, lpData, dwDataSize, dwFlags, FALSE );
+}
+
+
+static void
+DP_SetGroupData( lpGroupData lpGData, DWORD dwFlags,
+                 LPVOID lpData, DWORD dwDataSize )
+{
+  /* Clear out the data with this player */
+  if( ( dwFlags & DPSET_LOCAL ) &&
+      ( lpGData->dwLocalDataSize != 0 )
+    )
+  {
+    HeapFree( GetProcessHeap(), 0, lpGData->lpLocalData );
+    lpGData->lpLocalData = NULL;
+    lpGData->dwLocalDataSize = 0;
+  }
+  if( ( dwFlags & DPSET_REMOTE ) &&
+      ( lpGData->dwRemoteDataSize != 0 )
+    )
+  {
+    HeapFree( GetProcessHeap(), 0, lpGData->lpRemoteData );
+    lpGData->lpRemoteData = NULL;
+    lpGData->dwRemoteDataSize = 0;
+  }
+
+  /* Reallocate for new data */
+  if( lpData != NULL )
+  {
+    LPVOID lpNewData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                  sizeof( dwDataSize ) );
+    CopyMemory( lpNewData, lpData, dwDataSize );
+
+    if( dwFlags & DPSET_REMOTE )
+    {
+      lpGData->lpRemoteData     = lpNewData;
+      lpGData->dwRemoteDataSize = dwDataSize;
+    }
+
+    if( dwFlags & DPSET_LOCAL )
+    {
+      lpGData->lpLocalData     = lpData;
+      lpGData->dwLocalDataSize = dwDataSize;
+    }
+  }
+
+}
+
+/* This function will just create the storage for the new player.  */
+static
+lpPlayerData DP_CreatePlayer( IDirectPlay2Impl* This, LPDPID lpid,
+                              LPDPNAME lpName, DWORD dwFlags,
+                              HANDLE hEvent, BOOL bAnsi )
+{
+  lpPlayerData lpPData;
+
+  TRACE( "(%p)->(%p,%p,%u)\n", This, lpid, lpName, bAnsi );
+
+  /* Allocate the storage for the player and associate it with list element */
+  lpPData = (lpPlayerData) HeapAlloc( GetProcessHeap(),
+                                      HEAP_ZERO_MEMORY,
+                                      sizeof( *lpPData ) );
+  if( lpPData == NULL )
+  {
+    return NULL;
+  }
+
+  /* Set the desired player ID */
+  lpPData->dpid = *lpid;
+
+  DP_CopyDPNAMEStruct( &lpPData->name, lpName, bAnsi );
+
+  lpPData->dwFlags = dwFlags;
+
+  /* If we were given an event handle, duplicate it */
+  if( hEvent != 0 )
+  {
+    if( !DuplicateHandle( GetCurrentProcess(), hEvent,
+                          GetCurrentProcess(), &lpPData->hEvent,
+                          0, FALSE, DUPLICATE_SAME_ACCESS )
+      )
+    {
+      /* FIXME: Memory leak */
+      ERR( "Can't duplicate player msg handle %x\n", hEvent );
+    }
+  }
+
+  return lpPData;
+}
+
+/* Delete the contents of the DPNAME struct */
+static void
+DP_DeleteDPNameStruct( LPDPNAME lpDPName )
+{
+  HeapFree( GetProcessHeap(), HEAP_ZERO_MEMORY, lpDPName->psn.lpszShortNameA );
+  HeapFree( GetProcessHeap(), HEAP_ZERO_MEMORY, lpDPName->pln.lpszLongNameA );
+}
+
+/* This method assumes that all links to it are already deleted */
+static void
+DP_DeletePlayer( IDirectPlay2Impl* This, DPID dpid )
+{
+  lpPlayerList lpPList;
+
+  TRACE( "(%p)->(0x%08lx)\n", This, dpid );
+
+  DPQ_REMOVE_ENTRY( This->dp2->lpSysGroup->players, players, lpPData->dpid, ==, dpid, lpPList );
+
+  if( lpPList == NULL )
+  {
+    ERR( "DPID 0x%08lx not found\n", dpid );
+    return;
+  }
+
+  /* Verify that this is the last reference to the data */
+  if( --(lpPList->lpPData->uRef) )
+  {
+    FIXME( "Why is this not the last reference to player?\n" );
+    DebugBreak();
+  }
+
+  /* Delete player */
+  DP_DeleteDPNameStruct( &lpPList->lpPData->name );
+
+  CloseHandle( lpPList->lpPData->hEvent );
+  HeapFree( GetProcessHeap(), 0, lpPList->lpPData );
+
+  /* Delete Player List object */
+  HeapFree( GetProcessHeap(), 0, lpPList );
+}
+
+static lpPlayerList DP_FindPlayer( IDirectPlay2AImpl* This, DPID dpid )
+{
+  lpPlayerList lpPlayers;
+
+  TRACE( "(%p)->(0x%08lx)\n", This, dpid );
+
+  DPQ_FIND_ENTRY( This->dp2->lpSysGroup->players, players, lpPData->dpid, ==, dpid, lpPlayers );
+
+  return lpPlayers;
+}
+
+/* Basic area for Dst must already be allocated */
+static BOOL DP_CopyDPNAMEStruct( LPDPNAME lpDst, LPDPNAME lpSrc, BOOL bAnsi )
+{
+  if( lpSrc == NULL )
+  {
+    ZeroMemory( lpDst, sizeof( *lpDst ) );
+    lpDst->dwSize = sizeof( *lpDst );
+    return TRUE;
+  }
+
+  if( lpSrc->dwSize != sizeof( *lpSrc) )
+  {
+    return FALSE;
+  }
+
+  /* Delete any existing pointers */
+  if( lpDst->psn.lpszShortNameA )
+  {
+    HeapFree( GetProcessHeap(), 0, lpDst->psn.lpszShortNameA );
+  }
+
+  if( lpDst->pln.lpszLongNameA )
+  {
+    HeapFree( GetProcessHeap(), 0, lpDst->psn.lpszShortNameA );
+  }
+
+  /* Copy as required */
+  CopyMemory( lpDst, lpSrc, lpSrc->dwSize );
+
+  if( bAnsi )
+  {
+    if( lpSrc->psn.lpszShortNameA )
+    {
+      lpDst->psn.lpszShortNameA =
+        HEAP_strdupA( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                        lpSrc->psn.lpszShortNameA );
+    }
+    if( lpSrc->pln.lpszLongNameA )
+    {
+      lpDst->pln.lpszLongNameA =
+        HEAP_strdupA( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                        lpSrc->pln.lpszLongNameA );
+    }
+  }
+  else
+  {
+    if( lpSrc->psn.lpszShortNameA )
+    {
+      lpDst->psn.lpszShortName =
+        HEAP_strdupW( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                        lpSrc->psn.lpszShortName );
+    }
+    if( lpSrc->pln.lpszLongNameA )
+    {
+      lpDst->pln.lpszLongName =
+        HEAP_strdupW( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                        lpSrc->pln.lpszLongName );
+    }
+  }
+
+  return TRUE;
+}
+
+static void
+DP_SetPlayerData( lpPlayerData lpPData, DWORD dwFlags,
+                  LPVOID lpData, DWORD dwDataSize )
+{
+  /* Clear out the data with this player */
+  if( ( dwFlags & DPSET_LOCAL ) &&
+      ( lpPData->dwLocalDataSize != 0 )
+    )
+  {
+    HeapFree( GetProcessHeap(), 0, lpPData->lpLocalData );
+    lpPData->lpLocalData = NULL;
+    lpPData->dwLocalDataSize = 0;
+  }
+  if( ( dwFlags & DPSET_REMOTE ) &&
+      ( lpPData->dwRemoteDataSize != 0 )
+    )
+  {
+    HeapFree( GetProcessHeap(), 0, lpPData->lpRemoteData );
+    lpPData->lpRemoteData = NULL;
+    lpPData->dwRemoteDataSize = 0;
+  }
+
+  /* Reallocate for new data */
+  if( lpData != NULL )
+  {
+    LPVOID lpNewData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                  sizeof( dwDataSize ) );
+    CopyMemory( lpNewData, lpData, dwDataSize );
+
+    if( dwFlags & DPSET_REMOTE )
+    {
+      lpPData->lpRemoteData     = lpNewData;
+      lpPData->dwRemoteDataSize = dwDataSize;
+    }
+
+    if( dwFlags & DPSET_LOCAL )
+    {
+      lpPData->lpLocalData     = lpData;
+      lpPData->dwLocalDataSize = dwDataSize;
+    }
+  }
+
+}
+
+static HRESULT WINAPI DP_IF_CreatePlayer
+( IDirectPlay2Impl* This,
+  LPVOID lpMsgHdr, /* NULL for local creation, non NULL for remote creation */
+  LPDPID lpidPlayer,
+  LPDPNAME lpPlayerName,
+  HANDLE hEvent,
+  LPVOID lpData,
+  DWORD dwDataSize,
+  DWORD dwFlags,
+  BOOL bAnsi )
+{
+  lpPlayerData lpPData;
+  lpPlayerList lpPList;
+
+  TRACE( "(%p)->(%p,%p,%d,%p,0x%08lx,0x%08lx,%u)\n",
+         This, lpidPlayer, lpPlayerName, hEvent, lpData,
+         dwDataSize, dwFlags, bAnsi );
+
+  if( dwFlags == 0 )
+  {
+    dwFlags = DPPLAYER_SPECTATOR;
+  }
+
+  if( lpidPlayer == NULL )
+  {
+    return DPERR_INVALIDPARAMS;
+  }
+
+  /* Verify we know how to handle all the flags */
+  if( !( ( dwFlags & DPPLAYER_SERVERPLAYER ) ||
+         ( dwFlags & DPPLAYER_SPECTATOR )
+       )
+    )
+  {
+    /* Assume non fatal failure */
+    ERR( "unknown dwFlags = 0x%08lx\n", dwFlags );
+  }
+
+  /* If the name is not specified, we must provide one */
+  if( *lpidPlayer == DPID_UNKNOWN )
+  {
+    /* If we are the session master, we dish out the group/player ids */
+    if( This->dp2->bHostInterface )
+    {
+      *lpidPlayer = DP_NextObjectId();
+    }
+    else
+    {
+      HRESULT hr = DP_MSG_SendRequestPlayerId( This, dwFlags, lpidPlayer );
+
+      if( FAILED(hr) )
+      {
+        ERR( "Request for ID failed: %s\n", DPLAYX_HresultToString( hr ) );
+        return hr;
+      }
+    }
+  }
+  else
+  {
+    /* FIXME: Would be nice to perhaps verify that we don't already have
+     *        this player.
+     */
+  }
+
+  lpPData = DP_CreatePlayer( This, lpidPlayer, lpPlayerName, dwFlags,
+                             hEvent, bAnsi );
+
+  if( lpPData == NULL )
+  {
+    return DPERR_CANTADDPLAYER;
+  }
+
+  /* Create the list object and link it in */
+  lpPList = (lpPlayerList)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                     sizeof( *lpPList ) );
+  if( lpPList == NULL )
+  {
+    FIXME( "Memory leak\n" );
+    return DPERR_CANTADDPLAYER;
+  }
+
+  lpPData->uRef = 1;
+  lpPList->lpPData = lpPData;
+
+  /* Add the player to the system group */
+  DPQ_INSERT( This->dp2->lpSysGroup->players, lpPList, players );
+
+  /* Update the information and send it to all players in the session */
+  DP_SetPlayerData( lpPData, DPSET_REMOTE, lpData, dwDataSize );
+
+  /* Let the SP know that we've created this player */
+  if( This->dp2->spData.lpCB->CreatePlayer )
+  {
+    DPSP_CREATEPLAYERDATA data;
+    DWORD dwCreateFlags = 0;
+
+    TRACE( "Calling SP CreatePlayer\n" );
+
+    if( ( dwFlags & DPPLAYER_SERVERPLAYER ) &&
+        ( *lpidPlayer == DPID_SERVERPLAYER )
+      )
+      dwCreateFlags |= DPLAYI_PLAYER_APPSERVER;
+
+    if( ( dwFlags & DPPLAYER_SERVERPLAYER ) &&
+        ( *lpidPlayer == DPID_NAME_SERVER )
+      )
+      dwCreateFlags |= (DPLAYI_PLAYER_NAMESRVR|DPLAYI_PLAYER_SYSPLAYER);
+
+    if( lpMsgHdr == NULL )
+      dwCreateFlags |= DPLAYI_PLAYER_PLAYERLOCAL;
+
+    data.idPlayer          = *lpidPlayer;
+    data.dwFlags           = dwCreateFlags;
+    data.lpSPMessageHeader = lpMsgHdr;
+    data.lpISP             = This->dp2->spData.lpISP;
+
+    (*This->dp2->spData.lpCB->CreatePlayer)( &data );
+  }
+
+  /* Inform all other peers of the creation of a new player. If there are
+   * no peers keep this quiet.
+   * Also, if this was a remote event, no need to rebroadcast it.
+   */
+  if( ( lpMsgHdr == NULL ) &&
+      This->dp2->lpSessionDesc &&
+      ( This->dp2->lpSessionDesc->dwFlags & DPSESSION_MULTICASTSERVER ) )
+  {
+    DPMSG_CREATEPLAYERORGROUP msg;
+    msg.dwType = DPSYS_CREATEPLAYERORGROUP;
+
+    msg.dwPlayerType     = DPPLAYERTYPE_PLAYER;
+    msg.dpId             = *lpidPlayer;
+    msg.dwCurrentPlayers = 0; /* FIXME: Incorrect */
+    msg.lpData           = lpData;
+    msg.dwDataSize       = dwDataSize;
+    msg.dpnName          = *lpPlayerName;
+    msg.dpIdParent       = DPID_NOPARENT_GROUP;
+    msg.dwFlags          = DPMSG_CREATEPLAYER_DWFLAGS( dwFlags );
+
+    /* FIXME: Correct to just use send effectively? */
+    /* FIXME: Should size include data w/ message or just message "header" */
+    /* FIXME: Check return code */
+    DP_SendEx( This, DPID_SERVERPLAYER, DPID_ALLPLAYERS, 0, &msg, sizeof( msg ),
+               0, 0, NULL, NULL, bAnsi );
+  }
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_CreatePlayer
+          ( LPDIRECTPLAY2A iface, LPDPID lpidPlayer, LPDPNAME lpPlayerName,
+            HANDLE hEvent, LPVOID lpData, DWORD dwDataSize, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+
+  if( dwFlags & DPPLAYER_SERVERPLAYER )
+  {
+    *lpidPlayer = DPID_SERVERPLAYER;
+  }
+  else
+  {
+    *lpidPlayer = DPID_UNKNOWN;
+  }
+
+  return DP_IF_CreatePlayer( This, NULL, lpidPlayer, lpPlayerName, hEvent,
+                           lpData, dwDataSize, dwFlags, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_CreatePlayer
+          ( LPDIRECTPLAY2 iface, LPDPID lpidPlayer, LPDPNAME lpPlayerName,
+            HANDLE hEvent, LPVOID lpData, DWORD dwDataSize, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+
+  if( dwFlags & DPPLAYER_SERVERPLAYER )
+  {
+    *lpidPlayer = DPID_SERVERPLAYER;
+  }
+  else
+  {
+    *lpidPlayer = DPID_UNKNOWN;
+  }
+
+  return DP_IF_CreatePlayer( This, NULL, lpidPlayer, lpPlayerName, hEvent,
+                           lpData, dwDataSize, dwFlags, FALSE );
+}
+
+static DPID DP_GetRemoteNextObjectId(void)
+{
+  FIXME( ":stub\n" );
+
+  /* Hack solution */
+  return DP_NextObjectId();
+}
+
+static HRESULT WINAPI DP_IF_DeletePlayerFromGroup
+          ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idGroup,
+            DPID idPlayer, BOOL bAnsi )
+{
+  HRESULT hr = DP_OK;
+
+  lpGroupData  lpGData;
+  lpPlayerList lpPList;
+
+  TRACE( "(%p)->(%p,0x%08lx,0x%08lx,%u)\n",
+         This, lpMsgHdr, idGroup, idPlayer, bAnsi );
+
+  /* Find the group */
+  if( ( lpGData = DP_FindAnyGroup( This, idGroup ) ) == NULL )
+  {
+    return DPERR_INVALIDGROUP;
+  }
+
+  /* Find the player */
+  if( ( lpPList = DP_FindPlayer( This, idPlayer ) ) == NULL )
+  {
+    return DPERR_INVALIDPLAYER;
+  }
+
+  /* Remove the player shortcut from the group */
+  DPQ_REMOVE_ENTRY( lpGData->players, players, lpPData->dpid, ==, idPlayer, lpPList );
+
+  if( lpPList == NULL )
+  {
+    return DPERR_INVALIDPLAYER;
+  }
+
+  /* One less reference */
+  lpPList->lpPData->uRef--;
+
+  /* Delete the Player List element */
+  HeapFree( GetProcessHeap(), 0, lpPList );
+
+  /* Inform the SP if they care */
+  if( This->dp2->spData.lpCB->RemovePlayerFromGroup )
+  {
+    DPSP_REMOVEPLAYERFROMGROUPDATA data;
+
+    TRACE( "Calling SP RemovePlayerFromGroup\n" );
+
+    data.idPlayer = idPlayer;
+    data.idGroup  = idGroup;
+    data.lpISP    = This->dp2->spData.lpISP;
+
+    hr = (*This->dp2->spData.lpCB->RemovePlayerFromGroup)( &data );
+  }
+
+  /* Need to send a DELETEPLAYERFROMGROUP message */
+  FIXME( "Need to send a message\n" );
+
+  return hr;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_DeletePlayerFromGroup
+          ( LPDIRECTPLAY2A iface, DPID idGroup, DPID idPlayer )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_DeletePlayerFromGroup( This, NULL, idGroup, idPlayer, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_DeletePlayerFromGroup
+          ( LPDIRECTPLAY2 iface, DPID idGroup, DPID idPlayer )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_DeletePlayerFromGroup( This, NULL, idGroup, idPlayer, FALSE );
+}
+
+typedef struct _DPRGOPContext
+{
+  IDirectPlay3Impl* This;
+  BOOL              bAnsi;
+  DPID              idGroup;
+} DPRGOPContext, *lpDPRGOPContext;
+
+static BOOL CALLBACK
+cbRemoveGroupOrPlayer(
+    DPID            dpId,
+    DWORD           dwPlayerType,
+    LPCDPNAME       lpName,
+    DWORD           dwFlags,
+    LPVOID          lpContext )
+{
+  lpDPRGOPContext lpCtxt = (lpDPRGOPContext)lpContext;
+
+  TRACE( "Removing element:0x%08lx (type:0x%08lx) from element:0x%08lx\n",
+           dpId, dwPlayerType, lpCtxt->idGroup );
+
+  if( dwPlayerType == DPPLAYERTYPE_GROUP )
+  {
+    if( FAILED( DP_IF_DeleteGroupFromGroup( lpCtxt->This, lpCtxt->idGroup,
+                                            dpId )
+              )
+      )
+    {
+      ERR( "Unable to delete group 0x%08lx from group 0x%08lx\n",
+             dpId, lpCtxt->idGroup );
+    }
+  }
+  else
+  {
+    if( FAILED( DP_IF_DeletePlayerFromGroup( (IDirectPlay2Impl*)lpCtxt->This,
+                                             NULL, lpCtxt->idGroup,
+                                             dpId, lpCtxt->bAnsi )
+              )
+      )
+    {
+      ERR( "Unable to delete player 0x%08lx from grp 0x%08lx\n",
+             dpId, lpCtxt->idGroup );
+    }
+  }
+
+  return TRUE; /* Continue enumeration */
+}
+
+static HRESULT WINAPI DP_IF_DestroyGroup
+          ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idGroup, BOOL bAnsi )
+{
+  lpGroupData lpGData;
+  DPRGOPContext context;
+
+  FIXME( "(%p)->(%p,0x%08lx,%u): semi stub\n",
+         This, lpMsgHdr, idGroup, bAnsi );
+
+  /* Find the group */
+  if( ( lpGData = DP_FindAnyGroup( This, idGroup ) ) == NULL )
+  {
+    return DPERR_INVALIDPLAYER; /* yes player */
+  }
+
+  context.This    = (IDirectPlay3Impl*)This;
+  context.bAnsi   = bAnsi;
+  context.idGroup = idGroup;
+
+  /* Remove all players that this group has */
+  DP_IF_EnumGroupPlayers( This, idGroup, NULL,
+                          cbRemoveGroupOrPlayer, (LPVOID)&context, 0, bAnsi );
+
+  /* Remove all links to groups that this group has since this is dp3 */
+  DP_IF_EnumGroupsInGroup( (IDirectPlay3Impl*)This, idGroup, NULL,
+                           cbRemoveGroupOrPlayer, (LPVOID)&context, 0, bAnsi );
+
+  /* Remove this group from the parent group - if it has one */
+  if( ( idGroup != DPID_SYSTEM_GROUP ) &&
+      ( lpGData->parent != DPID_SYSTEM_GROUP )
+    )
+  {
+    DP_IF_DeleteGroupFromGroup( (IDirectPlay3Impl*)This, lpGData->parent,
+                                idGroup );
+  }
+
+  /* Now delete this group data and list from the system group */
+  DP_DeleteGroup( This, idGroup );
+
+  /* Let the SP know that we've destroyed this group */
+  if( This->dp2->spData.lpCB->DeleteGroup )
+  {
+    DPSP_DELETEGROUPDATA data;
+
+    FIXME( "data.dwFlags is incorrect\n" );
+
+    data.idGroup = idGroup;
+    data.dwFlags = 0;
+    data.lpISP   = This->dp2->spData.lpISP;
+
+    (*This->dp2->spData.lpCB->DeleteGroup)( &data );
+  }
+
+  FIXME( "Send out a DESTORYPLAYERORGROUP message\n" );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_DestroyGroup
+          ( LPDIRECTPLAY2A iface, DPID idGroup )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_DestroyGroup( This, NULL, idGroup, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_DestroyGroup
+          ( LPDIRECTPLAY2 iface, DPID idGroup )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_DestroyGroup( This, NULL, idGroup, FALSE );
+}
+
+typedef struct _DPFAGContext
+{
+  IDirectPlay2Impl* This;
+  DPID              idPlayer;
+  BOOL              bAnsi;
+} DPFAGContext, *lpDPFAGContext;
+
+static HRESULT WINAPI DP_IF_DestroyPlayer
+          ( IDirectPlay2Impl* This, LPVOID lpMsgHdr, DPID idPlayer, BOOL bAnsi )
+{
+  DPFAGContext cbContext;
+
+  FIXME( "(%p)->(%p,0x%08lx,%u): semi stub\n",
+         This, lpMsgHdr, idPlayer, bAnsi );
+
+  if( DP_FindPlayer( This, idPlayer ) == NULL )
+  {
+    return DPERR_INVALIDPLAYER;
+  }
+
+  /* FIXME: If the player is remote, we must be the host to delete this */
+
+  cbContext.This     = This;
+  cbContext.idPlayer = idPlayer;
+  cbContext.bAnsi    = bAnsi;
+
+  /* Find each group and call DeletePlayerFromGroup if the player is a
+     member of the group */
+  DP_IF_EnumGroups( This, NULL, cbDeletePlayerFromAllGroups,
+                    (LPVOID)&cbContext, DPENUMGROUPS_ALL, bAnsi );
+
+  /* Now delete player and player list from the sys group */
+  DP_DeletePlayer( This, idPlayer );
+
+  /* Let the SP know that we've destroyed this group */
+  if( This->dp2->spData.lpCB->DeletePlayer )
+  {
+    DPSP_DELETEPLAYERDATA data;
+
+    FIXME( "data.dwFlags is incorrect\n" );
+
+    data.idPlayer = idPlayer;
+    data.dwFlags = 0;
+    data.lpISP   = This->dp2->spData.lpISP;
+
+    (*This->dp2->spData.lpCB->DeletePlayer)( &data );
+  }
+
+  FIXME( "Send a DELETEPLAYERORGROUP msg\n" );
+
+  return DP_OK;
+}
+
+static BOOL CALLBACK
+cbDeletePlayerFromAllGroups(
+    DPID            dpId,
+    DWORD           dwPlayerType,
+    LPCDPNAME       lpName,
+    DWORD           dwFlags,
+    LPVOID          lpContext )
+{
+  lpDPFAGContext lpCtxt = (lpDPFAGContext)lpContext;
+
+  if( dwPlayerType == DPPLAYERTYPE_GROUP )
+  {
+    DP_IF_DeletePlayerFromGroup( lpCtxt->This, NULL, dpId, lpCtxt->idPlayer,
+                                 lpCtxt->bAnsi );
+
+    /* Enumerate all groups in this group since this will normally only
+     * be called for top level groups
+     */
+    DP_IF_EnumGroupsInGroup( (IDirectPlay3Impl*)lpCtxt->This,
+                             dpId, NULL,
+                             cbDeletePlayerFromAllGroups,
+                             (LPVOID)lpContext, DPENUMGROUPS_ALL,
+                             lpCtxt->bAnsi );
+
+  }
+  else
+  {
+    ERR( "Group callback has dwPlayerType = 0x%08lx\n", dwPlayerType );
+  }
+
+  return TRUE;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_DestroyPlayer
+          ( LPDIRECTPLAY2A iface, DPID idPlayer )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_DestroyPlayer( This, NULL, idPlayer, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_DestroyPlayer
+          ( LPDIRECTPLAY2 iface, DPID idPlayer )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_DestroyPlayer( This, NULL, idPlayer, FALSE );
+}
+
+static HRESULT WINAPI DP_IF_EnumGroupPlayers
+          ( IDirectPlay2Impl* This, DPID idGroup, LPGUID lpguidInstance,
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2,
+            LPVOID lpContext, DWORD dwFlags, BOOL bAnsi )
+{
+  lpGroupData  lpGData;
+  lpPlayerList lpPList;
+
+  FIXME("(%p)->(0x%08lx,%p,%p,%p,0x%08lx,%u): semi stub\n",
+          This, idGroup, lpguidInstance, lpEnumPlayersCallback2,
+          lpContext, dwFlags, bAnsi );
+
+  /* Find the group */
+  if( ( lpGData = DP_FindAnyGroup( This, idGroup ) ) == NULL )
+  {
+    return DPERR_INVALIDGROUP;
+  }
+
+  if( DPQ_IS_EMPTY( lpGData->players ) )
+  {
+    return DP_OK;
+  }
+
+  lpPList = DPQ_FIRST( lpGData->players );
+
+  /* Walk the players in this group */
+  for( ;; )
+  {
+    /* We do not enum the name server or app server as they are of no
+     * concequence to the end user.
+     */
+    if( ( lpPList->lpPData->dpid != DPID_NAME_SERVER ) &&
+        ( lpPList->lpPData->dpid != DPID_SERVERPLAYER )
+      )
+    {
+
+      /* FIXME: Need to add stuff for dwFlags checking */
+
+      if( !lpEnumPlayersCallback2( lpPList->lpPData->dpid, DPPLAYERTYPE_PLAYER,
+                                   &lpPList->lpPData->name,
+                                   lpPList->lpPData->dwFlags,
+                                   lpContext )
+        )
+      {
+        /* User requested break */
+        return DP_OK;
+      }
+    }
+
+    if( DPQ_IS_ENDOFLIST( lpPList->players ) )
+    {
+      break;
+    }
+
+    lpPList = DPQ_NEXT( lpPList->players );
+  }
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_EnumGroupPlayers
+          ( LPDIRECTPLAY2A iface, DPID idGroup, LPGUID lpguidInstance,
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2,
+            LPVOID lpContext, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_EnumGroupPlayers( This, idGroup, lpguidInstance,
+                               lpEnumPlayersCallback2, lpContext,
+                               dwFlags, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_EnumGroupPlayers
+          ( LPDIRECTPLAY2 iface, DPID idGroup, LPGUID lpguidInstance,
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2,
+            LPVOID lpContext, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_EnumGroupPlayers( This, idGroup, lpguidInstance,
+                               lpEnumPlayersCallback2, lpContext,
+                               dwFlags, FALSE );
+}
+
+/* NOTE: This only enumerates top level groups (created with CreateGroup) */
+static HRESULT WINAPI DP_IF_EnumGroups
+          ( IDirectPlay2Impl* This, LPGUID lpguidInstance,
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2,
+            LPVOID lpContext, DWORD dwFlags, BOOL bAnsi )
+{
+  return DP_IF_EnumGroupsInGroup( (IDirectPlay3Impl*)This,
+                                  DPID_SYSTEM_GROUP, lpguidInstance,
+                                  lpEnumPlayersCallback2, lpContext,
+                                  dwFlags, bAnsi );
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_EnumGroups
+          ( LPDIRECTPLAY2A iface, LPGUID lpguidInstance,
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2,
+            LPVOID lpContext, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_EnumGroups( This, lpguidInstance, lpEnumPlayersCallback2,
+                         lpContext, dwFlags, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_EnumGroups
+          ( LPDIRECTPLAY2 iface, LPGUID lpguidInstance,
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2,
+            LPVOID lpContext, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_EnumGroups( This, lpguidInstance, lpEnumPlayersCallback2,
+                         lpContext, dwFlags, FALSE );
+}
+
+static HRESULT WINAPI DP_IF_EnumPlayers
+          ( IDirectPlay2Impl* This, LPGUID lpguidInstance,
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2,
+            LPVOID lpContext, DWORD dwFlags, BOOL bAnsi )
+{
+  return DP_IF_EnumGroupPlayers( This, DPID_SYSTEM_GROUP, lpguidInstance,
+                                 lpEnumPlayersCallback2, lpContext,
+                                 dwFlags, bAnsi );
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_EnumPlayers
+          ( LPDIRECTPLAY2A iface, LPGUID lpguidInstance,
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2,
+            LPVOID lpContext, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_EnumPlayers( This, lpguidInstance, lpEnumPlayersCallback2,
+                          lpContext, dwFlags, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_EnumPlayers
+          ( LPDIRECTPLAY2 iface, LPGUID lpguidInstance,
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2,
+            LPVOID lpContext, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_EnumPlayers( This, lpguidInstance, lpEnumPlayersCallback2,
+                          lpContext, dwFlags, FALSE );
+}
+
+/* This function should call the registered callback function that the user
+   passed into EnumSessions for each entry available.
+ */
+static void DP_InvokeEnumSessionCallbacks
+       ( LPDPENUMSESSIONSCALLBACK2 lpEnumSessionsCallback2,
+         LPVOID lpNSInfo,
+         DWORD dwTimeout,
+         LPVOID lpContext )
+{
+  LPDPSESSIONDESC2 lpSessionDesc;
+
+  FIXME( ": not checking for conditions\n" );
+
+  NS_ResetSessionEnumeration( lpNSInfo );
+
+  /* Enumerate all sessions */
+  /* FIXME: Need to indicate ANSI */
+  while( (lpSessionDesc = NS_WalkSessions( lpNSInfo ) ) != NULL )
+  {
+    TRACE( "EnumSessionsCallback2 invoked\n" );
+    if( !lpEnumSessionsCallback2( lpSessionDesc, &dwTimeout, 0, lpContext ) )
+    {
+      return;
+    }
+  }
+
+  /* Invoke one last time to indicate that there is no more to come */
+  lpEnumSessionsCallback2( NULL, &dwTimeout, DPESC_TIMEDOUT, lpContext );
+}
+
+static DWORD CALLBACK DP_EnumSessionsSendAsyncRequestThread( LPVOID lpContext )
+{
+  EnumSessionAsyncCallbackData* data = (EnumSessionAsyncCallbackData*)lpContext;
+  HANDLE hSuicideRequest = data->hSuicideRequest;
+  DWORD dwTimeout = data->dwTimeout;
+
+  TRACE( "Thread started with timeout = 0x%08lx\n", dwTimeout );
+
+  for( ;; )
+  {
+    HRESULT hr;
+
+    /* Sleep up to dwTimeout waiting for request to terminate thread */
+    if( WaitForSingleObject( hSuicideRequest, dwTimeout ) == WAIT_OBJECT_0 )
+    {
+      TRACE( "Thread terminating on terminate request\n" );
+      break;
+    }
+
+    /* Now resend the enum request */
+    hr = NS_SendSessionRequestBroadcast( &data->requestGuid,
+                                         data->dwEnumSessionFlags,
+                                         data->lpSpData );
+
+    if( FAILED(hr) )
+    {
+      ERR( "Enum broadcase request failed: %s\n", DPLAYX_HresultToString(hr) );
+      /* FIXME: Should we kill this thread? How to inform the main thread? */
+    }
+
+  }
+
+  TRACE( "Thread terminating\n" );
+
+  /* Clean up the thread data */
+  CloseHandle( hSuicideRequest );
+  HeapFree( GetProcessHeap(), 0, lpContext );
+
+  /* FIXME: Need to have some notification to main app thread that this is
+   *        dead. It would serve two purposes. 1) allow sync on termination
+   *        so that we don't actually send something to ourselves when we
+   *        become name server (race condition) and 2) so that if we die
+   *        abnormally something else will be able to tell.
+   */
+
+  return 1;
+}
+
+static void DP_KillEnumSessionThread( IDirectPlay2Impl* This )
+{
+  /* Does a thread exist? If so we were doing an async enum session */
+  if( This->dp2->hEnumSessionThread != INVALID_HANDLE_VALUE )
+  {
+    TRACE( "Killing EnumSession thread\n" );
+
+    /* Request that the thread kill itself nicely */
+    SetEvent( This->dp2->hKillEnumSessionThreadEvent );
+    CloseHandle( This->dp2->hKillEnumSessionThreadEvent );
+
+    /* We no longer need to know about the thread */
+    CloseHandle( This->dp2->hEnumSessionThread );
+
+    This->dp2->hEnumSessionThread = INVALID_HANDLE_VALUE;
+  }
+}
+
+static HRESULT WINAPI DP_IF_EnumSessions
+          ( IDirectPlay2Impl* This, LPDPSESSIONDESC2 lpsd, DWORD dwTimeout,
+            LPDPENUMSESSIONSCALLBACK2 lpEnumSessionsCallback2,
+            LPVOID lpContext, DWORD dwFlags, BOOL bAnsi )
+{
+  HRESULT hr = DP_OK;
+
+  TRACE( "(%p)->(%p,0x%08lx,%p,%p,0x%08lx,%u)\n",
+         This, lpsd, dwTimeout, lpEnumSessionsCallback2, lpContext, dwFlags,
+         bAnsi );
+
+  /* Can't enumerate if the interface is already open */
+  if( This->dp2->bConnectionOpen )
+  {
+    return DPERR_GENERIC;
+  }
+
+  /* Use the service provider default? */
+  if( dwTimeout == 0 )
+  {
+    DPCAPS spCaps;
+    spCaps.dwSize = sizeof( spCaps );
+
+    DP_IF_GetCaps( This, &spCaps, 0 );
+    dwTimeout = spCaps.dwTimeout;
+
+    /* FIXME: If it's still 0, we need to provide the IP default */
+  }
+
+  if( dwFlags & DPENUMSESSIONS_STOPASYNC )
+  {
+    DP_KillEnumSessionThread( This );
+    return hr;
+  }
+
+  /* FIXME: Interface locking sucks in this method */
+  if( ( dwFlags & DPENUMSESSIONS_ASYNC ) )
+  {
+    /* Enumerate everything presently in the local session cache */
+    DP_InvokeEnumSessionCallbacks( lpEnumSessionsCallback2,
+                                   This->dp2->lpNameServerData, dwTimeout,
+                                   lpContext );
+
+
+    /* See if we've already created a thread to service this interface */
+    if( This->dp2->hEnumSessionThread == INVALID_HANDLE_VALUE )
+    {
+      DWORD dwThreadId;
+
+      /* Send the first enum request inline since the user may cancel a dialog
+       * if one is presented. Also, may also have a connecting return code.
+       */
+      hr = NS_SendSessionRequestBroadcast( &lpsd->guidApplication,
+                                           dwFlags, &This->dp2->spData );
+
+      if( !FAILED(hr) )
+      {
+        EnumSessionAsyncCallbackData* lpData
+          = (EnumSessionAsyncCallbackData*)HeapAlloc( GetProcessHeap(),
+                                                      HEAP_ZERO_MEMORY,
+                                                       sizeof( *lpData ) );
+        /* FIXME: need to kill the thread on object deletion */
+        lpData->lpSpData  = &This->dp2->spData;
+        CopyMemory( &lpData->requestGuid, &lpsd->guidApplication, sizeof(GUID) );
+        lpData->dwEnumSessionFlags = dwFlags;
+        lpData->dwTimeout = dwTimeout;
+
+        This->dp2->hKillEnumSessionThreadEvent =
+          CreateEventA( NULL, TRUE, FALSE, NULL );
+
+        if( !DuplicateHandle( GetCurrentProcess(),
+                              This->dp2->hKillEnumSessionThreadEvent,
+                              GetCurrentProcess(),
+                              &lpData->hSuicideRequest,
+                              0, FALSE, DUPLICATE_SAME_ACCESS )
+          )
+        {
+          ERR( "Can't duplicate thread killing handle\n" );
+        }
+
+        TRACE( ": creating EnumSessionsRequest thread\n" );
+
+        This->dp2->hEnumSessionThread = CreateThread( NULL,
+                                                      0,
+                                                      DP_EnumSessionsSendAsyncRequestThread,
+                                                      lpData,
+                                                      0,
+                                                      &dwThreadId );
+      }
+    }
+  }
+  else
+  {
+    /* Invalidate the session cache for the interface */
+    NS_InvalidateSessionCache( This->dp2->lpNameServerData );
+
+    /* Send the broadcast for session enumeration */
+    hr = NS_SendSessionRequestBroadcast( &lpsd->guidApplication,
+                                         dwFlags,
+                                         &This->dp2->spData );
+
+
+    SleepEx( dwTimeout, FALSE );
+
+    DP_InvokeEnumSessionCallbacks( lpEnumSessionsCallback2,
+                                   This->dp2->lpNameServerData, dwTimeout,
+                                   lpContext );
+  }
+
+  return hr;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_EnumSessions
+          ( LPDIRECTPLAY2A iface, LPDPSESSIONDESC2 lpsd, DWORD dwTimeout,
+            LPDPENUMSESSIONSCALLBACK2 lpEnumSessionsCallback2,
+            LPVOID lpContext, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_EnumSessions( This, lpsd, dwTimeout, lpEnumSessionsCallback2,
+                             lpContext, dwFlags, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_EnumSessions
+          ( LPDIRECTPLAY2 iface, LPDPSESSIONDESC2 lpsd, DWORD dwTimeout,
+            LPDPENUMSESSIONSCALLBACK2 lpEnumSessionsCallback2,
+            LPVOID lpContext, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_EnumSessions( This, lpsd, dwTimeout, lpEnumSessionsCallback2,
+                             lpContext, dwFlags, FALSE );
+}
+
+static HRESULT WINAPI DP_IF_GetPlayerCaps
+          ( IDirectPlay2Impl* This, DPID idPlayer, LPDPCAPS lpDPCaps,
+            DWORD dwFlags )
+{
+  DPSP_GETCAPSDATA data;
+
+  TRACE("(%p)->(0x%08lx,%p,0x%08lx)\n", This, idPlayer, lpDPCaps, dwFlags);
+
+  /* Query the service provider */
+  data.idPlayer = idPlayer;
+  data.dwFlags  = dwFlags;
+  data.lpCaps   = lpDPCaps;
+  data.lpISP    = This->dp2->spData.lpISP;
+
+  return (*This->dp2->spData.lpCB->GetCaps)( &data );
+}
+
+static HRESULT WINAPI DP_IF_GetCaps
+          ( IDirectPlay2Impl* This, LPDPCAPS lpDPCaps, DWORD dwFlags )
+{
+  return DP_IF_GetPlayerCaps( This, DPID_ALLPLAYERS, lpDPCaps, dwFlags );
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_GetCaps
+          ( LPDIRECTPLAY2A iface, LPDPCAPS lpDPCaps, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_GetCaps( This, lpDPCaps, dwFlags );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_GetCaps
+          ( LPDIRECTPLAY2 iface, LPDPCAPS lpDPCaps, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_GetCaps( This, lpDPCaps, dwFlags );
+}
+
+static HRESULT WINAPI DP_IF_GetGroupData
+          ( IDirectPlay2Impl* This, DPID idGroup, LPVOID lpData,
+            LPDWORD lpdwDataSize, DWORD dwFlags, BOOL bAnsi )
+{
+  lpGroupData lpGData;
+  DWORD dwRequiredBufferSize;
+  LPVOID lpCopyDataFrom;
+
+  TRACE( "(%p)->(0x%08lx,%p,%p,0x%08lx,%u)\n",
+         This, idGroup, lpData, lpdwDataSize, dwFlags, bAnsi );
+
+  if( ( lpGData = DP_FindAnyGroup( This, idGroup ) ) == NULL )
+  {
+    return DPERR_INVALIDGROUP;
+  }
+
+  /* How much buffer is required? */
+  if( dwFlags & DPSET_REMOTE )
+  {
+    dwRequiredBufferSize = lpGData->dwRemoteDataSize;
+    lpCopyDataFrom       = lpGData->lpRemoteData;
+  }
+  else if( dwFlags & DPSET_LOCAL )
+  {
+    dwRequiredBufferSize = lpGData->dwLocalDataSize;
+    lpCopyDataFrom       = lpGData->lpLocalData;
+  }
+  else
+  {
+    ERR( "Neither local or remote data requested!?!\n" );
+    dwRequiredBufferSize = 0;
+    lpCopyDataFrom = NULL;
+  }
+
+  /* Is the user requesting to know how big a buffer is required? */
+  if( ( lpData == NULL ) ||
+      ( *lpdwDataSize < dwRequiredBufferSize )
+    )
+  {
+    *lpdwDataSize = dwRequiredBufferSize;
+    return DPERR_BUFFERTOOSMALL;
+  }
+
+  CopyMemory( lpData, lpCopyDataFrom, dwRequiredBufferSize );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_GetGroupData
+          ( LPDIRECTPLAY2A iface, DPID idGroup, LPVOID lpData,
+            LPDWORD lpdwDataSize, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_GetGroupData( This, idGroup, lpData, lpdwDataSize,
+                           dwFlags, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_GetGroupData
+          ( LPDIRECTPLAY2 iface, DPID idGroup, LPVOID lpData,
+            LPDWORD lpdwDataSize, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_GetGroupData( This, idGroup, lpData, lpdwDataSize,
+                           dwFlags, FALSE );
+}
+
+static HRESULT WINAPI DP_IF_GetGroupName
+          ( IDirectPlay2Impl* This, DPID idGroup, LPVOID lpData,
+            LPDWORD lpdwDataSize, BOOL bAnsi )
+{
+  lpGroupData lpGData;
+  LPDPNAME    lpName = (LPDPNAME)lpData;
+  DWORD       dwRequiredDataSize;
+
+  FIXME("(%p)->(0x%08lx,%p,%p,%u) ANSI ignored\n",
+          This, idGroup, lpData, lpdwDataSize, bAnsi );
+
+  if( ( lpGData = DP_FindAnyGroup( This, idGroup ) ) == NULL )
+  {
+    return DPERR_INVALIDGROUP;
+  }
+
+  dwRequiredDataSize = lpGData->name.dwSize;
+
+  if( lpGData->name.psn.lpszShortNameA )
+  {
+    dwRequiredDataSize += strlen( lpGData->name.psn.lpszShortNameA ) + 1;
+  }
+
+  if( lpGData->name.pln.lpszLongNameA )
+  {
+    dwRequiredDataSize += strlen( lpGData->name.pln.lpszLongNameA ) + 1;
+  }
+
+  if( ( lpData == NULL ) ||
+      ( *lpdwDataSize < dwRequiredDataSize )
+    )
+  {
+    *lpdwDataSize = dwRequiredDataSize;
+    return DPERR_BUFFERTOOSMALL;
+  }
+
+  /* Copy the structure */
+  CopyMemory( lpName, &lpGData->name, lpGData->name.dwSize );
+
+  if( lpGData->name.psn.lpszShortNameA )
+  {
+    strcpy( ((char*)lpName)+lpGData->name.dwSize,
+            lpGData->name.psn.lpszShortNameA );
+  }
+  else
+  {
+    lpName->psn.lpszShortNameA = NULL;
+  }
+
+  if( lpGData->name.psn.lpszShortNameA )
+  {
+    strcpy( ((char*)lpName)+lpGData->name.dwSize,
+            lpGData->name.pln.lpszLongNameA );
+  }
+  else
+  {
+    lpName->pln.lpszLongNameA = NULL;
+  }
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_GetGroupName
+          ( LPDIRECTPLAY2A iface, DPID idGroup, LPVOID lpData,
+            LPDWORD lpdwDataSize )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_GetGroupName( This, idGroup, lpData, lpdwDataSize, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_GetGroupName
+          ( LPDIRECTPLAY2 iface, DPID idGroup, LPVOID lpData,
+            LPDWORD lpdwDataSize )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_GetGroupName( This, idGroup, lpData, lpdwDataSize, FALSE );
+}
+
+static HRESULT WINAPI DP_IF_GetMessageCount
+          ( IDirectPlay2Impl* This, DPID idPlayer,
+            LPDWORD lpdwCount, BOOL bAnsi )
+{
+  FIXME("(%p)->(0x%08lx,%p,%u): stub\n", This, idPlayer, lpdwCount, bAnsi );
+  return DP_IF_GetMessageQueue( (IDirectPlay4Impl*)This, 0, idPlayer,
+                                DPMESSAGEQUEUE_RECEIVE, lpdwCount, NULL,
+                                bAnsi );
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_GetMessageCount
+          ( LPDIRECTPLAY2A iface, DPID idPlayer, LPDWORD lpdwCount )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_GetMessageCount( This, idPlayer, lpdwCount, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_GetMessageCount
+          ( LPDIRECTPLAY2 iface, DPID idPlayer, LPDWORD lpdwCount )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_GetMessageCount( This, idPlayer, lpdwCount, FALSE );
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_GetPlayerAddress
+          ( LPDIRECTPLAY2A iface, DPID idPlayer, LPVOID lpData, LPDWORD lpdwDataSize )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  FIXME("(%p)->(0x%08lx,%p,%p): stub\n", This, idPlayer, lpData, lpdwDataSize );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_GetPlayerAddress
+          ( LPDIRECTPLAY2 iface, DPID idPlayer, LPVOID lpData, LPDWORD lpdwDataSize )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  FIXME("(%p)->(0x%08lx,%p,%p): stub\n", This, idPlayer, lpData, lpdwDataSize );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_GetPlayerCaps
+          ( LPDIRECTPLAY2A iface, DPID idPlayer, LPDPCAPS lpPlayerCaps,
+            DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_GetPlayerCaps( This, idPlayer, lpPlayerCaps, dwFlags );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_GetPlayerCaps
+          ( LPDIRECTPLAY2 iface, DPID idPlayer, LPDPCAPS lpPlayerCaps,
+            DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_GetPlayerCaps( This, idPlayer, lpPlayerCaps, dwFlags );
+}
+
+static HRESULT WINAPI DP_IF_GetPlayerData
+          ( IDirectPlay2Impl* This, DPID idPlayer, LPVOID lpData,
+            LPDWORD lpdwDataSize, DWORD dwFlags, BOOL bAnsi )
+{
+  lpPlayerList lpPList;
+  DWORD dwRequiredBufferSize;
+  LPVOID lpCopyDataFrom;
+
+  TRACE( "(%p)->(0x%08lx,%p,%p,0x%08lx,%u)\n",
+         This, idPlayer, lpData, lpdwDataSize, dwFlags, bAnsi );
+
+  if( ( lpPList = DP_FindPlayer( This, idPlayer ) ) == NULL )
+  {
+    return DPERR_INVALIDPLAYER;
+  }
+
+  /* How much buffer is required? */
+  if( dwFlags & DPSET_REMOTE )
+  {
+    dwRequiredBufferSize = lpPList->lpPData->dwRemoteDataSize;
+    lpCopyDataFrom       = lpPList->lpPData->lpRemoteData;
+  }
+  else if( dwFlags & DPSET_LOCAL )
+  {
+    dwRequiredBufferSize = lpPList->lpPData->dwLocalDataSize;
+    lpCopyDataFrom       = lpPList->lpPData->lpLocalData;
+  }
+  else
+  {
+    ERR( "Neither local or remote data requested!?!\n" );
+    dwRequiredBufferSize = 0;
+    lpCopyDataFrom = NULL;
+  }
+
+  /* Is the user requesting to know how big a buffer is required? */
+  if( ( lpData == NULL ) ||
+      ( *lpdwDataSize < dwRequiredBufferSize )
+    )
+  {
+    *lpdwDataSize = dwRequiredBufferSize;
+    return DPERR_BUFFERTOOSMALL;
+  }
+
+  CopyMemory( lpData, lpCopyDataFrom, dwRequiredBufferSize );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_GetPlayerData
+          ( LPDIRECTPLAY2A iface, DPID idPlayer, LPVOID lpData,
+            LPDWORD lpdwDataSize, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_GetPlayerData( This, idPlayer, lpData, lpdwDataSize,
+                            dwFlags, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_GetPlayerData
+          ( LPDIRECTPLAY2 iface, DPID idPlayer, LPVOID lpData,
+            LPDWORD lpdwDataSize, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_GetPlayerData( This, idPlayer, lpData, lpdwDataSize,
+                            dwFlags, FALSE );
+}
+
+static HRESULT WINAPI DP_IF_GetPlayerName
+          ( IDirectPlay2Impl* This, DPID idPlayer, LPVOID lpData,
+            LPDWORD lpdwDataSize, BOOL bAnsi )
+{
+  lpPlayerList lpPList;
+  LPDPNAME    lpName = (LPDPNAME)lpData;
+  DWORD       dwRequiredDataSize;
+
+  FIXME( "(%p)->(0x%08lx,%p,%p,%u): ANSI \n",
+         This, idPlayer, lpData, lpdwDataSize, bAnsi );
+
+  if( ( lpPList = DP_FindPlayer( This, idPlayer ) ) == NULL )
+  {
+    return DPERR_INVALIDPLAYER;
+  }
+
+  dwRequiredDataSize = lpPList->lpPData->name.dwSize;
+
+  if( lpPList->lpPData->name.psn.lpszShortNameA )
+  {
+    dwRequiredDataSize += strlen( lpPList->lpPData->name.psn.lpszShortNameA ) + 1;
+  }
+
+  if( lpPList->lpPData->name.pln.lpszLongNameA )
+  {
+    dwRequiredDataSize += strlen( lpPList->lpPData->name.pln.lpszLongNameA ) + 1;
+  }
+
+  if( ( lpData == NULL ) ||
+      ( *lpdwDataSize < dwRequiredDataSize )
+    )
+  {
+    *lpdwDataSize = dwRequiredDataSize;
+    return DPERR_BUFFERTOOSMALL;
+  }
+
+  /* Copy the structure */
+  CopyMemory( lpName, &lpPList->lpPData->name, lpPList->lpPData->name.dwSize );
+
+  if( lpPList->lpPData->name.psn.lpszShortNameA )
+  {
+    strcpy( ((char*)lpName)+lpPList->lpPData->name.dwSize,
+            lpPList->lpPData->name.psn.lpszShortNameA );
+  }
+  else
+  {
+    lpName->psn.lpszShortNameA = NULL;
+  }
+
+  if( lpPList->lpPData->name.psn.lpszShortNameA )
+  {
+    strcpy( ((char*)lpName)+lpPList->lpPData->name.dwSize,
+            lpPList->lpPData->name.pln.lpszLongNameA );
+  }
+  else
+  {
+    lpName->pln.lpszLongNameA = NULL;
+  }
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_GetPlayerName
+          ( LPDIRECTPLAY2A iface, DPID idPlayer, LPVOID lpData,
+            LPDWORD lpdwDataSize )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_GetPlayerName( This, idPlayer, lpData, lpdwDataSize, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_GetPlayerName
+          ( LPDIRECTPLAY2 iface, DPID idPlayer, LPVOID lpData,
+            LPDWORD lpdwDataSize )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_GetPlayerName( This, idPlayer, lpData, lpdwDataSize, FALSE );
+}
+
+static HRESULT WINAPI DP_GetSessionDesc
+          ( IDirectPlay2Impl* This, LPVOID lpData, LPDWORD lpdwDataSize,
+            BOOL bAnsi )
+{
+  DWORD dwRequiredSize;
+
+  TRACE( "(%p)->(%p,%p,%u)\n", This, lpData, lpdwDataSize, bAnsi );
+
+  if( ( lpData == NULL ) && ( lpdwDataSize == NULL ) )
+  {
+    return DPERR_INVALIDPARAMS;
+  }
+
+  /* FIXME: Get from This->dp2->lpSessionDesc */
+  dwRequiredSize = DP_CalcSessionDescSize( This->dp2->lpSessionDesc, bAnsi );
+
+  if ( ( lpData == NULL ) ||
+       ( *lpdwDataSize < dwRequiredSize )
+     )
+  {
+    *lpdwDataSize = dwRequiredSize;
+    return DPERR_BUFFERTOOSMALL;
+  }
+
+  DP_CopySessionDesc( (LPDPSESSIONDESC2)lpData, This->dp2->lpSessionDesc, bAnsi );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_GetSessionDesc
+          ( LPDIRECTPLAY2A iface, LPVOID lpData, LPDWORD lpdwDataSize )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_GetSessionDesc( This, lpData, lpdwDataSize, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_GetSessionDesc
+          ( LPDIRECTPLAY2 iface, LPVOID lpData, LPDWORD lpdwDataSize )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_GetSessionDesc( This, lpData, lpdwDataSize, TRUE );
+}
+
+/* Intended only for COM compatibility. Always returns an error. */
+static HRESULT WINAPI DirectPlay2AImpl_Initialize
+          ( LPDIRECTPLAY2A iface, LPGUID lpGUID )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  TRACE("(%p)->(%p): stub\n", This, lpGUID );
+  return DPERR_ALREADYINITIALIZED;
+}
+
+/* Intended only for COM compatibility. Always returns an error. */
+static HRESULT WINAPI DirectPlay2WImpl_Initialize
+          ( LPDIRECTPLAY2 iface, LPGUID lpGUID )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  TRACE("(%p)->(%p): stub\n", This, lpGUID );
+  return DPERR_ALREADYINITIALIZED;
+}
+
+
+static HRESULT WINAPI DP_SecureOpen
+          ( IDirectPlay2Impl* This, LPCDPSESSIONDESC2 lpsd, DWORD dwFlags,
+            LPCDPSECURITYDESC lpSecurity, LPCDPCREDENTIALS lpCredentials,
+            BOOL bAnsi )
+{
+  HRESULT hr = DP_OK;
+
+  FIXME( "(%p)->(%p,0x%08lx,%p,%p): partial stub\n",
+         This, lpsd, dwFlags, lpSecurity, lpCredentials );
+
+  if( This->dp2->bConnectionOpen )
+  {
+    TRACE( ": rejecting already open connection.\n" );
+    return DPERR_ALREADYINITIALIZED;
+  }
+
+  /* If we're enumerating, kill the thread */
+  DP_KillEnumSessionThread( This );
+
+  if( dwFlags & DPOPEN_CREATE )
+  {
+    /* Rightoo - this computer is the host and the local computer needs to be
+       the name server so that others can join this session */
+    NS_SetLocalComputerAsNameServer( lpsd );
+
+    This->dp2->bHostInterface = TRUE;
+
+    hr = DP_SetSessionDesc( This, lpsd, 0, TRUE, bAnsi );
+    if( FAILED( hr ) )
+    {
+      ERR( "Unable to set session desc: %s\n", DPLAYX_HresultToString( hr ) );
+      return hr;
+    }
+  }
+
+  /* Invoke the conditional callback for the service provider */
+  if( This->dp2->spData.lpCB->Open )
+  {
+    DPSP_OPENDATA data;
+
+    FIXME( "Not all data fields are correct. Need new parameter\n" );
+
+    data.bCreate           = (dwFlags & DPOPEN_CREATE ) ? TRUE : FALSE;
+    data.lpSPMessageHeader = (dwFlags & DPOPEN_CREATE ) ? NULL
+                                                        : NS_GetNSAddr( This->dp2->lpNameServerData );
+    data.lpISP             = This->dp2->spData.lpISP;
+    data.bReturnStatus     = (dwFlags & DPOPEN_RETURNSTATUS) ? TRUE : FALSE;
+    data.dwOpenFlags       = dwFlags;
+    data.dwSessionFlags    = This->dp2->lpSessionDesc->dwFlags;
+
+    hr = (*This->dp2->spData.lpCB->Open)(&data);
+    if( FAILED( hr ) )
+    {
+      ERR( "Unable to open session: %s\n", DPLAYX_HresultToString( hr ) );
+      return hr;
+    }
+  }
+
+  {
+    /* Create the system group of which everything is a part of */
+    DPID systemGroup = DPID_SYSTEM_GROUP;
+
+    hr = DP_IF_CreateGroup( This, NULL, &systemGroup, NULL,
+                            NULL, 0, 0, TRUE );
+
+  }
+
+  if( dwFlags & DPOPEN_JOIN )
+  {
+    DPID dpidServerId = DPID_SERVERPLAYER;
+
+    /* Create the server player for this interface. This way we can receive
+     * messages for this session.
+     */
+    /* FIXME: I suppose that we should be setting an event for a receive
+     *        type of thing. That way the messaging thread could know to wake
+     *        up. DPlay would then trigger the hEvent for the player the
+     *        message is directed to.
+     */
+    hr = DP_IF_CreatePlayer( This, NULL, &dpidServerId, NULL, 0, NULL,
+                             0,
+                             DPPLAYER_SERVERPLAYER | DPPLAYER_LOCAL , bAnsi );
+  }
+  else if( dwFlags & DPOPEN_CREATE )
+  {
+    DPID dpidNameServerId = DPID_NAME_SERVER;
+
+    hr = DP_IF_CreatePlayer( This, NULL, &dpidNameServerId, NULL, 0, NULL,
+                             0, DPPLAYER_SERVERPLAYER, bAnsi );
+  }
+
+  return hr;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_Open
+          ( LPDIRECTPLAY2A iface, LPDPSESSIONDESC2 lpsd, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  TRACE("(%p)->(%p,0x%08lx)\n", This, lpsd, dwFlags );
+  return DP_SecureOpen( This, lpsd, dwFlags, NULL, NULL, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_Open
+          ( LPDIRECTPLAY2 iface, LPDPSESSIONDESC2 lpsd, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  TRACE("(%p)->(%p,0x%08lx)\n", This, lpsd, dwFlags );
+  return DP_SecureOpen( This, lpsd, dwFlags, NULL, NULL, FALSE );
+}
+
+static HRESULT WINAPI DP_IF_Receive
+          ( IDirectPlay2Impl* This, LPDPID lpidFrom, LPDPID lpidTo,
+            DWORD dwFlags, LPVOID lpData, LPDWORD lpdwDataSize, BOOL bAnsi )
+{
+  LPDPMSG lpMsg = NULL;
+
+  FIXME( "(%p)->(%p,%p,0x%08lx,%p,%p,%u): stub\n",
+         This, lpidFrom, lpidTo, dwFlags, lpData, lpdwDataSize, bAnsi );
+
+  if( dwFlags == 0 )
+  {
+    dwFlags = DPRECEIVE_ALL;
+  }
+
+  /* If the lpData is NULL, we must be peeking the message */
+  if(  ( lpData == NULL ) &&
+      !( dwFlags & DPRECEIVE_PEEK )
+    )
+  {
+    return DPERR_INVALIDPARAMS;
+  }
+
+  if( dwFlags & DPRECEIVE_ALL )
+  {
+    lpMsg = This->dp2->receiveMsgs.lpQHFirst;
+
+    if( !( dwFlags & DPRECEIVE_PEEK ) )
+    {
+      FIXME( "Remove from queue\n" );
+    }
+  }
+  else if( ( dwFlags & DPRECEIVE_TOPLAYER ) ||
+           ( dwFlags & DPRECEIVE_FROMPLAYER )
+         )
+  {
+    FIXME( "Find matching message 0x%08lx\n", dwFlags );
+  }
+  else
+  {
+    ERR( "Hmmm..dwFlags 0x%08lx\n", dwFlags );
+  }
+
+  if( lpMsg == NULL )
+  {
+    return DPERR_NOMESSAGES;
+  }
+
+  /* Copy into the provided buffer */
+  CopyMemory( lpData, lpMsg->msg, *lpdwDataSize );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_Receive
+          ( LPDIRECTPLAY2A iface, LPDPID lpidFrom, LPDPID lpidTo,
+            DWORD dwFlags, LPVOID lpData, LPDWORD lpdwDataSize )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_Receive( This, lpidFrom, lpidTo, dwFlags,
+                        lpData, lpdwDataSize, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_Receive
+          ( LPDIRECTPLAY2 iface, LPDPID lpidFrom, LPDPID lpidTo,
+            DWORD dwFlags, LPVOID lpData, LPDWORD lpdwDataSize )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_Receive( This, lpidFrom, lpidTo, dwFlags,
+                        lpData, lpdwDataSize, FALSE );
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_Send
+          ( LPDIRECTPLAY2A iface, DPID idFrom, DPID idTo, DWORD dwFlags, LPVOID lpData, DWORD dwDataSize )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_SendEx( This, idFrom, idTo, dwFlags, lpData, dwDataSize,
+                    0, 0, NULL, NULL, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_Send
+          ( LPDIRECTPLAY2 iface, DPID idFrom, DPID idTo, DWORD dwFlags, LPVOID lpData, DWORD dwDataSize )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_SendEx( This, idFrom, idTo, dwFlags, lpData, dwDataSize,
+                    0, 0, NULL, NULL, FALSE );
+}
+
+static HRESULT WINAPI DP_IF_SetGroupData
+          ( IDirectPlay2Impl* This, DPID idGroup, LPVOID lpData,
+            DWORD dwDataSize, DWORD dwFlags, BOOL bAnsi )
+{
+  lpGroupData lpGData;
+
+  TRACE( "(%p)->(0x%08lx,%p,0x%08lx,0x%08lx,%u)\n",
+         This, idGroup, lpData, dwDataSize, dwFlags, bAnsi );
+
+  /* Parameter check */
+  if( ( lpData == NULL ) &&
+      ( dwDataSize != 0 )
+    )
+  {
+    return DPERR_INVALIDPARAMS;
+  }
+
+  /* Find the pointer to the data for this player */
+  if( ( lpGData = DP_FindAnyGroup( This, idGroup ) ) == NULL )
+  {
+    return DPERR_INVALIDOBJECT;
+  }
+
+  if( dwFlags & DPSET_REMOTE )
+  {
+    FIXME( "Was this group created by this interface?\n" );
+    /* FIXME: If this is a remote update need to allow it but not
+     *        send a message.
+     */
+  }
+
+  DP_SetGroupData( lpGData, dwFlags, lpData, dwDataSize );
+
+  /* FIXME: Only send a message if this group is local to the session otherwise
+   * it will have been rejected above
+   */
+  if( dwFlags & DPSET_REMOTE )
+  {
+    FIXME( "Send msg?\n" );
+  }
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_SetGroupData
+          ( LPDIRECTPLAY2A iface, DPID idGroup, LPVOID lpData,
+            DWORD dwDataSize, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_SetGroupData( This, idGroup, lpData, dwDataSize, dwFlags, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_SetGroupData
+          ( LPDIRECTPLAY2 iface, DPID idGroup, LPVOID lpData,
+            DWORD dwDataSize, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_SetGroupData( This, idGroup, lpData, dwDataSize, dwFlags, FALSE );
+}
+
+static HRESULT WINAPI DP_IF_SetGroupName
+          ( IDirectPlay2Impl* This, DPID idGroup, LPDPNAME lpGroupName,
+            DWORD dwFlags, BOOL bAnsi )
+{
+  lpGroupData lpGData;
+
+  TRACE( "(%p)->(0x%08lx,%p,0x%08lx,%u)\n", This, idGroup,
+         lpGroupName, dwFlags, bAnsi );
+
+  if( ( lpGData = DP_FindAnyGroup( This, idGroup ) ) == NULL )
+  {
+    return DPERR_INVALIDGROUP;
+  }
+
+  DP_CopyDPNAMEStruct( &lpGData->name, lpGroupName, bAnsi );
+
+  /* Should send a DPMSG_SETPLAYERORGROUPNAME message */
+  FIXME( "Message not sent and dwFlags ignored\n" );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_SetGroupName
+          ( LPDIRECTPLAY2A iface, DPID idGroup, LPDPNAME lpGroupName,
+            DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_SetGroupName( This, idGroup, lpGroupName, dwFlags, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_SetGroupName
+          ( LPDIRECTPLAY2 iface, DPID idGroup, LPDPNAME lpGroupName,
+            DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_SetGroupName( This, idGroup, lpGroupName, dwFlags, FALSE );
+}
+
+static HRESULT WINAPI DP_IF_SetPlayerData
+          ( IDirectPlay2Impl* This, DPID idPlayer, LPVOID lpData,
+            DWORD dwDataSize, DWORD dwFlags, BOOL bAnsi )
+{
+  lpPlayerList lpPList;
+
+  TRACE( "(%p)->(0x%08lx,%p,0x%08lx,0x%08lx,%u)\n",
+         This, idPlayer, lpData, dwDataSize, dwFlags, bAnsi );
+
+  /* Parameter check */
+  if( ( lpData == NULL ) &&
+      ( dwDataSize != 0 )
+    )
+  {
+    return DPERR_INVALIDPARAMS;
+  }
+
+  /* Find the pointer to the data for this player */
+  if( ( lpPList = DP_FindPlayer( This, idPlayer ) ) == NULL )
+  {
+    return DPERR_INVALIDPLAYER;
+  }
+
+  if( dwFlags & DPSET_REMOTE )
+  {
+    FIXME( "Was this group created by this interface?\n" );
+    /* FIXME: If this is a remote update need to allow it but not
+     *        send a message.
+     */
+  }
+
+  DP_SetPlayerData( lpPList->lpPData, dwFlags, lpData, dwDataSize );
+
+  if( dwFlags & DPSET_REMOTE )
+  {
+    FIXME( "Send msg?\n" );
+  }
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_SetPlayerData
+          ( LPDIRECTPLAY2A iface, DPID idPlayer, LPVOID lpData,
+            DWORD dwDataSize, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_SetPlayerData( This, idPlayer, lpData, dwDataSize,
+                              dwFlags, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_SetPlayerData
+          ( LPDIRECTPLAY2 iface, DPID idPlayer, LPVOID lpData,
+            DWORD dwDataSize, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_SetPlayerData( This, idPlayer, lpData, dwDataSize,
+                              dwFlags, FALSE );
+}
+
+static HRESULT WINAPI DP_IF_SetPlayerName
+          ( IDirectPlay2Impl* This, DPID idPlayer, LPDPNAME lpPlayerName,
+            DWORD dwFlags, BOOL bAnsi )
+{
+  lpPlayerList lpPList;
+
+  TRACE( "(%p)->(0x%08lx,%p,0x%08lx,%u)\n",
+         This, idPlayer, lpPlayerName, dwFlags, bAnsi );
+
+  if( ( lpPList = DP_FindPlayer( This, idPlayer ) ) == NULL )
+  {
+    return DPERR_INVALIDGROUP;
+  }
+
+  DP_CopyDPNAMEStruct( &lpPList->lpPData->name, lpPlayerName, bAnsi );
+
+  /* Should send a DPMSG_SETPLAYERORGROUPNAME message */
+  FIXME( "Message not sent and dwFlags ignored\n" );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_SetPlayerName
+          ( LPDIRECTPLAY2A iface, DPID idPlayer, LPDPNAME lpPlayerName,
+            DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_SetPlayerName( This, idPlayer, lpPlayerName, dwFlags, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_SetPlayerName
+          ( LPDIRECTPLAY2 iface, DPID idPlayer, LPDPNAME lpPlayerName,
+            DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_IF_SetPlayerName( This, idPlayer, lpPlayerName, dwFlags, FALSE );
+}
+
+static HRESULT WINAPI DP_SetSessionDesc
+          ( IDirectPlay2Impl* This, LPCDPSESSIONDESC2 lpSessDesc,
+            DWORD dwFlags, BOOL bInitial, BOOL bAnsi  )
+{
+  DWORD            dwRequiredSize;
+  LPDPSESSIONDESC2 lpTempSessDesc;
+
+  TRACE( "(%p)->(%p,0x%08lx,%u,%u)\n",
+         This, lpSessDesc, dwFlags, bInitial, bAnsi );
+
+  if( dwFlags )
+  {
+    return DPERR_INVALIDPARAMS;
+  }
+
+  /* Only the host is allowed to update the session desc */
+  if( !This->dp2->bHostInterface )
+  {
+    return DPERR_ACCESSDENIED;
+  }
+
+  /* FIXME: Copy into This->dp2->lpSessionDesc */
+  dwRequiredSize = DP_CalcSessionDescSize( lpSessDesc, bAnsi );
+  lpTempSessDesc = (LPDPSESSIONDESC2)HeapAlloc( GetProcessHeap(),
+                                                HEAP_ZERO_MEMORY,
+                                                dwRequiredSize );
+
+  if( lpTempSessDesc == NULL )
+  {
+    return DPERR_OUTOFMEMORY;
+  }
+
+  /* Free the old */
+  HeapFree( GetProcessHeap(), 0, This->dp2->lpSessionDesc );
+
+  This->dp2->lpSessionDesc = lpTempSessDesc;
+
+  /* Set the new */
+  DP_CopySessionDesc( This->dp2->lpSessionDesc, lpSessDesc, bAnsi );
+
+  /* If this is an external invocation of the interface, we should be
+   * letting everyone know that things have changed. Otherwise this is
+   * just an initialization and it doesn't need to be propagated.
+   */
+  if( !bInitial )
+  {
+    FIXME( "Need to send a DPMSG_SETSESSIONDESC msg to everyone\n" );
+  }
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay2AImpl_SetSessionDesc
+          ( LPDIRECTPLAY2A iface, LPDPSESSIONDESC2 lpSessDesc, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_SetSessionDesc( This, lpSessDesc, dwFlags, FALSE, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay2WImpl_SetSessionDesc
+          ( LPDIRECTPLAY2 iface, LPDPSESSIONDESC2 lpSessDesc, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface);
+  return DP_SetSessionDesc( This, lpSessDesc, dwFlags, FALSE, TRUE );
+}
+
+/* FIXME: See about merging some of this stuff with dplayx_global.c stuff */
+DWORD DP_CalcSessionDescSize( LPCDPSESSIONDESC2 lpSessDesc, BOOL bAnsi )
+{
+  DWORD dwSize = 0;
+
+  if( lpSessDesc == NULL )
+  {
+    /* Hmmm..don't need any size? */
+    ERR( "NULL lpSessDesc\n" );
+    return dwSize;
+  }
+
+  dwSize += sizeof( *lpSessDesc );
+
+  if( bAnsi )
+  {
+    if( lpSessDesc->sess.lpszSessionNameA )
+    {
+      dwSize += lstrlenA( lpSessDesc->sess.lpszSessionNameA ) + 1;
+    }
+
+    if( lpSessDesc->pass.lpszPasswordA )
+    {
+      dwSize += lstrlenA( lpSessDesc->pass.lpszPasswordA ) + 1;
+    }
+  }
+  else /* UNICODE */
+  {
+    if( lpSessDesc->sess.lpszSessionName )
+    {
+      dwSize += sizeof( WCHAR ) *
+        ( lstrlenW( lpSessDesc->sess.lpszSessionName ) + 1 );
+    }
+
+    if( lpSessDesc->pass.lpszPassword )
+    {
+      dwSize += sizeof( WCHAR ) *
+        ( lstrlenW( lpSessDesc->pass.lpszPassword ) + 1 );
+    }
+  }
+
+  return dwSize;
+}
+
+/* Assumes that contugous buffers are already allocated. */
+static void DP_CopySessionDesc( LPDPSESSIONDESC2 lpSessionDest,
+                                LPCDPSESSIONDESC2 lpSessionSrc, BOOL bAnsi )
+{
+  BYTE* lpStartOfFreeSpace;
+
+  if( lpSessionDest == NULL )
+  {
+    ERR( "NULL lpSessionDest\n" );
+    return;
+  }
+
+  CopyMemory( lpSessionDest, lpSessionSrc, sizeof( *lpSessionSrc ) );
+
+  lpStartOfFreeSpace = ((BYTE*)lpSessionDest) + sizeof( *lpSessionSrc );
+
+  if( bAnsi )
+  {
+    if( lpSessionSrc->sess.lpszSessionNameA )
+    {
+      lstrcpyA( (LPSTR)lpStartOfFreeSpace,
+                lpSessionDest->sess.lpszSessionNameA );
+      lpSessionDest->sess.lpszSessionNameA = (LPSTR)lpStartOfFreeSpace;
+      lpStartOfFreeSpace +=
+        lstrlenA( (LPSTR)lpSessionDest->sess.lpszSessionNameA ) + 1;
+    }
+
+    if( lpSessionSrc->pass.lpszPasswordA )
+    {
+      lstrcpyA( (LPSTR)lpStartOfFreeSpace,
+                lpSessionDest->pass.lpszPasswordA );
+      lpSessionDest->pass.lpszPasswordA = (LPSTR)lpStartOfFreeSpace;
+      lpStartOfFreeSpace +=
+        lstrlenA( (LPSTR)lpSessionDest->pass.lpszPasswordA ) + 1;
+    }
+  }
+  else /* UNICODE */
+  {
+    if( lpSessionSrc->sess.lpszSessionName )
+    {
+      lstrcpyW( (LPWSTR)lpStartOfFreeSpace,
+                lpSessionDest->sess.lpszSessionName );
+      lpSessionDest->sess.lpszSessionName = (LPWSTR)lpStartOfFreeSpace;
+      lpStartOfFreeSpace += sizeof(WCHAR) *
+        ( lstrlenW( (LPWSTR)lpSessionDest->sess.lpszSessionName ) + 1 );
+    }
+
+    if( lpSessionSrc->pass.lpszPassword )
+    {
+      lstrcpyW( (LPWSTR)lpStartOfFreeSpace,
+                lpSessionDest->pass.lpszPassword );
+      lpSessionDest->pass.lpszPassword = (LPWSTR)lpStartOfFreeSpace;
+      lpStartOfFreeSpace += sizeof(WCHAR) *
+        ( lstrlenW( (LPWSTR)lpSessionDest->pass.lpszPassword ) + 1 );
+    }
+  }
+}
+
+
+static HRESULT WINAPI DP_IF_AddGroupToGroup
+          ( IDirectPlay3Impl* This, DPID idParentGroup, DPID idGroup )
+{
+  lpGroupData lpGParentData;
+  lpGroupData lpGData;
+  lpGroupList lpNewGList;
+
+  TRACE( "(%p)->(0x%08lx,0x%08lx)\n", This, idParentGroup, idGroup );
+
+  if( ( lpGParentData = DP_FindAnyGroup( (IDirectPlay2AImpl*)This, idParentGroup ) ) == NULL )
+  {
+    return DPERR_INVALIDGROUP;
+  }
+
+  if( ( lpGData = DP_FindAnyGroup( (IDirectPlay2AImpl*)This, idGroup ) ) == NULL )
+  {
+    return DPERR_INVALIDGROUP;
+  }
+
+  /* Create a player list (ie "shortcut" ) */
+  lpNewGList = (lpGroupList)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                       sizeof( *lpNewGList ) );
+  if( lpNewGList == NULL )
+  {
+    return DPERR_CANTADDPLAYER;
+  }
+
+  /* Add the shortcut */
+  lpGData->uRef++;
+  lpNewGList->lpGData = lpGData;
+
+  /* Add the player to the list of players for this group */
+  DPQ_INSERT( lpGData->groups, lpNewGList, groups );
+
+  /* Send a ADDGROUPTOGROUP message */
+  FIXME( "Not sending message\n" );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay3AImpl_AddGroupToGroup
+          ( LPDIRECTPLAY3A iface, DPID idParentGroup, DPID idGroup )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  return DP_IF_AddGroupToGroup( This, idParentGroup, idGroup );
+}
+
+static HRESULT WINAPI DirectPlay3WImpl_AddGroupToGroup
+          ( LPDIRECTPLAY3 iface, DPID idParentGroup, DPID idGroup )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  return DP_IF_AddGroupToGroup( This, idParentGroup, idGroup );
+}
+
+static HRESULT WINAPI DP_IF_CreateGroupInGroup
+          ( IDirectPlay3Impl* This, LPVOID lpMsgHdr, DPID idParentGroup,
+            LPDPID lpidGroup, LPDPNAME lpGroupName, LPVOID lpData,
+            DWORD dwDataSize, DWORD dwFlags, BOOL bAnsi )
+{
+  lpGroupData lpGParentData;
+  lpGroupList lpGList;
+  lpGroupData lpGData;
+
+  TRACE( "(%p)->(0x%08lx,%p,%p,%p,0x%08lx,0x%08lx,%u)\n",
+         This, idParentGroup, lpidGroup, lpGroupName, lpData,
+         dwDataSize, dwFlags, bAnsi );
+
+  /* Verify that the specified parent is valid */
+  if( ( lpGParentData = DP_FindAnyGroup( (IDirectPlay2AImpl*)This,
+                                         idParentGroup ) ) == NULL
+    )
+  {
+    return DPERR_INVALIDGROUP;
+  }
+
+  lpGData = DP_CreateGroup( (IDirectPlay2AImpl*)This, lpidGroup, lpGroupName,
+                            dwFlags, idParentGroup, bAnsi );
+
+  if( lpGData == NULL )
+  {
+    return DPERR_CANTADDPLAYER; /* yes player not group */
+  }
+
+  /* Something else is referencing this data */
+  lpGData->uRef++;
+
+  DP_SetGroupData( lpGData, DPSET_REMOTE, lpData, dwDataSize );
+
+  /* The list has now been inserted into the interface group list. We now
+     need to put a "shortcut" to this group in the parent group */
+  lpGList = (lpGroupList)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                    sizeof( *lpGList ) );
+  if( lpGList == NULL )
+  {
+    FIXME( "Memory leak\n" );
+    return DPERR_CANTADDPLAYER; /* yes player not group */
+  }
+
+  lpGList->lpGData = lpGData;
+
+  DPQ_INSERT( lpGParentData->groups, lpGList, groups );
+
+  /* Let the SP know that we've created this group */
+  if( This->dp2->spData.lpCB->CreateGroup )
+  {
+    DPSP_CREATEGROUPDATA data;
+
+    TRACE( "Calling SP CreateGroup\n" );
+
+    data.idGroup           = *lpidGroup;
+    data.dwFlags           = dwFlags;
+    data.lpSPMessageHeader = lpMsgHdr;
+    data.lpISP             = This->dp2->spData.lpISP;
+
+    (*This->dp2->spData.lpCB->CreateGroup)( &data );
+  }
+
+  /* Inform all other peers of the creation of a new group. If there are
+   * no peers keep this quiet.
+   */
+  if( This->dp2->lpSessionDesc &&
+      ( This->dp2->lpSessionDesc->dwFlags & DPSESSION_MULTICASTSERVER ) )
+  {
+    DPMSG_CREATEPLAYERORGROUP msg;
+
+    msg.dwType = DPSYS_CREATEPLAYERORGROUP;
+    msg.dwPlayerType = DPPLAYERTYPE_GROUP;
+    msg.dpId = *lpidGroup;
+    msg.dwCurrentPlayers = idParentGroup; /* FIXME: Incorrect? */
+    msg.lpData = lpData;
+    msg.dwDataSize = dwDataSize;
+    msg.dpnName = *lpGroupName;
+
+    /* FIXME: Correct to just use send effectively? */
+    /* FIXME: Should size include data w/ message or just message "header" */
+    /* FIXME: Check return code */
+    DP_SendEx( (IDirectPlay2Impl*)This,
+               DPID_SERVERPLAYER, DPID_ALLPLAYERS, 0, &msg, sizeof( msg ),
+               0, 0, NULL, NULL, bAnsi );
+  }
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay3AImpl_CreateGroupInGroup
+          ( LPDIRECTPLAY3A iface, DPID idParentGroup, LPDPID lpidGroup,
+            LPDPNAME lpGroupName, LPVOID lpData, DWORD dwDataSize,
+            DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+
+  *lpidGroup = DPID_UNKNOWN;
+
+  return DP_IF_CreateGroupInGroup( This, NULL, idParentGroup, lpidGroup,
+                                   lpGroupName, lpData, dwDataSize, dwFlags,
+                                   TRUE );
+}
+
+static HRESULT WINAPI DirectPlay3WImpl_CreateGroupInGroup
+          ( LPDIRECTPLAY3 iface, DPID idParentGroup, LPDPID lpidGroup,
+            LPDPNAME lpGroupName, LPVOID lpData, DWORD dwDataSize,
+            DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+
+  *lpidGroup = DPID_UNKNOWN;
+
+  return DP_IF_CreateGroupInGroup( This, NULL, idParentGroup, lpidGroup,
+                                   lpGroupName, lpData, dwDataSize,
+                                   dwFlags, FALSE );
+}
+
+static HRESULT WINAPI DP_IF_DeleteGroupFromGroup
+          ( IDirectPlay3Impl* This, DPID idParentGroup, DPID idGroup )
+{
+  lpGroupList lpGList;
+  lpGroupData lpGParentData;
+
+  TRACE("(%p)->(0x%08lx,0x%08lx)\n", This, idParentGroup, idGroup );
+
+  /* Is the parent group valid? */
+  if( ( lpGParentData = DP_FindAnyGroup( (IDirectPlay2AImpl*)This, idParentGroup ) ) == NULL )
+  {
+    return DPERR_INVALIDGROUP;
+  }
+
+  /* Remove the group from the parent group queue */
+  DPQ_REMOVE_ENTRY( lpGParentData->groups, groups, lpGData->dpid, ==, idGroup, lpGList );
+
+  if( lpGList == NULL )
+  {
+    return DPERR_INVALIDGROUP;
+  }
+
+  /* Decrement the ref count */
+  lpGList->lpGData->uRef--;
+
+  /* Free up the list item */
+  HeapFree( GetProcessHeap(), 0, lpGList );
+
+  /* Should send a DELETEGROUPFROMGROUP message */
+  FIXME( "message not sent\n" );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay3AImpl_DeleteGroupFromGroup
+          ( LPDIRECTPLAY3 iface, DPID idParentGroup, DPID idGroup )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  return DP_IF_DeleteGroupFromGroup( This, idParentGroup, idGroup );
+}
+
+static HRESULT WINAPI DirectPlay3WImpl_DeleteGroupFromGroup
+          ( LPDIRECTPLAY3 iface, DPID idParentGroup, DPID idGroup )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  return DP_IF_DeleteGroupFromGroup( This, idParentGroup, idGroup );
+}
+
+static HRESULT WINAPI DirectPlay3AImpl_EnumConnections
+          ( LPDIRECTPLAY3A iface, LPCGUID lpguidApplication, LPDPENUMCONNECTIONSCALLBACK lpEnumCallback, LPVOID lpContext, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  TRACE("(%p)->(%p,%p,%p,0x%08lx)\n", This, lpguidApplication, lpEnumCallback, lpContext, dwFlags );
+
+  /* A default dwFlags (0) is backwards compatible -- DPCONNECTION_DIRECTPLAY */
+  if( dwFlags == 0 )
+  {
+    dwFlags = DPCONNECTION_DIRECTPLAY;
+  }
+
+  if( ! ( ( dwFlags & DPCONNECTION_DIRECTPLAY ) ||
+          ( dwFlags & DPCONNECTION_DIRECTPLAYLOBBY ) )
+    )
+  {
+    return DPERR_INVALIDFLAGS;
+  }
+
+  if( (LPVOID)lpEnumCallback==NULL || *(int*)lpEnumCallback==NULL )
+  {
+     return DPERR_INVALIDPARAMS;
+  }
+
+  /* Enumerate DirectPlay service providers */
+  if( dwFlags & DPCONNECTION_DIRECTPLAY )
+  {
+    HKEY hkResult;
+    LPCSTR searchSubKey    = "SOFTWARE\\Microsoft\\DirectPlay\\Service Providers";
+    LPSTR guidDataSubKey   = "Guid";
+    char subKeyName[51];
+    DWORD dwIndex, sizeOfSubKeyName=50;
+    FILETIME filetime;
+
+    /* Need to loop over the service providers in the registry */
+    if( RegOpenKeyExA( HKEY_LOCAL_MACHINE, searchSubKey,
+                         0, KEY_READ, &hkResult ) != ERROR_SUCCESS )
+    {
+      /* Hmmm. Does this mean that there are no service providers? */
+      ERR(": no service providers?\n");
+      return DP_OK;
+    }
+
+
+    /* Traverse all the service providers we have available */
+    for( dwIndex=0;
+         RegEnumKeyExA( hkResult, dwIndex, subKeyName, &sizeOfSubKeyName,
+                        NULL, NULL, NULL, &filetime ) != ERROR_NO_MORE_ITEMS;
+         ++dwIndex, sizeOfSubKeyName=51 )
+    {
+
+      HKEY     hkServiceProvider;
+      GUID     serviceProviderGUID;
+      DWORD    returnTypeGUID, sizeOfReturnBuffer = 50;
+      char     returnBuffer[51];
+      LPWSTR   lpWGUIDString;
+      DPNAME   dpName;
+      HRESULT  hr;
+
+      DPCOMPOUNDADDRESSELEMENT dpCompoundAddress;
+      LPVOID                   lpAddressBuffer = NULL;
+      DWORD                    dwAddressBufferSize = 0;
+
+      TRACE(" this time through: %s\n", subKeyName );
+
+      /* Get a handle for this particular service provider */
+      if( RegOpenKeyExA( hkResult, subKeyName, 0, KEY_READ,
+                         &hkServiceProvider ) != ERROR_SUCCESS )
+      {
+         ERR(": what the heck is going on?\n" );
+         continue;
+      }
+
+      if( RegQueryValueExA( hkServiceProvider, guidDataSubKey,
+                            NULL, &returnTypeGUID, (LPBYTE)returnBuffer,
+                            &sizeOfReturnBuffer ) != ERROR_SUCCESS )
+      {
+        ERR(": missing GUID registry data members\n" );
+        continue;
+      }
+
+      /* FIXME: Check return types to ensure we're interpreting data right */
+      lpWGUIDString = HEAP_strdupAtoW( GetProcessHeap(), 0, (LPCSTR)returnBuffer );
+      CLSIDFromString( (LPCOLESTR)lpWGUIDString, &serviceProviderGUID );
+      HeapFree( GetProcessHeap(), 0, lpWGUIDString );
+      /* FIXME: Have I got a memory leak on the serviceProviderGUID? */
+
+      /* Fill in the DPNAME struct for the service provider */
+      dpName.dwSize             = sizeof( dpName );
+      dpName.dwFlags            = 0;
+      dpName.psn.lpszShortNameA = subKeyName;
+      dpName.pln.lpszLongNameA  = NULL;
+
+      /* Create the compound address for the service provider.
+         NOTE: This is a gruesome architectural scar right now. DP uses DPL and DPL uses DP
+               nast stuff. This may be why the native dll just gets around this little bit by
+               allocating an 80 byte buffer which isn't even a filled with a valid compound
+               address. Oh well. Creating a proper compound address is the way to go anyways
+               despite this method taking slightly more heap space and realtime :) */
+      dpCompoundAddress.dwDataSize   = sizeof( GUID );
+      memcpy( &dpCompoundAddress.guidDataType, &DPAID_ServiceProvider,
+              sizeof( GUID ) ) ;
+      dpCompoundAddress.lpData       = &serviceProviderGUID;
+
+      if( ( hr = DPL_CreateCompoundAddress( &dpCompoundAddress, 1, lpAddressBuffer,
+                                     &dwAddressBufferSize, TRUE ) ) != DPERR_BUFFERTOOSMALL )
+      {
+        ERR( "can't get buffer size: %s\n", DPLAYX_HresultToString( hr ) );
+        return hr;
+      }
+
+      /* Now allocate the buffer */
+      lpAddressBuffer = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwAddressBufferSize );
+
+      if( ( hr = DPL_CreateCompoundAddress( &dpCompoundAddress, 1, lpAddressBuffer,
+                                     &dwAddressBufferSize, TRUE ) ) != DP_OK )
+      {
+        ERR( "can't create address: %s\n", DPLAYX_HresultToString( hr ) );
+        return hr;
+      }
+
+      /* The enumeration will return FALSE if we are not to continue */
+      if( !lpEnumCallback( &serviceProviderGUID, lpAddressBuffer, dwAddressBufferSize,
+                           &dpName, DPCONNECTION_DIRECTPLAY, lpContext ) )
+      {
+         return DP_OK;
+      }
+    }
+  }
+
+  /* Enumerate DirectPlayLobby service providers */
+  if( dwFlags & DPCONNECTION_DIRECTPLAYLOBBY )
+  {
+    HKEY hkResult;
+    LPCSTR searchSubKey    = "SOFTWARE\\Microsoft\\DirectPlay\\Lobby Providers";
+    LPSTR guidDataSubKey   = "Guid";
+    char subKeyName[51];
+    DWORD dwIndex, sizeOfSubKeyName=50;
+    FILETIME filetime;
+
+    /* Need to loop over the service providers in the registry */
+    if( RegOpenKeyExA( HKEY_LOCAL_MACHINE, searchSubKey,
+                         0, KEY_READ, &hkResult ) != ERROR_SUCCESS )
+    {
+      /* Hmmm. Does this mean that there are no service providers? */
+      ERR(": no service providers?\n");
+      return DP_OK;
+    }
+
+
+    /* Traverse all the lobby providers we have available */
+    for( dwIndex=0;
+         RegEnumKeyExA( hkResult, dwIndex, subKeyName, &sizeOfSubKeyName,
+                        NULL, NULL, NULL, &filetime ) != ERROR_NO_MORE_ITEMS;
+         ++dwIndex, sizeOfSubKeyName=51 )
+    {
+
+      HKEY     hkServiceProvider;
+      GUID     serviceProviderGUID;
+      DWORD    returnTypeGUID, sizeOfReturnBuffer = 50;
+      char     returnBuffer[51];
+      LPWSTR   lpWGUIDString;
+      DPNAME   dpName;
+      HRESULT  hr;
+
+      DPCOMPOUNDADDRESSELEMENT dpCompoundAddress;
+      LPVOID                   lpAddressBuffer = NULL;
+      DWORD                    dwAddressBufferSize = 0;
+
+      TRACE(" this time through: %s\n", subKeyName );
+
+      /* Get a handle for this particular service provider */
+      if( RegOpenKeyExA( hkResult, subKeyName, 0, KEY_READ,
+                         &hkServiceProvider ) != ERROR_SUCCESS )
+      {
+         ERR(": what the heck is going on?\n" );
+         continue;
+      }
+
+      if( RegQueryValueExA( hkServiceProvider, guidDataSubKey,
+                            NULL, &returnTypeGUID, (LPBYTE)returnBuffer,
+                            &sizeOfReturnBuffer ) != ERROR_SUCCESS )
+      {
+        ERR(": missing GUID registry data members\n" );
+        continue;
+      }
+
+      /* FIXME: Check return types to ensure we're interpreting data right */
+      lpWGUIDString = HEAP_strdupAtoW( GetProcessHeap(), 0, returnBuffer );
+      CLSIDFromString( (LPCOLESTR)lpWGUIDString, &serviceProviderGUID );
+      HeapFree( GetProcessHeap(), 0, lpWGUIDString );
+      /* FIXME: Have I got a memory leak on the serviceProviderGUID? */
+
+      /* Fill in the DPNAME struct for the service provider */
+      dpName.dwSize             = sizeof( dpName );
+      dpName.dwFlags            = 0;
+      dpName.psn.lpszShortNameA = subKeyName;
+      dpName.pln.lpszLongNameA  = NULL;
+
+      /* Create the compound address for the service provider.
+         NOTE: This is a gruesome architectural scar right now. DP uses DPL and DPL uses DP
+               nast stuff. This may be why the native dll just gets around this little bit by
+               allocating an 80 byte buffer which isn't even a filled with a valid compound
+               address. Oh well. Creating a proper compound address is the way to go anyways
+               despite this method taking slightly more heap space and realtime :) */
+      dpCompoundAddress.guidDataType = DPAID_LobbyProvider;
+      dpCompoundAddress.dwDataSize   = sizeof( GUID );
+      dpCompoundAddress.lpData       = &serviceProviderGUID;
+
+      if( ( hr = DPL_CreateCompoundAddress( &dpCompoundAddress, 1, lpAddressBuffer,
+                                     &dwAddressBufferSize, TRUE ) ) != DPERR_BUFFERTOOSMALL )
+      {
+        ERR( "can't get buffer size: %s\n", DPLAYX_HresultToString( hr ) );
+        return hr;
+      }
+
+      /* Now allocate the buffer */
+      lpAddressBuffer = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwAddressBufferSize );
+
+      if( ( hr = DPL_CreateCompoundAddress( &dpCompoundAddress, 1, lpAddressBuffer,
+                                     &dwAddressBufferSize, TRUE ) ) != DP_OK )
+      {
+        ERR( "can't create address: %s\n", DPLAYX_HresultToString( hr ) );
+        return hr;
+      }
+
+      /* The enumeration will return FALSE if we are not to continue */
+      if( !lpEnumCallback( &serviceProviderGUID, lpAddressBuffer, dwAddressBufferSize,
+                           &dpName, DPCONNECTION_DIRECTPLAYLOBBY, lpContext ) )
+      {
+         return DP_OK;
+      }
+    }
+  }
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay3WImpl_EnumConnections
+          ( LPDIRECTPLAY3 iface, LPCGUID lpguidApplication, LPDPENUMCONNECTIONSCALLBACK lpEnumCallback, LPVOID lpContext, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  FIXME("(%p)->(%p,%p,%p,0x%08lx): stub\n", This, lpguidApplication, lpEnumCallback, lpContext, dwFlags );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DP_IF_EnumGroupsInGroup
+          ( IDirectPlay3AImpl* This, DPID idGroup, LPGUID lpguidInstance,
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2,
+            LPVOID lpContext, DWORD dwFlags, BOOL bAnsi )
+{
+  lpGroupList lpGList;
+  lpGroupData lpGData;
+
+  FIXME( "(%p)->(0x%08lx,%p,%p,%p,0x%08lx,%u): semi stub\n",
+         This, idGroup, lpguidInstance, lpEnumPlayersCallback2,
+         lpContext, dwFlags, bAnsi );
+
+  if( ( lpGData = DP_FindAnyGroup( (IDirectPlay2AImpl*)This, idGroup ) ) == NULL )
+  {
+    return DPERR_INVALIDGROUP;
+  }
+
+  if( DPQ_IS_EMPTY( lpGData->groups ) )
+  {
+    return DP_OK;
+  }
+
+  lpGList = DPQ_FIRST( lpGData->groups );
+
+  for( ;; )
+  {
+    /* FIXME: Should check dwFlags for match here */
+
+    if( !(*lpEnumPlayersCallback2)( lpGList->lpGData->dpid, DPPLAYERTYPE_GROUP,
+                                    &lpGList->lpGData->name, dwFlags,
+                                    lpContext ) )
+    {
+      return DP_OK; /* User requested break */
+    }
+
+    if( DPQ_IS_ENDOFLIST( lpGList->groups ) )
+    {
+      break;
+    }
+
+    lpGList = DPQ_NEXT( lpGList->groups );
+
+  }
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay3AImpl_EnumGroupsInGroup
+          ( LPDIRECTPLAY3A iface, DPID idGroup, LPGUID lpguidInstance,
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2, LPVOID lpContext,
+            DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  return DP_IF_EnumGroupsInGroup( This, idGroup, lpguidInstance,
+                                  lpEnumPlayersCallback2, lpContext, dwFlags,
+                                  TRUE );
+}
+
+static HRESULT WINAPI DirectPlay3WImpl_EnumGroupsInGroup
+          ( LPDIRECTPLAY3A iface, DPID idGroup, LPGUID lpguidInstance,
+            LPDPENUMPLAYERSCALLBACK2 lpEnumPlayersCallback2, LPVOID lpContext,
+            DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  return DP_IF_EnumGroupsInGroup( This, idGroup, lpguidInstance,
+                                  lpEnumPlayersCallback2, lpContext, dwFlags,
+                                  FALSE );
+}
+
+static HRESULT WINAPI DirectPlay3AImpl_GetGroupConnectionSettings
+          ( LPDIRECTPLAY3A iface, DWORD dwFlags, DPID idGroup, LPVOID lpData, LPDWORD lpdwDataSize )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  FIXME("(%p)->(0x%08lx,0x%08lx,%p,%p): stub\n", This, dwFlags, idGroup, lpData, lpdwDataSize );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay3WImpl_GetGroupConnectionSettings
+          ( LPDIRECTPLAY3 iface, DWORD dwFlags, DPID idGroup, LPVOID lpData, LPDWORD lpdwDataSize )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  FIXME("(%p)->(0x%08lx,0x%08lx,%p,%p): stub\n", This, dwFlags, idGroup, lpData, lpdwDataSize );
+  return DP_OK;
+}
+
+BOOL CALLBACK DP_GetSpLpGuidFromCompoundAddress(
+    REFGUID         guidDataType,
+    DWORD           dwDataSize,
+    LPCVOID         lpData,
+    LPVOID          lpContext )
+{
+  /* Looking for the GUID of the provider to load */
+  if( ( IsEqualGUID( guidDataType, &DPAID_ServiceProvider ) ) ||
+      ( IsEqualGUID( guidDataType, &DPAID_LobbyProvider ) )
+    )
+  {
+    TRACE( "Found SP/LP (%s) %s (data size = 0x%08lx)\n",
+           debugstr_guid( guidDataType ), debugstr_guid( lpData ), dwDataSize );
+
+    if( dwDataSize != sizeof( GUID ) )
+    {
+      ERR( "Invalid sp/lp guid size 0x%08lx\n", dwDataSize );
+    }
+
+    memcpy( lpContext, lpData, dwDataSize );
+
+    /* There shouldn't be more than 1 GUID/compound address */
+    return FALSE;
+  }
+
+  /* Still waiting for what we want */
+  return TRUE;
+}
+
+
+/* Find and perform a LoadLibrary on the requested SP or LP GUID */
+static HMODULE DP_LoadSP( LPCGUID lpcGuid, LPSPINITDATA lpSpData )
+{
+  UINT i;
+  LPCSTR spSubKey         = "SOFTWARE\\Microsoft\\DirectPlay\\Service Providers";
+  LPCSTR lpSubKey         = "SOFTWARE\\Microsoft\\DirectPlay\\Lobby Providers";
+  LPCSTR guidDataSubKey   = "Guid";
+  LPCSTR majVerDataSubKey = "dwReserved1";
+  LPCSTR minVerDataSubKey = "dwReserved2";
+  LPCSTR pathSubKey       = "Path";
+
+  TRACE( " request to load %s\n", debugstr_guid( lpcGuid ) );
+
+  /* FIXME: Cloned code with a quick hack. */
+  for( i=0; i<2; i++ )
+  {
+    HKEY hkResult;
+    LPCSTR searchSubKey;
+    char subKeyName[51];
+    DWORD dwIndex, sizeOfSubKeyName=50;
+    FILETIME filetime;
+
+    (i == 0) ? (searchSubKey = spSubKey ) : (searchSubKey = lpSubKey );
+
+
+    /* Need to loop over the service providers in the registry */
+    if( RegOpenKeyExA( HKEY_LOCAL_MACHINE, searchSubKey,
+                         0, KEY_READ, &hkResult ) != ERROR_SUCCESS )
+    {
+      /* Hmmm. Does this mean that there are no service providers? */
+      ERR(": no service providers?\n");
+      return 0;
+    }
+
+    /* Traverse all the service providers we have available */
+    for( dwIndex=0;
+         RegEnumKeyExA( hkResult, dwIndex, subKeyName, &sizeOfSubKeyName,
+                        NULL, NULL, NULL, &filetime ) != ERROR_NO_MORE_ITEMS;
+         ++dwIndex, sizeOfSubKeyName=51 )
+    {
+
+      HKEY     hkServiceProvider;
+      GUID     serviceProviderGUID;
+      DWORD    returnType, sizeOfReturnBuffer = 255;
+      char     returnBuffer[256];
+      LPWSTR   lpWGUIDString;
+      DWORD    dwTemp;
+
+      TRACE(" this time through: %s\n", subKeyName );
+
+      /* Get a handle for this particular service provider */
+      if( RegOpenKeyExA( hkResult, subKeyName, 0, KEY_READ,
+                         &hkServiceProvider ) != ERROR_SUCCESS )
+      {
+         ERR(": what the heck is going on?\n" );
+         continue;
+      }
+
+      if( RegQueryValueExA( hkServiceProvider, (LPSTR)guidDataSubKey,
+                            NULL, &returnType, (LPBYTE)returnBuffer,
+                            &sizeOfReturnBuffer ) != ERROR_SUCCESS )
+      {
+        ERR(": missing GUID registry data members\n" );
+        continue;
+      }
+
+      /* FIXME: Check return types to ensure we're interpreting data right */
+      lpWGUIDString = HEAP_strdupAtoW( GetProcessHeap(), 0, returnBuffer );
+      CLSIDFromString( (LPCOLESTR)lpWGUIDString, &serviceProviderGUID );
+      HeapFree( GetProcessHeap(), 0, lpWGUIDString );
+      /* FIXME: Have I got a memory leak on the serviceProviderGUID? */
+
+      /* Determine if this is the Service Provider that the user asked for */
+      if( !IsEqualGUID( &serviceProviderGUID, lpcGuid ) )
+      {
+        continue;
+      }
+
+      /* Save the name of the SP or LP */
+      lpSpData->lpszName = HEAP_strdupAtoW( GetProcessHeap(), 0, subKeyName );
+
+      sizeOfReturnBuffer = 255;
+
+      /* Get dwReserved1 */
+      if( RegQueryValueExA( hkServiceProvider, (LPSTR)majVerDataSubKey,
+                            NULL, &returnType, (LPBYTE)returnBuffer,
+                            &sizeOfReturnBuffer ) != ERROR_SUCCESS )
+      {
+         ERR(": missing dwReserved1 registry data members\n") ;
+         continue;
+      }
+
+      lpSpData->dwReserved1 = GET_DWORD( returnBuffer );
+
+      sizeOfReturnBuffer = 255;
+
+      /* Get dwReserved2 */
+      if( RegQueryValueExA( hkServiceProvider, (LPSTR)minVerDataSubKey,
+                            NULL, &returnType, (LPBYTE)returnBuffer,
+                            &sizeOfReturnBuffer ) != ERROR_SUCCESS )
+      {
+         ERR(": missing dwReserved1 registry data members\n") ;
+         continue;
+      }
+
+      lpSpData->dwReserved2 = GET_DWORD( returnBuffer );
+
+
+      sizeOfReturnBuffer = 255;
+
+      /* Get the path for this service provider */
+      if( ( dwTemp = RegQueryValueExA( hkServiceProvider, (LPSTR)pathSubKey,
+                            NULL, NULL, (LPBYTE)returnBuffer,
+                            &sizeOfReturnBuffer ) ) != ERROR_SUCCESS )
+      {
+        ERR(": missing PATH registry data members: 0x%08lx\n", dwTemp );
+        continue;
+      }
+
+      return LoadLibraryA( returnBuffer );
+    }
+  }
+
+  return 0;
+}
+
+static HRESULT WINAPI DirectPlay3AImpl_InitializeConnection
+          ( LPDIRECTPLAY3A iface, LPVOID lpConnection, DWORD dwFlags )
+{
+  HMODULE hServiceProvider;
+  HRESULT hr;
+  LPDPSP_SPINIT SPInit;
+  GUID guidSP;
+  DWORD dwAddrSize = 80; /* FIXME: Need to calculate it correctly */
+
+  ICOM_THIS(IDirectPlay3Impl,iface);
+
+  TRACE("(%p)->(%p,0x%08lx)\n", This, lpConnection, dwFlags );
+
+  if( dwFlags != 0 )
+  {
+    return DPERR_INVALIDFLAGS;
+  }
+
+  if( This->dp2->bConnectionInitialized == TRUE )
+  {
+    return DPERR_ALREADYINITIALIZED;
+  }
+
+  /* Find out what the requested SP is and how large this buffer is */
+  hr = DPL_EnumAddress( DP_GetSpLpGuidFromCompoundAddress, lpConnection,
+                        dwAddrSize, &guidSP );
+
+  if( FAILED(hr) )
+  {
+    ERR( "Invalid compound address?\n" );
+    return DPERR_UNAVAILABLE;
+  }
+
+  /* Initialize what we can of the Service Provider required information.
+   * The rest will be done in DP_LoadSP
+   */
+  This->dp2->spData.lpAddress = (LPDPADDRESS)lpConnection;
+  This->dp2->spData.dwAddressSize = dwAddrSize;
+  This->dp2->spData.lpGuid = &guidSP;
+
+  /* Load the service provider */
+  hServiceProvider = DP_LoadSP( &guidSP, &This->dp2->spData );
+
+  if( hServiceProvider == 0 )
+  {
+    ERR( "Unable to load service provider\n" );
+    return DPERR_UNAVAILABLE;
+  }
+
+  /* Initialize the service provider by calling SPInit */
+  SPInit = (LPDPSP_SPINIT)GetProcAddress( hServiceProvider, "SPInit" );
+
+  if( SPInit == NULL )
+  {
+    ERR( "Service provider doesn't provide SPInit interface?\n" );
+    FreeLibrary( hServiceProvider );
+    return DPERR_UNAVAILABLE;
+  }
+
+  TRACE( "Calling SPInit\n" );
+  hr = (*SPInit)( &This->dp2->spData );
+
+  if( FAILED(hr) )
+  {
+    ERR( "SP Initialization failed: %s\n", DPLAYX_HresultToString(hr) );
+    FreeLibrary( hServiceProvider );
+    return hr;
+  }
+
+  /* This interface is now initialized */
+  This->dp2->bConnectionInitialized = TRUE;
+
+  /* Store the handle of the module so that we can unload it later */
+  This->dp2->hServiceProvider = hServiceProvider;
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay3WImpl_InitializeConnection
+          ( LPDIRECTPLAY3 iface, LPVOID lpConnection, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  FIXME("(%p)->(%p,0x%08lx): stub\n", This, lpConnection, dwFlags );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay3AImpl_SecureOpen
+          ( LPDIRECTPLAY3A iface, LPCDPSESSIONDESC2 lpsd, DWORD dwFlags,
+            LPCDPSECURITYDESC lpSecurity, LPCDPCREDENTIALS lpCredentials )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface); /* Yes a dp 2 interface */
+  return DP_SecureOpen( This, lpsd, dwFlags, lpSecurity, lpCredentials, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay3WImpl_SecureOpen
+          ( LPDIRECTPLAY3 iface, LPCDPSESSIONDESC2 lpsd, DWORD dwFlags,
+            LPCDPSECURITYDESC lpSecurity, LPCDPCREDENTIALS lpCredentials )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface); /* Yes a dp 2 interface */
+  return DP_SecureOpen( This, lpsd, dwFlags, lpSecurity, lpCredentials, FALSE );
+}
+
+static HRESULT WINAPI DirectPlay3AImpl_SendChatMessage
+          ( LPDIRECTPLAY3A iface, DPID idFrom, DPID idTo, DWORD dwFlags, LPDPCHAT lpChatMessage )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  FIXME("(%p)->(0x%08lx,0x%08lx,0x%08lx,%p): stub\n", This, idFrom, idTo, dwFlags, lpChatMessage );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay3WImpl_SendChatMessage
+          ( LPDIRECTPLAY3 iface, DPID idFrom, DPID idTo, DWORD dwFlags, LPDPCHAT lpChatMessage )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  FIXME("(%p)->(0x%08lx,0x%08lx,0x%08lx,%p): stub\n", This, idFrom, idTo, dwFlags, lpChatMessage );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay3AImpl_SetGroupConnectionSettings
+          ( LPDIRECTPLAY3A iface, DWORD dwFlags, DPID idGroup, LPDPLCONNECTION lpConnection )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  FIXME("(%p)->(0x%08lx,0x%08lx,%p): stub\n", This, dwFlags, idGroup, lpConnection );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay3WImpl_SetGroupConnectionSettings
+          ( LPDIRECTPLAY3 iface, DWORD dwFlags, DPID idGroup, LPDPLCONNECTION lpConnection )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  FIXME("(%p)->(0x%08lx,0x%08lx,%p): stub\n", This, dwFlags, idGroup, lpConnection );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay3AImpl_StartSession
+          ( LPDIRECTPLAY3A iface, DWORD dwFlags, DPID idGroup )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  FIXME("(%p)->(0x%08lx,0x%08lx): stub\n", This, dwFlags, idGroup );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay3WImpl_StartSession
+          ( LPDIRECTPLAY3 iface, DWORD dwFlags, DPID idGroup )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  FIXME("(%p)->(0x%08lx,0x%08lx): stub\n", This, dwFlags, idGroup );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay3AImpl_GetGroupFlags
+          ( LPDIRECTPLAY3A iface, DPID idGroup, LPDWORD lpdwFlags )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  FIXME("(%p)->(0x%08lx,%p): stub\n", This, idGroup, lpdwFlags );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay3WImpl_GetGroupFlags
+          ( LPDIRECTPLAY3 iface, DPID idGroup, LPDWORD lpdwFlags )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  FIXME("(%p)->(0x%08lx,%p): stub\n", This, idGroup, lpdwFlags );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DP_IF_GetGroupParent
+          ( IDirectPlay3AImpl* This, DPID idGroup, LPDPID lpidGroup,
+            BOOL bAnsi )
+{
+  lpGroupData lpGData;
+
+  TRACE("(%p)->(0x%08lx,%p,%u)\n", This, idGroup, lpidGroup, bAnsi );
+
+  if( ( lpGData = DP_FindAnyGroup( (IDirectPlay2AImpl*)This, idGroup ) ) == NULL )
+  {
+    return DPERR_INVALIDGROUP;
+  }
+
+  *lpidGroup = lpGData->dpid;
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay3AImpl_GetGroupParent
+          ( LPDIRECTPLAY3A iface, DPID idGroup, LPDPID lpidGroup )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  return DP_IF_GetGroupParent( This, idGroup, lpidGroup, TRUE );
+}
+static HRESULT WINAPI DirectPlay3WImpl_GetGroupParent
+          ( LPDIRECTPLAY3 iface, DPID idGroup, LPDPID lpidGroup )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  return DP_IF_GetGroupParent( This, idGroup, lpidGroup, FALSE );
+}
+
+static HRESULT WINAPI DirectPlay3AImpl_GetPlayerAccount
+          ( LPDIRECTPLAY3A iface, DPID idPlayer, DWORD dwFlags, LPVOID lpData, LPDWORD lpdwDataSize )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  FIXME("(%p)->(0x%08lx,0x%08lx,%p,%p): stub\n", This, idPlayer, dwFlags, lpData, lpdwDataSize );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay3WImpl_GetPlayerAccount
+          ( LPDIRECTPLAY3 iface, DPID idPlayer, DWORD dwFlags, LPVOID lpData, LPDWORD lpdwDataSize )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  FIXME("(%p)->(0x%08lx,0x%08lx,%p,%p): stub\n", This, idPlayer, dwFlags, lpData, lpdwDataSize );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay3AImpl_GetPlayerFlags
+          ( LPDIRECTPLAY3A iface, DPID idPlayer, LPDWORD lpdwFlags )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  FIXME("(%p)->(0x%08lx,%p): stub\n", This, idPlayer, lpdwFlags );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay3WImpl_GetPlayerFlags
+          ( LPDIRECTPLAY3 iface, DPID idPlayer, LPDWORD lpdwFlags )
+{
+  ICOM_THIS(IDirectPlay3Impl,iface);
+  FIXME("(%p)->(0x%08lx,%p): stub\n", This, idPlayer, lpdwFlags );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay4AImpl_GetGroupOwner
+          ( LPDIRECTPLAY4A iface, DPID idGroup, LPDPID lpidGroupOwner )
+{
+  ICOM_THIS(IDirectPlay4Impl,iface);
+  FIXME("(%p)->(0x%08lx,%p): stub\n", This, idGroup, lpidGroupOwner );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay4WImpl_GetGroupOwner
+          ( LPDIRECTPLAY4 iface, DPID idGroup, LPDPID lpidGroupOwner )
+{
+  ICOM_THIS(IDirectPlay4Impl,iface);
+  FIXME("(%p)->(0x%08lx,%p): stub\n", This, idGroup, lpidGroupOwner );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay4AImpl_SetGroupOwner
+          ( LPDIRECTPLAY4A iface, DPID idGroup , DPID idGroupOwner )
+{
+  ICOM_THIS(IDirectPlay4Impl,iface);
+  FIXME("(%p)->(0x%08lx,0x%08lx): stub\n", This, idGroup, idGroupOwner );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DirectPlay4WImpl_SetGroupOwner
+          ( LPDIRECTPLAY4 iface, DPID idGroup , DPID idGroupOwner )
+{
+  ICOM_THIS(IDirectPlay4Impl,iface);
+  FIXME("(%p)->(0x%08lx,0x%08lx): stub\n", This, idGroup, idGroupOwner );
+  return DP_OK;
+}
+
+static HRESULT WINAPI DP_SendEx
+          ( IDirectPlay2Impl* This, DPID idFrom, DPID idTo, DWORD dwFlags,
+            LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout,
+            LPVOID lpContext, LPDWORD lpdwMsgID, BOOL bAnsi )
+{
+  lpPlayerList lpPList;
+  lpGroupData  lpGData;
+  BOOL         bValidDestination = FALSE;
+
+  FIXME( "(%p)->(0x%08lx,0x%08lx,0x%08lx,%p,0x%08lx,0x%08lx,0x%08lx,%p,%p,%u)"
+         ": stub\n",
+         This, idFrom, idTo, dwFlags, lpData, dwDataSize, dwPriority,
+         dwTimeout, lpContext, lpdwMsgID, bAnsi );
+
+  /* FIXME: Add parameter checking */
+  /* FIXME: First call to this needs to aquire a message id which will be
+   *        used for multiple sends
+   */
+
+  /* NOTE: Can't send messages to yourself - this will be trapped in receive */
+
+  /* Verify that the message is being sent from a valid local player. The
+   * from player may be anonymous DPID_UNKNOWN
+   */
+  if( idFrom != DPID_UNKNOWN )
+  {
+    if( ( lpPList = DP_FindPlayer( This, idFrom ) ) == NULL )
+    {
+      WARN( "INFO: Invalid from player 0x%08lx\n", idFrom );
+      return DPERR_INVALIDPLAYER;
+    }
+  }
+
+  /* Verify that the message is being sent to a valid player, group or to
+   * everyone. If it's valid, send it to those players.
+   */
+  if( idTo == DPID_ALLPLAYERS )
+  {
+    bValidDestination = TRUE;
+
+    /* See if SP has the ability to multicast. If so, use it */
+    if( This->dp2->spData.lpCB->SendToGroupEx )
+    {
+      FIXME( "Use group sendex to group 0\n" );
+    }
+    else if( This->dp2->spData.lpCB->SendToGroup ) /* obsolete interface */
+    {
+      FIXME( "Use obsolete group send to group 0\n" );
+    }
+    else /* No multicast, multiplicate */
+    {
+      /* Send to all players we know about */
+      FIXME( "Send to all players using EnumPlayersInGroup\n" );
+    }
+  }
+
+  if( ( !bValidDestination ) &&
+      ( DP_FindPlayer( This, idTo ) != NULL )
+    )
+  {
+    bValidDestination = TRUE;
+
+    /* Have the service provider send this message */
+    /* FIXME: Could optimize for local interface sends */
+    return DP_SP_SendEx( This, dwFlags, lpData, dwDataSize, dwPriority,
+                         dwTimeout, lpContext, lpdwMsgID );
+  }
+
+  if( ( !bValidDestination ) &&
+      ( ( lpGData = DP_FindAnyGroup( This, idTo ) ) != NULL )
+    )
+  {
+    bValidDestination = TRUE;
+
+    /* See if SP has the ability to multicast. If so, use it */
+    if( This->dp2->spData.lpCB->SendToGroupEx )
+    {
+      FIXME( "Use group sendex\n" );
+    }
+    else if( This->dp2->spData.lpCB->SendToGroup ) /* obsolete interface */
+    {
+      FIXME( "Use obsolete group send to group\n" );
+    }
+    else /* No multicast, multiplicate */
+    {
+      FIXME( "Send to all players using EnumPlayersInGroup\n" );
+    }
+
+#if 0
+    if( bExpectReply )
+    {
+      DWORD dwWaitReturn;
+
+      This->dp2->hReplyEvent = CreateEventA( NULL, FALSE, FALSE, NULL );
+
+      dwWaitReturn = WaitForSingleObject( hReplyEvent, dwTimeout );
+      if( dwWaitReturn != WAIT_OBJECT_0 )
+      {
+        ERR( "Wait failed 0x%08lx\n", dwWaitReturn );
+      }
+    }
+#endif
+  }
+
+  if( !bValidDestination )
+  {
+    return DPERR_INVALIDPLAYER;
+  }
+  else
+  {
+    /* FIXME: Should return what the send returned */
+    return DP_OK;
+  }
+}
+
+
+static HRESULT WINAPI DirectPlay4AImpl_SendEx
+          ( LPDIRECTPLAY4A iface, DPID idFrom, DPID idTo, DWORD dwFlags,
+            LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout,
+            LPVOID lpContext, LPDWORD lpdwMsgID )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface); /* yes downcast to 2 */
+  return DP_SendEx( This, idFrom, idTo, dwFlags, lpData, dwDataSize,
+                    dwPriority, dwTimeout, lpContext, lpdwMsgID, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay4WImpl_SendEx
+          ( LPDIRECTPLAY4 iface, DPID idFrom, DPID idTo, DWORD dwFlags,
+            LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout,
+            LPVOID lpContext, LPDWORD lpdwMsgID )
+{
+  ICOM_THIS(IDirectPlay2Impl,iface); /* yes downcast to 2 */
+  return DP_SendEx( This, idFrom, idTo, dwFlags, lpData, dwDataSize,
+                    dwPriority, dwTimeout, lpContext, lpdwMsgID, FALSE );
+}
+
+static HRESULT WINAPI DP_SP_SendEx
+          ( IDirectPlay2Impl* This, DWORD dwFlags,
+            LPVOID lpData, DWORD dwDataSize, DWORD dwPriority, DWORD dwTimeout,
+            LPVOID lpContext, LPDWORD lpdwMsgID )
+{
+  LPDPMSG lpMElem;
+
+  FIXME( ": stub\n" );
+
+  /* FIXME: This queuing should only be for async messages */
+
+  lpMElem = (LPDPMSG)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                               sizeof( *lpMElem ) );
+  lpMElem->msg = (DPMSG_GENERIC*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                            dwDataSize );
+
+  CopyMemory( lpMElem->msg, lpData, dwDataSize );
+
+  /* FIXME: Need to queue based on priority */
+  DPQ_INSERT( This->dp2->sendMsgs, lpMElem, msgs );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI DP_IF_GetMessageQueue
+          ( IDirectPlay4Impl* This, DPID idFrom, DPID idTo, DWORD dwFlags,
+            LPDWORD lpdwNumMsgs, LPDWORD lpdwNumBytes, BOOL bAnsi )
+{
+  HRESULT hr = DP_OK;
+
+  FIXME( "(%p)->(0x%08lx,0x%08lx,0x%08lx,%p,%p,%u): semi stub\n",
+         This, idFrom, idTo, dwFlags, lpdwNumMsgs, lpdwNumBytes, bAnsi );
+
+  /* FIXME: Do we need to do idFrom and idTo sanity checking here? */
+  /* FIXME: What about sends which are not immediate? */
+
+  if( This->dp2->spData.lpCB->GetMessageQueue )
+  {
+    DPSP_GETMESSAGEQUEUEDATA data;
+
+    FIXME( "Calling SP GetMessageQueue - is it right?\n" );
+
+    /* FIXME: None of this is documented :( */
+
+    data.lpISP        = This->dp2->spData.lpISP;
+    data.dwFlags      = dwFlags;
+    data.idFrom       = idFrom;
+    data.idTo         = idTo;
+    data.lpdwNumMsgs  = lpdwNumMsgs;
+    data.lpdwNumBytes = lpdwNumBytes;
+
+    hr = (*This->dp2->spData.lpCB->GetMessageQueue)( &data );
+  }
+  else
+  {
+    FIXME( "No SP for GetMessageQueue - fake some data\n" );
+  }
+
+  return hr;
+}
+
+static HRESULT WINAPI DirectPlay4AImpl_GetMessageQueue
+          ( LPDIRECTPLAY4A iface, DPID idFrom, DPID idTo, DWORD dwFlags,
+            LPDWORD lpdwNumMsgs, LPDWORD lpdwNumBytes )
+{
+  ICOM_THIS(IDirectPlay4Impl,iface);
+  return DP_IF_GetMessageQueue( This, idFrom, idTo, dwFlags, lpdwNumMsgs,
+                                lpdwNumBytes, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay4WImpl_GetMessageQueue
+          ( LPDIRECTPLAY4 iface, DPID idFrom, DPID idTo, DWORD dwFlags,
+            LPDWORD lpdwNumMsgs, LPDWORD lpdwNumBytes )
+{
+  ICOM_THIS(IDirectPlay4Impl,iface);
+  return DP_IF_GetMessageQueue( This, idFrom, idTo, dwFlags, lpdwNumMsgs,
+                                lpdwNumBytes, FALSE );
+}
+
+static HRESULT WINAPI DP_IF_CancelMessage
+          ( IDirectPlay4Impl* This, DWORD dwMsgID, DWORD dwFlags,
+            DWORD dwMinPriority, DWORD dwMaxPriority, BOOL bAnsi )
+{
+  HRESULT hr = DP_OK;
+
+  FIXME( "(%p)->(0x%08lx,0x%08lx,%u): semi stub\n",
+         This, dwMsgID, dwFlags, bAnsi );
+
+  if( This->dp2->spData.lpCB->Cancel )
+  {
+    DPSP_CANCELDATA data;
+
+    TRACE( "Calling SP Cancel\n" );
+
+    /* FIXME: Undocumented callback */
+
+    data.lpISP          = This->dp2->spData.lpISP;
+    data.dwFlags        = dwFlags;
+    data.lprglpvSPMsgID = NULL;
+    data.cSPMsgID       = dwMsgID;
+    data.dwMinPriority  = dwMinPriority;
+    data.dwMaxPriority  = dwMaxPriority;
+
+    hr = (*This->dp2->spData.lpCB->Cancel)( &data );
+  }
+  else
+  {
+    FIXME( "SP doesn't implement Cancel\n" );
+  }
+
+  return hr;
+}
+
+static HRESULT WINAPI DirectPlay4AImpl_CancelMessage
+          ( LPDIRECTPLAY4A iface, DWORD dwMsgID, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay4Impl,iface);
+
+  if( dwFlags != 0 )
+  {
+    return DPERR_INVALIDFLAGS;
+  }
+
+  if( dwMsgID == 0 )
+  {
+    dwFlags |= DPCANCELSEND_ALL;
+  }
+
+  return DP_IF_CancelMessage( This, dwMsgID, dwFlags, 0, 0, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay4WImpl_CancelMessage
+          ( LPDIRECTPLAY4 iface, DWORD dwMsgID, DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay4Impl,iface);
+
+  if( dwFlags != 0 )
+  {
+    return DPERR_INVALIDFLAGS;
+  }
+
+  if( dwMsgID == 0 )
+  {
+    dwFlags |= DPCANCELSEND_ALL;
+  }
+
+  return DP_IF_CancelMessage( This, dwMsgID, dwFlags, 0, 0, FALSE );
+}
+
+static HRESULT WINAPI DirectPlay4AImpl_CancelPriority
+          ( LPDIRECTPLAY4A iface, DWORD dwMinPriority, DWORD dwMaxPriority,
+            DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay4Impl,iface);
+
+  if( dwFlags != 0 )
+  {
+    return DPERR_INVALIDFLAGS;
+  }
+
+  return DP_IF_CancelMessage( This, 0, DPCANCELSEND_PRIORITY, dwMinPriority,
+                              dwMaxPriority, TRUE );
+}
+
+static HRESULT WINAPI DirectPlay4WImpl_CancelPriority
+          ( LPDIRECTPLAY4 iface, DWORD dwMinPriority, DWORD dwMaxPriority,
+            DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlay4Impl,iface);
+
+  if( dwFlags != 0 )
+  {
+    return DPERR_INVALIDFLAGS;
+  }
+
+  return DP_IF_CancelMessage( This, 0, DPCANCELSEND_PRIORITY, dwMinPriority,
+                              dwMaxPriority, FALSE );
+}
+
+ICOM_VTABLE(IDirectPlay2) directPlay2WVT =
+{
+  ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+  (HRESULT(*CALLBACK)(IDirectPlay2*,const IID*const,LPVOID*))DP_QueryInterface,
+  (  ULONG(*CALLBACK)(IDirectPlay2*)) DP_AddRef,
+  (  ULONG(*CALLBACK)(IDirectPlay2*)) DP_Release,
+
+  DirectPlay2WImpl_AddPlayerToGroup,
+  DirectPlay2WImpl_Close,
+  DirectPlay2WImpl_CreateGroup,
+  DirectPlay2WImpl_CreatePlayer,
+  DirectPlay2WImpl_DeletePlayerFromGroup,
+  DirectPlay2WImpl_DestroyGroup,
+  DirectPlay2WImpl_DestroyPlayer,
+  DirectPlay2WImpl_EnumGroupPlayers,
+  DirectPlay2WImpl_EnumGroups,
+  DirectPlay2WImpl_EnumPlayers,
+  DirectPlay2WImpl_EnumSessions,
+  DirectPlay2WImpl_GetCaps,
+  DirectPlay2WImpl_GetGroupData,
+  DirectPlay2WImpl_GetGroupName,
+  DirectPlay2WImpl_GetMessageCount,
+  DirectPlay2WImpl_GetPlayerAddress,
+  DirectPlay2WImpl_GetPlayerCaps,
+  DirectPlay2WImpl_GetPlayerData,
+  DirectPlay2WImpl_GetPlayerName,
+  DirectPlay2WImpl_GetSessionDesc,
+  DirectPlay2WImpl_Initialize,
+  DirectPlay2WImpl_Open,
+  DirectPlay2WImpl_Receive,
+  DirectPlay2WImpl_Send,
+  DirectPlay2WImpl_SetGroupData,
+  DirectPlay2WImpl_SetGroupName,
+  DirectPlay2WImpl_SetPlayerData,
+  DirectPlay2WImpl_SetPlayerName,
+  DirectPlay2WImpl_SetSessionDesc
+};
+
+ICOM_VTABLE(IDirectPlay2) directPlay2AVT =
+{
+  ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+  (HRESULT(*CALLBACK)(IDirectPlay2*,const IID*const,LPVOID*))DP_QueryInterface,
+  (  ULONG(*CALLBACK)(IDirectPlay2*))DP_AddRef,
+  (  ULONG(*CALLBACK)(IDirectPlay2*))DP_Release,
+
+  DirectPlay2AImpl_AddPlayerToGroup,
+  DirectPlay2AImpl_Close,
+  DirectPlay2AImpl_CreateGroup,
+  DirectPlay2AImpl_CreatePlayer,
+  DirectPlay2AImpl_DeletePlayerFromGroup,
+  DirectPlay2AImpl_DestroyGroup,
+  DirectPlay2AImpl_DestroyPlayer,
+  DirectPlay2AImpl_EnumGroupPlayers,
+  DirectPlay2AImpl_EnumGroups,
+  DirectPlay2AImpl_EnumPlayers,
+  DirectPlay2AImpl_EnumSessions,
+  DirectPlay2AImpl_GetCaps,
+  DirectPlay2AImpl_GetGroupData,
+  DirectPlay2AImpl_GetGroupName,
+  DirectPlay2AImpl_GetMessageCount,
+  DirectPlay2AImpl_GetPlayerAddress,
+  DirectPlay2AImpl_GetPlayerCaps,
+  DirectPlay2AImpl_GetPlayerData,
+  DirectPlay2AImpl_GetPlayerName,
+  DirectPlay2AImpl_GetSessionDesc,
+  DirectPlay2AImpl_Initialize,
+  DirectPlay2AImpl_Open,
+  DirectPlay2AImpl_Receive,
+  DirectPlay2AImpl_Send,
+  DirectPlay2AImpl_SetGroupData,
+  DirectPlay2AImpl_SetGroupName,
+  DirectPlay2AImpl_SetPlayerData,
+  DirectPlay2AImpl_SetPlayerName,
+  DirectPlay2AImpl_SetSessionDesc
+};
+
+#define XCAST (void*)
+
+ICOM_VTABLE(IDirectPlay3) directPlay3AVT =
+{
+  ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+  (HRESULT(*CALLBACK)(IDirectPlay3*,const IID*const,LPVOID*))DP_QueryInterface,
+  (ULONG(*CALLBACK)(IDirectPlay3*))DP_AddRef,
+  (ULONG(*CALLBACK)(IDirectPlay3*))DP_Release,
+
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,DPID))DirectPlay2AImpl_AddPlayerToGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay3*))DirectPlay2AImpl_Close,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPDPID,LPDPNAME,LPVOID,DWORD,DWORD))DirectPlay2AImpl_CreateGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPDPID,LPDPNAME,HANDLE,LPVOID,DWORD,DWORD))DirectPlay2AImpl_CreatePlayer,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,DPID))DirectPlay2AImpl_DeletePlayerFromGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID))DirectPlay2AImpl_DestroyGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID))DirectPlay2AImpl_DestroyPlayer,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD))DirectPlay2AImpl_EnumGroupPlayers,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD))DirectPlay2AImpl_EnumGroups,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD))DirectPlay2AImpl_EnumPlayers,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPDPSESSIONDESC2,DWORD,LPDPENUMSESSIONSCALLBACK2,LPVOID,DWORD))DirectPlay2AImpl_EnumSessions,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPDPCAPS,DWORD))DirectPlay2AImpl_GetCaps,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPVOID,LPDWORD,DWORD))DirectPlay2AImpl_GetGroupData,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPVOID,LPDWORD))DirectPlay2AImpl_GetGroupName,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPDWORD))DirectPlay2AImpl_GetMessageCount,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPVOID,LPDWORD))DirectPlay2AImpl_GetPlayerAddress,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPDPCAPS,DWORD))DirectPlay2AImpl_GetPlayerCaps,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPVOID,LPDWORD,DWORD))DirectPlay2AImpl_GetPlayerData,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPVOID,LPDWORD))DirectPlay2AImpl_GetPlayerName,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPVOID,LPDWORD))DirectPlay2AImpl_GetSessionDesc,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPGUID))DirectPlay2AImpl_Initialize,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPDPSESSIONDESC2,DWORD))DirectPlay2AImpl_Open,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPDPID,LPDPID,DWORD,LPVOID,LPDWORD))DirectPlay2AImpl_Receive,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,DPID,DWORD,LPVOID,DWORD))DirectPlay2AImpl_Send,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPVOID,DWORD,DWORD))DirectPlay2AImpl_SetGroupData,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPDPNAME,DWORD))DirectPlay2AImpl_SetGroupName,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPVOID,DWORD,DWORD))DirectPlay2AImpl_SetPlayerData,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPDPNAME,DWORD))DirectPlay2AImpl_SetPlayerName,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPDPSESSIONDESC2,DWORD))DirectPlay2AImpl_SetSessionDesc,
+
+  DirectPlay3AImpl_AddGroupToGroup,
+  DirectPlay3AImpl_CreateGroupInGroup,
+  DirectPlay3AImpl_DeleteGroupFromGroup,
+  DirectPlay3AImpl_EnumConnections,
+  DirectPlay3AImpl_EnumGroupsInGroup,
+  DirectPlay3AImpl_GetGroupConnectionSettings,
+  DirectPlay3AImpl_InitializeConnection,
+  DirectPlay3AImpl_SecureOpen,
+  DirectPlay3AImpl_SendChatMessage,
+  DirectPlay3AImpl_SetGroupConnectionSettings,
+  DirectPlay3AImpl_StartSession,
+  DirectPlay3AImpl_GetGroupFlags,
+  DirectPlay3AImpl_GetGroupParent,
+  DirectPlay3AImpl_GetPlayerAccount,
+  DirectPlay3AImpl_GetPlayerFlags
+};
+#undef XCAST
+
+ICOM_VTABLE(IDirectPlay3) directPlay3WVT =
+{
+  ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+  (HRESULT(*CALLBACK)(IDirectPlay3*,const IID*const,LPVOID*))DP_QueryInterface,
+  (ULONG(*CALLBACK)(IDirectPlay3*))DP_AddRef,
+  (ULONG(*CALLBACK)(IDirectPlay3*))DP_Release,
+
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,DPID))DirectPlay2WImpl_AddPlayerToGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay3*))DirectPlay2WImpl_Close,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPDPID,LPDPNAME,LPVOID,DWORD,DWORD))DirectPlay2WImpl_CreateGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPDPID,LPDPNAME,HANDLE,LPVOID,DWORD,DWORD))DirectPlay2WImpl_CreatePlayer,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,DPID))DirectPlay2WImpl_DeletePlayerFromGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID))DirectPlay2WImpl_DestroyGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID))DirectPlay2WImpl_DestroyPlayer,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD))DirectPlay2WImpl_EnumGroupPlayers,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD))DirectPlay2WImpl_EnumGroups,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD))DirectPlay2WImpl_EnumPlayers,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPDPSESSIONDESC2,DWORD,LPDPENUMSESSIONSCALLBACK2,LPVOID,DWORD))DirectPlay2WImpl_EnumSessions,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPDPCAPS,DWORD))DirectPlay2WImpl_GetCaps,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPVOID,LPDWORD,DWORD))DirectPlay2WImpl_GetGroupData,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPVOID,LPDWORD))DirectPlay2WImpl_GetGroupName,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPDWORD))DirectPlay2WImpl_GetMessageCount,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPVOID,LPDWORD))DirectPlay2WImpl_GetPlayerAddress,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPDPCAPS,DWORD))DirectPlay2WImpl_GetPlayerCaps,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPVOID,LPDWORD,DWORD))DirectPlay2WImpl_GetPlayerData,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPVOID,LPDWORD))DirectPlay2WImpl_GetPlayerName,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPVOID,LPDWORD))DirectPlay2WImpl_GetSessionDesc,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPGUID))DirectPlay2WImpl_Initialize,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPDPSESSIONDESC2,DWORD))DirectPlay2WImpl_Open,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPDPID,LPDPID,DWORD,LPVOID,LPDWORD))DirectPlay2WImpl_Receive,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,DPID,DWORD,LPVOID,DWORD))DirectPlay2WImpl_Send,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPVOID,DWORD,DWORD))DirectPlay2WImpl_SetGroupData,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPDPNAME,DWORD))DirectPlay2WImpl_SetGroupName,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPVOID,DWORD,DWORD))DirectPlay2WImpl_SetPlayerData,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,DPID,LPDPNAME,DWORD))DirectPlay2WImpl_SetPlayerName,
+  (HRESULT(*CALLBACK)(IDirectPlay3*,LPDPSESSIONDESC2,DWORD))DirectPlay2WImpl_SetSessionDesc,
+
+  DirectPlay3WImpl_AddGroupToGroup,
+  DirectPlay3WImpl_CreateGroupInGroup,
+  DirectPlay3WImpl_DeleteGroupFromGroup,
+  DirectPlay3WImpl_EnumConnections,
+  DirectPlay3WImpl_EnumGroupsInGroup,
+  DirectPlay3WImpl_GetGroupConnectionSettings,
+  DirectPlay3WImpl_InitializeConnection,
+  DirectPlay3WImpl_SecureOpen,
+  DirectPlay3WImpl_SendChatMessage,
+  DirectPlay3WImpl_SetGroupConnectionSettings,
+  DirectPlay3WImpl_StartSession,
+  DirectPlay3WImpl_GetGroupFlags,
+  DirectPlay3WImpl_GetGroupParent,
+  DirectPlay3WImpl_GetPlayerAccount,
+  DirectPlay3WImpl_GetPlayerFlags
+};
+
+ICOM_VTABLE(IDirectPlay4) directPlay4WVT =
+{
+  ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+  (HRESULT(*CALLBACK)(IDirectPlay4*,const IID*const,LPVOID*))DP_QueryInterface,
+  (ULONG(*CALLBACK)(IDirectPlay4*))DP_AddRef,
+  (ULONG(*CALLBACK)(IDirectPlay4*))DP_Release,
+
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,DPID))DirectPlay2WImpl_AddPlayerToGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay4*))DirectPlay2WImpl_Close,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPDPID,LPDPNAME,LPVOID,DWORD,DWORD))DirectPlay2WImpl_CreateGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPDPID,LPDPNAME,HANDLE,LPVOID,DWORD,DWORD))DirectPlay2WImpl_CreatePlayer,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,DPID))DirectPlay2WImpl_DeletePlayerFromGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID))DirectPlay2WImpl_DestroyGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID))DirectPlay2WImpl_DestroyPlayer,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD))DirectPlay2WImpl_EnumGroupPlayers,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD))DirectPlay2WImpl_EnumGroups,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD))DirectPlay2WImpl_EnumPlayers,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPDPSESSIONDESC2,DWORD,LPDPENUMSESSIONSCALLBACK2,LPVOID,DWORD))DirectPlay2WImpl_EnumSessions,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPDPCAPS,DWORD))DirectPlay2WImpl_GetCaps,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPVOID,LPDWORD,DWORD))DirectPlay2WImpl_GetGroupData,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPVOID,LPDWORD))DirectPlay2WImpl_GetGroupName,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPDWORD))DirectPlay2WImpl_GetMessageCount,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPVOID,LPDWORD))DirectPlay2WImpl_GetPlayerAddress,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPDPCAPS,DWORD))DirectPlay2WImpl_GetPlayerCaps,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPVOID,LPDWORD,DWORD))DirectPlay2WImpl_GetPlayerData,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPVOID,LPDWORD))DirectPlay2WImpl_GetPlayerName,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPVOID,LPDWORD))DirectPlay2WImpl_GetSessionDesc,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPGUID))DirectPlay2WImpl_Initialize,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPDPSESSIONDESC2,DWORD))DirectPlay2WImpl_Open,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPDPID,LPDPID,DWORD,LPVOID,LPDWORD))DirectPlay2WImpl_Receive,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,DPID,DWORD,LPVOID,DWORD))DirectPlay2WImpl_Send,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPVOID,DWORD,DWORD))DirectPlay2WImpl_SetGroupData,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPDPNAME,DWORD))DirectPlay2WImpl_SetGroupName,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPVOID,DWORD,DWORD))DirectPlay2WImpl_SetPlayerData,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPDPNAME,DWORD))DirectPlay2WImpl_SetPlayerName,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPDPSESSIONDESC2,DWORD))DirectPlay2WImpl_SetSessionDesc,
+
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,DPID))DirectPlay3WImpl_AddGroupToGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPDPID,LPDPNAME,LPVOID,DWORD,DWORD))DirectPlay3WImpl_CreateGroupInGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,DPID))DirectPlay3WImpl_DeleteGroupFromGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPCGUID,LPDPENUMCONNECTIONSCALLBACK,LPVOID,DWORD))DirectPlay3WImpl_EnumConnections,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD))DirectPlay3WImpl_EnumGroupsInGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DWORD,DPID,LPVOID,LPDWORD))DirectPlay3WImpl_GetGroupConnectionSettings,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPVOID,DWORD))DirectPlay3WImpl_InitializeConnection,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPCDPSESSIONDESC2,DWORD,LPCDPSECURITYDESC,LPCDPCREDENTIALS))DirectPlay3WImpl_SecureOpen,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,DPID,DWORD,LPDPCHAT))DirectPlay3WImpl_SendChatMessage,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DWORD,DPID,LPDPLCONNECTION))DirectPlay3WImpl_SetGroupConnectionSettings,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DWORD,DPID))DirectPlay3WImpl_StartSession,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPDWORD))DirectPlay3WImpl_GetGroupFlags,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPDPID))DirectPlay3WImpl_GetGroupParent,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,DWORD,LPVOID,LPDWORD))DirectPlay3WImpl_GetPlayerAccount,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPDWORD))DirectPlay3WImpl_GetPlayerFlags,
+
+  DirectPlay4WImpl_GetGroupOwner,
+  DirectPlay4WImpl_SetGroupOwner,
+  DirectPlay4WImpl_SendEx,
+  DirectPlay4WImpl_GetMessageQueue,
+  DirectPlay4WImpl_CancelMessage,
+  DirectPlay4WImpl_CancelPriority
+};
+
+ICOM_VTABLE(IDirectPlay4) directPlay4AVT =
+{
+  ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+  (HRESULT(*CALLBACK)(IDirectPlay4*,const IID*const,LPVOID*))DP_QueryInterface,
+  (ULONG(*CALLBACK)(IDirectPlay4*))DP_AddRef,
+  (ULONG(*CALLBACK)(IDirectPlay4*))DP_Release,
+
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,DPID))DirectPlay2AImpl_AddPlayerToGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay4*))DirectPlay2AImpl_Close,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPDPID,LPDPNAME,LPVOID,DWORD,DWORD))DirectPlay2AImpl_CreateGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPDPID,LPDPNAME,HANDLE,LPVOID,DWORD,DWORD))DirectPlay2AImpl_CreatePlayer,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,DPID))DirectPlay2AImpl_DeletePlayerFromGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID))DirectPlay2AImpl_DestroyGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID))DirectPlay2AImpl_DestroyPlayer,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD))DirectPlay2AImpl_EnumGroupPlayers,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD))DirectPlay2AImpl_EnumGroups,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD))DirectPlay2AImpl_EnumPlayers,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPDPSESSIONDESC2,DWORD,LPDPENUMSESSIONSCALLBACK2,LPVOID,DWORD))DirectPlay2AImpl_EnumSessions,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPDPCAPS,DWORD))DirectPlay2AImpl_GetCaps,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPVOID,LPDWORD,DWORD))DirectPlay2AImpl_GetGroupData,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPVOID,LPDWORD))DirectPlay2AImpl_GetGroupName,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPDWORD))DirectPlay2AImpl_GetMessageCount,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPVOID,LPDWORD))DirectPlay2AImpl_GetPlayerAddress,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPDPCAPS,DWORD))DirectPlay2AImpl_GetPlayerCaps,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPVOID,LPDWORD,DWORD))DirectPlay2AImpl_GetPlayerData,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPVOID,LPDWORD))DirectPlay2AImpl_GetPlayerName,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPVOID,LPDWORD))DirectPlay2AImpl_GetSessionDesc,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPGUID))DirectPlay2AImpl_Initialize,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPDPSESSIONDESC2,DWORD))DirectPlay2AImpl_Open,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPDPID,LPDPID,DWORD,LPVOID,LPDWORD))DirectPlay2AImpl_Receive,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,DPID,DWORD,LPVOID,DWORD))DirectPlay2AImpl_Send,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPVOID,DWORD,DWORD))DirectPlay2AImpl_SetGroupData,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPDPNAME,DWORD))DirectPlay2AImpl_SetGroupName,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPVOID,DWORD,DWORD))DirectPlay2AImpl_SetPlayerData,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPDPNAME,DWORD))DirectPlay2AImpl_SetPlayerName,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPDPSESSIONDESC2,DWORD))DirectPlay2AImpl_SetSessionDesc,
+
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,DPID))DirectPlay3AImpl_AddGroupToGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPDPID,LPDPNAME,LPVOID,DWORD,DWORD))DirectPlay3AImpl_CreateGroupInGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,DPID))DirectPlay3AImpl_DeleteGroupFromGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPCGUID,LPDPENUMCONNECTIONSCALLBACK,LPVOID,DWORD))DirectPlay3AImpl_EnumConnections,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD))DirectPlay3AImpl_EnumGroupsInGroup,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DWORD,DPID,LPVOID,LPDWORD))DirectPlay3AImpl_GetGroupConnectionSettings,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPVOID,DWORD))DirectPlay3AImpl_InitializeConnection,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,LPCDPSESSIONDESC2,DWORD,LPCDPSECURITYDESC,LPCDPCREDENTIALS))DirectPlay3AImpl_SecureOpen,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,DPID,DWORD,LPDPCHAT))DirectPlay3AImpl_SendChatMessage,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DWORD,DPID,LPDPLCONNECTION))DirectPlay3AImpl_SetGroupConnectionSettings,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DWORD,DPID))DirectPlay3AImpl_StartSession,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPDWORD))DirectPlay3AImpl_GetGroupFlags,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPDPID))DirectPlay3AImpl_GetGroupParent,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,DWORD,LPVOID,LPDWORD))DirectPlay3AImpl_GetPlayerAccount,
+  (HRESULT(*CALLBACK)(IDirectPlay4*,DPID,LPDWORD))DirectPlay3AImpl_GetPlayerFlags,
+
+  DirectPlay4AImpl_GetGroupOwner,
+  DirectPlay4AImpl_SetGroupOwner,
+  DirectPlay4AImpl_SendEx,
+  DirectPlay4AImpl_GetMessageQueue,
+  DirectPlay4AImpl_CancelMessage,
+  DirectPlay4AImpl_CancelPriority
+};
+
+
+/***************************************************************************
+ *  DirectPlayEnumerateA (DPLAYX.2)
+ *
+ *  The pointer to the structure lpContext will be filled with the
+ *  appropriate data for each service offered by the OS. These services are
+ *  not necessarily available on this particular machine but are defined
+ *  as simple service providers under the "Service Providers" registry key.
+ *  This structure is then passed to lpEnumCallback for each of the different
+ *  services.
+ *
+ *  This API is useful only for applications written using DirectX3 or
+ *  worse. It is superceeded by IDirectPlay3::EnumConnections which also
+ *  gives information on the actual connections.
+ *
+ * defn of a service provider:
+ * A dynamic-link library used by DirectPlay to communicate over a network.
+ * The service provider contains all the network-specific code required
+ * to send and receive messages. Online services and network operators can
+ * supply service providers to use specialized hardware, protocols, communications
+ * media, and network resources.
+ *
+ * TODO: Allocate string buffer space from the heap (length from reg)
+ *       Pass real device driver numbers...
+ *       Get the GUID properly...
+ */
+HRESULT WINAPI DirectPlayEnumerateA( LPDPENUMDPCALLBACKA lpEnumCallback,
+                                     LPVOID lpContext )
+{
+
+  HKEY   hkResult;
+  LPCSTR searchSubKey    = "SOFTWARE\\Microsoft\\DirectPlay\\Service Providers";
+  DWORD  dwIndex;
+  DWORD  sizeOfSubKeyName=50;
+  char   subKeyName[51];
+  FILETIME filetime;
+
+  TRACE(": lpEnumCallback=%p lpContext=%p\n", lpEnumCallback, lpContext );
+
+  if( !(void*)lpEnumCallback || !*(int*)lpEnumCallback )
+  {
+     return DPERR_INVALIDPARAMS;
+  }
+
+  /* Need to loop over the service providers in the registry */
+  if( RegOpenKeyExA( HKEY_LOCAL_MACHINE, searchSubKey,
+                       0, KEY_READ, &hkResult ) != ERROR_SUCCESS )
+  {
+    /* Hmmm. Does this mean that there are no service providers? */
+    ERR(": no service providers?\n");
+    return DP_OK;
+  }
+
+  /* Traverse all the service providers we have available */
+  for( dwIndex=0;
+       RegEnumKeyExA( hkResult, dwIndex, subKeyName, &sizeOfSubKeyName,
+                      NULL, NULL, NULL, &filetime ) != ERROR_NO_MORE_ITEMS;
+       ++dwIndex, sizeOfSubKeyName=50 )
+  {
+    LPSTR    majVerDataSubKey = "dwReserved1";
+    LPSTR    minVerDataSubKey = "dwReserved2";
+    LPSTR    guidDataSubKey   = "Guid";
+    HKEY     hkServiceProvider;
+    GUID     serviceProviderGUID;
+    DWORD    returnTypeGUID, returnTypeReserved, sizeOfReturnBuffer = 50;
+    char     returnBuffer[51];
+    DWORD    majVersionNum , minVersionNum = 0;
+    LPWSTR   lpWGUIDString;
+
+    TRACE(" this time through: %s\n", subKeyName );
+
+    /* Get a handle for this particular service provider */
+    if( RegOpenKeyExA( hkResult, subKeyName, 0, KEY_READ,
+                         &hkServiceProvider ) != ERROR_SUCCESS )
+    {
+      ERR(": what the heck is going on?\n" );
+      continue;
+    }
+
+    /* Get the GUID, Device major number and device minor number
+     * from the registry.
+     */
+    if( RegQueryValueExA( hkServiceProvider, guidDataSubKey,
+                            NULL, &returnTypeGUID, (LPBYTE)returnBuffer,
+                            &sizeOfReturnBuffer ) != ERROR_SUCCESS )
+    {
+      ERR(": missing GUID registry data members\n" );
+      continue;
+    }
+
+    /* FIXME: Check return types to ensure we're interpreting data right */
+    lpWGUIDString = HEAP_strdupAtoW( GetProcessHeap(), 0, returnBuffer );
+    CLSIDFromString( (LPCOLESTR)lpWGUIDString, &serviceProviderGUID );
+    HeapFree( GetProcessHeap(), 0, lpWGUIDString );
+
+    /* FIXME: Need to know which of dwReserved1 and dwReserved2 are maj and min */
+
+    sizeOfReturnBuffer = 50;
+    if( RegQueryValueExA( hkServiceProvider, majVerDataSubKey,
+                            NULL, &returnTypeReserved, (LPBYTE)returnBuffer,
+                            &sizeOfReturnBuffer ) != ERROR_SUCCESS )
+    {
+      ERR(": missing dwReserved1 registry data members\n") ;
+      continue;
+    }
+
+    majVersionNum = GET_DWORD( returnBuffer );
+
+    sizeOfReturnBuffer = 50;
+    if( RegQueryValueExA( hkServiceProvider, minVerDataSubKey,
+                            NULL, &returnTypeReserved, (LPBYTE)returnBuffer,
+                            &sizeOfReturnBuffer ) != ERROR_SUCCESS )
+    {
+      ERR(": missing dwReserved2 registry data members\n") ;
+      continue;
+    }
+
+    minVersionNum = GET_DWORD( returnBuffer );
+
+
+    /* The enumeration will return FALSE if we are not to continue */
+    if( !lpEnumCallback( &serviceProviderGUID , subKeyName,
+                         majVersionNum, minVersionNum, lpContext ) )
+    {
+      WARN("lpEnumCallback returning FALSE\n" );
+      break;
+    }
+  }
+
+  return DP_OK;
+
+}
+
+/***************************************************************************
+ *  DirectPlayEnumerateW (DPLAYX.3)
+ *
+ */
+HRESULT WINAPI DirectPlayEnumerateW( LPDPENUMDPCALLBACKW lpEnumCallback, LPVOID lpContext )
+{
+
+  FIXME(":stub\n");
+
+  return DPERR_OUTOFMEMORY;
+
+}
+
+typedef struct tagCreateEnum
+{
+  LPVOID  lpConn;
+  LPCGUID lpGuid;
+} CreateEnumData, *lpCreateEnumData;
+
+/* Find and copy the matching connection for the SP guid */
+static BOOL CALLBACK cbDPCreateEnumConnections(
+    LPCGUID     lpguidSP,
+    LPVOID      lpConnection,
+    DWORD       dwConnectionSize,
+    LPCDPNAME   lpName,
+    DWORD       dwFlags,
+    LPVOID      lpContext)
+{
+  lpCreateEnumData lpData = (lpCreateEnumData)lpContext;
+
+  if( IsEqualGUID( lpguidSP, lpData->lpGuid ) )
+  {
+    TRACE( "Found SP entry with guid %s\n", debugstr_guid(lpData->lpGuid) );
+
+    lpData->lpConn = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                dwConnectionSize );
+    CopyMemory( lpData->lpConn, lpConnection, dwConnectionSize );
+
+    /* Found the record that we were looking for */
+    return FALSE;
+  }
+
+  /* Haven't found what were looking for yet */
+  return TRUE;
+}
+
+
+/***************************************************************************
+ *  DirectPlayCreate (DPLAYX.1) (DPLAY.1)
+ *
+ */
+HRESULT WINAPI DirectPlayCreate
+( LPGUID lpGUID, LPDIRECTPLAY2 *lplpDP, IUnknown *pUnk)
+{
+  HRESULT hr;
+  LPDIRECTPLAY3A lpDP3A;
+  CreateEnumData cbData;
+
+  TRACE( "lpGUID=%s lplpDP=%p pUnk=%p\n", debugstr_guid(lpGUID), lplpDP, pUnk );
+
+  if( pUnk != NULL )
+  {
+    return CLASS_E_NOAGGREGATION;
+  }
+
+
+  /* Create an IDirectPlay object. We don't support that so we'll cheat and
+     give them an IDirectPlay2A object and hope that doesn't cause problems */
+  if( DP_CreateInterface( &IID_IDirectPlay2A, (LPVOID*)lplpDP ) != DP_OK )
+  {
+    return DPERR_UNAVAILABLE;
+  }
+
+  if( IsEqualGUID( &GUID_NULL, lpGUID ) )
+  {
+    /* The GUID_NULL means don't bind a service provider. Just return the
+       interface as is */
+    return DP_OK;
+  }
+
+  /* Bind the desired service provider since lpGUID is non NULL */
+  TRACE( "Service Provider binding for %s\n", debugstr_guid(lpGUID) );
+
+  /* We're going to use a DP3 interface */
+  hr = IDirectPlayX_QueryInterface( *lplpDP, &IID_IDirectPlay3A,
+                                    (LPVOID*)&lpDP3A );
+  if( FAILED(hr) )
+  {
+    ERR( "Failed to get DP3 interface: %s\n", DPLAYX_HresultToString(hr) );
+    return hr;
+  }
+
+  cbData.lpConn = NULL;
+  cbData.lpGuid = lpGUID;
+
+  /* We were given a service provider, find info about it... */
+  hr = IDirectPlayX_EnumConnections( lpDP3A, NULL, cbDPCreateEnumConnections,
+                                     &cbData, DPCONNECTION_DIRECTPLAY );
+  if( ( FAILED(hr) ) ||
+      ( cbData.lpConn == NULL )
+    )
+  {
+    ERR( "Failed to get Enum for SP: %s\n", DPLAYX_HresultToString(hr) );
+    IDirectPlayX_Release( lpDP3A );
+    return DPERR_UNAVAILABLE;
+  }
+
+  /* Initialize the service provider */
+  hr = IDirectPlayX_InitializeConnection( lpDP3A, cbData.lpConn, 0 );
+  if( FAILED(hr) )
+  {
+    ERR( "Failed to Initialize SP: %s\n", DPLAYX_HresultToString(hr) );
+    HeapFree( GetProcessHeap(), 0, cbData.lpConn );
+    IDirectPlayX_Release( lpDP3A );
+    return hr;
+  }
+
+  /* Release our version of the interface now that we're done with it */
+  IDirectPlayX_Release( lpDP3A );
+  HeapFree( GetProcessHeap(), 0, cbData.lpConn );
+
+  return DP_OK;
+}
Index: trunk/src/DPlayX/dplay_global.h
===================================================================
--- trunk/src/DPlayX/dplay_global.h	(revision 4314)
+++ trunk/src/DPlayX/dplay_global.h	(revision 4314)
@@ -0,0 +1,196 @@
+#ifndef __WINE_DPLAY_GLOBAL_INCLUDED
+#define __WINE_DPLAY_GLOBAL_INCLUDED
+
+#include "dplaysp.h"
+#include "dplayx_queue.h"
+
+extern HRESULT DPL_EnumAddress( LPDPENUMADDRESSCALLBACK lpEnumAddressCallback, 
+                                LPCVOID lpAddress, DWORD dwAddressSize, 
+                                LPVOID lpContext );
+
+extern DWORD DP_CalcSessionDescSize( LPCDPSESSIONDESC2 lpSessDesc, BOOL bAnsi );
+
+/*****************************************************************************
+ * Predeclare the interface implementation structures
+ */
+typedef struct IDirectPlay2Impl IDirectPlay2AImpl;
+typedef struct IDirectPlay2Impl IDirectPlay2Impl;
+typedef struct IDirectPlay3Impl IDirectPlay3AImpl;
+typedef struct IDirectPlay3Impl IDirectPlay3Impl;
+typedef struct IDirectPlay4Impl IDirectPlay4AImpl;
+typedef struct IDirectPlay4Impl IDirectPlay4Impl;
+
+typedef struct tagDirectPlayIUnknownData
+{
+  ULONG             ulObjRef;
+  CRITICAL_SECTION  DP_lock;
+} DirectPlayIUnknownData;
+
+typedef struct tagEnumSessionAsyncCallbackData
+{
+  LPSPINITDATA lpSpData;
+  GUID         requestGuid;
+  DWORD        dwEnumSessionFlags;
+  DWORD        dwTimeout;
+  HANDLE       hSuicideRequest;
+} EnumSessionAsyncCallbackData;
+
+struct PlayerData
+{
+  /* Individual player information */
+  DPID dpid;
+
+  DPNAME name;
+  HANDLE hEvent;
+
+  ULONG  uRef;  /* What is the reference count on this data? */
+
+  /* View of local data */
+  LPVOID lpLocalData;
+  DWORD  dwLocalDataSize;
+
+  /* View of remote data */
+  LPVOID lpRemoteData;
+  DWORD  dwRemoteDataSize;
+
+  DWORD  dwFlags; /* Special remarks about the type of player */
+};
+typedef struct PlayerData* lpPlayerData;
+
+struct PlayerList
+{
+  DPQ_ENTRY(PlayerList) players;
+
+  lpPlayerData lpPData;
+};
+typedef struct PlayerList* lpPlayerList;
+
+struct GroupData
+{
+  /* Internal information */
+  DPID parent; /* If parent == 0 it's a top level group */
+
+  ULONG uRef; /* Reference count */
+
+  DPQ_HEAD(GroupList)  groups;  /* A group has [0..n] groups */
+  DPQ_HEAD(PlayerList) players; /* A group has [0..n] players */
+
+  DPID idGroupOwner; /* ID of player who owns the group */
+
+  DWORD dwFlags; /* Flags describing anything special about the group */
+
+  DPID   dpid;
+  DPNAME name;
+
+  /* View of local data */
+  LPVOID lpLocalData;
+  DWORD  dwLocalDataSize;
+
+  /* View of remote data */
+  LPVOID lpRemoteData;
+  DWORD  dwRemoteDataSize;
+};
+typedef struct GroupData  GroupData;
+typedef struct GroupData* lpGroupData;
+
+struct GroupList
+{
+  DPQ_ENTRY(GroupList) groups;
+
+  lpGroupData lpGData;
+};
+typedef struct GroupList* lpGroupList;
+
+struct DPMSG
+{
+  DPQ_ENTRY( DPMSG ) msgs;
+  DPMSG_GENERIC* msg;
+};
+typedef struct DPMSG* LPDPMSG;
+
+/* Contains all dp1 and dp2 data members */
+typedef struct tagDirectPlay2Data
+{
+  BOOL   bConnectionOpen;
+
+  /* For async EnumSessions requests */
+  HANDLE hEnumSessionThread;
+  HANDLE hKillEnumSessionThreadEvent;
+
+  LPVOID lpNameServerData; /* DPlay interface doesn't know contents */
+
+  BOOL bHostInterface; /* Did this interface create the session */
+
+  lpGroupData lpSysGroup; /* System group with _everything_ in it */
+
+  LPDPSESSIONDESC2 lpSessionDesc;
+
+  /* I/O Msg queues */
+  DPQ_HEAD( DPMSG ) receiveMsgs; /* Msg receive queue */
+  DPQ_HEAD( DPMSG ) sendMsgs;    /* Msg send pending queue */
+
+  /* Information about the service provider active on this connection */
+  SPINITDATA spData;
+
+  /* Our service provider */
+  HMODULE hServiceProvider;
+
+  BOOL bConnectionInitialized;
+
+
+  /* proof of concept for message reception */
+  HANDLE hMsgReceipt;
+  LPVOID lpMsgReceived;
+} DirectPlay2Data;
+
+typedef struct tagDirectPlay3Data
+{
+  BOOL dummy;
+} DirectPlay3Data;
+typedef struct tagDirectPlay4Data
+{
+  BOOL dummy;
+} DirectPlay4Data;
+
+#define DP_IMPL_FIELDS \
+  ULONG ulInterfaceRef; \
+  DirectPlayIUnknownData*  unk; \
+  DirectPlay2Data*         dp2; \
+  DirectPlay3Data*         dp3; \
+  DirectPlay4Data*         dp4;
+
+struct IDirectPlay2Impl
+{
+  ICOM_VFIELD(IDirectPlay2);
+  DP_IMPL_FIELDS
+};
+
+struct IDirectPlay3Impl
+{
+  ICOM_VFIELD(IDirectPlay3);
+  DP_IMPL_FIELDS
+};
+
+struct IDirectPlay4Impl
+{
+  ICOM_VFIELD(IDirectPlay4);
+  DP_IMPL_FIELDS
+};
+
+/* Forward declarations of virtual tables */
+extern ICOM_VTABLE(IDirectPlay2) directPlay2AVT;
+extern ICOM_VTABLE(IDirectPlay3) directPlay3AVT;
+extern ICOM_VTABLE(IDirectPlay4) directPlay4AVT;
+
+extern ICOM_VTABLE(IDirectPlay2) directPlay2WVT;
+extern ICOM_VTABLE(IDirectPlay3) directPlay3WVT;
+extern ICOM_VTABLE(IDirectPlay4) directPlay4WVT;
+
+
+HRESULT DP_HandleMessage( IDirectPlay2Impl* This, LPCVOID lpMessageBody,
+                          DWORD  dwMessageBodySize, LPCVOID lpMessageHeader,
+                          WORD wCommandId, WORD wVersion, 
+                          LPVOID* lplpReply, LPDWORD lpdwMsgSize );
+
+
+#endif /* __WINE_DPLAY_GLOBAL_INCLUDED */
Index: trunk/src/DPlayX/dplaysp.cpp
===================================================================
--- trunk/src/DPlayX/dplaysp.cpp	(revision 4314)
+++ trunk/src/DPlayX/dplaysp.cpp	(revision 4314)
@@ -0,0 +1,928 @@
+/* This contains the implementation of the interface Service
+ * Providers require to communicate with Direct Play
+ *
+ * Copyright 2000 Peter Hunnisett <hunnise@nortelnetworks.com>
+ */
+
+#include <string.h>
+
+#include <odin.h>
+#define ICOM_CINTERFACE 1
+#define CINTERFACE
+
+#include "winerror.h"
+#include "heap.h"
+#include "debugtools.h"
+
+#include "dpinit.h"
+#include "dplaysp.h"
+#include "dplay_global.h"
+#include "name_server.h"
+#include "dplayx_messages.h"
+
+#include "dplayx_global.h" /* FIXME: For global hack */
+
+/* FIXME: Need to add interface locking inside procedures */
+
+DEFAULT_DEBUG_CHANNEL(dplay);
+
+#define debugstr_guid(a) a
+
+
+/* Prototypes */
+static BOOL DPSP_CreateIUnknown( LPVOID lpSP );
+static BOOL DPSP_DestroyIUnknown( LPVOID lpSP );
+static BOOL DPSP_CreateDirectPlaySP( LPVOID lpSP, IDirectPlay2Impl* dp );
+static BOOL DPSP_DestroyDirectPlaySP( LPVOID lpSP );
+
+
+/* Predefine the interface */
+typedef struct IDirectPlaySPImpl IDirectPlaySPImpl;
+
+typedef struct tagDirectPlaySPIUnknownData
+{
+  ULONG             ulObjRef;
+  CRITICAL_SECTION  DPSP_lock;
+} DirectPlaySPIUnknownData;
+
+typedef struct tagDirectPlaySPData
+{
+  LPVOID lpSpRemoteData;
+  DWORD  dwSpRemoteDataSize; /* Size of data pointed to by lpSpRemoteData */
+
+  LPVOID lpSpLocalData;
+  DWORD  dwSpLocalDataSize; /* Size of data pointed to by lpSpLocalData */
+
+  IDirectPlay2Impl* dplay; /* FIXME: This should perhaps be iface not impl */
+
+  LPVOID lpPlayerData; /* FIXME: Need to figure out how this actually behaves */
+  DWORD dwPlayerDataSize;
+} DirectPlaySPData;
+
+#define DPSP_IMPL_FIELDS \
+   ULONG ulInterfaceRef; \
+   DirectPlaySPIUnknownData* unk; \
+   DirectPlaySPData* sp;
+
+struct IDirectPlaySPImpl
+{
+  ICOM_VFIELD(IDirectPlaySP);
+  DPSP_IMPL_FIELDS
+};
+
+/* Forward declaration of virtual tables */
+extern ICOM_VTABLE(IDirectPlaySP) directPlaySPVT;
+
+
+
+/* Create the SP interface */
+extern
+HRESULT DPSP_CreateInterface( REFIID riid, LPVOID* ppvObj, IDirectPlay2Impl* dp )
+{
+  TRACE( " for %s\n", debugstr_guid( riid ) );
+
+  *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                       sizeof( IDirectPlaySPImpl ) );
+
+  if( *ppvObj == NULL )
+  {
+    return DPERR_OUTOFMEMORY;
+  }
+
+  if( IsEqualGUID( &IID_IDirectPlaySP, riid ) )
+  {
+    ICOM_THIS(IDirectPlaySPImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlaySPVT;
+  }
+  else
+  {
+    /* Unsupported interface */
+    HeapFree( GetProcessHeap(), 0, *ppvObj );
+    *ppvObj = NULL;
+
+    return E_NOINTERFACE;
+  }
+
+  /* Initialize it */
+  if( DPSP_CreateIUnknown( *ppvObj ) &&
+      DPSP_CreateDirectPlaySP( *ppvObj, dp )
+    )
+  {
+    IDirectPlaySP_AddRef( (LPDIRECTPLAYSP)*ppvObj );
+    return S_OK;
+  }
+
+  /* Initialize failed, destroy it */
+  DPSP_DestroyDirectPlaySP( *ppvObj );
+  DPSP_DestroyIUnknown( *ppvObj );
+
+  HeapFree( GetProcessHeap(), 0, *ppvObj );
+  *ppvObj = NULL;
+
+  return DPERR_NOMEMORY;
+}
+
+static BOOL DPSP_CreateIUnknown( LPVOID lpSP )
+{
+  ICOM_THIS(IDirectPlaySPImpl,lpSP);
+
+  This->unk = (DirectPlaySPIUnknownData*)HeapAlloc( GetProcessHeap(),
+                                                    HEAP_ZERO_MEMORY,
+                                                    sizeof( *(This->unk) ) );
+
+  if ( This->unk == NULL )
+  {
+    return FALSE;
+  }
+
+  InitializeCriticalSection( &This->unk->DPSP_lock );
+
+  return TRUE;
+}
+
+static BOOL DPSP_DestroyIUnknown( LPVOID lpSP )
+{
+  ICOM_THIS(IDirectPlaySPImpl,lpSP);
+
+  DeleteCriticalSection( &This->unk->DPSP_lock );
+  HeapFree( GetProcessHeap(), 0, This->unk );
+
+  return TRUE;
+}
+
+
+static BOOL DPSP_CreateDirectPlaySP( LPVOID lpSP, IDirectPlay2Impl* dp )
+{
+  ICOM_THIS(IDirectPlaySPImpl,lpSP);
+
+  This->sp = (DirectPlaySPData*)HeapAlloc( GetProcessHeap(),
+                                           HEAP_ZERO_MEMORY,
+                                           sizeof( *(This->sp) ) );
+
+  if ( This->sp == NULL )
+  {
+    return FALSE;
+  }
+
+  This->sp->dplay = dp;
+
+  /* Normally we should be keeping a reference, but since only the dplay
+   * interface that created us can destroy us, we do not keep a reference
+   * to it (ie we'd be stuck with always having one reference to the dplay
+   * object, and hence us, around).
+   * NOTE: The dp object does reference count us.
+   */
+  /* IDirectPlayX_AddRef( (LPDIRECTPLAY2)dp ); */
+
+  return TRUE;
+}
+
+static BOOL DPSP_DestroyDirectPlaySP( LPVOID lpSP )
+{
+  ICOM_THIS(IDirectPlaySPImpl,lpSP);
+
+  /* Normally we should be keeping a reference, but since only the dplay
+   * interface that created us can destroy us, we do not keep a reference
+   * to it (ie we'd be stuck with always having one reference to the dplay
+   * object, and hence us, around).
+   * NOTE: The dp object does reference count us.
+   */
+  /*IDirectPlayX_Release( (LPDIRECTPLAY2)This->sp->dplay ); */
+
+  HeapFree( GetProcessHeap(), 0, This->sp->lpSpRemoteData );
+  HeapFree( GetProcessHeap(), 0, This->sp->lpSpLocalData );
+
+  /* FIXME: Need to delete player queue */
+
+  HeapFree( GetProcessHeap(), 0, This->sp );
+  return TRUE;
+}
+
+/* Interface implementation */
+
+static HRESULT WINAPI DPSP_QueryInterface
+( LPDIRECTPLAYSP iface,
+  REFIID riid,
+  LPVOID* ppvObj )
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+  TRACE("(%p)->(%s,%p)\n", This, debugstr_guid( riid ), ppvObj );
+
+  *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                       sizeof( IDirectPlaySPImpl ) );
+
+  if( *ppvObj == NULL )
+  {
+    return DPERR_OUTOFMEMORY;
+  }
+
+  CopyMemory( *ppvObj, iface, sizeof( IDirectPlaySPImpl )  );
+  (*(IDirectPlaySPImpl**)ppvObj)->ulInterfaceRef = 0;
+
+  if( IsEqualGUID( &IID_IDirectPlaySP, riid ) )
+  {
+    ICOM_THIS(IDirectPlaySPImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlaySPVT;
+  }
+  else
+  {
+    /* Unsupported interface */
+    HeapFree( GetProcessHeap(), 0, *ppvObj );
+    *ppvObj = NULL;
+
+    return E_NOINTERFACE;
+  }
+
+  IDirectPlaySP_AddRef( (LPDIRECTPLAYSP)*ppvObj );
+
+  return S_OK;
+}
+
+static ULONG WINAPI DPSP_AddRef
+( LPDIRECTPLAYSP iface )
+{
+  ULONG ulInterfaceRefCount, ulObjRefCount;
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  ulObjRefCount       = InterlockedIncrement( (LPLONG)&This->unk->ulObjRef );
+  ulInterfaceRefCount = InterlockedIncrement( (LPLONG)&This->ulInterfaceRef );
+
+  TRACE( "ref count incremented to %lu:%lu for %p\n",
+         ulInterfaceRefCount, ulObjRefCount, This );
+
+  return ulObjRefCount;
+}
+
+static ULONG WINAPI DPSP_Release
+( LPDIRECTPLAYSP iface )
+{
+  ULONG ulInterfaceRefCount, ulObjRefCount;
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  ulObjRefCount       = InterlockedDecrement( (LPLONG)&This->unk->ulObjRef );
+  ulInterfaceRefCount = InterlockedDecrement( (LPLONG)&This->ulInterfaceRef );
+
+  TRACE( "ref count decremented to %lu:%lu for %p\n",
+         ulInterfaceRefCount, ulObjRefCount, This );
+
+  /* Deallocate if this is the last reference to the object */
+  if( ulObjRefCount == 0 )
+  {
+     DPSP_DestroyDirectPlaySP( This );
+     DPSP_DestroyIUnknown( This );
+  }
+
+  if( ulInterfaceRefCount == 0 )
+  {
+    HeapFree( GetProcessHeap(), 0, This );
+  }
+
+  return ulInterfaceRefCount;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_AddMRUEntry
+( LPDIRECTPLAYSP iface,
+  LPCWSTR lpSection,
+  LPCWSTR lpKey,
+  LPCVOID lpData,
+  DWORD   dwDataSize,
+  DWORD   dwMaxEntries
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  FIXME( "(%p)->(%p,%p%p,0x%08lx,0x%08lx): stub\n",
+         This, lpSection, lpKey, lpData, dwDataSize, dwMaxEntries );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_CreateAddress
+( LPDIRECTPLAYSP iface,
+  REFGUID guidSP,
+  REFGUID guidDataType,
+  LPCVOID lpData,
+  DWORD   dwDataSize,
+  LPVOID  lpAddress,
+  LPDWORD lpdwAddressSize
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  FIXME( "(%p)->(%s,%s,%p,0x%08lx,%p,%p): stub\n",
+         This, debugstr_guid(guidSP), debugstr_guid(guidDataType),
+         lpData, dwDataSize, lpAddress, lpdwAddressSize );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_EnumAddress
+( LPDIRECTPLAYSP iface,
+  LPDPENUMADDRESSCALLBACK lpEnumAddressCallback,
+  LPCVOID lpAddress,
+  DWORD dwAddressSize,
+  LPVOID lpContext
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  TRACE( "(%p)->(%p,%p,0x%08lx,%p)\n",
+         This, lpEnumAddressCallback, lpAddress, dwAddressSize, lpContext );
+
+  DPL_EnumAddress( lpEnumAddressCallback, lpAddress, dwAddressSize, lpContext );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_EnumMRUEntries
+( LPDIRECTPLAYSP iface,
+  LPCWSTR lpSection,
+  LPCWSTR lpKey,
+  LPENUMMRUCALLBACK lpEnumMRUCallback,
+  LPVOID lpContext
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  FIXME( "(%p)->(%p,%p,%p,%p,): stub\n",
+         This, lpSection, lpKey, lpEnumMRUCallback, lpContext );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_GetPlayerFlags
+( LPDIRECTPLAYSP iface,
+  DPID idPlayer,
+  LPDWORD lpdwPlayerFlags
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  FIXME( "(%p)->(0x%08lx,%p): stub\n",
+         This, idPlayer, lpdwPlayerFlags );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_GetSPPlayerData
+( LPDIRECTPLAYSP iface,
+  DPID idPlayer,
+  LPVOID* lplpData,
+  LPDWORD lpdwDataSize,
+  DWORD dwFlags
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  TRACE( "Called on process 0x%08lx\n", GetCurrentProcessId() );
+  FIXME( "(%p)->(0x%08lx,%p,%p,0x%08lx): stub\n",
+         This, idPlayer, lplpData, lpdwDataSize, dwFlags );
+
+  *lplpData     = This->sp->lpPlayerData;
+  *lpdwDataSize = This->sp->dwPlayerDataSize;
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_HandleMessage
+( LPDIRECTPLAYSP iface,
+  LPVOID lpMessageBody,
+  DWORD  dwMessageBodySize,
+  LPVOID lpMessageHeader
+)
+{
+  LPDPMSG_SENDENVELOPE lpMsg = (LPDPMSG_SENDENVELOPE)lpMessageBody;
+  HRESULT hr = DPERR_GENERIC;
+  WORD wCommandId;
+  WORD wVersion;
+
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  TRACE( "Called on process 0x%08lx\n", GetCurrentProcessId() );
+  FIXME( "(%p)->(%p,0x%08lx,%p): mostly stub\n",
+         This, lpMessageBody, dwMessageBodySize, lpMessageHeader );
+
+  wCommandId = lpMsg->wCommandId;
+  wVersion   = lpMsg->wVersion;
+
+  TRACE( "Incomming message has envelope of 0x%08lx, %u, %u\n",
+         lpMsg->dwMagic, wCommandId, wVersion );
+
+  if( lpMsg->dwMagic != DPMSGMAGIC_DPLAYMSG )
+  {
+    ERR( "Unknown magic 0x%08lx!\n", lpMsg->dwMagic );
+  }
+
+  switch( lpMsg->wCommandId )
+  {
+    /* Name server needs to handle this request */
+    case DPMSGCMD_ENUMSESSIONSREQUEST:
+    {
+      DPSP_REPLYDATA data;
+
+      data.lpSPMessageHeader = lpMessageHeader;
+      data.idNameServer      = 0;
+      data.lpISP             = iface;
+
+      NS_ReplyToEnumSessionsRequest( lpMessageBody, &data, This->sp->dplay );
+
+      hr = (This->sp->dplay->dp2->spData.lpCB->Reply)( &data );
+
+      if( FAILED(hr) )
+      {
+        ERR( "Reply failed 0x%08lx\n", hr );
+      }
+
+      break;
+    }
+
+    /* Name server needs to handle this request */
+    case DPMSGCMD_ENUMSESSIONSREPLY:
+    {
+      NS_SetRemoteComputerAsNameServer( lpMessageHeader,
+                                        This->sp->dplay->dp2->spData.dwSPHeaderSize,
+                                        (LPDPMSG_ENUMSESSIONSREPLY)lpMessageBody,
+                                        This->sp->dplay->dp2->lpNameServerData );
+
+      /* No reply expected */
+
+      break;
+    }
+
+    case DPMSGCMD_NEWPLAYERIDREPLY:
+    case DPMSGCMD_REQUESTNEWPLAYERID:
+    {
+      DPSP_REPLYDATA data;
+
+      data.lpMessage     = NULL;
+      data.dwMessageSize = 0;
+
+      /* Pass this message to the dplay interface to handle */
+      DP_HandleMessage( This->sp->dplay, lpMessageBody, dwMessageBodySize,
+                        lpMessageHeader, wCommandId, wVersion,
+                        &data.lpMessage, &data.dwMessageSize );
+
+      /* Do we want a reply? */
+      if( data.lpMessage != NULL )
+      {
+        HRESULT hr;
+
+        data.lpSPMessageHeader = lpMessageHeader;
+        data.idNameServer      = 0;
+        data.lpISP             = iface;
+
+        hr = (This->sp->dplay->dp2->spData.lpCB->Reply)( &data );
+
+        if( FAILED(hr) )
+        {
+          ERR( "Reply failed %s\n", DPLAYX_HresultToString(hr) );
+        }
+      }
+
+      break;
+    }
+
+    default:
+      FIXME( "Unknown Command of %u and size 0x%08lx\n",
+             lpMsg->wCommandId, dwMessageBodySize );
+  }
+
+#if 0
+  HRESULT hr = DP_OK;
+  HANDLE  hReceiveEvent = 0;
+  /* FIXME: Aquire some sort of interface lock */
+  /* FIXME: Need some sort of context for this callback. Need to determine
+   *        how this is actually done with the SP
+   */
+  /* FIXME: Who needs to delete the message when done? */
+  switch( lpMsg->dwType )
+  {
+    case DPSYS_CREATEPLAYERORGROUP:
+    {
+      LPDPMSG_CREATEPLAYERORGROUP msg = (LPDPMSG_CREATEPLAYERORGROUP)lpMsg;
+
+      if( msg->dwPlayerType == DPPLAYERTYPE_PLAYER )
+      {
+        hr = DP_IF_CreatePlayer( This, lpMessageHeader, msg->dpId,
+                                 &msg->dpnName, 0, msg->lpData,
+                                 msg->dwDataSize, msg->dwFlags, ... );
+      }
+      else if( msg->dwPlayerType == DPPLAYERTYPE_GROUP )
+      {
+        /* Group in group situation? */
+        if( msg->dpIdParent == DPID_NOPARENT_GROUP )
+        {
+          hr = DP_IF_CreateGroup( This, lpMessageHeader, msg->dpId,
+                                  &msg->dpnName, 0, msg->lpData,
+                                  msg->dwDataSize, msg->dwFlags, ... );
+        }
+        else /* Group in Group */
+        {
+          hr = DP_IF_CreateGroupInGroup( This, lpMessageHeader, msg->dpIdParent,
+                                         &msg->dpnName, 0, msg->lpData,
+                                         msg->dwDataSize, msg->dwFlags, ... );
+        }
+      }
+      else /* Hmmm? */
+      {
+        ERR( "Corrupt msg->dwPlayerType for DPSYS_CREATEPLAYERORGROUP\n" );
+        return;
+      }
+
+      break;
+    }
+
+    case DPSYS_DESTROYPLAYERORGROUP:
+    {
+      LPDPMSG_DESTROYPLAYERORGROUP msg = (LPDPMSG_DESTROYPLAYERORGROUP)lpMsg;
+
+      if( msg->dwPlayerType == DPPLAYERTYPE_PLAYER )
+      {
+        hr = DP_IF_DestroyPlayer( This, msg->dpId, ... );
+      }
+      else if( msg->dwPlayerType == DPPLAYERTYPE_GROUP )
+      {
+        hr = DP_IF_DestroyGroup( This, msg->dpId, ... );
+      }
+      else /* Hmmm? */
+      {
+        ERR( "Corrupt msg->dwPlayerType for DPSYS_DESTROYPLAYERORGROUP\n" );
+        return;
+      }
+
+      break;
+    }
+
+    case DPSYS_ADDPLAYERTOGROUP:
+    {
+      LPDPMSG_ADDPLAYERTOGROUP msg = (LPDPMSG_ADDPLAYERTOGROUP)lpMsg;
+
+      hr = DP_IF_AddPlayerToGroup( This, msg->dpIdGroup, msg->dpIdPlayer, ... );
+      break;
+    }
+
+    case DPSYS_DELETEPLAYERFROMGROUP:
+    {
+      LPDPMSG_DELETEPLAYERFROMGROUP msg = (LPDPMSG_DELETEPLAYERFROMGROUP)lpMsg;
+
+      hr = DP_IF_DeletePlayerFromGroup( This, msg->dpIdGroup, msg->dpIdPlayer,
+                                        ... );
+
+      break;
+    }
+
+    case DPSYS_SESSIONLOST:
+    {
+      LPDPMSG_SESSIONLOST msg = (LPDPMSG_SESSIONLOST)lpMsg;
+
+      FIXME( "DPSYS_SESSIONLOST not handled\n" );
+
+      break;
+    }
+
+    case DPSYS_HOST:
+    {
+      LPDPMSG_HOST msg = (LPDPMSG_HOST)lpMsg;
+
+      FIXME( "DPSYS_HOST not handled\n" );
+
+      break;
+    }
+
+    case DPSYS_SETPLAYERORGROUPDATA:
+    {
+      LPDPMSG_SETPLAYERORGROUPDATA msg = (LPDPMSG_SETPLAYERORGROUPDATA)lpMsg;
+
+      if( msg->dwPlayerType == DPPLAYERTYPE_PLAYER )
+      {
+        hr = DP_IF_SetPlayerData( This, msg->dpId, msg->lpData, msg->dwDataSize,                                  DPSET_REMOTE, ... );
+      }
+      else if( msg->dwPlayerType == DPPLAYERTYPE_GROUP )
+      {
+        hr = DP_IF_SetGroupData( This, msg->dpId, msg->lpData, msg->dwDataSize,
+                                 DPSET_REMOTE, ... );
+      }
+      else /* Hmmm? */
+      {
+        ERR( "Corrupt msg->dwPlayerType for LPDPMSG_SETPLAYERORGROUPDATA\n" );
+        return;
+      }
+
+      break;
+    }
+
+    case DPSYS_SETPLAYERORGROUPNAME:
+    {
+      LPDPMSG_SETPLAYERORGROUPNAME msg = (LPDPMSG_SETPLAYERORGROUPNAME)lpMsg;
+
+      if( msg->dwPlayerType == DPPLAYERTYPE_PLAYER )
+      {
+        hr = DP_IF_SetPlayerName( This, msg->dpId, msg->dpnName, ... );
+      }
+      else if( msg->dwPlayerType == DPPLAYERTYPE_GROUP )
+      {
+        hr = DP_IF_SetGroupName( This, msg->dpId, msg->dpnName, ... );
+      }
+      else /* Hmmm? */
+      {
+        ERR( "Corrupt msg->dwPlayerType for LPDPMSG_SETPLAYERORGROUPDATA\n" );
+        return;
+      }
+
+      break;
+    }
+
+    case DPSYS_SETSESSIONDESC;
+    {
+      LPDPMSG_SETSESSIONDESC msg = (LPDPMSG_SETSESSIONDESC)lpMsg;
+
+      hr = DP_IF_SetSessionDesc( This, &msg->dpDesc );
+
+      break;
+    }
+
+    case DPSYS_ADDGROUPTOGROUP:
+    {
+      LPDPMSG_ADDGROUPTOGROUP msg = (LPDPMSG_ADDGROUPTOGROUP)lpMsg;
+
+      hr = DP_IF_AddGroupToGroup( This, msg->dpIdParentGroup, msg->dpIdGroup,
+                                  ... );
+
+      break;
+    }
+
+    case DPSYS_DELETEGROUPFROMGROUP:
+    {
+      LPDPMSG_DELETEGROUPFROMGROUP msg = (LPDPMSG_DELETEGROUPFROMGROUP)lpMsg;
+
+      hr = DP_IF_DeleteGroupFromGroup( This, msg->dpIdParentGroup,
+                                       msg->dpIdGroup, ... );
+
+      break;
+    }
+
+    case DPSYS_SECUREMESSAGE:
+    {
+      LPDPMSG_SECUREMESSAGE msg = (LPDPMSG_SECUREMESSAGE)lpMsg;
+
+      FIXME( "DPSYS_SECUREMESSAGE not implemented\n" );
+
+      break;
+    }
+
+    case DPSYS_STARTSESSION:
+    {
+      LPDPMSG_STARTSESSION msg = (LPDPMSG_STARTSESSION)lpMsg;
+
+      FIXME( "DPSYS_STARTSESSION not implemented\n" );
+
+      break;
+    }
+
+    case DPSYS_CHAT:
+    {
+      LPDPMSG_CHAT msg = (LPDPMSG_CHAT)lpMsg;
+
+      FIXME( "DPSYS_CHAT not implemeneted\n" );
+
+      break;
+    }
+
+    case DPSYS_SETGROUPOWNER:
+    {
+      LPDPMSG_SETGROUPOWNER msg = (LPDPMSG_SETGROUPOWNER)lpMsg;
+
+      FIXME( "DPSYS_SETGROUPOWNER not implemented\n" );
+
+      break;
+    }
+
+    case DPSYS_SENDCOMPLETE:
+    {
+      LPDPMSG_SENDCOMPLETE msg = (LPDPMSG_SENDCOMPLETE)lpMsg;
+
+      FIXME( "DPSYS_SENDCOMPLETE not implemented\n" );
+
+      break;
+    }
+
+    default:
+    {
+      /* NOTE: This should be a user defined type. There is nothing that we
+       *       need to do with it except queue it.
+       */
+      TRACE( "Received user message type(?) 0x%08lx through SP.\n",
+              lpMsg->dwType );
+      break;
+    }
+  }
+
+  FIXME( "Queue message in the receive queue. Need some context data!\n" );
+
+  if( FAILED(hr) )
+  {
+    ERR( "Unable to perform action for msg type 0x%08lx\n", lpMsg->dwType );
+  }
+  /* If a receieve event was registered for this player, invoke it */
+  if( hReceiveEvent )
+  {
+    SetEvent( hReceiveEvent );
+  }
+#endif
+
+  return hr;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_SetSPPlayerData
+( LPDIRECTPLAYSP iface,
+  DPID idPlayer,
+  LPVOID lpData,
+  DWORD dwDataSize,
+  DWORD dwFlags
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  /* FIXME: I'm not sure if this stuff should be associated with the DPlay
+   *        player lists. How else would this stuff get deleted?
+   */
+
+  TRACE( "Called on process 0x%08lx\n", GetCurrentProcessId() );
+  FIXME( "(%p)->(0x%08lx,%p,0x%08lx,0x%08lx): stub\n",
+         This, idPlayer, lpData, dwDataSize, dwFlags );
+
+  This->sp->lpPlayerData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwDataSize );
+
+  This->sp->dwPlayerDataSize = dwDataSize;
+  CopyMemory( This->sp->lpPlayerData, lpData, dwDataSize );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_CreateCompoundAddress
+( LPDIRECTPLAYSP iface,
+  LPCDPCOMPOUNDADDRESSELEMENT lpElements,
+  DWORD dwElementCount,
+  LPVOID lpAddress,
+  LPDWORD lpdwAddressSize
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  FIXME( "(%p)->(%p,0x%08lx,%p,%p): stub\n",
+         This, lpElements, dwElementCount, lpAddress, lpdwAddressSize );
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_GetSPData
+( LPDIRECTPLAYSP iface,
+  LPVOID* lplpData,
+  LPDWORD lpdwDataSize,
+  DWORD dwFlags
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  TRACE( "Called on process 0x%08lx\n", GetCurrentProcessId() );
+  TRACE( "(%p)->(%p,%p,0x%08lx)\n",
+         This, lplpData, lpdwDataSize, dwFlags );
+
+#if 0
+  /* This is what the documentation says... */
+  if( dwFlags != 0 )
+  {
+    return DPERR_INVALIDPARAMS;
+  }
+#else
+  /* ... but most service providers call this with 1 */
+  /* Guess that this is using a DPSET_LOCAL or DPSET_REMOTE type of
+   * thing?
+   */
+  if( dwFlags != 0 )
+  {
+    FIXME( "Undocumented dwFlags 0x%08lx used\n", dwFlags );
+  }
+#endif
+
+  /* Yes, we're supposed to return a pointer to the memory we have stored! */
+  if( dwFlags == DPSET_REMOTE )
+  {
+    *lpdwDataSize = This->sp->dwSpRemoteDataSize;
+    *lplpData     = This->sp->lpSpRemoteData;
+  }
+  else if( dwFlags == DPSET_LOCAL )
+  {
+    *lpdwDataSize = This->sp->dwSpLocalDataSize;
+    *lplpData     = This->sp->lpSpLocalData;
+  }
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlaySPImpl_SetSPData
+( LPDIRECTPLAYSP iface,
+  LPVOID lpData,
+  DWORD dwDataSize,
+  DWORD dwFlags
+)
+{
+  LPVOID lpSpData;
+
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  TRACE( "Called on process 0x%08lx\n", GetCurrentProcessId() );
+  TRACE( "(%p)->(%p,0x%08lx,0x%08lx)\n",
+         This, lpData, dwDataSize, dwFlags );
+
+#if 0
+  /* This is what the documentation says... */
+  if( dwFlags != 0 )
+  {
+    return DPERR_INVALIDPARAMS;
+  }
+#else
+  /* ... but most service providers call this with 1 */
+  /* Guess that this is using a DPSET_LOCAL or DPSET_REMOTE type of
+   * thing?
+   */
+  if( dwFlags != 0 )
+  {
+    FIXME( "Undocumented dwFlags 0x%08lx used\n", dwFlags );
+  }
+#endif
+
+  if( dwFlags == DPSET_REMOTE )
+  {
+    lpSpData = DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY, dwDataSize );
+  }
+  else
+  {
+    lpSpData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwDataSize );
+  }
+
+  CopyMemory( lpSpData, lpData, dwDataSize );
+
+  /* If we have data already allocated, free it and replace it */
+  if( dwFlags == DPSET_REMOTE )
+  {
+    /* FIXME: This doesn't strictly make sense as there is no means to share
+     *        this shared data. Must be misinterpreting something...
+     */
+    if( This->sp->lpSpRemoteData )
+    {
+      DPLAYX_PrivHeapFree( This->sp->lpSpRemoteData );
+    }
+
+    /* NOTE: dwDataSize is also stored in the heap structure */
+    This->sp->dwSpRemoteDataSize = dwDataSize;
+    This->sp->lpSpRemoteData = lpSpData;
+  }
+  else if ( dwFlags == DPSET_LOCAL )
+  {
+    if( This->sp->lpSpLocalData )
+    {
+      HeapFree( GetProcessHeap(), 0, This->sp->lpSpLocalData );
+    }
+
+    This->sp->lpSpLocalData     = lpSpData;
+    This->sp->dwSpLocalDataSize = dwDataSize;
+  }
+
+  return DP_OK;
+}
+
+static VOID WINAPI IDirectPlaySPImpl_SendComplete
+( LPDIRECTPLAYSP iface,
+  LPVOID unknownA,
+  DWORD unknownB
+)
+{
+  ICOM_THIS(IDirectPlaySPImpl,iface);
+
+  FIXME( "(%p)->(%p,0x%08lx): stub\n",
+         This, unknownA, unknownB );
+}
+
+
+struct ICOM_VTABLE(IDirectPlaySP) directPlaySPVT =
+{
+  ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+
+  DPSP_QueryInterface,
+  DPSP_AddRef,
+  DPSP_Release,
+
+  IDirectPlaySPImpl_AddMRUEntry,
+  IDirectPlaySPImpl_CreateAddress,
+  IDirectPlaySPImpl_EnumAddress,
+  IDirectPlaySPImpl_EnumMRUEntries,
+  IDirectPlaySPImpl_GetPlayerFlags,
+  IDirectPlaySPImpl_GetSPPlayerData,
+  IDirectPlaySPImpl_HandleMessage,
+  IDirectPlaySPImpl_SetSPPlayerData,
+  IDirectPlaySPImpl_CreateCompoundAddress,
+  IDirectPlaySPImpl_GetSPData,
+  IDirectPlaySPImpl_SetSPData,
+  IDirectPlaySPImpl_SendComplete
+};
Index: trunk/src/DPlayX/dplaysp.h
===================================================================
--- trunk/src/DPlayX/dplaysp.h	(revision 4314)
+++ trunk/src/DPlayX/dplaysp.h	(revision 4314)
@@ -0,0 +1,337 @@
+#ifndef __WINE_DIRECT_PLAY_SP_H
+#define __WINE_DIRECT_PLAY_SP_H
+
+#include "dplay.h"
+#include "dplobby.h"
+
+/* GUID for IDirectPlaySP  {0C9F6360-CC61-11cf-ACEC-00AA006886E3} */
+DEFINE_GUID(IID_IDirectPlaySP, 0xc9f6360, 0xcc61, 0x11cf, 0xac, 0xec, 0x0, 0xaa, 0x0, 0x68, 0x86, 0xe3);
+typedef struct IDirectPlaySP IDirectPlaySP, *LPDIRECTPLAYSP;
+
+
+typedef BOOL (CALLBACK* LPENUMMRUCALLBACK)( LPCVOID lpData,
+                                            DWORD  dwDataSize,
+                                            LPVOID lpContext );
+
+/* For SP. Top 16 bits is dplay, bottom 16 is SP */
+#define DPSP_MAJORVERSION 0x00060000
+#define DPSP_DX5VERSION   0x00050000
+#define DPSP_DX3VERSION   0x00040000
+
+#define DPSP_MAJORVERSIONMASK 0xFFFF0000
+#define DPSP_MINORVERSIONMASK 0x0000FFFF
+
+
+/* Some flags */
+#define DPLAYI_PLAYER_SYSPLAYER      0x00000001
+#define DPLAYI_PLAYER_NAMESRVR       0x00000002
+#define DPLAYI_PLAYER_PLAYERINGROUP  0x00000004
+#define DPLAYI_PLAYER_PLAYERLOCAL    0x00000008
+#define DPLAYI_GROUP_SYSGROUP        0x00000020
+#define DPLAYI_GROUP_DPLAYOWNS       0x00000040
+#define DPLAYI_PLAYER_APPSERVER      0x00000080
+#define DPLAYI_GROUP_HIDDEN          0x00000400
+
+/* Define the COM interface */
+#define ICOM_INTERFACE IDirectPlaySP
+#define IDirectPlaySP_METHODS \
+   ICOM_METHOD5(HRESULT,AddMRUEntry, LPCWSTR,lpSection, LPCWSTR,lpKey, LPCVOID,lpData, DWORD,dwDataSize, DWORD,dwMaxEntries ) \
+   ICOM_METHOD6(HRESULT,CreateAddress, REFGUID,guidSP, REFGUID,guidDataType, LPCVOID,lpData, DWORD,dwDataSize, LPVOID,lpAddress,LPDWORD,lpdwAddressSize) \
+   ICOM_METHOD4(HRESULT,EnumAddress, LPDPENUMADDRESSCALLBACK,lpEnumAddressCallback, LPCVOID,lpAddress, DWORD,dwAddressSize, LPVOID,lpContext ) \
+   ICOM_METHOD4(HRESULT,EnumMRUEntries, LPCWSTR,lpSection, LPCWSTR,lpKey, LPENUMMRUCALLBACK,lpEnumMRUCallback, LPVOID,lpContext ) \
+   ICOM_METHOD2(HRESULT,GetPlayerFlags,       DPID,idPlayer, LPDWORD,lpdwPlayerFlags ) \
+   ICOM_METHOD4(HRESULT,GetSPPlayerData,      DPID,idPlayer, LPVOID*,lplpData, LPDWORD,lpdwDataSize, DWORD,dwFlags ) \
+   ICOM_METHOD3(HRESULT,HandleMessage,        LPVOID,lpMessageBody, DWORD,dwMessageBodySize, LPVOID,lpMessageHeader ) \
+   ICOM_METHOD4(HRESULT,SetSPPlayerData,      DPID,idPlayer, LPVOID,lpData, DWORD,dwDataSize, DWORD,dwFlags ) \
+   ICOM_METHOD4(HRESULT,CreateCompoundAddress, LPCDPCOMPOUNDADDRESSELEMENT,lpElements, DWORD,dwElementCount, LPVOID,lpAddress, LPDWORD,lpdwAddressSize ) \
+   ICOM_METHOD3(HRESULT,GetSPData,                LPVOID*,lplpData, LPDWORD,dwDataSize, DWORD,dwFlags ) \
+   ICOM_METHOD3(HRESULT,SetSPData,                LPVOID,lpData, DWORD,dwDataSize, DWORD,dwFlags ) \
+   ICOM_METHOD2(VOID,SendComplete,              LPVOID,arg1, DWORD,arg2 )
+
+#define IDirectPlaySP_IMETHODS \
+   IUnknown_IMETHODS \
+   IDirectPlaySP_METHODS
+
+ICOM_DEFINE(IDirectPlaySP,IUnknown)
+#undef ICOM_INTERFACE
+
+/*** IUnknown methods ***/
+#define IDirectPlaySP_QueryInterface(p,a,b) ICOM_CALL2(QueryInterface,p,a,b)
+#define IDirectPlaySP_AddRef(p)             ICOM_CALL (AddRef,p)
+#define IDirectPlaySP_Release(p)            ICOM_CALL (Release,p)
+/*** IDirectPlaySP methods ***/
+#define IDirectPlaySP_AddMRUEntry           ICOM_CALL5(AddMRUEntry,p,a,b,c,d,e)
+#define IDirectPlaySP_CreateAddress         ICOM_CALL6(CreateAddress,p,a,b,c,d,e,f)
+#define IDirectPlaySP_EnumAddress           ICOM_CALL4(EnumAddress,p,a,b,c,d)
+#define IDirectPlaySP_EnumMRUEntries        ICOM_CALL4(EnumMRUEntries,p,a,b,c,d)
+#define IDirectPlaySP_GetPlayerFlags        ICOM_CALL2(GetPlayerFlags,p,a,b)
+#define IDirectPlaySP_GetSPPlayerData       ICOM_CALL4(GetSPPlayerData,p,a,b,c,d)
+#define IDirectPlaySP_HandleMessage         ICOM_CALL3(HandleMessage,p,a,b,c)
+#define IDirectPlaySP_SetSPPlayerData       ICOM_CALL4(SetSPPlayerData,p,a,b,c,d)
+#define IDirectPlaySP_CreateCompoundAddress ICOM_CALL4(CreateCompoundAddress,p,a,b,c,d)
+#define IDirectPlaySP_GetSPData             ICOM_CALL3(GetSPData,p,a,b,c)
+#define IDirectPlaySP_SetSPData             ICOM_CALL3(SetSPData,p,a,b,c)
+#define IDirectPlaySP_SendComplete          ICOM_CALL2(SendComplete,p,a,b)
+
+/* SP Callback stuff */
+
+typedef struct tagDPSP_ADDPLAYERTOGROUPDATA
+{
+  DPID           idPlayer;
+  DPID           idGroup;
+  IDirectPlaySP* lpISP;
+} DPSP_ADDPLAYERTOGROUPDATA, *LPDPSP_ADDPLAYERTOGROUPDATA;
+
+typedef struct tagDPSP_CLOSEDATA
+{
+  IDirectPlaySP* lpISP;
+} DPSP_CLOSEDATA, *LPDPSP_CLOSEDATA;
+
+typedef struct tagDPSP_CREATEGROUPDATA
+{
+  DPID           idGroup;
+  DWORD          dwFlags;
+  LPVOID         lpSPMessageHeader;
+  IDirectPlaySP* lpISP;
+} DPSP_CREATEGROUPDATA, *LPDPSP_CREATEGROUPDATA;
+
+typedef struct tagDPSP_CREATEPLAYERDATA
+{
+  DPID           idPlayer;
+  DWORD          dwFlags;
+  LPVOID         lpSPMessageHeader;
+  IDirectPlaySP* lpISP;
+} DPSP_CREATEPLAYERDATA, *LPDPSP_CREATEPLAYERDATA;
+
+typedef struct tagDPSP_DELETEGROUPDATA
+{
+  DPID           idGroup;
+  DWORD          dwFlags;
+  IDirectPlaySP* lpISP;
+} DPSP_DELETEGROUPDATA, *LPDPSP_DELETEGROUPDATA;
+
+typedef struct tagDPSP_DELETEPLAYERDATA
+{
+  DPID           idPlayer;
+  DWORD          dwFlags;
+  IDirectPlaySP* lpISP;
+} DPSP_DELETEPLAYERDATA, *LPDPSP_DELETEPLAYERDATA;
+
+typedef struct tagDPSP_ENUMSESSIONSDATA
+{
+  LPVOID         lpMessage;
+  DWORD          dwMessageSize;
+  IDirectPlaySP* lpISP;
+  BOOL           bReturnStatus;
+} DPSP_ENUMSESSIONSDATA, *LPDPSP_ENUMSESSIONSDATA;
+
+typedef struct _DPSP_GETADDRESSDATA
+{
+  DPID           idPlayer;
+  DWORD          dwFlags;
+  LPDPADDRESS    lpAddress;
+  LPDWORD        lpdwAddressSize;
+  IDirectPlaySP* lpISP;
+} DPSP_GETADDRESSDATA, *LPDPSP_GETADDRESSDATA;
+
+typedef struct tagDPSP_GETADDRESSCHOICESDATA
+{
+  LPDPADDRESS    lpAddress;
+  LPDWORD        lpdwAddressSize;
+  IDirectPlaySP* lpISP;
+} DPSP_GETADDRESSCHOICESDATA, *LPDPSP_GETADDRESSCHOICESDATA;
+
+typedef struct tagDPSP_GETCAPSDATA
+{
+  DPID           idPlayer;
+  LPDPCAPS       lpCaps;
+  DWORD          dwFlags;
+  IDirectPlaySP* lpISP;
+} DPSP_GETCAPSDATA, *LPDPSP_GETCAPSDATA;
+
+typedef struct tagDPSP_OPENDATA
+{
+  BOOL           bCreate;
+  LPVOID         lpSPMessageHeader;
+  IDirectPlaySP* lpISP;
+  BOOL           bReturnStatus;
+  DWORD          dwOpenFlags;
+  DWORD          dwSessionFlags;
+} DPSP_OPENDATA, *LPDPSP_OPENDATA;
+
+typedef struct tagDPSP_REMOVEPLAYERFROMGROUPDATA
+{
+  DPID           idPlayer;
+  DPID           idGroup;
+  IDirectPlaySP* lpISP;
+} DPSP_REMOVEPLAYERFROMGROUPDATA, *LPDPSP_REMOVEPLAYERFROMGROUPDATA;
+
+typedef struct tagDPSP_REPLYDATA
+{
+  LPVOID         lpSPMessageHeader;
+  LPVOID         lpMessage;
+  DWORD          dwMessageSize;
+  DPID           idNameServer;
+  IDirectPlaySP* lpISP;
+} DPSP_REPLYDATA, *LPDPSP_REPLYDATA;
+
+typedef struct tagDPSP_SENDDATA
+{
+  DWORD          dwFlags;
+  DPID           idPlayerTo;
+  DPID           idPlayerFrom;
+  LPVOID         lpMessage;
+  DWORD          dwMessageSize;
+  BOOL           bSystemMessage;
+  IDirectPlaySP* lpISP;
+} DPSP_SENDDATA, *LPDPSP_SENDDATA;
+
+typedef struct tagDPSP_SENDTOGROUPDATA
+{
+  DWORD          dwFlags;
+  DPID           idGroupTo;
+  DPID           idPlayerFrom;
+  LPVOID         lpMessage;
+  DWORD          dwMessageSize;
+  IDirectPlaySP* lpISP;
+} DPSP_SENDTOGROUPDATA, *LPDPSP_SENDTOGROUPDATA;
+
+typedef struct tagDPSP_SENDEXDATA
+{
+  IDirectPlaySP* lpISP;
+  DWORD          dwFlags;
+  DPID           idPlayerTo;
+  DPID           idPlayerFrom;
+  LPSGBUFFER     lpSendBuffers;
+  DWORD          cBuffers;
+  DWORD          dwMessageSize;
+  DWORD          dwPriority;
+  DWORD          dwTimeout;
+  LPVOID         lpDPContext;
+  LPDWORD        lpdwSPMsgID;
+  BOOL           bSystemMessage;
+} DPSP_SENDEXDATA, *LPDPSP_SENDEXDATA;
+
+typedef struct tagDPSP_SENDTOGROUPEXDATA
+{
+  IDirectPlaySP* lpISP;
+  DWORD          dwFlags;
+  DPID           idGroupTo;
+  DPID           idPlayerFrom;
+  LPSGBUFFER     lpSendBuffers;
+  DWORD          cBuffers;
+  DWORD          dwMessageSize;
+  DWORD          dwPriority;
+  DWORD          dwTimeout;
+  LPVOID         lpDPContext;
+  LPDWORD        lpdwSPMsgID;
+} DPSP_SENDTOGROUPEXDATA, *LPDPSP_SENDTOGROUPEXDATA;
+
+typedef struct tagDPSP_GETMESSAGEQUEUEDATA
+{
+  IDirectPlaySP* lpISP;
+  DWORD          dwFlags;
+  DPID           idFrom;
+  DPID           idTo;
+  LPDWORD        lpdwNumMsgs;
+  LPDWORD        lpdwNumBytes;
+} DPSP_GETMESSAGEQUEUEDATA, *LPDPSP_GETMESSAGEQUEUEDATA;
+
+#define DPCANCELSEND_PRIORITY 0x00000001
+#define DPCANCELSEND_ALL      0x00000002
+
+typedef struct tagDPSP_CANCELDATA
+{
+  IDirectPlaySP* lpISP;
+  DWORD          dwFlags;
+  LPRGLPVOID     lprglpvSPMsgID;
+  DWORD          cSPMsgID;
+  DWORD          dwMinPriority;
+  DWORD          dwMaxPriority;
+} DPSP_CANCELDATA, *LPDPSP_CANCELDATA;
+
+typedef struct tagDPSP_SHUTDOWNDATA
+{
+  IDirectPlaySP* lpISP;
+} DPSP_SHUTDOWNDATA, *LPDPSP_SHUTDOWNDATA;
+
+
+/* Prototypes returned by SPInit */
+typedef HRESULT (WINAPI *LPDPSP_CREATEPLAYER)(LPDPSP_CREATEPLAYERDATA);
+typedef HRESULT (WINAPI *LPDPSP_DELETEPLAYER)(LPDPSP_DELETEPLAYERDATA);
+typedef HRESULT (WINAPI *LPDPSP_SEND)(LPDPSP_SENDDATA);
+typedef HRESULT (WINAPI *LPDPSP_ENUMSESSIONS)(LPDPSP_ENUMSESSIONSDATA);
+typedef HRESULT (WINAPI *LPDPSP_REPLY)(LPDPSP_REPLYDATA);
+typedef HRESULT (WINAPI *LPDPSP_SHUTDOWN)(void);
+typedef HRESULT (WINAPI *LPDPSP_CREATEGROUP)(LPDPSP_CREATEGROUPDATA);
+typedef HRESULT (WINAPI *LPDPSP_DELETEGROUP)(LPDPSP_DELETEGROUPDATA);
+typedef HRESULT (WINAPI *LPDPSP_ADDPLAYERTOGROUP)(LPDPSP_ADDPLAYERTOGROUPDATA);
+typedef HRESULT (WINAPI *LPDPSP_REMOVEPLAYERFROMGROUP)(LPDPSP_REMOVEPLAYERFROMGROUPDATA);
+typedef HRESULT (WINAPI *LPDPSP_GETCAPS)(LPDPSP_GETCAPSDATA);
+typedef HRESULT (WINAPI *LPDPSP_GETADDRESS)(LPDPSP_GETADDRESSDATA);
+typedef HRESULT (WINAPI *LPDPSP_GETADDRESSCHOICES)(LPDPSP_GETADDRESSCHOICESDATA);
+typedef HRESULT (WINAPI *LPDPSP_OPEN)(LPDPSP_OPENDATA);
+typedef HRESULT (WINAPI *LPDPSP_CLOSE)(void);
+typedef HRESULT (WINAPI *LPDPSP_SENDTOGROUP)(LPDPSP_SENDTOGROUPDATA);
+typedef HRESULT (WINAPI *LPDPSP_SHUTDOWNEX)(LPDPSP_SHUTDOWNDATA);
+typedef HRESULT (WINAPI *LPDPSP_CLOSEEX)(LPDPSP_CLOSEDATA);
+typedef HRESULT (WINAPI *LPDPSP_SENDEX)(LPDPSP_SENDEXDATA);
+typedef HRESULT (WINAPI *LPDPSP_SENDTOGROUPEX)(LPDPSP_SENDTOGROUPEXDATA);
+typedef HRESULT (WINAPI *LPDPSP_CANCEL)(LPDPSP_CANCELDATA);
+typedef HRESULT (WINAPI *LPDPSP_GETMESSAGEQUEUE)(LPDPSP_GETMESSAGEQUEUEDATA);
+
+
+typedef struct tagDPSP_SPCALLBACKS
+{
+    DWORD                        dwSize;
+    DWORD                        dwVersion;
+
+    LPDPSP_ENUMSESSIONS          EnumSessions;          /* Must be provided */
+    LPDPSP_REPLY                 Reply;                 /* Must be provided */
+    LPDPSP_SEND                  Send;                  /* Must be provided */
+    LPDPSP_ADDPLAYERTOGROUP      AddPlayerToGroup;      /* Optional */
+    LPDPSP_CLOSE                 Close;                 /* Optional */
+    LPDPSP_CREATEGROUP           CreateGroup;           /* Optional */
+    LPDPSP_CREATEPLAYER          CreatePlayer;          /* Optional */
+    LPDPSP_DELETEGROUP           DeleteGroup;           /* Optional */
+    LPDPSP_DELETEPLAYER          DeletePlayer;          /* Optional */
+    LPDPSP_GETADDRESS            GetAddress;            /* Optional */
+    LPDPSP_GETCAPS               GetCaps;               /* Optional */
+    LPDPSP_OPEN                  Open;                  /* Optional */
+    LPDPSP_REMOVEPLAYERFROMGROUP RemovePlayerFromGroup; /* Optional */
+    LPDPSP_SENDTOGROUP           SendToGroup;           /* Optional */
+    LPDPSP_SHUTDOWN              Shutdown;              /* Optional */
+
+    LPDPSP_CLOSEEX               CloseEx;               /* Optional */
+    LPDPSP_SHUTDOWNEX            ShutdownEx;            /* Optional */
+    LPDPSP_GETADDRESSCHOICES     GetAddressChoices;     /* Optional */
+
+    LPDPSP_SENDEX                SendEx;                /* Optional */
+    LPDPSP_SENDTOGROUPEX         SendToGroupEx;         /* Optional */
+    LPDPSP_CANCEL                Cancel;                /* Optional */
+    LPDPSP_GETMESSAGEQUEUE       GetMessageQueue;       /* Optional */
+} DPSP_SPCALLBACKS, *LPDPSP_SPCALLBACKS;
+
+typedef struct tagSPINITDATA
+{
+    LPDPSP_SPCALLBACKS  lpCB;
+    IDirectPlaySP*      lpISP;
+    LPWSTR              lpszName;
+    LPGUID              lpGuid;
+    DWORD               dwReserved1;
+    DWORD               dwReserved2;
+    DWORD               dwSPHeaderSize;
+    LPDPADDRESS         lpAddress;
+    DWORD               dwAddressSize;
+    DWORD               dwSPVersion;
+} SPINITDATA, *LPSPINITDATA;
+
+typedef HRESULT (WINAPI *LPDPSP_SPINIT)(LPSPINITDATA);
+
+/* This variable is exported from the DLL at ordinal 6 to be accessed by the
+ * SP directly
+ */
+extern __declspec(dllimport) DWORD gdwDPlaySPRefCount;
+
+#endif
+
Index: trunk/src/DPlayX/dplayx_global.cpp
===================================================================
--- trunk/src/DPlayX/dplayx_global.cpp	(revision 4314)
+++ trunk/src/DPlayX/dplayx_global.cpp	(revision 4314)
@@ -0,0 +1,1343 @@
+/* dplayx.dll global data implementation.
+ *
+ * Copyright 1999,2000 - Peter Hunnisett
+ *
+ * <presently under construction - contact hunnise@nortelnetworks.com>
+ *
+ */
+
+/* NOTE: Methods that begin with DPLAYX_ are used for dealing with
+ *       dplayx.dll data which is accessible from all processes.
+ */
+
+#include <string.h>
+
+#include <odin.h>
+#define ICOM_CINTERFACE 1
+#define CINTERFACE
+
+#include "debugtools.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/unicode.h"
+#include "heap.h"
+
+#include "wingdi.h"
+#include "winuser.h"
+
+#include "dplayx_global.h"
+#include "dplayx_messages.h" /* For CreateMessageReceptionThread only */
+
+DEFAULT_DEBUG_CHANNEL(dplay);
+
+#define debugstr_guid(a) a
+
+
+/* FIXME: Need to do all that fun other dll referencing type of stuff */
+
+/* Static data for all processes */
+static LPCSTR lpszDplayxSemaName = "WINE_DPLAYX_SM";
+static HANDLE hDplayxSema;
+
+static LPCSTR lpszDplayxFileMapping = "WINE_DPLAYX_FM";
+static HANDLE hDplayxSharedMem;
+
+static LPVOID lpSharedStaticData = NULL;
+
+
+#define DPLAYX_AquireSemaphore()  TRACE( "Waiting for DPLAYX semaphore\n" ); \
+                                  WaitForSingleObject( hDplayxSema, INFINITE );\
+                                  TRACE( "Through wait\n" )
+
+#define DPLAYX_ReleaseSemaphore() ReleaseSemaphore( hDplayxSema, 1, NULL ); \
+                                  TRACE( "DPLAYX Semaphore released\n" ) /* FIXME: Is this correct? */
+
+
+/* HACK for simple global data right now */
+#define dwStaticSharedSize (128 * 1024) /* 128 KBytes */
+#define dwDynamicSharedSize (512 * 1024) /* 512 KBytes */
+#define dwTotalSharedSize  ( dwStaticSharedSize + dwDynamicSharedSize )
+
+
+/* FIXME: Is there no easier way? */
+
+/* Pretend the entire dynamic area is carved up into 512 byte blocks.
+ * Each block has 4 bytes which are 0 unless used */
+#define dwBlockSize 512
+#define dwMaxBlock  (dwDynamicSharedSize/dwBlockSize)
+
+typedef struct
+{
+  DWORD used;
+  DWORD data[dwBlockSize-sizeof(DWORD)];
+} DPLAYX_MEM_SLICE;
+
+static DPLAYX_MEM_SLICE* lpMemArea;
+
+void DPLAYX_PrivHeapFree( LPVOID addr );
+void DPLAYX_PrivHeapFree( LPVOID addr )
+{
+  LPVOID lpAddrStart;
+  DWORD dwBlockUsed;
+
+  /* Handle getting passed a NULL */
+  if( addr == NULL )
+  {
+    return;
+  }
+
+  lpAddrStart = (LPVOID)((DWORD)addr - sizeof(DWORD)); /* Find block header */
+  dwBlockUsed =  ((BYTE*)lpAddrStart - (BYTE*)lpMemArea)/dwBlockSize;
+
+  lpMemArea[ dwBlockUsed ].used = 0;
+}
+
+/* FIXME: This should be static, but is being used for a hack right now */
+LPVOID DPLAYX_PrivHeapAlloc( DWORD flags, DWORD size );
+LPVOID DPLAYX_PrivHeapAlloc( DWORD flags, DWORD size )
+{
+  LPVOID lpvArea = NULL;
+  UINT   uBlockUsed;
+
+  if( size > (dwBlockSize - sizeof(DWORD)) )
+  {
+    FIXME( "Size exceeded. Request of 0x%08lx\n", size );
+    size = dwBlockSize - sizeof(DWORD);
+  }
+
+  /* Find blank area */
+  uBlockUsed = 0;
+  while( ( lpMemArea[ uBlockUsed ].used != 0 ) && ( uBlockUsed <= dwMaxBlock ) ) { uBlockUsed++; }
+
+  if( uBlockUsed <= dwMaxBlock )
+  {
+    /* Set the area used */
+    lpMemArea[ uBlockUsed ].used = 1;
+    lpvArea = &(lpMemArea[ uBlockUsed ].data);
+  }
+  else
+  {
+    ERR( "No free block found\n" );
+    return NULL;
+  }
+
+  if( flags & HEAP_ZERO_MEMORY )
+  {
+    ZeroMemory( lpvArea, size );
+  }
+
+  return lpvArea;
+}
+
+LPSTR DPLAYX_strdupA( DWORD flags, LPCSTR str );
+LPSTR DPLAYX_strdupA( DWORD flags, LPCSTR str )
+{
+  LPSTR p = (LPSTR)DPLAYX_PrivHeapAlloc( flags, strlen(str) + 1 );
+  if(p) {
+    strcpy( p, str );
+  }
+  return p;
+}
+
+LPWSTR DPLAYX_strdupW( DWORD flags, LPCWSTR str );
+LPWSTR DPLAYX_strdupW( DWORD flags, LPCWSTR str )
+{
+  INT len = strlenW(str) + 1;
+  LPWSTR p = (LPWSTR)DPLAYX_PrivHeapAlloc( flags, len * sizeof(WCHAR) );
+  if(p) {
+    strcpyW( p, str );
+  }
+  return p;
+}
+
+
+enum { numSupportedLobbies = 32, numSupportedSessions = 32 };
+typedef struct tagDPLAYX_LOBBYDATA
+{
+  /* Points to lpConn + block of contiguous extra memory for dynamic parts
+   * of the struct directly following
+   */
+  LPDPLCONNECTION lpConn;
+
+  /* Information for dplobby interfaces */
+  DWORD           dwAppID;
+  DWORD           dwAppLaunchedFromID;
+
+  /* Should this lobby app send messages to creator at important life
+   * stages
+   */
+  HANDLE hInformOnAppStart;
+  HANDLE hInformOnAppDeath;
+  HANDLE hInformOnSettingRead;
+
+  /* Sundries */
+  BOOL bWaitForConnectionSettings;
+  DWORD dwLobbyMsgThreadId;
+
+
+} DPLAYX_LOBBYDATA, *LPDPLAYX_LOBBYDATA;
+
+static DPLAYX_LOBBYDATA* lobbyData = NULL;
+/* static DPLAYX_LOBBYDATA lobbyData[ numSupportedLobbies ]; */
+
+static DPSESSIONDESC2* sessionData = NULL;
+/* static DPSESSIONDESC2* sessionData[ numSupportedSessions ]; */
+
+/* Function prototypes */
+DWORD DPLAYX_SizeOfLobbyDataA( LPDPLCONNECTION lpDplData );
+DWORD DPLAYX_SizeOfLobbyDataW( LPDPLCONNECTION lpDplData );
+void DPLAYX_CopyConnStructA( LPDPLCONNECTION dest, LPDPLCONNECTION src );
+void DPLAYX_CopyConnStructW( LPDPLCONNECTION dest, LPDPLCONNECTION src );
+BOOL DPLAYX_IsAppIdLobbied( DWORD dwAppId, LPDPLAYX_LOBBYDATA* dplData );
+void DPLAYX_InitializeLobbyDataEntry( LPDPLAYX_LOBBYDATA lpData );
+BOOL DPLAYX_CopyIntoSessionDesc2A( LPDPSESSIONDESC2  lpSessionDest,
+                                   LPCDPSESSIONDESC2 lpSessionSrc );
+
+
+
+/***************************************************************************
+ * Called to initialize the global data. This will only be used on the
+ * loading of the dll
+ ***************************************************************************/
+BOOL DPLAYX_ConstructData(void)
+{
+  SECURITY_ATTRIBUTES s_attrib;
+  BOOL                bInitializeSharedMemory = FALSE;
+  LPVOID              lpDesiredMemoryMapStart = (LPVOID)0x50000000;
+  HANDLE              hInformOnStart;
+
+  TRACE( "DPLAYX dll loaded - construct called\n" );
+
+  /* Create a semaphore to block access to DPLAYX global data structs */
+
+  s_attrib.bInheritHandle       = TRUE;
+  s_attrib.lpSecurityDescriptor = NULL;
+  s_attrib.nLength              = sizeof(s_attrib);
+
+  hDplayxSema = CreateSemaphoreA( &s_attrib, 1, 1, lpszDplayxSemaName );
+
+  /* First instance creates the semaphore. Others just use it */
+  if( GetLastError() == ERROR_SUCCESS )
+  {
+    TRACE( "Semaphore %u created\n", hDplayxSema );
+
+    /* The semaphore creator will also build the shared memory */
+    bInitializeSharedMemory = TRUE;
+  }
+  else if ( GetLastError() == ERROR_ALREADY_EXISTS )
+  {
+    TRACE( "Found semaphore handle %u\n", hDplayxSema );
+  }
+  else
+  {
+    ERR( ": semaphore error 0x%08lx\n", GetLastError() );
+    return FALSE;
+  }
+
+  SetLastError( ERROR_SUCCESS );
+
+  DPLAYX_AquireSemaphore();
+
+  hDplayxSharedMem = CreateFileMappingA( INVALID_HANDLE_VALUE,
+                                         &s_attrib,
+                                         PAGE_READWRITE | SEC_COMMIT,
+                                         0,
+                                         dwTotalSharedSize,
+                                         lpszDplayxFileMapping );
+
+  if( GetLastError() == ERROR_SUCCESS )
+  {
+    TRACE( "File mapped %u created\n", hDplayxSharedMem );
+  }
+  else if ( GetLastError() == ERROR_ALREADY_EXISTS )
+  {
+    TRACE( "Found FileMapping handle %u\n", hDplayxSharedMem );
+  }
+  else
+  {
+    ERR( ": unable to create shared memory 0x%08lx\n", GetLastError() );
+    return FALSE;
+  }
+
+  lpSharedStaticData = MapViewOfFileEx( hDplayxSharedMem,
+                                        FILE_MAP_WRITE,
+                                        0, 0, 0, lpDesiredMemoryMapStart );
+
+  if( lpSharedStaticData == NULL )
+  {
+    ERR( ": unable to map static data into process memory space: 0x%08lx\n",
+         GetLastError() );
+    return FALSE;
+  }
+  else
+  {
+    if( lpDesiredMemoryMapStart == lpSharedStaticData )
+    {
+      TRACE( "File mapped to %p\n", lpSharedStaticData );
+    }
+    else
+    {
+      /* Presently the shared data structures use pointers. If the
+       * files are no mapped into the same area, the pointers will no
+       * longer make any sense :(
+       * FIXME: In the future make the shared data structures have some
+       *        sort of fixup to make them independent between data spaces.
+       *        This will also require a rework of the session data stuff.
+       */
+      ERR( "File mapped to %p (not %p). Expect failure\n",
+            lpSharedStaticData, lpDesiredMemoryMapStart );
+    }
+  }
+
+  /* Dynamic area starts just after the static area */
+  lpMemArea = (DPLAYX_MEM_SLICE*)((BYTE*)lpSharedStaticData + dwStaticSharedSize);
+
+  /* FIXME: Crude hack */
+  lobbyData   = (DPLAYX_LOBBYDATA*)lpSharedStaticData;
+  sessionData = (DPSESSIONDESC2*)((BYTE*)lpSharedStaticData + (dwStaticSharedSize/2));
+
+  /* Initialize shared data segments. */
+  if( bInitializeSharedMemory )
+  {
+    UINT i;
+
+    TRACE( "Initializing shared memory\n" );
+
+    /* Set all lobbies to be "empty" */
+    for( i=0; i < numSupportedLobbies; i++ )
+    {
+      DPLAYX_InitializeLobbyDataEntry( &lobbyData[ i ] );
+    }
+
+    /* Set all sessions to be "empty" */
+    for( i=0; i < numSupportedSessions; i++ )
+    {
+      sessionData[i].dwSize = 0;
+    }
+
+    /* Zero out the dynmaic area */
+    ZeroMemory( lpMemArea, dwDynamicSharedSize );
+
+    /* Just for fun sync the whole data area */
+    FlushViewOfFile( lpSharedStaticData, dwTotalSharedSize );
+  }
+
+  DPLAYX_ReleaseSemaphore();
+
+  /* Everything was created correctly. Signal the lobby client that
+   * we started up correctly
+   */
+  if( DPLAYX_GetThisLobbyHandles( &hInformOnStart, NULL, NULL, FALSE ) &&
+      hInformOnStart
+    )
+  {
+    BOOL bSuccess;
+    bSuccess = SetEvent( hInformOnStart );
+    TRACE( "Signalling lobby app start event %u %s\n",
+             hInformOnStart, bSuccess ? "succeed" : "failed" );
+
+    /* Close out handle */
+    DPLAYX_GetThisLobbyHandles( &hInformOnStart, NULL, NULL, TRUE );
+  }
+
+  return TRUE;
+}
+
+/***************************************************************************
+ * Called to destroy all global data. This will only be used on the
+ * unloading of the dll
+ ***************************************************************************/
+BOOL DPLAYX_DestructData(void)
+{
+  HANDLE hInformOnDeath;
+
+  TRACE( "DPLAYX dll unloaded - destruct called\n" );
+
+  /* If required, inform that this app is dying */
+  if( DPLAYX_GetThisLobbyHandles( NULL, &hInformOnDeath, NULL, FALSE ) &&
+      hInformOnDeath
+    )
+  {
+    BOOL bSuccess;
+    bSuccess = SetEvent( hInformOnDeath );
+    TRACE( "Signalling lobby app death event %u %s\n",
+             hInformOnDeath, bSuccess ? "succeed" : "failed" );
+
+    /* Close out handle */
+    DPLAYX_GetThisLobbyHandles( NULL, &hInformOnDeath, NULL, TRUE );
+  }
+
+  /* DO CLEAN UP (LAST) */
+
+  /* Delete the semaphore */
+  CloseHandle( hDplayxSema );
+
+  /* Delete shared memory file mapping */
+  UnmapViewOfFile( lpSharedStaticData );
+  CloseHandle( hDplayxSharedMem );
+
+  return FALSE;
+}
+
+
+void DPLAYX_InitializeLobbyDataEntry( LPDPLAYX_LOBBYDATA lpData )
+{
+  ZeroMemory( lpData, sizeof( *lpData ) );
+}
+
+/* NOTE: This must be called with the semaphore aquired.
+ * TRUE/FALSE with a pointer to it's data returned. Pointer data is
+ * is only valid if TRUE is returned.
+ */
+BOOL DPLAYX_IsAppIdLobbied( DWORD dwAppID, LPDPLAYX_LOBBYDATA* lplpDplData )
+{
+  UINT i;
+
+  *lplpDplData = NULL;
+
+  if( dwAppID == 0 )
+  {
+    dwAppID = GetCurrentProcessId();
+    TRACE( "Translated dwAppID == 0 into 0x%08lx\n", dwAppID );
+  }
+
+  for( i=0; i < numSupportedLobbies; i++ )
+  {
+    if( lobbyData[ i ].dwAppID == dwAppID )
+    {
+      /* This process is lobbied */
+      TRACE( "Found 0x%08lx @ %u\n", dwAppID, i );
+      *lplpDplData = &lobbyData[ i ];
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+/* Reserve a spot for the new appliction. TRUE means success and FALSE failure.  */
+BOOL DPLAYX_CreateLobbyApplication( DWORD dwAppID )
+{
+  UINT i;
+
+  /* 0 is the marker for unused application data slots */
+  if( dwAppID == 0 )
+  {
+    return FALSE;
+  }
+
+  DPLAYX_AquireSemaphore();
+
+  /* Find an empty space in the list and insert the data */
+  for( i=0; i < numSupportedLobbies; i++ )
+  {
+    if( lobbyData[ i ].dwAppID == 0 )
+    {
+      /* This process is now lobbied */
+      TRACE( "Setting lobbyData[%u] for (0x%08lx,0x%08lx)\n",
+              i, dwAppID, GetCurrentProcessId() );
+
+      lobbyData[ i ].dwAppID              = dwAppID;
+      lobbyData[ i ].dwAppLaunchedFromID  = GetCurrentProcessId();
+
+      /* FIXME: Where is the best place for this? In interface or here? */
+      lobbyData[ i ].hInformOnAppStart = 0;
+      lobbyData[ i ].hInformOnAppDeath = 0;
+      lobbyData[ i ].hInformOnSettingRead = 0;
+
+      DPLAYX_ReleaseSemaphore();
+      return TRUE;
+    }
+  }
+
+  ERR( "No empty lobbies\n" );
+
+  DPLAYX_ReleaseSemaphore();
+  return FALSE;
+}
+
+/* I'm not sure when I'm going to need this, but here it is */
+BOOL DPLAYX_DestroyLobbyApplication( DWORD dwAppID )
+{
+  UINT i;
+
+  DPLAYX_AquireSemaphore();
+
+  /* Find an empty space in the list and insert the data */
+  for( i=0; i < numSupportedLobbies; i++ )
+  {
+    if( lobbyData[ i ].dwAppID == dwAppID )
+    {
+      /* FIXME: Should free up anything unused. Tisk tisk :0 */
+      /* Mark this entry unused */
+      TRACE( "Marking lobbyData[%u] unused\n", i );
+      DPLAYX_InitializeLobbyDataEntry( &lobbyData[ i ] );
+
+      DPLAYX_ReleaseSemaphore();
+      return TRUE;
+    }
+  }
+
+  DPLAYX_ReleaseSemaphore();
+  ERR( "Unable to find global entry for application\n" );
+  return FALSE;
+}
+
+BOOL DPLAYX_SetLobbyHandles( DWORD dwAppID,
+                             HANDLE hStart, HANDLE hDeath, HANDLE hConnRead )
+{
+  LPDPLAYX_LOBBYDATA lpLData;
+
+  /* Need to explictly give lobby application. Can't set for yourself */
+  if( dwAppID == 0 )
+  {
+    return FALSE;
+  }
+
+  DPLAYX_AquireSemaphore();
+
+  if( !DPLAYX_IsAppIdLobbied( dwAppID, &lpLData ) )
+  {
+    DPLAYX_ReleaseSemaphore();
+    return FALSE;
+  }
+
+  lpLData->hInformOnAppStart    = hStart;
+  lpLData->hInformOnAppDeath    = hDeath;
+  lpLData->hInformOnSettingRead = hConnRead;
+
+  DPLAYX_ReleaseSemaphore();
+
+  return TRUE;
+}
+
+BOOL DPLAYX_GetThisLobbyHandles( LPHANDLE lphStart,
+                                 LPHANDLE lphDeath,
+                                 LPHANDLE lphConnRead,
+                                 BOOL     bClearSetHandles )
+{
+  LPDPLAYX_LOBBYDATA lpLData;
+
+  DPLAYX_AquireSemaphore();
+
+  if( !DPLAYX_IsAppIdLobbied( 0, &lpLData ) )
+  {
+    DPLAYX_ReleaseSemaphore();
+    return FALSE;
+  }
+
+  if( lphStart != NULL )
+  {
+    if( lpLData->hInformOnAppStart == 0 )
+    {
+      DPLAYX_ReleaseSemaphore();
+      return FALSE;
+    }
+
+    *lphStart = lpLData->hInformOnAppStart;
+
+    if( bClearSetHandles )
+    {
+      CloseHandle( lpLData->hInformOnAppStart );
+      lpLData->hInformOnAppStart = 0;
+    }
+  }
+
+  if( lphDeath != NULL )
+  {
+    if( lpLData->hInformOnAppDeath == 0 )
+    {
+      DPLAYX_ReleaseSemaphore();
+      return FALSE;
+    }
+
+    *lphDeath = lpLData->hInformOnAppDeath;
+
+    if( bClearSetHandles )
+    {
+      CloseHandle( lpLData->hInformOnAppDeath );
+      lpLData->hInformOnAppDeath = 0;
+    }
+  }
+
+  if( lphConnRead != NULL )
+  {
+    if( lpLData->hInformOnSettingRead == 0 )
+    {
+      DPLAYX_ReleaseSemaphore();
+      return FALSE;
+    }
+
+    *lphConnRead = lpLData->hInformOnSettingRead;
+
+    if( bClearSetHandles )
+    {
+      CloseHandle( lpLData->hInformOnSettingRead );
+      lpLData->hInformOnSettingRead = 0;
+    }
+  }
+
+  DPLAYX_ReleaseSemaphore();
+
+  return TRUE;
+}
+
+
+HRESULT DPLAYX_GetConnectionSettingsA
+( DWORD dwAppID,
+  LPVOID lpData,
+  LPDWORD lpdwDataSize )
+{
+  LPDPLAYX_LOBBYDATA lpDplData;
+  DWORD              dwRequiredDataSize = 0;
+  HANDLE             hInformOnSettingRead;
+
+  DPLAYX_AquireSemaphore();
+
+  if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
+  {
+    DPLAYX_ReleaseSemaphore();
+
+    TRACE( "Application 0x%08lx is not lobbied\n", dwAppID );
+    return DPERR_NOTLOBBIED;
+  }
+
+  dwRequiredDataSize = DPLAYX_SizeOfLobbyDataA( lpDplData->lpConn );
+
+  /* Do they want to know the required buffer size or is the provided buffer
+   * big enough?
+   */
+  if ( ( lpData == NULL ) ||
+       ( *lpdwDataSize < dwRequiredDataSize )
+     )
+  {
+    DPLAYX_ReleaseSemaphore();
+
+    *lpdwDataSize = DPLAYX_SizeOfLobbyDataA( lpDplData->lpConn );
+
+    return DPERR_BUFFERTOOSMALL;
+  }
+
+  DPLAYX_CopyConnStructA( (LPDPLCONNECTION)lpData, lpDplData->lpConn );
+
+  DPLAYX_ReleaseSemaphore();
+
+  /* They have gotten the information - signal the event if required */
+  if( DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, FALSE ) &&
+      hInformOnSettingRead
+    )
+  {
+    BOOL bSuccess;
+    bSuccess = SetEvent( hInformOnSettingRead );
+    TRACE( "Signalling setting read event %u %s\n",
+             hInformOnSettingRead, bSuccess ? "succeed" : "failed" );
+
+    /* Close out handle */
+    DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, TRUE );
+  }
+
+  return DP_OK;
+}
+
+/* Assumption: Enough contiguous space was allocated at dest */
+void DPLAYX_CopyConnStructA( LPDPLCONNECTION dest, LPDPLCONNECTION src )
+{
+  BYTE* lpStartOfFreeSpace;
+
+  CopyMemory( dest, src, sizeof( DPLCONNECTION ) );
+
+  lpStartOfFreeSpace = ((BYTE*)dest) + sizeof( DPLCONNECTION );
+
+  /* Copy the LPDPSESSIONDESC2 structure if it exists */
+  if( src->lpSessionDesc )
+  {
+    dest->lpSessionDesc = (LPDPSESSIONDESC2)lpStartOfFreeSpace;
+    lpStartOfFreeSpace += sizeof( DPSESSIONDESC2 );
+    CopyMemory( dest->lpSessionDesc, src->lpSessionDesc, sizeof( DPSESSIONDESC2 ) );
+
+    /* Session names may or may not exist */
+    if( src->lpSessionDesc->sess.lpszSessionNameA )
+    {
+      strcpy( (LPSTR)lpStartOfFreeSpace, src->lpSessionDesc->sess.lpszSessionNameA );
+      dest->lpSessionDesc->sess.lpszSessionNameA = (LPSTR)lpStartOfFreeSpace;
+      lpStartOfFreeSpace +=
+        strlen( (LPSTR)dest->lpSessionDesc->sess.lpszSessionNameA ) + 1;
+    }
+
+    if( src->lpSessionDesc->pass.lpszPasswordA )
+    {
+      strcpy( (LPSTR)lpStartOfFreeSpace, src->lpSessionDesc->pass.lpszPasswordA );
+      dest->lpSessionDesc->pass.lpszPasswordA = (LPSTR)lpStartOfFreeSpace;
+      lpStartOfFreeSpace +=
+        strlen( (LPSTR)dest->lpSessionDesc->pass.lpszPasswordA ) + 1;
+    }
+  }
+
+  /* DPNAME structure is optional */
+  if( src->lpPlayerName )
+  {
+    dest->lpPlayerName = (LPDPNAME)lpStartOfFreeSpace;
+    lpStartOfFreeSpace += sizeof( DPNAME );
+    CopyMemory( dest->lpPlayerName, src->lpPlayerName, sizeof( DPNAME ) );
+
+    if( src->lpPlayerName->psn.lpszShortNameA )
+    {
+      strcpy( (LPSTR)lpStartOfFreeSpace, src->lpPlayerName->psn.lpszShortNameA );
+      dest->lpPlayerName->psn.lpszShortNameA = (LPSTR)lpStartOfFreeSpace;
+      lpStartOfFreeSpace +=
+        strlen( (LPSTR)dest->lpPlayerName->psn.lpszShortNameA ) + 1;
+    }
+
+    if( src->lpPlayerName->pln.lpszLongNameA )
+    {
+      strcpy( (LPSTR)lpStartOfFreeSpace, src->lpPlayerName->pln.lpszLongNameA );
+      dest->lpPlayerName->pln.lpszLongNameA = (LPSTR)lpStartOfFreeSpace;
+      lpStartOfFreeSpace +=
+        strlen( (LPSTR)dest->lpPlayerName->pln.lpszLongName ) + 1 ;
+    }
+
+  }
+
+  /* Copy address if it exists */
+  if( src->lpAddress )
+  {
+    dest->lpAddress = (LPVOID)lpStartOfFreeSpace;
+    CopyMemory( lpStartOfFreeSpace, src->lpAddress, src->dwAddressSize );
+    /* No need to advance lpStartOfFreeSpace as there is no more "dynamic" data */
+  }
+}
+
+HRESULT DPLAYX_GetConnectionSettingsW
+( DWORD dwAppID,
+  LPVOID lpData,
+  LPDWORD lpdwDataSize )
+{
+  LPDPLAYX_LOBBYDATA lpDplData;
+  DWORD              dwRequiredDataSize = 0;
+  HANDLE             hInformOnSettingRead;
+
+  DPLAYX_AquireSemaphore();
+
+  if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
+  {
+    DPLAYX_ReleaseSemaphore();
+    return DPERR_NOTLOBBIED;
+  }
+
+  dwRequiredDataSize = DPLAYX_SizeOfLobbyDataW( lpDplData->lpConn );
+
+  /* Do they want to know the required buffer size or is the provided buffer
+   * big enough?
+   */
+  if ( ( lpData == NULL ) ||
+       ( *lpdwDataSize < dwRequiredDataSize )
+     )
+  {
+    DPLAYX_ReleaseSemaphore();
+
+    *lpdwDataSize = DPLAYX_SizeOfLobbyDataW( lpDplData->lpConn );
+
+    return DPERR_BUFFERTOOSMALL;
+  }
+
+  DPLAYX_CopyConnStructW( (LPDPLCONNECTION)lpData, lpDplData->lpConn );
+
+  DPLAYX_ReleaseSemaphore();
+
+  /* They have gotten the information - signal the event if required */
+  if( DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, FALSE ) &&
+      hInformOnSettingRead
+    )
+  {
+    BOOL bSuccess;
+    bSuccess = SetEvent( hInformOnSettingRead );
+    TRACE( "Signalling setting read event %u %s\n",
+             hInformOnSettingRead, bSuccess ? "succeed" : "failed" );
+
+    /* Close out handle */
+    DPLAYX_GetThisLobbyHandles( NULL, NULL, &hInformOnSettingRead, TRUE );
+  }
+
+  return DP_OK;
+}
+
+/* Assumption: Enough contiguous space was allocated at dest */
+void DPLAYX_CopyConnStructW( LPDPLCONNECTION dest, LPDPLCONNECTION src )
+{
+  BYTE*              lpStartOfFreeSpace;
+
+  CopyMemory( dest, src, sizeof( DPLCONNECTION ) );
+
+  lpStartOfFreeSpace = ( (BYTE*)dest) + sizeof( DPLCONNECTION );
+
+  /* Copy the LPDPSESSIONDESC2 structure if it exists */
+  if( src->lpSessionDesc )
+  {
+    dest->lpSessionDesc = (LPDPSESSIONDESC2)lpStartOfFreeSpace;
+    lpStartOfFreeSpace += sizeof( DPSESSIONDESC2 );
+    CopyMemory( dest->lpSessionDesc, src->lpSessionDesc, sizeof( DPSESSIONDESC2 ) );
+
+    /* Session names may or may not exist */
+    if( src->lpSessionDesc->sess.lpszSessionName )
+    {
+      strcpyW( (LPWSTR)lpStartOfFreeSpace, dest->lpSessionDesc->sess.lpszSessionName );
+      src->lpSessionDesc->sess.lpszSessionName = (LPWSTR)lpStartOfFreeSpace;
+      lpStartOfFreeSpace +=  sizeof(WCHAR) *
+        ( strlenW( (LPWSTR)dest->lpSessionDesc->sess.lpszSessionName ) + 1 );
+    }
+
+    if( src->lpSessionDesc->pass.lpszPassword )
+    {
+      strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpSessionDesc->pass.lpszPassword );
+      dest->lpSessionDesc->pass.lpszPassword = (LPWSTR)lpStartOfFreeSpace;
+      lpStartOfFreeSpace +=  sizeof(WCHAR) *
+        ( strlenW( (LPWSTR)dest->lpSessionDesc->pass.lpszPassword ) + 1 );
+    }
+  }
+
+  /* DPNAME structure is optional */
+  if( src->lpPlayerName )
+  {
+    dest->lpPlayerName = (LPDPNAME)lpStartOfFreeSpace;
+    lpStartOfFreeSpace += sizeof( DPNAME );
+    CopyMemory( dest->lpPlayerName, src->lpPlayerName, sizeof( DPNAME ) );
+
+    if( src->lpPlayerName->psn.lpszShortName )
+    {
+      strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpPlayerName->psn.lpszShortName );
+      dest->lpPlayerName->psn.lpszShortName = (LPWSTR)lpStartOfFreeSpace;
+      lpStartOfFreeSpace +=  sizeof(WCHAR) *
+        ( strlenW( (LPWSTR)dest->lpPlayerName->psn.lpszShortName ) + 1 );
+    }
+
+    if( src->lpPlayerName->pln.lpszLongName )
+    {
+      strcpyW( (LPWSTR)lpStartOfFreeSpace, src->lpPlayerName->pln.lpszLongName );
+      dest->lpPlayerName->pln.lpszLongName = (LPWSTR)lpStartOfFreeSpace;
+      lpStartOfFreeSpace +=  sizeof(WCHAR) *
+        ( strlenW( (LPWSTR)dest->lpPlayerName->pln.lpszLongName ) + 1 );
+    }
+
+  }
+
+  /* Copy address if it exists */
+  if( src->lpAddress )
+  {
+    dest->lpAddress = (LPVOID)lpStartOfFreeSpace;
+    CopyMemory( lpStartOfFreeSpace, src->lpAddress, src->dwAddressSize );
+    /* No need to advance lpStartOfFreeSpace as there is no more "dynamic" data */
+  }
+
+}
+
+/* Store the structure into the shared data structre. Ensure that allocs for
+ * variable length strings come from the shared data structure.
+ * FIXME: We need to free information as well
+ */
+HRESULT DPLAYX_SetConnectionSettingsA
+( DWORD dwFlags,
+  DWORD dwAppID,
+  LPDPLCONNECTION lpConn )
+{
+  LPDPLAYX_LOBBYDATA lpDplData;
+
+  /* Paramater check */
+  if( dwFlags || !lpConn )
+  {
+    ERR("invalid parameters.\n");
+    return DPERR_INVALIDPARAMS;
+  }
+
+  /* Store information */
+  if(  lpConn->dwSize != sizeof(DPLCONNECTION) )
+  {
+    ERR(": old/new DPLCONNECTION type? Size=%08lx vs. expected=%ul bytes\n",
+         lpConn->dwSize, sizeof( DPLCONNECTION ) );
+
+    return DPERR_INVALIDPARAMS;
+  }
+
+  DPLAYX_AquireSemaphore();
+
+  if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
+  {
+    DPLAYX_ReleaseSemaphore();
+
+    return DPERR_NOTLOBBIED;
+  }
+
+  if(  (!lpConn->lpSessionDesc ) ||
+       ( lpConn->lpSessionDesc->dwSize != sizeof( DPSESSIONDESC2 ) )
+    )
+  {
+    DPLAYX_ReleaseSemaphore();
+
+    ERR("DPSESSIONDESC passed in? Size=%lu vs. expected=%u bytes\n",
+         lpConn->lpSessionDesc->dwSize, sizeof( DPSESSIONDESC2 ) );
+
+    return DPERR_INVALIDPARAMS;
+  }
+
+  /* Free the existing memory */
+  DPLAYX_PrivHeapFree( lpDplData->lpConn );
+
+  lpDplData->lpConn = (LPDPLCONNECTION) DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY,
+                                                              DPLAYX_SizeOfLobbyDataA( lpConn ) );
+
+  DPLAYX_CopyConnStructA( lpDplData->lpConn, lpConn );
+
+
+  DPLAYX_ReleaseSemaphore();
+
+  /* FIXME: Send a message - I think */
+
+  return DP_OK;
+}
+
+/* Store the structure into the shared data structre. Ensure that allocs for
+ * variable length strings come from the shared data structure.
+ * FIXME: We need to free information as well
+ */
+HRESULT DPLAYX_SetConnectionSettingsW
+( DWORD dwFlags,
+  DWORD dwAppID,
+  LPDPLCONNECTION lpConn )
+{
+  LPDPLAYX_LOBBYDATA lpDplData;
+
+  /* Paramater check */
+  if( dwFlags || !lpConn )
+  {
+    ERR("invalid parameters.\n");
+    return DPERR_INVALIDPARAMS;
+  }
+
+  /* Store information */
+  if(  lpConn->dwSize != sizeof(DPLCONNECTION) )
+  {
+    ERR(": old/new DPLCONNECTION type? Size=%lu vs. expected=%u bytes\n",
+         lpConn->dwSize, sizeof( DPLCONNECTION ) );
+
+    return DPERR_INVALIDPARAMS;
+  }
+
+  DPLAYX_AquireSemaphore();
+
+  if ( ! DPLAYX_IsAppIdLobbied( dwAppID, &lpDplData ) )
+  {
+    DPLAYX_ReleaseSemaphore();
+
+    return DPERR_NOTLOBBIED;
+  }
+
+  /* Free the existing memory */
+  DPLAYX_PrivHeapFree( lpDplData->lpConn );
+
+  lpDplData->lpConn = (LPDPLCONNECTION) DPLAYX_PrivHeapAlloc( HEAP_ZERO_MEMORY,
+                                                              DPLAYX_SizeOfLobbyDataW( lpConn ) );
+
+  DPLAYX_CopyConnStructW( lpDplData->lpConn, lpConn );
+
+
+  DPLAYX_ReleaseSemaphore();
+
+  /* FIXME: Send a message - I think */
+
+  return DP_OK;
+}
+
+DWORD DPLAYX_SizeOfLobbyDataA( LPDPLCONNECTION lpConn )
+{
+  DWORD dwTotalSize = sizeof( DPLCONNECTION );
+
+  /* Just a safety check */
+  if( lpConn == NULL )
+  {
+    ERR( "lpConn is NULL\n" );
+    return 0;
+  }
+
+  if( lpConn->lpSessionDesc != NULL )
+  {
+    dwTotalSize += sizeof( DPSESSIONDESC2 );
+
+    if( lpConn->lpSessionDesc->sess.lpszSessionNameA )
+    {
+      dwTotalSize += strlen( lpConn->lpSessionDesc->sess.lpszSessionNameA ) + 1;
+    }
+
+    if( lpConn->lpSessionDesc->pass.lpszPasswordA )
+    {
+      dwTotalSize += strlen( lpConn->lpSessionDesc->pass.lpszPasswordA ) + 1;
+    }
+  }
+
+  if( lpConn->lpPlayerName != NULL )
+  {
+    dwTotalSize += sizeof( DPNAME );
+
+    if( lpConn->lpPlayerName->psn.lpszShortNameA )
+    {
+      dwTotalSize += strlen( lpConn->lpPlayerName->psn.lpszShortNameA ) + 1;
+    }
+
+    if( lpConn->lpPlayerName->pln.lpszLongNameA )
+    {
+      dwTotalSize += strlen( lpConn->lpPlayerName->pln.lpszLongNameA ) + 1;
+    }
+
+  }
+
+  dwTotalSize += lpConn->dwAddressSize;
+
+  return dwTotalSize;
+}
+
+DWORD DPLAYX_SizeOfLobbyDataW( LPDPLCONNECTION lpConn )
+{
+  DWORD dwTotalSize = sizeof( DPLCONNECTION );
+
+  /* Just a safety check */
+  if( lpConn == NULL )
+  {
+    ERR( "lpConn is NULL\n" );
+    return 0;
+  }
+
+  if( lpConn->lpSessionDesc != NULL )
+  {
+    dwTotalSize += sizeof( DPSESSIONDESC2 );
+
+    if( lpConn->lpSessionDesc->sess.lpszSessionName )
+    {
+      dwTotalSize += sizeof( WCHAR ) *
+        ( strlenW( lpConn->lpSessionDesc->sess.lpszSessionName ) + 1 );
+    }
+
+    if( lpConn->lpSessionDesc->pass.lpszPassword )
+    {
+      dwTotalSize += sizeof( WCHAR ) *
+        ( strlenW( lpConn->lpSessionDesc->pass.lpszPassword ) + 1 );
+    }
+  }
+
+  if( lpConn->lpPlayerName != NULL )
+  {
+    dwTotalSize += sizeof( DPNAME );
+
+    if( lpConn->lpPlayerName->psn.lpszShortName )
+    {
+      dwTotalSize += sizeof( WCHAR ) *
+        ( strlenW( lpConn->lpPlayerName->psn.lpszShortName ) + 1 );
+    }
+
+    if( lpConn->lpPlayerName->pln.lpszLongName )
+    {
+      dwTotalSize += sizeof( WCHAR ) *
+        ( strlenW( lpConn->lpPlayerName->pln.lpszLongName ) + 1 );
+    }
+
+  }
+
+  dwTotalSize += lpConn->dwAddressSize;
+
+  return dwTotalSize;
+}
+
+
+
+LPDPSESSIONDESC2 DPLAYX_CopyAndAllocateSessionDesc2A( LPCDPSESSIONDESC2 lpSessionSrc )
+{
+   LPDPSESSIONDESC2 lpSessionDest =
+     (LPDPSESSIONDESC2)HeapAlloc( GetProcessHeap(),
+                                  HEAP_ZERO_MEMORY, sizeof( *lpSessionSrc ) );
+   DPLAYX_CopyIntoSessionDesc2A( lpSessionDest, lpSessionSrc );
+
+   return lpSessionDest;
+}
+
+/* Copy an ANSI session desc structure to the given buffer */
+BOOL DPLAYX_CopyIntoSessionDesc2A( LPDPSESSIONDESC2  lpSessionDest,
+                                   LPCDPSESSIONDESC2 lpSessionSrc )
+{
+  CopyMemory( lpSessionDest, lpSessionSrc, sizeof( *lpSessionSrc ) );
+
+  if( lpSessionSrc->sess.lpszSessionNameA )
+  {
+    lpSessionDest->sess.lpszSessionNameA =
+      HEAP_strdupA( GetProcessHeap(),
+                    HEAP_ZERO_MEMORY, lpSessionSrc->sess.lpszSessionNameA );
+  }
+  if( lpSessionSrc->pass.lpszPasswordA )
+  {
+    lpSessionDest->pass.lpszPasswordA =
+      HEAP_strdupA( GetProcessHeap(),
+                    HEAP_ZERO_MEMORY, lpSessionSrc->pass.lpszPasswordA );
+  }
+
+  return TRUE;
+}
+
+/* Start the index at 0. index will be updated to equal that which should
+   be passed back into this function for the next element */
+LPDPSESSIONDESC2 DPLAYX_CopyAndAllocateLocalSession( UINT* index )
+{
+  for( ; (*index) < numSupportedSessions; (*index)++ )
+  {
+    if( sessionData[(*index)].dwSize != 0 )
+    {
+      return DPLAYX_CopyAndAllocateSessionDesc2A( &sessionData[(*index)++] );
+    }
+  }
+
+  /* No more sessions */
+  return NULL;
+}
+
+/* Start the index at 0. index will be updated to equal that which should
+   be passed back into this function for the next element */
+BOOL DPLAYX_CopyLocalSession( UINT* index, LPDPSESSIONDESC2 lpsd )
+{
+  for( ; (*index) < numSupportedSessions; (*index)++ )
+  {
+    if( sessionData[(*index)].dwSize != 0 )
+    {
+      return DPLAYX_CopyIntoSessionDesc2A( lpsd, &sessionData[(*index)++] );
+    }
+  }
+
+  /* No more sessions */
+  return FALSE;
+}
+
+void DPLAYX_SetLocalSession( LPCDPSESSIONDESC2 lpsd )
+{
+  UINT i;
+
+  /* FIXME: Is this an error if it exists already? */
+
+  /* Crude/wrong implementation for now. Just always add to first empty spot */
+  for( i=0; i < numSupportedSessions; i++ )
+  {
+    /* Is this one empty? */
+    if( sessionData[i].dwSize == 0 )
+    {
+      DPLAYX_CopyIntoSessionDesc2A( &sessionData[i], lpsd );
+      break;
+    }
+  }
+
+}
+
+BOOL DPLAYX_WaitForConnectionSettings( BOOL bWait )
+{
+  LPDPLAYX_LOBBYDATA lpLobbyData;
+
+  DPLAYX_AquireSemaphore();
+
+  if( !DPLAYX_IsAppIdLobbied( 0, &lpLobbyData ) )
+  {
+    DPLAYX_ReleaseSemaphore();
+    return FALSE;
+  }
+
+  lpLobbyData->bWaitForConnectionSettings = bWait;
+
+  DPLAYX_ReleaseSemaphore();
+
+  return TRUE;
+}
+
+BOOL DPLAYX_AnyLobbiesWaitingForConnSettings(void)
+{
+  UINT i;
+  BOOL bFound = FALSE;
+
+  DPLAYX_AquireSemaphore();
+
+  for( i=0; i < numSupportedLobbies; i++ )
+  {
+    if( ( lobbyData[ i ].dwAppID != 0 ) &&            /* lobby initialized */
+        ( lobbyData[ i ].bWaitForConnectionSettings ) /* Waiting */
+      )
+    {
+      bFound = TRUE;
+      break;
+    }
+  }
+
+  DPLAYX_ReleaseSemaphore();
+
+  return bFound;
+}
+
+BOOL DPLAYX_SetLobbyMsgThreadId( DWORD dwAppId, DWORD dwThreadId )
+{
+  LPDPLAYX_LOBBYDATA lpLobbyData;
+
+  DPLAYX_AquireSemaphore();
+
+  if( !DPLAYX_IsAppIdLobbied( dwAppId, &lpLobbyData ) )
+  {
+    DPLAYX_ReleaseSemaphore();
+    return FALSE;
+  }
+
+  lpLobbyData->dwLobbyMsgThreadId = dwThreadId;
+
+  DPLAYX_ReleaseSemaphore();
+
+  return TRUE;
+}
+
+/* NOTE: This is potentially not thread safe. You are not guaranteed to end up
+         with the correct string printed in the case where the HRESULT is not
+         known. You'll just get the last hr passed in printed. This can change
+         over time if this method is used alot :) */
+LPCSTR DPLAYX_HresultToString(HRESULT hr)
+{
+  static char szTempStr[12];
+
+  switch (hr)
+  {
+    case DP_OK:
+      return "DP_OK";
+    case DPERR_ALREADYINITIALIZED:
+      return "DPERR_ALREADYINITIALIZED";
+    case DPERR_ACCESSDENIED:
+      return "DPERR_ACCESSDENIED";
+    case DPERR_ACTIVEPLAYERS:
+      return "DPERR_ACTIVEPLAYERS";
+    case DPERR_BUFFERTOOSMALL:
+      return "DPERR_BUFFERTOOSMALL";
+    case DPERR_CANTADDPLAYER:
+      return "DPERR_CANTADDPLAYER";
+    case DPERR_CANTCREATEGROUP:
+      return "DPERR_CANTCREATEGROUP";
+    case DPERR_CANTCREATEPLAYER:
+      return "DPERR_CANTCREATEPLAYER";
+    case DPERR_CANTCREATESESSION:
+      return "DPERR_CANTCREATESESSION";
+    case DPERR_CAPSNOTAVAILABLEYET:
+      return "DPERR_CAPSNOTAVAILABLEYET";
+    case DPERR_EXCEPTION:
+      return "DPERR_EXCEPTION";
+    case DPERR_GENERIC:
+      return "DPERR_GENERIC";
+    case DPERR_INVALIDFLAGS:
+      return "DPERR_INVALIDFLAGS";
+    case DPERR_INVALIDOBJECT:
+      return "DPERR_INVALIDOBJECT";
+    case DPERR_INVALIDPARAMS:
+      return "DPERR_INVALIDPARAMS";
+    case DPERR_INVALIDPLAYER:
+      return "DPERR_INVALIDPLAYER";
+    case DPERR_INVALIDGROUP:
+      return "DPERR_INVALIDGROUP";
+    case DPERR_NOCAPS:
+      return "DPERR_NOCAPS";
+    case DPERR_NOCONNECTION:
+      return "DPERR_NOCONNECTION";
+    case DPERR_OUTOFMEMORY:
+      return "DPERR_OUTOFMEMORY";
+    case DPERR_NOMESSAGES:
+      return "DPERR_NOMESSAGES";
+    case DPERR_NONAMESERVERFOUND:
+      return "DPERR_NONAMESERVERFOUND";
+    case DPERR_NOPLAYERS:
+      return "DPERR_NOPLAYERS";
+    case DPERR_NOSESSIONS:
+      return "DPERR_NOSESSIONS";
+/* This one isn't defined yet in WINE sources. I don't know the value
+    case DPERR_PENDING:
+      return "DPERR_PENDING";
+*/
+    case DPERR_SENDTOOBIG:
+      return "DPERR_SENDTOOBIG";
+    case DPERR_TIMEOUT:
+      return "DPERR_TIMEOUT";
+    case DPERR_UNAVAILABLE:
+      return "DPERR_UNAVAILABLE";
+    case DPERR_UNSUPPORTED:
+      return "DPERR_UNSUPPORTED";
+    case DPERR_BUSY:
+      return "DPERR_BUSY";
+    case DPERR_USERCANCEL:
+      return "DPERR_USERCANCEL";
+    case DPERR_NOINTERFACE:
+      return "DPERR_NOINTERFACE";
+    case DPERR_CANNOTCREATESERVER:
+      return "DPERR_CANNOTCREATESERVER";
+    case DPERR_PLAYERLOST:
+      return "DPERR_PLAYERLOST";
+    case DPERR_SESSIONLOST:
+      return "DPERR_SESSIONLOST";
+    case DPERR_UNINITIALIZED:
+      return "DPERR_UNINITIALIZED";
+    case DPERR_NONEWPLAYERS:
+      return "DPERR_NONEWPLAYERS";
+    case DPERR_INVALIDPASSWORD:
+      return "DPERR_INVALIDPASSWORD";
+    case DPERR_CONNECTING:
+      return "DPERR_CONNECTING";
+    case DPERR_CONNECTIONLOST:
+      return "DPERR_CONNECTIONLOST";
+    case DPERR_UNKNOWNMESSAGE:
+      return "DPERR_UNKNOWNMESSAGE";
+    case DPERR_CANCELFAILED:
+      return "DPERR_CANCELFAILED";
+    case DPERR_INVALIDPRIORITY:
+      return "DPERR_INVALIDPRIORITY";
+    case DPERR_NOTHANDLED:
+      return "DPERR_NOTHANDLED";
+    case DPERR_CANCELLED:
+      return "DPERR_CANCELLED";
+    case DPERR_ABORTED:
+      return "DPERR_ABORTED";
+    case DPERR_BUFFERTOOLARGE:
+      return "DPERR_BUFFERTOOLARGE";
+    case DPERR_CANTCREATEPROCESS:
+      return "DPERR_CANTCREATEPROCESS";
+    case DPERR_APPNOTSTARTED:
+      return "DPERR_APPNOTSTARTED";
+    case DPERR_INVALIDINTERFACE:
+      return "DPERR_INVALIDINTERFACE";
+    case DPERR_NOSERVICEPROVIDER:
+      return "DPERR_NOSERVICEPROVIDER";
+    case DPERR_UNKNOWNAPPLICATION:
+      return "DPERR_UNKNOWNAPPLICATION";
+    case DPERR_NOTLOBBIED:
+      return "DPERR_NOTLOBBIED";
+    case DPERR_SERVICEPROVIDERLOADED:
+      return "DPERR_SERVICEPROVIDERLOADED";
+    case DPERR_ALREADYREGISTERED:
+      return "DPERR_ALREADYREGISTERED";
+    case DPERR_NOTREGISTERED:
+      return "DPERR_NOTREGISTERED";
+    case DPERR_AUTHENTICATIONFAILED:
+      return "DPERR_AUTHENTICATIONFAILED";
+    case DPERR_CANTLOADSSPI:
+      return "DPERR_CANTLOADSSPI";
+    case DPERR_ENCRYPTIONFAILED:
+      return "DPERR_ENCRYPTIONFAILED";
+    case DPERR_SIGNFAILED:
+      return "DPERR_SIGNFAILED";
+    case DPERR_CANTLOADSECURITYPACKAGE:
+      return "DPERR_CANTLOADSECURITYPACKAGE";
+    case DPERR_ENCRYPTIONNOTSUPPORTED:
+      return "DPERR_ENCRYPTIONNOTSUPPORTED";
+    case DPERR_CANTLOADCAPI:
+      return "DPERR_CANTLOADCAPI";
+    case DPERR_NOTLOGGEDIN:
+      return "DPERR_NOTLOGGEDIN";
+    case DPERR_LOGONDENIED:
+      return "DPERR_LOGONDENIED";
+    default:
+      /* For errors not in the list, return HRESULT as a string
+         This part is not thread safe */
+      WARN( "Unknown error 0x%08lx\n", hr );
+      wsprintfA( szTempStr, "0x%08lx", hr );
+      return szTempStr;
+  }
+}
+
Index: trunk/src/DPlayX/dplayx_global.h
===================================================================
--- trunk/src/DPlayX/dplayx_global.h	(revision 4314)
+++ trunk/src/DPlayX/dplayx_global.h	(revision 4314)
@@ -0,0 +1,54 @@
+
+#ifndef __WINE_DPLAYX_GLOBAL
+#define __WINE_DPLAYX_GLOBAL
+
+#include "dplay.h"
+
+BOOL DPLAYX_ConstructData(void);
+BOOL DPLAYX_DestructData(void);
+
+HRESULT DPLAYX_GetConnectionSettingsA ( DWORD dwAppID, 
+                                        LPVOID lpData, 
+                                        LPDWORD lpdwDataSize );
+HRESULT DPLAYX_GetConnectionSettingsW ( DWORD dwAppID, 
+                                        LPVOID lpData,
+                                        LPDWORD lpdwDataSize );
+
+HRESULT DPLAYX_SetConnectionSettingsA ( DWORD dwFlags, 
+                                        DWORD dwAppID, 
+                                        LPDPLCONNECTION lpConn );
+HRESULT DPLAYX_SetConnectionSettingsW ( DWORD dwFlags, 
+                                        DWORD dwAppID, 
+                                        LPDPLCONNECTION lpConn );
+
+BOOL DPLAYX_CreateLobbyApplication( DWORD dwAppID );
+BOOL DPLAYX_DestroyLobbyApplication( DWORD dwAppID );
+
+BOOL DPLAYX_WaitForConnectionSettings( BOOL bWait );
+BOOL DPLAYX_AnyLobbiesWaitingForConnSettings(void);
+
+BOOL DPLAYX_SetLobbyHandles( DWORD dwAppID, 
+                             HANDLE hStart, HANDLE hDeath, HANDLE hConnRead );
+BOOL DPLAYX_GetThisLobbyHandles( LPHANDLE lphStart, 
+                                 LPHANDLE lphDeath,
+                                 LPHANDLE lphConnRead, BOOL bClearSetHandles );
+
+LPDPSESSIONDESC2 DPLAYX_CopyAndAllocateLocalSession( UINT* index );
+BOOL DPLAYX_CopyLocalSession( UINT* index, LPDPSESSIONDESC2 lpsd );
+void DPLAYX_SetLocalSession( LPCDPSESSIONDESC2 lpsd );
+
+BOOL DPLAYX_SetLobbyMsgThreadId( DWORD dwAppId, DWORD dwThreadId );
+
+/* FIXME: This should not be here */
+LPVOID DPLAYX_PrivHeapAlloc( DWORD flags, DWORD size );
+void DPLAYX_PrivHeapFree( LPVOID addr );
+
+LPSTR DPLAYX_strdupA( DWORD flags, LPCSTR str );
+LPWSTR DPLAYX_strdupW( DWORD flags, LPCWSTR str );
+/* FIXME: End shared data alloc which should be local */
+
+
+/* Convert a DP or DPL HRESULT code into a string for human consumption */
+LPCSTR DPLAYX_HresultToString( HRESULT hr );
+
+#endif /* __WINE_DPLAYX_GLOBAL */
Index: trunk/src/DPlayX/dplayx_main.cpp
===================================================================
--- trunk/src/DPlayX/dplayx_main.cpp	(revision 4314)
+++ trunk/src/DPlayX/dplayx_main.cpp	(revision 4314)
@@ -0,0 +1,94 @@
+/*
+ * DPLAYX.DLL LibMain
+ *
+ * Copyright 1999,2000 - Peter Hunnisett
+ *
+ * contact <hunnise@nortelnetworks.com>
+ */
+#include <string.h>
+#include <odin.h>
+#define ICOM_CINTERFACE 1
+#define CINTERFACE
+#include "winerror.h"
+#include "winbase.h"
+#include "debugtools.h"
+#define INITGUID
+#include "initguid.h"  /* To define the GUIDs */
+#include "dplaysp.h"
+#include "dplayx_global.h"
+
+DEFAULT_DEBUG_CHANNEL(dplay);
+
+DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
+
+static DWORD DPLAYX_dwProcessesAttached = 0;
+
+/* This is a globally exported variable at ordinal 6 of DPLAYX.DLL */
+DWORD gdwDPlaySPRefCount = 0; /* FIXME: Should it be initialized here? */
+
+extern "C" {
+void CDECL _ctordtorTerm( void );
+}
+
+BOOL WINAPI DPLAYX_LibMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
+{
+
+  TRACE( "(%u,0x%08lx,%p) & 0x%08lx\n", hinstDLL, fdwReason, lpvReserved, DPLAYX_dwProcessesAttached );
+
+  switch ( fdwReason )
+  {
+    case DLL_PROCESS_ATTACH:
+    {
+
+      if ( DPLAYX_dwProcessesAttached++ == 0 )
+      {
+        /* First instance perform construction of global processor data */
+        return DPLAYX_ConstructData();
+      }
+
+      break;
+    }
+
+    case DLL_PROCESS_DETACH:
+    {
+
+      if ( --DPLAYX_dwProcessesAttached == 0 )
+      {
+        BOOL rc;
+        /* Last instance performs destruction of global processor data */
+        rc = DPLAYX_DestructData();
+        _ctordtorTerm();
+        return rc;
+      }
+
+      break;
+    }
+
+    case DLL_THREAD_ATTACH: /* Do nothing */
+    case DLL_THREAD_DETACH: /* Do nothing */
+      break;
+    default:
+      break;
+
+  }
+
+  return TRUE;
+}
+
+/***********************************************************************
+ *              DllCanUnloadNow (DPLAYX.10)
+ */
+HRESULT WINAPI DPLAYX_DllCanUnloadNow(void)
+{
+  HRESULT hr = ( gdwDPlaySPRefCount > 0 ) ? S_FALSE : S_OK;
+
+  /* FIXME: Should I be putting a check in for class factory objects
+   *        as well
+   */
+
+  TRACE( ": returning 0x%08lx\n", hr );
+
+  return hr;
+}
+
+
Index: trunk/src/DPlayX/dplayx_messages.cpp
===================================================================
--- trunk/src/DPlayX/dplayx_messages.cpp	(revision 4314)
+++ trunk/src/DPlayX/dplayx_messages.cpp	(revision 4314)
@@ -0,0 +1,248 @@
+/* DirectPlay & DirectPlayLobby messaging implementation
+ *
+ * Copyright 2000 - Peter Hunnisett
+ *
+ * <presently under construction - contact hunnise@nortelnetworks.com>
+ *
+ */
+#include <string.h>
+
+#include <odin.h>
+#define ICOM_CINTERFACE 1
+#define CINTERFACE
+
+#include "winbase.h"
+#include "debugtools.h"
+
+#include "wingdi.h"
+#include "winuser.h"
+#include "winerror.h"
+
+#include "dplayx_messages.h"
+#include "dplay_global.h"
+#include "dplayx_global.h"
+
+DEFAULT_DEBUG_CHANNEL(dplay)
+
+#define debugstr_guid(a) a
+
+typedef struct tagMSGTHREADINFO
+{
+  HANDLE hStart;
+  HANDLE hDeath;
+  HANDLE hSettingRead;
+  HANDLE hNotifyEvent;
+} MSGTHREADINFO, *LPMSGTHREADINFO;
+
+
+static DWORD CALLBACK DPL_MSG_ThreadMain( LPVOID lpContext );
+
+/* Create the message reception thread to allow the application to receive
+ * asynchronous message reception
+ */
+DWORD CreateLobbyMessageReceptionThread( HANDLE hNotifyEvent, HANDLE hStart,
+                                         HANDLE hDeath, HANDLE hConnRead )
+{
+  DWORD           dwMsgThreadId;
+  LPMSGTHREADINFO lpThreadInfo;
+
+  lpThreadInfo = (LPMSGTHREADINFO) HeapAlloc( GetProcessHeap(), 0, sizeof( *lpThreadInfo ) );
+  if( lpThreadInfo == NULL )
+  {
+    return 0;
+  }
+
+  /* The notify event may or may not exist. Depends if async comm or not */
+  if( hNotifyEvent &&
+      !DuplicateHandle( GetCurrentProcess(), hNotifyEvent,
+                        GetCurrentProcess(), &lpThreadInfo->hNotifyEvent,
+                        0, FALSE, DUPLICATE_SAME_ACCESS ) )
+  {
+    ERR( "Unable to duplicate event handle\n" );
+    goto error;
+  }
+
+  /* These 3 handles don't need to be duplicated because we don't keep a
+   * reference to them where they're created. They're created specifically
+   * for the message thread
+   */
+  lpThreadInfo->hStart       = hStart;
+  lpThreadInfo->hDeath       = hDeath;
+  lpThreadInfo->hSettingRead = hConnRead;
+
+  if( !CreateThread( NULL,                  /* Security attribs */
+                     0,                     /* Stack */
+                     DPL_MSG_ThreadMain,    /* Msg reception function */
+                     lpThreadInfo,          /* Msg reception func parameter */
+                     0,                     /* Flags */
+                     &dwMsgThreadId         /* Updated with thread id */
+                   )
+    )
+  {
+    ERR( "Unable to create msg thread\n" );
+    goto error;
+  }
+
+  /* FIXME: Should I be closing the handle to the thread or does that
+            terminate the thread? */
+
+  return dwMsgThreadId;
+
+error:
+
+  HeapFree( GetProcessHeap(), 0, lpThreadInfo );
+
+  return 0;
+}
+
+static DWORD CALLBACK DPL_MSG_ThreadMain( LPVOID lpContext )
+{
+  LPMSGTHREADINFO lpThreadInfo = (LPMSGTHREADINFO)lpContext;
+  DWORD dwWaitResult;
+
+  TRACE( "Msg thread created. Waiting on app startup\n" );
+
+  /* Wait to ensure that the lobby application is started w/ 1 min timeout */
+  dwWaitResult = WaitForSingleObject( lpThreadInfo->hStart, 10000 /* 10 sec */ );
+  if( dwWaitResult == WAIT_TIMEOUT )
+  {
+    FIXME( "Should signal app/wait creation failure (0x%08lx)\n", dwWaitResult );
+    goto end_of_thread;
+  }
+
+  /* Close this handle as it's not needed anymore */
+  CloseHandle( lpThreadInfo->hStart );
+  lpThreadInfo->hStart = 0;
+
+  /* Wait until the lobby knows what it is */
+  dwWaitResult = WaitForSingleObject( lpThreadInfo->hSettingRead, INFINITE );
+  if( dwWaitResult == WAIT_TIMEOUT )
+  {
+    ERR( "App Read connection setting timeout fail (0x%08lx)\n", dwWaitResult );
+  }
+
+  /* Close this handle as it's not needed anymore */
+  CloseHandle( lpThreadInfo->hSettingRead );
+  lpThreadInfo->hSettingRead = 0;
+
+  TRACE( "App created && intialized starting main message reception loop\n" );
+
+  for ( ;; )
+  {
+    MSG lobbyMsg;
+#ifdef STRICT
+    HANDLE hNullHandle = NULL;
+#else
+    HANDLE hNullHandle = 0;
+#endif
+
+    GetMessageW( &lobbyMsg, hNullHandle, 0, 0 );
+  }
+
+end_of_thread:
+  TRACE( "Msg thread exiting!\n" );
+  HeapFree( GetProcessHeap(), 0, lpThreadInfo );
+
+  return 0;
+}
+
+
+HRESULT DP_MSG_SendRequestPlayerId( IDirectPlay2AImpl* This, DWORD dwFlags,
+                                    LPDPID lpdpidAllocatedId )
+{
+  LPVOID                     lpMsg;
+  LPDPMSG_REQUESTNEWPLAYERID lpMsgBody;
+  DWORD                      dwMsgSize;
+  DWORD                      dwWaitReturn;
+  HRESULT                    hr = DP_OK;
+
+  FIXME( "semi stub\n" );
+
+  DebugBreak();
+
+  dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody );
+
+  lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize );
+
+  lpMsgBody = (LPDPMSG_REQUESTNEWPLAYERID)( (BYTE*)lpMsg +
+                                             This->dp2->spData.dwSPHeaderSize );
+
+  lpMsgBody->envelope.dwMagic    = DPMSGMAGIC_DPLAYMSG;
+  lpMsgBody->envelope.wCommandId = DPMSGCMD_REQUESTNEWPLAYERID;
+  lpMsgBody->envelope.wVersion   = DPMSGVER_DP6;
+
+  lpMsgBody->dwFlags = dwFlags;
+
+  /* FIXME: Need to have a more advanced queuing system as this needs to
+   *        block on send until we get response. Otherwise we need to be
+   *        able to ensure we can pick out the exact response. Of course,
+   *        with something as non critical as this, would it matter? The
+   *        id has been effectively reserved for this session...
+   */
+  {
+    DPSP_SENDDATA data;
+
+    data.dwFlags        = DPSEND_GUARANTEED;
+    data.idPlayerTo     = 0; /* Name server */
+    data.idPlayerFrom   = 0; /* Sending from DP */
+    data.lpMessage      = lpMsg;
+    data.dwMessageSize  = dwMsgSize;
+    data.bSystemMessage = TRUE; /* Allow reply to be sent */
+    data.lpISP          = This->dp2->spData.lpISP;
+
+    /* Setup for receipt */
+    This->dp2->hMsgReceipt = CreateEventA( NULL, FALSE, FALSE, NULL );
+
+    hr = (*This->dp2->spData.lpCB->Send)( &data );
+
+    if( FAILED(hr) )
+    {
+      ERR( "Request for new playerID send failed: %s\n",
+           DPLAYX_HresultToString( hr ) );
+    }
+  }
+
+  dwWaitReturn = WaitForSingleObject( This->dp2->hMsgReceipt, 30000 );
+  if( dwWaitReturn != WAIT_OBJECT_0 )
+  {
+    ERR( "Wait failed 0x%08lx\n", dwWaitReturn );
+  }
+
+  CloseHandle( This->dp2->hMsgReceipt );
+  This->dp2->hMsgReceipt = 0;
+
+  /* Need to examine the data and extract the new player id */
+  /* I just hope that dplay doesn't return the whole new player! */
+
+  return hr;
+}
+
+
+/* This function seems to cause a trap in the SP. It would seem unnecessary */
+/* FIXME: Remove this method if not required */
+HRESULT DP_MSG_OpenStream( IDirectPlay2AImpl* This )
+{
+  HRESULT       hr;
+  DPSP_SENDDATA data;
+
+  data.dwFlags        = DPSEND_OPENSTREAM;
+  data.idPlayerTo     = 0; /* Name server */
+  data.idPlayerFrom   = 0; /* From DP itself */
+  data.lpMessage      = NULL;
+  data.dwMessageSize  = This->dp2->spData.dwSPHeaderSize;
+  data.bSystemMessage = FALSE; /* FIXME? */
+  data.lpISP          = This->dp2->spData.lpISP;
+
+  hr = (*This->dp2->spData.lpCB->Send)( &data );
+
+  if( FAILED(hr) )
+  {
+      ERR( "Request for open stream send failed: %s\n",
+           DPLAYX_HresultToString( hr ) );
+  }
+
+  /* FIXME: hack to give some time for channel to open */
+  SleepEx( 1000 /* 1 sec */, FALSE );
+
+  return hr;
+}
Index: trunk/src/DPlayX/dplayx_messages.h
===================================================================
--- trunk/src/DPlayX/dplayx_messages.h	(revision 4314)
+++ trunk/src/DPlayX/dplayx_messages.h	(revision 4314)
@@ -0,0 +1,153 @@
+
+#ifndef __WINE_DPLAYX_MESSAGES__
+#define __WINE_DPLAYX_MESSAGES__
+
+#include "windef.h"
+#include "dplay.h"
+//#include "rpc.h" /* For GUID */
+
+#include "dplay_global.h"
+
+DWORD CreateLobbyMessageReceptionThread( HANDLE hNotifyEvent, HANDLE hStart,
+                                         HANDLE hDeath, HANDLE hConnRead );
+
+
+/* Message types etc. */
+#include "pshpack1.h"
+
+/* Non provided messages for DPLAY - guess work which may be wrong :( */
+#define DPMSGCMD_ENUMSESSIONSREPLY    1
+#define DPMSGCMD_ENUMSESSIONSREQUEST  2
+
+#define DPMSGCMD_GETSETNAMETABLE      3 /* Request info from NS about
+                                           existing players/groups etc. Is
+                                           also used for reply */
+
+#define DPMSGCMD_REQUESTNEWPLAYERID   5
+
+#define DPMSGCMD_NEWPLAYERIDREPLY     7
+#define DPMSGCMD_CREATESESSION        8
+#define DPMSGCMD_CREATENEWPLAYER      9
+#define DPMSGCMD_SYSTEMMESSAGE        10
+#define DPMSGCMD_DELETEPLAYER         11
+#define DPMSGCMD_DELETEGROUP          12
+
+#define DPMSGCMD_ENUMGROUPS           17
+
+#define DPMSGCMD_FORWARDCREATEPLAYER  19  /* This may be a get name table req */
+
+/* This is what DP 6 defines it as. Don't know what it means. All messages
+ * defined below are DPMSGVER_DP6.
+ */
+#define DPMSGVER_DP6 11
+
+/* MAGIC number at the start of all dplay packets ("play" in ASCII) */
+#define DPMSGMAGIC_DPLAYMSG  0x79616c70
+
+/* All messages sent from the system are sent with this at the beginning of
+ * the message.
+ */
+
+/* Size is 4 bytes */
+typedef struct tagDPMSG_SENDENVELOPE
+{
+  DWORD dwMagic;
+  WORD  wCommandId;
+  WORD  wVersion;
+} DPMSG_SENDENVELOPE, *LPDPMSG_SENDENVELOPE;
+typedef const DPMSG_SENDENVELOPE* LPCDPMSG_SENDENVELOPE;
+
+typedef struct tagDPMSG_SYSMSGENVELOPE
+{
+  DWORD dwPlayerFrom;
+  DWORD dwPlayerTo;
+} DPMSG_SYSMSGENVELOPE, *LPDPMSG_SYSMSGENVELOPE;
+typedef const DPMSG_SYSMSGENVELOPE* LPCDPMSG_SYSMSGENVELOPE;
+
+
+typedef struct tagDPMSG_ENUMSESSIONSREPLY
+{
+  DPMSG_SENDENVELOPE envelope;
+
+#if 0
+  DWORD dwSize;  /* Size of DPSESSIONDESC2 struct */
+  DWORD dwFlags; /* Sessions flags */
+
+  GUID guidInstance; /* Not 100% sure this is what it is... */
+
+  GUID guidApplication;
+
+  DWORD dwMaxPlayers;
+  DWORD dwCurrentPlayers;
+
+  BYTE unknown[36];
+#else
+  DPSESSIONDESC2 sd;
+#endif
+
+  DWORD dwUnknown;  /* Seems to be equal to 0x5c which is a "\\" */
+                    /* Encryption package string? */
+
+  /* At the end we have ... */
+  /* WCHAR wszSessionName[1];  Var length with NULL terminal */
+
+} DPMSG_ENUMSESSIONSREPLY, *LPDPMSG_ENUMSESSIONSREPLY;
+typedef const DPMSG_ENUMSESSIONSREPLY* LPCDPMSG_ENUMSESSIONSREPLY;
+
+typedef struct tagDPMSG_ENUMSESSIONSREQUEST
+{
+  DPMSG_SENDENVELOPE envelope;
+
+  GUID  guidApplication;
+
+  DWORD dwPasswordSize; /* A Guess. This is normally 0x00000000. */
+                        /* This might be the name server DPID which
+                           is needed for the reply */
+
+  DWORD dwFlags; /* dwFlags from EnumSessions */
+
+} DPMSG_ENUMSESSIONSREQUEST, *LPDPMSG_ENUMSESSIONSREQUEST;
+typedef const DPMSG_ENUMSESSIONSREQUEST* LPCDPMSG_ENUMSESSIONSREQUEST;
+
+/* Size is 146 received - with 18 or 20 bytes header = ~128 bytes */
+typedef struct tagDPMSG_CREATESESSION
+{
+  DPMSG_SENDENVELOPE envelope;
+} DPMSG_CREATESESSION, *LPDPMSG_CREATESESSION;
+typedef const DPMSG_CREATESESSION* LPCDPMSG_CREATESESSION;
+
+/* 12 bytes msg */
+typedef struct tagDPMSG_REQUESTNEWPLAYERID
+{
+  DPMSG_SENDENVELOPE envelope;
+
+  DWORD dwFlags;  /* dwFlags used for CreatePlayer */
+
+} DPMSG_REQUESTNEWPLAYERID, *LPDPMSG_REQUESTNEWPLAYERID;
+typedef const DPMSG_REQUESTNEWPLAYERID* LPCDPMSG_REQUESTNEWPLAYERID;
+
+/* 64 byte - ~18 header ~= 46 bytes msg */
+typedef struct tagDPMSG_NEWPLAYERIDREPLY
+{
+  DPMSG_SENDENVELOPE envelope;
+
+#if 0
+  DPID dpidNewPlayerId;
+#else
+  BYTE unknown[38];
+#endif
+
+} DPMSG_NEWPLAYERIDREPLY, *LPDPMSG_NEWPLAYERIDREPLY;
+typedef const DPMSG_NEWPLAYERIDREPLY* LPCDPMSG_NEWPLAYERIDREPLY;
+
+#include "poppack.h"
+
+
+HRESULT DP_MSG_SendRequestPlayerId( IDirectPlay2AImpl* This, DWORD dwFlags,
+                                    LPDPID lpdipidAllocatedId );
+
+/* FIXME: I don't think that this is a needed method */
+HRESULT DP_MSG_OpenStream( IDirectPlay2AImpl* This );
+
+
+#endif
Index: trunk/src/DPlayX/dplayx_queue.h
===================================================================
--- trunk/src/DPlayX/dplayx_queue.h	(revision 4314)
+++ trunk/src/DPlayX/dplayx_queue.h	(revision 4314)
@@ -0,0 +1,138 @@
+/* A queue definition based on sys/queue.h TAILQ definitions
+ * 
+ * Blame any implementation mistakes on Peter Hunnisett
+ * <hunnise@nortelnetworks.com>
+ */
+
+#ifndef __WINE_DPLAYX_QUEUE_H
+#define __WINE_DPLAYX_QUEUE_H
+
+#include "winbase.h"
+
+#define DPQ_INSERT(a,b,c) DPQ_INSERT_IN_TAIL(a,b,c)
+
+/*
+ * Tail queue definitions.
+ */
+#define DPQ_HEAD(type)                                           \
+struct {                                                         \
+        struct type *lpQHFirst; /* first element */              \
+        struct type **lpQHLast; /* addr of last next element */  \
+}
+
+#define DPQ_ENTRY(type)                                               \
+struct {                                                              \
+        struct type *lpQNext;  /* next element */                     \
+        struct type **lpQPrev; /* address of previous next element */ \
+}
+
+/*
+ * Tail queue functions.
+ */
+#define DPQ_INIT(head)                       \
+do{                                          \
+        (head).lpQHFirst = NULL;             \
+        (head).lpQHLast = &(head).lpQHFirst; \
+} while(0)
+
+/* Front of the queue */
+#define DPQ_FIRST( head ) ( (head).lpQHFirst )
+
+/* Check if the queue has any elements */
+#define DPQ_IS_EMPTY( head ) ( DPQ_FIRST(head) == NULL ) 
+
+/* Next entry -- FIXME: Convert everything over to this macro ... */
+#define DPQ_NEXT( elem ) (elem).lpQNext
+
+#define DPQ_IS_ENDOFLIST( elem ) \
+    ( DPQ_NEXT(elem) == NULL )
+
+/* Insert element at end of queue */
+#define DPQ_INSERT_IN_TAIL(head, elm, field)     \
+do {                                             \
+        (elm)->field.lpQNext = NULL;             \
+        (elm)->field.lpQPrev = (head).lpQHLast;  \
+        *(head).lpQHLast = (elm);                \
+        (head).lpQHLast = &(elm)->field.lpQNext; \
+} while(0)
+
+/* Remove element from the queue */
+#define DPQ_REMOVE(head, elm, field)                    \
+do {                                                    \
+        if (((elm)->field.lpQNext) != NULL)             \
+                (elm)->field.lpQNext->field.lpQPrev =   \
+                    (elm)->field.lpQPrev;               \
+        else                                            \
+                (head).lpQHLast = (elm)->field.lpQPrev; \
+        *(elm)->field.lpQPrev = (elm)->field.lpQNext;   \
+} while(0)
+
+/* head - pointer to DPQ_HEAD struct
+ * elm  - how to find the next element
+ * field - to be concatenated to rc to compare with fieldToCompare
+ * fieldToCompare - The value that we're comparing against
+ * fieldCompareOperator - The logical operator to compare field and
+ *                        fieldToCompare. 
+ * rc - Variable to put the return code. Same type as (head).lpQHFirst
+ */
+#define DPQ_FIND_ENTRY( head, elm, field, fieldCompareOperator, fieldToCompare, rc )\
+do {                                                           \
+  (rc) = (head).lpQHFirst; /* NULL head? */                    \
+                                                               \
+  while( rc )                                                  \
+  {                                                            \
+      /* What we're searching for? */                          \
+      if( (rc)->field fieldCompareOperator (fieldToCompare) )  \
+      {                                                        \
+        break; /* rc == correct element */                     \
+      }                                                        \
+                                                               \
+      /* End of list check */                                  \
+      if( ( (rc) = (rc)->elm.lpQNext ) == (head).lpQHFirst )   \
+      {                                                        \
+        rc = NULL;                                             \
+        break;                                                 \
+      }                                                        \
+  }                                                            \
+} while(0)
+
+/* head - pointer to DPQ_HEAD struct
+ * elm  - how to find the next element
+ * field - to be concatenated to rc to compare with fieldToEqual
+ * fieldToCompare - The value that we're comparing against
+ * fieldCompareOperator - The logical operator to compare field and
+ *                        fieldToCompare.
+ * rc - Variable to put the return code. Same type as (head).lpQHFirst
+ */
+#define DPQ_REMOVE_ENTRY( head, elm, field, fieldCompareOperator, fieldToCompare, rc )\
+do {                                                           \
+  DPQ_FIND_ENTRY( head, elm, field, fieldCompareOperator, fieldToCompare, rc );\
+                                                               \
+  /* Was the element found? */                                 \
+  if( rc )                                                     \
+  {                                                            \
+    DPQ_REMOVE( head, rc, elm );                               \
+  }                                                            \
+} while(0)
+
+/* Delete the entire queue 
+ * head - pointer to the head of the queue
+ * field - field to access the next elements of the queue
+ * type - type of the pointer to the element element
+ * df - a delete function to be called. Declared with DPQ_DECL_DELETECB.
+ */
+#define DPQ_DELETEQ( head, field, type, df )            \
+while( !DPQ_IS_EMPTY(head) )                               \
+{                                                       \
+  type holder = (head).lpQHFirst;                      \
+  DPQ_REMOVE( head, holder, field );                    \
+  df( holder );                                \
+}
+
+/* How to define the method to be passed to DPQ_DELETEQ */
+#define DPQ_DECL_DELETECB( name, type ) void name( type elem )
+
+/* Prototype of a method which just performs a HeapFree on the elem */
+DPQ_DECL_DELETECB( cbDeleteElemFromHeap, LPVOID );
+
+#endif /* __WINE_DPLAYX_QUEUE_H */
Index: trunk/src/DPlayX/dplobby.cpp
===================================================================
--- trunk/src/DPlayX/dplobby.cpp	(revision 4314)
+++ trunk/src/DPlayX/dplobby.cpp	(revision 4314)
@@ -0,0 +1,1961 @@
+/* Direct Play Lobby 2 & 3 Implementation
+ *
+ * Copyright 1998,1999,2000 - Peter Hunnisett
+ *
+ * <presently under construction - contact hunnise@nortelnetworks.com>
+ *
+ */
+#include <string.h>
+
+#include <odin.h>
+#define ICOM_CINTERFACE 1
+#define CINTERFACE
+
+#include "winerror.h"
+#include "winnt.h"
+#include "winreg.h"
+#include "heap.h"
+#include "heapstring.h"
+#include "debugtools.h"
+
+#include "dplobby.h"
+#include "dpinit.h"
+#include "dplayx_global.h"
+#include "dplayx_messages.h"
+#include "dplayx_queue.h"
+
+DEFAULT_DEBUG_CHANNEL(dplay)
+
+#define debugstr_guid(a) a
+
+/*****************************************************************************
+ * Predeclare the interface implementation structures
+ */
+typedef struct IDirectPlayLobbyImpl  IDirectPlayLobbyAImpl;
+typedef struct IDirectPlayLobbyImpl  IDirectPlayLobbyWImpl;
+typedef struct IDirectPlayLobby2Impl IDirectPlayLobby2AImpl;
+typedef struct IDirectPlayLobby2Impl IDirectPlayLobby2WImpl;
+typedef struct IDirectPlayLobby3Impl IDirectPlayLobby3AImpl;
+typedef struct IDirectPlayLobby3Impl IDirectPlayLobby3WImpl;
+
+/* Forward declarations for this module helper methods */
+HRESULT DPL_CreateCompoundAddress ( LPCDPCOMPOUNDADDRESSELEMENT lpElements, DWORD dwElementCount,
+                                    LPVOID lpAddress, LPDWORD lpdwAddressSize, BOOL bAnsiInterface );
+
+HRESULT DPL_CreateAddress( REFGUID guidSP, REFGUID guidDataType, LPCVOID lpData, DWORD dwDataSize,
+                           LPVOID lpAddress, LPDWORD lpdwAddressSize, BOOL bAnsiInterface );
+
+
+
+extern HRESULT DPL_EnumAddress( LPDPENUMADDRESSCALLBACK lpEnumAddressCallback, LPCVOID lpAddress,
+                                DWORD dwAddressSize, LPVOID lpContext );
+
+static HRESULT WINAPI DPL_ConnectEx( IDirectPlayLobbyAImpl* This,
+                                     DWORD dwFlags, REFIID riid,
+                                     LPVOID* lplpDP, IUnknown* pUnk );
+
+BOOL DPL_CreateAndSetLobbyHandles( DWORD dwDestProcessId, HANDLE hDestProcess,
+                                   LPHANDLE lphStart, LPHANDLE lphDeath,
+                                   LPHANDLE lphRead );
+
+
+/*****************************************************************************
+ * IDirectPlayLobby {1,2,3} implementation structure
+ *
+ * The philosophy behind this extra pointer derefernce is that I wanted to
+ * have the same structure for all types of objects without having to do
+ * alot of casting. I also only wanted to implement an interface in the
+ * object it was "released" with IUnknown interface being implemented in the 1 version.
+ * Of course, with these new interfaces comes the data required to keep the state required
+ * by these interfaces. So, basically, the pointers contain the data associated with
+ * a release. If you use the data associated with release 3 in a release 2 object, you'll
+ * get a run time trap, as that won't have any data.
+ *
+ */
+struct DPLMSG
+{
+  DPQ_ENTRY( DPLMSG ) msgs;  /* Link to next queued message */
+};
+typedef struct DPLMSG* LPDPLMSG;
+
+typedef struct tagDirectPlayLobbyIUnknownData
+{
+  ULONG             ulObjRef;
+  CRITICAL_SECTION  DPL_lock;
+} DirectPlayLobbyIUnknownData;
+
+typedef struct tagDirectPlayLobbyData
+{
+  HKEY  hkCallbackKeyHack;
+  DWORD dwMsgThread;
+  DPQ_HEAD( DPLMSG ) msgs;  /* List of messages received */
+} DirectPlayLobbyData;
+
+typedef struct tagDirectPlayLobby2Data
+{
+  BOOL dummy;
+} DirectPlayLobby2Data;
+
+typedef struct tagDirectPlayLobby3Data
+{
+  BOOL dummy;
+} DirectPlayLobby3Data;
+
+#define DPL_IMPL_FIELDS \
+ ULONG ulInterfaceRef; \
+ DirectPlayLobbyIUnknownData*  unk; \
+ DirectPlayLobbyData*          dpl; \
+ DirectPlayLobby2Data*         dpl2; \
+ DirectPlayLobby3Data*         dpl3;
+
+struct IDirectPlayLobbyImpl
+{
+    ICOM_VFIELD(IDirectPlayLobby);
+    DPL_IMPL_FIELDS
+};
+
+struct IDirectPlayLobby2Impl
+{
+    ICOM_VFIELD(IDirectPlayLobby2);
+    DPL_IMPL_FIELDS
+};
+
+struct IDirectPlayLobby3Impl
+{
+    ICOM_VFIELD(IDirectPlayLobby3);
+    DPL_IMPL_FIELDS
+};
+
+
+/* Forward declarations of virtual tables */
+extern ICOM_VTABLE(IDirectPlayLobby)  directPlayLobbyWVT;
+extern ICOM_VTABLE(IDirectPlayLobby2) directPlayLobby2WVT;
+extern ICOM_VTABLE(IDirectPlayLobby3) directPlayLobby3WVT;
+
+extern ICOM_VTABLE(IDirectPlayLobby)  directPlayLobbyAVT;
+extern ICOM_VTABLE(IDirectPlayLobby2) directPlayLobby2AVT;
+extern ICOM_VTABLE(IDirectPlayLobby3) directPlayLobby3AVT;
+
+
+
+
+static BOOL DPL_CreateIUnknown( LPVOID lpDPL )
+{
+  ICOM_THIS(IDirectPlayLobbyAImpl,lpDPL);
+
+  This->unk = (DirectPlayLobbyIUnknownData*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                                       sizeof( *(This->unk) ) );
+  if ( This->unk == NULL )
+  {
+    return FALSE;
+  }
+
+  InitializeCriticalSection( &This->unk->DPL_lock );
+
+  return TRUE;
+}
+
+static BOOL DPL_DestroyIUnknown( LPVOID lpDPL )
+{
+  ICOM_THIS(IDirectPlayLobbyAImpl,lpDPL);
+
+  DeleteCriticalSection( &This->unk->DPL_lock );
+  HeapFree( GetProcessHeap(), 0, This->unk );
+
+  return TRUE;
+}
+
+static BOOL DPL_CreateLobby1( LPVOID lpDPL )
+{
+  ICOM_THIS(IDirectPlayLobbyAImpl,lpDPL);
+
+  This->dpl = (DirectPlayLobbyData*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                               sizeof( *(This->dpl) ) );
+  if ( This->dpl == NULL )
+  {
+    return FALSE;
+  }
+
+  DPQ_INIT( This->dpl->msgs );
+
+  return TRUE;
+}
+
+static BOOL DPL_DestroyLobby1( LPVOID lpDPL )
+{
+  ICOM_THIS(IDirectPlayLobbyAImpl,lpDPL);
+
+  if( This->dpl->dwMsgThread )
+  {
+    FIXME( "Should kill the msg thread\n" );
+  }
+
+  DPQ_DELETEQ( This->dpl->msgs, msgs, LPDPLMSG, cbDeleteElemFromHeap );
+
+  /* Delete the contents */
+  HeapFree( GetProcessHeap(), 0, This->dpl );
+
+  return TRUE;
+}
+
+static BOOL DPL_CreateLobby2( LPVOID lpDPL )
+{
+  ICOM_THIS(IDirectPlayLobby2AImpl,lpDPL);
+
+  This->dpl2 = (DirectPlayLobby2Data*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                                 sizeof( *(This->dpl2) ) );
+  if ( This->dpl2 == NULL )
+  {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+static BOOL DPL_DestroyLobby2( LPVOID lpDPL )
+{
+  ICOM_THIS(IDirectPlayLobby2AImpl,lpDPL);
+
+  HeapFree( GetProcessHeap(), 0, This->dpl2 );
+
+  return TRUE;
+}
+
+static BOOL DPL_CreateLobby3( LPVOID lpDPL )
+{
+  ICOM_THIS(IDirectPlayLobby3AImpl,lpDPL);
+
+  This->dpl3 = (DirectPlayLobby3Data*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                                 sizeof( *(This->dpl3) ) );
+  if ( This->dpl3 == NULL )
+  {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+static BOOL DPL_DestroyLobby3( LPVOID lpDPL )
+{
+  ICOM_THIS(IDirectPlayLobby3AImpl,lpDPL);
+
+  HeapFree( GetProcessHeap(), 0, This->dpl3 );
+
+  return TRUE;
+}
+
+
+/* The COM interface for upversioning an interface
+ * We've been given a GUID (riid) and we need to replace the present
+ * interface with that of the requested interface.
+ *
+ * Snip from some Microsoft document:
+ * There are four requirements for implementations of QueryInterface (In these
+ * cases, "must succeed" means "must succeed barring catastrophic failure."):
+ *
+ *  * The set of interfaces accessible on an object through
+ *    IUnknown::QueryInterface must be static, not dynamic. This means that
+ *    if a call to QueryInterface for a pointer to a specified interface
+ *    succeeds the first time, it must succeed again, and if it fails the
+ *    first time, it must fail on all subsequent queries.
+ *  * It must be symmetric ~W if a client holds a pointer to an interface on
+ *    an object, and queries for that interface, the call must succeed.
+ *  * It must be reflexive ~W if a client holding a pointer to one interface
+ *    queries successfully for another, a query through the obtained pointer
+ *    for the first interface must succeed.
+ *  * It must be transitive ~W if a client holding a pointer to one interface
+ *    queries successfully for a second, and through that pointer queries
+ *    successfully for a third interface, a query for the first interface
+ *    through the pointer for the third interface must succeed.
+ */
+extern
+HRESULT DPL_CreateInterface
+         ( REFIID riid, LPVOID* ppvObj )
+{
+  TRACE( " for %s\n", debugstr_guid( riid ) );
+
+  *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                       sizeof( IDirectPlayLobbyWImpl ) );
+
+  if( *ppvObj == NULL )
+  {
+    return DPERR_OUTOFMEMORY;
+  }
+
+  if( IsEqualGUID( &IID_IDirectPlayLobby, riid ) )
+  {
+    ICOM_THIS(IDirectPlayLobbyWImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobbyWVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlayLobbyA, riid ) )
+  {
+    ICOM_THIS(IDirectPlayLobbyAImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobbyAVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlayLobby2, riid ) )
+  {
+    ICOM_THIS(IDirectPlayLobby2WImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobby2WVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlayLobby2A, riid ) )
+  {
+    ICOM_THIS(IDirectPlayLobby2AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobby2AVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlayLobby3, riid ) )
+  {
+    ICOM_THIS(IDirectPlayLobby3WImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobby3WVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlayLobby3A, riid ) )
+  {
+    ICOM_THIS(IDirectPlayLobby3AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobby3AVT;
+  }
+  else
+  {
+    /* Unsupported interface */
+    HeapFree( GetProcessHeap(), 0, *ppvObj );
+    *ppvObj = NULL;
+
+    return E_NOINTERFACE;
+  }
+
+  /* Initialize it */
+  if ( DPL_CreateIUnknown( *ppvObj ) &&
+       DPL_CreateLobby1( *ppvObj ) &&
+       DPL_CreateLobby2( *ppvObj ) &&
+       DPL_CreateLobby3( *ppvObj )
+     )
+  {
+    IDirectPlayLobby_AddRef( (LPDIRECTPLAYLOBBY)*ppvObj );
+    return S_OK;
+  }
+
+  /* Initialize failed, destroy it */
+  DPL_DestroyLobby3( *ppvObj );
+  DPL_DestroyLobby2( *ppvObj );
+  DPL_DestroyLobby1( *ppvObj );
+  DPL_DestroyIUnknown( *ppvObj );
+  HeapFree( GetProcessHeap(), 0, *ppvObj );
+
+  *ppvObj = NULL;
+  return DPERR_NOMEMORY;
+}
+
+static HRESULT WINAPI DPL_QueryInterface
+( LPDIRECTPLAYLOBBYA iface,
+  REFIID riid,
+  LPVOID* ppvObj )
+{
+  ICOM_THIS(IDirectPlayLobbyAImpl,iface);
+  TRACE("(%p)->(%s,%p)\n", This, debugstr_guid( riid ), ppvObj );
+
+  *ppvObj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                       sizeof( IDirectPlayLobbyWImpl ) );
+
+  if( *ppvObj == NULL )
+  {
+    return DPERR_OUTOFMEMORY;
+  }
+
+  CopyMemory( *ppvObj, iface, sizeof( IDirectPlayLobbyWImpl )  );
+  (*(IDirectPlayLobbyWImpl**)ppvObj)->ulInterfaceRef = 0;
+
+  if( IsEqualGUID( &IID_IDirectPlayLobby, riid ) )
+  {
+    ICOM_THIS(IDirectPlayLobbyWImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobbyWVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlayLobbyA, riid ) )
+  {
+    ICOM_THIS(IDirectPlayLobbyAImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobbyAVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlayLobby2, riid ) )
+  {
+    ICOM_THIS(IDirectPlayLobby2WImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobby2WVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlayLobby2A, riid ) )
+  {
+    ICOM_THIS(IDirectPlayLobby2AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobby2AVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlayLobby3, riid ) )
+  {
+    ICOM_THIS(IDirectPlayLobby3WImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobby3WVT;
+  }
+  else if( IsEqualGUID( &IID_IDirectPlayLobby3A, riid ) )
+  {
+    ICOM_THIS(IDirectPlayLobby3AImpl,*ppvObj);
+    ICOM_VTBL(This) = &directPlayLobby3AVT;
+  }
+  else
+  {
+    /* Unsupported interface */
+    HeapFree( GetProcessHeap(), 0, *ppvObj );
+    *ppvObj = NULL;
+
+    return E_NOINTERFACE;
+  }
+
+  IDirectPlayLobby_AddRef( (LPDIRECTPLAYLOBBY)*ppvObj );
+
+  return S_OK;
+}
+
+/*
+ * Simple procedure. Just increment the reference count to this
+ * structure and return the new reference count.
+ */
+static ULONG WINAPI DPL_AddRef
+( LPDIRECTPLAYLOBBY iface )
+{
+  ULONG ulInterfaceRefCount, ulObjRefCount;
+  ICOM_THIS(IDirectPlayLobbyWImpl,iface);
+
+  ulObjRefCount       = InterlockedIncrement( (LPLONG)&This->unk->ulObjRef );
+  ulInterfaceRefCount = InterlockedIncrement( (LPLONG)&This->ulInterfaceRef );
+
+  TRACE( "ref count incremented to %lu:%lu for %p\n",
+         ulInterfaceRefCount, ulObjRefCount, This );
+
+  return ulObjRefCount;
+}
+
+/*
+ * Simple COM procedure. Decrease the reference count to this object.
+ * If the object no longer has any reference counts, free up the associated
+ * memory.
+ */
+static ULONG WINAPI DPL_Release
+( LPDIRECTPLAYLOBBYA iface )
+{
+  ULONG ulInterfaceRefCount, ulObjRefCount;
+  ICOM_THIS(IDirectPlayLobbyAImpl,iface);
+
+  ulObjRefCount       = InterlockedDecrement( (LPLONG)&This->unk->ulObjRef );
+  ulInterfaceRefCount = InterlockedDecrement( (LPLONG)&This->ulInterfaceRef );
+
+  TRACE( "ref count decremented to %lu:%lu for %p\n",
+         ulInterfaceRefCount, ulObjRefCount, This );
+
+  /* Deallocate if this is the last reference to the object */
+  if( ulObjRefCount == 0 )
+  {
+     DPL_DestroyLobby3( This );
+     DPL_DestroyLobby2( This );
+     DPL_DestroyLobby1( This );
+     DPL_DestroyIUnknown( This );
+  }
+
+  if( ulInterfaceRefCount == 0 )
+  {
+    HeapFree( GetProcessHeap(), 0, This );
+  }
+
+  return ulInterfaceRefCount;
+}
+
+
+/********************************************************************
+ *
+ * Connects an application to the session specified by the DPLCONNECTION
+ * structure currently stored with the DirectPlayLobby object.
+ *
+ * Returns a IDirectPlay interface.
+ *
+ */
+static HRESULT WINAPI DPL_ConnectEx
+( IDirectPlayLobbyAImpl* This,
+  DWORD     dwFlags,
+  REFIID    riid,
+  LPVOID*   lplpDP,
+  IUnknown* pUnk)
+{
+  HRESULT         hr;
+  DWORD           dwOpenFlags = 0;
+  DWORD           dwConnSize = 0;
+  LPDPLCONNECTION lpConn;
+
+  FIXME("(%p)->(0x%08lx,%p,%p): semi stub\n", This, dwFlags, lplpDP, pUnk );
+
+  if( pUnk )
+  {
+     return DPERR_INVALIDPARAMS;
+  }
+
+  /* Backwards compatibility */
+  if( dwFlags == 0 )
+  {
+    dwFlags = DPCONNECT_RETURNSTATUS;
+  }
+
+  /* Create the DirectPlay interface */
+  if( ( hr = DP_CreateInterface( riid, lplpDP ) ) != DP_OK )
+  {
+     ERR( "error creating interface for %s:%s.\n",
+          debugstr_guid( riid ), DPLAYX_HresultToString( hr ) );
+     return hr;
+  }
+
+  /* FIXME: Is it safe/correct to use appID of 0? */
+  hr = IDirectPlayLobby_GetConnectionSettings( (LPDIRECTPLAYLOBBY)This,
+                                               0, NULL, &dwConnSize );
+  if( hr != DPERR_BUFFERTOOSMALL )
+  {
+    return hr;
+  }
+
+  lpConn = (LPDPLCONNECTION)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwConnSize );
+
+  if( lpConn == NULL )
+  {
+    return DPERR_NOMEMORY;
+  }
+
+  /* FIXME: Is it safe/correct to use appID of 0? */
+  hr = IDirectPlayLobby_GetConnectionSettings( (LPDIRECTPLAYLOBBY)This,
+                                               0, lpConn, &dwConnSize );
+  if( FAILED( hr ) )
+  {
+    return hr;
+  }
+
+#if 0
+  /* - Need to call IDirectPlay::EnumConnections with the service provider to get that good information
+   * - Need to call CreateAddress to create the lpConnection param for IDirectPlay::InitializeConnection
+   * - Call IDirectPlay::InitializeConnection
+   */
+
+  /* Now initialize the Service Provider */
+  hr = IDirectPlayX_InitializeConnection( (*(LPDIRECTPLAY2*)lplpDP),
+#endif
+
+
+  /* Setup flags to pass into DirectPlay::Open */
+  if( dwFlags & DPCONNECT_RETURNSTATUS )
+  {
+    dwOpenFlags |= DPOPEN_RETURNSTATUS;
+  }
+  dwOpenFlags |= lpConn->dwFlags;
+
+  hr = IDirectPlayX_Open( (*(LPDIRECTPLAY2*)lplpDP), lpConn->lpSessionDesc,
+                          dwOpenFlags );
+
+  HeapFree( GetProcessHeap(), 0, lpConn );
+
+  return hr;
+}
+
+static HRESULT WINAPI IDirectPlayLobbyAImpl_Connect
+( LPDIRECTPLAYLOBBYA iface,
+  DWORD dwFlags,
+  LPDIRECTPLAY2A* lplpDP,
+  IUnknown* pUnk)
+{
+  ICOM_THIS(IDirectPlayLobbyAImpl,iface);
+  return DPL_ConnectEx( This, dwFlags, &IID_IDirectPlay2A,
+                        (LPVOID*)lplpDP, pUnk );
+}
+
+static HRESULT WINAPI IDirectPlayLobbyWImpl_Connect
+( LPDIRECTPLAYLOBBY iface,
+  DWORD dwFlags,
+  LPDIRECTPLAY2* lplpDP,
+  IUnknown* pUnk)
+{
+  ICOM_THIS(IDirectPlayLobbyAImpl,iface); /* Yes cast to A */
+  return DPL_ConnectEx( This, dwFlags, &IID_IDirectPlay2,
+                        (LPVOID*)lplpDP, pUnk );
+}
+
+/********************************************************************
+ *
+ * Creates a DirectPlay Address, given a service provider-specific network
+ * address.
+ * Returns an address contains the globally unique identifier
+ * (GUID) of the service provider and data that the service provider can
+ * interpret as a network address.
+ *
+ * NOTE: It appears that this method is supposed to be really really stupid
+ *       with no error checking on the contents.
+ */
+static HRESULT WINAPI IDirectPlayLobbyAImpl_CreateAddress
+( LPDIRECTPLAYLOBBYA iface,
+  REFGUID guidSP,
+  REFGUID guidDataType,
+  LPCVOID lpData,
+  DWORD dwDataSize,
+  LPVOID lpAddress,
+  LPDWORD lpdwAddressSize )
+{
+  return DPL_CreateAddress( guidSP, guidDataType, lpData, dwDataSize,
+                            lpAddress, lpdwAddressSize, TRUE );
+}
+
+static HRESULT WINAPI IDirectPlayLobbyWImpl_CreateAddress
+( LPDIRECTPLAYLOBBY iface,
+  REFGUID guidSP,
+  REFGUID guidDataType,
+  LPCVOID lpData,
+  DWORD dwDataSize,
+  LPVOID lpAddress,
+  LPDWORD lpdwAddressSize )
+{
+  return DPL_CreateAddress( guidSP, guidDataType, lpData, dwDataSize,
+                            lpAddress, lpdwAddressSize, FALSE );
+}
+
+HRESULT DPL_CreateAddress(
+  REFGUID guidSP,
+  REFGUID guidDataType,
+  LPCVOID lpData,
+  DWORD dwDataSize,
+  LPVOID lpAddress,
+  LPDWORD lpdwAddressSize,
+  BOOL bAnsiInterface )
+{
+  const DWORD dwNumAddElements = 2; /* Service Provide & address data type */
+  DPCOMPOUNDADDRESSELEMENT addressElements[ 2 /* dwNumAddElements */ ];
+
+  TRACE( "(%p)->(%p,%p,0x%08lx,%p,%p,%d)\n", guidSP, guidDataType, lpData, dwDataSize,
+                                             lpAddress, lpdwAddressSize, bAnsiInterface );
+
+  addressElements[ 0 ].guidDataType = DPAID_ServiceProvider;
+  addressElements[ 0 ].dwDataSize = sizeof( GUID );
+  addressElements[ 0 ].lpData = (LPVOID)guidSP;
+
+  addressElements[ 1 ].guidDataType = *guidDataType;
+  addressElements[ 1 ].dwDataSize = dwDataSize;
+  addressElements[ 1 ].lpData = (LPVOID)lpData;
+
+  /* Call CreateCompoundAddress to cut down on code.
+     NOTE: We can do this because we don't support DPL 1 interfaces! */
+  return DPL_CreateCompoundAddress( addressElements, dwNumAddElements,
+                                    lpAddress, lpdwAddressSize, bAnsiInterface );
+}
+
+
+
+/********************************************************************
+ *
+ * Parses out chunks from the DirectPlay Address buffer by calling the
+ * given callback function, with lpContext, for each of the chunks.
+ *
+ */
+static HRESULT WINAPI IDirectPlayLobbyAImpl_EnumAddress
+( LPDIRECTPLAYLOBBYA iface,
+  LPDPENUMADDRESSCALLBACK lpEnumAddressCallback,
+  LPCVOID lpAddress,
+  DWORD dwAddressSize,
+  LPVOID lpContext )
+{
+  ICOM_THIS(IDirectPlayLobbyAImpl,iface);
+
+  TRACE("(%p)->(%p,%p,0x%08lx,%p)\n", This, lpEnumAddressCallback, lpAddress,
+                                      dwAddressSize, lpContext );
+
+  return DPL_EnumAddress( lpEnumAddressCallback, lpAddress, dwAddressSize, lpContext );
+}
+
+static HRESULT WINAPI IDirectPlayLobbyWImpl_EnumAddress
+( LPDIRECTPLAYLOBBY iface,
+  LPDPENUMADDRESSCALLBACK lpEnumAddressCallback,
+  LPCVOID lpAddress,
+  DWORD dwAddressSize,
+  LPVOID lpContext )
+{
+  ICOM_THIS(IDirectPlayLobbyWImpl,iface);
+
+  TRACE("(%p)->(%p,%p,0x%08lx,%p)\n", This, lpEnumAddressCallback, lpAddress,
+                                      dwAddressSize, lpContext );
+
+  return DPL_EnumAddress( lpEnumAddressCallback, lpAddress, dwAddressSize, lpContext );
+}
+
+extern HRESULT DPL_EnumAddress( LPDPENUMADDRESSCALLBACK lpEnumAddressCallback, LPCVOID lpAddress,
+                                DWORD dwAddressSize, LPVOID lpContext )
+{
+  DWORD dwTotalSizeEnumerated = 0;
+
+  /* FIXME: First chunk is always the total size chunk - Should we report it? */
+
+  while ( dwTotalSizeEnumerated < dwAddressSize )
+  {
+    LPDPADDRESS lpElements = (LPDPADDRESS)lpAddress;
+    DWORD dwSizeThisEnumeration;
+
+    /* Invoke the enum method. If false is returned, stop enumeration */
+    if ( !lpEnumAddressCallback( &lpElements->guidDataType,
+                                 lpElements->dwDataSize,
+                                 (BYTE*)lpElements + sizeof( DPADDRESS ),
+                                 lpContext ) )
+    {
+      break;
+    }
+
+    dwSizeThisEnumeration  = sizeof( DPADDRESS ) + lpElements->dwDataSize;
+    lpAddress = (BYTE*) lpAddress + dwSizeThisEnumeration;
+    dwTotalSizeEnumerated += dwSizeThisEnumeration;
+  }
+
+  return DP_OK;
+}
+
+/********************************************************************
+ *
+ * Enumerates all the address types that a given service provider needs to
+ * build the DirectPlay Address.
+ *
+ */
+static HRESULT WINAPI IDirectPlayLobbyAImpl_EnumAddressTypes
+( LPDIRECTPLAYLOBBYA iface,
+  LPDPLENUMADDRESSTYPESCALLBACK lpEnumAddressTypeCallback,
+  REFGUID guidSP,
+  LPVOID lpContext,
+  DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlayLobbyAImpl,iface);
+
+  HKEY   hkResult;
+  LPCSTR searchSubKey    = "SOFTWARE\\Microsoft\\DirectPlay\\Service Providers";
+  DWORD  dwIndex, sizeOfSubKeyName=50;
+  char   subKeyName[51];
+  FILETIME filetime;
+
+  TRACE(" (%p)->(%p,%p,%p,0x%08lx)\n", This, lpEnumAddressTypeCallback, guidSP, lpContext, dwFlags );
+
+  if( dwFlags != 0 )
+  {
+    return DPERR_INVALIDPARAMS;
+  }
+
+  if( !(void*)lpEnumAddressTypeCallback || !*(int*)lpEnumAddressTypeCallback )
+  {
+     return DPERR_INVALIDPARAMS;
+  }
+
+  if( guidSP == NULL )
+  {
+    return DPERR_INVALIDOBJECT;
+  }
+
+    /* Need to loop over the service providers in the registry */
+    if( RegOpenKeyExA( HKEY_LOCAL_MACHINE, searchSubKey,
+                         0, KEY_READ, &hkResult ) != ERROR_SUCCESS )
+    {
+      /* Hmmm. Does this mean that there are no service providers? */
+      ERR(": no service providers?\n");
+      return DP_OK;
+    }
+
+    /* Traverse all the service providers we have available */
+    for( dwIndex=0;
+         RegEnumKeyExA( hkResult, dwIndex, subKeyName, &sizeOfSubKeyName,
+                        NULL, NULL, NULL, &filetime ) != ERROR_NO_MORE_ITEMS;
+         ++dwIndex, sizeOfSubKeyName=50 )
+    {
+
+      HKEY     hkServiceProvider, hkServiceProviderAt;
+      GUID     serviceProviderGUID;
+      DWORD    returnTypeGUID, sizeOfReturnBuffer = 50;
+      char     atSubKey[51];
+      char     returnBuffer[51];
+      LPWSTR   lpWGUIDString;
+      DWORD    dwAtIndex;
+      LPSTR    atKey = "Address Types";
+      LPSTR    guidDataSubKey   = "Guid";
+      FILETIME filetime;
+
+
+      TRACE(" this time through: %s\n", subKeyName );
+
+      /* Get a handle for this particular service provider */
+      if( RegOpenKeyExA( hkResult, subKeyName, 0, KEY_READ,
+                         &hkServiceProvider ) != ERROR_SUCCESS )
+      {
+         ERR(": what the heck is going on?\n" );
+         continue;
+      }
+
+      if( RegQueryValueExA( hkServiceProvider, guidDataSubKey,
+                            NULL, &returnTypeGUID, (LPBYTE)returnBuffer,
+                            &sizeOfReturnBuffer ) != ERROR_SUCCESS )
+      {
+        ERR(": missing GUID registry data members\n" );
+        continue;
+      }
+
+      /* FIXME: Check return types to ensure we're interpreting data right */
+      lpWGUIDString = HEAP_strdupAtoW( GetProcessHeap(), 0, returnBuffer );
+      CLSIDFromString( (LPCOLESTR)lpWGUIDString, &serviceProviderGUID );
+      HeapFree( GetProcessHeap(), 0, lpWGUIDString );
+      /* FIXME: Have I got a memory leak on the serviceProviderGUID? */
+
+      /* Determine if this is the Service Provider that the user asked for */
+      if( !IsEqualGUID( &serviceProviderGUID, guidSP ) )
+      {
+        continue;
+      }
+
+      /* Get a handle for this particular service provider */
+      if( RegOpenKeyExA( hkServiceProvider, atKey, 0, KEY_READ,
+                         &hkServiceProviderAt ) != ERROR_SUCCESS )
+      {
+        TRACE(": No Address Types registry data sub key/members\n" );
+        break;
+      }
+
+      /* Traverse all the address type we have available */
+      for( dwAtIndex=0;
+           RegEnumKeyExA( hkServiceProviderAt, dwAtIndex, atSubKey, &sizeOfSubKeyName,
+                          NULL, NULL, NULL, &filetime ) != ERROR_NO_MORE_ITEMS;
+           ++dwAtIndex, sizeOfSubKeyName=50 )
+      {
+        TRACE( "Found Address Type GUID %s\n", atSubKey );
+
+        /* FIXME: Check return types to ensure we're interpreting data right */
+        lpWGUIDString = HEAP_strdupAtoW( GetProcessHeap(), 0, atSubKey );
+        CLSIDFromString( (LPCOLESTR)lpWGUIDString, &serviceProviderGUID );
+        HeapFree( GetProcessHeap(), 0, lpWGUIDString );
+        /* FIXME: Have I got a memory leak on the serviceProviderGUID? */
+
+        /* The enumeration will return FALSE if we are not to continue */
+        if( !lpEnumAddressTypeCallback( &serviceProviderGUID, lpContext, 0 ) )
+        {
+           WARN("lpEnumCallback returning FALSE\n" );
+           break; /* FIXME: This most likely has to break from the procedure...*/
+        }
+
+      }
+
+      /* We only enumerate address types for 1 GUID. We've found it, so quit looking */
+      break;
+    }
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlayLobbyWImpl_EnumAddressTypes
+( LPDIRECTPLAYLOBBY iface,
+  LPDPLENUMADDRESSTYPESCALLBACK lpEnumAddressTypeCallback,
+  REFGUID guidSP,
+  LPVOID lpContext,
+  DWORD dwFlags )
+{
+  FIXME(":stub\n");
+  return DPERR_OUTOFMEMORY;
+}
+
+/********************************************************************
+ *
+ * Enumerates what applications are registered with DirectPlay by
+ * invoking the callback function with lpContext.
+ *
+ */
+static HRESULT WINAPI IDirectPlayLobbyWImpl_EnumLocalApplications
+( LPDIRECTPLAYLOBBY iface,
+  LPDPLENUMLOCALAPPLICATIONSCALLBACK lpEnumLocalAppCallback,
+  LPVOID lpContext,
+  DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlayLobbyWImpl,iface);
+
+  FIXME("(%p)->(%p,%p,0x%08lx):stub\n", This, lpEnumLocalAppCallback, lpContext, dwFlags );
+
+  return DPERR_OUTOFMEMORY;
+}
+
+static HRESULT WINAPI IDirectPlayLobbyAImpl_EnumLocalApplications
+( LPDIRECTPLAYLOBBYA iface,
+  LPDPLENUMLOCALAPPLICATIONSCALLBACK lpEnumLocalAppCallback,
+  LPVOID lpContext,
+  DWORD dwFlags )
+{
+  ICOM_THIS(IDirectPlayLobbyAImpl,iface);
+
+  HKEY hkResult;
+  LPCSTR searchSubKey    = "SOFTWARE\\Microsoft\\DirectPlay\\Applications";
+  LPSTR guidDataSubKey   = "Guid";
+  DWORD dwIndex, sizeOfSubKeyName=50;
+  char subKeyName[51];
+  FILETIME filetime;
+
+  TRACE("(%p)->(%p,%p,0x%08lx)\n", This, lpEnumLocalAppCallback, lpContext, dwFlags );
+
+  if( dwFlags != 0 )
+  {
+    return DPERR_INVALIDPARAMS;
+  }
+
+  if( !(void*)lpEnumLocalAppCallback || !*(int*)lpEnumLocalAppCallback )
+  {
+     return DPERR_INVALIDPARAMS;
+  }
+
+  /* Need to loop over the service providers in the registry */
+  if( RegOpenKeyExA( HKEY_LOCAL_MACHINE, searchSubKey,
+                     0, KEY_READ, &hkResult ) != ERROR_SUCCESS )
+  {
+    /* Hmmm. Does this mean that there are no service providers? */
+    ERR(": no service providers?\n");
+    return DP_OK;
+  }
+
+  /* Traverse all registered applications */
+  for( dwIndex=0;
+       RegEnumKeyExA( hkResult, dwIndex, subKeyName, &sizeOfSubKeyName, NULL, NULL, NULL, &filetime ) != ERROR_NO_MORE_ITEMS;
+       ++dwIndex, sizeOfSubKeyName=50 )
+  {
+
+    HKEY       hkServiceProvider;
+    GUID       serviceProviderGUID;
+    DWORD      returnTypeGUID, sizeOfReturnBuffer = 50;
+    char       returnBuffer[51];
+    LPWSTR     lpWGUIDString;
+    DPLAPPINFO dplAppInfo;
+
+    TRACE(" this time through: %s\n", subKeyName );
+
+    /* Get a handle for this particular service provider */
+    if( RegOpenKeyExA( hkResult, subKeyName, 0, KEY_READ,
+                       &hkServiceProvider ) != ERROR_SUCCESS )
+    {
+       ERR(": what the heck is going on?\n" );
+       continue;
+    }
+
+    if( RegQueryValueExA( hkServiceProvider, guidDataSubKey,
+                          NULL, &returnTypeGUID, (LPBYTE)returnBuffer,
+                          &sizeOfReturnBuffer ) != ERROR_SUCCESS )
+    {
+      ERR(": missing GUID registry data members\n" );
+      continue;
+    }
+
+    /* FIXME: Check return types to ensure we're interpreting data right */
+    lpWGUIDString = HEAP_strdupAtoW( GetProcessHeap(), 0, returnBuffer );
+    CLSIDFromString( (LPCOLESTR)lpWGUIDString, &serviceProviderGUID );
+    HeapFree( GetProcessHeap(), 0, lpWGUIDString );
+    /* FIXME: Have I got a memory leak on the serviceProviderGUID? */
+
+    dplAppInfo.dwSize               = sizeof( dplAppInfo );
+    dplAppInfo.guidApplication      = serviceProviderGUID;
+    dplAppInfo.appName.lpszAppNameA = subKeyName;
+
+    EnterCriticalSection( &This->unk->DPL_lock );
+
+    memcpy( &This->dpl->hkCallbackKeyHack, &hkServiceProvider, sizeof( hkServiceProvider ) );
+
+    if( !lpEnumLocalAppCallback( &dplAppInfo, lpContext, dwFlags ) )
+    {
+       LeaveCriticalSection( &This->unk->DPL_lock );
+       break;
+    }
+
+    LeaveCriticalSection( &This->unk->DPL_lock );
+  }
+
+  return DP_OK;
+}
+
+/********************************************************************
+ *
+ * Retrieves the DPLCONNECTION structure that contains all the information
+ * needed to start and connect an application. This was generated using
+ * either the RunApplication or SetConnectionSettings methods.
+ *
+ * NOTES: If lpData is NULL then just return lpdwDataSize. This allows
+ *        the data structure to be allocated by our caller which can then
+ *        call this procedure/method again with a valid data pointer.
+ */
+static HRESULT WINAPI IDirectPlayLobbyAImpl_GetConnectionSettings
+( LPDIRECTPLAYLOBBYA iface,
+  DWORD dwAppID,
+  LPVOID lpData,
+  LPDWORD lpdwDataSize )
+{
+  ICOM_THIS(IDirectPlayLobbyAImpl,iface);
+  HRESULT hr;
+
+  TRACE("(%p)->(0x%08lx,%p,%p)\n", This, dwAppID, lpData, lpdwDataSize );
+
+  EnterCriticalSection( &This->unk->DPL_lock );
+
+  hr = DPLAYX_GetConnectionSettingsA( dwAppID,
+                                      lpData,
+                                      lpdwDataSize
+                                    );
+
+  LeaveCriticalSection( &This->unk->DPL_lock );
+
+  return hr;
+}
+
+static HRESULT WINAPI IDirectPlayLobbyWImpl_GetConnectionSettings
+( LPDIRECTPLAYLOBBY iface,
+  DWORD dwAppID,
+  LPVOID lpData,
+  LPDWORD lpdwDataSize )
+{
+  ICOM_THIS(IDirectPlayLobbyWImpl,iface);
+  HRESULT hr;
+
+  TRACE("(%p)->(0x%08lx,%p,%p)\n", This, dwAppID, lpData, lpdwDataSize );
+
+  EnterCriticalSection( &This->unk->DPL_lock );
+
+  hr = DPLAYX_GetConnectionSettingsW( dwAppID,
+                                      lpData,
+                                      lpdwDataSize
+                                    );
+
+  LeaveCriticalSection( &This->unk->DPL_lock );
+
+  return hr;
+}
+
+/********************************************************************
+ *
+ * Retrieves the message sent between a lobby client and a DirectPlay
+ * application. All messages are queued until received.
+ *
+ */
+static HRESULT WINAPI IDirectPlayLobbyAImpl_ReceiveLobbyMessage
+( LPDIRECTPLAYLOBBYA iface,
+  DWORD dwFlags,
+  DWORD dwAppID,
+  LPDWORD lpdwMessageFlags,
+  LPVOID lpData,
+  LPDWORD lpdwDataSize )
+{
+  ICOM_THIS(IDirectPlayLobbyAImpl,iface);
+  FIXME(":stub %p %08lx %08lx %p %p %p\n", This, dwFlags, dwAppID, lpdwMessageFlags, lpData,
+         lpdwDataSize );
+  return DPERR_OUTOFMEMORY;
+}
+
+static HRESULT WINAPI IDirectPlayLobbyWImpl_ReceiveLobbyMessage
+( LPDIRECTPLAYLOBBY iface,
+  DWORD dwFlags,
+  DWORD dwAppID,
+  LPDWORD lpdwMessageFlags,
+  LPVOID lpData,
+  LPDWORD lpdwDataSize )
+{
+  ICOM_THIS(IDirectPlayLobbyWImpl,iface);
+  FIXME(":stub %p %08lx %08lx %p %p %p\n", This, dwFlags, dwAppID, lpdwMessageFlags, lpData,
+         lpdwDataSize );
+  return DPERR_OUTOFMEMORY;
+}
+
+typedef struct tagRunApplicationEnumStruct
+{
+  IDirectPlayLobbyAImpl* This;
+
+  GUID  appGUID;
+  LPSTR lpszPath;
+  LPSTR lpszFileName;
+  LPSTR lpszCommandLine;
+  LPSTR lpszCurrentDirectory;
+} RunApplicationEnumStruct, *lpRunApplicationEnumStruct;
+
+/* To be called by RunApplication to find how to invoke the function */
+static BOOL CALLBACK RunApplicationA_EnumLocalApplications
+( LPCDPLAPPINFO   lpAppInfo,
+  LPVOID          lpContext,
+  DWORD           dwFlags )
+{
+  lpRunApplicationEnumStruct lpData = (lpRunApplicationEnumStruct)lpContext;
+
+  if( IsEqualGUID( &lpAppInfo->guidApplication, &lpData->appGUID ) )
+  {
+    char  returnBuffer[200];
+    DWORD returnType, sizeOfReturnBuffer;
+    LPSTR clSubKey   = "CommandLine";
+    LPSTR cdSubKey   = "CurrentDirectory";
+    LPSTR fileSubKey = "File";
+    LPSTR pathSubKey = "Path";
+
+    /* FIXME: Lazy man hack - dplay struct has the present reg key saved */
+
+    sizeOfReturnBuffer = 200;
+
+    /* Get all the appropriate data from the registry */
+    if( RegQueryValueExA( lpData->This->dpl->hkCallbackKeyHack, clSubKey,
+                          NULL, &returnType, (LPBYTE)returnBuffer,
+                          &sizeOfReturnBuffer ) != ERROR_SUCCESS )
+    {
+       ERR( ": missing CommandLine registry data member\n" );
+    }
+    else
+    {
+      lpData->lpszCommandLine = HEAP_strdupA( GetProcessHeap(), HEAP_ZERO_MEMORY, returnBuffer );
+    }
+
+    sizeOfReturnBuffer = 200;
+
+    if( RegQueryValueExA( lpData->This->dpl->hkCallbackKeyHack, cdSubKey,
+                          NULL, &returnType, (LPBYTE)returnBuffer,
+                          &sizeOfReturnBuffer ) != ERROR_SUCCESS )
+    {
+       ERR( ": missing CurrentDirectory registry data member\n" );
+    }
+    else
+    {
+      lpData->lpszCurrentDirectory = HEAP_strdupA( GetProcessHeap(), HEAP_ZERO_MEMORY, returnBuffer );
+    }
+
+    sizeOfReturnBuffer = 200;
+
+    if( RegQueryValueExA( lpData->This->dpl->hkCallbackKeyHack, fileSubKey,
+                          NULL, &returnType, (LPBYTE)returnBuffer,
+                          &sizeOfReturnBuffer ) != ERROR_SUCCESS )
+    {
+       ERR( ": missing File registry data member\n" );
+    }
+    else
+    {
+      lpData->lpszFileName = HEAP_strdupA( GetProcessHeap(), HEAP_ZERO_MEMORY, returnBuffer );
+    }
+
+    sizeOfReturnBuffer = 200;
+
+    if( RegQueryValueExA( lpData->This->dpl->hkCallbackKeyHack, pathSubKey,
+                          NULL, &returnType, (LPBYTE)returnBuffer,
+                          &sizeOfReturnBuffer ) != ERROR_SUCCESS )
+    {
+       ERR( ": missing Path registry data member\n" );
+    }
+    else
+    {
+      lpData->lpszPath = HEAP_strdupA( GetProcessHeap(), HEAP_ZERO_MEMORY, returnBuffer );
+    }
+
+    return FALSE; /* No need to keep going as we found what we wanted */
+  }
+
+  return TRUE; /* Keep enumerating, haven't found the application yet */
+}
+
+BOOL DPL_CreateAndSetLobbyHandles( DWORD dwDestProcessId, HANDLE hDestProcess,
+                                   LPHANDLE lphStart, LPHANDLE lphDeath,
+                                   LPHANDLE lphRead )
+{
+  /* These are the handles for the created process */
+  HANDLE hAppStart, hAppDeath, hAppRead;
+  SECURITY_ATTRIBUTES s_attrib;
+
+  s_attrib.nLength              = sizeof( s_attrib );
+  s_attrib.lpSecurityDescriptor = NULL;
+  s_attrib.bInheritHandle       = TRUE;
+
+  /* FIXME: Is there a handle leak here? */
+  *lphStart = CreateEventA( &s_attrib, TRUE, FALSE, NULL );
+  *lphDeath = CreateEventA( &s_attrib, TRUE, FALSE, NULL );
+  *lphRead  = CreateEventA( &s_attrib, TRUE, FALSE, NULL );
+
+  if( ( !DuplicateHandle( GetCurrentProcess(), *lphStart,
+                          hDestProcess, &hAppStart,
+                          0, FALSE, DUPLICATE_SAME_ACCESS ) ) ||
+      ( !DuplicateHandle( GetCurrentProcess(), *lphDeath,
+                          hDestProcess, &hAppDeath,
+                          0, FALSE, DUPLICATE_SAME_ACCESS ) ) ||
+      ( !DuplicateHandle( GetCurrentProcess(), *lphRead,
+                          hDestProcess, &hAppRead,
+                          0, FALSE, DUPLICATE_SAME_ACCESS ) )
+    )
+  {
+    /* FIXME: Handle leak... */
+    ERR( "Unable to dup handles\n" );
+    return FALSE;
+  }
+
+  if( !DPLAYX_SetLobbyHandles( dwDestProcessId,
+                               hAppStart, hAppDeath, hAppRead ) )
+  {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+
+/********************************************************************
+ *
+ * Starts an application and passes to it all the information to
+ * connect to a session.
+ *
+ */
+static HRESULT WINAPI IDirectPlayLobbyAImpl_RunApplication
+( LPDIRECTPLAYLOBBYA iface,
+  DWORD dwFlags,
+  LPDWORD lpdwAppID,
+  LPDPLCONNECTION lpConn,
+  HANDLE hReceiveEvent )
+{
+  ICOM_THIS(IDirectPlayLobbyAImpl,iface);
+  HRESULT hr;
+  RunApplicationEnumStruct enumData;
+  char temp[200];
+  STARTUPINFOA startupInfo;
+  PROCESS_INFORMATION newProcessInfo;
+  LPSTR appName;
+  DWORD dwSuspendCount;
+  HANDLE hStart, hDeath, hSettingRead;
+
+  TRACE( "(%p)->(0x%08lx,%p,%p,%x)\n",
+         This, dwFlags, lpdwAppID, lpConn, hReceiveEvent );
+
+  if( dwFlags != 0 )
+  {
+    return DPERR_INVALIDPARAMS;
+  }
+
+  if( DPLAYX_AnyLobbiesWaitingForConnSettings() )
+  {
+    FIXME( "Waiting lobby not being handled correctly\n" );
+  }
+
+  EnterCriticalSection( &This->unk->DPL_lock );
+
+  ZeroMemory( &enumData, sizeof( enumData ) );
+  enumData.This    = This;
+  enumData.appGUID = lpConn->lpSessionDesc->guidApplication;
+
+  /* Our callback function will fill up the enumData structure with all the information
+     required to start a new process */
+  IDirectPlayLobby_EnumLocalApplications( iface, RunApplicationA_EnumLocalApplications,
+                                          (LPVOID)(&enumData), 0 );
+
+  /* First the application name */
+  strcpy( temp, enumData.lpszPath );
+  strcat( temp, "\\" );
+  strcat( temp, enumData.lpszFileName );
+  HeapFree( GetProcessHeap(), 0, enumData.lpszPath );
+  HeapFree( GetProcessHeap(), 0, enumData.lpszFileName );
+  appName = HEAP_strdupA( GetProcessHeap(), HEAP_ZERO_MEMORY, temp );
+
+  /* Now the command line */
+  strcat( temp, " " );
+  strcat( temp, enumData.lpszCommandLine );
+  HeapFree( GetProcessHeap(), 0, enumData.lpszCommandLine );
+  enumData.lpszCommandLine = HEAP_strdupA( GetProcessHeap(), HEAP_ZERO_MEMORY, temp );
+
+  ZeroMemory( &startupInfo, sizeof( startupInfo ) );
+  startupInfo.cb = sizeof( startupInfo );
+  /* FIXME: Should any fields be filled in? */
+
+  ZeroMemory( &newProcessInfo, sizeof( newProcessInfo ) );
+
+  if( !CreateProcessA( appName,
+                       enumData.lpszCommandLine,
+                       NULL,
+                       NULL,
+                       FALSE,
+                       CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE | CREATE_SUSPENDED, /* Creation Flags */
+                       NULL,
+                       enumData.lpszCurrentDirectory,
+                       &startupInfo,
+                       &newProcessInfo
+                     )
+    )
+  {
+    ERR( "Failed to create process for app %s\n", appName );
+
+    HeapFree( GetProcessHeap(), 0, appName );
+    HeapFree( GetProcessHeap(), 0, enumData.lpszCommandLine );
+    HeapFree( GetProcessHeap(), 0, enumData.lpszCurrentDirectory );
+
+    return DPERR_CANTCREATEPROCESS;
+  }
+
+  HeapFree( GetProcessHeap(), 0, appName );
+  HeapFree( GetProcessHeap(), 0, enumData.lpszCommandLine );
+  HeapFree( GetProcessHeap(), 0, enumData.lpszCurrentDirectory );
+
+  /* Reserve this global application id! */
+  if( !DPLAYX_CreateLobbyApplication( newProcessInfo.dwProcessId ) )
+  {
+    ERR( "Unable to create global application data for 0x%08lx\n",
+           newProcessInfo.dwProcessId );
+  }
+
+  hr = IDirectPlayLobby_SetConnectionSettings( iface, 0, newProcessInfo.dwProcessId, lpConn );
+
+  if( hr != DP_OK )
+  {
+    ERR( "SetConnectionSettings failure %s\n", DPLAYX_HresultToString( hr ) );
+    return hr;
+  }
+
+  /* Setup the handles for application notification */
+  DPL_CreateAndSetLobbyHandles( newProcessInfo.dwProcessId,
+                                newProcessInfo.hProcess,
+                                &hStart, &hDeath, &hSettingRead );
+
+  /* Setup the message thread ID */
+  This->dpl->dwMsgThread =
+    CreateLobbyMessageReceptionThread( hReceiveEvent, hStart, hDeath, hSettingRead );
+
+  DPLAYX_SetLobbyMsgThreadId( newProcessInfo.dwProcessId, This->dpl->dwMsgThread );
+
+  LeaveCriticalSection( &This->unk->DPL_lock );
+
+  /* Everything seems to have been set correctly, update the dwAppID */
+  *lpdwAppID = newProcessInfo.dwProcessId;
+
+  /* Unsuspend the process - should return the prev suspension count */
+  if( ( dwSuspendCount = ResumeThread( newProcessInfo.hThread ) ) != 1 )
+  {
+    ERR( "ResumeThread failed with 0x%08lx\n", dwSuspendCount );
+  }
+
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlayLobbyWImpl_RunApplication
+( LPDIRECTPLAYLOBBY iface,
+  DWORD dwFlags,
+  LPDWORD lpdwAppID,
+  LPDPLCONNECTION lpConn,
+  HANDLE hReceiveEvent )
+{
+  ICOM_THIS(IDirectPlayLobbyWImpl,iface);
+  FIXME( "(%p)->(0x%08lx,%p,%p,%p):stub\n", This, dwFlags, lpdwAppID, lpConn, (void *)hReceiveEvent );
+  return DPERR_OUTOFMEMORY;
+}
+
+/********************************************************************
+ *
+ * Sends a message between the application and the lobby client.
+ * All messages are queued until received.
+ *
+ */
+static HRESULT WINAPI IDirectPlayLobbyAImpl_SendLobbyMessage
+( LPDIRECTPLAYLOBBYA iface,
+  DWORD dwFlags,
+  DWORD dwAppID,
+  LPVOID lpData,
+  DWORD dwDataSize )
+{
+  FIXME(":stub\n");
+  return DPERR_OUTOFMEMORY;
+}
+
+static HRESULT WINAPI IDirectPlayLobbyWImpl_SendLobbyMessage
+( LPDIRECTPLAYLOBBY iface,
+  DWORD dwFlags,
+  DWORD dwAppID,
+  LPVOID lpData,
+  DWORD dwDataSize )
+{
+  FIXME(":stub\n");
+  return DPERR_OUTOFMEMORY;
+}
+
+/********************************************************************
+ *
+ * Modifies the DPLCONNECTION structure to contain all information
+ * needed to start and connect an application.
+ *
+ */
+static HRESULT WINAPI IDirectPlayLobbyWImpl_SetConnectionSettings
+( LPDIRECTPLAYLOBBY iface,
+  DWORD dwFlags,
+  DWORD dwAppID,
+  LPDPLCONNECTION lpConn )
+{
+  ICOM_THIS(IDirectPlayLobbyWImpl,iface);
+  HRESULT hr;
+
+  TRACE("(%p)->(0x%08lx,0x%08lx,%p)\n", This, dwFlags, dwAppID, lpConn );
+
+  EnterCriticalSection( &This->unk->DPL_lock );
+
+  hr = DPLAYX_SetConnectionSettingsW( dwFlags, dwAppID, lpConn );
+
+  /* FIXME: Don't think that this is supposed to fail, but the docuementation
+            is somewhat sketchy. I'll try creating a lobby application
+            for this... */
+  if( hr == DPERR_NOTLOBBIED )
+  {
+    FIXME( "Unlobbied app setting connections. Is this correct behavior?\n" );
+    if( dwAppID == 0 )
+    {
+       dwAppID = GetCurrentProcessId();
+    }
+    DPLAYX_CreateLobbyApplication( dwAppID );
+    hr = DPLAYX_SetConnectionSettingsW( dwFlags, dwAppID, lpConn );
+  }
+
+  LeaveCriticalSection( &This->unk->DPL_lock );
+
+  return hr;
+}
+
+static HRESULT WINAPI IDirectPlayLobbyAImpl_SetConnectionSettings
+( LPDIRECTPLAYLOBBYA iface,
+  DWORD dwFlags,
+  DWORD dwAppID,
+  LPDPLCONNECTION lpConn )
+{
+  ICOM_THIS(IDirectPlayLobbyAImpl,iface);
+  HRESULT hr;
+
+  TRACE("(%p)->(0x%08lx,0x%08lx,%p)\n", This, dwFlags, dwAppID, lpConn );
+
+  EnterCriticalSection( &This->unk->DPL_lock );
+
+  hr = DPLAYX_SetConnectionSettingsA( dwFlags, dwAppID, lpConn );
+
+  /* FIXME: Don't think that this is supposed to fail, but the docuementation
+            is somewhat sketchy. I'll try creating a lobby application
+            for this... */
+  if( hr == DPERR_NOTLOBBIED )
+  {
+    FIXME( "Unlobbied app setting connections. Is this correct behavior?\n" );
+    dwAppID = GetCurrentProcessId();
+    DPLAYX_CreateLobbyApplication( dwAppID );
+    hr = DPLAYX_SetConnectionSettingsA( dwFlags, dwAppID, lpConn );
+  }
+
+  LeaveCriticalSection( &This->unk->DPL_lock );
+
+  return hr;
+}
+
+/********************************************************************
+ *
+ * Registers an event that will be set when a lobby message is received.
+ *
+ */
+static HRESULT WINAPI IDirectPlayLobbyAImpl_SetLobbyMessageEvent
+( LPDIRECTPLAYLOBBYA iface,
+  DWORD dwFlags,
+  DWORD dwAppID,
+  HANDLE hReceiveEvent )
+{
+  FIXME(":stub\n");
+  return DPERR_OUTOFMEMORY;
+}
+
+static HRESULT WINAPI IDirectPlayLobbyWImpl_SetLobbyMessageEvent
+( LPDIRECTPLAYLOBBY iface,
+  DWORD dwFlags,
+  DWORD dwAppID,
+  HANDLE hReceiveEvent )
+{
+  FIXME(":stub\n");
+  return DPERR_OUTOFMEMORY;
+}
+
+
+/* DPL 2 methods */
+static HRESULT WINAPI IDirectPlayLobby2WImpl_CreateCompoundAddress
+( LPDIRECTPLAYLOBBY2 iface,
+  LPCDPCOMPOUNDADDRESSELEMENT lpElements,
+  DWORD dwElementCount,
+  LPVOID lpAddress,
+  LPDWORD lpdwAddressSize )
+{
+  return DPL_CreateCompoundAddress( lpElements, dwElementCount, lpAddress, lpdwAddressSize, FALSE );
+}
+
+static HRESULT WINAPI IDirectPlayLobby2AImpl_CreateCompoundAddress
+( LPDIRECTPLAYLOBBY2A iface,
+  LPCDPCOMPOUNDADDRESSELEMENT lpElements,
+  DWORD dwElementCount,
+  LPVOID lpAddress,
+  LPDWORD lpdwAddressSize )
+{
+  return DPL_CreateCompoundAddress( lpElements, dwElementCount, lpAddress, lpdwAddressSize, TRUE );
+}
+
+HRESULT DPL_CreateCompoundAddress
+( LPCDPCOMPOUNDADDRESSELEMENT lpElements,
+  DWORD dwElementCount,
+  LPVOID lpAddress,
+  LPDWORD lpdwAddressSize,
+  BOOL bAnsiInterface )
+{
+  DWORD dwSizeRequired = 0;
+  DWORD dwElements;
+  LPCDPCOMPOUNDADDRESSELEMENT lpOrigElements = lpElements;
+
+  TRACE("(%p,0x%08lx,%p,%p)\n", lpElements, dwElementCount, lpAddress, lpdwAddressSize );
+
+  /* Parameter check */
+  if( ( lpElements == NULL ) ||
+      ( dwElementCount == 0 )   /* FIXME: Not sure if this is a failure case */
+    )
+  {
+    return DPERR_INVALIDPARAMS;
+  }
+
+  /* Add the total size chunk */
+  dwSizeRequired += sizeof( DPADDRESS ) + sizeof( DWORD );
+
+  /* Calculate the size of the buffer required */
+  for ( dwElements = dwElementCount; dwElements > 0; --dwElements, ++lpElements )
+  {
+    if ( ( IsEqualGUID( &lpElements->guidDataType, &DPAID_ServiceProvider ) ) ||
+         ( IsEqualGUID( &lpElements->guidDataType, &DPAID_LobbyProvider ) )
+       )
+    {
+      dwSizeRequired += sizeof( DPADDRESS ) + sizeof( GUID );
+    }
+    else if ( ( IsEqualGUID( &lpElements->guidDataType, &DPAID_Phone ) ) ||
+              ( IsEqualGUID( &lpElements->guidDataType, &DPAID_Modem ) ) ||
+              ( IsEqualGUID( &lpElements->guidDataType, &DPAID_INet ) )
+            )
+    {
+      if( !bAnsiInterface )
+      {
+        ERR( "Ansi GUIDs used for unicode interface\n" );
+        return DPERR_INVALIDFLAGS;
+      }
+
+      dwSizeRequired += sizeof( DPADDRESS ) + lpElements->dwDataSize;
+    }
+    else if ( ( IsEqualGUID( &lpElements->guidDataType, &DPAID_PhoneW ) ) ||
+              ( IsEqualGUID( &lpElements->guidDataType, &DPAID_ModemW ) ) ||
+              ( IsEqualGUID( &lpElements->guidDataType, &DPAID_INetW ) )
+            )
+    {
+      if( bAnsiInterface )
+      {
+        ERR( "Unicode GUIDs used for ansi interface\n" );
+        return DPERR_INVALIDFLAGS;
+      }
+
+      FIXME( "Right size for unicode interface?\n" );
+      dwSizeRequired += sizeof( DPADDRESS ) + lpElements->dwDataSize * sizeof( WCHAR );
+    }
+    else if ( IsEqualGUID( &lpElements->guidDataType, &DPAID_INetPort ) )
+    {
+      dwSizeRequired += sizeof( DPADDRESS ) + sizeof( WORD );
+    }
+    else if ( IsEqualGUID( &lpElements->guidDataType, &DPAID_ComPort ) )
+    {
+      FIXME( "Right size for unicode interface?\n" );
+      dwSizeRequired += sizeof( DPADDRESS ) + sizeof( DPCOMPORTADDRESS ); /* FIXME: Right size? */
+    }
+    else
+    {
+      ERR( "Unknown GUID %s\n", debugstr_guid(&lpElements->guidDataType) );
+      return DPERR_INVALIDFLAGS;
+    }
+  }
+
+  /* The user wants to know how big a buffer to allocate for us */
+  if( ( lpAddress == NULL ) ||
+      ( *lpdwAddressSize < dwSizeRequired )
+    )
+  {
+    *lpdwAddressSize = dwSizeRequired;
+    return DPERR_BUFFERTOOSMALL;
+  }
+
+  /* Add the total size chunk */
+  {
+    LPDPADDRESS lpdpAddress = (LPDPADDRESS)lpAddress;
+
+    CopyMemory( &lpdpAddress->guidDataType, &DPAID_TotalSize, sizeof( GUID ) );
+    lpdpAddress->dwDataSize = sizeof( DWORD );
+    lpAddress = (char *) lpAddress + sizeof( DPADDRESS );
+
+    *(LPDWORD)lpAddress = dwSizeRequired;
+    lpAddress = (char *) lpAddress + sizeof( DWORD );
+  }
+
+  /* Calculate the size of the buffer required */
+  for( dwElements = dwElementCount, lpElements = lpOrigElements;
+       dwElements > 0;
+       --dwElements, ++lpElements )
+  {
+    if ( ( IsEqualGUID( &lpElements->guidDataType, &DPAID_ServiceProvider ) ) ||
+         ( IsEqualGUID( &lpElements->guidDataType, &DPAID_LobbyProvider ) )
+       )
+    {
+      LPDPADDRESS lpdpAddress = (LPDPADDRESS)lpAddress;
+
+      CopyMemory( &lpdpAddress->guidDataType, &lpElements->guidDataType,
+                  sizeof( GUID ) );
+      lpdpAddress->dwDataSize = sizeof( GUID );
+      lpAddress = (char *) lpAddress + sizeof( DPADDRESS );
+
+      CopyMemory( lpAddress, lpElements->lpData, sizeof( GUID ) );
+      lpAddress = (char *) lpAddress + sizeof( GUID );
+    }
+    else if ( ( IsEqualGUID( &lpElements->guidDataType, &DPAID_Phone ) ) ||
+              ( IsEqualGUID( &lpElements->guidDataType, &DPAID_Modem ) ) ||
+              ( IsEqualGUID( &lpElements->guidDataType, &DPAID_INet ) )
+            )
+    {
+      LPDPADDRESS lpdpAddress = (LPDPADDRESS)lpAddress;
+
+      CopyMemory( &lpdpAddress->guidDataType, &lpElements->guidDataType,
+                  sizeof( GUID ) );
+      lpdpAddress->dwDataSize = lpElements->dwDataSize;
+      lpAddress = (char *) lpAddress + sizeof( DPADDRESS );
+
+      lstrcpynA( (LPSTR)lpAddress,
+                 (LPCSTR)lpElements->lpData,
+                 lpElements->dwDataSize );
+      lpAddress = (char *) lpAddress + lpElements->dwDataSize;
+    }
+    else if ( ( IsEqualGUID( &lpElements->guidDataType, &DPAID_PhoneW ) ) ||
+              ( IsEqualGUID( &lpElements->guidDataType, &DPAID_ModemW ) ) ||
+              ( IsEqualGUID( &lpElements->guidDataType, &DPAID_INetW ) )
+            )
+    {
+      LPDPADDRESS lpdpAddress = (LPDPADDRESS)lpAddress;
+
+      CopyMemory( &lpdpAddress->guidDataType, &lpElements->guidDataType,
+                  sizeof( GUID ) );
+      lpdpAddress->dwDataSize = lpElements->dwDataSize;
+      lpAddress = (char *) lpAddress + sizeof( DPADDRESS );
+
+      lstrcpynW( (LPWSTR)lpAddress,
+                 (LPCWSTR)lpElements->lpData,
+                 lpElements->dwDataSize );
+      lpAddress = (char *) lpAddress + lpElements->dwDataSize * sizeof( WCHAR );
+    }
+    else if ( IsEqualGUID( &lpElements->guidDataType, &DPAID_INetPort ) )
+    {
+      LPDPADDRESS lpdpAddress = (LPDPADDRESS)lpAddress;
+
+      CopyMemory( &lpdpAddress->guidDataType, &lpElements->guidDataType,
+                  sizeof( GUID ) );
+      lpdpAddress->dwDataSize = lpElements->dwDataSize;
+      lpAddress = (char *) lpAddress + sizeof( DPADDRESS );
+
+      *((LPWORD)lpAddress) = *((LPWORD)lpElements->lpData);
+      lpAddress = (char *) lpAddress + sizeof( WORD );
+    }
+    else if ( IsEqualGUID( &lpElements->guidDataType, &DPAID_ComPort ) )
+    {
+      LPDPADDRESS lpdpAddress = (LPDPADDRESS)lpAddress;
+
+      CopyMemory( &lpdpAddress->guidDataType, &lpElements->guidDataType,
+                  sizeof( GUID ) );
+      lpdpAddress->dwDataSize = lpElements->dwDataSize;
+      lpAddress = (char *) lpAddress + sizeof( DPADDRESS );
+
+      CopyMemory( lpAddress, lpElements->lpData, sizeof( DPADDRESS ) );
+      lpAddress = (char *) lpAddress + sizeof( DPADDRESS );
+    }
+  }
+
+  return DP_OK;
+}
+
+/* DPL 3 methods */
+
+static HRESULT WINAPI IDirectPlayLobby3WImpl_ConnectEx
+( LPDIRECTPLAYLOBBY3 iface, DWORD dwFlags, REFIID riid,
+  LPVOID* lplpDP, IUnknown* pUnk )
+{
+  ICOM_THIS( IDirectPlayLobbyAImpl, iface );
+  return DPL_ConnectEx( This, dwFlags, riid, lplpDP, pUnk );
+}
+
+static HRESULT WINAPI IDirectPlayLobby3AImpl_ConnectEx
+( LPDIRECTPLAYLOBBY3A iface, DWORD dwFlags, REFIID riid,
+  LPVOID* lplpDP, IUnknown* pUnk )
+{
+  ICOM_THIS( IDirectPlayLobbyAImpl, iface );
+  return DPL_ConnectEx( This, dwFlags, riid, lplpDP, pUnk );
+}
+
+static HRESULT WINAPI IDirectPlayLobby3WImpl_RegisterApplication
+( LPDIRECTPLAYLOBBY3 iface, DWORD dwFlags, LPDPAPPLICATIONDESC lpAppDesc )
+{
+  FIXME(":stub\n");
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlayLobby3AImpl_RegisterApplication
+( LPDIRECTPLAYLOBBY3A iface, DWORD dwFlags, LPDPAPPLICATIONDESC lpAppDesc )
+{
+  FIXME(":stub\n");
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlayLobby3WImpl_UnregisterApplication
+( LPDIRECTPLAYLOBBY3 iface, DWORD dwFlags, REFGUID lpAppDesc )
+{
+  FIXME(":stub\n");
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlayLobby3AImpl_UnregisterApplication
+( LPDIRECTPLAYLOBBY3A iface, DWORD dwFlags, REFGUID lpAppDesc )
+{
+  FIXME(":stub\n");
+  return DP_OK;
+}
+
+static HRESULT WINAPI IDirectPlayLobby3WImpl_WaitForConnectionSettings
+( LPDIRECTPLAYLOBBY3 iface, DWORD dwFlags )
+{
+  HRESULT hr         = DP_OK;
+  BOOL    bStartWait = (dwFlags & DPLWAIT_CANCEL) ? FALSE : TRUE;
+
+  TRACE( "(%p)->(0x%08lx)\n", iface, dwFlags );
+
+  if( DPLAYX_WaitForConnectionSettings( bStartWait ) )
+  {
+    /* FIXME: What is the correct error return code? */
+    hr = DPERR_NOTLOBBIED;
+  }
+
+  return hr;
+}
+
+static HRESULT WINAPI IDirectPlayLobby3AImpl_WaitForConnectionSettings
+( LPDIRECTPLAYLOBBY3A iface, DWORD dwFlags )
+{
+  HRESULT hr         = DP_OK;
+  BOOL    bStartWait = (dwFlags & DPLWAIT_CANCEL) ? FALSE : TRUE;
+
+  TRACE( "(%p)->(0x%08lx)\n", iface, dwFlags );
+
+  if( DPLAYX_WaitForConnectionSettings( bStartWait ) )
+  {
+    /* FIXME: What is the correct error return code? */
+    hr = DPERR_NOTLOBBIED;
+  }
+
+  return hr;
+}
+
+
+/* Virtual Table definitions for DPL{1,2,3}{A,W} */
+
+/* Direct Play Lobby 1 (ascii) Virtual Table for methods */
+/* All lobby 1 methods are exactly the same except QueryInterface */
+struct ICOM_VTABLE(IDirectPlayLobby) directPlayLobbyAVT =
+{
+  ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+
+  (HRESULT(*CALLBACK)(IDirectPlayLobby*,const IID*const,LPVOID*))DPL_QueryInterface,
+  (ULONG(*CALLBACK)(IDirectPlayLobby*))DPL_AddRef,
+  (ULONG(*CALLBACK)(IDirectPlayLobby*))DPL_Release,
+
+  IDirectPlayLobbyAImpl_Connect,
+  IDirectPlayLobbyAImpl_CreateAddress,
+  IDirectPlayLobbyAImpl_EnumAddress,
+  IDirectPlayLobbyAImpl_EnumAddressTypes,
+  IDirectPlayLobbyAImpl_EnumLocalApplications,
+  IDirectPlayLobbyAImpl_GetConnectionSettings,
+  IDirectPlayLobbyAImpl_ReceiveLobbyMessage,
+  IDirectPlayLobbyAImpl_RunApplication,
+  IDirectPlayLobbyAImpl_SendLobbyMessage,
+  IDirectPlayLobbyAImpl_SetConnectionSettings,
+  IDirectPlayLobbyAImpl_SetLobbyMessageEvent
+};
+
+/* Direct Play Lobby 1 (unicode) Virtual Table for methods */
+struct ICOM_VTABLE(IDirectPlayLobby) directPlayLobbyWVT =
+{
+  ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+
+  (HRESULT(*CALLBACK)(IDirectPlayLobby*,const IID*const,LPVOID*))DPL_QueryInterface,
+  (ULONG(*CALLBACK)(IDirectPlayLobby*))DPL_AddRef,
+  (ULONG(*CALLBACK)(IDirectPlayLobby*))DPL_Release,
+
+  IDirectPlayLobbyWImpl_Connect,
+  IDirectPlayLobbyWImpl_CreateAddress,
+  IDirectPlayLobbyWImpl_EnumAddress,
+  IDirectPlayLobbyWImpl_EnumAddressTypes,
+  IDirectPlayLobbyWImpl_EnumLocalApplications,
+  IDirectPlayLobbyWImpl_GetConnectionSettings,
+  IDirectPlayLobbyWImpl_ReceiveLobbyMessage,
+  IDirectPlayLobbyWImpl_RunApplication,
+  IDirectPlayLobbyWImpl_SendLobbyMessage,
+  IDirectPlayLobbyWImpl_SetConnectionSettings,
+  IDirectPlayLobbyWImpl_SetLobbyMessageEvent
+};
+
+# define XCAST(fun)     (void*)
+
+/* Direct Play Lobby 2 (ascii) Virtual Table for methods */
+struct ICOM_VTABLE(IDirectPlayLobby2) directPlayLobby2AVT =
+{
+  ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,const IID*const,LPVOID*))DPL_QueryInterface,
+  (ULONG(*CALLBACK)(IDirectPlayLobby2*))DPL_AddRef,
+  (ULONG(*CALLBACK)(IDirectPlayLobby2*))DPL_Release,
+
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,DWORD,LPDIRECTPLAY2*,IUnknown*))IDirectPlayLobbyAImpl_Connect,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,const GUID*const,const GUID*const,LPCVOID,DWORD,LPVOID,LPDWORD))IDirectPlayLobbyAImpl_CreateAddress,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,LPDPENUMADDRESSCALLBACK,LPCVOID,DWORD,LPVOID))IDirectPlayLobbyAImpl_EnumAddress,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,LPDPLENUMADDRESSTYPESCALLBACK,const GUID*const,LPVOID,DWORD))IDirectPlayLobbyAImpl_EnumAddressTypes,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,LPDPLENUMLOCALAPPLICATIONSCALLBACK,LPVOID,DWORD))IDirectPlayLobbyAImpl_EnumLocalApplications,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,DWORD,LPVOID,LPDWORD))IDirectPlayLobbyAImpl_GetConnectionSettings,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,DWORD,DWORD,LPDWORD,LPVOID,LPDWORD))IDirectPlayLobbyAImpl_ReceiveLobbyMessage,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,DWORD,LPDWORD,LPDPLCONNECTION,HANDLE))IDirectPlayLobbyAImpl_RunApplication,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,DWORD,DWORD,LPVOID,DWORD))IDirectPlayLobbyAImpl_SendLobbyMessage,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,DWORD,DWORD,LPDPLCONNECTION))IDirectPlayLobbyAImpl_SetConnectionSettings,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,DWORD,DWORD,HANDLE))IDirectPlayLobbyAImpl_SetLobbyMessageEvent,
+
+  IDirectPlayLobby2AImpl_CreateCompoundAddress
+};
+
+/* Direct Play Lobby 2 (unicode) Virtual Table for methods */
+struct ICOM_VTABLE(IDirectPlayLobby2) directPlayLobby2WVT =
+{
+  ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,const IID*const,LPVOID*))DPL_QueryInterface,
+  (ULONG(*CALLBACK)(IDirectPlayLobby2*))DPL_AddRef,
+  (ULONG(*CALLBACK)(IDirectPlayLobby2*))DPL_Release,
+
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,DWORD,LPDIRECTPLAY2*,IUnknown*))IDirectPlayLobbyWImpl_Connect,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,const GUID*const,const GUID*const,LPCVOID,DWORD,LPVOID,LPDWORD))IDirectPlayLobbyWImpl_CreateAddress,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,LPDPENUMADDRESSCALLBACK,LPCVOID,DWORD,LPVOID))IDirectPlayLobbyWImpl_EnumAddress,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,LPDPLENUMADDRESSTYPESCALLBACK,const GUID*const,LPVOID,DWORD))IDirectPlayLobbyWImpl_EnumAddressTypes,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,LPDPLENUMLOCALAPPLICATIONSCALLBACK,LPVOID,DWORD))IDirectPlayLobbyWImpl_EnumLocalApplications,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,DWORD,LPVOID,LPDWORD))IDirectPlayLobbyWImpl_GetConnectionSettings,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,DWORD,DWORD,LPDWORD,LPVOID,LPDWORD))IDirectPlayLobbyWImpl_ReceiveLobbyMessage,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,DWORD,LPDWORD,LPDPLCONNECTION,HANDLE))IDirectPlayLobbyWImpl_RunApplication,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,DWORD,DWORD,LPVOID,DWORD))IDirectPlayLobbyWImpl_SendLobbyMessage,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,DWORD,DWORD,LPDPLCONNECTION))IDirectPlayLobbyWImpl_SetConnectionSettings,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby2*,DWORD,DWORD,HANDLE))IDirectPlayLobbyWImpl_SetLobbyMessageEvent,
+
+  IDirectPlayLobby2WImpl_CreateCompoundAddress
+};
+
+struct ICOM_VTABLE(IDirectPlayLobby3) directPlayLobby3AVT =
+{
+  ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,const IID*const,LPVOID*))DPL_QueryInterface,
+  (ULONG(*CALLBACK)(IDirectPlayLobby3*))DPL_AddRef,
+  (ULONG(*CALLBACK)(IDirectPlayLobby3*))DPL_Release,
+
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,DWORD,LPDIRECTPLAY2*,IUnknown*))IDirectPlayLobbyAImpl_Connect,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,const GUID*const,const GUID*const,LPCVOID,DWORD,LPVOID,LPDWORD))IDirectPlayLobbyAImpl_CreateAddress,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,LPDPENUMADDRESSCALLBACK,LPCVOID,DWORD,LPVOID))IDirectPlayLobbyAImpl_EnumAddress,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,LPDPLENUMADDRESSTYPESCALLBACK,const GUID*const,LPVOID,DWORD))IDirectPlayLobbyAImpl_EnumAddressTypes,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,LPDPLENUMLOCALAPPLICATIONSCALLBACK,LPVOID,DWORD))IDirectPlayLobbyAImpl_EnumLocalApplications,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,DWORD,LPVOID,LPDWORD))IDirectPlayLobbyAImpl_GetConnectionSettings,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,DWORD,DWORD,LPDWORD,LPVOID,LPDWORD))IDirectPlayLobbyAImpl_ReceiveLobbyMessage,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,DWORD,LPDWORD,LPDPLCONNECTION,HANDLE))IDirectPlayLobbyAImpl_RunApplication,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,DWORD,DWORD,LPVOID,DWORD))IDirectPlayLobbyAImpl_SendLobbyMessage,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,DWORD,DWORD,LPDPLCONNECTION))IDirectPlayLobbyAImpl_SetConnectionSettings,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,DWORD,DWORD,HANDLE))IDirectPlayLobbyAImpl_SetLobbyMessageEvent,
+
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,LPCDPCOMPOUNDADDRESSELEMENT,DWORD,LPVOID,LPDWORD))IDirectPlayLobby2AImpl_CreateCompoundAddress,
+
+  IDirectPlayLobby3AImpl_ConnectEx,
+  IDirectPlayLobby3AImpl_RegisterApplication,
+  IDirectPlayLobby3AImpl_UnregisterApplication,
+  IDirectPlayLobby3AImpl_WaitForConnectionSettings
+};
+
+/* Direct Play Lobby 3 (unicode) Virtual Table for methods */
+
+struct ICOM_VTABLE(IDirectPlayLobby3) directPlayLobby3WVT =
+{
+  ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,const IID*const,LPVOID*))DPL_QueryInterface,
+  (ULONG(*CALLBACK)(IDirectPlayLobby3*))DPL_AddRef,
+  (ULONG(*CALLBACK)(IDirectPlayLobby3*))DPL_Release,
+
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,DWORD,LPDIRECTPLAY2*,IUnknown*))IDirectPlayLobbyWImpl_Connect,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,const GUID*const,const GUID*const,LPCVOID,DWORD,LPVOID,LPDWORD))IDirectPlayLobbyWImpl_CreateAddress,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,LPDPENUMADDRESSCALLBACK,LPCVOID,DWORD,LPVOID))IDirectPlayLobbyWImpl_EnumAddress,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,LPDPLENUMADDRESSTYPESCALLBACK,const GUID*const,LPVOID,DWORD))IDirectPlayLobbyWImpl_EnumAddressTypes,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,LPDPLENUMLOCALAPPLICATIONSCALLBACK,LPVOID,DWORD))IDirectPlayLobbyWImpl_EnumLocalApplications,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,DWORD,LPVOID,LPDWORD))IDirectPlayLobbyWImpl_GetConnectionSettings,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,DWORD,DWORD,LPDWORD,LPVOID,LPDWORD))IDirectPlayLobbyWImpl_ReceiveLobbyMessage,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,DWORD,LPDWORD,LPDPLCONNECTION,HANDLE))IDirectPlayLobbyWImpl_RunApplication,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,DWORD,DWORD,LPVOID,DWORD))IDirectPlayLobbyWImpl_SendLobbyMessage,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,DWORD,DWORD,LPDPLCONNECTION))IDirectPlayLobbyWImpl_SetConnectionSettings,
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,DWORD,DWORD,HANDLE))IDirectPlayLobbyWImpl_SetLobbyMessageEvent,
+
+  (HRESULT(*CALLBACK)(IDirectPlayLobby3*,LPCDPCOMPOUNDADDRESSELEMENT,DWORD,LPVOID,LPDWORD))IDirectPlayLobby2WImpl_CreateCompoundAddress,
+
+  IDirectPlayLobby3WImpl_ConnectEx,
+  IDirectPlayLobby3WImpl_RegisterApplication,
+  IDirectPlayLobby3WImpl_UnregisterApplication,
+  IDirectPlayLobby3WImpl_WaitForConnectionSettings
+};
+
+
+/*********************************************************
+ *
+ * Direct Play Lobby Interface Implementation
+ *
+ *********************************************************/
+
+/***************************************************************************
+ *  DirectPlayLobbyCreateA   (DPLAYX.4)
+ *
+ */
+HRESULT WINAPI DirectPlayLobbyCreateA( LPGUID lpGUIDDSP,
+                                       LPDIRECTPLAYLOBBYA *lplpDPL,
+                                       IUnknown *lpUnk,
+                                       LPVOID lpData,
+                                       DWORD dwDataSize )
+{
+  TRACE("lpGUIDDSP=%p lplpDPL=%p lpUnk=%p lpData=%p dwDataSize=%08lx\n",
+        lpGUIDDSP,lplpDPL,lpUnk,lpData,dwDataSize);
+
+  /* Parameter Check: lpGUIDSP, lpUnk & lpData must be NULL. dwDataSize must
+   * equal 0. These fields are mostly for future expansion.
+   */
+  if ( lpGUIDDSP || lpData || dwDataSize )
+  {
+     *lplpDPL = NULL;
+     return DPERR_INVALIDPARAMS;
+  }
+
+  if( lpUnk )
+  {
+     *lplpDPL = NULL;
+     ERR("Bad parameters!\n" );
+     return CLASS_E_NOAGGREGATION;
+  }
+
+  return DPL_CreateInterface( &IID_IDirectPlayLobbyA, (void**)lplpDPL );
+}
+
+/***************************************************************************
+ *  DirectPlayLobbyCreateW   (DPLAYX.5)
+ *
+ */
+HRESULT WINAPI DirectPlayLobbyCreateW( LPGUID lpGUIDDSP,
+                                       LPDIRECTPLAYLOBBY *lplpDPL,
+                                       IUnknown *lpUnk,
+                                       LPVOID lpData,
+                                       DWORD dwDataSize )
+{
+  TRACE("lpGUIDDSP=%p lplpDPL=%p lpUnk=%p lpData=%p dwDataSize=%08lx\n",
+        lpGUIDDSP,lplpDPL,lpUnk,lpData,dwDataSize);
+
+  /* Parameter Check: lpGUIDSP, lpUnk & lpData must be NULL. dwDataSize must
+   * equal 0. These fields are mostly for future expansion.
+   */
+  if ( lpGUIDDSP || lpData || dwDataSize )
+  {
+     *lplpDPL = NULL;
+     ERR("Bad parameters!\n" );
+     return DPERR_INVALIDPARAMS;
+  }
+
+  if( lpUnk )
+  {
+     *lplpDPL = NULL;
+     ERR("Bad parameters!\n" );
+     return CLASS_E_NOAGGREGATION;
+  }
+
+  return DPL_CreateInterface( &IID_IDirectPlayLobby, (void**)lplpDPL );
+}
Index: trunk/src/DPlayX/initterm.cpp
===================================================================
--- trunk/src/DPlayX/initterm.cpp	(revision 4314)
+++ trunk/src/DPlayX/initterm.cpp	(revision 4314)
@@ -0,0 +1,95 @@
+/* $Id: initterm.cpp,v 1.4 2000-09-24 22:26:09 hugh Exp $ */
+
+/*
+ * DLL entry point
+ *
+ * Copyright 1998 Sander van Leeuwen
+ * Copyright 1998 Peter Fitzsimmons
+ *
+ *
+ * Project Odin Software License can be found in LICENSE.TXT
+ *
+ */
+
+/*-------------------------------------------------------------*/
+/* INITERM.C -- Source for a custom dynamic link library       */
+/*              initialization and termination (_DLL_InitTerm) */
+/*              function.                                      */
+/*                                                             */
+/* When called to perform initialization, this sample function */
+/* gets storage for an array of integers, and initializes its  */
+/* elements with random integers.  At termination time, it     */
+/* frees the array.  Substitute your own special processing.   */
+/*-------------------------------------------------------------*/
+
+
+/* Include files */
+#define  INCL_DOSMODULEMGR
+#define  INCL_DOSPROCESS
+#include <os2wrap.h>    //Odin32 OS/2 api wrappers
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <odin.h>
+#include <win32type.h>
+#include <winconst.h>
+#include <odinlx.h>
+#include <misc.h>       /*PLF Wed  98-03-18 23:18:15*/
+
+extern "C" {
+void CDECL _ctordtorInit( void );
+
+ //Win32 resource table (produced by wrc)
+ extern DWORD _Resource_PEResTab;
+ extern BOOL WINAPI DPLAYX_LibMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved );
+}
+
+static HMODULE dllHandle = 0;
+
+/****************************************************************************/
+/* _DLL_InitTerm is the function that gets called by the operating system   */
+/* loader when it loads and frees this DLL for each process that accesses   */
+/* this DLL.  However, it only gets called the first time the DLL is loaded */
+/* and the last time it is freed for a particular process.  The system      */
+/* linkage convention MUST be used because the operating system loader is   */
+/* calling this function.                                                   */
+/****************************************************************************/
+unsigned long SYSTEM _DLL_InitTerm(unsigned long hModule, unsigned long
+                                   ulFlag)
+{
+   size_t i;
+   APIRET rc;
+
+   /*-------------------------------------------------------------------------*/
+   /* If ulFlag is zero then the DLL is being loaded so initialization should */
+   /* be performed.  If ulFlag is 1 then the DLL is being freed so            */
+   /* termination should be performed.                                        */
+   /*-------------------------------------------------------------------------*/
+
+   switch (ulFlag) {
+      case 0:
+         _ctordtorInit();
+
+         CheckVersionFromHMOD(PE2LX_VERSION, hModule); /*PLF Wed  98-03-18 05:28:48*/
+
+   dllHandle = RegisterLxDll(hModule, DPLAYX_LibMain, (PVOID)&_Resource_PEResTab);
+         if(dllHandle == 0)
+    return 0UL;
+
+         break;
+      case 1:
+         if(dllHandle) {
+    UnregisterLxDll(dllHandle);
+         }
+         break;
+      default:
+         return 0UL;
+   }
+
+   /***********************************************************/
+   /* A non-zero value must be returned to indicate success.  */
+   /***********************************************************/
+   return 1UL;
+}
+//******************************************************************************
+//******************************************************************************
Index: trunk/src/DPlayX/name_server.cpp
===================================================================
--- trunk/src/DPlayX/name_server.cpp	(revision 4314)
+++ trunk/src/DPlayX/name_server.cpp	(revision 4314)
@@ -0,0 +1,331 @@
+/* DPLAYX.DLL name server implementation
+ *
+ * Copyright 2000 - Peter Hunnisett
+ *
+ * <presently under construction - contact hunnise@nortelnetworks.com>
+ *
+ */
+
+/* NOTE: Methods with the NS_ prefix are name server methods */
+
+#include <string.h>
+
+#include <odin.h>
+#define ICOM_CINTERFACE 1
+#define CINTERFACE
+
+#include "winbase.h"
+#include "winuser.h"
+#include "debugtools.h"
+#include "heap.h"
+#include "heapstring.h"
+
+#include "dplayx_global.h"
+#include "name_server.h"
+#include "dplaysp.h"
+#include "dplayx_messages.h"
+#include "dplayx_queue.h"
+
+/* FIXME: Need to create a crit section, store and use it */
+
+DEFAULT_DEBUG_CHANNEL(dplay);
+
+#define debugstr_guid(a) a
+
+/* NS specific structures */
+struct NSCacheData
+{
+  DPQ_ENTRY(NSCacheData) next;
+
+  DWORD dwTime; /* Time at which data was last known valid */
+  LPDPSESSIONDESC2 data;
+
+  LPVOID lpNSAddrHdr;
+
+};
+typedef struct NSCacheData NSCacheData, *lpNSCacheData;
+
+struct NSCache
+{
+  lpNSCacheData present; /* keep track of what is to be looked at when walking */
+
+  DPQ_HEAD(NSCacheData) first;
+};
+typedef struct NSCache NSCache, *lpNSCache;
+
+/* Name Server functions
+ * ---------------------
+ */
+void NS_SetLocalComputerAsNameServer( LPCDPSESSIONDESC2 lpsd )
+{
+#if 0
+  DPLAYX_SetLocalSession( lpsd );
+#endif
+}
+
+/* Store the given NS remote address for future reference */
+void NS_SetRemoteComputerAsNameServer( LPVOID                    lpNSAddrHdr,
+                                       DWORD                     dwHdrSize,
+                                       LPDPMSG_ENUMSESSIONSREPLY lpMsg,
+                                       LPVOID                    lpNSInfo )
+{
+  lpNSCache     lpCache = (lpNSCache)lpNSInfo;
+  lpNSCacheData lpCacheNode;
+
+  TRACE( "%p, %p, %p\n", lpNSAddrHdr, lpMsg, lpNSInfo );
+
+  /* FIXME: Should check to see if the reply is for an existing session. If
+   *        so we just update the contents and update the timestamp.
+   */
+  lpCacheNode = (lpNSCacheData)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                          sizeof( *lpCacheNode ) );
+
+  if( lpCacheNode == NULL )
+  {
+    ERR( "no memory for NS node\n" );
+    return;
+  }
+
+  lpCacheNode->lpNSAddrHdr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                        dwHdrSize );
+  CopyMemory( lpCacheNode->lpNSAddrHdr, lpNSAddrHdr, dwHdrSize );
+
+
+  lpCacheNode->data = (LPDPSESSIONDESC2)HeapAlloc( GetProcessHeap(),
+                                                   HEAP_ZERO_MEMORY,
+                                                   sizeof( *lpCacheNode->data ) );
+
+  if( lpCacheNode->data == NULL )
+  {
+    ERR( "no memory for SESSIONDESC2\n" );
+    return;
+  }
+
+  CopyMemory( lpCacheNode->data, &lpMsg->sd, sizeof( *lpCacheNode->data ) );
+  lpCacheNode->data->sess.lpszSessionNameA = HEAP_strdupWtoA( GetProcessHeap(),
+                                                              HEAP_ZERO_MEMORY,
+                                                              (LPWSTR)(lpMsg+1) );
+
+  lpCacheNode->dwTime = GetTickCount();
+
+  DPQ_INSERT(lpCache->first, lpCacheNode, next );
+
+  lpCache->present = lpCacheNode;
+
+  /* Use this message as an oportunity to weed out any old sessions so
+   * that we don't enum them again
+   */
+  NS_PruneSessionCache( lpNSInfo );
+}
+
+LPVOID NS_GetNSAddr( LPVOID lpNSInfo )
+{
+  lpNSCache lpCache = (lpNSCache)lpNSInfo;
+
+  FIXME( ":quick stub\n" );
+
+  /* Ok. Cheat and don't search for the correct stuff just take the first.
+   * FIXME: In the future how are we to know what is _THE_ enum we used?
+   */
+
+  return lpCache->first.lpQHFirst->lpNSAddrHdr;
+}
+
+/* This function is responsible for sending a request for all other known
+   nameservers to send us what sessions they have registered locally
+ */
+HRESULT NS_SendSessionRequestBroadcast( LPCGUID lpcGuid,
+                                        DWORD dwFlags,
+                                        LPSPINITDATA lpSpData )
+
+{
+  DPSP_ENUMSESSIONSDATA data;
+  LPDPMSG_ENUMSESSIONSREQUEST lpMsg;
+
+  TRACE( "enumerating for guid %s\n", debugstr_guid( lpcGuid ) );
+
+  /* Get the SP to deal with sending the EnumSessions request */
+  FIXME( ": not all data fields are correct\n" );
+
+  data.dwMessageSize = lpSpData->dwSPHeaderSize + sizeof( *lpMsg ); /*FIXME!*/
+  data.lpMessage = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                              data.dwMessageSize );
+  data.lpISP = lpSpData->lpISP;
+  data.bReturnStatus = (dwFlags & DPENUMSESSIONS_RETURNSTATUS) ? TRUE : FALSE;
+
+
+  lpMsg = (LPDPMSG_ENUMSESSIONSREQUEST)(((BYTE*)data.lpMessage)+lpSpData->dwSPHeaderSize);
+
+  /* Setup EnumSession reqest message */
+  lpMsg->envelope.dwMagic    = DPMSGMAGIC_DPLAYMSG;
+  lpMsg->envelope.wCommandId = DPMSGCMD_ENUMSESSIONSREQUEST;
+  lpMsg->envelope.wVersion   = DPMSGVER_DP6;
+
+  lpMsg->dwPasswordSize = 0; /* FIXME: If enumerating passwords..? */
+  lpMsg->dwFlags        = dwFlags;
+
+  CopyMemory( &lpMsg->guidApplication, lpcGuid, sizeof( *lpcGuid ) );
+
+  return (lpSpData->lpCB->EnumSessions)( &data );
+}
+
+DPQ_DECL_DELETECB( cbDeleteNSNodeFromHeap, lpNSCacheData );
+DPQ_DECL_DELETECB( cbDeleteNSNodeFromHeap, lpNSCacheData )
+{
+  /* FIXME: Memory leak on data (contained ptrs) */
+  HeapFree( GetProcessHeap(), 0, elem->data );
+  HeapFree( GetProcessHeap(), 0, elem->lpNSAddrHdr );
+  HeapFree( GetProcessHeap(), 0, elem );
+}
+
+
+
+/* Render all data in a session cache invalid */
+void NS_InvalidateSessionCache( LPVOID lpNSInfo )
+{
+  lpNSCache lpCache = (lpNSCache)lpNSInfo;
+
+  if( lpCache == NULL )
+  {
+    ERR( ": invalidate non existant cache\n" );
+    return;
+  }
+
+  DPQ_DELETEQ( lpCache->first, next, lpNSCacheData, cbDeleteNSNodeFromHeap );
+
+  /* NULL out the walking pointer */
+  lpCache->present = NULL;
+}
+
+/* Create and initialize a session cache */
+BOOL NS_InitializeSessionCache( LPVOID* lplpNSInfo )
+{
+  lpNSCache lpCache = (lpNSCache)HeapAlloc( GetProcessHeap(),
+                                            HEAP_ZERO_MEMORY,
+                                            sizeof( *lpCache ) );
+
+  *lplpNSInfo = lpCache;
+
+  if( lpCache == NULL )
+  {
+    return FALSE;
+  }
+
+  DPQ_INIT(lpCache->first);
+  lpCache->present = NULL;
+
+  return TRUE;
+}
+
+/* Delete a session cache */
+void NS_DeleteSessionCache( LPVOID lpNSInfo )
+{
+  NS_InvalidateSessionCache( (lpNSCache)lpNSInfo );
+}
+
+/* Reinitialize the present pointer for this cache */
+void NS_ResetSessionEnumeration( LPVOID lpNSInfo )
+{
+
+  ((lpNSCache)lpNSInfo)->present = ((lpNSCache)lpNSInfo)->first.lpQHFirst;
+}
+
+LPDPSESSIONDESC2 NS_WalkSessions( LPVOID lpNSInfo )
+{
+  LPDPSESSIONDESC2 lpSessionDesc;
+  lpNSCache lpCache = (lpNSCache)lpNSInfo;
+
+  /* FIXME: The pointers could disappear when walking if a prune happens */
+
+  /* Test for end of the list */
+  if( lpCache->present == NULL )
+  {
+    return NULL;
+  }
+
+  lpSessionDesc = lpCache->present->data;
+
+  /* Advance tracking pointer */
+  lpCache->present = lpCache->present->next.lpQNext;
+
+  return lpSessionDesc;
+}
+
+/* This method should check to see if there are any sessions which are
+ * older than the criteria. If so, just delete that information.
+ */
+void NS_PruneSessionCache( LPVOID lpNSInfo )
+{
+  lpNSCache     lpCache = (lpNSCache)lpNSInfo;
+  lpNSCacheData lpCacheEntry;
+
+  DWORD dwPresentTime = GetTickCount();
+#if defined( HACK_TIMEGETTIME )
+  DWORD dwPruneTime   = dwPresentTime - 2; /* One iteration with safety */
+#else
+  DWORD dwPruneTime   = dwPresentTime - 10000 /* 10 secs? */;
+#endif
+
+  FIXME( ": semi stub\n" );
+
+  /* FIXME: This doesn't handle time roll over correctly */
+  /* FIXME: Session memory leak on delete */
+  do
+  {
+    DPQ_FIND_ENTRY( lpCache->first, next, dwTime, <=, dwPruneTime, lpCacheEntry );
+  }
+  while( lpCacheEntry != NULL );
+
+}
+
+
+
+/* Message stuff */
+void NS_ReplyToEnumSessionsRequest( LPVOID lpMsg,
+                                    LPDPSP_REPLYDATA lpReplyData,
+                                    IDirectPlay2Impl* lpDP )
+{
+  LPDPMSG_ENUMSESSIONSREPLY rmsg;
+  DWORD dwVariableSize;
+  DWORD dwVariableLen;
+  LPWSTR string;
+  /* LPDPMSG_ENUMSESSIONSREQUEST msg = (LPDPMSG_ENUMSESSIONSREQUEST)lpMsg; */
+  BOOL bAnsi = TRUE; /* FIXME: This needs to be in the DPLAY interface */
+
+  FIXME( ": few fixed + need to check request for response\n" );
+
+  dwVariableLen = bAnsi ? lstrlenA( lpDP->dp2->lpSessionDesc->sess.lpszSessionNameA ) + 1
+                         : lstrlenW( lpDP->dp2->lpSessionDesc->sess.lpszSessionName ) + 1;
+
+  dwVariableSize = dwVariableLen * sizeof( WCHAR );
+
+  lpReplyData->dwMessageSize = lpDP->dp2->spData.dwSPHeaderSize +
+                                 sizeof( *rmsg ) + dwVariableSize;
+  lpReplyData->lpMessage     = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                          lpReplyData->dwMessageSize );
+
+  rmsg = (LPDPMSG_ENUMSESSIONSREPLY)( (BYTE*)lpReplyData->lpMessage +
+                                             lpDP->dp2->spData.dwSPHeaderSize);
+
+  rmsg->envelope.dwMagic    = DPMSGMAGIC_DPLAYMSG;
+  rmsg->envelope.wCommandId = DPMSGCMD_ENUMSESSIONSREPLY;
+  rmsg->envelope.wVersion   = DPMSGVER_DP6;
+
+  CopyMemory( &rmsg->sd, lpDP->dp2->lpSessionDesc,
+              sizeof( lpDP->dp2->lpSessionDesc->dwSize ) );
+  rmsg->dwUnknown = 0x0000005c;
+  if( bAnsi )
+  {
+    string = HEAP_strdupAtoW( GetProcessHeap(), 0,
+                              lpDP->dp2->lpSessionDesc->sess.lpszSessionNameA );
+    /* FIXME: Memory leak */
+  }
+  else
+  {
+    string = lpDP->dp2->lpSessionDesc->sess.lpszSessionName;
+  }
+
+  lstrcpyW( (LPWSTR)(rmsg+1), string );
+
+}
+
Index: trunk/src/DPlayX/name_server.h
===================================================================
--- trunk/src/DPlayX/name_server.h	(revision 4314)
+++ trunk/src/DPlayX/name_server.h	(revision 4314)
@@ -0,0 +1,35 @@
+
+#ifndef __WINE_DPLAYX_NAMESERVER
+#define __WINE_DPLAYX_NAMESERVER
+
+#include "dplay.h"
+#include "dplaysp.h"
+#include "dplayx_messages.h"
+#include "dplay_global.h"
+
+void NS_SetLocalComputerAsNameServer( LPCDPSESSIONDESC2 lpsd );
+void NS_SetRemoteComputerAsNameServer( LPVOID lpNSAddrHdr,
+                                       DWORD dwHdrSize,
+                                       LPDPMSG_ENUMSESSIONSREPLY lpMsg,
+                                       LPVOID lpNSInfo );
+LPVOID NS_GetNSAddr( LPVOID lpNSInfo );
+
+void NS_ReplyToEnumSessionsRequest( LPVOID lpMsg, 
+                                    LPDPSP_REPLYDATA lpReplyData,
+                                    IDirectPlay2Impl* lpDP );
+
+HRESULT NS_SendSessionRequestBroadcast( LPCGUID lpcGuid,
+                                        DWORD dwFlags,
+                                        LPSPINITDATA lpSpData );
+                                        
+
+BOOL NS_InitializeSessionCache( LPVOID* lplpNSInfo );
+void NS_DeleteSessionCache( LPVOID lpNSInfo );
+void NS_InvalidateSessionCache( LPVOID lpNSInfo );
+
+
+void NS_ResetSessionEnumeration( LPVOID lpNSInfo );
+LPDPSESSIONDESC2 NS_WalkSessions( LPVOID lpNSInfo );
+void NS_PruneSessionCache( LPVOID lpNSInfo );
+
+#endif /* __WINE_DPLAYX_NAMESERVER */
