/** * vim: set ts=4 : * ============================================================================= * SourceMod Counter-Strike:Source Extension * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 3.0, as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . * * As a special exception, AlliedModders LLC gives you permission to link the * code of this program (as well as its derivative works) to "Half-Life 2," the * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software * by the Valve Corporation. You must obey the GNU General Public License in * all respects for all other code used. Additionally, AlliedModders LLC grants * this exception to all derivative works. AlliedModders LLC defines further * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), * or . * * Version: $Id$ */ #include "extension.h" #include "util_cstrike.h" #include "RegNatives.h" #include #if SOURCE_ENGINE == SE_CSGO #include "itemdef-hash.h" ClassnameMap g_mapClassToDefIdx; ItemIndexMap g_mapDefIdxToClass; WeaponIDMap g_mapWeaponIDToDefIdx; #endif #define REGISTER_ADDR(name, defaultret, code) \ void *addr; \ if (!g_pGameConf->GetMemSig(name, &addr) || !addr) \ { \ g_pSM->LogError(myself, "Failed to lookup %s signature.", name); \ return defaultret; \ } \ code; \ g_RegNatives.Register(pWrapper); #define GET_MEMSIG(name, defaultret) \ if (!g_pGameConf->GetMemSig(name, &addr) || !addr) \ { \ g_pSM->LogError(myself, "Failed to lookup %s signature.", name); \ 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(CBaseEntity *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(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; } CCSWeaponData *GetCCSWeaponData(CEconItemView *view) { static ICallWrapper *pWrapper = NULL; if (!pWrapper) { REGISTER_ADDR("GetCCSWeaponData", NULL, PassInfo retpass; \ retpass.flags = PASSFLAG_BYVAL; \ retpass.type = PassType_Basic; \ retpass.size = sizeof(CCSWeaponData *); \ pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, &retpass, NULL, 0)) } unsigned char vstk[sizeof(CEconItemView *)]; unsigned char *vptr = vstk; *(CEconItemView **)vptr = view; CCSWeaponData *pWpnData = NULL; pWrapper->Execute(vstk, &pWpnData); return pWpnData; } CEconItemSchema *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); //On windows/mac this is actually ItemSystem() + sizeof(void *) is ItemSchema #if defined(PLATFORM_WINDOWS) || defined(PLATFORM_APPLE) return (CEconItemSchema *)((intptr_t)pSchema + sizeof(void *)); #else return (CEconItemSchema *)pSchema; #endif } CEconItemDefinition *GetItemDefintionByName(const char *classname) { CEconItemSchema *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(CEconItemDefinition *); 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; CEconItemDefinition *pItemDef = NULL; pWrapper->Execute(vstk, &pItemDef); return pItemDef; } void CreateHashMaps() { CEconItemSchema *pSchema = GetItemSchema(); if (!pSchema) return; static const char *pPriceKey = NULL; if (!pPriceKey) { pPriceKey = g_pGameConf->GetKeyValue("PriceKey"); if (!pPriceKey) { return; } } static int iHashMapOffset = -1; if (iHashMapOffset == -1) { if (!g_pGameConf->GetOffset("ItemDefHashOffset", &iHashMapOffset) || iHashMapOffset == -1) { return; } } g_mapClassToDefIdx.init(); g_mapDefIdxToClass.init(); g_mapWeaponIDToDefIdx.init(); CHashItemDef *map = (CHashItemDef *)((intptr_t)pSchema + iHashMapOffset); for (int i = 0; i < map->currentElements; i++) { HashItemDef_Node node = map->pMem[i]; if (!node.pDef || !node.pDef->m_pKv) continue; KeyValues *pClassname = node.pDef->m_pKv->FindKey("name", false); KeyValues *pAttributes = node.pDef->m_pKv->FindKey("attributes", false); if (pClassname && pAttributes) { KeyValues *pPrice = pAttributes->FindKey(pPriceKey, false); if (pPrice) { const char *classname = pClassname->GetString(); unsigned int price = pPrice->GetInt(); uint16_t iItemDefIdx = node.pDef->m_iDefinitionIndex; SMCSWeapon iWeaponID = GetWeaponIdFromDefIdx(iItemDefIdx); int iLoadoutslot = node.pDef->GetDefaultLoadoutSlot(); ClassnameMap::Insert i = g_mapClassToDefIdx.findForAdd(classname); g_mapClassToDefIdx.add(i, ke::AString(classname), ItemDefHashValue(iLoadoutslot, price, iWeaponID, iItemDefIdx, classname)); ItemIndexMap::Insert x = g_mapDefIdxToClass.findForAdd(iItemDefIdx); g_mapDefIdxToClass.add(x, iItemDefIdx, ItemDefHashValue(iLoadoutslot, price, iWeaponID, iItemDefIdx, classname)); WeaponIDMap::Insert t = g_mapWeaponIDToDefIdx.findForAdd(iWeaponID); g_mapWeaponIDToDefIdx.add(t, iWeaponID, ItemDefHashValue(iLoadoutslot, price, iWeaponID, iItemDefIdx, classname)); } } } } void ClearHashMaps() { g_mapClassToDefIdx.clear(); g_mapDefIdxToClass.clear(); g_mapWeaponIDToDefIdx.clear(); } SMCSWeapon GetWeaponIdFromDefIdx(uint16_t iDefIdx) { //DEAR GOD THIS IS HIDEOUS //None in the middle are weapons that dont exist. //If they are added and use the same idx they should be changed to their respective ones static SMCSWeapon weaponIDMap[SMCSWeapon_MAXWEAPONIDS] = { SMCSWeapon_NONE, SMCSWeapon_DEAGLE, SMCSWeapon_ELITE, SMCSWeapon_FIVESEVEN, SMCSWeapon_GLOCK, SMCSWeapon_NONE, SMCSWeapon_NONE, SMCSWeapon_AK47, SMCSWeapon_AUG, SMCSWeapon_AWP, SMCSWeapon_FAMAS, SMCSWeapon_G3SG1, SMCSWeapon_NONE, SMCSWeapon_GALILAR, SMCSWeapon_M249, SMCSWeapon_NONE, SMCSWeapon_M4A1, SMCSWeapon_MAC10, SMCSWeapon_NONE, SMCSWeapon_P90, SMCSWeapon_NONE, SMCSWeapon_NONE, SMCSWeapon_NONE, SMCSWeapon_NONE, SMCSWeapon_UMP45, SMCSWeapon_XM1014, SMCSWeapon_BIZON, SMCSWeapon_MAG7, SMCSWeapon_NEGEV, SMCSWeapon_SAWEDOFF, SMCSWeapon_TEC9, SMCSWeapon_TASER, SMCSWeapon_HKP2000, SMCSWeapon_MP7, SMCSWeapon_MP9, SMCSWeapon_NOVA, SMCSWeapon_P250, SMCSWeapon_NONE, SMCSWeapon_SCAR20, SMCSWeapon_SG556, SMCSWeapon_SSG08, SMCSWeapon_KNIFE_GG, SMCSWeapon_KNIFE, SMCSWeapon_FLASHBANG, SMCSWeapon_HEGRENADE, SMCSWeapon_SMOKEGRENADE, SMCSWeapon_MOLOTOV, SMCSWeapon_DECOY, SMCSWeapon_INCGRENADE, SMCSWeapon_C4, SMCSWeapon_KEVLAR, SMCSWeapon_ASSAULTSUIT, SMCSWeapon_HEAVYASSAULTSUIT, SMCSWeapon_NONE, SMCSWeapon_NIGHTVISION, SMCSWeapon_DEFUSER }; if (iDefIdx >= SMCSWeapon_MAXWEAPONIDS) return (SMCSWeapon)iDefIdx; else return weaponIDMap[iDefIdx]; } ItemDefHashValue *GetHashValueFromWeapon(const char *szWeapon) { char tempWeapon[MAX_WEAPON_NAME_LENGTH]; ke::SafeStrcpy(tempWeapon, sizeof(tempWeapon), szWeapon); Q_strlower(tempWeapon); if (strstr(tempWeapon, "weapon_") == NULL && strstr(tempWeapon, "item_") == NULL) { static const char *szClassPrefixs[] = { "weapon_", "item_" }; for (unsigned int i = 0; i < SM_ARRAYSIZE(szClassPrefixs); i++) { char classname[MAX_WEAPON_NAME_LENGTH]; ke::SafeSprintf(classname, sizeof(classname), "%s%s", szClassPrefixs[i], tempWeapon); ClassnameMap::Result res = g_mapClassToDefIdx.find(classname); if (res.found()) return &res->value; } return NULL; } ClassnameMap::Result res = g_mapClassToDefIdx.find(tempWeapon); if (res.found()) return &res->value; return NULL; } #endif #if SOURCE_ENGINE != SE_CSGO void *GetWeaponInfo(int weaponID) { void *info; static ICallWrapper *pWrapper = NULL; if (!pWrapper) { REGISTER_ADDR("GetWeaponInfo", NULL, PassInfo pass[1]; \ PassInfo retpass; \ pass[0].flags = PASSFLAG_BYVAL; \ pass[0].type = PassType_Basic; \ pass[0].size = sizeof(int); \ retpass.flags = PASSFLAG_BYVAL; \ retpass.type = PassType_Basic; \ retpass.size = sizeof(void *); \ pWrapper = g_pBinTools->CreateCall(addr, CallConv_Cdecl, &retpass, pass, 1)) } unsigned char vstk[sizeof(int)]; unsigned char *vptr = vstk; *(int *)vptr = weaponID; pWrapper->Execute(vstk, &info); return info; } #endif const char *GetWeaponNameFromClassname(const char *weapon) { char *szTemp = strstr((char *)weapon, "_"); if (!szTemp) { return weapon; } else { return (const char *)((intptr_t)szTemp + 1); } } const char *GetTranslatedWeaponAlias(const char *weapon) { #if SOURCE_ENGINE != SE_CSGO const char *alias = NULL; static ICallWrapper *pWrapper = NULL; if (!pWrapper) { REGISTER_ADDR("GetTranslatedWeaponAlias", weapon, PassInfo pass[1]; \ PassInfo retpass; \ pass[0].flags = PASSFLAG_BYVAL; \ pass[0].type = PassType_Basic; \ pass[0].size = sizeof(const char *); \ retpass.flags = PASSFLAG_BYVAL; \ retpass.type = PassType_Basic; \ retpass.size = sizeof(const char *); \ pWrapper = g_pBinTools->CreateCall(addr, CallConv_Cdecl, &retpass, pass, 1)) } unsigned char vstk[sizeof(const char *)]; unsigned char *vptr = vstk; *(const char **)vptr = GetWeaponNameFromClassname(weapon); pWrapper->Execute(vstk, &alias); 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 (size_t i = 0; i < SM_ARRAYSIZE(szAliases) / 2; i++) { if (Q_stristr(GetWeaponNameFromClassname(weapon), szAliases[i * 2]) != 0) return szAliases[i * 2 + 1]; } return GetWeaponNameFromClassname(weapon); #endif } int AliasToWeaponID(const char *weapon) { #if SOURCE_ENGINE != SE_CSGO int weaponID = 0; static ICallWrapper *pWrapper = NULL; if (!pWrapper) { REGISTER_ADDR("AliasToWeaponID", 0, PassInfo pass[1]; \ PassInfo retpass; \ pass[0].flags = PASSFLAG_BYVAL; \ pass[0].type = PassType_Basic; \ pass[0].size = sizeof(const char *); \ retpass.flags = PASSFLAG_BYVAL; \ retpass.type = PassType_Basic; \ retpass.size = sizeof(int); \ pWrapper = g_pBinTools->CreateCall(addr, CallConv_Cdecl, &retpass, pass, 1)) } unsigned char vstk[sizeof(const char *)]; unsigned char *vptr = vstk; *(const char **)vptr = GetWeaponNameFromClassname(weapon); pWrapper->Execute(vstk, &weaponID); return weaponID; #else ItemDefHashValue *pHashValue = GetHashValueFromWeapon(weapon); if (pHashValue) return pHashValue->m_iWeaponID; return 0; #endif } const char *WeaponIDToAlias(int weaponID) { #if SOURCE_ENGINE != SE_CSGO const char *alias = NULL; static ICallWrapper *pWrapper = NULL; if (!pWrapper) { REGISTER_ADDR("WeaponIDToAlias", 0, PassInfo pass[1]; \ PassInfo retpass; \ pass[0].flags = PASSFLAG_BYVAL; \ pass[0].type = PassType_Basic; \ pass[0].size = sizeof(int); \ retpass.flags = PASSFLAG_BYVAL; \ retpass.type = PassType_Basic; \ retpass.size = sizeof(const char *); \ pWrapper = g_pBinTools->CreateCall(addr, CallConv_Cdecl, &retpass, pass, 1)) } unsigned char vstk[sizeof(int)]; unsigned char *vptr = vstk; *(int *)vptr = weaponID; pWrapper->Execute(vstk, &alias); return alias; #else WeaponIDMap::Result res = g_mapWeaponIDToDefIdx.find((SMCSWeapon)weaponID); if (res.found()) return res->value.m_szItemName; return NULL; #endif } bool IsValidWeaponID(int id) { if (id <= (int)SMCSWeapon_NONE) return false; #if SOURCE_ENGINE == SE_CSGO WeaponIDMap::Result res = g_mapWeaponIDToDefIdx.find((SMCSWeapon)id); if (!res.found()) return false; #else else if (id > SMCSWeapon_NIGHTVISION || !GetWeaponInfo(id)) return false; #endif return true; }