Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/game/t4/mp/components/events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,13 @@ Detour Events::Cmd_Init_Detour;

void *Events::Scr_ShutdownSystem_Hook(scriptInstance_t inst, int sys, int bComplete)
{
for (size_t i = 0; i < ARRAYSIZE(vmShutdownHandlers); ++i)
// These handlers own server script state.
if (inst == SCRIPTINSTANCE_SERVER)
{
vmShutdownHandlers[i]();
for (size_t i = 0; i < ARRAYSIZE(vmShutdownHandlers); ++i)
{
vmShutdownHandlers[i]();
}
}

return Scr_ShutdownSystem_Detour.GetOriginal<Scr_ShutdownSystem_t>()(inst, sys, bComplete);
Expand Down
91 changes: 91 additions & 0 deletions src/game/t4/mp/components/gsc_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ namespace t4
{
namespace mp
{
std::unordered_map<const char *, const char *> GSCFunctions::ReplacedFunctions;
static void CloseAllScriptFiles()
{
script_files::CloseAll();
}

void GSCFunctions::OnVMShutdown()
{
ReplacedFunctions.clear();
CloseAllScriptFiles();
}

Expand Down Expand Up @@ -172,8 +174,88 @@ void GSCrGetPlayerclipBrushesContainingPoint()
}
}

Detour GSCFunctions::VM_Execute_Detour;

const char *GSCFunctions::GetCodePosForParam(int index)
{
const int numParams = static_cast<int>(Scr_GetNumParam(SCRIPTINSTANCE_SERVER));

if (index < 0 || index >= numParams)
return nullptr;

const VariableValue *value = &scrVmPub[SCRIPTINSTANCE_SERVER].top[-index];

if (value->type != VAR_FUNCTION)
return nullptr;

return value->u.codePosValue;
}

const char *GSCFunctions::GetReplacedPos(const char *pos, scriptInstance_t inst)
{
if (inst != SCRIPTINSTANCE_SERVER || !pos || ReplacedFunctions.empty())
return pos;

const auto it = ReplacedFunctions.find(pos);
return it != ReplacedFunctions.end() ? it->second : pos;
}

extern "C" const char *T4_GetReplacedPos(const char *pos, scriptInstance_t inst)
{
return GSCFunctions::GetReplacedPos(pos, inst);
}

__declspec(naked) void GSCFunctions::VM_Execute_Hook()
{
__asm
{
mr r3, r10
mr r4, r31
bl T4_GetReplacedPos

mr r10, r3

addi r11, r10, 1
lbz r9, 0(r10)
addi r10, r27, -0x4068
slwi r21, r31, 2

lis r12, 0x8234
ori r12, r12, 0x6480
mtctr r12
bctr
}
}

void GSCFunctions::ReplaceFunc()
{
if (Scr_GetNumParam(SCRIPTINSTANCE_SERVER) != 2)
{
Scr_Error("replacefunc: expected two function parameters", SCRIPTINSTANCE_SERVER);
return;
}

const char *what = GetCodePosForParam(0);
const char *with = GetCodePosForParam(1);

if (!what)
{
Scr_Error("replacefunc: first parameter must be a function", SCRIPTINSTANCE_SERVER);
return;
}

if (!with)
{
Scr_Error("replacefunc: second parameter must be a function", SCRIPTINSTANCE_SERVER);
return;
}

ReplacedFunctions[what] = with;
}

static const gsc::Entry<BuiltinFunction> functions[] = {
{"addtestclient", GScr_AddTestClient, BUILTIN_ANY},
{"replacefunc", GSCFunctions::ReplaceFunc, BUILTIN_ANY},
{"getplayerclipbrushescontainingpoint", GSCrGetPlayerclipBrushesContainingPoint, BUILTIN_ANY},
{"fs_testfile", GScr_FS_TestFile, BUILTIN_ANY},
{"fs_fopen", GScr_FS_FOpen, BUILTIN_ANY},
Expand All @@ -191,7 +273,10 @@ BuiltinFunction Scr_GetFunction_Hook(const char **pName, int *type)
{
const gsc::Entry<BuiltinFunction> *function = gsc::Find(*pName, functions);
if (function)
{
*type = function->type;
return function->actionFunc;
}
}
return Scr_GetFunction_Detour.GetOriginal<decltype(&Scr_GetFunction_Hook)>()(pName, type);
}
Expand All @@ -200,11 +285,17 @@ GSCFunctions::GSCFunctions()
{
Scr_GetFunction_Detour = Detour(Scr_GetFunction, Scr_GetFunction_Hook);
Scr_GetFunction_Detour.Install();

// Intercept TU7 VM_Execute's opcode fetch, then resume at 0x82346480.
VM_Execute_Detour = Detour(reinterpret_cast<void *>(0x82346470), reinterpret_cast<const void *>(VM_Execute_Hook));
VM_Execute_Detour.Install();
}

GSCFunctions::~GSCFunctions()
{
Scr_GetFunction_Detour.Remove();
VM_Execute_Detour.Remove();
ReplacedFunctions.clear();
CloseAllScriptFiles();
}
} // namespace mp
Expand Down
16 changes: 16 additions & 0 deletions src/game/t4/mp/components/gsc_functions.h
Original file line number Diff line number Diff line change
@@ -1,18 +1,34 @@
#pragma once

