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