Fix cstrike extension natives/forwards due to update

This commit is contained in:
Ruben Gonzalez 2017-08-21 10:28:59 -04:00
parent 8627c0cbfc
commit 3eb297bee1
6 changed files with 540 additions and 347 deletions

View File

@ -8,7 +8,9 @@ bool g_pIgnoreTerminateDetour = false;
bool g_pIgnoreCSWeaponDropDetour = false;
bool g_PriceDetoured = false;
bool g_HandleBuyDetoured = false;
#if SOURCE_ENGINE != SE_CSGO
int lastclient = -1;
#endif
IForward *g_pHandleBuyForward = NULL;
IForward *g_pPriceForward = NULL;
@ -22,10 +24,77 @@ CDetour *DCSWeaponDrop = NULL;
int weaponNameOffset = -1;
#if SOURCE_ENGINE == SE_CSGO
DETOUR_DECL_MEMBER3(DetourHandleBuy, int, int, iUnknown, const char *, weapon, bool, bRebuy)
DETOUR_DECL_MEMBER3(DetourHandleBuy, int, int, iLoadoutSlot, void *, pWpnDataRef, bool, bRebuy)
{
int client = gamehelpers->EntityToBCompatRef(reinterpret_cast<CBaseEntity *>(this));
CEconItemView *pView = GetEconItemView(this, iLoadoutSlot);
if (!pView)
{
return DETOUR_MEMBER_CALL(DetourHandleBuy)(iLoadoutSlot, pWpnDataRef, bRebuy);
}
void *pWpnData = GetCCSWeaponData(pView);
if (!pWpnData)
{
return DETOUR_MEMBER_CALL(DetourHandleBuy)(iLoadoutSlot, pWpnDataRef, bRebuy);
}
const char *szClassname = *(const char **)((intptr_t)pWpnData + weaponNameOffset);
char weaponName[128];
if (strstr(szClassname, "knife"))
{
Q_strcpy(weaponName, "knife");
}
else
{
const char *underscore = strstr(szClassname, "_");
if (underscore)
{
Q_strncpy(weaponName, (const char *)((intptr_t)underscore + 1), sizeof(weaponName));
}
else
{
Q_strcpy(weaponName, szClassname);
}
}
cell_t result = Pl_Continue;
g_pHandleBuyForward->PushCell(client);
g_pHandleBuyForward->PushString(weaponName);
g_pHandleBuyForward->Execute(&result);
if (result != Pl_Continue)
{
return 0;
}
int originalPrice = 0;
if (g_iPriceOffset != -1)
{
originalPrice = *(int *)((intptr_t)pWpnData + g_iPriceOffset);
int changedPrice = CallPriceForward(client, weaponName, originalPrice);
if (originalPrice != changedPrice)
*(int *)((intptr_t)pWpnData + g_iPriceOffset) = changedPrice;
}
int ret = DETOUR_MEMBER_CALL(DetourHandleBuy)(iLoadoutSlot, pWpnDataRef, bRebuy);
if (g_iPriceOffset != -1)
*(int *)((intptr_t)pWpnData + g_iPriceOffset) = originalPrice;
return ret;
}
#else
DETOUR_DECL_MEMBER1(DetourHandleBuy, int, const char *, weapon)
#endif
{
int client = gamehelpers->EntityToBCompatRef(reinterpret_cast<CBaseEntity *>(this));
@ -43,31 +112,17 @@ DETOUR_DECL_MEMBER1(DetourHandleBuy, int, const char *, weapon)
return 0;
}
#if SOURCE_ENGINE == SE_CSGO
int val = DETOUR_MEMBER_CALL(DetourHandleBuy)(iUnknown, weapon, bRebuy);
#else
int val = DETOUR_MEMBER_CALL(DetourHandleBuy)(weapon);
#endif
lastclient = -1;
return val;
}
#endif
#if SOURCE_ENGINE != SE_CSGO
DETOUR_DECL_MEMBER0(DetourWeaponPrice, int)
#elif defined(WIN32)
DETOUR_DECL_MEMBER2(DetourWeaponPrice, int, CEconItemView *, pEconItem, int, iUnknown)
#else
DETOUR_DECL_MEMBER3(DetourWeaponPrice, int, CEconItemView *, pEconItem, int, iUnknown, float, fUnknown)
#endif
{
#if SOURCE_ENGINE != SE_CSGO
int price = DETOUR_MEMBER_CALL(DetourWeaponPrice)();
#elif defined(WIN32)
int price = DETOUR_MEMBER_CALL(DetourWeaponPrice)(pEconItem, iUnknown);
#else
int price = DETOUR_MEMBER_CALL(DetourWeaponPrice)(pEconItem, iUnknown, fUnknown);
#endif
if (lastclient == -1)
return price;
@ -76,6 +131,7 @@ DETOUR_DECL_MEMBER3(DetourWeaponPrice, int, CEconItemView *, pEconItem, int, iUn
return CallPriceForward(lastclient, weapon_name, price);
}
#endif
#if SOURCE_ENGINE != SE_CSGO || !defined(WIN32)
DETOUR_DECL_MEMBER2(DetourTerminateRound, void, float, delay, int, reason)
@ -179,6 +235,7 @@ DETOUR_DECL_MEMBER3(DetourCSWeaponDrop, void, CBaseEntity *, weapon, bool, bDrop
bool CreateWeaponPriceDetour()
{
#if SOURCE_ENGINE != SE_CSGO
if (weaponNameOffset == -1)
{
if (!g_pGameConf->GetOffset("WeaponName", &weaponNameOffset))
@ -188,18 +245,7 @@ bool CreateWeaponPriceDetour()
}
}
#if SOURCE_ENGINE == SE_CSGO
void *pGetWeaponPriceAddress = GetWeaponPriceFunction();
if(!pGetWeaponPriceAddress)
{
g_pSM->LogError(myself, "GetWeaponPrice detour could not be initialized - Disabled OnGetWeaponPrice forward.");
}
DWeaponPrice = DETOUR_CREATE_MEMBER(DetourWeaponPrice, pGetWeaponPriceAddress);
#else
DWeaponPrice = DETOUR_CREATE_MEMBER(DetourWeaponPrice, "GetWeaponPrice");
#endif
if (DWeaponPrice != NULL)
{
if (!CreateHandleBuyDetour())
@ -211,7 +257,27 @@ bool CreateWeaponPriceDetour()
g_PriceDetoured = true;
return true;
}
#else
if (g_iPriceOffset == -1)
{
if (!g_pGameConf->GetOffset("WeaponPrice", &g_iPriceOffset))
{
smutils->LogError(myself, "Could not find WeaponPrice offset - Disabled OnGetWeaponPrice forward");
return false;
}
}
if (!CreateHandleBuyDetour())
{
g_pSM->LogError(myself, "GetWeaponPrice detour could not be initialized - HandleCommand_Buy_Internal failed to detour, disabled OnGetWeaponPrice forward.");
return false;
}
else
{
g_PriceDetoured = true;
return true;
}
#endif
g_pSM->LogError(myself, "GetWeaponPrice detour could not be initialized - Disabled OnGetWeaponPrice forward.");
return false;
@ -236,6 +302,16 @@ bool CreateHandleBuyDetour()
if (g_HandleBuyDetoured)
return true;
#if SOURCE_ENGINE == SE_CSGO
if (weaponNameOffset == -1)
{
if (!g_pGameConf->GetOffset("WeaponName", &weaponNameOffset))
{
smutils->LogError(myself, "Could not find WeaponName offset - Disabled OnBuyCommand forward");
return false;
}
}
#endif
DHandleBuy = DETOUR_CREATE_MEMBER(DetourHandleBuy, "HandleCommand_Buy_Internal");
if (DHandleBuy != NULL)
@ -305,6 +381,7 @@ void RemoveCSWeaponDropDetour()
}
g_pCSWeaponDropDetoured = false;
}
int CallPriceForward(int client, const char *weapon_name, int price)
{
int changedprice = price;

View File

@ -14,4 +14,7 @@ extern IForward *g_pHandleBuyForward;
extern IForward *g_pPriceForward;
extern IForward *g_pTerminateRoundForward;
extern IForward *g_pCSWeaponDropForward;
#if SOURCE_ENGINE == SE_CSGO
extern int g_iPriceOffset;
#endif
#endif //_INCLUDE_CSTRIKE_FORWARDS_H_

View File

@ -398,8 +398,14 @@ static cell_t CS_GetTranslatedWeaponAlias(IPluginContext *pContext, const cell_t
return 1;
}
#if SOURCE_ENGINE != SE_CSGO
static cell_t CS_GetWeaponPrice(IPluginContext *pContext, const cell_t *params)
{
CBaseEntity *pEntity;
if (!(pEntity = GetCBaseEntity(params[1], true)))
{
return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
}
if (!IsValidWeaponID(params[2]))
return pContext->ThrowNativeError("Invalid WeaponID passed for this game");
@ -407,24 +413,14 @@ static cell_t CS_GetWeaponPrice(IPluginContext *pContext, const cell_t *params)
int id = GetRealWeaponID(params[2]);
//Hard code return values for weapons that dont call GetWeaponPrice and always use default value.
#if SOURCE_ENGINE == SE_CSGO
if (id == WEAPON_C4 || id == WEAPON_KNIFE || id == WEAPON_KNIFE_GG)
return 0;
#else
if (id == WEAPON_C4 || id == WEAPON_KNIFE || id == WEAPON_SHIELD)
return 0;
else if (id == WEAPON_KEVLAR)
return 650;
else if (id == WEAPON_ASSAULTSUIT)
return 1000;
#endif
else if (id == WEAPON_NIGHTVISION)
return 1250;
#if SOURCE_ENGINE == SE_CSGO
else if (id == WEAPON_DEFUSER)
return 400;
#endif
void *info = GetWeaponInfo(id);
@ -438,139 +434,7 @@ static cell_t CS_GetWeaponPrice(IPluginContext *pContext, const cell_t *params)
{
return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
}
#if SOURCE_ENGINE == SE_CSGO
static ICallWrapper *pWrapper = NULL;
if(!pWrapper)
{
void *pGetWeaponPrice = GetWeaponPriceFunction();
if(!pGetWeaponPrice)
{
return pContext->ThrowNativeError("Failed to locate function");
}
#ifdef _WIN32
const size_t GWP_ARGC = 2;
#else
const size_t GWP_ARGC = 3;
#endif
PassInfo pass[GWP_ARGC];
PassInfo ret;
pass[0].flags = PASSFLAG_BYVAL;
pass[0].type = PassType_Basic;
pass[0].size = sizeof(CEconItemView *);
pass[1].flags = PASSFLAG_BYVAL;
pass[1].type = PassType_Basic;
pass[1].size = sizeof(int);
#ifndef _WIN32
pass[2].flags = PASSFLAG_BYVAL;
pass[2].type = PassType_Float;
pass[2].size = sizeof(float);
#endif
ret.flags = PASSFLAG_BYVAL;
ret.type = PassType_Basic;
ret.size = sizeof(int);
pWrapper = g_pBinTools->CreateCall(pGetWeaponPrice, CallConv_ThisCall, &ret, pass, GWP_ARGC);
}
// Get a CEconItemView for the m4
// Found in CCSPlayer::HandleCommand_Buy_Internal
// Linux a1 - CCSPlayer *pEntity, v5 - Player Team, a3 - ItemLoadoutSlot -1 use default loadoutslot:
// v4 = *(int (__cdecl **)(_DWORD, _DWORD, _DWORD))(*(_DWORD *)(a1 + 9492) + 36); // offset 9
// v6 = v4(a1 + 9492, v5, a3);
// Windows v5 - CCSPlayer *pEntity a4 - ItemLoadoutSlot -1 use default loadoutslot:
// v8 = (*(int (__stdcall **)(_DWORD, int))(*(_DWORD *)(v5 + 9472) + 32))(*(_DWORD *)(v5 + 760), a4); // offset 8
// The function is CCSPlayerInventory::GetItemInLoadout(int, int)
// We can pass NULL view to the GetAttribute to use default loadoutslot.
// We only really care about m4a1/m4a4 as price differs between them
// thisPtrOffset = 9472/9492
static ICallWrapper *pGetView = NULL;
static int thisPtrOffset = -1;
CEconItemView *view = NULL;
if(!pGetView)
{
int offset = -1;
int byteOffset = -1;
void *pHandleCommandBuy = NULL;
if (!g_pGameConf->GetOffset("GetItemInLoadout", &offset) || offset == -1)
{
smutils->LogError(myself, "Failed to get GetItemInLoadout offset. Reverting to NULL ItemView");
}
else if (!g_pGameConf->GetOffset("CCSPlayerInventoryOffset", &byteOffset) || byteOffset == -1)
{
smutils->LogError(myself, "Failed to get CCSPlayerInventoryOffset offset. Reverting to NULL ItemView");
}
else if (!g_pGameConf->GetMemSig("HandleCommand_Buy_Internal", &pHandleCommandBuy) || !pHandleCommandBuy)
{
smutils->LogError(myself, "Failed to get HandleCommand_Buy_Internal function. Reverting to NULL ItemView");
}
else
{
thisPtrOffset = *(int *)((intptr_t)pHandleCommandBuy + byteOffset);
PassInfo pass[2];
PassInfo ret;
pass[0].flags = PASSFLAG_BYVAL;
pass[0].type = PassType_Basic;
pass[0].size = sizeof(int);
pass[1].flags = PASSFLAG_BYVAL;
pass[1].type = PassType_Basic;
pass[1].size = sizeof(int);
ret.flags = PASSFLAG_BYVAL;
ret.type = PassType_Basic;
ret.size = sizeof(void *);
pGetView = g_pBinTools->CreateVCall(offset, 0, 0, &ret, pass, 2);
g_RegNatives.Register(pGetView);
}
}
IPlayerInfo *playerinfo = playerhelpers->GetGamePlayer(params[1])->GetPlayerInfo();
if(pGetView && thisPtrOffset != -1 && playerinfo)
{
//If the gun isnt an M4 we ignore this as M4 is the only one that differs in price based on Loadout item.
int iLoadoutSlot = -1;
if(id == WEAPON_M4)
{
iLoadoutSlot = 15;
}
unsigned char vstk_view[sizeof(void *) + sizeof(int) * 2];
unsigned char *vptr_view = vstk_view;
*(void **)vptr_view = (void *)((intptr_t)pEntity + thisPtrOffset);
vptr_view += sizeof(void *);
*(int *)vptr_view = playerinfo->GetTeamIndex();
vptr_view += sizeof(int);
*(int *)vptr_view = iLoadoutSlot;
pGetView->Execute(vstk_view, &view);
}
#if defined(WIN32)
unsigned char vstk[sizeof(void *) * 2 + sizeof(int)];
#else
unsigned char vstk[sizeof(void *) * 2 + sizeof(int) + sizeof(float)];
#endif
unsigned char *vptr = vstk;
*(void **)vptr = info;
vptr += sizeof(void *);
*(CEconItemView **)vptr = view;
vptr += sizeof(CEconItemView *);
*(int *)vptr = 0;
#if !defined(WIN32)
vptr += sizeof(int);
*(float *)vptr = 1.0;
#endif
int price = 0;
pWrapper->Execute(vstk, &price);
#else
if (g_iPriceOffset == -1)
{
if (!g_pGameConf->GetOffset("WeaponPrice", &g_iPriceOffset))
@ -581,7 +445,6 @@ static cell_t CS_GetWeaponPrice(IPluginContext *pContext, const cell_t *params)
}
int price = *(int *)((intptr_t)info + g_iPriceOffset);
#endif
if (params[3] || weaponNameOffset == -1)
return price;
@ -590,6 +453,80 @@ static cell_t CS_GetWeaponPrice(IPluginContext *pContext, const cell_t *params)
return CallPriceForward(params[1], weapon_name, price);
}
#else
static cell_t CS_GetWeaponPrice(IPluginContext *pContext, const cell_t *params)
{
CBaseEntity *pEntity;
if (!(pEntity = GetCBaseEntity(params[1], true)))
{
return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
}
if (!IsValidWeaponID(params[2]))
return pContext->ThrowNativeError("Invalid WeaponID passed for this game");
static int iLoadoutSlotOffset = -1;
if (iLoadoutSlotOffset == -1)
{
if (!g_pGameConf->GetOffset("LoadoutSlotOffset", &iLoadoutSlotOffset) || iLoadoutSlotOffset == -1)
{
iLoadoutSlotOffset = -1;
return pContext->ThrowNativeError("Failed to get LoadoutSlotOffset offset.");
}
}
if (g_iPriceOffset == -1)
{
if (!g_pGameConf->GetOffset("WeaponPrice", &g_iPriceOffset) || g_iPriceOffset == -1)
{
g_iPriceOffset = -1;
return pContext->ThrowNativeError("Failed to get WeaponPrice offset");
}
}
int id = GetRealWeaponID(params[2]);
if (id == WEAPON_C4 || id == WEAPON_KNIFE || id == WEAPON_KNIFE_GG)
return 0;
else if (id == WEAPON_NIGHTVISION)
return 1250;
else if (id == WEAPON_DEFUSER)
return 400;
char classname[128];
if (id < CSGOWeapon_KEVLAR)
Q_snprintf(classname, sizeof(classname), "weapon_%s", WeaponIDToAlias(params[2]));
else
Q_snprintf(classname, sizeof(classname), "item_%s", WeaponIDToAlias(params[2]));
void *pDef = GetItemDefintionByName(classname);
int iLoadoutSlot = *(int *)((intptr_t)pDef + iLoadoutSlotOffset);
CEconItemView *pView = GetEconItemView(pEntity, iLoadoutSlot);
if (!pView)
{
return pContext->ThrowNativeError("Failed to get CEconItemVIiew for %s", classname);
}
void *pWpnData = GetCCSWeaponData(pView);
if (!pWpnData)
{
return pContext->ThrowNativeError("Failed to get CCSWeaponData for %s", classname);
}
int price = *(int *)((intptr_t)pWpnData + g_iPriceOffset);
if (params[3] || weaponNameOffset == -1)
return price;
return CallPriceForward(params[1], WeaponIDToAlias(params[2]), price);
}
#endif
static cell_t CS_GetClientClanTag(IPluginContext *pContext, const cell_t *params)
{
@ -683,6 +620,10 @@ static cell_t CS_AliasToWeaponID(IPluginContext *pContext, const cell_t *params)
{
return SMCSWeapon_P250;
}
else if (strstr(weapon, "m4a1_silencer") != NULL)
{
return SMCSWeapon_M4A1;
}
#endif
int id = GetFakeWeaponID(AliasToWeaponID(weapon));

View File

@ -33,6 +33,7 @@
#include "extension.h"
#include "RegNatives.h"
#include <iplayerinfo.h>
#define REGISTER_ADDR(name, defaultret, code) \
void *addr; \
@ -51,10 +52,205 @@
return defaultret;\
}
#if SOURCE_ENGINE == SE_CSGO
// Get a CEconItemView for the m4
// Found in CCSPlayer::HandleCommand_Buy_Internal
// Linux a1 - CCSPlayer *pEntity, v5 - Player Team, a3 - ItemLoadoutSlot -1 use default loadoutslot:
// v4 = *(int (__cdecl **)(_DWORD, _DWORD, _DWORD))(*(_DWORD *)(a1 + 9492) + 36); // offset 9
// v6 = v4(a1 + 9492, v5, a3);
// Windows v5 - CCSPlayer *pEntity a4 - ItemLoadoutSlot -1 use default loadoutslot:
// v8 = (*(int (__stdcall **)(_DWORD, int))(*(_DWORD *)(v5 + 9472) + 32))(*(_DWORD *)(v5 + 760), a4); // offset 8
// The function is CCSPlayerInventory::GetItemInLoadout(int, int)
// We can pass NULL view to the GetAttribute to use default loadoutslot.
// We only really care about m4a1/m4a4 as price differs between them
// thisPtrOffset = 9472/9492
CEconItemView *GetEconItemView(void *pEntity, int iSlot)
{
if (!pEntity)
return NULL;
static ICallWrapper *pWrapper = NULL;
static int thisPtrOffset = -1;
if (!pWrapper)
{
int offset = -1;
int byteOffset = -1;
void *pHandleCommandBuy = NULL;
if (!g_pGameConf->GetOffset("GetItemInLoadout", &offset) || offset == -1)
{
smutils->LogError(myself, "Failed to get GetItemInLoadout offset.");
return NULL;
}
else if (!g_pGameConf->GetOffset("CCSPlayerInventoryOffset", &byteOffset) || byteOffset == -1)
{
smutils->LogError(myself, "Failed to get CCSPlayerInventoryOffset offset.");
return NULL;
}
else if (!g_pGameConf->GetMemSig("HandleCommand_Buy_Internal", &pHandleCommandBuy) || !pHandleCommandBuy)
{
smutils->LogError(myself, "Failed to get HandleCommand_Buy_Internal function.");
return NULL;
}
else
{
thisPtrOffset = *(int *)((intptr_t)pHandleCommandBuy + byteOffset);
PassInfo pass[2];
PassInfo ret;
pass[0].flags = PASSFLAG_BYVAL;
pass[0].type = PassType_Basic;
pass[0].size = sizeof(int);
pass[1].flags = PASSFLAG_BYVAL;
pass[1].type = PassType_Basic;
pass[1].size = sizeof(int);
ret.flags = PASSFLAG_BYVAL;
ret.type = PassType_Basic;
ret.size = sizeof(CEconItemView *);
pWrapper = g_pBinTools->CreateVCall(offset, 0, 0, &ret, pass, 2);
g_RegNatives.Register(pWrapper);
}
}
int client = gamehelpers->EntityToBCompatRef(reinterpret_cast<CBaseEntity *>(pEntity));
IPlayerInfo *playerinfo = playerhelpers->GetGamePlayer(client)->GetPlayerInfo();
if (!playerinfo)
return NULL;
int team = playerinfo->GetTeamIndex();
if (team != 2 && team != 3)
return NULL;
CEconItemView *ret;
unsigned char vstk[sizeof(void *) + sizeof(int) * 2];
unsigned char *vptr = vstk;
*(void **)vptr = (void *)((intptr_t)pEntity + thisPtrOffset);
vptr += sizeof(void *);
*(int *)vptr = team;
vptr += sizeof(int);
*(int *)vptr = iSlot;
pWrapper->Execute(vstk, &ret);
return ret;
}
void *GetCCSWeaponData(CEconItemView *view)
{
static ICallWrapper *pWrapper = NULL;
if (!pWrapper)
{
REGISTER_ADDR("GetCCSWeaponData", NULL,
PassInfo pass[1]; \
PassInfo retpass; \
pass[0].flags = PASSFLAG_BYVAL; \
pass[0].type = PassType_Basic; \
pass[0].size = sizeof(CEconItemView *); \
retpass.flags = PASSFLAG_BYVAL; \
retpass.type = PassType_Basic; \
retpass.size = sizeof(void *); \
pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, &retpass, pass, 1))
}
unsigned char vstk[sizeof(const char *)];
unsigned char *vptr = vstk;
*(CEconItemView **)vptr = view;
void *pDef = NULL;
pWrapper->Execute(vstk, &pDef);
return pDef;
}
void *GetItemSchema()
{
static ICallWrapper *pWrapper = NULL;
if (!pWrapper)
{
REGISTER_ADDR("GetItemSchema", NULL,
PassInfo retpass; \
retpass.flags = PASSFLAG_BYVAL; \
retpass.type = PassType_Basic; \
retpass.size = sizeof(void *); \
pWrapper = g_pBinTools->CreateCall(addr, CallConv_Cdecl, &retpass, NULL, 0))
}
void *pSchema = NULL;
pWrapper->Execute(NULL, &pSchema);
//In windows this is actually ItemSystem() + 4 is ItemSchema
#ifdef WIN32
return (void *)((intptr_t)pSchema + 4);
#else
return pSchema;
#endif
}
void *GetItemDefintionByName(const char *classname)
{
void *pSchema = GetItemSchema();
if (!pSchema)
return NULL;
static ICallWrapper *pWrapper = NULL;
if (!pWrapper)
{
int offset = -1;
if (!g_pGameConf->GetOffset("GetItemDefintionByName", &offset) || offset == -1)
{
smutils->LogError(myself, "Failed to get GetItemDefintionByName offset.");
return NULL;
}
PassInfo pass[1];
PassInfo ret;
pass[0].flags = PASSFLAG_BYVAL;
pass[0].type = PassType_Basic;
pass[0].size = sizeof(const char *);
ret.flags = PASSFLAG_BYVAL;
ret.type = PassType_Basic;
ret.size = sizeof(void *);
pWrapper = g_pBinTools->CreateVCall(offset, 0, 0, &ret, pass, 1);
g_RegNatives.Register(pWrapper);
}
unsigned char vstk[sizeof(void *) + sizeof(const char *)];
unsigned char *vptr = vstk;
*(void **)vptr = pSchema;
vptr += sizeof(void *);
*(const char **)vptr = classname;
void *pItemDef = NULL;
pWrapper->Execute(vstk, &pItemDef);
return pItemDef;
}
#endif
#if SOURCE_ENGINE != SE_CSGO
void *GetWeaponInfo(int weaponID)
{
void *info;
#if SOURCE_ENGINE != SE_CSGO || !defined(WIN32)
static ICallWrapper *pWrapper = NULL;
if (!pWrapper)
{
@ -77,30 +273,15 @@ void *GetWeaponInfo(int weaponID)
pWrapper->Execute(vstk, &info);
#else
static void *addr = NULL;
if(!addr)
{
GET_MEMSIG("GetWeaponInfo", 0);
}
__asm
{
mov ecx, weaponID
call addr
mov info, eax
}
#endif
return info;
}
#endif
const char *GetTranslatedWeaponAlias(const char *weapon)
{
#if SOURCE_ENGINE != SE_CSGO
const char *alias = NULL;
#if SOURCE_ENGINE != SE_CSGO || !defined(WIN32)
static ICallWrapper *pWrapper = NULL;
if (!pWrapper)
@ -123,28 +304,42 @@ const char *GetTranslatedWeaponAlias(const char *weapon)
*(const char **)vptr = weapon;
pWrapper->Execute(vstk, &alias);
#else
static void *addr = NULL;
if(!addr)
{
GET_MEMSIG("GetTranslatedWeaponAlias", weapon);
}
__asm
{
mov ecx, weapon
call addr
mov alias, eax
}
#endif
return alias;
#else //this should work for both games maybe replace both?
static const char *szAliases[] =
{
"cv47", "ak47",
"magnum", "awp",
"d3au1", "g3sg1",
"clarion", "famas",
"bullpup", "aug",
"9x19mm", "glock",
"nighthawk", "deagle",
"elites", "elite",
"fn57", "fiveseven",
"autoshotgun", "xm1014",
"c90", "p90",
"vest", "kevlar",
"vesthelm", "assaultsuit",
"nvgs", "nightvision"
};
for (int i = 0; i < SM_ARRAYSIZE(szAliases)/2; i++)
{
if (stricmp(weapon, szAliases[i * 2]) == 0)
return szAliases[i * 2 + 1];
}
return weapon;
#endif
}
int AliasToWeaponID(const char *weapon)
{
#if SOURCE_ENGINE != SE_CSGO
int weaponID = 0;
#if SOURCE_ENGINE != SE_CSGO || !defined(WIN32)
static ICallWrapper *pWrapper = NULL;
if (!pWrapper)
@ -167,28 +362,23 @@ int AliasToWeaponID(const char *weapon)
*(const char **)vptr = weapon;
pWrapper->Execute(vstk, &weaponID);
#else
static void *addr = NULL;
if(!addr)
{
GET_MEMSIG("AliasToWeaponID", 0);
}
__asm
{
mov ecx, weapon
call addr
mov weaponID, eax
}
#endif
return weaponID;
#else //this is horribly broken hackfix for now.
for (unsigned int i = 0; i < SM_ARRAYSIZE(szWeaponInfo); i++)
{
if (stricmp(weapon, szWeaponInfo[i]) == 0)
return i;
}
return 0;
#endif
}
const char *WeaponIDToAlias(int weaponID)
{
const char *alias = NULL;
#if SOURCE_ENGINE != SE_CSGO || !defined(WIN32)
const char *alias = NULL;
static ICallWrapper *pWrapper = NULL;
if (!pWrapper)
@ -211,81 +401,19 @@ const char *WeaponIDToAlias(int weaponID)
*(int *)vptr = GetRealWeaponID(weaponID);
pWrapper->Execute(vstk, &alias);
#else
static void *addr = NULL;
if(!addr)
{
GET_MEMSIG("WeaponIDToAlias", 0);
}
int realWeaponID = GetRealWeaponID(weaponID);
__asm
{
mov ecx, realWeaponID
call addr
mov alias, eax
}
#endif
return alias;
}
#if SOURCE_ENGINE == SE_CSGO
void *GetWeaponPriceFunction()
{
static void *pGetWeaponPriceAddress = nullptr;
if (pGetWeaponPriceAddress)
{
return pGetWeaponPriceAddress;
}
void *pAddress = nullptr;
int offset = 0;
int callOffset = 0;
const char* byteCheck = nullptr;
if (!g_pGameConf->GetMemSig("GetWeaponPrice", &pAddress) || !pAddress)
{
g_pSM->LogError(myself, "Failed to get GetWeaponPrice address.");
return nullptr;
}
if (!g_pGameConf->GetOffset("GetWeaponPriceFunc", &offset))
{
// If no offset specified, assume that GetWeaponPrice is the func we want, and not just our
// helper to find the real one.
pGetWeaponPriceAddress = pAddress;
return pGetWeaponPriceAddress;
}
#if defined( _WIN32 )
byteCheck = g_pGameConf->GetKeyValue("GetWeaponPriceByteCheck");
#elif defined( _LINUX )
byteCheck = g_pGameConf->GetKeyValue("GetWeaponPriceByteCheck_Linux");
#else
// We don't compile for csgo on mac anymore
#error Unsupported platform
int realID = GetRealWeaponID(weaponID);
if (realID < SM_ARRAYSIZE(szWeaponInfo) && realID > 0)
return szWeaponInfo[realID];
return NULL;
#endif
if (byteCheck == nullptr)
{
g_pSM->LogError(myself, "Failed to get GetWeaponPriceByteCheck keyvalue.");
return nullptr;
}
uint8_t iByte = strtoul(byteCheck, nullptr, 16);
if (iByte != *(uint8_t *)((intptr_t)pAddress + (offset-1)))
{
g_pSM->LogError(myself, "GetWeaponPrice Byte check failed.");
return nullptr;
}
callOffset = *(uint32_t *)((intptr_t)pAddress + offset);
pGetWeaponPriceAddress = (void *)((intptr_t)pAddress + offset + callOffset + sizeof(int));
return pGetWeaponPriceAddress;
}
#endif
int GetRealWeaponID(int weaponId)
{

View File

@ -35,6 +35,69 @@
#if SOURCE_ENGINE == SE_CSGO
class CEconItemView;
CEconItemView *GetEconItemView(void *pEntity, int iSlot);
void *GetCCSWeaponData(CEconItemView *view);
void *GetItemSchema();
void *GetItemDefintionByName(const char *classname);
static const char *szWeaponInfo[] =
{
"none",
"deagle",
"elite",
"fiveseven",
"glock",
"p228",
"usp",
"ak47",
"aug",
"awp",
"famas",
"g3sg1",
"galil",
"galilar",
"m249",
"m3",
"m4a1",
"mac10",
"mp5",
"p90",
"scout",
"sg550",
"sg552",
"tmp",
"ump45",
"xm1014",
"bizon",
"mag7",
"negev",
"sawedoff",
"tec9",
"taser",
"hkp2000",
"mp7",
"mp9",
"nova",
"p250",
"scar17",
"scar20",
"sg556",
"ssg08",
"knifegg",
"knife",
"flashbang",
"hegrenade",
"smokegrenade",
"molotov",
"decoy",
"incgrenade",
"c4",
"kevlar",
"assaultsuit",
"nvg",
"defuser"
};
enum CSGOWeapon
{
CSGOWeapon_NONE,
@ -151,7 +214,10 @@ enum SMCSWeapon
SMCSWeapon_INCGRENADE,
SMCSWeapon_DEFUSER
};
#if SOURCE_ENGINE != SE_CSGO
void *GetWeaponInfo(int weaponID);
#endif
const char *GetTranslatedWeaponAlias(const char *weapon);
@ -164,7 +230,4 @@ int GetRealWeaponID(int weaponId);
int GetFakeWeaponID(int weaponId);
bool IsValidWeaponID(int weaponId);
void *GetWeaponPriceFunction();
#endif

View File

@ -13,26 +13,22 @@
{
"#default"
{
"Keys"
{
"GetWeaponPriceByteCheck" "E9"
"GetWeaponPriceByteCheck_Linux" "E8"
}
"Offsets"
{
//Offset of szClassName in CCSWeaponInfo
// Offset of szClassName in CCSWeaponData, szDefaultName is @ 140 which returns the default weapon class for the loadoutslot ignoring the users inventory.
"WeaponName"
{
"windows" "6"
"linux" "6"
"mac" "6"
"windows" "4"
"linux" "4"
"mac" "4"
}
//Econ update doesnt check this now but for what we use it hopefully its fine.
// In HandleCommand_Buy_Internal
// -*(_DWORD *)(v34 + 204)
"WeaponPrice"
{
"windows" "2528"
"linux" "2528"
"mac" "2528"
"windows" "204"
"linux" "204"
"mac" "204"
}
//Offset into CheckRestartRound
"CTTeamScoreOffset"
@ -67,20 +63,30 @@
"linux" "9"
"mac" "9"
}
"GetWeaponPriceFunc"
"GetItemDefintionByName" //_ZN15CEconItemSchema23GetItemDefinitionByNameEPKc
{
"windows" "105"
"linux" "152"
"windows" "42"
"linux" "41"
"mac" "41"
}
//This is GetWeaponPriceFunc offset -1 (only used by GDC)
"GetWeaponPriceFuncGDC"
// Search for "%s (ID %llu) at backpack slot %d" (CCSPlayerInventory::DumpInventoryToConsole(bool))
// Jump to the vtable 2 functions above calls CCStrike15ItemDefinition::GetLoadoutSlot(CCStrike15ItemDefinition *this, int)
// This is an offset into CCStrike15ItemDefinition that contains the loadoutslot IGNORING the team param (the second param in the function)
"LoadoutSlotOffset"
{
"windows" "104"
"linux" "151"
"windows" "588"
"linux" "588"
"mac" "588"
}
}
"Signatures"
{
"GetItemSchema"
{
"library" "server"
"windows" "\xA1\x2A\x2A\x2A\x2A\x85\xC0\x75\x2A\xA1\x2A\x2A\x2A\x2A\x56\x68\x2A\x2A\x00\x00\x8B"
"linux" "\x55\x89\xE5\x83\xEC\x08\xE8\x2A\x2A\x2A\x2A\xC9\x83\xC0\x04\xC3"
}
"RoundRespawn"
{
"library" "server"
@ -117,18 +123,6 @@
"windows" "\x55\x8B\xEC\x83\xE4\xF8\x83\xEC\x2A\x53\x8B\xD9\xF3\x0F\x2A\x2A\x2A\x2A\x56\x57\x89\x2A\x2A\x2A\x83\xBB"
"linux" "\x55\x89\xE5\x57\x56\x53\x81\xEC\xDC\x00\x00\x00\x8B\x75\x08\x8B\x86"
}
"GetTranslatedWeaponAlias"
{
"library" "server"
"windows" "\x56\x57\x8B\xF9\x33\xF6\x66\x66\x0F\x1F\x84\x00\x00\x00\x00\x00\x8B\x0C\xF5\x2A\x2A\x2A\x2A\x8B\xD7\xE8\x2A\x2A\x2A\x2A\x85\xC0\x74\x2A\x46\x83\xFE\x19"
"linux" "\x55\x89\xE5\x56\x53\x31\xDB\x83\xEC\x10\x8B\x75\x08\xEB\x2A\x90\x83\xC3\x01\x83\xFB\x19"
}
"GetWeaponInfo"
{
"library" "server"
"windows" "\x55\x8B\xEC\x83\xEC\x08\x33\xC0\x85\xC9"
"linux" "\x55\x89\xE5\x56\x53\x31\xDB\x83\xEC\x10\x8B\x55\x08\x85\xD2"
}
//In CS:GO this is actually CCSGameRules::CheckRestartRound(void) but to keep same gamedata as cs:s.
"CheckWinLimit"
{
@ -136,38 +130,25 @@
"windows" "\x55\x8B\xEC\x83\xE4\xF8\x83\xEC\x2A\x53\x56\x57\x8B\xF9\x8B\x0D\x2A\x2A\x2A\x2A\x81\xF9"
"linux" "\x55\x89\xE5\x56\x53\x83\xEC\x70\xA1\x2A\x2A\x2A\x2A\x8B\x35\x2A\x2A\x2A\x2A\x8B"
}
"AliasToWeaponID"
{
"library" "server"
"windows" "\x57\x8B\xF9\x85\xFF\x75\x2A\x33\xC0\x5F\xC3\x56\x33\xF6\x66\x90\x6A\x5F"
"linux" "\x55\x31\xC0\x89\xE5\x56\x53\x83\xEC\x10\x8B\x75\x08\x85\xF6\x74\x2A\x31\xDB\xEB\x2A\x8D"
}
"WeaponIDToAlias"
{
"library" "server"
"windows" "\x33\xC0\x39\x0C\xC5\x2A\x2A\x2A\x2A\x74\x2A\x40\x83\xF8\x3E\x72\x2A\x33\xC0\xC3\x6A\x5F"
"linux" "\x55\x89\xE5\x83\xEC\x18\x8B\x55\x08\x39\x15\x2A\x2A\x2A\x2A\x74"
}
"SetClanTag"
{
"library" "server"
"windows" "\x55\x8B\xEC\x8B\x55\x08\x85\xD2\x74\x2A\x8D\x81\x34\x25\x00\x00"
"linux" "\x55\x89\xE5\x83\xEC\x18\x8B\x45\x0C\x85\xC0\x74\x2A\x89\x44\x24\x04\x8B\x45\x08\xC7\x44\x24\x08\x10\x00\x00\x00"
}
// Since it's not possible to make a unique signature for CCSWeaponInfo::GetWeaponInfo, this is instead a signature to a
// function that calls it. The offset from the signature to the CCSWeaponInfo func offset is the GetWeaponPriceFunc offset.
"GetWeaponPrice"
{
"library" "server"
"windows" "\x55\x8B\xEC\x8B\xD1\x8B\x4D\x08\x85\xC9\x75\x2A\x83\xC8\xFF"
"linux" "\x55\x89\xE5\x83\xEC\x18\x89\x5D\xF8\x8B\x5D\x10\x89\x75\xFC\x8B\x4D\x08"
}
"SetModelFromClass"
{
"library" "server"
"windows" "\x53\x56\x57\x8B\xF9\x8B\x87\x00\x03\x00\x00"
"linux" "\x55\x89\xE5\x83\xEC\x28\x89\x5D\xF4\x8B\x5D\x08\x89\x75\xF8\x89\x7D\xFC\x89\x1C\x24\xE8\x2A\x2A\x2A\x2A\x83\xF8\x02"
}
//GetCCSWeaponData Found in HandleCommand_Buy_Internal
"GetCCSWeaponData"
{
"library" "server"
"windows" "\x85\xC9\x75\x2A\x33\xC0\xC3\xE8\x2A\x2A\x2A\x2A\x8B"
"linux" "\x55\x89\xE5\x83\xEC\x18\x8B\x45\x08\x85\xC0\x74\x2A\x89\x04\x24"
}
}
}