301 lines
12 KiB
C
301 lines
12 KiB
C
#ifndef _CEGCLIENTWRAPPER_H_
|
|
#define _CEGCLIENTWRAPPER_H_
|
|
|
|
#pragma once
|
|
|
|
#include "xbox/xboxstubs.h" // defines DWORD, DWORD_PTR, etc.
|
|
|
|
// required by cegclient.h, but no need to pull in all of windef.h
|
|
#if defined( PLATFORM_64BITS )
|
|
typedef int64 INT_PTR;
|
|
#else
|
|
typedef int32 INT_PTR;
|
|
#endif
|
|
|
|
typedef unsigned char BYTE;
|
|
typedef BYTE *LPBYTE;
|
|
typedef int32 INT32;
|
|
typedef uint32 DWORD32;
|
|
typedef uint64 DWORD64;
|
|
|
|
|
|
#define CEG_GET_CONSTANT_VALUE_AVOID_CEG( fn_name ) fn_name() // makes it easy for us to turn off CEG checks if one of the CEG'ed functions has a bigger impact on perf than expected
|
|
|
|
// To disable CEG in your build for one or more modules, add "/NO_CEG" to your VPC parameters
|
|
#if (!defined( USE_CEG ) || ( !defined( CLIENT_DLL ) && !defined( GAME_DLL ) ) )
|
|
|
|
#define CEG_NOINLINE
|
|
|
|
// Stub out functions if CEG is disabled
|
|
#define STEAMWORKS_REGISTERTHREAD() (true)
|
|
#define STEAMWORKS_UNREGISTERTHREAD() (true)
|
|
#define STEAMWORKS_INITCEGLIBRARY() (true)
|
|
#define STEAMWORKS_TERMCEGLIBRARY() (true)
|
|
#define STEAMWORKS_TESTSECRET()
|
|
#define STEAMWORKS_TESTSECRETALWAYS()
|
|
#define STEAMWORKS_SELFCHECK()
|
|
#define STEAMWORKS_TESTSECRET_AMORTIZE( period )
|
|
#define STEAMWORKS_TESTSECRETALWAYS_AMORTIZE( period )
|
|
#define STEAMWORKS_SELFCHECK_AMORTIZE( period )
|
|
#define RANDOM_CEG_TEST_SECRET()
|
|
#define RANDOM_CEG_TEST_SECRET_PERIOD( testPeriod, checkPeriod )
|
|
#define RANDOM_CEG_TEST_SECRET_LINE_PERIOD( testPeriod, testLinePeriod, checkPeriod, checkLinePeriod )
|
|
#define CEG_PROTECT_FUNCTION( unquotedSymbolHelper )
|
|
#define CEG_ENCRYPT_FUNCTION( unquotedSymbolHelper )
|
|
#define CEG_PROTECT_MEMBER_FUNCTION( unquotedSymbolHelper )
|
|
#define CEG_PROTECT_VIRTUAL_FUNCTION( unquotedSymbolHelper )
|
|
#define CEG_PROTECT_STATIC_MEMBER_FUNCTION( unquotedSymbolHelper, fn_name )
|
|
#define CEG_DECLARE_CONSTANT_FUNCTION( fn_name ) extern DWORD __cdecl fn_name();
|
|
#define CEG_DECLARE_CONSTANT_FLOAT_FUNCTION( fn_name ) extern float __cdecl fn_name();
|
|
#define CEG_DEFINE_CONSTANT_FUNCTION( fn_name, val ) DWORD __cdecl fn_name() { return val; }
|
|
#define CEG_DEFINE_CONSTANT_FLOAT_FUNCTION( fn_name, val ) float __cdecl fn_name() { return val; }
|
|
#define CEG_GET_CONSTANT_VALUE( fn_name ) fn_name()
|
|
#define CEG_GCV_PRE()
|
|
#define CEG_GCV_POST()
|
|
|
|
#else // CEG is enabled
|
|
|
|
#if defined( _GAMECONSOLE ) || defined( POSIX ) || defined( NO_STEAM )
|
|
#error
|
|
#endif
|
|
|
|
#include "cegclient.h"
|
|
|
|
// use this on any functions where we use CEG_PROTECT to ensure they don't get inlined in the CEG build
|
|
#define CEG_NOINLINE __declspec(noinline)
|
|
|
|
// wrapping CEG calls in macros allows us to hide interface changes and centrally add checks and perf instrumentation
|
|
#define STEAMWORKS_REGISTERTHREAD() Steamworks_RegisterThread()
|
|
#define STEAMWORKS_UNREGISTERTHREAD() Steamworks_UnRegisterThread()
|
|
#define STEAMWORKS_INITCEGLIBRARY() Steamworks_InitCEGLibrary()
|
|
#define STEAMWORKS_TERMCEGLIBRARY() Steamworks_TermCEGLibrary()
|
|
#define CEG_DECLARE_CONSTANT_FUNCTION( fn_name ) CEG_Declare_Constant_Function( fn_name )
|
|
#define CEG_DECLARE_CONSTANT_FLOAT_FUNCTION( fn_name ) CEG_Declare_ConstantFloat_Function( fn_name )
|
|
#define CEG_DEFINE_CONSTANT_FUNCTION( fn_name, val ) CEG_Define_Constant_Function( fn_name, val )
|
|
#define CEG_DEFINE_CONSTANT_FLOAT_FUNCTION( fn_name, val ) CEG_Define_ConstantFloat_Function2( fn_name, val )
|
|
#define CEG_GET_CONSTANT_VALUE( fn_name ) CEG_GetConstantValue( fn_name )
|
|
|
|
// seeded with large primes at frequencies that lead to approximately one check every 30 seconds
|
|
#define RANDOM_CEG_TEST_SECRET() \
|
|
RANDOM_CEG_TEST_SECRET_LINE_PERIOD( 128449, 19, 1102441, 397 )
|
|
|
|
#define RANDOM_CEG_TEST_SECRET_PERIOD( testPeriod, checkPeriod ) \
|
|
RANDOM_CEG_TEST_SECRET_LINE_PERIOD( testPeriod, 0, checkPeriod, 0 )
|
|
|
|
// uncomment here to enable dev messages per CEG call, including CEG performance numbers
|
|
//#define PROFILE_CEG
|
|
#if !defined( PROFILE_CEG )
|
|
|
|
#define CEG_PROFILE_DECL
|
|
#define CEG_PROFILE_BLOCK( fn_name, fn_type, identifier ) fn_name;
|
|
#define CEG_GCV_PRE()
|
|
#define CEG_GCV_POST()
|
|
|
|
#else // defined( PROFILE_CEG )
|
|
|
|
#include "tier0\fasttimer.h"
|
|
|
|
extern CAverageCycleCounter allCEG;
|
|
extern CAverageCycleCounter allTestSecret;
|
|
extern CAverageCycleCounter allSelfCheck;
|
|
extern CAverageCycleCounter allProtectMember;
|
|
extern CAverageCycleCounter allProtectVirtual;
|
|
|
|
#if !defined( MEMOVERRIDE_MODULE )
|
|
#define MEMOVERRIDE_MODULE UNKNOWN_MODULE
|
|
#endif
|
|
|
|
#define _MKSTRING(arg) #arg
|
|
#define MKSTRING(arg) _MKSTRING(arg)
|
|
|
|
#define CEG_PROFILE_BLOCK( fn_name, fn_type, identifier ) \
|
|
static unsigned long s_##fn_type##Hits = 0; \
|
|
s_##fn_type##Hits++; \
|
|
static CAverageCycleCounter this##fn_type; \
|
|
{ \
|
|
CAverageTimeMarker this##fn_type##Marker( &this##fn_type ); \
|
|
CAverageTimeMarker all##fn_type##Marker( &all##fn_type ); \
|
|
CAverageTimeMarker allCEGMarker( &allCEG ); \
|
|
fn_name; \
|
|
} \
|
|
DevMsg( "%s: %s%s %s:%d - %.2fms avg (%.2fms total, %.2fms peak, %d iters), %lu/%lu = %.5f%% hit rate.\n" \
|
|
"Avg CEG %s Cost: %.2fms Avg CEG Cost: %.2fms\n", \
|
|
MKSTRING(MEMOVERRIDE_MODULE), identifier, #fn_type, __FILE__, __LINE__, \
|
|
this##fn_type.GetAverageMilliseconds(), this##fn_type.GetTotalMilliseconds(), this##fn_type.GetPeakMilliseconds(), this##fn_type.GetIters(), \
|
|
s_##fn_type##Hits, s_tests, \
|
|
float( s_##fn_type##Hits * 100 ) / float( s_tests ), \
|
|
#fn_type, \
|
|
all##fn_type.GetAverageMilliseconds(), \
|
|
allCEG.GetAverageMilliseconds() );
|
|
|
|
#define CEG_PROFILE_DECL \
|
|
static unsigned long int s_tests = 0; \
|
|
s_tests++;
|
|
|
|
#define CEG_GCV_PRE() \
|
|
CFastTimer __FUNCTION__##Timer; \
|
|
__FUNCTION__##Timer.Start();
|
|
|
|
#define CEG_GCV_POST() \
|
|
__FUNCTION__##Timer.End(); \
|
|
if( __FUNCTION__##Timer.GetDuration().GetMillisecondsF() > 0.5f ) \
|
|
{ \
|
|
DevMsg( "%s: GetConstantValue in %s : %.2fms\n", \
|
|
MKSTRING(MEMOVERRIDE_MODULE), __FUNCTION__, __FUNCTION__##Timer.GetDuration().GetMillisecondsF() ); \
|
|
}
|
|
|
|
#endif // PROFILE_CEG off/on
|
|
|
|
// the below macros allow us to turn profiling on or off with a single #ifdef, using the same underlying implementation in each case
|
|
#define RANDOM_CEG_TEST_SECRET_LINE_PERIOD( testPeriod, testLinePeriod, checkPeriod, checkLinePeriod ) \
|
|
do { \
|
|
const unsigned long tp = testPeriod + testLinePeriod * __LINE__; \
|
|
const unsigned long cp = checkPeriod + checkLinePeriod * __LINE__; \
|
|
static unsigned long s_nCegCounter = __LINE__ ^ __COUNTER__ ^ reinterpret_cast<int>( &s_nCegCounter ); \
|
|
++s_nCegCounter; \
|
|
CEG_PROFILE_DECL; \
|
|
if ( !( ( s_nCegCounter ) % ( tp ) ) ) \
|
|
{ \
|
|
CEG_PROFILE_BLOCK( Steamworks_TestSecretAlways(), TestSecret, "Random " ); \
|
|
} \
|
|
else if ( !( ( s_nCegCounter ) % ( cp ) ) ) \
|
|
{ \
|
|
CEG_PROFILE_BLOCK( Steamworks_SelfCheck(), SelfCheck, "Random " ); \
|
|
} \
|
|
} \
|
|
while( 0 );
|
|
|
|
//
|
|
// Can't grab metrics - although this is placed within the scope a function -
|
|
// The effect is to change calls to the function to first execute some CEG code,
|
|
// and this CEG code is not available within this scope.
|
|
// The CEG code computes the address of the containing function, and jumps to it.
|
|
//
|
|
#define CEG_PROTECT_FUNCTION( unquotedSymbolHelper ) \
|
|
CEG_ProtectFunction( );
|
|
|
|
//
|
|
// Can't grab metrics - although this is placed within the scope a function -
|
|
// The effect is to change calls to the function to first execute some CEG code,
|
|
// and this CEG code is not available within this scope.
|
|
//
|
|
// The body of the function that contains this macro is encrypted, and the CEG code
|
|
// decrypts the opcodes and places them in executable memory, and then executes it.
|
|
// Upon return, the executable memory is released.
|
|
//
|
|
#define CEG_ENCRYPT_FUNCTION( unquotedSymbolHelper ) \
|
|
CEG_EncryptFunction( );
|
|
|
|
#define CEG_PROTECT_MEMBER_FUNCTION( unquotedSymbolHelper ) \
|
|
do { \
|
|
CEG_PROFILE_DECL; \
|
|
CEG_PROFILE_BLOCK( CEG_ProtectFunction( ), ProtectMember, "" ); \
|
|
} \
|
|
while( 0 );
|
|
|
|
#define CEG_PROTECT_VIRTUAL_FUNCTION( unquotedSymbolHelper ) \
|
|
do { \
|
|
CEG_PROFILE_DECL; \
|
|
CEG_PROFILE_BLOCK( CEG_ProtectFunction( ), ProtectVirtual, "" ); \
|
|
} while( 0 );
|
|
|
|
// defined outside the scope of a function, so can't trivially grab metrics
|
|
#define CEG_PROTECT_STATIC_MEMBER_FUNCTION( unquotedSymbolHelper, fn_name ) \
|
|
CEG_Protect_StaticMemberFunction( unquotedSymbolHelper, fn_name );
|
|
|
|
#define STEAMWORKS_TESTSECRET() \
|
|
do { \
|
|
CEG_PROFILE_DECL; \
|
|
CEG_PROFILE_BLOCK( Steamworks_TestSecret(), TestSecret, "" ); \
|
|
} while( 0 );
|
|
|
|
#define STEAMWORKS_TESTSECRETALWAYS() \
|
|
do { \
|
|
CEG_PROFILE_DECL; \
|
|
CEG_PROFILE_BLOCK( Steamworks_TestSecretAlways(), TestSecret, "" ); \
|
|
} while( 0 );
|
|
|
|
#define STEAMWORKS_SELFCHECK() \
|
|
do { \
|
|
CEG_PROFILE_DECL; \
|
|
CEG_PROFILE_BLOCK( Steamworks_SelfCheck(), SelfCheck, "" ); \
|
|
} while( 0 );
|
|
|
|
// AMORTIZED
|
|
|
|
#define STEAMWORKS_TESTSECRET_AMORTIZE( period ) \
|
|
do { \
|
|
const unsigned long tp = period; \
|
|
static unsigned long s_nCegCounter = __LINE__ ^ __COUNTER__ ^ reinterpret_cast<int>( &s_nCegCounter ); \
|
|
++s_nCegCounter; \
|
|
CEG_PROFILE_DECL; \
|
|
if ( !( ( s_nCegCounter ) % ( tp ) ) ) \
|
|
{ \
|
|
CEG_PROFILE_BLOCK( Steamworks_TestSecret(), TestSecret, "Amortized " ); \
|
|
} \
|
|
} while( 0 );
|
|
|
|
#define STEAMWORKS_TESTSECRETALWAYS_AMORTIZE( period ) \
|
|
do { \
|
|
const unsigned long tp = period; \
|
|
static unsigned long s_nCegCounter = __LINE__ ^ __COUNTER__ ^ reinterpret_cast<int>( &s_nCegCounter ); \
|
|
++s_nCegCounter; \
|
|
CEG_PROFILE_DECL; \
|
|
if ( !( ( s_nCegCounter ) % ( tp ) ) ) \
|
|
{ \
|
|
CEG_PROFILE_BLOCK( Steamworks_TestSecretAlways(), TestSecret, "Amortized " ); \
|
|
} \
|
|
} while( 0 );
|
|
|
|
#define STEAMWORKS_SELFCHECK_AMORTIZE( period ) \
|
|
do { \
|
|
const unsigned long tp = period; \
|
|
static unsigned long s_nCegCounter = __LINE__ ^ __COUNTER__ ^ reinterpret_cast<int>( &s_nCegCounter ); \
|
|
++s_nCegCounter; \
|
|
CEG_PROFILE_DECL; \
|
|
if ( !( ( s_nCegCounter ) % ( tp ) ) ) \
|
|
{ \
|
|
CEG_PROFILE_BLOCK( Steamworks_SelfCheck(), TestSecret, "Amortized " ); \
|
|
} \
|
|
} while( 0 );
|
|
|
|
|
|
#endif // CEG disabled/enabled
|
|
|
|
#if defined( CLIENT_DLL ) // client-only
|
|
|
|
void Init_GCVs();
|
|
|
|
CEG_DECLARE_CONSTANT_FUNCTION( HudAllowTextChatFlag );
|
|
CEG_DECLARE_CONSTANT_FUNCTION( HudAllowBuyMenuFlag );
|
|
CEG_DECLARE_CONSTANT_FUNCTION( UiAllowProperTintFlag );
|
|
|
|
#elif defined( GAME_DLL ) // server-only
|
|
|
|
void Init_GCVs();
|
|
|
|
#endif // defined( GAME_DLL )
|
|
|
|
// Worst-case, if you need to debug CEG calls or CEG perf, and CEG profiling isn't doing the trick (it doesn't cover everything), you can
|
|
// do a binary search on one or all of the below calls by restoring part of the #if 0 block below. You'll get a duplicate definition warning,
|
|
// but anything redefined below will be a no-op in the build, allowing you to rule it out as a potential perf issue.
|
|
// Remember that CEG builds neeed to be rebuilds (CEG'ed dlls only), but the /MP flag should make it about 60 seconds per rebuild.
|
|
#if 0
|
|
#define STEAMWORKS_TESTSECRET()
|
|
#define STEAMWORKS_TESTSECRETALWAYS()
|
|
#define STEAMWORKS_SELFCHECK()
|
|
#define RANDOM_CEG_TEST_SECRET()
|
|
#define RANDOM_CEG_TEST_SECRET_PERIOD( testPeriod, checkPeriod )
|
|
#define RANDOM_CEG_TEST_SECRET_LINE_PERIOD( testPeriod, testLinePeriod, checkPeriod, checkLinePeriod )
|
|
#define CEG_PROTECT_FUNCTION( unquotedSymbolHelper )
|
|
#define CEG_PROTECT_MEMBER_FUNCTION( unquotedSymbolHelper )
|
|
#define CEG_PROTECT_VIRTUAL_FUNCTION( unquotedSymbolHelper )
|
|
#define CEG_PROTECT_STATIC_MEMBER_FUNCTION( unquotedSymbolHelper, fn_name )
|
|
#define CEG_DECLARE_CONSTANT_FUNCTION( fn_name ) extern DWORD __cdecl fn_name();
|
|
#define CEG_DECLARE_CONSTANT_FLOAT_FUNCTION( fn_name ) extern float __cdecl fn_name();
|
|
#define CEG_DEFINE_CONSTANT_FUNCTION( fn_name, val ) DWORD __cdecl fn_name() { return val; }
|
|
#define CEG_DEFINE_CONSTANT_FLOAT_FUNCTION( fn_name, val ) float __cdecl fn_name() { return val; }
|
|
#define CEG_GET_CONSTANT_VALUE( fn_name ) fn_name()
|
|
#endif
|
|
|
|
#endif //_CEGCLIENTWRAPPER_H_
|