Add a number of useful forwards and natives to the cstrike extension (bug 4732, r=fyren).
This commit is contained in:
parent
177cc87985
commit
e0f670499c
@ -15,7 +15,10 @@ if AMBuild.target['platform'] in sdk['platform']:
|
|||||||
'natives.cpp',
|
'natives.cpp',
|
||||||
'RegNatives.cpp',
|
'RegNatives.cpp',
|
||||||
'timeleft.cpp',
|
'timeleft.cpp',
|
||||||
'sdk/smsdk_ext.cpp'
|
'forwards.cpp',
|
||||||
|
'sdk/smsdk_ext.cpp',
|
||||||
|
'CDetour/detours.cpp',
|
||||||
|
'asm/asm.c'
|
||||||
])
|
])
|
||||||
SM.PostSetupHL2Job(extension, binary, 'ep2v')
|
SM.PostSetupHL2Job(extension, binary, 'ep2v')
|
||||||
SM.AutoVersion('extensions/cstrike', binary)
|
SM.AutoVersion('extensions/cstrike', binary)
|
||||||
|
98
extensions/cstrike/CDetour/detourhelpers.h
Normal file
98
extensions/cstrike/CDetour/detourhelpers.h
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/**
|
||||||
|
* vim: set ts=4 :
|
||||||
|
* =============================================================================
|
||||||
|
* SourceMod
|
||||||
|
* Copyright (C) 2004-2007 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 <http://www.sourcemod.net/license.php>.
|
||||||
|
*
|
||||||
|
* Version: $Id: detourhelpers.h 248 2008-08-27 00:56:22Z pred $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _INCLUDE_SOURCEMOD_DETOURHELPERS_H_
|
||||||
|
#define _INCLUDE_SOURCEMOD_DETOURHELPERS_H_
|
||||||
|
|
||||||
|
#if defined PLATFORM_POSIX
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#define PAGE_SIZE 4096
|
||||||
|
#define ALIGN(ar) ((long)ar & ~(PAGE_SIZE-1))
|
||||||
|
#define PAGE_EXECUTE_READWRITE PROT_READ|PROT_WRITE|PROT_EXEC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct patch_t
|
||||||
|
{
|
||||||
|
patch_t()
|
||||||
|
{
|
||||||
|
patch[0] = 0;
|
||||||
|
bytes = 0;
|
||||||
|
}
|
||||||
|
unsigned char patch[20];
|
||||||
|
size_t bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void ProtectMemory(void *addr, int length, int prot)
|
||||||
|
{
|
||||||
|
#if defined PLATFORM_POSIX
|
||||||
|
void *addr2 = (void *)ALIGN(addr);
|
||||||
|
mprotect(addr2, sysconf(_SC_PAGESIZE), prot);
|
||||||
|
#elif defined PLATFORM_WINDOWS
|
||||||
|
DWORD old_prot;
|
||||||
|
VirtualProtect(addr, length, prot, &old_prot);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void SetMemPatchable(void *address, size_t size)
|
||||||
|
{
|
||||||
|
ProtectMemory(address, (int)size, PAGE_EXECUTE_READWRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void DoGatePatch(unsigned char *target, void *callback)
|
||||||
|
{
|
||||||
|
SetMemPatchable(target, 20);
|
||||||
|
|
||||||
|
target[0] = 0xFF; /* JMP */
|
||||||
|
target[1] = 0x25; /* MEM32 */
|
||||||
|
*(void **)(&target[2]) = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ApplyPatch(void *address, int offset, const patch_t *patch, patch_t *restore)
|
||||||
|
{
|
||||||
|
ProtectMemory(address, 20, PAGE_EXECUTE_READWRITE);
|
||||||
|
|
||||||
|
unsigned char *addr = (unsigned char *)address + offset;
|
||||||
|
if (restore)
|
||||||
|
{
|
||||||
|
for (size_t i=0; i<patch->bytes; i++)
|
||||||
|
{
|
||||||
|
restore->patch[i] = addr[i];
|
||||||
|
}
|
||||||
|
restore->bytes = patch->bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i=0; i<patch->bytes; i++)
|
||||||
|
{
|
||||||
|
addr[i] = patch->patch[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //_INCLUDE_SOURCEMOD_DETOURHELPERS_H_
|
192
extensions/cstrike/CDetour/detours.cpp
Normal file
192
extensions/cstrike/CDetour/detours.cpp
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
/**
|
||||||
|
* vim: set ts=4 :
|
||||||
|
* =============================================================================
|
||||||
|
* SourceMod
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 <http://www.sourcemod.net/license.php>.
|
||||||
|
*
|
||||||
|
* Version: $Id: detours.cpp 248 2008-08-27 00:56:22Z pred $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "detours.h"
|
||||||
|
#include <asm/asm.h>
|
||||||
|
|
||||||
|
ISourcePawnEngine *CDetourManager::spengine = NULL;
|
||||||
|
IGameConfig *CDetourManager::gameconf = NULL;
|
||||||
|
|
||||||
|
void CDetourManager::Init(ISourcePawnEngine *spengine, IGameConfig *gameconf)
|
||||||
|
{
|
||||||
|
CDetourManager::spengine = spengine;
|
||||||
|
CDetourManager::gameconf = gameconf;
|
||||||
|
}
|
||||||
|
|
||||||
|
CDetour *CDetourManager::CreateDetour(void *callbackfunction, void **trampoline, const char *signame)
|
||||||
|
{
|
||||||
|
CDetour *detour = new CDetour(callbackfunction, trampoline, signame);
|
||||||
|
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;
|
||||||
|
detoured = false;
|
||||||
|
detour_address = NULL;
|
||||||
|
detour_trampoline = NULL;
|
||||||
|
this->signame = signame;
|
||||||
|
this->detour_callback = callbackfunction;
|
||||||
|
spengine = NULL;
|
||||||
|
gameconf = NULL;
|
||||||
|
this->trampoline = trampoline;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDetour::Init(ISourcePawnEngine *spengine, IGameConfig *gameconf)
|
||||||
|
{
|
||||||
|
this->spengine = spengine;
|
||||||
|
this->gameconf = gameconf;
|
||||||
|
|
||||||
|
if (!CreateDetour())
|
||||||
|
{
|
||||||
|
enabled = false;
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
enabled = true;
|
||||||
|
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDetour::Destroy()
|
||||||
|
{
|
||||||
|
DeleteDetour();
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDetour::IsEnabled()
|
||||||
|
{
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDetour::CreateDetour()
|
||||||
|
{
|
||||||
|
if (!gameconf->GetMemSig(signame, &detour_address))
|
||||||
|
{
|
||||||
|
g_pSM->LogError(myself, "Could not locate %s - Disabling detour", signame);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!detour_address)
|
||||||
|
{
|
||||||
|
g_pSM->LogError(myself, "Sigscan for %s failed - Disabling detour to prevent crashes", signame);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
detour_restore.bytes = copy_bytes((unsigned char *)detour_address, NULL, OP_JMP_SIZE+1);
|
||||||
|
|
||||||
|
/* First, save restore bits */
|
||||||
|
for (size_t i=0; i<detour_restore.bytes; i++)
|
||||||
|
{
|
||||||
|
detour_restore.patch[i] = ((unsigned char *)detour_address)[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
JitWriter wr;
|
||||||
|
JitWriter *jit = ≀
|
||||||
|
jit_uint32_t CodeSize = 0;
|
||||||
|
|
||||||
|
wr.outbase = NULL;
|
||||||
|
wr.outptr = NULL;
|
||||||
|
|
||||||
|
jit_rewind:
|
||||||
|
|
||||||
|
/* Patch old bytes in */
|
||||||
|
if (wr.outbase != NULL)
|
||||||
|
{
|
||||||
|
copy_bytes((unsigned char *)detour_address, (unsigned char*)wr.outptr, detour_restore.bytes);
|
||||||
|
}
|
||||||
|
wr.outptr += detour_restore.bytes;
|
||||||
|
|
||||||
|
/* Return to the original function */
|
||||||
|
jitoffs_t call = IA32_Jump_Imm32(jit, 0);
|
||||||
|
IA32_Write_Jump32_Abs(jit, call, (unsigned char *)detour_address + detour_restore.bytes);
|
||||||
|
|
||||||
|
if (wr.outbase == NULL)
|
||||||
|
{
|
||||||
|
CodeSize = wr.get_outputpos();
|
||||||
|
wr.outbase = (jitcode_t)spengine->AllocatePageMemory(CodeSize);
|
||||||
|
spengine->SetReadWrite(wr.outbase);
|
||||||
|
wr.outptr = wr.outbase;
|
||||||
|
detour_trampoline = wr.outbase;
|
||||||
|
goto jit_rewind;
|
||||||
|
}
|
||||||
|
|
||||||
|
spengine->SetReadExecute(wr.outbase);
|
||||||
|
|
||||||
|
*trampoline = detour_trampoline;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDetour::DeleteDetour()
|
||||||
|
{
|
||||||
|
if (detoured)
|
||||||
|
{
|
||||||
|
DisableDetour();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (detour_trampoline)
|
||||||
|
{
|
||||||
|
/* Free the allocated trampoline memory */
|
||||||
|
spengine->FreePageMemory(detour_trampoline);
|
||||||
|
detour_trampoline = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDetour::EnableDetour()
|
||||||
|
{
|
||||||
|
if (!detoured)
|
||||||
|
{
|
||||||
|
DoGatePatch((unsigned char *)detour_address, &detour_callback);
|
||||||
|
detoured = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDetour::DisableDetour()
|
||||||
|
{
|
||||||
|
if (detoured)
|
||||||
|
{
|
||||||
|
/* Remove the patch */
|
||||||
|
ApplyPatch(detour_address, 0, &detour_restore, NULL);
|
||||||
|
detoured = false;
|
||||||
|
}
|
||||||
|
}
|
241
extensions/cstrike/CDetour/detours.h
Normal file
241
extensions/cstrike/CDetour/detours.h
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
/**
|
||||||
|
* vim: set ts=4 :
|
||||||
|
* =============================================================================
|
||||||
|
* SourceMod
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 <http://www.sourcemod.net/license.php>.
|
||||||
|
*
|
||||||
|
* Version: $Id: detours.h 257 2008-09-23 03:12:13Z pred $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _INCLUDE_SOURCEMOD_DETOURS_H_
|
||||||
|
#define _INCLUDE_SOURCEMOD_DETOURS_H_
|
||||||
|
|
||||||
|
#include "extension.h"
|
||||||
|
#include <jit/jit_helpers.h>
|
||||||
|
#include <jit/x86/x86_macros.h>
|
||||||
|
#include "detourhelpers.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CDetours class for SourceMod Extensions by pRED*
|
||||||
|
* detourhelpers.h entirely stolen from CSS:DM and were written by BAILOPAN (I assume).
|
||||||
|
* asm.h/c from devmaster.net (thanks cybermind) edited by pRED* to handle gcc -fPIC thunks correctly
|
||||||
|
* Concept by Nephyrin Zey (http://www.doublezen.net/) and Windows Detour Library (http://research.microsoft.com/sn/detours/)
|
||||||
|
* Member function pointer ideas by Don Clugston (http://www.codeproject.com/cpp/FastDelegate.asp)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DETOUR_MEMBER_CALL(name) (this->*name##_Actual)
|
||||||
|
#define DETOUR_STATIC_CALL(name) (name##_Actual)
|
||||||
|
|
||||||
|
#define DETOUR_DECL_STATIC0(name, ret) \
|
||||||
|
ret (*name##_Actual)(void) = NULL; \
|
||||||
|
ret name(void)
|
||||||
|
|
||||||
|
#define DETOUR_DECL_STATIC1(name, ret, p1type, p1name) \
|
||||||
|
ret (*name##_Actual)(p1type) = NULL; \
|
||||||
|
ret name(p1type p1name)
|
||||||
|
|
||||||
|
#define DETOUR_DECL_STATIC4(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name) \
|
||||||
|
ret (*name##_Actual)(p1type, p2type, p3type, p4type) = NULL; \
|
||||||
|
ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name)
|
||||||
|
|
||||||
|
#define DETOUR_DECL_MEMBER0(name, ret) \
|
||||||
|
class name##Class \
|
||||||
|
{ \
|
||||||
|
public: \
|
||||||
|
ret name(); \
|
||||||
|
static ret (name##Class::* name##_Actual)(void); \
|
||||||
|
}; \
|
||||||
|
ret (name##Class::* name##Class::name##_Actual)(void) = NULL; \
|
||||||
|
ret name##Class::name()
|
||||||
|
|
||||||
|
#define DETOUR_DECL_MEMBER1(name, ret, p1type, p1name) \
|
||||||
|
class name##Class \
|
||||||
|
{ \
|
||||||
|
public: \
|
||||||
|
ret name(p1type p1name); \
|
||||||
|
static ret (name##Class::* name##_Actual)(p1type); \
|
||||||
|
}; \
|
||||||
|
ret (name##Class::* name##Class::name##_Actual)(p1type) = NULL; \
|
||||||
|
ret name##Class::name(p1type p1name)
|
||||||
|
|
||||||
|
#define DETOUR_DECL_MEMBER2(name, ret, p1type, p1name, p2type, p2name) \
|
||||||
|
class name##Class \
|
||||||
|
{ \
|
||||||
|
public: \
|
||||||
|
ret name(p1type p1name, p2type p2name); \
|
||||||
|
static ret (name##Class::* name##_Actual)(p1type, p2type); \
|
||||||
|
}; \
|
||||||
|
ret (name##Class::* name##Class::name##_Actual)(p1type, p2type) = NULL; \
|
||||||
|
ret name##Class::name(p1type p1name, p2type p2name)
|
||||||
|
|
||||||
|
#define DETOUR_DECL_MEMBER3(name, ret, p1type, p1name, p2type, p2name, p3type, p3name) \
|
||||||
|
class name##Class \
|
||||||
|
{ \
|
||||||
|
public: \
|
||||||
|
ret name(p1type p1name, p2type p2name, p3type p3name); \
|
||||||
|
static ret (name##Class::* name##_Actual)(p1type, p2type, p3type); \
|
||||||
|
}; \
|
||||||
|
ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type) = NULL; \
|
||||||
|
ret name##Class::name(p1type p1name, p2type p2name, p3type p3name)
|
||||||
|
|
||||||
|
#define DETOUR_DECL_MEMBER4(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name) \
|
||||||
|
class name##Class \
|
||||||
|
{ \
|
||||||
|
public: \
|
||||||
|
ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name); \
|
||||||
|
static ret (name##Class::* name##_Actual)(p1type, p2type, p3type, p4type); \
|
||||||
|
}; \
|
||||||
|
ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type) = NULL; \
|
||||||
|
ret name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name)
|
||||||
|
|
||||||
|
|
||||||
|
#define GET_MEMBER_CALLBACK(name) (void *)GetCodeAddress(&name##Class::name)
|
||||||
|
#define GET_MEMBER_TRAMPOLINE(name) (void **)(&name##Class::name##_Actual)
|
||||||
|
|
||||||
|
#define GET_STATIC_CALLBACK(name) (void *)&name
|
||||||
|
#define GET_STATIC_TRAMPOLINE(name) (void **)&name##_Actual
|
||||||
|
|
||||||
|
#define DETOUR_CREATE_MEMBER(name, gamedata) CDetourManager::CreateDetour(GET_MEMBER_CALLBACK(name), GET_MEMBER_TRAMPOLINE(name), gamedata);
|
||||||
|
#define DETOUR_CREATE_STATIC(name, gamedata) CDetourManager::CreateDetour(GET_STATIC_CALLBACK(name), GET_STATIC_TRAMPOLINE(name), gamedata);
|
||||||
|
|
||||||
|
|
||||||
|
class GenericClass {};
|
||||||
|
typedef void (GenericClass::*VoidFunc)();
|
||||||
|
|
||||||
|
inline void *GetCodeAddr(VoidFunc mfp)
|
||||||
|
{
|
||||||
|
return *(void **)&mfp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a member function pointer to a void pointer.
|
||||||
|
* This relies on the assumption that the code address lies at mfp+0
|
||||||
|
* This is the case for both g++ and later MSVC versions on non virtual functions but may be different for other compilers
|
||||||
|
* Based on research by Don Clugston : http://www.codeproject.com/cpp/FastDelegate.asp
|
||||||
|
*/
|
||||||
|
#define GetCodeAddress(mfp) GetCodeAddr(reinterpret_cast<VoidFunc>(mfp))
|
||||||
|
|
||||||
|
class CDetourManager;
|
||||||
|
|
||||||
|
class CDetour
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
bool IsEnabled();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These would be somewhat self-explanatory I hope
|
||||||
|
*/
|
||||||
|
void EnableDetour();
|
||||||
|
void DisableDetour();
|
||||||
|
|
||||||
|
void Destroy();
|
||||||
|
|
||||||
|
friend class CDetourManager;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CDetour(void *callbackfunction, void **trampoline, const char *signame);
|
||||||
|
|
||||||
|
bool Init(ISourcePawnEngine *spengine, IGameConfig *gameconf);
|
||||||
|
private:
|
||||||
|
|
||||||
|
/* These create/delete the allocated memory */
|
||||||
|
bool CreateDetour();
|
||||||
|
void DeleteDetour();
|
||||||
|
|
||||||
|
bool enabled;
|
||||||
|
bool detoured;
|
||||||
|
|
||||||
|
patch_t detour_restore;
|
||||||
|
/* Address of the detoured function */
|
||||||
|
void *detour_address;
|
||||||
|
/* Address of the allocated trampoline function */
|
||||||
|
void *detour_trampoline;
|
||||||
|
/* Address of the callback handler */
|
||||||
|
void *detour_callback;
|
||||||
|
/* The function pointer used to call our trampoline */
|
||||||
|
void **trampoline;
|
||||||
|
|
||||||
|
const char *signame;
|
||||||
|
ISourcePawnEngine *spengine;
|
||||||
|
IGameConfig *gameconf;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CDetourManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
static void Init(ISourcePawnEngine *spengine, IGameConfig *gameconf);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new detour
|
||||||
|
*
|
||||||
|
* @param callbackfunction Void pointer to your detour callback function.
|
||||||
|
* @param trampoline Address of the trampoline pointer
|
||||||
|
* @param signame Section name containing a signature to fetch from the gamedata file.
|
||||||
|
* @return A new CDetour pointer to control your detour.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* CBaseServer::ConnectClient(netadr_s &, int, int, int, char const*, char const*, char const*, int)
|
||||||
|
*
|
||||||
|
* Define a new class with the required function and a member function pointer to the same type:
|
||||||
|
*
|
||||||
|
* class CBaseServerDetour
|
||||||
|
* {
|
||||||
|
* public:
|
||||||
|
* bool ConnectClient(void *netaddr_s, int, int, int, char const*, char const*, char const*, int);
|
||||||
|
* static bool (CBaseServerDetour::* ConnectClient_Actual)(void *netaddr_s, int, int, int, char const*, char const*, char const*, int);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void *callbackfunc = GetCodeAddress(&CBaseServerDetour::ConnectClient);
|
||||||
|
* void **trampoline = (void **)(&CBaseServerDetour::ConnectClient_Actual);
|
||||||
|
*
|
||||||
|
* Creation:
|
||||||
|
* CDetourManager::CreateDetour(callbackfunc, trampoline, "ConnectClient");
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
*
|
||||||
|
* CBaseServerDetour::ConnectClient(void *netaddr_s, int, int, int, char const*, char const*, char const*, int)
|
||||||
|
* {
|
||||||
|
* //pre hook code
|
||||||
|
* bool result = (this->*ConnectClient_Actual)(netaddr_s, rest of params);
|
||||||
|
* //post hook code
|
||||||
|
* return result;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
friend class CBlocker;
|
||||||
|
friend class CDetour;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static ISourcePawnEngine *spengine;
|
||||||
|
static IGameConfig *gameconf;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _INCLUDE_SOURCEMOD_DETOURS_H_
|
@ -18,7 +18,7 @@ PROJECT = game.cstrike
|
|||||||
#Uncomment for Metamod: Source enabled extension
|
#Uncomment for Metamod: Source enabled extension
|
||||||
USEMETA = true
|
USEMETA = true
|
||||||
|
|
||||||
OBJECTS = sdk/smsdk_ext.cpp extension.cpp natives.cpp RegNatives.cpp timeleft.cpp
|
OBJECTS = sdk/smsdk_ext.cpp extension.cpp natives.cpp RegNatives.cpp timeleft.cpp forwards.cpp CDetour/detours.cpp asm/asm.c
|
||||||
|
|
||||||
##############################################
|
##############################################
|
||||||
### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ###
|
### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ###
|
||||||
@ -156,7 +156,9 @@ $(BIN_DIR)/%.o: %.cpp
|
|||||||
$(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
|
$(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
|
||||||
|
|
||||||
all: check
|
all: check
|
||||||
mkdir -p $(BIN_DIR)/sdk
|
mkdir -p $(BIN_DIR)/sdk
|
||||||
|
mkdir -p $(BIN_DIR)/CDetour
|
||||||
|
mkdir -p $(BIN_DIR)/asm
|
||||||
if [ "$(USEMETA)" = "true" ]; then \
|
if [ "$(USEMETA)" = "true" ]; then \
|
||||||
ln -sf $(HL2LIB)/$(LIB_PREFIX)vstdlib$(LIB_SUFFIX); \
|
ln -sf $(HL2LIB)/$(LIB_PREFIX)vstdlib$(LIB_SUFFIX); \
|
||||||
ln -sf $(HL2LIB)/$(LIB_PREFIX)tier0$(LIB_SUFFIX); \
|
ln -sf $(HL2LIB)/$(LIB_PREFIX)tier0$(LIB_SUFFIX); \
|
||||||
|
421
extensions/cstrike/asm/asm.c
Normal file
421
extensions/cstrike/asm/asm.c
Normal file
@ -0,0 +1,421 @@
|
|||||||
|
#include "asm.h"
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define REG_EAX 0
|
||||||
|
#define REG_ECX 1
|
||||||
|
#define REG_EDX 2
|
||||||
|
#define REG_EBX 3
|
||||||
|
|
||||||
|
#define IA32_MOV_REG_IMM 0xB8 // encoding is +r <imm32>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern void Msg( const char *, ... );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a call to a fpic thunk has just been written into dest.
|
||||||
|
* If found replaces it with a direct mov that sets the required register to the value of pc.
|
||||||
|
*
|
||||||
|
* @param dest Destination buffer where a call opcode + addr (5 bytes) has just been written.
|
||||||
|
* @param pc The program counter value that needs to be set (usually the next address from the source).
|
||||||
|
* @noreturn
|
||||||
|
*/
|
||||||
|
void check_thunks(unsigned char *dest, unsigned char *pc)
|
||||||
|
{
|
||||||
|
#if defined WIN32
|
||||||
|
return;
|
||||||
|
#else
|
||||||
|
/* Step write address back 4 to the start of the function address */
|
||||||
|
unsigned char *writeaddr = dest - 4;
|
||||||
|
unsigned char *calloffset = *(unsigned char **)writeaddr;
|
||||||
|
unsigned char *calladdr = (unsigned char *)(dest + (unsigned int)calloffset);
|
||||||
|
|
||||||
|
/* Lookup name of function being called */
|
||||||
|
if ((*calladdr == 0x8B) && (*(calladdr+2) == 0x24) && (*(calladdr+3) == 0xC3))
|
||||||
|
{
|
||||||
|
//a thunk maybe?
|
||||||
|
char movByte = IA32_MOV_REG_IMM;
|
||||||
|
|
||||||
|
/* Calculate the correct mov opcode */
|
||||||
|
switch (*(calladdr+1))
|
||||||
|
{
|
||||||
|
case 0x04:
|
||||||
|
{
|
||||||
|
movByte += REG_EAX;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0x1C:
|
||||||
|
{
|
||||||
|
movByte += REG_EBX;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0x0C:
|
||||||
|
{
|
||||||
|
movByte += REG_ECX;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0x14:
|
||||||
|
{
|
||||||
|
movByte += REG_EDX;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
Msg("Unknown thunk: %c\n", *(calladdr+1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move our write address back one to where the call opcode was */
|
||||||
|
writeaddr--;
|
||||||
|
|
||||||
|
|
||||||
|
/* Write our mov */
|
||||||
|
*writeaddr = movByte;
|
||||||
|
writeaddr++;
|
||||||
|
|
||||||
|
/* Write the value - The provided program counter value */
|
||||||
|
*(void **)writeaddr = (void *)pc;
|
||||||
|
writeaddr += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//if dest is NULL, returns minimum number of bytes needed to be copied
|
||||||
|
//if dest is not NULL, it will copy the bytes to dest as well as fix CALLs and JMPs
|
||||||
|
//http://www.devmaster.net/forums/showthread.php?t=2311
|
||||||
|
int copy_bytes(unsigned char *func, unsigned char* dest, int required_len) {
|
||||||
|
int bytecount = 0;
|
||||||
|
|
||||||
|
while(bytecount < required_len && *func != 0xCC)
|
||||||
|
{
|
||||||
|
// prefixes F0h, F2h, F3h, 66h, 67h, D8h-DFh, 2Eh, 36h, 3Eh, 26h, 64h and 65h
|
||||||
|
int operandSize = 4;
|
||||||
|
int FPU = 0;
|
||||||
|
int twoByte = 0;
|
||||||
|
unsigned char opcode = 0x90;
|
||||||
|
unsigned char modRM = 0xFF;
|
||||||
|
while(*func == 0xF0 ||
|
||||||
|
*func == 0xF2 ||
|
||||||
|
*func == 0xF3 ||
|
||||||
|
(*func & 0xFC) == 0x64 ||
|
||||||
|
(*func & 0xF8) == 0xD8 ||
|
||||||
|
(*func & 0x7E) == 0x62)
|
||||||
|
{
|
||||||
|
if(*func == 0x66)
|
||||||
|
{
|
||||||
|
operandSize = 2;
|
||||||
|
}
|
||||||
|
else if((*func & 0xF8) == 0xD8)
|
||||||
|
{
|
||||||
|
FPU = *func;
|
||||||
|
if (dest)
|
||||||
|
*dest++ = *func++;
|
||||||
|
else
|
||||||
|
func++;
|
||||||
|
bytecount++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dest)
|
||||||
|
*dest++ = *func++;
|
||||||
|
else
|
||||||
|
func++;
|
||||||
|
bytecount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// two-byte opcode byte
|
||||||
|
if(*func == 0x0F)
|
||||||
|
{
|
||||||
|
twoByte = 1;
|
||||||
|
if (dest)
|
||||||
|
*dest++ = *func++;
|
||||||
|
else
|
||||||
|
func++;
|
||||||
|
bytecount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// opcode byte
|
||||||
|
opcode = *func++;
|
||||||
|
if (dest) *dest++ = opcode;
|
||||||
|
bytecount++;
|
||||||
|
|
||||||
|
// mod R/M byte
|
||||||
|
modRM = 0xFF;
|
||||||
|
if(FPU)
|
||||||
|
{
|
||||||
|
if((opcode & 0xC0) != 0xC0)
|
||||||
|
{
|
||||||
|
modRM = opcode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(!twoByte)
|
||||||
|
{
|
||||||
|
if((opcode & 0xC4) == 0x00 ||
|
||||||
|
((opcode & 0xF4) == 0x60 && ((opcode & 0x0A) == 0x02 || (opcode & 0x09) == 0x09)) ||
|
||||||
|
(opcode & 0xF0) == 0x80 ||
|
||||||
|
((opcode & 0xF8) == 0xC0 && (opcode & 0x0E) != 0x02) ||
|
||||||
|
(opcode & 0xFC) == 0xD0 ||
|
||||||
|
(opcode & 0xF6) == 0xF6)
|
||||||
|
{
|
||||||
|
modRM = *func++;
|
||||||
|
if (dest) *dest++ = modRM;
|
||||||
|
bytecount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(((opcode & 0xF0) == 0x00 && (opcode & 0x0F) >= 0x04 && (opcode & 0x0D) != 0x0D) ||
|
||||||
|
(opcode & 0xF0) == 0x30 ||
|
||||||
|
opcode == 0x77 ||
|
||||||
|
(opcode & 0xF0) == 0x80 ||
|
||||||
|
((opcode & 0xF0) == 0xA0 && (opcode & 0x07) <= 0x02) ||
|
||||||
|
(opcode & 0xF8) == 0xC8)
|
||||||
|
{
|
||||||
|
// No mod R/M byte
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
modRM = *func++;
|
||||||
|
if (dest) *dest++ = modRM;
|
||||||
|
bytecount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SIB
|
||||||
|
if((modRM & 0x07) == 0x04 &&
|
||||||
|
(modRM & 0xC0) != 0xC0)
|
||||||
|
{
|
||||||
|
if (dest)
|
||||||
|
*dest++ = *func++; //SIB
|
||||||
|
else
|
||||||
|
func++;
|
||||||
|
bytecount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mod R/M displacement
|
||||||
|
|
||||||
|
// Dword displacement, no base
|
||||||
|
if((modRM & 0xC5) == 0x05) {
|
||||||
|
if (dest) {
|
||||||
|
*(unsigned int*)dest = *(unsigned int*)func;
|
||||||
|
dest += 4;
|
||||||
|
}
|
||||||
|
func += 4;
|
||||||
|
bytecount += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Byte displacement
|
||||||
|
if((modRM & 0xC0) == 0x40) {
|
||||||
|
if (dest)
|
||||||
|
*dest++ = *func++;
|
||||||
|
else
|
||||||
|
func++;
|
||||||
|
bytecount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dword displacement
|
||||||
|
if((modRM & 0xC0) == 0x80) {
|
||||||
|
if (dest) {
|
||||||
|
*(unsigned int*)dest = *(unsigned int*)func;
|
||||||
|
dest += 4;
|
||||||
|
}
|
||||||
|
func += 4;
|
||||||
|
bytecount += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// immediate
|
||||||
|
if(FPU)
|
||||||
|
{
|
||||||
|
// Can't have immediate operand
|
||||||
|
}
|
||||||
|
else if(!twoByte)
|
||||||
|
{
|
||||||
|
if((opcode & 0xC7) == 0x04 ||
|
||||||
|
(opcode & 0xFE) == 0x6A || // PUSH/POP/IMUL
|
||||||
|
(opcode & 0xF0) == 0x70 || // Jcc
|
||||||
|
opcode == 0x80 ||
|
||||||
|
opcode == 0x83 ||
|
||||||
|
(opcode & 0xFD) == 0xA0 || // MOV
|
||||||
|
opcode == 0xA8 || // TEST
|
||||||
|
(opcode & 0xF8) == 0xB0 || // MOV
|
||||||
|
(opcode & 0xFE) == 0xC0 || // RCL
|
||||||
|
opcode == 0xC6 || // MOV
|
||||||
|
opcode == 0xCD || // INT
|
||||||
|
(opcode & 0xFE) == 0xD4 || // AAD/AAM
|
||||||
|
(opcode & 0xF8) == 0xE0 || // LOOP/JCXZ
|
||||||
|
opcode == 0xEB ||
|
||||||
|
(opcode == 0xF6 && (modRM & 0x30) == 0x00)) // TEST
|
||||||
|
{
|
||||||
|
if (dest)
|
||||||
|
*dest++ = *func++;
|
||||||
|
else
|
||||||
|
func++;
|
||||||
|
bytecount++;
|
||||||
|
}
|
||||||
|
else if((opcode & 0xF7) == 0xC2) // RET
|
||||||
|
{
|
||||||
|
if (dest) {
|
||||||
|
*(unsigned short*)dest = *(unsigned short*)func;
|
||||||
|
dest += 2;
|
||||||
|
}
|
||||||
|
func += 2;
|
||||||
|
bytecount += 2;
|
||||||
|
}
|
||||||
|
else if((opcode & 0xFC) == 0x80 ||
|
||||||
|
(opcode & 0xC7) == 0x05 ||
|
||||||
|
(opcode & 0xF8) == 0xB8 ||
|
||||||
|
(opcode & 0xFE) == 0xE8 || // CALL/Jcc
|
||||||
|
(opcode & 0xFE) == 0x68 ||
|
||||||
|
(opcode & 0xFC) == 0xA0 ||
|
||||||
|
(opcode & 0xEE) == 0xA8 ||
|
||||||
|
opcode == 0xC7 ||
|
||||||
|
(opcode == 0xF7 && (modRM & 0x30) == 0x00))
|
||||||
|
{
|
||||||
|
if (dest) {
|
||||||
|
//Fix CALL/JMP offset
|
||||||
|
if ((opcode & 0xFE) == 0xE8) {
|
||||||
|
if (operandSize == 4)
|
||||||
|
{
|
||||||
|
*(long*)dest = ((func + *(long*)func) - dest);
|
||||||
|
|
||||||
|
//pRED* edit. func is the current address of the call address, +4 is the next instruction, so the value of $pc
|
||||||
|
check_thunks(dest+4, func+4);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*(short*)dest = ((func + *(short*)func) - dest);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (operandSize == 4)
|
||||||
|
*(unsigned long*)dest = *(unsigned long*)func;
|
||||||
|
else
|
||||||
|
*(unsigned short*)dest = *(unsigned short*)func;
|
||||||
|
}
|
||||||
|
dest += operandSize;
|
||||||
|
}
|
||||||
|
func += operandSize;
|
||||||
|
bytecount += operandSize;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(opcode == 0xBA || // BT
|
||||||
|
opcode == 0x0F || // 3DNow!
|
||||||
|
(opcode & 0xFC) == 0x70 || // PSLLW
|
||||||
|
(opcode & 0xF7) == 0xA4 || // SHLD
|
||||||
|
opcode == 0xC2 ||
|
||||||
|
opcode == 0xC4 ||
|
||||||
|
opcode == 0xC5 ||
|
||||||
|
opcode == 0xC6)
|
||||||
|
{
|
||||||
|
if (dest)
|
||||||
|
*dest++ = *func++;
|
||||||
|
else
|
||||||
|
func++;
|
||||||
|
}
|
||||||
|
else if((opcode & 0xF0) == 0x80) // Jcc -i
|
||||||
|
{
|
||||||
|
if (dest) {
|
||||||
|
if (operandSize == 4)
|
||||||
|
*(unsigned long*)dest = *(unsigned long*)func;
|
||||||
|
else
|
||||||
|
*(unsigned short*)dest = *(unsigned short*)func;
|
||||||
|
|
||||||
|
dest += operandSize;
|
||||||
|
}
|
||||||
|
func += operandSize;
|
||||||
|
bytecount += operandSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytecount;
|
||||||
|
}
|
||||||
|
|
||||||
|
//insert a specific JMP instruction at the given location
|
||||||
|
void inject_jmp(void* src, void* dest) {
|
||||||
|
*(unsigned char*)src = OP_JMP;
|
||||||
|
*(long*)((unsigned char*)src+1) = (long)((unsigned char*)dest - ((unsigned char*)src + OP_JMP_SIZE));
|
||||||
|
}
|
||||||
|
|
||||||
|
//fill a given block with NOPs
|
||||||
|
void fill_nop(void* src, unsigned int len) {
|
||||||
|
unsigned char* src2 = (unsigned char*)src;
|
||||||
|
while (len) {
|
||||||
|
*src2++ = OP_NOP;
|
||||||
|
--len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* eval_jump(void* src) {
|
||||||
|
unsigned char* addr = (unsigned char*)src;
|
||||||
|
|
||||||
|
if (!addr) return 0;
|
||||||
|
|
||||||
|
//import table jump
|
||||||
|
if (addr[0] == OP_PREFIX && addr[1] == OP_JMP_SEG) {
|
||||||
|
addr += 2;
|
||||||
|
addr = *(unsigned char**)addr;
|
||||||
|
//TODO: if addr points into the IAT
|
||||||
|
return *(void**)addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//8bit offset
|
||||||
|
else if (addr[0] == OP_JMP_BYTE) {
|
||||||
|
addr = &addr[OP_JMP_BYTE_SIZE] + *(char*)&addr[1];
|
||||||
|
//mangled 32bit jump?
|
||||||
|
if (addr[0] == OP_JMP) {
|
||||||
|
addr = addr + *(int*)&addr[1];
|
||||||
|
}
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
//32bit offset
|
||||||
|
else if (addr[0] == OP_JMP) {
|
||||||
|
addr = &addr[OP_JMP_SIZE] + *(int*)&addr[1];
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
from ms detours package
|
||||||
|
static bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress)
|
||||||
|
{
|
||||||
|
MEMORY_BASIC_INFORMATION mbi;
|
||||||
|
VirtualQuery((PVOID)pbCode, &mbi, sizeof(mbi));
|
||||||
|
__try {
|
||||||
|
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase;
|
||||||
|
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
|
||||||
|
pDosHeader->e_lfanew);
|
||||||
|
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pbAddress >= ((PBYTE)pDosHeader +
|
||||||
|
pNtHeader->OptionalHeader
|
||||||
|
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) &&
|
||||||
|
pbAddress < ((PBYTE)pDosHeader +
|
||||||
|
pNtHeader->OptionalHeader
|
||||||
|
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress +
|
||||||
|
pNtHeader->OptionalHeader
|
||||||
|
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
40
extensions/cstrike/asm/asm.h
Normal file
40
extensions/cstrike/asm/asm.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#ifndef __ASM_H__
|
||||||
|
#define __ASM_H__
|
||||||
|
|
||||||
|
#define OP_JMP 0xE9
|
||||||
|
#define OP_JMP_SIZE 5
|
||||||
|
|
||||||
|
#define OP_NOP 0x90
|
||||||
|
#define OP_NOP_SIZE 1
|
||||||
|
|
||||||
|
#define OP_PREFIX 0xFF
|
||||||
|
#define OP_JMP_SEG 0x25
|
||||||
|
|
||||||
|
#define OP_JMP_BYTE 0xEB
|
||||||
|
#define OP_JMP_BYTE_SIZE 2
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void check_thunks(unsigned char *dest, unsigned char *pc);
|
||||||
|
|
||||||
|
//if dest is NULL, returns minimum number of bytes needed to be copied
|
||||||
|
//if dest is not NULL, it will copy the bytes to dest as well as fix CALLs and JMPs
|
||||||
|
//http://www.devmaster.net/forums/showthread.php?t=2311
|
||||||
|
int copy_bytes(unsigned char *func, unsigned char* dest, int required_len);
|
||||||
|
|
||||||
|
//insert a specific JMP instruction at the given location
|
||||||
|
void inject_jmp(void* src, void* dest);
|
||||||
|
|
||||||
|
//fill a given block with NOPs
|
||||||
|
void fill_nop(void* src, unsigned int len);
|
||||||
|
|
||||||
|
//evaluate a JMP at the target
|
||||||
|
void* eval_jump(void* src);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //__ASM_H__
|
@ -34,6 +34,8 @@
|
|||||||
#include "RegNatives.h"
|
#include "RegNatives.h"
|
||||||
#include "timeleft.h"
|
#include "timeleft.h"
|
||||||
#include "iplayerinfo.h"
|
#include "iplayerinfo.h"
|
||||||
|
#include "ISDKTools.h"
|
||||||
|
#include "forwards.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file extension.cpp
|
* @file extension.cpp
|
||||||
@ -53,6 +55,8 @@ SMEXT_LINK(&g_CStrike);
|
|||||||
|
|
||||||
extern sp_nativeinfo_t g_CSNatives[];
|
extern sp_nativeinfo_t g_CSNatives[];
|
||||||
|
|
||||||
|
ISDKTools *g_pSDKTools = NULL;
|
||||||
|
|
||||||
bool CStrike::SDK_OnLoad(char *error, size_t maxlength, bool late)
|
bool CStrike::SDK_OnLoad(char *error, size_t maxlength, bool late)
|
||||||
{
|
{
|
||||||
if (strcmp(g_pSM->GetGameFolderName(), "cstrike") != 0)
|
if (strcmp(g_pSM->GetGameFolderName(), "cstrike") != 0)
|
||||||
@ -75,9 +79,23 @@ bool CStrike::SDK_OnLoad(char *error, size_t maxlength, bool late)
|
|||||||
|
|
||||||
sharesys->AddNatives(myself, g_CSNatives);
|
sharesys->AddNatives(myself, g_CSNatives);
|
||||||
sharesys->RegisterLibrary(myself, "cstrike");
|
sharesys->RegisterLibrary(myself, "cstrike");
|
||||||
|
plsys->AddPluginsListener(this);
|
||||||
|
|
||||||
playerhelpers->RegisterCommandTargetProcessor(this);
|
playerhelpers->RegisterCommandTargetProcessor(this);
|
||||||
|
|
||||||
|
CDetourManager::Init(g_pSM->GetScriptingEngine(), g_pGameConf);
|
||||||
|
|
||||||
|
g_pHandleBuyForward = forwards->CreateForward("CS_OnBuyCommand", ET_Event, 2, NULL, Param_Cell, Param_String);
|
||||||
|
g_pPriceForward = forwards->CreateForward("CS_OnGetWeaponPrice", ET_Event, 3, NULL, Param_Cell, Param_String, Param_CellByRef);
|
||||||
|
g_pTerminateRoundForward = forwards->CreateForward("CS_OnTerminateRound", ET_Event, 2, NULL, Param_FloatByRef, Param_CellByRef);
|
||||||
|
g_pCSWeaponDropForward = forwards->CreateForward("CS_OnCSWeaponDrop", ET_Event, 2, NULL, Param_Cell, Param_Cell);
|
||||||
|
|
||||||
|
|
||||||
|
m_TerminateRoundDetourEnabled = false;
|
||||||
|
m_WeaponPriceDetourEnabled = false;
|
||||||
|
m_HandleBuyDetourEnabled = false;
|
||||||
|
m_CSWeaponDetourEnabled = false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,11 +117,26 @@ void CStrike::SDK_OnUnload()
|
|||||||
}
|
}
|
||||||
g_RegNatives.UnregisterAll();
|
g_RegNatives.UnregisterAll();
|
||||||
gameconfs->CloseGameConfigFile(g_pGameConf);
|
gameconfs->CloseGameConfigFile(g_pGameConf);
|
||||||
|
plsys->RemovePluginsListener(this);
|
||||||
playerhelpers->UnregisterCommandTargetProcessor(this);
|
playerhelpers->UnregisterCommandTargetProcessor(this);
|
||||||
|
forwards->ReleaseForward(g_pHandleBuyForward);
|
||||||
|
forwards->ReleaseForward(g_pPriceForward);
|
||||||
|
forwards->ReleaseForward(g_pTerminateRoundForward);
|
||||||
|
forwards->ReleaseForward(g_pCSWeaponDropForward);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStrike::SDK_OnAllLoaded()
|
void CStrike::SDK_OnAllLoaded()
|
||||||
{
|
{
|
||||||
|
SM_GET_LATE_IFACE(SDKTOOLS, g_pSDKTools);
|
||||||
|
if (g_pSDKTools == NULL)
|
||||||
|
{
|
||||||
|
smutils->LogError(myself, "SDKTools interface not found. TerminateRound native disabled.");
|
||||||
|
}
|
||||||
|
else if (g_pSDKTools->GetInterfaceVersion() < 2)
|
||||||
|
{
|
||||||
|
//<psychonic> THIS ISN'T DA LIMBO STICK. LOW IS BAD
|
||||||
|
smutils->LogError(myself, "SDKTools interface is outdated. TerminateRound native disabled.");
|
||||||
|
}
|
||||||
gameevents->AddListener(&g_TimeLeftEvents, "round_start", true);
|
gameevents->AddListener(&g_TimeLeftEvents, "round_start", true);
|
||||||
gameevents->AddListener(&g_TimeLeftEvents, "round_end", true);
|
gameevents->AddListener(&g_TimeLeftEvents, "round_end", true);
|
||||||
SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, &g_TimeLeftEvents, &TimeLeftEvents::LevelInit, true);
|
SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, &g_TimeLeftEvents, &TimeLeftEvents::LevelInit, true);
|
||||||
@ -257,3 +290,48 @@ const char *CStrike::GetExtensionDateString()
|
|||||||
return SM_BUILD_TIMESTAMP;
|
return SM_BUILD_TIMESTAMP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CStrike::OnPluginLoaded(IPlugin *plugin)
|
||||||
|
{
|
||||||
|
if (!m_WeaponPriceDetourEnabled && g_pPriceForward->GetFunctionCount())
|
||||||
|
{
|
||||||
|
m_WeaponPriceDetourEnabled = CreateWeaponPriceDetour();
|
||||||
|
if (m_WeaponPriceDetourEnabled)
|
||||||
|
m_HandleBuyDetourEnabled = true;
|
||||||
|
}
|
||||||
|
if (!m_TerminateRoundDetourEnabled && g_pTerminateRoundForward->GetFunctionCount())
|
||||||
|
{
|
||||||
|
m_TerminateRoundDetourEnabled = CreateTerminateRoundDetour();
|
||||||
|
}
|
||||||
|
if (!m_HandleBuyDetourEnabled && g_pHandleBuyForward->GetFunctionCount())
|
||||||
|
{
|
||||||
|
m_HandleBuyDetourEnabled = CreateHandleBuyDetour();
|
||||||
|
}
|
||||||
|
if (!m_CSWeaponDetourEnabled && g_pCSWeaponDropForward->GetFunctionCount())
|
||||||
|
{
|
||||||
|
m_CSWeaponDetourEnabled = CreateCSWeaponDropDetour();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CStrike::OnPluginUnloaded(IPlugin *plugin)
|
||||||
|
{
|
||||||
|
if (m_WeaponPriceDetourEnabled && !g_pPriceForward->GetFunctionCount())
|
||||||
|
{
|
||||||
|
RemoveWeaponPriceDetour();
|
||||||
|
m_WeaponPriceDetourEnabled = false;
|
||||||
|
}
|
||||||
|
if (m_TerminateRoundDetourEnabled && !g_pTerminateRoundForward->GetFunctionCount())
|
||||||
|
{
|
||||||
|
RemoveTerminateRoundDetour();
|
||||||
|
m_TerminateRoundDetourEnabled = false;
|
||||||
|
}
|
||||||
|
if (m_HandleBuyDetourEnabled && !g_pHandleBuyForward->GetFunctionCount())
|
||||||
|
{
|
||||||
|
RemoveHandleBuyDetour();
|
||||||
|
m_HandleBuyDetourEnabled = false;
|
||||||
|
}
|
||||||
|
if (m_CSWeaponDetourEnabled && !g_pCSWeaponDropForward->GetFunctionCount())
|
||||||
|
{
|
||||||
|
RemoveCSWeaponDropDetour();
|
||||||
|
m_CSWeaponDetourEnabled = false;;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -38,7 +38,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "smsdk_ext.h"
|
#include "smsdk_ext.h"
|
||||||
|
#include "CDetour/detours.h"
|
||||||
#include <IBinTools.h>
|
#include <IBinTools.h>
|
||||||
|
#include <ISDKTools.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sample implementation of the SDK Extension.
|
* @brief Sample implementation of the SDK Extension.
|
||||||
@ -46,7 +48,8 @@
|
|||||||
*/
|
*/
|
||||||
class CStrike :
|
class CStrike :
|
||||||
public SDKExtension,
|
public SDKExtension,
|
||||||
public ICommandTargetProcessor
|
public ICommandTargetProcessor,
|
||||||
|
public IPluginsListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
@ -91,6 +94,9 @@ public:
|
|||||||
const char *GetExtensionDateString();
|
const char *GetExtensionDateString();
|
||||||
public:
|
public:
|
||||||
bool ProcessCommandTarget(cmd_target_info_t *info);
|
bool ProcessCommandTarget(cmd_target_info_t *info);
|
||||||
|
public: //IPluginsListener
|
||||||
|
void OnPluginLoaded(IPlugin *plugin);
|
||||||
|
void OnPluginUnloaded(IPlugin *plugin);
|
||||||
public:
|
public:
|
||||||
#if defined SMEXT_CONF_METAMOD
|
#if defined SMEXT_CONF_METAMOD
|
||||||
/**
|
/**
|
||||||
@ -124,11 +130,21 @@ public:
|
|||||||
*/
|
*/
|
||||||
//virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength);
|
//virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength);
|
||||||
#endif
|
#endif
|
||||||
|
private:
|
||||||
|
bool m_WeaponPriceDetourEnabled;
|
||||||
|
bool m_TerminateRoundDetourEnabled;
|
||||||
|
bool m_HandleBuyDetourEnabled;
|
||||||
|
bool m_CSWeaponDetourEnabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Interfaces from SourceMod */
|
/* Interfaces from SourceMod */
|
||||||
extern IBinTools *g_pBinTools;
|
extern IBinTools *g_pBinTools;
|
||||||
extern IGameConfig *g_pGameConf;
|
extern IGameConfig *g_pGameConf;
|
||||||
|
extern ISDKTools *g_pSDKTools;
|
||||||
extern int g_msgHintText;
|
extern int g_msgHintText;
|
||||||
|
extern bool g_pIgnoreTerminateDetour;
|
||||||
|
extern bool g_pIgnoreCSWeaponDropDetour;
|
||||||
|
extern bool g_pTerminateRoundDetoured;
|
||||||
|
extern bool g_pCSWeaponDropDetoured;
|
||||||
|
|
||||||
#endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
|
#endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
|
||||||
|
234
extensions/cstrike/forwards.cpp
Normal file
234
extensions/cstrike/forwards.cpp
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
#include "extension.h"
|
||||||
|
#include "forwards.h"
|
||||||
|
|
||||||
|
bool g_pTerminateRoundDetoured = false;
|
||||||
|
bool g_pCSWeaponDropDetoured = false;
|
||||||
|
bool g_pIgnoreTerminateDetour = false;
|
||||||
|
bool g_pIgnoreCSWeaponDropDetour = false;
|
||||||
|
bool g_PriceDetoured = false;
|
||||||
|
bool g_HandleBuyDetoured = false;
|
||||||
|
int lastclient = -1;
|
||||||
|
|
||||||
|
IForward *g_pHandleBuyForward = NULL;
|
||||||
|
IForward *g_pPriceForward = NULL;
|
||||||
|
IForward *g_pTerminateRoundForward = NULL;
|
||||||
|
IForward *g_pCSWeaponDropForward = NULL;
|
||||||
|
CDetour *DHandleBuy = NULL;
|
||||||
|
CDetour *DWeaponPrice = NULL;
|
||||||
|
CDetour *DTerminateRound = NULL;
|
||||||
|
CDetour *DCSWeaponDrop = NULL;
|
||||||
|
|
||||||
|
int weaponNameOffset = -1;
|
||||||
|
|
||||||
|
DETOUR_DECL_MEMBER1(DetourHandleBuy, int, const char *, weapon)
|
||||||
|
{
|
||||||
|
int client = engine->IndexOfEdict(engine->PEntityOfEntIndex(gamehelpers->EntityToBCompatRef(reinterpret_cast<CBaseEntity *>(this))));
|
||||||
|
|
||||||
|
lastclient = client;
|
||||||
|
|
||||||
|
cell_t result = Pl_Continue;
|
||||||
|
|
||||||
|
g_pHandleBuyForward->PushCell(client);
|
||||||
|
g_pHandleBuyForward->PushString(weapon);
|
||||||
|
g_pHandleBuyForward->Execute(&result);
|
||||||
|
|
||||||
|
if (result != Pl_Continue)
|
||||||
|
{
|
||||||
|
lastclient = -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int val = DETOUR_MEMBER_CALL(DetourHandleBuy)(weapon);
|
||||||
|
lastclient = -1;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
DETOUR_DECL_MEMBER0(DetourWeaponPrice, int)
|
||||||
|
{
|
||||||
|
int price = DETOUR_MEMBER_CALL(DetourWeaponPrice)();
|
||||||
|
|
||||||
|
if (lastclient == -1)
|
||||||
|
return price;
|
||||||
|
|
||||||
|
const char *weapon_name = reinterpret_cast<char *>(this+weaponNameOffset);
|
||||||
|
|
||||||
|
int original = price;
|
||||||
|
|
||||||
|
cell_t result = Pl_Continue;
|
||||||
|
|
||||||
|
g_pPriceForward->PushCell(lastclient);
|
||||||
|
g_pPriceForward->PushString(weapon_name);
|
||||||
|
g_pPriceForward->PushCellByRef(&price);
|
||||||
|
g_pPriceForward->Execute(&result);
|
||||||
|
|
||||||
|
if (result == Pl_Continue)
|
||||||
|
return original;
|
||||||
|
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
|
||||||
|
DETOUR_DECL_MEMBER2(DetourTerminateRound, void, float, delay, int, reason)
|
||||||
|
{
|
||||||
|
if (g_pIgnoreTerminateDetour)
|
||||||
|
{
|
||||||
|
g_pIgnoreTerminateDetour = false;
|
||||||
|
DETOUR_MEMBER_CALL(DetourTerminateRound)(delay, reason);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float orgdelay = delay;
|
||||||
|
int orgreason = reason;
|
||||||
|
|
||||||
|
cell_t result = Pl_Continue;
|
||||||
|
|
||||||
|
g_pTerminateRoundForward->PushFloatByRef(&delay);
|
||||||
|
g_pTerminateRoundForward->PushCellByRef(&reason);
|
||||||
|
g_pTerminateRoundForward->Execute(&result);
|
||||||
|
|
||||||
|
if (result >= Pl_Handled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (result == Pl_Changed)
|
||||||
|
return DETOUR_MEMBER_CALL(DetourTerminateRound)(delay, reason);
|
||||||
|
|
||||||
|
return DETOUR_MEMBER_CALL(DetourTerminateRound)(orgdelay, orgreason);
|
||||||
|
}
|
||||||
|
DETOUR_DECL_MEMBER3(DetourCSWeaponDrop, void, CBaseEntity *, weapon, bool, something, bool, toss)
|
||||||
|
{
|
||||||
|
if (g_pIgnoreCSWeaponDropDetour)
|
||||||
|
{
|
||||||
|
g_pIgnoreCSWeaponDropDetour = false;
|
||||||
|
DETOUR_MEMBER_CALL(DetourCSWeaponDrop)(weapon, something, toss);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int client = engine->IndexOfEdict(engine->PEntityOfEntIndex(gamehelpers->EntityToBCompatRef(reinterpret_cast<CBaseEntity *>(this))));
|
||||||
|
int weaponIndex = engine->IndexOfEdict(engine->PEntityOfEntIndex(gamehelpers->EntityToBCompatRef(weapon)));
|
||||||
|
|
||||||
|
cell_t result = Pl_Continue;
|
||||||
|
g_pCSWeaponDropForward->PushCell(client);
|
||||||
|
g_pCSWeaponDropForward->PushCell(weaponIndex);
|
||||||
|
g_pCSWeaponDropForward->Execute(&result);
|
||||||
|
|
||||||
|
|
||||||
|
if (result == Pl_Continue)
|
||||||
|
DETOUR_MEMBER_CALL(DetourCSWeaponDrop)(weapon, something, toss);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool CreateWeaponPriceDetour()
|
||||||
|
{
|
||||||
|
if (weaponNameOffset == -1)
|
||||||
|
{
|
||||||
|
if (!g_pGameConf->GetOffset("WeaponName", &weaponNameOffset))
|
||||||
|
{
|
||||||
|
smutils->LogError(myself, "Could not find WeaponName offset - Disabled OnGetWeaponPrice forward");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DWeaponPrice = DETOUR_CREATE_MEMBER(DetourWeaponPrice, "GetWeaponPrice");
|
||||||
|
|
||||||
|
if (DWeaponPrice != NULL)
|
||||||
|
{
|
||||||
|
if (!CreateHandleBuyDetour())
|
||||||
|
{
|
||||||
|
g_pSM->LogError(myself, "GetWeaponPrice detour could not be initialized - HandleCommand_Buy_Internal failed to detour, disabled OnGetWeaponPrice forward.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DWeaponPrice->EnableDetour();
|
||||||
|
g_PriceDetoured = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_pSM->LogError(myself, "GetWeaponPrice detour could not be initialized - Disabled OnGetWeaponPrice forward.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateTerminateRoundDetour()
|
||||||
|
{
|
||||||
|
DTerminateRound = DETOUR_CREATE_MEMBER(DetourTerminateRound, "TerminateRound");
|
||||||
|
|
||||||
|
if (DTerminateRound != NULL)
|
||||||
|
{
|
||||||
|
DTerminateRound->EnableDetour();
|
||||||
|
g_pTerminateRoundDetoured = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
g_pSM->LogError(myself, "TerminateRound detour could not be initialized - Disabled OnTerminateRound forward");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateHandleBuyDetour()
|
||||||
|
{
|
||||||
|
if (g_HandleBuyDetoured)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
DHandleBuy = DETOUR_CREATE_MEMBER(DetourHandleBuy, "HandleCommand_Buy_Internal");
|
||||||
|
|
||||||
|
if (DHandleBuy != NULL)
|
||||||
|
{
|
||||||
|
DHandleBuy->EnableDetour();
|
||||||
|
g_HandleBuyDetoured = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
g_pSM->LogError(myself, "HandleCommand_Buy_Internal detour could not be initialized - Disabled OnBuyCommand forward");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateCSWeaponDropDetour()
|
||||||
|
{
|
||||||
|
DCSWeaponDrop = DETOUR_CREATE_MEMBER(DetourCSWeaponDrop, "CSWeaponDrop");
|
||||||
|
|
||||||
|
if (DCSWeaponDrop != NULL)
|
||||||
|
{
|
||||||
|
DCSWeaponDrop->EnableDetour();
|
||||||
|
g_pCSWeaponDropDetoured = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_pSM->LogError(myself, "CSWeaponDrop detour could not be initialized - Disabled OnCSWeaponDrop forward");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveWeaponPriceDetour()
|
||||||
|
{
|
||||||
|
if (DWeaponPrice != NULL)
|
||||||
|
{
|
||||||
|
DWeaponPrice->Destroy();
|
||||||
|
DWeaponPrice = NULL;
|
||||||
|
}
|
||||||
|
g_PriceDetoured = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveHandleBuyDetour()
|
||||||
|
{
|
||||||
|
if (g_PriceDetoured)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (DHandleBuy != NULL)
|
||||||
|
{
|
||||||
|
DHandleBuy->Destroy();
|
||||||
|
DHandleBuy = NULL;
|
||||||
|
}
|
||||||
|
g_HandleBuyDetoured = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveTerminateRoundDetour()
|
||||||
|
{
|
||||||
|
if (DTerminateRound != NULL)
|
||||||
|
{
|
||||||
|
DTerminateRound->Destroy();
|
||||||
|
DTerminateRound = NULL;
|
||||||
|
}
|
||||||
|
g_pTerminateRoundDetoured = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveCSWeaponDropDetour()
|
||||||
|
{
|
||||||
|
if (DCSWeaponDrop != NULL)
|
||||||
|
{
|
||||||
|
DCSWeaponDrop->Destroy();
|
||||||
|
DCSWeaponDrop = NULL;
|
||||||
|
}
|
||||||
|
g_pCSWeaponDropDetoured = false;
|
||||||
|
}
|
17
extensions/cstrike/forwards.h
Normal file
17
extensions/cstrike/forwards.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef _INCLUDE_CSTRIKE_FORWARDS_H_
|
||||||
|
#define _INCLUDE_CSTRIKE_FORWARDS_H_
|
||||||
|
bool CreateWeaponPriceDetour();
|
||||||
|
bool CreateHandleBuyDetour();
|
||||||
|
bool CreateTerminateRoundDetour();
|
||||||
|
bool CreateCSWeaponDropDetour();
|
||||||
|
void RemoveWeaponPriceDetour();
|
||||||
|
void RemoveHandleBuyDetour();
|
||||||
|
void RemoveTerminateRoundDetour();
|
||||||
|
void RemoveCSWeaponDropDetour();
|
||||||
|
|
||||||
|
extern IServerGameEnts *gameents;
|
||||||
|
extern IForward *g_pHandleBuyForward;
|
||||||
|
extern IForward *g_pPriceForward;
|
||||||
|
extern IForward *g_pTerminateRoundForward;
|
||||||
|
extern IForward *g_pCSWeaponDropForward;
|
||||||
|
#endif //_INCLUDE_CSTRIKE_FORWARDS_H_
|
@ -505,7 +505,7 @@
|
|||||||
Name="VCPostBuildEventTool"
|
Name="VCPostBuildEventTool"
|
||||||
/>
|
/>
|
||||||
</Configuration>
|
</Configuration>
|
||||||
<Configuration
|
<Configuration
|
||||||
Name="Debug - Episode1|Win32"
|
Name="Debug - Episode1|Win32"
|
||||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||||
IntermediateDirectory="$(ConfigurationName)"
|
IntermediateDirectory="$(ConfigurationName)"
|
||||||
@ -681,6 +681,10 @@
|
|||||||
RelativePath="..\extension.cpp"
|
RelativePath="..\extension.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\forwards.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\natives.cpp"
|
RelativePath="..\natives.cpp"
|
||||||
>
|
>
|
||||||
@ -703,6 +707,10 @@
|
|||||||
RelativePath="..\extension.h"
|
RelativePath="..\extension.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\forwards.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\RegNatives.h"
|
RelativePath="..\RegNatives.h"
|
||||||
>
|
>
|
||||||
@ -739,6 +747,34 @@
|
|||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
</Filter>
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="asm"
|
||||||
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\asm\asm.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\asm\asm.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="CDetour"
|
||||||
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\CDetour\detourhelpers.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\CDetour\detours.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\CDetour\detours.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
</Files>
|
</Files>
|
||||||
<Globals>
|
<Globals>
|
||||||
</Globals>
|
</Globals>
|
||||||
|
@ -31,10 +31,11 @@
|
|||||||
|
|
||||||
#include "extension.h"
|
#include "extension.h"
|
||||||
#include "RegNatives.h"
|
#include "RegNatives.h"
|
||||||
|
#include <server_class.h>
|
||||||
|
|
||||||
#define REGISTER_NATIVE_ADDR(name, code) \
|
#define REGISTER_NATIVE_ADDR(name, code) \
|
||||||
void *addr; \
|
void *addr; \
|
||||||
if (!g_pGameConf->GetMemSig(name, &addr)) \
|
if (!g_pGameConf->GetMemSig(name, &addr) || !addr) \
|
||||||
{ \
|
{ \
|
||||||
return pContext->ThrowNativeError("Failed to locate function"); \
|
return pContext->ThrowNativeError("Failed to locate function"); \
|
||||||
} \
|
} \
|
||||||
@ -71,6 +72,59 @@ inline CBaseEntity *GetCBaseEntity(int num, bool isplayer)
|
|||||||
return pUnk->GetBaseEntity();
|
return pUnk->GetBaseEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool UTIL_FindDataTable(SendTable *pTable, const char *name, sm_sendprop_info_t *info, unsigned int offset)
|
||||||
|
{
|
||||||
|
const char *pname;
|
||||||
|
int props = pTable->GetNumProps();
|
||||||
|
SendProp *prop;
|
||||||
|
SendTable *table;
|
||||||
|
|
||||||
|
for (int i=0; i<props; i++)
|
||||||
|
{
|
||||||
|
prop = pTable->GetProp(i);
|
||||||
|
|
||||||
|
if ((table = prop->GetDataTable()) != NULL)
|
||||||
|
{
|
||||||
|
pname = table->GetName();
|
||||||
|
if (pname && strcmp(name, pname) == 0)
|
||||||
|
{
|
||||||
|
info->prop = prop;
|
||||||
|
info->actual_offset = offset + info->prop->GetOffset();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UTIL_FindDataTable(table,
|
||||||
|
name,
|
||||||
|
info,
|
||||||
|
offset + prop->GetOffset())
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Taken From Sourcemod Core
|
||||||
|
unsigned int strncopy(char *dest, const char *src, size_t count)
|
||||||
|
{
|
||||||
|
if (!count)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *start = dest;
|
||||||
|
while ((*src) && (--count))
|
||||||
|
{
|
||||||
|
*dest++ = *src++;
|
||||||
|
}
|
||||||
|
*dest = '\0';
|
||||||
|
|
||||||
|
return (dest - start);
|
||||||
|
}
|
||||||
|
|
||||||
static cell_t CS_RespawnPlayer(IPluginContext *pContext, const cell_t *params)
|
static cell_t CS_RespawnPlayer(IPluginContext *pContext, const cell_t *params)
|
||||||
{
|
{
|
||||||
static ICallWrapper *pWrapper = NULL;
|
static ICallWrapper *pWrapper = NULL;
|
||||||
@ -120,10 +174,165 @@ static cell_t CS_SwitchTeam(IPluginContext *pContext, const cell_t *params)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static cell_t CS_DropWeapon(IPluginContext *pContext, const cell_t *params)
|
||||||
|
{
|
||||||
|
static ICallWrapper *pWrapper = NULL;
|
||||||
|
if (!pWrapper)
|
||||||
|
{
|
||||||
|
REGISTER_NATIVE_ADDR("CSWeaponDrop",
|
||||||
|
PassInfo pass[3]; \
|
||||||
|
pass[0].flags = PASSFLAG_BYVAL; \
|
||||||
|
pass[0].type = PassType_Basic; \
|
||||||
|
pass[0].size = sizeof(CBaseEntity *); \
|
||||||
|
pass[1].flags = PASSFLAG_BYVAL; \
|
||||||
|
pass[1].type = PassType_Basic; \
|
||||||
|
pass[1].size = sizeof(bool); \
|
||||||
|
pass[2].flags = PASSFLAG_BYVAL; \
|
||||||
|
pass[2].type = PassType_Basic; \
|
||||||
|
pass[2].size = sizeof(bool); \
|
||||||
|
pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 3))
|
||||||
|
}
|
||||||
|
|
||||||
|
CBaseEntity *pEntity;
|
||||||
|
if (!(pEntity = GetCBaseEntity(params[1], true)))
|
||||||
|
{
|
||||||
|
return pContext->ThrowNativeError("Client index %d is not valid", params[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
CBaseEntity *pWeapon;
|
||||||
|
if (!(pWeapon = GetCBaseEntity(params[2], false)))
|
||||||
|
{
|
||||||
|
return pContext->ThrowNativeError("Weapon index %d is not valid", params[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Psychonic is awesome for this
|
||||||
|
sm_sendprop_info_t *spi = new sm_sendprop_info_t;
|
||||||
|
IServerUnknown *pUnk = (IServerUnknown *)pWeapon;
|
||||||
|
IServerNetworkable *pNet = pUnk->GetNetworkable();
|
||||||
|
|
||||||
|
if (!UTIL_FindDataTable(pNet->GetServerClass()->m_pTable, "DT_WeaponCSBase", spi, 0))
|
||||||
|
return pContext->ThrowNativeError("Entity index %d is not a weapon", params[2]);
|
||||||
|
|
||||||
|
if (!gamehelpers->FindSendPropInfo("CBaseCombatWeapon", "m_hOwnerEntity", spi))
|
||||||
|
return pContext->ThrowNativeError("Invalid entity index %d for weapon", params[2]);
|
||||||
|
|
||||||
|
CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pWeapon + spi->actual_offset);
|
||||||
|
if (params[1] != hndl.GetEntryIndex())
|
||||||
|
return pContext->ThrowNativeError("Weapon %d is not owned by client %d", params[2], params[1]);
|
||||||
|
|
||||||
|
if (params[4] == 1 && g_pCSWeaponDropDetoured)
|
||||||
|
g_pIgnoreCSWeaponDropDetour = true;
|
||||||
|
|
||||||
|
unsigned char vstk[sizeof(CBaseEntity *) * 2 + sizeof(bool) * 2];
|
||||||
|
unsigned char *vptr = vstk;
|
||||||
|
|
||||||
|
// <psychonic> first one is always false. second is true to toss, false to just drop
|
||||||
|
*(CBaseEntity **)vptr = pEntity;
|
||||||
|
vptr += sizeof(CBaseEntity *);
|
||||||
|
*(CBaseEntity **)vptr = pWeapon;
|
||||||
|
vptr += sizeof(CBaseEntity *);
|
||||||
|
*(bool *)vptr = false;
|
||||||
|
vptr += sizeof(bool);
|
||||||
|
*(bool *)vptr = (params[3]) ? true : false;
|
||||||
|
|
||||||
|
pWrapper->Execute(vstk, NULL);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static cell_t CS_TerminateRound(IPluginContext *pContext, const cell_t *params)
|
||||||
|
{
|
||||||
|
if (g_pSDKTools == NULL)
|
||||||
|
{
|
||||||
|
smutils->LogError(myself, "SDKTools interface not found. TerminateRound native disabled.");
|
||||||
|
}
|
||||||
|
else if (g_pSDKTools->GetInterfaceVersion() < 2)
|
||||||
|
{
|
||||||
|
//<psychonic> THIS ISN'T DA LIMBO STICK. LOW IS BAD
|
||||||
|
return pContext->ThrowNativeError("SDKTools interface is outdated. TerminateRound native disabled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
static ICallWrapper *pWrapper = NULL;
|
||||||
|
|
||||||
|
if (!pWrapper)
|
||||||
|
{
|
||||||
|
REGISTER_NATIVE_ADDR("TerminateRound",
|
||||||
|
PassInfo pass[2]; \
|
||||||
|
pass[0].flags = PASSFLAG_BYVAL; \
|
||||||
|
pass[0].type = PassType_Basic; \
|
||||||
|
pass[0].size = sizeof(float); \
|
||||||
|
pass[1].flags = PASSFLAG_BYVAL; \
|
||||||
|
pass[1].type = PassType_Basic; \
|
||||||
|
pass[1].size = sizeof(int); \
|
||||||
|
pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
void *gamerules = g_pSDKTools->GetGameRules();
|
||||||
|
if (gamerules == NULL)
|
||||||
|
{
|
||||||
|
return pContext->ThrowNativeError("GameRules not available. TerminateRound native disabled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params[3] == 1 && g_pTerminateRoundDetoured)
|
||||||
|
g_pIgnoreTerminateDetour = true;
|
||||||
|
|
||||||
|
unsigned char vstk[sizeof(void *) + sizeof(float)+ sizeof(int)];
|
||||||
|
unsigned char *vptr = vstk;
|
||||||
|
|
||||||
|
*(void **)vptr = gamerules;
|
||||||
|
vptr += sizeof(void *);
|
||||||
|
*(float *)vptr = sp_ctof(params[1]);
|
||||||
|
vptr += sizeof(float);
|
||||||
|
*(int*)vptr = params[2];
|
||||||
|
|
||||||
|
pWrapper->Execute(vstk, NULL);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static cell_t CS_GetTranslatedWeaponAlias(IPluginContext *pContext, const cell_t *params)
|
||||||
|
{
|
||||||
|
static ICallWrapper *pWrapper = NULL;
|
||||||
|
|
||||||
|
if (!pWrapper)
|
||||||
|
{
|
||||||
|
REGISTER_NATIVE_ADDR("GetTranslatedWeaponAlias",
|
||||||
|
PassInfo pass[1]; \
|
||||||
|
PassInfo retpass[1]; \
|
||||||
|
pass[0].flags = PASSFLAG_BYVAL; \
|
||||||
|
pass[0].type = PassType_Basic; \
|
||||||
|
pass[0].size = sizeof(char *); \
|
||||||
|
retpass[0].flags = PASSFLAG_BYVAL; \
|
||||||
|
retpass[0].type = PassType_Basic; \
|
||||||
|
retpass[0].size = sizeof(char *); \
|
||||||
|
pWrapper = g_pBinTools->CreateCall(addr, CallConv_Cdecl, &retpass[0], pass, 1))
|
||||||
|
}
|
||||||
|
char *dest, *weapon;
|
||||||
|
|
||||||
|
pContext->LocalToString(params[2], &dest);
|
||||||
|
pContext->LocalToString(params[1], &weapon);
|
||||||
|
|
||||||
|
char *ret = new char[128];
|
||||||
|
|
||||||
|
unsigned char vstk[sizeof(char *)];
|
||||||
|
unsigned char *vptr = vstk;
|
||||||
|
|
||||||
|
*(char **)vptr = weapon;
|
||||||
|
|
||||||
|
pWrapper->Execute(vstk, &ret);
|
||||||
|
|
||||||
|
strncopy(dest, ret, params[3]);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
sp_nativeinfo_t g_CSNatives[] =
|
sp_nativeinfo_t g_CSNatives[] =
|
||||||
{
|
{
|
||||||
{"CS_RespawnPlayer", CS_RespawnPlayer},
|
{"CS_RespawnPlayer", CS_RespawnPlayer},
|
||||||
{"CS_SwitchTeam", CS_SwitchTeam},
|
{"CS_SwitchTeam", CS_SwitchTeam},
|
||||||
|
{"CS_DropWeapon", CS_DropWeapon},
|
||||||
|
{"CS_TerminateRound", CS_TerminateRound},
|
||||||
|
{"CS_GetTranslatedWeaponAlias", CS_GetTranslatedWeaponAlias},
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -59,16 +59,17 @@
|
|||||||
#define SMEXT_CONF_METAMOD
|
#define SMEXT_CONF_METAMOD
|
||||||
|
|
||||||
/** Enable interfaces you want to use here by uncommenting lines */
|
/** Enable interfaces you want to use here by uncommenting lines */
|
||||||
//#define SMEXT_ENABLE_FORWARDSYS
|
#define SMEXT_ENABLE_FORWARDSYS
|
||||||
//#define SMEXT_ENABLE_HANDLESYS
|
//#define SMEXT_ENABLE_HANDLESYS
|
||||||
#define SMEXT_ENABLE_PLAYERHELPERS
|
#define SMEXT_ENABLE_PLAYERHELPERS
|
||||||
//#define SMEXT_ENABLE_DBMANAGER
|
//#define SMEXT_ENABLE_DBMANAGER
|
||||||
#define SMEXT_ENABLE_GAMECONF
|
#define SMEXT_ENABLE_GAMECONF
|
||||||
//#define SMEXT_ENABLE_MEMUTILS
|
//#define SMEXT_ENABLE_MEMUTILS
|
||||||
//#define SMEXT_ENABLE_GAMEHELPERS
|
#define SMEXT_ENABLE_GAMEHELPERS
|
||||||
#define SMEXT_ENABLE_TIMERSYS
|
#define SMEXT_ENABLE_TIMERSYS
|
||||||
//#define SMEXT_ENABLE_THREADER
|
//#define SMEXT_ENABLE_THREADER
|
||||||
//#define SMEXT_ENABLE_LIBSYS
|
//#define SMEXT_ENABLE_LIBSYS
|
||||||
#define SMEXT_ENABLE_USERMSGS
|
#define SMEXT_ENABLE_USERMSGS
|
||||||
|
#define SMEXT_ENABLE_PLUGINSYS
|
||||||
|
|
||||||
#endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
|
#endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
|
||||||
|
@ -76,6 +76,9 @@
|
|||||||
#if defined SMEXT_ENABLE_USERMSGS
|
#if defined SMEXT_ENABLE_USERMSGS
|
||||||
#include <IUserMessages.h>
|
#include <IUserMessages.h>
|
||||||
#endif
|
#endif
|
||||||
|
#if defined SMEXT_ENABLE_PLUGINSYS
|
||||||
|
#include <IPluginSys.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined SMEXT_CONF_METAMOD
|
#if defined SMEXT_CONF_METAMOD
|
||||||
#include <ISmmPlugin.h>
|
#include <ISmmPlugin.h>
|
||||||
@ -266,6 +269,10 @@ extern ILibrarySys *libsys;
|
|||||||
#if defined SMEXT_ENABLE_USERMSGS
|
#if defined SMEXT_ENABLE_USERMSGS
|
||||||
extern IUserMessages *usermsgs;
|
extern IUserMessages *usermsgs;
|
||||||
#endif
|
#endif
|
||||||
|
#if defined SMEXT_ENABLE_PLUGINSYS
|
||||||
|
extern IPluginManager *plsys;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#if defined SMEXT_CONF_METAMOD
|
#if defined SMEXT_CONF_METAMOD
|
||||||
PLUGIN_GLOBALVARS();
|
PLUGIN_GLOBALVARS();
|
||||||
|
@ -13,6 +13,16 @@
|
|||||||
{
|
{
|
||||||
"cstrike"
|
"cstrike"
|
||||||
{
|
{
|
||||||
|
"Offsets"
|
||||||
|
{
|
||||||
|
//Offset of szClassName in CCSWeaponInfo
|
||||||
|
"WeaponName"
|
||||||
|
{
|
||||||
|
"windows" "6"
|
||||||
|
"linux" "6"
|
||||||
|
"mac" "6"
|
||||||
|
}
|
||||||
|
}
|
||||||
"Signatures"
|
"Signatures"
|
||||||
{
|
{
|
||||||
"RoundRespawn"
|
"RoundRespawn"
|
||||||
@ -29,6 +39,41 @@
|
|||||||
"linux" "@_ZN9CCSPlayer10SwitchTeamEi"
|
"linux" "@_ZN9CCSPlayer10SwitchTeamEi"
|
||||||
"mac" "@_ZN9CCSPlayer10SwitchTeamEi"
|
"mac" "@_ZN9CCSPlayer10SwitchTeamEi"
|
||||||
}
|
}
|
||||||
|
"HandleCommand_Buy_Internal"
|
||||||
|
{
|
||||||
|
"library" "server"
|
||||||
|
"windows" "\x55\x8B\xEC\x81\xEC\x2A\x01\x00\x00\x89\x8D\x58"
|
||||||
|
"linux" "@_ZN9CCSPlayer26HandleCommand_Buy_InternalEPKc"
|
||||||
|
"mac" "@_ZN9CCSPlayer26HandleCommand_Buy_InternalEPKc"
|
||||||
|
}
|
||||||
|
"GetWeaponPrice"
|
||||||
|
{
|
||||||
|
"library" "server"
|
||||||
|
"windows" "\x8B\x81\xB0\x08\x00\x00\xC3"
|
||||||
|
"linux" "@_ZNK13CCSWeaponInfo14GetWeaponPriceEv"
|
||||||
|
"mac" "@_ZNK13CCSWeaponInfo14GetWeaponPriceEv"
|
||||||
|
}
|
||||||
|
"CSWeaponDrop"//Wildcard first 6 bytes for CS:S DM
|
||||||
|
{
|
||||||
|
"library" "server"
|
||||||
|
"windows" "\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x89\x8D\x5C\x2A\x2A\x2A\xC6\x45\x2A\x2A\x8B\x8D\x5C\x2A\x2A\x2A\xE8"
|
||||||
|
"linux" "@_ZN9CCSPlayer12CSWeaponDropEP17CBaseCombatWeaponbb"
|
||||||
|
"mac" "@_ZN9CCSPlayer12CSWeaponDropEP17CBaseCombatWeaponbb"
|
||||||
|
}
|
||||||
|
"TerminateRound"
|
||||||
|
{
|
||||||
|
"library" "server"
|
||||||
|
"windows" "\x83\xEC\x2A\x53\x8B\x5C\x2A\x2A\x55\x56\x57\x33\xF6\x8B\xE9\x33"
|
||||||
|
"linux" "@_ZN12CCSGameRules14TerminateRoundEfi"
|
||||||
|
"mac" "@_ZN12CCSGameRules14TerminateRoundEfi"
|
||||||
|
}
|
||||||
|
"GetTranslatedWeaponAlias"
|
||||||
|
{
|
||||||
|
"library" "server"
|
||||||
|
"windows" "\xA1\x2A\x2A\x2A\x2A\x80\x2A\x00\x53"
|
||||||
|
"linux" "@_Z24GetTranslatedWeaponAliasPKc"
|
||||||
|
"mac" "@_Z24GetTranslatedWeaponAliasPKc"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,70 @@
|
|||||||
#define CS_SLOT_GRENADE 3 /**< Grenade slot (will only return one grenade). */
|
#define CS_SLOT_GRENADE 3 /**< Grenade slot (will only return one grenade). */
|
||||||
#define CS_SLOT_C4 4 /**< C4 slot. */
|
#define CS_SLOT_C4 4 /**< C4 slot. */
|
||||||
|
|
||||||
|
enum CSRoundEndReason
|
||||||
|
{
|
||||||
|
CSRoundEnd_TargetBombed = 0, // Target Successfully Bombed!
|
||||||
|
CSRoundEnd_VIPEscaped, // The VIP has escaped!
|
||||||
|
CSRoundEnd_VIPKilled, // VIP has been assassinated!
|
||||||
|
CSRoundEnd_TerroristsEscaped, // The terrorists have escaped!
|
||||||
|
CSRoundEnd_CTStoppedEscape, // The CTs have prevented most of the terrorists from escaping!
|
||||||
|
CSRoundEnd_TerroristsStopped, // Escaping terrorists have all been neutralized!
|
||||||
|
CSRoundEnd_BombDefused, // The bomb has been defused!
|
||||||
|
CSRoundEnd_CTWin, // Counter-Terrorists Win!
|
||||||
|
CSRoundEnd_TerroristWin, // Terrorists Win!
|
||||||
|
CSRoundEnd_Draw, // Round Draw!
|
||||||
|
CSRoundEnd_HostagesRescued, // All Hostages have been rescued!
|
||||||
|
CSRoundEnd_TargetSaved, // Target has been saved!
|
||||||
|
CSRoundEnd_HostagesNotRescued, // Hostages have not been rescued!
|
||||||
|
CSRoundEnd_TerroristsNotEscaped, // Terrorists have not escaped!
|
||||||
|
CSRoundEnd_VIPNotEscaped, // VIP has not escaped!
|
||||||
|
CSRoundEnd_GameStart // Game Commencing!
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a player attempts to purchase an item.
|
||||||
|
* Return Plugin_Continue to allow the purchase or return a
|
||||||
|
* higher action to deny.
|
||||||
|
*
|
||||||
|
* @param client Client index
|
||||||
|
* @param weapon User input for weapon name
|
||||||
|
*/
|
||||||
|
forward Action:CS_OnBuyCommand(client, const String:weapon[]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when CSWeaponDrop is called
|
||||||
|
* Return Plugin_Continue to allow the call or return a
|
||||||
|
* higher action to deny.
|
||||||
|
*
|
||||||
|
* @param client Client index
|
||||||
|
* @param weapon Weapon index
|
||||||
|
*/
|
||||||
|
forward Action:CS_OnCSWeaponDrop(client, weaponIndex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when game retrieves a weapon's price for a player.
|
||||||
|
* Return Plugin_Continue to use default value or return a higher
|
||||||
|
* action to use a newly-set price.
|
||||||
|
*
|
||||||
|
* @note This can be called multiple times per weapon purchase
|
||||||
|
*
|
||||||
|
* @param client Client index
|
||||||
|
* @param weapon Weapon classname
|
||||||
|
* @param price Buffer param for the price of the weapon
|
||||||
|
*/
|
||||||
|
forward Action:CS_OnGetWeaponPrice(client, const String:weapon[], &price);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when TerminateRound is called.
|
||||||
|
* Return Plugin_Continue to ignore, return Plugin_Changed to continue,
|
||||||
|
* using the given delay and reason, or return Plugin_Handled or a higher
|
||||||
|
* action to block TerminateRound from firing.
|
||||||
|
*
|
||||||
|
* @param delay Time (in seconds) until new round starts
|
||||||
|
* @param reason Reason for round end
|
||||||
|
*/
|
||||||
|
forward Action:CS_OnTerminateRound(&Float:delay, &CSRoundEndReason:reason);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Respawns a player.
|
* Respawns a player.
|
||||||
*
|
*
|
||||||
@ -64,6 +128,42 @@ native CS_RespawnPlayer(client);
|
|||||||
*/
|
*/
|
||||||
native CS_SwitchTeam(client, team);
|
native CS_SwitchTeam(client, team);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces a player to drop or toss their weapon
|
||||||
|
*
|
||||||
|
* @param client Player's index.
|
||||||
|
* @param weaponIndex Index of weapon to drop.
|
||||||
|
* @param toss True to toss weapon (with velocity) or false to just drop weapon
|
||||||
|
* @param blockhook Set to true to stop the corresponding CS_OnCSWeaponDrop
|
||||||
|
*
|
||||||
|
* @noreturn
|
||||||
|
* @error Invalid client index, client not in game, or invalid weapon index.
|
||||||
|
*/
|
||||||
|
native CS_DropWeapon(client, weaponIndex, bool:toss, bool:blockhook = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces round to end with a reason
|
||||||
|
*
|
||||||
|
* @param delay Time (in seconds) to delay before new round starts
|
||||||
|
* @param reason Reason for the round ending
|
||||||
|
* @param blockhook Set to true to stop the corresponding CS_OnTerminateRound
|
||||||
|
* forward from being called.
|
||||||
|
* @noreturn
|
||||||
|
*/
|
||||||
|
native CS_TerminateRound(Float:delay, CSRoundEndReason:reason, bool:blockhook = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a weapon name from a weapon alias
|
||||||
|
*
|
||||||
|
* @param alias Weapons alias to get weapon name for.
|
||||||
|
* @param weapon Buffer to store weapons name
|
||||||
|
* @param size Size of buffer to store the weapons name.
|
||||||
|
* @noreturn
|
||||||
|
*
|
||||||
|
* @note Will set the buffer to the original alias if it is not an alias to a weapon.
|
||||||
|
*/
|
||||||
|
native CS_GetTranslatedWeaponAlias(const String:alias[], String:weapon[], size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do not edit below this line!
|
* Do not edit below this line!
|
||||||
*/
|
*/
|
||||||
@ -84,5 +184,9 @@ public __ext_cstrike_SetNTVOptional()
|
|||||||
{
|
{
|
||||||
MarkNativeAsOptional("CS_RespawnPlayer");
|
MarkNativeAsOptional("CS_RespawnPlayer");
|
||||||
MarkNativeAsOptional("CS_SwitchTeam");
|
MarkNativeAsOptional("CS_SwitchTeam");
|
||||||
|
MarkNativeAsOptional("CS_DropWeapon");
|
||||||
|
MarkNativeAsOptional("CS_TerminateRound");
|
||||||
|
MarkNativeAsOptional("CS_GetTranslatedWeaponAlias");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user