Release 20030408.
[wine/gsoc-2012-control.git] / dlls / dplayx / dplayx_messages.c
blobf1684c77ff819c4b724186d5e56dee54f2104a7e
1 /* DirectPlay & DirectPlayLobby messaging implementation
3 * Copyright 2000,2001 - Peter Hunnisett
5 * <presently under construction - contact hunnise@nortelnetworks.com>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include <string.h>
23 #include "winbase.h"
24 #include "wine/debug.h"
26 #include "wingdi.h"
27 #include "winuser.h"
28 #include "winerror.h"
30 #include "dplayx_messages.h"
31 #include "dplay_global.h"
32 #include "dplayx_global.h"
33 #include "name_server.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(dplay);
37 typedef struct tagMSGTHREADINFO
39 HANDLE hStart;
40 HANDLE hDeath;
41 HANDLE hSettingRead;
42 HANDLE hNotifyEvent;
43 } MSGTHREADINFO, *LPMSGTHREADINFO;
45 static DWORD CALLBACK DPL_MSG_ThreadMain( LPVOID lpContext );
46 static LPVOID DP_MSG_ExpectReply( IDirectPlay2AImpl* This, LPDPSP_SENDDATA data,
47 DWORD dwWaitTime, WORD wReplyCommandId,
48 LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize );
51 /* Create the message reception thread to allow the application to receive
52 * asynchronous message reception
54 DWORD CreateLobbyMessageReceptionThread( HANDLE hNotifyEvent, HANDLE hStart,
55 HANDLE hDeath, HANDLE hConnRead )
57 DWORD dwMsgThreadId;
58 LPMSGTHREADINFO lpThreadInfo;
60 lpThreadInfo = HeapAlloc( GetProcessHeap(), 0, sizeof( *lpThreadInfo ) );
61 if( lpThreadInfo == NULL )
63 return 0;
66 /* The notify event may or may not exist. Depends if async comm or not */
67 if( hNotifyEvent &&
68 !DuplicateHandle( GetCurrentProcess(), hNotifyEvent,
69 GetCurrentProcess(), &lpThreadInfo->hNotifyEvent,
70 0, FALSE, DUPLICATE_SAME_ACCESS ) )
72 ERR( "Unable to duplicate event handle\n" );
73 goto error;
76 /* These 3 handles don't need to be duplicated because we don't keep a
77 * reference to them where they're created. They're created specifically
78 * for the message thread
80 lpThreadInfo->hStart = hStart;
81 lpThreadInfo->hDeath = hDeath;
82 lpThreadInfo->hSettingRead = hConnRead;
84 if( !CreateThread( NULL, /* Security attribs */
85 0, /* Stack */
86 DPL_MSG_ThreadMain, /* Msg reception function */
87 lpThreadInfo, /* Msg reception func parameter */
88 0, /* Flags */
89 &dwMsgThreadId /* Updated with thread id */
93 ERR( "Unable to create msg thread\n" );
94 goto error;
97 /* FIXME: Should I be closing the handle to the thread or does that
98 terminate the thread? */
100 return dwMsgThreadId;
102 error:
104 HeapFree( GetProcessHeap(), 0, lpThreadInfo );
106 return 0;
109 static DWORD CALLBACK DPL_MSG_ThreadMain( LPVOID lpContext )
111 LPMSGTHREADINFO lpThreadInfo = (LPMSGTHREADINFO)lpContext;
112 DWORD dwWaitResult;
114 TRACE( "Msg thread created. Waiting on app startup\n" );
116 /* Wait to ensure that the lobby application is started w/ 1 min timeout */
117 dwWaitResult = WaitForSingleObject( lpThreadInfo->hStart, 10000 /* 10 sec */ );
118 if( dwWaitResult == WAIT_TIMEOUT )
120 FIXME( "Should signal app/wait creation failure (0x%08lx)\n", dwWaitResult );
121 goto end_of_thread;
124 /* Close this handle as it's not needed anymore */
125 CloseHandle( lpThreadInfo->hStart );
126 lpThreadInfo->hStart = 0;
128 /* Wait until the lobby knows what it is */
129 dwWaitResult = WaitForSingleObject( lpThreadInfo->hSettingRead, INFINITE );
130 if( dwWaitResult == WAIT_TIMEOUT )
132 ERR( "App Read connection setting timeout fail (0x%08lx)\n", dwWaitResult );
135 /* Close this handle as it's not needed anymore */
136 CloseHandle( lpThreadInfo->hSettingRead );
137 lpThreadInfo->hSettingRead = 0;
139 TRACE( "App created && intialized starting main message reception loop\n" );
141 for ( ;; )
143 MSG lobbyMsg;
144 GetMessageW( &lobbyMsg, 0, 0, 0 );
147 end_of_thread:
148 TRACE( "Msg thread exiting!\n" );
149 HeapFree( GetProcessHeap(), 0, lpThreadInfo );
151 return 0;
154 /* DP messageing stuff */
155 static HANDLE DP_MSG_BuildAndLinkReplyStruct( IDirectPlay2Impl* This,
156 LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList,
157 WORD wReplyCommandId );
158 static LPVOID DP_MSG_CleanReplyStruct( LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList,
159 LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize );
162 static
163 HANDLE DP_MSG_BuildAndLinkReplyStruct( IDirectPlay2Impl* This,
164 LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList, WORD wReplyCommandId )
166 lpReplyStructList->replyExpected.hReceipt = CreateEventA( NULL, FALSE, FALSE, NULL );
167 lpReplyStructList->replyExpected.wExpectedReply = wReplyCommandId;
168 lpReplyStructList->replyExpected.lpReplyMsg = NULL;
169 lpReplyStructList->replyExpected.dwMsgBodySize = 0;
171 /* Insert into the message queue while locked */
172 EnterCriticalSection( &This->unk->DP_lock );
173 DPQ_INSERT( This->dp2->replysExpected, lpReplyStructList, replysExpected );
174 LeaveCriticalSection( &This->unk->DP_lock );
176 return lpReplyStructList->replyExpected.hReceipt;
179 static
180 LPVOID DP_MSG_CleanReplyStruct( LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList,
181 LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize )
183 CloseHandle( lpReplyStructList->replyExpected.hReceipt );
185 *lplpReplyMsg = lpReplyStructList->replyExpected.lpReplyMsg;
186 *lpdwMsgBodySize = lpReplyStructList->replyExpected.dwMsgBodySize;
188 return lpReplyStructList->replyExpected.lpReplyMsg;
191 HRESULT DP_MSG_SendRequestPlayerId( IDirectPlay2AImpl* This, DWORD dwFlags,
192 LPDPID lpdpidAllocatedId )
194 LPVOID lpMsg;
195 LPDPMSG_REQUESTNEWPLAYERID lpMsgBody;
196 DWORD dwMsgSize;
197 HRESULT hr = DP_OK;
199 dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody );
201 lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize );
203 lpMsgBody = (LPDPMSG_REQUESTNEWPLAYERID)( (BYTE*)lpMsg +
204 This->dp2->spData.dwSPHeaderSize );
206 /* Compose dplay message envelope */
207 lpMsgBody->envelope.dwMagic = DPMSGMAGIC_DPLAYMSG;
208 lpMsgBody->envelope.wCommandId = DPMSGCMD_REQUESTNEWPLAYERID;
209 lpMsgBody->envelope.wVersion = DPMSGVER_DP6;
211 /* Compose the body of the message */
212 lpMsgBody->dwFlags = dwFlags;
214 /* Send the message */
216 DPSP_SENDDATA data;
218 data.dwFlags = DPSEND_GUARANTEED;
219 data.idPlayerTo = 0; /* Name server */
220 data.idPlayerFrom = 0; /* Sending from DP */
221 data.lpMessage = lpMsg;
222 data.dwMessageSize = dwMsgSize;
223 data.bSystemMessage = TRUE; /* Allow reply to be sent */
224 data.lpISP = This->dp2->spData.lpISP;
226 TRACE( "Asking for player id w/ dwFlags 0x%08lx\n",
227 lpMsgBody->dwFlags );
229 DP_MSG_ExpectReply( This, &data, DPMSG_DEFAULT_WAIT_TIME, DPMSGCMD_NEWPLAYERIDREPLY,
230 &lpMsg, &dwMsgSize );
233 /* Need to examine the data and extract the new player id */
234 if( !FAILED(hr) )
236 LPCDPMSG_NEWPLAYERIDREPLY lpcReply;
238 lpcReply = (LPCDPMSG_NEWPLAYERIDREPLY)lpMsg;
240 *lpdpidAllocatedId = lpcReply->dpidNewPlayerId;
242 TRACE( "Received reply for id = 0x%08lx\n", lpcReply->dpidNewPlayerId );
244 /* FIXME: I think that the rest of the message has something to do
245 * with remote data for the player that perhaps I need to setup.
246 * However, with the information that is passed, all that it could
247 * be used for is a standardized intialization value, which I'm
248 * guessing we can do without. Unless the message content is the same
249 * for several different messages?
252 HeapFree( GetProcessHeap(), 0, lpMsg );
255 return hr;
258 HRESULT DP_MSG_ForwardPlayerCreation( IDirectPlay2AImpl* This, DPID dpidServer )
260 LPVOID lpMsg;
261 LPDPMSG_FORWARDADDPLAYER lpMsgBody;
262 DWORD dwMsgSize;
263 HRESULT hr = DP_OK;
265 dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody );
267 lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize );
269 lpMsgBody = (LPDPMSG_FORWARDADDPLAYER)( (BYTE*)lpMsg +
270 This->dp2->spData.dwSPHeaderSize );
272 /* Compose dplay message envelope */
273 lpMsgBody->envelope.dwMagic = DPMSGMAGIC_DPLAYMSG;
274 lpMsgBody->envelope.wCommandId = DPMSGCMD_FORWARDADDPLAYER;
275 lpMsgBody->envelope.wVersion = DPMSGVER_DP6;
277 #if 0
279 LPBYTE lpPData;
280 DWORD dwDataSize;
282 /* SP Player remote data needs to be propagated at some point - is this the point? */
283 IDirectPlaySP_GetSPPlayerData( This->dp2->spData.lpISP, 0, (LPVOID*)&lpPData, &dwDataSize, DPSET_REMOTE );
285 ERR( "Player Data size is 0x%08lx\n"
286 "[%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x]\n"
287 "[%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x]\n",
289 dwDataSize,
290 lpPData[0], lpPData[1], lpPData[2], lpPData[3], lpPData[4],
291 lpPData[5], lpPData[6], lpPData[7], lpPData[8], lpPData[9],
292 lpPData[10], lpPData[11], lpPData[12], lpPData[13], lpPData[14],
293 lpPData[15], lpPData[16], lpPData[17], lpPData[18], lpPData[19],
294 lpPData[20], lpPData[21], lpPData[22], lpPData[23], lpPData[24],
295 lpPData[25], lpPData[26], lpPData[27], lpPData[28], lpPData[29],
296 lpPData[30], lpPData[31]
298 DebugBreak();
300 #endif
302 /* Compose body of message */
303 lpMsgBody->dpidAppServer = dpidServer;
304 lpMsgBody->unknown2[0] = 0x0;
305 lpMsgBody->unknown2[1] = 0x1c;
306 lpMsgBody->unknown2[2] = 0x6c;
307 lpMsgBody->unknown2[3] = 0x50;
308 lpMsgBody->unknown2[4] = 0x9;
310 lpMsgBody->dpidAppServer2 = dpidServer;
311 lpMsgBody->unknown3[0] = 0x0;
312 lpMsgBody->unknown3[0] = 0x0;
313 lpMsgBody->unknown3[0] = 0x20;
314 lpMsgBody->unknown3[0] = 0x0;
315 lpMsgBody->unknown3[0] = 0x0;
317 lpMsgBody->dpidAppServer3 = dpidServer;
318 lpMsgBody->unknown4[0] = 0x30;
319 lpMsgBody->unknown4[1] = 0xb;
320 lpMsgBody->unknown4[2] = 0x0;
322 lpMsgBody->unknown4[3] = NS_GetNsMagic( This->dp2->lpNameServerData ) -
323 0x02000000;
324 TRACE( "Setting first magic to 0x%08lx\n", lpMsgBody->unknown4[3] );
326 lpMsgBody->unknown4[4] = 0x0;
327 lpMsgBody->unknown4[5] = 0x0;
328 lpMsgBody->unknown4[6] = 0x0;
330 #if 0
331 lpMsgBody->unknown4[7] = NS_GetOtherMagic( This->dp2->lpNameServerData )
332 #else
333 lpMsgBody->unknown4[7] = NS_GetNsMagic( This->dp2->lpNameServerData );
334 #endif
335 TRACE( "Setting second magic to 0x%08lx\n", lpMsgBody->unknown4[7] );
337 lpMsgBody->unknown4[8] = 0x0;
338 lpMsgBody->unknown4[9] = 0x0;
339 lpMsgBody->unknown4[10] = 0x0;
340 lpMsgBody->unknown4[11] = 0x0;
341 lpMsgBody->unknown4[12] = 0x0;
343 lpMsgBody->unknown5[0] = 0x0;
344 lpMsgBody->unknown5[1] = 0x0;
346 /* Send the message */
348 DPSP_SENDDATA data;
350 data.dwFlags = DPSEND_GUARANTEED;
351 data.idPlayerTo = 0; /* Name server */
352 data.idPlayerFrom = dpidServer; /* Sending from session server */
353 data.lpMessage = lpMsg;
354 data.dwMessageSize = dwMsgSize;
355 data.bSystemMessage = TRUE; /* Allow reply to be sent */
356 data.lpISP = This->dp2->spData.lpISP;
358 TRACE( "Sending forward player request with 0x%08lx\n", dpidServer );
360 lpMsg = DP_MSG_ExpectReply( This, &data,
361 DPMSG_WAIT_60_SECS,
362 DPMSGCMD_GETNAMETABLEREPLY,
363 &lpMsg, &dwMsgSize );
366 /* Need to examine the data and extract the new player id */
367 if( lpMsg != NULL )
369 FIXME( "Name Table reply received: stub\n" );
372 return hr;
375 /* Queue up a structure indicating that we want a reply of type wReplyCommandId. DPlay does
376 * not seem to offer any way of uniquely differentiating between replies of the same type
377 * relative to the request sent. There is an implicit assumption that there will be no
378 * ordering issues on sends and receives from the opposite machine. No wonder MS is not
379 * a networking company.
381 static
382 LPVOID DP_MSG_ExpectReply( IDirectPlay2AImpl* This, LPDPSP_SENDDATA lpData,
383 DWORD dwWaitTime, WORD wReplyCommandId,
384 LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize )
386 HRESULT hr;
387 HANDLE hMsgReceipt;
388 DP_MSG_REPLY_STRUCT_LIST replyStructList;
389 DWORD dwWaitReturn;
391 /* Setup for receipt */
392 hMsgReceipt = DP_MSG_BuildAndLinkReplyStruct( This, &replyStructList,
393 wReplyCommandId );
395 TRACE( "Sending msg and expecting cmd %u in reply within %lu ticks\n",
396 wReplyCommandId, dwWaitTime );
397 hr = (*This->dp2->spData.lpCB->Send)( lpData );
399 if( FAILED(hr) )
401 ERR( "Send failed: %s\n", DPLAYX_HresultToString( hr ) );
402 return NULL;
405 /* The reply message will trigger the hMsgReceipt event effectively switching
406 * control back to this thread. See DP_MSG_ReplyReceived.
408 dwWaitReturn = WaitForSingleObject( hMsgReceipt, dwWaitTime );
409 if( dwWaitReturn != WAIT_OBJECT_0 )
411 ERR( "Wait failed 0x%08lx\n", dwWaitReturn );
412 return NULL;
415 /* Clean Up */
416 return DP_MSG_CleanReplyStruct( &replyStructList, lplpReplyMsg, lpdwMsgBodySize );
419 /* Determine if there is a matching request for this incomming message and then copy
420 * all important data. It is quite silly to have to copy the message, but the documents
421 * indicate that a copy is taken. Silly really.
423 void DP_MSG_ReplyReceived( IDirectPlay2AImpl* This, WORD wCommandId,
424 LPCVOID lpcMsgBody, DWORD dwMsgBodySize )
426 LPDP_MSG_REPLY_STRUCT_LIST lpReplyList;
428 #if 0
429 if( wCommandId == DPMSGCMD_FORWARDADDPLAYER )
431 DebugBreak();
433 #endif
435 /* Find, and immediately remove (to avoid double triggering), the appropriate entry. Call locked to
436 * avoid problems.
438 EnterCriticalSection( &This->unk->DP_lock );
439 DPQ_REMOVE_ENTRY( This->dp2->replysExpected, replysExpected, replyExpected.wExpectedReply,\
440 ==, wCommandId, lpReplyList );
441 LeaveCriticalSection( &This->unk->DP_lock );
443 if( lpReplyList != NULL )
445 lpReplyList->replyExpected.dwMsgBodySize = dwMsgBodySize;
446 lpReplyList->replyExpected.lpReplyMsg = HeapAlloc( GetProcessHeap(),
447 HEAP_ZERO_MEMORY,
448 dwMsgBodySize );
449 CopyMemory( lpReplyList->replyExpected.lpReplyMsg,
450 lpcMsgBody, dwMsgBodySize );
452 /* Signal the thread which sent the message that it has a reply */
453 SetEvent( lpReplyList->replyExpected.hReceipt );
455 else
457 ERR( "No receipt event set - only expecting in reply mode\n" );
458 DebugBreak();
462 void DP_MSG_ToSelf( IDirectPlay2AImpl* This, DPID dpidSelf )
464 LPVOID lpMsg;
465 LPDPMSG_SENDENVELOPE lpMsgBody;
466 DWORD dwMsgSize;
468 dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody );
470 lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize );
472 lpMsgBody = (LPDPMSG_SENDENVELOPE)( (BYTE*)lpMsg +
473 This->dp2->spData.dwSPHeaderSize );
475 /* Compose dplay message envelope */
476 lpMsgBody->dwMagic = DPMSGMAGIC_DPLAYMSG;
477 lpMsgBody->wCommandId = DPMSGCMD_JUSTENVELOPE;
478 lpMsgBody->wVersion = DPMSGVER_DP6;
480 /* Send the message to ourselves */
482 DPSP_SENDDATA data;
484 data.dwFlags = 0;
485 data.idPlayerTo = dpidSelf; /* Sending to session server */
486 data.idPlayerFrom = 0; /* Sending from session server */
487 data.lpMessage = lpMsg;
488 data.dwMessageSize = dwMsgSize;
489 data.bSystemMessage = TRUE; /* Allow reply to be sent */
490 data.lpISP = This->dp2->spData.lpISP;
492 lpMsg = DP_MSG_ExpectReply( This, &data,
493 DPMSG_WAIT_5_SECS,
494 DPMSGCMD_JUSTENVELOPE,
495 &lpMsg, &dwMsgSize );
499 void DP_MSG_ErrorReceived( IDirectPlay2AImpl* This, WORD wCommandId,
500 LPCVOID lpMsgBody, DWORD dwMsgBodySize )
502 LPCDPMSG_FORWARDADDPLAYERNACK lpcErrorMsg;
504 lpcErrorMsg = (LPCDPMSG_FORWARDADDPLAYERNACK)lpMsgBody;
506 ERR( "Received error message %u. Error is %s\n",
507 wCommandId, DPLAYX_HresultToString( lpcErrorMsg->errorCode) );
508 DebugBreak();