2022-02-27 12:53:44 +00:00
# include <STDInclude.hpp>
2022-07-06 17:48:40 +02:00
# include "Script.hpp"
2017-01-19 22:23:59 +01:00
namespace Components
{
2022-07-18 13:01:29 +02:00
std : : unordered_map < std : : string , Script : : ScriptFunction > Script : : CustomScrFunctions ;
std : : unordered_map < std : : string , Script : : ScriptMethod > Script : : CustomScrMethods ;
2017-01-19 22:23:59 +01:00
std : : string Script : : ScriptName ;
std : : vector < std : : string > Script : : ScriptNameStack ;
unsigned short Script : : FunctionName ;
2020-12-08 16:14:47 -06:00
std : : unordered_map < int , std : : string > Script : : ScriptBaseProgramNum ;
2022-06-25 14:22:13 +02:00
int Script : : LastFrameTime = - 1 ;
2021-11-13 13:15:27 +00:00
std : : unordered_map < const char * , const char * > Script : : ReplacedFunctions ;
2022-01-24 03:15:05 +00:00
const char * Script : : ReplacedPos = nullptr ;
2022-06-25 14:22:13 +02:00
std : : vector < int > Script : : ScriptMainHandles ;
std : : vector < int > Script : : ScriptInitHandles ;
2017-01-19 22:23:59 +01:00
2022-10-28 11:52:34 +01:00
void Script : : ShowDeprecationWarning ( )
{
Toast : : Show ( " cardicon_gumby " , " WARNING! " , " You are using deprecated HttpGet/HttpCancel GSC function. " , 2048 ) ;
Logger : : Print ( Game : : CON_CHANNEL_SCRIPT , " *** DEPRECATION WARNING *** \n " ) ;
Logger : : PrintError ( Game : : CON_CHANNEL_ERROR , " Attempted to execute deprecated built-in HttpGet/HttpCancel! These functions have been deemed unsafe and are scheduled for removal. Please update your mod! \n " ) ;
Logger : : Print ( Game : : CON_CHANNEL_SCRIPT , " *************************** \n " ) ;
}
2017-01-19 22:23:59 +01:00
void Script : : FunctionError ( )
{
2022-04-12 14:34:51 +02:00
const auto * funcName = Game : : SL_ConvertToString ( Script : : FunctionName ) ;
2017-01-19 22:23:59 +01:00
Game : : Scr_ShutdownAllocNode ( ) ;
2022-06-12 23:07:53 +02:00
Logger : : Print ( Game : : CON_CHANNEL_PARSERSCRIPT , " \n " ) ;
Logger : : Print ( Game : : CON_CHANNEL_PARSERSCRIPT , " ******* script compile error ******* \n " ) ;
Logger : : Print ( Game : : CON_CHANNEL_PARSERSCRIPT , " Error: unknown function {} in {} \n " , funcName , Script : : ScriptName ) ;
Logger : : Print ( Game : : CON_CHANNEL_PARSERSCRIPT , " ************************************ \n " ) ;
2017-01-19 22:23:59 +01:00
2022-06-12 23:07:53 +02:00
Logger : : Error ( Game : : ERR_SCRIPT_DROP , " script compile error \n unknown function {} \n {} \n \n " , funcName , Script : : ScriptName ) ;
2017-01-19 22:23:59 +01:00
}
__declspec ( naked ) void Script : : StoreFunctionNameStub ( )
{
__asm
{
mov eax , [ esp - 8 h ]
mov Script : : FunctionName , ax
sub esp , 0 Ch
push 0
push edi
mov eax , 612 DB6h
jmp eax
}
}
2022-01-16 17:25:51 +00:00
void Script : : RuntimeError ( const char * codePos , unsigned int index , const char * msg , const char * dialogMessage )
{
// Allow error messages to be printed if developer mode is on
2022-01-23 19:32:20 +00:00
// Should check scrVarPub.developer but it's absent
// in this version of the game so let's check the dvar
2022-08-10 23:03:26 +02:00
if ( ! Game : : scrVmPub - > terminal_error & & ! ( * Game : : com_developer ) - > current . integer )
2022-01-16 17:25:51 +00:00
return ;
2022-01-23 19:32:20 +00:00
// If were are developing let's call RuntimeErrorInternal
2022-01-18 00:21:25 +00:00
// scrVmPub.debugCode seems to be always false
2022-01-16 17:25:51 +00:00
if ( Game : : scrVmPub - > debugCode | | Game : : scrVarPub - > developer_script )
{
2022-06-12 23:07:53 +02:00
Game : : RuntimeErrorInternal ( Game : : CON_CHANNEL_PARSERSCRIPT , codePos , index , msg ) ;
2022-01-16 17:25:51 +00:00
}
else
{
2022-06-12 23:07:53 +02:00
Logger : : Print ( Game : : CON_CHANNEL_PARSERSCRIPT , " {} \n " , msg ) ;
2022-01-16 17:25:51 +00:00
}
2022-03-15 22:49:58 +00:00
// Let's not throw error unless we have to
2022-04-09 16:29:58 +02:00
if ( Game : : scrVmPub - > terminal_error )
{
if ( dialogMessage = = nullptr )
dialogMessage = " " ;
2022-01-16 17:25:51 +00:00
2022-06-12 23:07:53 +02:00
Logger : : Error ( Game : : ERR_SCRIPT_DROP , " \x15 script runtime error \n (see console for details) \n {} \n {} " , msg , dialogMessage ) ;
2022-04-09 16:29:58 +02:00
}
2022-01-16 17:25:51 +00:00
}
2017-01-19 22:23:59 +01:00
void Script : : StoreScriptName ( const char * name )
{
Script : : ScriptNameStack . push_back ( Script : : ScriptName ) ;
Script : : ScriptName = name ;
if ( ! Utils : : String : : EndsWith ( Script : : ScriptName , " .gsc " ) )
{
Script : : ScriptName . append ( " .gsc " ) ;
}
}
__declspec ( naked ) void Script : : StoreScriptNameStub ( )
{
__asm
{
2017-02-01 13:44:25 +01:00
pushad
lea ecx , [ esp + 30 h ]
2017-01-19 22:23:59 +01:00
push ecx
call Script : : StoreScriptName
add esp , 4 h
2017-02-01 13:44:25 +01:00
popad
2017-01-19 22:23:59 +01:00
push ebp
mov ebp , ds : 1 CDEAA8h
2017-02-01 13:44:25 +01:00
push 427 DC3h
retn
2017-01-19 22:23:59 +01:00
}
}
void Script : : RestoreScriptName ( )
{
Script : : ScriptName = Script : : ScriptNameStack . back ( ) ;
Script : : ScriptNameStack . pop_back ( ) ;
}
__declspec ( naked ) void Script : : RestoreScriptNameStub ( )
{
__asm
{
2017-02-01 13:44:25 +01:00
pushad
2017-01-19 22:23:59 +01:00
call Script : : RestoreScriptName
2017-02-01 13:44:25 +01:00
popad
2017-01-19 22:23:59 +01:00
mov ds : 1 CDEAA8h , ebp
2017-02-01 13:44:25 +01:00
push 427E77 h
retn
2017-01-19 22:23:59 +01:00
}
}
void Script : : PrintSourcePos ( const char * filename , unsigned int offset )
{
FileSystem : : File script ( filename ) ;
if ( script . exists ( ) )
{
std : : string buffer = script . getBuffer ( ) ;
Utils : : String : : Replace ( buffer , " \t " , " " ) ;
2022-04-10 14:13:32 +02:00
auto line = 1 , lineOffset = 0 , inlineOffset = 0 ;
2017-01-19 22:23:59 +01:00
2022-04-10 14:13:32 +02:00
for ( size_t i = 0 ; i < buffer . size ( ) ; + + i )
2017-01-19 22:23:59 +01:00
{
// Terminate line
if ( i = = offset )
{
while ( buffer [ i ] ! = ' \r ' & & buffer [ i ] ! = ' \n ' & & buffer [ i ] ! = ' \0 ' )
{
+ + i ;
}
buffer [ i ] = ' \0 ' ;
break ;
}
if ( buffer [ i ] = = ' \n ' )
{
+ + line ;
2022-04-10 14:13:32 +02:00
lineOffset = static_cast < int > ( i ) ; // Includes the line break!
2017-01-19 22:23:59 +01:00
inlineOffset = 0 ;
}
else
{
+ + inlineOffset ;
}
}
2022-06-12 23:07:53 +02:00
Logger : : Print ( Game : : CON_CHANNEL_PARSERSCRIPT , " in file {}, line {}: " , filename , line ) ;
Logger : : Print ( Game : : CON_CHANNEL_PARSERSCRIPT , " {} \n " , buffer . substr ( lineOffset ) ) ;
2017-01-19 22:23:59 +01:00
2022-04-10 14:13:32 +02:00
for ( auto i = 0 ; i < ( inlineOffset - 1 ) ; + + i )
2017-01-19 22:23:59 +01:00
{
2022-06-12 23:07:53 +02:00
Logger : : Print ( Game : : CON_CHANNEL_PARSERSCRIPT , " " ) ;
2017-01-19 22:23:59 +01:00
}
2022-06-12 23:07:53 +02:00
Logger : : Print ( Game : : CON_CHANNEL_PARSERSCRIPT , " * \n " ) ;
2017-01-19 22:23:59 +01:00
}
else
{
2022-06-12 23:07:53 +02:00
Logger : : Print ( Game : : CON_CHANNEL_PARSERSCRIPT , " in file {}, offset {} \n " , filename , offset ) ;
2017-01-19 22:23:59 +01:00
}
}
void Script : : CompileError ( unsigned int offset , const char * message , . . . )
{
2022-02-01 21:51:17 +01:00
char msgbuf [ 1024 ] = { 0 } ;
va_list va ;
va_start ( va , message ) ;
2022-03-02 10:10:03 +00:00
_vsnprintf_s ( msgbuf , _TRUNCATE , message , va ) ;
2022-02-01 21:51:17 +01:00
va_end ( va ) ;
2017-01-19 22:23:59 +01:00
Game : : Scr_ShutdownAllocNode ( ) ;
2022-06-12 23:07:53 +02:00
Logger : : Print ( Game : : CON_CHANNEL_PARSERSCRIPT , " \n " ) ;
Logger : : Print ( Game : : CON_CHANNEL_PARSERSCRIPT , " ******* script compile error ******* \n " ) ;
Logger : : Print ( Game : : CON_CHANNEL_PARSERSCRIPT , " Error: {} " , msgbuf ) ;
2017-01-19 22:23:59 +01:00
Script : : PrintSourcePos ( Script : : ScriptName . data ( ) , offset ) ;
2022-06-12 23:07:53 +02:00
Logger : : Print ( Game : : CON_CHANNEL_PARSERSCRIPT , " ************************************ \n \n " ) ;
2017-01-19 22:23:59 +01:00
2022-06-12 23:07:53 +02:00
Logger : : Error ( Game : : ERR_SCRIPT_DROP , " script compile error \n {} \n {} \n (see console for actual details) \n " , msgbuf , Script : : ScriptName ) ;
2017-01-19 22:23:59 +01:00
}
2022-06-25 14:22:13 +02:00
void Script : : Scr_LoadGameType_Stub ( )
2017-01-19 22:23:59 +01:00
{
2022-06-25 14:22:13 +02:00
for ( const auto & handle : Script : : ScriptMainHandles )
2017-01-19 22:23:59 +01:00
{
2022-06-25 14:22:13 +02:00
const auto id = Game : : Scr_ExecThread ( handle , 0 ) ;
Game : : Scr_FreeThread ( static_cast < std : : uint16_t > ( id ) ) ;
2017-01-19 22:23:59 +01:00
}
2022-06-25 14:22:13 +02:00
Game : : Scr_LoadGameType ( ) ;
2017-01-19 22:23:59 +01:00
}
2022-06-25 14:22:13 +02:00
void Script : : Scr_StartupGameType_Stub ( )
2017-01-19 22:23:59 +01:00
{
2022-06-25 14:22:13 +02:00
for ( const auto & handle : Script : : ScriptInitHandles )
2017-01-19 22:23:59 +01:00
{
2022-06-25 14:22:13 +02:00
const auto id = Game : : Scr_ExecThread ( handle , 0 ) ;
Game : : Scr_FreeThread ( static_cast < std : : uint16_t > ( id ) ) ;
2017-01-19 22:23:59 +01:00
}
2022-06-25 14:22:13 +02:00
Game : : Scr_StartupGameType ( ) ;
2017-01-19 22:23:59 +01:00
}
2022-08-23 09:12:20 +02:00
// Do not use C++ objects because Scr_LoadScript may longjmp
2022-06-25 14:22:13 +02:00
void Script : : GScr_LoadGameTypeScript_Stub ( )
2017-01-19 22:23:59 +01:00
{
2022-06-25 14:22:13 +02:00
// Clear handles (from previous GSC loading session)
Script : : ScriptMainHandles . clear ( ) ;
Script : : ScriptInitHandles . clear ( ) ;
2017-01-19 22:23:59 +01:00
2022-08-23 09:12:20 +02:00
char path [ MAX_PATH ] { } ;
2017-01-19 22:23:59 +01:00
2022-08-23 09:12:20 +02:00
auto numFiles = 0 ;
const auto * * files = Game : : FS_ListFiles ( " scripts/ " , " gsc " , Game : : FS_LIST_ALL , & numFiles , 10 ) ;
for ( auto i = 0 ; i < numFiles ; + + i )
2017-01-19 22:23:59 +01:00
{
2022-08-23 09:12:20 +02:00
const auto * scriptFile = files [ i ] ;
Logger : : Print ( " Loading script {}... \n " , scriptFile ) ;
2017-01-19 22:23:59 +01:00
2022-08-23 09:12:20 +02:00
sprintf_s ( path , " %s/%s " , " scripts " , scriptFile ) ;
2017-01-19 22:23:59 +01:00
2022-08-23 09:12:20 +02:00
// Scr_LoadScriptInternal will add the '.gsc' suffix so we remove it
path [ std : : strlen ( path ) - 4 ] = ' \0 ' ;
2022-03-15 22:49:58 +00:00
2022-08-23 09:12:20 +02:00
if ( ! Game : : Scr_LoadScript ( path ) )
2022-03-15 22:49:58 +00:00
{
2022-08-23 09:12:20 +02:00
Logger : : Print ( " Script {} encountered an error while loading. (doesn't exist?) " , path ) ;
continue ;
2022-03-15 22:49:58 +00:00
}
2022-06-25 14:22:13 +02:00
2022-08-23 09:12:20 +02:00
Logger : : Print ( " Script {}.gsc loaded successfully. \n " , path ) ;
2022-06-25 14:22:13 +02:00
Logger : : Debug ( " Finding script handle main or init... " ) ;
2022-08-23 09:12:20 +02:00
const auto initHandle = Game : : Scr_GetFunctionHandle ( path , " init " ) ;
2022-06-25 14:22:13 +02:00
if ( initHandle ! = 0 )
2017-01-19 22:23:59 +01:00
{
2022-06-25 14:22:13 +02:00
Script : : ScriptInitHandles . push_back ( initHandle ) ;
2017-01-19 22:23:59 +01:00
}
2022-06-25 14:22:13 +02:00
2022-08-23 09:12:20 +02:00
const auto mainHandle = Game : : Scr_GetFunctionHandle ( path , " main " ) ;
2022-06-25 14:22:13 +02:00
if ( mainHandle ! = 0 )
{
Script : : ScriptMainHandles . push_back ( mainHandle ) ;
}
// Allow scripts with no handles
2017-01-19 22:23:59 +01:00
}
2022-08-23 09:12:20 +02:00
Game : : FS_FreeFileList ( files , 10 ) ;
2017-01-19 22:23:59 +01:00
Game : : GScr_LoadGameTypeScript ( ) ;
}
2022-07-18 13:01:29 +02:00
void Script : : AddFunction ( const std : : string & name , Game : : BuiltinFunction func , bool type )
2017-05-13 12:09:58 +02:00
{
2022-07-18 13:01:29 +02:00
Script : : ScriptFunction toAdd ;
2022-02-23 11:38:37 +00:00
toAdd . actionFunc = func ;
toAdd . type = type ;
2022-07-23 23:22:58 +02:00
CustomScrFunctions . insert_or_assign ( Utils : : String : : ToLower ( name ) , toAdd ) ;
2022-02-23 11:38:37 +00:00
}
2022-07-18 13:01:29 +02:00
void Script : : AddMethod ( const std : : string & name , Game : : BuiltinMethod func , bool type )
2022-02-23 11:38:37 +00:00
{
2022-07-18 13:01:29 +02:00
Script : : ScriptMethod toAdd ;
2022-02-23 11:38:37 +00:00
toAdd . actionFunc = func ;
toAdd . type = type ;
2022-07-23 23:22:58 +02:00
CustomScrMethods . insert_or_assign ( Utils : : String : : ToLower ( name ) , toAdd ) ;
2022-02-23 11:38:37 +00:00
}
2022-04-12 14:34:51 +02:00
Game : : BuiltinFunction Script : : BuiltIn_GetFunctionStub ( const char * * pName , int * type )
2017-05-14 20:14:52 +02:00
{
2022-04-10 14:13:32 +02:00
if ( pName ! = nullptr )
2017-05-13 12:09:58 +02:00
{
2022-02-23 11:38:37 +00:00
// If no function was found let's call game's function
2022-10-14 22:56:09 +01:00
if ( const auto itr = Script : : CustomScrFunctions . find ( Utils : : String : : ToLower ( * pName ) ) ; itr ! = Script : : CustomScrFunctions . end ( ) )
2017-05-13 12:09:58 +02:00
{
2022-10-14 22:56:09 +01:00
* type = itr - > second . type ;
return itr - > second . actionFunc ;
2022-02-23 11:38:37 +00:00
}
2017-05-13 12:09:58 +02:00
}
2022-04-10 14:00:55 +02:00
else
{
for ( const auto & [ name , builtin ] : Script : : CustomScrFunctions )
{
Game : : Scr_RegisterFunction ( reinterpret_cast < int > ( builtin . actionFunc ) , name . data ( ) ) ;
}
}
2017-05-13 12:09:58 +02:00
2022-04-12 14:34:51 +02:00
return Utils : : Hook : : Call < Game : : BuiltinFunction ( const char * * , int * ) > ( 0x5FA2B0 ) ( pName , type ) ; // BuiltIn_GetFunction
2017-05-13 12:09:58 +02:00
}
2022-07-18 13:01:29 +02:00
Game : : BuiltinMethod Script : : BuiltIn_GetMethodStub ( const char * * pName , int * type )
2017-05-13 12:09:58 +02:00
{
2022-04-10 14:13:32 +02:00
if ( pName ! = nullptr )
2017-05-13 12:09:58 +02:00
{
2022-02-23 11:38:37 +00:00
// If no method was found let's call game's function
2022-10-14 22:56:09 +01:00
if ( const auto itr = Script : : CustomScrMethods . find ( Utils : : String : : ToLower ( * pName ) ) ; itr ! = Script : : CustomScrMethods . end ( ) )
2022-02-23 11:38:37 +00:00
{
2022-10-14 22:56:09 +01:00
* type = itr - > second . type ;
return itr - > second . actionFunc ;
2022-02-23 11:38:37 +00:00
}
2020-12-08 16:14:47 -06:00
}
2022-04-10 14:00:55 +02:00
else
{
for ( const auto & [ name , builtin ] : Script : : CustomScrMethods )
{
Game : : Scr_RegisterFunction ( reinterpret_cast < int > ( builtin . actionFunc ) , name . data ( ) ) ;
}
}
2022-02-23 11:38:37 +00:00
2022-04-12 14:34:51 +02:00
return Utils : : Hook : : Call < Game : : BuiltinMethod ( const char * * , int * ) > ( 0x5FA360 ) ( pName , type ) ; // Player_GetMethod
2020-12-08 16:14:47 -06:00
}
void Script : : StoreScriptBaseProgramNum ( )
{
Script : : ScriptBaseProgramNum . insert_or_assign ( Utils : : Hook : : Get < int > ( 0x1CFEEF8 ) , Script : : ScriptName ) ;
}
void Script : : Scr_PrintPrevCodePos ( int scriptPos )
{
2022-02-04 15:08:43 +01:00
auto bestCodePos = - 1 , nextCodePos = - 1 , offset = - 1 ;
2020-12-08 16:14:47 -06:00
std : : string file ;
2022-02-01 21:51:17 +01:00
for ( const auto & [ key , value ] : Script : : ScriptBaseProgramNum )
2020-12-08 16:14:47 -06:00
{
2022-03-15 22:49:58 +00:00
const auto codePos = key ;
2020-12-08 16:14:47 -06:00
if ( codePos > scriptPos )
{
if ( nextCodePos = = - 1 | | codePos < nextCodePos )
nextCodePos = codePos ;
continue ;
}
if ( codePos < bestCodePos )
continue ;
bestCodePos = codePos ;
2022-02-01 21:51:17 +01:00
file = value ;
2020-12-08 16:14:47 -06:00
offset = scriptPos - bestCodePos ;
}
if ( bestCodePos = = - 1 )
return ;
2022-06-12 23:07:53 +02:00
Logger : : Print ( Game : : CON_CHANNEL_PARSERSCRIPT , " \n @ {} ({} - {}) \n " , scriptPos , bestCodePos , nextCodePos ) ;
Logger : : Print ( Game : : CON_CHANNEL_PARSERSCRIPT , " in {} ({} through the source) \n \n " , file , ( ( offset * 100.0f ) / ( nextCodePos - bestCodePos ) ) ) ;
2020-12-08 16:14:47 -06:00
}
__declspec ( naked ) void Script : : Scr_PrintPrevCodePosStub ( )
{
__asm
{
push esi
call Script : : Scr_PrintPrevCodePos
add esp , 4 h
2017-05-14 20:14:52 +02:00
pop esi
2017-05-13 12:09:58 +02:00
retn
}
}
2020-12-08 16:14:47 -06:00
__declspec ( naked ) void Script : : StoreScriptBaseProgramNumStub ( )
{
__asm
{
// execute our hook
pushad
call Script : : StoreScriptBaseProgramNum
popad
// execute overwritten code caused by the jump hook
2022-08-01 16:30:31 +02:00
sub eax , ds : 201 A460h // gScrVarPub_programBuffer
add esp , 0 Ch
mov ds : 1 CFEEF8h , eax // gScrCompilePub_programLen
2020-12-08 16:14:47 -06:00
// jump back to the original code
2022-08-01 16:30:31 +02:00
push 426 C3Bh
2020-12-08 16:14:47 -06:00
retn
}
}
2021-11-13 13:22:06 +00:00
unsigned int Script : : SetExpFogStub ( )
2017-04-29 23:08:41 +02:00
{
2022-07-06 17:48:40 +02:00
if ( Game : : Scr_GetNumParam ( ) = = 6 )
2017-04-29 23:08:41 +02:00
{
2021-11-13 18:51:37 +00:00
std : : memmove ( & Game : : scrVmPub - > top [ - 4 ] , & Game : : scrVmPub - > top [ - 5 ] , sizeof ( Game : : VariableValue ) * 6 ) ;
Game : : scrVmPub - > top + = 1 ;
2022-07-06 17:48:40 +02:00
Game : : scrVmPub - > top [ - 6 ] . type = Game : : VAR_FLOAT ;
2021-11-15 17:44:23 +00:00
Game : : scrVmPub - > top [ - 6 ] . u . floatValue = 0.0f ;
2017-05-07 19:04:57 +02:00
2021-11-13 18:51:37 +00:00
+ + Game : : scrVmPub - > outparamcount ;
2017-04-29 23:08:41 +02:00
}
return Game : : Scr_GetNumParam ( ) ;
}
2021-11-13 20:46:58 +00:00
const char * Script : : GetCodePosForParam ( int index )
2021-11-13 13:15:27 +00:00
{
2021-11-13 20:55:22 +00:00
if ( static_cast < unsigned int > ( index ) > = Game : : scrVmPub - > outparamcount )
2021-11-13 18:51:37 +00:00
{
2022-01-16 13:45:18 +00:00
Game : : Scr_ParamError ( static_cast < unsigned int > ( index ) , " ^1GetCodePosForParam: Index is out of range! \n " ) ;
2021-11-13 18:51:37 +00:00
return " " ;
}
2022-07-21 18:56:16 +02:00
const auto * value = & Game : : scrVmPub - > top [ - index ] ;
2021-11-13 18:51:37 +00:00
2022-11-01 23:23:25 +00:00
if ( value - > type ! = Game : : VAR_FUNCTION )
2021-11-13 18:51:37 +00:00
{
2022-01-16 13:45:18 +00:00
Game : : Scr_ParamError ( static_cast < unsigned int > ( index ) , " ^1GetCodePosForParam: Expects a function as parameter! \n " ) ;
2021-11-13 18:51:37 +00:00
return " " ;
}
return value - > u . codePosValue ;
2021-11-13 13:15:27 +00:00
}
void Script : : GetReplacedPos ( const char * pos )
{
2022-05-31 18:38:09 +02:00
if ( Script : : ReplacedFunctions . contains ( pos ) )
2021-11-13 13:15:27 +00:00
{
2021-11-13 13:22:06 +00:00
Script : : ReplacedPos = Script : : ReplacedFunctions [ pos ] ;
2021-11-13 13:15:27 +00:00
}
}
void Script : : SetReplacedPos ( const char * what , const char * with )
{
2021-11-13 18:51:37 +00:00
if ( what [ 0 ] = = ' \0 ' | | with [ 0 ] = = ' \0 ' )
{
2022-06-12 23:07:53 +02:00
Logger : : Warning ( Game : : CON_CHANNEL_SCRIPT , " Invalid parameters passed to ReplacedFunctions \n " ) ;
2021-11-13 18:51:37 +00:00
return ;
}
2022-05-31 18:38:09 +02:00
if ( Script : : ReplacedFunctions . contains ( what ) )
2021-11-13 13:15:27 +00:00
{
2022-06-12 23:07:53 +02:00
Logger : : Warning ( Game : : CON_CHANNEL_SCRIPT , " ReplacedFunctions already contains codePosValue for a function \n " ) ;
2021-11-13 13:15:27 +00:00
}
Script : : ReplacedFunctions [ what ] = with ;
}
__declspec ( naked ) void Script : : VMExecuteInternalStub ( )
{
__asm
{
pushad
push edx
call Script : : GetReplacedPos
pop edx
popad
cmp Script : : ReplacedPos , 0
jne SetPos
movzx eax , byte ptr [ edx ]
inc edx
Loc1 :
cmp eax , 0x8B
push ecx
mov ecx , 0x2045094
mov [ ecx ] , eax
mov ecx , 0x2040CD4
mov [ ecx ] , edx
pop ecx
2021-11-13 18:51:37 +00:00
push 0x61E944
2021-11-13 13:15:27 +00:00
retn
SetPos :
mov edx , Script : : ReplacedPos
mov Script : : ReplacedPos , 0
movzx eax , byte ptr [ edx ]
inc edx
jmp Loc1
}
}
2022-02-25 12:17:57 +00:00
Game : : client_t * Script : : GetClient ( const Game : : gentity_t * ent )
2020-07-23 06:36:35 +03:00
{
2022-02-25 12:17:57 +00:00
assert ( ent ! = nullptr ) ;
2020-07-23 06:36:35 +03:00
2022-02-25 12:17:57 +00:00
if ( ent - > client = = nullptr )
2020-07-23 06:36:35 +03:00
{
2022-02-25 12:17:57 +00:00
Game : : Scr_ObjectError ( Utils : : String : : VA ( " Entity %i is not a player " , ent - > s . number ) ) ;
2022-01-07 22:00:44 +01:00
return nullptr ;
2020-07-23 06:36:35 +03:00
}
2022-01-07 22:00:44 +01:00
2022-04-12 14:34:51 +02:00
if ( ent - > s . number > = * Game : : svs_clientCount )
2022-01-23 19:32:20 +00:00
{
2022-02-25 12:17:57 +00:00
Game : : Scr_ObjectError ( Utils : : String : : VA ( " Entity %i is out of bounds " , ent - > s . number ) ) ;
2022-01-23 19:32:20 +00:00
return nullptr ;
}
2022-02-25 12:17:57 +00:00
return & Game : : svs_clients [ ent - > s . number ] ;
2020-07-23 06:36:35 +03:00
}
2020-12-04 16:42:47 -06:00
void Script : : AddFunctions ( )
{
2022-07-23 23:22:58 +02:00
Script : : AddFunction ( " ReplaceFunc " , [ ] // gsc: ReplaceFunc(<function>, <function>)
2021-11-13 13:15:27 +00:00
{
2022-07-06 17:48:40 +02:00
if ( Game : : Scr_GetNumParam ( ) ! = 2 )
2021-11-13 13:15:27 +00:00
{
Game : : Scr_Error ( " ^1ReplaceFunc: Needs two parameters! \n " ) ;
return ;
}
const auto what = Script : : GetCodePosForParam ( 0 ) ;
2021-11-13 18:51:37 +00:00
const auto with = Script : : GetCodePosForParam ( 1 ) ;
2021-11-13 13:15:27 +00:00
Script : : SetReplacedPos ( what , with ) ;
} ) ;
2020-12-04 16:42:47 -06:00
// System time
2022-07-23 23:22:58 +02:00
Script : : AddFunction ( " GetSystemMilliseconds " , [ ] // gsc: GetSystemMilliseconds()
2020-12-08 16:18:09 -06:00
{
SYSTEMTIME time ;
GetSystemTime ( & time ) ;
2020-12-04 16:42:47 -06:00
2020-12-08 16:18:09 -06:00
Game : : Scr_AddInt ( time . wMilliseconds ) ;
} ) ;
2020-12-04 16:42:47 -06:00
// Executes command to the console
2022-07-23 23:22:58 +02:00
Script : : AddFunction ( " Exec " , [ ] // gsc: Exec(<string>)
2020-12-08 16:18:09 -06:00
{
2022-01-07 22:00:44 +01:00
const auto str = Game : : Scr_GetString ( 0 ) ;
2022-01-23 19:32:20 +00:00
if ( str = = nullptr )
{
2022-02-01 21:51:17 +01:00
Game : : Scr_ParamError ( 0 , " ^1Exec: Illegal parameter! \n " ) ;
2022-01-23 19:32:20 +00:00
return ;
}
2020-12-08 16:18:09 -06:00
Command : : Execute ( str , false ) ;
} ) ;
2020-12-04 16:42:47 -06:00
2022-01-24 03:15:05 +00:00
// Allow printing to the console even when developer is 0
2022-07-23 23:22:58 +02:00
Script : : AddFunction ( " PrintConsole " , [ ] // gsc: PrintConsole(<string>)
2022-01-24 03:15:05 +00:00
{
2022-06-12 23:07:53 +02:00
for ( std : : size_t i = 0 ; i < Game : : Scr_GetNumParam ( ) ; + + i )
2022-01-24 03:15:05 +00:00
{
2022-06-12 23:07:53 +02:00
const auto * str = Game : : Scr_GetString ( i ) ;
2022-01-24 03:15:05 +00:00
if ( str = = nullptr )
{
2022-02-01 21:51:17 +01:00
Game : : Scr_ParamError ( i , " ^1PrintConsole: Illegal parameter! \n " ) ;
2022-01-24 03:15:05 +00:00
return ;
}
2022-06-12 23:07:53 +02:00
Logger : : Print ( Game : : level - > scriptPrintChannel , " {} " , str ) ;
2022-01-24 03:15:05 +00:00
}
} ) ;
2022-01-15 13:49:33 +00:00
// PlayerCmd_AreControlsFrozen GSC function from Black Ops 2
2022-07-23 23:22:58 +02:00
Script : : AddMethod ( " AreControlsFrozen " , [ ] ( Game : : scr_entref_t entref ) // Usage: self AreControlsFrozen();
2022-01-14 00:21:14 +00:00
{
2022-02-25 12:17:57 +00:00
const auto * ent = Game : : GetPlayerEntity ( entref ) ;
2022-01-14 00:21:14 +00:00
2022-01-15 19:18:15 +00:00
Game : : Scr_AddBool ( ( ent - > client - > flags & Game : : PLAYER_FLAG_FROZEN ) ! = 0 ) ;
2022-01-14 00:21:14 +00:00
} ) ;
2020-12-04 16:42:47 -06:00
}
2017-01-19 22:23:59 +01:00
Script : : Script ( )
{
Utils : : Hook ( 0x612DB0 , Script : : StoreFunctionNameStub , HOOK_JUMP ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x427E71 , Script : : RestoreScriptNameStub , HOOK_JUMP ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x427DBC , Script : : StoreScriptNameStub , HOOK_JUMP ) . install ( ) - > quick ( ) ;
2020-12-08 16:14:47 -06:00
Utils : : Hook ( 0x426C2D , Script : : StoreScriptBaseProgramNumStub , HOOK_JUMP ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x42281B , Script : : Scr_PrintPrevCodePosStub , HOOK_JUMP ) . install ( ) - > quick ( ) ;
2022-01-16 17:25:51 +00:00
Utils : : Hook ( 0x61E3AD , Script : : RuntimeError , HOOK_CALL ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x621976 , Script : : RuntimeError , HOOK_CALL ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x62246E , Script : : RuntimeError , HOOK_CALL ) . install ( ) - > quick ( ) ;
2022-02-09 23:40:27 +00:00
// Skip check in GScr_CheckAllowedToSetPersistentData to prevent log spam in RuntimeError.
// On IW5 the function is entirely nullsubbed
Utils : : Hook : : Set < BYTE > ( 0x5F8DBF , 0xEB ) ;
2017-01-19 22:23:59 +01:00
Utils : : Hook ( 0x612E8D , Script : : FunctionError , HOOK_CALL ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x612EA2 , Script : : FunctionError , HOOK_CALL ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x434260 , Script : : CompileError , HOOK_JUMP ) . install ( ) - > quick ( ) ;
2022-06-25 14:22:13 +02:00
Utils : : Hook ( 0x48EFFE , Script : : Scr_LoadGameType_Stub , HOOK_CALL ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x48F008 , Script : : Scr_StartupGameType_Stub , HOOK_CALL ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x45D44A , Script : : GScr_LoadGameTypeScript_Stub , HOOK_CALL ) . install ( ) - > quick ( ) ;
2017-04-29 23:08:41 +02:00
2022-02-23 11:38:37 +00:00
// Fetch custom functions
Utils : : Hook ( 0x44E72E , Script : : BuiltIn_GetFunctionStub , HOOK_CALL ) . install ( ) - > quick ( ) ; // Scr_GetFunction
2022-07-18 13:01:29 +02:00
Utils : : Hook ( 0x4EC8DD , Script : : BuiltIn_GetMethodStub , HOOK_CALL ) . install ( ) - > quick ( ) ; // Scr_GetMethod
2017-05-13 12:09:58 +02:00
2017-04-29 23:08:41 +02:00
Utils : : Hook ( 0x5F41A3 , Script : : SetExpFogStub , HOOK_CALL ) . install ( ) - > quick ( ) ;
2017-05-14 20:14:52 +02:00
2021-11-13 13:15:27 +00:00
Utils : : Hook ( 0x61E92E , Script : : VMExecuteInternalStub , HOOK_JUMP ) . install ( ) - > quick ( ) ;
Utils : : Hook : : Nop ( 0x61E933 , 1 ) ;
2022-08-10 23:03:26 +02:00
Scheduler : : Loop ( [ ]
2020-12-19 16:50:51 -06:00
{
if ( ! Game : : SV_Loaded ( ) )
return ;
2022-03-15 22:49:58 +00:00
const auto nowMs = Game : : Sys_Milliseconds ( ) ;
2020-12-19 16:50:51 -06:00
if ( Script : : LastFrameTime ! = - 1 )
{
2022-08-10 23:03:26 +02:00
const auto timeTaken = ( nowMs - Script : : LastFrameTime ) * static_cast < int > ( ( * Game : : com_timescale ) - > current . value ) ;
2020-12-19 16:50:51 -06:00
if ( timeTaken > = 500 )
2022-08-10 23:03:26 +02:00
{
2022-06-12 23:07:53 +02:00
Logger : : Print ( Game : : CON_CHANNEL_PARSERSCRIPT , " Hitch warning: {} msec frame time \n " , timeTaken ) ;
2022-08-10 23:03:26 +02:00
}
2020-12-19 16:50:51 -06:00
}
Script : : LastFrameTime = nowMs ;
2022-05-05 15:03:14 +01:00
} , Scheduler : : Pipeline : : SERVER ) ;
2020-12-19 16:50:51 -06:00
2022-04-09 16:29:58 +02:00
# ifdef _DEBUG
2022-05-04 12:44:45 +01:00
Script : : AddFunction ( " DebugBox " , [ ]
2020-12-08 16:18:09 -06:00
{
2022-02-22 17:16:12 +00:00
const auto * message = Game : : Scr_GetString ( 0 ) ;
if ( message = = nullptr )
{
Game : : Scr_Error ( " ^1DebugBox: Illegal parameter! \n " ) ;
}
MessageBoxA ( nullptr , message , " DEBUG " , MB_OK ) ;
2022-02-25 11:58:11 +00:00
} , 1 ) ;
2022-04-09 16:29:58 +02:00
# endif
2018-07-17 14:30:29 +02:00
2020-12-04 16:42:47 -06:00
Script : : AddFunctions ( ) ;
2022-06-13 20:16:57 +02:00
Events : : OnVMShutdown ( [ ]
2021-11-13 18:51:37 +00:00
{
Script : : ReplacedFunctions . clear ( ) ;
} ) ;
2017-01-19 22:23:59 +01:00
}
}