Get rid of the vararg and generic stuff.
[wine] / dlls / dplayx / dplayx_messages.c
1 /* DirectPlay & DirectPlayLobby messaging implementation
2  *
3  * Copyright 2000,2001 - Peter Hunnisett
4  *
5  * <presently under construction - contact hunnise@nortelnetworks.com>
6  *
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.
11  *
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.
16  *
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
20  */
21
22 #include <string.h>
23 #include "winbase.h"
24 #include "wine/debug.h"
25
26 #include "wingdi.h"
27 #include "winuser.h"
28 #include "winerror.h"
29
30 #include "dplayx_messages.h"
31 #include "dplay_global.h"
32 #include "dplayx_global.h"
33 #include "name_server.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(dplay);
36
37 typedef struct tagMSGTHREADINFO
38 {
39   HANDLE hStart;
40   HANDLE hDeath;
41   HANDLE hSettingRead;
42   HANDLE hNotifyEvent;
43 } MSGTHREADINFO, *LPMSGTHREADINFO;
44
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 );
49
50
51 /* Create the message reception thread to allow the application to receive
52  * asynchronous message reception
53  */
54 DWORD CreateLobbyMessageReceptionThread( HANDLE hNotifyEvent, HANDLE hStart,
55                                          HANDLE hDeath, HANDLE hConnRead )
56 {
57   DWORD           dwMsgThreadId;
58   LPMSGTHREADINFO lpThreadInfo;
59
60   lpThreadInfo = HeapAlloc( GetProcessHeap(), 0, sizeof( *lpThreadInfo ) );
61   if( lpThreadInfo == NULL )
62   {
63     return 0;
64   }
65
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 ) )
71   {
72     ERR( "Unable to duplicate event handle\n" );
73     goto error;
74   }
75
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
79    */
80   lpThreadInfo->hStart       = hStart;
81   lpThreadInfo->hDeath       = hDeath;
82   lpThreadInfo->hSettingRead = hConnRead;
83
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 */
90                    )
91     )
92   {
93     ERR( "Unable to create msg thread\n" );
94     goto error;
95   }
96
97   /* FIXME: Should I be closing the handle to the thread or does that
98             terminate the thread? */
99
100   return dwMsgThreadId;
101
102 error:
103
104   HeapFree( GetProcessHeap(), 0, lpThreadInfo );
105
106   return 0;
107 }
108
109 static DWORD CALLBACK DPL_MSG_ThreadMain( LPVOID lpContext )
110 {
111   LPMSGTHREADINFO lpThreadInfo = (LPMSGTHREADINFO)lpContext;
112   DWORD dwWaitResult;
113
114   TRACE( "Msg thread created. Waiting on app startup\n" );
115
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 )
119   {
120     FIXME( "Should signal app/wait creation failure (0x%08lx)\n", dwWaitResult );
121     goto end_of_thread;
122   }
123
124   /* Close this handle as it's not needed anymore */
125   CloseHandle( lpThreadInfo->hStart );
126   lpThreadInfo->hStart = 0;
127
128   /* Wait until the lobby knows what it is */
129   dwWaitResult = WaitForSingleObject( lpThreadInfo->hSettingRead, INFINITE );
130   if( dwWaitResult == WAIT_TIMEOUT )
131   {
132     ERR( "App Read connection setting timeout fail (0x%08lx)\n", dwWaitResult );
133   }
134
135   /* Close this handle as it's not needed anymore */
136   CloseHandle( lpThreadInfo->hSettingRead );
137   lpThreadInfo->hSettingRead = 0;
138
139   TRACE( "App created && intialized starting main message reception loop\n" );
140
141   for ( ;; )
142   {
143     MSG lobbyMsg;
144     GetMessageW( &lobbyMsg, 0, 0, 0 );
145   }
146
147 end_of_thread:
148   TRACE( "Msg thread exiting!\n" );
149   HeapFree( GetProcessHeap(), 0, lpThreadInfo );
150
151   return 0;
152 }
153
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 );
160
161
162 static
163 HANDLE DP_MSG_BuildAndLinkReplyStruct( IDirectPlay2Impl* This,
164                                        LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList, WORD wReplyCommandId )
165 {
166   lpReplyStructList->replyExpected.hReceipt       = CreateEventA( NULL, FALSE, FALSE, NULL );
167   lpReplyStructList->replyExpected.wExpectedReply = wReplyCommandId;
168   lpReplyStructList->replyExpected.lpReplyMsg     = NULL;
169   lpReplyStructList->replyExpected.dwMsgBodySize  = 0;
170
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 );
175
176   return lpReplyStructList->replyExpected.hReceipt;
177 }
178
179 static
180 LPVOID DP_MSG_CleanReplyStruct( LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList,
181                                 LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize  )
182 {
183   CloseHandle( lpReplyStructList->replyExpected.hReceipt );
184
185   *lplpReplyMsg    = lpReplyStructList->replyExpected.lpReplyMsg;
186   *lpdwMsgBodySize = lpReplyStructList->replyExpected.dwMsgBodySize;
187
188   return lpReplyStructList->replyExpected.lpReplyMsg;
189 }
190
191 HRESULT DP_MSG_SendRequestPlayerId( IDirectPlay2AImpl* This, DWORD dwFlags,
192                                     LPDPID lpdpidAllocatedId )
193 {
194   LPVOID                     lpMsg;
195   LPDPMSG_REQUESTNEWPLAYERID lpMsgBody;
196   DWORD                      dwMsgSize;
197   HRESULT                    hr = DP_OK;
198
199   dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody );
200
201   lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize );
202
203   lpMsgBody = (LPDPMSG_REQUESTNEWPLAYERID)( (BYTE*)lpMsg +
204                                              This->dp2->spData.dwSPHeaderSize );
205
206   /* Compose dplay message envelope */
207   lpMsgBody->envelope.dwMagic    = DPMSGMAGIC_DPLAYMSG;
208   lpMsgBody->envelope.wCommandId = DPMSGCMD_REQUESTNEWPLAYERID;
209   lpMsgBody->envelope.wVersion   = DPMSGVER_DP6;
210
211   /* Compose the body of the message */
212   lpMsgBody->dwFlags = dwFlags;
213
214   /* Send the message */
215   {
216     DPSP_SENDDATA data;
217
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;
225
226     TRACE( "Asking for player id w/ dwFlags 0x%08lx\n",
227            lpMsgBody->dwFlags );
228
229     DP_MSG_ExpectReply( This, &data, DPMSG_DEFAULT_WAIT_TIME, DPMSGCMD_NEWPLAYERIDREPLY,
230                         &lpMsg, &dwMsgSize );
231   }
232
233   /* Need to examine the data and extract the new player id */
234   if( !FAILED(hr) )
235   {
236     LPCDPMSG_NEWPLAYERIDREPLY lpcReply;
237
238     lpcReply = (LPCDPMSG_NEWPLAYERIDREPLY)lpMsg;
239
240     *lpdpidAllocatedId = lpcReply->dpidNewPlayerId;
241
242     TRACE( "Received reply for id = 0x%08lx\n", lpcReply->dpidNewPlayerId );
243
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?
250      */
251
252     HeapFree( GetProcessHeap(), 0, lpMsg );
253   }
254
255   return hr;
256 }
257
258 HRESULT DP_MSG_ForwardPlayerCreation( IDirectPlay2AImpl* This, DPID dpidServer )
259 {
260   LPVOID                   lpMsg;
261   LPDPMSG_FORWARDADDPLAYER lpMsgBody;
262   DWORD                    dwMsgSize;
263   HRESULT                  hr = DP_OK;
264
265   dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody );
266
267   lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize );
268
269   lpMsgBody = (LPDPMSG_FORWARDADDPLAYER)( (BYTE*)lpMsg +
270                                           This->dp2->spData.dwSPHeaderSize );
271
272   /* Compose dplay message envelope */
273   lpMsgBody->envelope.dwMagic    = DPMSGMAGIC_DPLAYMSG;
274   lpMsgBody->envelope.wCommandId = DPMSGCMD_FORWARDADDPLAYER;
275   lpMsgBody->envelope.wVersion   = DPMSGVER_DP6;
276
277 #if 0
278   {
279     LPBYTE lpPData;
280     DWORD  dwDataSize;
281
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 );
284
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",
288
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]
297         );
298     DebugBreak();
299   }
300 #endif
301
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;
309
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;
316
317   lpMsgBody->dpidAppServer3 = dpidServer;
318   lpMsgBody->unknown4[0] =  0x30;
319   lpMsgBody->unknown4[1] =  0xb;
320   lpMsgBody->unknown4[2] =  0x0;
321
322   lpMsgBody->unknown4[3] =  NS_GetNsMagic( This->dp2->lpNameServerData ) -
323                             0x02000000;
324   TRACE( "Setting first magic to 0x%08lx\n", lpMsgBody->unknown4[3] );
325
326   lpMsgBody->unknown4[4] =  0x0;
327   lpMsgBody->unknown4[5] =  0x0;
328   lpMsgBody->unknown4[6] =  0x0;
329
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] );
336
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;
342
343   lpMsgBody->unknown5[0] = 0x0;
344   lpMsgBody->unknown5[1] = 0x0;
345
346   /* Send the message */
347   {
348     DPSP_SENDDATA data;
349
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;
357
358     TRACE( "Sending forward player request with 0x%08lx\n", dpidServer );
359
360     lpMsg = DP_MSG_ExpectReply( This, &data,
361                                 DPMSG_WAIT_60_SECS,
362                                 DPMSGCMD_GETNAMETABLEREPLY,
363                                 &lpMsg, &dwMsgSize );
364   }
365
366   /* Need to examine the data and extract the new player id */
367   if( lpMsg != NULL )
368   {
369     FIXME( "Name Table reply received: stub\n" );
370   }
371
372   return hr;
373 }
374
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.
380  */
381 static
382 LPVOID DP_MSG_ExpectReply( IDirectPlay2AImpl* This, LPDPSP_SENDDATA lpData,
383                            DWORD dwWaitTime, WORD wReplyCommandId,
384                            LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize )
385 {
386   HRESULT                  hr;
387   HANDLE                   hMsgReceipt;
388   DP_MSG_REPLY_STRUCT_LIST replyStructList;
389   DWORD                    dwWaitReturn;
390
391   /* Setup for receipt */
392   hMsgReceipt = DP_MSG_BuildAndLinkReplyStruct( This, &replyStructList,
393                                                 wReplyCommandId );
394
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 );
398
399   if( FAILED(hr) )
400   {
401     ERR( "Send failed: %s\n", DPLAYX_HresultToString( hr ) );
402     return NULL;
403   }
404
405   /* The reply message will trigger the hMsgReceipt event effectively switching
406    * control back to this thread. See DP_MSG_ReplyReceived.
407    */
408   dwWaitReturn = WaitForSingleObject( hMsgReceipt, dwWaitTime );
409   if( dwWaitReturn != WAIT_OBJECT_0 )
410   {
411     ERR( "Wait failed 0x%08lx\n", dwWaitReturn );
412     return NULL;
413   }
414
415   /* Clean Up */
416   return DP_MSG_CleanReplyStruct( &replyStructList, lplpReplyMsg, lpdwMsgBodySize );
417 }
418
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.
422  */
423 void DP_MSG_ReplyReceived( IDirectPlay2AImpl* This, WORD wCommandId,
424                            LPCVOID lpcMsgBody, DWORD dwMsgBodySize )
425 {
426   LPDP_MSG_REPLY_STRUCT_LIST lpReplyList;
427
428 #if 0
429   if( wCommandId == DPMSGCMD_FORWARDADDPLAYER )
430   {
431     DebugBreak();
432   }
433 #endif
434
435   /* Find, and immediately remove (to avoid double triggering), the appropriate entry. Call locked to
436    * avoid problems.
437    */
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 );
442
443   if( lpReplyList != NULL )
444   {
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 );
451
452     /* Signal the thread which sent the message that it has a reply */
453     SetEvent( lpReplyList->replyExpected.hReceipt );
454   }
455   else
456   {
457     ERR( "No receipt event set - only expecting in reply mode\n" );
458     DebugBreak();
459   }
460 }
461
462 void DP_MSG_ToSelf( IDirectPlay2AImpl* This, DPID dpidSelf )
463 {
464   LPVOID                   lpMsg;
465   LPDPMSG_SENDENVELOPE     lpMsgBody;
466   DWORD                    dwMsgSize;
467
468   dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody );
469
470   lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize );
471
472   lpMsgBody = (LPDPMSG_SENDENVELOPE)( (BYTE*)lpMsg +
473                                       This->dp2->spData.dwSPHeaderSize );
474
475   /* Compose dplay message envelope */
476   lpMsgBody->dwMagic    = DPMSGMAGIC_DPLAYMSG;
477   lpMsgBody->wCommandId = DPMSGCMD_JUSTENVELOPE;
478   lpMsgBody->wVersion   = DPMSGVER_DP6;
479
480   /* Send the message to ourselves */
481   {
482     DPSP_SENDDATA data;
483
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;
491
492     lpMsg = DP_MSG_ExpectReply( This, &data,
493                                 DPMSG_WAIT_5_SECS,
494                                 DPMSGCMD_JUSTENVELOPE,
495                                 &lpMsg, &dwMsgSize );
496   }
497 }
498
499 void DP_MSG_ErrorReceived( IDirectPlay2AImpl* This, WORD wCommandId,
500                            LPCVOID lpMsgBody, DWORD dwMsgBodySize )
501 {
502   LPCDPMSG_FORWARDADDPLAYERNACK lpcErrorMsg;
503
504   lpcErrorMsg = (LPCDPMSG_FORWARDADDPLAYERNACK)lpMsgBody;
505
506   ERR( "Received error message %u. Error is %s\n",
507        wCommandId, DPLAYX_HresultToString( lpcErrorMsg->errorCode) );
508   DebugBreak();
509 }
510