Fix OnStartRecording and OnStopRecording forwards on linux

CHLTVServer::StartRecording is called directly in tv_record on linux,
ignoring the vtable.
Add a detour on linux for these two functions, so we always notice when
recording starts.
Windows actually always uses the vtable to get the function address, so
we don't need to detour anything on windows.
This commit is contained in:
Peace-Maker 2016-03-09 23:04:28 +01:00
parent 22c3803718
commit 7c4048690b
7 changed files with 189 additions and 19 deletions

View File

@ -36,6 +36,12 @@ for sdk_name in ['css', 'csgo']:
binary = Extension.HL2Config(project, projectName + '.ext.' + sdk.ext, sdk)
compiler = binary.compiler
if builder.target_platform == 'linux':
binary.sources += [
os.path.join(Extension.sm_root, 'public', 'CDetour', 'CDetour.cpp'),
os.path.join(Extension.sm_root, 'public', 'asm', 'asm.c')
]
if sdk.name == 'csgo':
compiler.cxxincludes += [
os.path.join(sdk.path, 'common', 'protobuf-2.5.0', 'src'),

View File

@ -95,6 +95,10 @@ bool SourceTVManager::SDK_OnLoad(char *error, size_t maxlength, bool late)
g_HLTVServers.InitHooks();
#ifndef WIN32
CDetourManager::Init(smutils->GetScriptingEngine(), g_pGameConf);
#endif
sharesys->AddNatives(myself, sourcetv_natives);
sharesys->RegisterLibrary(myself, "sourcetvmanager");

View File

@ -40,6 +40,9 @@
#include "smsdk_ext.h"
#include <IBinTools.h>
#include <ISDKTools.h>
#ifndef WIN32
#include "CDetour\detours.h"
#endif
#include "ihltvdirector.h"
#include "ihltv.h"
#include "iserver.h"

View File

@ -35,12 +35,15 @@
CForwardManager g_pSTVForwards;
// Only windows always uses the vtable for these. Linux does direct calls, so we use detours there.
#ifdef WIN32
SH_DECL_HOOK2_void(IDemoRecorder, StartRecording, SH_NOATTRIB, 0, const char *, bool)
#if SOURCE_ENGINE == SE_CSGO
SH_DECL_HOOK1_void(IDemoRecorder, StopRecording, SH_NOATTRIB, 0, CGameInfo const *)
#else
SH_DECL_HOOK0_void(IDemoRecorder, StopRecording, SH_NOATTRIB, 0)
#endif
#endif // SOURCE_ENGINE == SE_CSGO
#endif // !WIN32
#if SOURCE_ENGINE == SE_CSGO
SH_DECL_MANUALHOOK13(CHLTVServer_ConnectClient, 0, 0, 0, IClient *, const netadr_t &, int, int, int, const char *, const char *, const char *, int, CUtlVector<NetMsg_SplitPlayerConnect *> &, bool, CrossPlayPlatform_t, const unsigned char *, int);
@ -126,14 +129,18 @@ void CForwardManager::Shutdown()
void CForwardManager::HookRecorder(IDemoRecorder *recorder)
{
#ifdef WIN32
SH_ADD_HOOK(IDemoRecorder, StartRecording, recorder, SH_MEMBER(this, &CForwardManager::OnStartRecording_Post), true);
SH_ADD_HOOK(IDemoRecorder, StopRecording, recorder, SH_MEMBER(this, &CForwardManager::OnStopRecording_Post), true);
#endif
}
void CForwardManager::UnhookRecorder(IDemoRecorder *recorder)
{
#ifdef WIN32
SH_REMOVE_HOOK(IDemoRecorder, StartRecording, recorder, SH_MEMBER(this, &CForwardManager::OnStartRecording_Post), true);
SH_REMOVE_HOOK(IDemoRecorder, StopRecording, recorder, SH_MEMBER(this, &CForwardManager::OnStopRecording_Post), true);
#endif
}
void CForwardManager::HookServer(HLTVServerWrapper *wrapper)
@ -367,21 +374,12 @@ void CForwardManager::OnSpectatorPutInServer()
RETURN_META(MRES_IGNORED);
}
// These two hooks are actually only hooked on windows.
void CForwardManager::OnStartRecording_Post(const char *filename, bool bContinuously)
{
if (m_StartRecordingFwd->GetFunctionCount() == 0)
RETURN_META(MRES_IGNORED);
IDemoRecorder *recorder = META_IFACEPTR(IDemoRecorder);
HLTVServerWrapper *wrapper = g_HLTVServers.GetWrapper(recorder);
int instance = -1;
if (wrapper)
instance = wrapper->GetInstanceNumber();
m_StartRecordingFwd->PushCell(instance);
m_StartRecordingFwd->PushString(filename);
m_StartRecordingFwd->Execute();
CallOnStartRecording(recorder, filename, bContinuously);
RETURN_META(MRES_IGNORED);
}
@ -391,12 +389,33 @@ void CForwardManager::OnStopRecording_Post(CGameInfo const *info)
void CForwardManager::OnStopRecording_Post()
#endif
{
if (m_StopRecordingFwd->GetFunctionCount() == 0)
RETURN_META(MRES_IGNORED);
IDemoRecorder *recorder = META_IFACEPTR(IDemoRecorder);
CallOnStopRecording(recorder);
RETURN_META(MRES_IGNORED);
}
void CForwardManager::CallOnStartRecording(IDemoRecorder *recorder, const char *filename, bool bContinuously)
{
if (m_StartRecordingFwd->GetFunctionCount() == 0)
return;
HLTVServerWrapper *wrapper = g_HLTVServers.GetWrapper(recorder);
int instance = -1;
if (wrapper)
instance = wrapper->GetInstanceNumber();
m_StartRecordingFwd->PushCell(instance);
m_StartRecordingFwd->PushString(filename);
m_StartRecordingFwd->Execute();
}
void CForwardManager::CallOnStopRecording(IDemoRecorder *recorder)
{
if (m_StopRecordingFwd->GetFunctionCount() == 0)
return;
if (!recorder->IsRecording())
RETURN_META(MRES_IGNORED);
return;
char *pDemoFile = (char *)recorder->GetDemoFile();
@ -409,6 +428,87 @@ void CForwardManager::OnStopRecording_Post()
m_StopRecordingFwd->PushString(pDemoFile);
m_StopRecordingFwd->PushCell(recorder->GetRecordingTick());
m_StopRecordingFwd->Execute();
RETURN_META(MRES_IGNORED);
}
// Only need to detour these on Linux. Windows always uses the vtable.
#ifndef WIN32
DETOUR_DECL_MEMBER2(DetourHLTVStartRecording, void, const char *, filename, bool, bContinuously)
{
// Call the original first.
DETOUR_MEMBER_CALL(DetourHLTVStartRecording)(filename, bContinuously);
IDemoRecorder *recorder = (IDemoRecorder *)this;
g_pSTVForwards.CallOnStartRecording(recorder, filename, bContinuously);
}
#if SOURCE_ENGINE == SE_CSGO
DETOUR_DECL_MEMBER1(DetourHLTVStopRecording, void, CGameInfo const *, info)
#else
DETOUR_DECL_MEMBER0(DetourHLTVStopRecording, void)
#endif
{
// Call the original first.
#if SOURCE_ENGINE == SE_CSGO
DETOUR_MEMBER_CALL(DetourHLTVStopRecording)(info);
#else
DETOUR_MEMBER_CALL(DetourHLTVStopRecording)();
#endif
IDemoRecorder *recorder = (IDemoRecorder *)this;
g_pSTVForwards.CallOnStopRecording(recorder);
}
bool CForwardManager::CreateStartRecordingDetour()
{
if (m_bStartRecordingDetoured)
return true;
m_DStartRecording = DETOUR_CREATE_MEMBER(DetourHLTVStartRecording, "CHLTVDemoRecorder::StartRecording");
if (m_DStartRecording != nullptr)
{
m_DStartRecording->EnableDetour();
m_bStartRecordingDetoured = true;
return true;
}
smutils->LogError(myself, "CHLTVDemoRecorder::StartRecording detour could not be initialized.");
return false;
}
void CForwardManager::RemoveStartRecordingDetour()
{
if (m_DStartRecording != nullptr)
{
m_DStartRecording->Destroy();
m_DStartRecording = nullptr;
}
m_bStartRecordingDetoured = false;
}
bool CForwardManager::CreateStopRecordingDetour()
{
if (m_bStopRecordingDetoured)
return true;
m_DStopRecording = DETOUR_CREATE_MEMBER(DetourHLTVStopRecording, "CHLTVDemoRecorder::StopRecording");
if (m_DStopRecording != nullptr)
{
m_DStopRecording->EnableDetour();
m_bStopRecordingDetoured = true;
return true;
}
smutils->LogError(myself, "CHLTVDemoRecorder::StartRecording detour could not be initialized.");
return false;
}
void CForwardManager::RemoveStopRecordingDetour()
{
if (m_DStopRecording != nullptr)
{
m_DStopRecording->Destroy();
m_DStopRecording = nullptr;
}
m_bStopRecordingDetoured = false;
}
#endif

