Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1777 lines
37 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #include "pch_tier0.h"
  9. #ifdef _WIN32
  10. #define WIN_32_LEAN_AND_MEAN
  11. #include <windows.h>
  12. #include <winsock.h>
  13. #endif
  14. #include <time.h>
  15. #include <assert.h>
  16. #include <stdio.h>
  17. #include <stdarg.h>
  18. #include <string.h>
  19. #include "tier0/vcrmode.h"
  20. #include "tier0/dbg.h"
  21. // FIXME: We totally have a bad tier dependency here
  22. #include "inputsystem/inputenums.h"
  23. #ifndef NO_VCR
  24. #define PvRealloc realloc
  25. #define PvAlloc malloc
  26. #define VCR_RuntimeAssert(x) VCR_RuntimeAssertFn(x, #x)
  27. double g_flLastVCRFloatTimeValue;
  28. bool g_bExpectingWindowProcCalls = false;
  29. IVCRHelpers *g_pHelpers = 0;
  30. FILE *g_pVCRFile = NULL;
  31. VCRMode_t g_VCRMode = VCR_Disabled;
  32. VCRMode_t g_OldVCRMode = VCR_Invalid; // Stored temporarily between SetEnabled(0)/SetEnabled(1) blocks.
  33. int g_iCurEvent = 0;
  34. int g_CurFilePos = 0; // So it knows when we're done playing back.
  35. int g_FileLen = 0;
  36. VCREvent g_LastReadEvent = (VCREvent)-1; // Last VCR_ReadEvent() call.
  37. int g_LastEventThread; // The thread index of the thread that g_LastReadEvent is intended for.
  38. int g_bVCREnabled = 0;
  39. // ------------------------------------------------------------------------------------------ //
  40. // These wrappers exist because for some reason thread-blocking functions nuke the
  41. // last function on the call stack, so it's very hard to debug without these wrappers.
  42. // ------------------------------------------------------------------------------------------ //
  43. inline unsigned long Wrap_WaitForSingleObject( HANDLE hObj, DWORD duration )
  44. {
  45. return WaitForSingleObject( hObj, duration );
  46. }
  47. inline unsigned long Wrap_WaitForMultipleObjects( uint32 nHandles, const void **pHandles, int bWaitAll, uint32 timeout )
  48. {
  49. return WaitForMultipleObjects( nHandles, (const HANDLE *)pHandles, bWaitAll, timeout );
  50. }
  51. inline void Wrap_EnterCriticalSection( CRITICAL_SECTION *pSection )
  52. {
  53. EnterCriticalSection( pSection );
  54. }
  55. // ------------------------------------------------------------------------------------------ //
  56. // Threadsafe debugging file output.
  57. // ------------------------------------------------------------------------------------------ //
  58. FILE *g_pDebugFile = 0;
  59. CRITICAL_SECTION g_DebugFileCS;
  60. class CCSInit
  61. {
  62. public:
  63. CCSInit()
  64. {
  65. InitializeCriticalSection( &g_DebugFileCS );
  66. }
  67. ~CCSInit()
  68. {
  69. DeleteCriticalSection( &g_DebugFileCS );
  70. }
  71. } g_DebugFileCS222;
  72. void VCR_Debug( const char *pMsg, ... )
  73. {
  74. va_list marker;
  75. va_start( marker, pMsg );
  76. EnterCriticalSection( &g_DebugFileCS );
  77. if ( !g_pDebugFile )
  78. g_pDebugFile = fopen( "c:\\vcrdebug.txt", "wt" );
  79. if ( g_pDebugFile )
  80. {
  81. vfprintf( g_pDebugFile, pMsg, marker );
  82. fflush( g_pDebugFile );
  83. }
  84. LeaveCriticalSection( &g_DebugFileCS );
  85. va_end( marker );
  86. }
  87. // ------------------------------------------------------------------------------------------ //
  88. // VCR threading support.
  89. // It uses 2 methods to implement threading, depending on whether you're recording or not.
  90. //
  91. // If you're recording, it uses critical sections to control access to the events written
  92. // into the file.
  93. //
  94. // During playback, every thread waits on a windows event handle. When a VCR event is done
  95. // being read out, it peeks ahead and sees which thread should get the next VCR event
  96. // and it wakes up that thread.
  97. // ------------------------------------------------------------------------------------------ //
  98. #define MAX_VCR_THREADS 512
  99. class CVCRThreadInfo
  100. {
  101. public:
  102. DWORD m_ThreadID; // The Windows thread ID.
  103. HANDLE m_hWaitEvent; // Used to get the signal that there is an event for this thread.
  104. bool m_bEnabled; // By default, this is true, but it can be set to false to temporarily disable a thread's VCR usage.
  105. };
  106. CVCRThreadInfo *g_pVCRThreads = NULL; // This gets allocated to MAX_VCR_THREADS size if we're doing any VCR recording or playback.
  107. int g_nVCRThreads = 0;
  108. // Used to avoid writing the thread ID into events that are for the main thread.
  109. DWORD g_VCRMainThreadID = 0;
  110. // Set to true if VCR_Start is ever called.
  111. bool g_bVCRStartCalled = false;
  112. unsigned short GetCurrentVCRThreadIndex()
  113. {
  114. DWORD hCurThread = GetCurrentThreadId();
  115. for ( int i=0; i < g_nVCRThreads; i++ )
  116. {
  117. if ( g_pVCRThreads[i].m_ThreadID == hCurThread )
  118. return (unsigned short)i;
  119. }
  120. Error( "GetCurrentVCRThreadInfo: no matching thread." );
  121. return 0;
  122. }
  123. CVCRThreadInfo* GetCurrentVCRThreadInfo()
  124. {
  125. return &g_pVCRThreads[ GetCurrentVCRThreadIndex() ];
  126. }
  127. static void VCR_SignalNextEvent();
  128. // ------------------------------------------------------------------------------------------ //
  129. // This manages which thread gets the next event.
  130. // ------------------------------------------------------------------------------------------ //
  131. CRITICAL_SECTION g_VCRCriticalSection;
  132. class CVCRThreadSafe
  133. {
  134. public:
  135. CVCRThreadSafe()
  136. {
  137. m_bSignalledNextEvent = false;
  138. if ( g_VCRMode == VCR_Record )
  139. {
  140. Wrap_EnterCriticalSection( &g_VCRCriticalSection );
  141. }
  142. else if ( g_VCRMode == VCR_Playback )
  143. {
  144. // Wait until our event is signalled, telling us that we are the next guy in line for an event.
  145. WaitForSingleObject( GetCurrentVCRThreadInfo()->m_hWaitEvent, INFINITE );
  146. }
  147. }
  148. ~CVCRThreadSafe()
  149. {
  150. if ( g_VCRMode == VCR_Record )
  151. {
  152. LeaveCriticalSection( &g_VCRCriticalSection );
  153. }
  154. else if ( g_VCRMode == VCR_Playback && !m_bSignalledNextEvent )
  155. {
  156. // Set the event for the next thread's VCR event.
  157. VCR_SignalNextEvent();
  158. }
  159. }
  160. void SignalNextEvent()
  161. {
  162. VCR_SignalNextEvent();
  163. m_bSignalledNextEvent = true;
  164. }
  165. private:
  166. bool m_bSignalledNextEvent;
  167. };
  168. class CVCRThreadSafeInitter
  169. {
  170. public:
  171. CVCRThreadSafeInitter()
  172. {
  173. InitializeCriticalSection( &g_VCRCriticalSection );
  174. }
  175. ~CVCRThreadSafeInitter()
  176. {
  177. DeleteCriticalSection( &g_VCRCriticalSection );
  178. }
  179. } g_VCRThreadSafeInitter;
  180. #define VCR_THREADSAFE CVCRThreadSafe vcrThreadSafe;
  181. // ---------------------------------------------------------------------- //
  182. // Internal functions.
  183. // ---------------------------------------------------------------------- //
  184. static void VCR_Error( const char *pFormat, ... )
  185. {
  186. #ifdef _DEBUG
  187. // Figure out which thread we're in, for the debugger.
  188. DWORD curThreadId = GetCurrentThreadId();
  189. int iCurThread = -1;
  190. for ( int i=0; i < g_nVCRThreads; i++ )
  191. {
  192. if ( g_pVCRThreads[i].m_ThreadID == curThreadId )
  193. iCurThread = i;
  194. }
  195. DebuggerBreak();
  196. #endif
  197. char str[256];
  198. va_list marker;
  199. va_start( marker, pFormat );
  200. _vsnprintf( str, sizeof( str ), pFormat, marker );
  201. va_end( marker );
  202. g_pHelpers->ErrorMessage( str );
  203. VCREnd();
  204. }
  205. static void VCR_RuntimeAssertFn(int bAssert, char const *pStr)
  206. {
  207. if(!bAssert)
  208. {
  209. VCR_Error( "*** VCR ASSERT FAILED: %s ***\n", pStr );
  210. }
  211. }
  212. static void VCR_Read(void *pDest, int size)
  213. {
  214. if(!g_pVCRFile)
  215. {
  216. memset(pDest, 0, size);
  217. return;
  218. }
  219. fread(pDest, 1, size, g_pVCRFile);
  220. g_CurFilePos += size;
  221. VCR_RuntimeAssert(g_CurFilePos <= g_FileLen);
  222. if(g_CurFilePos >= g_FileLen)
  223. {
  224. VCREnd();
  225. }
  226. }
  227. template<class T>
  228. static void VCR_ReadVal(T &val)
  229. {
  230. VCR_Read(&val, sizeof(val));
  231. }
  232. static void VCR_Write(void const *pSrc, int size)
  233. {
  234. fwrite(pSrc, 1, size, g_pVCRFile);
  235. fflush(g_pVCRFile);
  236. }
  237. template<class T>
  238. static void VCR_WriteVal(T &val)
  239. {
  240. VCR_Write(&val, sizeof(val));
  241. }
  242. void VCR_SignalNextEvent()
  243. {
  244. // When this function is called, we know that we are the only thread that is accessing the VCR file.
  245. unsigned char event;
  246. VCR_Read( &event, 1 );
  247. // Verify that we're in the correct thread for this event.
  248. unsigned short threadID;
  249. if ( event & 0x80 )
  250. {
  251. VCR_ReadVal( threadID );
  252. event &= ~0x80;
  253. }
  254. else
  255. {
  256. threadID = 0;
  257. }
  258. // Must be a valid thread ID.
  259. if ( threadID >= g_nVCRThreads )
  260. {
  261. Error( "VCR_ReadEvent: invalid threadID (%d).", threadID );
  262. }
  263. // Now signal the next thread.
  264. g_LastReadEvent = (VCREvent)event;
  265. g_LastEventThread = threadID;
  266. SetEvent( g_pVCRThreads[threadID].m_hWaitEvent );
  267. }
  268. static VCREvent VCR_ReadEvent()
  269. {
  270. return g_LastReadEvent;
  271. }
  272. static void VCR_WriteEvent( VCREvent event )
  273. {
  274. unsigned char cEvent = (unsigned char)event;
  275. unsigned short threadID = GetCurrentVCRThreadIndex();
  276. if ( threadID == 0 )
  277. {
  278. VCR_Write( &cEvent, 1 );
  279. }
  280. else
  281. {
  282. cEvent |= 0x80;
  283. VCR_Write( &cEvent, 1 );
  284. VCR_WriteVal( threadID );
  285. }
  286. }
  287. static void VCR_IncrementEvent()
  288. {
  289. ++g_iCurEvent;
  290. }
  291. static void VCR_Event(VCREvent type)
  292. {
  293. if ( g_VCRMode == VCR_Disabled )
  294. return;
  295. VCR_IncrementEvent();
  296. if(g_VCRMode == VCR_Record)
  297. {
  298. VCR_WriteEvent(type);
  299. }
  300. else
  301. {
  302. VCREvent currentEvent = VCR_ReadEvent();
  303. VCR_RuntimeAssert( currentEvent == type );
  304. }
  305. }
  306. // ---------------------------------------------------------------------- //
  307. // VCR trace interface.
  308. // ---------------------------------------------------------------------- //
  309. class CVCRTrace : public IVCRTrace
  310. {
  311. public:
  312. virtual VCREvent ReadEvent()
  313. {
  314. return VCR_ReadEvent();
  315. }
  316. virtual void Read( void *pDest, int size )
  317. {
  318. VCR_Read( pDest, size );
  319. }
  320. };
  321. static CVCRTrace g_VCRTrace;
  322. // ---------------------------------------------------------------------- //
  323. // VCR interface.
  324. // ---------------------------------------------------------------------- //
  325. static int VCR_Start( char const *pFilename, bool bRecord, IVCRHelpers *pHelpers )
  326. {
  327. unsigned long version;
  328. g_VCRMainThreadID = GetCurrentThreadId();
  329. g_bVCRStartCalled = true;
  330. // Setup the initial VCR thread list.
  331. g_pVCRThreads = new CVCRThreadInfo[MAX_VCR_THREADS];
  332. g_pVCRThreads[0].m_ThreadID = GetCurrentThreadId();
  333. g_pVCRThreads[0].m_hWaitEvent = CreateEvent( NULL, false, false, NULL );
  334. g_pVCRThreads[0].m_bEnabled = true;
  335. g_nVCRThreads = 1;
  336. g_pHelpers = pHelpers;
  337. VCREnd();
  338. g_OldVCRMode = VCR_Invalid;
  339. if ( bRecord )
  340. {
  341. char *pCommandLine = GetCommandLine();
  342. if ( !strstr( pCommandLine, "-nosound" ) )
  343. Error( "VCR record: must use -nosound." );
  344. g_pVCRFile = fopen( pFilename, "wb" );
  345. if( g_pVCRFile )
  346. {
  347. // Write the version.
  348. version = VCRFILE_VERSION;
  349. VCR_Write(&version, sizeof(version));
  350. g_VCRMode = VCR_Record;
  351. return TRUE;
  352. }
  353. else
  354. {
  355. return FALSE;
  356. }
  357. }
  358. else
  359. {
  360. g_pVCRFile = fopen( pFilename, "rb" );
  361. if( g_pVCRFile )
  362. {
  363. // Get the file length.
  364. fseek(g_pVCRFile, 0, SEEK_END);
  365. g_FileLen = ftell(g_pVCRFile);
  366. fseek(g_pVCRFile, 0, SEEK_SET);
  367. g_CurFilePos = 0;
  368. // Verify the file version.
  369. VCR_Read(&version, sizeof(version));
  370. if(version != VCRFILE_VERSION)
  371. {
  372. assert(!"VCR_Start: invalid file version");
  373. VCREnd();
  374. return FALSE;
  375. }
  376. g_VCRMode = VCR_Playback;
  377. VCR_SignalNextEvent(); // Signal the first thread for its event.
  378. return TRUE;
  379. }
  380. else
  381. {
  382. return FALSE;
  383. }
  384. }
  385. }
  386. static void VCR_End()
  387. {
  388. if ( g_pVCRFile )
  389. {
  390. fclose(g_pVCRFile);
  391. g_pVCRFile = NULL;
  392. }
  393. if ( g_VCRMode == VCR_Playback )
  394. {
  395. // It's going to get screwy now, especially if we have threads, so just exit.
  396. #ifdef _DEBUG
  397. if ( IsDebuggerPresent() )
  398. DebuggerBreak();
  399. #endif
  400. TerminateProcess( GetCurrentProcess(), 1 );
  401. }
  402. g_VCRMode = VCR_Disabled;
  403. }
  404. static IVCRTrace* VCR_GetVCRTraceInterface()
  405. {
  406. return &g_VCRTrace;
  407. }
  408. static VCRMode_t VCR_GetMode()
  409. {
  410. return g_VCRMode;
  411. }
  412. static void VCR_SetEnabled( int bEnabled )
  413. {
  414. if ( g_VCRMode != VCR_Disabled )
  415. {
  416. g_pVCRThreads[ GetCurrentVCRThreadIndex() ].m_bEnabled = (bEnabled != 0);
  417. }
  418. }
  419. inline bool IsVCRModeEnabledForThisThread()
  420. {
  421. if ( g_VCRMode == VCR_Disabled || !g_bVCRStartCalled )
  422. return false;
  423. return g_pVCRThreads[ GetCurrentVCRThreadIndex() ].m_bEnabled;
  424. }
  425. static void VCR_SyncToken(char const *pToken)
  426. {
  427. // Preamble.
  428. if ( !IsVCRModeEnabledForThisThread() )
  429. return;
  430. unsigned char len;
  431. VCR_THREADSAFE;
  432. VCR_Event(VCREvent_SyncToken);
  433. if(g_VCRMode == VCR_Record)
  434. {
  435. int intLen = strlen( pToken );
  436. assert( intLen <= 255 );
  437. len = (unsigned char)intLen;
  438. VCR_Write(&len, 1);
  439. VCR_Write(pToken, len);
  440. }
  441. else if(g_VCRMode == VCR_Playback)
  442. {
  443. char test[256];
  444. VCR_Read(&len, 1);
  445. VCR_Read(test, len);
  446. VCR_RuntimeAssert( len == (unsigned char)strlen(pToken) );
  447. VCR_RuntimeAssert( memcmp(pToken, test, len) == 0 );
  448. }
  449. }
  450. static double VCR_Hook_Sys_FloatTime(double time)
  451. {
  452. // Preamble.
  453. if ( !IsVCRModeEnabledForThisThread() )
  454. return time;
  455. VCR_THREADSAFE;
  456. VCR_Event(VCREvent_Sys_FloatTime);
  457. if(g_VCRMode == VCR_Record)
  458. {
  459. VCR_Write(&time, sizeof(time));
  460. }
  461. else if(g_VCRMode == VCR_Playback)
  462. {
  463. VCR_Read(&time, sizeof(time));
  464. g_flLastVCRFloatTimeValue = time;
  465. }
  466. return time;
  467. }
  468. static int VCR_Hook_PeekMessage(
  469. struct tagMSG *msg,
  470. void *hWnd,
  471. unsigned int wMsgFilterMin,
  472. unsigned int wMsgFilterMax,
  473. unsigned int wRemoveMsg
  474. )
  475. {
  476. // Preamble.
  477. if ( !IsVCRModeEnabledForThisThread() )
  478. return PeekMessage((MSG*)msg, (HWND)hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
  479. VCR_THREADSAFE;
  480. if( g_VCRMode == VCR_Record )
  481. {
  482. // The trapped windowproc calls should be flushed by the time we get here.
  483. int ret;
  484. ret = PeekMessage( (MSG*)msg, (HWND)hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg );
  485. // NOTE: this must stay AFTER the trapped window proc calls or things get
  486. // read back in the wrong order.
  487. VCR_Event( VCREvent_PeekMessage );
  488. VCR_WriteVal(ret);
  489. if(ret)
  490. VCR_Write(msg, sizeof(MSG));
  491. return ret;
  492. }
  493. else
  494. {
  495. Assert( g_VCRMode == VCR_Playback );
  496. // Playback any windows messages that got trapped.
  497. VCR_Event( VCREvent_PeekMessage );
  498. int ret;
  499. VCR_ReadVal(ret);
  500. if(ret)
  501. VCR_Read(msg, sizeof(MSG));
  502. return ret;
  503. }
  504. }
  505. void VCR_Hook_RecordGameMsg( const InputEvent_t& event )
  506. {
  507. // Preamble.
  508. if ( !IsVCRModeEnabledForThisThread() )
  509. return;
  510. VCR_THREADSAFE;
  511. if ( g_VCRMode == VCR_Record )
  512. {
  513. VCR_Event( VCREvent_GameMsg );
  514. char val = 1;
  515. VCR_WriteVal( val );
  516. VCR_WriteVal( event.m_nType );
  517. VCR_WriteVal( event.m_nData );
  518. VCR_WriteVal( event.m_nData2 );
  519. VCR_WriteVal( event.m_nData3 );
  520. }
  521. }
  522. void VCR_Hook_RecordEndGameMsg()
  523. {
  524. // Preamble.
  525. if ( !IsVCRModeEnabledForThisThread() )
  526. return;
  527. VCR_THREADSAFE;
  528. if ( g_VCRMode == VCR_Record )
  529. {
  530. VCR_Event( VCREvent_GameMsg );
  531. char val = 0;
  532. VCR_WriteVal( val ); // record that there are no more messages.
  533. }
  534. }
  535. bool VCR_Hook_PlaybackGameMsg( InputEvent_t* pEvent )
  536. {
  537. // Preamble.
  538. if ( !IsVCRModeEnabledForThisThread() )
  539. return false;
  540. VCR_THREADSAFE;
  541. if ( g_VCRMode == VCR_Playback )
  542. {
  543. VCR_Event( VCREvent_GameMsg );
  544. char bMsg;
  545. VCR_ReadVal( bMsg );
  546. if ( bMsg )
  547. {
  548. VCR_ReadVal( pEvent->m_nType );
  549. VCR_ReadVal( pEvent->m_nData );
  550. VCR_ReadVal( pEvent->m_nData2 );
  551. VCR_ReadVal( pEvent->m_nData3 );
  552. return true;
  553. }
  554. }
  555. return false;
  556. }
  557. static void VCR_Hook_GetCursorPos(struct tagPOINT *pt)
  558. {
  559. // Preamble.
  560. if ( !IsVCRModeEnabledForThisThread() )
  561. {
  562. GetCursorPos(pt);
  563. return;
  564. }
  565. VCR_THREADSAFE;
  566. VCR_Event(VCREvent_GetCursorPos);
  567. if(g_VCRMode == VCR_Playback)
  568. {
  569. VCR_ReadVal(*pt);
  570. }
  571. else
  572. {
  573. GetCursorPos(pt);
  574. if(g_VCRMode == VCR_Record)
  575. {
  576. VCR_WriteVal(*pt);
  577. }
  578. }
  579. }
  580. static void VCR_Hook_ScreenToClient(void *hWnd, struct tagPOINT *pt)
  581. {
  582. // Preamble.
  583. if ( !IsVCRModeEnabledForThisThread() )
  584. {
  585. ScreenToClient((HWND)hWnd, pt);
  586. return;
  587. }
  588. VCR_THREADSAFE;
  589. VCR_Event(VCREvent_ScreenToClient);
  590. if(g_VCRMode == VCR_Playback)
  591. {
  592. VCR_ReadVal(*pt);
  593. }
  594. else
  595. {
  596. ScreenToClient((HWND)hWnd, pt);
  597. if(g_VCRMode == VCR_Record)
  598. {
  599. VCR_WriteVal(*pt);
  600. }
  601. }
  602. }
  603. static int VCR_Hook_recvfrom(int s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen)
  604. {
  605. // Preamble.
  606. if ( !IsVCRModeEnabledForThisThread() )
  607. {
  608. return recvfrom((SOCKET)s, buf, len, flags, from, fromlen);
  609. }
  610. VCR_THREADSAFE;
  611. VCR_Event(VCREvent_recvfrom);
  612. int ret;
  613. if ( g_VCRMode == VCR_Playback )
  614. {
  615. // Get the result from our file.
  616. VCR_Read(&ret, sizeof(ret));
  617. if(ret == SOCKET_ERROR)
  618. {
  619. int err;
  620. VCR_ReadVal(err);
  621. WSASetLastError(err);
  622. }
  623. else
  624. {
  625. VCR_Read( buf, ret );
  626. char bFrom;
  627. VCR_ReadVal( bFrom );
  628. if ( bFrom )
  629. {
  630. VCR_Read( from, *fromlen );
  631. }
  632. }
  633. }
  634. else
  635. {
  636. ret = recvfrom((SOCKET)s, buf, len, flags, from, fromlen);
  637. if ( g_VCRMode == VCR_Record )
  638. {
  639. // Record the result.
  640. VCR_Write(&ret, sizeof(ret));
  641. if(ret == SOCKET_ERROR)
  642. {
  643. int err = WSAGetLastError();
  644. VCR_WriteVal(err);
  645. }
  646. else
  647. {
  648. VCR_Write( buf, ret );
  649. char bFrom = !!from;
  650. VCR_WriteVal( bFrom );
  651. if ( bFrom )
  652. VCR_Write( from, *fromlen );
  653. }
  654. }
  655. }
  656. return ret;
  657. }
  658. static int VCR_Hook_recv(int s, char *buf, int len, int flags)
  659. {
  660. // Preamble.
  661. if ( !IsVCRModeEnabledForThisThread() )
  662. {
  663. return recv( (SOCKET)s, buf, len, flags );
  664. }
  665. VCR_THREADSAFE;
  666. VCR_Event(VCREvent_recv);
  667. int ret;
  668. if ( g_VCRMode == VCR_Playback )
  669. {
  670. // Get the result from our file.
  671. VCR_Read(&ret, sizeof(ret));
  672. if(ret == SOCKET_ERROR)
  673. {
  674. int err;
  675. VCR_ReadVal(err);
  676. WSASetLastError(err);
  677. }
  678. else
  679. {
  680. VCR_Read( buf, ret );
  681. }
  682. }
  683. else
  684. {
  685. ret = recv( (SOCKET)s, buf, len, flags );
  686. if ( g_VCRMode == VCR_Record )
  687. {
  688. // Record the result.
  689. VCR_Write(&ret, sizeof(ret));
  690. if(ret == SOCKET_ERROR)
  691. {
  692. int err = WSAGetLastError();
  693. VCR_WriteVal(err);
  694. }
  695. else
  696. {
  697. VCR_Write( buf, ret );
  698. }
  699. }
  700. }
  701. return ret;
  702. }
  703. static int VCR_Hook_send(int s, const char *buf, int len, int flags)
  704. {
  705. // Preamble.
  706. if ( !IsVCRModeEnabledForThisThread() )
  707. {
  708. return send( (SOCKET)s, buf, len, flags );
  709. }
  710. VCR_THREADSAFE;
  711. VCR_Event(VCREvent_send);
  712. int ret;
  713. if ( g_VCRMode == VCR_Playback )
  714. {
  715. // Get the result from our file.
  716. VCR_Read(&ret, sizeof(ret));
  717. if(ret == SOCKET_ERROR)
  718. {
  719. int err;
  720. VCR_ReadVal(err);
  721. WSASetLastError(err);
  722. }
  723. }
  724. else
  725. {
  726. ret = send( (SOCKET)s, buf, len, flags );
  727. if ( g_VCRMode == VCR_Record )
  728. {
  729. // Record the result.
  730. VCR_Write(&ret, sizeof(ret));
  731. if(ret == SOCKET_ERROR)
  732. {
  733. int err = WSAGetLastError();
  734. VCR_WriteVal(err);
  735. }
  736. }
  737. }
  738. return ret;
  739. }
  740. static void VCR_Hook_Cmd_Exec(char **f)
  741. {
  742. // Preamble.
  743. if ( !IsVCRModeEnabledForThisThread() )
  744. return;
  745. VCR_THREADSAFE;
  746. VCR_Event(VCREvent_Cmd_Exec);
  747. if(g_VCRMode == VCR_Playback)
  748. {
  749. int len;
  750. VCR_Read(&len, sizeof(len));
  751. if(len == -1)
  752. {
  753. *f = NULL;
  754. }
  755. else
  756. {
  757. *f = (char*)PvAlloc(len);
  758. VCR_Read(*f, len);
  759. }
  760. }
  761. else if(g_VCRMode == VCR_Record)
  762. {
  763. int len;
  764. char *str = *f;
  765. if(str)
  766. {
  767. len = strlen(str)+1;
  768. VCR_Write(&len, sizeof(len));
  769. VCR_Write(str, len);
  770. }
  771. else
  772. {
  773. len = -1;
  774. VCR_Write(&len, sizeof(len));
  775. }
  776. }
  777. }
  778. static char* VCR_Hook_GetCommandLine()
  779. {
  780. // This function is special in that it can be called before VCR mode is initialized.
  781. // In this special case, just return the command line.
  782. if ( !g_pVCRThreads )
  783. return GetCommandLine();
  784. // Preamble.
  785. if ( !IsVCRModeEnabledForThisThread() )
  786. return GetCommandLine();
  787. VCR_THREADSAFE;
  788. VCR_Event(VCREvent_CmdLine);
  789. int len;
  790. char *ret;
  791. if(g_VCRMode == VCR_Playback)
  792. {
  793. VCR_Read(&len, sizeof(len));
  794. ret = new char[len];
  795. VCR_Read(ret, len);
  796. }
  797. else
  798. {
  799. ret = GetCommandLine();
  800. if(g_VCRMode == VCR_Record)
  801. {
  802. len = strlen(ret) + 1;
  803. VCR_WriteVal(len);
  804. VCR_Write(ret, len);
  805. }
  806. }
  807. return ret;
  808. }
  809. static long VCR_Hook_RegOpenKeyEx( void *hKey, const char *lpSubKey, unsigned long ulOptions, unsigned long samDesired, void *pHKey )
  810. {
  811. // Preamble.
  812. if ( !IsVCRModeEnabledForThisThread() )
  813. return RegOpenKeyEx( (HKEY)hKey, lpSubKey, ulOptions, samDesired, (PHKEY)pHKey );
  814. VCR_THREADSAFE;
  815. VCR_Event(VCREvent_RegOpenKeyEx);
  816. long ret;
  817. if(g_VCRMode == VCR_Playback)
  818. {
  819. VCR_ReadVal(ret); // (don't actually write anything to the person's registry when playing back).
  820. }
  821. else
  822. {
  823. ret = RegOpenKeyEx( (HKEY)hKey, lpSubKey, ulOptions, samDesired, (PHKEY)pHKey );
  824. if(g_VCRMode == VCR_Record)
  825. VCR_WriteVal(ret);
  826. }
  827. return ret;
  828. }
  829. static long VCR_Hook_RegSetValueEx(void *hKey, tchar const *lpValueName, unsigned long Reserved, unsigned long dwType, unsigned char const *lpData, unsigned long cbData)
  830. {
  831. // Preamble.
  832. if ( !IsVCRModeEnabledForThisThread() )
  833. return RegSetValueEx((HKEY)hKey, lpValueName, Reserved, dwType, lpData, cbData);
  834. VCR_THREADSAFE;
  835. VCR_Event(VCREvent_RegSetValueEx);
  836. long ret;
  837. if(g_VCRMode == VCR_Playback)
  838. {
  839. VCR_ReadVal(ret); // (don't actually write anything to the person's registry when playing back).
  840. }
  841. else
  842. {
  843. ret = RegSetValueEx((HKEY)hKey, lpValueName, Reserved, dwType, lpData, cbData);
  844. if(g_VCRMode == VCR_Record)
  845. VCR_WriteVal(ret);
  846. }
  847. return ret;
  848. }
  849. static long VCR_Hook_RegQueryValueEx(void *hKey, tchar const *lpValueName, unsigned long *lpReserved, unsigned long *lpType, unsigned char *lpData, unsigned long *lpcbData)
  850. {
  851. // Preamble.
  852. if ( !IsVCRModeEnabledForThisThread() )
  853. return RegQueryValueEx((HKEY)hKey, lpValueName, lpReserved, lpType, lpData, lpcbData);
  854. VCR_THREADSAFE;
  855. VCR_Event(VCREvent_RegQueryValueEx);
  856. // Doesn't support this being null right now (although it would be trivial to add support).
  857. assert(lpData);
  858. long ret;
  859. unsigned long dummy = 0;
  860. if(g_VCRMode == VCR_Playback)
  861. {
  862. VCR_ReadVal(ret);
  863. VCR_ReadVal(lpType ? *lpType : dummy);
  864. VCR_ReadVal(*lpcbData);
  865. VCR_Read(lpData, *lpcbData);
  866. }
  867. else
  868. {
  869. ret = RegQueryValueEx((HKEY)hKey, lpValueName, lpReserved, lpType, lpData, lpcbData);
  870. if(g_VCRMode == VCR_Record)
  871. {
  872. VCR_WriteVal(ret);
  873. VCR_WriteVal(lpType ? *lpType : dummy);
  874. VCR_WriteVal(*lpcbData);
  875. VCR_Write(lpData, *lpcbData);
  876. }
  877. }
  878. return ret;
  879. }
  880. static long VCR_Hook_RegCreateKeyEx(void *hKey, char const *lpSubKey, unsigned long Reserved, char *lpClass, unsigned long dwOptions,
  881. unsigned long samDesired, void *lpSecurityAttributes, void *phkResult, unsigned long *lpdwDisposition)
  882. {
  883. // Preamble.
  884. if ( !IsVCRModeEnabledForThisThread() )
  885. return RegCreateKeyEx((HKEY)hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired, (LPSECURITY_ATTRIBUTES)lpSecurityAttributes, (HKEY*)phkResult, lpdwDisposition);
  886. VCR_THREADSAFE;
  887. VCR_Event(VCREvent_RegCreateKeyEx);
  888. long ret;
  889. if(g_VCRMode == VCR_Playback)
  890. {
  891. VCR_ReadVal(ret); // (don't actually write anything to the person's registry when playing back).
  892. }
  893. else
  894. {
  895. ret = RegCreateKeyEx((HKEY)hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired, (LPSECURITY_ATTRIBUTES)lpSecurityAttributes, (HKEY*)phkResult, lpdwDisposition);
  896. if(g_VCRMode == VCR_Record)
  897. VCR_WriteVal(ret);
  898. }
  899. return ret;
  900. }
  901. static void VCR_Hook_RegCloseKey(void *hKey)
  902. {
  903. // Preamble.
  904. if ( !IsVCRModeEnabledForThisThread() )
  905. {
  906. RegCloseKey( (HKEY)hKey );
  907. return;
  908. }
  909. VCR_THREADSAFE;
  910. VCR_Event(VCREvent_RegCloseKey);
  911. if(g_VCRMode == VCR_Playback)
  912. {
  913. }
  914. else
  915. {
  916. RegCloseKey((HKEY)hKey);
  917. }
  918. }
  919. int VCR_Hook_GetNumberOfConsoleInputEvents( void *hInput, unsigned long *pNumEvents )
  920. {
  921. // Preamble.
  922. if ( !IsVCRModeEnabledForThisThread() )
  923. return GetNumberOfConsoleInputEvents( (HANDLE)hInput, pNumEvents );
  924. VCR_THREADSAFE;
  925. VCR_Event( VCREvent_GetNumberOfConsoleInputEvents );
  926. char ret;
  927. if ( g_VCRMode == VCR_Playback )
  928. {
  929. VCR_ReadVal( ret );
  930. VCR_ReadVal( *pNumEvents );
  931. }
  932. else
  933. {
  934. ret = (char)GetNumberOfConsoleInputEvents( (HANDLE)hInput, pNumEvents );
  935. if ( g_VCRMode == VCR_Record )
  936. {
  937. VCR_WriteVal( ret );
  938. VCR_WriteVal( *pNumEvents );
  939. }
  940. }
  941. return ret;
  942. }
  943. int VCR_Hook_ReadConsoleInput( void *hInput, void *pRecs, int nMaxRecs, unsigned long *pNumRead )
  944. {
  945. // Preamble.
  946. if ( !IsVCRModeEnabledForThisThread() )
  947. return ReadConsoleInput( (HANDLE)hInput, (INPUT_RECORD*)pRecs, nMaxRecs, pNumRead );
  948. VCR_THREADSAFE;
  949. VCR_Event( VCREvent_ReadConsoleInput );
  950. char ret;
  951. if ( g_VCRMode == VCR_Playback )
  952. {
  953. VCR_ReadVal( ret );
  954. if ( ret )
  955. {
  956. VCR_ReadVal( *pNumRead );
  957. VCR_Read( pRecs, *pNumRead * sizeof( INPUT_RECORD ) );
  958. }
  959. }
  960. else
  961. {
  962. ret = (char)ReadConsoleInput( (HANDLE)hInput, (INPUT_RECORD*)pRecs, nMaxRecs, pNumRead );
  963. if ( g_VCRMode == VCR_Record )
  964. {
  965. VCR_WriteVal( ret );
  966. if ( ret )
  967. {
  968. VCR_WriteVal( *pNumRead );
  969. VCR_Write( pRecs, *pNumRead * sizeof( INPUT_RECORD ) );
  970. }
  971. }
  972. }
  973. return ret;
  974. }
  975. void VCR_Hook_LocalTime( struct tm *today )
  976. {
  977. // We just provide a wrapper on this function so we can protect access to time() everywhere.
  978. time_t ltime;
  979. time( &ltime );
  980. tm *pTime = localtime( &ltime );
  981. memcpy( today, pTime, sizeof( *today ) );
  982. // Preamble.
  983. if ( !IsVCRModeEnabledForThisThread() )
  984. return;
  985. VCR_THREADSAFE;
  986. VCR_Event( VCREvent_LocalTime );
  987. if ( g_VCRMode == VCR_Playback )
  988. {
  989. VCR_Read( today, sizeof( *today ) );
  990. }
  991. else if ( g_VCRMode == VCR_Record )
  992. {
  993. VCR_Write( today, sizeof( *today ) );
  994. }
  995. }
  996. void VCR_Hook_Time( long *today )
  997. {
  998. // We just provide a wrapper on this function so we can protect access to time() everywhere.
  999. // NOTE: For 64-bit systems we should eventually get a function that takes a time_t, but we should have
  1000. // until about 2038 to do that before we overflow a long.
  1001. time_t curTime;
  1002. time( &curTime );
  1003. // Preamble.
  1004. if ( !IsVCRModeEnabledForThisThread() )
  1005. {
  1006. *today = (long)curTime;
  1007. return;
  1008. }
  1009. VCR_THREADSAFE;
  1010. VCR_Event( VCREvent_Time );
  1011. if ( g_VCRMode == VCR_Playback )
  1012. {
  1013. VCR_Read( &curTime, sizeof( curTime ) );
  1014. }
  1015. else if ( g_VCRMode == VCR_Record )
  1016. {
  1017. VCR_Write( &curTime, sizeof( curTime ) );
  1018. }
  1019. *today = (long)curTime;
  1020. }
  1021. short VCR_Hook_GetKeyState( int nVirtKey )
  1022. {
  1023. // Preamble.
  1024. if ( !IsVCRModeEnabledForThisThread() )
  1025. return ::GetKeyState( nVirtKey );
  1026. VCR_THREADSAFE;
  1027. VCR_Event( VCREvent_GetKeyState );
  1028. short ret;
  1029. if ( g_VCRMode == VCR_Playback )
  1030. {
  1031. VCR_ReadVal( ret );
  1032. }
  1033. else
  1034. {
  1035. ret = ::GetKeyState( nVirtKey );
  1036. if ( g_VCRMode == VCR_Record )
  1037. VCR_WriteVal( ret );
  1038. }
  1039. return ret;
  1040. }
  1041. void VCR_GenericRecord( const char *pEventName, const void *pData, int len )
  1042. {
  1043. // Preamble.
  1044. if ( !IsVCRModeEnabledForThisThread() )
  1045. return;
  1046. VCR_THREADSAFE;
  1047. VCR_Event( VCREvent_Generic );
  1048. if ( g_VCRMode != VCR_Record )
  1049. Error( "VCR_GenericRecord( %s ): not recording a VCR file", pEventName );
  1050. // Write the event name (or 255 if none).
  1051. int nameLen = 255;
  1052. if ( pEventName )
  1053. {
  1054. nameLen = strlen( pEventName ) + 1;
  1055. if ( nameLen >= 255 )
  1056. {
  1057. VCR_Error( "VCR_GenericRecord( %s ): nameLen too long (%d)", pEventName, nameLen );
  1058. return;
  1059. }
  1060. }
  1061. unsigned char ucNameLen = (unsigned char)nameLen;
  1062. VCR_WriteVal( ucNameLen );
  1063. VCR_Write( pEventName, ucNameLen );
  1064. // Write the data.
  1065. VCR_WriteVal( len );
  1066. VCR_Write( pData, len );
  1067. }
  1068. int VCR_GenericPlaybackInternal( const char *pEventName, void *pOutData, int maxLen, bool bForceSameLen, bool bForceSameContents )
  1069. {
  1070. // Preamble.
  1071. if ( !IsVCRModeEnabledForThisThread() || g_VCRMode != VCR_Playback )
  1072. Error( "VCR_Playback( %s ): not playing back a VCR file", pEventName );
  1073. VCR_THREADSAFE;
  1074. VCR_Event( VCREvent_Generic );
  1075. unsigned char nameLen;
  1076. VCR_ReadVal( nameLen );
  1077. if ( nameLen != 255 )
  1078. {
  1079. char testName[512];
  1080. VCR_Read( testName, nameLen );
  1081. if ( strcmp( pEventName, testName ) != 0 )
  1082. {
  1083. VCR_Error( "VCR_GenericPlayback( %s ) - event name does not match '%s'", pEventName, testName );
  1084. return 0;
  1085. }
  1086. }
  1087. int dataLen;
  1088. VCR_ReadVal( dataLen );
  1089. if ( dataLen > maxLen )
  1090. {
  1091. VCR_Error( "VCR_GenericPlayback( %s ) - generic data too long (greater than maxLen: %d)", pEventName, maxLen );
  1092. return 0;
  1093. }
  1094. else if ( bForceSameLen && dataLen != maxLen )
  1095. {
  1096. VCR_Error( "VCR_GenericPlayback( %s ) - data size in file (%d) different than desired (%d)", pEventName, dataLen, maxLen );
  1097. return 0;
  1098. }
  1099. if ( bForceSameContents )
  1100. {
  1101. if ( !bForceSameLen )
  1102. Error( "bForceSameContents and !bForceSameLen not allowed." );
  1103. static char *pTempData = new char[dataLen];
  1104. static int tempDataLen = dataLen;
  1105. if ( tempDataLen < dataLen )
  1106. {
  1107. delete [] pTempData;
  1108. pTempData = new char[dataLen];
  1109. tempDataLen = dataLen;
  1110. }
  1111. VCR_Read( pTempData, dataLen );
  1112. if ( memcmp( pTempData, pOutData, dataLen ) != 0 )
  1113. {
  1114. VCR_Error( "VCR_GenericPlayback: data doesn't match on playback." );
  1115. }
  1116. }
  1117. else
  1118. {
  1119. VCR_Read( pOutData, dataLen );
  1120. }
  1121. return dataLen;
  1122. }
  1123. int VCR_GenericPlayback( const char *pEventName, void *pOutData, int maxLen, bool bForceSameLen )
  1124. {
  1125. return VCR_GenericPlaybackInternal( pEventName, pOutData, maxLen, bForceSameLen, false );
  1126. }
  1127. void VCR_GenericValue( const char *pEventName, void *pData, int maxLen )
  1128. {
  1129. // Preamble.
  1130. if ( !IsVCRModeEnabledForThisThread() )
  1131. return;
  1132. if ( !pEventName )
  1133. pEventName = "";
  1134. if ( g_VCRMode == VCR_Record )
  1135. VCR_GenericRecord( pEventName, pData, maxLen );
  1136. else if ( g_VCRMode == VCR_Playback )
  1137. VCR_GenericPlaybackInternal( pEventName, pData, maxLen, true, false );
  1138. }
  1139. void VCR_GenericValueVerify( const tchar *pEventName, const void *pData, int maxLen )
  1140. {
  1141. // Preamble.
  1142. if ( !IsVCRModeEnabledForThisThread() )
  1143. return;
  1144. if ( !pEventName )
  1145. pEventName = "";
  1146. if ( g_VCRMode == VCR_Record )
  1147. VCR_GenericRecord( pEventName, pData, maxLen );
  1148. else if ( g_VCRMode == VCR_Playback )
  1149. VCR_GenericPlaybackInternal( pEventName, (void*)pData, maxLen, true, true );
  1150. }
  1151. void WriteShortString( const char *pStr )
  1152. {
  1153. int len = strlen( pStr ) + 1;
  1154. if ( len >= 0xFFFF )
  1155. {
  1156. Error( "VCR_WriteShortString, string too long (%d characters).", len );
  1157. }
  1158. unsigned short twobytes = (unsigned short)len;
  1159. VCR_WriteVal( twobytes );
  1160. VCR_Write( pStr, len );
  1161. }
  1162. void ReadAndVerifyShortString( const char *pStr )
  1163. {
  1164. int len = strlen( pStr ) + 1;
  1165. unsigned short incomingSize;
  1166. VCR_ReadVal( incomingSize );
  1167. if ( incomingSize != len )
  1168. VCR_Error( "ReadAndVerifyShortString (%s), lengths different.", pStr );
  1169. static char *pTempData = 0;
  1170. static int tempDataLen = 0;
  1171. if ( tempDataLen < len )
  1172. {
  1173. delete [] pTempData;
  1174. pTempData = new char[len];
  1175. tempDataLen = len;
  1176. }
  1177. VCR_Read( pTempData, len );
  1178. if ( memcmp( pTempData, pStr, len ) != 0 )
  1179. {
  1180. VCR_Error( "ReadAndVerifyShortString: strings different ('%s' vs '%s').", pStr, pTempData );
  1181. }
  1182. }
  1183. void VCR_GenericRecordString( const char *pEventName, const char *pString )
  1184. {
  1185. // Preamble.
  1186. if ( !IsVCRModeEnabledForThisThread() )
  1187. return;
  1188. VCR_THREADSAFE;
  1189. VCR_Event( VCREvent_GenericString );
  1190. if ( g_VCRMode != VCR_Record )
  1191. Error( "VCR_GenericRecordString( %s ): not recording a VCR file", pEventName );
  1192. // Write the event name (or 255 if none).
  1193. WriteShortString( pEventName );
  1194. WriteShortString( pString );
  1195. }
  1196. void VCR_GenericPlaybackString( const char *pEventName, const char *pString )
  1197. {
  1198. // Preamble.
  1199. if ( !IsVCRModeEnabledForThisThread() || g_VCRMode != VCR_Playback )
  1200. Error( "VCR_GenericPlaybackString( %s ): not playing back a VCR file", pEventName );
  1201. VCR_THREADSAFE;
  1202. VCR_Event( VCREvent_GenericString );
  1203. ReadAndVerifyShortString( pEventName );
  1204. ReadAndVerifyShortString( pString );
  1205. }
  1206. void VCR_GenericString( const char *pEventName, const char *pString )
  1207. {
  1208. // Preamble.
  1209. if ( !IsVCRModeEnabledForThisThread() )
  1210. return;
  1211. if ( !pEventName )
  1212. pEventName = "";
  1213. if ( !pString )
  1214. pString = "";
  1215. if ( g_VCRMode == VCR_Record )
  1216. VCR_GenericRecordString( pEventName, pString );
  1217. else if ( g_VCRMode == VCR_Playback )
  1218. VCR_GenericPlaybackString( pEventName, pString );
  1219. }
  1220. double VCR_GetPercentCompleted()
  1221. {
  1222. if ( g_VCRMode == VCR_Playback )
  1223. {
  1224. return (double)g_CurFilePos / g_FileLen;
  1225. }
  1226. else
  1227. {
  1228. return 0;
  1229. }
  1230. }
  1231. void* VCR_CreateThread(
  1232. void *lpThreadAttributes,
  1233. unsigned long dwStackSize,
  1234. void *lpStartAddress,
  1235. void *lpParameter,
  1236. unsigned long dwCreationFlags,
  1237. unsigned long *lpThreadID )
  1238. {
  1239. unsigned dwThreadID = 0;
  1240. // Use _beginthreadex because it sets up C runtime
  1241. // correctly, and is safer than _beginthread. See MSDN.
  1242. // Preamble.
  1243. if ( !IsVCRModeEnabledForThisThread() )
  1244. {
  1245. if ( g_VCRMode == VCR_Disabled )
  1246. {
  1247. HANDLE hThread = (void *)_beginthreadex(
  1248. (LPSECURITY_ATTRIBUTES)lpThreadAttributes,
  1249. dwStackSize,
  1250. (unsigned (__stdcall *) (void *))lpStartAddress,
  1251. lpParameter,
  1252. dwCreationFlags,
  1253. &dwThreadID );
  1254. if ( lpThreadID )
  1255. *lpThreadID = dwThreadID;
  1256. return hThread;
  1257. }
  1258. else
  1259. {
  1260. Error( "VCR_CreateThread: VCR mode disabled in calling thread." );
  1261. }
  1262. }
  1263. // We could make this work without too much pain.
  1264. if ( GetCurrentThreadId() != g_VCRMainThreadID )
  1265. {
  1266. Error( "VCR_CreateThread called outside main thread." );
  1267. }
  1268. if ( g_nVCRThreads >= MAX_VCR_THREADS )
  1269. {
  1270. // This is easy to fix if we ever hit it.. just allow more threads.
  1271. Error( "VCR_CreateThread: g_nVCRThreads >= MAX_VCR_THREADS." );
  1272. }
  1273. // Write out the VCR event saying this thread is being created.
  1274. VCR_THREADSAFE;
  1275. VCR_Event( VCREvent_CreateThread );
  1276. // Create the thread.
  1277. HANDLE hThread = (void*)_beginthreadex(
  1278. (LPSECURITY_ATTRIBUTES)lpThreadAttributes,
  1279. dwStackSize,
  1280. (unsigned (__stdcall *) (void *))lpStartAddress,
  1281. lpParameter,
  1282. dwCreationFlags | CREATE_SUSPENDED,
  1283. &dwThreadID );
  1284. if ( lpThreadID )
  1285. *lpThreadID = dwThreadID;
  1286. if ( !hThread )
  1287. {
  1288. // We don't handle this case in VCR mode (but we could pretty easily).
  1289. if ( g_VCRMode == VCR_Playback || g_VCRMode == VCR_Record )
  1290. Error( "VCR_CreateThread: CreateThread() failed." );
  1291. return NULL;
  1292. }
  1293. // Register this thread so we can write its ID into future VCR events.
  1294. int iNewThread = g_nVCRThreads++;
  1295. g_pVCRThreads[iNewThread].m_ThreadID = dwThreadID;
  1296. g_pVCRThreads[iNewThread].m_hWaitEvent = CreateEvent( NULL, false, false, NULL );
  1297. g_pVCRThreads[iNewThread].m_bEnabled = true;
  1298. // Now resume the thread.
  1299. if ( !( dwCreationFlags & CREATE_SUSPENDED ) )
  1300. {
  1301. ResumeThread( hThread );
  1302. }
  1303. return hThread;
  1304. }
  1305. unsigned long VCR_WaitForSingleObject(
  1306. void *handle,
  1307. unsigned long dwMilliseconds )
  1308. {
  1309. // Preamble.
  1310. if ( !IsVCRModeEnabledForThisThread() )
  1311. return Wrap_WaitForSingleObject( handle, dwMilliseconds );
  1312. //Error( "VCR_WaitForSingleObject: VCR mode disabled in calling thread." );
  1313. // We have to do the wait here BEFORE we acquire the VCR mutex, otherwise, we could freeze
  1314. // the thread that's supposed to signal "handle".
  1315. unsigned long ret = 0;
  1316. if ( g_VCRMode == VCR_Record )
  1317. {
  1318. ret = Wrap_WaitForSingleObject( handle, dwMilliseconds );
  1319. }
  1320. VCR_THREADSAFE;
  1321. VCR_Event( VCREvent_WaitForSingleObject );
  1322. char val = 1;
  1323. if ( g_VCRMode == VCR_Record )
  1324. {
  1325. if ( ret == WAIT_ABANDONED )
  1326. val = 2;
  1327. else if ( ret == WAIT_TIMEOUT )
  1328. val = 3;
  1329. VCR_WriteVal( val );
  1330. return ret;
  1331. }
  1332. else
  1333. {
  1334. Assert( g_VCRMode == VCR_Playback );
  1335. VCR_ReadVal( val );
  1336. if ( val == 1 )
  1337. {
  1338. // Hack job.. let other threads start reading events now.. we're basically saying here that we're
  1339. // finished reading our VCR event. If we didn't pass the buck onto the next one, if the event hadn't
  1340. // already been signalled, it might never get signalled.
  1341. vcrThreadSafe.SignalNextEvent();
  1342. // If it wrote 1, then we know that this call has to signal the object, so just wait until it gets signalled.
  1343. ret = Wrap_WaitForSingleObject( handle, INFINITE );
  1344. if ( ret == WAIT_ABANDONED || ret == WAIT_TIMEOUT )
  1345. {
  1346. Error( "VCR_WaitForSingleObject: got inconsistent value on playback." );
  1347. }
  1348. return ret;
  1349. }
  1350. else
  1351. {
  1352. // Return whatever the function returned while it was recording.
  1353. return (val == 2) ? WAIT_ABANDONED : WAIT_TIMEOUT;
  1354. }
  1355. }
  1356. }
  1357. unsigned long VCR_WaitForMultipleObjects( uint32 nHandles, const void **pHandles, int bWaitAll, uint32 timeout )
  1358. {
  1359. // Preamble.
  1360. if ( !IsVCRModeEnabledForThisThread() )
  1361. return Wrap_WaitForMultipleObjects( nHandles, pHandles, bWaitAll, timeout );
  1362. // TODO:
  1363. AssertMsg( 0, "Need to implement VCR_WaitForMultipleObjects" );
  1364. return 0;
  1365. }
  1366. void VCR_EnterCriticalSection( void *pInputCS )
  1367. {
  1368. CRITICAL_SECTION *pCS = (CRITICAL_SECTION*)pInputCS;
  1369. if ( !IsVCRModeEnabledForThisThread() )
  1370. {
  1371. Wrap_EnterCriticalSection( pCS );
  1372. return;
  1373. }
  1374. // While recording, let's get the critical section first.
  1375. if ( g_VCRMode == VCR_Record )
  1376. {
  1377. Wrap_EnterCriticalSection( pCS );
  1378. }
  1379. VCR_THREADSAFE;
  1380. VCR_Event( VCREvent_EnterCriticalSection );
  1381. if ( g_VCRMode == VCR_Playback )
  1382. {
  1383. // When playing back, we want to grab the CS -after- the event has been read out, because it means that
  1384. // we're the only thread that is at this spot now. If we tried to grab the CS before calling VCR_Event,
  1385. // then it might let the wrong thread have the CS on playback.
  1386. Wrap_EnterCriticalSection( pCS );
  1387. }
  1388. }
  1389. // ---------------------------------------------------------------------- //
  1390. // The global VCR interface.
  1391. // ---------------------------------------------------------------------- //
  1392. VCR_t g_VCR =
  1393. {
  1394. VCR_Start,
  1395. VCR_End,
  1396. VCR_GetVCRTraceInterface,
  1397. VCR_GetMode,
  1398. VCR_SetEnabled,
  1399. VCR_SyncToken,
  1400. VCR_Hook_Sys_FloatTime,
  1401. VCR_Hook_PeekMessage,
  1402. VCR_Hook_RecordGameMsg,
  1403. VCR_Hook_RecordEndGameMsg,
  1404. VCR_Hook_PlaybackGameMsg,
  1405. VCR_Hook_recvfrom,
  1406. VCR_Hook_GetCursorPos,
  1407. VCR_Hook_ScreenToClient,
  1408. VCR_Hook_Cmd_Exec,
  1409. VCR_Hook_GetCommandLine,
  1410. VCR_Hook_RegOpenKeyEx,
  1411. VCR_Hook_RegSetValueEx,
  1412. VCR_Hook_RegQueryValueEx,
  1413. VCR_Hook_RegCreateKeyEx,
  1414. VCR_Hook_RegCloseKey,
  1415. VCR_Hook_GetNumberOfConsoleInputEvents,
  1416. VCR_Hook_ReadConsoleInput,
  1417. VCR_Hook_LocalTime,
  1418. VCR_Hook_GetKeyState,
  1419. VCR_Hook_recv,
  1420. VCR_Hook_send,
  1421. VCR_GenericRecord,
  1422. VCR_GenericPlayback,
  1423. VCR_GenericValue,
  1424. VCR_GetPercentCompleted,
  1425. VCR_CreateThread,
  1426. VCR_WaitForSingleObject,
  1427. VCR_EnterCriticalSection,
  1428. VCR_Hook_Time,
  1429. VCR_GenericString,
  1430. VCR_GenericValueVerify,
  1431. VCR_WaitForMultipleObjects,
  1432. };
  1433. VCR_t *g_pVCR = &g_VCR;
  1434. #endif // NO_VCR