From 22033c21f293ca6d9cbb333b6e9db1cc84cf469e Mon Sep 17 00:00:00 2001 From: Ruben Gonzalez Date: Fri, 25 Aug 2017 10:19:19 -0400 Subject: [PATCH 1/2] Fix CS_GetWeaponPrice returning incorrect weapon prices. --- extensions/cstrike/natives.cpp | 19 +++++------ extensions/cstrike/util_cstrike.cpp | 42 ++++++++++++++++++++++--- extensions/cstrike/util_cstrike.h | 1 + gamedata/sm-cstrike.games/game.csgo.txt | 9 ++++++ 4 files changed, 55 insertions(+), 16 deletions(-) diff --git a/extensions/cstrike/natives.cpp b/extensions/cstrike/natives.cpp index d294e364..23d277c1 100644 --- a/extensions/cstrike/natives.cpp +++ b/extensions/cstrike/natives.cpp @@ -459,7 +459,7 @@ static cell_t CS_GetWeaponPrice(IPluginContext *pContext, const cell_t *params) if (!IsValidWeaponID(params[2])) return pContext->ThrowNativeError("Invalid WeaponID passed for this game"); - static int iLoadoutSlotOffset = -1; + /*static int iLoadoutSlotOffset = -1; if (iLoadoutSlotOffset == -1) { @@ -468,7 +468,7 @@ static cell_t CS_GetWeaponPrice(IPluginContext *pContext, const cell_t *params) iLoadoutSlotOffset = -1; return pContext->ThrowNativeError("Failed to get LoadoutSlotOffset offset."); } - } + }*/ if (g_iPriceOffset == -1) { @@ -497,16 +497,7 @@ static cell_t CS_GetWeaponPrice(IPluginContext *pContext, const cell_t *params) 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); + void *pWpnData = GetCCSWpnDataFromItemDef(pDef); if (!pWpnData) { @@ -618,6 +609,10 @@ static cell_t CS_AliasToWeaponID(IPluginContext *pContext, const cell_t *params) { return SMCSWeapon_M4A1; } + else if (strstr(weapon, "revolver") != NULL) + { + return SMCSWeapon_DEAGLE; + } #endif int id = GetFakeWeaponID(AliasToWeaponID(weapon)); diff --git a/extensions/cstrike/util_cstrike.cpp b/extensions/cstrike/util_cstrike.cpp index f4a3b0ae..e77e262b 100644 --- a/extensions/cstrike/util_cstrike.cpp +++ b/extensions/cstrike/util_cstrike.cpp @@ -161,16 +161,16 @@ void *GetCCSWeaponData(CEconItemView *view) pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, &retpass, pass, 1)) } - unsigned char vstk[sizeof(const char *)]; + unsigned char vstk[sizeof(void *)]; unsigned char *vptr = vstk; *(CEconItemView **)vptr = view; - void *pDef = NULL; + void *pWpnData = NULL; - pWrapper->Execute(vstk, &pDef); + pWrapper->Execute(vstk, &pWpnData); - return pDef; + return pWpnData; } void *GetItemSchema() @@ -244,6 +244,40 @@ void *GetItemDefintionByName(const char *classname) return pItemDef; } + +void *GetCCSWpnDataFromItemDef(void *pItemDef) +{ + if (!pItemDef) + return NULL; + + static ICallWrapper *pWrapper = NULL; + + if (!pWrapper) + { + // In windows this is a sig to the inlined code in GetCCSWeaponData + // We abuse the fact that the ItemDef is stored in ecx + REGISTER_ADDR("GetCCSWeaponDataFromDef", NULL, + PassInfo pass[1]; \ + PassInfo retpass; \ + pass[0].flags = PASSFLAG_BYVAL; \ + pass[0].type = PassType_Basic; \ + pass[0].size = sizeof(void *); \ + 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(void *)]; + unsigned char *vptr = vstk; + + *(void **)vptr = pItemDef; + + void *pWpnData = NULL; + pWrapper->Execute(vstk, &pWpnData); + + return pWpnData; +} #endif #if SOURCE_ENGINE != SE_CSGO diff --git a/extensions/cstrike/util_cstrike.h b/extensions/cstrike/util_cstrike.h index 267bb486..86fe4a48 100644 --- a/extensions/cstrike/util_cstrike.h +++ b/extensions/cstrike/util_cstrike.h @@ -39,6 +39,7 @@ CEconItemView *GetEconItemView(void *pEntity, int iSlot); void *GetCCSWeaponData(CEconItemView *view); void *GetItemSchema(); void *GetItemDefintionByName(const char *classname); +void *GetCCSWpnDataFromItemDef(void *pItemDef); static const char *szWeaponInfo[] = { diff --git a/gamedata/sm-cstrike.games/game.csgo.txt b/gamedata/sm-cstrike.games/game.csgo.txt index 8f328b22..8602d87e 100644 --- a/gamedata/sm-cstrike.games/game.csgo.txt +++ b/gamedata/sm-cstrike.games/game.csgo.txt @@ -143,12 +143,21 @@ "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 + //Uses an econitemview "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" } + //In windows this is to inlined code. + //It is within GetCCSWeaponData + "GetCCSWeaponDataFromDef" + { + "library" "server" + "windows" "\x8B\x01\x56\x8B\x35\x2A\x2A\x2A\x2A\xFF\x10\x0F\xB7\xC0\xB9\x2A\x2A\x2A\x2A\x50\xFF\x56\x08\x5E\xC3" + "linux" "\x55\x31\xC0\x89\xE5\x53\x83\xEC\x14\x8B\x55\x08\x85\xD2" + } } } From 0e7a3b0173c254756eb8b5630c17dc02ab8aca4a Mon Sep 17 00:00:00 2001 From: Ruben Gonzalez Date: Mon, 28 Aug 2017 10:17:12 -0400 Subject: [PATCH 2/2] Use keyvalue to get weapon price, change void * to appropriate class names. --- extensions/cstrike/forwards.cpp | 7 +-- extensions/cstrike/natives.cpp | 59 +++++++++------------- extensions/cstrike/util_cstrike.cpp | 66 ++++++------------------- extensions/cstrike/util_cstrike.h | 38 +++++++++++--- gamedata/sm-cstrike.games/game.csgo.txt | 12 ++--- 5 files changed, 76 insertions(+), 106 deletions(-) diff --git a/extensions/cstrike/forwards.cpp b/extensions/cstrike/forwards.cpp index 914044ab..129aafe4 100644 --- a/extensions/cstrike/forwards.cpp +++ b/extensions/cstrike/forwards.cpp @@ -26,16 +26,17 @@ int weaponNameOffset = -1; #if SOURCE_ENGINE == SE_CSGO DETOUR_DECL_MEMBER3(DetourHandleBuy, int, int, iLoadoutSlot, void *, pWpnDataRef, bool, bRebuy) { - int client = gamehelpers->EntityToBCompatRef(reinterpret_cast(this)); + CBaseEntity *pEntity = reinterpret_cast(this); + int client = gamehelpers->EntityToBCompatRef(pEntity); - CEconItemView *pView = GetEconItemView(this, iLoadoutSlot); + CEconItemView *pView = GetEconItemView(pEntity, iLoadoutSlot); if (!pView) { return DETOUR_MEMBER_CALL(DetourHandleBuy)(iLoadoutSlot, pWpnDataRef, bRebuy); } - void *pWpnData = GetCCSWeaponData(pView); + CCSWeaponData *pWpnData = GetCCSWeaponData(pView); if (!pWpnData) { diff --git a/extensions/cstrike/natives.cpp b/extensions/cstrike/natives.cpp index 23d277c1..cb8043aa 100644 --- a/extensions/cstrike/natives.cpp +++ b/extensions/cstrike/natives.cpp @@ -34,7 +34,6 @@ #include "forwards.h" #include "util_cstrike.h" #include -#include int g_iPriceOffset = -1; @@ -456,37 +455,21 @@ static cell_t CS_GetWeaponPrice(IPluginContext *pContext, const cell_t *params) 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 const char *pPriceKey = NULL; - /*static int iLoadoutSlotOffset = -1; - - if (iLoadoutSlotOffset == -1) + if (!pPriceKey) { - if (!g_pGameConf->GetOffset("LoadoutSlotOffset", &iLoadoutSlotOffset) || iLoadoutSlotOffset == -1) + pPriceKey = g_pGameConf->GetKeyValue("PriceKey"); + if (!pPriceKey) { - 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"); + return pContext->ThrowNativeError("Failed to get PriceKey KeyValue."); } } - int id = GetRealWeaponID(params[2]); + if (!IsValidWeaponID(params[2])) + return pContext->ThrowNativeError("Invalid WeaponID passed for this game"); - 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; + int id = GetRealWeaponID(params[2]); char classname[128]; @@ -495,16 +478,21 @@ static cell_t CS_GetWeaponPrice(IPluginContext *pContext, const cell_t *params) else Q_snprintf(classname, sizeof(classname), "item_%s", WeaponIDToAlias(params[2])); - void *pDef = GetItemDefintionByName(classname); + CEconItemDefinition *pDef = GetItemDefintionByName(classname); - void *pWpnData = GetCCSWpnDataFromItemDef(pDef); - - if (!pWpnData) + if (!pDef) { - return pContext->ThrowNativeError("Failed to get CCSWeaponData for %s", classname); + return pContext->ThrowNativeError("Failed to get CEconItemDefinition for %s", classname); } - int price = *(int *)((intptr_t)pWpnData + g_iPriceOffset); + KeyValues *pAttributes = pDef->m_pKv->FindKey("attributes", false); + + if (!pAttributes) + { + return pContext->ThrowNativeError("Failed to get item attributes keyvalue for %s", classname); + } + + int price = pAttributes->GetInt(pPriceKey, 0); if (params[3] || weaponNameOffset == -1) return price; @@ -559,14 +547,11 @@ static cell_t CS_SetClientClanTag(IPluginContext *pContext, const cell_t *params if (!pWrapper) { REGISTER_NATIVE_ADDR("SetClanTag", - PassInfo pass[2]; \ + PassInfo pass[1]; \ pass[0].flags = PASSFLAG_BYVAL; \ pass[0].type = PassType_Basic; \ - pass[0].size = sizeof(CBaseEntity *); \ - pass[1].flags = PASSFLAG_BYVAL; \ - pass[1].type = PassType_Basic; \ - pass[1].size = sizeof(char *); \ - pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 2)) + pass[0].size = sizeof(char *); \ + pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 1)) } CBaseEntity *pEntity; diff --git a/extensions/cstrike/util_cstrike.cpp b/extensions/cstrike/util_cstrike.cpp index e77e262b..f5728663 100644 --- a/extensions/cstrike/util_cstrike.cpp +++ b/extensions/cstrike/util_cstrike.cpp @@ -66,7 +66,7 @@ // We only really care about m4a1/m4a4 as price differs between them // thisPtrOffset = 9472/9492 -CEconItemView *GetEconItemView(void *pEntity, int iSlot) +CEconItemView *GetEconItemView(CBaseEntity *pEntity, int iSlot) { if (!pEntity) return NULL; @@ -116,7 +116,7 @@ CEconItemView *GetEconItemView(void *pEntity, int iSlot) } } - int client = gamehelpers->EntityToBCompatRef(reinterpret_cast(pEntity)); + int client = gamehelpers->EntityToBCompatRef(pEntity); IPlayerInfo *playerinfo = playerhelpers->GetGamePlayer(client)->GetPlayerInfo(); @@ -143,37 +143,33 @@ CEconItemView *GetEconItemView(void *pEntity, int iSlot) return ret; } -void *GetCCSWeaponData(CEconItemView *view) +CCSWeaponData *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)) + retpass.size = sizeof(CCSWeaponData *); \ + pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, &retpass, NULL, 0)) } - unsigned char vstk[sizeof(void *)]; + unsigned char vstk[sizeof(CEconItemView *)]; unsigned char *vptr = vstk; *(CEconItemView **)vptr = view; - void *pWpnData = NULL; + CCSWeaponData *pWpnData = NULL; pWrapper->Execute(vstk, &pWpnData); return pWpnData; } -void *GetItemSchema() +CEconItemSchema *GetItemSchema() { static ICallWrapper *pWrapper = NULL; @@ -192,15 +188,15 @@ void *GetItemSchema() //In windows this is actually ItemSystem() + 4 is ItemSchema #ifdef WIN32 - return (void *)((intptr_t)pSchema + 4); + return (CEconItemSchema *)((intptr_t)pSchema + 4); #else - return pSchema; + return (CEconItemSchema *)pSchema; #endif } -void *GetItemDefintionByName(const char *classname) +CEconItemDefinition *GetItemDefintionByName(const char *classname) { - void *pSchema = GetItemSchema(); + CEconItemSchema *pSchema = GetItemSchema(); if (!pSchema) return NULL; @@ -225,7 +221,7 @@ void *GetItemDefintionByName(const char *classname) ret.flags = PASSFLAG_BYVAL; ret.type = PassType_Basic; - ret.size = sizeof(void *); + ret.size = sizeof(CEconItemDefinition *); pWrapper = g_pBinTools->CreateVCall(offset, 0, 0, &ret, pass, 1); @@ -239,45 +235,11 @@ void *GetItemDefintionByName(const char *classname) vptr += sizeof(void *); *(const char **)vptr = classname; - void *pItemDef = NULL; + CEconItemDefinition *pItemDef = NULL; pWrapper->Execute(vstk, &pItemDef); return pItemDef; } - -void *GetCCSWpnDataFromItemDef(void *pItemDef) -{ - if (!pItemDef) - return NULL; - - static ICallWrapper *pWrapper = NULL; - - if (!pWrapper) - { - // In windows this is a sig to the inlined code in GetCCSWeaponData - // We abuse the fact that the ItemDef is stored in ecx - REGISTER_ADDR("GetCCSWeaponDataFromDef", NULL, - PassInfo pass[1]; \ - PassInfo retpass; \ - pass[0].flags = PASSFLAG_BYVAL; \ - pass[0].type = PassType_Basic; \ - pass[0].size = sizeof(void *); \ - 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(void *)]; - unsigned char *vptr = vstk; - - *(void **)vptr = pItemDef; - - void *pWpnData = NULL; - pWrapper->Execute(vstk, &pWpnData); - - return pWpnData; -} #endif #if SOURCE_ENGINE != SE_CSGO diff --git a/extensions/cstrike/util_cstrike.h b/extensions/cstrike/util_cstrike.h index 86fe4a48..cb98e861 100644 --- a/extensions/cstrike/util_cstrike.h +++ b/extensions/cstrike/util_cstrike.h @@ -33,13 +33,39 @@ #define _INCLUDE_CSTRIKE_UTIL_H_ #if SOURCE_ENGINE == SE_CSGO -class CEconItemView; +#include "extension.h" -CEconItemView *GetEconItemView(void *pEntity, int iSlot); -void *GetCCSWeaponData(CEconItemView *view); -void *GetItemSchema(); -void *GetItemDefintionByName(const char *classname); -void *GetCCSWpnDataFromItemDef(void *pItemDef); +class CEconItemView; +class CCSWeaponData; +class CEconItemSchema; + +class CEconItemDefinition +{ +public: + void **m_pVtable; + KeyValues *m_pKv; + uint16_t m_iDefinitionIndex; + int GetDefaultLoadoutSlot() + { + static int iLoadoutSlotOffset = -1; + + if (iLoadoutSlotOffset == -1) + { + if (!g_pGameConf->GetOffset("LoadoutSlotOffset", &iLoadoutSlotOffset) || iLoadoutSlotOffset == -1) + { + iLoadoutSlotOffset = -1; + return -1; + } + } + + return *(int *)((intptr_t)this + iLoadoutSlotOffset); + } +}; + +CEconItemView *GetEconItemView(CBaseEntity *pEntity, int iSlot); +CCSWeaponData *GetCCSWeaponData(CEconItemView *view); +CEconItemSchema *GetItemSchema(); +CEconItemDefinition *GetItemDefintionByName(const char *classname); static const char *szWeaponInfo[] = { diff --git a/gamedata/sm-cstrike.games/game.csgo.txt b/gamedata/sm-cstrike.games/game.csgo.txt index 8602d87e..26107bd2 100644 --- a/gamedata/sm-cstrike.games/game.csgo.txt +++ b/gamedata/sm-cstrike.games/game.csgo.txt @@ -13,6 +13,10 @@ { "#default" { + "Keys" + { + "PriceKey" "in game price" + } "Offsets" { // Offset of szClassName in CCSWeaponData, szDefaultName is @ 140 which returns the default weapon class for the loadoutslot ignoring the users inventory. @@ -150,14 +154,6 @@ "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" } - //In windows this is to inlined code. - //It is within GetCCSWeaponData - "GetCCSWeaponDataFromDef" - { - "library" "server" - "windows" "\x8B\x01\x56\x8B\x35\x2A\x2A\x2A\x2A\xFF\x10\x0F\xB7\xC0\xB9\x2A\x2A\x2A\x2A\x50\xFF\x56\x08\x5E\xC3" - "linux" "\x55\x31\xC0\x89\xE5\x53\x83\xEC\x14\x8B\x55\x08\x85\xD2" - } } }