#include "pch.h"
#include <unordered_map>

namespace t4
{
namespace mp
{
// Keep the assembly bridge distinct from IW3's C-linkage GetReplacedPos symbol.
extern "C" const char *T4_GetReplacedPos(const char *pos, scriptInstance_t inst);

class GSCFunctions : public Module
{
public:
GSCFunctions();
~GSCFunctions();

static void OnVMShutdown();
static void ReplaceFunc();

private:
friend const char *T4_GetReplacedPos(const char *pos, scriptInstance_t inst);

static std::unordered_map<const char *, const char *> ReplacedFunctions;

static const char *GetCodePosForParam(int index);
static const char *GetReplacedPos(const char *pos, scriptInstance_t inst);

static Detour VM_Execute_Detour;
static void VM_Execute_Hook();
};
} // namespace mp
} // namespace t4
75 changes: 75 additions & 0 deletions src/game/t4/mp/structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1393,5 +1393,80 @@ enum connstate_t
CA_ACTIVE = 0x9,
};

enum Vartype_t : __int32
{
VAR_UNDEFINED = 0x0,
VAR_POINTER = 0x1,
VAR_STRING = 0x2,
VAR_ISTRING = 0x3,
VAR_VECTOR = 0x4,
VAR_FLOAT = 0x5,
VAR_INTEGER = 0x6,
VAR_CODEPOS = 0x7,
VAR_PRECODEPOS = 0x8,
VAR_FUNCTION = 0x9,
};

struct VariableStackBuffer;

union VariableUnion
{
int intValue;
unsigned int uintValue;
float floatValue;
unsigned int stringValue;
const float *vectorValue;
const char *codePosValue;
unsigned int pointerValue;
VariableStackBuffer *stackValue;
unsigned int entityOffset;
};

struct VariableValue
{
VariableUnion u;
Vartype_t type;
};

static_assert(sizeof(VariableValue) == 0x8, "");

struct function_stack_t
{
const char *pos;
unsigned int localId;
unsigned int localVarCount;
VariableValue *top;
VariableValue *startTop;
};

struct function_frame_t
{
function_stack_t fs;
int topType;
};

struct scrVmPub_t
{
unsigned int *localVars;
VariableValue *maxstack;
int function_count;
function_frame_t *function_frame;
VariableValue *top;
bool debugCode;
bool abort_on_error;
bool terminal_error;
unsigned int inparamcount;
unsigned int outparamcount;
function_frame_t function_frame_start[32];
VariableValue stack[2048];
};

static_assert(sizeof(function_frame_t) == 0x18, "");
static_assert(offsetof(scrVmPub_t, top) == 0x10, "");
static_assert(offsetof(scrVmPub_t, outparamcount) == 0x1C, "");
static_assert(offsetof(scrVmPub_t, function_frame_start) == 0x20, "");
static_assert(offsetof(scrVmPub_t, stack) == 0x320, "");
static_assert(sizeof(scrVmPub_t) == 0x4320, "");

} // namespace mp
} // namespace t4
1 change: 1 addition & 0 deletions src/game/t4/mp/symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ static auto Scr_GetGenericField = reinterpret_cast<void (*)(unsigned __int8 *b,
static auto Scr_GetInt =
reinterpret_cast<int (*)(unsigned int index, scriptInstance_t inst, __int64 a3, __int64 a4)>(0x8234AFD0);
static auto Scr_GetNumParam = reinterpret_cast<unsigned int (*)(scriptInstance_t inst)>(0x82345650);
static scrVmPub_t *scrVmPub = reinterpret_cast<scrVmPub_t *>(0x85AF2F98);
static auto Scr_GetObjectField =
reinterpret_cast<void (*)(unsigned int classnum, int entnum, int offset, scriptInstance_t inst)>(0x822556B0);
static auto Scr_GetString = reinterpret_cast<const char *(*)(unsigned int index, scriptInstance_t inst)>(0x8234B550);
Expand Down
Loading