| /* This contains the implementation of the interface Service |
| * Providers require to communicate with Direct Play |
| * |
| * Copyright 2000 Peter Hunnisett |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include <string.h> |
| #include "winerror.h" |
| #include "wine/debug.h" |
| |
| #include "wine/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 */ |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(dplay); |
| |
| typedef struct IDirectPlaySPImpl |
| { |
| IDirectPlaySP IDirectPlaySP_iface; |
| LONG ref; |
| void *remote_data; |
| DWORD remote_data_size; |
| void *local_data; |
| DWORD local_data_size; |
| IDirectPlayImpl *dplay; /* FIXME: This should perhaps be iface not impl */ |
| } IDirectPlaySPImpl; |
| |
| /* This structure is passed to the DP object for safe keeping */ |
| typedef struct tagDP_SPPLAYERDATA |
| { |
| LPVOID lpPlayerLocalData; |
| DWORD dwPlayerLocalDataSize; |
| |
| LPVOID lpPlayerRemoteData; |
| DWORD dwPlayerRemoteDataSize; |
| } DP_SPPLAYERDATA, *LPDP_SPPLAYERDATA; |
| |
| |
| static inline IDirectPlaySPImpl *impl_from_IDirectPlaySP( IDirectPlaySP *iface ) |
| { |
| return CONTAINING_RECORD( iface, IDirectPlaySPImpl, IDirectPlaySP_iface ); |
| } |
| |
| static HRESULT WINAPI IDirectPlaySPImpl_QueryInterface( IDirectPlaySP *iface, REFIID riid, |
| void **ppv ) |
| { |
| TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid( riid ), ppv ); |
| |
| if ( IsEqualGUID( &IID_IUnknown, riid ) || IsEqualGUID( &IID_IDirectPlaySP, riid ) ) |
| { |
| *ppv = iface; |
| IDirectPlaySP_AddRef( iface ); |
| return S_OK; |
| } |
| |
| FIXME( "Unsupported interface %s\n", debugstr_guid( riid ) ); |
| *ppv = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI IDirectPlaySPImpl_AddRef( IDirectPlaySP *iface ) |
| { |
| IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface ); |
| ULONG ref = InterlockedIncrement( &This->ref ); |
| |
| TRACE( "(%p) ref=%d\n", This, ref ); |
| |
| return ref; |
| } |
| |
| static ULONG WINAPI IDirectPlaySPImpl_Release( IDirectPlaySP *iface ) |
| { |
| IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface ); |
| ULONG ref = InterlockedDecrement( &This->ref ); |
| |
| TRACE( "(%p) ref=%d\n", This, ref ); |
| |
| if( !ref ) |
| { |
| HeapFree( GetProcessHeap(), 0, This->remote_data ); |
| HeapFree( GetProcessHeap(), 0, This->local_data ); |
| HeapFree( GetProcessHeap(), 0, This ); |
| } |
| |
| return ref; |
| } |
| |
| static HRESULT WINAPI IDirectPlaySPImpl_AddMRUEntry( IDirectPlaySP *iface, LPCWSTR lpSection, |
| LPCWSTR lpKey, const void *lpData, DWORD dwDataSize, DWORD dwMaxEntries ) |
| { |
| IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface ); |
| |
| /* Should be able to call the comctl32 undocumented MRU routines. |
| I suspect that the interface works appropriately */ |
| FIXME( "(%p)->(%p,%p%p,0x%08x,0x%08x): stub\n", |
| This, lpSection, lpKey, lpData, dwDataSize, dwMaxEntries ); |
| |
| return DP_OK; |
| } |
| |
| static HRESULT WINAPI IDirectPlaySPImpl_CreateAddress( IDirectPlaySP *iface, REFGUID guidSP, |
| REFGUID guidDataType, const void *lpData, DWORD dwDataSize, void *lpAddress, |
| DWORD *lpdwAddressSize ) |
| { |
| IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface ); |
| |
| FIXME( "(%p)->(%s,%s,%p,0x%08x,%p,%p): stub\n", |
| This, debugstr_guid(guidSP), debugstr_guid(guidDataType), |
| lpData, dwDataSize, lpAddress, lpdwAddressSize ); |
| |
| return DP_OK; |
| } |
| |
| static HRESULT WINAPI IDirectPlaySPImpl_EnumAddress( IDirectPlaySP *iface, |
| LPDPENUMADDRESSCALLBACK lpEnumAddressCallback, const void *lpAddress, DWORD dwAddressSize, |
| void *lpContext ) |
| { |
| IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface ); |
| |
| TRACE( "(%p)->(%p,%p,0x%08x,%p)\n", |
| This, lpEnumAddressCallback, lpAddress, dwAddressSize, lpContext ); |
| |
| DPL_EnumAddress( lpEnumAddressCallback, lpAddress, dwAddressSize, lpContext ); |
| |
| return DP_OK; |
| } |
| |
| static HRESULT WINAPI IDirectPlaySPImpl_EnumMRUEntries( IDirectPlaySP *iface, LPCWSTR lpSection, |
| LPCWSTR lpKey, LPENUMMRUCALLBACK lpEnumMRUCallback, void *lpContext ) |
| { |
| IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface ); |
| |
| /* Should be able to call the comctl32 undocumented MRU routines. |
| I suspect that the interface works appropriately */ |
| FIXME( "(%p)->(%p,%p,%p,%p): stub\n", |
| This, lpSection, lpKey, lpEnumMRUCallback, lpContext ); |
| |
| return DP_OK; |
| } |
| |
| static HRESULT WINAPI IDirectPlaySPImpl_GetPlayerFlags( IDirectPlaySP *iface, DPID idPlayer, |
| DWORD *lpdwPlayerFlags ) |
| { |
| IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface ); |
| |
| FIXME( "(%p)->(0x%08x,%p): stub\n", |
| This, idPlayer, lpdwPlayerFlags ); |
| |
| return DP_OK; |
| } |
| |
| static HRESULT WINAPI IDirectPlaySPImpl_GetSPPlayerData( IDirectPlaySP *iface, DPID idPlayer, |
| void **lplpData, DWORD *lpdwDataSize, DWORD dwFlags ) |
| { |
| IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface ); |
| HRESULT hr; |
| LPDP_SPPLAYERDATA lpPlayerData; |
| |
| TRACE( "(%p)->(0x%08x,%p,%p,0x%08x)\n", |
| This, idPlayer, lplpData, lpdwDataSize, dwFlags ); |
| |
| hr = DP_GetSPPlayerData( This->dplay, idPlayer, (void**)&lpPlayerData ); |
| |
| if( FAILED(hr) ) |
| { |
| TRACE( "Couldn't get player data: %s\n", DPLAYX_HresultToString(hr) ); |
| return DPERR_INVALIDPLAYER; |
| } |
| |
| /* What to do in the case where there is nothing set yet? */ |
| if( dwFlags == DPSET_LOCAL ) |
| { |
| *lplpData = lpPlayerData->lpPlayerLocalData; |
| *lpdwDataSize = lpPlayerData->dwPlayerLocalDataSize; |
| } |
| else if( dwFlags == DPSET_REMOTE ) |
| { |
| *lplpData = lpPlayerData->lpPlayerRemoteData; |
| *lpdwDataSize = lpPlayerData->dwPlayerRemoteDataSize; |
| } |
| |
| if( *lplpData == NULL ) |
| { |
| hr = DPERR_GENERIC; |
| } |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI IDirectPlaySPImpl_HandleMessage( IDirectPlaySP *iface, void *lpMessageBody, |
| DWORD dwMessageBodySize, void *lpMessageHeader ) |
| { |
| IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface ); |
| LPDPMSG_SENDENVELOPE lpMsg = lpMessageBody; |
| HRESULT hr = DPERR_GENERIC; |
| WORD wCommandId; |
| WORD wVersion; |
| DPSP_REPLYDATA data; |
| |
| FIXME( "(%p)->(%p,0x%08x,%p): mostly stub\n", |
| This, lpMessageBody, dwMessageBodySize, lpMessageHeader ); |
| |
| wCommandId = lpMsg->wCommandId; |
| wVersion = lpMsg->wVersion; |
| |
| TRACE( "Incoming message has envelope of 0x%08x, %u, %u\n", |
| lpMsg->dwMagic, wCommandId, wVersion ); |
| |
| if( lpMsg->dwMagic != DPMSGMAGIC_DPLAYMSG ) |
| { |
| ERR( "Unknown magic 0x%08x!\n", lpMsg->dwMagic ); |
| return DPERR_GENERIC; |
| } |
| |
| #if 0 |
| { |
| const LPDWORD lpcHeader = lpMessageHeader; |
| |
| TRACE( "lpMessageHeader = [0x%08lx] [0x%08lx] [0x%08lx] [0x%08lx] [0x%08lx]\n", |
| lpcHeader[0], lpcHeader[1], lpcHeader[2], lpcHeader[3], lpcHeader[4] ); |
| } |
| #endif |
| |
| /* Pass everything else to Direct Play */ |
| data.lpMessage = NULL; |
| data.dwMessageSize = 0; |
| |
| /* Pass this message to the dplay interface to handle */ |
| hr = DP_HandleMessage( This->dplay, lpMessageBody, dwMessageBodySize, lpMessageHeader, |
| wCommandId, wVersion, &data.lpMessage, &data.dwMessageSize ); |
| |
| if( FAILED(hr) ) |
| { |
| ERR( "Command processing failed %s\n", DPLAYX_HresultToString(hr) ); |
| } |
| |
| /* Do we want a reply? */ |
| if( data.lpMessage != NULL ) |
| { |
| data.lpSPMessageHeader = lpMessageHeader; |
| data.idNameServer = 0; |
| data.lpISP = iface; |
| |
| hr = This->dplay->dp2->spData.lpCB->Reply( &data ); |
| |
| if( FAILED(hr) ) |
| { |
| ERR( "Reply failed %s\n", DPLAYX_HresultToString(hr) ); |
| } |
| } |
| |
| return hr; |
| |
| #if 0 |
| HRESULT hr = DP_OK; |
| HANDLE hReceiveEvent = 0; |
| /* FIXME: Acquire 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 = 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 = 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 = lpMsg; |
| |
| hr = DP_IF_AddPlayerToGroup( This, msg->dpIdGroup, msg->dpIdPlayer, ... ); |
| break; |
| } |
| |
| case DPSYS_DELETEPLAYERFROMGROUP: |
| { |
| LPDPMSG_DELETEPLAYERFROMGROUP msg = lpMsg; |
| |
| hr = DP_IF_DeletePlayerFromGroup( This, msg->dpIdGroup, msg->dpIdPlayer, |
| ... ); |
| |
| break; |
| } |
| |
| case DPSYS_SESSIONLOST: |
| { |
| LPDPMSG_SESSIONLOST msg = lpMsg; |
| |
| FIXME( "DPSYS_SESSIONLOST not handled\n" ); |
| |
| break; |
| } |
| |
| case DPSYS_HOST: |
| { |
| LPDPMSG_HOST msg = lpMsg; |
| |
| FIXME( "DPSYS_HOST not handled\n" ); |
| |
| break; |
| } |
| |
| case DPSYS_SETPLAYERORGROUPDATA: |
| { |
| LPDPMSG_SETPLAYERORGROUPDATA msg = 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 = 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 = lpMsg; |
| |
| hr = DP_IF_SetSessionDesc( This, &msg->dpDesc ); |
| |
| break; |
| } |
| |
| case DPSYS_ADDGROUPTOGROUP: |
| { |
| LPDPMSG_ADDGROUPTOGROUP msg = lpMsg; |
| |
| hr = DP_IF_AddGroupToGroup( This, msg->dpIdParentGroup, msg->dpIdGroup, |
| ... ); |
| |
| break; |
| } |
| |
| case DPSYS_DELETEGROUPFROMGROUP: |
| { |
| LPDPMSG_DELETEGROUPFROMGROUP msg = lpMsg; |
| |
| hr = DP_IF_DeleteGroupFromGroup( This, msg->dpIdParentGroup, |
| msg->dpIdGroup, ... ); |
| |
| break; |
| } |
| |
| case DPSYS_SECUREMESSAGE: |
| { |
| LPDPMSG_SECUREMESSAGE msg = lpMsg; |
| |
| FIXME( "DPSYS_SECUREMESSAGE not implemented\n" ); |
| |
| break; |
| } |
| |
| case DPSYS_STARTSESSION: |
| { |
| LPDPMSG_STARTSESSION msg = lpMsg; |
| |
| FIXME( "DPSYS_STARTSESSION not implemented\n" ); |
| |
| break; |
| } |
| |
| case DPSYS_CHAT: |
| { |
| LPDPMSG_CHAT msg = lpMsg; |
| |
| FIXME( "DPSYS_CHAT not implemeneted\n" ); |
| |
| break; |
| } |
| |
| case DPSYS_SETGROUPOWNER: |
| { |
| LPDPMSG_SETGROUPOWNER msg = lpMsg; |
| |
| FIXME( "DPSYS_SETGROUPOWNER not implemented\n" ); |
| |
| break; |
| } |
| |
| case DPSYS_SENDCOMPLETE: |
| { |
| LPDPMSG_SENDCOMPLETE msg = 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 receive event was registered for this player, invoke it */ |
| if( hReceiveEvent ) |
| { |
| SetEvent( hReceiveEvent ); |
| } |
| #endif |
| } |
| |
| static HRESULT WINAPI IDirectPlaySPImpl_SetSPPlayerData( IDirectPlaySP *iface, DPID idPlayer, |
| void *lpData, DWORD dwDataSize, DWORD dwFlags ) |
| { |
| IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface ); |
| HRESULT hr; |
| LPDP_SPPLAYERDATA lpPlayerEntry; |
| LPVOID lpPlayerData; |
| |
| TRACE( "(%p)->(0x%08x,%p,0x%08x,0x%08x)\n", This, idPlayer, lpData, dwDataSize, dwFlags ); |
| |
| hr = DP_GetSPPlayerData( This->dplay, idPlayer, (void**)&lpPlayerEntry ); |
| if( FAILED(hr) ) |
| { |
| /* Player must not exist */ |
| return DPERR_INVALIDPLAYER; |
| } |
| |
| lpPlayerData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwDataSize ); |
| CopyMemory( lpPlayerData, lpData, dwDataSize ); |
| |
| if( dwFlags == DPSET_LOCAL ) |
| { |
| lpPlayerEntry->lpPlayerLocalData = lpPlayerData; |
| lpPlayerEntry->dwPlayerLocalDataSize = dwDataSize; |
| } |
| else if( dwFlags == DPSET_REMOTE ) |
| { |
| lpPlayerEntry->lpPlayerRemoteData = lpPlayerData; |
| lpPlayerEntry->dwPlayerRemoteDataSize = dwDataSize; |
| } |
| |
| hr = DP_SetSPPlayerData( This->dplay, idPlayer, lpPlayerEntry ); |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI IDirectPlaySPImpl_CreateCompoundAddress( IDirectPlaySP *iface, |
| const DPCOMPOUNDADDRESSELEMENT *lpElements, DWORD dwElementCount, void *lpAddress, |
| DWORD *lpdwAddressSize ) |
| { |
| IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface ); |
| |
| FIXME( "(%p)->(%p,0x%08x,%p,%p): stub\n", |
| This, lpElements, dwElementCount, lpAddress, lpdwAddressSize ); |
| |
| return DP_OK; |
| } |
| |
| static HRESULT WINAPI IDirectPlaySPImpl_GetSPData( IDirectPlaySP *iface, void **lplpData, |
| DWORD *lpdwDataSize, DWORD dwFlags ) |
| { |
| IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface ); |
| HRESULT hr = DP_OK; |
| |
| TRACE( "(%p)->(%p,%p,0x%08x)\n", This, lplpData, lpdwDataSize, dwFlags ); |
| |
| #if 0 |
| /* This is what the documentation says... */ |
| if( dwFlags != DPSET_REMOTE ) |
| { |
| 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 != DPSET_REMOTE ) |
| { |
| TRACE( "Undocumented dwFlags 0x%08x used\n", dwFlags ); |
| } |
| #endif |
| |
| /* FIXME: What to do in the case where this isn't initialized yet? */ |
| |
| /* Yes, we're supposed to return a pointer to the memory we have stored! */ |
| if( dwFlags == DPSET_REMOTE ) |
| { |
| *lpdwDataSize = This->remote_data_size; |
| *lplpData = This->remote_data; |
| |
| if( !This->remote_data ) |
| hr = DPERR_GENERIC; |
| } |
| else if( dwFlags == DPSET_LOCAL ) |
| { |
| *lpdwDataSize = This->local_data_size; |
| *lplpData = This->local_data; |
| |
| if( !This->local_data ) |
| hr = DPERR_GENERIC; |
| } |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI IDirectPlaySPImpl_SetSPData( IDirectPlaySP *iface, void *lpData, |
| DWORD dwDataSize, DWORD dwFlags ) |
| { |
| IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface ); |
| LPVOID lpSpData; |
| |
| TRACE( "(%p)->(%p,0x%08x,0x%08x)\n", This, lpData, dwDataSize, dwFlags ); |
| |
| #if 0 |
| /* This is what the documentation says... */ |
| if( dwFlags != DPSET_REMOTE ) |
| { |
| 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 != DPSET_REMOTE ) |
| { |
| TRACE( "Undocumented dwFlags 0x%08x used\n", dwFlags ); |
| } |
| #endif |
| |
| 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 ) |
| { |
| HeapFree( GetProcessHeap(), 0, This->remote_data ); |
| This->remote_data_size = dwDataSize; |
| This->remote_data = lpSpData; |
| } |
| else if ( dwFlags == DPSET_LOCAL ) |
| { |
| HeapFree( GetProcessHeap(), 0, This->local_data ); |
| This->local_data = lpSpData; |
| This->local_data_size = dwDataSize; |
| } |
| |
| return DP_OK; |
| } |
| |
| static void WINAPI IDirectPlaySPImpl_SendComplete( IDirectPlaySP *iface, void *unknownA, |
| DWORD unknownB ) |
| { |
| IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface ); |
| |
| FIXME( "(%p)->(%p,0x%08x): stub\n", |
| This, unknownA, unknownB ); |
| } |
| |
| static const IDirectPlaySPVtbl directPlaySPVT = |
| { |
| IDirectPlaySPImpl_QueryInterface, |
| IDirectPlaySPImpl_AddRef, |
| IDirectPlaySPImpl_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 |
| }; |
| |
| HRESULT dplaysp_create( REFIID riid, void **ppv, IDirectPlayImpl *dp ) |
| { |
| IDirectPlaySPImpl *obj; |
| HRESULT hr; |
| |
| TRACE( "(%s, %p)\n", debugstr_guid( riid ), ppv ); |
| |
| *ppv = NULL; |
| obj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *obj ) ); |
| if ( !obj ) |
| return DPERR_OUTOFMEMORY; |
| |
| obj->IDirectPlaySP_iface.lpVtbl = &directPlaySPVT; |
| obj->ref = 1; |
| obj->dplay = dp; |
| |
| hr = IDirectPlaySP_QueryInterface( &obj->IDirectPlaySP_iface, riid, ppv ); |
| IDirectPlaySP_Release( &obj->IDirectPlaySP_iface ); |
| |
| return hr; |
| } |
| |
| /* DP external interfaces to call into DPSP interface */ |
| |
| /* Allocate the structure */ |
| LPVOID DPSP_CreateSPPlayerData(void) |
| { |
| TRACE( "Creating SPPlayer data struct\n" ); |
| return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, |
| sizeof( DP_SPPLAYERDATA ) ); |
| } |