extensions/cstrike/natives.cpp
author Ruben Gonzalez <drifter01620@gmail.com>
Thu May 22 09:49:27 2014 -0400 (2014-05-22)
changeset 4378 568f4497ef4a
parent 4252 e665928a4dba
permissions -rw-r--r--
Update CS:GO team score gamedata.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod Counter-Strike:Source Extension
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "extension.h"
    33 #include "RegNatives.h"
    34 #include "forwards.h"
    35 #include "util_cstrike.h"
    36 #include <server_class.h>
    37 #include <iplayerinfo.h>
    38 
    39 int g_iPriceOffset = -1;
    40 
    41 #define REGISTER_NATIVE_ADDR(name, code) \
    42 	void *addr; \
    43 	if (!g_pGameConf->GetMemSig(name, &addr) || !addr) \
    44 	{ \
    45 		return pContext->ThrowNativeError("Failed to locate function"); \
    46 	} \
    47 	code; \
    48 	g_RegNatives.Register(pWrapper);
    49 
    50 #define GET_MEMSIG(name) \
    51 	if (!g_pGameConf->GetMemSig(name, &addr) || !addr) \
    52 	{ \
    53 		return pContext->ThrowNativeError("Failed to locate function"); \
    54 	}
    55 
    56 inline CBaseEntity *GetCBaseEntity(int num, bool isplayer)
    57 {
    58 	edict_t *pEdict = gamehelpers->EdictOfIndex(num);
    59 	if (!pEdict || pEdict->IsFree())
    60 	{
    61 		return NULL;
    62 	}
    63 
    64 	if (num > 0 && num <= playerhelpers->GetMaxClients())
    65 	{
    66 		IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(pEdict);
    67 		if (!pPlayer || !pPlayer->IsConnected())
    68 		{
    69 			return NULL;
    70 		}
    71 	}
    72 	else if (isplayer) 
    73 	{
    74 		return NULL;
    75 	}
    76 
    77 	IServerUnknown *pUnk;
    78 	if ((pUnk=pEdict->GetUnknown()) == NULL)
    79 	{
    80 		return NULL;
    81 	}
    82 
    83 	return pUnk->GetBaseEntity();
    84 }
    85 
    86 bool UTIL_FindDataTable(SendTable *pTable, const char *name, sm_sendprop_info_t *info, unsigned int offset)
    87 {
    88 	const char *pname;
    89 	int props = pTable->GetNumProps();
    90 	SendProp *prop;
    91 	SendTable *table;
    92 
    93 	for (int i=0; i<props; i++)
    94 	{
    95 		prop = pTable->GetProp(i);
    96 
    97 		if ((table = prop->GetDataTable()) != NULL)
    98 		{
    99 			pname = table->GetName();
   100 			if (pname && strcmp(name, pname) == 0)
   101 			{
   102 				info->prop = prop;
   103 				info->actual_offset = offset + info->prop->GetOffset();
   104 				return true;
   105 			}
   106 
   107 			if (UTIL_FindDataTable(table, 
   108 				name,
   109 				info,
   110 				offset + prop->GetOffset())
   111 				)
   112 			{
   113 				return true;
   114 			}
   115 		}
   116 	}
   117 
   118 	return false;
   119 }
   120 
   121 //Taken From Sourcemod Core
   122 unsigned int strncopy(char *dest, const char *src, size_t count)
   123 {
   124 	if (!count)
   125 	{
   126 		return 0;
   127 	}
   128 
   129 	char *start = dest;
   130 	while ((*src) && (--count))
   131 	{
   132 		*dest++ = *src++;
   133 	}
   134 	*dest = '\0';
   135 
   136 	return (dest - start);
   137 }
   138 
   139 static cell_t CS_RespawnPlayer(IPluginContext *pContext, const cell_t *params)
   140 {
   141 	static ICallWrapper *pWrapper = NULL;
   142 	if (!pWrapper)
   143 	{
   144 		REGISTER_NATIVE_ADDR("RoundRespawn", 
   145 			pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, NULL, 0));
   146 	}
   147 
   148 	CBaseEntity *pEntity;
   149 	if (!(pEntity=GetCBaseEntity(params[1], true)))
   150 	{
   151 		return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
   152 	}
   153 	pWrapper->Execute(&pEntity, NULL);
   154 
   155 	return 1;
   156 }
   157 
   158 static cell_t CS_SwitchTeam(IPluginContext *pContext, const cell_t *params)
   159 {
   160 #if SOURCE_ENGINE != SE_CSGO || !defined(WIN32)
   161 	static ICallWrapper *pWrapper = NULL;
   162 	if (!pWrapper)
   163 	{
   164 		REGISTER_NATIVE_ADDR("SwitchTeam", 
   165 			PassInfo pass[1]; \
   166 			pass[0].flags = PASSFLAG_BYVAL; \
   167 			pass[0].size = sizeof(int); \
   168 			pass[0].type = PassType_Basic; \
   169 			pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 1))
   170 	}
   171 
   172 	CBaseEntity *pEntity;
   173 	if (!(pEntity=GetCBaseEntity(params[1], true)))
   174 	{
   175 		return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
   176 	}
   177 
   178 	unsigned char vstk[sizeof(CBaseEntity *) + sizeof(int)];
   179 	unsigned char *vptr = vstk;
   180 
   181 	*(CBaseEntity **)vptr = pEntity;
   182 	vptr += sizeof(CBaseEntity *);
   183 	*(int *)vptr = params[2];
   184 	pWrapper->Execute(vstk, NULL);
   185 #else
   186 	if (g_pSDKTools == NULL)
   187 	{
   188 		return pContext->ThrowNativeError("SDKTools interface not found. SwitchTeam native disabled.");
   189 	}
   190 
   191 	static void *addr = NULL;
   192 	if(!addr)
   193 	{
   194 		GET_MEMSIG("SwitchTeam");
   195 	}
   196 
   197 	void *gamerules = g_pSDKTools->GetGameRules();
   198 	if (gamerules == NULL)
   199 	{
   200 		return pContext->ThrowNativeError("GameRules not available. SwitchTeam native disabled.");
   201 	}
   202 
   203 	CBaseEntity *pEntity;
   204 	if (!(pEntity=GetCBaseEntity(params[1], true)))
   205 	{
   206 		return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
   207 	}
   208 
   209 	int team = params[2];
   210 
   211 	__asm
   212 	{
   213 		push team
   214 		mov ecx, pEntity
   215 		mov ebx, gamerules
   216  
   217 		call addr
   218 	}
   219 #endif
   220 
   221 	return 1;
   222 }
   223 
   224 static cell_t CS_DropWeapon(IPluginContext *pContext, const cell_t *params)
   225 {
   226 	static ICallWrapper *pWrapper = NULL;
   227 	if (!pWrapper)
   228 	{
   229 		REGISTER_NATIVE_ADDR("CSWeaponDrop",
   230 			PassInfo pass[3]; \
   231 			pass[0].flags = PASSFLAG_BYVAL; \
   232 			pass[0].type  = PassType_Basic; \
   233 			pass[0].size  = sizeof(CBaseEntity *); \
   234 			pass[1].flags = PASSFLAG_BYVAL; \
   235 			pass[1].type  = PassType_Basic; \
   236 			pass[1].size  = sizeof(bool); \
   237 			pass[2].flags = PASSFLAG_BYVAL; \
   238 			pass[2].type  = PassType_Basic; \
   239 			pass[2].size  = sizeof(bool); \
   240 			pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 3))
   241 	}
   242 
   243 	CBaseEntity *pEntity;
   244 	if (!(pEntity = GetCBaseEntity(params[1], true)))
   245 	{
   246 		return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
   247 	}
   248 
   249 	CBaseEntity *pWeapon;
   250 	if (!(pWeapon = GetCBaseEntity(params[2], false)))
   251 	{
   252 		return pContext->ThrowNativeError("Weapon index %d is not valid", params[2]);
   253 	}
   254 
   255 	//Psychonic is awesome for this
   256 	sm_sendprop_info_t spi;
   257 	IServerUnknown *pUnk = (IServerUnknown *)pWeapon;
   258 	IServerNetworkable *pNet = pUnk->GetNetworkable();
   259 
   260 	if (!UTIL_FindDataTable(pNet->GetServerClass()->m_pTable, "DT_WeaponCSBase", &spi, 0))
   261 		return pContext->ThrowNativeError("Entity index %d is not a weapon", params[2]);
   262 
   263 	if (!gamehelpers->FindSendPropInfo("CBaseCombatWeapon", "m_hOwnerEntity", &spi))
   264 		return pContext->ThrowNativeError("Invalid entity index %d for weapon", params[2]);
   265 
   266 	CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pWeapon + spi.actual_offset);
   267 	if (params[1] != hndl.GetEntryIndex() || hndl != ((IServerEntity *)pEntity)->GetRefEHandle())
   268 		return pContext->ThrowNativeError("Weapon %d is not owned by client %d", params[2], params[1]);
   269 
   270 	if (params[4] == 1 && g_pCSWeaponDropDetoured)
   271 		g_pIgnoreCSWeaponDropDetour = true;
   272 
   273 	unsigned char vstk[sizeof(CBaseEntity *) * 2 + sizeof(bool) * 2];
   274 	unsigned char *vptr = vstk;
   275 
   276 	// <psychonic> first one is always false. second is true to toss, false to just drop
   277 	*(CBaseEntity **)vptr = pEntity;
   278 	vptr += sizeof(CBaseEntity *);
   279 	*(CBaseEntity **)vptr = pWeapon;
   280 	vptr += sizeof(CBaseEntity *);
   281 	*(bool *)vptr = false;
   282 	vptr += sizeof(bool);
   283 	*(bool *)vptr = (params[3]) ? true : false;
   284 
   285  	pWrapper->Execute(vstk, NULL);
   286 
   287 	return 1;
   288 }
   289 
   290 static cell_t CS_TerminateRound(IPluginContext *pContext, const cell_t *params)
   291 {
   292 	if (g_pSDKTools == NULL)
   293 	{
   294 		return pContext->ThrowNativeError("SDKTools interface not found. TerminateRound native disabled.");
   295 	}
   296 	else if (g_pSDKTools->GetInterfaceVersion() < 2)
   297 	{
   298 		//<psychonic> THIS ISN'T DA LIMBO STICK. LOW IS BAD
   299 		return pContext->ThrowNativeError("SDKTools interface is outdated. TerminateRound native disabled.");
   300 	}
   301 
   302 	void *gamerules = g_pSDKTools->GetGameRules();
   303 	if (gamerules == NULL)
   304 	{
   305 		return pContext->ThrowNativeError("GameRules not available. TerminateRound native disabled.");
   306 	}
   307 
   308 #if SOURCE_ENGINE != SE_CSGO || !defined(WIN32)
   309 	static ICallWrapper *pWrapper = NULL;
   310 
   311 	if (!pWrapper)
   312 	{
   313 		REGISTER_NATIVE_ADDR("TerminateRound",
   314 			PassInfo pass[2]; \
   315 			pass[0].flags = PASSFLAG_BYVAL; \
   316 			pass[0].type = PassType_Basic; \
   317 			pass[0].size = sizeof(float); \
   318 			pass[1].flags = PASSFLAG_BYVAL; \
   319 			pass[1].type = PassType_Basic; \
   320 			pass[1].size = sizeof(int); \
   321 			pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 2))
   322 	}
   323 
   324 	if (params[3] == 1 && g_pTerminateRoundDetoured)
   325 		g_pIgnoreTerminateDetour = true;
   326 
   327 	unsigned char vstk[sizeof(void *) + sizeof(float)+ sizeof(int)];
   328 	unsigned char *vptr = vstk;
   329 
   330 	*(void **)vptr = gamerules;
   331 	vptr += sizeof(void *);
   332 	*(float *)vptr = sp_ctof(params[1]);
   333 	vptr += sizeof(float);
   334 	*(int*)vptr = params[2];
   335 
   336 	pWrapper->Execute(vstk, NULL);
   337 #else
   338 	static void *addr = NULL;
   339 
   340 	if(!addr)
   341 	{
   342 		GET_MEMSIG("TerminateRound");
   343 	}
   344 
   345 	if (params[3] == 1 && g_pTerminateRoundDetoured)
   346 		g_pIgnoreTerminateDetour = true;
   347 
   348 	float delay = sp_ctof(params[1]);
   349 	int reason = params[2];
   350 	signed int unknown = 1;//We might want to find what this is?
   351 	__asm
   352 	{
   353 		push reason
   354 		movss xmm1, delay
   355 		mov edi, unknown
   356 		mov ecx, gamerules
   357 		call addr
   358 	}
   359 
   360 #endif
   361 	return 1;
   362 }
   363 
   364 static cell_t CS_WeaponIDToAlias(IPluginContext *pContext, const cell_t *params)
   365 {
   366 	if (!IsValidWeaponID(params[1]))
   367 		return pContext->ThrowNativeError("Invalid WeaponID passed for this game");
   368 
   369 	char *dest;
   370 
   371 	pContext->LocalToString(params[2], &dest);
   372 
   373 	const char *ret = WeaponIDToAlias(params[1]);
   374 	
   375 	if (ret == NULL)
   376 	{
   377 		return 0;
   378 	}
   379 	
   380 	return strncopy(dest, ret, params[3]);
   381 }
   382 
   383 static cell_t CS_GetTranslatedWeaponAlias(IPluginContext *pContext, const cell_t *params)
   384 {
   385 	char *weapon;
   386 	char *dest;
   387 
   388 	pContext->LocalToString(params[2], &dest);
   389 	pContext->LocalToString(params[1], &weapon);
   390 
   391 	const char *ret = GetTranslatedWeaponAlias(weapon);
   392 	strncopy(dest, ret, params[3]);
   393 	
   394 	return 1;
   395 }
   396 
   397 static cell_t CS_GetWeaponPrice(IPluginContext *pContext, const cell_t *params)
   398 {
   399 	if (!IsValidWeaponID(params[2]))
   400 		return pContext->ThrowNativeError("Invalid WeaponID passed for this game");
   401 
   402 	int id = GetRealWeaponID(params[2]);
   403 
   404 	//Hard code return values for weapons that dont call GetWeaponPrice and always use default value.
   405 #if SOURCE_ENGINE == SE_CSGO
   406 	if (id == WEAPON_C4 || id == WEAPON_KNIFE || id == WEAPON_KNIFE_GG)
   407 		return 0;
   408 #else
   409  	if (id == WEAPON_C4 || id == WEAPON_KNIFE || id == WEAPON_SHIELD)
   410 		return 0;
   411 	else if (id == WEAPON_KEVLAR)
   412 		return 650;
   413 	else if (id == WEAPON_ASSAULTSUIT)
   414 		return 1000;
   415 #endif
   416 	else if (id == WEAPON_NIGHTVISION)
   417 		return 1250;
   418 #if SOURCE_ENGINE == SE_CSGO
   419 	else if (id == WEAPON_DEFUSER)
   420 		return 400;
   421 #endif
   422 
   423 	void *info = GetWeaponInfo(id);
   424 		
   425 	if (!info)
   426 	{
   427 		return pContext->ThrowNativeError("Failed to get weaponinfo");
   428 	}
   429 
   430 	CBaseEntity *pEntity;
   431 	if (!(pEntity = GetCBaseEntity(params[1], true)))
   432 	{
   433 		return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
   434 	}
   435 #if SOURCE_ENGINE == SE_CSGO
   436 	static ICallWrapper *pWrapper = NULL;
   437 
   438 	if (!pWrapper)
   439 	{
   440 		REGISTER_NATIVE_ADDR("GetAttributeInt",
   441 		PassInfo pass[2]; \
   442 		PassInfo ret; \
   443 		pass[0].flags = PASSFLAG_BYVAL; \
   444 		pass[0].type = PassType_Basic; \
   445 		pass[0].size = sizeof(char *); \
   446 		pass[1].flags = PASSFLAG_BYVAL; \
   447 		pass[1].type = PassType_Basic; \
   448 		pass[1].size = sizeof(CEconItemView *); \
   449 		ret.flags = PASSFLAG_BYVAL; \
   450 		ret.type = PassType_Basic; \
   451 		ret.size = sizeof(int); \
   452 		pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, &ret, pass, 2))
   453 	}
   454 
   455 	// Get a CEconItemView for the m4
   456 	// Found in CCSPlayer::HandleCommand_Buy_Internal
   457 	// Linux a1 - CCSPlayer *pEntity, v5 - Player Team, a3 - ItemLoadoutSlot -1 use default loadoutslot:
   458 	// v4 = *(int (__cdecl **)(_DWORD, _DWORD, _DWORD))(*(_DWORD *)(a1 + 9492) + 36); // offset 9
   459 	// v6 = v4(a1 + 9492, v5, a3);
   460 	// Windows v5 - CCSPlayer *pEntity a4 -  ItemLoadoutSlot -1 use default loadoutslot:
   461 	// v8 = (*(int (__stdcall **)(_DWORD, int))(*(_DWORD *)(v5 + 9472) + 32))(*(_DWORD *)(v5 + 760), a4); // offset 8
   462 	// The function is CCSPlayerInventory::GetItemInLoadout(int, int)
   463 	// We can pass NULL view to the GetAttribute to use default loadoutslot.
   464 	// We only really care about m4a1/m4a4 as price differs between them
   465 	// thisPtrOffset = 9472/9492
   466 
   467 	static ICallWrapper *pGetView = NULL;
   468 	static int thisPtrOffset = -1;
   469 	CEconItemView *view = NULL;
   470 
   471 	if(!pGetView)
   472 	{
   473 		int offset = -1;
   474 		int byteOffset = -1;
   475 		void *pHandleCommandBuy = NULL;
   476 		if (!g_pGameConf->GetOffset("GetItemInLoadout", &offset) || offset == -1)
   477 		{
   478 			smutils->LogError(myself, "Failed to get GetItemInLoadout offset. Reverting to NULL ItemView");
   479 		}
   480 		else if (!g_pGameConf->GetOffset("CCSPlayerInventoryOffset", &byteOffset) || byteOffset == -1)
   481 		{
   482 			smutils->LogError(myself, "Failed to get CCSPlayerInventoryOffset offset. Reverting to NULL ItemView");
   483 		}
   484 		else if (!g_pGameConf->GetMemSig("HandleCommand_Buy_Internal", &pHandleCommandBuy) || !pHandleCommandBuy)
   485 		{
   486 			smutils->LogError(myself, "Failed to get HandleCommand_Buy_Internal function. Reverting to NULL ItemView");
   487 		}
   488 		else
   489 		{
   490 			thisPtrOffset = *(int *)((intptr_t)pHandleCommandBuy + byteOffset);
   491 
   492 			PassInfo pass[2];
   493 			PassInfo ret;
   494 			pass[0].flags = PASSFLAG_BYVAL;
   495 			pass[0].type  = PassType_Basic;
   496 			pass[0].size  = sizeof(int);
   497 			pass[1].flags = PASSFLAG_BYVAL;
   498 			pass[1].type  = PassType_Basic;
   499 			pass[1].size  = sizeof(int);
   500 
   501 			ret.flags = PASSFLAG_BYVAL;
   502 			ret.type = PassType_Basic;
   503 			ret.size = sizeof(void *);
   504 
   505 			pGetView = g_pBinTools->CreateVCall(offset, 0, 0, &ret, pass, 2);
   506 			g_RegNatives.Register(pGetView);
   507 		}
   508 	}
   509 
   510 	IPlayerInfo *playerinfo = playerhelpers->GetGamePlayer(params[1])->GetPlayerInfo();
   511 	if(pGetView && thisPtrOffset != -1 && playerinfo)
   512 	{
   513 		//If the gun isnt an M4 we ignore this as M4 is the only one that differs in price based on Loadout item.
   514 		int iLoadoutSlot = -1;
   515 		if(id == WEAPON_M4)
   516 		{
   517 			iLoadoutSlot = 15;
   518 		}
   519 
   520 		unsigned char vstk_view[sizeof(void *) + sizeof(int) * 2];
   521 		unsigned char *vptr_view = vstk_view;
   522 
   523 		*(void **)vptr_view = (void *)((intptr_t)pEntity + thisPtrOffset);
   524 		vptr_view += sizeof(void *);
   525 		*(int *)vptr_view = playerinfo->GetTeamIndex();
   526 		vptr_view += sizeof(int);
   527 		*(int *)vptr_view = iLoadoutSlot;
   528 
   529 		pGetView->Execute(vstk_view, &view);
   530 	}
   531 
   532 	unsigned char vstk[sizeof(void *) * 2 + sizeof(char *)];
   533 	unsigned char *vptr = vstk;
   534 
   535 	*(void **)vptr = info;
   536 	vptr += sizeof(void *);
   537 	*(const char **)vptr = "in game price";
   538 	vptr += sizeof(const char *);
   539 	*(CEconItemView **)vptr = view;
   540 
   541 	int price = 0;
   542  	pWrapper->Execute(vstk, &price);
   543 #else
   544 	if (g_iPriceOffset == -1)
   545 	{
   546 		if (!g_pGameConf->GetOffset("WeaponPrice", &g_iPriceOffset))
   547 		{
   548 			g_iPriceOffset = -1;
   549 			return pContext->ThrowNativeError("Failed to get WeaponPrice offset");
   550 		}
   551 	}
   552 
   553 	int price = *(int *)((intptr_t)info + g_iPriceOffset);
   554 #endif
   555 
   556 	if (params[3] || weaponNameOffset == -1)
   557 		return price;
   558 
   559 	const char *weapon_name = (const char *)((intptr_t)info + weaponNameOffset);
   560 
   561 	return CallPriceForward(params[1], weapon_name, price);
   562 }
   563 
   564 static cell_t CS_GetClientClanTag(IPluginContext *pContext, const cell_t *params)
   565 {
   566 	static void *addr;
   567 	if (!addr)
   568 	{
   569 		if (!g_pGameConf->GetMemSig("SetClanTag", &addr) || !addr)
   570 		{
   571 			return pContext->ThrowNativeError("Failed to locate function");
   572 		}
   573 	}
   574 
   575 	CBaseEntity *pEntity;
   576 	if (!(pEntity = GetCBaseEntity(params[1], true)))
   577 	{
   578 		return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
   579 	}
   580 
   581 	static int tagOffsetOffset = -1;
   582 	static int tagOffset;
   583 
   584 	if (tagOffsetOffset == -1)
   585 	{
   586 		if (!g_pGameConf->GetOffset("ClanTagOffset", &tagOffsetOffset))
   587 		{
   588 			tagOffsetOffset = -1;
   589 			return pContext->ThrowNativeError("Unable to find ClanTagOffset gamedata");
   590 		}
   591 
   592 		tagOffset = *(int *)((intptr_t)addr + tagOffsetOffset);
   593 	}
   594 
   595 	size_t len;
   596 
   597 	const char *src = (char *)((intptr_t)pEntity + tagOffset);
   598 	pContext->StringToLocalUTF8(params[2], params[3], src, &len);
   599 
   600 	return len;
   601 }
   602 
   603 static cell_t CS_SetClientClanTag(IPluginContext *pContext, const cell_t *params)
   604 {
   605 	static ICallWrapper *pWrapper = NULL;
   606 
   607 	if (!pWrapper)
   608 	{
   609 		REGISTER_NATIVE_ADDR("SetClanTag",
   610 			PassInfo pass[2]; \
   611 			pass[0].flags = PASSFLAG_BYVAL; \
   612 			pass[0].type  = PassType_Basic; \
   613 			pass[0].size  = sizeof(CBaseEntity *); \
   614 			pass[1].flags = PASSFLAG_BYVAL; \
   615 			pass[1].type  = PassType_Basic; \
   616 			pass[1].size  = sizeof(char *); \
   617 			pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 2))
   618 	}
   619 
   620 	CBaseEntity *pEntity;
   621 	if (!(pEntity = GetCBaseEntity(params[1], true)))
   622 	{
   623 		return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
   624 	}
   625 
   626 	char *szNewTag;
   627 	pContext->LocalToString(params[2], &szNewTag);
   628 
   629 	unsigned char vstk[sizeof(CBaseEntity *) + sizeof(char *)];
   630 	unsigned char *vptr = vstk;
   631 
   632 	*(CBaseEntity **)vptr = pEntity;
   633 	vptr += sizeof(CBaseEntity *);
   634 	*(char **)vptr = szNewTag;
   635 
   636 	pWrapper->Execute(vstk, NULL);
   637 
   638 	return 1;
   639 }
   640 
   641 static cell_t CS_AliasToWeaponID(IPluginContext *pContext, const cell_t *params)
   642 {
   643 	char *weapon;
   644 
   645 	pContext->LocalToString(params[1], &weapon);
   646 
   647 #if SOURCE_ENGINE == SE_CSGO
   648 	if (strstr(weapon, "usp_silencer") != NULL)
   649 	{
   650 		return SMCSWeapon_HKP2000;
   651 	}
   652 	else if(strstr(weapon, "cz75a") != NULL)
   653 	{
   654 		return SMCSWeapon_P250;
   655 	}
   656 #endif
   657 
   658 	int id = GetFakeWeaponID(AliasToWeaponID(weapon));
   659 
   660 	if (!IsValidWeaponID(id))
   661 		return SMCSWeapon_NONE;
   662 
   663 	return id;
   664 }
   665 
   666 static cell_t CS_SetTeamScore(IPluginContext *pContext, const cell_t *params)
   667 {
   668 	if (g_pSDKTools == NULL)
   669 	{
   670 		smutils->LogError(myself, "SDKTools interface not found. CS_SetTeamScore native disabled.");
   671 	}
   672 	else if (g_pSDKTools->GetInterfaceVersion() < 2)
   673 	{
   674 		//<psychonic> THIS ISN'T DA LIMBO STICK. LOW IS BAD
   675 		return pContext->ThrowNativeError("SDKTools interface is outdated. CS_SetTeamScore native disabled.");
   676 	}
   677 
   678 	static void *addr;
   679 	if (!addr)
   680 	{
   681 		if (!g_pGameConf->GetMemSig("CheckWinLimit", &addr) || !addr)
   682 		{
   683 			return pContext->ThrowNativeError("Failed to locate CheckWinLimit function");
   684 		}
   685 	}
   686 
   687 	static int ctTeamOffsetOffset = -1;
   688 	static int ctTeamOffset;
   689 
   690 	static int tTeamOffsetOffset = -1;
   691 	static int tTeamOffset;
   692 
   693 	if (ctTeamOffsetOffset == -1)
   694 	{
   695 		if (!g_pGameConf->GetOffset("CTTeamScoreOffset", &ctTeamOffsetOffset))
   696 		{
   697 			ctTeamOffsetOffset = -1;
   698 			return pContext->ThrowNativeError("Unable to find CTTeamOffset gamedata");
   699 		}
   700 
   701 		ctTeamOffset = *(int *)((intptr_t)addr + ctTeamOffsetOffset);
   702 	}
   703 
   704 	if (tTeamOffsetOffset == -1)
   705 	{
   706 		if (!g_pGameConf->GetOffset("TTeamScoreOffset", &tTeamOffsetOffset))
   707 		{
   708 			tTeamOffsetOffset = -1;
   709 			return pContext->ThrowNativeError("Unable to find CTTeamOffset gamedata");
   710 		}
   711 
   712 		tTeamOffset = *(int *)((intptr_t)addr + tTeamOffsetOffset);
   713 	}
   714 
   715 	void *gamerules = g_pSDKTools->GetGameRules();
   716 	if (gamerules == NULL)
   717 	{
   718 		return pContext->ThrowNativeError("GameRules not available. CS_SetTeamScore native disabled.");
   719 	}
   720 
   721 	if (params[1] == 3)
   722 	{
   723 		*(int16_t *)((intptr_t)gamerules+ctTeamOffset) = params[2];
   724 		return 1;
   725 	}
   726 	else if (params[1] == 2)
   727 	{
   728 		*(int16_t *)((intptr_t)gamerules+tTeamOffset) = params[2];
   729 		return 1;
   730 	}
   731 	return pContext->ThrowNativeError("Invalid team index passed (%i).", params[1]);
   732 }
   733 
   734 static cell_t CS_GetTeamScore(IPluginContext *pContext, const cell_t *params)
   735 {
   736 	if (g_pSDKTools == NULL)
   737 	{
   738 		smutils->LogError(myself, "SDKTools interface not found. CS_GetTeamScore native disabled.");
   739 	}
   740 	else if (g_pSDKTools->GetInterfaceVersion() < 2)
   741 	{
   742 		//<psychonic> THIS ISN'T DA LIMBO STICK. LOW IS BAD
   743 		return pContext->ThrowNativeError("SDKTools interface is outdated. CS_GetTeamScore native disabled.");
   744 	}
   745 
   746 	static void *addr;
   747 	if (!addr)
   748 	{
   749 		if (!g_pGameConf->GetMemSig("CheckWinLimit", &addr) || !addr)
   750 		{
   751 			return pContext->ThrowNativeError("Failed to locate CheckWinLimit function");
   752 		}
   753 	}
   754 
   755 	static int ctTeamOffsetOffset = -1;
   756 	static int ctTeamOffset;
   757 
   758 	static int tTeamOffsetOffset = -1;
   759 	static int tTeamOffset;
   760 
   761 	if (ctTeamOffsetOffset == -1)
   762 	{
   763 		if (!g_pGameConf->GetOffset("CTTeamScoreOffset", &ctTeamOffsetOffset))
   764 		{
   765 			ctTeamOffsetOffset = -1;
   766 			return pContext->ThrowNativeError("Unable to find CTTeamOffset gamedata");
   767 		}
   768 
   769 		ctTeamOffset = *(int *)((intptr_t)addr + ctTeamOffsetOffset);
   770 	}
   771 
   772 	if (tTeamOffsetOffset == -1)
   773 	{
   774 		if (!g_pGameConf->GetOffset("TTeamScoreOffset", &tTeamOffsetOffset))
   775 		{
   776 			tTeamOffsetOffset = -1;
   777 			return pContext->ThrowNativeError("Unable to find CTTeamOffset gamedata");
   778 		}
   779 
   780 		tTeamOffset = *(int *)((intptr_t)addr + tTeamOffsetOffset);
   781 	}
   782 
   783 	void *gamerules = g_pSDKTools->GetGameRules();
   784 	if (gamerules == NULL)
   785 	{
   786 		return pContext->ThrowNativeError("GameRules not available. CS_GetTeamScore native disabled.");
   787 	}
   788 
   789 	if (params[1] == 3)
   790 	{
   791 		return *(int16_t *)((intptr_t)gamerules+ctTeamOffset);
   792 	}
   793 	else if (params[1] == 2)
   794 	{
   795 		return *(int16_t *)((intptr_t)gamerules+tTeamOffset);
   796 	}
   797 	return pContext->ThrowNativeError("Invalid team index passed (%i).", params[1]);
   798 }
   799 
   800 static cell_t CS_IsValidWeaponID(IPluginContext *pContext, const cell_t *params)
   801 {
   802 	return IsValidWeaponID(params[1]) ? 1 : 0;
   803 }
   804 
   805 template <typename T>
   806 static inline T *GetPlayerVarAddressOrError(const char *pszGamedataName, IPluginContext *pContext, CBaseEntity *pPlayerEntity)
   807 {
   808 	char szBaseName[128];
   809 	g_pSM->Format(szBaseName, sizeof(szBaseName), "%sBase", pszGamedataName);
   810 
   811 	const char *pszBaseVar = g_pGameConf->GetKeyValue(szBaseName);
   812 	if (pszBaseVar == NULL)
   813 	{
   814 		pContext->ThrowNativeError("Failed to locate %s key in gamedata", szBaseName);
   815 		return NULL;
   816 	}
   817 
   818 	int interimOffset = 0;
   819 	sm_sendprop_info_t info;
   820 	if (gamehelpers->FindSendPropInfo("CCSPlayer", pszBaseVar, &info))
   821 	{
   822 		interimOffset = info.actual_offset;
   823 	}
   824 	else
   825 	{
   826 		datamap_t *pMap = gamehelpers->GetDataMap(pPlayerEntity);
   827 		typedescription_t *td = gamehelpers->FindInDataMap(pMap, pszBaseVar);
   828 		if (td)
   829 		{
   830 #if SOURCE_ENGINE >= SE_LEFT4DEAD
   831 			interimOffset = td->fieldOffset;
   832 #else
   833 			interimOffset = td->fieldOffset[TD_OFFSET_NORMAL];
   834 #endif
   835 		}
   836 	}
   837 
   838 	if (interimOffset == 0)
   839 	{
   840 		pContext->ThrowNativeError("Failed to find property \"%s\" on player.", pszBaseVar);
   841 		return NULL;
   842 	}
   843 		
   844 
   845 	int tempOffset;
   846 	if (!g_pGameConf->GetOffset(pszGamedataName, &tempOffset))
   847 	{
   848 		pContext->ThrowNativeError("Failed to locate %s offset in gamedata", pszGamedataName);
   849 		return NULL;
   850 	}
   851 
   852 	return (T *)((intptr_t)pPlayerEntity + interimOffset + tempOffset);
   853 }
   854 
   855 template <typename T>
   856 static inline cell_t GetPlayerVar(IPluginContext *pContext, const cell_t *params, const char *varName)
   857 {
   858 	CBaseEntity *pPlayer = GetCBaseEntity(params[1], true);
   859 	if (!pPlayer)
   860 	{
   861 		return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
   862 	}
   863 
   864 	T *pVar = GetPlayerVarAddressOrError<T>(varName, pContext, pPlayer);
   865 	if (pVar)
   866 	{	
   867 		return (cell_t)*pVar;
   868 	}
   869 
   870 	return 0;
   871 }
   872 
   873 template <typename T>
   874 static inline cell_t SetPlayerVar(IPluginContext *pContext, const cell_t *params, const char *varName)
   875 {
   876 	CBaseEntity *pPlayer = GetCBaseEntity(params[1], true);
   877 	if (!pPlayer)
   878 	{
   879 		return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
   880 	}
   881 
   882 	T *pVar = GetPlayerVarAddressOrError<T>(varName, pContext, pPlayer);
   883 	if (pVar)
   884 	{
   885 		*pVar = (T)params[2];
   886 	}
   887 
   888 	return 0;
   889 }
   890 
   891 static cell_t CS_SetMVPCount(IPluginContext *pContext, const cell_t *params)
   892 {
   893 	return SetPlayerVar<int>(pContext, params, "MVPs");
   894 }
   895 
   896 static cell_t CS_GetMVPCount(IPluginContext *pContext, const cell_t *params)
   897 {
   898 	return GetPlayerVar<int>(pContext, params, "MVPs");
   899 }
   900 
   901 static cell_t CS_SetClientContributionScore(IPluginContext *pContext, const cell_t *params)
   902 {
   903 #if SOURCE_ENGINE == SE_CSGO
   904 	return SetPlayerVar<int>(pContext, params, "CScore");
   905 #else
   906 	return pContext->ThrowNativeError("SetClientContributionScore is not supported on this game");
   907 #endif
   908 }
   909 
   910 static cell_t CS_GetClientContributionScore(IPluginContext *pContext, const cell_t *params)
   911 {
   912 #if SOURCE_ENGINE == SE_CSGO
   913 	return GetPlayerVar<int>(pContext, params, "CScore");
   914 #else
   915 	return pContext->ThrowNativeError("GetClientContributionScore is not supported on this game");
   916 #endif
   917 }
   918 
   919 static cell_t CS_SetClientAssists(IPluginContext *pContext, const cell_t *params)
   920 {
   921 #if SOURCE_ENGINE == SE_CSGO
   922 	return SetPlayerVar<int>(pContext, params, "Assists");
   923 #else
   924 	return pContext->ThrowNativeError("SetClientAssists is not supported on this game");
   925 #endif
   926 }
   927 
   928 static cell_t CS_GetClientAssists(IPluginContext *pContext, const cell_t *params)
   929 {
   930 #if SOURCE_ENGINE == SE_CSGO
   931 	return GetPlayerVar<int>(pContext, params, "Assists");
   932 #else
   933 	return pContext->ThrowNativeError("GetClientAssists is not supported on this game");
   934 #endif
   935 }
   936 
   937 static cell_t CS_UpdateClientModel(IPluginContext *pContext, const cell_t *params)
   938 {
   939 	static ICallWrapper *pWrapper = NULL;
   940 	if (!pWrapper)
   941 	{
   942 		REGISTER_NATIVE_ADDR("SetModelFromClass", 
   943 			pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, NULL, 0));
   944 	}
   945 
   946 	CBaseEntity *pEntity;
   947 	if (!(pEntity=GetCBaseEntity(params[1], true)))
   948 	{
   949 		return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
   950 	}
   951 
   952 	pWrapper->Execute(&pEntity, NULL);
   953 
   954 	return 1;
   955 }
   956 sp_nativeinfo_t g_CSNatives[] = 
   957 {
   958 	{"CS_RespawnPlayer",			CS_RespawnPlayer}, 
   959 	{"CS_SwitchTeam",				CS_SwitchTeam},
   960 	{"CS_DropWeapon",				CS_DropWeapon},
   961 	{"CS_TerminateRound",			CS_TerminateRound},
   962 	{"CS_GetTranslatedWeaponAlias",	CS_GetTranslatedWeaponAlias},
   963 	{"CS_GetWeaponPrice",			CS_GetWeaponPrice},
   964 	{"CS_GetClientClanTag",			CS_GetClientClanTag},
   965 	{"CS_SetClientClanTag",			CS_SetClientClanTag},
   966 	{"CS_AliasToWeaponID",			CS_AliasToWeaponID},
   967 	{"CS_GetTeamScore",				CS_GetTeamScore},
   968 	{"CS_SetTeamScore",				CS_SetTeamScore},
   969 	{"CS_GetMVPCount",				CS_GetMVPCount},
   970 	{"CS_SetMVPCount",				CS_SetMVPCount},
   971 	{"CS_WeaponIDToAlias",			CS_WeaponIDToAlias},
   972 	{"CS_GetClientContributionScore",	CS_GetClientContributionScore},
   973 	{"CS_SetClientContributionScore",	CS_SetClientContributionScore},
   974 	{"CS_GetClientAssists",			CS_GetClientAssists},
   975 	{"CS_SetClientAssists",			CS_SetClientAssists},
   976 	{"CS_UpdateClientModel",		CS_UpdateClientModel},
   977 	{"CS_IsValidWeaponID",			CS_IsValidWeaponID},
   978 	{NULL,							NULL}
   979 };
   980