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
91 changes: 91 additions & 0 deletions src/game/iw3/mp/components/gsc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace iw3
{
namespace mp
{
std::unordered_map<const char *, const char *> GSC::ReplacedFunctions;
namespace
{
static const gsc::Entry<BuiltinFunction> functions[] = {
Expand All @@ -23,6 +24,7 @@ static const gsc::Entry<BuiltinFunction> functions[] = {
{"float", GScr_Float, BUILTIN_ANY},
{"precachestring", Scr_PrecacheString_Stub, BUILTIN_ANY},
{"addtestclient", GScr_AddTestClient, BUILTIN_ANY},
{"replacefunc", GSC::ReplaceFunc, BUILTIN_ANY},
};

static const gsc::Entry<BuiltinMethod> methods[] = {
Expand Down Expand Up @@ -83,6 +85,85 @@ BuiltinMethod Scr_GetMethod_Hook(const char **pName, int *type)
return Scr_GetMethod_Detour.GetOriginal<decltype(Scr_GetMethod)>()(pName, type);
}

Detour GSC::VM_Execute_Detour;

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

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

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

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

return value->u.codePosValue;
}

const char *GSC::GetReplacedPos(const char *pos)
{
if (!pos || ReplacedFunctions.empty())
return pos;

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

extern "C" const char *GetReplacedPos(const char *pos)
{
return GSC::GetReplacedPos(pos);
}

__declspec(naked) void GSC::VM_Execute_Hook()
{
__asm
{
mr r3, r11
bl GetReplacedPos

mr r11, r3
mr r10, r3

lbz r11, 0(r11)
addi r10, r10, 1
cmplwi cr6, r11, 0x86
stw r11, 0x400(r17)

lis r12, 0x8221
ori r12, r12, 0x2E40
mtctr r12
bctr
}
}

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

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

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

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

ReplacedFunctions[what] = with;
}

GSC::GSC()
{
Scr_GetFunction_Detour = Detour(Scr_GetFunction, Scr_GetFunction_Hook);
Expand All @@ -91,11 +172,17 @@ GSC::GSC()
Scr_GetMethod_Detour = Detour(Scr_GetMethod, Scr_GetMethod_Hook);
Scr_GetMethod_Detour.Install();

// Intercept TU4 VM_Execute's opcode fetch to redirect registered function code positions, then resume at
// 0x82212E40.
VM_Execute_Detour = Detour(reinterpret_cast<void *>(0x82212E30), reinterpret_cast<const void *>(VM_Execute_Hook));
VM_Execute_Detour.Install();

InitializeHudElemMethods();
}

void GSC::OnVMShutdown()
{
ReplacedFunctions.clear();
ClearHudElemLocalizedStringState();
}

Expand All @@ -106,6 +193,10 @@ GSC::~GSC()
Scr_GetFunction_Detour.Remove();

Scr_GetMethod_Detour.Remove();

VM_Execute_Detour.Remove();

ReplacedFunctions.clear();
}
} // namespace mp
} // namespace iw3
16 changes: 16 additions & 0 deletions src/game/iw3/mp/components/gsc.h
Original file line number Diff line number Diff line change
@@ -1,18 +1,34 @@
#pragma once

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

namespace iw3
{
namespace mp
{
// The Xbox PowerPC inline assembler requires an unmangled name for its direct branch.
extern "C" const char *GetReplacedPos(const char *pos);

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

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

private:
friend const char *GetReplacedPos(const char *pos);

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

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

static Detour VM_Execute_Detour;
static void VM_Execute_Hook();
};
} // namespace mp
} // namespace iw3
59 changes: 59 additions & 0 deletions src/game/iw3/mp/structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -3779,5 +3779,64 @@ struct __declspec(align(2)) playerStatNetworkData
};
static_assert(sizeof(playerStatNetworkData) == 0x421C, "");

struct VariableStackBuffer;

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

struct VariableValue
{
VariableUnion u;
int 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 iw3
1 change: 1 addition & 0 deletions src/game/iw3/mp/symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@ static const char **var_typename = reinterpret_cast<const char **>(0x823A2820);
static auto cgMedia = reinterpret_cast<cgMedia_t *>(0x823F28A8);
static auto sharedUiInfo = reinterpret_cast<sharedUiInfo_t *>(0x849F8080);
static auto scr_const = reinterpret_cast<scr_const_t *>(0x82A22F18);
static scrVmPub_t &scrVmPub = *reinterpret_cast<scrVmPub_t *>(0x82E3B0B0);

static int *g_trackLoadProgress = reinterpret_cast<int *>(0x824754FC);
static int *g_totalSize = reinterpret_cast<int *>(0x824754F8);
Expand Down
Loading