Add a number of useful forwards and natives to the cstrike extension (bug 4732, r=fyren).

This commit is contained in:
Drifer 2011-06-26 01:25:42 -07:00
parent 177cc87985
commit e0f670499c
17 changed files with 1753 additions and 9 deletions

View File

@ -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)

View 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_

View 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 = &wr;
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;
}
}

View 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_

View File

@ -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); \

View 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;
}
}
*/

View 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__

View File

@ -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;;
}
}

View File

@ -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_

View 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;
}

View 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_

View File

@ -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>

View File

@ -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}
};

View File

@ -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_

View File

@ -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();

View File

@ -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"
}
}
}
}

View File

@ -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