diff --git a/extensions/cstrike/forwards.cpp b/extensions/cstrike/forwards.cpp index 9d6bb150..bfef880f 100644 --- a/extensions/cstrike/forwards.cpp +++ b/extensions/cstrike/forwards.cpp @@ -61,9 +61,21 @@ DETOUR_DECL_MEMBER1(DetourHandleBuy, int, const char *, weapon) #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 { - int price = DETOUR_MEMBER_CALL(DetourWeaponPrice)(); +#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; @@ -71,19 +83,6 @@ DETOUR_DECL_MEMBER0(DetourWeaponPrice, int) return CallPriceForward(lastclient, weapon_name, price); } -#else -DETOUR_DECL_MEMBER2(DetourWeaponPrice, int, const char *, szAttribute, CEconItemView *, pEconItem) -{ - int price = DETOUR_MEMBER_CALL(DetourWeaponPrice)(szAttribute, pEconItem); - - if(lastclient == -1 || strcmp(szAttribute, "in game price") != 0) - return price; - - const char *weapon_name = reinterpret_cast(this+weaponNameOffset); - - return CallPriceForward(lastclient, weapon_name, price); -} -#endif #if SOURCE_ENGINE != SE_CSGO || !defined(WIN32) DETOUR_DECL_MEMBER2(DetourTerminateRound, void, float, delay, int, reason) @@ -201,8 +200,15 @@ bool CreateWeaponPriceDetour() } } -#if SOURCE_ENGINE == SE_CSGO - DWeaponPrice = DETOUR_CREATE_MEMBER(DetourWeaponPrice, "GetAttributeInt"); +#if SOURCE_ENGINE == SE_CSGO && defined(WIN32) + 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 diff --git a/extensions/cstrike/natives.cpp b/extensions/cstrike/natives.cpp index f6f07ea1..1b4ebcf5 100644 --- a/extensions/cstrike/natives.cpp +++ b/extensions/cstrike/natives.cpp @@ -419,6 +419,7 @@ static cell_t CS_GetTranslatedWeaponAlias(IPluginContext *pContext, const cell_t static cell_t CS_GetWeaponPrice(IPluginContext *pContext, const cell_t *params) { + if (!IsValidWeaponID(params[2])) return pContext->ThrowNativeError("Invalid WeaponID passed for this game"); @@ -426,6 +427,7 @@ static cell_t CS_GetWeaponPrice(IPluginContext *pContext, const cell_t *params) //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 @@ -458,23 +460,49 @@ static cell_t CS_GetWeaponPrice(IPluginContext *pContext, const cell_t *params) #if SOURCE_ENGINE == SE_CSGO static ICallWrapper *pWrapper = NULL; +#if defined(WIN32) + if(!pWrapper) + { + void *pGetWeaponPrice = GetWeaponPriceFunction(); + if(!pGetWeaponPrice) + { + return pContext->ThrowNativeError("Failed to locate function"); + } + + PassInfo pass[2]; + 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); + ret.flags = PASSFLAG_BYVAL; + ret.type = PassType_Basic; + ret.size = sizeof(int); + pWrapper = g_pBinTools->CreateCall(pGetWeaponPrice, CallConv_ThisCall, &ret, pass, 2); + } +#else if (!pWrapper) { - REGISTER_NATIVE_ADDR("GetAttributeInt", - PassInfo pass[2]; \ + REGISTER_NATIVE_ADDR("GetWeaponPrice", + PassInfo pass[3]; \ PassInfo ret; \ pass[0].flags = PASSFLAG_BYVAL; \ pass[0].type = PassType_Basic; \ - pass[0].size = sizeof(char *); \ + pass[0].size = sizeof(CEconItemView *); \ pass[1].flags = PASSFLAG_BYVAL; \ pass[1].type = PassType_Basic; \ - pass[1].size = sizeof(CEconItemView *); \ + pass[1].size = sizeof(int); \ + pass[2].flags = PASSFLAG_BYVAL; \ + pass[2].type = PassType_Float; \ + pass[2].size = sizeof(float); \ ret.flags = PASSFLAG_BYVAL; \ ret.type = PassType_Basic; \ ret.size = sizeof(int); \ - pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, &ret, pass, 2)) + pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, &ret, pass, 3)) } - +#endif // 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: @@ -552,14 +580,22 @@ static cell_t CS_GetWeaponPrice(IPluginContext *pContext, const cell_t *params) pGetView->Execute(vstk_view, &view); } - unsigned char vstk[sizeof(void *) * 2 + sizeof(char *)]; +#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 *); - *(const char **)vptr = "in game price"; - vptr += sizeof(const char *); *(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); diff --git a/extensions/cstrike/util_cstrike.cpp b/extensions/cstrike/util_cstrike.cpp index ad1c4b7c..58e0573f 100644 --- a/extensions/cstrike/util_cstrike.cpp +++ b/extensions/cstrike/util_cstrike.cpp @@ -229,6 +229,56 @@ const char *WeaponIDToAlias(int weaponID) #endif return alias; } + +#if SOURCE_ENGINE == SE_CSGO && defined(WIN32) +void *GetWeaponPriceFunction() +{ + static void *pGetWeaponPriceAddress = NULL; + + if(pGetWeaponPriceAddress == NULL) + { + void *pAddress = NULL; + int offset = 0; + int callOffset = 0; + const char* byteCheck = NULL; + + if(!g_pGameConf->GetMemSig("GetWeaponPrice", &pAddress) || pAddress == NULL) + { + g_pSM->LogError(myself, "Failed to get GetWeaponPrice address."); + return NULL; + } + + if(!g_pGameConf->GetOffset("GetWeaponPriceFunc", &offset)) + { + g_pSM->LogError(myself, "Failed to get GetWeaponPriceFunc offset."); + return NULL; + } + + byteCheck = g_pGameConf->GetKeyValue("GetWeaponPriceByteCheck"); + + if(byteCheck == NULL) + { + g_pSM->LogError(myself, "Failed to get GetWeaponPriceByteCheck keyvalue."); + return NULL; + } + + uint8_t iByte = strtoul(byteCheck, NULL, 16); + + if(iByte != *(uint8_t *)((intptr_t)pAddress + (offset-1))) + { + g_pSM->LogError(myself, "GetWeaponPrice Byte check failed."); + return NULL; + } + + callOffset = *(uint32_t *)((intptr_t)pAddress + offset); + + pGetWeaponPriceAddress = (void *)((intptr_t)pAddress + offset + callOffset + sizeof(int)); + } + + return pGetWeaponPriceAddress; +} +#endif + int GetRealWeaponID(int weaponId) { #if SOURCE_ENGINE == SE_CSGO diff --git a/extensions/cstrike/util_cstrike.h b/extensions/cstrike/util_cstrike.h index 9f5445d4..160ca805 100644 --- a/extensions/cstrike/util_cstrike.h +++ b/extensions/cstrike/util_cstrike.h @@ -165,4 +165,6 @@ 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 ac0c4f93..ceb49733 100644 --- a/gamedata/sm-cstrike.games/game.csgo.txt +++ b/gamedata/sm-cstrike.games/game.csgo.txt @@ -13,6 +13,10 @@ { "csgo" { + "Keys" + { + "GetWeaponPriceByteCheck" "E9" + } "Offsets" { //Offset of szClassName in CCSWeaponInfo @@ -62,6 +66,15 @@ "linux" "9" "mac" "9" } + "GetWeaponPriceFunc" + { + "windows" "84" + } + //This is GetWeaponPriceFunc offset -1 (only used by GDC) + "GetWeaponPriceFuncGDC" + { + "windows" "83" + } } "Signatures" { @@ -143,13 +156,13 @@ "linux" "@_ZN9CCSPlayer10SetClanTagEPKc" "mac" "@_ZN9CCSPlayer10SetClanTagEPKc" } - //Wild card first 6 bytes since we call it and detour it. - "GetAttributeInt" + //In windows this is CCSPlayer::GetWeaponPrice NOT CCSWeaponInfo::GetWeaponPrice + "GetWeaponPrice" { "library" "server" - "windows" "\x2A\x2A\x2A\x2A\x2A\x2A\x83\xEC\x1C\x53\x8B\x5D\x0C\xC7\x44\x24\x04\x00\x00\x00\x00\x56\x8B\xF1\x89\x74\x24\x0C\x57\x8B\x7D\x08\x85\xDB\x74\x2A\x80\x7B\x2A\x2A\x74\x2A\xE8\x2A\x2A\x2A\x2A\x57" - "linux" "@_ZNK16FileWeaponInfo_t15GetAttributeIntEPKcPK13CEconItemView" - "mac" "@_ZNK16FileWeaponInfo_t15GetAttributeIntEPKcPK13CEconItemView" + "windows" "\x55\x8B\xEC\x8B\xD1\x8B\x4D\x08\x83\xF9\x33\x75\x2A\x83\xBA" + "linux" "@_ZNK13CCSWeaponInfo14GetWeaponPriceEPK13CEconItemViewif" + "mac" "@_ZNK13CCSWeaponInfo14GetWeaponPriceEPK13CEconItemViewif" } "SetModelFromClass" { diff --git a/public/CDetour/detours.cpp b/public/CDetour/detours.cpp index f31c8c84..a82777c2 100644 --- a/public/CDetour/detours.cpp +++ b/public/CDetour/detours.cpp @@ -58,6 +58,23 @@ CDetour *CDetourManager::CreateDetour(void *callbackfunction, void **trampoline, return NULL; } +CDetour *CDetourManager::CreateDetour(void *callbackfunction, void **trampoline, void *pAddress) +{ + CDetour *detour = new CDetour(callbackfunction, trampoline, pAddress); + if (detour) + { + if (!detour->Init(spengine, gameconf)) + { + delete detour; + return NULL; + } + + return detour; + } + + return NULL; +} + CDetour::CDetour(void *callbackfunction, void **trampoline, const char *signame) { enabled = false; @@ -71,6 +88,19 @@ CDetour::CDetour(void *callbackfunction, void **trampoline, const char *signame) this->trampoline = trampoline; } +CDetour::CDetour(void*callbackfunction, void **trampoline, void *pAddress) +{ + enabled = false; + detoured = false; + detour_address = pAddress; + detour_trampoline = NULL; + this->signame = NULL; + this->detour_callback = callbackfunction; + spengine = NULL; + gameconf = NULL; + this->trampoline = trampoline; +} + bool CDetour::Init(ISourcePawnEngine *spengine, IGameConfig *gameconf) { this->spengine = spengine; @@ -100,11 +130,16 @@ bool CDetour::IsEnabled() bool CDetour::CreateDetour() { - if (!gameconf->GetMemSig(signame, &detour_address)) + if (signame && !gameconf->GetMemSig(signame, &detour_address)) { g_pSM->LogError(myself, "Could not locate %s - Disabling detour", signame); return false; } + else if(!detour_address) + { + g_pSM->LogError(myself, "Invalid detour address passed - Disabling detour to prevent crashes"); + return false; + } if (!detour_address) { diff --git a/public/CDetour/detours.h b/public/CDetour/detours.h index db42462e..d81b177e 100644 --- a/public/CDetour/detours.h +++ b/public/CDetour/detours.h @@ -167,6 +167,7 @@ public: protected: CDetour(void *callbackfunction, void **trampoline, const char *signame); + CDetour(void*callbackfunction, void **trampoline, void *pAddress); bool Init(ISourcePawnEngine *spengine, IGameConfig *gameconf); private: @@ -239,6 +240,7 @@ public: * Note we changed the netadr_s reference into a void* to avoid needing to define the type */ static CDetour *CreateDetour(void *callbackfunction, void **trampoline, const char *signame); + static CDetour *CreateDetour(void *callbackfunction, void **trampoline, void *pAddress); friend class CBlocker; friend class CDetour; diff --git a/tools/gdc-psyfork/symbols.txt b/tools/gdc-psyfork/symbols.txt index 14eb407a..d2e0172e 100644 --- a/tools/gdc-psyfork/symbols.txt +++ b/tools/gdc-psyfork/symbols.txt @@ -324,6 +324,8 @@ "CalcDominationAndRevenge_Offset" "CalcDomRevPatch" "CalcDominationAndRevenge_Byte_Win" "0F" "CalcDominationAndRevenge_Byte_Lin" "74" + "GetWeaponPrice_Offset" "GetWeaponPriceFuncGDC" + "GetWeaponPrice_Byte_Win" "E9" } }