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