Skip to content

Guide: Hooking game functions

Archie_UwU edited this page Jan 4, 2024 · 4 revisions

Note

This guide is written with YYToolkit Next in mind. While it is possible to adapt these methods to YYToolkit Legacy, doing so would require using undocumented functions such as MmGetScriptData.

Also, note that the code snippets below omit any error handling. If your module relies on these hooks, ensuring their integrity is critical!

Hooking refers to a process of intercepting engine functions for the purpose of altering or monitoring arguments and return values. While hooking is a generally supported process, issues may sometimes arise, and it is always better to use the CreateCallback function if applicable.


Built-in function hooking

Syntax

void Hook(
    [out]          RValue*    Result,
    [in, optional] CInstance* Self,
    [in, optional] CInstance* Other,
    [in]           int        ArgumentCount,
    [in, optional] RValue*    Arguments
);

Parameters

Result

A reference (or pointer) to a buffer into which the result of the function is written. If the function returns no value, this parameter is left untouched. This parameter is never nullptr, and can safely be turned into RValue&.

Self

An optional pointer to the instance referred to via the self keyword. This value may be nullptr if the function is being called by another YYToolkit module. You may use this pointer with API functions such as GetInstanceMember to manipulate variables of the instance.

Other

An optional pointer to the instance referred to via the other keyword. This value may be nullptr if the function is being called by another YYToolkit module. If the currently executing snippet of code has no other instance, the Self and Other arguments point to the same instance. You may use this pointer with API functions such as GetInstanceMember to manipulate variables of the instance.

ArgumentCount

The number of arguments passed to the function.

Arguments

A pointer to the first element in an array of RValue, with the valid indices ranging from 0 to ArgumentCount - 1.

Example

You may create a hook for a built-in function by using GetNamedRoutinePointer to get the a pointer to the engine function. This pointer will be supplied to the MmCreateHook Aurie API function. For an example, refer to the code snippet below.

TRoutine game_function = nullptr;
TRoutine original_function = nullptr;

// Get a pointer to the target function using YYToolkit's interface
g_YYTKInterface->GetNamedRoutinePointer(
    "Target Function",
    reinterpret_cast<PVOID*>(&game_function)
);

// Create the hook
MmCreateHook(
    g_ArSelfModule,
    "My Hook",
    game_function,
    Hook,
    reinterpret_cast<PVOID*>(&original_function)
);

Script function hooking

Syntax

RValue& Hook(
   [in, optional] CInstance* Self,
   [in, optional] CInstance* Other,
   [out]          RValue&    ReturnValue,
   [in]           int        ArgumentCount
   [in, optional] RValue**   Arguments
);

Parameters

Self

An optional pointer to the instance referred to via the self keyword. This value may be nullptr if the function is being called by another YYToolkit module. You may use this pointer with API functions such as GetInstanceMember to manipulate variables of the instance.

Other

An optional pointer to the instance referred to via the other keyword. This value may be nullptr if the function is being called by another YYToolkit module. If the currently executing snippet of code has no other instance, the Self and Other arguments point to the same instance. You may use this pointer with API functions such as GetInstanceMember to manipulate variables of the instance.

ReturnValue

A reference (or pointer) to an engine-allocated buffer, into which the return value of the script is written. This reference is always valid, even for scripts that return no value.

ArgumentCount

The number of arguments passed to the function.

Arguments

An optional pointer to the first element in an array of RValue*, which are interpreted as arguments to the script. If the ArgumentCount parameter is set to 0, this pointer may be nullptr.

Return Value

The function always returns the reference pased into the ReturnValue parameter.

Caution

Returning anything other than ReturnValue from a hooked script function is undefined behavior.

The engine may become "confused" on what the return value is, which could have an impact on game stability.

Example

You may create a hook on a script function by using GetNamedRoutinePointer, and extracting the function pointer from the CScript object.

CScript* script_data = nullptr;
int script_index = 0;

// Get the script data
g_YYTKInterface->GetNamedRoutinePointer(
    "Target script name",
    reinterpret_cast<PVOID*>(&script_data)
);

// Create the hook
MmCreateHook(
    g_ArSelfModule,
    "My Hook",
    script_data->m_Functions->m_ScriptFunction,
    Hook,
    reinterpret_cast<PVOID*>(&original_function)
);

Quick Access

Documentation

Writeups

Clone this wiki locally