diff --git a/.gitignore b/.gitignore
index 0f1bf8c2..cc670aea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,8 @@
build/
MSVC14/Debug/
MSVC14/Release/
+MSVC14/Binary/
+MSVC14/packages/
MSVC14/*.suo
MSVC14/*.user
MSVC14/.vs
diff --git a/AMBuilder b/AMBuilder
index 0af08396..1853d996 100644
--- a/AMBuilder
+++ b/AMBuilder
@@ -25,6 +25,7 @@ sourceFiles = [
'prop.cpp',
'util/trace.cpp',
'util/backtrace.cpp',
+ 'util/rtti.cpp',
'stub/baseentity.cpp',
'stub/gamerules.cpp',
'stub/misc.cpp',
@@ -43,9 +44,10 @@ sourceFiles = [
'mod/mod_stickypusher.cpp',
'mod/mod_robottickle.cpp',
'mod/mod_botmulticlassitem.cpp',
- 'mod/debug_airblastbox.cpp',
- 'mod/debug_deflectcylinder.cpp',
- 'mod/debug_ehsphere.cpp',
+ 'mod/mod_robotheadnoslide.cpp',
+ 'mod/visualize_airblastbox.cpp',
+ 'mod/visualize_deflectcylinder.cpp',
+ 'mod/visualize_ehsphere.cpp',
]
###############
diff --git a/MSVC14/Performance.psess b/MSVC14/Performance.psess
index 84a5cb49..8dfaa4fc 100644
--- a/MSVC14/Performance.psess
+++ b/MSVC14/Performance.psess
@@ -4,7 +4,7 @@
sigsegv.sln
Instrumentation
None
- C:\Profiling\Report.vsp
+ C:\Profiling\Instrumentation.vsp
true
true
Timestamp
@@ -14,6 +14,8 @@
10
false
+
+
@@ -30,14 +32,26 @@
false
false
+
+
+
+
+
+
false
+
+
+
+
+
+
false
- Release\sigsegv.ext.2.tf2.dll
+ Binary\sigsegv.ext.2.tf2.dll
01/01/0001 00:00:00
true
true
@@ -54,7 +68,7 @@
IIS
InternetExplorer
- true
+ false
false
false
@@ -85,7 +99,7 @@
+exec cheat
IIS
InternetExplorer
- true
+ false
false
false
diff --git a/MSVC14/hl2.psess b/MSVC14/hl2.psess
new file mode 100644
index 00000000..c9077406
--- /dev/null
+++ b/MSVC14/hl2.psess
@@ -0,0 +1,103 @@
+
+
+
+ sigsegv.sln
+ Sampling
+ None
+ C:\Profiling\Sampling.vsp
+ true
+ true
+ Timestamp
+ Cycles
+ 10000000
+ 10
+ 10
+
+ false
+
+
+
+ false
+ 500
+
+ \(_Total)\
+ \(_Total)\
+ \\
+
+
+
+ true
+ false
+ false
+
+ false
+
+
+ false
+
+
+
+ C:\Program Files (x86)\Steam\SteamApps\common\Team Fortress 2\hl2.exe
+ 01/01/0001 00:00:00
+ true
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ true
+ Executable
+ C:\Program Files (x86)\Steam\SteamApps\common\Team Fortress 2\hl2.exe
+ C:\Program Files (x86)\Steam\SteamApps\common\Team Fortress 2\
+ -steam -game tf -novid -console -condebug -insecure -windowed -w 1280 -h 720 +maxplayers 32 +developer 1 +map mvm_example
+ +exec cheat
+ IIS
+ InternetExplorer
+ true
+ false
+
+ false
+
+
+ false
+
+
+
+ Binary\sigsegv.ext.2.tf2.dll
+ 01/01/0001 00:00:00
+ true
+ true
+ false
+ false
+ false
+ false
+ false
+ true
+ false
+ Executable
+ .\
+
+
+ IIS
+ InternetExplorer
+ true
+ false
+
+ false
+
+
+ false
+
+ {B3E797CF-4E77-4C9D-B8A8-7589B6902206}|sigsegv.vcxproj
+ sigsegv.vcxproj
+ sigsegv
+
+
+
+
+ C:\Program Files (x86)\Steam\SteamApps\common\Team Fortress 2\hl2.exe
+
+
+
\ No newline at end of file
diff --git a/MSVC14/packages.config b/MSVC14/packages.config
new file mode 100644
index 00000000..e4a801d0
--- /dev/null
+++ b/MSVC14/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/MSVC14/sigsegv.vcxproj b/MSVC14/sigsegv.vcxproj
index ff1714bb..0e210149 100644
--- a/MSVC14/sigsegv.vcxproj
+++ b/MSVC14/sigsegv.vcxproj
@@ -15,6 +15,7 @@
sigsegv
Win32Proj
sigsegv
+ 8.1
@@ -55,6 +56,12 @@
sigsegv.ext.2.tf2
sigsegv.ext.2.tf2
+
+ $(SolutionDir)Binary\
+
+
+ $(SolutionDir)Binary\
+
/D SE_EPISODEONE=1 /D SE_DARKMESSIAH=2 /D SE_ORANGEBOX=3 /D SE_BLOODYGOODTIME=4 /D SE_EYE=5 /D SE_CSS=6 /D SE_ORANGEBOXVALVE=7 /D SE_LEFT4DEAD=8 /D SE_LEFT4DEAD2=9 /D SE_ALIENSWARM=10 /D SE_PORTAL2=11 /D SE_CSGO=12
@@ -91,8 +98,8 @@
true
- copy $(TargetDir)\sigsegv.ext.2.tf2.dll $(SolutionDir)\..\build\package\addons\sourcemod\extensions\
-copy $(TargetDir)\sigsegv.ext.2.tf2.pdb $(SolutionDir)\..\build\package\addons\sourcemod\extensions\
+
+
@@ -128,8 +135,8 @@ copy $(TargetDir)\sigsegv.ext.2.tf2.pdb $(SolutionDir)\..\build\package\addons\s
true
- copy $(TargetDir)\sigsegv.ext.2.tf2.dll $(SolutionDir)\..\build\package\addons\sourcemod\extensions\
-copy $(TargetDir)\sigsegv.ext.2.tf2.pdb $(SolutionDir)\..\build\package\addons\sourcemod\extensions\
+
+
@@ -155,15 +162,16 @@ copy $(TargetDir)\sigsegv.ext.2.tf2.pdb $(SolutionDir)\..\build\package\addons\s
-
-
-
+
+
+
+
@@ -179,6 +187,7 @@ copy $(TargetDir)\sigsegv.ext.2.tf2.pdb $(SolutionDir)\..\build\package\addons\s
+
@@ -224,11 +233,22 @@ copy $(TargetDir)\sigsegv.ext.2.tf2.pdb $(SolutionDir)\..\build\package\addons\s
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
\ No newline at end of file
diff --git a/MSVC14/sigsegv.vcxproj.filters b/MSVC14/sigsegv.vcxproj.filters
index 64126386..1366c1c8 100644
--- a/MSVC14/sigsegv.vcxproj.filters
+++ b/MSVC14/sigsegv.vcxproj.filters
@@ -74,15 +74,6 @@
Source Files
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
Source Files
@@ -134,6 +125,21 @@
Source Files
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
@@ -271,5 +277,11 @@
Header Files
+
+ Header Files
+
+
+
+
\ No newline at end of file
diff --git a/PCH.mak b/PCH.mak
index 2ebf1e92..1091d806 100644
--- a/PCH.mak
+++ b/PCH.mak
@@ -1,2 +1,4 @@
$(OUTFILE): $(INFILE)
+ #-rm $(OUTFILE)
+ #-touch $(OUTFILE)
$(CXX) -x c++-header $(INFILE) -o $(OUTFILE)
diff --git a/PackageScript b/PackageScript
index 3fd27540..2842d738 100644
--- a/PackageScript
+++ b/PackageScript
@@ -40,6 +40,7 @@ def CopyFiles(src, dest, files):
# GameData files
CopyFiles('gamedata/sigsegv', 'addons/sourcemod/gamedata/sigsegv',
[
+ 'rtti.txt',
'vtables.txt',
'datamaps.txt',
'globals.txt',
diff --git a/abi.h b/abi.h
index 67b5671f..dcc7c763 100644
--- a/abi.h
+++ b/abi.h
@@ -78,5 +78,18 @@ struct __RTTI_CompleteObjectLocator
#pragma warning(default:4200)
#endif
+#if defined _MSC_VER
+/* from VC/crt/src/vcruntime/rtti.cpp */
+extern "C" PVOID __CLRCALL_OR_CDECL __RTDynamicCast (
+ PVOID inptr,
+ LONG VfDelta,
+// const _TypeDescriptor *SrcType,
+// const _TypeDescriptor *TargetType,
+ PVOID SrcType,
+ PVOID TargetType,
+ BOOL isReference
+ ) throw(...);
+#endif
+
#endif
diff --git a/addr/addr.cpp b/addr/addr.cpp
index 1dbcca7b..4066ee23 100644
--- a/addr/addr.cpp
+++ b/addr/addr.cpp
@@ -3,6 +3,14 @@
#include "abi.h"
+#if defined __GNUC__
+#warning TODO: refactor prologue-finding code into a shared block somewhere
+#warning TODO: make prologue-finding code look for the closest aligned ebp prologue *OR* ebx/ebp prologue
+#warning (ensure that ScanResults::FIRST with two scanners will short-circuit properly)
+#warning TODO: increase arbitrary 0x1000 limit in prologue-finding code
+#endif
+
+
void IAddr::Init()
{
if (this->m_State != State::INITIAL) {
@@ -143,6 +151,7 @@ bool IAddr_VTable::FindAddrLinux(uintptr_t& addr) const
bool IAddr_VTable::FindAddrWin(uintptr_t& addr) const
{
+#if 0
/* STEP 1: get ptr to _TypeDescriptor by finding typeinfo name string */
std::vector scan1_matches;
for (auto match : PreScan::WinRTTI_Server()) {
@@ -189,6 +198,8 @@ bool IAddr_VTable::FindAddrWin(uintptr_t& addr) const
addr = (uintptr_t)p_VT;
return true;
+#endif
+ return false;
}
@@ -200,7 +211,7 @@ bool IAddr_DataDescMap::FindAddrWin(uintptr_t& addr) const
return false;
}
- CSingleScan scan1(ScanDir::FORWARD, CLibSegBounds(this->GetLibrary(), ".data"), 4, new CBasicScanner(ScanResults::ALL, (const void *)&p_str, 0x4));
+ CSingleScan scan1(CLibSegBounds(this->GetLibrary(), ".data"), new CBasicScanner(ScanResults::ALL, (const void *)&p_str, 0x4));
struct GetDataDescMap { uint8_t buf[6]; };
std::vector scanners;
for (auto match : scan1.Matches()) {
@@ -212,7 +223,7 @@ bool IAddr_DataDescMap::FindAddrWin(uintptr_t& addr) const
scanners.push_back(new CBasicScanner(ScanResults::ALL, (const void *)&gddm, 0x6));
}
- CMultiScan scan2(ScanDir::FORWARD, CLibSegBounds(this->GetLibrary(), ".text"), 0x10, scanners);
+ CMultiScan scan2(CLibSegBounds(this->GetLibrary(), ".text"), scanners);
std::vector results;
for (auto scanner : scanners) {
if (scanner->Matches().size() == 1) {
@@ -307,7 +318,7 @@ bool IAddr_Func_EBPPrologue_UniqueRef::FindAddrWin(uintptr_t& addr) const
return false;
}
- CSingleScan scan1(ScanDir::FORWARD, CLibSegBounds(this->GetLibrary(), ".text"), 1, new CBasicScanner(ScanResults::ALL, (const void *)&p_ref, 0x4));
+ CSingleScan scan1(CLibSegBounds(this->GetLibrary(), ".text"), new CBasicScanner(ScanResults::ALL, (const void *)&p_ref, 0x4));
if (scan1.Matches().size() != 1) {
DevMsg("IAddr_Func_EBPPrologue_UniqueRef: \"%s\": found %u refs to ostensibly unique symbol\n", this->GetName(), scan1.Matches().size());
return false;
@@ -318,7 +329,7 @@ bool IAddr_Func_EBPPrologue_UniqueRef::FindAddrWin(uintptr_t& addr) const
0x55, // +0000 push ebp
0x8b, 0xec, // +0001 mov ebp,esp
};
- CSingleScan scan2(ScanDir::REVERSE, CAddrOffBounds(p_in_func, -0x1000), 0x10, new CBasicScanner(ScanResults::FIRST, (const void *)prologue, sizeof(prologue)));
+ CSingleScan scan2(CAddrOffBounds(p_in_func, -0x1000), new CBasicScanner(ScanResults::FIRST, (const void *)prologue, sizeof(prologue)));
if (scan2.Matches().size() != 1) {
DevMsg("IAddr_Func_EBPPrologue_UniqueRef: \"%s\": could not locate EBP prologue\n", this->GetName());
return false;
@@ -338,7 +349,7 @@ bool IAddr_Func_EBPPrologue_UniqueStr::FindAddrWin(uintptr_t& addr) const
return false;
}
- CSingleScan scan1(ScanDir::FORWARD, CLibSegBounds(this->GetLibrary(), ".text"), 1, new CBasicScanner(ScanResults::ALL, (const void *)&p_str, 0x4));
+ CSingleScan scan1(CLibSegBounds(this->GetLibrary(), ".text"), new CBasicScanner(ScanResults::ALL, (const void *)&p_str, 0x4));
if (scan1.Matches().size() != 1) {
DevMsg("IAddr_Func_EBPPrologue_UniqueStr: \"%s\": found %u refs to ostensibly unique string\n", this->GetName(), scan1.Matches().size());
return false;
@@ -349,7 +360,7 @@ bool IAddr_Func_EBPPrologue_UniqueStr::FindAddrWin(uintptr_t& addr) const
0x55, // +0000 push ebp
0x8b, 0xec, // +0001 mov ebp,esp
};
- CSingleScan scan2(ScanDir::REVERSE, CAddrOffBounds(p_in_func, -0x1000), 0x10, new CBasicScanner(ScanResults::FIRST, (const void *)prologue, sizeof(prologue)));
+ CSingleScan scan2(CAddrOffBounds(p_in_func, -0x10000), new CBasicScanner(ScanResults::FIRST, (const void *)prologue, sizeof(prologue)));
if (scan2.Matches().size() != 1) {
DevMsg("IAddr_Func_EBPPrologue_UniqueStr: \"%s\": could not locate EBP prologue\n", this->GetName());
return false;
@@ -369,7 +380,7 @@ bool IAddr_Func_EBPPrologue_UniqueStr_KnownVTIdx::FindAddrWin(uintptr_t& addr) c
return false;
}
- CSingleScan scan1(ScanDir::FORWARD, CLibSegBounds(this->GetLibrary(), ".text"), 1, new CBasicScanner(ScanResults::ALL, (const void *)&p_str, 0x4));
+ CSingleScan scan1(CLibSegBounds(this->GetLibrary(), ".text"), new CBasicScanner(ScanResults::ALL, (const void *)&p_str, 0x4));
if (scan1.Matches().size() != 1) {
DevMsg("IAddr_Func_EBPPrologue_UniqueStr_KnownVTIdx: \"%s\": found %u refs to ostensibly unique string\n", this->GetName(), scan1.Matches().size());
return false;
@@ -380,7 +391,7 @@ bool IAddr_Func_EBPPrologue_UniqueStr_KnownVTIdx::FindAddrWin(uintptr_t& addr) c
0x55, // +0000 push ebp
0x8b, 0xec, // +0001 mov ebp,esp
};
- CSingleScan scan2(ScanDir::REVERSE, CAddrOffBounds(p_in_func, -0x1000), 0x10, new CBasicScanner(ScanResults::FIRST, (const void *)prologue, sizeof(prologue)));
+ CSingleScan scan2(CAddrOffBounds(p_in_func, -0x10000), new CBasicScanner(ScanResults::FIRST, (const void *)prologue, sizeof(prologue)));
if (scan2.Matches().size() != 1) {
DevMsg("IAddr_Func_EBPPrologue_UniqueStr_KnownVTIdx: \"%s\": could not locate EBP prologue\n", this->GetName());
return false;
@@ -424,7 +435,7 @@ bool IAddr_Func_EBPPrologue_VProf::FindAddrWin(uintptr_t& addr) const
};
*(const char **)(vprof + 0x01) = p_name;
*(const char **)(vprof + 0x06) = p_group;
- CSingleScan scan1(ScanDir::FORWARD, CLibSegBounds(this->GetLibrary(), ".text"), 1, new CBasicScanner(ScanResults::FIRST, (const void *)vprof, sizeof(vprof)));
+ CSingleScan scan1(CLibSegBounds(this->GetLibrary(), ".text"), new CBasicScanner(ScanResults::FIRST, (const void *)vprof, sizeof(vprof)));
if (scan1.Matches().size() != 1) {
DevMsg("IAddr_Func_EBPPrologue_VProf: \"%s\": could not locate VPROF_BUDGET\n", this->GetName());
return false;
@@ -435,7 +446,7 @@ bool IAddr_Func_EBPPrologue_VProf::FindAddrWin(uintptr_t& addr) const
0x55, // +0000 push ebp
0x8b, 0xec, // +0001 mov ebp,esp
};
- CSingleScan scan2(ScanDir::REVERSE, CAddrOffBounds(p_in_func, -0x1000), 0x10, new CBasicScanner(ScanResults::FIRST, (const void *)prologue, sizeof(prologue)));
+ CSingleScan scan2(CAddrOffBounds(p_in_func, -0x10000), new CBasicScanner(ScanResults::FIRST, (const void *)prologue, sizeof(prologue)));
if (scan2.Matches().size() != 1) {
DevMsg("IAddr_Func_EBPPrologue_VProf: \"%s\": could not locate EBP prologue\n", this->GetName());
return false;
diff --git a/addr/misc.cpp b/addr/misc.cpp
index 1bb65ffb..eb3a1305 100644
--- a/addr/misc.cpp
+++ b/addr/misc.cpp
@@ -85,7 +85,7 @@ class CAddr_pszWpnEntTranslationList : public IAddr_Sym
auto strscan3 = new CStringScanner(ScanResults::ALL, "tf_weapon_shotgun_hwg");
auto strscan4 = new CStringScanner(ScanResults::ALL, "tf_weapon_shotgun_pyro");
auto strscan5 = new CStringScanner(ScanResults::ALL, "tf_weapon_shotgun_primary");
- CMultiScan scan1(ScanDir::FORWARD, CLibSegBounds(Library::SERVER, ".rdata"), 1,
+ CMultiScan scan1(CLibSegBounds(Library::SERVER, ".rdata"),
{ strscan1, strscan2, strscan3, strscan4, strscan5 });
if (strscan1->Matches().size() != 1) { DevMsg("Fail strscan1\n"); return false; }
if (strscan2->Matches().size() != 1) { DevMsg("Fail strscan2\n"); return false; }
@@ -100,7 +100,7 @@ class CAddr_pszWpnEntTranslationList : public IAddr_Sym
mask.SetDword(0x1c, 0xffffffff); seek.SetDword(0x1c, (uint32_t)strscan3->Matches()[0]);
mask.SetDword(0x20, 0xffffffff); seek.SetDword(0x20, (uint32_t)strscan4->Matches()[0]);
mask.SetDword(0x28, 0xffffffff); seek.SetDword(0x28, (uint32_t)strscan5->Matches()[0]);
- CSingleScan scan2(ScanDir::FORWARD, CLibSegBounds(Library::SERVER, ".data"), 4, new CMaskedScanner(ScanResults::ALL, seek, mask));
+ CSingleScan scan2(CLibSegBounds(Library::SERVER, ".data"), new CMaskedScanner(ScanResults::ALL, seek, mask));
if (scan2.Matches().size() != 1) { DevMsg("Fail scan2 %u\n", scan2.Matches().size()); return false; }
auto match = (const char **)scan2.Matches()[0];
@@ -171,7 +171,7 @@ class CAddr_CTFPlayer_CanBeForcedToLaugh : public IAddr_Sym
// DevMsg("g_pGameRules: %08x\n", (uintptr_t)addr_g_pGameRules);
// DevMsg("m_bPlayingMannVsMachine: %08x\n", off_CTFGameRules_m_bPlayingMannVsMachine);
- CSingleScan scan1(ScanDir::FORWARD, CLibSegBounds(this->GetLibrary(), ".text"), 0x10, new CMaskedScanner(ScanResults::ALL, seek, mask));
+ CSingleScan scan1(CLibSegBounds(this->GetLibrary(), ".text"), new CMaskedScanner(ScanResults::ALL, seek, mask));
if (scan1.Matches().size() != 1) {
DevMsg("Fail scan1 %u\n", scan1.Matches().size());
return false;
diff --git a/addr/prescan.cpp b/addr/prescan.cpp
index 02b9719d..283bdc48 100644
--- a/addr/prescan.cpp
+++ b/addr/prescan.cpp
@@ -1,7 +1,7 @@
#include "addr/prescan.h"
-CSingleScan *PreScan::s_WinRTTI_server = nullptr;
+//CSingleScan *PreScan::s_WinRTTI_server = nullptr;
void PreScan::DoScans()
@@ -9,8 +9,8 @@ void PreScan::DoScans()
#if defined _WINDOWS
DevMsg("PreScan::DoScans\n");
- s_WinRTTI_server = new CSingleScan(ScanDir::FORWARD, CLibSegBounds(Library::SERVER, ".data"), 1, new CStringPrefixScanner(ScanResults::ALL, ".?AV"));
-
+// s_WinRTTI_server = new CSingleScan(ScanDir::FORWARD, CLibSegBounds(Library::SERVER, ".data"), 1, new CStringPrefixScanner(ScanResults::ALL, ".?AV"));
+//
// for (auto match : s_WinRTTI_server->Matches()) {
// DevMsg("[PreScan] 0x%08x \"%s\"\n", (uintptr_t)match, (const char *)match);
// }
diff --git a/addr/prescan.h b/addr/prescan.h
index 1941ec1d..97f42859 100644
--- a/addr/prescan.h
+++ b/addr/prescan.h
@@ -10,10 +10,10 @@ class PreScan
public:
static void DoScans();
- static const std::vector& WinRTTI_Server() { return s_WinRTTI_server->Matches(); }
+// static const std::vector& WinRTTI_Server() { return s_WinRTTI_server->Matches(); }
-private:
- static CSingleScan *s_WinRTTI_server;
+//private:
+// static CSingleScan *s_WinRTTI_server;
};
diff --git a/common.h b/common.h
index e95deb3f..08f8a4b6 100644
--- a/common.h
+++ b/common.h
@@ -18,6 +18,7 @@ class IEngineTrace;
class IStaticPropMgrServer;
class IVDebugOverlay;
class CGlobalVars;
+class CBaseEntityList;
extern IVEngineServer *engine;
@@ -28,13 +29,17 @@ extern IStaticPropMgrServer *staticpropmgr;
extern IVDebugOverlay *debugoverlay;
extern CGlobalVars *gpGlobals;
+extern CBaseEntityList *g_pEntityList;
/* C++ standard library */
#include
#include
#include
+#include
+#include
#include
+#include
#include
#include
#include
@@ -47,6 +52,9 @@ extern CGlobalVars *gpGlobals;
/* AMTL */
#include
+/* Boost */
+//#include
+
/* Source SDK */
#include "sdk2013/annotations.h"
#include "sdk2013/basetypes.h"
diff --git a/extension.cpp b/extension.cpp
index 9d2dede2..b2047c94 100644
--- a/extension.cpp
+++ b/extension.cpp
@@ -7,6 +7,7 @@
#include "addr/prescan.h"
#include "gameconf.h"
#include "prop.h"
+#include "util/rtti.h"
CExtSigsegv g_Ext;
@@ -19,6 +20,7 @@ IStaticPropMgrServer *staticpropmgr;
IVDebugOverlay *debugoverlay;
CGlobalVars *gpGlobals;
+CBaseEntityList *g_pEntityList;
//ISDKTools *g_pSDKTools;
@@ -28,10 +30,13 @@ bool CExtSigsegv::SDK_OnLoad(char *error, size_t maxlen, bool late)
// sharesys->AddDependency(myself, "sdktools.ext", true, true);
// SM_GET_IFACE(SDKTOOLS, g_pSDKTools);
+ g_pEntityList = reinterpret_cast(gamehelpers->GetGlobalEntityList());
+
PreScan::DoScans();
if (!g_GCHook.LoadAll(error, maxlen)) goto fail;
LibMgr::Load();
+ RTTI::PreLoad(); goto fail; // REMOVE ME
AddrManager::Load();
if (!Link::InitAll(error, maxlen)) goto fail;
diff --git a/gameconf.cpp b/gameconf.cpp
index 23904ccb..afc45582 100644
--- a/gameconf.cpp
+++ b/gameconf.cpp
@@ -5,6 +5,7 @@
static const char *const configs[] = {
+ "sigsegv/rtti",
"sigsegv/vtables",
"sigsegv/datamaps",
"sigsegv/globals",
diff --git a/gamedata/sigsegv/misc.txt b/gamedata/sigsegv/misc.txt
index c7edc697..c43ec36f 100644
--- a/gamedata/sigsegv/misc.txt
+++ b/gamedata/sigsegv/misc.txt
@@ -43,20 +43,20 @@
// uniref "pszWpnEntTranslationList"
// }
- "CBaseEntity::GetNetworkable"
+ "CBaseEntity::CalcAbsolutePosition"
{
type "sym"
- sym "_ZN11CBaseEntity14GetNetworkableEv"
+ sym "_ZN11CBaseEntity20CalcAbsolutePositionEv"
}
- "CBaseEntity::CalcAbsolutePosition"
+ "CBaseEntity::GetNetworkable"
{
type "sym"
- sym "_ZN11CBaseEntity20CalcAbsolutePositionEv"
+ sym "_ZN11CBaseEntity14GetNetworkableEv"
}
- "CBaseEntity::IsAlive"
+ "CBaseEntity::IsPlayer"
{
type "sym"
- sym "_ZN11CBaseEntity7IsAliveEv"
+ sym "_ZNK11CBaseEntity8IsPlayerEv"
}
"CBasePlayer::IsBot"
diff --git a/gamedata/sigsegv/rtti.txt b/gamedata/sigsegv/rtti.txt
new file mode 100644
index 00000000..cd50554e
--- /dev/null
+++ b/gamedata/sigsegv/rtti.txt
@@ -0,0 +1,36 @@
+// rtti
+
+"Games"
+{
+ "#default"
+ {
+ "#supported"
+ {
+ engine "tf2"
+ }
+
+ "sigsegv"
+ {
+ "addrs"
+ {
+ // TODO:
+ // remove the need for this file
+ // and make rtti addr finding automatic
+
+ "[RTTI] CTFPlayer"
+ {
+ type "rtti"
+ sym "_ZTI9CTFPlayer"
+ winstr ".?AVCTFPlayer@@"
+ }
+
+ "[RTTI] CTFBot"
+ {
+ type "rtti"
+ sym "_ZTI6CTFBot"
+ winstr ".?AVCTFBot@@"
+ }
+ }
+ }
+ }
+}
diff --git a/gamedata/sigsegv/tfplayer.txt b/gamedata/sigsegv/tfplayer.txt
index 5f8da44a..38c26dcc 100644
--- a/gamedata/sigsegv/tfplayer.txt
+++ b/gamedata/sigsegv/tfplayer.txt
@@ -41,9 +41,22 @@
sym "_ZN9CTFPlayer21SpeakConceptIfAllowedEiPKcPcjP16IRecipientFilter"
unistr "disguiseclass:%s"
}
- // CTFPlayer::TFPlayerThink
- // warning! warning! not a straightforward ebp prologue!
- // push ebx; mov ebx, esp
+ "CTFPlayer::TFPlayerThink"
+ {
+ // weird function with dual ebx/ebp frame pointers for 16-byte alignment
+ type "func ebpprologue unistr"
+ sym "_ZN9CTFPlayer13TFPlayerThinkEv"
+ unistr "Heal Target: ClipSize Data Limit Exceeded: %d (max 500)\n"
+ // alt: "disguise_as_dispenser_on_crouch"
+ // alt: "#TF_Powerup_Supernova_Deploy"
+ // alt: "respawn_ghost"
+ // alt: "reviver"
+ // alt: "sticky_jump_landed"
+ // alt: "rocket_jump_landed"
+ // alt: "head_scale"
+ // alt: "torso_scale"
+ // alt: "hand_scale"
+ }
}
}
}
diff --git a/gamedata/sigsegv/vtables.txt b/gamedata/sigsegv/vtables.txt
index 94a4d5aa..99107f88 100644
--- a/gamedata/sigsegv/vtables.txt
+++ b/gamedata/sigsegv/vtables.txt
@@ -13,6 +13,10 @@
{
"addrs"
{
+ // TODO:
+ // remove the need for this file
+ // and make vtable addr finding automatic
+
"[VT] CGameRules"
{
type "vtable"
diff --git a/library.cpp b/library.cpp
index 1686331a..97d7ec88 100644
--- a/library.cpp
+++ b/library.cpp
@@ -102,6 +102,13 @@ void *LibMgr::FindSym(Library lib, const char *sym)
return g_MemUtils.ResolveSymbol(handle, sym);
}
+void LibMgr::ForEachSym(Library lib, void (*functor)(Symbol *))
+{
+ void *handle = s_LibHandles.at(lib);
+ assert(handle != nullptr);
+ g_MemUtils.ForEachSymbol(handle, functor);
+}
+
#if defined _LINUX || defined _OSX
diff --git a/library.h b/library.h
index bea3f42f..5c683fac 100644
--- a/library.h
+++ b/library.h
@@ -48,6 +48,7 @@ class LibMgr
static const LibInfo& GetInfo(Library lib);
static void *FindSym(Library lib, const char *sym);
+ static void ForEachSym(Library lib, void (*functor)(Symbol *));
private:
LibMgr() {}
diff --git a/mem/extract.h b/mem/extract.h
index eac0a196..00043c1f 100644
--- a/mem/extract.h
+++ b/mem/extract.h
@@ -97,7 +97,7 @@ bool IExtract::Check()
uintptr_t addr_min = (uintptr_t)this->m_pFuncAddr + this->m_iFuncOffMin;
uintptr_t addr_max = (uintptr_t)this->m_pFuncAddr + this->m_iFuncOffMax + this->m_iLength;
- CSingleScan scan(ScanDir::FORWARD, CAddrAddrBounds((void *)addr_min, (void *)addr_max), 1,
+ CSingleScan scan(CAddrAddrBounds((void *)addr_min, (void *)addr_max),
new CMaskedScanner(ScanResults::ALL, this->m_BufExtract, this->m_MaskExtract));
if (scan.Matches().size() != 1) {
diff --git a/mem/patch.cpp b/mem/patch.cpp
index 5b4050bd..9fc987ed 100644
--- a/mem/patch.cpp
+++ b/mem/patch.cpp
@@ -29,7 +29,7 @@ bool IPatch::Check()
uintptr_t addr_min = (uintptr_t)this->m_pFuncAddr + this->m_iFuncOffMin;
uintptr_t addr_max = (uintptr_t)this->m_pFuncAddr + this->m_iFuncOffMax + this->m_iLength;
- CSingleScan scan(ScanDir::FORWARD, CAddrAddrBounds((void *)addr_min, (void *)addr_max), 1,
+ CSingleScan scan(CAddrAddrBounds((void *)addr_min, (void *)addr_max),
new CMaskedScanner(ScanResults::ALL, this->m_BufVerify, this->m_MaskVerify));
if (scan.Matches().size() != 1) {
diff --git a/mem/scan.cpp b/mem/scan.cpp
index 536db3ac..c1fe85e0 100644
--- a/mem/scan.cpp
+++ b/mem/scan.cpp
@@ -30,14 +30,14 @@ void IScanner::AddMatch(const void *match)
void CBasicScanner::CheckOne(const void *where)
{
- if (memcmp(where, this->m_Seek, this->m_Len) == 0) {
+ if (memcmp(where, this->m_Seek, this->GetBufLen()) == 0) {
this->AddMatch(where);
}
}
void CMaskedScanner::CheckOne(const void *where)
{
- int len = this->m_Seek.GetSize();
+ int len = this->GetBufLen();
for (int i = 0; i < len; ++i) {
uint8_t b_mem = *((uint8_t *)where + i);
@@ -61,98 +61,17 @@ void CStringScanner::CheckOne(const void *where)
void CStringPrefixScanner::CheckOne(const void *where)
{
- if (strncmp((const char *)where, this->m_Str, strlen(this->m_Str)) == 0) {
+ if (strncmp((const char *)where, this->m_Str, this->GetBufLen() - 1) == 0) {
this->AddMatch(where);
}
}
-void IScan::DoScan()
-{
- bool fwd;
- switch (this->m_Dir) {
- case ScanDir::FORWARD:
- fwd = true;
- break;
- case ScanDir::REVERSE:
- fwd = false;
- break;
- default:
- assert(false);
- }
-
- for (auto scanner : this->m_Scanners) {
- scanner->Reset();
- }
-
- int align = this->m_Align;
-
- const uint8_t *p_low = (const uint8_t *)this->m_Bounds.GetLowerBound();
- const uint8_t *p_high = (const uint8_t *)this->m_Bounds.GetUpperBound();
-
- const uint8_t *ptr = (fwd ? p_low : p_high - align);
- const uint8_t *end = (fwd ? p_high : p_low - align);
-
- uintptr_t rem = (uintptr_t)ptr % align;
- if (rem != 0) {
- if (fwd) {
- ptr += (align - rem);
- } else {
- ptr -= rem;
- }
- }
-
- int incr = (fwd ? align : -align);
-
-// DevMsg("IScan::DoScan: ptr 0x%08x, end 0x%08x, incr %c0x%08x\n", (uintptr_t)ptr, (uintptr_t)end, (fwd ? '+' : '-'), align);
-
- /* use an array because std::vector is horrendously slow here */
- int num_scanners = this->m_Scanners.size();
- IScanner **scanners = new IScanner *[num_scanners];
- for (int i = 0; i < num_scanners; ++i) {
- scanners[i] = this->m_Scanners[i];
- }
-
- while (fwd ? (ptr < end) : (ptr > end)) {
-// bool all_done = false;
-
- for (int i = 0; i < num_scanners; ++i) {
- IScanner *scanner = scanners[i];
-
- if (scanner->IsDone()) {
- continue;
- }
-
- if (ptr + scanner->GetBufLen() <= p_high) {
- scanner->CheckOne(ptr);
-#if 0
-
- if (scanner->IsDone()) {
- scanners.erase(scanner);
- if (scanners.empty()) {
- all_done = true;
- }
- }
-#endif
- }
- }
-
-// if (all_done) {
-// break;
-// }
-
- ptr += incr;
- }
-
- delete[] scanners;
-}
-
-
namespace Scan
{
const char *FindUniqueConstStr(const char *str)
{
- CSingleScan scan(ScanDir::FORWARD, CLibSegBounds(Library::SERVER, ".rdata"), 1, new CStringScanner(ScanResults::ALL, str));
+ CSingleScan scan(CLibSegBounds(Library::SERVER, ".rdata"), new CStringScanner(ScanResults::ALL, str));
if (scan.Matches().size() == 1) {
return (const char *)scan.Matches()[0];
diff --git a/mem/scan.h b/mem/scan.h
index cb753af2..fbb8c88e 100644
--- a/mem/scan.h
+++ b/mem/scan.h
@@ -93,17 +93,20 @@ class IScanner
void Reset() { this->m_Matches.clear(); }
bool IsDone() const { return this->m_bDone; }
+ ScanResults GetResultsType() const { return this->m_RType; }
+ int GetBufLen() const { return this->m_Len; }
+
virtual void CheckOne(const void *where) = 0;
- virtual int GetBufLen() const = 0;
protected:
- IScanner(ScanResults rtype) :
- m_RType(rtype) {}
+ IScanner(ScanResults rtype, int len) :
+ m_RType(rtype), m_Len(len) {}
void AddMatch(const void *match);
private:
ScanResults m_RType;
+ int m_Len;
std::vector m_Matches;
bool m_bDone = false;
@@ -113,7 +116,7 @@ class CBasicScanner : public IScanner
{
public:
CBasicScanner(ScanResults rtype, const void *seek, int len) :
- IScanner(rtype), m_Len(len)
+ IScanner(rtype, len)
{
this->m_Seek = new uint8_t[len];
memcpy(this->m_Seek, seek, len);
@@ -123,11 +126,9 @@ class CBasicScanner : public IScanner
delete[] this->m_Seek;
}
- virtual int GetBufLen() const override { return this->m_Len; }
virtual void CheckOne(const void *where) override;
private:
- int m_Len;
uint8_t *m_Seek;
};
@@ -135,12 +136,11 @@ class CMaskedScanner : public IScanner
{
public:
CMaskedScanner(ScanResults rtype, const ByteBuf& seek, const ByteBuf& mask) :
- IScanner(rtype), m_Seek(seek), m_Mask(mask)
+ IScanner(rtype, seek.GetSize()), m_Seek(seek), m_Mask(mask)
{
assert(this->m_Seek.GetSize() == this->m_Mask.GetSize());
}
- virtual int GetBufLen() const override { return this->m_Seek.GetSize(); }
virtual void CheckOne(const void *where) override;
private:
@@ -152,9 +152,8 @@ class CStringScanner : public IScanner
{
public:
CStringScanner(ScanResults rtype, const char *str) :
- IScanner(rtype), m_Str(str) {}
+ IScanner(rtype, strlen(str) + 1), m_Str(str) {}
- virtual int GetBufLen() const override { return strlen(this->m_Str) + 1; }
virtual void CheckOne(const void *where) override;
private:
@@ -165,9 +164,8 @@ class CStringPrefixScanner : public IScanner
{
public:
CStringPrefixScanner(ScanResults rtype, const char *str) :
- IScanner(rtype), m_Str(str) {}
+ IScanner(rtype, strlen(str) + 1), m_Str(str) {}
- virtual int GetBufLen() const override { return strlen(this->m_Str) + 1; }
virtual void CheckOne(const void *where) override;
private:
@@ -175,11 +173,12 @@ class CStringPrefixScanner : public IScanner
};
+template
class IScan
{
protected:
- IScan(ScanDir dir, const IBounds& bounds, int align, const std::vector& scanners) :
- m_Dir(dir), m_Bounds(bounds), m_Align(align), m_Scanners(scanners) {}
+ IScan(const IBounds& bounds, const std::vector& scanners) :
+ m_Bounds(bounds), m_Scanners(scanners) {}
virtual ~IScan()
{
for (auto scanner : this->m_Scanners) {
@@ -191,17 +190,19 @@ class IScan
void DoScan();
private:
- ScanDir m_Dir;
+ template
+ void InternalScan();
+
const IBounds& m_Bounds;
- int m_Align;
std::vector m_Scanners;
};
-class CSingleScan : public IScan
+template
+class CSingleScan : public IScan
{
public:
- CSingleScan(ScanDir dir, const IBounds& bounds, int align, IScanner *scanner) :
- IScan(dir, bounds, align, { scanner }), m_Scanner(scanner)
+ CSingleScan(const IBounds& bounds, IScanner *scanner) :
+ IScan(bounds, { scanner }), m_Scanner(scanner)
{
this->DoScan();
}
@@ -212,16 +213,89 @@ class CSingleScan : public IScan
IScanner *m_Scanner;
};
-class CMultiScan : public IScan
+template
+class CMultiScan : public IScan
{
public:
- CMultiScan(ScanDir dir, const IBounds& bounds, int align, const std::vector& scanners) :
- IScan(dir, bounds, align, scanners)
+ CMultiScan(const IBounds& bounds, const std::vector& scanners) :
+ IScan(bounds, scanners)
{
this->DoScan();
}
};
+template
+class CThreadedScan
+{
+public:
+ CThreadedScan(const IBounds& bounds, const std::vector& scanners) :
+ m_Bounds(bounds)
+ {
+ for (auto scanner : scanners) {
+ this->m_WorkToDo.push_back(scanner);
+ }
+
+ int n_threads = Min(THREADS, (int)scanners.size());
+
+ std::vector threads;
+ for (int i = 0; i < n_threads; ++i) {
+ DevMsg("CThreadedScan: creating thread #%d\n", i);
+ threads.emplace_back(&CThreadedScan::Worker, this, i);
+ }
+
+ for (auto& thread : threads) {
+ DevMsg("CThreadedScan: join\n");
+ thread.join();
+ }
+ DevMsg("CThreadedScan: done\n");
+ }
+
+private:
+ void Worker(int id)
+ {
+// DevMsg("CThreadedScan::Worker #%d BEGIN\n", id);
+
+ IScanner *scanner;
+
+ while ((scanner = this->GetWork()) != nullptr) {
+// DevMsg("CThreadedScan::Worker #%d SCAN BEGIN %08x\n", id, (uintptr_t)scanner);
+ CSingleScan *scan = new CSingleScan(this->m_Bounds, scanner);
+// DevMsg("CThreadedScan::Worker #%d SCAN END %08x\n", id, (uintptr_t)scanner);
+ this->SubmitResult(scan);
+ }
+
+// DevMsg("CThreadedScan::Worker #%d END\n", id);
+ }
+
+ IScanner *GetWork()
+ {
+ std::lock_guard lock(this->m_MutexToDo);
+
+ if (this->m_WorkToDo.empty()) {
+ return nullptr;
+ }
+
+ IScanner *work = this->m_WorkToDo.front();
+ this->m_WorkToDo.pop_front();
+ return work;
+ }
+
+ void SubmitResult(CSingleScan *result)
+ {
+ std::lock_guard lock(this->m_MutexDone);
+
+ this->m_WorkDone.emplace_back(std::unique_ptr>(result));
+ }
+
+ const IBounds& m_Bounds;
+
+ std::mutex m_MutexToDo;
+ std::list m_WorkToDo;
+
+ std::mutex m_MutexDone;
+ std::vector>> m_WorkDone;
+};
+
namespace Scan
{
@@ -229,4 +303,92 @@ namespace Scan
}
+template
+inline void IScan::DoScan()
+{
+ bool never_done = true;
+ for (auto scanner : this->m_Scanners) {
+ if (scanner->GetResultsType() != ScanResults::ALL) {
+ never_done = false;
+ break;
+ }
+ }
+
+ if (never_done) {
+ this->InternalScan();
+ } else {
+ this->InternalScan();
+ }
+}
+
+template template
+inline void IScan::InternalScan()
+{
+ constexpr bool FWD = (DIR == ScanDir::FORWARD);
+
+ for (auto scanner : this->m_Scanners) {
+ scanner->Reset();
+ }
+
+ const uint8_t *p_low = (const uint8_t *)this->m_Bounds.GetLowerBound();
+ const uint8_t *p_high = (const uint8_t *)this->m_Bounds.GetUpperBound();
+
+ const uint8_t *ptr = (FWD ? p_low : p_high - ALIGN);
+ const uint8_t *end = (FWD ? p_high : p_low - ALIGN);
+
+ uintptr_t rem = (uintptr_t)ptr % ALIGN;
+ if (rem != 0) {
+ if (FWD) {
+ ptr += (ALIGN - rem);
+ } else {
+ ptr -= rem;
+ }
+ }
+
+ constexpr int INCR = (FWD ? ALIGN : -ALIGN);
+
+// DevMsg("IScan::DoScan: ptr 0x%08x, end 0x%08x, incr %c0x%08x\n", (uintptr_t)ptr, (uintptr_t)end, (FWD ? '+' : '-'), ALIGN);
+
+ /* use an array because std::vector is horrendously slow here */
+ int num_scanners = this->m_Scanners.size();
+ IScanner **scanners = new IScanner *[num_scanners];
+ for (int i = 0; i < num_scanners; ++i) {
+ scanners[i] = this->m_Scanners[i];
+ }
+
+ while (FWD ? (ptr < end) : (ptr > end)) {
+// bool all_done = false;
+
+ for (int i = 0; i < num_scanners; ++i) {
+ IScanner *scanner = scanners[i];
+
+ if (!NEVER_DONE && scanner->IsDone()) {
+ continue;
+ }
+
+ if (ptr + scanner->GetBufLen() <= p_high) {
+ scanner->CheckOne(ptr);
+#if 0
+
+ if (scanner->IsDone()) {
+ scanners.erase(scanner);
+ if (scanners.empty()) {
+ all_done = true;
+ }
+ }
+#endif
+ }
+ }
+
+// if (all_done) {
+// break;
+// }
+
+ ptr += INCR;
+ }
+
+ delete[] scanners;
+}
+
+
#endif
diff --git a/mod/debug_airblastbox.cpp b/mod/debug_airblastbox.cpp
deleted file mode 100644
index da9247ed..00000000
--- a/mod/debug_airblastbox.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-#include "mod.h"
-#include "sm/detours.h"
-#include "stub/stub.h"
-#include "util/util.h"
-
-
-#ifdef EXPERIMENTAL
-
-static ConVar cvar_duration("sigsegv_debug_airblastbox_duration", "0.1", FCVAR_NOTIFY,
- "Debug: box draw duration");
-
-static ConVar cvar_color_r("sigsegv_debug_airblastbox_color_r", "255", FCVAR_NOTIFY,
- "Debug: box color (red)");
-static ConVar cvar_color_g("sigsegv_debug_airblastbox_color_g", "255", FCVAR_NOTIFY,
- "Debug: box color (green)");
-static ConVar cvar_color_b("sigsegv_debug_airblastbox_color_b", "255", FCVAR_NOTIFY,
- "Debug: box color (blue)");
-static ConVar cvar_color_a("sigsegv_debug_airblastbox_color_a", "255", FCVAR_NOTIFY,
- "Debug: box color (alpha)");
-
-
-static RefCount rc_CTFWeaponBase_DeflectProjectiles;
-DETOUR_DECL_MEMBER(bool, CTFWeaponBase_DeflectProjectiles, void)
-{
- SCOPED_INCREMENT(rc_CTFWeaponBase_DeflectProjectiles);
- return DETOUR_MEMBER_CALL(CTFWeaponBase_DeflectProjectiles)();
-}
-
-DETOUR_DECL_STATIC(int, UTIL_EntitiesInBox, const Vector& mins, const Vector& maxs, CFlaggedEntitiesEnum *pEnum)
-{
- if (rc_CTFWeaponBase_DeflectProjectiles.NonZero()) {
- NDebugOverlay::Box(vec3_origin, mins, maxs,
- cvar_color_r.GetInt(),
- cvar_color_g.GetInt(),
- cvar_color_b.GetInt(),
- cvar_color_a.GetInt(),
- cvar_duration.GetFloat());
- }
-
- return DETOUR_STATIC_CALL(UTIL_EntitiesInBox)(mins, maxs, pEnum);
-}
-
-
-class CMod_Debug_AirblastBox : public IMod
-{
-public:
- CMod_Debug_AirblastBox() : IMod("Debug_AirblastBox")
- {
- MOD_ADD_DETOUR_MEMBER(CTFWeaponBase, DeflectProjectiles);
- MOD_ADD_DETOUR_GLOBAL(UTIL_EntitiesInBox);
- }
-
- void SetEnabled(bool enable)
- {
- this->ToggleAllDetours(enable);
- }
-};
-static CMod_Debug_AirblastBox s_Mod;
-
-
-static ConVar cvar_enable("sigsegv_debug_airblastbox_enable", "0", FCVAR_NOTIFY,
- "Debug: draw box used for airblast deflection of projectiles",
- [](IConVar *pConVar, const char *pOldValue, float flOldValue) {
- ConVarRef var(pConVar);
- s_Mod.SetEnabled(var.GetBool());
- });
-
-#endif
diff --git a/mod/debug_deflectcylinder.cpp b/mod/debug_deflectcylinder.cpp
deleted file mode 100644
index be04831f..00000000
--- a/mod/debug_deflectcylinder.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-#include "mod.h"
-#include "sm/detours.h"
-#include "stub/stub.h"
-#include "util/util.h"
-
-
-#ifdef EXPERIMENTAL
-
-static ConVar cvar_duration("sigsegv_debug_deflectcylinder_duration", "0.1", FCVAR_NOTIFY,
- "Debug: cylinder draw duration");
-
-static ConVar cvar_color_r("sigsegv_debug_deflectcylinder_color_r", "255", FCVAR_NOTIFY,
- "Debug: cylinder color (red)");
-static ConVar cvar_color_g("sigsegv_debug_deflectcylinder_color_g", "255", FCVAR_NOTIFY,
- "Debug: cylinder color (green)");
-static ConVar cvar_color_b("sigsegv_debug_deflectcylinder_color_b", "255", FCVAR_NOTIFY,
- "Debug: cylinder color (blue)");
-static ConVar cvar_color_a("sigsegv_debug_deflectcylinder_color_a", "0", FCVAR_NOTIFY,
- "Debug: cylinder color (alpha)");
-
-static ConVar cvar_sphere_enable("sigsegv_debug_deflectcylinder_sphere_enable", "0", FCVAR_NOTIFY,
- "Debug: sphere enable");
-static ConVar cvar_sphere_color_r("sigsegv_debug_deflectcylinder_sphere_color_r", "255", FCVAR_NOTIFY,
- "Debug: sphere color (red)");
-static ConVar cvar_sphere_color_g("sigsegv_debug_deflectcylinder_sphere_color_g", "255", FCVAR_NOTIFY,
- "Debug: sphere color (green)");
-static ConVar cvar_sphere_color_b("sigsegv_debug_deflectcylinder_sphere_color_b", "255", FCVAR_NOTIFY,
- "Debug: sphere color (blue)");
-static ConVar cvar_sphere_color_a("sigsegv_debug_deflectcylinder_sphere_color_a", "0", FCVAR_NOTIFY,
- "Debug: sphere color (alpha)");
-
-
-static RefCount rc_CTFMinigun_AttackProjectiles;
-DETOUR_DECL_MEMBER(void, CTFMinigun_AttackEnemyProjectiles, void)
-{
- SCOPED_INCREMENT(rc_CTFMinigun_AttackProjectiles);
- DETOUR_MEMBER_CALL(CTFMinigun_AttackEnemyProjectiles)();
-}
-
-static bool s_IsMiniBoss;
-DETOUR_DECL_MEMBER(bool, CTFPlayer_IsMiniBoss, void)
-{
- bool result = DETOUR_MEMBER_CALL(CTFPlayer_IsMiniBoss)();
- s_IsMiniBoss = result;
- return result;
-}
-
-DETOUR_DECL_STATIC(int, UTIL_EntitiesInSphere, const Vector& center, float radius, CFlaggedEntitiesEnum *pEnum)
-{
- if (rc_CTFMinigun_AttackProjectiles.NonZero() && cvar_sphere_enable.GetBool()) {
- NDebugOverlay::Sphere(center, QAngle(0.0f, 0.0f, 0.0f), radius,
- cvar_sphere_color_r.GetInt(),
- cvar_sphere_color_g.GetInt(),
- cvar_sphere_color_b.GetInt(),
- cvar_sphere_color_a.GetInt(),
- false,
- cvar_duration.GetFloat());
-
- NDebugOverlay::Sphere(center, QAngle(0.0f, 30.0f, 0.0f), radius,
- cvar_sphere_color_r.GetInt(),
- cvar_sphere_color_g.GetInt(),
- cvar_sphere_color_b.GetInt(),
- cvar_sphere_color_a.GetInt(),
- false,
- cvar_duration.GetFloat());
-
- NDebugOverlay::Sphere(center, QAngle(0.0f, 60.0f, 0.0f), radius,
- cvar_sphere_color_r.GetInt(),
- cvar_sphere_color_g.GetInt(),
- cvar_sphere_color_b.GetInt(),
- cvar_sphere_color_a.GetInt(),
- false,
- cvar_duration.GetFloat());
- }
-
- return DETOUR_STATIC_CALL(UTIL_EntitiesInSphere)(center, radius, pEnum);
-}
-
-DETOUR_DECL_STATIC(float, CalcDistanceToLineSegment, const Vector& P, const Vector& vLineA, const Vector& vLineB, float *t)
-{
- float dist = DETOUR_STATIC_CALL(CalcDistanceToLineSegment)(P, vLineA, vLineB, t);
-
- if (rc_CTFMinigun_AttackProjectiles.NonZero()) {
- float radius = (s_IsMiniBoss ? 56.0f : 38.0f);
- bool is_inside = (dist <= radius);
-
- NDebugOverlay::Cross3D(P, 10.0f,
- (is_inside ? 0 : 255),
- (is_inside ? 255 : 0),
- 0,
- true,
- cvar_duration.GetFloat());
-
- static int lastframe = -1;
- if (gpGlobals->framecount != lastframe) {
- lastframe = gpGlobals->framecount;
-
- /*NDebugOverlay::Line(vLineA, vLineB,
- cvar_color_r.GetFloat(),
- cvar_color_g.GetFloat(),
- cvar_color_b.GetFloat(),
- true,
- cvar_duration.GetFloat());*/
-
- QAngle ang;
- VectorAngles(vLineA - vLineB, ang);
-
- for (int i = 0; i <= 10; ++i) {
- float pA = ((float)i / 10.0f);
- float pB = (1.0f - pA);
-
- NDebugOverlay::Circle((pA * vLineA) + (pB * vLineB), ang, radius,
- cvar_color_r.GetFloat(),
- cvar_color_g.GetFloat(),
- cvar_color_b.GetFloat(),
- cvar_color_a.GetFloat(),
- false,
- cvar_duration.GetFloat());
- }
- }
- }
-
- return dist;
-}
-
-
-class CMod_Debug_DeflectCylinder : public IMod
-{
-public:
- CMod_Debug_DeflectCylinder() : IMod("Debug_DeflectCylinder")
- {
- MOD_ADD_DETOUR_MEMBER(CTFMinigun, AttackEnemyProjectiles);
- MOD_ADD_DETOUR_MEMBER(CTFPlayer, IsMiniBoss);
- MOD_ADD_DETOUR_GLOBAL(UTIL_EntitiesInSphere);
- MOD_ADD_DETOUR_GLOBAL(CalcDistanceToLineSegment);
- }
-
- void SetEnabled(bool enable)
- {
- this->ToggleAllDetours(enable);
- }
-};
-static CMod_Debug_DeflectCylinder s_Mod;
-
-
-static ConVar cvar_enable("sigsegv_debug_deflectcylinder_enable", "0", FCVAR_NOTIFY,
- "Debug: draw cylinder used for heavy's projectile deflection upgrade",
- [](IConVar *pConVar, const char *pOldValue, float flOldValue) {
- ConVarRef var(pConVar);
- s_Mod.SetEnabled(var.GetBool());
- });
-
-#endif
diff --git a/mod/debug_ehsphere.cpp b/mod/debug_ehsphere.cpp
deleted file mode 100644
index e93b4814..00000000
--- a/mod/debug_ehsphere.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-#include "mod.h"
-#include "sm/detours.h"
-#include "stub/stub.h"
-#include "util/util.h"
-
-
-#ifdef EXPERIMENTAL
-
-static ConVar cvar_duration("sigsegv_debug_ehsphere_duration", "0.5", FCVAR_NOTIFY,
- "Debug: EH sphere draw duration");
-
-static ConVar cvar_color_r("sigsegv_debug_ehsphere_color_r", "255", FCVAR_NOTIFY,
- "Debug: EH sphere color (red)");
-static ConVar cvar_color_g("sigsegv_debug_ehsphere_color_g", "255", FCVAR_NOTIFY,
- "Debug: EH sphere color (green)");
-static ConVar cvar_color_b("sigsegv_debug_ehsphere_color_b", "255", FCVAR_NOTIFY,
- "Debug: EH sphere color (blue)");
-static ConVar cvar_color_a("sigsegv_debug_ehsphere_color_a", "0", FCVAR_NOTIFY,
- "Debug: EH sphere color (alpha)");
-
-
-static RefCount rc_CTFSniperRifle_ExplosiveHeadShot;
-DETOUR_DECL_MEMBER(void, CTFSniperRifle_ExplosiveHeadShot, CTFPlayer *player1, CTFPlayer *player2)
-{
- SCOPED_INCREMENT(rc_CTFSniperRifle_ExplosiveHeadShot);
- DETOUR_MEMBER_CALL(CTFSniperRifle_ExplosiveHeadShot)(player1, player2);
-}
-
-DETOUR_DECL_STATIC(int, UTIL_EntitiesInSphere, const Vector& center, float radius, CFlaggedEntitiesEnum *pEnum)
-{
- if (rc_CTFSniperRifle_ExplosiveHeadShot.NonZero()) {
- for (int i = 0; i < 8; ++i) {
- NDebugOverlay::Sphere(center, QAngle(0.0f, (float)i * 22.5f, 0.0f), radius,
- cvar_color_r.GetInt(),
- cvar_color_g.GetInt(),
- cvar_color_b.GetInt(),
- cvar_color_a.GetInt(),
- false,
- cvar_duration.GetFloat());
- }
- }
-
- return DETOUR_STATIC_CALL(UTIL_EntitiesInSphere)(center, radius, pEnum);
-}
-
-
-class CMod_Debug_EHSphere : public IMod
-{
-public:
- CMod_Debug_EHSphere() : IMod("Debug_EHSphere")
- {
- MOD_ADD_DETOUR_MEMBER(CTFSniperRifle, ExplosiveHeadShot);
- MOD_ADD_DETOUR_GLOBAL(UTIL_EntitiesInSphere);
- }
-
- void SetEnabled(bool enable)
- {
- this->ToggleAllDetours(enable);
- }
-};
-static CMod_Debug_EHSphere s_Mod;
-
-
-static ConVar cvar_enable("sigsegv_debug_ehsphere_enable", "0", FCVAR_NOTIFY,
- "Debug: draw sphere used for sniper's explosive headshot upgrade",
- [](IConVar *pConVar, const char *pOldValue, float flOldValue) {
- ConVarRef var(pConVar);
- s_Mod.SetEnabled(var.GetBool());
- });
-
-#endif
diff --git a/mod/mod_autocollectspawncredits.cpp b/mod/mod_autocollectspawncredits.cpp
index 76f2acda..d980e334 100644
--- a/mod/mod_autocollectspawncredits.cpp
+++ b/mod/mod_autocollectspawncredits.cpp
@@ -5,50 +5,53 @@
#include "re/nextbot.h"
-static RefCount rc_CCurrencyPack_ComeToRest;
-DETOUR_DECL_MEMBER(void, CCurrencyPack_ComeToRest, void)
+namespace Mod_AutoCollectSpawnCredits
{
- SCOPED_INCREMENT(rc_CCurrencyPack_ComeToRest);
- return DETOUR_MEMBER_CALL(CCurrencyPack_ComeToRest)();
-}
-
-DETOUR_DECL_MEMBER(CNavArea *, CNavMesh_GetNavArea, const Vector& v1, float f1)
-{
- CNavArea *area = DETOUR_MEMBER_CALL(CNavMesh_GetNavArea)(v1, f1);
+ RefCount rc_CCurrencyPack_ComeToRest;
+ DETOUR_DECL_MEMBER(void, CCurrencyPack_ComeToRest)
+ {
+ SCOPED_INCREMENT(rc_CCurrencyPack_ComeToRest);
+ return DETOUR_MEMBER_CALL(CCurrencyPack_ComeToRest)();
+ }
- if (area != nullptr && rc_CCurrencyPack_ComeToRest.NonZero()) {
- TFNavAttributeType attr = reinterpret_cast(area)->GetTFAttributes();
+ DETOUR_DECL_MEMBER(CNavArea *, CNavMesh_GetNavArea, const Vector& v1, float f1)
+ {
+ CNavArea *area = DETOUR_MEMBER_CALL(CNavMesh_GetNavArea)(v1, f1);
- if ((attr & BLUE_SPAWN_ROOM) != 0) {
-// DevMsg("CCurrencyPack landed in BLUE_SPAWN_ROOM area; auto-collecting\n");
- return nullptr;
+ if (area != nullptr && rc_CCurrencyPack_ComeToRest.NonZero()) {
+ TFNavAttributeType attr = reinterpret_cast(area)->GetTFAttributes();
+
+ if ((attr & BLUE_SPAWN_ROOM) != 0) {
+ // DevMsg("CCurrencyPack landed in BLUE_SPAWN_ROOM area; auto-collecting\n");
+ return nullptr;
+ }
}
+
+ return area;
}
- return area;
-}
-
-
-class CMod_AutoCollectSpawnCredits : public IMod
-{
-public:
- CMod_AutoCollectSpawnCredits() : IMod("AutoCollectSpawnCredits")
- {
- MOD_ADD_DETOUR_MEMBER(CCurrencyPack, ComeToRest);
- MOD_ADD_DETOUR_MEMBER(CNavMesh, GetNavArea);
- }
- void SetEnabled(bool enable)
+ class CMod : public IMod
{
- this->ToggleAllDetours(enable);
- }
-};
-static CMod_AutoCollectSpawnCredits s_Mod;
-
-
-static ConVar cvar_enable("sigsegv_autocollectspawncredits_enable", "0", FCVAR_NOTIFY,
- "Mod: auto-collect credits that land in the bots' spawn area",
- [](IConVar *pConVar, const char *pOldValue, float flOldValue) {
- ConVarRef var(pConVar);
- s_Mod.SetEnabled(var.GetBool());
- });
+ public:
+ CMod() : IMod("AutoCollectSpawnCredits")
+ {
+ MOD_ADD_DETOUR_MEMBER(CCurrencyPack, ComeToRest);
+ MOD_ADD_DETOUR_MEMBER(CNavMesh, GetNavArea);
+ }
+
+ void SetEnabled(bool enable)
+ {
+ this->ToggleAllDetours(enable);
+ }
+ };
+ CMod s_Mod;
+
+
+ ConVar cvar_enable("sigsegv_autocollectspawncredits_enable", "0", FCVAR_NOTIFY,
+ "Mod: auto-collect credits that land in the bots' spawn area",
+ [](IConVar *pConVar, const char *pOldValue, float flOldValue) {
+ ConVarRef var(pConVar);
+ s_Mod.SetEnabled(var.GetBool());
+ });
+}
diff --git a/mod/mod_botmulticlassitem.cpp b/mod/mod_botmulticlassitem.cpp
index aca8045e..30acc0a3 100644
--- a/mod/mod_botmulticlassitem.cpp
+++ b/mod/mod_botmulticlassitem.cpp
@@ -3,148 +3,151 @@
#include "util/util.h"
-/* a less strict version of TranslateWeaponEntForClass (don't return empty strings) */
-static const char *TranslateWeaponEntForClass_improved(const char *name, int classnum)
+namespace Mod_BotMultiClassItem
{
- if (strcasecmp(name, "tf_weapon_shotgun") == 0) {
- switch (classnum) {
- case TF_CLASS_SOLDIER:
- return "tf_weapon_shotgun_soldier";
- case TF_CLASS_PYRO:
- return "tf_weapon_shotgun_pyro";
- case TF_CLASS_HEAVYWEAPONS:
- return "tf_weapon_shotgun_hwg";
- case TF_CLASS_ENGINEER:
- return "tf_weapon_shotgun_primary";
- default:
- return "tf_weapon_shotgun_primary";
+ /* a less strict version of TranslateWeaponEntForClass (don't return empty strings) */
+ const char *TranslateWeaponEntForClass_improved(const char *name, int classnum)
+ {
+ if (strcasecmp(name, "tf_weapon_shotgun") == 0) {
+ switch (classnum) {
+ case TF_CLASS_SOLDIER:
+ return "tf_weapon_shotgun_soldier";
+ case TF_CLASS_PYRO:
+ return "tf_weapon_shotgun_pyro";
+ case TF_CLASS_HEAVYWEAPONS:
+ return "tf_weapon_shotgun_hwg";
+ case TF_CLASS_ENGINEER:
+ return "tf_weapon_shotgun_primary";
+ default:
+ return "tf_weapon_shotgun_primary";
+ }
}
- }
-
- if (strcasecmp(name, "tf_weapon_pistol") == 0) {
- switch (classnum) {
- case TF_CLASS_SCOUT:
- return "tf_weapon_pistol_scout";
- case TF_CLASS_ENGINEER:
- return "tf_weapon_pistol";
- default:
- return "tf_weapon_pistol";
+
+ if (strcasecmp(name, "tf_weapon_pistol") == 0) {
+ switch (classnum) {
+ case TF_CLASS_SCOUT:
+ return "tf_weapon_pistol_scout";
+ case TF_CLASS_ENGINEER:
+ return "tf_weapon_pistol";
+ default:
+ return "tf_weapon_pistol";
+ }
}
- }
-
- if (strcasecmp(name, "tf_weapon_shovel") == 0 || strcasecmp(name, "tf_weapon_bottle") == 0) {
- switch (classnum) {
- case TF_CLASS_SOLDIER:
- return "tf_weapon_shovel";
- case TF_CLASS_DEMOMAN:
- return "tf_weapon_bottle";
+
+ if (strcasecmp(name, "tf_weapon_shovel") == 0 || strcasecmp(name, "tf_weapon_bottle") == 0) {
+ switch (classnum) {
+ case TF_CLASS_SOLDIER:
+ return "tf_weapon_shovel";
+ case TF_CLASS_DEMOMAN:
+ return "tf_weapon_bottle";
+ }
}
- }
-
- if (strcasecmp(name, "saxxy") == 0) {
- switch (classnum) {
- case TF_CLASS_SCOUT:
- return "tf_weapon_bat";
- case TF_CLASS_SOLDIER:
- return "tf_weapon_shovel";
- case TF_CLASS_PYRO:
- return "tf_weapon_fireaxe";
- case TF_CLASS_DEMOMAN:
- return "tf_weapon_bottle";
- case TF_CLASS_HEAVYWEAPONS:
- return "tf_weapon_fireaxe";
- case TF_CLASS_ENGINEER:
- return "tf_weapon_wrench";
- case TF_CLASS_MEDIC:
- return "tf_weapon_bonesaw";
- case TF_CLASS_SNIPER:
- return "tf_weapon_club";
- case TF_CLASS_SPY:
- return "tf_weapon_knife";
+
+ if (strcasecmp(name, "saxxy") == 0) {
+ switch (classnum) {
+ case TF_CLASS_SCOUT:
+ return "tf_weapon_bat";
+ case TF_CLASS_SOLDIER:
+ return "tf_weapon_shovel";
+ case TF_CLASS_PYRO:
+ return "tf_weapon_fireaxe";
+ case TF_CLASS_DEMOMAN:
+ return "tf_weapon_bottle";
+ case TF_CLASS_HEAVYWEAPONS:
+ return "tf_weapon_fireaxe";
+ case TF_CLASS_ENGINEER:
+ return "tf_weapon_wrench";
+ case TF_CLASS_MEDIC:
+ return "tf_weapon_bonesaw";
+ case TF_CLASS_SNIPER:
+ return "tf_weapon_club";
+ case TF_CLASS_SPY:
+ return "tf_weapon_knife";
+ }
}
- }
-
- if (strcasecmp(name, "tf_weapon_throwable") == 0) {
- switch (classnum) {
- case TF_CLASS_MEDIC:
- return "tf_weapon_throwable_primary";
- default:
- return "tf_weapon_throwable_secondary";
+
+ if (strcasecmp(name, "tf_weapon_throwable") == 0) {
+ switch (classnum) {
+ case TF_CLASS_MEDIC:
+ return "tf_weapon_throwable_primary";
+ default:
+ return "tf_weapon_throwable_secondary";
+ }
}
- }
-
- if (strcasecmp(name, "tf_weapon_parachute") == 0) {
- switch (classnum) {
- case TF_CLASS_SOLDIER:
- return "tf_weapon_parachute_secondary";
- case TF_CLASS_DEMOMAN:
- return "tf_weapon_parachute_primary";
- default:
- return "tf_weapon_parachute";
+
+ if (strcasecmp(name, "tf_weapon_parachute") == 0) {
+ switch (classnum) {
+ case TF_CLASS_SOLDIER:
+ return "tf_weapon_parachute_secondary";
+ case TF_CLASS_DEMOMAN:
+ return "tf_weapon_parachute_primary";
+ default:
+ return "tf_weapon_parachute";
+ }
}
- }
-
- if (strcasecmp(name, "tf_weapon_revolver") == 0) {
- switch (classnum) {
- case TF_CLASS_ENGINEER:
- return "tf_weapon_revolver_secondary";
- default:
- return "tf_weapon_revolver";
+
+ if (strcasecmp(name, "tf_weapon_revolver") == 0) {
+ switch (classnum) {
+ case TF_CLASS_ENGINEER:
+ return "tf_weapon_revolver_secondary";
+ default:
+ return "tf_weapon_revolver";
+ }
}
+
+ /* if not handled: return original entity name, not an empty string */
+ return name;
}
- /* if not handled: return original entity name, not an empty string */
- return name;
-}
-
-
-static RefCount rc_CTFBot_AddItem;
-static int bot_classnum = TF_CLASS_UNDEFINED;
-DETOUR_DECL_MEMBER(void, CTFBot_AddItem, const char *item)
-{
- SCOPED_INCREMENT(rc_CTFBot_AddItem);
- bot_classnum = reinterpret_cast(this)->GetPlayerClass()->GetClassIndex();
- DETOUR_MEMBER_CALL(CTFBot_AddItem)(item);
-}
-
-DETOUR_DECL_STATIC(CBaseEntity *, CreateEntityByName, const char *className, int iForceEdictIndex)
-{
- if (rc_CTFBot_AddItem.NonZero()) {
- className = TranslateWeaponEntForClass_improved(className, bot_classnum);
- }
- return DETOUR_STATIC_CALL(CreateEntityByName)(className, iForceEdictIndex);
-}
-
-
-// void CTFBot::AddItem(const char *name)
-// - CBaseEntity *CItemGeneration::GenerateRandomItem(CItemSelectionCriteria *criteria, const Vector& pos, const QAngle& ang)
-// - CBaseEntity *CItemGeneration::SpawnItem(int itemdefidx, const Vector& pos, const QAngle& ang, int, int, const char *class_or_null)
-// - CBaseEntity *CreateEntityByName(const char *className, int iForceEdictIndex)
-
-// const char *TranslateWeaponEntForClass(const char *name, int classnum)
-
-
-class CMod_BotMultiClassItem : public IMod
-{
-public:
- CMod_BotMultiClassItem() : IMod("BotMultiClassItem")
+ RefCount rc_CTFBot_AddItem;
+ int bot_classnum = TF_CLASS_UNDEFINED;
+ DETOUR_DECL_MEMBER(void, CTFBot_AddItem, const char *item)
{
- MOD_ADD_DETOUR_MEMBER(CTFBot, AddItem);
- MOD_ADD_DETOUR_GLOBAL(CreateEntityByName);
+ SCOPED_INCREMENT(rc_CTFBot_AddItem);
+ bot_classnum = reinterpret_cast(this)->GetPlayerClass()->GetClassIndex();
+ DETOUR_MEMBER_CALL(CTFBot_AddItem)(item);
}
- void SetEnabled(bool enable)
+ DETOUR_DECL_STATIC(CBaseEntity *, CreateEntityByName, const char *className, int iForceEdictIndex)
{
- this->ToggleAllDetours(enable);
+ if (rc_CTFBot_AddItem.NonZero()) {
+ className = TranslateWeaponEntForClass_improved(className, bot_classnum);
+ }
+
+ return DETOUR_STATIC_CALL(CreateEntityByName)(className, iForceEdictIndex);
}
-};
-static CMod_BotMultiClassItem s_Mod;
-
-
-static ConVar cvar_enable("sigsegv_botmulticlassitem_enable", "0", FCVAR_NOTIFY,
- "Mod: remap item entity names so bots can be given multi-class weapons",
- [](IConVar *pConVar, const char *pOldValue, float flOldValue) {
- ConVarRef var(pConVar);
- s_Mod.SetEnabled(var.GetBool());
- });
+
+
+ // void CTFBot::AddItem(const char *name)
+ // - CBaseEntity *CItemGeneration::GenerateRandomItem(CItemSelectionCriteria *criteria, const Vector& pos, const QAngle& ang)
+ // - CBaseEntity *CItemGeneration::SpawnItem(int itemdefidx, const Vector& pos, const QAngle& ang, int, int, const char *class_or_null)
+ // - CBaseEntity *CreateEntityByName(const char *className, int iForceEdictIndex)
+
+ // const char *TranslateWeaponEntForClass(const char *name, int classnum)
+
+
+ class CMod : public IMod
+ {
+ public:
+ CMod() : IMod("BotMultiClassItem")
+ {
+ MOD_ADD_DETOUR_MEMBER(CTFBot, AddItem);
+ MOD_ADD_DETOUR_GLOBAL(CreateEntityByName);
+ }
+
+ void SetEnabled(bool enable)
+ {
+ this->ToggleAllDetours(enable);
+ }
+ };
+ CMod s_Mod;
+
+
+ ConVar cvar_enable("sigsegv_botmulticlassitem_enable", "0", FCVAR_NOTIFY,
+ "Mod: remap item entity names so bots can be given multi-class weapons",
+ [](IConVar *pConVar, const char *pOldValue, float flOldValue) {
+ ConVarRef var(pConVar);
+ s_Mod.SetEnabled(var.GetBool());
+ });
+}
diff --git a/mod/mod_engienopush.cpp b/mod/mod_engienopush.cpp
index 9dc7771f..bd16d4fe 100644
--- a/mod/mod_engienopush.cpp
+++ b/mod/mod_engienopush.cpp
@@ -5,71 +5,63 @@
#include "re/nextbot.h"
-static RefCount rc_TeleSpawn_Update;
-DETOUR_DECL_MEMBER(ActionResult, CTFBotMvMEngineerTeleportSpawn_Update, CTFBot *actor, float dt)
+namespace Mod_EngieNoPush
{
- SCOPED_INCREMENT(rc_TeleSpawn_Update);
- return DETOUR_MEMBER_CALL(CTFBotMvMEngineerTeleportSpawn_Update)(actor, dt);
-}
-
-static RefCount rc_BuildSentry_Update;
-DETOUR_DECL_MEMBER(ActionResult, CTFBotMvMEngineerBuildSentryGun_Update, CTFBot *actor, float dt)
-{
- SCOPED_INCREMENT(rc_BuildSentry_Update);
- return DETOUR_MEMBER_CALL(CTFBotMvMEngineerBuildSentryGun_Update)(actor, dt);
-}
-
-static RefCount rc_BuildTele_Update;
-DETOUR_DECL_MEMBER(ActionResult, CTFBotMvMEngineerBuildTeleportExit_Update, CTFBot *actor, float dt)
-{
- SCOPED_INCREMENT(rc_BuildTele_Update);
- return DETOUR_MEMBER_CALL(CTFBotMvMEngineerBuildTeleportExit_Update)(actor, dt);
-}
-
-#if 0
-DETOUR_DECL_MEMBER(void, CTFGameRules_PushAllPlayersAway, const Vector *v1, float f1, float f2, int i1, CUtlVector *vec)
-{
- if (rc_TeleSpawn_Update.NonZero() || rc_BuildSentry_Update.NonZero() || rc_BuildTele_Update.NonZero()) {
- return;
+ RefCount rc_TeleSpawn_Update;
+ DETOUR_DECL_MEMBER(ActionResult, CTFBotMvMEngineerTeleportSpawn_Update, CTFBot *actor, float dt)
+ {
+ SCOPED_INCREMENT(rc_TeleSpawn_Update);
+ return DETOUR_MEMBER_CALL(CTFBotMvMEngineerTeleportSpawn_Update)(actor, dt);
}
- DETOUR_MEMBER_CALL(CTFGameRules_PushAllPlayersAway)(v1, f1, f2, i1, vec);
-}
-#endif
-
-DETOUR_DECL_MEMBER(void, CTFPlayer_ApplyAbsVelocityImpulse, const Vector *v1)
-{
- if (rc_TeleSpawn_Update.NonZero() || rc_BuildSentry_Update.NonZero() || rc_BuildTele_Update.NonZero()) {
- return;
+ RefCount rc_BuildSentry_Update;
+ DETOUR_DECL_MEMBER(ActionResult, CTFBotMvMEngineerBuildSentryGun_Update, CTFBot *actor, float dt)
+ {
+ SCOPED_INCREMENT(rc_BuildSentry_Update);
+ return DETOUR_MEMBER_CALL(CTFBotMvMEngineerBuildSentryGun_Update)(actor, dt);
}
- DETOUR_MEMBER_CALL(CTFPlayer_ApplyAbsVelocityImpulse)(v1);
-}
-
-
-class CMod_EngieNoPush : public IMod
-{
-public:
- CMod_EngieNoPush() : IMod("EngieNoPush")
+ RefCount rc_BuildTele_Update;
+ DETOUR_DECL_MEMBER(ActionResult, CTFBotMvMEngineerBuildTeleportExit_Update, CTFBot *actor, float dt)
{
- MOD_ADD_DETOUR_MEMBER(CTFBotMvMEngineerTeleportSpawn, Update);
- MOD_ADD_DETOUR_MEMBER(CTFBotMvMEngineerBuildSentryGun, Update);
- MOD_ADD_DETOUR_MEMBER(CTFBotMvMEngineerBuildTeleportExit, Update);
- //MOD_ADD_DETOUR_MEMBER(CTFGameRules, PushAllPlayersAway);
- MOD_ADD_DETOUR_MEMBER(CTFPlayer, ApplyAbsVelocityImpulse);
+ SCOPED_INCREMENT(rc_BuildTele_Update);
+ return DETOUR_MEMBER_CALL(CTFBotMvMEngineerBuildTeleportExit_Update)(actor, dt);
}
- void SetEnabled(bool enable)
+ /* would prefer to detour CTFGameRules::PushAllPlayersAway, but it's hard to reliably locate on Windows */
+ DETOUR_DECL_MEMBER(void, CTFPlayer_ApplyAbsVelocityImpulse, const Vector *v1)
{
- this->ToggleAllDetours(enable);
+ if (rc_TeleSpawn_Update.NonZero() || rc_BuildSentry_Update.NonZero() || rc_BuildTele_Update.NonZero()) {
+ return;
+ }
+
+ DETOUR_MEMBER_CALL(CTFPlayer_ApplyAbsVelocityImpulse)(v1);
}
-};
-static CMod_EngieNoPush s_Mod;
-
-
-static ConVar cvar_enable("sigsegv_engienopush_enable", "0", FCVAR_NOTIFY,
- "Mod: remove engiebot push force when spawning and building",
- [](IConVar *pConVar, const char *pOldValue, float flOldValue) {
- ConVarRef var(pConVar);
- s_Mod.SetEnabled(var.GetBool());
- });
+
+
+ class CMod : public IMod
+ {
+ public:
+ CMod() : IMod("EngieNoPush")
+ {
+ MOD_ADD_DETOUR_MEMBER(CTFBotMvMEngineerTeleportSpawn, Update);
+ MOD_ADD_DETOUR_MEMBER(CTFBotMvMEngineerBuildSentryGun, Update);
+ MOD_ADD_DETOUR_MEMBER(CTFBotMvMEngineerBuildTeleportExit, Update);
+ MOD_ADD_DETOUR_MEMBER(CTFPlayer, ApplyAbsVelocityImpulse);
+ }
+
+ void SetEnabled(bool enable)
+ {
+ this->ToggleAllDetours(enable);
+ }
+ };
+ CMod s_Mod;
+
+
+ ConVar cvar_enable("sigsegv_engienopush_enable", "0", FCVAR_NOTIFY,
+ "Mod: remove engiebot push force when spawning and building",
+ [](IConVar *pConVar, const char *pOldValue, float flOldValue) {
+ ConVarRef var(pConVar);
+ s_Mod.SetEnabled(var.GetBool());
+ });
+}
diff --git a/mod/mod_robotheadnoslide.cpp b/mod/mod_robotheadnoslide.cpp
new file mode 100644
index 00000000..3f68f737
--- /dev/null
+++ b/mod/mod_robotheadnoslide.cpp
@@ -0,0 +1,56 @@
+#include "mod.h"
+#include "util/scope.h"
+#include "sm/detours.h"
+#include "stub/gamerules.h"
+#include "stub/tfplayer.h"
+
+
+namespace Mod_RobotHeadNoSlide
+{
+ RefCount rc_TFPlayerThink;
+ DETOUR_DECL_MEMBER(void, CTFPlayer_TFPlayerThink)
+ {
+ SCOPED_INCREMENT(rc_TFPlayerThink);
+ DETOUR_MEMBER_CALL(CTFPlayer_TFPlayerThink)();
+ }
+
+ DETOUR_DECL_MEMBER(void, CTFPlayer_ApplyAbsVelocityImpulse, const Vector *v1)
+ {
+ if (rc_TFPlayerThink.NonZero() && v1->z == 100.0f &&
+ TFGameRules() && TFGameRules()->IsMannVsMachineMode()) {
+ CTFPlayer *player = reinterpret_cast(this);
+
+ CBaseEntity *groundent = player->GetGroundEntity();
+ if (groundent != nullptr && groundent->IsPlayer()) {
+ return;
+ }
+ }
+
+ DETOUR_MEMBER_CALL(CTFPlayer_ApplyAbsVelocityImpulse)(v1);
+ }
+
+
+ class CMod : public IMod
+ {
+ public:
+ CMod() : IMod("RobotHeadNoSlide")
+ {
+ MOD_ADD_DETOUR_MEMBER(CTFPlayer, TFPlayerThink);
+ // MOD_ADD_DETOUR_MEMBER(CTFPlayer, ApplyAbsVelocityImpulse);
+ }
+
+ void SetEnabled(bool enable)
+ {
+ this->ToggleAllDetours(enable);
+ }
+ };
+ CMod s_Mod;
+
+
+ ConVar cvar_enable("sigsegv_robotheadnoslide_enable", "0", FCVAR_NOTIFY,
+ "Mod: remove the sliding force that prevents players from standing on robots' heads",
+ [](IConVar *pConVar, const char *pOldValue, float flOldValue) {
+ ConVarRef var(pConVar);
+ s_Mod.SetEnabled(var.GetBool());
+ });
+}
diff --git a/mod/mod_robottickle.cpp b/mod/mod_robottickle.cpp
index 69e8f488..0b90263f 100644
--- a/mod/mod_robottickle.cpp
+++ b/mod/mod_robottickle.cpp
@@ -1,40 +1,50 @@
#include "mod.h"
#include "sm/detours.h"
-#include "stub/stub.h"
+#include "stub/tfplayer.h"
-#ifdef EXPERIMENTAL
+#if defined __GNUC__
+#warning TODO: move cvars into class
+#endif
+
-DETOUR_DECL_MEMBER(bool, CTFPlayer_CanBeForcedToLaugh)
+namespace Mod_RobotTickle
{
+ ConVar cvar_giants("sigsegv_robottickle_giants", "0", FCVAR_NOTIFY,
+ "Mod: if set to 0, tickling will only be enabled for non-giant robots");
- return DETOUR_MEMBER_CALL(CTFPlayer_CanBeForcedToLaugh)();
-}
-
-
-class CMod_RobotTickle : public IMod
-{
-public:
- CMod_RobotTickle() : IMod("RobotTickle")
+ DETOUR_DECL_MEMBER(bool, CTFPlayer_CanBeForcedToLaugh)
{
+ if (!cvar_giants.GetBool()) {
+ CTFPlayer *player = reinterpret_cast(this);
+ return !player->IsMiniBoss();
+ }
+ return true;
}
- void SetEnabled(bool enable)
+
+ class CMod : public IMod
{
- this->ToggleAllDetours(enable);
- }
-};
-static CMod_RobotTickle s_Mod;
-
-
-/*static ConVar cvar_enable("sigsegv_robottickle_enable", "0", FCVAR_NOTIFY,
- "Mod: auto-collect credits that land in the bots' spawn area",
- [](IConVar *pConVar, const char *pOldValue, float flOldValue) {
- ConVarRef var(pConVar);
- s_Mod.SetEnabled(var.GetBool());
- });
-*/
-
-#endif
+ public:
+ CMod() : IMod("RobotTickle")
+ {
+ MOD_ADD_DETOUR_MEMBER(CTFPlayer, CanBeForcedToLaugh);
+ }
+
+ void SetEnabled(bool enable)
+ {
+ this->ToggleAllDetours(enable);
+ }
+ };
+ CMod s_Mod;
+
+
+ ConVar cvar_enable("sigsegv_robottickle_enable", "0", FCVAR_NOTIFY,
+ "Mod: allow robots to be tickled (e.g. by the Holiday Punch)",
+ [](IConVar *pConVar, const char *pOldValue, float flOldValue) {
+ ConVarRef var(pConVar);
+ s_Mod.SetEnabled(var.GetBool());
+ });
+}
diff --git a/mod/mod_sniperchargeuncap.cpp b/mod/mod_sniperchargeuncap.cpp
index 5eacd7f9..8b3bb572 100644
--- a/mod/mod_sniperchargeuncap.cpp
+++ b/mod/mod_sniperchargeuncap.cpp
@@ -1,131 +1,134 @@
#include "mod.h"
-#if defined _LINUX
-
-static constexpr uint8_t s_Buf[] = {
- 0xf3, 0x0f, 0x10, 0x45, 0x00, // +0000 movss xmm0,DWORD PTR [ebp-0xXX]
- 0xf3, 0x0f, 0x58, 0x45, 0x00, // +0005 addss xmm0,DWORD PTR [ebp-0xXX]
- 0xf3, 0x0f, 0x10, 0xc8, // +000A movss xmm1,xmm0
- 0x0f, 0x28, 0xc1, // +000E movaps xmm0,xmm1
- 0x0f, 0x57, 0xc9, // +0011 xorps xmm1,xmm1
- 0xf3, 0x0f, 0x5f, 0xc1, // +0014 maxss xmm0,xmm1
- 0xf3, 0x0f, 0x5d, 0x05, 0x00, 0x00, 0x00, 0x00, // +0018 minss xmm0,DWORD PTR [XXXXXXXX]
- 0xf3, 0x0f, 0x11, 0x45, 0x00, // +0020 movss DWORD PTR [ebp-0xXX],xmm0
- 0xf3, 0x0f, 0x59, 0x40, 0x10, // +0025 mulss xmm0,DWORD PTR [eax+0x10]
-};
-
-struct CPatch_UncapChargeRate_Common : public IPatch
+namespace Mod_SniperChargeUncap
{
- CPatch_UncapChargeRate_Common() : IPatch(sizeof(s_Buf)) {}
+#if defined _LINUX
+
+ constexpr uint8_t s_Buf[] = {
+ 0xf3, 0x0f, 0x10, 0x45, 0x00, // +0000 movss xmm0,DWORD PTR [ebp-0xXX]
+ 0xf3, 0x0f, 0x58, 0x45, 0x00, // +0005 addss xmm0,DWORD PTR [ebp-0xXX]
+ 0xf3, 0x0f, 0x10, 0xc8, // +000A movss xmm1,xmm0
+ 0x0f, 0x28, 0xc1, // +000E movaps xmm0,xmm1
+ 0x0f, 0x57, 0xc9, // +0011 xorps xmm1,xmm1
+ 0xf3, 0x0f, 0x5f, 0xc1, // +0014 maxss xmm0,xmm1
+ 0xf3, 0x0f, 0x5d, 0x05, 0x00, 0x00, 0x00, 0x00, // +0018 minss xmm0,DWORD PTR [XXXXXXXX]
+ 0xf3, 0x0f, 0x11, 0x45, 0x00, // +0020 movss DWORD PTR [ebp-0xXX],xmm0
+ 0xf3, 0x0f, 0x59, 0x40, 0x10, // +0025 mulss xmm0,DWORD PTR [eax+0x10]
+ };
- virtual void GetVerifyInfo(ByteBuf& buf, ByteBuf& mask) const override
+ struct CPatch_UncapChargeRate_Common : public IPatch
{
- buf.CopyFrom(s_Buf);
+ CPatch_UncapChargeRate_Common() : IPatch(sizeof(s_Buf)) {}
- mask.SetRange(0x05 + 4, 1, 0x00);
- mask.SetRange(0x00 + 4, 1, 0x00);
- mask.SetRange(0x18 + 4, 4, 0x00);
- mask.SetRange(0x20 + 4, 1, 0x00);
- }
+ virtual void GetVerifyInfo(ByteBuf& buf, ByteBuf& mask) const override
+ {
+ buf.CopyFrom(s_Buf);
+
+ mask.SetRange(0x05 + 4, 1, 0x00);
+ mask.SetRange(0x00 + 4, 1, 0x00);
+ mask.SetRange(0x18 + 4, 4, 0x00);
+ mask.SetRange(0x20 + 4, 1, 0x00);
+ }
+
+ virtual void GetPatchInfo(ByteBuf& buf, ByteBuf& mask) const override
+ {
+ /* NOP out the MINSS instruction */
+ buf.SetRange(0x18, 8, 0x90);
+ mask.SetRange(0x18, 8, 0xff);
+ }
+ };
- virtual void GetPatchInfo(ByteBuf& buf, ByteBuf& mask) const override
+ struct CPatch_UncapChargeRate_CTFSniperRifle : public CPatch_UncapChargeRate_Common
{
- /* NOP out the MINSS instruction */
- buf.SetRange(0x18, 8, 0x90);
- mask.SetRange(0x18, 8, 0xff);
- }
-};
-
-struct CPatch_UncapChargeRate_CTFSniperRifle : public CPatch_UncapChargeRate_Common
-{
- virtual const char *GetFuncName() const override { return "CTFSniperRifle::ItemPostFrame"; }
- virtual uint32_t GetFuncOffMin() const override { return 0x0000; }
- virtual uint32_t GetFuncOffMax() const override { return 0x0380; } // @ 0x026a
-};
-
-struct CPatch_UncapChargeRate_CTFSniperRifleClassic : CPatch_UncapChargeRate_Common
-{
- virtual const char *GetFuncName() const override { return "CTFSniperRifleClassic::ItemPostFrame"; }
- virtual uint32_t GetFuncOffMin() const override { return 0x0000; }
- virtual uint32_t GetFuncOffMax() const override { return 0x0280; } // @ 0x0146
-};
-
+ virtual const char *GetFuncName() const override { return "CTFSniperRifle::ItemPostFrame"; }
+ virtual uint32_t GetFuncOffMin() const override { return 0x0000; }
+ virtual uint32_t GetFuncOffMax() const override { return 0x0380; } // @ 0x026a
+ };
+
+ struct CPatch_UncapChargeRate_CTFSniperRifleClassic : CPatch_UncapChargeRate_Common
+ {
+ virtual const char *GetFuncName() const override { return "CTFSniperRifleClassic::ItemPostFrame"; }
+ virtual uint32_t GetFuncOffMin() const override { return 0x0000; }
+ virtual uint32_t GetFuncOffMax() const override { return 0x0280; } // @ 0x0146
+ };
+
#elif defined _WINDOWS
-
-static constexpr uint8_t s_Buf[] = {
- 0xa1, 0x00, 0x00, 0x00, 0x00, // +0000 mov eax,DWORD PTR [xxxxxxxx]
- 0xf3, 0x0f, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, // +0005 movss xmm0,DWORD PTR [xxxxxxxx]
- 0xf3, 0x0f, 0x10, 0x50, 0x10, // +000D movss xmm2,DWORD PTR [eax+0x10]
- 0xd9, 0x5d, 0x00, // +0012 fstp [ebp-0xXX]
- 0xf3, 0x0f, 0x10, 0x4d, 0x00, // +0015 movss xmm1,[ebp-0xXX]
- 0xf3, 0x0f, 0x5f, 0xc8, // +001A maxss xmm1,xmm0
- 0xf3, 0x0f, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, // +001E movss xmm0,DWORD PTR [xxxxxxxx]
- 0xf3, 0x0f, 0x5d, 0xc8, // +0026 minss xmm1,xmm0
- 0xf3, 0x0f, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, // +002A movss xmm0,DWORD PTR [xxxxxxxx]
-};
-
-struct CPatch_UncapChargeRate_Common : public IPatch
-{
- CPatch_UncapChargeRate_Common() : IPatch(sizeof(s_Buf)) {}
- virtual void GetVerifyInfo(ByteBuf& buf, ByteBuf& mask) const override
+ constexpr uint8_t s_Buf[] = {
+ 0xa1, 0x00, 0x00, 0x00, 0x00, // +0000 mov eax,DWORD PTR [xxxxxxxx]
+ 0xf3, 0x0f, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, // +0005 movss xmm0,DWORD PTR [xxxxxxxx]
+ 0xf3, 0x0f, 0x10, 0x50, 0x10, // +000D movss xmm2,DWORD PTR [eax+0x10]
+ 0xd9, 0x5d, 0x00, // +0012 fstp [ebp-0xXX]
+ 0xf3, 0x0f, 0x10, 0x4d, 0x00, // +0015 movss xmm1,[ebp-0xXX]
+ 0xf3, 0x0f, 0x5f, 0xc8, // +001A maxss xmm1,xmm0
+ 0xf3, 0x0f, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, // +001E movss xmm0,DWORD PTR [xxxxxxxx]
+ 0xf3, 0x0f, 0x5d, 0xc8, // +0026 minss xmm1,xmm0
+ 0xf3, 0x0f, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, // +002A movss xmm0,DWORD PTR [xxxxxxxx]
+ };
+
+ struct CPatch_UncapChargeRate_Common : public IPatch
{
- buf.CopyFrom(s_Buf);
+ CPatch_UncapChargeRate_Common() : IPatch(sizeof(s_Buf)) {}
+
+ virtual void GetVerifyInfo(ByteBuf& buf, ByteBuf& mask) const override
+ {
+ buf.CopyFrom(s_Buf);
+
+ mask.SetRange(0x00 + 1, 4, 0x00);
+ mask.SetRange(0x05 + 4, 4, 0x00);
+ mask.SetRange(0x12 + 2, 1, 0x00);
+ mask.SetRange(0x15 + 4, 1, 0x00);
+ mask.SetRange(0x1e + 4, 4, 0x00);
+ mask.SetRange(0x2a + 4, 4, 0x00);
+ }
- mask.SetRange(0x00 + 1, 4, 0x00);
- mask.SetRange(0x05 + 4, 4, 0x00);
- mask.SetRange(0x12 + 2, 1, 0x00);
- mask.SetRange(0x15 + 4, 1, 0x00);
- mask.SetRange(0x1e + 4, 4, 0x00);
- mask.SetRange(0x2a + 4, 4, 0x00);
- }
+ virtual void GetPatchInfo(ByteBuf& buf, ByteBuf& mask) const override
+ {
+ /* NOP out the MINSS instruction */
+ buf.SetRange(0x26, 4, 0x90);
+ mask.SetRange(0x26, 4, 0xff);
+ }
+ };
- virtual void GetPatchInfo(ByteBuf& buf, ByteBuf& mask) const override
+ struct CPatch_UncapChargeRate_CTFSniperRifle : public CPatch_UncapChargeRate_Common
{
- /* NOP out the MINSS instruction */
- buf.SetRange(0x26, 4, 0x90);
- mask.SetRange(0x26, 4, 0xff);
- }
-};
-
-struct CPatch_UncapChargeRate_CTFSniperRifle : public CPatch_UncapChargeRate_Common
-{
- virtual const char *GetFuncName() const override { return "CTFSniperRifle::ItemPostFrame"; }
- virtual uint32_t GetFuncOffMin() const override { return 0x0000; }
- virtual uint32_t GetFuncOffMax() const override { return 0x0200; } // @ 0x16e
-};
-
-struct CPatch_UncapChargeRate_CTFSniperRifleClassic : CPatch_UncapChargeRate_Common
-{
- virtual const char *GetFuncName() const override { return "CTFSniperRifleClassic::ItemPostFrame"; }
- virtual uint32_t GetFuncOffMin() const override { return 0x0000; }
- virtual uint32_t GetFuncOffMax() const override { return 0x0200; } // @ 0x162
-};
-
-#endif
-
-
-class CMod_SniperChargeUncap : public IMod
-{
-public:
- CMod_SniperChargeUncap() : IMod("SniperChargeUncap")
+ virtual const char *GetFuncName() const override { return "CTFSniperRifle::ItemPostFrame"; }
+ virtual uint32_t GetFuncOffMin() const override { return 0x0000; }
+ virtual uint32_t GetFuncOffMax() const override { return 0x0200; } // @ 0x16e
+ };
+
+ struct CPatch_UncapChargeRate_CTFSniperRifleClassic : CPatch_UncapChargeRate_Common
{
- this->AddPatch(new CPatch_UncapChargeRate_CTFSniperRifle());
- this->AddPatch(new CPatch_UncapChargeRate_CTFSniperRifleClassic());
- }
+ virtual const char *GetFuncName() const override { return "CTFSniperRifleClassic::ItemPostFrame"; }
+ virtual uint32_t GetFuncOffMin() const override { return 0x0000; }
+ virtual uint32_t GetFuncOffMax() const override { return 0x0200; } // @ 0x162
+ };
+
+#endif
- void SetEnabled(bool enable)
+
+ class CMod : public IMod
{
- this->ToggleAllPatches(enable);
- }
-};
-static CMod_SniperChargeUncap s_Mod;
-
-
-static ConVar cvar_enable("sigsegv_sniperchargeuncap_enable", "0", FCVAR_NOTIFY,
- "Mod: remove the 200 percent upper limit on sniper rifle charge rate",
- [](IConVar *pConVar, const char *pOldValue, float flOldValue) {
- ConVarRef var(pConVar);
- s_Mod.SetEnabled(var.GetBool());
- });
+ public:
+ CMod() : IMod("SniperChargeUncap")
+ {
+ this->AddPatch(new CPatch_UncapChargeRate_CTFSniperRifle());
+ this->AddPatch(new CPatch_UncapChargeRate_CTFSniperRifleClassic());
+ }
+
+ void SetEnabled(bool enable)
+ {
+ this->ToggleAllPatches(enable);
+ }
+ };
+ CMod s_Mod;
+
+
+ ConVar cvar_enable("sigsegv_sniperchargeuncap_enable", "0", FCVAR_NOTIFY,
+ "Mod: remove the 200 percent upper limit on sniper rifle charge rate",
+ [](IConVar *pConVar, const char *pOldValue, float flOldValue) {
+ ConVarRef var(pConVar);
+ s_Mod.SetEnabled(var.GetBool());
+ });
+}
diff --git a/mod/mod_stickypusher.cpp b/mod/mod_stickypusher.cpp
index e3d40726..aeef28d5 100644
--- a/mod/mod_stickypusher.cpp
+++ b/mod/mod_stickypusher.cpp
@@ -5,139 +5,142 @@
#ifdef EXPERIMENTAL
-constexpr int MISSION_PUSHSTICKIES = 7;
-
-
-class CTFBotMissionPushStickies : public Action
+namespace Mod_StickyPusher
{
-public:
- CTFBotMissionPushStickies()
- {
- DevMsg("CTFBotMissionPushStickies::CTFBotMissionPushStickies\n");
-
- // TODO
- }
+ constexpr int MISSION_PUSHSTICKIES = 7;
- virtual ~CTFBotMissionPushStickies()
+
+ class CTFBotMissionPushStickies : public Action
{
- DevMsg("CTFBotMissionPushStickies::~CTFBotMissionPushStickies\n");
+ public:
+ CTFBotMissionPushStickies()
+ {
+ DevMsg("CTFBotMissionPushStickies::CTFBotMissionPushStickies\n");
+
+ // TODO
+ }
- // TODO
- }
+ virtual ~CTFBotMissionPushStickies()
+ {
+ DevMsg("CTFBotMissionPushStickies::~CTFBotMissionPushStickies\n");
+
+ // TODO
+ }
+
+ virtual const char *GetName() const override { return "MissionPushStickies"; }
+
+ virtual ActionResult OnStart(CTFBot *actor, Action *action) override
+ {
+ DevMsg("CTFBotMissionPushStickies::OnStart(#%d)\n", ENTINDEX(actor));
+
+ // TODO
+
+ return ActionResult::Continue();
+ }
+
+ virtual ActionResult Update(CTFBot *actor, float dt) override
+ {
+ DevMsg("CTFBotMissionPushStickies::Update(#%d)\n", ENTINDEX(actor));
+
+ CTFBotPathCost cost_func((CTFBot *)0x11111111, RETREAT_ROUTE);
+
+
+ // TODO
+
+ return ActionResult::Continue();
+ }
+
+ virtual void OnEnd(CTFBot *actor, Action *action) override
+ {
+ DevMsg("CTFBotMissionPushStickies::OnEnd(#%d)\n", ENTINDEX(actor));
+
+ // TODO
+ }
+ };
- virtual const char *GetName() const override { return "MissionPushStickies"; }
- virtual ActionResult OnStart(CTFBot *actor, Action *action) override
+ RefCount rc_CMissionPopulator_Parse;
+ DETOUR_DECL_MEMBER(void, CMissionPopulator_Parse, KeyValues *kv)
{
- DevMsg("CTFBotMissionPushStickies::OnStart(#%d)\n", ENTINDEX(actor));
-
- // TODO
-
- return ActionResult::Continue();
+ SCOPED_INCREMENT(rc_CMissionPopulator_Parse);
+ return DETOUR_MEMBER_CALL(CMissionPopulator_Parse)(kv);
}
- virtual ActionResult Update(CTFBot *actor, float dt) override
+ DETOUR_DECL_MEMBER(bool, CSpawnLocation_Parse, KeyValues *kv)
{
- DevMsg("CTFBotMissionPushStickies::Update(#%d)\n", ENTINDEX(actor));
-
- CTFBotPathCost cost_func((CTFBot *)0x11111111, RETREAT_ROUTE);
-
-
- // TODO
+ if (rc_CMissionPopulator_Parse.NonZero()) {
+ if (V_stricmp(kv->GetName(), "Objective") == 0 &&
+ V_stricmp(kv->GetString(), "PushStickies") == 0) {
+ int *p_m_Objective = (int *)((uintptr_t)this - (offsetof(CMissionPopulator, m_Where) -
+ offsetof(CMissionPopulator, m_Objective)));
+
+ *p_m_Objective = MISSION_PUSHSTICKIES;
+ return true;
+ }
+ }
- return ActionResult::Continue();
+ return DETOUR_MEMBER_CALL(CSpawnLocation_Parse)(kv);
}
- virtual void OnEnd(CTFBot *actor, Action *action) override
+
+ DETOUR_DECL_MEMBER(void, CMissionPopulator_Update)
{
- DevMsg("CTFBotMissionPushStickies::OnEnd(#%d)\n", ENTINDEX(actor));
+ DETOUR_MEMBER_CALL(CMissionPopulator_Update)();
- // TODO
- }
-};
-
-
-static RefCount rc_CMissionPopulator_Parse;
-DETOUR_DECL_MEMBER(void, CMissionPopulator_Parse, KeyValues *kv)
-{
- SCOPED_INCREMENT(rc_CMissionPopulator_Parse);
- return DETOUR_MEMBER_CALL(CMissionPopulator_Parse)(kv);
-}
-
-DETOUR_DECL_MEMBER(bool, CSpawnLocation_Parse, KeyValues *kv)
-{
- if (rc_CMissionPopulator_Parse.NonZero()) {
- if (V_stricmp(kv->GetName(), "Objective") == 0 &&
- V_stricmp(kv->GetString(), "PushStickies") == 0) {
- int *p_m_Objective = (int *)((uintptr_t)this - (offsetof(CMissionPopulator, m_Where) -
- offsetof(CMissionPopulator, m_Objective)));
-
- *p_m_Objective = MISSION_PUSHSTICKIES;
- return true;
+ CMissionPopulator *realthis = reinterpret_cast(this);
+
+ int objective = realthis->m_Objective;
+ if (objective == MISSION_PUSHSTICKIES) {
+ realthis->UpdateMission(objective);
}
}
- return DETOUR_MEMBER_CALL(CSpawnLocation_Parse)(kv);
-}
-
-
-DETOUR_DECL_MEMBER(void, CMissionPopulator_Update)
-{
- DETOUR_MEMBER_CALL(CMissionPopulator_Update)();
-
- CMissionPopulator *realthis = reinterpret_cast(this);
-
- int objective = realthis->m_Objective;
- if (objective == MISSION_PUSHSTICKIES) {
- realthis->UpdateMission(objective);
- }
-}
-
-
-// TODO: detour to make CMissionPopulator::Update call CMissionPopulator::UpdateMission for mission numbers above 5
-// TODO: detour to add "PushStickies" as a valid Objective KV for CMissionPopulator::Parse
-// TODO: detour to make CTFBotScenarioMonitor::DesiredScenarioAndClassAction select CTFBotMissionPushStickies for MISSION_PUSHSTICKIES
-
-
-DETOUR_DECL_MEMBER(Action *, CTFBotScenarioMonitor_DesiredScenarioAndClassAction, CTFBot *actor)
-{
- DevMsg("CTFBotScenarioMonitor::DesiredScenarioAndClassAction\n");
- int mission = actor->GetMission();
- DevMsg("actor->m_nMission = %d\n", mission);
+ // TODO: detour to make CMissionPopulator::Update call CMissionPopulator::UpdateMission for mission numbers above 5
+ // TODO: detour to add "PushStickies" as a valid Objective KV for CMissionPopulator::Parse
+ // TODO: detour to make CTFBotScenarioMonitor::DesiredScenarioAndClassAction select CTFBotMissionPushStickies for MISSION_PUSHSTICKIES
- return new CTFBotMissionPushStickies();
- //DETOUR_MEMBER_CALL(CTFBotScenarioMonitor_DesiredScenarioAndClassAction)(actor);
-}
-
-
-class CMod_StickyPusher : public IMod
-{
-public:
- CMod_StickyPusher() : IMod("StickyPusher")
+ DETOUR_DECL_MEMBER(Action *, CTFBotScenarioMonitor_DesiredScenarioAndClassAction, CTFBot *actor)
{
- MOD_ADD_DETOUR_MEMBER(CMissionPopulator, Parse);
- MOD_ADD_DETOUR_MEMBER(CSpawnLocation, Parse);
+ DevMsg("CTFBotScenarioMonitor::DesiredScenarioAndClassAction\n");
+
+ int mission = actor->GetMission();
+ DevMsg("actor->m_nMission = %d\n", mission);
- MOD_ADD_DETOUR_MEMBER(CMissionPopulator, Update);
+ return new CTFBotMissionPushStickies();
- MOD_ADD_DETOUR_MEMBER(CTFBotScenarioMonitor, DesiredScenarioAndClassAction);
+ //DETOUR_MEMBER_CALL(CTFBotScenarioMonitor_DesiredScenarioAndClassAction)(actor);
}
- void SetEnabled(bool enable)
+
+ class CMod : public IMod
{
- this->ToggleAllDetours(enable);
- }
-};
-static CMod_StickyPusher s_Mod;
-
-
-static ConVar cvar_enable("sigsegv_stickypusher_enable", "0", FCVAR_NOTIFY,
- "Mod: add new CTFBotMissionStickyPusher AI behavior",
- [](IConVar *pConVar, const char *pOldValue, float flOldValue) {
- ConVarRef var(pConVar);
- s_Mod.SetEnabled(var.GetBool());
- });
+ public:
+ CMod() : IMod("StickyPusher")
+ {
+ MOD_ADD_DETOUR_MEMBER(CMissionPopulator, Parse);
+ MOD_ADD_DETOUR_MEMBER(CSpawnLocation, Parse);
+
+ MOD_ADD_DETOUR_MEMBER(CMissionPopulator, Update);
+
+ MOD_ADD_DETOUR_MEMBER(CTFBotScenarioMonitor, DesiredScenarioAndClassAction);
+ }
+
+ void SetEnabled(bool enable)
+ {
+ this->ToggleAllDetours(enable);
+ }
+ };
+ CMod s_Mod;
+
+
+ ConVar cvar_enable("sigsegv_stickypusher_enable", "0", FCVAR_NOTIFY,
+ "Mod: add new CTFBotMissionStickyPusher AI behavior",
+ [](IConVar *pConVar, const char *pOldValue, float flOldValue) {
+ ConVarRef var(pConVar);
+ s_Mod.SetEnabled(var.GetBool());
+ });
+}
#endif
diff --git a/mod/visualize_airblastbox.cpp b/mod/visualize_airblastbox.cpp
new file mode 100644
index 00000000..02e9543c
--- /dev/null
+++ b/mod/visualize_airblastbox.cpp
@@ -0,0 +1,76 @@
+#include "mod.h"
+#include "sm/detours.h"
+#include "stub/stub.h"
+#include "util/util.h"
+
+
+#if defined __GNUC__
+#warning TODO: move cvars into class
+#endif
+
+
+#ifdef EXPERIMENTAL
+
+namespace Mod_Visualize_AirblastBox
+{
+ ConVar cvar_duration("sigsegv_visualize_airblastbox_duration", "0.1", FCVAR_NOTIFY,
+ "Visualization: box draw duration");
+
+ ConVar cvar_color_r("sigsegv_visualize_airblastbox_color_r", "255", FCVAR_NOTIFY,
+ "Visualization: box color (red)");
+ ConVar cvar_color_g("sigsegv_visualize_airblastbox_color_g", "255", FCVAR_NOTIFY,
+ "Visualization: box color (green)");
+ ConVar cvar_color_b("sigsegv_visualize_airblastbox_color_b", "255", FCVAR_NOTIFY,
+ "Visualization: box color (blue)");
+ ConVar cvar_color_a("sigsegv_visualize_airblastbox_color_a", "255", FCVAR_NOTIFY,
+ "Visualization: box color (alpha)");
+
+
+ RefCount rc_CTFWeaponBase_DeflectProjectiles;
+ DETOUR_DECL_MEMBER(bool, CTFWeaponBase_DeflectProjectiles)
+ {
+ SCOPED_INCREMENT(rc_CTFWeaponBase_DeflectProjectiles);
+ return DETOUR_MEMBER_CALL(CTFWeaponBase_DeflectProjectiles)();
+ }
+
+ DETOUR_DECL_STATIC(int, UTIL_EntitiesInBox, const Vector& mins, const Vector& maxs, CFlaggedEntitiesEnum *pEnum)
+ {
+ if (rc_CTFWeaponBase_DeflectProjectiles.NonZero()) {
+ NDebugOverlay::Box(vec3_origin, mins, maxs,
+ cvar_color_r.GetInt(),
+ cvar_color_g.GetInt(),
+ cvar_color_b.GetInt(),
+ cvar_color_a.GetInt(),
+ cvar_duration.GetFloat());
+ }
+
+ return DETOUR_STATIC_CALL(UTIL_EntitiesInBox)(mins, maxs, pEnum);
+ }
+
+
+ class CMod : public IMod
+ {
+ public:
+ CMod() : IMod("Visualize_AirblastBox")
+ {
+ MOD_ADD_DETOUR_MEMBER(CTFWeaponBase, DeflectProjectiles);
+ MOD_ADD_DETOUR_GLOBAL(UTIL_EntitiesInBox);
+ }
+
+ void SetEnabled(bool enable)
+ {
+ this->ToggleAllDetours(enable);
+ }
+ };
+ CMod s_Mod;
+
+
+ ConVar cvar_enable("sigsegv_visualize_airblastbox_enable", "0", FCVAR_NOTIFY,
+ "Visualization: draw box used for airblast deflection of projectiles",
+ [](IConVar *pConVar, const char *pOldValue, float flOldValue) {
+ ConVarRef var(pConVar);
+ s_Mod.SetEnabled(var.GetBool());
+ });
+}
+
+#endif
diff --git a/mod/visualize_deflectcylinder.cpp b/mod/visualize_deflectcylinder.cpp
new file mode 100644
index 00000000..b2f26487
--- /dev/null
+++ b/mod/visualize_deflectcylinder.cpp
@@ -0,0 +1,161 @@
+#include "mod.h"
+#include "sm/detours.h"
+#include "stub/stub.h"
+#include "util/util.h"
+
+
+#if defined __GNUC__
+#warning TODO: move cvars into class
+#endif
+
+
+#ifdef EXPERIMENTAL
+
+namespace Mod_Visualize_DeflectCylinder
+{
+ ConVar cvar_duration("sigsegv_visualize_deflectcylinder_duration", "0.1", FCVAR_NOTIFY,
+ "Visualization: cylinder draw duration");
+
+ ConVar cvar_color_r("sigsegv_visualize_deflectcylinder_color_r", "255", FCVAR_NOTIFY,
+ "Visualization: cylinder color (red)");
+ ConVar cvar_color_g("sigsegv_visualize_deflectcylinder_color_g", "255", FCVAR_NOTIFY,
+ "Visualization: cylinder color (green)");
+ ConVar cvar_color_b("sigsegv_visualize_deflectcylinder_color_b", "255", FCVAR_NOTIFY,
+ "Visualization: cylinder color (blue)");
+ ConVar cvar_color_a("sigsegv_visualize_deflectcylinder_color_a", "0", FCVAR_NOTIFY,
+ "Visualization: cylinder color (alpha)");
+
+ ConVar cvar_sphere_enable("sigsegv_visualize_deflectcylinder_sphere_enable", "0", FCVAR_NOTIFY,
+ "Visualization: sphere enable");
+ ConVar cvar_sphere_color_r("sigsegv_visualize_deflectcylinder_sphere_color_r", "255", FCVAR_NOTIFY,
+ "Visualization: sphere color (red)");
+ ConVar cvar_sphere_color_g("sigsegv_visualize_deflectcylinder_sphere_color_g", "255", FCVAR_NOTIFY,
+ "Visualization: sphere color (green)");
+ ConVar cvar_sphere_color_b("sigsegv_visualize_deflectcylinder_sphere_color_b", "255", FCVAR_NOTIFY,
+ "Visualization: sphere color (blue)");
+ ConVar cvar_sphere_color_a("sigsegv_visualize_deflectcylinder_sphere_color_a", "0", FCVAR_NOTIFY,
+ "Visualization: sphere color (alpha)");
+
+
+ RefCount rc_CTFMinigun_AttackProjectiles;
+ DETOUR_DECL_MEMBER(void, CTFMinigun_AttackEnemyProjectiles)
+ {
+ SCOPED_INCREMENT(rc_CTFMinigun_AttackProjectiles);
+ DETOUR_MEMBER_CALL(CTFMinigun_AttackEnemyProjectiles)();
+ }
+
+ bool is_MiniBoss;
+ DETOUR_DECL_MEMBER(bool, CTFPlayer_IsMiniBoss)
+ {
+ bool result = DETOUR_MEMBER_CALL(CTFPlayer_IsMiniBoss)();
+ is_MiniBoss = result;
+ return result;
+ }
+
+ DETOUR_DECL_STATIC(int, UTIL_EntitiesInSphere, const Vector& center, float radius, CFlaggedEntitiesEnum *pEnum)
+ {
+ if (rc_CTFMinigun_AttackProjectiles.NonZero() && cvar_sphere_enable.GetBool()) {
+ NDebugOverlay::Sphere(center, QAngle(0.0f, 0.0f, 0.0f), radius,
+ cvar_sphere_color_r.GetInt(),
+ cvar_sphere_color_g.GetInt(),
+ cvar_sphere_color_b.GetInt(),
+ cvar_sphere_color_a.GetInt(),
+ false,
+ cvar_duration.GetFloat());
+
+ NDebugOverlay::Sphere(center, QAngle(0.0f, 30.0f, 0.0f), radius,
+ cvar_sphere_color_r.GetInt(),
+ cvar_sphere_color_g.GetInt(),
+ cvar_sphere_color_b.GetInt(),
+ cvar_sphere_color_a.GetInt(),
+ false,
+ cvar_duration.GetFloat());
+
+ NDebugOverlay::Sphere(center, QAngle(0.0f, 60.0f, 0.0f), radius,
+ cvar_sphere_color_r.GetInt(),
+ cvar_sphere_color_g.GetInt(),
+ cvar_sphere_color_b.GetInt(),
+ cvar_sphere_color_a.GetInt(),
+ false,
+ cvar_duration.GetFloat());
+ }
+
+ return DETOUR_STATIC_CALL(UTIL_EntitiesInSphere)(center, radius, pEnum);
+ }
+
+ DETOUR_DECL_STATIC(float, CalcDistanceToLineSegment, const Vector& P, const Vector& vLineA, const Vector& vLineB, float *t)
+ {
+ float dist = DETOUR_STATIC_CALL(CalcDistanceToLineSegment)(P, vLineA, vLineB, t);
+
+ if (rc_CTFMinigun_AttackProjectiles.NonZero()) {
+ float radius = (is_MiniBoss ? 56.0f : 38.0f);
+ bool is_inside = (dist <= radius);
+
+ NDebugOverlay::Cross3D(P, 10.0f,
+ (is_inside ? 0 : 255),
+ (is_inside ? 255 : 0),
+ 0,
+ true,
+ cvar_duration.GetFloat());
+
+ static int lastframe = -1;
+ if (gpGlobals->framecount != lastframe) {
+ lastframe = gpGlobals->framecount;
+
+ /*NDebugOverlay::Line(vLineA, vLineB,
+ cvar_color_r.GetFloat(),
+ cvar_color_g.GetFloat(),
+ cvar_color_b.GetFloat(),
+ true,
+ cvar_duration.GetFloat());*/
+
+ QAngle ang;
+ VectorAngles(vLineA - vLineB, ang);
+
+ for (int i = 0; i <= 10; ++i) {
+ float pA = ((float)i / 10.0f);
+ float pB = (1.0f - pA);
+
+ NDebugOverlay::Circle((pA * vLineA) + (pB * vLineB), ang, radius,
+ cvar_color_r.GetFloat(),
+ cvar_color_g.GetFloat(),
+ cvar_color_b.GetFloat(),
+ cvar_color_a.GetFloat(),
+ false,
+ cvar_duration.GetFloat());
+ }
+ }
+ }
+
+ return dist;
+ }
+
+
+ class CMod : public IMod
+ {
+ public:
+ CMod() : IMod("Visualize_DeflectCylinder")
+ {
+ MOD_ADD_DETOUR_MEMBER(CTFMinigun, AttackEnemyProjectiles);
+ MOD_ADD_DETOUR_MEMBER(CTFPlayer, IsMiniBoss);
+ MOD_ADD_DETOUR_GLOBAL(UTIL_EntitiesInSphere);
+ MOD_ADD_DETOUR_GLOBAL(CalcDistanceToLineSegment);
+ }
+
+ void SetEnabled(bool enable)
+ {
+ this->ToggleAllDetours(enable);
+ }
+ };
+ CMod s_Mod;
+
+
+ ConVar cvar_enable("sigsegv_visualize_deflectcylinder_enable", "0", FCVAR_NOTIFY,
+ "Visualization: draw cylinder used for heavy's projectile deflection upgrade",
+ [](IConVar *pConVar, const char *pOldValue, float flOldValue) {
+ ConVarRef var(pConVar);
+ s_Mod.SetEnabled(var.GetBool());
+ });
+}
+
+#endif
diff --git a/mod/visualize_ehsphere.cpp b/mod/visualize_ehsphere.cpp
new file mode 100644
index 00000000..de462759
--- /dev/null
+++ b/mod/visualize_ehsphere.cpp
@@ -0,0 +1,79 @@
+#include "mod.h"
+#include "sm/detours.h"
+#include "stub/stub.h"
+#include "util/util.h"
+
+
+#if defined __GNUC__
+#warning TODO: move cvars into class
+#endif
+
+
+#ifdef EXPERIMENTAL
+
+namespace Mod_Visualize_EHSphere
+{
+ ConVar cvar_duration("sigsegv_visualize_ehsphere_duration", "0.5", FCVAR_NOTIFY,
+ "Visualization: EH sphere draw duration");
+
+ ConVar cvar_color_r("sigsegv_visualize_ehsphere_color_r", "255", FCVAR_NOTIFY,
+ "Visualization: EH sphere color (red)");
+ ConVar cvar_color_g("sigsegv_visualize_ehsphere_color_g", "255", FCVAR_NOTIFY,
+ "Visualization: EH sphere color (green)");
+ ConVar cvar_color_b("sigsegv_visualize_ehsphere_color_b", "255", FCVAR_NOTIFY,
+ "Visualization: EH sphere color (blue)");
+ ConVar cvar_color_a("sigsegv_visualize_ehsphere_color_a", "0", FCVAR_NOTIFY,
+ "Visualization: EH sphere color (alpha)");
+
+
+ RefCount rc_CTFSniperRifle_ExplosiveHeadShot;
+ DETOUR_DECL_MEMBER(void, CTFSniperRifle_ExplosiveHeadShot, CTFPlayer *player1, CTFPlayer *player2)
+ {
+ SCOPED_INCREMENT(rc_CTFSniperRifle_ExplosiveHeadShot);
+ DETOUR_MEMBER_CALL(CTFSniperRifle_ExplosiveHeadShot)(player1, player2);
+ }
+
+ DETOUR_DECL_STATIC(int, UTIL_EntitiesInSphere, const Vector& center, float radius, CFlaggedEntitiesEnum *pEnum)
+ {
+ if (rc_CTFSniperRifle_ExplosiveHeadShot.NonZero()) {
+ for (int i = 0; i < 8; ++i) {
+ NDebugOverlay::Sphere(center, QAngle(0.0f, (float)i * 22.5f, 0.0f), radius,
+ cvar_color_r.GetInt(),
+ cvar_color_g.GetInt(),
+ cvar_color_b.GetInt(),
+ cvar_color_a.GetInt(),
+ false,
+ cvar_duration.GetFloat());
+ }
+ }
+
+ return DETOUR_STATIC_CALL(UTIL_EntitiesInSphere)(center, radius, pEnum);
+ }
+
+
+ class CMod : public IMod
+ {
+ public:
+ CMod() : IMod("Visualize_EHSphere")
+ {
+ MOD_ADD_DETOUR_MEMBER(CTFSniperRifle, ExplosiveHeadShot);
+ MOD_ADD_DETOUR_GLOBAL(UTIL_EntitiesInSphere);
+ }
+
+ void SetEnabled(bool enable)
+ {
+ this->ToggleAllDetours(enable);
+ }
+ };
+ CMod s_Mod;
+
+
+ ConVar cvar_enable("sigsegv_visualize_ehsphere_enable", "0", FCVAR_NOTIFY,
+ "Visualization: draw sphere used for sniper's explosive headshot upgrade",
+ [](IConVar *pConVar, const char *pOldValue, float flOldValue) {
+ ConVarRef var(pConVar);
+ s_Mod.SetEnabled(var.GetBool());
+ });
+}
+
+#endif
diff --git a/prop.h b/prop.h
index 84cab089..454df0ba 100644
--- a/prop.h
+++ b/prop.h
@@ -192,76 +192,97 @@ class CProp_Extract : public IPropTyped
// virtual: NOPE
// members: NOPE
template
-class CPropAccessor_Read
+class CPropAccessor_Base
{
public:
- CPropAccessor_Read() = delete;
-// ~CPropAccessor_Read() = delete;
-
- operator const T&() const { return this->Get(); }
-
- /* this is questionable and probably ought to be removed */
- operator T*() const { return this->GetPtr(); }
-
- const T& operator=(const T& val) = delete;
+ CPropAccessor_Base() = delete;
-protected:
T *GetPtr() const
{
int off = -1;
assert(PROP->GetOffset(off));
-
return reinterpret_cast((uintptr_t)this + off);
}
+};
+
+
+template
+class CPropAccessor_Read : public CPropAccessor_Base
+{
+public:
+ CPropAccessor_Read() = delete;
- const T& Get() const
- {
- return *this->GetPtr();
- }
+ operator const T&() const { return *this->GetPtr(); }
+ const T& operator=(const T& val) = delete;
+};
+/* specialization for CHandle */
+template
+class CPropAccessor_Read, IPROP, PROP> : public CPropAccessor_Base, IPROP, PROP>
+{
+public:
+ CPropAccessor_Read() = delete;
+
+ operator U*() const { return *this->GetPtr(); }
+ U* operator=(U* val) = delete;
};
+
template
class CPropAccessor_Write : public CPropAccessor_Read
{
public:
CPropAccessor_Write() = delete;
-// ~CPropAccessor_Write() = delete;
- const T& operator=(const T& val) { this->Set(val); return val; }
+ const T& operator=(const T& val)
+ {
+ if (NET) {
+ /* TODO: update network state */
+ assert(false);
+ }
+ *this->GetPtr() = val;
+ return val;
+ }
+};
+/* specialization for CHandle */
+template
+class CPropAccessor_Write, IPROP, PROP, NET> : public CPropAccessor_Read, IPROP, PROP>
+{
+public:
+ CPropAccessor_Write() = delete;
-protected:
- void Set(const T& val) const
+ U* operator=(U* val)
{
if (NET) {
/* TODO: update network state */
assert(false);
}
-
*this->GetPtr() = val;
+ return val;
}
};
#define DEF_SENDPROP(T, P) \
- typedef CProp_SendProp _tp_##P; \
- static _tp_##P s_prop_##P; \
- typedef CPropAccessor_Write _ta_##P; \
- _ta_##P P; \
- static_assert(std::is_empty<_ta_##P>::value, "Prop accessor isn't an empty type")
+ typedef CProp_SendProp _type_prop_##P; \
+ static _type_prop_##P s_prop_##P; \
+ /*typedef CPropAccessor_Write _type_accessor_##P;*/ \
+ typedef CPropAccessor_Read _type_accessor_##P; \
+ _type_accessor_##P P; \
+ static_assert(std::is_empty<_type_accessor_##P>::value, "Prop accessor isn't an empty type")
#define DEF_DATAMAP(T, P) \
- typedef CProp_DataMap _tp_##P; \
- static _tp_##P s_prop_##P; \
- typedef CPropAccessor_Write _ta_##P; \
- _ta_##P P; \
- static_assert(std::is_empty<_ta_##P>::value, "Prop accessor isn't an empty type")
+ typedef CProp_DataMap _type_prop_##P; \
+ static _type_prop_##P s_prop_##P; \
+ typedef CPropAccessor_Write _type_accessor_##P; \
+ _type_accessor_##P P; \
+ static_assert(std::is_empty<_type_accessor_##P>::value, "Prop accessor isn't an empty type")
#define DEF_EXTRACT(T, P) \
- typedef CProp_Extract _tp_##P; \
- static _tp_##P s_prop_##P; \
- typedef CPropAccessor_Write _ta_##P; \
- _ta_##P P; \
- static_assert(std::is_empty<_ta_##P>::value, "Prop accessor isn't an empty type")
+ typedef CProp_Extract _type_prop_##P; \
+ static _type_prop_##P s_prop_##P; \
+ typedef CPropAccessor_Write _type_accessor_##P; \
+ _type_accessor_##P P; \
+ static_assert(std::is_empty<_type_accessor_##P>::value, "Prop accessor isn't an empty type")
#define IMPL_SENDPROP(T, C, P, SC) \
diff --git a/sm/MemoryUtils.cpp b/sm/MemoryUtils.cpp
index 84123884..41a94709 100644
--- a/sm/MemoryUtils.cpp
+++ b/sm/MemoryUtils.cpp
@@ -568,3 +568,101 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
return true;
}
+
+void MemoryUtils::ForEachSymbol(void *handle, void (*functor)(Symbol *))
+{
+ /* do a bogus symbol lookup to force everything into the symbol table cache */
+ assert(ResolveSymbol(handle, "________________") == nullptr);
+
+#ifdef PLATFORM_LINUX
+ struct link_map *dlmap;
+ LibSymbolTable *libtable;
+ SymbolTable *table;
+
+ dlmap = (struct link_map *)handle;
+ table = NULL;
+
+ /* See if we already have a symbol table for this library */
+ for (size_t i = 0; i < m_SymTables.size(); i++)
+ {
+ libtable = m_SymTables[i];
+ if (libtable->lib_base == dlmap->l_addr)
+ {
+ table = &libtable->table;
+ break;
+ }
+ }
+
+ /* If we don't have a symbol table for this library, then create one */
+ if (table == NULL)
+ {
+ libtable = new LibSymbolTable();
+ libtable->table.Initialize();
+ libtable->lib_base = dlmap->l_addr;
+ libtable->last_pos = 0;
+ table = &libtable->table;
+ m_SymTables.push_back(libtable);
+ }
+
+ table->ForEachSymbol(functor);
+#elif defined PLATFORM_APPLE
+ uintptr_t dlbase;
+ uint32_t image_count;
+ LibSymbolTable *libtable;
+ SymbolTable *table;
+ Symbol *symbol_entry;
+
+ dlbase = 0;
+ image_count = m_ImageList->infoArrayCount;
+ table = NULL;
+
+ /* Loop through mach-o images in process.
+ * We can skip index 0 since that is just the executable.
+ */
+ for (uint32_t i = 1; i < image_count; i++)
+ {
+ const struct dyld_image_info &info = m_ImageList->infoArray[i];
+
+ /* "Load" each one until we get a matching handle */
+ void *h = dlopen(info.imageFilePath, RTLD_NOLOAD);
+ if (h == handle)
+ {
+ dlbase = (uintptr_t)info.imageLoadAddress;
+ dlclose(h);
+ break;
+ }
+
+ dlclose(h);
+ }
+
+ if (!dlbase)
+ {
+ /* Uh oh, we couldn't find a matching handle */
+ return;
+ }
+
+ /* See if we already have a symbol table for this library */
+ for (size_t i = 0; i < m_SymTables.size(); i++)
+ {
+ libtable = m_SymTables[i];
+ if (libtable->lib_base == dlbase)
+ {
+ table = &libtable->table;
+ break;
+ }
+ }
+
+ /* If we don't have a symbol table for this library, then create one */
+ if (table == NULL)
+ {
+ libtable = new LibSymbolTable();
+ libtable->table.Initialize();
+ libtable->lib_base = dlbase;
+ libtable->last_pos = 0;
+ table = &libtable->table;
+ m_SymTables.push_back(libtable);
+ }
+
+ table->ForEachSymbol(functor);
+#endif
+}
diff --git a/sm/MemoryUtils.h b/sm/MemoryUtils.h
index 2e7b6f66..a5d91e34 100644
--- a/sm/MemoryUtils.h
+++ b/sm/MemoryUtils.h
@@ -32,12 +32,12 @@
#include
#include
-#if defined PLATFORM_LINUX || defined PLATFORM_APPLE
+//#if defined PLATFORM_LINUX || defined PLATFORM_APPLE
#include
#include "sm_symtable.h"
using namespace SourceHook;
-#endif
+//#endif
#ifdef PLATFORM_APPLE
#include
@@ -65,6 +65,7 @@ class MemoryUtils
~MemoryUtils();
void *ResolveSymbol(void *handle, const char *symbol);
bool GetLibraryInfo(const void *libPtr, DynLibInfo &lib);
+ void ForEachSymbol(void *handle, void (*functor)(Symbol *));
#if defined PLATFORM_LINUX || defined PLATFORM_APPLE
private:
CVector m_SymTables;
diff --git a/sm/detours.h b/sm/detours.h
index a61982d6..000c91d2 100644
--- a/sm/detours.h
+++ b/sm/detours.h
@@ -48,7 +48,7 @@
#define DETOUR_STATIC_CALL(name) (Actual_##name)
#define DETOUR_DECL_STATIC(ret, name, ...) \
- static CDetour *detour_##name = nullptr; \
+ CDetour *detour_##name = nullptr; \
static ret (*Actual_##name)(__VA_ARGS__) = nullptr; \
static ret Detour_##name(__VA_ARGS__)
diff --git a/sm/sm_symtable.h b/sm/sm_symtable.h
index 1e2e9c40..99f74a6d 100644
--- a/sm/sm_symtable.h
+++ b/sm/sm_symtable.h
@@ -221,6 +221,19 @@ class SymbolTable
return kvs;
}
+
+ void ForEachSymbol(void (*functor)(Symbol *))
+ {
+ for (uint32_t i = 0; i < nbuckets; ++i) {
+ Symbol *sym = buckets[i];
+
+ while (sym != nullptr) {
+ functor(sym);
+ sym = sym->tbl_next;
+ }
+ }
+ }
+
private:
uint32_t nbuckets;
uint32_t nused;
diff --git a/stub/baseentity.cpp b/stub/baseentity.cpp
index d0615c45..1fedf19d 100644
--- a/stub/baseentity.cpp
+++ b/stub/baseentity.cpp
@@ -4,9 +4,12 @@
IMPL_DATAMAP(int, CBaseEntity, m_iEFlags);
IMPL_DATAMAP(Vector, CBaseEntity, m_vecAbsOrigin);
-IMPL_SENDPROP(int, CBaseEntity, m_iHealth, CBasePlayer);
-IMPL_SENDPROP(char, CBaseEntity, m_lifeState, CBasePlayer);
-IMPL_SENDPROP(int, CBaseEntity, m_iTeamNum, CBaseEntity);
+IMPL_SENDPROP(int, CBaseEntity, m_iTeamNum, CBaseEntity);
+IMPL_SENDPROP(int, CBaseEntity, m_iHealth, CBasePlayer);
+IMPL_SENDPROP(char, CBaseEntity, m_lifeState, CBasePlayer);
+IMPL_SENDPROP(CHandle, CBaseEntity, m_hGroundEntity, CBasePlayer);
FuncThunk CBaseEntity::ft_GetNetworkable( "CBaseEntity::GetNetworkable");
FuncThunk CBaseEntity::ft_CalcAbsolutePosition("CBaseEntity::CalcAbsolutePosition");
+
+VFuncThunk CBaseEntity::vt_IsPlayer("[VT] CBaseEntity", "CBaseEntity::IsPlayer");
diff --git a/stub/baseentity.h b/stub/baseentity.h
index 9e7567c1..fab6e8d3 100644
--- a/stub/baseentity.h
+++ b/stub/baseentity.h
@@ -15,23 +15,29 @@ class CBaseEntity
bool IsEFlagSet(int nEFlagMask) const;
/* getter/setter */
- int GetTeamNumber() const { return this->m_iTeamNum; }
- bool IsAlive() { return (this->m_lifeState == LIFE_ALIVE); }
+ int GetTeamNumber() const { return this->m_iTeamNum; }
+ int GetHealth() const { return this->m_iHealth; }
+ bool IsAlive() const { return (this->m_lifeState == LIFE_ALIVE); }
+ CBaseEntity *GetGroundEntity() const { return this->m_hGroundEntity; }
/* thunk */
- IServerNetworkable *GetNetworkable() { return (*ft_GetNetworkable)(this); }
- void CalcAbsolutePosition() { (*ft_CalcAbsolutePosition)(this); }
+ IServerNetworkable *GetNetworkable() { return (*ft_GetNetworkable)(this); }
+ void CalcAbsolutePosition() { (*ft_CalcAbsolutePosition)(this); }
+ bool IsPlayer() const { return (vt_IsPlayer.Get(this))(this); }
private:
DEF_DATAMAP(int, m_iEFlags);
DEF_DATAMAP(Vector, m_vecAbsOrigin);
- DEF_SENDPROP(int, m_iHealth);
- DEF_SENDPROP(char, m_lifeState);
- DEF_SENDPROP(int, m_iTeamNum);
+ DEF_SENDPROP(int, m_iTeamNum);
+ DEF_SENDPROP(int, m_iHealth);
+ DEF_SENDPROP(char, m_lifeState);
+ DEF_SENDPROP(CHandle, m_hGroundEntity);
static FuncThunk ft_GetNetworkable;
static FuncThunk ft_CalcAbsolutePosition;
+
+ static VFuncThunk vt_IsPlayer;
};
inline CBaseEntity *GetContainingEntity(edict_t *pent)
diff --git a/stub/tfbot.cpp b/stub/tfbot.cpp
index a7e74cb8..dae4a680 100644
--- a/stub/tfbot.cpp
+++ b/stub/tfbot.cpp
@@ -75,3 +75,22 @@ FuncThunk CTFBot::ft_GetLocomotionInterface("
FuncThunk CTFBot::ft_GetBodyInterface ("CTFBot::GetBodyInterface");
FuncThunk CTFBot::ft_GetVisionInterface ("CTFBot::GetVisionInterface");
FuncThunk CTFBot::ft_GetIntentionInterface ("CTFBot::GetIntentionInterface");
+
+
+#if defined __GNUC__
+#warning remove me
+#endif
+#if 0
+#include "re/nextbot.h"
+#include "util/dynamic_cast.h"
+void TEST_REMOVE_ME(CBaseEntity *ent)
+{
+ printf("%s", typeid(CTFPlayer).name());
+ printf("%s", typeid(Action).name());
+
+
+ //#warning remove me!
+// CTFPlayer *player/* = rtti_cast(ent)*/;
+// CTFBot *bot = rtti_cast(player);
+}
+#endif
diff --git a/stub/tfbot.h b/stub/tfbot.h
index 4a287bf9..f4dedf4d 100644
--- a/stub/tfbot.h
+++ b/stub/tfbot.h
@@ -18,6 +18,8 @@ class NextBotPlayer : public T {};
class CTFBot : public NextBotPlayer
{
public:
+ static const char *GetRTTIAddrName() { return "[RTTI] CTFBot"; }
+
int GetMission() const { return this->m_nMission; }
/* thunk */
@@ -63,4 +65,8 @@ class CTFBot : public NextBotPlayer
};
+// TODO: ToTFBot
+// (WARNING: DON'T ASSUME THAT DYNAMIC CASTS ARE SAFE!)
+
+
#endif
diff --git a/stub/tfplayer.h b/stub/tfplayer.h
index fd31b3cf..239f273d 100644
--- a/stub/tfplayer.h
+++ b/stub/tfplayer.h
@@ -67,8 +67,10 @@ class CTFPlayerShared
class CTFPlayer : public CBaseMultiplayerPlayer
{
public:
- CTFPlayerClass *GetPlayerClass() { return m_PlayerClass; }
- const CTFPlayerClass *GetPlayerClass() const { return m_PlayerClass; }
+ static const char *GetRTTIAddrName() { return "[RTTI] CTFPlayer"; }
+
+ CTFPlayerClass *GetPlayerClass() { return m_PlayerClass.GetPtr(); }
+ const CTFPlayerClass *GetPlayerClass() const { return m_PlayerClass.GetPtr(); }
bool IsPlayerClass(int iClass) const;
@@ -83,4 +85,9 @@ class CTFPlayer : public CBaseMultiplayerPlayer
};
+// TODO: ToBasePlayer
+// TODO: ToTFPlayer
+// (WARNING: DON'T ASSUME THAT DYNAMIC CASTS ARE SAFE!)
+
+
#endif
diff --git a/util/rtti.cpp b/util/rtti.cpp
new file mode 100644
index 00000000..9ae7da7d
--- /dev/null
+++ b/util/rtti.cpp
@@ -0,0 +1,279 @@
+#include "util/rtti.h"
+#include "library.h"
+#include "mem/scan.h"
+
+
+#if defined __GNUC__
+#warning TODO: find/replace all cases of "[VT]" and "[RTTI]"
+#warning TODO: remove all cases of GetRTTIAddrName
+#endif
+
+
+#if defined _MSC_VER
+class Prof
+{
+public:
+ static void Begin()
+ {
+ s_bFail = false;
+
+ if (!QueryPerformanceFrequency(&s_Freq)) s_bFail = true;
+ if (!QueryPerformanceCounter(&s_Count1)) s_bFail = true;
+ }
+ static void End(const char *msg)
+ {
+ if (!QueryPerformanceCounter(&s_Count2)) s_bFail = true;
+
+ if (s_bFail) {
+ DevMsg("PROF %s: failed\n", msg);
+ } else {
+ int64_t freq = s_Freq.QuadPart;
+ int64_t delta = s_Count2.QuadPart - s_Count1.QuadPart;
+
+ double sec = (double)delta / (double)freq;
+ double msec = sec * 1000.0;
+
+ DevMsg("PROF %s: %.3f ms\n", msg, msec);
+ }
+ }
+
+private:
+ static bool s_bFail;
+ static LARGE_INTEGER s_Freq;
+ static LARGE_INTEGER s_Count1;
+ static LARGE_INTEGER s_Count2;
+};
+bool Prof::s_bFail;
+LARGE_INTEGER Prof::s_Freq;
+LARGE_INTEGER Prof::s_Count1;
+LARGE_INTEGER Prof::s_Count2;
+#endif
+
+
+namespace RTTI
+{
+ // Windows: str typeinfo(T).raw_name() => _TypeDescriptor*
+ // Linux: str typeinfo(T).name() => abi::__class_type_info*
+ static std::map s_RTTI;
+
+ // Windows: str typeinfo(T).raw_name() => void **
+ // Linux: str typeinfo(T).name() => void **
+ static std::map s_VT;
+
+
+ void PreLoad()
+ {
+ DevMsg("RTTI::PreLoad BEGIN\n");
+
+#if defined __GNUC__
+
+ LibMgr::ForEachSym(Library::SERVER,
+ [](Symbol *sym)
+ {
+ const char *buf = sym->buffer();
+ char name[4096];
+
+ if (sym->length >= 4 && memcmp(buf, "_ZT", 3) == 0) {
+ bool is_rtti = (buf[3] == 'I');
+ bool is_vt = (buf[3] == 'V');
+
+ if (is_rtti || is_vt) {
+ size_t len = sym->length - 4;
+ len = Min(len, sizeof(name) - 1);
+
+ memcpy(name, buf + 4, len);
+ name[sym->length] = '\0';
+
+ if (is_rtti) {
+ std::string key(name);
+ auto addr = (const rtti_t *)(sym->address);
+
+ if (s_RTTI.find(key) != s_RTTI.end()) {
+ DevWarning("RTTI::PreLoad: duplicate symbol \"_ZTI%s\"\n", name);
+ } else {
+ s_RTTI[key] = addr;
+ // DevMsg("RTTI: %08x \"%s\"\n", (uintptr_t)addr, name);
+ }
+ } else if (is_vt) {
+ std::string key(name);
+ auto addr = (const void **)((uintptr_t)(sym->address) + offsetof(vtable, vfptrs));
+
+ if (s_VT.find(key) != s_VT.end()) {
+ DevWarning("RTTI::PreLoad: duplicate symbol \"_ZTV%s\"\n", name);
+ } else {
+ s_VT[key] = addr;
+ // DevMsg("VT: %08x \"%s\"\n", (uintptr_t)addr, name);
+ }
+ }
+ }
+ }
+ }
+ );
+
+#elif defined _MSC_VER
+
+ int n_total = 0;
+ int n_skip = 0;
+ int n_add = 0;
+
+ std::vector exclude;
+ exclude.emplace_back(R"(^.*@CryptoPP@@$)");
+ exclude.emplace_back(R"(^.*@protobuf@google@@$)");
+ exclude.emplace_back(R"(^.*@GCSDK@@$)");
+ exclude.emplace_back(R"(^\.\?AVCGC.*$)");
+ exclude.emplace_back(R"(^\.\?AV\?\$CGC.*$)");
+ exclude.emplace_back(R"(^\.\?AVCMsg.*$)");
+ exclude.emplace_back(R"(^\.\?AVCSO.*$)");
+ exclude.emplace_back(R"(^\.\?AV\?\$CUtl\w+DataOps.*$)");
+ exclude.emplace_back(R"(^\.\?AV\?\$CEntityFactory@.*$)");
+ exclude.emplace_back(R"(^\.\?AVNetworkVar_.*$)");
+
+ Prof::Begin();
+ CSingleScan scan1(CLibSegBounds(Library::SERVER, ".data"), new CStringPrefixScanner(ScanResults::ALL, ".?AV"));
+ Prof::End("TD scan");
+ Prof::Begin();
+ for (auto match : scan1.Matches()) {
+ auto name = (const char *)match;
+ auto addr = (const rtti_t *)((uintptr_t)match - offsetof(_TypeDescriptor, name));
+
+ ++n_total;
+ bool skip = false;
+ for (auto& filter : exclude) {
+ if (std::regex_match(name, filter, std::regex_constants::match_any)) {
+ skip = true;
+ break;
+ }
+ }
+ if (skip) {
+ ++n_skip;
+ continue;
+ }
+ ++n_add;
+
+ std::string key(name);
+ if (s_RTTI.find(key) != s_RTTI.end()) {
+ // DevWarning("RTTI::PreLoad: duplicate RTTI str \"%s\"\n", name);
+ } else {
+ s_RTTI[key] = addr;
+ // DevMsg("\"%s\" TD @ %08x\n", name, (uintptr_t)s_RTTI[key]);
+ }
+ }
+ DevMsg("RTTI::PreLoad:\n"
+ "total %d\n"
+ "skip %d\n"
+ "add %d\n",
+ n_total, n_skip, n_add);
+ Prof::End("TD post");
+
+
+ Prof::Begin();
+ std::map scannermap_COL;
+ std::vector scanners_COL;
+ for (const auto& pair : s_RTTI) {
+ auto name = pair.first;
+ auto p_TD = pair.second;
+
+ __RTTI_CompleteObjectLocator seek_COL = {
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ const_cast<_TypeDescriptor *>(p_TD),
+ };
+
+ IScanner *scanner = new CBasicScanner(ScanResults::FIRST, (const void *)&seek_COL, 0x10);
+
+ scanners_COL.push_back(scanner);
+ scannermap_COL[scanner] = name;
+ }
+ Prof::End("COL pre");
+ Prof::Begin();
+ //CMultiScan scan_COL(CLibSegBounds(Library::SERVER, ".rdata"), scanners_COL);
+ CThreadedScan scan_COL(CLibSegBounds(Library::SERVER, ".rdata"), scanners_COL);
+ Prof::End("COL scan");
+
+ Prof::Begin();
+ std::map results_COL;
+ for (auto scanner : scanners_COL) {
+ auto& name = scannermap_COL[scanner];
+
+ if (scanner->Matches().size() != 1) {
+ // DevMsg("RTTI::PreLoad: %u TD refs for \"%s\"\n", scanner->Matches().size(), name.c_str());
+ continue;
+ }
+
+ results_COL[name] = (__RTTI_CompleteObjectLocator *)scanner->Matches()[0];
+ // DevMsg("\"%s\" COL @ %08x\n", name.c_str(), (uintptr_t)results_COL[name]);
+ }
+ Prof::End("COL post");
+
+
+ Prof::Begin();
+ std::map scannermap_VT;
+ std::vector scanners_VT;
+ for (const auto& pair : results_COL) {
+ auto name = pair.first;
+ auto p_COL = pair.second;
+
+ IScanner *scanner = new CBasicScanner(ScanResults::FIRST, (const void *)&p_COL, 0x4);
+
+ scanners_VT.push_back(scanner);
+ scannermap_VT[scanner] = name;
+ }
+ Prof::End("VT pre");
+ Prof::Begin();
+ //CMultiScan scan_VT(CLibSegBounds(Library::SERVER, ".rdata"), scanners_VT);
+ CThreadedScan scan_VT(CLibSegBounds(Library::SERVER, ".rdata"), scanners_VT);
+ Prof::End("VT scan");
+
+ Prof::Begin();
+ for (auto scanner : scanners_VT) {
+ auto& name = scannermap_VT[scanner];
+
+ if (scanner->Matches().size() != 1) {
+ // DevMsg("RTTI::PreLoad: %u COL refs for \"%s\"\n", scanner->Matches().size(), name.c_str());
+ continue;
+ }
+
+ s_VT[name] = (const void **)((uintptr_t)scanner->Matches()[0] + 0x4);
+ // DevMsg("\"%s\" VT @ %08x\n", name.c_str(), (uintptr_t)s_VT[name]);
+ }
+ Prof::End("VT post");
+
+#endif
+
+
+ DevMsg("RTTI::PreLoad: found %u RTTI\n", s_RTTI.size());
+ DevMsg("RTTI::PreLoad: found %u VT\n", s_VT.size());
+
+ // RTTI
+ // Linux: find all symbols of format "_ZTI"
+ // Windows: find typename strings, find refs, subtract 8 bytes
+
+ // VTable
+ // Linux: find all symbols of format "_ZTV", add 8 bytes
+ // Windows: do some fancy backtracking from RTTI info
+ }
+
+
+ const rtti_t *GetRTTI(const char *name)
+ {
+ auto it = s_RTTI.find(std::string(name));
+ if (it == s_RTTI.end()) {
+ DevMsg("RTTI::GetRTTI FAIL: no RTTI addr for name \"%s\"\n", name);
+ return nullptr;
+ }
+
+ return (*it).second;
+ }
+
+ const void **GetVTable(const char *name)
+ {
+ auto it = s_VT.find(std::string(name));
+ if (it == s_VT.end()) {
+ DevMsg("RTTI::GetVTable FAIL: no VT addr for name \"%s\"\n", name);
+ return nullptr;
+ }
+
+ return (*it).second;
+ }
+}
diff --git a/util/rtti.h b/util/rtti.h
new file mode 100644
index 00000000..1dac77d0
--- /dev/null
+++ b/util/rtti.h
@@ -0,0 +1,55 @@
+#ifndef _INCLUDE_SIGSEGV_UTIL_RTTI_H_
+#define _INCLUDE_SIGSEGV_UTIL_RTTI_H_
+
+
+#include "abi.h"
+
+
+#if defined __GNUC__
+ typedef abi::__class_type_info rtti_t;
+#elif defined _MSC_VER
+ typedef _TypeDescriptor rtti_t;
+#endif
+
+
+namespace RTTI
+{
+ void PreLoad();
+
+ const rtti_t *GetRTTI(const char *name);
+ const void **GetVTable(const char *name);
+
+#if defined __GNUC__
+ template inline const rtti_t *GetRTTI() { return RTTI::GetRTTI(typeid(T).name()); }
+ template inline const void **GetVTable() { return RTTI::GetVTable(typeid(T).name()); }
+#elif defined _MSC_VER
+ template inline const rtti_t *GetRTTI() { return RTTI::GetRTTI(typeid(T).raw_name()); }
+ template inline const void **GetVTable() { return RTTI::GetVTable(typeid(T).raw_name()); }
+#endif
+}
+
+
+template
+inline TO rtti_cast(const FROM ptr)
+{
+ if (ptr == nullptr) {
+ return nullptr;
+ }
+
+ auto rtti_from = RTTI::GetRTTI::type>();
+ auto rtti_to = RTTI::GetRTTI::type>();
+
+ assert(rtti_from != nullptr);
+ assert(rtti_to != nullptr);
+
+#if defined __GNUC__
+ void *result = abi::__dynamic_cast(ptr, rtti_from, rtti_to, -1);
+#elif defined _MSC_VER
+ void *result = __RTDynamicCast(ptr, 0, (void *)rtti_from, (void *)rtti_to, false);
+#endif
+
+ return reinterpret_cast(result);
+}
+
+
+#endif