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',
|
||||
'RegNatives.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.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
|
||||
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 ###
|
||||
@ -156,7 +156,9 @@ $(BIN_DIR)/%.o: %.cpp
|
||||
$(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
|
||||
|
||||
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 \
|
||||
ln -sf $(HL2LIB)/$(LIB_PREFIX)vstdlib$(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 "timeleft.h"
|
||||
#include "iplayerinfo.h"
|
||||
#include "ISDKTools.h"
|
||||
#include "forwards.h"
|
||||
|
||||
/**
|
||||
* @file extension.cpp
|
||||
@ -53,6 +55,8 @@ SMEXT_LINK(&g_CStrike);
|
||||
|
||||
extern sp_nativeinfo_t g_CSNatives[];
|
||||
|
||||
ISDKTools *g_pSDKTools = NULL;
|
||||
|
||||
bool CStrike::SDK_OnLoad(char *error, size_t maxlength, bool late)
|
||||
{
|
||||
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->RegisterLibrary(myself, "cstrike");
|
||||
plsys->AddPluginsListener(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;
|
||||
}
|
||||
|
||||
@ -99,11 +117,26 @@ void CStrike::SDK_OnUnload()
|
||||
}
|
||||
g_RegNatives.UnregisterAll();
|
||||
gameconfs->CloseGameConfigFile(g_pGameConf);
|
||||
plsys->RemovePluginsListener(this);
|
||||
playerhelpers->UnregisterCommandTargetProcessor(this);
|
||||
forwards->ReleaseForward(g_pHandleBuyForward);
|
||||
forwards->ReleaseForward(g_pPriceForward);
|
||||
forwards->ReleaseForward(g_pTerminateRoundForward);
|
||||
forwards->ReleaseForward(g_pCSWeaponDropForward);
|
||||
}
|
||||
|
||||
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_end", 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;
|
||||
}
|
||||
|
||||
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 "CDetour/detours.h"
|
||||
#include <IBinTools.h>
|
||||
#include <ISDKTools.h>
|
||||
|
||||
/**
|
||||
* @brief Sample implementation of the SDK Extension.
|
||||
@ -46,7 +48,8 @@
|
||||
*/
|
||||
class CStrike :
|
||||
public SDKExtension,
|
||||
public ICommandTargetProcessor
|
||||
public ICommandTargetProcessor,
|
||||
public IPluginsListener
|
||||
{
|
||||
public:
|
||||
/**
|
||||
@ -91,6 +94,9 @@ public:
|
||||
const char *GetExtensionDateString();
|
||||
public:
|
||||
bool ProcessCommandTarget(cmd_target_info_t *info);
|
||||
public: //IPluginsListener
|
||||
void OnPluginLoaded(IPlugin *plugin);
|
||||
void OnPluginUnloaded(IPlugin *plugin);
|
||||
public:
|
||||
#if defined SMEXT_CONF_METAMOD
|
||||
/**
|
||||
@ -124,11 +130,21 @@ public:
|
||||
*/
|
||||
//virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength);
|
||||
#endif
|
||||
private:
|
||||
bool m_WeaponPriceDetourEnabled;
|
||||
bool m_TerminateRoundDetourEnabled;
|
||||
bool m_HandleBuyDetourEnabled;
|
||||
bool m_CSWeaponDetourEnabled;
|
||||
};
|
||||
|
||||
/* Interfaces from SourceMod */
|
||||
extern IBinTools *g_pBinTools;
|
||||
extern IGameConfig *g_pGameConf;
|
||||
extern ISDKTools *g_pSDKTools;
|
||||
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_
|
||||
|
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"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
<Configuration
|
||||
Name="Debug - Episode1|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
@ -681,6 +681,10 @@
|
||||
RelativePath="..\extension.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\forwards.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\natives.cpp"
|
||||
>
|
||||
@ -703,6 +707,10 @@
|
||||
RelativePath="..\extension.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\forwards.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\RegNatives.h"
|
||||
>
|
||||
@ -739,6 +747,34 @@
|
||||
>
|
||||
</File>
|
||||
</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>
|
||||
<Globals>
|
||||
</Globals>
|
||||
|
@ -31,10 +31,11 @@
|
||||
|
||||
#include "extension.h"
|
||||
#include "RegNatives.h"
|
||||
#include <server_class.h>
|
||||
|
||||
#define REGISTER_NATIVE_ADDR(name, code) \
|
||||
void *addr; \
|
||||
if (!g_pGameConf->GetMemSig(name, &addr)) \
|
||||
if (!g_pGameConf->GetMemSig(name, &addr) || !addr) \
|
||||
{ \
|
||||
return pContext->ThrowNativeError("Failed to locate function"); \
|
||||
} \
|
||||
@ -71,6 +72,59 @@ inline CBaseEntity *GetCBaseEntity(int num, bool isplayer)
|
||||
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 ICallWrapper *pWrapper = NULL;
|
||||
@ -120,10 +174,165 @@ static cell_t CS_SwitchTeam(IPluginContext *pContext, const cell_t *params)
|
||||
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[] =
|
||||
{
|
||||
{"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}
|
||||
};
|
||||
|
||||
|
@ -59,16 +59,17 @@
|
||||
#define SMEXT_CONF_METAMOD
|
||||
|
||||
/** 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_PLAYERHELPERS
|
||||
//#define SMEXT_ENABLE_DBMANAGER
|
||||
#define SMEXT_ENABLE_GAMECONF
|
||||
//#define SMEXT_ENABLE_MEMUTILS
|
||||
//#define SMEXT_ENABLE_GAMEHELPERS
|
||||
#define SMEXT_ENABLE_GAMEHELPERS
|
||||
#define SMEXT_ENABLE_TIMERSYS
|
||||
//#define SMEXT_ENABLE_THREADER
|
||||
//#define SMEXT_ENABLE_LIBSYS
|
||||
#define SMEXT_ENABLE_USERMSGS
|
||||
#define SMEXT_ENABLE_PLUGINSYS
|
||||
|
||||
#endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
|
||||
|
@ -76,6 +76,9 @@
|
||||
#if defined SMEXT_ENABLE_USERMSGS
|
||||
#include <IUserMessages.h>
|
||||
#endif
|
||||
#if defined SMEXT_ENABLE_PLUGINSYS
|
||||
#include <IPluginSys.h>
|
||||
#endif
|
||||
|
||||
#if defined SMEXT_CONF_METAMOD
|
||||
#include <ISmmPlugin.h>
|
||||
@ -266,6 +269,10 @@ extern ILibrarySys *libsys;
|
||||
#if defined SMEXT_ENABLE_USERMSGS
|
||||
extern IUserMessages *usermsgs;
|
||||
#endif
|
||||
#if defined SMEXT_ENABLE_PLUGINSYS
|
||||
extern IPluginManager *plsys;
|
||||
#endif
|
||||
|
||||
|
||||
#if defined SMEXT_CONF_METAMOD
|
||||
PLUGIN_GLOBALVARS();
|
||||
|
@ -13,6 +13,16 @@
|
||||
{
|
||||
"cstrike"
|
||||
{
|
||||
"Offsets"
|
||||
{
|
||||
//Offset of szClassName in CCSWeaponInfo
|
||||
"WeaponName"
|
||||
{
|
||||
"windows" "6"
|
||||
"linux" "6"
|
||||
"mac" "6"
|
||||
}
|
||||
}
|
||||
"Signatures"
|
||||
{
|
||||
"RoundRespawn"
|
||||
@ -29,6 +39,41 @@
|
||||
"linux" "@_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_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.
|
||||
*
|
||||
@ -64,6 +128,42 @@ native CS_RespawnPlayer(client);
|
||||
*/
|
||||
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!
|
||||
*/
|
||||
@ -84,5 +184,9 @@ public __ext_cstrike_SetNTVOptional()
|
||||
{
|
||||
MarkNativeAsOptional("CS_RespawnPlayer");
|
||||
MarkNativeAsOptional("CS_SwitchTeam");
|
||||
MarkNativeAsOptional("CS_DropWeapon");
|
||||
MarkNativeAsOptional("CS_TerminateRound");
|
||||
MarkNativeAsOptional("CS_GetTranslatedWeaponAlias");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user