Implement multiline labels in the Large Item view by using DrawText
[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  */
8
9 #include <string.h>
10 #include "winbase.h"
11 #include "debugtools.h"
12
13 #include "wingdi.h"
14 #include "winuser.h"
15 #include "winerror.h"
16
17 #include "dplayx_messages.h"
18 #include "dplay_global.h"
19 #include "dplayx_global.h"
20 #include "name_server.h"
21
22 DEFAULT_DEBUG_CHANNEL(dplay);
23
24 typedef struct tagMSGTHREADINFO
25 {
26   HANDLE hStart;
27   HANDLE hDeath; 
28   HANDLE hSettingRead;
29   HANDLE hNotifyEvent; 
30 } MSGTHREADINFO, *LPMSGTHREADINFO;
31
32 static DWORD CALLBACK DPL_MSG_ThreadMain( LPVOID lpContext );
33 static LPVOID DP_MSG_ExpectReply( IDirectPlay2AImpl* This, LPDPSP_SENDDATA data, 
34                                   DWORD dwWaitTime, WORD wReplyCommandId,
35                                   LPVOID* lplpReplyMsg, LPDWORD 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
47   lpThreadInfo = HeapAlloc( GetProcessHeap(), 0, sizeof( *lpThreadInfo ) );
48   if( lpThreadInfo == NULL )
49   {
50     return 0;
51   }
52
53   /* The notify event may or may not exist. Depends if async comm or not */
54   if( hNotifyEvent &&
55       !DuplicateHandle( GetCurrentProcess(), hNotifyEvent, 
56                         GetCurrentProcess(), &lpThreadInfo->hNotifyEvent, 
57                         0, FALSE, DUPLICATE_SAME_ACCESS ) )
58   {
59     ERR( "Unable to duplicate event handle\n" );
60     goto error;
61   }
62
63   /* These 3 handles don't need to be duplicated because we don't keep a
64    * reference to them where they're created. They're created specifically
65    * for the message thread 
66    */
67   lpThreadInfo->hStart       = hStart;
68   lpThreadInfo->hDeath       = hDeath;
69   lpThreadInfo->hSettingRead = hConnRead;
70
71   if( !CreateThread( NULL,                  /* Security attribs */
72                      0,                     /* Stack */ 
73                      DPL_MSG_ThreadMain,    /* Msg reception function */
74                      lpThreadInfo,          /* Msg reception func parameter */
75                      0,                     /* Flags */
76                      &dwMsgThreadId         /* Updated with thread id */
77                    )
78     )
79   {
80     ERR( "Unable to create msg thread\n" );
81     goto error;
82   }
83
84   /* FIXME: Should I be closing the handle to the thread or does that
85             terminate the thread? */
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 = (LPMSGTHREADINFO)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%08lx)\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%08lx)\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 && intialized 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 messageing stuff */
142 static HANDLE DP_MSG_BuildAndLinkReplyStruct( IDirectPlay2Impl* This,
143                                               LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList, 
144                                               WORD wReplyCommandId );
145 static LPVOID DP_MSG_CleanReplyStruct( LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList,
146                                        LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize );
147
148
149 static
150 HANDLE DP_MSG_BuildAndLinkReplyStruct( IDirectPlay2Impl* This,
151                                        LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList, WORD wReplyCommandId )
152 {
153   lpReplyStructList->replyExpected.hReceipt       = CreateEventA( NULL, FALSE, FALSE, NULL );
154   lpReplyStructList->replyExpected.wExpectedReply = wReplyCommandId;
155   lpReplyStructList->replyExpected.lpReplyMsg     = NULL;
156   lpReplyStructList->replyExpected.dwMsgBodySize  = 0;
157
158   /* Insert into the message queue while locked */
159   EnterCriticalSection( &This->unk->DP_lock );
160     DPQ_INSERT( This->dp2->replysExpected, lpReplyStructList, replysExpected );
161   LeaveCriticalSection( &This->unk->DP_lock );
162
163   return lpReplyStructList->replyExpected.hReceipt;
164 }
165
166 static
167 LPVOID DP_MSG_CleanReplyStruct( LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList,
168                                 LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize  )
169 {
170   CloseHandle( lpReplyStructList->replyExpected.hReceipt );
171
172   *lplpReplyMsg    = lpReplyStructList->replyExpected.lpReplyMsg;
173   *lpdwMsgBodySize = lpReplyStructList->replyExpected.dwMsgBodySize;
174
175   return lpReplyStructList->replyExpected.lpReplyMsg;
176 }
177
178 HRESULT DP_MSG_SendRequestPlayerId( IDirectPlay2AImpl* This, DWORD dwFlags,
179                                     LPDPID lpdpidAllocatedId )
180 {
181   LPVOID                     lpMsg;
182   LPDPMSG_REQUESTNEWPLAYERID lpMsgBody;
183   DWORD                      dwMsgSize;
184   HRESULT                    hr = DP_OK;
185
186   dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody );
187
188   lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize );
189
190   lpMsgBody = (LPDPMSG_REQUESTNEWPLAYERID)( (BYTE*)lpMsg + 
191                                              This->dp2->spData.dwSPHeaderSize );
192
193   /* Compose dplay message envelope */
194   lpMsgBody->envelope.dwMagic    = DPMSGMAGIC_DPLAYMSG;
195   lpMsgBody->envelope.wCommandId = DPMSGCMD_REQUESTNEWPLAYERID;
196   lpMsgBody->envelope.wVersion   = DPMSGVER_DP6;
197
198   /* Compose the body of the message */
199   lpMsgBody->dwFlags = dwFlags;
200
201   /* Send the message */
202   {
203     DPSP_SENDDATA data;
204
205     data.dwFlags        = DPSEND_GUARANTEED;
206     data.idPlayerTo     = 0; /* Name server */
207     data.idPlayerFrom   = 0; /* Sending from DP */
208     data.lpMessage      = lpMsg;
209     data.dwMessageSize  = dwMsgSize;
210     data.bSystemMessage = TRUE; /* Allow reply to be sent */
211     data.lpISP          = This->dp2->spData.lpISP;
212
213     TRACE( "Asking for player id w/ dwFlags 0x%08lx\n", 
214            lpMsgBody->dwFlags );
215
216     DP_MSG_ExpectReply( This, &data, DPMSG_DEFAULT_WAIT_TIME, DPMSGCMD_NEWPLAYERIDREPLY, 
217                         &lpMsg, &dwMsgSize );
218   }
219
220   /* Need to examine the data and extract the new player id */
221   if( !FAILED(hr) )
222   {
223     LPCDPMSG_NEWPLAYERIDREPLY lpcReply; 
224
225     lpcReply = (LPCDPMSG_NEWPLAYERIDREPLY)lpMsg;
226
227     *lpdpidAllocatedId = lpcReply->dpidNewPlayerId;   
228
229     TRACE( "Received reply for id = 0x%08lx\n", lpcReply->dpidNewPlayerId );
230
231     /* FIXME: I think that the rest of the message has something to do
232      *        with remote data for the player that perhaps I need to setup.
233      *        However, with the information that is passed, all that it could
234      *        be used for is a standardized intialization value, which I'm 
235      *        guessing we can do without. Unless the message content is the same
236      *        for several different messages?
237      */
238
239     HeapFree( GetProcessHeap(), 0, lpMsg ); 
240   }
241
242   return hr;
243 }
244
245 HRESULT DP_MSG_ForwardPlayerCreation( IDirectPlay2AImpl* This, DPID dpidServer )
246 {
247   LPVOID                   lpMsg;
248   LPDPMSG_FORWARDADDPLAYER lpMsgBody;
249   DWORD                    dwMsgSize;
250   HRESULT                  hr = DP_OK;
251
252   dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody );
253
254   lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize );
255
256   lpMsgBody = (LPDPMSG_FORWARDADDPLAYER)( (BYTE*)lpMsg +
257                                           This->dp2->spData.dwSPHeaderSize );
258
259   /* Compose dplay message envelope */
260   lpMsgBody->envelope.dwMagic    = DPMSGMAGIC_DPLAYMSG;
261   lpMsgBody->envelope.wCommandId = DPMSGCMD_FORWARDADDPLAYER;
262   lpMsgBody->envelope.wVersion   = DPMSGVER_DP6;
263
264 #if 0
265   {
266     LPBYTE lpPData;
267     DWORD  dwDataSize;
268     
269     /* SP Player remote data needs to be propagated at some point - is this the point? */
270     IDirectPlaySP_GetSPPlayerData( This->dp2->spData.lpISP, 0, (LPVOID*)&lpPData, &dwDataSize, DPSET_REMOTE );
271     
272     ERR( "Player Data size is 0x%08lx\n"
273          "[%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x]\n"
274          "[%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x]\n", 
275  
276          dwDataSize,
277          lpPData[0], lpPData[1], lpPData[2], lpPData[3], lpPData[4],
278          lpPData[5], lpPData[6], lpPData[7], lpPData[8], lpPData[9],
279          lpPData[10], lpPData[11], lpPData[12], lpPData[13], lpPData[14],
280          lpPData[15], lpPData[16], lpPData[17], lpPData[18], lpPData[19],
281          lpPData[20], lpPData[21], lpPData[22], lpPData[23], lpPData[24],
282          lpPData[25], lpPData[26], lpPData[27], lpPData[28], lpPData[29],
283          lpPData[30], lpPData[31]                
284         );
285     DebugBreak();
286   }
287 #endif
288
289   /* Compose body of message */
290   lpMsgBody->dpidAppServer = dpidServer;
291   lpMsgBody->unknown2[0] = 0x0;
292   lpMsgBody->unknown2[1] = 0x1c;
293   lpMsgBody->unknown2[2] = 0x6c;
294   lpMsgBody->unknown2[3] = 0x50;
295   lpMsgBody->unknown2[4] = 0x9;
296
297   lpMsgBody->dpidAppServer2 = dpidServer;
298   lpMsgBody->unknown3[0] = 0x0;
299   lpMsgBody->unknown3[0] = 0x0;
300   lpMsgBody->unknown3[0] = 0x20;
301   lpMsgBody->unknown3[0] = 0x0;
302   lpMsgBody->unknown3[0] = 0x0;
303   
304   lpMsgBody->dpidAppServer3 = dpidServer;
305   lpMsgBody->unknown4[0] =  0x30; 
306   lpMsgBody->unknown4[1] =  0xb;
307   lpMsgBody->unknown4[2] =  0x0;
308
309   lpMsgBody->unknown4[3] =  NS_GetNsMagic( This->dp2->lpNameServerData ) -
310                             0x02000000;
311   TRACE( "Setting first magic to 0x%08lx\n", lpMsgBody->unknown4[3] );
312
313   lpMsgBody->unknown4[4] =  0x0;
314   lpMsgBody->unknown4[5] =  0x0;
315   lpMsgBody->unknown4[6] =  0x0;
316
317 #if 0
318   lpMsgBody->unknown4[7] =  NS_GetOtherMagic( This->dp2->lpNameServerData )
319 #else
320   lpMsgBody->unknown4[7] =  NS_GetNsMagic( This->dp2->lpNameServerData );
321 #endif
322   TRACE( "Setting second magic to 0x%08lx\n", lpMsgBody->unknown4[7] );
323
324   lpMsgBody->unknown4[8] =  0x0;
325   lpMsgBody->unknown4[9] =  0x0;
326   lpMsgBody->unknown4[10] = 0x0;
327   lpMsgBody->unknown4[11] = 0x0;
328   lpMsgBody->unknown4[12] = 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%08lx\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  */
368 static
369 LPVOID DP_MSG_ExpectReply( IDirectPlay2AImpl* This, LPDPSP_SENDDATA lpData, 
370                            DWORD dwWaitTime, WORD wReplyCommandId,
371                            LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize )
372 {
373   HRESULT                  hr;
374   HANDLE                   hMsgReceipt;
375   DP_MSG_REPLY_STRUCT_LIST replyStructList;
376   DWORD                    dwWaitReturn;
377  
378   /* Setup for receipt */
379   hMsgReceipt = DP_MSG_BuildAndLinkReplyStruct( This, &replyStructList, 
380                                                 wReplyCommandId );
381
382   TRACE( "Sending msg and expecting cmd %u in reply within %lu ticks\n", 
383          wReplyCommandId, dwWaitTime );
384   hr = (*This->dp2->spData.lpCB->Send)( lpData );
385
386   if( FAILED(hr) )
387   {
388     ERR( "Send failed: %s\n", DPLAYX_HresultToString( hr ) );
389     return NULL;
390   }
391
392   /* The reply message will trigger the hMsgReceipt event effectively switching
393    * control back to this thread. See DP_MSG_ReplyReceived.
394    */
395   dwWaitReturn = WaitForSingleObject( hMsgReceipt, dwWaitTime );
396   if( dwWaitReturn != WAIT_OBJECT_0 )
397   {
398     ERR( "Wait failed 0x%08lx\n", dwWaitReturn );
399     return NULL;
400   }
401
402   /* Clean Up */
403   return DP_MSG_CleanReplyStruct( &replyStructList, lplpReplyMsg, lpdwMsgBodySize );
404 }
405
406 /* Determine if there is a matching request for this incomming message and then copy
407  * all important data. It is quite silly to have to copy the message, but the documents
408  * indicate that a copy is taken. Silly really.
409  */
410 void DP_MSG_ReplyReceived( IDirectPlay2AImpl* This, WORD wCommandId, 
411                            LPCVOID lpcMsgBody, DWORD dwMsgBodySize )
412 {
413   LPDP_MSG_REPLY_STRUCT_LIST lpReplyList;
414
415 #if 0
416   if( wCommandId == DPMSGCMD_FORWARDADDPLAYER )
417   {
418     DebugBreak();
419   }
420 #endif
421
422   /* Find, and immediately remove (to avoid double triggering), the appropriate entry. Call locked to 
423    * avoid problems.
424    */
425   EnterCriticalSection( &This->unk->DP_lock );
426     DPQ_REMOVE_ENTRY( This->dp2->replysExpected, replysExpected, replyExpected.wExpectedReply,\
427                      ==, wCommandId, lpReplyList );
428   LeaveCriticalSection( &This->unk->DP_lock );  
429
430   if( lpReplyList != NULL )
431   {
432     lpReplyList->replyExpected.dwMsgBodySize = dwMsgBodySize; 
433     lpReplyList->replyExpected.lpReplyMsg = HeapAlloc( GetProcessHeap(),
434                                                        HEAP_ZERO_MEMORY,
435                                                        dwMsgBodySize );
436     CopyMemory( lpReplyList->replyExpected.lpReplyMsg, 
437                 lpcMsgBody, dwMsgBodySize );
438
439     /* Signal the thread which sent the message that it has a reply */
440     SetEvent( lpReplyList->replyExpected.hReceipt );
441   }
442   else
443   {
444     ERR( "No receipt event set - only expecting in reply mode\n" );
445     DebugBreak();
446   }
447 }
448
449 void DP_MSG_ToSelf( IDirectPlay2AImpl* This, DPID dpidSelf )
450 {
451   LPVOID                   lpMsg;
452   LPDPMSG_SENDENVELOPE     lpMsgBody;
453   DWORD                    dwMsgSize;
454
455   dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody );
456
457   lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize );
458
459   lpMsgBody = (LPDPMSG_SENDENVELOPE)( (BYTE*)lpMsg +
460                                       This->dp2->spData.dwSPHeaderSize );
461
462   /* Compose dplay message envelope */
463   lpMsgBody->dwMagic    = DPMSGMAGIC_DPLAYMSG;
464   lpMsgBody->wCommandId = DPMSGCMD_JUSTENVELOPE;
465   lpMsgBody->wVersion   = DPMSGVER_DP6;
466
467   /* Send the message to ourselves */
468   {
469     DPSP_SENDDATA data;
470
471     data.dwFlags        = 0;
472     data.idPlayerTo     = dpidSelf; /* Sending to session server */
473     data.idPlayerFrom   = 0; /* Sending from session server */
474     data.lpMessage      = lpMsg;
475     data.dwMessageSize  = dwMsgSize;
476     data.bSystemMessage = TRUE; /* Allow reply to be sent */
477     data.lpISP          = This->dp2->spData.lpISP;
478
479     lpMsg = DP_MSG_ExpectReply( This, &data,
480                                 DPMSG_WAIT_5_SECS,
481                                 DPMSGCMD_JUSTENVELOPE,
482                                 &lpMsg, &dwMsgSize );
483   }
484 }
485
486 void DP_MSG_ErrorReceived( IDirectPlay2AImpl* This, WORD wCommandId,
487                            LPCVOID lpMsgBody, DWORD dwMsgBodySize )
488 {
489   LPCDPMSG_FORWARDADDPLAYERNACK lpcErrorMsg;
490
491   lpcErrorMsg = (LPCDPMSG_FORWARDADDPLAYERNACK)lpMsgBody;
492
493   ERR( "Received error message %u. Error is %s\n", 
494        wCommandId, DPLAYX_HresultToString( lpcErrorMsg->errorCode) );
495   DebugBreak();
496 }
497