diff --git a/AMBuilder b/AMBuilder index 1a45f1d..f9ce5ca 100644 --- a/AMBuilder +++ b/AMBuilder @@ -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'), diff --git a/extension.cpp b/extension.cpp index b3f4199..ea861c3 100644 --- a/extension.cpp +++ b/extension.cpp @@ -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"); diff --git a/extension.h b/extension.h index a650c65..45292ec 100644 --- a/extension.h +++ b/extension.h @@ -40,6 +40,9 @@ #include "smsdk_ext.h" #include #include +#ifndef WIN32 +#include "CDetour\detours.h" +#endif #include "ihltvdirector.h" #include "ihltv.h" #include "iserver.h" diff --git a/forwards.cpp b/forwards.cpp index 2854bce..aa0a557 100644 --- a/forwards.cpp +++ b/forwards.cpp @@ -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 &, 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); -} \ No newline at end of file +// 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 \ No newline at end of file diff --git a/forwards.h b/forwards.h index 8829af1..5e7c1e4 100644 --- a/forwards.h +++ b/forwards.h @@ -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; diff --git a/hltvserverwrapper.cpp b/hltvserverwrapper.cpp index 532d21a..d4a60e4 100644 --- a/hltvserverwrapper.cpp +++ b/hltvserverwrapper.cpp @@ -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); } diff --git a/sourcetvmanager.games.txt b/sourcetvmanager.games.txt index b803011..ae064d2 100644 --- a/sourcetvmanager.games.txt +++ b/sourcetvmanager.games.txt @@ -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" + } } } } \ No newline at end of file