Probe journal listener route callbacks

This commit is contained in:
2026-06-20 00:40:34 -05:00
parent 451e84beb1
commit 3b0a66a87a
3 changed files with 264 additions and 0 deletions
+202
View File
@@ -16,6 +16,7 @@
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <initializer_list>
#include <mutex>
#include <sstream>
#include <string_view>
@@ -115,6 +116,15 @@ constexpr uintptr_t kMappinPositionQuerySlotOffset = 0x2E0;
constexpr uintptr_t kMappinCreateCustomPositionSlotOffset = 0x2F0;
constexpr uintptr_t kMappinReleaseQuerySlotOffset = 0x368;
constexpr uintptr_t kMappinActiveRouteSlotOffset = 0x3A8;
constexpr uintptr_t kJournalListenerObjectiveVt28Rva = 0x0EA89A8;
constexpr uintptr_t kJournalListenerObjectiveVt30Rva = 0x0EA8958;
constexpr uintptr_t kJournalListenerRouteVt28Rva = 0x0E63F80;
constexpr uintptr_t kJournalListenerRouteVt30Rva = 0x0E63E6C;
constexpr uintptr_t kJournalListenerRouteAltVt28Rva = 0x0E63F00;
constexpr uintptr_t kJournalListenerRouteAltVt30Rva = 0x08D136C;
constexpr uintptr_t kJournalListenerUiVt28Rva = 0x0431A34;
constexpr uintptr_t kJournalListenerUiVt30Rva = 0x055A4E4;
constexpr uintptr_t kJournalListenerTrackerVt28Rva = 0x14DE238;
HMODULE gModule = nullptr;
std::mutex gLogMutex;
@@ -146,6 +156,15 @@ uint32_t gMappinPositionQueryLogCount = 0;
uint32_t gMappinCreateCustomPositionLogCount = 0;
uint32_t gMappinReleaseQueryLogCount = 0;
uint32_t gMappinActiveRouteLogCount = 0;
uint32_t gJournalListenerObjectiveVt28LogCount = 0;
uint32_t gJournalListenerObjectiveVt30LogCount = 0;
uint32_t gJournalListenerRouteVt28LogCount = 0;
uint32_t gJournalListenerRouteVt30LogCount = 0;
uint32_t gJournalListenerRouteAltVt28LogCount = 0;
uint32_t gJournalListenerRouteAltVt30LogCount = 0;
uint32_t gJournalListenerUiVt28LogCount = 0;
uint32_t gJournalListenerUiVt30LogCount = 0;
uint32_t gJournalListenerTrackerVt28LogCount = 0;
uint64_t gLogStartTick = 0;
uint64_t gLogFrequency = 0;
void* gMappinSetTrackedTarget = nullptr;
@@ -174,6 +193,7 @@ using MappinPositionQueryFunc = void* (*)(void* aSystem, void* a2, void* a3, voi
using MappinCreateCustomPositionFunc = void* (*)(void* aSystem, void* aName, void* aParams, void* aPositionData);
using MappinReleaseQueryFunc = void (*)(void* aSystem, void* aHandle);
using MappinActiveRouteFunc = void* (*)(void* aSystem);
using JournalListenerCallbackFunc = void (*)(void* aListener, void* aEvent);
ProbeFunc gOriginalGpsSystemTick = nullptr;
JournalTrackEntryFunc gOriginalJournalTrackEntry = nullptr;
ScriptNativeVoidFunc gOriginalFrameMappinPathWrapper = nullptr;
@@ -196,6 +216,15 @@ MappinPositionQueryFunc gOriginalMappinPositionQuery = nullptr;
MappinCreateCustomPositionFunc gOriginalMappinCreateCustomPosition = nullptr;
MappinReleaseQueryFunc gOriginalMappinReleaseQuery = nullptr;
MappinActiveRouteFunc gOriginalMappinActiveRoute = nullptr;
JournalListenerCallbackFunc gOriginalJournalListenerObjectiveVt28 = nullptr;
JournalListenerCallbackFunc gOriginalJournalListenerObjectiveVt30 = nullptr;
JournalListenerCallbackFunc gOriginalJournalListenerRouteVt28 = nullptr;
JournalListenerCallbackFunc gOriginalJournalListenerRouteVt30 = nullptr;
JournalListenerCallbackFunc gOriginalJournalListenerRouteAltVt28 = nullptr;
JournalListenerCallbackFunc gOriginalJournalListenerRouteAltVt30 = nullptr;
JournalListenerCallbackFunc gOriginalJournalListenerUiVt28 = nullptr;
JournalListenerCallbackFunc gOriginalJournalListenerUiVt30 = nullptr;
JournalListenerCallbackFunc gOriginalJournalListenerTrackerVt28 = nullptr;
std::filesystem::path GetLogPath()
{
@@ -463,6 +492,72 @@ bool TryReadMemory(const void* aPointer, T& aValue)
return true;
}
void* ReadPointerField(void* aObject, uintptr_t aOffset)
{
void* value = nullptr;
if (!aObject)
{
return nullptr;
}
TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aObject) + aOffset), value);
return value;
}
void AppendObjectVtableSlots(std::ostringstream& aLine, void* aObject, std::initializer_list<uintptr_t> aSlots)
{
auto* vtable = ReadPointerField(aObject, 0);
AppendPointerWithRva(aLine, "vtable", vtable);
for (const auto slot : aSlots)
{
auto* target = vtable ? ReadPointerField(vtable, slot) : nullptr;
std::ostringstream label;
label << "slot" << std::hex << slot;
AppendPointerWithRva(aLine, label.str().c_str(), target);
}
}
void LogJournalListenerContext(const char* aName, uint32_t aCount, void* aListener, void* aEvent)
{
std::ostringstream line;
line << "hook " << aName << " call=" << aCount;
AppendPointerWithRva(line, "listener", aListener);
AppendPointerWithRva(line, "event", aEvent);
AppendReturnRva(line, __builtin_return_address(0));
auto* field08 = ReadPointerField(aListener, 0x08);
auto* field18 = ReadPointerField(aListener, 0x18);
auto* field20 = ReadPointerField(aListener, 0x20);
auto* field70 = ReadPointerField(aListener, 0x70);
auto* field78 = ReadPointerField(aListener, 0x78);
AppendPointerWithRva(line, "field08", field08);
AppendPointerWithRva(line, "field18", field18);
AppendPointerWithRva(line, "field20", field20);
AppendPointerWithRva(line, "field70", field70);
AppendPointerWithRva(line, "field78", field78);
LogRed4ext(line.str());
if (field70)
{
std::ostringstream owner;
owner << "journal-listener-context " << aName << " call=" << aCount;
AppendPointerWithRva(owner, "field70", field70);
AppendObjectVtableSlots(owner, field70, {0x1C8, 0x218, 0x230, 0x238, 0x240, 0x248, 0x340, 0x348, 0x358});
LogRed4ext(owner.str());
}
if (field78 && field78 != field70)
{
std::ostringstream service;
service << "journal-listener-context " << aName << " call=" << aCount;
AppendPointerWithRva(service, "field78", field78);
AppendObjectVtableSlots(service, field78,
{0x1C8, 0x218, 0x220, 0x230, 0x238, 0x240, 0x248, 0x340, 0x348, 0x358});
LogRed4ext(service.str());
}
}
void ScanRegionForString(const uint8_t* aRegionBegin, size_t aRegionSize, uintptr_t aImageBase, const char* aNeedle,
uint32_t& aMatches)
{
@@ -697,6 +792,77 @@ void DetourJournalTrackEntry(void* aThis, void* aEntryHandle)
}
}
void DispatchJournalListenerCallback(const char* aName, uint32_t& aCounter, JournalListenerCallbackFunc aOriginal,
void* aListener, void* aEvent)
{
constexpr uint32_t kMaxCalls = 64;
if (aCounter < kMaxCalls)
{
LogJournalListenerContext(aName, aCounter, aListener, aEvent);
++aCounter;
}
if (aOriginal)
{
aOriginal(aListener, aEvent);
}
}
void DetourJournalListenerObjectiveVt28(void* aListener, void* aEvent)
{
DispatchJournalListenerCallback("JournalListener::Objective vt28", gJournalListenerObjectiveVt28LogCount,
gOriginalJournalListenerObjectiveVt28, aListener, aEvent);
}
void DetourJournalListenerObjectiveVt30(void* aListener, void* aEvent)
{
DispatchJournalListenerCallback("JournalListener::Objective vt30", gJournalListenerObjectiveVt30LogCount,
gOriginalJournalListenerObjectiveVt30, aListener, aEvent);
}
void DetourJournalListenerRouteVt28(void* aListener, void* aEvent)
{
DispatchJournalListenerCallback("JournalListener::Route vt28", gJournalListenerRouteVt28LogCount,
gOriginalJournalListenerRouteVt28, aListener, aEvent);
}
void DetourJournalListenerRouteVt30(void* aListener, void* aEvent)
{
DispatchJournalListenerCallback("JournalListener::Route vt30", gJournalListenerRouteVt30LogCount,
gOriginalJournalListenerRouteVt30, aListener, aEvent);
}
void DetourJournalListenerRouteAltVt28(void* aListener, void* aEvent)
{
DispatchJournalListenerCallback("JournalListener::RouteAlt vt28", gJournalListenerRouteAltVt28LogCount,
gOriginalJournalListenerRouteAltVt28, aListener, aEvent);
}
void DetourJournalListenerRouteAltVt30(void* aListener, void* aEvent)
{
DispatchJournalListenerCallback("JournalListener::RouteAlt vt30", gJournalListenerRouteAltVt30LogCount,
gOriginalJournalListenerRouteAltVt30, aListener, aEvent);
}
void DetourJournalListenerUiVt28(void* aListener, void* aEvent)
{
DispatchJournalListenerCallback("JournalListener::UI vt28", gJournalListenerUiVt28LogCount,
gOriginalJournalListenerUiVt28, aListener, aEvent);
}
void DetourJournalListenerUiVt30(void* aListener, void* aEvent)
{
DispatchJournalListenerCallback("JournalListener::UI vt30", gJournalListenerUiVt30LogCount,
gOriginalJournalListenerUiVt30, aListener, aEvent);
}
void DetourJournalListenerTrackerVt28(void* aListener, void* aEvent)
{
DispatchJournalListenerCallback("JournalListener::Tracker vt28", gJournalListenerTrackerVt28LogCount,
gOriginalJournalListenerTrackerVt28, aListener, aEvent);
}
void DetourFrameMappinPathWrapper(void* aThis, void* aStack, void* aResult)
{
if (gFrameMappinPathWrapperLogCount < 64)
@@ -1294,6 +1460,33 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e
AttachProbeHook("JournalManager::TrackEntry impl", kJournalTrackEntryImplRva,
reinterpret_cast<void*>(&DetourJournalTrackEntry),
reinterpret_cast<void**>(&gOriginalJournalTrackEntry));
AttachProbeHook("JournalListener::Objective vt28", kJournalListenerObjectiveVt28Rva,
reinterpret_cast<void*>(&DetourJournalListenerObjectiveVt28),
reinterpret_cast<void**>(&gOriginalJournalListenerObjectiveVt28));
AttachProbeHook("JournalListener::Objective vt30", kJournalListenerObjectiveVt30Rva,
reinterpret_cast<void*>(&DetourJournalListenerObjectiveVt30),
reinterpret_cast<void**>(&gOriginalJournalListenerObjectiveVt30));
AttachProbeHook("JournalListener::Route vt28", kJournalListenerRouteVt28Rva,
reinterpret_cast<void*>(&DetourJournalListenerRouteVt28),
reinterpret_cast<void**>(&gOriginalJournalListenerRouteVt28));
AttachProbeHook("JournalListener::Route vt30", kJournalListenerRouteVt30Rva,
reinterpret_cast<void*>(&DetourJournalListenerRouteVt30),
reinterpret_cast<void**>(&gOriginalJournalListenerRouteVt30));
AttachProbeHook("JournalListener::RouteAlt vt28", kJournalListenerRouteAltVt28Rva,
reinterpret_cast<void*>(&DetourJournalListenerRouteAltVt28),
reinterpret_cast<void**>(&gOriginalJournalListenerRouteAltVt28));
AttachProbeHook("JournalListener::RouteAlt vt30", kJournalListenerRouteAltVt30Rva,
reinterpret_cast<void*>(&DetourJournalListenerRouteAltVt30),
reinterpret_cast<void**>(&gOriginalJournalListenerRouteAltVt30));
AttachProbeHook("JournalListener::UI vt28", kJournalListenerUiVt28Rva,
reinterpret_cast<void*>(&DetourJournalListenerUiVt28),
reinterpret_cast<void**>(&gOriginalJournalListenerUiVt28));
AttachProbeHook("JournalListener::UI vt30", kJournalListenerUiVt30Rva,
reinterpret_cast<void*>(&DetourJournalListenerUiVt30),
reinterpret_cast<void**>(&gOriginalJournalListenerUiVt30));
AttachProbeHook("JournalListener::Tracker vt28", kJournalListenerTrackerVt28Rva,
reinterpret_cast<void*>(&DetourJournalListenerTrackerVt28),
reinterpret_cast<void**>(&gOriginalJournalListenerTrackerVt28));
AttachProbeHook("FrameMappinPathWrapper", kFrameMappinPathWrapperRva,
reinterpret_cast<void*>(&DetourFrameMappinPathWrapper),
reinterpret_cast<void**>(&gOriginalFrameMappinPathWrapper));
@@ -1335,6 +1528,15 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e
{
DetachProbeHook("GPSSystem/Tick", kGpsSystemTickRva);
DetachProbeHook("JournalManager::TrackEntry impl", kJournalTrackEntryImplRva);
DetachProbeHook("JournalListener::Objective vt28", kJournalListenerObjectiveVt28Rva);
DetachProbeHook("JournalListener::Objective vt30", kJournalListenerObjectiveVt30Rva);
DetachProbeHook("JournalListener::Route vt28", kJournalListenerRouteVt28Rva);
DetachProbeHook("JournalListener::Route vt30", kJournalListenerRouteVt30Rva);
DetachProbeHook("JournalListener::RouteAlt vt28", kJournalListenerRouteAltVt28Rva);
DetachProbeHook("JournalListener::RouteAlt vt30", kJournalListenerRouteAltVt30Rva);
DetachProbeHook("JournalListener::UI vt28", kJournalListenerUiVt28Rva);
DetachProbeHook("JournalListener::UI vt30", kJournalListenerUiVt30Rva);
DetachProbeHook("JournalListener::Tracker vt28", kJournalListenerTrackerVt28Rva);
DetachProbeHook("FrameMappinPathWrapper", kFrameMappinPathWrapperRva);
DetachProbeHook("FrameMappinPathCore", kFrameMappinPathCoreRva);
DetachProbeHook("SetSelectedMappinWrapper", kSetSelectedMappinWrapperRva);