|
|
|
//========= 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((void const*)&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];
|
|
|
|
uint 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;
|
|
|
|
|
|
|
|
|