Sync with trunk rev.61910 to get latest improvements and bugfixes.
[reactos.git] / dll / directx / wine / dplayx / dplayx_messages.c
1 /* DirectPlay & DirectPlayLobby messaging implementation
2 *
3 * Copyright 2000,2001 - Peter Hunnisett
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 *
19 * NOTES
20 * o Messaging interface required for both DirectPlay and DirectPlayLobby.
21 */
22
23 #include "dplayx_global.h"
24
25 typedef struct tagMSGTHREADINFO
26 {
27 HANDLE hStart;
28 HANDLE hDeath;
29 HANDLE hSettingRead;
30 HANDLE hNotifyEvent;
31 } MSGTHREADINFO, *LPMSGTHREADINFO;
32
33 static DWORD CALLBACK DPL_MSG_ThreadMain( LPVOID lpContext );
34 static void *DP_MSG_ExpectReply( IDirectPlayImpl *This, DPSP_SENDDATA *data, DWORD dwWaitTime,
35 WORD wReplyCommandId, void **lplpReplyMsg, DWORD *lpdwMsgBodySize );
36
37
38 /* Create the message reception thread to allow the application to receive
39 * asynchronous message reception
40 */
41 DWORD CreateLobbyMessageReceptionThread( HANDLE hNotifyEvent, HANDLE hStart,
42 HANDLE hDeath, HANDLE hConnRead )
43 {
44 DWORD dwMsgThreadId;
45 LPMSGTHREADINFO lpThreadInfo;
46 HANDLE hThread;
47
48 lpThreadInfo = HeapAlloc( GetProcessHeap(), 0, sizeof( *lpThreadInfo ) );
49 if( lpThreadInfo == NULL )
50 {
51 return 0;
52 }
53
54 /* The notify event may or may not exist. Depends if async comm or not */
55 if( hNotifyEvent &&
56 !DuplicateHandle( GetCurrentProcess(), hNotifyEvent,
57 GetCurrentProcess(), &lpThreadInfo->hNotifyEvent,
58 0, FALSE, DUPLICATE_SAME_ACCESS ) )
59 {
60 ERR( "Unable to duplicate event handle\n" );
61 goto error;
62 }
63
64 /* These 3 handles don't need to be duplicated because we don't keep a
65 * reference to them where they're created. They're created specifically
66 * for the message thread
67 */
68 lpThreadInfo->hStart = hStart;
69 lpThreadInfo->hDeath = hDeath;
70 lpThreadInfo->hSettingRead = hConnRead;
71
72 hThread = CreateThread( NULL, /* Security attribs */
73 0, /* Stack */
74 DPL_MSG_ThreadMain, /* Msg reception function */
75 lpThreadInfo, /* Msg reception func parameter */
76 0, /* Flags */
77 &dwMsgThreadId /* Updated with thread id */
78 );
79 if ( hThread == NULL )
80 {
81 ERR( "Unable to create msg thread\n" );
82 goto error;
83 }
84
85 CloseHandle(hThread);
86
87 return dwMsgThreadId;
88
89 error:
90
91 HeapFree( GetProcessHeap(), 0, lpThreadInfo );
92
93 return 0;
94 }
95
96 static DWORD CALLBACK DPL_MSG_ThreadMain( LPVOID lpContext )
97 {
98 LPMSGTHREADINFO lpThreadInfo = lpContext;
99 DWORD dwWaitResult;
100
101 TRACE( "Msg thread created. Waiting on app startup\n" );
102
103 /* Wait to ensure that the lobby application is started w/ 1 min timeout */
104 dwWaitResult = WaitForSingleObject( lpThreadInfo->hStart, 10000 /* 10 sec */ );
105 if( dwWaitResult == WAIT_TIMEOUT )
106 {
107 FIXME( "Should signal app/wait creation failure (0x%08x)\n", dwWaitResult );
108 goto end_of_thread;
109 }
110
111 /* Close this handle as it's not needed anymore */
112 CloseHandle( lpThreadInfo->hStart );
113 lpThreadInfo->hStart = 0;
114
115 /* Wait until the lobby knows what it is */
116 dwWaitResult = WaitForSingleObject( lpThreadInfo->hSettingRead, INFINITE );
117 if( dwWaitResult == WAIT_TIMEOUT )
118 {
119 ERR( "App Read connection setting timeout fail (0x%08x)\n", dwWaitResult );
120 }
121
122 /* Close this handle as it's not needed anymore */
123 CloseHandle( lpThreadInfo->hSettingRead );
124 lpThreadInfo->hSettingRead = 0;
125
126 TRACE( "App created && initialized starting main message reception loop\n" );
127
128 for ( ;; )
129 {
130 MSG lobbyMsg;
131 GetMessageW( &lobbyMsg, 0, 0, 0 );
132 }
133
134 end_of_thread:
135 TRACE( "Msg thread exiting!\n" );
136 HeapFree( GetProcessHeap(), 0, lpThreadInfo );
137
138 return 0;
139 }
140
141 /* DP messaging stuff */
142 static HANDLE DP_MSG_BuildAndLinkReplyStruct( IDirectPlayImpl *This,
143 DP_MSG_REPLY_STRUCT_LIST *lpReplyStructList, WORD wReplyCommandId )
144 {
145 lpReplyStructList->replyExpected.hReceipt = CreateEventW( NULL, FALSE, FALSE, NULL );
146 lpReplyStructList->replyExpected.wExpectedReply = wReplyCommandId;
147 lpReplyStructList->replyExpected.lpReplyMsg = NULL;
148 lpReplyStructList->replyExpected.dwMsgBodySize = 0;
149
150 /* Insert into the message queue while locked */
151 EnterCriticalSection( &This->lock );
152 DPQ_INSERT( This->dp2->repliesExpected, lpReplyStructList, repliesExpected );
153 LeaveCriticalSection( &This->lock );
154
155 return lpReplyStructList->replyExpected.hReceipt;
156 }
157
158 static
159 LPVOID DP_MSG_CleanReplyStruct( LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList,
160 LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize )
161 {
162 CloseHandle( lpReplyStructList->replyExpected.hReceipt );
163
164 *lplpReplyMsg = lpReplyStructList->replyExpected.lpReplyMsg;
165 *lpdwMsgBodySize = lpReplyStructList->replyExpected.dwMsgBodySize;
166
167 return lpReplyStructList->replyExpected.lpReplyMsg;
168 }
169
170 HRESULT DP_MSG_SendRequestPlayerId( IDirectPlayImpl *This, DWORD dwFlags, DPID *lpdpidAllocatedId )
171 {
172 LPVOID lpMsg;
173 LPDPMSG_REQUESTNEWPLAYERID lpMsgBody;
174 DWORD dwMsgSize;
175 HRESULT hr = DP_OK;
176
177 dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody );
178
179 lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize );
180
181 lpMsgBody = (LPDPMSG_REQUESTNEWPLAYERID)( (BYTE*)lpMsg +
182 This->dp2->spData.dwSPHeaderSize );
183
184 /* Compose dplay message envelope */
185 lpMsgBody->envelope.dwMagic = DPMSGMAGIC_DPLAYMSG;
186 lpMsgBody->envelope.wCommandId = DPMSGCMD_REQUESTNEWPLAYERID;
187 lpMsgBody->envelope.wVersion = DPMSGVER_DP6;
188
189 /* Compose the body of the message */
190 lpMsgBody->dwFlags = dwFlags;
191
192 /* Send the message */
193 {
194 DPSP_SENDDATA data;
195
196 data.dwFlags = DPSEND_GUARANTEED;
197 data.idPlayerTo = 0; /* Name server */
198 data.idPlayerFrom = 0; /* Sending from DP */
199 data.lpMessage = lpMsg;
200 data.dwMessageSize = dwMsgSize;
201 data.bSystemMessage = TRUE; /* Allow reply to be sent */
202 data.lpISP = This->dp2->spData.lpISP;
203
204 TRACE( "Asking for player id w/ dwFlags 0x%08x\n",
205 lpMsgBody->dwFlags );
206
207 DP_MSG_ExpectReply( This, &data, DPMSG_DEFAULT_WAIT_TIME, DPMSGCMD_NEWPLAYERIDREPLY,
208 &lpMsg, &dwMsgSize );
209 }
210
211 /* Need to examine the data and extract the new player id */
212 if( SUCCEEDED(hr) )
213 {
214 LPCDPMSG_NEWPLAYERIDREPLY lpcReply;
215
216 lpcReply = lpMsg;
217
218 *lpdpidAllocatedId = lpcReply->dpidNewPlayerId;
219
220 TRACE( "Received reply for id = 0x%08x\n", lpcReply->dpidNewPlayerId );
221
222 /* FIXME: I think that the rest of the message has something to do
223 * with remote data for the player that perhaps I need to setup.
224 * However, with the information that is passed, all that it could
225 * be used for is a standardized initialization value, which I'm
226 * guessing we can do without. Unless the message content is the same
227 * for several different messages?
228 */
229
230 HeapFree( GetProcessHeap(), 0, lpMsg );
231 }
232
233 return hr;
234 }
235
236 HRESULT DP_MSG_ForwardPlayerCreation( IDirectPlayImpl *This, DPID dpidServer )
237 {
238 LPVOID lpMsg;
239 LPDPMSG_FORWARDADDPLAYER lpMsgBody;
240 DWORD dwMsgSize;
241 HRESULT hr = DP_OK;
242
243 dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody );
244
245 lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize );
246
247 lpMsgBody = (LPDPMSG_FORWARDADDPLAYER)( (BYTE*)lpMsg +
248 This->dp2->spData.dwSPHeaderSize );
249
250 /* Compose dplay message envelope */
251 lpMsgBody->envelope.dwMagic = DPMSGMAGIC_DPLAYMSG;
252 lpMsgBody->envelope.wCommandId = DPMSGCMD_FORWARDADDPLAYER;
253 lpMsgBody->envelope.wVersion = DPMSGVER_DP6;
254
255 #if 0
256 {
257 LPBYTE lpPData;
258 DWORD dwDataSize;
259
260 /* SP Player remote data needs to be propagated at some point - is this the point? */
261 IDirectPlaySP_GetSPPlayerData( This->dp2->spData.lpISP, 0, &lpPData, &dwDataSize, DPSET_REMOTE );
262
263 ERR( "Player Data size is 0x%08lx\n"
264 "[%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x]\n"
265 "[%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x]\n",
266
267 dwDataSize,
268 lpPData[0], lpPData[1], lpPData[2], lpPData[3], lpPData[4],
269 lpPData[5], lpPData[6], lpPData[7], lpPData[8], lpPData[9],
270 lpPData[10], lpPData[11], lpPData[12], lpPData[13], lpPData[14],
271 lpPData[15], lpPData[16], lpPData[17], lpPData[18], lpPData[19],
272 lpPData[20], lpPData[21], lpPData[22], lpPData[23], lpPData[24],
273 lpPData[25], lpPData[26], lpPData[27], lpPData[28], lpPData[29],
274 lpPData[30], lpPData[31]
275 );
276 DebugBreak();
277 }
278 #endif
279
280 /* Compose body of message */
281 lpMsgBody->dpidAppServer = dpidServer;
282 lpMsgBody->unknown2[0] = 0x0;
283 lpMsgBody->unknown2[1] = 0x1c;
284 lpMsgBody->unknown2[2] = 0x6c;
285 lpMsgBody->unknown2[3] = 0x50;
286 lpMsgBody->unknown2[4] = 0x9;
287
288 lpMsgBody->dpidAppServer2 = dpidServer;
289 lpMsgBody->unknown3[0] = 0x0;
290 lpMsgBody->unknown3[0] = 0x0;
291 lpMsgBody->unknown3[0] = 0x20;
292 lpMsgBody->unknown3[0] = 0x0;
293 lpMsgBody->unknown3[0] = 0x0;
294
295 lpMsgBody->dpidAppServer3 = dpidServer;
296 lpMsgBody->unknown4[0] = 0x30;
297 lpMsgBody->unknown4[1] = 0xb;
298 lpMsgBody->unknown4[2] = 0x0;
299
300 lpMsgBody->unknown4[3] = NS_GetNsMagic( This->dp2->lpNameServerData ) -
301 0x02000000;
302 TRACE( "Setting first magic to 0x%08x\n", lpMsgBody->unknown4[3] );
303
304 lpMsgBody->unknown4[4] = 0x0;
305 lpMsgBody->unknown4[5] = 0x0;
306 lpMsgBody->unknown4[6] = 0x0;
307
308 lpMsgBody->unknown4[7] = NS_GetNsMagic( This->dp2->lpNameServerData );
309 TRACE( "Setting second magic to 0x%08x\n", lpMsgBody->unknown4[7] );
310
311 lpMsgBody->unknown4[8] = 0x0;
312 lpMsgBody->unknown4[9] = 0x0;
313 lpMsgBody->unknown4[10] = 0x0;
314 lpMsgBody->unknown4[11] = 0x0;
315
316 lpMsgBody->unknown5[0] = 0x0;
317 lpMsgBody->unknown5[1] = 0x0;
318
319 /* Send the message */
320 {
321 DPSP_SENDDATA data;
322
323 data.dwFlags = DPSEND_GUARANTEED;
324 data.idPlayerTo = 0; /* Name server */
325 data.idPlayerFrom = dpidServer; /* Sending from session server */
326 data.lpMessage = lpMsg;
327 data.dwMessageSize = dwMsgSize;
328 data.bSystemMessage = TRUE; /* Allow reply to be sent */
329 data.lpISP = This->dp2->spData.lpISP;
330
331 TRACE( "Sending forward player request with 0x%08x\n", dpidServer );
332
333 lpMsg = DP_MSG_ExpectReply( This, &data,
334 DPMSG_WAIT_60_SECS,
335 DPMSGCMD_GETNAMETABLEREPLY,
336 &lpMsg, &dwMsgSize );
337 }
338
339 /* Need to examine the data and extract the new player id */
340 if( lpMsg != NULL )
341 {
342 FIXME( "Name Table reply received: stub\n" );
343 }
344
345 return hr;
346 }
347
348 /* Queue up a structure indicating that we want a reply of type wReplyCommandId. DPlay does
349 * not seem to offer any way of uniquely differentiating between replies of the same type
350 * relative to the request sent. There is an implicit assumption that there will be no
351 * ordering issues on sends and receives from the opposite machine. No wonder MS is not
352 * a networking company.
353 */
354 static void *DP_MSG_ExpectReply( IDirectPlayImpl *This, DPSP_SENDDATA *lpData, DWORD dwWaitTime,
355 WORD wReplyCommandId, void **lplpReplyMsg, DWORD *lpdwMsgBodySize )
356 {
357 HRESULT hr;
358 HANDLE hMsgReceipt;
359 DP_MSG_REPLY_STRUCT_LIST replyStructList;
360 DWORD dwWaitReturn;
361
362 /* Setup for receipt */
363 hMsgReceipt = DP_MSG_BuildAndLinkReplyStruct( This, &replyStructList,
364 wReplyCommandId );
365
366 TRACE( "Sending msg and expecting cmd %u in reply within %u ticks\n",
367 wReplyCommandId, dwWaitTime );
368 hr = (*This->dp2->spData.lpCB->Send)( lpData );
369
370 if( FAILED(hr) )
371 {
372 ERR( "Send failed: %s\n", DPLAYX_HresultToString( hr ) );
373 return NULL;
374 }
375
376 /* The reply message will trigger the hMsgReceipt event effectively switching
377 * control back to this thread. See DP_MSG_ReplyReceived.
378 */
379 dwWaitReturn = WaitForSingleObject( hMsgReceipt, dwWaitTime );
380 if( dwWaitReturn != WAIT_OBJECT_0 )
381 {
382 ERR( "Wait failed 0x%08x\n", dwWaitReturn );
383 return NULL;
384 }
385
386 /* Clean Up */
387 return DP_MSG_CleanReplyStruct( &replyStructList, lplpReplyMsg, lpdwMsgBodySize );
388 }
389
390 /* Determine if there is a matching request for this incoming message and then copy
391 * all important data. It is quite silly to have to copy the message, but the documents
392 * indicate that a copy is taken. Silly really.
393 */
394 void DP_MSG_ReplyReceived( IDirectPlayImpl *This, WORD wCommandId, const void *lpcMsgBody,
395 DWORD dwMsgBodySize )
396 {
397 LPDP_MSG_REPLY_STRUCT_LIST lpReplyList;
398
399 #if 0
400 if( wCommandId == DPMSGCMD_FORWARDADDPLAYER )
401 {
402 DebugBreak();
403 }
404 #endif
405
406 /* Find, and immediately remove (to avoid double triggering), the appropriate entry. Call locked to
407 * avoid problems.
408 */
409 EnterCriticalSection( &This->lock );
410 DPQ_REMOVE_ENTRY( This->dp2->repliesExpected, repliesExpected, replyExpected.wExpectedReply,
411 ==, wCommandId, lpReplyList );
412 LeaveCriticalSection( &This->lock );
413
414 if( lpReplyList != NULL )
415 {
416 lpReplyList->replyExpected.dwMsgBodySize = dwMsgBodySize;
417 lpReplyList->replyExpected.lpReplyMsg = HeapAlloc( GetProcessHeap(),
418 HEAP_ZERO_MEMORY,
419 dwMsgBodySize );
420 CopyMemory( lpReplyList->replyExpected.lpReplyMsg,
421 lpcMsgBody, dwMsgBodySize );
422
423 /* Signal the thread which sent the message that it has a reply */
424 SetEvent( lpReplyList->replyExpected.hReceipt );
425 }
426 else
427 {
428 ERR( "No receipt event set - only expecting in reply mode\n" );
429 DebugBreak();
430 }
431 }
432
433 void DP_MSG_ToSelf( IDirectPlayImpl *This, DPID dpidSelf )
434 {
435 LPVOID lpMsg;
436 LPDPMSG_SENDENVELOPE lpMsgBody;
437 DWORD dwMsgSize;
438
439 dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody );
440
441 lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize );
442
443 lpMsgBody = (LPDPMSG_SENDENVELOPE)( (BYTE*)lpMsg +
444 This->dp2->spData.dwSPHeaderSize );
445
446 /* Compose dplay message envelope */
447 lpMsgBody->dwMagic = DPMSGMAGIC_DPLAYMSG;
448 lpMsgBody->wCommandId = DPMSGCMD_JUSTENVELOPE;
449 lpMsgBody->wVersion = DPMSGVER_DP6;
450
451 /* Send the message to ourselves */
452 {
453 DPSP_SENDDATA data;
454
455 data.dwFlags = 0;
456 data.idPlayerTo = dpidSelf; /* Sending to session server */
457 data.idPlayerFrom = 0; /* Sending from session server */
458 data.lpMessage = lpMsg;
459 data.dwMessageSize = dwMsgSize;
460 data.bSystemMessage = TRUE; /* Allow reply to be sent */
461 data.lpISP = This->dp2->spData.lpISP;
462
463 lpMsg = DP_MSG_ExpectReply( This, &data,
464 DPMSG_WAIT_5_SECS,
465 DPMSGCMD_JUSTENVELOPE,
466 &lpMsg, &dwMsgSize );
467 }
468 }
469
470 void DP_MSG_ErrorReceived( IDirectPlayImpl *This, WORD wCommandId, const void *lpMsgBody,
471 DWORD dwMsgBodySize )
472 {
473 LPCDPMSG_FORWARDADDPLAYERNACK lpcErrorMsg;
474
475 lpcErrorMsg = lpMsgBody;
476
477 ERR( "Received error message %u. Error is %s\n",
478 wCommandId, DPLAYX_HresultToString( lpcErrorMsg->errorCode) );
479 DebugBreak();
480 }