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 */ 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 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 */ 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 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 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 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 */ 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 */ 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 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 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