diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /tier0/vcrmode_posix.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'tier0/vcrmode_posix.cpp')
| -rw-r--r-- | tier0/vcrmode_posix.cpp | 970 |
1 files changed, 970 insertions, 0 deletions
diff --git a/tier0/vcrmode_posix.cpp b/tier0/vcrmode_posix.cpp new file mode 100644 index 0000000..9e0003e --- /dev/null +++ b/tier0/vcrmode_posix.cpp @@ -0,0 +1,970 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include <time.h> +#include <assert.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include "tier0/threadtools.h" + +#define PROTECTED_THINGS_DISABLE +#include "tier0/vcrmode.h" +#include "tier0/dbg.h" +#include "extendedtrace.h" + +// FIXME: We totally have a bad tier dependency here +#include "inputsystem/InputEnums.h" + +#define VCRFILE_VERSION 2 + +#define VCR_RuntimeAssert(x) VCR_RuntimeAssertFn(x, #x) +#define PvAlloc malloc + +bool g_bExpectingWindowProcCalls = false; + +IVCRHelpers *g_pHelpers = 0; + +FILE *g_pVCRFile = NULL; +VCRMode_t g_VCRMode = VCR_Disabled; + +VCRMode_t g_OldVCRMode = (VCRMode_t)-1; // Stored temporarily between SetEnabled(0)/SetEnabled(1) blocks. +int g_iCurEvent = 0; + +int g_CurFilePos = 0; // So it knows when we're done playing back. +int g_FileLen = 0; + +VCREvent g_LastReadEvent = (VCREvent)-1; // Last VCR_ReadEvent() call. + +int g_bVCREnabled = 0; + + +// ---------------------------------------------------------------------- // +// Internal functions. +// ---------------------------------------------------------------------- // +static void VCR_Error( const char *pFormat, ... ) +{ +#if defined( _DEBUG ) + DebuggerBreak(); +#endif + char str[256]; + va_list marker; + va_start( marker, pFormat ); + _snprintf( str, sizeof( str ), pFormat, marker ); + va_end( marker ); + + g_pHelpers->ErrorMessage( str ); + VCREnd(); +} + +static void VCR_RuntimeAssertFn(int bAssert, char const *pStr) +{ + if(!bAssert) + { + VCR_Error( "*** VCR ASSERT FAILED: %s ***\n", pStr ); + } +} + +static void VCR_Read(void *pDest, int size) +{ + if(!g_pVCRFile) + { + memset(pDest, 0, size); + return; + } + + fread(pDest, 1, size, g_pVCRFile); + + g_CurFilePos += size; + + VCR_RuntimeAssert(g_CurFilePos <= g_FileLen); + + if(g_CurFilePos >= g_FileLen) + { + VCREnd(); + } +} + +template<class T> +static void VCR_ReadVal(T &val) +{ + VCR_Read(&val, sizeof(val)); +} + +static void VCR_Write(void const *pSrc, int size) +{ + fwrite(pSrc, 1, size, g_pVCRFile); + fflush(g_pVCRFile); +} + +template<class T> +static void VCR_WriteVal(T &val) +{ + VCR_Write(&val, sizeof(val)); +} + + +// Hook from ExtendedTrace.cpp +bool g_bTraceRead = false; +void OutputDebugStringFormat( const char *pMsg, ... ) +{ + char msg[4096]; + va_list marker; + va_start( marker, pMsg ); + _vsnprintf( msg, sizeof( msg )-1, pMsg, marker ); + va_end( marker ); + int len = strlen( msg ); + + if ( g_bTraceRead ) + { + char tempData[4096]; + int tempLen; + VCR_ReadVal( tempLen ); + VCR_RuntimeAssert( tempLen <= sizeof( tempData ) ); + VCR_Read( tempData, tempLen ); + tempData[tempLen] = 0; + fprintf( stderr, "FILE: " ); + fprintf( stderr, "%s", tempData ); + + VCR_RuntimeAssert( memcmp( msg, tempData, len ) == 0 ); + } + else + { + VCR_WriteVal( len ); + VCR_Write( msg, len ); + } +} + + +static VCREvent VCR_ReadEvent() +{ + g_bTraceRead = true; + //STACKTRACE(); + + char event; + VCR_Read(&event, 1); + g_LastReadEvent = (VCREvent)event; + + return (VCREvent)event; +} + + +static void VCR_WriteEvent(VCREvent event) +{ + g_bTraceRead = false; + //STACKTRACE(); + + // Write a stack trace. + char cEvent = (char)event; + VCR_Write(&cEvent, 1); +} + +static void VCR_IncrementEvent() +{ + ++g_iCurEvent; +} + +static void VCR_Event(VCREvent type) +{ + if(g_VCRMode == VCR_Disabled) + return; + + VCR_IncrementEvent(); + if(g_VCRMode == VCR_Record) + { + VCR_WriteEvent(type); + } + else + { + VCREvent currentEvent = VCR_ReadEvent(); + VCR_RuntimeAssert( currentEvent == type ); + } +} + + +// ---------------------------------------------------------------------- // +// VCR trace interface. +// ---------------------------------------------------------------------- // + +class CVCRTrace : public IVCRTrace +{ +public: + virtual VCREvent ReadEvent() + { + return VCR_ReadEvent(); + } + + virtual void Read( void *pDest, int size ) + { + VCR_Read( pDest, size ); + } +}; + +static CVCRTrace g_VCRTrace; + + +// ---------------------------------------------------------------------- // +// VCR interface. +// ---------------------------------------------------------------------- // + +static int VCR_Start( char const *pFilename, bool bRecord, IVCRHelpers *pHelpers ) +{ + unsigned long version; + + g_pHelpers = pHelpers; + + VCREnd(); + + EXTENDEDTRACEINITIALIZE( "/tmp/hl2" ); + + g_OldVCRMode = (VCRMode_t)-1; + if(bRecord) + { + g_pVCRFile = fopen( pFilename, "wb" ); + if( g_pVCRFile ) + { + // Write the version. + version = VCRFILE_VERSION; + VCR_Write(&version, sizeof(version)); + + g_VCRMode = VCR_Record; + return true; + } + else + { + return false; + } + } + else + { + g_pVCRFile = fopen( pFilename, "rb" ); + if( g_pVCRFile ) + { + // Get the file length. + fseek(g_pVCRFile, 0, SEEK_END); + g_FileLen = ftell(g_pVCRFile); + fseek(g_pVCRFile, 0, SEEK_SET); + g_CurFilePos = 0; + + // Verify the file version. + VCR_Read(&version, sizeof(version)); + if(version != VCRFILE_VERSION) + { + assert(!"VCR_Start: invalid file version"); + VCREnd(); + return FALSE; + } + + g_VCRMode = VCR_Playback; + return TRUE; + } + else + { + return FALSE; + } + } +} + + +static void VCR_End() +{ + if(g_pVCRFile) + { + fclose(g_pVCRFile); + g_pVCRFile = NULL; + } + + g_VCRMode = VCR_Disabled; + EXTENDEDTRACEUNINITIALIZE(); +} + + +static IVCRTrace* VCR_GetVCRTraceInterface() +{ + return &g_VCRTrace; +} + + +static VCRMode_t VCR_GetMode() +{ + return g_VCRMode; +} + + +static void VCR_SetEnabled(int bEnabled) +{ + if(bEnabled) + { + VCR_RuntimeAssert(g_OldVCRMode != (VCRMode_t)-1); + g_VCRMode = g_OldVCRMode; + g_OldVCRMode = (VCRMode_t)-1; + } + else + { + VCR_RuntimeAssert(g_OldVCRMode == (VCRMode_t)-1); + g_OldVCRMode = g_VCRMode; + g_VCRMode = VCR_Disabled; + } +} + + +static void VCR_SyncToken(char const *pToken) +{ + unsigned char len; + + VCR_Event(VCREvent_SyncToken); + + if(g_VCRMode == VCR_Record) + { + int intLen = strlen( pToken ); + assert( intLen <= 255 ); + + len = (unsigned char)intLen; + + VCR_Write(&len, 1); + VCR_Write(pToken, len); + } + else if(g_VCRMode == VCR_Playback) + { + char test[256]; + + VCR_Read(&len, 1); + VCR_Read(test, len); + + VCR_RuntimeAssert( len == (unsigned char)strlen(pToken) ); + VCR_RuntimeAssert( memcmp(pToken, test, len) == 0 ); + } +} + + +static double VCR_Hook_Sys_FloatTime(double time) +{ + VCR_Event(VCREvent_Sys_FloatTime); + + if(g_VCRMode == VCR_Record) + { + VCR_Write(&time, sizeof(time)); + } + else if(g_VCRMode == VCR_Playback) + { + VCR_Read(&time, sizeof(time)); + } + + return time; +} + + + +static int VCR_Hook_PeekMessage( + struct tagMSG *msg, + void *hWnd, + unsigned int wMsgFilterMin, + unsigned int wMsgFilterMax, + unsigned int wRemoveMsg + ) +{ + Assert( "VCR_Hook_PeekMessage unsupported" ); + return 0; +} + + +void VCR_Hook_RecordGameMsg( const InputEvent_t& event ) +{ + if ( g_VCRMode == VCR_Record ) + { + VCR_Event( VCREvent_GameMsg ); + + char val = 1; + VCR_WriteVal( val ); + VCR_WriteVal( event.m_nType ); + VCR_WriteVal( event.m_nData ); + VCR_WriteVal( event.m_nData2 ); + VCR_WriteVal( event.m_nData3 ); + } +} + + +void VCR_Hook_RecordEndGameMsg() +{ + if ( g_VCRMode == VCR_Record ) + { + VCR_Event( VCREvent_GameMsg ); + char val = 0; + VCR_WriteVal( val ); // record that there are no more messages. + } +} + + +bool VCR_Hook_PlaybackGameMsg( InputEvent_t* pEvent ) +{ + if ( g_VCRMode == VCR_Playback ) + { + VCR_Event( VCREvent_GameMsg ); + + char bMsg; + VCR_ReadVal( bMsg ); + if ( bMsg ) + { + VCR_ReadVal( pEvent->m_nType ); + VCR_ReadVal( pEvent->m_nData ); + VCR_ReadVal( pEvent->m_nData2 ); + VCR_ReadVal( pEvent->m_nData3 ); + return true; + } + } + + return false; +} + + + +static void VCR_Hook_GetCursorPos(struct tagPOINT *pt) +{ + Assert( "VCR_Hook_GetCursorPos unsupported" ); +} + + +static void VCR_Hook_ScreenToClient(void *hWnd, struct tagPOINT *pt) +{ + Assert( "VCR_Hook_GetCursorPos unsupported" ); +} + + +static int VCR_Hook_recvfrom(int s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen) +{ + VCR_Event(VCREvent_recvfrom); + + int ret; + if ( g_VCRMode == VCR_Playback ) + { + // Get the result from our file. + VCR_Read(&ret, sizeof(ret)); + if(ret == -1) + { + int err; + VCR_ReadVal(err); + errno = err; + } + else + { + VCR_Read( buf, ret ); + + char bFrom; + VCR_ReadVal( bFrom ); + if ( bFrom ) + { + VCR_Read( from, *fromlen ); + } + } + } + else + { + ret = recvfrom(s, buf, len, flags, from, (socklen_t *)(fromlen)); + + if ( g_VCRMode == VCR_Record ) + { + // Record the result. + VCR_Write(&ret, sizeof(ret)); + if(ret == -1) + { + VCR_WriteVal(errno); + } + else + { + VCR_Write( buf, ret ); + + char bFrom = !!from; + VCR_WriteVal( bFrom ); + if ( bFrom ) + VCR_Write( from, *fromlen ); + } + } + } + + return ret; +} + + +static void VCR_Hook_Cmd_Exec(char **f) +{ + VCR_Event(VCREvent_Cmd_Exec); + + if(g_VCRMode == VCR_Playback) + { + int len; + + VCR_Read(&len, sizeof(len)); + if(len == -1) + { + *f = NULL; + } + else + { + *f = (char*)PvAlloc(len); + VCR_Read(*f, len); + } + } + else if(g_VCRMode == VCR_Record) + { + int len; + char *str = *f; + + if(str) + { + len = strlen(str)+1; + VCR_Write(&len, sizeof(len)); + VCR_Write(str, len); + } + else + { + len = -1; + VCR_Write(&len, sizeof(len)); + } + } +} + +#define MAX_LINUX_CMDLINE 512 +static char linuxCmdline[ MAX_LINUX_CMDLINE +7 ]; // room for -steam + +const char * BuildCmdLine( int argc, char **argv, bool fAddSteam ) +{ + int len; + int i; + + for (len = 0, i = 0; i < argc; i++) + { + len += strlen(argv[i]); + } + + if ( len > MAX_LINUX_CMDLINE ) + { + printf( "command line too long, %i max\n", MAX_LINUX_CMDLINE ); + exit(-1); + return ""; + } + + linuxCmdline[0] = '\0'; + for ( i = 0; i < argc; i++ ) + { + if ( i > 0 ) + { + strcat( linuxCmdline, " " ); + } + strcat( linuxCmdline, argv[ i ] ); + } + + if( fAddSteam ) + { + strcat( linuxCmdline, " -steam" ); + } + + return linuxCmdline; +} + +char *GetCommandLine() +{ + return linuxCmdline; +} + + +static char* VCR_Hook_GetCommandLine() +{ + VCR_Event(VCREvent_CmdLine); + + int len; + char *ret; + + if(g_VCRMode == VCR_Playback) + { + VCR_Read(&len, sizeof(len)); + ret = new char[len]; + VCR_Read(ret, len); + } + else + { + ret = GetCommandLine(); + + if(g_VCRMode == VCR_Record) + { + len = strlen(ret) + 1; + VCR_WriteVal(len); + VCR_Write(ret, len); + } + } + + return ret; +} + + +static long VCR_Hook_RegOpenKeyEx( void *hKey, const char *lpSubKey, unsigned long ulOptions, unsigned long samDesired, void *pHKey ) +{ + Assert( "VCR_Hook_RegOpenKeyEx unsupported" ); + return 0; +} + + +static long VCR_Hook_RegSetValueEx(void *hKey, char const *lpValueName, unsigned long Reserved, unsigned long dwType, unsigned char const *lpData, unsigned long cbData) +{ + Assert( "VCR_Hook_RegSetValueEx unsupported" ); + return 0; +} + + +static long VCR_Hook_RegQueryValueEx(void *hKey, char const *lpValueName, unsigned long *lpReserved, unsigned long *lpType, unsigned char *lpData, unsigned long *lpcbData) +{ + Assert( "VCR_Hook_RegQueryValueEx unsupported" ); + return 0; +} + + +static long VCR_Hook_RegCreateKeyEx(void *hKey, char const *lpSubKey, unsigned long Reserved, char *lpClass, unsigned long dwOptions, + unsigned long samDesired, void *lpSecurityAttributes, void *phkResult, unsigned long *lpdwDisposition) +{ + Assert( "VCR_Hook_RegCreateKeyEx unsupported" ); + return 0; +} + + +static void VCR_Hook_RegCloseKey(void *hKey) +{ + Assert( "VCR_Hook_RegCloseKey unsupported" ); +} + + +int VCR_Hook_GetNumberOfConsoleInputEvents( void *hInput, unsigned long *pNumEvents ) +{ + VCR_Event( VCREvent_GetNumberOfConsoleInputEvents ); + + char ret; + if ( g_VCRMode == VCR_Playback ) + { + VCR_ReadVal( ret ); + VCR_ReadVal( *pNumEvents ); + } + else + { + ret = 1; + + if ( g_VCRMode == VCR_Record ) + { + VCR_WriteVal( ret ); + VCR_WriteVal( *pNumEvents ); + } + } + + return ret; +} + + +int VCR_Hook_ReadConsoleInput( void *hInput, void *pRecs, int nMaxRecs, unsigned long *pNumRead ) +{ + Assert( "VCR_Hook_ReadConsoleInput unsupported" ); + return 0; +} + + +void VCR_Hook_LocalTime( struct tm *today ) +{ + // We just provide a wrapper on this function so we can protect access to time() everywhere. + time_t ltime; + time( <ime ); + tm *pTime = localtime( <ime ); + memcpy( today, pTime, sizeof( *today ) ); +} + + +short VCR_Hook_GetKeyState( int nVirtKey ) +{ + Assert( "VCREvent_GetKeyState unsupported" ); + return 0; +} + +void VCR_GenericRecord( const char *pEventName, const void *pData, int len ) +{ + VCR_Event( VCREvent_Generic ); + + if ( g_VCRMode != VCR_Record ) + Error( "VCR_GenericRecord( %s ): not recording a VCR file", pEventName ); + + // Write the event name (or 255 if none). + int nameLen = 255; + if ( pEventName ) + { + nameLen = strlen( pEventName ) + 1; + if ( nameLen >= 255 ) + { + VCR_Error( "VCR_GenericRecord( %s ): nameLen too long (%d)", pEventName, nameLen ); + return; + } + } + unsigned char ucNameLen = (unsigned char)nameLen; + VCR_WriteVal( ucNameLen ); + VCR_Write( pEventName, ucNameLen ); + + // Write the data. + VCR_WriteVal( len ); + VCR_Write( pData, len ); +} + + +int VCR_GenericPlayback( const char *pEventName, void *pOutData, int maxLen, bool bForceSameLen ) +{ + VCR_Event( VCREvent_Generic ); + + if ( g_VCRMode != VCR_Playback ) + Error( "VCR_Playback( %s ): not playing back a VCR file", pEventName ); + + unsigned char nameLen; + VCR_ReadVal( nameLen ); + if ( nameLen != 255 ) + { + char testName[512]; + VCR_Read( testName, nameLen ); + if ( strcmp( pEventName, testName ) != 0 ) + { + VCR_Error( "VCR_GenericPlayback( %s ) - event name does not match '%s'", pEventName, testName ); + return 0; + } + } + + int dataLen; + VCR_ReadVal( dataLen ); + if ( dataLen > maxLen ) + { + VCR_Error( "VCR_GenericPlayback( %s ) - generic data too long (greater than maxLen: %d)", pEventName, maxLen ); + return 0; + } + else if ( bForceSameLen && dataLen != maxLen ) + { + VCR_Error( "VCR_GenericPlayback( %s ) - data size in file (%d) different than desired (%d)", pEventName, dataLen, maxLen ); + return 0; + } + + VCR_Read( pOutData, dataLen ); + return dataLen; +} + + +void VCR_GenericValue( const char *pEventName, void *pData, int maxLen ) +{ + if ( g_VCRMode == VCR_Record ) + VCR_GenericRecord( pEventName, pData, maxLen ); + else if ( g_VCRMode == VCR_Playback ) + VCR_GenericPlayback( pEventName, pData, maxLen, true ); +} + + +static int VCR_Hook_recv(int s, char *buf, int len, int flags) +{ + VCR_Event(VCREvent_recv); + + int ret; + if ( g_VCRMode == VCR_Playback ) + { + // Get the result from our file. + VCR_Read(&ret, sizeof(ret)); + if(ret == -1) + { + int err; + VCR_ReadVal(err); + errno = err; + } + else + { + VCR_Read( buf, ret ); + } + } + else + { + ret = recv( s, buf, len, flags ); + + if ( g_VCRMode == VCR_Record ) + { + // Record the result. + VCR_Write(&ret, sizeof(ret)); + if(ret == -1) + { + VCR_WriteVal(errno); + } + else + { + VCR_Write( buf, ret ); + } + } + } + + return ret; +} + +static int VCR_Hook_send(int s, const char *buf, int len, int flags) +{ + VCR_Event(VCREvent_send); + + int ret; + if ( g_VCRMode == VCR_Playback ) + { + // Get the result from our file. + VCR_Read(&ret, sizeof(ret)); + if(ret == -1) + { + int err; + VCR_ReadVal(err); + errno = err; + } + } + else + { + ret = send( s, buf, len, flags ); + + if ( g_VCRMode == VCR_Record ) + { + // Record the result. + VCR_Write(&ret, sizeof(ret)); + if(ret == -1) + { + VCR_WriteVal(errno); + } + } + } + + return ret; +} + + + +double VCR_GetPercentCompleted() +{ + if ( g_VCRMode == VCR_Playback ) + { + return (double)g_CurFilePos / g_FileLen; + } + else + { + return 0; + } +} + +void* VCR_CreateThread( + void *lpThreadAttributes, + unsigned long dwStackSize, + void *lpStartAddress, + void *lpParameter, + unsigned long dwCreationFlags, + unsigned long *lpThreadID ) +{ + return CreateSimpleThread( (ThreadFunc_t)lpStartAddress, lpParameter, lpThreadID, 0 ); +} + + + +unsigned long VCR_WaitForSingleObject( + void *handle, + unsigned long dwMilliseconds ) +{ + return -1; +} + +unsigned long VCR_WaitForMultipleObjects( uint32 nHandles, const void **pHandles, int bWaitAll, uint32 timeout ) +{ + return -1; +} + +void VCR_EnterCriticalSection( void *pInputCS ) +{ +} + + +void VCR_Hook_Time( long *today ) +{ + // We just provide a wrapper on this function so we can protect access to time() everywhere. + // NOTE: For 64-bit systems we should eventually get a function that takes a time_t, but we should have + // until about 2038 to do that before we overflow a long. + time_t curTime; + time( &curTime ); + + VCR_Event( VCREvent_Time ); + if ( g_VCRMode == VCR_Playback ) + { + VCR_Read( &curTime, sizeof( curTime ) ); + } + else if ( g_VCRMode == VCR_Record ) + { + VCR_Write( &curTime, sizeof( curTime ) ); + } + + *today = (long)curTime; +} + + + +void VCR_GenericString( const char *pEventName, const char *pString ) +{ +} + + +void VCR_GenericValueVerify( const tchar *pEventName, const void *pData, int maxLen ) +{ +} + + + +// ---------------------------------------------------------------------- // +// The global VCR interface. +// ---------------------------------------------------------------------- // + +VCR_t g_VCR = +{ + VCR_Start, + VCR_End, + VCR_GetVCRTraceInterface, + VCR_GetMode, + VCR_SetEnabled, + VCR_SyncToken, + VCR_Hook_Sys_FloatTime, + VCR_Hook_PeekMessage, + VCR_Hook_RecordGameMsg, + VCR_Hook_RecordEndGameMsg, + VCR_Hook_PlaybackGameMsg, + VCR_Hook_recvfrom, + VCR_Hook_GetCursorPos, + VCR_Hook_ScreenToClient, + VCR_Hook_Cmd_Exec, + VCR_Hook_GetCommandLine, + VCR_Hook_RegOpenKeyEx, + VCR_Hook_RegSetValueEx, + VCR_Hook_RegQueryValueEx, + VCR_Hook_RegCreateKeyEx, + VCR_Hook_RegCloseKey, + VCR_Hook_GetNumberOfConsoleInputEvents, + VCR_Hook_ReadConsoleInput, + VCR_Hook_LocalTime, + VCR_Hook_GetKeyState, + VCR_Hook_recv, + VCR_Hook_send, + VCR_GenericRecord, + VCR_GenericPlayback, + VCR_GenericValue, + + VCR_GetPercentCompleted, + VCR_CreateThread, + VCR_WaitForSingleObject, + VCR_EnterCriticalSection, + VCR_Hook_Time, + VCR_GenericString, + VCR_GenericValueVerify, + VCR_WaitForMultipleObjects, + +}; + +VCR_t *g_pVCR = &g_VCR; + + |