- Add a lot of traces to make debugging a bit easier.
[wine] / dlls / 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       = CreateEventA( 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   lpMsgBody->unknown4[12] = 0x0;
345
346   lpMsgBody->unknown5[0] = 0x0;
347   lpMsgBody->unknown5[1] = 0x0;
348
349   /* Send the message */
350   {
351     DPSP_SENDDATA data;
352
353     data.dwFlags        = DPSEND_GUARANTEED;
354     data.idPlayerTo     = 0; /* Name server */
355     data.idPlayerFrom   = dpidServer; /* Sending from session server */
356     data.lpMessage      = lpMsg;
357     data.dwMessageSize  = dwMsgSize;
358     data.bSystemMessage = TRUE; /* Allow reply to be sent */
359     data.lpISP          = This->dp2->spData.lpISP;
360
361     TRACE( "Sending forward player request with 0x%08lx\n", dpidServer );
362
363     lpMsg = DP_MSG_ExpectReply( This, &data,
364                                 DPMSG_WAIT_60_SECS,
365                                 DPMSGCMD_GETNAMETABLEREPLY,
366                                 &lpMsg, &dwMsgSize );
367   }
368
369   /* Need to examine the data and extract the new player id */
370   if( lpMsg != NULL )
371   {
372     FIXME( "Name Table reply received: stub\n" );
373   }
374
375   return hr;
376 }
377
378 /* Queue up a structure indicating that we want a reply of type wReplyCommandId. DPlay does
379  * not seem to offer any way of uniquely differentiating between replies of the same type
380  * relative to the request sent. There is an implicit assumption that there will be no
381  * ordering issues on sends and receives from the opposite machine. No wonder MS is not
382  * a networking company.
383  */
384 static
385 LPVOID DP_MSG_ExpectReply( IDirectPlay2AImpl* This, LPDPSP_SENDDATA lpData,
386                            DWORD dwWaitTime, WORD wReplyCommandId,
387                            LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize )
388 {
389   HRESULT                  hr;
390   HANDLE                   hMsgReceipt;
391   DP_MSG_REPLY_STRUCT_LIST replyStructList;
392   DWORD                    dwWaitReturn;
393
394   /* Setup for receipt */
395   hMsgReceipt = DP_MSG_BuildAndLinkReplyStruct( This, &replyStructList,
396                                                 wReplyCommandId );
397
398   TRACE( "Sending msg and expecting cmd %u in reply within %lu ticks\n",
399          wReplyCommandId, dwWaitTime );
400   hr = (*This->dp2->spData.lpCB->Send)( lpData );
401
402   if( FAILED(hr) )
403   {
404     ERR( "Send failed: %s\n", DPLAYX_HresultToString( hr ) );
405     return NULL;
406   }
407
408   /* The reply message will trigger the hMsgReceipt event effectively switching
409    * control back to this thread. See DP_MSG_ReplyReceived.
410    */
411   dwWaitReturn = WaitForSingleObject( hMsgReceipt, dwWaitTime );
412   if( dwWaitReturn != WAIT_OBJECT_0 )
413   {
414     ERR( "Wait failed 0x%08lx\n", dwWaitReturn );
415     return NULL;
416   }
417
418   /* Clean Up */
419   return DP_MSG_CleanReplyStruct( &replyStructList, lplpReplyMsg, lpdwMsgBodySize );
420 }
421
422 /* Determine if there is a matching request for this incoming message and then copy
423  * all important data. It is quite silly to have to copy the message, but the documents
424  * indicate that a copy is taken. Silly really.
425  */
426 void DP_MSG_ReplyReceived( IDirectPlay2AImpl* This, WORD wCommandId,
427                            LPCVOID lpcMsgBody, DWORD dwMsgBodySize )
428 {
429   LPDP_MSG_REPLY_STRUCT_LIST lpReplyList;
430
431 #if 0
432   if( wCommandId == DPMSGCMD_FORWARDADDPLAYER )
433   {
434     DebugBreak();
435   }
436 #endif
437
438   /* Find, and immediately remove (to avoid double triggering), the appropriate entry. Call locked to
439    * avoid problems.
440    */
441   EnterCriticalSection( &This->unk->DP_lock );
442     DPQ_REMOVE_ENTRY( This->dp2->replysExpected, replysExpected, replyExpected.wExpectedReply,\
443                      ==, wCommandId, lpReplyList );
444   LeaveCriticalSection( &This->unk->DP_lock );
445
446   if( lpReplyList != NULL )
447   {
448     lpReplyList->replyExpected.dwMsgBodySize = dwMsgBodySize;
449     lpReplyList->replyExpected.lpReplyMsg = HeapAlloc( GetProcessHeap(),
450                                                        HEAP_ZERO_MEMORY,
451                                                        dwMsgBodySize );
452     CopyMemory( lpReplyList->replyExpected.lpReplyMsg,
453                 lpcMsgBody, dwMsgBodySize );
454
455     /* Signal the thread which sent the message that it has a reply */
456     SetEvent( lpReplyList->replyExpected.hReceipt );
457   }
458   else
459   {
460     ERR( "No receipt event set - only expecting in reply mode\n" );
461     DebugBreak();
462   }
463 }
464
465 void DP_MSG_ToSelf( IDirectPlay2AImpl* This, DPID dpidSelf )
466 {
467   LPVOID                   lpMsg;
468   LPDPMSG_SENDENVELOPE     lpMsgBody;
469   DWORD                    dwMsgSize;
470
471   dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody );
472
473   lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize );
474
475   lpMsgBody = (LPDPMSG_SENDENVELOPE)( (BYTE*)lpMsg +
476                                       This->dp2->spData.dwSPHeaderSize );
477
478   /* Compose dplay message envelope */
479   lpMsgBody->dwMagic    = DPMSGMAGIC_DPLAYMSG;
480   lpMsgBody->wCommandId = DPMSGCMD_JUSTENVELOPE;
481   lpMsgBody->wVersion   = DPMSGVER_DP6;
482
483   /* Send the message to ourselves */
484   {
485     DPSP_SENDDATA data;
486
487     data.dwFlags        = 0;
488     data.idPlayerTo     = dpidSelf; /* Sending to session server */
489     data.idPlayerFrom   = 0; /* Sending from session server */
490     data.lpMessage      = lpMsg;
491     data.dwMessageSize  = dwMsgSize;
492     data.bSystemMessage = TRUE; /* Allow reply to be sent */
493     data.lpISP          = This->dp2->spData.lpISP;
494
495     lpMsg = DP_MSG_ExpectReply( This, &data,
496                                 DPMSG_WAIT_5_SECS,
497                                 DPMSGCMD_JUSTENVELOPE,
498                                 &lpMsg, &dwMsgSize );
499   }
500 }
501
502 void DP_MSG_ErrorReceived( IDirectPlay2AImpl* This, WORD wCommandId,
503                            LPCVOID lpMsgBody, DWORD dwMsgBodySize )
504 {
505   LPCDPMSG_FORWARDADDPLAYERNACK lpcErrorMsg;
506
507   lpcErrorMsg = (LPCDPMSG_FORWARDADDPLAYERNACK)lpMsgBody;
508
509   ERR( "Received error message %u. Error is %s\n",
510        wCommandId, DPLAYX_HresultToString( lpcErrorMsg->errorCode) );
511   DebugBreak();
512 }