Compare commits
198
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0cf8f2b11f | ||
|
|
91cc6b300c | ||
|
|
ec3351a320 | ||
|
|
aa2a71fc67 | ||
|
|
142e76bd92 | ||
|
|
632429700b | ||
|
|
a8d95ca044 | ||
|
|
28b4b248f7 | ||
|
|
964c9ae835 | ||
|
|
d099dbb659 | ||
|
|
688e274c30 | ||
|
|
a5ff53f00f | ||
|
|
38fc001a1d | ||
|
|
4b949e8228 | ||
|
|
080c4bb8e2 | ||
|
|
149b16cbf3 | ||
|
|
b940b01be4 | ||
|
|
45258b8561 | ||
|
|
ab7b9b231d | ||
|
|
76c2addeb9 | ||
|
|
7cb7703cf9 | ||
|
|
c2b204e810 | ||
|
|
24c92c021c | ||
|
|
dd370c3904 | ||
|
|
384fb668d1 | ||
|
|
1903c0bd2b | ||
|
|
13034eff05 | ||
|
|
a4703722be | ||
|
|
2e13460823 | ||
|
|
d356d80537 | ||
|
|
412759cbc4 | ||
|
|
08841227a4 | ||
|
|
18b2ffa786 | ||
|
|
3e9a48c778 | ||
|
|
339fd85c6f | ||
|
|
59144cf868 | ||
|
|
ad3a44427a | ||
|
|
737d8c3240 | ||
|
|
4688783baf | ||
|
|
ba5c6ad07c | ||
|
|
69045ccd52 | ||
|
|
cf39d4b820 | ||
|
|
cb218a4911 | ||
|
|
344284196c | ||
|
|
e7c0726acd | ||
|
|
d0b34e783d | ||
|
|
d481e5fcb7 | ||
|
|
1459c10112 | ||
|
|
8b9131f6e4 | ||
|
|
ce1a21ba0b | ||
|
|
e2237ce875 | ||
|
|
250bbf5955 | ||
|
|
0478912d73 | ||
|
|
d50e4b7a2f | ||
|
|
abf59a4f4b | ||
|
|
9b80e46097 | ||
|
|
bbbaebd32a | ||
|
|
1204d1fd0f | ||
|
|
ef34cc67b6 | ||
|
|
25184aa9c7 | ||
|
|
b789d30658 | ||
|
|
305799b441 | ||
|
|
92fc27f67f | ||
|
|
5292721f0b | ||
|
|
4e0c47a1d5 | ||
|
|
65b6602a19 | ||
|
|
1723f97202 | ||
|
|
ec92653bb7 | ||
|
|
b25d25890a | ||
|
|
b27243c3ac | ||
|
|
be648382d7 | ||
|
|
b5ed266941 | ||
|
|
2ffaa04130 | ||
|
|
eddf9cf843 | ||
|
|
7c1095e4e7 | ||
|
|
686e8b4d7e | ||
|
|
edc73649ff | ||
|
|
1ad8bb4560 | ||
|
|
7eabeaa384 | ||
|
|
4547270934 | ||
|
|
3a2c5cb0f7 | ||
|
|
7648c7fe03 | ||
|
|
ffb6d769e8 | ||
|
|
77f999f810 | ||
|
|
c5049990a3 | ||
|
|
0e4894e8db | ||
|
|
19f1ffa401 | ||
|
|
c206ac3099 | ||
|
|
e03320c80e | ||
|
|
2ae4624344 | ||
|
|
66c8005666 | ||
|
|
210219030e | ||
|
|
370fa8209b | ||
|
|
88a0a458a6 | ||
|
|
e4afe31fc0 | ||
|
|
fb0144900f | ||
|
|
542b7673d6 | ||
|
|
cc1c02e3de | ||
|
|
4fc0a85581 | ||
|
|
6733c4182d | ||
|
|
28ef59ada5 | ||
|
|
647e320b57 | ||
|
|
cdfbdcc29d | ||
|
|
4f94b4f900 | ||
|
|
53be0e0812 | ||
|
|
88dd142f5c | ||
|
|
1d5ec7b982 | ||
|
|
f1a71b0d5a | ||
|
|
85b8e09e8a | ||
|
|
df9b0ef1b3 | ||
|
|
f81412b4fa | ||
|
|
bd478ebdfc | ||
|
|
a57573864a | ||
|
|
6b6436db17 | ||
|
|
c88b18e520 | ||
|
|
8c001872a8 | ||
|
|
5db571bbc6 | ||
|
|
1f561b54f3 | ||
|
|
77a0fb4402 | ||
|
|
c72736dcfa | ||
|
|
d16f45c4b6 | ||
|
|
e280d33a28 | ||
|
|
fb1d501c24 | ||
|
|
3c0f700a6b | ||
|
|
45e9da90fc | ||
|
|
a60c443b29 | ||
|
|
b064366169 | ||
|
|
a06a903989 | ||
|
|
e67fdf2331 | ||
|
|
e2ca3a9493 | ||
|
|
9099634b95 | ||
|
|
f182feed9d | ||
|
|
02dd6a9c00 | ||
|
|
b421a0c594 | ||
|
|
9ec5cb819c | ||
|
|
f8e664b797 | ||
|
|
42575f7cc3 | ||
|
|
aa98f60ca3 | ||
|
|
d5f47a88e6 | ||
|
|
62f08a31fc | ||
|
|
e565c83494 | ||
|
|
a68ff2a587 | ||
|
|
377888276e | ||
|
|
52b8e95aae | ||
|
|
bb6e6fd3b6 | ||
|
|
0449342bb0 | ||
|
|
3171d4fb7b | ||
|
|
066818fbec | ||
|
|
255f5ea745 | ||
|
|
2e763c287c | ||
|
|
29c33bfcc9 | ||
|
|
88bdbf4866 | ||
|
|
1559b75ae7 | ||
|
|
ff00e3d036 | ||
|
|
daa1a4df14 | ||
|
|
4e2ace9d2f | ||
|
|
ee722d778f | ||
|
|
e82f0acdb8 | ||
|
|
383cd81be6 | ||
|
|
6250db7dba | ||
|
|
8ae3be7185 | ||
|
|
64b897822f | ||
|
|
0ad0d839a0 | ||
|
|
55382e0a0f | ||
|
|
d8b00675d6 | ||
|
|
633298b6a9 | ||
|
|
656a9fa94f | ||
|
|
bb7929594d | ||
|
|
d4545a105e | ||
|
|
1c788b5c0a | ||
|
|
7ce8a80bf6 | ||
|
|
d0c4657b75 | ||
|
|
167e6d8571 | ||
|
|
b909ea07ec | ||
|
|
410eb01f91 | ||
|
|
f18c6fb9af | ||
|
|
c66175d64e | ||
|
|
a3fbbe41f2 | ||
|
|
ed5713b3fd | ||
|
|
4b01feea45 | ||
|
|
800f613f92 | ||
|
|
4c9e856197 | ||
|
|
0cb3fcaed8 | ||
|
|
02e2659ba5 | ||
|
|
507b09c975 | ||
|
|
907fa77381 | ||
|
|
c5c302dccc | ||
|
|
c49d51206c | ||
|
|
f48cbaab75 | ||
|
|
61c634a870 | ||
|
|
fec978afb4 | ||
|
|
74ad70ba30 | ||
|
|
fe27c8810e | ||
|
|
b4c5726fc2 | ||
|
|
53c8d5533a | ||
|
|
4e66452e58 | ||
|
|
63545eb741 | ||
|
|
a7af242453 |
@@ -3,9 +3,11 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- '[0-9]+.[0-9]+-dev'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- '[0-9]+.[0-9]+-dev'
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
@@ -21,8 +23,8 @@ jobs:
|
||||
compiler_cxx: clang++
|
||||
- os: ubuntu-18.04
|
||||
os_short: linux
|
||||
compiler_cc: clang-3.9
|
||||
compiler_cxx: clang++-3.9
|
||||
compiler_cc: clang-8
|
||||
compiler_cxx: clang++-8
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: ${{ matrix.os_short }}-${{ matrix.compiler_cc }}
|
||||
|
||||
@@ -3,6 +3,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- '[0-9]+.[0-9]+-dev'
|
||||
paths:
|
||||
- 'plugins/include/*'
|
||||
- 'sourcepawn/**'
|
||||
|
||||
+4
-1
@@ -28,7 +28,7 @@ LIB-Debug/
|
||||
[Tt]humbs.db
|
||||
|
||||
# AMBuild build directories
|
||||
build/
|
||||
build*/
|
||||
obj-*/
|
||||
*~
|
||||
*.rej
|
||||
@@ -37,3 +37,6 @@ obj-*/
|
||||
*.swp
|
||||
*.gdb_history
|
||||
objdir
|
||||
|
||||
.vs/*
|
||||
.vscode/*
|
||||
@@ -1,6 +1,8 @@
|
||||
[submodule "public/amtl"]
|
||||
path = public/amtl
|
||||
url = https://github.com/alliedmodders/amtl
|
||||
shallow = true
|
||||
[submodule "sourcepawn"]
|
||||
path = sourcepawn
|
||||
url = https://github.com/alliedmodders/sourcepawn
|
||||
shallow = true
|
||||
|
||||
+84
-7
@@ -1,6 +1,7 @@
|
||||
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
|
||||
import collections
|
||||
import os, sys
|
||||
import subprocess
|
||||
import traceback
|
||||
|
||||
class SDK(object):
|
||||
@@ -31,11 +32,21 @@ class SDK(object):
|
||||
WinOnly = ['windows']
|
||||
WinLinux = ['windows', 'linux']
|
||||
WinLinuxMac = ['windows', 'linux', 'mac']
|
||||
Blade = {
|
||||
'windows': ['x86', 'x86_64'],
|
||||
'linux': ['x86_64'],
|
||||
'mac': ['x86_64']
|
||||
}
|
||||
CSGO = {
|
||||
'windows': ['x86'],
|
||||
'linux': ['x86', 'x86_64'],
|
||||
'mac': ['x86_64']
|
||||
}
|
||||
Mock = {
|
||||
'windows': ['x86', 'x86_64'],
|
||||
'linux': ['x86', 'x86_64'],
|
||||
'mac': ['x86_64']
|
||||
}
|
||||
|
||||
SDKMap = {
|
||||
'episode1': SDK('HL2SDK', '2.ep1', '1', 'EPISODEONE', WinLinux, 'episode1'),
|
||||
@@ -54,11 +65,12 @@ SDKMap = {
|
||||
'eye': SDK('HL2SDK-EYE', '2.eye', '5', 'EYE', WinOnly, 'eye'),
|
||||
'csgo': SDK('HL2SDKCSGO', '2.csgo', '21', 'CSGO', CSGO, 'csgo'),
|
||||
'portal2': SDK('HL2SDKPORTAL2', '2.portal2', '17', 'PORTAL2', [], 'portal2'),
|
||||
'blade': SDK('HL2SDKBLADE', '2.blade', '18', 'BLADE', WinOnly, 'blade'),
|
||||
'blade': SDK('HL2SDKBLADE', '2.blade', '18', 'BLADE', Blade, 'blade'),
|
||||
'insurgency': SDK('HL2SDKINSURGENCY', '2.insurgency', '19', 'INSURGENCY', WinLinuxMac, 'insurgency'),
|
||||
'contagion': SDK('HL2SDKCONTAGION', '2.contagion', '14', 'CONTAGION', WinOnly, 'contagion'),
|
||||
'bms': SDK('HL2SDKBMS', '2.bms', '10', 'BMS', WinLinux, 'bms'),
|
||||
'doi': SDK('HL2SDKDOI', '2.doi', '20', 'DOI', WinLinuxMac, 'doi'),
|
||||
'mock': SDK('HL2SDK-MOCK', '2.mock', '999', 'MOCK', Mock, 'mock'),
|
||||
}
|
||||
|
||||
# Stable sorting for command equivalence in AMBuild.
|
||||
@@ -110,7 +122,8 @@ class SMConfig(object):
|
||||
self.versionlib = None
|
||||
self.all_targets = []
|
||||
self.target_archs = set()
|
||||
|
||||
self.enable_asan = getattr(builder.options, 'enable_asan', False)
|
||||
self.asan_libs = {}
|
||||
|
||||
if builder.options.targets:
|
||||
target_archs = builder.options.targets.split(',')
|
||||
@@ -173,7 +186,7 @@ class SMConfig(object):
|
||||
else:
|
||||
sdk_path = ResolveEnvPath(sdk.envvar, sdk.folder)
|
||||
if sdk_path is None or not os.path.isdir(sdk_path):
|
||||
if use_all or sdk_name in sdk_list:
|
||||
if (use_all and sdk_name != 'mock') or sdk_name in sdk_list:
|
||||
raise Exception('Could not find a valid path for {0}'.format(sdk.envvar))
|
||||
continue
|
||||
if use_all or use_present or sdk_name in sdk_list:
|
||||
@@ -186,7 +199,9 @@ class SMConfig(object):
|
||||
if builder.options.mms_path:
|
||||
self.mms_root = builder.options.mms_path
|
||||
else:
|
||||
self.mms_root = ResolveEnvPath('MMSOURCE110', 'mmsource-1.10')
|
||||
self.mms_root = ResolveEnvPath('MMSOURCE111', 'mmsource-1.11')
|
||||
if not self.mms_root:
|
||||
self.mms_root = ResolveEnvPath('MMSOURCE110', 'mmsource-1.10')
|
||||
if not self.mms_root:
|
||||
self.mms_root = ResolveEnvPath('MMSOURCE_DEV', 'metamod-source')
|
||||
if not self.mms_root:
|
||||
@@ -336,6 +351,11 @@ class SMConfig(object):
|
||||
cxx.cxxflags += ['-Wno-deprecated']
|
||||
cxx.cflags += ['-Wno-sometimes-uninitialized']
|
||||
|
||||
if self.enable_asan:
|
||||
if not have_clang:
|
||||
raise Exception('--enable-asan only supported when using Clang')
|
||||
self.configure_asan(cxx)
|
||||
|
||||
# Work around SDK warnings.
|
||||
if cxx.version >= 'clang-10.0' or cxx.version >= 'apple-clang-12.0':
|
||||
cxx.cflags += [
|
||||
@@ -348,12 +368,18 @@ class SMConfig(object):
|
||||
cxx.cflags += ['-Wno-maybe-uninitialized']
|
||||
|
||||
if builder.options.opt == '1':
|
||||
cxx.cflags += ['-O3']
|
||||
if self.enable_asan:
|
||||
cxx.cflags += ['-O1']
|
||||
else:
|
||||
cxx.cflags += ['-O3']
|
||||
|
||||
# Don't omit the frame pointer.
|
||||
cxx.cflags += ['-fno-omit-frame-pointer']
|
||||
|
||||
def configure_msvc(self, cxx):
|
||||
if self.enable_asan:
|
||||
raise Exception('--enable-asan only supported when using Clang')
|
||||
|
||||
if builder.options.debug == '1':
|
||||
cxx.cflags += ['/MTd']
|
||||
cxx.linkflags += ['/NODEFAULTLIB:libcmt']
|
||||
@@ -399,6 +425,29 @@ class SMConfig(object):
|
||||
# Don't omit the frame pointer.
|
||||
cxx.cflags += ['/Oy-']
|
||||
|
||||
def configure_asan(self, cxx):
|
||||
if cxx.target.platform != 'linux':
|
||||
raise Exception('--enable-asan only supported on Linux')
|
||||
cxx.cflags += ['-fsanitize=address']
|
||||
cxx.linkflags += ['-fsanitize=address']
|
||||
if cxx.target.arch == 'x86':
|
||||
libclang_rt = 'libclang_rt.asan-i386.so'
|
||||
else:
|
||||
libclang_rt = 'libclang_rt.asan-x86_64.so'
|
||||
|
||||
try:
|
||||
argv = cxx.cxx_argv + ['--print-file-name', libclang_rt]
|
||||
output = subprocess.check_output(argv)
|
||||
output = output.decode('utf-8')
|
||||
output = output.strip()
|
||||
except:
|
||||
raise Exception('Could not find {}'.format(libclang_rt))
|
||||
|
||||
print('ASAN library for {}: {}'.format(cxx.target.arch, output))
|
||||
print('You will need to LD_PRELOAD this into srcds.')
|
||||
|
||||
self.asan_libs[cxx.target.arch] = os.path.dirname(output)
|
||||
|
||||
def configure_linux(self, cxx):
|
||||
cxx.defines += ['_LINUX', 'POSIX', '_FILE_OFFSET_BITS=64']
|
||||
cxx.linkflags += ['-lm']
|
||||
@@ -447,7 +496,7 @@ class SMConfig(object):
|
||||
'-current_version', self.productVersion
|
||||
]
|
||||
if self.use_auto_versioning():
|
||||
binary.compiler.linkflags += [self.versionlib[binary.compiler.target.arch]]
|
||||
binary.compiler.postlink += [self.versionlib[binary.compiler.target.arch]]
|
||||
binary.compiler.sourcedeps += SM.generated_headers
|
||||
return binary
|
||||
|
||||
@@ -456,6 +505,13 @@ class SMConfig(object):
|
||||
self.AddVersioning(binary)
|
||||
if binary.compiler.like('msvc'):
|
||||
binary.compiler.linkflags += ['/SUBSYSTEM:WINDOWS']
|
||||
|
||||
# Dumb clang behavior means we have to manually find libclang_rt.
|
||||
if self.enable_asan:
|
||||
binary.compiler.linkflags += [
|
||||
'-shared-libsan',
|
||||
'-Wl,-rpath={}'.format(self.asan_libs[binary.compiler.target.arch]),
|
||||
]
|
||||
return binary
|
||||
|
||||
def ProgramBuilder(self, compiler, name):
|
||||
@@ -463,6 +519,8 @@ class SMConfig(object):
|
||||
self.AddVersioning(binary)
|
||||
if '-static-libgcc' in binary.compiler.linkflags:
|
||||
binary.compiler.linkflags.remove('-static-libgcc')
|
||||
if self.enable_asan:
|
||||
binary.compiler.linkflags.append('-static-libsan')
|
||||
if '-lgcc_eh' in binary.compiler.linkflags:
|
||||
binary.compiler.linkflags.remove('-lgcc_eh')
|
||||
if binary.compiler.like('gcc'):
|
||||
@@ -569,7 +627,25 @@ class SMConfig(object):
|
||||
if compiler.target.platform == 'linux':
|
||||
if sdk.name in ['csgo', 'blade']:
|
||||
compiler.linkflags.remove('-static-libstdc++')
|
||||
compiler.linkflags += ['-lstdc++']
|
||||
compiler.defines += ['_GLIBCXX_USE_CXX11_ABI=0']
|
||||
elif compiler.target.platform == 'mac':
|
||||
if sdk.name in ['csgo']:
|
||||
# Switch libc++ to libstdc++ for protobuf linkage.
|
||||
compiler.cxxflags.remove('-stdlib=libc++')
|
||||
compiler.linkflags.remove('-stdlib=libc++')
|
||||
compiler.linkflags.remove('-lc++')
|
||||
|
||||
compiler.cxxflags += ['-stdlib=libstdc++']
|
||||
compiler.linkflags += ['-stdlib=libstdc++']
|
||||
compiler.linkflags += ['-lstdc++']
|
||||
|
||||
if 'c++1y' in compiler.cxxflags:
|
||||
compiler.cxxflags.remove('-std=c++1y')
|
||||
compiler.cxxflags += ['-std=c++11']
|
||||
elif 'c++14' in compiler.cxxflags:
|
||||
compiler.cxxflags.remove('-std=c++14')
|
||||
compiler.cxxflags += ['-std=c++11']
|
||||
|
||||
for path in paths:
|
||||
compiler.cxxincludes += [os.path.join(sdk.path, *path)]
|
||||
@@ -613,7 +689,7 @@ class SMConfig(object):
|
||||
if compiler.target.platform == 'linux':
|
||||
if sdk.name in ['css', 'hl2dm', 'dods', 'tf2', 'sdk2013', 'bms', 'nucleardawn', 'l4d2', 'insurgency', 'doi']:
|
||||
dynamic_libs = ['libtier0_srv.so', 'libvstdlib_srv.so']
|
||||
elif compiler.target.arch == 'x86_64' and sdk.name in ['csgo', 'blade']:
|
||||
elif compiler.target.arch == 'x86_64' and sdk.name in ['csgo', 'mock']:
|
||||
dynamic_libs = ['libtier0_client.so', 'libvstdlib_client.so']
|
||||
elif sdk.name in ['l4d', 'blade', 'insurgency', 'doi', 'csgo']:
|
||||
dynamic_libs = ['libtier0.so', 'libvstdlib.so']
|
||||
@@ -746,6 +822,7 @@ else:
|
||||
'extensions/clientprefs/AMBuilder',
|
||||
'extensions/curl/AMBuilder',
|
||||
'extensions/cstrike/AMBuilder',
|
||||
'extensions/dhooks/AMBuilder',
|
||||
'extensions/geoip/AMBuilder',
|
||||
'extensions/mysql/AMBuilder',
|
||||
'extensions/pgsql/AMBuilder',
|
||||
|
||||
+2
-1
@@ -3,6 +3,7 @@ image: Visual Studio 2015
|
||||
clone_folder: c:/projects/sourcemod
|
||||
clone_depth: 1
|
||||
install:
|
||||
- cmd: set PATH=C:\Python38;C:\Python38\Scripts;%PATH%
|
||||
- cmd: git submodule update --init --recursive
|
||||
- cmd: git pull --recurse-submodules
|
||||
- cmd: cd ..
|
||||
@@ -12,5 +13,5 @@ build_script:
|
||||
- cmd: call "%VS140COMNTOOLS%/vsvars32.bat"
|
||||
- cmd: mkdir build
|
||||
- cmd: cd build
|
||||
- cmd: c:\python27\python.exe ../configure.py --enable-optimize --no-mysql --sdks=episode1,css,tf2,l4d2,csgo
|
||||
- cmd: python.exe ../configure.py --enable-optimize --no-mysql --sdks=episode1,css,tf2,l4d2,csgo
|
||||
- cmd: ambuild
|
||||
|
||||
@@ -115,6 +115,8 @@ public:
|
||||
virtual void ConsolePrint(const char *fmt, ...) = 0;
|
||||
virtual void ConsolePrintVa(const char *fmt, va_list ap) = 0;
|
||||
|
||||
virtual void FormatSourceBinaryName(const char *basename, char *buffer, size_t maxlength) = 0;
|
||||
|
||||
// Game engine helper functions.
|
||||
virtual bool IsClientConVarQueryingSupported() = 0;
|
||||
virtual int QueryClientConVar(int client, const char *cvar) = 0;
|
||||
|
||||
@@ -73,6 +73,9 @@ struct sm_logic_t
|
||||
void (*FreeCellArray)(ICellArray *arr);
|
||||
void * (*FromPseudoAddress)(uint32_t pseudoAddr);
|
||||
uint32_t (*ToPseudoAddress)(void *addr);
|
||||
void (*SetEntityLumpWritable)(bool writable);
|
||||
bool (*ParseEntityLumpString)(const char *entityString, int &status, size_t &position);
|
||||
const char * (*GetEntityLumpString)();
|
||||
IScriptManager *scripts;
|
||||
IShareSys *sharesys;
|
||||
IExtensionSys *extsys;
|
||||
|
||||
+19
-17
@@ -1,30 +1,30 @@
|
||||
# vim: set ts=2 sw=2 tw=99 noet:
|
||||
import sys
|
||||
try:
|
||||
from ambuild2 import run, util
|
||||
from ambuild2 import run, util
|
||||
except:
|
||||
try:
|
||||
import ambuild
|
||||
sys.stderr.write('It looks like you have AMBuild 1 installed, but this project uses AMBuild 2.\n')
|
||||
sys.stderr.write('Upgrade to the latest version of AMBuild to continue.\n')
|
||||
except:
|
||||
sys.stderr.write('AMBuild must be installed to build this project.\n')
|
||||
sys.stderr.write('http://www.alliedmods.net/ambuild\n')
|
||||
sys.exit(1)
|
||||
try:
|
||||
import ambuild
|
||||
sys.stderr.write('It looks like you have AMBuild 1 installed, but this project uses AMBuild 2.\n')
|
||||
sys.stderr.write('Upgrade to the latest version of AMBuild to continue.\n')
|
||||
except:
|
||||
sys.stderr.write('AMBuild must be installed to build this project.\n')
|
||||
sys.stderr.write('http://www.alliedmods.net/ambuild\n')
|
||||
sys.exit(1)
|
||||
|
||||
# Hack to show a decent upgrade message, which wasn't done until 2.2.
|
||||
ambuild_version = getattr(run, 'CURRENT_API', '2.1')
|
||||
if ambuild_version.startswith('2.1'):
|
||||
sys.stderr.write("AMBuild 2.2 or higher is required; please update\n")
|
||||
sys.exit(1)
|
||||
sys.stderr.write("AMBuild 2.2 or higher is required; please update\n")
|
||||
sys.exit(1)
|
||||
|
||||
parser = run.BuildParser(sourcePath=sys.path[0], api='2.2')
|
||||
parser.options.add_argument('--hl2sdk-root', type=str, dest='hl2sdk_root', default=None,
|
||||
help='Root search folder for HL2SDKs')
|
||||
help='Root search folder for HL2SDKs')
|
||||
parser.options.add_argument('--mysql-path', type=str, dest='mysql_path', default=None,
|
||||
help='Path to MySQL 5')
|
||||
help='Path to MySQL 5')
|
||||
parser.options.add_argument('--mysql64-path', type=str, dest='mysql64_path', default=None,
|
||||
help='Path to 64-bit MySQL 5')
|
||||
help='Path to 64-bit MySQL 5')
|
||||
parser.options.add_argument('--mms-path', type=str, dest='mms_path', default=None,
|
||||
help='Path to Metamod:Source')
|
||||
parser.options.add_argument('--enable-debug', action='store_const', const='1', dest='debug',
|
||||
@@ -37,11 +37,13 @@ parser.options.add_argument('-s', '--sdks', default='present', dest='sdks',
|
||||
help='Build against specified SDKs; valid args are "none", "all", "present",'
|
||||
' or comma-delimited list of engine names')
|
||||
parser.options.add_argument('--breakpad-dump', action='store_true', dest='breakpad_dump',
|
||||
default=False, help='Dump and upload breakpad symbols')
|
||||
default=False, help='Dump and upload breakpad symbols')
|
||||
parser.options.add_argument('--disable-auto-versioning', action='store_true', dest='disable_auto_versioning',
|
||||
default=False, help='Disable the auto versioning script')
|
||||
parser.options.add_argument('--targets', type=str, dest='targets', default=None,
|
||||
help="Override the target architecture (use commas to separate multiple targets).")
|
||||
help="Override the target architecture (use commas to separate multiple targets).")
|
||||
parser.options.add_argument('--scripting-only', action='store_true', dest='scripting_only', default=False,
|
||||
help="Only build and package the files required for scripting in SourcePawn.")
|
||||
help="Only build and package the files required for scripting in SourcePawn.")
|
||||
parser.options.add_argument('--enable-asan', action='store_true', dest='enable_asan',
|
||||
default=False, help='Enable ASAN (clang only)')
|
||||
parser.Configure()
|
||||
|
||||
+42
-74
@@ -4,43 +4,42 @@ import os
|
||||
project = builder.LibraryProject('sourcemod')
|
||||
|
||||
project.sources += [
|
||||
'MenuStyle_Valve.cpp',
|
||||
'logic_bridge.cpp',
|
||||
'smn_entities.cpp',
|
||||
'sm_stringutil.cpp',
|
||||
'MenuVoting.cpp',
|
||||
'smn_events.cpp',
|
||||
'frame_hooks.cpp',
|
||||
'smn_nextmap.cpp',
|
||||
'sourcemm_api.cpp',
|
||||
'ChatTriggers.cpp',
|
||||
'smn_player.cpp',
|
||||
'sourcemod.cpp',
|
||||
'concmd_cleaner.cpp',
|
||||
'HalfLife2.cpp',
|
||||
'NextMap.cpp',
|
||||
'ConCmdManager.cpp',
|
||||
'ConVarManager.cpp',
|
||||
'ConsoleDetours.cpp',
|
||||
'CoreConfig.cpp',
|
||||
'EventManager.cpp',
|
||||
'GameHooks.cpp',
|
||||
'HalfLife2.cpp',
|
||||
'Logger.cpp',
|
||||
'MenuManager.cpp',
|
||||
'MenuStyle_Base.cpp',
|
||||
'MenuStyle_Radio.cpp',
|
||||
'MenuStyle_Valve.cpp',
|
||||
'MenuVoting.cpp',
|
||||
'NextMap.cpp',
|
||||
'PlayerManager.cpp',
|
||||
'TimerSys.cpp',
|
||||
'UserMessages.cpp',
|
||||
'concmd_cleaner.cpp',
|
||||
'frame_hooks.cpp',
|
||||
'logic_bridge.cpp',
|
||||
'pb_handle.cpp',
|
||||
'sm_autonatives.cpp',
|
||||
'sm_stringutil.cpp',
|
||||
'smn_commandline.cpp',
|
||||
'smn_console.cpp',
|
||||
'smn_entities.cpp',
|
||||
'smn_events.cpp',
|
||||
'CoreConfig.cpp',
|
||||
'Logger.cpp',
|
||||
'smn_halflife.cpp',
|
||||
'smn_console.cpp',
|
||||
'UserMessages.cpp',
|
||||
'MenuManager.cpp',
|
||||
'smn_hudtext.cpp',
|
||||
'smn_keyvalues.cpp',
|
||||
'smn_nextmap.cpp',
|
||||
'smn_player.cpp',
|
||||
'smn_usermsgs.cpp',
|
||||
'MenuStyle_Base.cpp',
|
||||
'smn_keyvalues.cpp',
|
||||
'smn_vector.cpp',
|
||||
'sourcemm_api.cpp',
|
||||
'sourcemod.cpp',
|
||||
'EventManager.cpp',
|
||||
'MenuStyle_Radio.cpp',
|
||||
'sm_autonatives.cpp',
|
||||
'ConsoleDetours.cpp',
|
||||
'smn_commandline.cpp',
|
||||
'GameHooks.cpp',
|
||||
]
|
||||
|
||||
for sdk_name in SM.sdks:
|
||||
@@ -59,20 +58,18 @@ for sdk_name in SM.sdks:
|
||||
builder.sourcePath
|
||||
]
|
||||
|
||||
pb_includes = []
|
||||
if sdk.name == 'csgo':
|
||||
pb_includes = [
|
||||
compiler.cxxincludes += [
|
||||
os.path.join(sdk.path, 'common', 'protobuf-2.5.0', 'src'),
|
||||
os.path.join(sdk.path, 'public', 'engine', 'protobuf'),
|
||||
os.path.join(sdk.path, 'public', 'game', 'shared', 'csgo', 'protobuf')
|
||||
]
|
||||
elif sdk.name == 'blade':
|
||||
pb_includes = [
|
||||
compiler.cxxincludes += [
|
||||
os.path.join(sdk.path, 'common', 'protobuf-2.5.0', 'src'),
|
||||
os.path.join(sdk.path, 'public', 'engine', 'protobuf'),
|
||||
os.path.join(sdk.path, 'public', 'game', 'shared', 'berimbau', 'protobuf')
|
||||
]
|
||||
compiler.cxxincludes += pb_includes
|
||||
|
||||
if compiler.like('msvc'):
|
||||
compiler.defines += ['_ALLOW_KEYWORD_MACROS']
|
||||
@@ -88,21 +85,27 @@ for sdk_name in SM.sdks:
|
||||
compiler.linkflags += ['-Wl,--exclude-libs=libprotobuf.a']
|
||||
elif compiler.target.platform == 'mac':
|
||||
if compiler.target.arch == 'x86':
|
||||
lib_path = os.path.join(sdk.path, 'lib', 'osx32', 'release', 'libprotobuf-libcxx.a')
|
||||
lib_path = os.path.join(sdk.path, 'lib', 'osx32', 'release', 'libprotobuf.a')
|
||||
elif compiler.target.arch == 'x86_64':
|
||||
lib_path = os.path.join(sdk.path, 'lib', 'osx64', 'release', 'libprotobuf-libcxx.a')
|
||||
lib_path = os.path.join(sdk.path, 'lib', 'osx64', 'release', 'libprotobuf.a')
|
||||
elif compiler.target.platform == 'windows':
|
||||
msvc_ver = compiler.version
|
||||
vs_year = ''
|
||||
platform = ''
|
||||
if compiler.target.arch == 'x86':
|
||||
platform = 'win32'
|
||||
elif compiler.target.arch == 'x86_64':
|
||||
platform = 'win64'
|
||||
|
||||
if 1900 <= msvc_ver < 2000:
|
||||
vs_year = '2015'
|
||||
else:
|
||||
raise Exception('Cannot find libprotobuf for MSVC version "' + str(compiler.version) + '"')
|
||||
|
||||
if 'DEBUG' in compiler.defines:
|
||||
lib_path = os.path.join(sdk.path, 'lib', 'win32', 'debug', 'vs' + vs_year, 'libprotobuf.lib')
|
||||
lib_path = os.path.join(sdk.path, 'lib', platform, 'debug', 'vs' + vs_year, 'libprotobuf.lib')
|
||||
else:
|
||||
lib_path = os.path.join(sdk.path, 'lib', 'win32', 'release', 'vs' + vs_year, 'libprotobuf.lib')
|
||||
lib_path = os.path.join(sdk.path, 'lib', platform, 'release', 'vs' + vs_year, 'libprotobuf.lib')
|
||||
compiler.linkflags.insert(0, lib_path)
|
||||
|
||||
if sdk.name in ['csgo', 'blade']:
|
||||
@@ -115,53 +118,18 @@ for sdk_name in SM.sdks:
|
||||
'vprof_tool.cpp',
|
||||
]
|
||||
|
||||
pb_sources = []
|
||||
if sdk.name == 'csgo':
|
||||
pb_sources = [
|
||||
binary.sources += [
|
||||
os.path.join(sdk.path, 'public', 'engine', 'protobuf', 'netmessages.pb.cc'),
|
||||
os.path.join(sdk.path, 'public', 'game', 'shared', 'csgo', 'protobuf', 'cstrike15_usermessages.pb.cc'),
|
||||
os.path.join(sdk.path, 'public', 'game', 'shared', 'csgo', 'protobuf', 'cstrike15_usermessage_helpers.cpp'),
|
||||
]
|
||||
elif sdk.name == 'blade':
|
||||
pb_sources = [
|
||||
binary.sources += [
|
||||
os.path.join(sdk.path, 'public', 'engine', 'protobuf', 'netmessages.pb.cc'),
|
||||
os.path.join(sdk.path, 'public', 'game', 'shared', 'berimbau', 'protobuf', 'berimbau_usermessages.pb.cc'),
|
||||
os.path.join(sdk.path, 'public', 'game', 'shared', 'berimbau', 'protobuf', 'berimbau_usermessage_helpers.cpp'),
|
||||
]
|
||||
if len(pb_sources):
|
||||
binary.sources += pb_sources
|
||||
binary.compiler.cxxdefines += ['PROTOBUF_ENABLE']
|
||||
|
||||
if cxx.target.platform == 'mac' and sdk.name in ['csgo']:
|
||||
# We need a proxy library since the game uses libstdc++.
|
||||
pb_binary = SM.HL2Library(builder, cxx, 'pbproxy.' + sdk.ext, sdk)
|
||||
pb_binary.sources += pb_sources
|
||||
pb_binary.sources += ['pb_proxy.cpp']
|
||||
pb_binary.compiler.cxxincludes += pb_includes
|
||||
|
||||
# Switch from libc++ to libstdc++.
|
||||
pb_binary.compiler.cxxflags.remove('-stdlib=libc++')
|
||||
pb_binary.compiler.linkflags.remove('-lc++')
|
||||
pb_binary.compiler.linkflags.remove('-stdlib=libc++')
|
||||
pb_binary.compiler.cxxflags.append('-stdlib=libstdc++')
|
||||
pb_binary.compiler.linkflags.append('-lstdc++')
|
||||
pb_binary.compiler.linkflags.append('-stdlib=libstdc++')
|
||||
if '-std=c++1y' in pb_binary.compiler.cxxflags:
|
||||
pb_binary.compiler.cxxflags.remove('-std=c++1y')
|
||||
elif '-std=c++14' in pb_binary.compiler.cxxflags:
|
||||
pb_binary.compiler.cxxflags.remove('-std=c++14')
|
||||
|
||||
if cxx.target.arch == 'x86':
|
||||
pb_lib_path = os.path.join(sdk.path, 'lib', 'osx32', 'release', 'libprotobuf.a')
|
||||
elif cxx.target.arch == 'x86_64':
|
||||
pb_lib_path = os.path.join(sdk.path, 'lib', 'osx64', 'release', 'libprotobuf.a')
|
||||
pb_binary.compiler.linkflags.append(pb_lib_path)
|
||||
|
||||
SM.binaries += [builder.Add(pb_binary)]
|
||||
|
||||
binary.compiler.cxxdefines += [
|
||||
'PROTOBUF_PROXY_ENABLE',
|
||||
'PROTOBUF_PROXY_BINARY_NAME="pbproxy.{}"'.format(sdk.ext),
|
||||
]
|
||||
|
||||
SM.binaries += builder.Add(project)
|
||||
|
||||
|
||||
+1
-1
@@ -115,7 +115,7 @@ void GameHooks::OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t *pPla
|
||||
const char *cvarName, const char *cvarValue){
|
||||
int client = IndexOfEdict(pPlayer);
|
||||
|
||||
# if SOURCE_ENGINE == SE_CSGO
|
||||
# if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
if (g_Players.HandleConVarQuery(cookie, client, result, cvarName, cvarValue))
|
||||
return;
|
||||
# endif
|
||||
|
||||
+3
-2
@@ -183,6 +183,7 @@ void CHalfLife2::InitLogicalEntData()
|
||||
|| SOURCE_ENGINE == SE_CSS \
|
||||
|| SOURCE_ENGINE == SE_SDK2013 \
|
||||
|| SOURCE_ENGINE == SE_BMS \
|
||||
|| SOURCE_ENGINE == SE_BLADE \
|
||||
|| SOURCE_ENGINE == SE_NUCLEARDAWN
|
||||
|
||||
if (g_SMAPI->GetServerFactory(false)("VSERVERTOOLS003", nullptr))
|
||||
@@ -1233,7 +1234,7 @@ bool IsWindowsReservedDeviceName(const char *pMapname)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SOURCE_ENGINE >= SE_LEFT4DEAD && defined PLATFORM_WINDOWS
|
||||
#if SOURCE_ENGINE >= SE_LEFT4DEAD && defined PLATFORM_WINDOWS && SOURCE_ENGINE != SE_MOCK
|
||||
// This frees memory allocated by the game using the game's CRT on Windows,
|
||||
// avoiding a crash due to heap corruption (issue #910).
|
||||
template< class T, class I >
|
||||
@@ -1384,7 +1385,7 @@ bool CHalfLife2::IsMapValid(const char *map)
|
||||
if (!map || !map[0])
|
||||
return false;
|
||||
|
||||
return FindMap(map) != SMFindMapResult::NotFound;
|
||||
return FindMap(map) == SMFindMapResult::Found;
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE < SE_ORANGEBOX
|
||||
|
||||
+1
-1
@@ -235,7 +235,7 @@ public: //IGameHelpers
|
||||
bool IsMapValid(const char *map);
|
||||
SMFindMapResult FindMap(char *pMapName, size_t nMapNameMax);
|
||||
SMFindMapResult FindMap(const char *pMapName, char *pFoundMap = NULL, size_t nMapNameMax = 0);
|
||||
#if SOURCE_ENGINE >= SE_LEFT4DEAD && defined PLATFORM_WINDOWS
|
||||
#if SOURCE_ENGINE >= SE_LEFT4DEAD && defined PLATFORM_WINDOWS && SOURCE_ENGINE != SE_MOCK
|
||||
void FreeUtlVectorUtlString(CUtlVector<CUtlString, CUtlMemoryGlobalMalloc<CUtlString>> &vec);
|
||||
#endif
|
||||
bool GetMapDisplayName(const char *pMapName, char *pDisplayname, size_t nMapNameMax);
|
||||
|
||||
@@ -503,6 +503,10 @@ void CRadioMenuPlayer::Radio_Refresh()
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
// TODO: find what happens past 240 on CS:GO
|
||||
CCSUsrMsg_ShowMenu *msg = (CCSUsrMsg_ShowMenu *)g_UserMsgs.StartProtobufMessage(g_ShowMenuId, players, 1, USERMSG_BLOCKHOOKS);
|
||||
if (!msg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
msg->set_bits_valid_slots(display_keys);
|
||||
msg->set_display_time(time);
|
||||
msg->set_menu_string(ptr);
|
||||
|
||||
+102
-47
@@ -196,6 +196,7 @@ void PlayerManager::OnSourceModAllInitialized()
|
||||
m_clcommandkv_post = forwardsys->CreateForward("OnClientCommandKeyValues_Post", ET_Ignore, 2, NULL, Param_Cell, Param_Cell);
|
||||
m_clinfochanged = forwardsys->CreateForward("OnClientSettingsChanged", ET_Ignore, 1, p2);
|
||||
m_clauth = forwardsys->CreateForward("OnClientAuthorized", ET_Ignore, 2, NULL, Param_Cell, Param_String);
|
||||
m_cllang = forwardsys->CreateForward("OnClientLanguageChanged", ET_Ignore, 2, NULL, Param_Cell, Param_Cell);
|
||||
m_onActivate = forwardsys->CreateForward("OnServerLoad", ET_Ignore, 0, NULL);
|
||||
m_onActivate2 = forwardsys->CreateForward("OnMapStart", ET_Ignore, 0, NULL);
|
||||
|
||||
@@ -212,6 +213,9 @@ void PlayerManager::OnSourceModAllInitialized()
|
||||
SH_ADD_HOOK(ConCommand, Dispatch, pCmd, SH_STATIC(CmdMaxplayersCallback), true);
|
||||
maxplayersCmd = pCmd;
|
||||
}
|
||||
|
||||
gameevents->AddListener(this, "player_connect", true);
|
||||
gameevents->AddListener(this, "player_disconnect", true);
|
||||
}
|
||||
|
||||
void PlayerManager::OnSourceModShutdown()
|
||||
@@ -246,6 +250,7 @@ void PlayerManager::OnSourceModShutdown()
|
||||
forwardsys->ReleaseForward(m_clcommandkv_post);
|
||||
forwardsys->ReleaseForward(m_clinfochanged);
|
||||
forwardsys->ReleaseForward(m_clauth);
|
||||
forwardsys->ReleaseForward(m_cllang);
|
||||
forwardsys->ReleaseForward(m_onActivate);
|
||||
forwardsys->ReleaseForward(m_onActivate2);
|
||||
|
||||
@@ -259,6 +264,8 @@ void PlayerManager::OnSourceModShutdown()
|
||||
{
|
||||
SH_REMOVE_HOOK(ConCommand, Dispatch, maxplayersCmd, SH_STATIC(CmdMaxplayersCallback), true);
|
||||
}
|
||||
|
||||
gameevents->RemoveListener(this);
|
||||
}
|
||||
|
||||
ConfigResult PlayerManager::OnSourceModConfigChanged(const char *key,
|
||||
@@ -512,7 +519,7 @@ bool PlayerManager::OnClientConnect(edict_t *pEntity, const char *pszName, const
|
||||
/* Get the client's language */
|
||||
if (m_QueryLang)
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
pPlayer->m_LangId = translator->GetServerLanguage();
|
||||
#else
|
||||
const char *name;
|
||||
@@ -520,6 +527,8 @@ bool PlayerManager::OnClientConnect(edict_t *pEntity, const char *pszName, const
|
||||
{
|
||||
unsigned int langid;
|
||||
pPlayer->m_LangId = (translator->GetLanguageByName(name, &langid)) ? langid : translator->GetServerLanguage();
|
||||
|
||||
OnClientLanguageChanged(client, pPlayer->m_LangId);
|
||||
} else {
|
||||
pPlayer->m_LangId = translator->GetServerLanguage();
|
||||
}
|
||||
@@ -666,6 +675,8 @@ void PlayerManager::OnClientPutInServer(edict_t *pEntity, const char *playername
|
||||
&& (m_SourceTVUserId == userId
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
|| strcmp(playername, "GOTV") == 0
|
||||
#elif SOURCE_ENGINE == SE_BLADE
|
||||
|| strcmp(playername, "BBTV") == 0
|
||||
#elif (SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_TF2 || SOURCE_ENGINE == SE_SDK2013 \
|
||||
|| SOURCE_ENGINE == SE_BMS || SOURCE_ENGINE == SE_NUCLEARDAWN || SOURCE_ENGINE == SE_LEFT4DEAD2)
|
||||
|| (tv_name && strcmp(playername, tv_name->GetString()) == 0) || (tv_name && tv_name->GetString()[0] == 0 && strcmp(playername, "unnamed") == 0)
|
||||
@@ -721,7 +732,7 @@ void PlayerManager::OnClientPutInServer(edict_t *pEntity, const char *playername
|
||||
}
|
||||
pPlayer->Authorize_Post();
|
||||
}
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
else if(m_QueryLang)
|
||||
{
|
||||
// Not a bot
|
||||
@@ -781,7 +792,7 @@ void PlayerManager::OnServerHibernationUpdate(bool bHibernating)
|
||||
CPlayer *pPlayer = &m_Players[i];
|
||||
if (pPlayer->IsConnected() && pPlayer->IsFakeClient())
|
||||
{
|
||||
#if SOURCE_ENGINE < SE_LEFT4DEAD || SOURCE_ENGINE >= SE_CSGO || SOURCE_ENGINE == SE_NUCLEARDAWN
|
||||
#if SOURCE_ENGINE < SE_LEFT4DEAD || SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_NUCLEARDAWN
|
||||
// These games have the bug fixed where hltv/replay was getting kicked on hibernation
|
||||
if (pPlayer->IsSourceTV() || pPlayer->IsReplay())
|
||||
continue;
|
||||
@@ -1337,65 +1348,69 @@ void PlayerManager::OnClientSettingsChanged(edict_t *pEntity)
|
||||
m_clinfochanged->PushCell(client);
|
||||
m_clinfochanged->Execute(&res, NULL);
|
||||
|
||||
if (pPlayer->IsFakeClient())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IPlayerInfo *info = pPlayer->GetPlayerInfo();
|
||||
const char *new_name = info ? info->GetName() : engine->GetClientConVarValue(client, "name");
|
||||
const char *old_name = pPlayer->m_Name.c_str();
|
||||
|
||||
#if SOURCE_ENGINE >= SE_LEFT4DEAD
|
||||
const char *networkid_force;
|
||||
if ((networkid_force = engine->GetClientConVarValue(client, "networkid_force")) && networkid_force[0] != '\0')
|
||||
{
|
||||
unsigned int accountId = pPlayer->GetSteamAccountID();
|
||||
logger->LogMessage("\"%s<%d><STEAM_1:%d:%d><>\" has bad networkid (id \"%s\") (ip \"%s\")",
|
||||
new_name, pPlayer->GetUserId(), accountId & 1, accountId >> 1, networkid_force, pPlayer->GetIPAddress());
|
||||
|
||||
pPlayer->Kick("NetworkID spoofing detected.");
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (strcmp(old_name, new_name) != 0)
|
||||
{
|
||||
AdminId id = adminsys->FindAdminByIdentity("name", new_name);
|
||||
if (id != INVALID_ADMIN_ID && pPlayer->GetAdminId() != id)
|
||||
if (!pPlayer->IsFakeClient())
|
||||
{
|
||||
if (!CheckSetAdminName(client, pPlayer, id))
|
||||
AdminId id = adminsys->FindAdminByIdentity("name", new_name);
|
||||
if (id != INVALID_ADMIN_ID && pPlayer->GetAdminId() != id)
|
||||
{
|
||||
char kickMsg[128];
|
||||
logicore.CoreTranslate(kickMsg, sizeof(kickMsg), "%T", 2, NULL, "Name Reserved", &client);
|
||||
pPlayer->Kick(kickMsg);
|
||||
RETURN_META(MRES_IGNORED);
|
||||
if (!CheckSetAdminName(client, pPlayer, id))
|
||||
{
|
||||
char kickMsg[128];
|
||||
logicore.CoreTranslate(kickMsg, sizeof(kickMsg), "%T", 2, NULL, "Name Reserved", &client);
|
||||
pPlayer->Kick(kickMsg);
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
}
|
||||
} else if ((id = adminsys->FindAdminByIdentity("name", old_name)) != INVALID_ADMIN_ID) {
|
||||
if (id == pPlayer->GetAdminId())
|
||||
{
|
||||
/* This player is changing their name; force them to drop admin privileges! */
|
||||
pPlayer->SetAdminId(INVALID_ADMIN_ID, false);
|
||||
else if ((id = adminsys->FindAdminByIdentity("name", old_name)) != INVALID_ADMIN_ID) {
|
||||
if (id == pPlayer->GetAdminId())
|
||||
{
|
||||
/* This player is changing their name; force them to drop admin privileges! */
|
||||
pPlayer->SetAdminId(INVALID_ADMIN_ID, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pPlayer->SetName(new_name);
|
||||
}
|
||||
|
||||
if (m_PassInfoVar.size() > 0)
|
||||
if (!pPlayer->IsFakeClient())
|
||||
{
|
||||
/* Try for a password change */
|
||||
const char *old_pass = pPlayer->m_LastPassword.c_str();
|
||||
const char *new_pass = engine->GetClientConVarValue(client, m_PassInfoVar.c_str());
|
||||
if (strcmp(old_pass, new_pass) != 0)
|
||||
if (m_PassInfoVar.size() > 0)
|
||||
{
|
||||
pPlayer->m_LastPassword.assign(new_pass);
|
||||
if (pPlayer->IsInGame() && pPlayer->IsAuthorized())
|
||||
/* Try for a password change */
|
||||
const char* old_pass = pPlayer->m_LastPassword.c_str();
|
||||
const char* new_pass = engine->GetClientConVarValue(client, m_PassInfoVar.c_str());
|
||||
if (strcmp(old_pass, new_pass) != 0)
|
||||
{
|
||||
/* If there is already an admin id assigned, this will just bail out. */
|
||||
pPlayer->DoBasicAdminChecks();
|
||||
pPlayer->m_LastPassword.assign(new_pass);
|
||||
if (pPlayer->IsInGame() && pPlayer->IsAuthorized())
|
||||
{
|
||||
/* If there is already an admin id assigned, this will just bail out. */
|
||||
pPlayer->DoBasicAdminChecks();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE >= SE_LEFT4DEAD
|
||||
const char* networkid_force;
|
||||
if ((networkid_force = engine->GetClientConVarValue(client, "networkid_force")) && networkid_force[0] != '\0')
|
||||
{
|
||||
unsigned int accountId = pPlayer->GetSteamAccountID();
|
||||
logger->LogMessage("\"%s<%d><STEAM_1:%d:%d><>\" has bad networkid (id \"%s\") (ip \"%s\")",
|
||||
new_name, pPlayer->GetUserId(), accountId & 1, accountId >> 1, networkid_force, pPlayer->GetIPAddress());
|
||||
|
||||
pPlayer->Kick("NetworkID spoofing detected.");
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Notify Extensions */
|
||||
List<IClientListener *>::iterator iter;
|
||||
IClientListener *pListener = NULL;
|
||||
@@ -1409,6 +1424,13 @@ void PlayerManager::OnClientSettingsChanged(edict_t *pEntity)
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerManager::OnClientLanguageChanged(int client, unsigned int language)
|
||||
{
|
||||
m_cllang->PushCell(client);
|
||||
m_cllang->PushCell(language);
|
||||
m_cllang->Execute(NULL);
|
||||
}
|
||||
|
||||
int PlayerManager::GetMaxClients()
|
||||
{
|
||||
return m_maxClients;
|
||||
@@ -1428,6 +1450,11 @@ int PlayerManager::GetNumPlayers()
|
||||
return m_PlayerCount;
|
||||
}
|
||||
|
||||
int PlayerManager::GetNumClients()
|
||||
{
|
||||
return m_ClientCount;
|
||||
}
|
||||
|
||||
int PlayerManager::GetClientOfUserId(int userid)
|
||||
{
|
||||
if (userid < 0 || userid > USHRT_MAX)
|
||||
@@ -2008,7 +2035,7 @@ void CmdMaxplayersCallback()
|
||||
g_Players.MaxPlayersChanged();
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
bool PlayerManager::HandleConVarQuery(QueryCvarCookie_t cookie, int client, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue)
|
||||
{
|
||||
for (int i = 1; i <= m_maxClients; i++)
|
||||
@@ -2016,7 +2043,9 @@ bool PlayerManager::HandleConVarQuery(QueryCvarCookie_t cookie, int client, EQue
|
||||
if (m_Players[i].m_LanguageCookie == cookie)
|
||||
{
|
||||
unsigned int langid;
|
||||
m_Players[i].m_LangId = (translator->GetLanguageByName(cvarValue, &langid)) ? langid : translator->GetServerLanguage();
|
||||
unsigned int new_langid = (translator->GetLanguageByName(cvarValue, &langid)) ? langid : translator->GetServerLanguage();
|
||||
m_Players[i].m_LangId = new_langid;
|
||||
OnClientLanguageChanged(i, new_langid);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -2026,6 +2055,27 @@ bool PlayerManager::HandleConVarQuery(QueryCvarCookie_t cookie, int client, EQue
|
||||
}
|
||||
#endif
|
||||
|
||||
/* IGameEventListener2::FireGameEvent */
|
||||
void PlayerManager::FireGameEvent(IGameEvent *pEvent)
|
||||
{
|
||||
const char *name = pEvent->GetName();
|
||||
|
||||
if (strcmp(name, "player_connect") == 0)
|
||||
{
|
||||
const int client = pEvent->GetInt("index") + 1;
|
||||
const int userid = pEvent->GetInt("userid");
|
||||
|
||||
m_ClientCount++;
|
||||
}
|
||||
else if (strcmp(name, "player_disconnect") == 0)
|
||||
{
|
||||
const int userid = pEvent->GetInt("userid");
|
||||
const int client = m_UserIdLookUp[userid];
|
||||
|
||||
m_ClientCount--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************
|
||||
*** PLAYER CODE ***
|
||||
@@ -2064,7 +2114,8 @@ void CPlayer::Initialize(const char *name, const char *ip, edict_t *pEntity)
|
||||
|| SOURCE_ENGINE == SE_HL2DM \
|
||||
|| SOURCE_ENGINE == SE_BMS \
|
||||
|| SOURCE_ENGINE == SE_INSURGENCY \
|
||||
|| SOURCE_ENGINE == SE_DOI
|
||||
|| SOURCE_ENGINE == SE_DOI \
|
||||
|| SOURCE_ENGINE == SE_BLADE
|
||||
m_pIClient = engine->GetIServer()->GetClient(m_iIndex - 1);
|
||||
#else
|
||||
#if SOURCE_ENGINE == SE_SDK2013
|
||||
@@ -2240,7 +2291,7 @@ void CPlayer::Disconnect()
|
||||
m_bIsSourceTV = false;
|
||||
m_bIsReplay = false;
|
||||
m_Serial.value = -1;
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
m_LanguageCookie = InvalidQueryCvarCookie;
|
||||
#endif
|
||||
ClearNetchannelQueue();
|
||||
@@ -2611,7 +2662,11 @@ unsigned int CPlayer::GetLanguageId()
|
||||
|
||||
void CPlayer::SetLanguageId(unsigned int id)
|
||||
{
|
||||
m_LangId = id;
|
||||
if(m_LangId != id)
|
||||
{
|
||||
m_LangId = id;
|
||||
g_Players.OnClientLanguageChanged(m_iIndex, id);
|
||||
}
|
||||
}
|
||||
|
||||
int CPlayer::GetUserId()
|
||||
|
||||
+13
-3
@@ -151,7 +151,7 @@ private:
|
||||
bool m_bIsReplay = false;
|
||||
serial_t m_Serial;
|
||||
CSteamID m_SteamId;
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
QueryCvarCookie_t m_LanguageCookie = InvalidQueryCvarCookie;
|
||||
#endif
|
||||
std::deque<std::string> m_PrintfBuffer;
|
||||
@@ -159,7 +159,8 @@ private:
|
||||
|
||||
class PlayerManager :
|
||||
public SMGlobalClass,
|
||||
public IPlayerManager
|
||||
public IPlayerManager,
|
||||
public IGameEventListener2
|
||||
{
|
||||
friend class CPlayer;
|
||||
public:
|
||||
@@ -192,6 +193,7 @@ public:
|
||||
#endif
|
||||
void OnClientSettingsChanged(edict_t *pEntity);
|
||||
//void OnClientSettingsChanged_Pre(edict_t *pEntity);
|
||||
void OnClientLanguageChanged(int client, unsigned int language);
|
||||
void OnServerHibernationUpdate(bool bHibernating);
|
||||
void OnClientPrintf(edict_t *pEdict, const char *szMsg);
|
||||
void OnPrintfFrameAction(unsigned int serial);
|
||||
@@ -202,6 +204,7 @@ public: //IPlayerManager
|
||||
IGamePlayer *GetGamePlayer(edict_t *pEdict);
|
||||
int GetMaxClients();
|
||||
int GetNumPlayers();
|
||||
int GetNumClients();
|
||||
int GetClientOfUserId(int userid);
|
||||
bool IsServerActivated();
|
||||
int FilterCommandTarget(IGamePlayer *pAdmin, IGamePlayer *pTarget, int flags);
|
||||
@@ -212,6 +215,11 @@ public: //IPlayerManager
|
||||
int GetClientFromSerial(unsigned int serial);
|
||||
void ClearAdminId(AdminId id);
|
||||
void RecheckAnyAdmins();
|
||||
public: // IGameEventListener2
|
||||
void FireGameEvent(IGameEvent *pEvent);
|
||||
#if SOURCE_ENGINE >= SE_ALIENSWARM
|
||||
virtual int GetEventDebugID( void ) { return 42; }
|
||||
#endif
|
||||
public:
|
||||
inline int MaxClients()
|
||||
{
|
||||
@@ -235,7 +243,7 @@ public:
|
||||
{
|
||||
return m_bInCCKVHook;
|
||||
}
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
bool HandleConVarQuery(QueryCvarCookie_t cookie, int client, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue);
|
||||
#endif
|
||||
private:
|
||||
@@ -253,6 +261,7 @@ private:
|
||||
IForward *m_clcommandkv_post;
|
||||
IForward *m_clinfochanged;
|
||||
IForward *m_clauth;
|
||||
IForward *m_cllang;
|
||||
IForward *m_onActivate;
|
||||
IForward *m_onActivate2;
|
||||
CPlayer *m_Players;
|
||||
@@ -272,6 +281,7 @@ private:
|
||||
int m_SourceTVUserId;
|
||||
int m_ReplayUserId;
|
||||
bool m_bInCCKVHook;
|
||||
int m_ClientCount;
|
||||
private:
|
||||
static const int NETMSG_TYPE_BITS = 5; // SVC_Print overhead for netmsg type
|
||||
static const int SVC_Print_BufferSize = 2048 - 1; // -1 for terminating \0
|
||||
|
||||
+19
-28
@@ -35,7 +35,6 @@
|
||||
#include "frame_hooks.h"
|
||||
#include "ConVarManager.h"
|
||||
#include "logic_bridge.h"
|
||||
#include <bridge/include/IProviderCallbacks.h>
|
||||
|
||||
#define TIMER_MIN_ACCURACY 0.1
|
||||
|
||||
@@ -171,8 +170,9 @@ void ITimer::Initialize(ITimedEvent *pCallbacks, float fInterval, float fToExec,
|
||||
TimerSystem::TimerSystem()
|
||||
{
|
||||
m_pMapTimer = NULL;
|
||||
m_bHasMapTickedYet = false;
|
||||
m_bHasMapSimulatedYet = false;
|
||||
m_fLastTickedTime = 0.0f;
|
||||
OnSourceModLevelEnd();
|
||||
}
|
||||
|
||||
TimerSystem::~TimerSystem()
|
||||
@@ -213,28 +213,29 @@ void TimerSystem::OnSourceModLevelEnd()
|
||||
{
|
||||
m_bHasMapTickedYet = false;
|
||||
m_bHasMapSimulatedYet = false;
|
||||
m_bWasSimulating = false;
|
||||
m_uFramesAhead = 0;
|
||||
}
|
||||
|
||||
/* Think is called before gpGlobals is updated every frame, even if the server is hibernating */
|
||||
void TimerSystem::Think(bool unused)
|
||||
void TimerSystem::GameFrame(bool simulating)
|
||||
{
|
||||
m_uFramesAhead++;
|
||||
bool simulating = m_bWasSimulating && m_uFramesAhead == 1;
|
||||
|
||||
if (m_bHasMapTickedYet) {
|
||||
g_fUniversalTime += gpGlobals->realtime - m_fLastTickedTime;
|
||||
} else {
|
||||
if (simulating && m_bHasMapTickedYet)
|
||||
{
|
||||
g_fUniversalTime += gpGlobals->curtime - m_fLastTickedTime;
|
||||
if (!m_bHasMapSimulatedYet)
|
||||
{
|
||||
m_bHasMapSimulatedYet = true;
|
||||
MapTimeLeftChanged();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_fUniversalTime += gpGlobals->interval_per_tick;
|
||||
}
|
||||
|
||||
m_fLastTickedTime = gpGlobals->realtime;
|
||||
m_fLastTickedTime = gpGlobals->curtime;
|
||||
m_bHasMapTickedYet = true;
|
||||
|
||||
logicore.callbacks->OnThink(simulating);
|
||||
|
||||
if (g_fUniversalTime >= g_fTimerThink) {
|
||||
if (g_fUniversalTime >= g_fTimerThink)
|
||||
{
|
||||
RunFrame();
|
||||
|
||||
g_fTimerThink = CalcNextThink(g_fTimerThink, TIMER_MIN_ACCURACY);
|
||||
@@ -242,19 +243,9 @@ void TimerSystem::Think(bool unused)
|
||||
|
||||
RunFrameHooks(simulating);
|
||||
|
||||
m_pOnGameFrame->Execute();
|
||||
}
|
||||
|
||||
/* GameFrame is called after gpGlobals is updated, and may not be called when the server is hibernating */
|
||||
void TimerSystem::GameFrame(bool simulating)
|
||||
{
|
||||
m_bWasSimulating = simulating;
|
||||
m_uFramesAhead = 0;
|
||||
|
||||
if (simulating && !m_bHasMapSimulatedYet)
|
||||
if (m_pOnGameFrame->GetFunctionCount())
|
||||
{
|
||||
m_bHasMapSimulatedYet = true;
|
||||
MapTimeLeftChanged();
|
||||
m_pOnGameFrame->Execute(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,6 @@ public: //ITimerSystem
|
||||
public:
|
||||
void RunFrame();
|
||||
void RemoveMapChangeTimers();
|
||||
void Think(bool unused);
|
||||
void GameFrame(bool simulating);
|
||||
private:
|
||||
List<ITimer *> m_SingleTimers;
|
||||
@@ -93,8 +92,6 @@ private:
|
||||
/* This is stuff for our manual ticking escapades. */
|
||||
bool m_bHasMapTickedYet; /** Has the map ticked yet? */
|
||||
bool m_bHasMapSimulatedYet; /** Has the map simulated yet? */
|
||||
bool m_bWasSimulating; /** Was the last GameFrame simulating */
|
||||
unsigned m_uFramesAhead; /** Number of frames Think is ahead of GameFrame */
|
||||
float m_fLastTickedTime; /** Last time that the game currently gave
|
||||
us while ticking.
|
||||
*/
|
||||
|
||||
+42
-49
@@ -42,10 +42,6 @@
|
||||
|
||||
UserMessages g_UserMsgs;
|
||||
|
||||
#ifdef USE_PROTOBUF_USERMESSAGES
|
||||
const protobuf::Message *GetMessagePrototype(int msg_type);
|
||||
#endif
|
||||
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
SH_DECL_HOOK3_void(IVEngineServer, SendUserMessage, SH_NOATTRIB, 0, IRecipientFilter &, int, const protobuf::Message &);
|
||||
#else
|
||||
@@ -62,7 +58,7 @@ UserMessages::UserMessages()
|
||||
: m_InterceptBuffer(m_pBase, 2500)
|
||||
{
|
||||
#else
|
||||
: m_InterceptBuffer(nullptr)
|
||||
: m_InterceptBuffer(NULL)
|
||||
{
|
||||
#endif
|
||||
m_HookCount = 0;
|
||||
@@ -237,7 +233,7 @@ google::protobuf::Message *UserMessages::StartProtobufMessage(int msg_id, const
|
||||
#ifndef USE_PROTOBUF_USERMESSAGES
|
||||
return NULL;
|
||||
#else
|
||||
PbHandle buffer;
|
||||
protobuf::Message *buffer;
|
||||
|
||||
if (m_InExec || m_InHook)
|
||||
{
|
||||
@@ -266,9 +262,8 @@ google::protobuf::Message *UserMessages::StartProtobufMessage(int msg_id, const
|
||||
if (m_CurFlags & USERMSG_BLOCKHOOKS)
|
||||
{
|
||||
// direct message creation, return buffer "from engine". keep track
|
||||
m_FakeEngineBuffer =
|
||||
PbHandle(GetMessagePrototype(msg_id)->New(), PbHandle::Owned, PbHandle::Local);
|
||||
buffer = m_FakeEngineBuffer.AsUnowned();
|
||||
m_FakeEngineBuffer = GetMessagePrototype(msg_id)->New();
|
||||
buffer = m_FakeEngineBuffer;
|
||||
} else {
|
||||
char messageName[32];
|
||||
if (!GetMessageName(msg_id, messageName, sizeof(messageName)))
|
||||
@@ -277,30 +272,27 @@ google::protobuf::Message *UserMessages::StartProtobufMessage(int msg_id, const
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// The message returned here should always local.
|
||||
auto msg = OnStartMessage_Pre(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id, messageName);
|
||||
protobuf::Message *msg = OnStartMessage_Pre(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id, messageName);
|
||||
switch (m_FakeMetaRes)
|
||||
{
|
||||
case MRES_IGNORED:
|
||||
case MRES_HANDLED:
|
||||
m_FakeEngineBuffer =
|
||||
PbHandle(GetMessagePrototype(msg_id)->New(), PbHandle::Owned, PbHandle::Local);
|
||||
buffer = m_FakeEngineBuffer.AsUnowned();
|
||||
m_FakeEngineBuffer = GetMessagePrototype(msg_id)->New();
|
||||
buffer = m_FakeEngineBuffer;
|
||||
break;
|
||||
|
||||
case MRES_OVERRIDE:
|
||||
m_FakeEngineBuffer =
|
||||
PbHandle(GetMessagePrototype(msg_id)->New(), PbHandle::Owned, PbHandle::Local);
|
||||
m_FakeEngineBuffer = GetMessagePrototype(msg_id)->New();
|
||||
// fallthrough
|
||||
case MRES_SUPERCEDE:
|
||||
buffer = std::move(msg);
|
||||
buffer = msg;
|
||||
break;
|
||||
}
|
||||
|
||||
OnStartMessage_Post(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id, messageName);
|
||||
}
|
||||
|
||||
assert(!buffer.is_owned());
|
||||
return buffer.GetLocalMessage();
|
||||
return buffer;
|
||||
#endif // USE_PROTOBUF_USERMESSAGES
|
||||
}
|
||||
|
||||
@@ -312,12 +304,11 @@ bool UserMessages::EndMessage()
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
PbHandle localBuffer = std::move(m_FakeEngineBuffer);
|
||||
if (m_CurFlags & USERMSG_BLOCKHOOKS)
|
||||
{
|
||||
PbHandle priv = localBuffer.ToPrivate(m_CurId);
|
||||
ENGINE_CALL(SendUserMessage)(static_cast<IRecipientFilter &>(m_CellRecFilter), m_CurId,
|
||||
*priv.GetPrivateMessage());
|
||||
ENGINE_CALL(SendUserMessage)(static_cast<IRecipientFilter &>(m_CellRecFilter), m_CurId, *m_FakeEngineBuffer);
|
||||
delete m_FakeEngineBuffer;
|
||||
m_FakeEngineBuffer = NULL;
|
||||
} else {
|
||||
OnMessageEnd_Pre();
|
||||
|
||||
@@ -326,12 +317,10 @@ bool UserMessages::EndMessage()
|
||||
case MRES_IGNORED:
|
||||
case MRES_HANDLED:
|
||||
case MRES_OVERRIDE:
|
||||
{
|
||||
PbHandle priv = localBuffer.ToPrivate(m_CurId);
|
||||
engine->SendUserMessage(static_cast<IRecipientFilter &>(m_CellRecFilter), m_CurId,
|
||||
*priv.GetPrivateMessage());
|
||||
engine->SendUserMessage(static_cast<IRecipientFilter &>(m_CellRecFilter), m_CurId, *m_FakeEngineBuffer);
|
||||
delete m_FakeEngineBuffer;
|
||||
m_FakeEngineBuffer = NULL;
|
||||
break;
|
||||
}
|
||||
//case MRES_SUPERCEDE:
|
||||
}
|
||||
|
||||
@@ -451,7 +440,7 @@ bool UserMessages::InternalHook(int msg_id, IBitBufUserMessageListener *pListene
|
||||
}
|
||||
|
||||
#ifdef USE_PROTOBUF_USERMESSAGES
|
||||
const protobuf::Message *GetMessagePrototype(int msg_type)
|
||||
const protobuf::Message *UserMessages::GetMessagePrototype(int msg_type)
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
return g_Cstrike15UsermessageHelpers.GetPrototype(msg_type);
|
||||
@@ -538,8 +527,7 @@ void UserMessages::OnSendUserMessage_Pre(IRecipientFilter &filter, int msg_type,
|
||||
}
|
||||
else
|
||||
{
|
||||
m_FakeEngineBuffer =
|
||||
PbHandle(&const_cast<protobuf::Message &>(msg), PbHandle::Unowned, PbHandle::Private);
|
||||
m_FakeEngineBuffer = &const_cast<protobuf::Message &>(msg);
|
||||
}
|
||||
|
||||
OnStartMessage_Post(&filter, msg_type, pszName);
|
||||
@@ -575,7 +563,7 @@ void UserMessages::OnSendUserMessage_Post(IRecipientFilter &filter, int msg_type
|
||||
#endif
|
||||
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
PbHandle UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name)
|
||||
protobuf::Message *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name)
|
||||
#elif SOURCE_ENGINE >= SE_LEFT4DEAD
|
||||
bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name)
|
||||
#else
|
||||
@@ -589,7 +577,7 @@ bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_typ
|
||||
|| (m_InExec && (m_CurFlags & USERMSG_BLOCKHOOKS)))
|
||||
{
|
||||
m_InHook = false;
|
||||
UM_RETURN_META_VALUE(MRES_IGNORED, nullptr);
|
||||
UM_RETURN_META_VALUE(MRES_IGNORED, NULL);
|
||||
}
|
||||
|
||||
m_CurId = msg_type;
|
||||
@@ -600,20 +588,22 @@ bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_typ
|
||||
if (!is_intercept_empty)
|
||||
{
|
||||
#ifdef USE_PROTOBUF_USERMESSAGES
|
||||
m_InterceptBuffer =
|
||||
PbHandle(GetMessagePrototype(msg_type)->New(), PbHandle::Owned, PbHandle::Local);
|
||||
UM_RETURN_META_VALUE(MRES_SUPERCEDE, m_InterceptBuffer.AsUnowned());
|
||||
if (m_InterceptBuffer)
|
||||
delete m_InterceptBuffer;
|
||||
m_InterceptBuffer = GetMessagePrototype(msg_type)->New();
|
||||
|
||||
UM_RETURN_META_VALUE(MRES_SUPERCEDE, m_InterceptBuffer);
|
||||
#else
|
||||
m_InterceptBuffer.Reset();
|
||||
UM_RETURN_META_VALUE(MRES_SUPERCEDE, &m_InterceptBuffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
UM_RETURN_META_VALUE(MRES_IGNORED, nullptr);
|
||||
UM_RETURN_META_VALUE(MRES_IGNORED, NULL);
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
void* UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name)
|
||||
protobuf::Message *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name)
|
||||
#elif SOURCE_ENGINE >= SE_LEFT4DEAD
|
||||
bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name)
|
||||
#else
|
||||
@@ -627,9 +617,9 @@ bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_ty
|
||||
|
||||
#ifdef USE_PROTOBUF_USERMESSAGES
|
||||
if (m_FakeMetaRes == MRES_SUPERCEDE)
|
||||
m_OrigBuffer = m_InterceptBuffer.AsUnowned();
|
||||
m_OrigBuffer = m_InterceptBuffer;
|
||||
else
|
||||
m_OrigBuffer = m_FakeEngineBuffer.AsUnowned();
|
||||
m_OrigBuffer = m_FakeEngineBuffer;
|
||||
#else
|
||||
m_OrigBuffer = META_RESULT_ORIG_RET(bf_write *);
|
||||
#endif
|
||||
@@ -725,11 +715,8 @@ void UserMessages::OnMessageEnd_Pre()
|
||||
{
|
||||
pInfo = (*iter);
|
||||
pInfo->IsHooked = true;
|
||||
|
||||
#ifdef USE_PROTOBUF_USERMESSAGES
|
||||
PbHandle local = m_InterceptBuffer.ToLocal(m_CurId);
|
||||
res = pInfo->Callback->InterceptUserMessage(m_CurId, local.GetLocalMessage(), m_CurRecFilter);
|
||||
m_InterceptBuffer.CopyFrom(local);
|
||||
res = pInfo->Callback->InterceptUserMessage(m_CurId, m_InterceptBuffer, m_CurRecFilter);
|
||||
#else
|
||||
res = pInfo->Callback->InterceptUserMessage(m_CurId, &m_InterceptBuffer, m_CurRecFilter);
|
||||
#endif
|
||||
@@ -781,8 +768,7 @@ void UserMessages::OnMessageEnd_Pre()
|
||||
if (!handled && intercepted)
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
PbHandle priv = m_InterceptBuffer.ToPrivate(m_CurId);
|
||||
ENGINE_CALL(SendUserMessage)(static_cast<IRecipientFilter &>(*m_CurRecFilter), m_CurId, *priv.GetPrivateMessage());
|
||||
ENGINE_CALL(SendUserMessage)(static_cast<IRecipientFilter &>(*m_CurRecFilter), m_CurId, *m_InterceptBuffer);
|
||||
#else
|
||||
bf_write *engine_bfw;
|
||||
#if SOURCE_ENGINE >= SE_LEFT4DEAD
|
||||
@@ -798,9 +784,12 @@ void UserMessages::OnMessageEnd_Pre()
|
||||
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
PbHandle tmp_msg(GetMessagePrototype(m_CurId)->New(), PbHandle::Owned, PbHandle::Local);
|
||||
tmp_msg.CopyFrom(m_OrigBuffer);
|
||||
auto pTempMsg = tmp_msg.GetLocalMessage();
|
||||
int size = m_OrigBuffer->ByteSize();
|
||||
uint8 *data = (uint8 *)stackalloc(size);
|
||||
m_OrigBuffer->SerializePartialToArray(data, size);
|
||||
|
||||
protobuf::Message *pTempMsg = GetMessagePrototype(m_CurId)->New();
|
||||
pTempMsg->ParsePartialFromArray(data, size);
|
||||
#else
|
||||
bf_write *pTempMsg = m_OrigBuffer;
|
||||
#endif
|
||||
@@ -823,6 +812,10 @@ void UserMessages::OnMessageEnd_Pre()
|
||||
pInfo->IsHooked = false;
|
||||
iter++;
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
delete pTempMsg;
|
||||
#endif
|
||||
}
|
||||
|
||||
UM_RETURN_META((intercepted) ? MRES_SUPERCEDE : MRES_IGNORED);
|
||||
|
||||
+6
-6
@@ -52,7 +52,6 @@ using namespace SourceMod;
|
||||
#include <google/protobuf/message.h>
|
||||
#include <google/protobuf/descriptor.h>
|
||||
#include <netmessages.pb.h>
|
||||
#include "pb_handle.h"
|
||||
|
||||
using namespace google;
|
||||
#else
|
||||
@@ -109,8 +108,8 @@ public:
|
||||
#endif
|
||||
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
PbHandle OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name);
|
||||
void* OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name);
|
||||
protobuf::Message *OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name);
|
||||
protobuf::Message *OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name);
|
||||
#elif SOURCE_ENGINE >= SE_LEFT4DEAD
|
||||
bf_write *OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name);
|
||||
bf_write *OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name);
|
||||
@@ -122,6 +121,7 @@ public:
|
||||
void OnMessageEnd_Post();
|
||||
private:
|
||||
#ifdef USE_PROTOBUF_USERMESSAGES
|
||||
const protobuf::Message *GetMessagePrototype(int msg_type);
|
||||
bool InternalHook(int msg_id, IProtobufUserMessageListener *pListener, bool intercept, bool isNew);
|
||||
bool InternalUnhook(int msg_id, IProtobufUserMessageListener *pListener, bool intercept, bool isNew);
|
||||
#else
|
||||
@@ -141,11 +141,11 @@ private:
|
||||
bf_read m_ReadBuffer;
|
||||
#else
|
||||
// The engine used to provide this. Now we track it.
|
||||
PbHandle m_OrigBuffer;
|
||||
PbHandle m_FakeEngineBuffer;
|
||||
protobuf::Message *m_OrigBuffer;
|
||||
protobuf::Message *m_FakeEngineBuffer;
|
||||
META_RES m_FakeMetaRes;
|
||||
|
||||
PbHandle m_InterceptBuffer;
|
||||
protobuf::Message *m_InterceptBuffer;
|
||||
#endif
|
||||
size_t m_HookCount;
|
||||
bool m_InHook;
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
#if SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
SH_DECL_HOOK1_void(ICvar, UnregisterConCommand, SH_NOATTRIB, 0, ConCommandBase *);
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
SH_DECL_HOOK2_void(ICvar, RegisterConCommand, SH_NOATTRIB, 0, ConCommandBase *, bool);
|
||||
#else
|
||||
SH_DECL_HOOK1_void(ICvar, RegisterConCommand, SH_NOATTRIB, 0, ConCommandBase *);
|
||||
@@ -82,7 +82,7 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
void LinkConCommandBase(ConCommandBase *pBase, bool unknown)
|
||||
#else
|
||||
void LinkConCommandBase(ConCommandBase *pBase)
|
||||
|
||||
@@ -84,6 +84,9 @@ for cxx in builder.targets:
|
||||
'smn_halflife.cpp',
|
||||
'FrameIterator.cpp',
|
||||
'DatabaseConfBuilder.cpp',
|
||||
'LumpManager.cpp',
|
||||
'smn_entitylump.cpp',
|
||||
'NativeInvoker.cpp',
|
||||
]
|
||||
|
||||
if binary.compiler.target.arch == 'x86_64':
|
||||
|
||||
+17
-21
@@ -128,8 +128,8 @@ void DBManager::OnHandleDestroy(HandleType_t type, void *object)
|
||||
|
||||
bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength)
|
||||
{
|
||||
ConfDbInfoList *list = m_Builder.GetConfigList();
|
||||
ke::RefPtr<ConfDbInfo> pInfo = list->GetDatabaseConf(name);
|
||||
ConfDbInfoList &list = m_Builder.GetConfigList();
|
||||
ke::RefPtr<ConfDbInfo> pInfo = list.GetDatabaseConf(name);
|
||||
|
||||
if (!pInfo)
|
||||
{
|
||||
@@ -148,7 +148,7 @@ bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool
|
||||
/* Try to assign a real driver pointer */
|
||||
if (pInfo->info.driver[0] == '\0')
|
||||
{
|
||||
std::string defaultDriver = list->GetDefaultDriver();
|
||||
std::string defaultDriver = list.GetDefaultDriver();
|
||||
if (!m_pDefault && defaultDriver.length() > 0)
|
||||
{
|
||||
m_pDefault = FindOrLoadDriver(defaultDriver.c_str());
|
||||
@@ -209,17 +209,14 @@ void DBManager::RemoveDriver(IDBDriver *pDriver)
|
||||
}
|
||||
}
|
||||
|
||||
ConfDbInfoList *list = m_Builder.GetConfigList();
|
||||
for (size_t i = 0; i < list->size(); i++)
|
||||
{
|
||||
ke::RefPtr<ConfDbInfo> current = list->at(i);
|
||||
if (current->realDriver == pDriver)
|
||||
ConfDbInfoList &list = m_Builder.GetConfigList();
|
||||
for (auto conf : list) {
|
||||
if (conf->realDriver == pDriver)
|
||||
{
|
||||
current->realDriver = NULL;
|
||||
conf->realDriver = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Someone unloaded the default driver? Silly.. */
|
||||
if (pDriver == m_pDefault)
|
||||
{
|
||||
@@ -255,8 +252,8 @@ void DBManager::RemoveDriver(IDBDriver *pDriver)
|
||||
|
||||
IDBDriver *DBManager::GetDefaultDriver()
|
||||
{
|
||||
ConfDbInfoList *list = m_Builder.GetConfigList();
|
||||
std::string defaultDriver = list->GetDefaultDriver();
|
||||
ConfDbInfoList &list = m_Builder.GetConfigList();
|
||||
std::string defaultDriver = list.GetDefaultDriver();
|
||||
if (!m_pDefault && defaultDriver.length() > 0)
|
||||
{
|
||||
m_pDefault = FindOrLoadDriver(defaultDriver.c_str());
|
||||
@@ -322,12 +319,12 @@ IDBDriver *DBManager::GetDriver(unsigned int index)
|
||||
|
||||
const DatabaseInfo *DBManager::FindDatabaseConf(const char *name)
|
||||
{
|
||||
ConfDbInfoList *list = m_Builder.GetConfigList();
|
||||
ke::RefPtr<ConfDbInfo> info = list->GetDatabaseConf(name);
|
||||
ConfDbInfoList &list = m_Builder.GetConfigList();
|
||||
ke::RefPtr<ConfDbInfo> info = list.GetDatabaseConf(name);
|
||||
if (!info)
|
||||
{
|
||||
// couldn't find requested conf, return default if exists
|
||||
info = list->GetDefaultConfiguration();
|
||||
info = list.GetDefaultConfiguration();
|
||||
if (!info)
|
||||
{
|
||||
return NULL;
|
||||
@@ -337,11 +334,10 @@ const DatabaseInfo *DBManager::FindDatabaseConf(const char *name)
|
||||
return &info->info;
|
||||
}
|
||||
|
||||
ConfDbInfo *DBManager::GetDatabaseConf(const char *name)
|
||||
ke::RefPtr<ConfDbInfo> DBManager::GetDatabaseConf(const char *name)
|
||||
{
|
||||
ConfDbInfoList *list = m_Builder.GetConfigList();
|
||||
ke::RefPtr<ConfDbInfo> info(list->GetDatabaseConf(name));
|
||||
return info;
|
||||
ConfDbInfoList &list = m_Builder.GetConfigList();
|
||||
return list.GetDatabaseConf(name);
|
||||
}
|
||||
|
||||
IDBDriver *DBManager::FindOrLoadDriver(const char *name)
|
||||
@@ -582,8 +578,8 @@ void DBManager::OnPluginWillUnload(IPlugin *plugin)
|
||||
|
||||
std::string DBManager::GetDefaultDriverName()
|
||||
{
|
||||
ConfDbInfoList *list = m_Builder.GetConfigList();
|
||||
return list->GetDefaultDriver();
|
||||
ConfDbInfoList &list = m_Builder.GetConfigList();
|
||||
return list.GetDefaultDriver();
|
||||
}
|
||||
|
||||
void DBManager::AddDependency(IExtension *myself, IDBDriver *driver)
|
||||
|
||||
@@ -71,7 +71,7 @@ public: //IDBManager
|
||||
void AddDriver(IDBDriver *pDrivera);
|
||||
void RemoveDriver(IDBDriver *pDriver);
|
||||
const DatabaseInfo *FindDatabaseConf(const char *name);
|
||||
ConfDbInfo *GetDatabaseConf(const char *name);
|
||||
ke::RefPtr<ConfDbInfo> GetDatabaseConf(const char *name);
|
||||
bool Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength);
|
||||
unsigned int GetDriverCount();
|
||||
IDBDriver *GetDriver(unsigned int index);
|
||||
|
||||
@@ -36,8 +36,8 @@
|
||||
#define DBPARSE_LEVEL_DATABASE 2
|
||||
|
||||
DatabaseConfBuilder::DatabaseConfBuilder()
|
||||
: m_ParseList(nullptr),
|
||||
m_InfoList(new ConfDbInfoList())
|
||||
: m_ParseList(),
|
||||
m_InfoList()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ DatabaseConfBuilder::~DatabaseConfBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
ConfDbInfoList *DatabaseConfBuilder::GetConfigList()
|
||||
ConfDbInfoList &DatabaseConfBuilder::GetConfigList()
|
||||
{
|
||||
return m_InfoList;
|
||||
}
|
||||
@@ -75,7 +75,7 @@ void DatabaseConfBuilder::ReadSMC_ParseStart()
|
||||
m_ParseLevel = 0;
|
||||
m_ParseState = DBPARSE_LEVEL_NONE;
|
||||
|
||||
m_ParseList = new ConfDbInfoList();
|
||||
m_ParseList.clear();
|
||||
}
|
||||
|
||||
SMCResult DatabaseConfBuilder::ReadSMC_NewSection(const SMCStates *states, const char *name)
|
||||
@@ -116,7 +116,7 @@ SMCResult DatabaseConfBuilder::ReadSMC_KeyValue(const SMCStates *states, const c
|
||||
{
|
||||
if (strcmp(key, "driver_default") == 0)
|
||||
{
|
||||
m_ParseList->SetDefaultDriver(value);
|
||||
m_ParseList.SetDefaultDriver(value);
|
||||
}
|
||||
} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
|
||||
if (strcmp(key, "driver") == 0)
|
||||
@@ -161,7 +161,7 @@ SMCResult DatabaseConfBuilder::ReadSMC_LeavingSection(const SMCStates *states)
|
||||
|
||||
/* Save it.. */
|
||||
m_ParseCurrent->AddRef();
|
||||
m_ParseList->push_back(m_ParseCurrent);
|
||||
m_ParseList.push_back(m_ParseCurrent);
|
||||
m_ParseCurrent = nullptr;
|
||||
|
||||
/* Go up one level */
|
||||
@@ -176,9 +176,7 @@ SMCResult DatabaseConfBuilder::ReadSMC_LeavingSection(const SMCStates *states)
|
||||
|
||||
void DatabaseConfBuilder::ReadSMC_ParseEnd(bool halted, bool failed)
|
||||
{
|
||||
m_InfoList->ReleaseMembers();
|
||||
delete m_InfoList;
|
||||
m_InfoList.clear();
|
||||
m_InfoList = m_ParseList;
|
||||
|
||||
m_ParseList = nullptr;
|
||||
m_ParseList.clear();
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
|
||||
#include <am-vector.h>
|
||||
#include <am-string.h>
|
||||
#include <am-refcounting.h>
|
||||
#include <am-refcounting-threadsafe.h>
|
||||
|
||||
class ConfDbInfo : public ke::RefcountedThreadsafe<ConfDbInfo>
|
||||
@@ -56,7 +57,7 @@ public:
|
||||
DatabaseInfo info;
|
||||
};
|
||||
|
||||
class ConfDbInfoList : public std::vector<ConfDbInfo *>
|
||||
class ConfDbInfoList : public std::vector<ke::RefPtr<ConfDbInfo>>
|
||||
{
|
||||
/* Allow internal usage of ConfDbInfoList */
|
||||
friend class DBManager;
|
||||
@@ -66,10 +67,10 @@ private:
|
||||
return m_DefDriver;
|
||||
}
|
||||
|
||||
ConfDbInfo *GetDatabaseConf(const char *name) {
|
||||
ke::RefPtr<ConfDbInfo> GetDatabaseConf(const char *name) {
|
||||
for (size_t i = 0; i < this->size(); i++)
|
||||
{
|
||||
ConfDbInfo *current = this->at(i);
|
||||
ke::RefPtr<ConfDbInfo> current = this->at(i);
|
||||
/* If we run into the default configuration, then we'll save it
|
||||
* for the next call to GetDefaultConfiguration */
|
||||
if (strcmp(current->name.c_str(), "default") == 0)
|
||||
@@ -83,20 +84,15 @@ private:
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
ConfDbInfo *GetDefaultConfiguration() {
|
||||
|
||||
ke::RefPtr<ConfDbInfo> GetDefaultConfiguration() {
|
||||
return m_DefaultConfig;
|
||||
}
|
||||
void SetDefaultDriver(const char *input) {
|
||||
m_DefDriver = std::string(input);
|
||||
}
|
||||
void ReleaseMembers() {
|
||||
for (size_t i = 0; i < this->size(); i++) {
|
||||
ConfDbInfo *current = this->at(i);
|
||||
current->Release();
|
||||
}
|
||||
}
|
||||
private:
|
||||
ConfDbInfo *m_DefaultConfig;
|
||||
ke::RefPtr<ConfDbInfo> m_DefaultConfig;
|
||||
std::string m_DefDriver;
|
||||
};
|
||||
|
||||
@@ -108,7 +104,7 @@ public:
|
||||
~DatabaseConfBuilder();
|
||||
void StartParse();
|
||||
void SetPath(char* path);
|
||||
ConfDbInfoList *GetConfigList();
|
||||
ConfDbInfoList &GetConfigList();
|
||||
public: //ITextListener_SMC
|
||||
void ReadSMC_ParseStart();
|
||||
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
|
||||
@@ -120,10 +116,10 @@ private:
|
||||
unsigned int m_ParseLevel;
|
||||
unsigned int m_ParseState;
|
||||
ConfDbInfo *m_ParseCurrent;
|
||||
ConfDbInfoList *m_ParseList;
|
||||
ConfDbInfoList m_ParseList;
|
||||
private:
|
||||
std::string m_Filename;
|
||||
ConfDbInfoList *m_InfoList;
|
||||
ConfDbInfoList m_InfoList;
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_DATABASE_CONF_BUILDER_H_
|
||||
|
||||
@@ -69,13 +69,13 @@ void DebugReport::GenerateErrorVA(IPluginContext *ctx, cell_t func_idx, int err,
|
||||
ke::SafeVsprintf(buffer, sizeof(buffer), message, ap);
|
||||
|
||||
const char *plname = pluginsys->FindPluginByContext(ctx->GetContext())->GetFilename();
|
||||
const char *error = g_pSourcePawn2->GetErrorString(err);
|
||||
|
||||
if (error)
|
||||
{
|
||||
g_Logger.LogError("[SM] Plugin \"%s\" encountered error %d: %s", plname, err, error);
|
||||
} else {
|
||||
g_Logger.LogError("[SM] Plugin \"%s\" encountered unknown error %d", plname, err);
|
||||
if (err >= 0) {
|
||||
const char *error = g_pSourcePawn2->GetErrorString(err);
|
||||
if (error)
|
||||
g_Logger.LogError("[SM] Plugin \"%s\" encountered error %d: %s", plname, err, error);
|
||||
else
|
||||
g_Logger.LogError("[SM] Plugin \"%s\" encountered unknown error %d", plname, err);
|
||||
}
|
||||
|
||||
g_Logger.LogError("[SM] %s", buffer);
|
||||
|
||||
@@ -47,6 +47,8 @@ public: // IDebugListener
|
||||
void ReportError(const IErrorReport &report, IFrameIterator &iter);
|
||||
void OnDebugSpew(const char *msg, ...);
|
||||
public:
|
||||
// If err is -1, caller assumes the automatic reporting by SourcePawn is
|
||||
// good enough, and only wants the supplemental logging provided here.
|
||||
void GenerateError(IPluginContext *ctx, cell_t func_idx, int err, const char *message, ...);
|
||||
void GenerateErrorVA(IPluginContext *ctx, cell_t func_idx, int err, const char *message, va_list ap);
|
||||
void GenerateCodeError(IPluginContext *ctx, uint32_t code_addr, int err, const char *message, ...);
|
||||
|
||||
@@ -240,6 +240,8 @@ void CLocalExtension::Unload()
|
||||
m_pLib->CloseLibrary();
|
||||
m_pLib = NULL;
|
||||
}
|
||||
|
||||
m_bFullyLoaded = false;
|
||||
}
|
||||
|
||||
bool CRemoteExtension::Reload(char *error, size_t maxlength)
|
||||
|
||||
@@ -85,15 +85,12 @@ static const char *g_pParseEngine = NULL;
|
||||
|
||||
#if defined PLATFORM_WINDOWS
|
||||
#define PLATFORM_NAME "windows" PLATFORM_ARCH_SUFFIX
|
||||
#define PLATFORM_SERVER_BINARY "server.dll"
|
||||
#elif defined PLATFORM_LINUX
|
||||
#define PLATFORM_NAME "linux" PLATFORM_ARCH_SUFFIX
|
||||
#define PLATFORM_COMPAT_ALT "mac" PLATFORM_ARCH_SUFFIX /* Alternate platform name if game data is missing for primary one */
|
||||
#define PLATFORM_SERVER_BINARY "server_i486.so"
|
||||
#elif defined PLATFORM_APPLE
|
||||
#define PLATFORM_NAME "mac" PLATFORM_ARCH_SUFFIX
|
||||
#define PLATFORM_COMPAT_ALT "linux" PLATFORM_ARCH_SUFFIX
|
||||
#define PLATFORM_SERVER_BINARY "server.dylib"
|
||||
#endif
|
||||
|
||||
struct TempSigInfo
|
||||
@@ -315,7 +312,10 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
|
||||
FILE *fp;
|
||||
char path[PLATFORM_MAX_PATH];
|
||||
|
||||
g_pSM->BuildPath(Path_Game, path, sizeof(path), "bin/" PLATFORM_SERVER_BINARY);
|
||||
char binName[64];
|
||||
bridge->FormatSourceBinaryName(name, binName, sizeof(binName));
|
||||
|
||||
g_pSM->BuildPath(Path_Game, path, sizeof(path), "bin/%s", binName);
|
||||
if ((fp = fopen(path, "rb")) == NULL)
|
||||
{
|
||||
ke::SafeSprintf(error, sizeof(error), "Could not open binary: %s", path);
|
||||
@@ -810,7 +810,10 @@ public:
|
||||
(!had_game && matched_engine) ||
|
||||
(matched_engine && matched_game))
|
||||
{
|
||||
fileList->push_back(cur_file);
|
||||
if (fileList->find(cur_file) == fileList->end())
|
||||
{
|
||||
fileList->push_back(cur_file);
|
||||
}
|
||||
}
|
||||
state = MSTATE_MAIN;
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@
|
||||
#include "sm_invalidparamhandler.h"
|
||||
#endif
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
HandleSystem g_HandleSys;
|
||||
|
||||
QHandle *ignore_handle;
|
||||
@@ -299,6 +301,28 @@ HandleError HandleSystem::MakePrimHandle(HandleType_t type,
|
||||
}
|
||||
}
|
||||
|
||||
if (owner)
|
||||
{
|
||||
owner->num_handles++;
|
||||
if (!owner->warned_handle_usage && owner->num_handles >= HANDLESYS_WARN_USAGE)
|
||||
{
|
||||
owner->warned_handle_usage = true;
|
||||
|
||||
std::string path = "<unknown>";
|
||||
if (auto plugin = scripts->FindPluginByIdentity(owner))
|
||||
{
|
||||
path = "plugin "s + plugin->GetFilename();
|
||||
}
|
||||
else if (auto ext = g_Extensions.GetExtensionFromIdent(owner))
|
||||
{
|
||||
path = "extension "s + ext->GetFilename();
|
||||
}
|
||||
|
||||
logger->LogError("[SM] Warning: %s is using more than %d handles!",
|
||||
path.c_str(), HANDLESYS_WARN_USAGE);
|
||||
}
|
||||
}
|
||||
|
||||
QHandle *pHandle = &m_Handles[handle];
|
||||
|
||||
assert(pHandle->set == false);
|
||||
@@ -320,7 +344,7 @@ HandleError HandleSystem::MakePrimHandle(HandleType_t type,
|
||||
|
||||
/* Create the hash value */
|
||||
Handle_t hash = pHandle->serial;
|
||||
hash <<= 16;
|
||||
hash <<= HANDLESYS_HANDLE_BITS;
|
||||
hash |= handle;
|
||||
|
||||
/* Add a reference count to the type */
|
||||
@@ -484,7 +508,7 @@ HandleError HandleSystem::GetHandle(Handle_t handle,
|
||||
unsigned int *in_index,
|
||||
bool ignoreFree)
|
||||
{
|
||||
unsigned int serial = (handle >> 16);
|
||||
unsigned int serial = (handle >> HANDLESYS_HANDLE_BITS);
|
||||
unsigned int index = (handle & HANDLESYS_HANDLE_MASK);
|
||||
|
||||
if (index == 0 || index > m_HandleTail || index > HANDLESYS_MAX_HANDLES)
|
||||
@@ -640,7 +664,7 @@ Handle_t HandleSystem::FastCloneHandle(Handle_t hndl)
|
||||
void HandleSystem::GetHandleUnchecked(Handle_t hndl, QHandle *& pHandle, unsigned int &index)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
unsigned int serial = (hndl >> 16);
|
||||
unsigned int serial = (hndl >> HANDLESYS_HANDLE_BITS);
|
||||
#endif
|
||||
index = (hndl & HANDLESYS_HANDLE_MASK);
|
||||
|
||||
@@ -664,6 +688,9 @@ HandleError HandleSystem::FreeHandle(QHandle *pHandle, unsigned int index)
|
||||
|
||||
QHandleType *pType = &m_Types[pHandle->type];
|
||||
|
||||
if (pHandle->owner && pHandle->owner->num_handles > 0)
|
||||
pHandle->owner->num_handles--;
|
||||
|
||||
if (pHandle->clone)
|
||||
{
|
||||
/* If we're a clone, decrease the parent reference count */
|
||||
@@ -1138,12 +1165,12 @@ void HandleSystem::Dump(const HandleReporter &fn)
|
||||
continue;
|
||||
}
|
||||
/* Get the index */
|
||||
unsigned int index = (m_Handles[i].serial << 16) | i;
|
||||
unsigned int index = (m_Handles[i].serial << HANDLESYS_HANDLE_BITS) | i;
|
||||
/* Determine the owner */
|
||||
const char *owner = "UNKNOWN";
|
||||
if (m_Handles[i].owner)
|
||||
{
|
||||
IdentityToken_t *pOwner = m_Handles[i].owner;
|
||||
IdentityToken_t *pOwner = m_Handles[i].owner;
|
||||
if (pOwner == g_pCoreIdent)
|
||||
{
|
||||
owner = "CORE";
|
||||
|
||||
@@ -42,14 +42,17 @@
|
||||
#include <sm_namehashset.h>
|
||||
#include "common_logic.h"
|
||||
|
||||
#define HANDLESYS_MAX_HANDLES (1<<15)
|
||||
#define HANDLESYS_HANDLE_BITS 20
|
||||
#define HANDLESYS_MAX_HANDLES ((1 << HANDLESYS_HANDLE_BITS) - 1)
|
||||
#define HANDLESYS_MAX_TYPES (1<<9)
|
||||
#define HANDLESYS_MAX_SUBTYPES 0xF
|
||||
#define HANDLESYS_SUBTYPE_MASK 0xF
|
||||
#define HANDLESYS_TYPEARRAY_SIZE (HANDLESYS_MAX_TYPES * (HANDLESYS_MAX_SUBTYPES + 1))
|
||||
#define HANDLESYS_MAX_SERIALS 0xFFFF
|
||||
#define HANDLESYS_SERIAL_MASK 0xFFFF0000
|
||||
#define HANDLESYS_HANDLE_MASK 0x0000FFFF
|
||||
#define HANDLESYS_SERIAL_BITS (32 - HANDLESYS_HANDLE_BITS)
|
||||
#define HANDLESYS_MAX_SERIALS (1 << HANDLESYS_SERIAL_BITS)
|
||||
#define HANDLESYS_SERIAL_MASK (((1 << HANDLESYS_SERIAL_BITS) - 1) << HANDLESYS_HANDLE_BITS)
|
||||
#define HANDLESYS_HANDLE_MASK ((1 << HANDLESYS_HANDLE_BITS) - 1)
|
||||
#define HANDLESYS_WARN_USAGE 100000
|
||||
|
||||
#define HANDLESYS_MEMUSAGE_MIN_VERSION 3
|
||||
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* Entity Lump Manager
|
||||
* Copyright (C) 2021-2022 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$
|
||||
*/
|
||||
|
||||
#include "LumpManager.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
EntityLumpParseResult::operator bool() const {
|
||||
return m_Status == Status_OK;
|
||||
}
|
||||
|
||||
EntityLumpParseResult EntityLumpManager::Parse(const char* pMapEntities) {
|
||||
m_Entities.clear();
|
||||
|
||||
std::istringstream mapEntities(pMapEntities);
|
||||
|
||||
for (;;) {
|
||||
std::string token;
|
||||
mapEntities >> std::ws >> token >> std::ws;
|
||||
|
||||
// Assert that we're at the start of a new block, otherwise we're done parsing
|
||||
if (token != "{") {
|
||||
if (token == "\0") {
|
||||
break;
|
||||
} else {
|
||||
return EntityLumpParseResult {
|
||||
Status_UnexpectedChar, mapEntities.tellg()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse key / value pairs until we reach a closing brace. We currently assume there
|
||||
* are only quoted keys / values up to the next closing brace.
|
||||
*
|
||||
* The SDK suggests that there are cases that could use non-quoted symbols and nested
|
||||
* braces (`shared/mapentities_shared.cpp::MapEntity_ParseToken`), but I haven't seen
|
||||
* those in practice.
|
||||
*/
|
||||
EntityLumpEntry entry;
|
||||
while (mapEntities.peek() != '}') {
|
||||
std::string key, value;
|
||||
|
||||
if (mapEntities.peek() != '"') {
|
||||
return EntityLumpParseResult {
|
||||
Status_UnexpectedChar, mapEntities.tellg()
|
||||
};
|
||||
}
|
||||
mapEntities >> quoted(key) >> std::ws;
|
||||
|
||||
if (mapEntities.peek() != '"') {
|
||||
return EntityLumpParseResult {
|
||||
Status_UnexpectedChar, mapEntities.tellg()
|
||||
};
|
||||
}
|
||||
mapEntities >> quoted(value) >> std::ws;
|
||||
|
||||
entry.emplace_back(key, value);
|
||||
}
|
||||
mapEntities.get();
|
||||
m_Entities.push_back(std::make_shared<EntityLumpEntry>(entry));
|
||||
}
|
||||
|
||||
return EntityLumpParseResult{};
|
||||
}
|
||||
|
||||
std::string EntityLumpManager::Dump() {
|
||||
std::ostringstream stream;
|
||||
for (const auto& entry : m_Entities) {
|
||||
// ignore empty entries
|
||||
if (entry->empty()) {
|
||||
continue;
|
||||
}
|
||||
stream << "{\n";
|
||||
for (const auto& pair : *entry) {
|
||||
stream << '"' << pair.first << "\" \"" << pair.second << '"' << '\n';
|
||||
}
|
||||
stream << "}\n";
|
||||
}
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::weak_ptr<EntityLumpEntry> EntityLumpManager::Get(size_t index) {
|
||||
return m_Entities[index];
|
||||
}
|
||||
|
||||
void EntityLumpManager::Erase(size_t index) {
|
||||
m_Entities.erase(m_Entities.begin() + index);
|
||||
}
|
||||
|
||||
void EntityLumpManager::Insert(size_t index) {
|
||||
m_Entities.emplace(m_Entities.begin() + index, std::make_shared<EntityLumpEntry>());
|
||||
}
|
||||
|
||||
size_t EntityLumpManager::Append() {
|
||||
auto it = m_Entities.emplace(m_Entities.end(), std::make_shared<EntityLumpEntry>());
|
||||
return std::distance(m_Entities.begin(), it);
|
||||
}
|
||||
|
||||
size_t EntityLumpManager::Length() {
|
||||
return m_Entities.size();
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* Entity Lump Manager
|
||||
* Copyright (C) 2021-2022 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$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_LUMPMANAGER_H_
|
||||
#define _INCLUDE_LUMPMANAGER_H_
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* Entity lump manager. Provides a list that stores a list of key / value pairs and the
|
||||
* functionality to (de)serialize it from / to an entity string.
|
||||
* This file and its corresponding .cpp should be compilable independently of SourceMod;
|
||||
* the SourceMod interop is located within smn_entitylump.
|
||||
*
|
||||
* @file lumpmanager.h
|
||||
* @brief Class definition for object that parses lumps.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief A container of key / value pairs.
|
||||
*/
|
||||
using EntityLumpEntry = std::vector<std::pair<std::string, std::string>>;
|
||||
|
||||
enum EntityLumpParseStatus {
|
||||
Status_OK,
|
||||
Status_UnexpectedChar,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Result of parsing an entity lump. On a parse error, m_Status is not Status_OK and
|
||||
* m_Position indicates the offset within the string that caused the parse error.
|
||||
*/
|
||||
struct EntityLumpParseResult {
|
||||
EntityLumpParseStatus m_Status;
|
||||
std::streamoff m_Position;
|
||||
|
||||
operator bool() const;
|
||||
const char* Description() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Manages entity lump entries.
|
||||
*/
|
||||
class EntityLumpManager
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Parses the map entities string into an internal representation.
|
||||
*/
|
||||
EntityLumpParseResult Parse(const char* pMapEntities);
|
||||
|
||||
/**
|
||||
* @brief Dumps the current internal representation out to an std::string.
|
||||
*/
|
||||
std::string Dump();
|
||||
|
||||
/**
|
||||
* @brief Returns a weak reference to an EntityLumpEntry. Used for handles on the scripting side.
|
||||
*/
|
||||
std::weak_ptr<EntityLumpEntry> Get(size_t index);
|
||||
|
||||
/**
|
||||
* @brief Removes an EntityLumpEntry at the given index, shifting down all entries after it by one.
|
||||
*/
|
||||
void Erase(size_t index);
|
||||
|
||||
/**
|
||||
* @brief Inserts a new EntityLumpEntry at the given index, shifting up the entries previously at the index and after it up by one.
|
||||
*/
|
||||
void Insert(size_t index);
|
||||
|
||||
/**
|
||||
* @brief Adds a new EntityLumpEntry to the end. Returns the index of the entry.
|
||||
*/
|
||||
size_t Append();
|
||||
|
||||
/**
|
||||
* @brief Returns the number of EntityLumpEntry items in the list.
|
||||
*/
|
||||
size_t Length();
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<EntityLumpEntry>> m_Entities;
|
||||
};
|
||||
|
||||
#endif // _INCLUDE_LUMPMANAGER_H_
|
||||
+51
-28
@@ -49,6 +49,7 @@ MemoryUtils g_MemUtils;
|
||||
|
||||
MemoryUtils::MemoryUtils()
|
||||
{
|
||||
m_InfoMap.init();
|
||||
#ifdef PLATFORM_APPLE
|
||||
|
||||
task_dyld_info_data_t dyld_info;
|
||||
@@ -77,20 +78,19 @@ void MemoryUtils::OnSourceModAllInitialized()
|
||||
|
||||
void *MemoryUtils::FindPattern(const void *libPtr, const char *pattern, size_t len)
|
||||
{
|
||||
DynLibInfo lib;
|
||||
bool found;
|
||||
char *ptr, *end;
|
||||
const DynLibInfo* lib = nullptr;
|
||||
|
||||
memset(&lib, 0, sizeof(DynLibInfo));
|
||||
|
||||
if (!GetLibraryInfo(libPtr, lib))
|
||||
if ((lib = GetLibraryInfo(libPtr)) == nullptr)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ptr = reinterpret_cast<char *>(lib.baseAddress);
|
||||
end = ptr + lib.memorySize - len;
|
||||
// Search in the original unaltered state of the binary.
|
||||
char *start = lib->originalCopy.get();
|
||||
char *ptr = start;
|
||||
char *end = ptr + lib->memorySize - len;
|
||||
|
||||
bool found;
|
||||
while (ptr < end)
|
||||
{
|
||||
found = true;
|
||||
@@ -103,8 +103,9 @@ void *MemoryUtils::FindPattern(const void *libPtr, const char *pattern, size_t l
|
||||
}
|
||||
}
|
||||
|
||||
// Translate the found offset into the actual live binary memory space.
|
||||
if (found)
|
||||
return ptr;
|
||||
return reinterpret_cast<char *>(lib->baseAddress) + (ptr - start);
|
||||
|
||||
ptr++;
|
||||
}
|
||||
@@ -116,6 +117,8 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
|
||||
{
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
|
||||
/* Add this this library into the cache */
|
||||
GetLibraryInfo(handle);
|
||||
return GetProcAddress((HMODULE)handle, symbol);
|
||||
|
||||
#elif defined PLATFORM_LINUX
|
||||
@@ -162,6 +165,9 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
|
||||
}
|
||||
}
|
||||
|
||||
/* Add this this library into the cache */
|
||||
GetLibraryInfo((void *)dlmap->l_addr);
|
||||
|
||||
/* If we don't have a symbol table for this library, then create one */
|
||||
if (table == NULL)
|
||||
{
|
||||
@@ -325,6 +331,9 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
|
||||
/* Uh oh, we couldn't find a matching handle */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Add this this library into the cache */
|
||||
GetLibraryInfo((void *)dlbase);
|
||||
|
||||
/* See if we already have a symbol table for this library */
|
||||
for (size_t i = 0; i < m_SymTables.size(); i++)
|
||||
@@ -429,15 +438,17 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
|
||||
const DynLibInfo *MemoryUtils::GetLibraryInfo(const void *libPtr)
|
||||
{
|
||||
uintptr_t baseAddr;
|
||||
|
||||
if (libPtr == NULL)
|
||||
{
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DynLibInfo lib;
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
|
||||
#ifdef PLATFORM_X86
|
||||
@@ -456,7 +467,7 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
|
||||
|
||||
if (!VirtualQuery(libPtr, &info, sizeof(MEMORY_BASIC_INFORMATION)))
|
||||
{
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
baseAddr = reinterpret_cast<uintptr_t>(info.AllocationBase);
|
||||
@@ -470,19 +481,19 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
|
||||
/* Check PE magic and signature */
|
||||
if (dos->e_magic != IMAGE_DOS_SIGNATURE || pe->Signature != IMAGE_NT_SIGNATURE || opt->Magic != PE_NT_OPTIONAL_HDR_MAGIC)
|
||||
{
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Check architecture */
|
||||
if (file->Machine != PE_FILE_MACHINE)
|
||||
{
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* For our purposes, this must be a dynamic library */
|
||||
if ((file->Characteristics & IMAGE_FILE_DLL) == 0)
|
||||
{
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Finally, we can do this */
|
||||
@@ -509,12 +520,12 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
|
||||
|
||||
if (!dladdr(libPtr, &info))
|
||||
{
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!info.dli_fbase || !info.dli_fname)
|
||||
{
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* This is for our insane sanity checks :o */
|
||||
@@ -524,31 +535,31 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
|
||||
/* Check ELF magic */
|
||||
if (memcmp(ELFMAG, file->e_ident, SELFMAG) != 0)
|
||||
{
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Check ELF version */
|
||||
if (file->e_ident[EI_VERSION] != EV_CURRENT)
|
||||
{
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Check ELF endianness */
|
||||
if (file->e_ident[EI_DATA] != ELFDATA2LSB)
|
||||
{
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Check ELF architecture */
|
||||
if (file->e_ident[EI_CLASS] != ELF_CLASS || file->e_machine != ELF_MACHINE)
|
||||
{
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* For our purposes, this must be a dynamic library/shared object */
|
||||
if (file->e_type != ET_DYN)
|
||||
{
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
phdrCount = file->e_phnum;
|
||||
@@ -598,12 +609,12 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
|
||||
|
||||
if (!dladdr(libPtr, &info))
|
||||
{
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!info.dli_fbase || !info.dli_fname)
|
||||
{
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* This is for our insane sanity checks :o */
|
||||
@@ -613,19 +624,19 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
|
||||
/* Check Mach-O magic */
|
||||
if (file->magic != MACH_MAGIC)
|
||||
{
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Check architecture */
|
||||
if (file->cputype != MACH_CPU_TYPE || file->cpusubtype != MACH_CPU_SUBTYPE)
|
||||
{
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* For our purposes, this must be a dynamic library */
|
||||
if (file->filetype != MH_DYLIB)
|
||||
{
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cmd_count = file->ncmds;
|
||||
@@ -646,5 +657,17 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
|
||||
|
||||
lib.baseAddress = reinterpret_cast<void *>(baseAddr);
|
||||
|
||||
return true;
|
||||
LibraryInfoMap::Insert i = m_InfoMap.findForAdd(lib.baseAddress);
|
||||
if (i.found())
|
||||
{
|
||||
// We already loaded this binary before.
|
||||
return &i->value;
|
||||
}
|
||||
|
||||
// Keep a copy of the binary in its initial unpatched state for lookup.
|
||||
lib.originalCopy = std::make_unique<char[]>(lib.memorySize);
|
||||
memcpy(lib.originalCopy.get(), lib.baseAddress, lib.memorySize);
|
||||
m_InfoMap.add(i, lib.baseAddress, std::move(lib));
|
||||
|
||||
return &i->value;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
|
||||
#include "common_logic.h"
|
||||
#include <IMemoryUtils.h>
|
||||
#include <am-hashmap.h>
|
||||
#include <memory>
|
||||
#if defined PLATFORM_LINUX || defined PLATFORM_APPLE
|
||||
#include <sh_vector.h>
|
||||
#include "sm_symtable.h"
|
||||
@@ -49,6 +51,7 @@ struct DynLibInfo
|
||||
{
|
||||
void *baseAddress;
|
||||
size_t memorySize;
|
||||
std::unique_ptr<char[]> originalCopy;
|
||||
};
|
||||
|
||||
#if defined PLATFORM_LINUX || defined PLATFORM_APPLE
|
||||
@@ -73,7 +76,7 @@ public: // IMemoryUtils
|
||||
void *FindPattern(const void *libPtr, const char *pattern, size_t len);
|
||||
void *ResolveSymbol(void *handle, const char *symbol);
|
||||
public:
|
||||
bool GetLibraryInfo(const void *libPtr, DynLibInfo &lib);
|
||||
const DynLibInfo *GetLibraryInfo(const void *libPtr);
|
||||
#if defined PLATFORM_LINUX || defined PLATFORM_APPLE
|
||||
private:
|
||||
CVector<LibSymbolTable *> m_SymTables;
|
||||
@@ -83,6 +86,8 @@ private:
|
||||
SInt32 m_OSXMinor;
|
||||
#endif
|
||||
#endif
|
||||
typedef ke::HashMap<void *, DynLibInfo, ke::PointerPolicy<void> > LibraryInfoMap;
|
||||
LibraryInfoMap m_InfoMap;
|
||||
};
|
||||
|
||||
extern MemoryUtils g_MemUtils;
|
||||
|
||||
@@ -0,0 +1,324 @@
|
||||
// vim: set sts=2 ts=8 sw=2 tw=99 et:
|
||||
//
|
||||
// Copyright (C) 2006-2015 AlliedModders LLC
|
||||
//
|
||||
// This file is part of SourcePawn. SourcePawn is free software: you can
|
||||
// redistribute it and/or modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with
|
||||
// SourcePawn. If not, see http://www.gnu.org/licenses/.
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "NativeInvoker.h"
|
||||
|
||||
/********************
|
||||
* FUNCTION CALLING *
|
||||
********************/
|
||||
|
||||
NativeInvoker::NativeInvoker(IPluginContext *pContext, const ke::RefPtr<Native> &native)
|
||||
: context_(pContext),
|
||||
m_curparam(0),
|
||||
m_errorstate(SP_ERROR_NONE),
|
||||
native_(native)
|
||||
{
|
||||
}
|
||||
|
||||
NativeInvoker::~NativeInvoker()
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
|
||||
bool
|
||||
NativeInvoker::IsRunnable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
IPluginContext *
|
||||
NativeInvoker::GetParentContext()
|
||||
{
|
||||
return context_;
|
||||
}
|
||||
|
||||
int NativeInvoker::PushCell(cell_t cell)
|
||||
{
|
||||
if (m_curparam >= SP_MAX_EXEC_PARAMS)
|
||||
return SetError(SP_ERROR_PARAMS_MAX);
|
||||
|
||||
m_info[m_curparam].marked = false;
|
||||
m_params[m_curparam] = cell;
|
||||
m_curparam++;
|
||||
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
int
|
||||
NativeInvoker::PushCellByRef(cell_t *cell, int flags)
|
||||
{
|
||||
return PushArray(cell, 1, flags);
|
||||
}
|
||||
|
||||
int
|
||||
NativeInvoker::PushFloat(float number)
|
||||
{
|
||||
cell_t val = sp::FloatCellUnion(number).cell;
|
||||
|
||||
return PushCell(val);
|
||||
}
|
||||
|
||||
int
|
||||
NativeInvoker::PushFloatByRef(float *number, int flags)
|
||||
{
|
||||
return PushCellByRef((cell_t *)number, flags);
|
||||
}
|
||||
|
||||
int
|
||||
NativeInvoker::PushArray(cell_t *inarray, unsigned int cells, int copyback)
|
||||
{
|
||||
if (m_curparam >= SP_MAX_EXEC_PARAMS)
|
||||
{
|
||||
return SetError(SP_ERROR_PARAMS_MAX);
|
||||
}
|
||||
|
||||
ParamInfo *info = &m_info[m_curparam];
|
||||
|
||||
info->flags = inarray ? copyback : 0;
|
||||
info->marked = true;
|
||||
info->size = cells;
|
||||
info->str.is_sz = false;
|
||||
info->orig_addr = inarray;
|
||||
|
||||
m_curparam++;
|
||||
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
int
|
||||
NativeInvoker::PushString(const char *string)
|
||||
{
|
||||
return _PushString(string, SM_PARAM_STRING_COPY, 0, strlen(string)+1);
|
||||
}
|
||||
|
||||
int
|
||||
NativeInvoker::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags)
|
||||
{
|
||||
return _PushString(buffer, sz_flags, cp_flags, length);
|
||||
}
|
||||
|
||||
int
|
||||
NativeInvoker::_PushString(const char *string, int sz_flags, int cp_flags, size_t len)
|
||||
{
|
||||
if (m_curparam >= SP_MAX_EXEC_PARAMS)
|
||||
return SetError(SP_ERROR_PARAMS_MAX);
|
||||
|
||||
ParamInfo *info = &m_info[m_curparam];
|
||||
|
||||
info->marked = true;
|
||||
info->orig_addr = (cell_t *)string;
|
||||
info->flags = cp_flags;
|
||||
info->size = len;
|
||||
info->str.sz_flags = sz_flags;
|
||||
info->str.is_sz = true;
|
||||
|
||||
m_curparam++;
|
||||
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
void
|
||||
NativeInvoker::Cancel()
|
||||
{
|
||||
if (!m_curparam)
|
||||
return;
|
||||
|
||||
m_errorstate = SP_ERROR_NONE;
|
||||
m_curparam = 0;
|
||||
}
|
||||
|
||||
int
|
||||
NativeInvoker::Execute(cell_t *result, cell_t buffer, cell_t size)
|
||||
{
|
||||
context_->ClearLastNativeError();
|
||||
|
||||
// For backward compatibility, we have to clear the exception state.
|
||||
// Otherwise code like this:
|
||||
//
|
||||
// static cell_t native(cx, params) {
|
||||
// for (auto callback : callbacks) {
|
||||
// callback->Execute();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Could unintentionally leak a pending exception back to the caller,
|
||||
// which wouldn't have happened before the Great Exception Refactoring.
|
||||
|
||||
SourcePawn::ExceptionHandler eh(context_);
|
||||
eh.Debug(!size);
|
||||
|
||||
if (!Invoke(result)) {
|
||||
if(size)
|
||||
context_->StringToLocalUTF8(buffer, size, eh.Message(), NULL);
|
||||
int Err = context_->GetLastNativeError();
|
||||
context_->ClearLastNativeError();
|
||||
return Err;
|
||||
}
|
||||
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
bool
|
||||
NativeInvoker::Invoke(cell_t *result)
|
||||
{
|
||||
if (!IsRunnable()) {
|
||||
Cancel();
|
||||
context_->ReportErrorNumber(SP_ERROR_NOT_RUNNABLE);
|
||||
return false;
|
||||
}
|
||||
if (int err = m_errorstate) {
|
||||
Cancel();
|
||||
context_->ReportErrorNumber(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
//This is for re-entrancy!
|
||||
cell_t _temp_params[SP_MAX_EXEC_PARAMS + 1];
|
||||
cell_t *temp_params = &_temp_params[1];
|
||||
ParamInfo temp_info[SP_MAX_EXEC_PARAMS];
|
||||
unsigned int numparams = m_curparam;
|
||||
unsigned int i;
|
||||
|
||||
if (numparams)
|
||||
{
|
||||
//Save the info locally, then reset it for re-entrant calls.
|
||||
memcpy(temp_info, m_info, numparams * sizeof(ParamInfo));
|
||||
}
|
||||
m_curparam = 0;
|
||||
|
||||
/* Initialize 0th parameter */
|
||||
_temp_params[0] = numparams;
|
||||
|
||||
/* Browse the parameters and build arrays */
|
||||
bool ok = true;
|
||||
for (i=0; i<numparams; i++) {
|
||||
/* Is this marked as an array? */
|
||||
if (temp_info[i].marked) {
|
||||
if (!temp_info[i].str.is_sz) {
|
||||
/* Allocate a normal/generic array */
|
||||
int err = context_->HeapAlloc(
|
||||
temp_info[i].size,
|
||||
&(temp_info[i].local_addr),
|
||||
&(temp_info[i].phys_addr));
|
||||
if (err != SP_ERROR_NONE) {
|
||||
context_->ReportErrorNumber(err);
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
if (temp_info[i].orig_addr)
|
||||
{
|
||||
memcpy(temp_info[i].phys_addr, temp_info[i].orig_addr, sizeof(cell_t) * temp_info[i].size);
|
||||
}
|
||||
} else {
|
||||
/* Calculate cells required for the string */
|
||||
size_t cells = (temp_info[i].size + sizeof(cell_t) - 1) / sizeof(cell_t);
|
||||
|
||||
/* Allocate the buffer */
|
||||
int err = context_->HeapAlloc(
|
||||
cells,
|
||||
&(temp_info[i].local_addr),
|
||||
&(temp_info[i].phys_addr));
|
||||
if (err != SP_ERROR_NONE) {
|
||||
context_->ReportErrorNumber(err);
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Copy original string if necessary */
|
||||
if ((temp_info[i].str.sz_flags & SM_PARAM_STRING_COPY) && (temp_info[i].orig_addr != NULL))
|
||||
{
|
||||
/* Cut off UTF-8 properly */
|
||||
if (temp_info[i].str.sz_flags & SM_PARAM_STRING_UTF8) {
|
||||
context_->StringToLocalUTF8(
|
||||
temp_info[i].local_addr,
|
||||
temp_info[i].size,
|
||||
(const char *)temp_info[i].orig_addr,
|
||||
NULL);
|
||||
}
|
||||
/* Copy a binary blob */
|
||||
else if (temp_info[i].str.sz_flags & SM_PARAM_STRING_BINARY)
|
||||
{
|
||||
memmove(temp_info[i].phys_addr, temp_info[i].orig_addr, temp_info[i].size);
|
||||
}
|
||||
/* Copy ASCII characters */
|
||||
else
|
||||
{
|
||||
context_->StringToLocal(
|
||||
temp_info[i].local_addr,
|
||||
temp_info[i].size,
|
||||
(const char *)temp_info[i].orig_addr);
|
||||
}
|
||||
}
|
||||
} /* End array/string calculation */
|
||||
/* Update the pushed parameter with the byref local address */
|
||||
temp_params[i] = temp_info[i].local_addr;
|
||||
} else {
|
||||
/* Just copy the value normally */
|
||||
temp_params[i] = m_params[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Make the call if we can */
|
||||
if (ok)
|
||||
{
|
||||
if(native_->native)
|
||||
*result = native_->native->func(context_, _temp_params);
|
||||
else
|
||||
*result = native_->fake->wrapper->Invoke(context_, _temp_params);
|
||||
}
|
||||
|
||||
/* i should be equal to the last valid parameter + 1 */
|
||||
bool docopies = ok;
|
||||
while (i--) {
|
||||
if (!temp_info[i].marked)
|
||||
continue;
|
||||
|
||||
if (docopies && (temp_info[i].flags & SM_PARAM_COPYBACK)) {
|
||||
if (temp_info[i].orig_addr) {
|
||||
if (temp_info[i].str.is_sz) {
|
||||
memcpy(temp_info[i].orig_addr, temp_info[i].phys_addr, temp_info[i].size);
|
||||
|
||||
} else {
|
||||
if (temp_info[i].size == 1) {
|
||||
*temp_info[i].orig_addr = *(temp_info[i].phys_addr);
|
||||
} else {
|
||||
memcpy(temp_info[i].orig_addr,
|
||||
temp_info[i].phys_addr,
|
||||
temp_info[i].size * sizeof(cell_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (int err = context_->HeapPop(temp_info[i].local_addr))
|
||||
context_->ReportErrorNumber(err);
|
||||
}
|
||||
|
||||
return context_->GetLastNativeError() == SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
int
|
||||
NativeInvoker::SetError(int err)
|
||||
{
|
||||
m_errorstate = err;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int NativeInvoker::CallFunction(const cell_t *params, unsigned int num_params, cell_t *result) { return 0; }
|
||||
funcid_t NativeInvoker::GetFunctionID() { return 0; }
|
||||
int NativeInvoker::Execute2(IPluginContext *ctx, cell_t *result) { return 0; }
|
||||
int NativeInvoker::CallFunction2(IPluginContext *ctx, const cell_t *params, unsigned int num_params, cell_t *result) { return 0; }
|
||||
IPluginRuntime *NativeInvoker::GetParentRuntime() { return NULL; }
|
||||
@@ -0,0 +1,78 @@
|
||||
// vim: set sts=2 ts=8 sw=2 tw=99 et:
|
||||
//
|
||||
// Copyright (C) 2006-2015 AlliedModders LLC
|
||||
//
|
||||
// This file is part of SourcePawn. SourcePawn is free software: you can
|
||||
// redistribute it and/or modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with
|
||||
// SourcePawn. If not, see http://www.gnu.org/licenses/.
|
||||
//
|
||||
#ifndef _INCLUDE_SOURCEMOD_NATIVE_INVOKER_H_
|
||||
#define _INCLUDE_SOURCEMOD_NATIVE_INVOKER_H_
|
||||
|
||||
#include <sp_vm_api.h>
|
||||
#include <amtl/am-refcounting.h>
|
||||
#include "Native.h"
|
||||
|
||||
struct ParamInfo
|
||||
{
|
||||
int flags; /* Copy-back flags */
|
||||
bool marked; /* Whether this is marked as being used */
|
||||
cell_t local_addr; /* Local address to free */
|
||||
cell_t *phys_addr; /* Physical address of our copy */
|
||||
cell_t *orig_addr; /* Original address to copy back to */
|
||||
ucell_t size; /* Size of array in bytes */
|
||||
struct {
|
||||
bool is_sz; /* is a string */
|
||||
int sz_flags; /* has sz flags */
|
||||
} str;
|
||||
};
|
||||
|
||||
class NativeInvoker : public IPluginFunction
|
||||
{
|
||||
public:
|
||||
NativeInvoker(IPluginContext *pContext, const ke::RefPtr<Native> &native);
|
||||
virtual ~NativeInvoker();
|
||||
|
||||
public:
|
||||
int PushCell(cell_t cell);
|
||||
int PushCellByRef(cell_t *cell, int flags);
|
||||
int PushFloat(float number);
|
||||
int PushFloatByRef(float *number, int flags);
|
||||
int PushArray(cell_t *inarray, unsigned int cells, int copyback);
|
||||
int PushString(const char *string);
|
||||
int PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags);
|
||||
int Execute(cell_t *result, cell_t buffer=0, cell_t size=0);
|
||||
void Cancel();
|
||||
int CallFunction(const cell_t *params, unsigned int num_params, cell_t *result);
|
||||
IPluginContext *GetParentContext();
|
||||
bool Invoke(cell_t *result);
|
||||
bool IsRunnable();
|
||||
funcid_t GetFunctionID();
|
||||
int Execute2(IPluginContext *ctx, cell_t *result);
|
||||
int CallFunction2(IPluginContext *ctx,
|
||||
const cell_t *params,
|
||||
unsigned int num_params,
|
||||
cell_t *result);
|
||||
IPluginRuntime *GetParentRuntime();
|
||||
const char *DebugName() {
|
||||
return native_->name();
|
||||
}
|
||||
|
||||
private:
|
||||
int _PushString(const char *string, int sz_flags, int cp_flags, size_t len);
|
||||
int SetError(int err);
|
||||
|
||||
private:
|
||||
IPluginContext *context_;
|
||||
cell_t m_params[SP_MAX_EXEC_PARAMS];
|
||||
ParamInfo m_info[SP_MAX_EXEC_PARAMS];
|
||||
unsigned int m_curparam;
|
||||
int m_errorstate;
|
||||
ke::RefPtr<Native> native_;
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_NATIVE_INVOKER_H_
|
||||
@@ -49,9 +49,11 @@ namespace SourceMod
|
||||
{
|
||||
struct IdentityToken_t
|
||||
{
|
||||
Handle_t ident;
|
||||
void *ptr;
|
||||
IdentityType_t type;
|
||||
Handle_t ident = 0;
|
||||
void *ptr = nullptr;
|
||||
IdentityType_t type = 0;
|
||||
size_t num_handles = 0;
|
||||
bool warned_handle_usage = false;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
#include "LibrarySys.h"
|
||||
#include "RootConsoleMenu.h"
|
||||
#include "CellArray.h"
|
||||
#include "smn_entitylump.h"
|
||||
#include <bridge/include/BridgeAPI.h>
|
||||
#include <bridge/include/IProviderCallbacks.h>
|
||||
|
||||
@@ -89,6 +90,8 @@ CNativeOwner g_CoreNatives;
|
||||
PseudoAddressManager pseudoAddr;
|
||||
#endif
|
||||
|
||||
EntityLumpParseResult lastParseResult;
|
||||
|
||||
static void AddCorePhraseFile(const char *filename)
|
||||
{
|
||||
g_pCorePhrases->AddPhraseFile(filename);
|
||||
@@ -135,6 +138,35 @@ static uint32_t ToPseudoAddress(void *addr)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void SetEntityLumpWritable(bool writable)
|
||||
{
|
||||
g_bLumpAvailableForWriting = writable;
|
||||
|
||||
// write-lock causes the map entities to be serialized out to string
|
||||
if (!writable)
|
||||
{
|
||||
g_strMapEntities = lumpmanager->Dump();
|
||||
}
|
||||
}
|
||||
|
||||
static bool ParseEntityLumpString(const char *pMapEntities, int &status, size_t &position)
|
||||
{
|
||||
lastParseResult = lumpmanager->Parse(pMapEntities);
|
||||
status = static_cast<int>(lastParseResult.m_Status);
|
||||
position = static_cast<size_t>(lastParseResult.m_Position);
|
||||
return lastParseResult;
|
||||
}
|
||||
|
||||
// returns nullptr if the original lump failed to parse
|
||||
static const char* GetEntityLumpString()
|
||||
{
|
||||
if (!lastParseResult)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return g_strMapEntities.c_str();
|
||||
}
|
||||
|
||||
// Defined in smn_filesystem.cpp.
|
||||
extern bool OnLogPrint(const char *msg);
|
||||
|
||||
@@ -170,6 +202,9 @@ static sm_logic_t logic =
|
||||
CellArray::Free,
|
||||
FromPseudoAddress,
|
||||
ToPseudoAddress,
|
||||
SetEntityLumpWritable,
|
||||
ParseEntityLumpString,
|
||||
GetEntityLumpString,
|
||||
&g_PluginSys,
|
||||
&g_ShareSys,
|
||||
&g_Extensions,
|
||||
|
||||
@@ -89,6 +89,6 @@ unsigned int UTIL_CRC32(const void *pdata, size_t data_length)
|
||||
crc = CRCTable[c] ^ (crc >> 8);
|
||||
}
|
||||
|
||||
return crc;
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,11 @@ static cell_t CreateArray(IPluginContext *pContext, const cell_t *params)
|
||||
|
||||
if (params[2])
|
||||
{
|
||||
array->resize(params[2]);
|
||||
if (!array->resize(params[2]))
|
||||
{
|
||||
delete array;
|
||||
return pContext->ThrowNativeError("Failed to resize array to startsize \"%u\".", params[2]);
|
||||
}
|
||||
}
|
||||
|
||||
Handle_t hndl = handlesys->CreateHandle(htCellArray, array, pContext->GetIdentity(), g_pCoreIdent, NULL);
|
||||
|
||||
@@ -83,6 +83,22 @@ static cell_t CreateStack(IPluginContext *pContext, const cell_t *params)
|
||||
return hndl;
|
||||
}
|
||||
|
||||
static cell_t ClearStack(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = handlesys->ReadHandle(params[1], htCellStack, &sec, (void **)&array)) != HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
array->clear();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t CloneStack(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *oldArray;
|
||||
@@ -425,6 +441,7 @@ REGISTER_NATIVES(cellStackNatives)
|
||||
|
||||
// Transitional syntax support.
|
||||
{"ArrayStack.ArrayStack", CreateStack},
|
||||
{"ArrayStack.Clear", ClearStack},
|
||||
{"ArrayStack.Clone", CloneStack},
|
||||
{"ArrayStack.Pop", ArrayStack_Pop},
|
||||
{"ArrayStack.PopString", ArrayStack_PopString},
|
||||
|
||||
+127
-2
@@ -32,6 +32,7 @@
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <list>
|
||||
#include "common_logic.h"
|
||||
#include "Logger.h"
|
||||
|
||||
@@ -56,6 +57,7 @@
|
||||
#include <bridge/include/CoreProvider.h>
|
||||
#include <bridge/include/IScriptManager.h>
|
||||
#include <bridge/include/IExtensionBridge.h>
|
||||
#include <sh_vector.h>
|
||||
|
||||
using namespace SourceMod;
|
||||
using namespace SourcePawn;
|
||||
@@ -114,6 +116,71 @@ public:
|
||||
}
|
||||
} g_CoreNativeHelpers;
|
||||
|
||||
/**
|
||||
* @brief Nearly identical to the standard core plugin iterator
|
||||
* with one key difference. Next doesn't increment the counter
|
||||
* the first time it is ran. This is a hack for the methodmap..
|
||||
*/
|
||||
class CMMPluginIterator
|
||||
: public IPluginIterator,
|
||||
public IPluginsListener
|
||||
{
|
||||
public:
|
||||
CMMPluginIterator(const CVector<SMPlugin *> *list)
|
||||
: m_hasStarted(false)
|
||||
{
|
||||
for(auto iter = list->begin(); iter != list->end(); ++iter) {
|
||||
m_list.push_back(*iter);
|
||||
}
|
||||
scripts->FreePluginList(list);
|
||||
|
||||
m_current = m_list.begin();
|
||||
|
||||
scripts->AddPluginsListener(this);
|
||||
}
|
||||
|
||||
virtual ~CMMPluginIterator()
|
||||
{
|
||||
scripts->RemovePluginsListener(this);
|
||||
}
|
||||
virtual bool MorePlugins() override
|
||||
{
|
||||
return (m_current != m_list.end());
|
||||
}
|
||||
virtual IPlugin *GetPlugin() override
|
||||
{
|
||||
return *m_current;
|
||||
}
|
||||
virtual void NextPlugin() override
|
||||
{
|
||||
if(!m_hasStarted)
|
||||
{
|
||||
m_hasStarted = true;
|
||||
return;
|
||||
}
|
||||
|
||||
m_current++;
|
||||
}
|
||||
virtual void Release() override
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void OnPluginDestroyed(IPlugin *plugin) override
|
||||
{
|
||||
if (*m_current == plugin)
|
||||
m_current = m_list.erase(m_current);
|
||||
else
|
||||
m_list.remove(static_cast<SMPlugin *>(plugin));
|
||||
}
|
||||
|
||||
private:
|
||||
std::list<SMPlugin *> m_list;
|
||||
std::list<SMPlugin *>::iterator m_current;
|
||||
bool m_hasStarted;
|
||||
};
|
||||
|
||||
void LogAction(Handle_t hndl, int type, int client, int target, const char *message)
|
||||
{
|
||||
if (g_OnLogAction->GetFunctionCount())
|
||||
@@ -146,7 +213,7 @@ void LogAction(Handle_t hndl, int type, int client, int target, const char *mess
|
||||
g_Logger.LogMessage("[%s] %s", logtag, message);
|
||||
}
|
||||
|
||||
static cell_t ThrowError(IPluginContext *pContext, const cell_t *params)
|
||||
static cell_t ThrowError(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char buffer[512];
|
||||
|
||||
@@ -264,6 +331,60 @@ static cell_t ReadPlugin(IPluginContext *pContext, const cell_t *params)
|
||||
return pPlugin->GetMyHandle();
|
||||
}
|
||||
|
||||
static cell_t PluginIterator_Create(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
IPluginIterator *iter = new CMMPluginIterator(scripts->ListPlugins());
|
||||
|
||||
Handle_t hndl = handlesys->CreateHandle(g_PlIter, iter, pContext->GetIdentity(), g_pCoreIdent, NULL);
|
||||
|
||||
if (hndl == BAD_HANDLE)
|
||||
{
|
||||
iter->Release();
|
||||
}
|
||||
|
||||
return hndl;
|
||||
}
|
||||
|
||||
static cell_t PluginIterator_Next(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = (Handle_t)params[1];
|
||||
HandleSecurity sec{pContext->GetIdentity(), g_pCoreIdent};
|
||||
HandleError err{};
|
||||
IPluginIterator *pIter = nullptr;
|
||||
|
||||
if ((err=handlesys->ReadHandle(hndl, g_PlIter, &sec, (void **)&pIter)) != HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
|
||||
}
|
||||
|
||||
if(!pIter->MorePlugins())
|
||||
return 0;
|
||||
|
||||
pIter->NextPlugin();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t PluginIterator_Plugin_get(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = (Handle_t)params[1];
|
||||
HandleSecurity sec{pContext->GetIdentity(), g_pCoreIdent};
|
||||
HandleError err{};
|
||||
IPluginIterator *pIter = nullptr;
|
||||
|
||||
if ((err=handlesys->ReadHandle(hndl, g_PlIter, &sec, (void **)&pIter)) != HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
|
||||
}
|
||||
|
||||
IPlugin *pPlugin = pIter->GetPlugin();
|
||||
if (!pPlugin)
|
||||
{
|
||||
return BAD_HANDLE;
|
||||
}
|
||||
|
||||
return pPlugin->GetMyHandle();
|
||||
}
|
||||
|
||||
IPlugin *GetPluginFromHandle(IPluginContext *pContext, Handle_t hndl)
|
||||
{
|
||||
if (hndl == BAD_HANDLE)
|
||||
@@ -969,7 +1090,7 @@ REGISTER_NATIVES(coreNatives)
|
||||
{"FormatTime", FormatTime},
|
||||
{"GetPluginIterator", GetPluginIterator},
|
||||
{"MorePlugins", MorePlugins},
|
||||
{"ReadPlugin", ReadPlugin},
|
||||
{"ReadPlugin", ReadPlugin},
|
||||
{"GetPluginStatus", GetPluginStatus},
|
||||
{"GetPluginFilename", GetPluginFilename},
|
||||
{"IsPluginDebugging", IsPluginDebugging},
|
||||
@@ -1000,5 +1121,9 @@ REGISTER_NATIVES(coreNatives)
|
||||
{"FrameIterator.LineNumber.get", FrameIterator_LineNumber},
|
||||
{"FrameIterator.GetFunctionName", FrameIterator_GetFunctionName},
|
||||
{"FrameIterator.GetFilePath", FrameIterator_GetFilePath},
|
||||
|
||||
{"PluginIterator.PluginIterator", PluginIterator_Create},
|
||||
{"PluginIterator.Next", PluginIterator_Next},
|
||||
{"PluginIterator.Plugin.get", PluginIterator_Plugin_get},
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,367 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* Entity Lump Manager
|
||||
* Copyright (C) 2021-2022 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$
|
||||
*/
|
||||
|
||||
#include "HandleSys.h"
|
||||
#include "common_logic.h"
|
||||
|
||||
#include "LumpManager.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
HandleType_t g_EntityLumpEntryType;
|
||||
|
||||
std::string g_strMapEntities;
|
||||
bool g_bLumpAvailableForWriting = false;
|
||||
|
||||
static EntityLumpManager s_LumpManager;
|
||||
EntityLumpManager *lumpmanager = &s_LumpManager;
|
||||
|
||||
class LumpManagerNatives :
|
||||
public IHandleTypeDispatch,
|
||||
public SMGlobalClass
|
||||
{
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModAllInitialized()
|
||||
{
|
||||
g_EntityLumpEntryType = handlesys->CreateType("EntityLumpEntry", this, 0, NULL, NULL, g_pCoreIdent, NULL);
|
||||
}
|
||||
void OnSourceModShutdown()
|
||||
{
|
||||
handlesys->RemoveType(g_EntityLumpEntryType, g_pCoreIdent);
|
||||
}
|
||||
public: //IHandleTypeDispatch
|
||||
void OnHandleDestroy(HandleType_t type, void* object)
|
||||
{
|
||||
if (type == g_EntityLumpEntryType)
|
||||
{
|
||||
delete reinterpret_cast<std::weak_ptr<EntityLumpEntry>*>(object);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static LumpManagerNatives s_LumpManagerNatives;
|
||||
|
||||
cell_t sm_LumpManagerGet(IPluginContext *pContext, const cell_t *params) {
|
||||
int index = params[1];
|
||||
if (index < 0 || index >= static_cast<int>(lumpmanager->Length())) {
|
||||
return pContext->ThrowNativeError("Invalid index %d", index);
|
||||
}
|
||||
|
||||
std::weak_ptr<EntityLumpEntry>* pReference = new std::weak_ptr<EntityLumpEntry>;
|
||||
*pReference = lumpmanager->Get(index);
|
||||
|
||||
return handlesys->CreateHandle(g_EntityLumpEntryType, pReference,
|
||||
pContext->GetIdentity(), g_pCoreIdent, NULL);
|
||||
}
|
||||
|
||||
cell_t sm_LumpManagerErase(IPluginContext *pContext, const cell_t *params) {
|
||||
if (!g_bLumpAvailableForWriting) {
|
||||
return pContext->ThrowNativeError("Cannot use EntityLump.Erase() outside of OnMapInit");
|
||||
}
|
||||
|
||||
int index = params[1];
|
||||
if (index < 0 || index >= static_cast<int>(lumpmanager->Length())) {
|
||||
return pContext->ThrowNativeError("Invalid index %d", index);
|
||||
}
|
||||
|
||||
lumpmanager->Erase(index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cell_t sm_LumpManagerInsert(IPluginContext *pContext, const cell_t *params) {
|
||||
if (!g_bLumpAvailableForWriting) {
|
||||
return pContext->ThrowNativeError("Cannot use EntityLump.Insert() outside of OnMapInit");
|
||||
}
|
||||
|
||||
int index = params[1];
|
||||
if (index < 0 || index > static_cast<int>(lumpmanager->Length())) {
|
||||
return pContext->ThrowNativeError("Invalid index %d", index);
|
||||
}
|
||||
|
||||
lumpmanager->Insert(index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cell_t sm_LumpManagerAppend(IPluginContext *pContext, const cell_t *params) {
|
||||
if (!g_bLumpAvailableForWriting) {
|
||||
return pContext->ThrowNativeError("Cannot use EntityLump.Append() outside of OnMapInit");
|
||||
}
|
||||
return lumpmanager->Append();
|
||||
}
|
||||
|
||||
cell_t sm_LumpManagerLength(IPluginContext *pContext, const cell_t *params) {
|
||||
return lumpmanager->Length();
|
||||
}
|
||||
|
||||
cell_t sm_LumpEntryGet(IPluginContext *pContext, const cell_t *params) {
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
HandleError err;
|
||||
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
|
||||
std::weak_ptr<EntityLumpEntry>* entryref = nullptr;
|
||||
if ((err = handlesys->ReadHandle(hndl, g_EntityLumpEntryType, &sec, (void**) &entryref)) != HandleError_None) {
|
||||
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (error: %d)", hndl, err);
|
||||
}
|
||||
|
||||
if (entryref->expired()) {
|
||||
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (reference expired)", hndl);
|
||||
}
|
||||
|
||||
auto entry = entryref->lock();
|
||||
|
||||
int index = params[2];
|
||||
if (index < 0 || index >= static_cast<int>(entry->size())) {
|
||||
return pContext->ThrowNativeError("Invalid index %d", index);
|
||||
}
|
||||
|
||||
const auto& pair = (*entry)[index];
|
||||
|
||||
size_t nBytes;
|
||||
pContext->StringToLocalUTF8(params[3], params[4], pair.first.c_str(), &nBytes);
|
||||
pContext->StringToLocalUTF8(params[5], params[6], pair.second.c_str(), &nBytes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
cell_t sm_LumpEntryUpdate(IPluginContext *pContext, const cell_t *params) {
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
HandleError err;
|
||||
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
|
||||
std::weak_ptr<EntityLumpEntry>* entryref = nullptr;
|
||||
if ((err = handlesys->ReadHandle(hndl, g_EntityLumpEntryType, &sec, (void**) &entryref)) != HandleError_None) {
|
||||
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (error: %d)", hndl, err);
|
||||
}
|
||||
|
||||
if (entryref->expired()) {
|
||||
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (reference expired)", hndl);
|
||||
}
|
||||
|
||||
if (!g_bLumpAvailableForWriting) {
|
||||
return pContext->ThrowNativeError("Cannot use EntityLumpEntry.Update() outside of OnMapInit");
|
||||
}
|
||||
|
||||
auto entry = entryref->lock();
|
||||
|
||||
int index = params[2];
|
||||
if (index < 0 || index >= static_cast<int>(entry->size())) {
|
||||
return pContext->ThrowNativeError("Invalid index %d", index);
|
||||
}
|
||||
|
||||
char *key, *value;
|
||||
pContext->LocalToStringNULL(params[3], &key);
|
||||
pContext->LocalToStringNULL(params[4], &value);
|
||||
|
||||
auto& pair = (*entry)[index];
|
||||
if (key != nullptr) {
|
||||
pair.first = key;
|
||||
}
|
||||
if (value != nullptr) {
|
||||
pair.second = value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
cell_t sm_LumpEntryInsert(IPluginContext *pContext, const cell_t *params) {
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
HandleError err;
|
||||
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
|
||||
std::weak_ptr<EntityLumpEntry>* entryref = nullptr;
|
||||
if ((err = handlesys->ReadHandle(hndl, g_EntityLumpEntryType, &sec, (void**) &entryref)) != HandleError_None) {
|
||||
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (error: %d)", hndl, err);
|
||||
}
|
||||
|
||||
if (entryref->expired()) {
|
||||
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (reference expired)", hndl);
|
||||
}
|
||||
|
||||
if (!g_bLumpAvailableForWriting) {
|
||||
return pContext->ThrowNativeError("Cannot use EntityLumpEntry.Insert() outside of OnMapInit");
|
||||
}
|
||||
|
||||
auto entry = entryref->lock();
|
||||
|
||||
int index = params[2];
|
||||
if (index < 0 || index > static_cast<int>(entry->size())) {
|
||||
return pContext->ThrowNativeError("Invalid index %d", index);
|
||||
}
|
||||
|
||||
char *key, *value;
|
||||
pContext->LocalToString(params[3], &key);
|
||||
pContext->LocalToString(params[4], &value);
|
||||
|
||||
entry->emplace(entry->begin() + index, key, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
cell_t sm_LumpEntryErase(IPluginContext *pContext, const cell_t *params) {
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
HandleError err;
|
||||
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
|
||||
std::weak_ptr<EntityLumpEntry>* entryref = nullptr;
|
||||
if ((err = handlesys->ReadHandle(hndl, g_EntityLumpEntryType, &sec, (void**) &entryref)) != HandleError_None) {
|
||||
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (error: %d)", hndl, err);
|
||||
}
|
||||
|
||||
if (entryref->expired()) {
|
||||
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (reference expired)", hndl);
|
||||
}
|
||||
|
||||
if (!g_bLumpAvailableForWriting) {
|
||||
return pContext->ThrowNativeError("Cannot use EntityLumpEntry.Erase() outside of OnMapInit");
|
||||
}
|
||||
|
||||
auto entry = entryref->lock();
|
||||
|
||||
int index = params[2];
|
||||
if (index < 0 || index >= static_cast<int>(entry->size())) {
|
||||
return pContext->ThrowNativeError("Invalid index %d", index);
|
||||
}
|
||||
|
||||
entry->erase(entry->begin() + index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
cell_t sm_LumpEntryAppend(IPluginContext *pContext, const cell_t *params) {
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
HandleError err;
|
||||
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
|
||||
std::weak_ptr<EntityLumpEntry>* entryref = nullptr;
|
||||
if ((err = handlesys->ReadHandle(hndl, g_EntityLumpEntryType, &sec, (void**) &entryref)) != HandleError_None) {
|
||||
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (error: %d)", hndl, err);
|
||||
}
|
||||
|
||||
if (entryref->expired()) {
|
||||
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (reference expired)", hndl);
|
||||
}
|
||||
|
||||
if (!g_bLumpAvailableForWriting) {
|
||||
return pContext->ThrowNativeError("Cannot use EntityLumpEntry.Append() outside of OnMapInit");
|
||||
}
|
||||
|
||||
auto entry = entryref->lock();
|
||||
|
||||
char *key, *value;
|
||||
pContext->LocalToString(params[2], &key);
|
||||
pContext->LocalToString(params[3], &value);
|
||||
|
||||
entry->emplace_back(key, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
cell_t sm_LumpEntryFindKey(IPluginContext *pContext, const cell_t *params) {
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
HandleError err;
|
||||
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
|
||||
std::weak_ptr<EntityLumpEntry>* entryref = nullptr;
|
||||
if ((err = handlesys->ReadHandle(hndl, g_EntityLumpEntryType, &sec, (void**) &entryref)) != HandleError_None) {
|
||||
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (error: %d)", hndl, err);
|
||||
}
|
||||
|
||||
if (entryref->expired()) {
|
||||
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (reference expired)", hndl);
|
||||
}
|
||||
|
||||
// start from the index after the current one
|
||||
int start = params[3] + 1;
|
||||
|
||||
auto entry = entryref->lock();
|
||||
|
||||
if (start < 0 || start >= static_cast<int>(entry->size())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *key;
|
||||
pContext->LocalToString(params[2], &key);
|
||||
|
||||
auto matches_key = [&key](std::pair<std::string,std::string> pair) {
|
||||
return pair.first == key;
|
||||
};
|
||||
|
||||
auto result = std::find_if(entry->begin() + start, entry->end(), matches_key);
|
||||
|
||||
if (result == entry->end()) {
|
||||
return -1;
|
||||
}
|
||||
return std::distance(entry->begin(), result);
|
||||
}
|
||||
|
||||
cell_t sm_LumpEntryLength(IPluginContext *pContext, const cell_t *params) {
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
HandleError err;
|
||||
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
|
||||
std::weak_ptr<EntityLumpEntry>* entryref = nullptr;
|
||||
if ((err = handlesys->ReadHandle(hndl, g_EntityLumpEntryType, &sec, (void**) &entryref)) != HandleError_None) {
|
||||
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (error: %d)", hndl, err);
|
||||
}
|
||||
|
||||
if (entryref->expired()) {
|
||||
return pContext->ThrowNativeError("Invalid EntityLumpEntry handle %x (reference expired)", hndl);
|
||||
}
|
||||
|
||||
auto entry = entryref->lock();
|
||||
return entry->size();
|
||||
}
|
||||
|
||||
REGISTER_NATIVES(entityLumpNatives)
|
||||
{
|
||||
{ "EntityLump.Get", sm_LumpManagerGet },
|
||||
{ "EntityLump.Erase", sm_LumpManagerErase },
|
||||
{ "EntityLump.Insert", sm_LumpManagerInsert },
|
||||
{ "EntityLump.Append", sm_LumpManagerAppend },
|
||||
{ "EntityLump.Length", sm_LumpManagerLength },
|
||||
|
||||
{ "EntityLumpEntry.Get", sm_LumpEntryGet },
|
||||
{ "EntityLumpEntry.Update", sm_LumpEntryUpdate },
|
||||
{ "EntityLumpEntry.Insert", sm_LumpEntryInsert },
|
||||
{ "EntityLumpEntry.Erase", sm_LumpEntryErase },
|
||||
{ "EntityLumpEntry.Append", sm_LumpEntryAppend },
|
||||
{ "EntityLumpEntry.FindKey", sm_LumpEntryFindKey },
|
||||
{ "EntityLumpEntry.Length.get", sm_LumpEntryLength },
|
||||
|
||||
{NULL, NULL}
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2022-2022 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$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_ENTITYLUMP_H_
|
||||
#define _INCLUDE_SOURCEMOD_ENTITYLUMP_H_
|
||||
|
||||
#include <IHandleSys.h>
|
||||
#include "LumpManager.h"
|
||||
|
||||
using namespace SourceMod;
|
||||
|
||||
extern std::string g_strMapEntities;
|
||||
extern bool g_bLumpAvailableForWriting;
|
||||
extern EntityLumpManager *lumpmanager;
|
||||
|
||||
#endif // _INCLUDE_SOURCEMOD_ENTITYLUMP_H_
|
||||
@@ -414,7 +414,7 @@ static cell_t FormatNativeString(IPluginContext *pContext, const cell_t *params)
|
||||
DetectExceptions eh(pContext);
|
||||
written = atcprintf(output_buffer, maxlen, format_buffer, s_curcaller, s_curparams, &var_param);
|
||||
if (eh.HasException())
|
||||
return 0;
|
||||
return pContext->GetLastNativeError();
|
||||
}
|
||||
|
||||
cell_t *addr;
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
#include <IHandleSys.h>
|
||||
#include <IForwardSys.h>
|
||||
#include <ISourceMod.h>
|
||||
#include "ShareSys.h"
|
||||
#include "NativeInvoker.h"
|
||||
|
||||
HandleType_t g_GlobalFwdType = 0;
|
||||
HandleType_t g_PrivateFwdType = 0;
|
||||
@@ -44,6 +46,7 @@ static bool s_CallStarted = false;
|
||||
static ICallable *s_pCallable = NULL;
|
||||
static IPluginFunction *s_pFunction = NULL;
|
||||
static IForward *s_pForward = NULL;
|
||||
static NativeInvoker *s_pInvoker = NULL;
|
||||
|
||||
class ForwardNativeHelpers :
|
||||
public SMGlobalClass,
|
||||
@@ -103,6 +106,9 @@ inline void ResetCall()
|
||||
s_pFunction = NULL;
|
||||
s_pForward = NULL;
|
||||
s_pCallable = NULL;
|
||||
if(s_pInvoker)
|
||||
delete s_pInvoker;
|
||||
s_pInvoker = NULL;
|
||||
}
|
||||
|
||||
static cell_t sm_GetFunctionByName(IPluginContext *pContext, const cell_t *params)
|
||||
@@ -367,6 +373,27 @@ static cell_t sm_CallStartForward(IPluginContext *pContext, const cell_t *params
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t sm_CallStartNative(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
ResetCall();
|
||||
|
||||
char *name;
|
||||
pContext->LocalToString(params[1], &name);
|
||||
|
||||
ke::RefPtr<Native> pNative = g_ShareSys.FindNative(name);
|
||||
|
||||
if (!pNative)
|
||||
return 0;//pContext->ThrowNativeError("Invalid native \"%s\"", name);
|
||||
|
||||
s_pInvoker = new NativeInvoker(pContext, pNative);
|
||||
|
||||
s_pCallable = static_cast<ICallable *>(s_pInvoker);
|
||||
|
||||
s_CallStarted = true;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t sm_CallPushCell(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
int err;
|
||||
@@ -657,6 +684,39 @@ static cell_t sm_CallFinish(IPluginContext *pContext, const cell_t *params)
|
||||
IForward *pForward = s_pForward;
|
||||
ResetCall();
|
||||
err = pForward->Execute(result, NULL);
|
||||
} else if (s_pInvoker) {
|
||||
err = s_pInvoker->Execute(result);
|
||||
ResetCall();
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static cell_t sm_CallFinishEx(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
int err = SP_ERROR_NOT_RUNNABLE;
|
||||
cell_t *result;
|
||||
|
||||
if (!s_CallStarted)
|
||||
{
|
||||
return pContext->ThrowNativeError("Cannot finish call when there is no call in progress");
|
||||
}
|
||||
|
||||
pContext->LocalToPhysAddr(params[1], &result);
|
||||
|
||||
// Note: Execute() swallows exceptions, so this is okay.
|
||||
if (s_pFunction)
|
||||
{
|
||||
IPluginFunction *pFunction = s_pFunction;
|
||||
ResetCall();
|
||||
err = pFunction->Execute(result, params[2], params[3]);
|
||||
} else if (s_pForward) {
|
||||
IForward *pForward = s_pForward;
|
||||
ResetCall();
|
||||
err = pForward->Execute(result, NULL);
|
||||
} else if (s_pInvoker) {
|
||||
err = s_pInvoker->Execute(result, params[2], params[3]);
|
||||
ResetCall();
|
||||
}
|
||||
|
||||
return err;
|
||||
@@ -743,6 +803,7 @@ REGISTER_NATIVES(functionNatives)
|
||||
{"RemoveAllFromForward", sm_RemoveAllFromForward},
|
||||
{"Call_StartFunction", sm_CallStartFunction},
|
||||
{"Call_StartForward", sm_CallStartForward},
|
||||
{"Call_StartNative", sm_CallStartNative},
|
||||
{"Call_PushCell", sm_CallPushCell},
|
||||
{"Call_PushCellRef", sm_CallPushCellRef},
|
||||
{"Call_PushFloat", sm_CallPushFloat},
|
||||
@@ -754,6 +815,7 @@ REGISTER_NATIVES(functionNatives)
|
||||
{"Call_PushNullVector", sm_CallPushNullVector},
|
||||
{"Call_PushNullString", sm_CallPushNullString},
|
||||
{"Call_Finish", sm_CallFinish},
|
||||
{"Call_FinishEx", sm_CallFinishEx},
|
||||
{"Call_Cancel", sm_CallCancel},
|
||||
{"RequestFrame", sm_AddFrameAction},
|
||||
|
||||
|
||||
@@ -592,7 +592,10 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
if (!libsys->FileTime(pMapList->path, FileTime_LastChange, &last_time)
|
||||
char realpath[PLATFORM_MAX_PATH];
|
||||
g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", pMapList->path);
|
||||
|
||||
if (!libsys->FileTime(realpath, FileTime_LastChange, &last_time)
|
||||
|| last_time > pMapList->last_modified_time)
|
||||
{
|
||||
/* Reparse */
|
||||
|
||||
+42
-94
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* vim: set ts=4 sw=4 tw=99 noet :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
@@ -141,10 +141,6 @@ public:
|
||||
void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
|
||||
unsigned int OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr);
|
||||
bool OnSetHandlerOption(const char *option, const void *data);
|
||||
#if 0
|
||||
void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
|
||||
void OnMenuDisplayItem(IBaseMenu *menu, int client, unsigned int item, const char **display);
|
||||
#endif
|
||||
private:
|
||||
cell_t DoAction(IBaseMenu *menu, MenuAction action, cell_t param1, cell_t param2, cell_t def_res=0);
|
||||
private:
|
||||
@@ -496,99 +492,51 @@ void CMenuHandler::OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *
|
||||
unsigned int winning_votes = results->item_list[0].count;
|
||||
|
||||
DoAction(menu, MenuAction_VoteEnd, winning_item, (total_votes << 16) | (winning_votes & 0xFFFF));
|
||||
} else {
|
||||
IPluginContext *pContext = m_pVoteResults->GetParentContext();
|
||||
bool no_call = false;
|
||||
int err;
|
||||
return;
|
||||
}
|
||||
|
||||
/* First array */
|
||||
cell_t client_array_address = -1;
|
||||
cell_t *client_array_base = NULL;
|
||||
cell_t client_array_size = results->num_clients + (results->num_clients * 2);
|
||||
if (client_array_size)
|
||||
{
|
||||
if ((err = pContext->HeapAlloc(client_array_size, &client_array_address, &client_array_base))
|
||||
!= SP_ERROR_NONE)
|
||||
{
|
||||
g_DbgReporter.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for client list.", client_array_size * sizeof(cell_t));
|
||||
no_call = true;
|
||||
} else {
|
||||
cell_t target_offs = sizeof(cell_t) * results->num_clients;
|
||||
cell_t *cur_index = client_array_base;
|
||||
cell_t *cur_array;
|
||||
for (unsigned int i=0; i<results->num_clients; i++)
|
||||
{
|
||||
/* Copy the array index */
|
||||
*cur_index = target_offs;
|
||||
/* Get the current array address */
|
||||
cur_array = (cell_t *)((char *)cur_index + target_offs);
|
||||
/* Store information */
|
||||
cur_array[0] = results->client_list[i].client;
|
||||
cur_array[1] = results->client_list[i].item;
|
||||
/* Adjust for the new target by subtracting one indirection
|
||||
* and adding one array.
|
||||
*/
|
||||
target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
|
||||
cur_index++;
|
||||
}
|
||||
}
|
||||
IPluginContext *pContext = m_pVoteResults->GetParentContext();
|
||||
AutoEnterHeapScope heap_scope(pContext);
|
||||
|
||||
/* First array */
|
||||
cell_t client_array_address = -1;
|
||||
if (cell_t client_array_size = results->num_clients * 2) {
|
||||
auto init = std::make_unique<cell_t[]>(client_array_size);
|
||||
for (unsigned int i = 0; i < results->num_clients; i++) {
|
||||
init[i * 2] = results->client_list[i].client;
|
||||
init[i * 2 + 1] = results->client_list[i].item;
|
||||
}
|
||||
|
||||
/* Second array */
|
||||
cell_t item_array_address = -1;
|
||||
cell_t *item_array_base = NULL;
|
||||
cell_t item_array_size = results->num_items + (results->num_items * 2);
|
||||
if (item_array_size)
|
||||
{
|
||||
if ((err = pContext->HeapAlloc(item_array_size, &item_array_address, &item_array_base))
|
||||
!= SP_ERROR_NONE)
|
||||
{
|
||||
g_DbgReporter.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for item list.", item_array_size);
|
||||
no_call = true;
|
||||
} else {
|
||||
cell_t target_offs = sizeof(cell_t) * results->num_items;
|
||||
cell_t *cur_index = item_array_base;
|
||||
cell_t *cur_array;
|
||||
for (unsigned int i=0; i<results->num_items; i++)
|
||||
{
|
||||
/* Copy the array index */
|
||||
*cur_index = target_offs;
|
||||
/* Get the current array address */
|
||||
cur_array = (cell_t *)((char *)cur_index + target_offs);
|
||||
/* Store information */
|
||||
cur_array[0] = results->item_list[i].item;
|
||||
cur_array[1] = results->item_list[i].count;
|
||||
/* Adjust for the new target by subtracting one indirection
|
||||
* and adding one array.
|
||||
*/
|
||||
target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
|
||||
cur_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally, push everything */
|
||||
if (!no_call)
|
||||
{
|
||||
m_pVoteResults->PushCell(menu->GetHandle());
|
||||
m_pVoteResults->PushCell(results->num_votes);
|
||||
m_pVoteResults->PushCell(results->num_clients);
|
||||
m_pVoteResults->PushCell(client_array_address);
|
||||
m_pVoteResults->PushCell(results->num_items);
|
||||
m_pVoteResults->PushCell(item_array_address);
|
||||
m_pVoteResults->Execute(NULL);
|
||||
}
|
||||
|
||||
/* Free what we allocated, in reverse order as required */
|
||||
if (item_array_address != -1)
|
||||
{
|
||||
pContext->HeapPop(item_array_address);
|
||||
}
|
||||
if (client_array_address != -1)
|
||||
{
|
||||
pContext->HeapPop(client_array_address);
|
||||
if (!pContext->HeapAlloc2dArray(results->num_clients, 2, &client_array_address, init.get())) {
|
||||
g_DbgReporter.GenerateError(pContext, m_fnVoteResult, -1,
|
||||
"Menu callback could not allocate cells for client list.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Second array */
|
||||
cell_t item_array_address = -1;
|
||||
if (cell_t item_array_size = results->num_items * 2) {
|
||||
auto init = std::make_unique<cell_t[]>(item_array_size);
|
||||
for (unsigned int i = 0; i < results->num_items; i++) {
|
||||
init[i * 2] = results->item_list[i].item;
|
||||
init[i * 2 + 1] = results->item_list[i].count;
|
||||
}
|
||||
if (!pContext->HeapAlloc2dArray(results->num_items, 2, &item_array_address, init.get())) {
|
||||
g_DbgReporter.GenerateError(pContext, m_fnVoteResult, -1,
|
||||
"Menu callback could not allocate %d cells for item list.",
|
||||
item_array_size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_pVoteResults->PushCell(menu->GetHandle());
|
||||
m_pVoteResults->PushCell(results->num_votes);
|
||||
m_pVoteResults->PushCell(results->num_clients);
|
||||
m_pVoteResults->PushCell(client_array_address);
|
||||
m_pVoteResults->PushCell(results->num_items);
|
||||
m_pVoteResults->PushCell(item_array_address);
|
||||
m_pVoteResults->Execute(NULL);
|
||||
}
|
||||
|
||||
bool CMenuHandler::OnSetHandlerOption(const char *option, const void *data)
|
||||
@@ -1014,7 +962,7 @@ static cell_t GetMenuExitBackButton(IPluginContext *pContext, const cell_t *para
|
||||
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
|
||||
}
|
||||
|
||||
return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
|
||||
return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXITBACK) ? 1 : 0;
|
||||
}
|
||||
|
||||
static cell_t SetMenuExitButton(IPluginContext *pContext, const cell_t *params)
|
||||
|
||||
+29
-26
@@ -270,18 +270,7 @@ static cell_t sm_GetClientCount(IPluginContext *pCtx, const cell_t *params)
|
||||
return playerhelpers->GetNumPlayers();
|
||||
}
|
||||
|
||||
int maxplayers = playerhelpers->GetMaxClients();
|
||||
int count = 0;
|
||||
for (int i = 1; i <= maxplayers; ++i)
|
||||
{
|
||||
IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(i);
|
||||
if ((pPlayer->IsConnected()) && !(pPlayer->IsInGame()))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return (playerhelpers->GetNumPlayers() + count);
|
||||
return playerhelpers->GetNumClients();
|
||||
}
|
||||
|
||||
static cell_t sm_GetMaxClients(IPluginContext *pCtx, const cell_t *params)
|
||||
@@ -1471,24 +1460,23 @@ static cell_t IsClientInKickQueue(IPluginContext *pContext, const cell_t *params
|
||||
return pPlayer->IsInKickQueue() ? 1 : 0;
|
||||
}
|
||||
|
||||
cmd_target_info_t g_ProcessTargetString_info;
|
||||
static cell_t ProcessTargetString(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
cmd_target_info_t info;
|
||||
|
||||
pContext->LocalToString(params[1], (char **) &info.pattern);
|
||||
info.admin = params[2];
|
||||
pContext->LocalToPhysAddr(params[3], &info.targets);
|
||||
info.max_targets = params[4];
|
||||
info.flags = params[5];
|
||||
pContext->LocalToString(params[6], &info.target_name);
|
||||
info.target_name_maxlength = params[7];
|
||||
pContext->LocalToString(params[1], (char **) &g_ProcessTargetString_info.pattern);
|
||||
g_ProcessTargetString_info.admin = params[2];
|
||||
pContext->LocalToPhysAddr(params[3], &g_ProcessTargetString_info.targets);
|
||||
g_ProcessTargetString_info.max_targets = params[4];
|
||||
g_ProcessTargetString_info.flags = params[5];
|
||||
pContext->LocalToString(params[6], &g_ProcessTargetString_info.target_name);
|
||||
g_ProcessTargetString_info.target_name_maxlength = params[7];
|
||||
|
||||
cell_t *tn_is_ml;
|
||||
pContext->LocalToPhysAddr(params[8], &tn_is_ml);
|
||||
|
||||
playerhelpers->ProcessCommandTarget(&info);
|
||||
playerhelpers->ProcessCommandTarget(&g_ProcessTargetString_info);
|
||||
|
||||
if (info.target_name_style == COMMAND_TARGETNAME_ML)
|
||||
if (g_ProcessTargetString_info.target_name_style == COMMAND_TARGETNAME_ML)
|
||||
{
|
||||
*tn_is_ml = 1;
|
||||
}
|
||||
@@ -1497,16 +1485,30 @@ static cell_t ProcessTargetString(IPluginContext *pContext, const cell_t *params
|
||||
*tn_is_ml = 0;
|
||||
}
|
||||
|
||||
if (info.num_targets == 0)
|
||||
if (g_ProcessTargetString_info.num_targets == 0)
|
||||
{
|
||||
return info.reason;
|
||||
return g_ProcessTargetString_info.reason;
|
||||
}
|
||||
else
|
||||
{
|
||||
return info.num_targets;
|
||||
return g_ProcessTargetString_info.num_targets;
|
||||
}
|
||||
}
|
||||
|
||||
static cell_t GetLastProcessTargetString(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
cell_t *admin, *flags;
|
||||
|
||||
pContext->StringToLocalUTF8(params[1], params[2], g_ProcessTargetString_info.pattern, NULL);
|
||||
pContext->LocalToPhysAddr(params[3], &admin);
|
||||
pContext->LocalToPhysAddr(params[4], &flags);
|
||||
|
||||
*admin = g_ProcessTargetString_info.admin;
|
||||
*flags = g_ProcessTargetString_info.flags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static cell_t FormatActivitySource(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
int value;
|
||||
@@ -1658,6 +1660,7 @@ REGISTER_NATIVES(playernatives)
|
||||
{ "NotifyPostAdminCheck", NotifyPostAdminCheck },
|
||||
{ "IsClientInKickQueue", IsClientInKickQueue },
|
||||
{ "ProcessTargetString", ProcessTargetString },
|
||||
{ "GetLastProcessTargetString", GetLastProcessTargetString },
|
||||
{ "FormatActivitySource", FormatActivitySource },
|
||||
{ "GetClientSerial", sm_GetClientSerial },
|
||||
{ "GetClientFromSerial", sm_GetClientFromSerial },
|
||||
|
||||
+128
-15
@@ -35,6 +35,7 @@
|
||||
#include "common_logic.h"
|
||||
#include "CellArray.h"
|
||||
#include <IHandleSys.h>
|
||||
#include <amtl/am-raii.h>
|
||||
|
||||
/***********************************
|
||||
* About the double array hack *
|
||||
@@ -95,7 +96,7 @@ void sort_random(cell_t *array, cell_t size)
|
||||
{
|
||||
int n = rand() % (i + 1);
|
||||
|
||||
if (array[i] != array[n])
|
||||
if (array[i] != array[n])
|
||||
{
|
||||
array[i] ^= array[n];
|
||||
array[n] ^= array[i];
|
||||
@@ -125,7 +126,7 @@ static cell_t sm_SortIntegers(IPluginContext *pContext, const cell_t *params)
|
||||
if (type == Sort_Ascending)
|
||||
{
|
||||
qsort(array, array_size, sizeof(cell_t), sort_ints_asc);
|
||||
}
|
||||
}
|
||||
else if (type == Sort_Descending)
|
||||
{
|
||||
qsort(array, array_size, sizeof(cell_t), sort_ints_desc);
|
||||
@@ -195,7 +196,7 @@ static cell_t sm_SortFloats(IPluginContext *pContext, const cell_t *params)
|
||||
static cell_t *g_CurStringArray = NULL;
|
||||
static cell_t *g_CurRebaseMap = NULL;
|
||||
|
||||
int sort_strings_asc(const void *blk1, const void *blk2)
|
||||
int sort_strings_asc_legacy(const void *blk1, const void *blk2)
|
||||
{
|
||||
cell_t reloc1 = *(cell_t *)blk1;
|
||||
cell_t reloc2 = *(cell_t *)blk2;
|
||||
@@ -206,7 +207,7 @@ int sort_strings_asc(const void *blk1, const void *blk2)
|
||||
return strcmp(str1, str2);
|
||||
}
|
||||
|
||||
int sort_strings_desc(const void *blk1, const void *blk2)
|
||||
int sort_strings_desc_legacy(const void *blk1, const void *blk2)
|
||||
{
|
||||
cell_t reloc1 = *(cell_t *)blk1;
|
||||
cell_t reloc2 = *(cell_t *)blk2;
|
||||
@@ -217,7 +218,7 @@ int sort_strings_desc(const void *blk1, const void *blk2)
|
||||
return strcmp(str2, str1);
|
||||
}
|
||||
|
||||
static cell_t sm_SortStrings(IPluginContext *pContext, const cell_t *params)
|
||||
static cell_t sm_SortStrings_Legacy(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
cell_t *array;
|
||||
cell_t array_size = params[2];
|
||||
@@ -245,11 +246,11 @@ static cell_t sm_SortStrings(IPluginContext *pContext, const cell_t *params)
|
||||
|
||||
if (type == Sort_Ascending)
|
||||
{
|
||||
qsort(array, array_size, sizeof(cell_t), sort_strings_asc);
|
||||
}
|
||||
qsort(array, array_size, sizeof(cell_t), sort_strings_asc_legacy);
|
||||
}
|
||||
else if (type == Sort_Descending)
|
||||
{
|
||||
qsort(array, array_size, sizeof(cell_t), sort_strings_desc);
|
||||
qsort(array, array_size, sizeof(cell_t), sort_strings_desc_legacy);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -275,6 +276,69 @@ static cell_t sm_SortStrings(IPluginContext *pContext, const cell_t *params)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static IPluginContext* sSortContext = nullptr;
|
||||
|
||||
int sort_strings_asc(const void *blk1, const void *blk2)
|
||||
{
|
||||
cell_t str_addr1 = *(cell_t *)blk1;
|
||||
cell_t str_addr2 = *(cell_t *)blk2;
|
||||
|
||||
char *str1;
|
||||
char *str2;
|
||||
if (sSortContext->LocalToString(str_addr1, &str1) != SP_ERROR_NONE ||
|
||||
sSortContext->LocalToString(str_addr2, &str2) != SP_ERROR_NONE)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return strcmp(str1, str2);
|
||||
}
|
||||
|
||||
int sort_strings_desc(const void *blk1, const void *blk2)
|
||||
{
|
||||
cell_t str_addr1 = *(cell_t *)blk1;
|
||||
cell_t str_addr2 = *(cell_t *)blk2;
|
||||
|
||||
char *str1;
|
||||
char *str2;
|
||||
if (sSortContext->LocalToString(str_addr1, &str1) != SP_ERROR_NONE ||
|
||||
sSortContext->LocalToString(str_addr2, &str2) != SP_ERROR_NONE)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return strcmp(str2, str1);
|
||||
}
|
||||
|
||||
static cell_t sm_SortStrings(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
auto rt = pContext->GetRuntime();
|
||||
if (!rt->UsesDirectArrays())
|
||||
return sm_SortStrings_Legacy(pContext, params);
|
||||
|
||||
cell_t *array;
|
||||
cell_t array_size = params[2];
|
||||
cell_t type = params[3];
|
||||
|
||||
pContext->LocalToPhysAddr(params[1], &array);
|
||||
|
||||
ke::SaveAndSet<IPluginContext*> set_context(&sSortContext, pContext);
|
||||
|
||||
if (type == Sort_Ascending)
|
||||
{
|
||||
qsort(array, array_size, sizeof(cell_t), sort_strings_asc);
|
||||
}
|
||||
else if (type == Sort_Descending)
|
||||
{
|
||||
qsort(array, array_size, sizeof(cell_t), sort_strings_desc);
|
||||
}
|
||||
else
|
||||
{
|
||||
sort_random(array, array_size);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct sort_info
|
||||
{
|
||||
IPluginFunction *pFunc;
|
||||
@@ -335,7 +399,7 @@ static cell_t sm_SortCustom1D(IPluginContext *pContext, const cell_t *params)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sort2d_amx_custom(const void *elem1, const void *elem2)
|
||||
static int sort2d_amx_custom_legacy(const void *elem1, const void *elem2)
|
||||
{
|
||||
if (g_SortInfo.eh->HasException())
|
||||
return 0;
|
||||
@@ -361,7 +425,7 @@ int sort2d_amx_custom(const void *elem1, const void *elem2)
|
||||
return result;
|
||||
}
|
||||
|
||||
static cell_t sm_SortCustom2D(IPluginContext *pContext, const cell_t *params)
|
||||
static cell_t sm_SortCustom2D_Legacy(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
cell_t *array;
|
||||
cell_t array_size = params[2];
|
||||
@@ -401,7 +465,7 @@ static cell_t sm_SortCustom2D(IPluginContext *pContext, const cell_t *params)
|
||||
array[i] = i;
|
||||
}
|
||||
|
||||
qsort(array, array_size, sizeof(cell_t), sort2d_amx_custom);
|
||||
qsort(array, array_size, sizeof(cell_t), sort2d_amx_custom_legacy);
|
||||
|
||||
/** Fixup process! */
|
||||
for (int i=0; i<array_size; i++)
|
||||
@@ -419,6 +483,55 @@ static cell_t sm_SortCustom2D(IPluginContext *pContext, const cell_t *params)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int sort2d_amx_custom(const void *elem1, const void *elem2)
|
||||
{
|
||||
if (g_SortInfo.eh->HasException())
|
||||
return 0;
|
||||
|
||||
cell_t iv1 = *(cell_t *)elem1;
|
||||
cell_t iv2 = *(cell_t *)elem2;
|
||||
|
||||
cell_t result = 0;
|
||||
g_SortInfo.pFunc->PushCell(iv1);
|
||||
g_SortInfo.pFunc->PushCell(iv2);
|
||||
g_SortInfo.pFunc->PushCell(g_SortInfo.array_addr);
|
||||
g_SortInfo.pFunc->PushCell(g_SortInfo.hndl);
|
||||
g_SortInfo.pFunc->Invoke(&result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static cell_t sm_SortCustom2D(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
auto rt = pContext->GetRuntime();
|
||||
if (!rt->UsesDirectArrays())
|
||||
return sm_SortCustom2D_Legacy(pContext, params);
|
||||
|
||||
cell_t *array;
|
||||
cell_t array_size = params[2];
|
||||
IPluginFunction *pFunction;
|
||||
|
||||
pContext->LocalToPhysAddr(params[1], &array);
|
||||
|
||||
if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
|
||||
{
|
||||
return pContext->ThrowNativeError("Function %x is not a valid function", params[3]);
|
||||
}
|
||||
|
||||
sort_info oldinfo = g_SortInfo;
|
||||
|
||||
DetectExceptions eh(pContext);
|
||||
g_SortInfo.pFunc = pFunction;
|
||||
g_SortInfo.hndl = params[4];
|
||||
g_SortInfo.array_addr = params[1];
|
||||
g_SortInfo.eh = &eh;
|
||||
|
||||
qsort(array, array_size, sizeof(cell_t), sort2d_amx_custom);
|
||||
|
||||
g_SortInfo = oldinfo;
|
||||
return 1;
|
||||
}
|
||||
|
||||
enum SortType
|
||||
{
|
||||
Sort_Integer = 0,
|
||||
@@ -454,7 +567,7 @@ static cell_t sm_SortADTArray(IPluginContext *pContext, const cell_t *params)
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = handlesys->ReadHandle(params[1], htCellArray, &sec, (void **)&cArray))
|
||||
if ((err = handlesys->ReadHandle(params[1], htCellArray, &sec, (void **)&cArray))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
@@ -491,7 +604,7 @@ static cell_t sm_SortADTArray(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
qsort(array, arraysize, blocksize * sizeof(cell_t), sort_floats_asc);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
qsort(array, arraysize, blocksize * sizeof(cell_t), sort_floats_desc);
|
||||
}
|
||||
@@ -502,7 +615,7 @@ static cell_t sm_SortADTArray(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
qsort(array, arraysize, blocksize * sizeof(cell_t), sort_adtarray_strings_asc);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
qsort(array, arraysize, blocksize * sizeof(cell_t), sort_adtarray_strings_desc);
|
||||
}
|
||||
@@ -545,7 +658,7 @@ static cell_t sm_SortADTArrayCustom(IPluginContext *pContext, const cell_t *para
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = handlesys->ReadHandle(params[1], htCellArray, &sec, (void **)&cArray))
|
||||
if ((err = handlesys->ReadHandle(params[1], htCellArray, &sec, (void **)&cArray))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
|
||||
@@ -170,7 +170,7 @@ static cell_t Int64ToString(IPluginContext *pCtx, const cell_t *params)
|
||||
char *str;
|
||||
pCtx->LocalToPhysAddr(params[1], &num);
|
||||
pCtx->LocalToString(params[2], &str);
|
||||
size_t res = ke::SafeSprintf(str, params[3], "%" KE_FMT_I64, (int64_t)num[1] << 32ll | (int64_t)num[0]);
|
||||
size_t res = ke::SafeSprintf(str, params[3], "%" KE_FMT_I64, (uint64_t)num[1] << 32ll | (uint32_t)num[0]);
|
||||
|
||||
return static_cast<cell_t>(res);
|
||||
}
|
||||
|
||||
@@ -1218,7 +1218,7 @@ reswitch:
|
||||
cell_t *target;
|
||||
pCtx->LocalToString(params[arg++], &key);
|
||||
pCtx->LocalToPhysAddr(params[arg++], &target);
|
||||
res = Translate(buf_p, llen, pCtx, key, *target, params, &arg, &error);
|
||||
res = Translate(buf_p, llen + 1, pCtx, key, *target, params, &arg, &error);
|
||||
if (error)
|
||||
{
|
||||
return 0;
|
||||
@@ -1235,7 +1235,7 @@ reswitch:
|
||||
size_t res;
|
||||
cell_t target = bridge->GetGlobalTarget();
|
||||
pCtx->LocalToString(params[arg++], &key);
|
||||
res = Translate(buf_p, llen, pCtx, key, target, params, &arg, &error);
|
||||
res = Translate(buf_p, llen + 1, pCtx, key, target, params, &arg, &error);
|
||||
if (error)
|
||||
{
|
||||
return 0;
|
||||
|
||||
+8
-38
@@ -51,9 +51,6 @@
|
||||
#include <bridge/include/IVEngineServerBridge.h>
|
||||
#include <bridge/include/IPlayerInfoBridge.h>
|
||||
#include <bridge/include/IFileSystemBridge.h>
|
||||
#if PROTOBUF_PROXY_ENABLE
|
||||
# include "pb_handle.h"
|
||||
#endif
|
||||
|
||||
sm_logic_t logicore;
|
||||
|
||||
@@ -496,6 +493,8 @@ const char *CoreProviderImpl::GetSourceEngineName()
|
||||
return "doi";
|
||||
#elif SOURCE_ENGINE == SE_CSGO
|
||||
return "csgo";
|
||||
#elif SOURCE_ENGINE == SE_MOCK
|
||||
return "mock";
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -655,41 +654,6 @@ void CoreProviderImpl::InitializeBridge()
|
||||
rootmenu = logicore.rootmenu;
|
||||
}
|
||||
|
||||
bool CoreProviderImpl::LoadProtobufProxy(char *error, size_t maxlength)
|
||||
{
|
||||
#if !defined(PROTOBUF_PROXY_ENABLE)
|
||||
return false;
|
||||
#else
|
||||
char file[PLATFORM_MAX_PATH];
|
||||
|
||||
#if !defined(PROTOBUF_PROXY_BINARY_NAME)
|
||||
# error "No engine suffix defined"
|
||||
#endif
|
||||
|
||||
/* Now it's time to load the logic binary */
|
||||
g_SMAPI->PathFormat(file,
|
||||
sizeof(file),
|
||||
"%s/bin/" PLATFORM_ARCH_FOLDER PROTOBUF_PROXY_BINARY_NAME PLATFORM_LIB_EXT,
|
||||
g_SourceMod.GetSourceModPath());
|
||||
|
||||
char myerror[255];
|
||||
pbproxy_ = ke::SharedLib::Open(file, myerror, sizeof(myerror));
|
||||
if (!pbproxy_) {
|
||||
ke::SafeSprintf(error, maxlength, "failed to load %s: %s", file, myerror);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto fn = pbproxy_->get<GetProtobufProxyFn>("GetProtobufProxy");
|
||||
if (!fn) {
|
||||
ke::SafeStrcpy(error, maxlength, "could not find GetProtobufProxy function");
|
||||
return false;
|
||||
}
|
||||
|
||||
gProtobufProxy = fn();
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CoreProviderImpl::LoadBridge(char *error, size_t maxlength)
|
||||
{
|
||||
char file[PLATFORM_MAX_PATH];
|
||||
@@ -770,6 +734,12 @@ CoreProviderImpl::DefineCommand(const char *name, const char *help, const Comman
|
||||
commands_.push_back(impl);
|
||||
}
|
||||
|
||||
void CoreProviderImpl::FormatSourceBinaryName(const char *basename, char *buffer, size_t maxlength)
|
||||
{
|
||||
bool use_prefix = (!strcasecmp(basename, "tier0") || !strcasecmp(basename, "vstdlib"));
|
||||
ke::SafeSprintf(buffer, maxlength, "%s%s%s%s", use_prefix ? SOURCE_BIN_PREFIX : "", basename, SOURCE_BIN_SUFFIX, SOURCE_BIN_EXT);
|
||||
}
|
||||
|
||||
void CoreProviderImpl::InitializeHooks()
|
||||
{
|
||||
hooks_.Start();
|
||||
|
||||
@@ -1,217 +0,0 @@
|
||||
/**
|
||||
* vim: set ts=4 sw=4 tw=99 noet :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2020 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$
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#if defined(PROTOBUF_ENABLE)
|
||||
#include <google/protobuf/message.h>
|
||||
|
||||
#if defined(PROTOBUF_PROXY_ENABLE)
|
||||
# include "pb_proxy.h"
|
||||
|
||||
extern IProtobufProxy* gProtobufProxy;
|
||||
#endif
|
||||
|
||||
const google::protobuf::Message *GetMessagePrototype(int msg_type);
|
||||
|
||||
// On SDKs which use protobufs, the engine has objects compiled against a specific
|
||||
// version of protobuf. Normally this is fine, we take care on Linux to use the
|
||||
// same C++ ABI. On macOS however, we use libc++ to enable C++11 functionality,
|
||||
// whereas the protobuf library has been compiled with libstc++. These ABIs are
|
||||
// not compatible.
|
||||
//
|
||||
// To address the problem, we introduce PbHandle. PbHandle is a wrapper around
|
||||
// protobuf::Message with two added pieces of state: whether or not the handle
|
||||
// "owns" the message (and can free it in its destructor), and whether or not
|
||||
// the handle was created by the engine (private) or created by SourceMod
|
||||
// (local).
|
||||
//
|
||||
// Whenever we transfer a protobuf::Message pointer to SourceMod, we must take
|
||||
// care to convert it to a Local version first. Whenever we transfer a protobuf
|
||||
// pointer to the engine, we must convert it to a Private handle.
|
||||
//
|
||||
// For platforms with no ABI differences (almost all of them), the handle is a
|
||||
// no-op. The private and local localities are compatible and no translation
|
||||
// takes place.
|
||||
//
|
||||
// On macOS, CS:GO does require translation. SourceMod loads a tiny shim
|
||||
// library that contains a copy of the protobuf sources compiled against the
|
||||
// game's ABI. It then provides serialization and deserialization methods.
|
||||
// SourceMod must not interact with the game's protobuf objects without first
|
||||
// going through this proxy library.
|
||||
//
|
||||
// Note that PbHandle is not quite like unique_ptr_. It can be converted into a
|
||||
// PbHandle that does not destroy the underlying object. This is mainly because
|
||||
// UserMessages.cpp has rather complex state, so it is useful to track locality
|
||||
// without destroying an object. An unowned PbHandle must not outlive the
|
||||
// owning PbHandle.
|
||||
class PbHandle
|
||||
{
|
||||
public:
|
||||
enum Ownership {
|
||||
Owned,
|
||||
Unowned,
|
||||
};
|
||||
enum Locality {
|
||||
Local,
|
||||
Private,
|
||||
};
|
||||
|
||||
PbHandle() : msg_(nullptr) {}
|
||||
PbHandle(decltype(nullptr)) : msg_(nullptr) {}
|
||||
PbHandle(google::protobuf::Message* msg, Ownership ownership, Locality locality)
|
||||
: msg_(msg),
|
||||
ownership_(ownership),
|
||||
locality_(locality)
|
||||
{}
|
||||
PbHandle(PbHandle&& other)
|
||||
: msg_(other.msg_),
|
||||
ownership_(other.ownership_),
|
||||
locality_(other.locality_)
|
||||
{
|
||||
other.msg_ = nullptr;
|
||||
}
|
||||
PbHandle(const PbHandle&) = delete;
|
||||
|
||||
~PbHandle() {
|
||||
maybe_free();
|
||||
}
|
||||
|
||||
PbHandle& operator =(PbHandle&& other) {
|
||||
if (other.msg_ != msg_)
|
||||
maybe_free();
|
||||
msg_ = other.msg_;
|
||||
ownership_ = other.ownership_;
|
||||
locality_ = other.locality_;
|
||||
other.msg_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
PbHandle& operator =(const PbHandle&) = delete;
|
||||
|
||||
google::protobuf::Message* operator ->() const { return msg_; }
|
||||
|
||||
google::protobuf::Message* GetLocalMessage() {
|
||||
assert(locality_ == Local);
|
||||
return msg_;
|
||||
}
|
||||
google::protobuf::Message* GetPrivateMessage() {
|
||||
assert(locality_ == Private);
|
||||
return msg_;
|
||||
}
|
||||
|
||||
PbHandle ToLocal(int msg_type) {
|
||||
if (locality_ == Local)
|
||||
return AsUnowned();
|
||||
#if defined(PROTOBUF_PROXY_ENABLE)
|
||||
PbHandle local(GetMessagePrototype(msg_type)->New(), Owned, Local);
|
||||
local.CopyFromPrivate(msg_);
|
||||
return local;
|
||||
#else
|
||||
return PbHandle(msg_, Unowned, Local);
|
||||
#endif
|
||||
}
|
||||
|
||||
PbHandle ToPrivate(int msg_type) {
|
||||
if (locality_ == Private)
|
||||
return AsUnowned();
|
||||
#if defined(PROTOBUF_PROXY_ENABLE)
|
||||
PbHandle priv(gProtobufProxy->NewPrototype(msg_type), Owned, Private);
|
||||
priv.CopyFromLocal(msg_);
|
||||
return priv;
|
||||
#else
|
||||
return PbHandle(msg_, Unowned, Private);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CopyFrom(const PbHandle& other) {
|
||||
if (other.msg_ == msg_) {
|
||||
assert(other.locality_ == locality_);
|
||||
return;
|
||||
}
|
||||
#if defined(PROTOBUF_PROXY_ENABLE)
|
||||
if (other.locality_ == Local)
|
||||
CopyFromLocal(other.msg_);
|
||||
else if (other.locality_ == Private)
|
||||
CopyFromPrivate(other.msg_);
|
||||
#else
|
||||
msg_->CopyFrom(*other.msg_);
|
||||
#endif
|
||||
}
|
||||
|
||||
PbHandle AsUnowned() {
|
||||
return PbHandle(msg_, Unowned, locality_);
|
||||
}
|
||||
|
||||
bool is_owned() const { return ownership_ == Owned; }
|
||||
bool is_local() const { return locality_ == Local; }
|
||||
|
||||
private:
|
||||
#if defined(PROTOBUF_PROXY_ENABLE)
|
||||
void CopyFromPrivate(google::protobuf::Message* message) {
|
||||
void* out;
|
||||
size_t len;
|
||||
if (!gProtobufProxy->Serialize(message, &out, &len))
|
||||
return;
|
||||
if (locality_ == Local)
|
||||
msg_->ParsePartialFromArray(out, len);
|
||||
else
|
||||
gProtobufProxy->Deserialize(out, len, msg_);
|
||||
gProtobufProxy->FreeBuffer(out);
|
||||
}
|
||||
|
||||
void CopyFromLocal(google::protobuf::Message* message) {
|
||||
if (locality_ == Local) {
|
||||
msg_->CopyFrom(*message);
|
||||
} else {
|
||||
auto data = message->SerializePartialAsString();
|
||||
gProtobufProxy->Deserialize(data.data(), data.size(), msg_);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void maybe_free() {
|
||||
if (ownership_ != Owned)
|
||||
return;
|
||||
#if defined(PROTOBUF_PROXY_ENABLE)
|
||||
if (locality_ == Private) {
|
||||
gProtobufProxy->FreeMessage(msg_);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
delete msg_;
|
||||
}
|
||||
|
||||
private:
|
||||
google::protobuf::Message* msg_ = nullptr;
|
||||
Ownership ownership_ = Unowned;
|
||||
Locality locality_ = Local;
|
||||
};
|
||||
|
||||
#endif // PROTOBUF_ENABLE
|
||||
@@ -1,100 +0,0 @@
|
||||
/**
|
||||
* vim: set ts=4 sw=4 tw=99 noet :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2020 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$
|
||||
*/
|
||||
#include "pb_proxy.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sm_platform.h>
|
||||
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
# include <cstrike15_usermessage_helpers.h>
|
||||
#else
|
||||
# error "No source engine compatibility"
|
||||
#endif
|
||||
|
||||
class ProtobufProxy : public IProtobufProxy
|
||||
{
|
||||
public:
|
||||
bool Serialize(const google::protobuf::Message* message, void** out, size_t* len);
|
||||
void FreeBuffer(void* data);
|
||||
bool Deserialize(const void* buffer, size_t len, google::protobuf::Message* message);
|
||||
google::protobuf::Message* NewPrototype(int msg_type);
|
||||
void FreeMessage(google::protobuf::Message* message);
|
||||
};
|
||||
|
||||
static ProtobufProxy sProtobufProxy;
|
||||
|
||||
PLATFORM_EXTERN_C IProtobufProxy*
|
||||
GetProtobufProxy()
|
||||
{
|
||||
return &sProtobufProxy;
|
||||
}
|
||||
|
||||
bool
|
||||
ProtobufProxy::Serialize(const google::protobuf::Message* message, void** out, size_t* len)
|
||||
{
|
||||
*len = message->ByteSize();
|
||||
*out = malloc(*len);
|
||||
if (!*out)
|
||||
return false;
|
||||
if (!message->SerializePartialToArray(*out, *len)) {
|
||||
free(*out);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ProtobufProxy::FreeBuffer(void* data)
|
||||
{
|
||||
free(data);
|
||||
}
|
||||
|
||||
bool
|
||||
ProtobufProxy::Deserialize(const void* buffer, size_t len, google::protobuf::Message* message)
|
||||
{
|
||||
return message->ParsePartialFromArray(buffer, len);
|
||||
}
|
||||
|
||||
google::protobuf::Message*
|
||||
ProtobufProxy::NewPrototype(int msg_type)
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
return g_Cstrike15UsermessageHelpers.GetPrototype(msg_type)->New();
|
||||
#else
|
||||
# error "No source engine compatibility."
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
ProtobufProxy::FreeMessage(google::protobuf::Message* message)
|
||||
{
|
||||
delete message;
|
||||
}
|
||||
+1
-2
@@ -38,7 +38,6 @@ public:
|
||||
|
||||
// Local functions.
|
||||
void InitializeBridge();
|
||||
bool LoadProtobufProxy(char *error, size_t maxlength);
|
||||
bool LoadBridge(char *error, size_t maxlength);
|
||||
void ShutdownBridge();
|
||||
|
||||
@@ -67,6 +66,7 @@ public:
|
||||
int QueryClientConVar(int client, const char *cvar) override;
|
||||
bool IsClientConVarQueryingSupported() override;
|
||||
void DefineCommand(const char *cmd, const char *help, const SourceMod::CommandFunc &callback) override;
|
||||
void FormatSourceBinaryName(const char *basename, char *buffer, size_t maxlength) override;
|
||||
|
||||
ke::RefPtr<CommandHook> AddCommandHook(ConCommand *cmd, const CommandHook::Callback &callback);
|
||||
ke::RefPtr<CommandHook> AddPostCommandHook(ConCommand *cmd, const CommandHook::Callback &callback);
|
||||
@@ -76,7 +76,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
ke::RefPtr<ke::SharedLib> pbproxy_;
|
||||
ke::RefPtr<ke::SharedLib> logic_;
|
||||
LogicInitFunction logic_init_;
|
||||
GameHooks hooks_;
|
||||
|
||||
@@ -827,6 +827,16 @@ static cell_t sm_RegAdminCmd(IPluginContext *pContext, const cell_t *params)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t sm_IsCommandCallback(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
const ICommandArgs *pCmd = g_HL2.PeekCommandStack();
|
||||
|
||||
if (!pCmd)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t sm_GetCmdArgs(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
const ICommandArgs *pCmd = g_HL2.PeekCommandStack();
|
||||
@@ -1505,6 +1515,7 @@ REGISTER_NATIVES(consoleNatives)
|
||||
{"GetConVarDefault", GetConVarDefault},
|
||||
{"RegServerCmd", sm_RegServerCmd},
|
||||
{"RegConsoleCmd", sm_RegConsoleCmd},
|
||||
{"IsCommandCallback", sm_IsCommandCallback},
|
||||
{"GetCmdArgString", sm_GetCmdArgString},
|
||||
{"GetCmdArgs", sm_GetCmdArgs},
|
||||
{"GetCmdArg", sm_GetCmdArg},
|
||||
|
||||
@@ -818,6 +818,12 @@ static cell_t FindSendPropOffs(IPluginContext *pContext, const cell_t *params)
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Before we added support for DPT_Array props, FindInSendTable would have given us the inner array prop itself.
|
||||
// To maintain compatibility with older plugins still using us, pluck out the inner array prop ourselves.
|
||||
if (pSend->GetType() == DPT_Array && pSend->GetArrayProp()) {
|
||||
return pSend->GetArrayProp()->GetOffset();
|
||||
}
|
||||
|
||||
return pSend->GetOffset();
|
||||
}
|
||||
|
||||
@@ -825,7 +831,7 @@ static cell_t FindSendPropInfo(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *cls, *prop;
|
||||
sm_sendprop_info_t info;
|
||||
cell_t *pType, *pBits, *pLocal, *pArraySize;
|
||||
cell_t *pType, *pBits, *pLocal, *pArraySize = nullptr;
|
||||
|
||||
pContext->LocalToString(params[1], &cls);
|
||||
pContext->LocalToString(params[2], &prop);
|
||||
|
||||
@@ -320,6 +320,10 @@ void UTIL_SendHudText(int client, const hud_text_parms &textparms, const char *p
|
||||
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
CCSUsrMsg_HudMsg *msg = (CCSUsrMsg_HudMsg *)g_UserMsgs.StartProtobufMessage(g_HudMsgNum, players, 1, 0);
|
||||
if (!msg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
msg->set_channel(textparms.channel & 0xFF);
|
||||
|
||||
CMsgVector2D *pos = msg->mutable_pos();
|
||||
@@ -373,7 +377,11 @@ static cell_t ShowSyncHudText(IPluginContext *pContext, const cell_t *params)
|
||||
Handle_t err;
|
||||
CPlayer *pPlayer;
|
||||
hud_syncobj_t *obj;
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
char message_buffer[512];
|
||||
#else
|
||||
char message_buffer[255-36];
|
||||
#endif
|
||||
|
||||
if (!s_HudMsgHelpers.IsSupported())
|
||||
{
|
||||
@@ -453,7 +461,11 @@ static cell_t ShowHudText(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
int client;
|
||||
CPlayer *pPlayer;
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
|
||||
char message_buffer[512];
|
||||
#else
|
||||
char message_buffer[255-36];
|
||||
#endif
|
||||
|
||||
if (!s_HudMsgHelpers.IsSupported())
|
||||
{
|
||||
|
||||
@@ -356,6 +356,27 @@ static cell_t GetAvgPackets(IPluginContext *pContext, const cell_t *params)
|
||||
return sp_ftoc(value);
|
||||
}
|
||||
|
||||
static cell_t sm_GetClientIClient(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
int client = params[1];
|
||||
|
||||
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
|
||||
if (!pPlayer)
|
||||
{
|
||||
return pContext->ThrowNativeError("Client index %d is invalid", client);
|
||||
}
|
||||
else if (!pPlayer->IsConnected())
|
||||
{
|
||||
return pContext->ThrowNativeError("Client %d is not connected", client);
|
||||
}
|
||||
else if (pPlayer->IsFakeClient())
|
||||
{
|
||||
return pContext->ThrowNativeError("Client %d is a bot", client);
|
||||
}
|
||||
|
||||
return (cell_t)pPlayer->GetIClient();
|
||||
}
|
||||
|
||||
static cell_t RunAdminCacheChecks(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
int client = params[1];
|
||||
@@ -392,6 +413,7 @@ REGISTER_NATIVES(playernatives)
|
||||
{"GetClientAvgChoke", GetAvgChoke},
|
||||
{"GetClientAvgData", GetAvgData},
|
||||
{"GetClientAvgPackets", GetAvgPackets},
|
||||
{"GetClientIClient", sm_GetClientIClient },
|
||||
{"RunAdminCacheChecks", RunAdminCacheChecks},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
+30
-3
@@ -46,6 +46,7 @@
|
||||
#include <amtl/os/am-path.h>
|
||||
#include <bridge/include/IExtensionBridge.h>
|
||||
#include <bridge/include/IScriptManager.h>
|
||||
#include <bridge/include/IProviderCallbacks.h>
|
||||
#include <bridge/include/ILogger.h>
|
||||
|
||||
SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, false, bool, const char *, const char *, const char *, const char *, bool, bool);
|
||||
@@ -53,6 +54,7 @@ SH_DECL_HOOK0_void(IServerGameDLL, LevelShutdown, SH_NOATTRIB, false);
|
||||
SH_DECL_HOOK1_void(IServerGameDLL, GameFrame, SH_NOATTRIB, false, bool);
|
||||
SH_DECL_HOOK1_void(IServerGameDLL, Think, SH_NOATTRIB, false, bool);
|
||||
SH_DECL_HOOK1_void(IVEngineServer, ServerCommand, SH_NOATTRIB, false, const char *);
|
||||
SH_DECL_HOOK0(IVEngineServer, GetMapEntitiesString, SH_NOATTRIB, 0, const char *);
|
||||
|
||||
SourceModBase g_SourceMod;
|
||||
|
||||
@@ -277,6 +279,7 @@ bool SourceModBase::InitializeSourceMod(char *error, size_t maxlength, bool late
|
||||
|
||||
/* Hook this now so we can detect startup without calling StartSourceMod() */
|
||||
SH_ADD_HOOK(IServerGameDLL, LevelInit, gamedll, SH_MEMBER(this, &SourceModBase::LevelInit), false);
|
||||
SH_ADD_HOOK(IVEngineServer, GetMapEntitiesString, engine, SH_MEMBER(this, &SourceModBase::GetMapEntitiesString), false);
|
||||
|
||||
/* Only load if we're not late */
|
||||
if (!late)
|
||||
@@ -290,7 +293,6 @@ bool SourceModBase::InitializeSourceMod(char *error, size_t maxlength, bool late
|
||||
void SourceModBase::StartSourceMod(bool late)
|
||||
{
|
||||
SH_ADD_HOOK(IServerGameDLL, LevelShutdown, gamedll, SH_MEMBER(this, &SourceModBase::LevelShutdown), false);
|
||||
SH_ADD_HOOK(IServerGameDLL, Think, gamedll, SH_MEMBER(&g_Timers, &TimerSystem::Think), false);
|
||||
SH_ADD_HOOK(IServerGameDLL, GameFrame, gamedll, SH_MEMBER(&g_Timers, &TimerSystem::GameFrame), false);
|
||||
|
||||
enginePatch = SH_GET_CALLCLASS(engine);
|
||||
@@ -362,6 +364,8 @@ void SourceModBase::StartSourceMod(bool late)
|
||||
{
|
||||
g_pSourcePawn2->InstallWatchdogTimer(atoi(timeout) * 1000);
|
||||
}
|
||||
|
||||
SH_ADD_HOOK(IServerGameDLL, Think, gamedll, SH_MEMBER(logicore.callbacks, &IProviderCallbacks::OnThink), false);
|
||||
}
|
||||
|
||||
static bool g_LevelEndBarrier = false;
|
||||
@@ -414,10 +418,32 @@ bool SourceModBase::LevelInit(char const *pMapName, char const *pMapEntities, ch
|
||||
|
||||
g_LevelEndBarrier = true;
|
||||
|
||||
int parseError;
|
||||
size_t position;
|
||||
bool success = logicore.ParseEntityLumpString(pMapEntities, parseError, position);
|
||||
|
||||
logicore.SetEntityLumpWritable(true);
|
||||
g_pOnMapInit->PushString(pMapName);
|
||||
g_pOnMapInit->Execute();
|
||||
logicore.SetEntityLumpWritable(false);
|
||||
|
||||
RETURN_META_VALUE(MRES_IGNORED, true);
|
||||
if (!success)
|
||||
{
|
||||
logger->LogError("Map entity lump parsing for %s failed with error code %d on position %d", pMapName, parseError, position);
|
||||
RETURN_META_VALUE(MRES_IGNORED, true);
|
||||
}
|
||||
|
||||
RETURN_META_VALUE_NEWPARAMS(MRES_HANDLED, true, &IServerGameDLL::LevelInit, (pMapName, logicore.GetEntityLumpString(), pOldLevel, pLandmarkName, loadGame, background));
|
||||
}
|
||||
|
||||
const char *SourceModBase::GetMapEntitiesString()
|
||||
{
|
||||
const char *pNewMapEntities = logicore.GetEntityLumpString();
|
||||
if (pNewMapEntities != nullptr)
|
||||
{
|
||||
RETURN_META_VALUE(MRES_SUPERCEDE, pNewMapEntities);
|
||||
}
|
||||
RETURN_META_VALUE(MRES_IGNORED, NULL);
|
||||
}
|
||||
|
||||
void SourceModBase::LevelShutdown()
|
||||
@@ -532,6 +558,7 @@ void SourceModBase::CloseSourceMod()
|
||||
return;
|
||||
|
||||
SH_REMOVE_HOOK(IServerGameDLL, LevelInit, gamedll, SH_MEMBER(this, &SourceModBase::LevelInit), false);
|
||||
SH_REMOVE_HOOK(IVEngineServer, GetMapEntitiesString, engine, SH_MEMBER(this, &SourceModBase::GetMapEntitiesString), false);
|
||||
|
||||
if (g_Loaded)
|
||||
{
|
||||
@@ -596,8 +623,8 @@ void SourceModBase::ShutdownServices()
|
||||
}
|
||||
|
||||
SH_REMOVE_HOOK(IServerGameDLL, LevelShutdown, gamedll, SH_MEMBER(this, &SourceModBase::LevelShutdown), false);
|
||||
SH_REMOVE_HOOK(IServerGameDLL, Think, gamedll, SH_MEMBER(&g_Timers, &TimerSystem::Think), false);
|
||||
SH_REMOVE_HOOK(IServerGameDLL, GameFrame, gamedll, SH_MEMBER(&g_Timers, &TimerSystem::GameFrame), false);
|
||||
SH_REMOVE_HOOK(IServerGameDLL, Think, gamedll, SH_MEMBER(logicore.callbacks, &IProviderCallbacks::OnThink), false);
|
||||
}
|
||||
|
||||
void SourceModBase::LogMessage(IExtension *pExt, const char *format, ...)
|
||||
|
||||
@@ -140,6 +140,7 @@ public: // ISourceMod
|
||||
private:
|
||||
void ShutdownServices();
|
||||
private:
|
||||
const char* GetMapEntitiesString();
|
||||
char m_SMBaseDir[PLATFORM_MAX_PATH];
|
||||
char m_SMRelDir[PLATFORM_MAX_PATH];
|
||||
char m_ModDir[32];
|
||||
|
||||
@@ -80,8 +80,15 @@ CallWrapper::CallWrapper(const SourceHook::ProtoInfo *protoInfo) : m_FnFlags(0)
|
||||
CallWrapper::CallWrapper(const SourceHook::ProtoInfo *protoInfo, const PassInfo *retInfo,
|
||||
const PassInfo paramInfo[], unsigned int fnFlags) : CallWrapper(protoInfo)
|
||||
{
|
||||
m_RetParam->fields = retInfo->fields;
|
||||
m_RetParam->numFields = retInfo->numFields;
|
||||
if (retInfo)
|
||||
{
|
||||
m_RetParam->fields = retInfo->fields;
|
||||
m_RetParam->numFields = retInfo->numFields;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_RetParam = nullptr;
|
||||
}
|
||||
|
||||
unsigned int argnum = protoInfo->numOfParams;
|
||||
for (unsigned int i = 0; i < argnum; i++)
|
||||
|
||||
@@ -100,7 +100,7 @@ bool CStrike::SDK_OnLoad(char *error, size_t maxlength, bool late)
|
||||
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);
|
||||
g_pCSWeaponDropForward = forwards->CreateForward("CS_OnCSWeaponDrop", ET_Event, 3, NULL, Param_Cell, Param_Cell, Param_Cell);
|
||||
|
||||
|
||||
m_TerminateRoundDetourEnabled = false;
|
||||
|
||||
@@ -216,12 +216,20 @@ DETOUR_DECL_MEMBER3(DetourTerminateRound, void, int, reason, int, unknown, int,
|
||||
#endif
|
||||
}
|
||||
|
||||
DETOUR_DECL_MEMBER2(DetourCSWeaponDrop, void, CBaseEntity *, weapon, bool, bThrowForward)
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
DETOUR_DECL_MEMBER3(DetourCSWeaponDrop, void, CBaseEntity *, weapon, bool, bThrowForward, bool, bDonated)
|
||||
#else
|
||||
DETOUR_DECL_MEMBER3(DetourCSWeaponDrop, void, CBaseEntity *, weapon, bool, bDropShield, bool, bThrowForward)
|
||||
#endif
|
||||
{
|
||||
if (g_pIgnoreCSWeaponDropDetour)
|
||||
{
|
||||
g_pIgnoreCSWeaponDropDetour = false;
|
||||
DETOUR_MEMBER_CALL(DetourCSWeaponDrop)(weapon, bThrowForward);
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
DETOUR_MEMBER_CALL(DetourCSWeaponDrop)(weapon, bThrowForward, bDonated);
|
||||
#else
|
||||
DETOUR_MEMBER_CALL(DetourCSWeaponDrop)(weapon, bDropShield, bThrowForward);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -231,12 +239,21 @@ DETOUR_DECL_MEMBER2(DetourCSWeaponDrop, void, CBaseEntity *, weapon, bool, bThro
|
||||
cell_t result = Pl_Continue;
|
||||
g_pCSWeaponDropForward->PushCell(client);
|
||||
g_pCSWeaponDropForward->PushCell(weaponIndex);
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
g_pCSWeaponDropForward->PushCell(bDonated);
|
||||
#else
|
||||
g_pCSWeaponDropForward->PushCell(false);
|
||||
#endif
|
||||
g_pCSWeaponDropForward->Execute(&result);
|
||||
|
||||
|
||||
if (result == Pl_Continue)
|
||||
{
|
||||
DETOUR_MEMBER_CALL(DetourCSWeaponDrop)(weapon, bThrowForward);
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
DETOUR_MEMBER_CALL(DetourCSWeaponDrop)(weapon, bThrowForward, bDonated);
|
||||
#else
|
||||
DETOUR_MEMBER_CALL(DetourCSWeaponDrop)(weapon, bDropShield, bThrowForward);
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
@@ -227,14 +227,17 @@ static cell_t CS_DropWeapon(IPluginContext *pContext, const cell_t *params)
|
||||
if (!pWrapper)
|
||||
{
|
||||
REGISTER_NATIVE_ADDR(WEAPONDROP_GAMEDATA_NAME,
|
||||
PassInfo pass[2]; \
|
||||
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); \
|
||||
pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 2))
|
||||
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;
|
||||
@@ -267,7 +270,11 @@ static cell_t CS_DropWeapon(IPluginContext *pContext, const cell_t *params)
|
||||
if (params[4] == 1 && g_pCSWeaponDropDetoured)
|
||||
g_pIgnoreCSWeaponDropDetour = true;
|
||||
|
||||
ArgBuffer<CBaseEntity*, CBaseEntity*, bool> vstk(pEntity, pWeapon, (params[3]) ? true : false);
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
ArgBuffer<CBaseEntity*, CBaseEntity*, bool, bool> vstk(pEntity, pWeapon, !!params[3], false);
|
||||
#else
|
||||
ArgBuffer<CBaseEntity*, CBaseEntity*, bool, bool> vstk(pEntity, pWeapon, false, !!params[3]);
|
||||
#endif
|
||||
|
||||
pWrapper->Execute(vstk, NULL);
|
||||
return 1;
|
||||
|
||||
@@ -39,9 +39,6 @@ bool bPatched = false;
|
||||
|
||||
RulesFix rulesfix;
|
||||
|
||||
ISteamGameServer *(*SteamAPI_SteamGameServer)();
|
||||
void (*SteamAPI_ISteamGameServer_SetKeyValue)(ISteamGameServer *self, const char *pKey, const char *pValue);
|
||||
|
||||
SH_DECL_HOOK1_void(IServerGameDLL, GameServerSteamAPIActivated, SH_NOATTRIB, 0, bool);
|
||||
|
||||
RulesFix::RulesFix() :
|
||||
@@ -80,39 +77,6 @@ bool SetMTUMax(int iValue)
|
||||
|
||||
void RulesFix::OnLoad()
|
||||
{
|
||||
ILibrary *pLibrary = libsys->OpenLibrary(
|
||||
#if defined ( PLATFORM_WINDOWS )
|
||||
"steam_api.dll"
|
||||
#elif defined( PLATFORM_LINUX )
|
||||
"libsteam_api.so"
|
||||
#elif defined( PLATFORM_APPLE )
|
||||
"libsteam_api.dylib"
|
||||
#else
|
||||
#error Unsupported platform
|
||||
#endif
|
||||
, nullptr, 0);
|
||||
if (pLibrary != nullptr)
|
||||
{
|
||||
const char *pSteamGameServerFuncName = "SteamAPI_SteamGameServer_v013";
|
||||
const char *pSetKeyValueFuncName = "SteamAPI_ISteamGameServer_SetKeyValue";
|
||||
|
||||
// When will hl2sdk-csgo be updated the SteamWorks SDK, change this to export.
|
||||
SteamAPI_SteamGameServer = reinterpret_cast<ISteamGameServer *(*)()>(pLibrary->GetSymbolAddress(pSteamGameServerFuncName));
|
||||
SteamAPI_ISteamGameServer_SetKeyValue = reinterpret_cast<void (*)(ISteamGameServer *self, const char *pKey, const char *pValue)>(pLibrary->GetSymbolAddress(pSetKeyValueFuncName));
|
||||
|
||||
if(SteamAPI_SteamGameServer == nullptr)
|
||||
{
|
||||
g_pSM->LogError(myself, "[CStrike] Failed to get %s function", pSteamGameServerFuncName);
|
||||
}
|
||||
|
||||
if(SteamAPI_ISteamGameServer_SetKeyValue == nullptr)
|
||||
{
|
||||
g_pSM->LogError(myself, "[CStrike] Failed to get %s function", pSetKeyValueFuncName);
|
||||
}
|
||||
|
||||
pLibrary->CloseLibrary();
|
||||
}
|
||||
|
||||
host_rules_show = g_pCVar->FindVar("host_rules_show");
|
||||
if (host_rules_show)
|
||||
{
|
||||
@@ -185,20 +149,18 @@ static void OnConVarChanged(IConVar *var, const char *pOldValue, float flOldValu
|
||||
|
||||
void RulesFix::OnNotifyConVarChanged(ConVar *pVar)
|
||||
{
|
||||
if (!bPatched || !SteamAPI_SteamGameServer || !SteamAPI_ISteamGameServer_SetKeyValue)
|
||||
if (!bPatched)
|
||||
return;
|
||||
|
||||
ISteamGameServer *pSteamClientGameServer = SteamAPI_SteamGameServer();
|
||||
|
||||
if (pSteamClientGameServer)
|
||||
if (SteamGameServer())
|
||||
{
|
||||
if (pVar->IsFlagSet(FCVAR_PROTECTED))
|
||||
{
|
||||
SteamAPI_ISteamGameServer_SetKeyValue(pSteamClientGameServer, pVar->GetName(), !pVar->GetString()[0] ? "0" : "1");
|
||||
SteamGameServer()->SetKeyValue(pVar->GetName(), !pVar->GetString()[0] ? "0" : "1");
|
||||
}
|
||||
else
|
||||
{
|
||||
SteamAPI_ISteamGameServer_SetKeyValue(pSteamClientGameServer, pVar->GetName(), pVar->GetString());
|
||||
SteamGameServer()->SetKeyValue(pVar->GetName(), pVar->GetString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,9 +68,7 @@
|
||||
#define SMEXT_ENABLE_GAMEHELPERS
|
||||
#define SMEXT_ENABLE_TIMERSYS
|
||||
//#define SMEXT_ENABLE_THREADER
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
#define SMEXT_ENABLE_LIBSYS
|
||||
#endif
|
||||
//#define SMEXT_ENABLE_LIBSYS
|
||||
#define SMEXT_ENABLE_USERMSGS
|
||||
#define SMEXT_ENABLE_PLUGINSYS
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
|
||||
import os
|
||||
|
||||
for cxx in builder.targets:
|
||||
binary = SM.ExtLibrary(builder, cxx, 'dhooks.ext')
|
||||
|
||||
# Only x86 on Linux and Windows is supported.
|
||||
if binary.compiler.target.platform == 'mac' or binary.compiler.target.arch != 'x86':
|
||||
continue
|
||||
|
||||
binary.compiler.defines += [
|
||||
'META_NO_HL2SDK',
|
||||
'HAVE_STRING_H',
|
||||
]
|
||||
|
||||
if binary.compiler.like('gcc'):
|
||||
binary.compiler.cflags += ['-Wno-invalid-offsetof']
|
||||
|
||||
binary.compiler.cxxincludes += [
|
||||
os.path.join(SM.mms_root, 'core'),
|
||||
os.path.join(SM.mms_root, 'core', 'sourcehook'),
|
||||
os.path.join(builder.sourcePath, 'public', 'jit'),
|
||||
os.path.join(builder.sourcePath, 'public', 'jit', 'x86'),
|
||||
os.path.join(builder.sourcePath, 'sourcepawn', 'include'),
|
||||
os.path.join(builder.sourcePath, 'sourcepawn', 'vm'),
|
||||
os.path.join(builder.sourcePath, 'sourcepawn', 'vm', 'x86'),
|
||||
os.path.join(builder.sourcePath, 'extensions', 'dhooks', 'DynamicHooks'),
|
||||
]
|
||||
|
||||
binary.sources += [
|
||||
'extension.cpp',
|
||||
'listeners.cpp',
|
||||
'natives.cpp',
|
||||
'signatures.cpp',
|
||||
'vhook.cpp',
|
||||
'util.cpp',
|
||||
'dynhooks_sourcepawn.cpp',
|
||||
'../../public/smsdk_ext.cpp',
|
||||
'../../public/asm/asm.c',
|
||||
'../../public/libudis86/decode.c',
|
||||
'../../public/libudis86/itab.c',
|
||||
'../../public/libudis86/syn-att.c',
|
||||
'../../public/libudis86/syn-intel.c',
|
||||
'../../public/libudis86/syn.c',
|
||||
'../../public/libudis86/udis86.c',
|
||||
'../../sourcepawn/vm/x86/assembler-x86.cpp',
|
||||
]
|
||||
|
||||
# DynamicHooks
|
||||
binary.sources += [
|
||||
os.path.join('DynamicHooks', 'hook.cpp'),
|
||||
os.path.join('DynamicHooks', 'manager.cpp'),
|
||||
os.path.join('DynamicHooks', 'registers.cpp'),
|
||||
os.path.join('DynamicHooks', 'conventions', 'x86MsCdecl.cpp'),
|
||||
os.path.join('DynamicHooks', 'conventions', 'x86MsStdcall.cpp'),
|
||||
os.path.join('DynamicHooks', 'conventions', 'x86MsFastcall.cpp'),
|
||||
]
|
||||
|
||||
if binary.compiler.target.platform == 'windows':
|
||||
binary.sources += [os.path.join('DynamicHooks', 'conventions', 'x86MsThiscall.cpp')]
|
||||
else:
|
||||
binary.sources += [os.path.join('DynamicHooks', 'conventions', 'x86GccThiscall.cpp')]
|
||||
|
||||
SM.extensions += [builder.Add(binary)]
|
||||
@@ -0,0 +1,290 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
#ifndef _CONVENTION_H
|
||||
#define _CONVENTION_H
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "registers.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
// ============================================================================
|
||||
// >> DataType_t
|
||||
// ============================================================================
|
||||
enum DataType_t
|
||||
{
|
||||
DATA_TYPE_VOID,
|
||||
DATA_TYPE_BOOL,
|
||||
DATA_TYPE_CHAR,
|
||||
DATA_TYPE_UCHAR,
|
||||
DATA_TYPE_SHORT,
|
||||
DATA_TYPE_USHORT,
|
||||
DATA_TYPE_INT,
|
||||
DATA_TYPE_UINT,
|
||||
DATA_TYPE_LONG,
|
||||
DATA_TYPE_ULONG,
|
||||
DATA_TYPE_LONG_LONG,
|
||||
DATA_TYPE_ULONG_LONG,
|
||||
DATA_TYPE_FLOAT,
|
||||
DATA_TYPE_DOUBLE,
|
||||
DATA_TYPE_POINTER,
|
||||
DATA_TYPE_STRING,
|
||||
DATA_TYPE_OBJECT
|
||||
};
|
||||
|
||||
typedef struct DataTypeSized_s {
|
||||
DataTypeSized_s()
|
||||
{
|
||||
type = DATA_TYPE_POINTER;
|
||||
size = 0;
|
||||
custom_register = None;
|
||||
}
|
||||
DataType_t type;
|
||||
size_t size;
|
||||
Register_t custom_register;
|
||||
} DataTypeSized_t;
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> FUNCTIONS
|
||||
// ============================================================================
|
||||
/*
|
||||
Returns the size after applying alignment.
|
||||
|
||||
@param <size>:
|
||||
The size that should be aligned.
|
||||
|
||||
@param <alignment>:
|
||||
The alignment that should be used.
|
||||
*/
|
||||
inline int Align(int size, int alignment)
|
||||
{
|
||||
int unaligned = size % alignment;
|
||||
if (unaligned == 0)
|
||||
return size;
|
||||
|
||||
return size + (alignment - unaligned);
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the size of a data type after applying alignment.
|
||||
|
||||
@param <type>:
|
||||
The data type you would like to get the size of.
|
||||
|
||||
@param <alignment>:
|
||||
The alignment that should be used.
|
||||
*/
|
||||
inline int GetDataTypeSize(DataTypeSized_t type, int iAlignment=4)
|
||||
{
|
||||
switch(type.type)
|
||||
{
|
||||
case DATA_TYPE_VOID: return 0;
|
||||
case DATA_TYPE_BOOL: return Align(sizeof(bool), iAlignment);
|
||||
case DATA_TYPE_CHAR: return Align(sizeof(char), iAlignment);
|
||||
case DATA_TYPE_UCHAR: return Align(sizeof(unsigned char), iAlignment);
|
||||
case DATA_TYPE_SHORT: return Align(sizeof(short), iAlignment);
|
||||
case DATA_TYPE_USHORT: return Align(sizeof(unsigned short), iAlignment);
|
||||
case DATA_TYPE_INT: return Align(sizeof(int), iAlignment);
|
||||
case DATA_TYPE_UINT: return Align(sizeof(unsigned int), iAlignment);
|
||||
case DATA_TYPE_LONG: return Align(sizeof(long), iAlignment);
|
||||
case DATA_TYPE_ULONG: return Align(sizeof(unsigned long), iAlignment);
|
||||
case DATA_TYPE_LONG_LONG: return Align(sizeof(long long), iAlignment);
|
||||
case DATA_TYPE_ULONG_LONG: return Align(sizeof(unsigned long long), iAlignment);
|
||||
case DATA_TYPE_FLOAT: return Align(sizeof(float), iAlignment);
|
||||
case DATA_TYPE_DOUBLE: return Align(sizeof(double), iAlignment);
|
||||
case DATA_TYPE_POINTER: return Align(sizeof(void *), iAlignment);
|
||||
case DATA_TYPE_STRING: return Align(sizeof(char *), iAlignment);
|
||||
case DATA_TYPE_OBJECT: return type.size;
|
||||
default: puts("Unknown data type.");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// >> CLASSES
|
||||
// ============================================================================
|
||||
/*
|
||||
This is the base class for every calling convention. Inherit from this class
|
||||
to create your own calling convention.
|
||||
*/
|
||||
class ICallingConvention
|
||||
{
|
||||
public:
|
||||
/*
|
||||
Initializes the calling convention.
|
||||
|
||||
@param <vecArgTypes>:
|
||||
A list of DataType_t objects, which define the arguments of the function.
|
||||
|
||||
@param <returnType>:
|
||||
The return type of the function.
|
||||
*/
|
||||
ICallingConvention(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment=4)
|
||||
{
|
||||
m_vecArgTypes = std::move(vecArgTypes);
|
||||
|
||||
for (size_t i=0; i < m_vecArgTypes.size(); i++)
|
||||
{
|
||||
DataTypeSized_t &type = m_vecArgTypes[i];
|
||||
if (!type.size)
|
||||
type.size = GetDataTypeSize(type, iAlignment);
|
||||
}
|
||||
m_returnType = returnType;
|
||||
if (!m_returnType.size)
|
||||
m_returnType.size = GetDataTypeSize(m_returnType, iAlignment);
|
||||
m_iAlignment = iAlignment;
|
||||
}
|
||||
|
||||
virtual ~ICallingConvention()
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
This should return a list of Register_t values. These registers will be
|
||||
saved for later access.
|
||||
*/
|
||||
virtual std::vector<Register_t> GetRegisters() = 0;
|
||||
|
||||
/*
|
||||
Returns the number of bytes that should be added to the stack to clean up.
|
||||
*/
|
||||
virtual int GetPopSize() = 0;
|
||||
|
||||
virtual int GetArgStackSize() = 0;
|
||||
virtual void** GetStackArgumentPtr(CRegisters* pRegisters) = 0;
|
||||
|
||||
/*
|
||||
Returns the number of bytes for the buffer to store all the arguments that are passed in a register in.
|
||||
*/
|
||||
virtual int GetArgRegisterSize() = 0;
|
||||
|
||||
/*
|
||||
Returns a pointer to the argument at the given index.
|
||||
|
||||
@param <iIndex>:
|
||||
The index of the argument.
|
||||
|
||||
@param <pRegisters>:
|
||||
A snapshot of all saved registers.
|
||||
*/
|
||||
virtual void* GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters) = 0;
|
||||
|
||||
/*
|
||||
*/
|
||||
virtual void ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr) = 0;
|
||||
|
||||
/*
|
||||
Returns a pointer to the return value.
|
||||
|
||||
@param <pRegisters>:
|
||||
A snapshot of all saved registers.
|
||||
*/
|
||||
virtual void* GetReturnPtr(CRegisters* pRegisters) = 0;
|
||||
|
||||
/*
|
||||
*/
|
||||
virtual void ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr) = 0;
|
||||
|
||||
/*
|
||||
Save the return value in a seperate buffer, so we can restore it after calling the original function.
|
||||
|
||||
@param <pRegisters>:
|
||||
A snapshot of all saved registers.
|
||||
*/
|
||||
virtual void SaveReturnValue(CRegisters* pRegisters)
|
||||
{
|
||||
std::unique_ptr<uint8_t[]> pSavedReturnValue = std::make_unique<uint8_t[]>(m_returnType.size);
|
||||
memcpy(pSavedReturnValue.get(), GetReturnPtr(pRegisters), m_returnType.size);
|
||||
m_pSavedReturnBuffers.push_back(std::move(pSavedReturnValue));
|
||||
}
|
||||
|
||||
virtual void RestoreReturnValue(CRegisters* pRegisters)
|
||||
{
|
||||
uint8_t* pSavedReturnValue = m_pSavedReturnBuffers.back().get();
|
||||
memcpy(GetReturnPtr(pRegisters), pSavedReturnValue, m_returnType.size);
|
||||
ReturnPtrChanged(pRegisters, pSavedReturnValue);
|
||||
m_pSavedReturnBuffers.pop_back();
|
||||
}
|
||||
|
||||
/*
|
||||
Save the value of arguments in a seperate buffer for the post callback.
|
||||
Compiler optimizations might cause the registers or stack space to be reused
|
||||
and overwritten during function execution if the value isn't needed anymore
|
||||
at some point. This leads to different values in the post hook.
|
||||
|
||||
@param <pRegisters>:
|
||||
A snapshot of all saved registers.
|
||||
*/
|
||||
virtual void SaveCallArguments(CRegisters* pRegisters)
|
||||
{
|
||||
int size = GetArgStackSize() + GetArgRegisterSize();
|
||||
std::unique_ptr<uint8_t[]> pSavedCallArguments = std::make_unique<uint8_t[]>(size);
|
||||
size_t offset = 0;
|
||||
for (size_t i = 0; i < m_vecArgTypes.size(); i++) {
|
||||
DataTypeSized_t &type = m_vecArgTypes[i];
|
||||
memcpy((void *)((unsigned long)pSavedCallArguments.get() + offset), GetArgumentPtr(i, pRegisters), type.size);
|
||||
offset += type.size;
|
||||
}
|
||||
m_pSavedCallArguments.push_back(std::move(pSavedCallArguments));
|
||||
}
|
||||
|
||||
virtual void RestoreCallArguments(CRegisters* pRegisters)
|
||||
{
|
||||
uint8_t *pSavedCallArguments = m_pSavedCallArguments.back().get();
|
||||
size_t offset = 0;
|
||||
for (size_t i = 0; i < m_vecArgTypes.size(); i++) {
|
||||
DataTypeSized_t &type = m_vecArgTypes[i];
|
||||
memcpy(GetArgumentPtr(i, pRegisters), (void *)((unsigned long)pSavedCallArguments + offset), type.size);
|
||||
offset += type.size;
|
||||
}
|
||||
m_pSavedCallArguments.pop_back();
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<DataTypeSized_t> m_vecArgTypes;
|
||||
DataTypeSized_t m_returnType;
|
||||
int m_iAlignment;
|
||||
// Save the return in case we call the original function and want to override the return again.
|
||||
std::vector<std::unique_ptr<uint8_t[]>> m_pSavedReturnBuffers;
|
||||
// Save call arguments in case the function reuses the space and overwrites the values for the post hook.
|
||||
std::vector<std::unique_ptr<uint8_t[]>> m_pSavedCallArguments;
|
||||
};
|
||||
|
||||
#endif // _CONVENTION_H
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
#ifndef _X86_GCC_CDECL_H
|
||||
#define _X86_GCC_CDECL_H
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "x86MsCdecl.h"
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> CLASSES
|
||||
// ============================================================================
|
||||
typedef x86MsCdecl x86GccCdecl;
|
||||
|
||||
|
||||
#endif // _X86_GCC_CDECL_H
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "x86GccThiscall.h"
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> CLASSES
|
||||
// ============================================================================
|
||||
|
||||
x86GccThiscall::x86GccThiscall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment) :
|
||||
x86GccCdecl(vecArgTypes, returnType, iAlignment)
|
||||
{
|
||||
// Always add the |this| pointer.
|
||||
DataTypeSized_t type;
|
||||
type.type = DATA_TYPE_POINTER;
|
||||
type.size = GetDataTypeSize(type, iAlignment);
|
||||
type.custom_register = None;
|
||||
m_vecArgTypes.insert(m_vecArgTypes.begin(), type);
|
||||
}
|
||||
|
||||
x86GccThiscall::~x86GccThiscall()
|
||||
{
|
||||
}
|
||||
|
||||
int x86GccThiscall::GetArgStackSize()
|
||||
{
|
||||
// Remove the this pointer from the arguments size.
|
||||
DataTypeSized_t type;
|
||||
type.type = DATA_TYPE_POINTER;
|
||||
return x86GccCdecl::GetArgStackSize() - GetDataTypeSize(type, m_iAlignment);
|
||||
}
|
||||
|
||||
void** x86GccThiscall::GetStackArgumentPtr(CRegisters* pRegisters)
|
||||
{
|
||||
// Skip return address and this pointer.
|
||||
DataTypeSized_t type;
|
||||
type.type = DATA_TYPE_POINTER;
|
||||
return (void **)(pRegisters->m_esp->GetValue<unsigned long>() + 4 + GetDataTypeSize(type, m_iAlignment));
|
||||
}
|
||||
|
||||
void x86GccThiscall::SaveCallArguments(CRegisters* pRegisters)
|
||||
{
|
||||
// Count the this pointer.
|
||||
int size = x86GccCdecl::GetArgStackSize() + GetArgRegisterSize();
|
||||
std::unique_ptr<uint8_t[]> pSavedCallArguments = std::make_unique<uint8_t[]>(size);
|
||||
size_t offset = 0;
|
||||
for (size_t i = 0; i < m_vecArgTypes.size(); i++) {
|
||||
DataTypeSized_t &type = m_vecArgTypes[i];
|
||||
memcpy((void *)((unsigned long)pSavedCallArguments.get() + offset), GetArgumentPtr(i, pRegisters), type.size);
|
||||
offset += type.size;
|
||||
}
|
||||
m_pSavedCallArguments.push_back(std::move(pSavedCallArguments));
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
#ifndef _X86_GCC_THISCALL_H
|
||||
#define _X86_GCC_THISCALL_H
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "x86GccCdecl.h"
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> CLASSES
|
||||
// ============================================================================
|
||||
// |this| pointer is always passed as implicit first argument on the stack.
|
||||
class x86GccThiscall: public x86GccCdecl
|
||||
{
|
||||
public:
|
||||
x86GccThiscall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment = 4);
|
||||
virtual ~x86GccThiscall();
|
||||
|
||||
virtual int GetArgStackSize();
|
||||
virtual void** GetStackArgumentPtr(CRegisters* pRegisters);
|
||||
|
||||
virtual void SaveCallArguments(CRegisters* pRegisters);
|
||||
};
|
||||
|
||||
#endif // _X86_GCC_THISCALL_H
|
||||
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "x86MsCdecl.h"
|
||||
#include <string.h>
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> x86MsCdecl
|
||||
// ============================================================================
|
||||
x86MsCdecl::x86MsCdecl(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment) :
|
||||
ICallingConvention(vecArgTypes, returnType, iAlignment)
|
||||
{
|
||||
if (m_returnType.size > 4)
|
||||
{
|
||||
m_pReturnBuffer = malloc(m_returnType.size);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pReturnBuffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
x86MsCdecl::~x86MsCdecl()
|
||||
{
|
||||
if (m_pReturnBuffer)
|
||||
{
|
||||
free(m_pReturnBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Register_t> x86MsCdecl::GetRegisters()
|
||||
{
|
||||
std::vector<Register_t> registers;
|
||||
|
||||
registers.push_back(ESP);
|
||||
|
||||
if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == DATA_TYPE_DOUBLE)
|
||||
{
|
||||
registers.push_back(ST0);
|
||||
}
|
||||
else
|
||||
{
|
||||
registers.push_back(EAX);
|
||||
if (m_pReturnBuffer)
|
||||
{
|
||||
registers.push_back(EDX);
|
||||
}
|
||||
}
|
||||
|
||||
// Save all the custom calling convention registers as well.
|
||||
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
|
||||
{
|
||||
if (m_vecArgTypes[i].custom_register == None)
|
||||
continue;
|
||||
|
||||
// TODO: Make sure the list is unique? Set?
|
||||
registers.push_back(m_vecArgTypes[i].custom_register);
|
||||
}
|
||||
|
||||
return registers;
|
||||
}
|
||||
|
||||
int x86MsCdecl::GetPopSize()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int x86MsCdecl::GetArgStackSize()
|
||||
{
|
||||
int iArgStackSize = 0;
|
||||
|
||||
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
|
||||
{
|
||||
if (m_vecArgTypes[i].custom_register == None)
|
||||
iArgStackSize += m_vecArgTypes[i].size;
|
||||
}
|
||||
|
||||
return iArgStackSize;
|
||||
}
|
||||
|
||||
void** x86MsCdecl::GetStackArgumentPtr(CRegisters* pRegisters)
|
||||
{
|
||||
return (void **)(pRegisters->m_esp->GetValue<unsigned long>() + 4);
|
||||
}
|
||||
|
||||
int x86MsCdecl::GetArgRegisterSize()
|
||||
{
|
||||
int iArgRegisterSize = 0;
|
||||
|
||||
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
|
||||
{
|
||||
if (m_vecArgTypes[i].custom_register != None)
|
||||
iArgRegisterSize += m_vecArgTypes[i].size;
|
||||
}
|
||||
|
||||
return iArgRegisterSize;
|
||||
}
|
||||
|
||||
void* x86MsCdecl::GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters)
|
||||
{
|
||||
if (iIndex >= m_vecArgTypes.size())
|
||||
return NULL;
|
||||
|
||||
// Check if this argument was passed in a register.
|
||||
if (m_vecArgTypes[iIndex].custom_register != None)
|
||||
{
|
||||
CRegister *pRegister = pRegisters->GetRegister(m_vecArgTypes[iIndex].custom_register);
|
||||
if (!pRegister)
|
||||
return NULL;
|
||||
|
||||
return pRegister->m_pAddress;
|
||||
}
|
||||
|
||||
// Skip return address.
|
||||
size_t iOffset = 4;
|
||||
for(unsigned int i=0; i < iIndex; i++)
|
||||
{
|
||||
if (m_vecArgTypes[i].custom_register == None)
|
||||
iOffset += m_vecArgTypes[i].size;
|
||||
}
|
||||
|
||||
return (void *) (pRegisters->m_esp->GetValue<unsigned long>() + iOffset);
|
||||
}
|
||||
|
||||
void x86MsCdecl::ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr)
|
||||
{
|
||||
}
|
||||
|
||||
void* x86MsCdecl::GetReturnPtr(CRegisters* pRegisters)
|
||||
{
|
||||
if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == DATA_TYPE_DOUBLE)
|
||||
return pRegisters->m_st0->m_pAddress;
|
||||
|
||||
if (m_pReturnBuffer)
|
||||
{
|
||||
// First half in eax, second half in edx
|
||||
memcpy(m_pReturnBuffer, pRegisters->m_eax, 4);
|
||||
memcpy((void *) ((unsigned long) m_pReturnBuffer + 4), pRegisters->m_edx, 4);
|
||||
return m_pReturnBuffer;
|
||||
}
|
||||
|
||||
return pRegisters->m_eax->m_pAddress;
|
||||
}
|
||||
|
||||
void x86MsCdecl::ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr)
|
||||
{
|
||||
if (m_pReturnBuffer)
|
||||
{
|
||||
// First half in eax, second half in edx
|
||||
memcpy(pRegisters->m_eax, m_pReturnBuffer, 4);
|
||||
memcpy(pRegisters->m_edx, (void *) ((unsigned long) m_pReturnBuffer + 4), 4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
#ifndef _X86_MS_CDECL_H
|
||||
#define _X86_MS_CDECL_H
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "../convention.h"
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> CLASSES
|
||||
// ============================================================================
|
||||
/*
|
||||
Source: DynCall manual and Windows docs
|
||||
|
||||
Registers:
|
||||
- eax = return value
|
||||
- edx = return value
|
||||
- esp = stack pointer
|
||||
- st0 = floating point return value
|
||||
|
||||
Parameter passing:
|
||||
- stack parameter order: right-to-left
|
||||
- caller cleans up the stack
|
||||
- all arguments are pushed onto the stack
|
||||
- alignment: 4 bytes
|
||||
|
||||
Return values:
|
||||
- return values of pointer or intergral type (<= 32 bits) are returned via the eax register
|
||||
- integers > 32 bits are returned via the eax and edx registers
|
||||
- floating pointer types are returned via the st0 register
|
||||
*/
|
||||
class x86MsCdecl: public ICallingConvention
|
||||
{
|
||||
public:
|
||||
x86MsCdecl(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment=4);
|
||||
virtual ~x86MsCdecl();
|
||||
|
||||
virtual std::vector<Register_t> GetRegisters();
|
||||
virtual int GetPopSize();
|
||||
virtual int GetArgStackSize();
|
||||
virtual void** GetStackArgumentPtr(CRegisters* pRegisters);
|
||||
virtual int GetArgRegisterSize();
|
||||
|
||||
virtual void* GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters);
|
||||
virtual void ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr);
|
||||
|
||||
virtual void* GetReturnPtr(CRegisters* pRegisters);
|
||||
virtual void ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr);
|
||||
|
||||
private:
|
||||
void* m_pReturnBuffer;
|
||||
};
|
||||
|
||||
#endif // _X86_MS_CDECL_H
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "x86MsFastcall.h"
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> x86MsFastcall
|
||||
// ============================================================================
|
||||
x86MsFastcall::x86MsFastcall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment) :
|
||||
x86MsStdcall(vecArgTypes, returnType, iAlignment)
|
||||
{
|
||||
// First argument is passed in ecx.
|
||||
if (m_vecArgTypes.size() > 0) {
|
||||
DataTypeSized_t &type = m_vecArgTypes[0];
|
||||
// Don't force the register on the user.
|
||||
// Why choose Fastcall if you set your own argument registers though..
|
||||
if (type.custom_register == None)
|
||||
type.custom_register = ECX;
|
||||
}
|
||||
|
||||
// Second argument is passed in edx.
|
||||
if (m_vecArgTypes.size() > 1) {
|
||||
DataTypeSized_t &type = m_vecArgTypes[1];
|
||||
if (type.custom_register == None)
|
||||
type.custom_register = EDX;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
#ifndef _X86_MS_FASTCALL_H
|
||||
#define _X86_MS_FASTCALL_H
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "x86MsStdcall.h"
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> CLASSES
|
||||
// ============================================================================
|
||||
/*
|
||||
Source: DynCall manual and Windows docs
|
||||
|
||||
Registers:
|
||||
- eax = return value
|
||||
- edx = return value
|
||||
- esp = stack pointer
|
||||
- st0 = floating point return value
|
||||
|
||||
Parameter passing:
|
||||
- first parameter in ecx, second parameter in edx, rest on the stack
|
||||
- stack parameter order: right-to-left
|
||||
- callee cleans up the stack
|
||||
- alignment: 4 bytes
|
||||
|
||||
Return values:
|
||||
- return values of pointer or intergral type (<= 32 bits) are returned via the eax register
|
||||
- integers > 32 bits are returned via the eax and edx registers
|
||||
- floating pointer types are returned via the st0 register
|
||||
*/
|
||||
class x86MsFastcall: public x86MsStdcall
|
||||
{
|
||||
public:
|
||||
x86MsFastcall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment=4);
|
||||
};
|
||||
|
||||
#endif // _X86_MS_FASTCALL_H
|
||||
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "x86MsStdcall.h"
|
||||
#include <string.h>
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> x86MsStdcall
|
||||
// ============================================================================
|
||||
x86MsStdcall::x86MsStdcall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment) :
|
||||
ICallingConvention(vecArgTypes, returnType, iAlignment)
|
||||
{
|
||||
if (m_returnType.size > 4)
|
||||
{
|
||||
m_pReturnBuffer = malloc(m_returnType.size);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pReturnBuffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
x86MsStdcall::~x86MsStdcall()
|
||||
{
|
||||
if (m_pReturnBuffer)
|
||||
{
|
||||
free(m_pReturnBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Register_t> x86MsStdcall::GetRegisters()
|
||||
{
|
||||
std::vector<Register_t> registers;
|
||||
|
||||
registers.push_back(ESP);
|
||||
|
||||
if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == DATA_TYPE_DOUBLE)
|
||||
{
|
||||
registers.push_back(ST0);
|
||||
}
|
||||
else
|
||||
{
|
||||
registers.push_back(EAX);
|
||||
if (m_pReturnBuffer)
|
||||
{
|
||||
registers.push_back(EDX);
|
||||
}
|
||||
}
|
||||
|
||||
// Save all the custom calling convention registers as well.
|
||||
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
|
||||
{
|
||||
if (m_vecArgTypes[i].custom_register == None)
|
||||
continue;
|
||||
|
||||
// TODO: Make sure the list is unique? Set?
|
||||
registers.push_back(m_vecArgTypes[i].custom_register);
|
||||
}
|
||||
|
||||
return registers;
|
||||
}
|
||||
|
||||
int x86MsStdcall::GetPopSize()
|
||||
{
|
||||
int iPopSize = 0;
|
||||
|
||||
for(size_t i=0; i < m_vecArgTypes.size(); i++)
|
||||
{
|
||||
if (m_vecArgTypes[i].custom_register == None)
|
||||
iPopSize += m_vecArgTypes[i].size;
|
||||
}
|
||||
|
||||
return iPopSize;
|
||||
}
|
||||
|
||||
int x86MsStdcall::GetArgStackSize()
|
||||
{
|
||||
int iArgStackSize = 0;
|
||||
|
||||
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
|
||||
{
|
||||
if (m_vecArgTypes[i].custom_register == None)
|
||||
iArgStackSize += m_vecArgTypes[i].size;
|
||||
}
|
||||
|
||||
return iArgStackSize;
|
||||
}
|
||||
|
||||
void** x86MsStdcall::GetStackArgumentPtr(CRegisters* pRegisters)
|
||||
{
|
||||
return (void **)(pRegisters->m_esp->GetValue<unsigned long>() + 4);
|
||||
}
|
||||
|
||||
int x86MsStdcall::GetArgRegisterSize()
|
||||
{
|
||||
int iArgRegisterSize = 0;
|
||||
|
||||
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
|
||||
{
|
||||
if (m_vecArgTypes[i].custom_register != None)
|
||||
iArgRegisterSize += m_vecArgTypes[i].size;
|
||||
}
|
||||
|
||||
return iArgRegisterSize;
|
||||
}
|
||||
|
||||
void* x86MsStdcall::GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters)
|
||||
{
|
||||
if (iIndex >= m_vecArgTypes.size())
|
||||
return NULL;
|
||||
|
||||
// Check if this argument was passed in a register.
|
||||
if (m_vecArgTypes[iIndex].custom_register != None)
|
||||
{
|
||||
CRegister *pRegister = pRegisters->GetRegister(m_vecArgTypes[iIndex].custom_register);
|
||||
if (!pRegister)
|
||||
return NULL;
|
||||
|
||||
return pRegister->m_pAddress;
|
||||
}
|
||||
|
||||
size_t iOffset = 4;
|
||||
for (unsigned int i = 0; i < iIndex; i++)
|
||||
{
|
||||
if (m_vecArgTypes[i].custom_register == None)
|
||||
iOffset += m_vecArgTypes[i].size;
|
||||
}
|
||||
|
||||
return (void *)(pRegisters->m_esp->GetValue<unsigned long>() + iOffset);
|
||||
}
|
||||
|
||||
void x86MsStdcall::ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr)
|
||||
{
|
||||
}
|
||||
|
||||
void* x86MsStdcall::GetReturnPtr(CRegisters* pRegisters)
|
||||
{
|
||||
if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == DATA_TYPE_DOUBLE)
|
||||
return pRegisters->m_st0->m_pAddress;
|
||||
|
||||
if (m_pReturnBuffer)
|
||||
{
|
||||
// First half in eax, second half in edx
|
||||
memcpy(m_pReturnBuffer, pRegisters->m_eax, 4);
|
||||
memcpy((void *) ((unsigned long) m_pReturnBuffer + 4), pRegisters->m_edx, 4);
|
||||
return m_pReturnBuffer;
|
||||
}
|
||||
|
||||
return pRegisters->m_eax->m_pAddress;
|
||||
}
|
||||
|
||||
void x86MsStdcall::ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr)
|
||||
{
|
||||
if (m_pReturnBuffer)
|
||||
{
|
||||
// First half in eax, second half in edx
|
||||
memcpy(pRegisters->m_eax, m_pReturnBuffer, 4);
|
||||
memcpy(pRegisters->m_edx, (void *) ((unsigned long) m_pReturnBuffer + 4), 4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
#ifndef _X86_MS_STDCALL_H
|
||||
#define _X86_MS_STDCALL_H
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "../convention.h"
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> CLASSES
|
||||
// ============================================================================
|
||||
/*
|
||||
Source: DynCall manual and Windows docs
|
||||
|
||||
Registers:
|
||||
- eax = return value
|
||||
- edx = return value
|
||||
- esp = stack pointer
|
||||
- st0 = floating point return value
|
||||
|
||||
Parameter passing:
|
||||
- stack parameter order: right-to-left
|
||||
- callee cleans up the stack
|
||||
- all arguments are pushed onto the stack
|
||||
- alignment: 4 bytes
|
||||
|
||||
Return values:
|
||||
- return values of pointer or intergral type (<= 32 bits) are returned via the eax register
|
||||
- integers > 32 bits are returned via the eax and edx registers
|
||||
- floating pointer types are returned via the st0 register
|
||||
*/
|
||||
class x86MsStdcall: public ICallingConvention
|
||||
{
|
||||
public:
|
||||
x86MsStdcall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment=4);
|
||||
virtual ~x86MsStdcall();
|
||||
|
||||
virtual std::vector<Register_t> GetRegisters();
|
||||
virtual int GetPopSize();
|
||||
virtual int GetArgStackSize();
|
||||
virtual void** GetStackArgumentPtr(CRegisters* pRegisters);
|
||||
virtual int GetArgRegisterSize();
|
||||
|
||||
virtual void* GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters);
|
||||
virtual void ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr);
|
||||
|
||||
virtual void* GetReturnPtr(CRegisters* pRegisters);
|
||||
virtual void ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr);
|
||||
|
||||
private:
|
||||
void* m_pReturnBuffer;
|
||||
};
|
||||
|
||||
#endif // _X86_MS_STDCALL_H
|
||||
@@ -0,0 +1,239 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "x86MsThiscall.h"
|
||||
#include <string.h>
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> x86MsThiscall
|
||||
// ============================================================================
|
||||
x86MsThiscall::x86MsThiscall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment) :
|
||||
ICallingConvention(vecArgTypes, returnType, iAlignment)
|
||||
{
|
||||
if (m_returnType.size > 4)
|
||||
{
|
||||
m_pReturnBuffer = malloc(m_returnType.size);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pReturnBuffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
x86MsThiscall::~x86MsThiscall()
|
||||
{
|
||||
if (m_pReturnBuffer)
|
||||
{
|
||||
free(m_pReturnBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Register_t> x86MsThiscall::GetRegisters()
|
||||
{
|
||||
std::vector<Register_t> registers;
|
||||
|
||||
registers.push_back(ESP);
|
||||
// TODO: Allow custom this register.
|
||||
registers.push_back(ECX);
|
||||
|
||||
if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == DATA_TYPE_DOUBLE)
|
||||
{
|
||||
registers.push_back(ST0);
|
||||
}
|
||||
else
|
||||
{
|
||||
registers.push_back(EAX);
|
||||
if (m_pReturnBuffer)
|
||||
{
|
||||
registers.push_back(EDX);
|
||||
}
|
||||
}
|
||||
|
||||
// Save all the custom calling convention registers as well.
|
||||
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
|
||||
{
|
||||
if (m_vecArgTypes[i].custom_register == None)
|
||||
continue;
|
||||
|
||||
// TODO: Make sure the list is unique? Set?
|
||||
registers.push_back(m_vecArgTypes[i].custom_register);
|
||||
}
|
||||
|
||||
return registers;
|
||||
}
|
||||
|
||||
int x86MsThiscall::GetPopSize()
|
||||
{
|
||||
// This pointer.
|
||||
// FIXME LINUX
|
||||
//int iPopSize = GetDataTypeSize(DATA_TYPE_POINTER, m_iAlignment);
|
||||
int iPopSize = 0;
|
||||
|
||||
for(size_t i=0; i < m_vecArgTypes.size(); i++)
|
||||
{
|
||||
// Only pop arguments that are actually on the stack.
|
||||
if (m_vecArgTypes[i].custom_register == None)
|
||||
iPopSize += m_vecArgTypes[i].size;
|
||||
}
|
||||
|
||||
return iPopSize;
|
||||
}
|
||||
|
||||
int x86MsThiscall::GetArgStackSize()
|
||||
{
|
||||
int iArgStackSize = 0;
|
||||
|
||||
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
|
||||
{
|
||||
if (m_vecArgTypes[i].custom_register == None)
|
||||
iArgStackSize += m_vecArgTypes[i].size;
|
||||
}
|
||||
|
||||
return iArgStackSize;
|
||||
}
|
||||
|
||||
void** x86MsThiscall::GetStackArgumentPtr(CRegisters* pRegisters)
|
||||
{
|
||||
return (void **)(pRegisters->m_esp->GetValue<unsigned long>() + 4);
|
||||
}
|
||||
|
||||
int x86MsThiscall::GetArgRegisterSize()
|
||||
{
|
||||
int iArgRegisterSize = 0;
|
||||
|
||||
for (size_t i = 0; i < m_vecArgTypes.size(); i++)
|
||||
{
|
||||
if (m_vecArgTypes[i].custom_register != None)
|
||||
iArgRegisterSize += m_vecArgTypes[i].size;
|
||||
}
|
||||
|
||||
return iArgRegisterSize;
|
||||
}
|
||||
|
||||
void* x86MsThiscall::GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters)
|
||||
{
|
||||
if (iIndex == 0)
|
||||
{
|
||||
// TODO: Allow custom this register.
|
||||
return pRegisters->m_ecx->m_pAddress;
|
||||
}
|
||||
|
||||
// The this pointer isn't explicitly defined as an argument.
|
||||
iIndex--;
|
||||
|
||||
if (iIndex >= m_vecArgTypes.size())
|
||||
return NULL;
|
||||
|
||||
// Check if this argument was passed in a register.
|
||||
if (m_vecArgTypes[iIndex].custom_register != None)
|
||||
{
|
||||
CRegister *pRegister = pRegisters->GetRegister(m_vecArgTypes[iIndex].custom_register);
|
||||
if (!pRegister)
|
||||
return NULL;
|
||||
|
||||
return pRegister->m_pAddress;
|
||||
}
|
||||
|
||||
size_t iOffset = 4;
|
||||
for(unsigned int i=0; i < iIndex; i++)
|
||||
{
|
||||
if (m_vecArgTypes[i].custom_register == None)
|
||||
iOffset += m_vecArgTypes[i].size;
|
||||
}
|
||||
|
||||
return (void *) (pRegisters->m_esp->GetValue<unsigned long>() + iOffset);
|
||||
}
|
||||
|
||||
void x86MsThiscall::ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr)
|
||||
{
|
||||
}
|
||||
|
||||
void* x86MsThiscall::GetReturnPtr(CRegisters* pRegisters)
|
||||
{
|
||||
if (m_returnType.type == DATA_TYPE_FLOAT || m_returnType.type == DATA_TYPE_DOUBLE)
|
||||
return pRegisters->m_st0->m_pAddress;
|
||||
|
||||
if (m_pReturnBuffer)
|
||||
{
|
||||
// First half in eax, second half in edx
|
||||
memcpy(m_pReturnBuffer, pRegisters->m_eax, 4);
|
||||
memcpy((void *) ((unsigned long) m_pReturnBuffer + 4), pRegisters->m_edx, 4);
|
||||
return m_pReturnBuffer;
|
||||
}
|
||||
|
||||
return pRegisters->m_eax->m_pAddress;
|
||||
}
|
||||
|
||||
void x86MsThiscall::ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr)
|
||||
{
|
||||
if (m_pReturnBuffer)
|
||||
{
|
||||
// First half in eax, second half in edx
|
||||
memcpy(pRegisters->m_eax, m_pReturnBuffer, 4);
|
||||
memcpy(pRegisters->m_edx, (void *) ((unsigned long) m_pReturnBuffer + 4), 4);
|
||||
}
|
||||
}
|
||||
|
||||
void x86MsThiscall::SaveCallArguments(CRegisters* pRegisters)
|
||||
{
|
||||
// Account for implicit this-pointer in ecx.
|
||||
int size = GetArgStackSize() + GetArgRegisterSize() + sizeof(void *);
|
||||
std::unique_ptr<uint8_t[]> pSavedCallArguments = std::make_unique<uint8_t[]>(size);
|
||||
memcpy(pSavedCallArguments.get(), GetArgumentPtr(0, pRegisters), sizeof(void *));
|
||||
|
||||
size_t offset = sizeof(void *);
|
||||
for (size_t i = 0; i < m_vecArgTypes.size(); i++) {
|
||||
DataTypeSized_t &type = m_vecArgTypes[i];
|
||||
memcpy((void *)((unsigned long)pSavedCallArguments.get() + offset), GetArgumentPtr(i + 1, pRegisters), type.size);
|
||||
offset += type.size;
|
||||
}
|
||||
m_pSavedCallArguments.push_back(std::move(pSavedCallArguments));
|
||||
}
|
||||
|
||||
void x86MsThiscall::RestoreCallArguments(CRegisters* pRegisters)
|
||||
{
|
||||
uint8_t* pSavedCallArguments = m_pSavedCallArguments.back().get();
|
||||
memcpy(GetArgumentPtr(0, pRegisters), pSavedCallArguments, sizeof(void *));
|
||||
|
||||
size_t offset = sizeof(void *);
|
||||
for (size_t i = 0; i < m_vecArgTypes.size(); i++) {
|
||||
DataTypeSized_t &type = m_vecArgTypes[i];
|
||||
memcpy(GetArgumentPtr(i + 1, pRegisters), (void *)((unsigned long)pSavedCallArguments + offset), type.size);
|
||||
offset += type.size;
|
||||
}
|
||||
m_pSavedCallArguments.pop_back();
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
#ifndef _X86_MS_THISCALL_H
|
||||
#define _X86_MS_THISCALL_H
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "../convention.h"
|
||||
#include <am-vector.h>
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> CLASSES
|
||||
// ============================================================================
|
||||
/*
|
||||
Source: DynCall manual and Windows docs
|
||||
|
||||
Registers:
|
||||
- eax = return value
|
||||
- ecx = this pointer
|
||||
- edx = return value
|
||||
- esp = stack pointer
|
||||
- st0 = floating point return value
|
||||
|
||||
Parameter passing:
|
||||
- stack parameter order: right-to-left
|
||||
- callee cleans up the stack
|
||||
- all other arguments are pushed onto the stack
|
||||
- alignment: 4 bytes
|
||||
|
||||
Return values:
|
||||
- return values of pointer or intergral type (<= 32 bits) are returned via the eax register
|
||||
- integers > 32 bits are returned via the eax and edx registers
|
||||
- floating pointer types are returned via the st0 register
|
||||
*/
|
||||
class x86MsThiscall: public ICallingConvention
|
||||
{
|
||||
public:
|
||||
x86MsThiscall(std::vector<DataTypeSized_t> &vecArgTypes, DataTypeSized_t returnType, int iAlignment=4);
|
||||
virtual ~x86MsThiscall();
|
||||
|
||||
virtual std::vector<Register_t> GetRegisters();
|
||||
virtual int GetPopSize();
|
||||
virtual int GetArgStackSize();
|
||||
virtual void** GetStackArgumentPtr(CRegisters* pRegisters);
|
||||
virtual int GetArgRegisterSize();
|
||||
|
||||
virtual void* GetArgumentPtr(unsigned int iIndex, CRegisters* pRegisters);
|
||||
virtual void ArgumentPtrChanged(unsigned int iIndex, CRegisters* pRegisters, void* pArgumentPtr);
|
||||
|
||||
virtual void* GetReturnPtr(CRegisters* pRegisters);
|
||||
virtual void ReturnPtrChanged(CRegisters* pRegisters, void* pReturnPtr);
|
||||
|
||||
virtual void SaveCallArguments(CRegisters* pRegisters);
|
||||
virtual void RestoreCallArguments(CRegisters* pRegisters);
|
||||
|
||||
private:
|
||||
void* m_pReturnBuffer;
|
||||
};
|
||||
|
||||
#endif // _X86_MS_THISCALL_H
|
||||
@@ -0,0 +1,504 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "hook.h"
|
||||
#include <asm/asm.h>
|
||||
#include <macro-assembler-x86.h>
|
||||
#include "extension.h"
|
||||
#include <jit/jit_helpers.h>
|
||||
#include <CDetour/detourhelpers.h>
|
||||
|
||||
using namespace sp;
|
||||
|
||||
// ============================================================================
|
||||
// >> DEFINITIONS
|
||||
// ============================================================================
|
||||
#define JMP_SIZE 6
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> CHook
|
||||
// ============================================================================
|
||||
CHook::CHook(void* pFunc, ICallingConvention* pConvention)
|
||||
{
|
||||
m_pFunc = pFunc;
|
||||
m_pRegisters = new CRegisters(pConvention->GetRegisters());
|
||||
m_pCallingConvention = pConvention;
|
||||
|
||||
if (!m_hookHandler.init())
|
||||
return;
|
||||
|
||||
if (!m_RetAddr.init())
|
||||
return;
|
||||
|
||||
unsigned char* pTarget = (unsigned char *) pFunc;
|
||||
|
||||
// Determine the number of bytes we need to copy
|
||||
int iBytesToCopy = copy_bytes(pTarget, NULL, JMP_SIZE);
|
||||
|
||||
// Create a buffer for the bytes to copy + a jump to the rest of the
|
||||
// function.
|
||||
unsigned char* pCopiedBytes = (unsigned char *) smutils->GetScriptingEngine()->AllocatePageMemory(iBytesToCopy + JMP_SIZE);
|
||||
|
||||
// Fill the array with NOP instructions
|
||||
memset(pCopiedBytes, 0x90, iBytesToCopy + JMP_SIZE);
|
||||
|
||||
// Copy the required bytes to our array
|
||||
copy_bytes(pTarget, pCopiedBytes, JMP_SIZE);
|
||||
|
||||
// Write a jump after the copied bytes to the function/bridge + number of bytes to copy
|
||||
DoGatePatch(pCopiedBytes + iBytesToCopy, pTarget + iBytesToCopy);
|
||||
|
||||
// Save the trampoline
|
||||
m_pTrampoline = (void *) pCopiedBytes;
|
||||
|
||||
// Create the bridge function
|
||||
m_pBridge = CreateBridge();
|
||||
|
||||
// Write a jump to the bridge
|
||||
DoGatePatch((unsigned char *) pFunc, m_pBridge);
|
||||
}
|
||||
|
||||
CHook::~CHook()
|
||||
{
|
||||
// Copy back the previously copied bytes
|
||||
copy_bytes((unsigned char *) m_pTrampoline, (unsigned char *) m_pFunc, JMP_SIZE);
|
||||
|
||||
// Free the trampoline buffer
|
||||
smutils->GetScriptingEngine()->FreePageMemory(m_pTrampoline);
|
||||
|
||||
// Free the asm bridge and new return address
|
||||
smutils->GetScriptingEngine()->FreePageMemory(m_pBridge);
|
||||
smutils->GetScriptingEngine()->FreePageMemory(m_pNewRetAddr);
|
||||
|
||||
delete m_pRegisters;
|
||||
delete m_pCallingConvention;
|
||||
}
|
||||
|
||||
void CHook::AddCallback(HookType_t eHookType, HookHandlerFn* pCallback)
|
||||
{
|
||||
if (!pCallback)
|
||||
return;
|
||||
|
||||
HookTypeMap::Insert i = m_hookHandler.findForAdd(eHookType);
|
||||
if (!i.found()) {
|
||||
HookHandlerSet set;
|
||||
set.init();
|
||||
m_hookHandler.add(i, eHookType, std::move(set));
|
||||
}
|
||||
|
||||
i->value.add(pCallback);
|
||||
}
|
||||
|
||||
void CHook::RemoveCallback(HookType_t eHookType, HookHandlerFn* pCallback)
|
||||
{
|
||||
HookTypeMap::Result r = m_hookHandler.find(eHookType);
|
||||
if (!r.found())
|
||||
return;
|
||||
|
||||
r->value.removeIfExists(pCallback);
|
||||
}
|
||||
|
||||
bool CHook::IsCallbackRegistered(HookType_t eHookType, HookHandlerFn* pCallback)
|
||||
{
|
||||
HookTypeMap::Result r = m_hookHandler.find(eHookType);
|
||||
if (!r.found())
|
||||
return false;
|
||||
|
||||
return r->value.has(pCallback);
|
||||
}
|
||||
|
||||
bool CHook::AreCallbacksRegistered()
|
||||
{
|
||||
HookTypeMap::Result r = m_hookHandler.find(HOOKTYPE_PRE);
|
||||
if (r.found() && r->value.elements() > 0)
|
||||
return true;
|
||||
|
||||
r = m_hookHandler.find(HOOKTYPE_POST);
|
||||
if (r.found() && r->value.elements() > 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ReturnAction_t CHook::HookHandler(HookType_t eHookType)
|
||||
{
|
||||
if (eHookType == HOOKTYPE_POST)
|
||||
{
|
||||
ReturnAction_t lastPreReturnAction = m_LastPreReturnAction.back();
|
||||
m_LastPreReturnAction.pop_back();
|
||||
if (lastPreReturnAction == ReturnAction_Override)
|
||||
m_pCallingConvention->RestoreReturnValue(m_pRegisters);
|
||||
if (lastPreReturnAction < ReturnAction_Supercede)
|
||||
m_pCallingConvention->RestoreCallArguments(m_pRegisters);
|
||||
}
|
||||
|
||||
ReturnAction_t returnAction = ReturnAction_Ignored;
|
||||
HookTypeMap::Result r = m_hookHandler.find(eHookType);
|
||||
if (!r.found())
|
||||
{
|
||||
// Still save the arguments for the post hook even if there
|
||||
// is no pre-handler registered.
|
||||
if (eHookType == HOOKTYPE_PRE)
|
||||
{
|
||||
m_LastPreReturnAction.push_back(returnAction);
|
||||
m_pCallingConvention->SaveCallArguments(m_pRegisters);
|
||||
}
|
||||
return returnAction;
|
||||
}
|
||||
|
||||
HookHandlerSet &callbacks = r->value;
|
||||
for(HookHandlerSet::iterator it=callbacks.iter(); !it.empty(); it.next())
|
||||
{
|
||||
ReturnAction_t result = ((HookHandlerFn) *it)(eHookType, this);
|
||||
if (result > returnAction)
|
||||
returnAction = result;
|
||||
}
|
||||
|
||||
if (eHookType == HOOKTYPE_PRE)
|
||||
{
|
||||
m_LastPreReturnAction.push_back(returnAction);
|
||||
if (returnAction == ReturnAction_Override)
|
||||
m_pCallingConvention->SaveReturnValue(m_pRegisters);
|
||||
if (returnAction < ReturnAction_Supercede)
|
||||
m_pCallingConvention->SaveCallArguments(m_pRegisters);
|
||||
}
|
||||
|
||||
return returnAction;
|
||||
}
|
||||
|
||||
void* __cdecl CHook::GetReturnAddress(void* pESP)
|
||||
{
|
||||
ReturnAddressMap::Result r = m_RetAddr.find(pESP);
|
||||
assert(r.found());
|
||||
if (!r.found())
|
||||
{
|
||||
smutils->LogError(myself, "FATAL: Failed to find return address of original function. Check the arguments and return type of your detour setup.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *pRetAddr = r->value.back();
|
||||
r->value.pop_back();
|
||||
|
||||
// Clear the stack address from the cache now that we ran the last post hook.
|
||||
if (r->value.empty())
|
||||
m_RetAddr.remove(r);
|
||||
|
||||
return pRetAddr;
|
||||
}
|
||||
|
||||
void __cdecl CHook::SetReturnAddress(void* pRetAddr, void* pESP)
|
||||
{
|
||||
ReturnAddressMap::Insert i = m_RetAddr.findForAdd(pESP);
|
||||
if (!i.found())
|
||||
m_RetAddr.add(i, pESP, std::vector<void *>());
|
||||
|
||||
i->value.push_back(pRetAddr);
|
||||
}
|
||||
|
||||
void* CHook::CreateBridge()
|
||||
{
|
||||
sp::MacroAssembler masm;
|
||||
Label label_supercede;
|
||||
|
||||
// Write a redirect to the post-hook code
|
||||
Write_ModifyReturnAddress(masm);
|
||||
|
||||
// Call the pre-hook handler and jump to label_supercede if ReturnAction_Supercede was returned
|
||||
Write_CallHandler(masm, HOOKTYPE_PRE);
|
||||
masm.cmpb(r8_al, ReturnAction_Supercede);
|
||||
|
||||
// Restore the previously saved registers, so any changes will be applied
|
||||
Write_RestoreRegisters(masm, HOOKTYPE_PRE);
|
||||
|
||||
masm.j(equal, &label_supercede);
|
||||
|
||||
// Jump to the trampoline
|
||||
masm.jmp(ExternalAddress(m_pTrampoline));
|
||||
|
||||
// This code will be executed if a pre-hook returns ReturnAction_Supercede
|
||||
masm.bind(&label_supercede);
|
||||
|
||||
// Finally, return to the caller
|
||||
// This will still call post hooks, but will skip the original function.
|
||||
masm.ret(m_pCallingConvention->GetPopSize());
|
||||
|
||||
void *base = smutils->GetScriptingEngine()->AllocatePageMemory(masm.length());
|
||||
masm.emitToExecutableMemory(base);
|
||||
return base;
|
||||
}
|
||||
|
||||
void CHook::Write_ModifyReturnAddress(sp::MacroAssembler& masm)
|
||||
{
|
||||
// Save scratch registers that are used by SetReturnAddress
|
||||
static void* pEAX = NULL;
|
||||
static void* pECX = NULL;
|
||||
static void* pEDX = NULL;
|
||||
|
||||
masm.movl(Operand(ExternalAddress(&pEAX)), eax);
|
||||
masm.movl(Operand(ExternalAddress(&pECX)), ecx);
|
||||
masm.movl(Operand(ExternalAddress(&pEDX)), edx);
|
||||
|
||||
// Store the return address in eax
|
||||
masm.movl(eax, Operand(esp, 0));
|
||||
|
||||
// Save the original return address by using the current esp as the key.
|
||||
// This should be unique until we have returned to the original caller.
|
||||
void (__cdecl CHook::*SetReturnAddress)(void*, void*) = &CHook::SetReturnAddress;
|
||||
masm.push(esp);
|
||||
masm.push(eax);
|
||||
masm.push(intptr_t(this));
|
||||
masm.call(ExternalAddress((void *&)SetReturnAddress));
|
||||
masm.addl(esp, 12);
|
||||
|
||||
// Restore scratch registers
|
||||
masm.movl(eax, Operand(ExternalAddress(&pEAX)));
|
||||
masm.movl(ecx, Operand(ExternalAddress(&pECX)));
|
||||
masm.movl(edx, Operand(ExternalAddress(&pEDX)));
|
||||
|
||||
// Override the return address. This is a redirect to our post-hook code
|
||||
m_pNewRetAddr = CreatePostCallback();
|
||||
masm.movl(Operand(esp, 0), intptr_t(m_pNewRetAddr));
|
||||
}
|
||||
|
||||
void* CHook::CreatePostCallback()
|
||||
{
|
||||
sp::MacroAssembler masm;
|
||||
|
||||
int iPopSize = m_pCallingConvention->GetPopSize();
|
||||
|
||||
// Subtract the previously added bytes (stack size + return address), so
|
||||
// that we can access the arguments again
|
||||
masm.subl(esp, iPopSize+4);
|
||||
|
||||
// Call the post-hook handler
|
||||
Write_CallHandler(masm, HOOKTYPE_POST);
|
||||
|
||||
// Restore the previously saved registers, so any changes will be applied
|
||||
Write_RestoreRegisters(masm, HOOKTYPE_POST);
|
||||
|
||||
// Save scratch registers that are used by GetReturnAddress
|
||||
static void* pEAX = NULL;
|
||||
static void* pECX = NULL;
|
||||
static void* pEDX = NULL;
|
||||
masm.movl(Operand(ExternalAddress(&pEAX)), eax);
|
||||
masm.movl(Operand(ExternalAddress(&pECX)), ecx);
|
||||
masm.movl(Operand(ExternalAddress(&pEDX)), edx);
|
||||
|
||||
// Get the original return address
|
||||
void* (__cdecl CHook::*GetReturnAddress)(void*) = &CHook::GetReturnAddress;
|
||||
masm.push(esp);
|
||||
masm.push(intptr_t(this));
|
||||
masm.call(ExternalAddress((void *&)GetReturnAddress));
|
||||
masm.addl(esp, 8);
|
||||
|
||||
// Save the original return address
|
||||
static void* pRetAddr = NULL;
|
||||
masm.movl(Operand(ExternalAddress(&pRetAddr)), eax);
|
||||
|
||||
// Restore scratch registers
|
||||
masm.movl(eax, Operand(ExternalAddress(&pEAX)));
|
||||
masm.movl(ecx, Operand(ExternalAddress(&pECX)));
|
||||
masm.movl(edx, Operand(ExternalAddress(&pEDX)));
|
||||
|
||||
// Add the bytes again to the stack (stack size + return address), so we
|
||||
// don't corrupt the stack.
|
||||
masm.addl(esp, iPopSize+4);
|
||||
|
||||
// Jump to the original return address
|
||||
masm.jmp(Operand(ExternalAddress(&pRetAddr)));
|
||||
|
||||
// Generate the code
|
||||
void *base = smutils->GetScriptingEngine()->AllocatePageMemory(masm.length());
|
||||
masm.emitToExecutableMemory(base);
|
||||
return base;
|
||||
}
|
||||
|
||||
void CHook::Write_CallHandler(sp::MacroAssembler& masm, HookType_t type)
|
||||
{
|
||||
ReturnAction_t (__cdecl CHook::*HookHandler)(HookType_t) = &CHook::HookHandler;
|
||||
|
||||
// Save the registers so that we can access them in our handlers
|
||||
Write_SaveRegisters(masm, type);
|
||||
|
||||
// Align the stack to 16 bytes.
|
||||
masm.subl(esp, 4);
|
||||
|
||||
// Call the global hook handler
|
||||
masm.push(type);
|
||||
masm.push(intptr_t(this));
|
||||
masm.call(ExternalAddress((void *&)HookHandler));
|
||||
masm.addl(esp, 12);
|
||||
}
|
||||
|
||||
void CHook::Write_SaveRegisters(sp::MacroAssembler& masm, HookType_t type)
|
||||
{
|
||||
std::vector<Register_t> vecRegistersToSave = m_pCallingConvention->GetRegisters();
|
||||
for(size_t i = 0; i < vecRegistersToSave.size(); i++)
|
||||
{
|
||||
switch(vecRegistersToSave[i])
|
||||
{
|
||||
// ========================================================================
|
||||
// >> 8-bit General purpose registers
|
||||
// ========================================================================
|
||||
case AL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_al->m_pAddress)), r8_al); break;
|
||||
case CL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_cl->m_pAddress)), r8_cl); break;
|
||||
case DL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_dl->m_pAddress)), r8_dl); break;
|
||||
case BL: masm.movl(Operand(ExternalAddress(m_pRegisters->m_bl->m_pAddress)), r8_bl); break;
|
||||
case AH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ah->m_pAddress)), r8_ah); break;
|
||||
case CH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ch->m_pAddress)), r8_ch); break;
|
||||
case DH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_dh->m_pAddress)), r8_dh); break;
|
||||
case BH: masm.movl(Operand(ExternalAddress(m_pRegisters->m_bh->m_pAddress)), r8_bh); break;
|
||||
|
||||
// ========================================================================
|
||||
// >> 32-bit General purpose registers
|
||||
// ========================================================================
|
||||
case EAX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_eax->m_pAddress)), eax); break;
|
||||
case ECX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ecx->m_pAddress)), ecx); break;
|
||||
case EDX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_edx->m_pAddress)), edx); break;
|
||||
case EBX: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ebx->m_pAddress)), ebx); break;
|
||||
case ESP: masm.movl(Operand(ExternalAddress(m_pRegisters->m_esp->m_pAddress)), esp); break;
|
||||
case EBP: masm.movl(Operand(ExternalAddress(m_pRegisters->m_ebp->m_pAddress)), ebp); break;
|
||||
case ESI: masm.movl(Operand(ExternalAddress(m_pRegisters->m_esi->m_pAddress)), esi); break;
|
||||
case EDI: masm.movl(Operand(ExternalAddress(m_pRegisters->m_edi->m_pAddress)), edi); break;
|
||||
|
||||
// ========================================================================
|
||||
// >> 128-bit XMM registers
|
||||
// ========================================================================
|
||||
// TODO: Also provide movups?
|
||||
case XMM0: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm0->m_pAddress)), xmm0); break;
|
||||
case XMM1: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm1->m_pAddress)), xmm1); break;
|
||||
case XMM2: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm2->m_pAddress)), xmm2); break;
|
||||
case XMM3: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm3->m_pAddress)), xmm3); break;
|
||||
case XMM4: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm4->m_pAddress)), xmm4); break;
|
||||
case XMM5: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm5->m_pAddress)), xmm5); break;
|
||||
case XMM6: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm6->m_pAddress)), xmm6); break;
|
||||
case XMM7: masm.movaps(Operand(ExternalAddress(m_pRegisters->m_xmm7->m_pAddress)), xmm7); break;
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// >> 80-bit FPU registers
|
||||
// ========================================================================
|
||||
case ST0:
|
||||
// Don't mess with the FPU stack in a pre-hook. The float return is returned in st0,
|
||||
// so only load it in a post hook to avoid writing back NaN.
|
||||
if (type == HOOKTYPE_POST)
|
||||
masm.fst32(Operand(ExternalAddress(m_pRegisters->m_st0->m_pAddress)));
|
||||
break;
|
||||
//case ST1: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st1->m_pAddress)), st1); break;
|
||||
//case ST2: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st2->m_pAddress)), st2); break;
|
||||
//case ST3: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st3->m_pAddress)), st3); break;
|
||||
//case ST4: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st4->m_pAddress)), st4); break;
|
||||
//case ST5: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st5->m_pAddress)), st5); break;
|
||||
//case ST6: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st6->m_pAddress)), st6); break;
|
||||
//case ST7: masm.movl(tword_ptr_abs(Ptr(m_pRegisters->m_st7->m_pAddress)), st7); break;
|
||||
|
||||
default: puts("Unsupported register.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CHook::Write_RestoreRegisters(sp::MacroAssembler& masm, HookType_t type)
|
||||
{
|
||||
std::vector<Register_t> vecRegistersToSave = m_pCallingConvention->GetRegisters();
|
||||
for (size_t i = 0; i < vecRegistersToSave.size(); i++)
|
||||
{
|
||||
switch (vecRegistersToSave[i])
|
||||
{
|
||||
// ========================================================================
|
||||
// >> 8-bit General purpose registers
|
||||
// ========================================================================
|
||||
case AL: masm.movl(r8_al, Operand(ExternalAddress(m_pRegisters->m_al->m_pAddress))); break;
|
||||
case CL: masm.movl(r8_cl, Operand(ExternalAddress(m_pRegisters->m_cl->m_pAddress))); break;
|
||||
case DL: masm.movl(r8_dl, Operand(ExternalAddress(m_pRegisters->m_dl->m_pAddress))); break;
|
||||
case BL: masm.movl(r8_bl, Operand(ExternalAddress(m_pRegisters->m_bl->m_pAddress))); break;
|
||||
case AH: masm.movl(r8_ah, Operand(ExternalAddress(m_pRegisters->m_ah->m_pAddress))); break;
|
||||
case CH: masm.movl(r8_ch, Operand(ExternalAddress(m_pRegisters->m_ch->m_pAddress))); break;
|
||||
case DH: masm.movl(r8_dh, Operand(ExternalAddress(m_pRegisters->m_dh->m_pAddress))); break;
|
||||
case BH: masm.movl(r8_bh, Operand(ExternalAddress(m_pRegisters->m_bh->m_pAddress))); break;
|
||||
|
||||
// ========================================================================
|
||||
// >> 32-bit General purpose registers
|
||||
// ========================================================================
|
||||
case EAX: masm.movl(eax, Operand(ExternalAddress(m_pRegisters->m_eax->m_pAddress))); break;
|
||||
case ECX: masm.movl(ecx, Operand(ExternalAddress(m_pRegisters->m_ecx->m_pAddress))); break;
|
||||
case EDX: masm.movl(edx, Operand(ExternalAddress(m_pRegisters->m_edx->m_pAddress))); break;
|
||||
case EBX: masm.movl(ebx, Operand(ExternalAddress(m_pRegisters->m_ebx->m_pAddress))); break;
|
||||
case ESP: masm.movl(esp, Operand(ExternalAddress(m_pRegisters->m_esp->m_pAddress))); break;
|
||||
case EBP: masm.movl(ebp, Operand(ExternalAddress(m_pRegisters->m_ebp->m_pAddress))); break;
|
||||
case ESI: masm.movl(esi, Operand(ExternalAddress(m_pRegisters->m_esi->m_pAddress))); break;
|
||||
case EDI: masm.movl(edi, Operand(ExternalAddress(m_pRegisters->m_edi->m_pAddress))); break;
|
||||
|
||||
// ========================================================================
|
||||
// >> 128-bit XMM registers
|
||||
// ========================================================================
|
||||
// TODO: Also provide movups?
|
||||
case XMM0: masm.movaps(xmm0, Operand(ExternalAddress(m_pRegisters->m_xmm0->m_pAddress))); break;
|
||||
case XMM1: masm.movaps(xmm1, Operand(ExternalAddress(m_pRegisters->m_xmm1->m_pAddress))); break;
|
||||
case XMM2: masm.movaps(xmm2, Operand(ExternalAddress(m_pRegisters->m_xmm2->m_pAddress))); break;
|
||||
case XMM3: masm.movaps(xmm3, Operand(ExternalAddress(m_pRegisters->m_xmm3->m_pAddress))); break;
|
||||
case XMM4: masm.movaps(xmm4, Operand(ExternalAddress(m_pRegisters->m_xmm4->m_pAddress))); break;
|
||||
case XMM5: masm.movaps(xmm5, Operand(ExternalAddress(m_pRegisters->m_xmm5->m_pAddress))); break;
|
||||
case XMM6: masm.movaps(xmm6, Operand(ExternalAddress(m_pRegisters->m_xmm6->m_pAddress))); break;
|
||||
case XMM7: masm.movaps(xmm7, Operand(ExternalAddress(m_pRegisters->m_xmm7->m_pAddress))); break;
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// >> 80-bit FPU registers
|
||||
// ========================================================================
|
||||
case ST0:
|
||||
if (type == HOOKTYPE_POST) {
|
||||
// Replace the top of the FPU stack.
|
||||
// Copy st0 to st0 and pop -> just pop the FPU stack.
|
||||
masm.fstp(st0);
|
||||
// Push a value to the FPU stack.
|
||||
// TODO: Only write back when changed? Save full 80bits for that case.
|
||||
// Avoid truncation of the data if it's unchanged.
|
||||
masm.fld32(Operand(ExternalAddress(m_pRegisters->m_st0->m_pAddress)));
|
||||
}
|
||||
break;
|
||||
//case ST1: masm.movl(st1, tword_ptr_abs(Ptr(m_pRegisters->m_st1->m_pAddress))); break;
|
||||
//case ST2: masm.movl(st2, tword_ptr_abs(Ptr(m_pRegisters->m_st2->m_pAddress))); break;
|
||||
//case ST3: masm.movl(st3, tword_ptr_abs(Ptr(m_pRegisters->m_st3->m_pAddress))); break;
|
||||
//case ST4: masm.movl(st4, tword_ptr_abs(Ptr(m_pRegisters->m_st4->m_pAddress))); break;
|
||||
//case ST5: masm.movl(st5, tword_ptr_abs(Ptr(m_pRegisters->m_st5->m_pAddress))); break;
|
||||
//case ST6: masm.movl(st6, tword_ptr_abs(Ptr(m_pRegisters->m_st6->m_pAddress))); break;
|
||||
//case ST7: masm.movl(st7, tword_ptr_abs(Ptr(m_pRegisters->m_st7->m_pAddress))); break;
|
||||
|
||||
default: puts("Unsupported register.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
#ifndef _HOOK_H
|
||||
#define _HOOK_H
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "registers.h"
|
||||
#include "convention.h"
|
||||
#include <am-hashmap.h>
|
||||
#include <am-hashset.h>
|
||||
|
||||
// ============================================================================
|
||||
// >> HookType_t
|
||||
// ============================================================================
|
||||
enum HookType_t
|
||||
{
|
||||
// Callback will be executed before the original function.
|
||||
HOOKTYPE_PRE,
|
||||
|
||||
// Callback will be executed after the original function.
|
||||
HOOKTYPE_POST
|
||||
};
|
||||
|
||||
enum ReturnAction_t
|
||||
{
|
||||
ReturnAction_Ignored, // handler didn't take any action
|
||||
ReturnAction_Handled, // plugin did something, but real function should still be called
|
||||
ReturnAction_Override, // call real function, but use my return value
|
||||
ReturnAction_Supercede // skip real function; use my return value
|
||||
};
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> TYPEDEFS
|
||||
// ============================================================================
|
||||
class CHook;
|
||||
typedef ReturnAction_t (*HookHandlerFn)(HookType_t, CHook*);
|
||||
|
||||
#ifdef __linux__
|
||||
#define __cdecl
|
||||
#endif
|
||||
|
||||
struct IntegerPolicy
|
||||
{
|
||||
static inline uint32_t hash(size_t i) {
|
||||
return ke::HashInteger<sizeof(size_t)>(i);
|
||||
}
|
||||
static inline bool matches(size_t i1, size_t i2) {
|
||||
return i1 == i2;
|
||||
}
|
||||
};
|
||||
|
||||
typedef ke::HashSet<HookHandlerFn*, ke::PointerPolicy<HookHandlerFn>> HookHandlerSet;
|
||||
typedef ke::HashMap<HookType_t, HookHandlerSet, IntegerPolicy> HookTypeMap;
|
||||
typedef ke::HashMap<void*, std::vector<void*>, ke::PointerPolicy<void>> ReturnAddressMap;
|
||||
|
||||
namespace sp
|
||||
{
|
||||
class MacroAssembler;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> CLASSES
|
||||
// ============================================================================
|
||||
|
||||
class CHook
|
||||
{
|
||||
private:
|
||||
friend class CHookManager;
|
||||
|
||||
/*
|
||||
Creates a new function hook.
|
||||
|
||||
@param <pFunc>:
|
||||
The address of the function to hook
|
||||
|
||||
@param <pConvention>:
|
||||
The calling convention of <pFunc>.
|
||||
*/
|
||||
CHook(void* pFunc, ICallingConvention* pConvention);
|
||||
~CHook();
|
||||
|
||||
public:
|
||||
/*
|
||||
Adds a hook handler to the hook.
|
||||
|
||||
@param type The hook type.
|
||||
@param pFunc The hook handler that should be added.
|
||||
*/
|
||||
void AddCallback(HookType_t type, HookHandlerFn* pFunc);
|
||||
|
||||
/*
|
||||
Removes a hook handler to the hook.
|
||||
|
||||
@param type The hook type.
|
||||
@param pFunc The hook handler that should be removed.
|
||||
*/
|
||||
void RemoveCallback(HookType_t type, HookHandlerFn* pFunc);
|
||||
|
||||
/*
|
||||
Checks if a hook handler is already added.
|
||||
|
||||
@param type The hook type.
|
||||
@param pFunc The hook handler that should be checked.
|
||||
*/
|
||||
bool IsCallbackRegistered(HookType_t type, HookHandlerFn* pFunc);
|
||||
|
||||
/*
|
||||
Checks if there are any hook handlers added to this hook.
|
||||
*/
|
||||
bool AreCallbacksRegistered();
|
||||
|
||||
template<class T>
|
||||
T GetArgument(int iIndex)
|
||||
{
|
||||
return *(T *) m_pCallingConvention->GetArgumentPtr(iIndex, m_pRegisters);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void SetArgument(int iIndex, T value)
|
||||
{
|
||||
void* pPtr = m_pCallingConvention->GetArgumentPtr(iIndex, m_pRegisters);
|
||||
*(T *) pPtr = value;
|
||||
m_pCallingConvention->ArgumentPtrChanged(iIndex, m_pRegisters, pPtr);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T GetReturnValue()
|
||||
{
|
||||
return *(T *) m_pCallingConvention->GetReturnPtr(m_pRegisters);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void SetReturnValue(T value)
|
||||
{
|
||||
void* pPtr = m_pCallingConvention->GetReturnPtr(m_pRegisters);
|
||||
*(T *) pPtr = value;
|
||||
m_pCallingConvention->ReturnPtrChanged(m_pRegisters, pPtr);
|
||||
}
|
||||
|
||||
private:
|
||||
void* CreateBridge();
|
||||
|
||||
void Write_ModifyReturnAddress(sp::MacroAssembler& masm);
|
||||
void Write_CallHandler(sp::MacroAssembler& masm, HookType_t type);
|
||||
void Write_SaveRegisters(sp::MacroAssembler& masm, HookType_t type);
|
||||
void Write_RestoreRegisters(sp::MacroAssembler& masm, HookType_t type);
|
||||
|
||||
void* CreatePostCallback();
|
||||
|
||||
ReturnAction_t __cdecl HookHandler(HookType_t type);
|
||||
|
||||
void* __cdecl GetReturnAddress(void* pESP);
|
||||
void __cdecl SetReturnAddress(void* pRetAddr, void* pESP);
|
||||
|
||||
public:
|
||||
|
||||
HookTypeMap m_hookHandler;
|
||||
|
||||
// Address of the original function
|
||||
void* m_pFunc;
|
||||
|
||||
ICallingConvention* m_pCallingConvention;
|
||||
|
||||
// Address of the bridge
|
||||
void* m_pBridge;
|
||||
|
||||
// Address of the trampoline
|
||||
void* m_pTrampoline;
|
||||
|
||||
// Register storage
|
||||
CRegisters* m_pRegisters;
|
||||
|
||||
// New return address
|
||||
void* m_pNewRetAddr;
|
||||
|
||||
ReturnAddressMap m_RetAddr;
|
||||
|
||||
// Save the last return action of the pre HookHandler for use in the post handler.
|
||||
std::vector<ReturnAction_t> m_LastPreReturnAction;
|
||||
};
|
||||
|
||||
#endif // _HOOK_H
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "manager.h"
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> CHookManager
|
||||
// ============================================================================
|
||||
CHook* CHookManager::HookFunction(void* pFunc, ICallingConvention* pConvention)
|
||||
{
|
||||
if (!pFunc)
|
||||
return NULL;
|
||||
|
||||
CHook* pHook = FindHook(pFunc);
|
||||
if (pHook)
|
||||
{
|
||||
delete pConvention;
|
||||
return pHook;
|
||||
}
|
||||
|
||||
pHook = new CHook(pFunc, pConvention);
|
||||
m_Hooks.push_back(pHook);
|
||||
return pHook;
|
||||
}
|
||||
|
||||
void CHookManager::UnhookFunction(void* pFunc)
|
||||
{
|
||||
if (!pFunc)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < m_Hooks.size(); i++)
|
||||
{
|
||||
CHook* pHook = m_Hooks[i];
|
||||
if (pHook->m_pFunc == pFunc)
|
||||
{
|
||||
m_Hooks.erase(m_Hooks.begin() + i);
|
||||
delete pHook;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CHook* CHookManager::FindHook(void* pFunc)
|
||||
{
|
||||
if (!pFunc)
|
||||
return NULL;
|
||||
|
||||
for(size_t i = 0; i < m_Hooks.size(); i++)
|
||||
{
|
||||
CHook* pHook = m_Hooks[i];
|
||||
if (pHook->m_pFunc == pFunc)
|
||||
return pHook;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CHookManager::UnhookAllFunctions()
|
||||
{
|
||||
for(size_t i = 0; i < m_Hooks.size(); i++)
|
||||
delete m_Hooks[i];
|
||||
|
||||
m_Hooks.clear();
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> GetHookManager
|
||||
// ============================================================================
|
||||
CHookManager* GetHookManager()
|
||||
{
|
||||
static CHookManager* s_pManager = new CHookManager;
|
||||
return s_pManager;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
#ifndef _MANAGER_H
|
||||
#define _MANAGER_H
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include "hook.h"
|
||||
#include "convention.h"
|
||||
#include <vector>
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> CHookManager
|
||||
// ============================================================================
|
||||
class CHookManager
|
||||
{
|
||||
public:
|
||||
/*
|
||||
Hooks the given function and returns a new CHook instance. If the
|
||||
function was already hooked, the existing CHook instance will be
|
||||
returned.
|
||||
*/
|
||||
CHook* HookFunction(void* pFunc, ICallingConvention* pConvention);
|
||||
|
||||
/*
|
||||
Removes all callbacks and restores the original function.
|
||||
*/
|
||||
void UnhookFunction(void* pFunc);
|
||||
|
||||
/*
|
||||
Returns either NULL or the found CHook instance.
|
||||
*/
|
||||
CHook* FindHook(void* pFunc);
|
||||
|
||||
/*
|
||||
Removes all callbacks and restores all functions.
|
||||
*/
|
||||
void UnhookAllFunctions();
|
||||
|
||||
public:
|
||||
std::vector<CHook *> m_Hooks;
|
||||
};
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> GetHookManager
|
||||
// ============================================================================
|
||||
/*
|
||||
Returns a pointer to a static CHookManager object.
|
||||
*/
|
||||
CHookManager* GetHookManager();
|
||||
|
||||
#endif // _MANAGER_H
|
||||
@@ -0,0 +1,509 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
#include "registers.h"
|
||||
|
||||
CRegisters::CRegisters(std::vector<Register_t> registers)
|
||||
{
|
||||
// ========================================================================
|
||||
// >> 8-bit General purpose registers
|
||||
// ========================================================================
|
||||
m_al = CreateRegister(registers, AL, 1);
|
||||
m_cl = CreateRegister(registers, CL, 1);
|
||||
m_dl = CreateRegister(registers, DL, 1);
|
||||
m_bl = CreateRegister(registers, BL, 1);
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
m_spl = CreateRegister(registers, SPL, 1);
|
||||
m_bpl = CreateRegister(registers, BPL, 1);
|
||||
m_sil = CreateRegister(registers, SIL, 1);
|
||||
m_dil = CreateRegister(registers, DIL, 1);
|
||||
m_r8b = CreateRegister(registers, R8B, 1);
|
||||
m_r9b = CreateRegister(registers, R9B, 1);
|
||||
m_r10b = CreateRegister(registers, R10B, 1);
|
||||
m_r11b = CreateRegister(registers, R11B, 1);
|
||||
m_r12b = CreateRegister(registers, R12B, 1);
|
||||
m_r13b = CreateRegister(registers, R13B, 1);
|
||||
m_r14b = CreateRegister(registers, R14B, 1);
|
||||
m_r15b = CreateRegister(registers, R15B, 1);
|
||||
*/
|
||||
|
||||
m_ah = CreateRegister(registers, AH, 1);
|
||||
m_ch = CreateRegister(registers, CH, 1);
|
||||
m_dh = CreateRegister(registers, DH, 1);
|
||||
m_bh = CreateRegister(registers, BH, 1);
|
||||
|
||||
// ========================================================================
|
||||
// >> 16-bit General purpose registers
|
||||
// ========================================================================
|
||||
m_ax = CreateRegister(registers, AX, 2);
|
||||
m_cx = CreateRegister(registers, CX, 2);
|
||||
m_dx = CreateRegister(registers, DX, 2);
|
||||
m_bx = CreateRegister(registers, BX, 2);
|
||||
m_sp = CreateRegister(registers, SP, 2);
|
||||
m_bp = CreateRegister(registers, BP, 2);
|
||||
m_si = CreateRegister(registers, SI, 2);
|
||||
m_di = CreateRegister(registers, DI, 2);
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
m_r8w = CreateRegister(registers, R8W, 2);
|
||||
m_r9w = CreateRegister(registers, R9W, 2);
|
||||
m_r10w = CreateRegister(registers, R10W, 2);
|
||||
m_r11w = CreateRegister(registers, R11W, 2);
|
||||
m_r12w = CreateRegister(registers, R12W, 2);
|
||||
m_r13w = CreateRegister(registers, R13W, 2);
|
||||
m_r14w = CreateRegister(registers, R14W, 2);
|
||||
m_r15w = CreateRegister(registers, R14W, 2);
|
||||
*/
|
||||
|
||||
// ========================================================================
|
||||
// >> 32-bit General purpose registers
|
||||
// ========================================================================
|
||||
m_eax = CreateRegister(registers, EAX, 4);
|
||||
m_ecx = CreateRegister(registers, ECX, 4);
|
||||
m_edx = CreateRegister(registers, EDX, 4);
|
||||
m_ebx = CreateRegister(registers, EBX, 4);
|
||||
m_esp = CreateRegister(registers, ESP, 4);
|
||||
m_ebp = CreateRegister(registers, EBP, 4);
|
||||
m_esi = CreateRegister(registers, ESI, 4);
|
||||
m_edi = CreateRegister(registers, EDI, 4);
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
m_r8d = CreateRegister(registers, R8D, 4);
|
||||
m_r9d = CreateRegister(registers, R9D, 4);
|
||||
m_r10d = CreateRegister(registers, R10D, 4);
|
||||
m_r11d = CreateRegister(registers, R11D, 4);
|
||||
m_r12d = CreateRegister(registers, R12D, 4);
|
||||
m_r13d = CreateRegister(registers, R13D, 4);
|
||||
m_r14d = CreateRegister(registers, R14D, 4);
|
||||
m_r15d = CreateRegister(registers, R15D, 4);
|
||||
*/
|
||||
|
||||
// ========================================================================
|
||||
// >> 64-bit General purpose registers
|
||||
// ========================================================================
|
||||
// 64-bit mode only
|
||||
/*
|
||||
m_rax = CreateRegister(registers, RAX, 8);
|
||||
m_rcx = CreateRegister(registers, RCX, 8);
|
||||
m_rdx = CreateRegister(registers, RDX, 8);
|
||||
m_rbx = CreateRegister(registers, RBX, 8);
|
||||
m_rsp = CreateRegister(registers, RSP, 8);
|
||||
m_rbp = CreateRegister(registers, RBP, 8);
|
||||
m_rsi = CreateRegister(registers, RSI, 8);
|
||||
m_rdi = CreateRegister(registers, RDI, 8);
|
||||
*/
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
m_r8 = CreateRegister(registers, R8, 8);
|
||||
m_r9 = CreateRegister(registers, R9, 8);
|
||||
m_r10 = CreateRegister(registers, R10, 8);
|
||||
m_r11 = CreateRegister(registers, R11, 8);
|
||||
m_r12 = CreateRegister(registers, R12, 8);
|
||||
m_r13 = CreateRegister(registers, R13, 8);
|
||||
m_r14 = CreateRegister(registers, R14, 8);
|
||||
m_r15 = CreateRegister(registers, R15, 8);
|
||||
*/
|
||||
|
||||
// ========================================================================
|
||||
// >> 64-bit MM (MMX) registers
|
||||
// ========================================================================
|
||||
m_mm0 = CreateRegister(registers, MM0, 8);
|
||||
m_mm1 = CreateRegister(registers, MM1, 8);
|
||||
m_mm2 = CreateRegister(registers, MM2, 8);
|
||||
m_mm3 = CreateRegister(registers, MM3, 8);
|
||||
m_mm4 = CreateRegister(registers, MM4, 8);
|
||||
m_mm5 = CreateRegister(registers, MM5, 8);
|
||||
m_mm6 = CreateRegister(registers, MM6, 8);
|
||||
m_mm7 = CreateRegister(registers, MM7, 8);
|
||||
|
||||
// ========================================================================
|
||||
// >> 128-bit XMM registers
|
||||
// ========================================================================
|
||||
// Copying data from xmm0-7 requires the memory address to be 16-byte aligned.
|
||||
m_xmm0 = CreateRegister(registers, XMM0, 16, 16);
|
||||
m_xmm1 = CreateRegister(registers, XMM1, 16, 16);
|
||||
m_xmm2 = CreateRegister(registers, XMM2, 16, 16);
|
||||
m_xmm3 = CreateRegister(registers, XMM3, 16, 16);
|
||||
m_xmm4 = CreateRegister(registers, XMM4, 16, 16);
|
||||
m_xmm5 = CreateRegister(registers, XMM5, 16, 16);
|
||||
m_xmm6 = CreateRegister(registers, XMM6, 16, 16);
|
||||
m_xmm7 = CreateRegister(registers, XMM7, 16, 16);
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
m_xmm8 = CreateRegister(registers, XMM8, 16);
|
||||
m_xmm9 = CreateRegister(registers, XMM9, 16);
|
||||
m_xmm10 = CreateRegister(registers, XMM10, 16);
|
||||
m_xmm11 = CreateRegister(registers, XMM11, 16);
|
||||
m_xmm12 = CreateRegister(registers, XMM12, 16);
|
||||
m_xmm13 = CreateRegister(registers, XMM13, 16);
|
||||
m_xmm14 = CreateRegister(registers, XMM14, 16);
|
||||
m_xmm15 = CreateRegister(registers, XMM15, 16);
|
||||
*/
|
||||
|
||||
// ========================================================================
|
||||
// >> 16-bit Segment registers
|
||||
// ========================================================================
|
||||
m_cs = CreateRegister(registers, CS, 2);
|
||||
m_ss = CreateRegister(registers, SS, 2);
|
||||
m_ds = CreateRegister(registers, DS, 2);
|
||||
m_es = CreateRegister(registers, ES, 2);
|
||||
m_fs = CreateRegister(registers, FS, 2);
|
||||
m_gs = CreateRegister(registers, GS, 2);
|
||||
|
||||
// ========================================================================
|
||||
// >> 80-bit FPU registers
|
||||
// ========================================================================
|
||||
m_st0 = CreateRegister(registers, ST0, 10);
|
||||
m_st1 = CreateRegister(registers, ST1, 10);
|
||||
m_st2 = CreateRegister(registers, ST2, 10);
|
||||
m_st3 = CreateRegister(registers, ST3, 10);
|
||||
m_st4 = CreateRegister(registers, ST4, 10);
|
||||
m_st5 = CreateRegister(registers, ST5, 10);
|
||||
m_st6 = CreateRegister(registers, ST6, 10);
|
||||
m_st7 = CreateRegister(registers, ST7, 10);
|
||||
}
|
||||
|
||||
CRegisters::~CRegisters()
|
||||
{
|
||||
// ========================================================================
|
||||
// >> 8-bit General purpose registers
|
||||
// ========================================================================
|
||||
DeleteRegister(m_al);
|
||||
DeleteRegister(m_cl);
|
||||
DeleteRegister(m_dl);
|
||||
DeleteRegister(m_bl);
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
DeleteRegister(m_spl);
|
||||
DeleteRegister(m_bpl);
|
||||
DeleteRegister(m_sil);
|
||||
DeleteRegister(m_dil);
|
||||
DeleteRegister(m_r8b);
|
||||
DeleteRegister(m_r9b);
|
||||
DeleteRegister(m_r10b);
|
||||
DeleteRegister(m_r11b);
|
||||
DeleteRegister(m_r12b);
|
||||
DeleteRegister(m_r13b);
|
||||
DeleteRegister(m_r14b);
|
||||
DeleteRegister(m_r15b);
|
||||
*/
|
||||
|
||||
DeleteRegister(m_ah);
|
||||
DeleteRegister(m_ch);
|
||||
DeleteRegister(m_dh);
|
||||
DeleteRegister(m_bh);
|
||||
|
||||
// ========================================================================
|
||||
// >> 16-bit General purpose registers
|
||||
// ========================================================================
|
||||
DeleteRegister(m_ax);
|
||||
DeleteRegister(m_cx);
|
||||
DeleteRegister(m_dx);
|
||||
DeleteRegister(m_bx);
|
||||
DeleteRegister(m_sp);
|
||||
DeleteRegister(m_bp);
|
||||
DeleteRegister(m_si);
|
||||
DeleteRegister(m_di);
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
DeleteRegister(m_r8w);
|
||||
DeleteRegister(m_r9w);
|
||||
DeleteRegister(m_r10w);
|
||||
DeleteRegister(m_r11w);
|
||||
DeleteRegister(m_r12w);
|
||||
DeleteRegister(m_r13w);
|
||||
DeleteRegister(m_r14w);
|
||||
DeleteRegister(m_r15w);
|
||||
*/
|
||||
|
||||
// ========================================================================
|
||||
// >> 32-bit General purpose registers
|
||||
// ========================================================================
|
||||
DeleteRegister(m_eax);
|
||||
DeleteRegister(m_ecx);
|
||||
DeleteRegister(m_edx);
|
||||
DeleteRegister(m_ebx);
|
||||
DeleteRegister(m_esp);
|
||||
DeleteRegister(m_ebp);
|
||||
DeleteRegister(m_esi);
|
||||
DeleteRegister(m_edi);
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
DeleteRegister(m_r8d);
|
||||
DeleteRegister(m_r9d);
|
||||
DeleteRegister(m_r10d);
|
||||
DeleteRegister(m_r11d);
|
||||
DeleteRegister(m_r12d);
|
||||
DeleteRegister(m_r13d);
|
||||
DeleteRegister(m_r14d);
|
||||
DeleteRegister(m_r15d);
|
||||
*/
|
||||
|
||||
// ========================================================================
|
||||
// >> 64-bit General purpose registers
|
||||
// ========================================================================
|
||||
// 64-bit mode only
|
||||
/*
|
||||
DeleteRegister(m_rax);
|
||||
DeleteRegister(m_rcx);
|
||||
DeleteRegister(m_rdx);
|
||||
DeleteRegister(m_rbx);
|
||||
DeleteRegister(m_rsp);
|
||||
DeleteRegister(m_rbp);
|
||||
DeleteRegister(m_rsi);
|
||||
DeleteRegister(m_rdi);
|
||||
*/
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
DeleteRegister(m_r8);
|
||||
DeleteRegister(m_r9);
|
||||
DeleteRegister(m_r10);
|
||||
DeleteRegister(m_r11);
|
||||
DeleteRegister(m_r12);
|
||||
DeleteRegister(m_r13);
|
||||
DeleteRegister(m_r14);
|
||||
DeleteRegister(m_r15);
|
||||
*/
|
||||
|
||||
// ========================================================================
|
||||
// >> 64-bit MM (MMX) registers
|
||||
// ========================================================================
|
||||
DeleteRegister(m_mm0);
|
||||
DeleteRegister(m_mm1);
|
||||
DeleteRegister(m_mm2);
|
||||
DeleteRegister(m_mm3);
|
||||
DeleteRegister(m_mm4);
|
||||
DeleteRegister(m_mm5);
|
||||
DeleteRegister(m_mm6);
|
||||
DeleteRegister(m_mm7);
|
||||
|
||||
// ========================================================================
|
||||
// >> 128-bit XMM registers
|
||||
// ========================================================================
|
||||
DeleteRegister(m_xmm0);
|
||||
DeleteRegister(m_xmm1);
|
||||
DeleteRegister(m_xmm2);
|
||||
DeleteRegister(m_xmm3);
|
||||
DeleteRegister(m_xmm4);
|
||||
DeleteRegister(m_xmm5);
|
||||
DeleteRegister(m_xmm6);
|
||||
DeleteRegister(m_xmm7);
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
DeleteRegister(m_xmm8);
|
||||
DeleteRegister(m_xmm9);
|
||||
DeleteRegister(m_xmm10);
|
||||
DeleteRegister(m_xmm11);
|
||||
DeleteRegister(m_xmm12);
|
||||
DeleteRegister(m_xmm13);
|
||||
DeleteRegister(m_xmm14);
|
||||
DeleteRegister(m_xmm15);
|
||||
*/
|
||||
|
||||
// ========================================================================
|
||||
// >> 2-bit Segment registers
|
||||
// ========================================================================
|
||||
DeleteRegister(m_cs);
|
||||
DeleteRegister(m_ss);
|
||||
DeleteRegister(m_ds);
|
||||
DeleteRegister(m_es);
|
||||
DeleteRegister(m_fs);
|
||||
DeleteRegister(m_gs);
|
||||
|
||||
// ========================================================================
|
||||
// >> 80-bit FPU registers
|
||||
// ========================================================================
|
||||
DeleteRegister(m_st0);
|
||||
DeleteRegister(m_st1);
|
||||
DeleteRegister(m_st2);
|
||||
DeleteRegister(m_st3);
|
||||
DeleteRegister(m_st4);
|
||||
DeleteRegister(m_st5);
|
||||
DeleteRegister(m_st6);
|
||||
DeleteRegister(m_st7);
|
||||
}
|
||||
|
||||
CRegister* CRegisters::CreateRegister(std::vector<Register_t>& registers, Register_t reg, uint16_t iSize, uint16_t iAlignment)
|
||||
{
|
||||
for(size_t i = 0; i < registers.size(); i++)
|
||||
{
|
||||
if (registers[i] == reg)
|
||||
{
|
||||
return new CRegister(iSize, iAlignment);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CRegisters::DeleteRegister(CRegister* pRegister)
|
||||
{
|
||||
if (pRegister)
|
||||
{
|
||||
delete pRegister;
|
||||
}
|
||||
}
|
||||
|
||||
CRegister* CRegisters::GetRegister(Register_t reg)
|
||||
{
|
||||
switch (reg)
|
||||
{
|
||||
case AL:
|
||||
return m_al;
|
||||
case CL:
|
||||
return m_cl;
|
||||
case DL:
|
||||
return m_dl;
|
||||
case BL:
|
||||
return m_bl;
|
||||
case AH:
|
||||
return m_ah;
|
||||
case CH:
|
||||
return m_ch;
|
||||
case DH:
|
||||
return m_dh;
|
||||
case BH:
|
||||
return m_bh;
|
||||
|
||||
case AX:
|
||||
return m_ax;
|
||||
case CX:
|
||||
return m_cx;
|
||||
case DX:
|
||||
return m_dx;
|
||||
case BX:
|
||||
return m_bx;
|
||||
case SP:
|
||||
return m_sp;
|
||||
case BP:
|
||||
return m_bp;
|
||||
case SI:
|
||||
return m_si;
|
||||
case DI:
|
||||
return m_di;
|
||||
|
||||
case EAX:
|
||||
return m_eax;
|
||||
case ECX:
|
||||
return m_ecx;
|
||||
case EDX:
|
||||
return m_edx;
|
||||
case EBX:
|
||||
return m_ebx;
|
||||
case ESP:
|
||||
return m_esp;
|
||||
case EBP:
|
||||
return m_ebp;
|
||||
case ESI:
|
||||
return m_esi;
|
||||
case EDI:
|
||||
return m_edi;
|
||||
|
||||
case MM0:
|
||||
return m_mm0;
|
||||
case MM1:
|
||||
return m_mm1;
|
||||
case MM2:
|
||||
return m_mm2;
|
||||
case MM3:
|
||||
return m_mm3;
|
||||
case MM4:
|
||||
return m_mm4;
|
||||
case MM5:
|
||||
return m_mm5;
|
||||
case MM6:
|
||||
return m_mm6;
|
||||
case MM7:
|
||||
return m_mm7;
|
||||
|
||||
case XMM0:
|
||||
return m_xmm0;
|
||||
case XMM1:
|
||||
return m_xmm1;
|
||||
case XMM2:
|
||||
return m_xmm2;
|
||||
case XMM3:
|
||||
return m_xmm3;
|
||||
case XMM4:
|
||||
return m_xmm4;
|
||||
case XMM5:
|
||||
return m_xmm5;
|
||||
case XMM6:
|
||||
return m_xmm6;
|
||||
case XMM7:
|
||||
return m_xmm7;
|
||||
|
||||
case CS:
|
||||
return m_cs;
|
||||
case SS:
|
||||
return m_ss;
|
||||
case DS:
|
||||
return m_ds;
|
||||
case ES:
|
||||
return m_es;
|
||||
case FS:
|
||||
return m_fs;
|
||||
case GS:
|
||||
return m_gs;
|
||||
|
||||
case ST0:
|
||||
return m_st0;
|
||||
case ST1:
|
||||
return m_st1;
|
||||
case ST2:
|
||||
return m_st2;
|
||||
case ST3:
|
||||
return m_st3;
|
||||
case ST4:
|
||||
return m_st4;
|
||||
case ST5:
|
||||
return m_st5;
|
||||
case ST6:
|
||||
return m_st6;
|
||||
case ST7:
|
||||
return m_st7;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,462 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* DynamicHooks
|
||||
* Copyright (C) 2015 Robin Gohmert. All rights reserved.
|
||||
* Copyright (C) 2018-2021 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
* asm.h/cpp from devmaster.net (thanks cybermind) edited by pRED* to handle gcc
|
||||
* -fPIC thunks correctly
|
||||
*
|
||||
* Idea and trampoline code taken from DynDetours (thanks your-name-here).
|
||||
*
|
||||
* Adopted to provide similar features to SourceHook by AlliedModders LLC.
|
||||
*/
|
||||
|
||||
#ifndef _REGISTERS_H
|
||||
#define _REGISTERS_H
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> INCLUDES
|
||||
// ============================================================================
|
||||
#include <vector>
|
||||
#include <am-platform.h>
|
||||
#include <cinttypes>
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> Register_t
|
||||
// ============================================================================
|
||||
enum Register_t
|
||||
{
|
||||
// No register at all.
|
||||
None,
|
||||
|
||||
// ========================================================================
|
||||
// >> 8-bit General purpose registers
|
||||
// ========================================================================
|
||||
AL,
|
||||
CL,
|
||||
DL,
|
||||
BL,
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
SPL,
|
||||
BPL,
|
||||
SIL,
|
||||
DIL,
|
||||
R8B,
|
||||
R9B,
|
||||
R10B,
|
||||
R11B,
|
||||
R12B,
|
||||
R13B,
|
||||
R14B,
|
||||
R15B,
|
||||
*/
|
||||
|
||||
AH,
|
||||
CH,
|
||||
DH,
|
||||
BH,
|
||||
|
||||
// ========================================================================
|
||||
// >> 16-bit General purpose registers
|
||||
// ========================================================================
|
||||
AX,
|
||||
CX,
|
||||
DX,
|
||||
BX,
|
||||
SP,
|
||||
BP,
|
||||
SI,
|
||||
DI,
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
R8W,
|
||||
R9W,
|
||||
R10W,
|
||||
R11W,
|
||||
R12W,
|
||||
R13W,
|
||||
R14W,
|
||||
R15W,
|
||||
*/
|
||||
|
||||
// ========================================================================
|
||||
// >> 32-bit General purpose registers
|
||||
// ========================================================================
|
||||
EAX,
|
||||
ECX,
|
||||
EDX,
|
||||
EBX,
|
||||
ESP,
|
||||
EBP,
|
||||
ESI,
|
||||
EDI,
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
R8D,
|
||||
R9D,
|
||||
R10D,
|
||||
R11D,
|
||||
R12D,
|
||||
R13D,
|
||||
R14D,
|
||||
R15D,
|
||||
*/
|
||||
|
||||
// ========================================================================
|
||||
// >> 64-bit General purpose registers
|
||||
// ========================================================================
|
||||
// 64-bit mode only
|
||||
/*
|
||||
RAX,
|
||||
RCX,
|
||||
RDX,
|
||||
RBX,
|
||||
RSP,
|
||||
RBP,
|
||||
RSI,
|
||||
RDI,
|
||||
*/
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
R8,
|
||||
R9,
|
||||
R10,
|
||||
R11,
|
||||
R12,
|
||||
R13,
|
||||
R14,
|
||||
R15,
|
||||
*/
|
||||
|
||||
// ========================================================================
|
||||
// >> 64-bit MM (MMX) registers
|
||||
// ========================================================================
|
||||
MM0,
|
||||
MM1,
|
||||
MM2,
|
||||
MM3,
|
||||
MM4,
|
||||
MM5,
|
||||
MM6,
|
||||
MM7,
|
||||
|
||||
// ========================================================================
|
||||
// >> 128-bit XMM registers
|
||||
// ========================================================================
|
||||
XMM0,
|
||||
XMM1,
|
||||
XMM2,
|
||||
XMM3,
|
||||
XMM4,
|
||||
XMM5,
|
||||
XMM6,
|
||||
XMM7,
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
XMM8,
|
||||
XMM9,
|
||||
XMM10,
|
||||
XMM11,
|
||||
XMM12,
|
||||
XMM13,
|
||||
XMM14,
|
||||
XMM15,
|
||||
*/
|
||||
|
||||
// ========================================================================
|
||||
// >> 16-bit Segment registers
|
||||
// ========================================================================
|
||||
CS,
|
||||
SS,
|
||||
DS,
|
||||
ES,
|
||||
FS,
|
||||
GS,
|
||||
|
||||
// ========================================================================
|
||||
// >> 80-bit FPU registers
|
||||
// ========================================================================
|
||||
ST0,
|
||||
ST1,
|
||||
ST2,
|
||||
ST3,
|
||||
ST4,
|
||||
ST5,
|
||||
ST6,
|
||||
ST7,
|
||||
};
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> CRegister
|
||||
// ============================================================================
|
||||
class CRegister
|
||||
{
|
||||
public:
|
||||
CRegister(uint16_t iSize, uint16_t iAlignment = 0)
|
||||
{
|
||||
m_iSize = iSize;
|
||||
m_iAlignment = iAlignment;
|
||||
if (iAlignment > 0)
|
||||
#ifdef KE_WINDOWS
|
||||
m_pAddress = _aligned_malloc(iSize, iAlignment);
|
||||
#else
|
||||
m_pAddress = aligned_alloc(iAlignment, iSize);
|
||||
#endif
|
||||
else
|
||||
m_pAddress = malloc(iSize);
|
||||
}
|
||||
|
||||
~CRegister()
|
||||
{
|
||||
|
||||
#ifdef KE_WINDOWS
|
||||
if (m_iAlignment > 0)
|
||||
_aligned_free(m_pAddress);
|
||||
else
|
||||
free(m_pAddress);
|
||||
#else
|
||||
free(m_pAddress);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T GetValue()
|
||||
{
|
||||
return *(T *) m_pAddress;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T GetPointerValue(int iOffset=0)
|
||||
{
|
||||
return *(T *) (GetValue<unsigned long>() + iOffset);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void SetValue(T value)
|
||||
{
|
||||
*(T *) m_pAddress = value;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void SetPointerValue(T value, int iOffset=0)
|
||||
{
|
||||
*(T *) (GetValue<unsigned long>() + iOffset) = value;
|
||||
}
|
||||
|
||||
public:
|
||||
uint16_t m_iSize;
|
||||
uint16_t m_iAlignment;
|
||||
void* m_pAddress;
|
||||
};
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// >> CRegisters
|
||||
// ============================================================================
|
||||
class CRegisters
|
||||
{
|
||||
public:
|
||||
CRegisters(std::vector<Register_t> registers);
|
||||
~CRegisters();
|
||||
|
||||
CRegister* GetRegister(Register_t reg);
|
||||
|
||||
private:
|
||||
CRegister* CreateRegister(std::vector<Register_t>& registers, Register_t reg, uint16_t iSize, uint16_t iAlignment = 0);
|
||||
void DeleteRegister(CRegister* pRegister);
|
||||
|
||||
public:
|
||||
// ========================================================================
|
||||
// >> 8-bit General purpose registers
|
||||
// ========================================================================
|
||||
CRegister* m_al;
|
||||
CRegister* m_cl;
|
||||
CRegister* m_dl;
|
||||
CRegister* m_bl;
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
CRegister* m_spl;
|
||||
CRegister* m_bpl;
|
||||
CRegister* m_sil;
|
||||
CRegister* m_dil;
|
||||
CRegister* m_r8b;
|
||||
CRegister* m_r9b;
|
||||
CRegister* m_r10b;
|
||||
CRegister* m_r11b;
|
||||
CRegister* m_r12b;
|
||||
CRegister* m_r13b;
|
||||
CRegister* m_r14b;
|
||||
CRegister* m_r15b;
|
||||
*/
|
||||
|
||||
CRegister* m_ah;
|
||||
CRegister* m_ch;
|
||||
CRegister* m_dh;
|
||||
CRegister* m_bh;
|
||||
|
||||
// ========================================================================
|
||||
// >> 16-bit General purpose registers
|
||||
// ========================================================================
|
||||
CRegister* m_ax;
|
||||
CRegister* m_cx;
|
||||
CRegister* m_dx;
|
||||
CRegister* m_bx;
|
||||
CRegister* m_sp;
|
||||
CRegister* m_bp;
|
||||
CRegister* m_si;
|
||||
CRegister* m_di;
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
CRegister* m_r8w;
|
||||
CRegister* m_r9w;
|
||||
CRegister* m_r10w;
|
||||
CRegister* m_r11w;
|
||||
CRegister* m_r12w;
|
||||
CRegister* m_r13w;
|
||||
CRegister* m_r14w;
|
||||
CRegister* m_r15w;
|
||||
*/
|
||||
|
||||
// ========================================================================
|
||||
// >> 32-bit General purpose registers
|
||||
// ========================================================================
|
||||
CRegister* m_eax;
|
||||
CRegister* m_ecx;
|
||||
CRegister* m_edx;
|
||||
CRegister* m_ebx;
|
||||
CRegister* m_esp;
|
||||
CRegister* m_ebp;
|
||||
CRegister* m_esi;
|
||||
CRegister* m_edi;
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
CRegister* m_r8d;
|
||||
CRegister* m_r9d;
|
||||
CRegister* m_r10d;
|
||||
CRegister* m_r11d;
|
||||
CRegister* m_r12d;
|
||||
CRegister* m_r13d;
|
||||
CRegister* m_r14d;
|
||||
CRegister* m_r15d;
|
||||
*/
|
||||
|
||||
// ========================================================================
|
||||
// >> 64-bit General purpose registers
|
||||
// ========================================================================
|
||||
// 64-bit mode only
|
||||
/*
|
||||
CRegister* m_rax;
|
||||
CRegister* m_rcx;
|
||||
CRegister* m_rdx;
|
||||
CRegister* m_rbx;
|
||||
CRegister* m_rsp;
|
||||
CRegister* m_rbp;
|
||||
CRegister* m_rsi;
|
||||
CRegister* m_rdi;
|
||||
*/
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
CRegister* m_r8;
|
||||
CRegister* m_r9;
|
||||
CRegister* m_r10;
|
||||
CRegister* m_r11;
|
||||
CRegister* m_r12;
|
||||
CRegister* m_r13;
|
||||
CRegister* m_r14;
|
||||
CRegister* m_r15;
|
||||
*/
|
||||
|
||||
// ========================================================================
|
||||
// >> 64-bit MM (MMX) registers
|
||||
// ========================================================================
|
||||
CRegister* m_mm0;
|
||||
CRegister* m_mm1;
|
||||
CRegister* m_mm2;
|
||||
CRegister* m_mm3;
|
||||
CRegister* m_mm4;
|
||||
CRegister* m_mm5;
|
||||
CRegister* m_mm6;
|
||||
CRegister* m_mm7;
|
||||
|
||||
// ========================================================================
|
||||
// >> 128-bit XMM registers
|
||||
// ========================================================================
|
||||
CRegister* m_xmm0;
|
||||
CRegister* m_xmm1;
|
||||
CRegister* m_xmm2;
|
||||
CRegister* m_xmm3;
|
||||
CRegister* m_xmm4;
|
||||
CRegister* m_xmm5;
|
||||
CRegister* m_xmm6;
|
||||
CRegister* m_xmm7;
|
||||
|
||||
// 64-bit mode only
|
||||
/*
|
||||
CRegister* m_xmm8;
|
||||
CRegister* m_xmm9;
|
||||
CRegister* m_xmm10;
|
||||
CRegister* m_xmm11;
|
||||
CRegister* m_xmm12;
|
||||
CRegister* m_xmm13;
|
||||
CRegister* m_xmm14;
|
||||
CRegister* m_xmm15;
|
||||
*/
|
||||
|
||||
// ========================================================================
|
||||
// >> 16-bit Segment registers
|
||||
// ========================================================================
|
||||
CRegister* m_cs;
|
||||
CRegister* m_ss;
|
||||
CRegister* m_ds;
|
||||
CRegister* m_es;
|
||||
CRegister* m_fs;
|
||||
CRegister* m_gs;
|
||||
|
||||
// ========================================================================
|
||||
// >> 80-bit FPU registers
|
||||
// ========================================================================
|
||||
CRegister* m_st0;
|
||||
CRegister* m_st1;
|
||||
CRegister* m_st2;
|
||||
CRegister* m_st3;
|
||||
CRegister* m_st4;
|
||||
CRegister* m_st5;
|
||||
CRegister* m_st6;
|
||||
CRegister* m_st7;
|
||||
};
|
||||
|
||||
#endif // _REGISTERS_H
|
||||
@@ -0,0 +1,664 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod Dynamic Hooks Extension
|
||||
* Copyright (C) 2012-2021 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$
|
||||
*/
|
||||
|
||||
#include "dynhooks_sourcepawn.h"
|
||||
#include "util.h"
|
||||
#include <memory>
|
||||
|
||||
#ifdef KE_WINDOWS
|
||||
#include "conventions/x86MsCdecl.h"
|
||||
#include "conventions/x86MsThiscall.h"
|
||||
#include "conventions/x86MsStdcall.h"
|
||||
#include "conventions/x86MsFastcall.h"
|
||||
typedef x86MsCdecl x86DetourCdecl;
|
||||
typedef x86MsThiscall x86DetourThisCall;
|
||||
typedef x86MsStdcall x86DetourStdCall;
|
||||
typedef x86MsFastcall x86DetourFastCall;
|
||||
#elif defined KE_LINUX
|
||||
#include "conventions/x86GccCdecl.h"
|
||||
#include "conventions/x86GccThiscall.h"
|
||||
#include "conventions/x86MsStdcall.h"
|
||||
#include "conventions/x86MsFastcall.h"
|
||||
typedef x86GccCdecl x86DetourCdecl;
|
||||
typedef x86GccThiscall x86DetourThisCall;
|
||||
// Uhm, stdcall on linux?
|
||||
typedef x86MsStdcall x86DetourStdCall;
|
||||
// Uhumm, fastcall on linux?
|
||||
typedef x86MsFastcall x86DetourFastCall;
|
||||
#else
|
||||
#error "Unsupported platform."
|
||||
#endif
|
||||
|
||||
// Keep a map of detours and their registered plugin callbacks.
|
||||
DetourMap g_pPreDetours;
|
||||
DetourMap g_pPostDetours;
|
||||
|
||||
void UnhookFunction(HookType_t hookType, CHook *pDetour)
|
||||
{
|
||||
CHookManager *pDetourManager = GetHookManager();
|
||||
pDetour->RemoveCallback(hookType, (HookHandlerFn *)(void *)&HandleDetour);
|
||||
// Only disable the detour if there are no more listeners.
|
||||
if (!pDetour->AreCallbacksRegistered())
|
||||
pDetourManager->UnhookFunction(pDetour->m_pFunc);
|
||||
}
|
||||
|
||||
bool AddDetourPluginHook(HookType_t hookType, CHook *pDetour, HookSetup *setup, IPluginFunction *pCallback)
|
||||
{
|
||||
DetourMap *map;
|
||||
if (hookType == HOOKTYPE_PRE)
|
||||
map = &g_pPreDetours;
|
||||
else
|
||||
map = &g_pPostDetours;
|
||||
|
||||
// See if we already have this detour in our list.
|
||||
PluginCallbackList *wrappers;
|
||||
DetourMap::Insert f = map->findForAdd(pDetour);
|
||||
if (f.found())
|
||||
{
|
||||
wrappers = f->value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a vector to store all the plugin callbacks in.
|
||||
wrappers = new PluginCallbackList;
|
||||
if (!map->add(f, pDetour, wrappers))
|
||||
{
|
||||
delete wrappers;
|
||||
UnhookFunction(hookType, pDetour);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the plugin callback to the detour list.
|
||||
CDynamicHooksSourcePawn *pWrapper = new CDynamicHooksSourcePawn(setup, pDetour, pCallback, hookType == HOOKTYPE_POST);
|
||||
wrappers->push_back(pWrapper);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoveDetourPluginHook(HookType_t hookType, CHook *pDetour, IPluginFunction *pCallback)
|
||||
{
|
||||
DetourMap *map;
|
||||
if (hookType == HOOKTYPE_PRE)
|
||||
map = &g_pPreDetours;
|
||||
else
|
||||
map = &g_pPostDetours;
|
||||
|
||||
DetourMap::Result res = map->find(pDetour);
|
||||
if (!res.found())
|
||||
return false;
|
||||
|
||||
// Remove the plugin's callback
|
||||
bool bRemoved = false;
|
||||
PluginCallbackList *wrappers = res->value;
|
||||
for (int i = wrappers->size()-1; i >= 0 ; i--)
|
||||
{
|
||||
CDynamicHooksSourcePawn *pWrapper = wrappers->at(i);
|
||||
if (pWrapper->plugin_callback == pCallback)
|
||||
{
|
||||
bRemoved = true;
|
||||
delete pWrapper;
|
||||
wrappers->erase(wrappers->begin() + i);
|
||||
}
|
||||
}
|
||||
|
||||
// No more plugin hooks on this callback. Free our structures.
|
||||
if (wrappers->empty())
|
||||
{
|
||||
delete wrappers;
|
||||
UnhookFunction(hookType, pDetour);
|
||||
map->remove(res);
|
||||
}
|
||||
|
||||
return bRemoved;
|
||||
}
|
||||
|
||||
void RemoveAllCallbacksForContext(HookType_t hookType, DetourMap *map, IPluginContext *pContext)
|
||||
{
|
||||
PluginCallbackList *wrappers;
|
||||
CDynamicHooksSourcePawn *pWrapper;
|
||||
DetourMap::iterator it = map->iter();
|
||||
// Run through all active detours we added.
|
||||
for (; !it.empty(); it.next())
|
||||
{
|
||||
wrappers = it->value;
|
||||
// See if there are callbacks of this plugin context registered
|
||||
// and remove them.
|
||||
for (int i = wrappers->size() - 1; i >= 0; i--)
|
||||
{
|
||||
pWrapper = wrappers->at(i);
|
||||
if (pWrapper->plugin_callback->GetParentRuntime()->GetDefaultContext() != pContext)
|
||||
continue;
|
||||
|
||||
delete pWrapper;
|
||||
wrappers->erase(wrappers->begin() + i);
|
||||
}
|
||||
|
||||
// No plugin interested in this hook anymore. unhook.
|
||||
if (wrappers->empty())
|
||||
{
|
||||
delete wrappers;
|
||||
UnhookFunction(hookType, it->key);
|
||||
it.erase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveAllCallbacksForContext(IPluginContext *pContext)
|
||||
{
|
||||
RemoveAllCallbacksForContext(HOOKTYPE_PRE, &g_pPreDetours, pContext);
|
||||
RemoveAllCallbacksForContext(HOOKTYPE_POST, &g_pPostDetours, pContext);
|
||||
}
|
||||
|
||||
void CleanupDetours(HookType_t hookType, DetourMap *map)
|
||||
{
|
||||
PluginCallbackList *wrappers;
|
||||
CDynamicHooksSourcePawn *pWrapper;
|
||||
DetourMap::iterator it = map->iter();
|
||||
// Run through all active detours we added.
|
||||
for (; !it.empty(); it.next())
|
||||
{
|
||||
wrappers = it->value;
|
||||
// Remove all callbacks
|
||||
for (int i = wrappers->size() - 1; i >= 0; i--)
|
||||
{
|
||||
pWrapper = wrappers->at(i);
|
||||
delete pWrapper;
|
||||
}
|
||||
|
||||
// Unhook the function
|
||||
delete wrappers;
|
||||
UnhookFunction(hookType, it->key);
|
||||
}
|
||||
map->clear();
|
||||
}
|
||||
|
||||
void CleanupDetours()
|
||||
{
|
||||
CleanupDetours(HOOKTYPE_PRE, &g_pPreDetours);
|
||||
CleanupDetours(HOOKTYPE_POST, &g_pPostDetours);
|
||||
}
|
||||
|
||||
ICallingConvention *ConstructCallingConvention(HookSetup *setup)
|
||||
{
|
||||
// Convert function parameter types into DynamicHooks structures.
|
||||
std::vector<DataTypeSized_t> vecArgTypes;
|
||||
for (size_t i = 0; i < setup->params.size(); i++)
|
||||
{
|
||||
ParamInfo &info = setup->params[i];
|
||||
DataTypeSized_t type;
|
||||
type.type = DynamicHooks_ConvertParamTypeFrom(info.type);
|
||||
type.size = info.size;
|
||||
type.custom_register = info.custom_register;
|
||||
vecArgTypes.push_back(type);
|
||||
}
|
||||
|
||||
DataTypeSized_t returnType;
|
||||
returnType.type = DynamicHooks_ConvertReturnTypeFrom(setup->returnType);
|
||||
returnType.size = 0;
|
||||
// TODO: Add support for a custom return register.
|
||||
returnType.custom_register = None;
|
||||
|
||||
ICallingConvention *pCallConv = nullptr;
|
||||
switch (setup->callConv)
|
||||
{
|
||||
case CallConv_CDECL:
|
||||
pCallConv = new x86DetourCdecl(vecArgTypes, returnType);
|
||||
break;
|
||||
case CallConv_THISCALL:
|
||||
pCallConv = new x86DetourThisCall(vecArgTypes, returnType);
|
||||
break;
|
||||
case CallConv_STDCALL:
|
||||
pCallConv = new x86DetourStdCall(vecArgTypes, returnType);
|
||||
break;
|
||||
case CallConv_FASTCALL:
|
||||
pCallConv = new x86DetourFastCall(vecArgTypes, returnType);
|
||||
break;
|
||||
default:
|
||||
smutils->LogError(myself, "Unknown calling convention %d.", setup->callConv);
|
||||
break;
|
||||
}
|
||||
|
||||
return pCallConv;
|
||||
}
|
||||
|
||||
// Some arguments might be optimized to be passed in registers instead of the stack.
|
||||
bool UpdateRegisterArgumentSizes(CHook* pDetour, HookSetup *setup)
|
||||
{
|
||||
// The registers the arguments are passed in might not be the same size as the actual parameter type.
|
||||
// Update the type info to the size of the register that's now holding that argument,
|
||||
// so we can copy the whole value.
|
||||
ICallingConvention* callingConvention = pDetour->m_pCallingConvention;
|
||||
std::vector<DataTypeSized_t> &argTypes = callingConvention->m_vecArgTypes;
|
||||
int numArgs = argTypes.size();
|
||||
|
||||
for (int i = 0; i < numArgs; i++)
|
||||
{
|
||||
// Ignore regular arguments on the stack.
|
||||
if (argTypes[i].custom_register == None)
|
||||
continue;
|
||||
|
||||
CRegister *reg = pDetour->m_pRegisters->GetRegister(argTypes[i].custom_register);
|
||||
// That register can't be handled yet.
|
||||
if (!reg)
|
||||
return false;
|
||||
|
||||
argTypes[i].size = reg->m_iSize;
|
||||
setup->params[i].size = reg->m_iSize;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Central handler for all detours. Heart of the detour support.
|
||||
ReturnAction_t HandleDetour(HookType_t hookType, CHook* pDetour)
|
||||
{
|
||||
// Can't call into SourcePawn offthread.
|
||||
if (g_MainThreadId != std::this_thread::get_id())
|
||||
return ReturnAction_Ignored;
|
||||
|
||||
DetourMap *map;
|
||||
if (hookType == HOOKTYPE_PRE)
|
||||
map = &g_pPreDetours;
|
||||
else
|
||||
map = &g_pPostDetours;
|
||||
|
||||
// Find the callback list for this detour.
|
||||
DetourMap::Result r = map->find(pDetour);
|
||||
if (!r.found())
|
||||
return ReturnAction_Ignored;
|
||||
|
||||
// List of all callbacks.
|
||||
PluginCallbackList *wrappers = r->value;
|
||||
|
||||
HookReturnStruct *returnStruct = NULL;
|
||||
Handle_t rHndl = BAD_HANDLE;
|
||||
|
||||
HookParamsStruct *paramStruct = NULL;
|
||||
Handle_t pHndl = BAD_HANDLE;
|
||||
|
||||
int argNum = pDetour->m_pCallingConvention->m_vecArgTypes.size();
|
||||
// Keep a copy of the last return value if some plugin wants to override or supercede the function.
|
||||
ReturnAction_t finalRet = ReturnAction_Ignored;
|
||||
std::unique_ptr<uint8_t[]> finalRetBuf = std::make_unique<uint8_t[]>(pDetour->m_pCallingConvention->m_returnType.size);
|
||||
|
||||
// Call all the plugin functions..
|
||||
for (size_t i = 0; i < wrappers->size(); i++)
|
||||
{
|
||||
CDynamicHooksSourcePawn *pWrapper = wrappers->at(i);
|
||||
IPluginFunction *pCallback = pWrapper->plugin_callback;
|
||||
|
||||
// Create a seperate buffer for changed return values for this plugin.
|
||||
// We update the finalRet above if the tempRet is higher than the previous ones in the callback list.
|
||||
ReturnAction_t tempRet = ReturnAction_Ignored;
|
||||
uint8_t *tempRetBuf = nullptr;
|
||||
|
||||
// Find the this pointer for thiscalls.
|
||||
// Don't even try to load it if the plugin doesn't care and set it to be ignored.
|
||||
if (pWrapper->callConv == CallConv_THISCALL && pWrapper->thisType != ThisPointer_Ignore)
|
||||
{
|
||||
// The this pointer is implicitly always the first argument.
|
||||
void *thisPtr = pDetour->GetArgument<void *>(0);
|
||||
cell_t thisAddr = GetThisPtr(thisPtr, pWrapper->thisType);
|
||||
pCallback->PushCell(thisAddr);
|
||||
}
|
||||
|
||||
// Create the structure for plugins to change/get the return value if the function returns something.
|
||||
if (pWrapper->returnType != ReturnType_Void)
|
||||
{
|
||||
// Create a handle for the return value to pass to the plugin callback.
|
||||
returnStruct = pWrapper->GetReturnStruct();
|
||||
HandleError err;
|
||||
rHndl = handlesys->CreateHandle(g_HookReturnHandle, returnStruct, pCallback->GetParentRuntime()->GetDefaultContext()->GetIdentity(), myself->GetIdentity(), &err);
|
||||
if (!rHndl)
|
||||
{
|
||||
pCallback->Cancel();
|
||||
pCallback->GetParentRuntime()->GetDefaultContext()->BlamePluginError(pCallback, "Error creating ReturnHandle in preparation to call hook callback. (error %d)", err);
|
||||
|
||||
if (returnStruct)
|
||||
delete returnStruct;
|
||||
|
||||
// Don't call more callbacks. They will probably fail too.
|
||||
break;
|
||||
}
|
||||
pCallback->PushCell(rHndl);
|
||||
}
|
||||
|
||||
// Create the structure for plugins to access the function arguments if it has some.
|
||||
if (argNum > 0)
|
||||
{
|
||||
paramStruct = pWrapper->GetParamStruct();
|
||||
HandleError err;
|
||||
pHndl = handlesys->CreateHandle(g_HookParamsHandle, paramStruct, pCallback->GetParentRuntime()->GetDefaultContext()->GetIdentity(), myself->GetIdentity(), &err);
|
||||
if (!pHndl)
|
||||
{
|
||||
pCallback->Cancel();
|
||||
pCallback->GetParentRuntime()->GetDefaultContext()->BlamePluginError(pCallback, "Error creating ThisHandle in preparation to call hook callback. (error %d)", err);
|
||||
|
||||
// Don't leak our own handles here! Free the return struct if we fail during the argument marshalling.
|
||||
if (rHndl)
|
||||
{
|
||||
HandleSecurity sec(pCallback->GetParentRuntime()->GetDefaultContext()->GetIdentity(), myself->GetIdentity());
|
||||
handlesys->FreeHandle(rHndl, &sec);
|
||||
rHndl = BAD_HANDLE;
|
||||
}
|
||||
|
||||
if (paramStruct)
|
||||
delete paramStruct;
|
||||
|
||||
// Don't call more callbacks. They will probably fail too.
|
||||
break;
|
||||
}
|
||||
pCallback->PushCell(pHndl);
|
||||
}
|
||||
|
||||
// Run the plugin callback.
|
||||
cell_t result = (cell_t)MRES_Ignored;
|
||||
pCallback->Execute(&result);
|
||||
|
||||
switch ((MRESReturn)result)
|
||||
{
|
||||
case MRES_Handled:
|
||||
tempRet = ReturnAction_Handled;
|
||||
break;
|
||||
case MRES_ChangedHandled:
|
||||
tempRet = ReturnAction_Handled;
|
||||
// Copy the changed parameter values from the plugin's parameter structure back into the actual detour arguments.
|
||||
pWrapper->UpdateParamsFromStruct(paramStruct);
|
||||
break;
|
||||
case MRES_ChangedOverride:
|
||||
case MRES_Override:
|
||||
case MRES_Supercede:
|
||||
// See if this function returns something we should override.
|
||||
if (pWrapper->returnType != ReturnType_Void)
|
||||
{
|
||||
// Make sure the plugin provided a new return value. Could be an oversight if MRES_ChangedOverride
|
||||
// is called without the return value actually being changed.
|
||||
if (!returnStruct->isChanged)
|
||||
{
|
||||
//Throw an error if no override was set
|
||||
tempRet = ReturnAction_Ignored;
|
||||
pCallback->GetParentRuntime()->GetDefaultContext()->BlamePluginError(pCallback, "Tried to override return value without return value being set");
|
||||
break;
|
||||
}
|
||||
|
||||
if (pWrapper->returnType == ReturnType_String || pWrapper->returnType == ReturnType_Int || pWrapper->returnType == ReturnType_Bool)
|
||||
{
|
||||
tempRetBuf = *(uint8_t **)returnStruct->newResult;
|
||||
}
|
||||
else if (pWrapper->returnType == ReturnType_Float)
|
||||
{
|
||||
*(float *)&tempRetBuf = *(float *)returnStruct->newResult;
|
||||
}
|
||||
else
|
||||
{
|
||||
tempRetBuf = (uint8_t *)returnStruct->newResult;
|
||||
}
|
||||
}
|
||||
|
||||
// Store if the plugin wants the original function to be called.
|
||||
if (result == MRES_Supercede)
|
||||
tempRet = ReturnAction_Supercede;
|
||||
else
|
||||
tempRet = ReturnAction_Override;
|
||||
|
||||
// Copy the changed parameter values from the plugin's parameter structure back into the actual detour arguments.
|
||||
if (result == MRES_ChangedOverride)
|
||||
pWrapper->UpdateParamsFromStruct(paramStruct);
|
||||
break;
|
||||
default:
|
||||
tempRet = ReturnAction_Ignored;
|
||||
break;
|
||||
}
|
||||
|
||||
// Prioritize the actions.
|
||||
if (finalRet <= tempRet)
|
||||
{
|
||||
// Copy the action and return value.
|
||||
finalRet = tempRet;
|
||||
memcpy(finalRetBuf.get(), &tempRetBuf, pDetour->m_pCallingConvention->m_returnType.size);
|
||||
}
|
||||
|
||||
// Free the handles again.
|
||||
HandleSecurity sec(pCallback->GetParentRuntime()->GetDefaultContext()->GetIdentity(), myself->GetIdentity());
|
||||
if (returnStruct)
|
||||
{
|
||||
handlesys->FreeHandle(rHndl, &sec);
|
||||
}
|
||||
if (paramStruct)
|
||||
{
|
||||
handlesys->FreeHandle(pHndl, &sec);
|
||||
}
|
||||
}
|
||||
|
||||
// If we want to use our own return value, write it back.
|
||||
if (finalRet >= ReturnAction_Override)
|
||||
{
|
||||
void* pPtr = pDetour->m_pCallingConvention->GetReturnPtr(pDetour->m_pRegisters);
|
||||
memcpy(pPtr, finalRetBuf.get(), pDetour->m_pCallingConvention->m_returnType.size);
|
||||
pDetour->m_pCallingConvention->ReturnPtrChanged(pDetour->m_pRegisters, pPtr);
|
||||
}
|
||||
|
||||
return finalRet;
|
||||
}
|
||||
|
||||
CDynamicHooksSourcePawn::CDynamicHooksSourcePawn(HookSetup *setup, CHook *pDetour, IPluginFunction *pCallback, bool post)
|
||||
{
|
||||
this->params = setup->params;
|
||||
this->offset = -1;
|
||||
this->returnFlag = setup->returnFlag;
|
||||
this->returnType = setup->returnType;
|
||||
this->post = post;
|
||||
this->plugin_callback = pCallback;
|
||||
this->entity = -1;
|
||||
this->thisType = setup->thisType;
|
||||
this->hookType = setup->hookType;
|
||||
this->m_pDetour = pDetour;
|
||||
this->callConv = setup->callConv;
|
||||
}
|
||||
|
||||
HookReturnStruct *CDynamicHooksSourcePawn::GetReturnStruct()
|
||||
{
|
||||
// Create buffers to store the return value of the function.
|
||||
HookReturnStruct *res = new HookReturnStruct();
|
||||
res->isChanged = false;
|
||||
res->type = this->returnType;
|
||||
res->orgResult = NULL;
|
||||
res->newResult = NULL;
|
||||
|
||||
// Copy the actual function's return value too for post hooks.
|
||||
if (this->post)
|
||||
{
|
||||
switch (this->returnType)
|
||||
{
|
||||
case ReturnType_String:
|
||||
res->orgResult = malloc(sizeof(string_t));
|
||||
res->newResult = malloc(sizeof(string_t));
|
||||
*(string_t *)res->orgResult = m_pDetour->GetReturnValue<string_t>();
|
||||
break;
|
||||
case ReturnType_Int:
|
||||
res->orgResult = malloc(sizeof(int));
|
||||
res->newResult = malloc(sizeof(int));
|
||||
*(int *)res->orgResult = m_pDetour->GetReturnValue<int>();
|
||||
break;
|
||||
case ReturnType_Bool:
|
||||
res->orgResult = malloc(sizeof(bool));
|
||||
res->newResult = malloc(sizeof(bool));
|
||||
*(bool *)res->orgResult = m_pDetour->GetReturnValue<bool>();
|
||||
break;
|
||||
case ReturnType_Float:
|
||||
res->orgResult = malloc(sizeof(float));
|
||||
res->newResult = malloc(sizeof(float));
|
||||
*(float *)res->orgResult = m_pDetour->GetReturnValue<float>();
|
||||
break;
|
||||
case ReturnType_Vector:
|
||||
{
|
||||
res->orgResult = malloc(sizeof(SDKVector));
|
||||
res->newResult = malloc(sizeof(SDKVector));
|
||||
SDKVector vec = m_pDetour->GetReturnValue<SDKVector>();
|
||||
*(SDKVector *)res->orgResult = vec;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
res->orgResult = m_pDetour->GetReturnValue<void *>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Pre hooks don't have access to the return value yet - duh.
|
||||
// Just create the buffers for overridden values.
|
||||
// TODO: Strip orgResult malloc.
|
||||
else
|
||||
{
|
||||
switch (this->returnType)
|
||||
{
|
||||
case ReturnType_String:
|
||||
res->orgResult = malloc(sizeof(string_t));
|
||||
res->newResult = malloc(sizeof(string_t));
|
||||
*(string_t *)res->orgResult = NULL_STRING;
|
||||
break;
|
||||
case ReturnType_Vector:
|
||||
res->orgResult = malloc(sizeof(SDKVector));
|
||||
res->newResult = malloc(sizeof(SDKVector));
|
||||
*(SDKVector *)res->orgResult = SDKVector();
|
||||
break;
|
||||
case ReturnType_Int:
|
||||
res->orgResult = malloc(sizeof(int));
|
||||
res->newResult = malloc(sizeof(int));
|
||||
*(int *)res->orgResult = 0;
|
||||
break;
|
||||
case ReturnType_Bool:
|
||||
res->orgResult = malloc(sizeof(bool));
|
||||
res->newResult = malloc(sizeof(bool));
|
||||
*(bool *)res->orgResult = false;
|
||||
break;
|
||||
case ReturnType_Float:
|
||||
res->orgResult = malloc(sizeof(float));
|
||||
res->newResult = malloc(sizeof(float));
|
||||
*(float *)res->orgResult = 0.0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
HookParamsStruct *CDynamicHooksSourcePawn::GetParamStruct()
|
||||
{
|
||||
// Save argument values of detoured function.
|
||||
HookParamsStruct *params = new HookParamsStruct();
|
||||
params->dg = this;
|
||||
|
||||
ICallingConvention* callingConvention = m_pDetour->m_pCallingConvention;
|
||||
size_t stackSize = callingConvention->GetArgStackSize();
|
||||
size_t paramsSize = stackSize + callingConvention->GetArgRegisterSize();
|
||||
std::vector<DataTypeSized_t> &argTypes = callingConvention->m_vecArgTypes;
|
||||
size_t numArgs = argTypes.size();
|
||||
|
||||
// Create space for original parameters and changes plugins might do.
|
||||
params->orgParams = (void **)malloc(paramsSize);
|
||||
params->newParams = (void **)malloc(paramsSize);
|
||||
params->isChanged = (bool *)malloc(numArgs * sizeof(bool));
|
||||
|
||||
// Save old stack parameters.
|
||||
if (stackSize > 0)
|
||||
{
|
||||
void *pArgPtr = m_pDetour->m_pCallingConvention->GetStackArgumentPtr(m_pDetour->m_pRegisters);
|
||||
memcpy(params->orgParams, pArgPtr, stackSize);
|
||||
}
|
||||
|
||||
memset(params->newParams, 0, paramsSize);
|
||||
memset(params->isChanged, false, numArgs * sizeof(bool));
|
||||
|
||||
size_t firstArg = 0;
|
||||
// TODO: Support custom register for this ptr.
|
||||
if (callConv == CallConv_THISCALL)
|
||||
firstArg = 1;
|
||||
|
||||
// Save the old parameters passed in a register.
|
||||
size_t offset = stackSize;
|
||||
for (size_t i = 0; i < numArgs; i++)
|
||||
{
|
||||
// We already saved the stack arguments.
|
||||
if (argTypes[i].custom_register == None)
|
||||
continue;
|
||||
|
||||
size_t size = argTypes[i].size;
|
||||
// Register argument values are saved after all stack arguments in this buffer.
|
||||
void *paramAddr = (void *)((intptr_t)params->orgParams + offset);
|
||||
void *regAddr = callingConvention->GetArgumentPtr(i + firstArg, m_pDetour->m_pRegisters);
|
||||
memcpy(paramAddr, regAddr, size);
|
||||
offset += size;
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
void CDynamicHooksSourcePawn::UpdateParamsFromStruct(HookParamsStruct *params)
|
||||
{
|
||||
// Function had no params to update now.
|
||||
if (!params)
|
||||
return;
|
||||
|
||||
ICallingConvention* callingConvention = m_pDetour->m_pCallingConvention;
|
||||
size_t stackSize = callingConvention->GetArgStackSize();
|
||||
std::vector<DataTypeSized_t> &argTypes = callingConvention->m_vecArgTypes;
|
||||
size_t numArgs = argTypes.size();
|
||||
|
||||
size_t firstArg = 0;
|
||||
// TODO: Support custom register for this ptr.
|
||||
if (callConv == CallConv_THISCALL)
|
||||
firstArg = 1;
|
||||
|
||||
size_t stackOffset = 0;
|
||||
// Values of arguments stored in registers are saved after the stack arguments.
|
||||
size_t registerOffset = stackSize;
|
||||
size_t offset;
|
||||
for (size_t i = 0; i < numArgs; i++)
|
||||
{
|
||||
size_t size = argTypes[i].size;
|
||||
// Only have to copy something if the plugin changed this parameter.
|
||||
if (params->isChanged[i])
|
||||
{
|
||||
// Get the offset of this argument in the linear buffer. Register argument values are placed after all stack arguments.
|
||||
offset = argTypes[i].custom_register == None ? stackOffset : registerOffset;
|
||||
|
||||
void *paramAddr = (void *)((intptr_t)params->newParams + offset);
|
||||
void *stackAddr = callingConvention->GetArgumentPtr(i + firstArg, m_pDetour->m_pRegisters);
|
||||
memcpy(stackAddr, paramAddr, size);
|
||||
}
|
||||
|
||||
// Keep track of the seperate stack and register arguments.
|
||||
if (argTypes[i].custom_register == None)
|
||||
stackOffset += size;
|
||||
else
|
||||
registerOffset += size;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod Dynamic Hooks Extension
|
||||
* Copyright (C) 2012-2021 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$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_DYNHOOKS_SP_H_
|
||||
#define _INCLUDE_DYNHOOKS_SP_H_
|
||||
|
||||
#include "manager.h"
|
||||
#include "vhook.h"
|
||||
#include <am-hashmap.h>
|
||||
|
||||
class CDynamicHooksSourcePawn;
|
||||
typedef ke::HashMap<IPluginFunction *, CDynamicHooksSourcePawn *, ke::PointerPolicy<IPluginFunction>> CallbackMap;
|
||||
typedef std::vector<CDynamicHooksSourcePawn *> PluginCallbackList;
|
||||
typedef ke::HashMap<CHook *, PluginCallbackList *, ke::PointerPolicy<CHook>> DetourMap;
|
||||
|
||||
//extern ke::Vector<CHook *> g_pDetours;
|
||||
// Keep a list of plugin callback -> Hook wrapper for easily removing plugin hooks
|
||||
//extern CallbackMap g_pPluginPreDetours;
|
||||
//extern CallbackMap g_pPluginPostDetours;
|
||||
// Keep a list of hook -> callbacks for calling in the detour handler
|
||||
extern DetourMap g_pPreDetours;
|
||||
extern DetourMap g_pPostDetours;
|
||||
|
||||
class CDynamicHooksSourcePawn : public DHooksInfo {
|
||||
public:
|
||||
CDynamicHooksSourcePawn(HookSetup *setup, CHook *pDetour, IPluginFunction *pCallback, bool post);
|
||||
|
||||
HookReturnStruct *GetReturnStruct();
|
||||
HookParamsStruct *GetParamStruct();
|
||||
void UpdateParamsFromStruct(HookParamsStruct *params);
|
||||
|
||||
public:
|
||||
CHook *m_pDetour;
|
||||
CallingConvention callConv;
|
||||
};
|
||||
|
||||
ICallingConvention *ConstructCallingConvention(HookSetup *setup);
|
||||
bool UpdateRegisterArgumentSizes(CHook* pDetour, HookSetup *setup);
|
||||
ReturnAction_t HandleDetour(HookType_t hookType, CHook* pDetour);
|
||||
bool AddDetourPluginHook(HookType_t hookType, CHook *pDetour, HookSetup *setup, IPluginFunction *pCallback);
|
||||
bool RemoveDetourPluginHook(HookType_t hookType, CHook *pDetour, IPluginFunction *pCallback);
|
||||
void RemoveAllCallbacksForContext(IPluginContext *pContext);
|
||||
void CleanupDetours();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,196 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod Dynamic Hooks Extension
|
||||
* Copyright (C) 2012-2021 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$
|
||||
*/
|
||||
|
||||
#include "extension.h"
|
||||
#include "listeners.h"
|
||||
#include "dynhooks_sourcepawn.h"
|
||||
#include "signatures.h"
|
||||
|
||||
DHooks g_DHooksIface; /**< Global singleton for extension's main interface */
|
||||
SMEXT_LINK(&g_DHooksIface);
|
||||
|
||||
IBinTools *g_pBinTools;
|
||||
ISDKHooks *g_pSDKHooks;
|
||||
ISDKTools *g_pSDKTools;
|
||||
DHooksEntityListener *g_pEntityListener = NULL;
|
||||
|
||||
HandleType_t g_HookSetupHandle = 0;
|
||||
HandleType_t g_HookParamsHandle = 0;
|
||||
HandleType_t g_HookReturnHandle = 0;
|
||||
|
||||
std::thread::id g_MainThreadId;
|
||||
|
||||
bool DHooks::SDK_OnLoad(char *error, size_t maxlength, bool late)
|
||||
{
|
||||
HandleError err;
|
||||
g_HookSetupHandle = handlesys->CreateType("HookSetup", this, 0, NULL, NULL, myself->GetIdentity(), &err);
|
||||
if(g_HookSetupHandle == 0)
|
||||
{
|
||||
snprintf(error, maxlength, "Could not create hook setup handle type (err: %d)", err);
|
||||
return false;
|
||||
}
|
||||
g_HookParamsHandle = handlesys->CreateType("HookParams", this, 0, NULL, NULL, myself->GetIdentity(), &err);
|
||||
if(g_HookParamsHandle == 0)
|
||||
{
|
||||
snprintf(error, maxlength, "Could not create hook params handle type (err: %d)", err);
|
||||
return false;
|
||||
}
|
||||
g_HookReturnHandle = handlesys->CreateType("HookReturn", this, 0, NULL, NULL, myself->GetIdentity(), &err);
|
||||
if(g_HookReturnHandle == 0)
|
||||
{
|
||||
snprintf(error, maxlength, "Could not create hook return handle type (err: %d)", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!g_pPreDetours.init())
|
||||
{
|
||||
snprintf(error, maxlength, "Could not initialize pre hook detours hashmap.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!g_pPostDetours.init())
|
||||
{
|
||||
snprintf(error, maxlength, "Could not initialize post hook detours hashmap.");
|
||||
return false;
|
||||
}
|
||||
|
||||
sharesys->AddDependency(myself, "bintools.ext", true, true);
|
||||
sharesys->AddDependency(myself, "sdktools.ext", true, true);
|
||||
sharesys->AddDependency(myself, "sdkhooks.ext", true, true);
|
||||
|
||||
sharesys->RegisterLibrary(myself, "dhooks");
|
||||
plsys->AddPluginsListener(this);
|
||||
sharesys->AddNatives(myself, g_Natives);
|
||||
|
||||
g_pEntityListener = new DHooksEntityListener();
|
||||
g_pSignatures = new SignatureGameConfig();
|
||||
g_MainThreadId = std::this_thread::get_id();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DHooks::OnHandleDestroy(HandleType_t type, void *object)
|
||||
{
|
||||
if(type == g_HookSetupHandle)
|
||||
{
|
||||
delete (HookSetup *)object;
|
||||
}
|
||||
else if(type == g_HookParamsHandle)
|
||||
{
|
||||
delete (HookParamsStruct *)object;
|
||||
}
|
||||
else if(type == g_HookReturnHandle)
|
||||
{
|
||||
delete (HookReturnStruct *)object;
|
||||
}
|
||||
}
|
||||
|
||||
void DHooks::SDK_OnAllLoaded()
|
||||
{
|
||||
SM_GET_LATE_IFACE(SDKTOOLS, g_pSDKTools);
|
||||
SM_GET_LATE_IFACE(BINTOOLS, g_pBinTools);
|
||||
SM_GET_LATE_IFACE(SDKHOOKS, g_pSDKHooks);
|
||||
|
||||
g_pSDKHooks->AddEntityListener(g_pEntityListener);
|
||||
gameconfs->AddUserConfigHook("Functions", g_pSignatures);
|
||||
}
|
||||
|
||||
void DHooks::SDK_OnUnload()
|
||||
{
|
||||
CleanupHooks();
|
||||
CleanupDetours();
|
||||
if(g_pEntityListener)
|
||||
{
|
||||
g_pEntityListener->CleanupListeners();
|
||||
g_pEntityListener->CleanupRemoveList();
|
||||
g_pSDKHooks->RemoveEntityListener(g_pEntityListener);
|
||||
delete g_pEntityListener;
|
||||
}
|
||||
plsys->RemovePluginsListener(this);
|
||||
|
||||
handlesys->RemoveType(g_HookSetupHandle, myself->GetIdentity());
|
||||
handlesys->RemoveType(g_HookParamsHandle, myself->GetIdentity());
|
||||
handlesys->RemoveType(g_HookReturnHandle, myself->GetIdentity());
|
||||
|
||||
gameconfs->RemoveUserConfigHook("Functions", g_pSignatures);
|
||||
}
|
||||
|
||||
bool DHooks::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late)
|
||||
{
|
||||
if(!SetupHookManager(ismm))
|
||||
{
|
||||
snprintf(error, maxlength, "Failed to get IHookManagerAutoGen iface");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DHooks::OnPluginUnloaded(IPlugin *plugin)
|
||||
{
|
||||
CleanupHooks(plugin->GetBaseContext());
|
||||
RemoveAllCallbacksForContext(plugin->GetBaseContext());
|
||||
if(g_pEntityListener)
|
||||
{
|
||||
g_pEntityListener->CleanupListeners(plugin->GetBaseContext());
|
||||
}
|
||||
}
|
||||
// The next 3 functions handle cleanup if our interfaces are going to be unloaded
|
||||
bool DHooks::QueryRunning(char *error, size_t maxlength)
|
||||
{
|
||||
SM_CHECK_IFACE(SDKTOOLS, g_pSDKTools);
|
||||
SM_CHECK_IFACE(BINTOOLS, g_pBinTools);
|
||||
SM_CHECK_IFACE(SDKHOOKS, g_pSDKHooks);
|
||||
return true;
|
||||
}
|
||||
void DHooks::NotifyInterfaceDrop(SMInterface *pInterface)
|
||||
{
|
||||
if(strcmp(pInterface->GetInterfaceName(), SMINTERFACE_SDKHOOKS_NAME) == 0)
|
||||
{
|
||||
if(g_pEntityListener)
|
||||
{
|
||||
// If this fails, remove this line and just delete the ent listener instead
|
||||
g_pSDKHooks->RemoveEntityListener(g_pEntityListener);
|
||||
|
||||
g_pEntityListener->CleanupListeners();
|
||||
delete g_pEntityListener;
|
||||
g_pEntityListener = NULL;
|
||||
}
|
||||
g_pSDKHooks = NULL;
|
||||
}
|
||||
else if(strcmp(pInterface->GetInterfaceName(), SMINTERFACE_BINTOOLS_NAME) == 0)
|
||||
{
|
||||
g_pBinTools = NULL;
|
||||
}
|
||||
else if(strcmp(pInterface->GetInterfaceName(), SMINTERFACE_SDKTOOLS_NAME) == 0)
|
||||
{
|
||||
g_pSDKTools = NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod Dynamic Hooks Extension
|
||||
* Copyright (C) 2012-2021 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$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
|
||||
#define _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
|
||||
|
||||
/**
|
||||
* @file extension.h
|
||||
* @brief Sample extension code header.
|
||||
*/
|
||||
|
||||
#include "smsdk_ext.h"
|
||||
|
||||
#include <ISDKHooks.h>
|
||||
#include <IBinTools.h>
|
||||
#include <ISDKTools.h>
|
||||
#include "sdk-hacks.h"
|
||||
#include <thread>
|
||||
|
||||
/**
|
||||
* @brief Sample implementation of the SDK Extension.
|
||||
* Note: Uncomment one of the pre-defined virtual functions in order to use it.
|
||||
*/
|
||||
|
||||
class DHooks : public SDKExtension, public ISMEntityListener, public IPluginsListener, public IHandleTypeDispatch
|
||||
{
|
||||
public:
|
||||
virtual void OnHandleDestroy(HandleType_t type, void *object);
|
||||
public: //IPluginsListener
|
||||
virtual void OnPluginUnloaded(IPlugin *plugin);
|
||||
public:
|
||||
/**
|
||||
* @brief This is called after the initial loading sequence has been processed.
|
||||
*
|
||||
* @param error Error message buffer.
|
||||
* @param maxlength Size of error message buffer.
|
||||
* @param late Whether or not the module was loaded after map load.
|
||||
* @return True to succeed loading, false to fail.
|
||||
*/
|
||||
virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late);
|
||||
|
||||
/**
|
||||
* @brief This is called right before the extension is unloaded.
|
||||
*/
|
||||
virtual void SDK_OnUnload();
|
||||
|
||||
/**
|
||||
* @brief This is called once all known extensions have been loaded.
|
||||
* Note: It is is a good idea to add natives here, if any are provided.
|
||||
*/
|
||||
virtual void SDK_OnAllLoaded();
|
||||
|
||||
/**
|
||||
* @brief Called when the pause state is changed.
|
||||
*/
|
||||
//virtual void SDK_OnPauseChange(bool paused);
|
||||
|
||||
/**
|
||||
* @brief this is called when Core wants to know if your extension is working.
|
||||
*
|
||||
* @param error Error message buffer.
|
||||
* @param maxlength Size of error message buffer.
|
||||
* @return True if working, false otherwise.
|
||||
*/
|
||||
virtual bool QueryRunning(char *error, size_t maxlength);
|
||||
//virtual bool QueryInterfaceDrop(SMInterface *pInterface);
|
||||
virtual void NotifyInterfaceDrop(SMInterface *pInterface);
|
||||
virtual void OnCoreMapEnd();
|
||||
public:
|
||||
#if defined SMEXT_CONF_METAMOD
|
||||
/**
|
||||
* @brief Called when Metamod is attached, before the extension version is called.
|
||||
*
|
||||
* @param error Error buffer.
|
||||
* @param maxlength Maximum size of error buffer.
|
||||
* @param late Whether or not Metamod considers this a late load.
|
||||
* @return True to succeed, false to fail.
|
||||
*/
|
||||
virtual bool SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late);
|
||||
|
||||
/**
|
||||
* @brief Called when Metamod is detaching, after the extension version is called.
|
||||
* NOTE: By default this is blocked unless sent from SourceMod.
|
||||
*
|
||||
* @param error Error buffer.
|
||||
* @param maxlength Maximum size of error buffer.
|
||||
* @return True to succeed, false to fail.
|
||||
*/
|
||||
//virtual bool SDK_OnMetamodUnload(char *error, size_t maxlength);
|
||||
|
||||
/**
|
||||
* @brief Called when Metamod's pause state is changing.
|
||||
* NOTE: By default this is blocked unless sent from SourceMod.
|
||||
*
|
||||
* @param paused Pause state being set.
|
||||
* @param error Error buffer.
|
||||
* @param maxlength Maximum size of error buffer.
|
||||
* @return True to succeed, false to fail.
|
||||
*/
|
||||
//virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength);
|
||||
#endif
|
||||
};
|
||||
extern SourceHook::IHookManagerAutoGen *g_pHookManager;
|
||||
extern sp_nativeinfo_t g_Natives[];
|
||||
extern std::thread::id g_MainThreadId;
|
||||
#endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
|
||||
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod Dynamic Hooks Extension
|
||||
* Copyright (C) 2012-2021 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$
|
||||
*/
|
||||
|
||||
#include "listeners.h"
|
||||
#include "vhook.h"
|
||||
|
||||
using namespace SourceHook;
|
||||
|
||||
std::vector<EntityListener> g_EntityListeners;
|
||||
std::vector<DHooksManager *>g_pRemoveList;
|
||||
|
||||
void FrameCleanupHooks(void *data)
|
||||
{
|
||||
for (int i = g_pRemoveList.size() - 1; i >= 0; i--)
|
||||
{
|
||||
DHooksManager *manager = g_pRemoveList.at(i);
|
||||
delete manager;
|
||||
g_pRemoveList.erase(g_pRemoveList.begin() + i);
|
||||
}
|
||||
}
|
||||
|
||||
void DHooks::OnCoreMapEnd()
|
||||
{
|
||||
for(int i = g_pHooks.size() -1; i >= 0; i--)
|
||||
{
|
||||
DHooksManager *manager = g_pHooks.at(i);
|
||||
if(manager->callback->hookType == HookType_GameRules)
|
||||
{
|
||||
delete manager;
|
||||
g_pHooks.erase(g_pHooks.begin() + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DHooksEntityListener::CleanupListeners(IPluginContext *pContext)
|
||||
{
|
||||
for(int i = g_EntityListeners.size() -1; i >= 0; i--)
|
||||
{
|
||||
if(pContext == NULL || pContext == g_EntityListeners.at(i).callback->GetParentRuntime()->GetDefaultContext())
|
||||
{
|
||||
g_EntityListeners.erase(g_EntityListeners.begin() + i);
|
||||
}
|
||||
}
|
||||
for (int i = g_pRemoveList.size() -1; i >= 0; i--)
|
||||
{
|
||||
DHooksManager *manager = g_pRemoveList.at(i);
|
||||
IPluginFunction *cb = manager->callback->plugin_callback;
|
||||
if (pContext == NULL || (cb && pContext == cb->GetParentRuntime()->GetDefaultContext()))
|
||||
{
|
||||
manager->callback->plugin_callback = nullptr;
|
||||
manager->remove_callback = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DHooksEntityListener::CleanupRemoveList()
|
||||
{
|
||||
for (int i = g_pRemoveList.size() - 1; i >= 0; i--)
|
||||
{
|
||||
DHooksManager *manager = g_pRemoveList.at(i);
|
||||
delete manager;
|
||||
}
|
||||
g_pRemoveList.clear();
|
||||
}
|
||||
|
||||
void DHooksEntityListener::OnEntityCreated(CBaseEntity *pEntity, const char *classname)
|
||||
{
|
||||
int entity = gamehelpers->EntityToBCompatRef(pEntity);
|
||||
|
||||
for(int i = g_EntityListeners.size() -1; i >= 0; i--)
|
||||
{
|
||||
EntityListener listerner = g_EntityListeners.at(i);
|
||||
if(listerner.type == ListenType_Created)
|
||||
{
|
||||
IPluginFunction *callback = listerner.callback;
|
||||
callback->PushCell(entity);
|
||||
callback->PushString(classname);
|
||||
callback->Execute(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DHooksEntityListener::OnEntityDestroyed(CBaseEntity *pEntity)
|
||||
{
|
||||
int entity = gamehelpers->EntityToBCompatRef(pEntity);
|
||||
|
||||
for(int i = g_EntityListeners.size() -1; i >= 0; i--)
|
||||
{
|
||||
EntityListener listerner = g_EntityListeners.at(i);
|
||||
if(listerner.type == ListenType_Deleted)
|
||||
{
|
||||
IPluginFunction *callback = listerner.callback;
|
||||
callback->PushCell(gamehelpers->EntityToBCompatRef(pEntity));
|
||||
callback->Execute(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = g_pHooks.size() -1; i >= 0; i--)
|
||||
{
|
||||
DHooksManager *manager = g_pHooks.at(i);
|
||||
if(manager->callback->hookType == HookType_Entity && manager->callback->entity == entity)
|
||||
{
|
||||
if(g_pRemoveList.size() == 0)
|
||||
{
|
||||
smutils->AddFrameAction(&FrameCleanupHooks, NULL);
|
||||
}
|
||||
|
||||
g_pRemoveList.push_back(manager);
|
||||
g_pHooks.erase(g_pHooks.begin() + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool DHooksEntityListener::AddPluginEntityListener(ListenType type, IPluginFunction *callback)
|
||||
{
|
||||
for(int i = g_EntityListeners.size() -1; i >= 0; i--)
|
||||
{
|
||||
EntityListener listerner = g_EntityListeners.at(i);
|
||||
if(listerner.callback == callback && listerner.type == type)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
EntityListener listener;
|
||||
listener.callback = callback;
|
||||
listener.type = type;
|
||||
g_EntityListeners.push_back(listener);
|
||||
return true;
|
||||
}
|
||||
bool DHooksEntityListener::RemovePluginEntityListener(ListenType type, IPluginFunction *callback)
|
||||
{
|
||||
for(int i = g_EntityListeners.size() -1; i >= 0; i--)
|
||||
{
|
||||
EntityListener listerner = g_EntityListeners.at(i);
|
||||
if(listerner.callback == callback && listerner.type == type)
|
||||
{
|
||||
g_EntityListeners.erase(g_EntityListeners.begin() + i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
/**
|
||||
* vim: set ts=4 sw=4 tw=99 noet :
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2020 AlliedModders LLC. All rights reserved.
|
||||
* SourceMod Dynamic Hooks Extension
|
||||
* Copyright (C) 2012-2021 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
|
||||
@@ -28,31 +28,36 @@
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <google/protobuf/message.h>
|
||||
#ifndef _INCLUDE_LISTENERS_H_
|
||||
#define _INCLUDE_LISTENERS_H_
|
||||
|
||||
class IProtobufProxy
|
||||
#include "extension.h"
|
||||
#include "vhook.h"
|
||||
|
||||
enum ListenType
|
||||
{
|
||||
public:
|
||||
// Serialize the given message into a buffer. The buffer and its length are placed in |out|.
|
||||
// The buffer must be freed with FreeBuffer.
|
||||
virtual bool Serialize(const google::protobuf::Message* message, void** out, size_t* len) = 0;
|
||||
virtual void FreeBuffer(void* data) = 0;
|
||||
|
||||
// Deserialize the given buffer into a message.
|
||||
virtual bool Deserialize(const void* buffer, size_t len, google::protobuf::Message* message) = 0;
|
||||
|
||||
// Allocate/free a prototype.
|
||||
virtual google::protobuf::Message* NewPrototype(int msg_type) = 0;
|
||||
virtual void FreeMessage(google::protobuf::Message* message) = 0;
|
||||
|
||||
bool Serialize(const google::protobuf::Message& message, void** out, size_t* len) {
|
||||
return Serialize(&message, out, len);
|
||||
}
|
||||
bool Deserialize(const void* buffer, size_t len, google::protobuf::Message& message) {
|
||||
return Deserialize(buffer, len, &message);
|
||||
}
|
||||
ListenType_Created,
|
||||
ListenType_Deleted
|
||||
};
|
||||
|
||||
typedef IProtobufProxy*(*GetProtobufProxyFn)();
|
||||
class DHooksEntityListener : public ISMEntityListener
|
||||
{
|
||||
public:
|
||||
virtual void OnEntityCreated(CBaseEntity *pEntity, const char *classname);
|
||||
virtual void OnEntityDestroyed(CBaseEntity *pEntity);
|
||||
void CleanupListeners(IPluginContext *func = NULL);
|
||||
void CleanupRemoveList();
|
||||
bool AddPluginEntityListener(ListenType type, IPluginFunction *callback);
|
||||
bool RemovePluginEntityListener(ListenType type, IPluginFunction *callback);
|
||||
};
|
||||
|
||||
|
||||
struct EntityListener
|
||||
{
|
||||
ListenType type;
|
||||
IPluginFunction *callback;
|
||||
};
|
||||
|
||||
extern std::vector<DHooksManager *> g_pHooks;
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,14 @@
|
||||
/**
|
||||
* vim: set ts=4 sw=4 tw=99 noet :
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2020 AlliedModders LLC. All rights reserved.
|
||||
* SourceMod Dynamic Hooks Extension
|
||||
* Copyright (C) 2012-2021 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
|
||||
@@ -28,8 +28,18 @@
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
#include "pb_handle.h"
|
||||
|
||||
#if defined PROTOBUF_PROXY_ENABLE
|
||||
IProtobufProxy* gProtobufProxy = nullptr;
|
||||
#ifndef _INCLUDE_NATIVES_H_
|
||||
#define _INCLUDE_NATIVES_H_
|
||||
|
||||
#include "extension.h"
|
||||
#include "vhook.h"
|
||||
#include "listeners.h"
|
||||
|
||||
extern DHooksEntityListener *g_pEntityListener;
|
||||
extern ISDKTools *g_pSDKTools;
|
||||
extern HandleType_t g_HookSetupHandle;
|
||||
extern HandleType_t g_HookParamsHandle;
|
||||
extern HandleType_t g_HookReturnHandle;
|
||||
extern std::vector<DHooksManager *> g_pHooks;
|
||||
#endif
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod Dynamic Hooks Extension
|
||||
* Copyright (C) 2012-2021 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$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SDK_HACKS_H_
|
||||
#define _INCLUDE_SDK_HACKS_H_
|
||||
|
||||
class SDKVector
|
||||
{
|
||||
public:
|
||||
SDKVector(float x1, float y1, float z1)
|
||||
{
|
||||
this->x = x1;
|
||||
this->y = y1;
|
||||
this->z = z1;
|
||||
}
|
||||
SDKVector(void)
|
||||
{
|
||||
this->x = 0.0;
|
||||
this->y = 0.0;
|
||||
this->z = 0.0;
|
||||
}
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
|
||||
struct string_t
|
||||
{
|
||||
public:
|
||||
bool operator!() const { return ( pszValue == NULL ); }
|
||||
bool operator==( const string_t &rhs ) const { return ( pszValue == rhs.pszValue ); }
|
||||
bool operator!=( const string_t &rhs ) const { return ( pszValue != rhs.pszValue ); }
|
||||
bool operator<( const string_t &rhs ) const { return ((void *)pszValue < (void *)rhs.pszValue ); }
|
||||
|
||||
const char *ToCStr() const { return ( pszValue ) ? pszValue : ""; }
|
||||
|
||||
protected:
|
||||
const char *pszValue;
|
||||
};
|
||||
|
||||
struct castable_string_t : public string_t // string_t is used in unions, hence, no constructor allowed
|
||||
{
|
||||
castable_string_t() { pszValue = NULL; }
|
||||
castable_string_t( const char *pszFrom ) { pszValue = (pszFrom && *pszFrom) ? pszFrom : 0; }
|
||||
};
|
||||
|
||||
#define NULL_STRING castable_string_t()
|
||||
#define STRING( string_t_obj ) (string_t_obj).ToCStr()
|
||||
#define MAKE_STRING( c_str ) castable_string_t( c_str )
|
||||
|
||||
#define FL_EDICT_FREE (1<<1)
|
||||
|
||||
struct edict_t
|
||||
{
|
||||
public:
|
||||
bool IsFree()
|
||||
{
|
||||
return (m_fStateFlags & FL_EDICT_FREE) != 0;
|
||||
}
|
||||
private:
|
||||
int m_fStateFlags;
|
||||
};
|
||||
|
||||
class CBaseHandle
|
||||
{
|
||||
/*public:
|
||||
bool IsValid() const {return m_Index != INVALID_EHANDLE_INDEX;}
|
||||
int GetEntryIndex() const
|
||||
{
|
||||
if ( !IsValid() )
|
||||
return NUM_ENT_ENTRIES-1;
|
||||
return m_Index & ENT_ENTRY_MASK;
|
||||
}*/
|
||||
private:
|
||||
unsigned long m_Index;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,619 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod Dynamic Hooks Extension
|
||||
* Copyright (C) 2012-2021 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$
|
||||
*/
|
||||
|
||||
#include <signatures.h>
|
||||
|
||||
SignatureGameConfig *g_pSignatures;
|
||||
|
||||
enum ParseState
|
||||
{
|
||||
PState_None,
|
||||
PState_Root,
|
||||
PState_Function,
|
||||
PState_Arguments,
|
||||
PState_Argument
|
||||
};
|
||||
|
||||
ParseState g_ParseState;
|
||||
unsigned int g_IgnoreLevel;
|
||||
// The parent section type of a platform specific "windows" or "linux" section.
|
||||
ParseState g_PlatformOnlyState;
|
||||
|
||||
SignatureWrapper *g_CurrentSignature;
|
||||
std::string g_CurrentFunctionName;
|
||||
ArgumentInfo g_CurrentArgumentInfo;
|
||||
|
||||
SignatureWrapper *SignatureGameConfig::GetFunctionSignature(const char *function)
|
||||
{
|
||||
auto sig = signatures_.find(function);
|
||||
if (!sig.found())
|
||||
return nullptr;
|
||||
|
||||
return sig->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Game config "Functions" section parsing.
|
||||
*/
|
||||
SMCResult SignatureGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *name)
|
||||
{
|
||||
// We're ignoring the parent section. Ignore all child sections as well.
|
||||
if (g_IgnoreLevel > 0)
|
||||
{
|
||||
g_IgnoreLevel++;
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
// Handle platform specific sections first.
|
||||
#if defined WIN32
|
||||
if (!strcmp(name, "windows"))
|
||||
#elif defined _LINUX
|
||||
if (!strcmp(name, "linux"))
|
||||
#elif defined _OSX
|
||||
if (!strcmp(name, "mac"))
|
||||
#endif
|
||||
{
|
||||
// We're already in a section for a different OS that we're ignoring. Can't have a section for our OS in here.
|
||||
if (g_IgnoreLevel > 0)
|
||||
{
|
||||
smutils->LogError(myself, "Unreachable platform specific section in \"%s\" Function: line: %i col: %i", g_CurrentFunctionName.c_str(), states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
|
||||
// We don't support nested (useless) sections of the same OS like "windows" { "windows" { "foo" "bar" } }
|
||||
if (g_PlatformOnlyState != PState_None)
|
||||
{
|
||||
smutils->LogError(myself, "Duplicate platform specific section for \"%s\". Already parsing only for that OS: line: %i col: %i", name, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
|
||||
// This is a specific block for us.
|
||||
g_PlatformOnlyState = g_ParseState;
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
#if defined WIN32
|
||||
else if (!strcmp(name, "linux") || !strcmp(name, "mac"))
|
||||
#elif defined _LINUX
|
||||
else if (!strcmp(name, "windows") || !strcmp(name, "mac"))
|
||||
#elif defined _OSX
|
||||
else if (!strcmp(name, "windows") || !strcmp(name, "linux"))
|
||||
#endif
|
||||
{
|
||||
if (g_PlatformOnlyState != PState_None)
|
||||
{
|
||||
smutils->LogError(myself, "Unreachable platform specific section in \"%s\" Function: line: %i col: %i", g_CurrentFunctionName.c_str(), states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
|
||||
// A specific block for a different platform.
|
||||
g_IgnoreLevel++;
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
switch (g_ParseState)
|
||||
{
|
||||
case PState_Root:
|
||||
{
|
||||
auto sig = signatures_.find(name);
|
||||
if (sig.found())
|
||||
g_CurrentSignature = sig->value;
|
||||
else
|
||||
g_CurrentSignature = new SignatureWrapper();
|
||||
g_CurrentFunctionName = name;
|
||||
g_ParseState = PState_Function;
|
||||
break;
|
||||
}
|
||||
|
||||
case PState_Function:
|
||||
{
|
||||
if (!strcmp(name, "arguments"))
|
||||
{
|
||||
g_ParseState = PState_Arguments;
|
||||
}
|
||||
else
|
||||
{
|
||||
smutils->LogError(myself, "Unknown subsection \"%s\" (expected \"arguments\"): line: %i col: %i", name, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PState_Arguments:
|
||||
{
|
||||
g_ParseState = PState_Argument;
|
||||
g_CurrentArgumentInfo.name = name;
|
||||
|
||||
// Reset the parameter info.
|
||||
ParamInfo info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.flags = PASSFLAG_BYVAL;
|
||||
g_CurrentArgumentInfo.info = info;
|
||||
|
||||
// See if we already have info about this argument.
|
||||
for (auto &arg : g_CurrentSignature->args) {
|
||||
if (!arg.name.compare(name)) {
|
||||
// Continue changing that argument now.
|
||||
g_CurrentArgumentInfo.info = arg.info;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
smutils->LogError(myself, "Unknown subsection \"%s\": line: %i col: %i", name, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
SMCResult SignatureGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
|
||||
{
|
||||
// We don't care for anything in this section or subsections.
|
||||
if (g_IgnoreLevel > 0)
|
||||
return SMCResult_Continue;
|
||||
|
||||
switch (g_ParseState)
|
||||
{
|
||||
case PState_Function:
|
||||
|
||||
if (!strcmp(key, "signature"))
|
||||
{
|
||||
if (g_CurrentSignature->address.length() > 0 || g_CurrentSignature->offset.length() > 0)
|
||||
{
|
||||
smutils->LogError(myself, "Cannot have \"signature\", \"address\" or \"offset\" keys at the same time in one function: line: %i col: %i", states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
g_CurrentSignature->signature = value;
|
||||
}
|
||||
else if (!strcmp(key, "address"))
|
||||
{
|
||||
if (g_CurrentSignature->signature.length() > 0 || g_CurrentSignature->offset.length() > 0)
|
||||
{
|
||||
smutils->LogError(myself, "Cannot have \"signature\", \"address\" or \"offset\" keys at the same time in one function: line: %i col: %i", states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
g_CurrentSignature->address = value;
|
||||
}
|
||||
else if (!strcmp(key, "offset"))
|
||||
{
|
||||
if (g_CurrentSignature->address.length() > 0 || g_CurrentSignature->signature.length() > 0)
|
||||
{
|
||||
smutils->LogError(myself, "Cannot have \"signature\", \"address\" or \"offset\" keys at the same time in one function: line: %i col: %i", states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
g_CurrentSignature->offset = value;
|
||||
}
|
||||
else if (!strcmp(key, "callconv"))
|
||||
{
|
||||
CallingConvention callConv;
|
||||
|
||||
if (!strcmp(value, "cdecl"))
|
||||
callConv = CallConv_CDECL;
|
||||
else if (!strcmp(value, "thiscall"))
|
||||
callConv = CallConv_THISCALL;
|
||||
else if (!strcmp(value, "stdcall"))
|
||||
callConv = CallConv_STDCALL;
|
||||
else if (!strcmp(value, "fastcall"))
|
||||
callConv = CallConv_FASTCALL;
|
||||
else
|
||||
{
|
||||
smutils->LogError(myself, "Invalid calling convention \"%s\": line: %i col: %i", value, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
|
||||
g_CurrentSignature->callConv = callConv;
|
||||
}
|
||||
else if (!strcmp(key, "hooktype"))
|
||||
{
|
||||
HookType hookType;
|
||||
|
||||
if (!strcmp(value, "entity"))
|
||||
hookType = HookType_Entity;
|
||||
else if (!strcmp(value, "gamerules"))
|
||||
hookType = HookType_GameRules;
|
||||
else if (!strcmp(value, "raw"))
|
||||
hookType = HookType_Raw;
|
||||
else
|
||||
{
|
||||
smutils->LogError(myself, "Invalid hook type \"%s\": line: %i col: %i", value, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
|
||||
g_CurrentSignature->hookType = hookType;
|
||||
}
|
||||
else if (!strcmp(key, "return"))
|
||||
{
|
||||
g_CurrentSignature->retType = GetReturnTypeFromString(value);
|
||||
|
||||
if (g_CurrentSignature->retType == ReturnType_Unknown)
|
||||
{
|
||||
smutils->LogError(myself, "Invalid return type \"%s\": line: %i col: %i", value, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
}
|
||||
else if (!strcmp(key, "this"))
|
||||
{
|
||||
if (!strcmp(value, "ignore"))
|
||||
g_CurrentSignature->thisType = ThisPointer_Ignore;
|
||||
else if (!strcmp(value, "entity"))
|
||||
g_CurrentSignature->thisType = ThisPointer_CBaseEntity;
|
||||
else if (!strcmp(value, "address"))
|
||||
g_CurrentSignature->thisType = ThisPointer_Address;
|
||||
else
|
||||
{
|
||||
smutils->LogError(myself, "Invalid this type \"%s\": line: %i col: %i", value, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
smutils->LogError(myself, "Unknown key in Functions section \"%s\": line: %i col: %i", key, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
break;
|
||||
|
||||
case PState_Argument:
|
||||
|
||||
if (!strcmp(key, "type"))
|
||||
{
|
||||
g_CurrentArgumentInfo.info.type = GetHookParamTypeFromString(value);
|
||||
if (g_CurrentArgumentInfo.info.type == HookParamType_Unknown)
|
||||
{
|
||||
smutils->LogError(myself, "Invalid argument type \"%s\" for argument \"%s\": line: %i col: %i", value, g_CurrentArgumentInfo.name.c_str(), states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
}
|
||||
else if (!strcmp(key, "size"))
|
||||
{
|
||||
g_CurrentArgumentInfo.info.size = static_cast<int>(strtol(value, NULL, 0));
|
||||
|
||||
if (g_CurrentArgumentInfo.info.size < 1)
|
||||
{
|
||||
smutils->LogError(myself, "Invalid argument size \"%s\" for argument \"%s\": line: %i col: %i", value, g_CurrentArgumentInfo.name.c_str(), states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
}
|
||||
else if (!strcmp(key, "flags"))
|
||||
{
|
||||
size_t flags = 0;
|
||||
if (strstr(value, "byval"))
|
||||
flags |= PASSFLAG_BYVAL;
|
||||
if (strstr(value, "byref"))
|
||||
flags |= PASSFLAG_BYREF;
|
||||
if (strstr(value, "odtor"))
|
||||
flags |= PASSFLAG_ODTOR;
|
||||
if (strstr(value, "octor"))
|
||||
flags |= PASSFLAG_OCTOR;
|
||||
if (strstr(value, "oassignop"))
|
||||
flags |= PASSFLAG_OASSIGNOP;
|
||||
#ifdef PASSFLAG_OCOPYCTOR
|
||||
if (strstr(value, "ocopyctor"))
|
||||
flags |= PASSFLAG_OCOPYCTOR;
|
||||
#endif
|
||||
#ifdef PASSFLAG_OUNALIGN
|
||||
if (strstr(value, "ounalign"))
|
||||
flags |= PASSFLAG_OUNALIGN;
|
||||
#endif
|
||||
|
||||
g_CurrentArgumentInfo.info.flags = flags;
|
||||
}
|
||||
else if (!strcmp(key, "register"))
|
||||
{
|
||||
g_CurrentArgumentInfo.info.custom_register = GetCustomRegisterFromString(value);
|
||||
|
||||
if (g_CurrentArgumentInfo.info.custom_register == Register_t::None)
|
||||
{
|
||||
smutils->LogError(myself, "Invalid register \"%s\": line: %i col: %i", value, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
smutils->LogError(myself, "Unknown key in Functions section \"%s\": line: %i col: %i", key, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
smutils->LogError(myself, "Unknown key in Functions section \"%s\": line: %i col: %i", key, states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
SMCResult SignatureGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
|
||||
{
|
||||
// We were ignoring this section.
|
||||
if (g_IgnoreLevel > 0)
|
||||
{
|
||||
g_IgnoreLevel--;
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
// We were in a section only for our OS.
|
||||
if (g_PlatformOnlyState == g_ParseState)
|
||||
{
|
||||
g_PlatformOnlyState = PState_None;
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
switch (g_ParseState)
|
||||
{
|
||||
case PState_Function:
|
||||
g_ParseState = PState_Root;
|
||||
|
||||
if (!g_CurrentSignature->address.length() && !g_CurrentSignature->signature.length() && !g_CurrentSignature->offset.length())
|
||||
{
|
||||
smutils->LogError(myself, "Function \"%s\" doesn't have a \"signature\", \"offset\" nor \"address\" set: line: %i col: %i", g_CurrentFunctionName.c_str(), states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
|
||||
if (!g_CurrentSignature->offset.length())
|
||||
{
|
||||
// DynamicDetours doesn't expose the passflags concept like SourceHook.
|
||||
// See if we're trying to set some invalid flags on detour arguments.
|
||||
for (auto &arg : g_CurrentSignature->args)
|
||||
{
|
||||
if ((arg.info.flags & ~PASSFLAG_BYVAL) > 0)
|
||||
{
|
||||
smutils->LogError(myself, "Function \"%s\" uses unsupported pass flags in argument \"%s\". Flags are only supported for virtual hooks: line: %i col: %i", g_CurrentFunctionName.c_str(), arg.name.c_str(), states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save this function signature in our cache.
|
||||
signatures_.insert(g_CurrentFunctionName.c_str(), g_CurrentSignature);
|
||||
g_CurrentFunctionName = "";
|
||||
g_CurrentSignature = nullptr;
|
||||
break;
|
||||
case PState_Arguments:
|
||||
g_ParseState = PState_Function;
|
||||
break;
|
||||
case PState_Argument:
|
||||
g_ParseState = PState_Arguments;
|
||||
|
||||
if (g_CurrentArgumentInfo.info.type == HookParamType_Unknown)
|
||||
{
|
||||
smutils->LogError(myself, "Missing argument type for argument \"%s\": line: %i col: %i", g_CurrentArgumentInfo.name.c_str(), states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
|
||||
// The size wasn't set in the config. See if that's fine and we can guess it from the type.
|
||||
if (!g_CurrentArgumentInfo.info.size)
|
||||
{
|
||||
if (g_CurrentArgumentInfo.info.type == HookParamType_Object)
|
||||
{
|
||||
smutils->LogError(myself, "Object param \"%s\" being set with no size: line: %i col: %i", g_CurrentArgumentInfo.name.c_str(), states->line, states->col);
|
||||
return SMCResult_HaltFail;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_CurrentArgumentInfo.info.size = GetParamTypeSize(g_CurrentArgumentInfo.info.type);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_CurrentArgumentInfo.info.pass_type == SourceHook::PassInfo::PassType::PassType_Unknown)
|
||||
g_CurrentArgumentInfo.info.pass_type = GetParamTypePassType(g_CurrentArgumentInfo.info.type);
|
||||
|
||||
// See if we were changing an existing argument.
|
||||
bool changed = false;
|
||||
for (auto &arg : g_CurrentSignature->args)
|
||||
{
|
||||
if (!arg.name.compare(g_CurrentArgumentInfo.name))
|
||||
{
|
||||
arg.info = g_CurrentArgumentInfo.info;
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// This was a new argument. Add it to the end of the list.
|
||||
if (!changed)
|
||||
g_CurrentSignature->args.push_back(g_CurrentArgumentInfo);
|
||||
|
||||
g_CurrentArgumentInfo.name = "";
|
||||
break;
|
||||
}
|
||||
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
void SignatureGameConfig::ReadSMC_ParseStart()
|
||||
{
|
||||
g_ParseState = PState_Root;
|
||||
g_IgnoreLevel = 0;
|
||||
g_PlatformOnlyState = PState_None;
|
||||
g_CurrentSignature = nullptr;
|
||||
g_CurrentFunctionName = "";
|
||||
g_CurrentArgumentInfo.name = "";
|
||||
}
|
||||
|
||||
ReturnType SignatureGameConfig::GetReturnTypeFromString(const char *str)
|
||||
{
|
||||
if (!strcmp(str, "void"))
|
||||
return ReturnType_Void;
|
||||
else if (!strcmp(str, "int"))
|
||||
return ReturnType_Int;
|
||||
else if (!strcmp(str, "bool"))
|
||||
return ReturnType_Bool;
|
||||
else if (!strcmp(str, "float"))
|
||||
return ReturnType_Float;
|
||||
else if (!strcmp(str, "string"))
|
||||
return ReturnType_String;
|
||||
else if (!strcmp(str, "stringptr"))
|
||||
return ReturnType_StringPtr;
|
||||
else if (!strcmp(str, "charptr"))
|
||||
return ReturnType_CharPtr;
|
||||
else if (!strcmp(str, "vector"))
|
||||
return ReturnType_Vector;
|
||||
else if (!strcmp(str, "vectorptr"))
|
||||
return ReturnType_VectorPtr;
|
||||
else if (!strcmp(str, "cbaseentity"))
|
||||
return ReturnType_CBaseEntity;
|
||||
else if (!strcmp(str, "edict"))
|
||||
return ReturnType_Edict;
|
||||
|
||||
return ReturnType_Unknown;
|
||||
}
|
||||
|
||||
HookParamType SignatureGameConfig::GetHookParamTypeFromString(const char *str)
|
||||
{
|
||||
if (!strcmp(str, "int"))
|
||||
return HookParamType_Int;
|
||||
else if (!strcmp(str, "bool"))
|
||||
return HookParamType_Bool;
|
||||
else if (!strcmp(str, "float"))
|
||||
return HookParamType_Float;
|
||||
else if (!strcmp(str, "string"))
|
||||
return HookParamType_String;
|
||||
else if (!strcmp(str, "stringptr"))
|
||||
return HookParamType_StringPtr;
|
||||
else if (!strcmp(str, "charptr"))
|
||||
return HookParamType_CharPtr;
|
||||
else if (!strcmp(str, "vectorptr"))
|
||||
return HookParamType_VectorPtr;
|
||||
else if (!strcmp(str, "cbaseentity"))
|
||||
return HookParamType_CBaseEntity;
|
||||
else if (!strcmp(str, "objectptr"))
|
||||
return HookParamType_ObjectPtr;
|
||||
else if (!strcmp(str, "edict"))
|
||||
return HookParamType_Edict;
|
||||
else if (!strcmp(str, "object"))
|
||||
return HookParamType_Object;
|
||||
|
||||
return HookParamType_Unknown;
|
||||
}
|
||||
|
||||
Register_t SignatureGameConfig::GetCustomRegisterFromString(const char *str)
|
||||
{
|
||||
if (!strcmp(str, "al"))
|
||||
return AL;
|
||||
else if (!strcmp(str, "cl"))
|
||||
return CL;
|
||||
else if (!strcmp(str, "dl"))
|
||||
return DL;
|
||||
else if (!strcmp(str, "bl"))
|
||||
return BL;
|
||||
else if (!strcmp(str, "ah"))
|
||||
return AH;
|
||||
else if (!strcmp(str, "ch"))
|
||||
return CH;
|
||||
else if (!strcmp(str, "dh"))
|
||||
return DH;
|
||||
else if (!strcmp(str, "bh"))
|
||||
return BH;
|
||||
|
||||
else if (!strcmp(str, "ax"))
|
||||
return AX;
|
||||
else if (!strcmp(str, "cx"))
|
||||
return CX;
|
||||
else if (!strcmp(str, "dx"))
|
||||
return DX;
|
||||
else if (!strcmp(str, "bx"))
|
||||
return BX;
|
||||
else if (!strcmp(str, "sp"))
|
||||
return SP;
|
||||
else if (!strcmp(str, "bp"))
|
||||
return BP;
|
||||
else if (!strcmp(str, "si"))
|
||||
return SI;
|
||||
else if (!strcmp(str, "di"))
|
||||
return DI;
|
||||
|
||||
else if (!strcmp(str, "eax"))
|
||||
return EAX;
|
||||
else if (!strcmp(str, "ecx"))
|
||||
return ECX;
|
||||
else if (!strcmp(str, "edx"))
|
||||
return EDX;
|
||||
else if (!strcmp(str, "ebx"))
|
||||
return EBX;
|
||||
else if (!strcmp(str, "esp"))
|
||||
return ESP;
|
||||
else if (!strcmp(str, "ebp"))
|
||||
return EBP;
|
||||
else if (!strcmp(str, "esi"))
|
||||
return ESI;
|
||||
else if (!strcmp(str, "edi"))
|
||||
return EDI;
|
||||
|
||||
else if (!strcmp(str, "mm0"))
|
||||
return MM0;
|
||||
else if (!strcmp(str, "mm1"))
|
||||
return MM1;
|
||||
else if (!strcmp(str, "mm2"))
|
||||
return MM2;
|
||||
else if (!strcmp(str, "mm3"))
|
||||
return MM3;
|
||||
else if (!strcmp(str, "mm4"))
|
||||
return MM4;
|
||||
else if (!strcmp(str, "mm5"))
|
||||
return MM5;
|
||||
else if (!strcmp(str, "mm6"))
|
||||
return MM6;
|
||||
else if (!strcmp(str, "mm7"))
|
||||
return MM7;
|
||||
|
||||
else if (!strcmp(str, "xmm0"))
|
||||
return XMM0;
|
||||
else if (!strcmp(str, "xmm1"))
|
||||
return XMM1;
|
||||
else if (!strcmp(str, "xmm2"))
|
||||
return XMM2;
|
||||
else if (!strcmp(str, "xmm3"))
|
||||
return XMM3;
|
||||
else if (!strcmp(str, "xmm4"))
|
||||
return XMM4;
|
||||
else if (!strcmp(str, "xmm5"))
|
||||
return XMM5;
|
||||
else if (!strcmp(str, "xmm6"))
|
||||
return XMM6;
|
||||
else if (!strcmp(str, "xmm7"))
|
||||
return XMM7;
|
||||
|
||||
else if (!strcmp(str, "cs"))
|
||||
return CS;
|
||||
else if (!strcmp(str, "ss"))
|
||||
return SS;
|
||||
else if (!strcmp(str, "ds"))
|
||||
return DS;
|
||||
else if (!strcmp(str, "es"))
|
||||
return ES;
|
||||
else if (!strcmp(str, "fs"))
|
||||
return FS;
|
||||
else if (!strcmp(str, "gs"))
|
||||
return GS;
|
||||
|
||||
else if (!strcmp(str, "st0"))
|
||||
return ST0;
|
||||
|
||||
return Register_t::None;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod Dynamic Hooks Extension
|
||||
* Copyright (C) 2012-2021 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$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SIGNATURES_H_
|
||||
#define _INCLUDE_SIGNATURES_H_
|
||||
|
||||
#include "extension.h"
|
||||
#include "util.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sm_stringhashmap.h>
|
||||
|
||||
struct ArgumentInfo {
|
||||
ArgumentInfo() : name()
|
||||
{ }
|
||||
|
||||
ArgumentInfo(std::string name, ParamInfo info) : name(name), info(info)
|
||||
{ }
|
||||
|
||||
std::string name;
|
||||
ParamInfo info;
|
||||
};
|
||||
|
||||
class SignatureWrapper {
|
||||
public:
|
||||
std::string signature;
|
||||
std::string address;
|
||||
std::string offset;
|
||||
std::vector<ArgumentInfo> args;
|
||||
CallingConvention callConv;
|
||||
HookType hookType;
|
||||
ReturnType retType;
|
||||
ThisPointerType thisType;
|
||||
};
|
||||
|
||||
class SignatureGameConfig : public ITextListener_SMC {
|
||||
public:
|
||||
SignatureWrapper *GetFunctionSignature(const char *function);
|
||||
public:
|
||||
//ITextListener_SMC
|
||||
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
|
||||
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
|
||||
SMCResult ReadSMC_LeavingSection(const SMCStates *states);
|
||||
void ReadSMC_ParseStart();
|
||||
|
||||
private:
|
||||
ReturnType GetReturnTypeFromString(const char *str);
|
||||
HookParamType GetHookParamTypeFromString(const char *str);
|
||||
Register_t GetCustomRegisterFromString(const char *str);
|
||||
|
||||
private:
|
||||
StringHashMap<SignatureWrapper *> signatures_;
|
||||
};
|
||||
|
||||
extern SignatureGameConfig *g_pSignatures;
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user