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.
__________________