Time ago I've had troubles
with hooking this function with Orpheu.
Now I tried to make a module to deal with this, by knowing it was some kind of weird optimization. It has the same behavior that SetAnimation function has. Reference:
https://forums.alliedmods.net/showpo...postcount=1462
First, what I tried to do is to read Arkshine's way to deal with SetAnimation module:
https://forums.alliedmods.net/showpo...51&postcount=7
Then I looked through the gamedll with IDA Pro and discover what does register store.
Inside CBasePlayer:
ackDeadPlayerItems:
and..:
What I guessed first was "ECX contains packAmmo parameter", which makes sense reading a decompiled version of PackDeadPlayerItems (it's always 1)
Then, reading this code, I tried to understand decompiled version of it.
PHP Code:
void packPlayerItem(CBasePlayer *pPlayer, CBasePlayerItem *pItem, bool packAmmo)
{
if (pItem) // ITEM CHECK FIRST
{
const char *modelName = GetCSModelName(pItem->m_iId);
if (modelName)
{
CWeaponBox *pWeaponBox = (CWeaponBox *)CBaseEntity::Create("weaponbox", pPlayer->pev->origin, pPlayer->pev->angles, ENT(pPlayer->pev));
pWeaponBox->pev->angles.x = 0;
pWeaponBox->pev->angles.y = 0;
pWeaponBox->pev->velocity = pPlayer->pev->velocity * 0.75;
pWeaponBox->SetThink(&CWeaponBox::Kill);
pWeaponBox->pev->nextthink = gpGlobals->time + 300;
pWeaponBox->PackWeapon(pItem);
if (packAmmo) // 3RD PARAM CONDITION
pWeaponBox->PackAmmo(MAKE_STRING(CBasePlayerItem::ItemInfoArray[pItem->m_iId].pszAmmo1), pPlayer->m_rgAmmo[pItem->PrimaryAmmoIndex()]);
SET_MODEL(ENT(pWeaponBox->pev), modelName);
}
}
}
Then, just with the decompiled version:
Code:
int __cdecl packPlayerItem
(CBasePlayer *a1, CBasePlayerItem *a2,
bool a3
)
{
int result; // eax@0
int v4; // edx@0
char v5; // cl@0
int v6;
// ebx@1
int v7;
// esi@1
int v8;
// ebp@2
int v9;
// edi@3
int v10;
// edx@3
int v11;
// eax@3
long double v12; // fst6@3
long double v13;
// fst7@3
long double v14;
// fst5@3
globalvars_t *v15;
// edx@3
int v16;
// eax@3
int v17;
// eax@6
int this;
// [sp+0h] [bp-3Ch]@0
edict_s *v19;
// [sp+10h] [bp-2Ch]@0
char v20;
// [sp+1Fh] [bp-1Dh]@1
v6 = result;
v7 = v4;
v20 = v5;
if ( v4 ) // if(pItem) ???
{
result = GetCSModelName
(this
);
v8 = result;
if ( result
)
{
v9 = CBaseEntity::
Create(
"weaponbox",
(char *
)(*
(_DWORD *
)(v6 +
4) +
8),
(const Vector *
)(*
(_DWORD *
)(v6 +
4) +
80),
*
(const Vector **
)(*
(_DWORD *
)(v6 +
4) +
520),
v19
);
*
(_DWORD *
)(*
(_DWORD *
)(v9 +
4) +
80) =
0;
*
(_DWORD *
)(*
(_DWORD *
)(v9 +
4) +
88) =
0;
v10 = *
(_DWORD *
)(v6 +
4);
v11 = *
(_DWORD *
)(v9 +
4);
v12 = *
(float *
)(v10 +
40) *
0.75;
v13 = *
(float *
)(v10 +
36) *
0.75;
v14 =
0.75 * *
(float *
)(v10 +
32);
v15 = gpGlobals;
*
(float *
)(v11 +
32) = v14;
*
(float *
)(v11 +
36) = v13;
*
(float *
)(v11 +
40) = v12;
*
(_DWORD *
)(v9 +
16) = CWeaponBox::
Kill;
v16 = *
(_DWORD *
)(v9 +
4);
*
(_DWORD *
)(v9 +
20) =
0;
*
(float *
)(v16 +
260) = v15->time +
300.0;
CWeaponBox::
PackWeapon((CWeaponBox *
)v9,
(CBasePlayerItem *
)v7
);
if ( v20 ) // 3RD PARAM CONDITION
{
v17 =
(*
(int
(__cdecl **
)(int
))(*
(_DWORD *
)v7 +
296))(v7
);
CWeaponBox::
PackAmmo(
(CWeaponBox *
)v9,
*
(_DWORD *
)&CBasePlayerItem::
ItemInfoArray[44 * *
(_DWORD *
)(v7 +
188) +
8]
-
(unsigned int
)gpGlobals->pStringBase,
*
(_DWORD *
)(v6 +
4 * v17 +
1524));
}
result =
((int
(__cdecl *
)(_DWORD, int
))g_engfuncs.
pfnSetModel)(*
(_DWORD *
)(*
(_DWORD *
)(v9 +
4) +
520), v8
);
}
}
return result;
}
Just read it and you'll understand.
v20 is packAmmo, v20 equals to v5 and v5 contains the value of CL register, lower part of ECX register, set as 1 in the pictures below. (
also see this one)
v4 is pItem, and it points to EDX register.
Inside CBaseEntity::Create you can see references with v6 to the player pev->origin entvar. then you guess that v6 = result, and results references to the EAX register.
Now it makes sense this picture
I managed to create this code, by reading a few guides of Inline Assembly in cpp and also Arkshine's SetAnimation hooker:
PHP Code:
//native Call_packPlayerItem(iId, iAnim)
static cell AMX_NATIVE_CALL Native_Call_packPlayerItem(AMX *amx, cell *params)
{
void* pPlayer = TypeConversion.id_to_cbase(params[1]);
if(!pPlayer)
{
MF_LogError(amx, AMX_ERR_NATIVE, "Invalid player %d", params[1]);
return 0;
}
void* pItem = TypeConversion.id_to_cbase(params[2]);
if(!pItem)
{
MF_LogError(amx, AMX_ERR_NATIVE, "Invalid item %d", params[2]);
return 0;
}
int packAmmo = params[3];
SERVER_PRINT(UTIL_VarArgs("Calling packPlayerItem(%p, %p, %s)\n", pPlayer, pItem, packAmmo ? "TRUE" : "FALSE"));
asm volatile
(
"movl %0, %%eax;"
"movl %1, %%edx;"
"movl %2, %%ecx;"
: /* no output */
: "m" (pPlayer), "m" (pItem), "m" (packAmmo)
: "%eax", "%edx", "%ecx"
);
OrigFunc_packPlayerItem(pPlayer, pItem, (bool)packAmmo);
return 1;
}
Retrieving it's address correctly also:
PHP Code:
void Patch_packPlayerItem()
{
OrigFunc_packPlayerItem = Hooker->MemorySearch< Func_packPlayerItem >( "0x83,*,*,0x85,*,0x89,*,*,*,0x89,*,0x89,*,*,*,0x89,*,0x89,*,*,*,0x89", ( void* )MDLL_Spawn, FALSE );
if(OrigFunc_packPlayerItem)
{
// hooking was a hell also, so what I did was just to get function address
/*Hook_packPlayerItem = Hooker->CreateHook( ( void* )OrigFunc_packPlayerItem, ( void* )OnpackPlayerItem, TRUE );
if( Hook_packPlayerItem )
SERVER_PRINT( "Hook created successfully\n" );
else
SERVER_PRINT( "Hook creation failed\n" );*/
}
else
SERVER_PRINT( "Signature/symbol could not be found\n" );
}
Output throws correct values, function executed normally, weapon thrown and tested with players.
PHP Code:
L 02/08/2017 - 05:27:16: "PM32<1><STEAM_0:0:nothankslol><>" joined team "CT"
Calling packPlayerItem(0xac02210, 0xabfec30, TRUE)
Calling packPlayerItem(0xac02210, 0xac061e0, TRUE)
Calling packPlayerItem(0xac02210, 0xac06a10, TRUE)
The most FUNNY about about this is the next.
If I comment SERVER_PRINT call, server crashes with a seg fault. Why? I dont know.
So, my first guess is the inline assembler code that I wrote has a mistake, and I believe it's that. Am I doing right with it? Or am I forgetting something?
Thanks for reading.
__________________