This is an approach to give some kind of tool with the aim to help developers go deeper inside Engine (aka GoldSrc).
It helps a lot when there's to many offsets to research.
By the time, this is only for linux but anyone who want to test on windows are welcome.
What we have here? A bunch of C header files (most of them from HL-SDK) adapted to work on IDA and maybe at compile time.
How to use it? - Download engine-sdk.zip
- Uncompress somewhere on your old hard disk
- Open a new project on IDA
- On IDA Press CTRL+F9 and select engine.h from the directory created with engine-sdk
- Check you don't get any error (you shouldn't)
- Do a exorcist like head turn
- Press SHIFT+F1 to see LOCAL TYPES window on IDA
- Select all listed entries, right click, Syncronize to idb
- Give us a smile
What's included?
Common C structs from HLSDK
C structs for vtable and offsets on some classesglobalvars_t
enginefuncs_t
CBaseEntity
CBaseDelay
CBaseAnimating
CWeaponBox
CBasePlayerItem
CBaseToggle
CBasePlayer
Manually reversed structssvs_t (linux symbol: svs)
client_t (linux symbol: host_client)
netchan_t
and others
CaveatsSome original data types are objects (like Vector) so the better solution I found is to replace them with static structures (use _Origin type for float Vector type and _iOrigin type for int types).
What's next?I'll be working on other unknown structures (sv is the big one) and maybe adding new vtable structures.
Errors?Yes, maybe there're some errors but sharing this will help to make it better.
Anyone who wants to help are welcome.
Now, let see what can be done with this
SV_ParseVoiceData
After setting the correct definition of the function we can get a more readable code (put your mouse cursor over the function name, press Y and then write modify the definition).
[IMG]http://img594.**************/img594/9751/svparsevoicedata.jpg[/IMG]
SV_InactivateClients
Here's another example.
[IMG]http://img545.**************/img545/3470/svinactivateclients.jpg[/IMG]
This seem to be easy to reach.
Let's see some other examples (now on CS library).
First open a new project with cs_i386.so and import this SDK.
Look wha't I get after some work on function and variables definitions.
Code:
int __cdecl CBaseEntity::TakeHealth(CBaseEntity *pEnt, float flHealth)
{
entvars_s *pev; // ecx@1
__int16 v3; // fps@1
long double takedamage; // fst6@1
char v5; // c0@1
char v6; // c2@1
char v7; // c3@1
__int16 v8; // fps@2
long double v9; // fst7@2
char v10; // c0@2
char v11; // c2@2
char v12; // c3@2
int result; // eax@3
entvars_s *pevaux; // edx@4
pev = pEnt->pev;
takedamage = pEnt->pev->takedamage;
UNDEF(v3);
v5 = takedamage < 0.0;
v6 = 0;
v7 = takedamage == 0.0;
if ( (HIBYTE(v3) & 0x45) != 64
&& (v9 = pev->health,
UNDEF(v8),
v10 = v9 < pev->max_health,
v11 = 0,
v12 = v9 == pev->max_health,
(HIBYTE(v8) & 0x45) == 1) )
{
pev->health = v9 + flHealth;
pevaux = pEnt->pev;
if ( pevaux->max_health < (long double)pevaux->health )
pevaux->health = pevaux->max_health;
result = 1;
}
else
{
result = 0;
}
return result;
}
Now, here's another example with vtable functions (extracted from CBasePlayer :: PreThink)
Code:
v24 = v23 < *(float *)(g_pGameRules + 152);
v25 = 0;
v26 = v23 == *(float *)(g_pGameRules + 152);
if ( (unsigned __int8)((HIBYTE(v22) & 0x45) - 1) >= 0x40u )
{
if ( !((int (__cdecl *)(CBasePlayer *))ent->vtable->CBasePlayer__IsBot)(ent) )
{
g_engfuncs.pfnCVarGetFloat("mp_autokick");
UNDEF(v27);
v28 = 0.0 < v23;
v29 = 0;
v30 = 0.0 == v23;
if ( (HIBYTE(v27) & 0x45) != 64 )
{
GetTeam(ent->m_iPlayerteam);
g_engfuncs.pfnGetPlayerAuthId(ent->pev->pContainingEntity);
g_engfuncs.pfnGetPlayerUserId(ent->pev->pContainingEntity);
UTIL_LogPrintf(
"\"%s<%i><%s><%s>\" triggered \"Game_idle_kick\" (auto)\n",
LOBYTE(gpGlobals->pStringBase) + LOBYTE(ent->pev->netname));
UTIL_ClientPrintAll(2, "#Game_idle_kick", &gpGlobals->pStringBase[ent->pev->netname], 0, 0, 0);
v31 = (char *)UTIL_VarArgs("kick \"%s\"\n", LOBYTE(gpGlobals->pStringBase) + LOBYTE(ent->pev->netname));
g_engfuncs.pfnServerCommand(v31);
*(float *)&ent->player_dummy3[2] = gpGlobals->time;
}
}
}
}
if ( g_pGameRules && (*(int (__cdecl **)(int))(*(_DWORD *)(g_pGameRules + 8) + 20))(g_pGameRules) )
LOBYTE(ent->m_rgpPlayerItems[0]) &= 0xFDu;
else
LOBYTE(ent->m_rgpPlayerItems[0]) |= 2u;
ent->vtable->CBasePlayer__UpdateClientData();
CBasePlayer::CheckTimeBasedDamage(ent);
CBasePlayer::CheckSuitUpdate(ent);
if ( ent->m_afPhysicsFlags & 2 )
BYTE3(ent->pev->flags) |= 1u;
else
BYTE3(ent->pev->flags) &= 0xFEu;
if ( ent->pev->iuser1 && ent->m_afPhysicsFlags & 0x20 )
{
CBasePlayer::Observer_HandleButtons(ent);
CBasePlayer::Observer_CheckTarget(ent);
CBasePlayer::Observer_CheckProperties(ent);
return;
}
v32 = ent->pev->deadflag;
if ( v32 > 0 && v32 != 3 )
{
CBasePlayer::PlayerDeathThink(ent);
return;
}
v33 = ent->pev->groundentity;
if ( v33 || (v33 = g_engfuncs.pfnPEntityOfEntOffset(0)) != 0 )
v34 = v33->pvPrivateData;
else
v34 = 0;
ent->pev->iuser4 = v34 && (*(int (__cdecl **)(void *))(*((_DWORD *)v34 + 37) + 44))(v34) == 14;
if ( !(ent->m_afPhysicsFlags & 2) )
{
if ( SLOBYTE(ent->m_fWeapon) < 0 )
ent->m_fWeapon = 192;
goto LABEL_84;
}
v35 = ent->pev->groundentity;
if ( v35 || (v35 = g_engfuncs.pfnPEntityOfEntOffset(0)) != 0 )
v36 = v35->pvPrivateData;
else
v36 = 0;
v37 = v36;