Ownerdata listviews were not clearing previously selected items when a
[wine] / dlls / dplayx / dplayx_messages.c
1 /* DirectPlay & DirectPlayLobby messaging implementation
2  *
3  * Copyright 2000 - Peter Hunnisett
4  *
5  * <presently under construction - contact hunnise@nortelnetworks.com>
6  *
7  */
8
9 #include "winbase.h"
10 #include "debugtools.h"
11
12 #include "wingdi.h"
13 #include "winuser.h"
14 #include "winerror.h"
15
16 #include "dplayx_messages.h"
17 #include "dplay_global.h"
18 #include "dplayx_global.h"
19
20 DEFAULT_DEBUG_CHANNEL(dplay);
21
22 typedef struct tagMSGTHREADINFO
23 {
24   HANDLE hStart;
25   HANDLE hDeath; 
26   HANDLE hSettingRead;
27   HANDLE hNotifyEvent; 
28 } MSGTHREADINFO, *LPMSGTHREADINFO;
29
30 static DWORD CALLBACK DPL_MSG_ThreadMain( LPVOID lpContext );
31 static LPVOID DP_MSG_ExpectReply( IDirectPlay2AImpl* This, LPDPSP_SENDDATA data, 
32                                   DWORD dwWaitTime, WORD wReplyCommandId,
33                                   LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize );
34
35
36 /* Create the message reception thread to allow the application to receive
37  * asynchronous message reception
38  */
39 DWORD CreateLobbyMessageReceptionThread( HANDLE hNotifyEvent, HANDLE hStart, 
40                                          HANDLE hDeath, HANDLE hConnRead )
41 {
42   DWORD           dwMsgThreadId;
43   LPMSGTHREADINFO lpThreadInfo;
44
45   lpThreadInfo = HeapAlloc( GetProcessHeap(), 0, sizeof( *lpThreadInfo ) );
46   if( lpThreadInfo == NULL )
47   {
48     return 0;
49   }
50
51   /* The notify event may or may not exist. Depends if async comm or not */
52   if( hNotifyEvent &&
53       !DuplicateHandle( GetCurrentProcess(), hNotifyEvent, 
54                         GetCurrentProcess(), &lpThreadInfo->hNotifyEvent, 
55                         0, FALSE, DUPLICATE_SAME_ACCESS ) )
56   {
57     ERR( "Unable to duplicate event handle\n" );
58     goto error;
59   }
60
61   /* These 3 handles don't need to be duplicated because we don't keep a
62    * reference to them where they're created. They're created specifically
63    * for the message thread 
64    */
65   lpThreadInfo->hStart       = hStart;
66   lpThreadInfo->hDeath       = hDeath;
67   lpThreadInfo->hSettingRead = hConnRead;
68
69   if( !CreateThread( NULL,                  /* Security attribs */
70                      0,                     /* Stack */ 
71                      DPL_MSG_ThreadMain,    /* Msg reception function */
72                      lpThreadInfo,          /* Msg reception func parameter */
73                      0,                     /* Flags */
74                      &dwMsgThreadId         /* Updated with thread id */
75                    )
76     )
77   {
78     ERR( "Unable to create msg thread\n" );
79     goto error;
80   }
81
82   /* FIXME: Should I be closing the handle to the thread or does that
83             terminate the thread? */
84  
85   return dwMsgThreadId;
86
87 error:
88
89   HeapFree( GetProcessHeap(), 0, lpThreadInfo );
90
91   return 0;
92 }
93
94 static DWORD CALLBACK DPL_MSG_ThreadMain( LPVOID lpContext )
95 {
96   LPMSGTHREADINFO lpThreadInfo = (LPMSGTHREADINFO)lpContext;
97   DWORD dwWaitResult;
98  
99   TRACE( "Msg thread created. Waiting on app startup\n" );
100
101   /* Wait to ensure that the lobby application is started w/ 1 min timeout */
102   dwWaitResult = WaitForSingleObject( lpThreadInfo->hStart, 10000 /* 10 sec */ );
103   if( dwWaitResult == WAIT_TIMEOUT )
104   {
105     FIXME( "Should signal app/wait creation failure (0x%08lx)\n", dwWaitResult );
106     goto end_of_thread;
107   }
108
109   /* Close this handle as it's not needed anymore */
110   CloseHandle( lpThreadInfo->hStart );
111   lpThreadInfo->hStart = 0;
112
113   /* Wait until the lobby knows what it is */
114   dwWaitResult = WaitForSingleObject( lpThreadInfo->hSettingRead, INFINITE );
115   if( dwWaitResult == WAIT_TIMEOUT )
116   {
117     ERR( "App Read connection setting timeout fail (0x%08lx)\n", dwWaitResult );
118   }
119
120   /* Close this handle as it's not needed anymore */
121   CloseHandle( lpThreadInfo->hSettingRead );
122   lpThreadInfo->hSettingRead = 0;
123
124   TRACE( "App created && intialized starting main message reception loop\n" );
125
126   for ( ;; )
127   {
128     MSG lobbyMsg;
129 #ifdef STRICT
130     HANDLE hNullHandle = NULL;
131 #else
132     HANDLE hNullHandle = 0;
133 #endif
134
135     GetMessageW( &lobbyMsg, hNullHandle, 0, 0 );
136   }
137
138 end_of_thread:
139   TRACE( "Msg thread exiting!\n" );
140   HeapFree( GetProcessHeap(), 0, lpThreadInfo );
141
142   return 0;
143 }
144
145 /* DP messageing stuff */
146 static HANDLE DP_MSG_BuildAndLinkReplyStruct( IDirectPlay2Impl* This,
147                                               LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList, 
148                                               WORD wReplyCommandId );
149 static LPVOID DP_MSG_CleanReplyStruct( LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList,
150                                        LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize );
151
152
153 static
154 HANDLE DP_MSG_BuildAndLinkReplyStruct( IDirectPlay2Impl* This,
155                                        LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList, WORD wReplyCommandId )
156 {
157   lpReplyStructList->replyExpected.hReceipt       = CreateEventA( NULL, FALSE, FALSE, NULL );
158   lpReplyStructList->replyExpected.wExpectedReply = wReplyCommandId;
159   lpReplyStructList->replyExpected.lpReplyMsg     = NULL;
160   lpReplyStructList->replyExpected.dwMsgBodySize  = 0;
161
162   /* Insert into the message queue while locked */
163   EnterCriticalSection( &This->unk->DP_lock );
164     DPQ_INSERT( This->dp2->replysExpected, lpReplyStructList, replysExpected );
165   LeaveCriticalSection( &This->unk->DP_lock );
166
167   return lpReplyStructList->replyExpected.hReceipt;
168 }
169
170 static
171 LPVOID DP_MSG_CleanReplyStruct( LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList,
172                                 LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize  )
173 {
174   CloseHandle( lpReplyStructList->replyExpected.hReceipt );
175
176   *lplpReplyMsg    = lpReplyStructList->replyExpected.lpReplyMsg;
177   *lpdwMsgBodySize = lpReplyStructList->replyExpected.dwMsgBodySize;
178
179   return lpReplyStructList->replyExpected.lpReplyMsg;
180 }
181
182 HRESULT DP_MSG_SendRequestPlayerId( IDirectPlay2AImpl* This, DWORD dwFlags,
183                                     LPDPID lpdpidAllocatedId )
184 {
185   LPVOID                     lpMsg;
186   LPDPMSG_REQUESTNEWPLAYERID lpMsgBody;
187   DWORD                      dwMsgSize;
188   HRESULT                    hr = DP_OK;
189
190   dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody );
191
192   lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize );
193
194   lpMsgBody = (LPDPMSG_REQUESTNEWPLAYERID)( (BYTE*)lpMsg + 
195                                              This->dp2->spData.dwSPHeaderSize );
196
197   /* Compose dplay message envelope */
198   lpMsgBody->envelope.dwMagic    = DPMSGMAGIC_DPLAYMSG;
199   lpMsgBody->envelope.wCommandId = DPMSGCMD_REQUESTNEWPLAYERID;
200   lpMsgBody->envelope.wVersion   = DPMSGVER_DP6;
201
202   /* Compose the body of the message */
203   lpMsgBody->dwFlags = dwFlags;
204
205   /* Send the message */
206   {
207     DPSP_SENDDATA data;
208
209     data.dwFlags        = DPSEND_GUARANTEED;
210     data.idPlayerTo     = 0; /* Name server */
211     data.idPlayerFrom   = 0; /* Sending from DP */
212     data.lpMessage      = lpMsg;
213     data.dwMessageSize  = dwMsgSize;
214     data.bSystemMessage = TRUE; /* Allow reply to be sent */
215     data.lpISP          = This->dp2->spData.lpISP;
216
217     TRACE( "Asking for player id w/ dwFlags 0x%08lx\n", 
218            lpMsgBody->dwFlags );
219
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( !FAILED(hr) )
227   {
228     LPCDPMSG_NEWPLAYERIDREPLY lpcReply; 
229
230     lpcReply = (LPCDPMSG_NEWPLAYERIDREPLY)lpMsg;
231
232     *lpdpidAllocatedId = lpcReply->dpidNewPlayerId;   
233
234     TRACE( "Received reply for id = 0x%08lx\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 intialization 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
250 HRESULT DP_MSG_ForwardPlayerCreation( IDirectPlay2AImpl* 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, dpidServer, (LPVOID*)&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[0] = 0x0;
305   lpMsgBody->unknown3[0] = 0x20;
306   lpMsgBody->unknown3[0] = 0x0;
307   lpMsgBody->unknown3[0] = 0x0;
308   
309   lpMsgBody->dpidAppServer3 = dpidServer;
310   lpMsgBody->unknown4[0] =  0x30; 
311   lpMsgBody->unknown4[1] =  0xb;
312   lpMsgBody->unknown4[2] =  0x0;
313   lpMsgBody->unknown4[3] =  0x1e090002;
314   lpMsgBody->unknown4[4] =  0x0;
315   lpMsgBody->unknown4[5] =  0x0;
316   lpMsgBody->unknown4[6] =  0x0;
317   lpMsgBody->unknown4[7] =  0x32090002;
318   lpMsgBody->unknown4[8] =  0x0;
319   lpMsgBody->unknown4[9] =  0x0;
320   lpMsgBody->unknown4[10] = 0x0;
321   lpMsgBody->unknown4[11] = 0x0;
322   lpMsgBody->unknown4[12] = 0x0;
323
324   lpMsgBody->unknown5[0] = 0x0;
325   lpMsgBody->unknown5[1] = 0x0;
326
327   /* Send the message */
328   {
329     DPSP_SENDDATA data;
330
331     data.dwFlags        = DPSEND_GUARANTEED;
332     data.idPlayerTo     = 0; /* Name server */
333     data.idPlayerFrom   = dpidServer; /* Sending from session server */
334     data.lpMessage      = lpMsg;
335     data.dwMessageSize  = dwMsgSize;
336     data.bSystemMessage = TRUE; /* Allow reply to be sent */
337     data.lpISP          = This->dp2->spData.lpISP;
338
339     lpMsg = DP_MSG_ExpectReply( This, &data, 
340                                 DPMSG_WAIT_60_SECS, 
341                                 DPMSGCMD_GETNAMETABLEREPLY,
342                                 &lpMsg, &dwMsgSize );
343   }
344
345   /* Need to examine the data and extract the new player id */
346   if( lpMsg != NULL )
347   {
348     FIXME( "Name Table reply received: stub\n" );
349   }
350
351   return hr;
352 }
353
354 /* Queue up a structure indicating that we want a reply of type wReplyCommandId. DPlay does
355  * not seem to offer any way of uniquely differentiating between replies of the same type
356  * relative to the request sent. There is an implicit assumption that there will be no
357  * ordering issues on sends and receives from the opposite machine. No wonder MS is not
358  * a networking company.
359  */
360 static
361 LPVOID DP_MSG_ExpectReply( IDirectPlay2AImpl* This, LPDPSP_SENDDATA lpData, 
362                            DWORD dwWaitTime, WORD wReplyCommandId,
363                            LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize )
364 {
365   HRESULT                  hr;
366   HANDLE                   hMsgReceipt;
367   DP_MSG_REPLY_STRUCT_LIST replyStructList;
368   DWORD                    dwWaitReturn;
369  
370   /* Setup for receipt */
371   hMsgReceipt = DP_MSG_BuildAndLinkReplyStruct( This, &replyStructList, 
372                                                 wReplyCommandId );
373
374   TRACE( "Sending msg and expecting cmd %u in reply within %lu ticks\n", 
375          wReplyCommandId, dwWaitTime );
376   hr = (*This->dp2->spData.lpCB->Send)( lpData );
377
378   if( FAILED(hr) )
379   {
380     ERR( "Request for new playerID send failed: %s\n",
381          DPLAYX_HresultToString( hr ) );
382     return NULL;
383   }
384
385   dwWaitReturn = WaitForSingleObject( hMsgReceipt, dwWaitTime );
386   if( dwWaitReturn != WAIT_OBJECT_0 )
387   {
388     ERR( "Wait failed 0x%08lx\n", dwWaitReturn );
389     return NULL;
390   }
391
392   /* Clean Up */
393   return DP_MSG_CleanReplyStruct( &replyStructList, lplpReplyMsg, lpdwMsgBodySize );
394 }
395
396 /* Determine if there is a matching request for this incomming message and then copy
397  * all important data. It is quite silly to have to copy the message, but the documents
398  * indicate that a copy is taken. Silly really.
399  */
400 void DP_MSG_ReplyReceived( IDirectPlay2AImpl* This, WORD wCommandId, 
401                            LPCVOID lpcMsgBody, DWORD dwMsgBodySize )
402 {
403   LPDP_MSG_REPLY_STRUCT_LIST lpReplyList;
404
405 #if 0
406   if( wCommandId == DPMSGCMD_FORWARDADDPLAYER )
407   {
408     DebugBreak();
409   }
410 #endif
411
412   /* Find, and immediately remove (to avoid double triggering), the appropriate entry. Call locked to 
413    * avoid problems.
414    */
415   EnterCriticalSection( &This->unk->DP_lock );
416     DPQ_REMOVE_ENTRY( This->dp2->replysExpected, replysExpected, replyExpected.wExpectedReply,\
417                      ==, wCommandId, lpReplyList );
418   LeaveCriticalSection( &This->unk->DP_lock );  
419
420   if( lpReplyList != NULL )
421   {
422     lpReplyList->replyExpected.dwMsgBodySize = dwMsgBodySize; 
423     lpReplyList->replyExpected.lpReplyMsg = HeapAlloc( GetProcessHeap(),
424                                                        HEAP_ZERO_MEMORY,
425                                                        dwMsgBodySize );
426     CopyMemory( lpReplyList->replyExpected.lpReplyMsg, 
427                 lpcMsgBody, dwMsgBodySize );
428
429     /* Signal the thread which sent the message that it has a reply */
430     SetEvent( lpReplyList->replyExpected.hReceipt );
431   }
432   else
433   {
434     ERR( "No receipt event set - only expecting in reply mode\n" );
435     DebugBreak();
436   }
437  
438 }
439
440 void DP_MSG_ErrorReceived( IDirectPlay2AImpl* This, WORD wCommandId,
441                            LPCVOID lpMsgBody, DWORD dwMsgBodySize )
442 {
443   LPCDPMSG_FORWARDADDPLAYERNACK lpcErrorMsg;
444
445   lpcErrorMsg = (LPCDPMSG_FORWARDADDPLAYERNACK)lpMsgBody;
446
447   ERR( "Received error message %u. Error is %s\n", 
448        wCommandId, DPLAYX_HresultToString( lpcErrorMsg->errorCode) );
449   DebugBreak();
450 }
451