From 494f83395ff4208d7e096e008bff1e721f7479d6 Mon Sep 17 00:00:00 2001 From: Ruben Gonzalez Date: Mon, 21 Aug 2017 10:28:59 -0400 Subject: [PATCH] Fix cstrike extension natives/forwards due to update Fix using Q_strcpy instead of Q_strncpy Fix CS:S build Fix trying to use signature function for WeaponIDToAlias on linux. --- extensions/cstrike/forwards.cpp | 135 +++++++-- extensions/cstrike/forwards.h | 3 + extensions/cstrike/natives.cpp | 213 +++++--------- extensions/cstrike/util_cstrike.cpp | 354 ++++++++++++++++-------- extensions/cstrike/util_cstrike.h | 69 ++++- gamedata/sm-cstrike.games/game.csgo.txt | 85 +++--- 6 files changed, 523 insertions(+), 336 deletions(-) diff --git a/extensions/cstrike/forwards.cpp b/extensions/cstrike/forwards.cpp index 5c7a641bf8..914044ab90 100644 --- a/extensions/cstrike/forwards.cpp +++ b/extensions/cstrike/forwards.cpp @@ -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(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_strncpy(weaponName, "knife", sizeof(weaponName)); + } + else + { + const char *underscore = strstr(szClassname, "_"); + if (underscore) + { + Q_strncpy(weaponName, (const char *)((intptr_t)underscore + 1), sizeof(weaponName)); + } + else + { + Q_strncpy(weaponName, szClassname, sizeof(weaponName)); + } + } + + 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(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; diff --git a/extensions/cstrike/forwards.h b/extensions/cstrike/forwards.h index 785c3224ce..b307fe30ca 100644 --- a/extensions/cstrike/forwards.h +++ b/extensions/cstrike/forwards.h @@ -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_ diff --git a/extensions/cstrike/natives.cpp b/extensions/cstrike/natives.cpp index d304abbb1c..d294e364ab 100644 --- a/extensions/cstrike/natives.cpp +++ b/extensions/cstrike/natives.cpp @@ -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); @@ -433,163 +429,98 @@ static cell_t CS_GetWeaponPrice(IPluginContext *pContext, const cell_t *params) return pContext->ThrowNativeError("Failed to get weaponinfo"); } + if (g_iPriceOffset == -1) + { + if (!g_pGameConf->GetOffset("WeaponPrice", &g_iPriceOffset)) + { + g_iPriceOffset = -1; + return pContext->ThrowNativeError("Failed to get WeaponPrice offset"); + } + } + + int price = *(int *)((intptr_t)info + g_iPriceOffset); + + if (params[3] || weaponNameOffset == -1) + return price; + + const char *weapon_name = (const char *)((intptr_t)info + weaponNameOffset); + + 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 SOURCE_ENGINE == SE_CSGO - static ICallWrapper *pWrapper = NULL; - if(!pWrapper) - { - void *pGetWeaponPrice = GetWeaponPriceFunction(); - if(!pGetWeaponPrice) - { - return pContext->ThrowNativeError("Failed to locate function"); - } + if (!IsValidWeaponID(params[2])) + return pContext->ThrowNativeError("Invalid WeaponID passed for this game"); - -#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 + static int iLoadoutSlotOffset = -1; + + if (iLoadoutSlotOffset == -1) + { + if (!g_pGameConf->GetOffset("LoadoutSlotOffset", &iLoadoutSlotOffset) || iLoadoutSlotOffset == -1) { - 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); + iLoadoutSlotOffset = -1; + return pContext->ThrowNativeError("Failed to get LoadoutSlotOffset offset."); } } - IPlayerInfo *playerinfo = playerhelpers->GetGamePlayer(params[1])->GetPlayerInfo(); - if(pGetView && thisPtrOffset != -1 && playerinfo) + if (g_iPriceOffset == -1) { - //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) + if (!g_pGameConf->GetOffset("WeaponPrice", &g_iPriceOffset) || g_iPriceOffset == -1) { - iLoadoutSlot = 15; + g_iPriceOffset = -1; + return pContext->ThrowNativeError("Failed to get WeaponPrice offset"); } + } - unsigned char vstk_view[sizeof(void *) + sizeof(int) * 2]; - unsigned char *vptr_view = vstk_view; + int id = GetRealWeaponID(params[2]); - *(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; + 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; - pGetView->Execute(vstk_view, &view); - } + char classname[128]; -#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; + 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 **)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 + void *pDef = GetItemDefintionByName(classname); - int price = 0; - pWrapper->Execute(vstk, &price); -#else - if (g_iPriceOffset == -1) + int iLoadoutSlot = *(int *)((intptr_t)pDef + iLoadoutSlotOffset); + + CEconItemView *pView = GetEconItemView(pEntity, iLoadoutSlot); + + if (!pView) { - if (!g_pGameConf->GetOffset("WeaponPrice", &g_iPriceOffset)) - { - g_iPriceOffset = -1; - return pContext->ThrowNativeError("Failed to get WeaponPrice offset"); - } + return pContext->ThrowNativeError("Failed to get CEconItemVIiew for %s", classname); } - int price = *(int *)((intptr_t)info + g_iPriceOffset); -#endif + 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; - const char *weapon_name = (const char *)((intptr_t)info + weaponNameOffset); - - return CallPriceForward(params[1], weapon_name, price); + return CallPriceForward(params[1], WeaponIDToAlias(params[2]), price); } +#endif static cell_t CS_GetClientClanTag(IPluginContext *pContext, const cell_t *params) { @@ -683,6 +614,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)); diff --git a/extensions/cstrike/util_cstrike.cpp b/extensions/cstrike/util_cstrike.cpp index 58e5137495..f4a3b0ae51 100644 --- a/extensions/cstrike/util_cstrike.cpp +++ b/extensions/cstrike/util_cstrike.cpp @@ -33,6 +33,7 @@ #include "extension.h" #include "RegNatives.h" +#include #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(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); - } + return alias; +#else //this should work for both games maybe replace both? - __asm + 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++) { - mov ecx, weapon - call addr - mov alias, eax + if (stricmp(weapon, szAliases[i * 2]) == 0) + return szAliases[i * 2 + 1]; } + + return weapon; #endif - return alias; } 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 + return weaponID; +#else //this is horribly broken hackfix for now. + for (unsigned int i = 0; i < SM_ARRAYSIZE(szWeaponInfo); i++) { - mov ecx, weapon - call addr - mov weaponID, eax + if (stricmp(weapon, szWeaponInfo[i]) == 0) + return i; } + return 0; #endif - return weaponID; } const char *WeaponIDToAlias(int weaponID) { +#if SOURCE_ENGINE != SE_CSGO const char *alias = NULL; -#if SOURCE_ENGINE != SE_CSGO || !defined(WIN32) + 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"); + return alias; #else - // We don't compile for csgo on mac anymore - #error Unsupported platform -#endif - if (byteCheck == nullptr) - { - g_pSM->LogError(myself, "Failed to get GetWeaponPriceByteCheck keyvalue."); - return nullptr; - } + int realID = GetRealWeaponID(weaponID); - 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; - } + if (realID < SM_ARRAYSIZE(szWeaponInfo) && realID > 0) + return szWeaponInfo[realID]; - callOffset = *(uint32_t *)((intptr_t)pAddress + offset); + return NULL; - pGetWeaponPriceAddress = (void *)((intptr_t)pAddress + offset + callOffset + sizeof(int)); - - return pGetWeaponPriceAddress; +#endif + } -#endif int GetRealWeaponID(int weaponId) { diff --git a/extensions/cstrike/util_cstrike.h b/extensions/cstrike/util_cstrike.h index 160ca8050b..267bb48605 100644 --- a/extensions/cstrike/util_cstrike.h +++ b/extensions/cstrike/util_cstrike.h @@ -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 diff --git a/gamedata/sm-cstrike.games/game.csgo.txt b/gamedata/sm-cstrike.games/game.csgo.txt index b99d950673..4e9a64a2c2 100644 --- a/gamedata/sm-cstrike.games/game.csgo.txt +++ b/gamedata/sm-cstrike.games/game.csgo.txt @@ -13,26 +13,22 @@ { "csgo" { - "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" + } } }