xref: /reactos/dll/directx/wine/dplayx/dplaysp.c (revision 803b5e13)
1 /* This contains the implementation of the interface Service
2  * Providers require to communicate with Direct Play
3  *
4  * Copyright 2000 Peter Hunnisett
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include <string.h>
22 #include "winerror.h"
23 #include "wine/debug.h"
24 
25 #include "wine/dplaysp.h"
26 #include "dplay_global.h"
27 #include "name_server.h"
28 #include "dplayx_messages.h"
29 
30 #include "dplayx_global.h" /* FIXME: For global hack */
31 
32 /* FIXME: Need to add interface locking inside procedures */
33 
34 WINE_DEFAULT_DEBUG_CHANNEL(dplay);
35 
36 typedef struct IDirectPlaySPImpl
37 {
38   IDirectPlaySP IDirectPlaySP_iface;
39   LONG ref;
40   void *remote_data;
41   DWORD remote_data_size;
42   void *local_data;
43   DWORD local_data_size;
44   IDirectPlayImpl *dplay; /* FIXME: This should perhaps be iface not impl */
45 } IDirectPlaySPImpl;
46 
47 /* This structure is passed to the DP object for safe keeping */
48 typedef struct tagDP_SPPLAYERDATA
49 {
50   LPVOID lpPlayerLocalData;
51   DWORD  dwPlayerLocalDataSize;
52 
53   LPVOID lpPlayerRemoteData;
54   DWORD  dwPlayerRemoteDataSize;
55 } DP_SPPLAYERDATA, *LPDP_SPPLAYERDATA;
56 
57 
58 static inline IDirectPlaySPImpl *impl_from_IDirectPlaySP( IDirectPlaySP *iface )
59 {
60   return CONTAINING_RECORD( iface, IDirectPlaySPImpl, IDirectPlaySP_iface );
61 }
62 
63 static HRESULT WINAPI IDirectPlaySPImpl_QueryInterface( IDirectPlaySP *iface, REFIID riid,
64         void **ppv )
65 {
66   TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid( riid ), ppv );
67 
68   if ( IsEqualGUID( &IID_IUnknown, riid ) || IsEqualGUID( &IID_IDirectPlaySP, riid ) )
69   {
70     *ppv = iface;
71     IDirectPlaySP_AddRef( iface );
72     return S_OK;
73   }
74 
75   FIXME( "Unsupported interface %s\n", debugstr_guid( riid ) );
76   *ppv = NULL;
77   return E_NOINTERFACE;
78 }
79 
80 static ULONG WINAPI IDirectPlaySPImpl_AddRef( IDirectPlaySP *iface )
81 {
82   IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface );
83   ULONG ref = InterlockedIncrement( &This->ref );
84 
85   TRACE( "(%p) ref=%d\n", This, ref );
86 
87   return ref;
88 }
89 
90 static ULONG WINAPI IDirectPlaySPImpl_Release( IDirectPlaySP *iface )
91 {
92   IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface );
93   ULONG ref = InterlockedDecrement( &This->ref );
94 
95   TRACE( "(%p) ref=%d\n", This, ref );
96 
97   if( !ref )
98   {
99     HeapFree( GetProcessHeap(), 0, This->remote_data );
100     HeapFree( GetProcessHeap(), 0, This->local_data );
101     HeapFree( GetProcessHeap(), 0, This );
102   }
103 
104   return ref;
105 }
106 
107 static HRESULT WINAPI IDirectPlaySPImpl_AddMRUEntry( IDirectPlaySP *iface, LPCWSTR lpSection,
108         LPCWSTR lpKey, const void *lpData, DWORD dwDataSize, DWORD dwMaxEntries )
109 {
110   IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface );
111 
112   /* Should be able to call the comctl32 undocumented MRU routines.
113      I suspect that the interface works appropriately */
114   FIXME( "(%p)->(%p,%p%p,0x%08x,0x%08x): stub\n",
115          This, lpSection, lpKey, lpData, dwDataSize, dwMaxEntries );
116 
117   return DP_OK;
118 }
119 
120 static HRESULT WINAPI IDirectPlaySPImpl_CreateAddress( IDirectPlaySP *iface, REFGUID guidSP,
121         REFGUID guidDataType, const void *lpData, DWORD dwDataSize, void *lpAddress,
122         DWORD *lpdwAddressSize )
123 {
124   IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface );
125 
126   FIXME( "(%p)->(%s,%s,%p,0x%08x,%p,%p): stub\n",
127          This, debugstr_guid(guidSP), debugstr_guid(guidDataType),
128          lpData, dwDataSize, lpAddress, lpdwAddressSize );
129 
130   return DP_OK;
131 }
132 
133 static HRESULT WINAPI IDirectPlaySPImpl_EnumAddress( IDirectPlaySP *iface,
134         LPDPENUMADDRESSCALLBACK lpEnumAddressCallback, const void *lpAddress, DWORD dwAddressSize,
135         void *lpContext )
136 {
137   IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface );
138 
139   TRACE( "(%p)->(%p,%p,0x%08x,%p)\n",
140          This, lpEnumAddressCallback, lpAddress, dwAddressSize, lpContext );
141 
142   DPL_EnumAddress( lpEnumAddressCallback, lpAddress, dwAddressSize, lpContext );
143 
144   return DP_OK;
145 }
146 
147 static HRESULT WINAPI IDirectPlaySPImpl_EnumMRUEntries( IDirectPlaySP *iface, LPCWSTR lpSection,
148         LPCWSTR lpKey, LPENUMMRUCALLBACK lpEnumMRUCallback, void *lpContext )
149 {
150   IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface );
151 
152   /* Should be able to call the comctl32 undocumented MRU routines.
153      I suspect that the interface works appropriately */
154   FIXME( "(%p)->(%p,%p,%p,%p): stub\n",
155          This, lpSection, lpKey, lpEnumMRUCallback, lpContext );
156 
157   return DP_OK;
158 }
159 
160 static HRESULT WINAPI IDirectPlaySPImpl_GetPlayerFlags( IDirectPlaySP *iface, DPID idPlayer,
161         DWORD *lpdwPlayerFlags )
162 {
163   IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface );
164 
165   FIXME( "(%p)->(0x%08x,%p): stub\n",
166          This, idPlayer, lpdwPlayerFlags );
167 
168   return DP_OK;
169 }
170 
171 static HRESULT WINAPI IDirectPlaySPImpl_GetSPPlayerData( IDirectPlaySP *iface, DPID idPlayer,
172         void **lplpData, DWORD *lpdwDataSize, DWORD dwFlags )
173 {
174   IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface );
175   HRESULT hr;
176   LPDP_SPPLAYERDATA lpPlayerData;
177 
178   TRACE( "(%p)->(0x%08x,%p,%p,0x%08x)\n",
179          This, idPlayer, lplpData, lpdwDataSize, dwFlags );
180 
181   hr = DP_GetSPPlayerData( This->dplay, idPlayer, (void**)&lpPlayerData );
182 
183   if( FAILED(hr) )
184   {
185     TRACE( "Couldn't get player data: %s\n", DPLAYX_HresultToString(hr) );
186     return DPERR_INVALIDPLAYER;
187   }
188 
189   /* What to do in the case where there is nothing set yet? */
190   if( dwFlags == DPSET_LOCAL )
191   {
192     *lplpData     = lpPlayerData->lpPlayerLocalData;
193     *lpdwDataSize = lpPlayerData->dwPlayerLocalDataSize;
194   }
195   else if( dwFlags == DPSET_REMOTE )
196   {
197     *lplpData     = lpPlayerData->lpPlayerRemoteData;
198     *lpdwDataSize = lpPlayerData->dwPlayerRemoteDataSize;
199   }
200 
201   if( *lplpData == NULL )
202   {
203     hr = DPERR_GENERIC;
204   }
205 
206   return hr;
207 }
208 
209 static HRESULT WINAPI IDirectPlaySPImpl_HandleMessage( IDirectPlaySP *iface, void *lpMessageBody,
210         DWORD dwMessageBodySize, void *lpMessageHeader )
211 {
212   IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface );
213   LPDPMSG_SENDENVELOPE lpMsg = lpMessageBody;
214   HRESULT hr = DPERR_GENERIC;
215   WORD wCommandId;
216   WORD wVersion;
217   DPSP_REPLYDATA data;
218 
219   FIXME( "(%p)->(%p,0x%08x,%p): mostly stub\n",
220          This, lpMessageBody, dwMessageBodySize, lpMessageHeader );
221 
222   wCommandId = lpMsg->wCommandId;
223   wVersion   = lpMsg->wVersion;
224 
225   TRACE( "Incoming message has envelope of 0x%08x, %u, %u\n",
226          lpMsg->dwMagic, wCommandId, wVersion );
227 
228   if( lpMsg->dwMagic != DPMSGMAGIC_DPLAYMSG )
229   {
230     ERR( "Unknown magic 0x%08x!\n", lpMsg->dwMagic );
231     return DPERR_GENERIC;
232   }
233 
234 #if 0
235   {
236     const LPDWORD lpcHeader = lpMessageHeader;
237 
238     TRACE( "lpMessageHeader = [0x%08lx] [0x%08lx] [0x%08lx] [0x%08lx] [0x%08lx]\n",
239            lpcHeader[0], lpcHeader[1], lpcHeader[2], lpcHeader[3], lpcHeader[4] );
240    }
241 #endif
242 
243   /* Pass everything else to Direct Play */
244   data.lpMessage     = NULL;
245   data.dwMessageSize = 0;
246 
247   /* Pass this message to the dplay interface to handle */
248   hr = DP_HandleMessage( This->dplay, lpMessageBody, dwMessageBodySize, lpMessageHeader,
249                          wCommandId, wVersion, &data.lpMessage, &data.dwMessageSize );
250 
251   if( FAILED(hr) )
252   {
253     ERR( "Command processing failed %s\n", DPLAYX_HresultToString(hr) );
254   }
255 
256   /* Do we want a reply? */
257   if( data.lpMessage != NULL )
258   {
259     data.lpSPMessageHeader = lpMessageHeader;
260     data.idNameServer      = 0;
261     data.lpISP             = iface;
262 
263     hr = This->dplay->dp2->spData.lpCB->Reply( &data );
264 
265     if( FAILED(hr) )
266     {
267       ERR( "Reply failed %s\n", DPLAYX_HresultToString(hr) );
268     }
269   }
270 
271   return hr;
272 
273 #if 0
274   HRESULT hr = DP_OK;
275   HANDLE  hReceiveEvent = 0;
276   /* FIXME: Acquire some sort of interface lock */
277   /* FIXME: Need some sort of context for this callback. Need to determine
278    *        how this is actually done with the SP
279    */
280   /* FIXME: Who needs to delete the message when done? */
281   switch( lpMsg->dwType )
282   {
283     case DPSYS_CREATEPLAYERORGROUP:
284     {
285       LPDPMSG_CREATEPLAYERORGROUP msg = lpMsg;
286 
287       if( msg->dwPlayerType == DPPLAYERTYPE_PLAYER )
288       {
289         hr = DP_IF_CreatePlayer( This, lpMessageHeader, msg->dpId,
290                                  &msg->dpnName, 0, msg->lpData,
291                                  msg->dwDataSize, msg->dwFlags, ... );
292       }
293       else if( msg->dwPlayerType == DPPLAYERTYPE_GROUP )
294       {
295         /* Group in group situation? */
296         if( msg->dpIdParent == DPID_NOPARENT_GROUP )
297         {
298           hr = DP_IF_CreateGroup( This, lpMessageHeader, msg->dpId,
299                                   &msg->dpnName, 0, msg->lpData,
300                                   msg->dwDataSize, msg->dwFlags, ... );
301         }
302         else /* Group in Group */
303         {
304           hr = DP_IF_CreateGroupInGroup( This, lpMessageHeader, msg->dpIdParent,
305                                          &msg->dpnName, 0, msg->lpData,
306                                          msg->dwDataSize, msg->dwFlags, ... );
307         }
308       }
309       else /* Hmmm? */
310       {
311         ERR( "Corrupt msg->dwPlayerType for DPSYS_CREATEPLAYERORGROUP\n" );
312         return;
313       }
314 
315       break;
316     }
317 
318     case DPSYS_DESTROYPLAYERORGROUP:
319     {
320       LPDPMSG_DESTROYPLAYERORGROUP msg = lpMsg;
321 
322       if( msg->dwPlayerType == DPPLAYERTYPE_PLAYER )
323       {
324         hr = DP_IF_DestroyPlayer( This, msg->dpId, ... );
325       }
326       else if( msg->dwPlayerType == DPPLAYERTYPE_GROUP )
327       {
328         hr = DP_IF_DestroyGroup( This, msg->dpId, ... );
329       }
330       else /* Hmmm? */
331       {
332         ERR( "Corrupt msg->dwPlayerType for DPSYS_DESTROYPLAYERORGROUP\n" );
333         return;
334       }
335 
336       break;
337     }
338 
339     case DPSYS_ADDPLAYERTOGROUP:
340     {
341       LPDPMSG_ADDPLAYERTOGROUP msg = lpMsg;
342 
343       hr = DP_IF_AddPlayerToGroup( This, msg->dpIdGroup, msg->dpIdPlayer, ... );
344       break;
345     }
346 
347     case DPSYS_DELETEPLAYERFROMGROUP:
348     {
349       LPDPMSG_DELETEPLAYERFROMGROUP msg = lpMsg;
350 
351       hr = DP_IF_DeletePlayerFromGroup( This, msg->dpIdGroup, msg->dpIdPlayer,
352                                         ... );
353 
354       break;
355     }
356 
357     case DPSYS_SESSIONLOST:
358     {
359       LPDPMSG_SESSIONLOST msg = lpMsg;
360 
361       FIXME( "DPSYS_SESSIONLOST not handled\n" );
362 
363       break;
364     }
365 
366     case DPSYS_HOST:
367     {
368       LPDPMSG_HOST msg = lpMsg;
369 
370       FIXME( "DPSYS_HOST not handled\n" );
371 
372       break;
373     }
374 
375     case DPSYS_SETPLAYERORGROUPDATA:
376     {
377       LPDPMSG_SETPLAYERORGROUPDATA msg = lpMsg;
378 
379       if( msg->dwPlayerType == DPPLAYERTYPE_PLAYER )
380       {
381         hr = DP_IF_SetPlayerData( This, msg->dpId, msg->lpData, msg->dwDataSize,                                  DPSET_REMOTE, ... );
382       }
383       else if( msg->dwPlayerType == DPPLAYERTYPE_GROUP )
384       {
385         hr = DP_IF_SetGroupData( This, msg->dpId, msg->lpData, msg->dwDataSize,
386                                  DPSET_REMOTE, ... );
387       }
388       else /* Hmmm? */
389       {
390         ERR( "Corrupt msg->dwPlayerType for LPDPMSG_SETPLAYERORGROUPDATA\n" );
391         return;
392       }
393 
394       break;
395     }
396 
397     case DPSYS_SETPLAYERORGROUPNAME:
398     {
399       LPDPMSG_SETPLAYERORGROUPNAME msg = lpMsg;
400 
401       if( msg->dwPlayerType == DPPLAYERTYPE_PLAYER )
402       {
403         hr = DP_IF_SetPlayerName( This, msg->dpId, msg->dpnName, ... );
404       }
405       else if( msg->dwPlayerType == DPPLAYERTYPE_GROUP )
406       {
407         hr = DP_IF_SetGroupName( This, msg->dpId, msg->dpnName, ... );
408       }
409       else /* Hmmm? */
410       {
411         ERR( "Corrupt msg->dwPlayerType for LPDPMSG_SETPLAYERORGROUPDATA\n" );
412         return;
413       }
414 
415       break;
416     }
417 
418     case DPSYS_SETSESSIONDESC;
419     {
420       LPDPMSG_SETSESSIONDESC msg = lpMsg;
421 
422       hr = DP_IF_SetSessionDesc( This, &msg->dpDesc );
423 
424       break;
425     }
426 
427     case DPSYS_ADDGROUPTOGROUP:
428     {
429       LPDPMSG_ADDGROUPTOGROUP msg = lpMsg;
430 
431       hr = DP_IF_AddGroupToGroup( This, msg->dpIdParentGroup, msg->dpIdGroup,
432                                   ... );
433 
434       break;
435     }
436 
437     case DPSYS_DELETEGROUPFROMGROUP:
438     {
439       LPDPMSG_DELETEGROUPFROMGROUP msg = lpMsg;
440 
441       hr = DP_IF_DeleteGroupFromGroup( This, msg->dpIdParentGroup,
442                                        msg->dpIdGroup, ... );
443 
444       break;
445     }
446 
447     case DPSYS_SECUREMESSAGE:
448     {
449       LPDPMSG_SECUREMESSAGE msg = lpMsg;
450 
451       FIXME( "DPSYS_SECUREMESSAGE not implemented\n" );
452 
453       break;
454     }
455 
456     case DPSYS_STARTSESSION:
457     {
458       LPDPMSG_STARTSESSION msg = lpMsg;
459 
460       FIXME( "DPSYS_STARTSESSION not implemented\n" );
461 
462       break;
463     }
464 
465     case DPSYS_CHAT:
466     {
467       LPDPMSG_CHAT msg = lpMsg;
468 
469       FIXME( "DPSYS_CHAT not implemented\n" );
470 
471       break;
472     }
473 
474     case DPSYS_SETGROUPOWNER:
475     {
476       LPDPMSG_SETGROUPOWNER msg = lpMsg;
477 
478       FIXME( "DPSYS_SETGROUPOWNER not implemented\n" );
479 
480       break;
481     }
482 
483     case DPSYS_SENDCOMPLETE:
484     {
485       LPDPMSG_SENDCOMPLETE msg = lpMsg;
486 
487       FIXME( "DPSYS_SENDCOMPLETE not implemented\n" );
488 
489       break;
490     }
491 
492     default:
493     {
494       /* NOTE: This should be a user defined type. There is nothing that we
495        *       need to do with it except queue it.
496        */
497       TRACE( "Received user message type(?) 0x%08lx through SP.\n",
498               lpMsg->dwType );
499       break;
500     }
501   }
502 
503   FIXME( "Queue message in the receive queue. Need some context data!\n" );
504 
505   if( FAILED(hr) )
506   {
507     ERR( "Unable to perform action for msg type 0x%08lx\n", lpMsg->dwType );
508   }
509   /* If a receive event was registered for this player, invoke it */
510   if( hReceiveEvent )
511   {
512     SetEvent( hReceiveEvent );
513   }
514 #endif
515 }
516 
517 static HRESULT WINAPI IDirectPlaySPImpl_SetSPPlayerData( IDirectPlaySP *iface, DPID idPlayer,
518         void *lpData, DWORD dwDataSize, DWORD dwFlags )
519 {
520   IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface );
521   HRESULT           hr;
522   LPDP_SPPLAYERDATA lpPlayerEntry;
523   LPVOID            lpPlayerData;
524 
525   TRACE( "(%p)->(0x%08x,%p,0x%08x,0x%08x)\n", This, idPlayer, lpData, dwDataSize, dwFlags );
526 
527   hr = DP_GetSPPlayerData( This->dplay, idPlayer, (void**)&lpPlayerEntry );
528   if( FAILED(hr) )
529   {
530     /* Player must not exist */
531     return DPERR_INVALIDPLAYER;
532   }
533 
534   lpPlayerData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwDataSize );
535   CopyMemory( lpPlayerData, lpData, dwDataSize );
536 
537   if( dwFlags == DPSET_LOCAL )
538   {
539     lpPlayerEntry->lpPlayerLocalData = lpPlayerData;
540     lpPlayerEntry->dwPlayerLocalDataSize = dwDataSize;
541   }
542   else if( dwFlags == DPSET_REMOTE )
543   {
544     lpPlayerEntry->lpPlayerRemoteData = lpPlayerData;
545     lpPlayerEntry->dwPlayerRemoteDataSize = dwDataSize;
546   }
547 
548   hr = DP_SetSPPlayerData( This->dplay, idPlayer, lpPlayerEntry );
549 
550   return hr;
551 }
552 
553 static HRESULT WINAPI IDirectPlaySPImpl_CreateCompoundAddress( IDirectPlaySP *iface,
554         const DPCOMPOUNDADDRESSELEMENT *lpElements, DWORD dwElementCount, void *lpAddress,
555         DWORD *lpdwAddressSize )
556 {
557   IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface );
558 
559   FIXME( "(%p)->(%p,0x%08x,%p,%p): stub\n",
560          This, lpElements, dwElementCount, lpAddress, lpdwAddressSize );
561 
562   return DP_OK;
563 }
564 
565 static HRESULT WINAPI IDirectPlaySPImpl_GetSPData( IDirectPlaySP *iface, void **lplpData,
566         DWORD *lpdwDataSize, DWORD dwFlags )
567 {
568   IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface );
569   HRESULT hr = DP_OK;
570 
571   TRACE( "(%p)->(%p,%p,0x%08x)\n", This, lplpData, lpdwDataSize, dwFlags );
572 
573 #if 0
574   /* This is what the documentation says... */
575   if( dwFlags != DPSET_REMOTE )
576   {
577     return DPERR_INVALIDPARAMS;
578   }
579 #else
580   /* ... but most service providers call this with 1 */
581   /* Guess that this is using a DPSET_LOCAL or DPSET_REMOTE type of
582    * thing?
583    */
584   if( dwFlags != DPSET_REMOTE )
585   {
586     TRACE( "Undocumented dwFlags 0x%08x used\n", dwFlags );
587   }
588 #endif
589 
590   /* FIXME: What to do in the case where this isn't initialized yet? */
591 
592   /* Yes, we're supposed to return a pointer to the memory we have stored! */
593   if( dwFlags == DPSET_REMOTE )
594   {
595     *lpdwDataSize = This->remote_data_size;
596     *lplpData = This->remote_data;
597 
598     if( !This->remote_data )
599       hr = DPERR_GENERIC;
600   }
601   else if( dwFlags == DPSET_LOCAL )
602   {
603     *lpdwDataSize = This->local_data_size;
604     *lplpData = This->local_data;
605 
606     if( !This->local_data )
607       hr = DPERR_GENERIC;
608   }
609 
610   return hr;
611 }
612 
613 static HRESULT WINAPI IDirectPlaySPImpl_SetSPData( IDirectPlaySP *iface, void *lpData,
614         DWORD dwDataSize, DWORD dwFlags )
615 {
616   IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface );
617   LPVOID lpSpData;
618 
619   TRACE( "(%p)->(%p,0x%08x,0x%08x)\n", This, lpData, dwDataSize, dwFlags );
620 
621 #if 0
622   /* This is what the documentation says... */
623   if( dwFlags != DPSET_REMOTE )
624   {
625     return DPERR_INVALIDPARAMS;
626   }
627 #else
628   /* ... but most service providers call this with 1 */
629   /* Guess that this is using a DPSET_LOCAL or DPSET_REMOTE type of
630    * thing?
631    */
632   if( dwFlags != DPSET_REMOTE )
633   {
634     TRACE( "Undocumented dwFlags 0x%08x used\n", dwFlags );
635   }
636 #endif
637 
638   lpSpData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwDataSize );
639   CopyMemory( lpSpData, lpData, dwDataSize );
640 
641   /* If we have data already allocated, free it and replace it */
642   if( dwFlags == DPSET_REMOTE )
643   {
644     HeapFree( GetProcessHeap(), 0, This->remote_data );
645     This->remote_data_size = dwDataSize;
646     This->remote_data = lpSpData;
647   }
648   else if ( dwFlags == DPSET_LOCAL )
649   {
650     HeapFree( GetProcessHeap(), 0, This->local_data );
651     This->local_data = lpSpData;
652     This->local_data_size = dwDataSize;
653   }
654 
655   return DP_OK;
656 }
657 
658 static void WINAPI IDirectPlaySPImpl_SendComplete( IDirectPlaySP *iface, void *unknownA,
659         DWORD unknownB )
660 {
661   IDirectPlaySPImpl *This = impl_from_IDirectPlaySP( iface );
662 
663   FIXME( "(%p)->(%p,0x%08x): stub\n",
664          This, unknownA, unknownB );
665 }
666 
667 static const IDirectPlaySPVtbl directPlaySPVT =
668 {
669   IDirectPlaySPImpl_QueryInterface,
670   IDirectPlaySPImpl_AddRef,
671   IDirectPlaySPImpl_Release,
672   IDirectPlaySPImpl_AddMRUEntry,
673   IDirectPlaySPImpl_CreateAddress,
674   IDirectPlaySPImpl_EnumAddress,
675   IDirectPlaySPImpl_EnumMRUEntries,
676   IDirectPlaySPImpl_GetPlayerFlags,
677   IDirectPlaySPImpl_GetSPPlayerData,
678   IDirectPlaySPImpl_HandleMessage,
679   IDirectPlaySPImpl_SetSPPlayerData,
680   IDirectPlaySPImpl_CreateCompoundAddress,
681   IDirectPlaySPImpl_GetSPData,
682   IDirectPlaySPImpl_SetSPData,
683   IDirectPlaySPImpl_SendComplete
684 };
685 
686 HRESULT dplaysp_create( REFIID riid, void **ppv, IDirectPlayImpl *dp )
687 {
688   IDirectPlaySPImpl *obj;
689   HRESULT hr;
690 
691   TRACE( "(%s, %p)\n", debugstr_guid( riid ), ppv );
692 
693   *ppv = NULL;
694   obj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *obj ) );
695   if ( !obj )
696     return DPERR_OUTOFMEMORY;
697 
698   obj->IDirectPlaySP_iface.lpVtbl = &directPlaySPVT;
699   obj->ref = 1;
700   obj->dplay = dp;
701 
702   hr = IDirectPlaySP_QueryInterface( &obj->IDirectPlaySP_iface, riid, ppv );
703   IDirectPlaySP_Release( &obj->IDirectPlaySP_iface );
704 
705   return hr;
706 }
707 
708 /* DP external interfaces to call into DPSP interface */
709 
710 /* Allocate the structure */
711 LPVOID DPSP_CreateSPPlayerData(void)
712 {
713   TRACE( "Creating SPPlayer data struct\n" );
714   return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
715                     sizeof( DP_SPPLAYERDATA ) );
716 }
717