К примеру, на основе Hamsandwich takedamage прописывается индекс виртуальной функции в
hamdata.ini. Который потом парсится в
config_parser.cpp. В функции
process_key:
[pawn]
for (int i=0; i< HAM_LAST_ENTRY_DONT_USE_ME_LOL; i++)
{
if (strncmp(data, hooklist[i].name, size)==0)
{
data+=size+1;
trim_line(data);
int value=read_number(data);
hooklist[i].isset=1;
hooklist[i].vtid=value;
set=1;
break;
}
}
[/pawn]
Соответственно значения сохраняются в hooklist, используя enum из
ham_const.h, к примеру:
[pawn]
Ham_TakeDamage
[/pawn]
Если вести отсчет от 0, то Ham_TakeDamage - 9 в нашем enum'е.
Сама структура hooklist прописана в
hooklist.h:
[pawn]
typedef struct hook_s
{
int isset; // whether or not this hook is registered with hamdata
int vtid; // vtable index of this function
const char *name; // name used in the keys
bool isvoid; // whether or not the target trampoline uses voids
int paramcount; // how many parameters are in the func
void *targetfunc; // the target hook
int (*makefunc)(AMX *, const char*); // function that creates forwards
cell (*call)(AMX *, cell*); // function to call the vcall
} hook_t;
extern hook_t hooklist[];
[/pawn]
Также в
hook_native.cpp заполняется наш hooklist:
[pawn]
hook_t hooklist[] =
{
{ V("spawn", Void_Void) },
{ V("precache", Void_Void) },
{ V("keyvalue", Void_Int) },
{ V("objectcaps", Int_Void) },
{ V("activate", Void_Void) },
{ V("setobjectcollisionbox", Void_Void) },
{ V("classify", Int_Void) },
{ V("deathnotice", Void_Entvar) },
{ V("traceattack", Void_Entvar_Float_Vector_Trace_Int) },
{ V("takedamage", Int_Entvar_Entvar_Float_Int) },
[/pawn]
На основе define:
[pawn]
#define V(__KEYNAME, __STUFF__) 0, 0, __KEYNAME, RT_##__STUFF__, PC_##__STUFF__, reinterpret_cast<void *>(Hook_##__STUFF__), Create_##__STUFF__, Call_##__STUFF__
[/pawn]
В результате чего еще будут функции, если рассматривать takedamage - Int_Entvar_Entvar_Float_Int с префиксом Call_, Hook_, Create_, который есть в call_funcs.cpp (.h), hook_callbacks.cpp (.h), hook_create.cpp (.h).
По идеи в Call происходит вызов функции, в Hook ее перехват.
Еще в
ham_utils.h есть:
[pawn]
inline void **GetVTable(void *pthis, int size)
{
return *((void***)(((char*)pthis)+size));
}
inline void *GetVTableEntry(void *pthis, int ventry, int size)
{
void **vtbl=GetVTable(pthis, size);
return vtbl[ventry];
}
[/pawn]
Которые используются другой функцией
_GetFunction из
call_funcs.cpp:
[pawn]
inline void *GetFunction(void *pthis, int id, bool &istramp)
{
istramp=false;
void *func=GetVTableEntry(pthis, hooklist[id].vtid, Offsets.GetBase());
// Check to see if it's a trampoline
CVector<Hook *>::iterator end=hooks[id].end();
for (CVector<Hook *>::iterator i=hooks[id].begin();
i!=end;
++i)
{
if (func==(*i)->tramp)
{
istramp=true;
return func;
}
}
return func;
}
inline void *_GetFunction(void *pthis, int id)
{
void **vtbl=GetVTable(pthis, Offsets.GetBase());
int **ivtbl=(int **)vtbl;
void *func=ivtbl[hooklist[id].vtid];
// Iterate through the hooks for the id, see if the function is found
CVector<Hook *>::iterator end=hooks[id].end();
for (CVector<Hook *>::iterator i=hooks[id].begin();
i!=end;
++i)
{
// If the function points to a trampoline, then return the original
// function.
if (func==(*i)->tramp)
{
printf("Func=0x%08X\n",reinterpret_cast<unsigned int>((*i)->func));
return (*i)->func;
}
}
// this is an original function
printf("Func=0x%08X\n",reinterpret_cast<unsigned int>(func));
return func;
}
[/pawn]
GetFunction используется в SETUP:
[pawn]
#define SETUP(NUMARGS) \
if (((NUMARGS + 2) * sizeof(cell)) > (unsigned)params[0]) \
{ \
MF_LogError(amx, AMX_ERR_NATIVE, "Bad arg count. Expected %d, got %d.", NUMARGS + 2, params[0] / sizeof(cell)); \
return 0; \
} \
int func=params[1]; \
int id=params[2]; \
CHECK_FUNCTION(func); \
CHECK_ENTITY(id); \
void *pv=IndexToPrivate(id); \
bool istramp; \
void *__func=GetFunction(pv, func, istramp); \
if (!istramp && !gDoForwards) \
{ \
gDoForwards=true; \
}
[/pawn]
Где получаем __func, а помним что у takdamage - Int_Entvar_Entvar_Float_Int, то находим ее с префиксом Call_ и смотрим:
[pawn]
cell Call_Int_Entvar_Entvar_Float_Int(AMX *amx, cell *params)
{
SETUP(4);
int id3=*MF_GetAmxAddr(amx, params[3]);
int id4=*MF_GetAmxAddr(amx, params[4]);
float f5=amx_ctof2(*MF_GetAmxAddr(amx, params[5]));
int i6=*MF_GetAmxAddr(amx, params[6]);
CHECK_ENTITY(id3);
CHECK_ENTITY(id4);
entvars_t *ev3=&(INDEXENT_NEW(id3)->v);
entvars_t *ev4=&(INDEXENT_NEW(id4)->v);
#ifdef _WIN32
return reinterpret_cast<int (__fastcall *)(void *, int, entvars_t *, entvars_t *, float, int)>(__func)(pv, 0, ev3, ev4, f5, i6);
#elif defined __linux__
return reinterpret_cast<int (*)(void *, entvars_t *, entvars_t *, float, int)>(__func)(pv, ev3, ev4, f5, i6);
#endif
}
[/pawn]
Здесь видно используется SETUP, а затем наш __func через reinterpret_cast.
Это лишь структура работы, чтобы более внятно понять весь механизм надо знать C++, чтобы понимать все действия в коде.