#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( &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( &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( &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( &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_