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