View File

@ -69,9 +69,19 @@ public:
void HookServer(HLTVServerWrapper *server);
void UnhookServer(HLTVServerWrapper *server);
#ifndef WIN32
bool CreateStartRecordingDetour();
void RemoveStartRecordingDetour();
bool CreateStopRecordingDetour();
void RemoveStopRecordingDetour();
#endif
void CallOnServerStart(IHLTVServer *server);
void CallOnServerShutdown(IHLTVServer *server);
void CallOnStartRecording(IDemoRecorder *recorder, const char *filename, bool bContinuously);
void CallOnStopRecording(IDemoRecorder *recorder);
private:
void HookClient(IClient *client);
void UnhookClient(IClient *client);
@ -106,6 +116,14 @@ private:
bool m_bHasRejectConnectionOffset = false;
bool m_bHasGetChallengeTypeOffset = false;
bool m_bHasActivatePlayerOffset = false;
// Only need the detours on linux. Windows always uses its vtables..
#ifndef WIN32
bool m_bStartRecordingDetoured = false;
CDetour *m_DStartRecording = nullptr;
bool m_bStopRecordingDetoured = false;
CDetour *m_DStopRecording = nullptr;
#endif
};
extern CForwardManager g_pSTVForwards;

View File

@ -261,6 +261,11 @@ void HLTVServerWrapperManager::InitHooks()
void HLTVServerWrapperManager::ShutdownHooks()
{
#ifndef WIN32
g_pSTVForwards.RemoveStartRecordingDetour();
g_pSTVForwards.RemoveStopRecordingDetour();
#endif
#if SOURCE_ENGINE != SE_CSGO
if (m_bSendNetMsgHooked)
{
@ -272,6 +277,12 @@ void HLTVServerWrapperManager::ShutdownHooks()
void HLTVServerWrapperManager::AddServer(IHLTVServer *hltvserver)
{
#ifndef WIN32
// Create the detours once the first sourcetv server is created.
g_pSTVForwards.CreateStartRecordingDetour();
g_pSTVForwards.CreateStopRecordingDetour();
#endif
HLTVServerWrapper *wrapper = new HLTVServerWrapper(hltvserver);
m_HLTVServers.append(wrapper);
}

View File

@ -111,6 +111,20 @@
"linux" "@_ZN11CHLTVServer24GetRecordingDemoFilenameEv"
"windows" "\x81\xC1\x2A\x2A\x2A\x2A\x8B\x01\xFF\x20"
}
// StartRecording and StopRecording are virtual, but get called directly in the linux binary..
// Need to add a detour.
"CHLTVDemoRecorder::StartRecording"
{
"library" "engine"
"linux" "@_ZN17CHLTVDemoRecorder14StartRecordingEPKcb"
}
"CHLTVDemoRecorder::StopRecording"
{
"library" "engine"
"linux" "@_ZN17CHLTVDemoRecorder13StopRecordingEPK9CGameInfo"
}
}
}
"cstrike"
@ -237,6 +251,20 @@
// "HLTV server shutting down"
"windows" "\x56\x8B\xF1\x8B\x86\x2A\x2A\x2A\x2A\x8D\x8E\x2A\x2A\x2A\x2A\xFF\x50\x2A\x8B\x86\x2A\x2A\x2A\x2A\x8D\x8E"
}
// StartRecording and StopRecording are virtual, but get called directly in the linux binary..
// Need to add a detour.
"CHLTVDemoRecorder::StartRecording"
{
"library" "engine"
"linux" "@_ZN17CHLTVDemoRecorder14StartRecordingEPKcb"
}
"CHLTVDemoRecorder::StopRecording"
{
"library" "engine"
"linux" "@_ZN17CHLTVDemoRecorder13StopRecordingEv"
}
}
}
}