Probe mappin route event dispatch

This commit is contained in:
2026-06-20 00:59:35 -05:00
parent 3b0a66a87a
commit 2bbc6cbb46
3 changed files with 411 additions and 4 deletions
+349 -1
View File
@@ -125,6 +125,12 @@ constexpr uintptr_t kJournalListenerRouteAltVt30Rva = 0x08D136C;
constexpr uintptr_t kJournalListenerUiVt28Rva = 0x0431A34;
constexpr uintptr_t kJournalListenerUiVt30Rva = 0x055A4E4;
constexpr uintptr_t kJournalListenerTrackerVt28Rva = 0x14DE238;
constexpr uintptr_t kJournalRouteBridgeRva = 0x0598250;
constexpr uintptr_t kMappinRouteEventEnqueueRva = 0x13763D8;
constexpr uintptr_t kMappinRouteEventHandlerRva = 0x0AA62D0;
constexpr uintptr_t kMappinRouteActivateRva = 0x0AA6330;
constexpr uintptr_t kMappinRouteDeactivateRva = 0x27ABD7C;
constexpr uintptr_t kRouteBuildCandidateRva = 0x05625A4;
HMODULE gModule = nullptr;
std::mutex gLogMutex;
@@ -165,6 +171,12 @@ uint32_t gJournalListenerRouteAltVt30LogCount = 0;
uint32_t gJournalListenerUiVt28LogCount = 0;
uint32_t gJournalListenerUiVt30LogCount = 0;
uint32_t gJournalListenerTrackerVt28LogCount = 0;
uint32_t gJournalRouteBridgeLogCount = 0;
uint32_t gMappinRouteEventEnqueueLogCount = 0;
uint32_t gMappinRouteEventHandlerLogCount = 0;
uint32_t gMappinRouteActivateLogCount = 0;
uint32_t gMappinRouteDeactivateLogCount = 0;
uint32_t gRouteBuildCandidateLogCount = 0;
uint64_t gLogStartTick = 0;
uint64_t gLogFrequency = 0;
void* gMappinSetTrackedTarget = nullptr;
@@ -194,6 +206,15 @@ using MappinCreateCustomPositionFunc = void* (*)(void* aSystem, void* aName, voi
using MappinReleaseQueryFunc = void (*)(void* aSystem, void* aHandle);
using MappinActiveRouteFunc = void* (*)(void* aSystem);
using JournalListenerCallbackFunc = void (*)(void* aListener, void* aEvent);
using JournalRouteBridgeFunc = bool (*)(void* aThis, void* aEntryHandle);
using MappinRouteEventEnqueueFunc = void (*)(void* aSystem, uint32_t aRouteId, uint8_t aActive);
using MappinRouteEventHandlerFunc = void (*)(void* aEvent, void* aRouteHandle);
using MappinRouteToggleFunc = void (*)(void* aSystem, void* aRouteRef);
using RouteBuildCandidateFunc = void (*)(void* aThis, uint32_t aRouteId, uint32_t aAltRouteId,
uint32_t aPreviousRouteId, void* aRoutePositionData,
void* aPrimaryDescriptor, void* aSecondaryDescriptor, void* aRouteData88,
void* aRouteData108, void* aRouteData114, uint8_t aFlag,
uint32_t aMode);
ProbeFunc gOriginalGpsSystemTick = nullptr;
JournalTrackEntryFunc gOriginalJournalTrackEntry = nullptr;
ScriptNativeVoidFunc gOriginalFrameMappinPathWrapper = nullptr;
@@ -225,6 +246,12 @@ JournalListenerCallbackFunc gOriginalJournalListenerRouteAltVt30 = nullptr;
JournalListenerCallbackFunc gOriginalJournalListenerUiVt28 = nullptr;
JournalListenerCallbackFunc gOriginalJournalListenerUiVt30 = nullptr;
JournalListenerCallbackFunc gOriginalJournalListenerTrackerVt28 = nullptr;
JournalRouteBridgeFunc gOriginalJournalRouteBridge = nullptr;
MappinRouteEventEnqueueFunc gOriginalMappinRouteEventEnqueue = nullptr;
MappinRouteEventHandlerFunc gOriginalMappinRouteEventHandler = nullptr;
MappinRouteToggleFunc gOriginalMappinRouteActivate = nullptr;
MappinRouteToggleFunc gOriginalMappinRouteDeactivate = nullptr;
RouteBuildCandidateFunc gOriginalRouteBuildCandidate = nullptr;
std::filesystem::path GetLogPath()
{
@@ -508,16 +535,142 @@ void AppendObjectVtableSlots(std::ostringstream& aLine, void* aObject, std::init
{
auto* vtable = ReadPointerField(aObject, 0);
AppendPointerWithRva(aLine, "vtable", vtable);
if (!IsInImage(vtable))
{
return;
}
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);
const auto labelText = label.str();
AppendPointerWithRva(aLine, labelText.c_str(), target);
}
}
void AppendPointerField(std::ostringstream& aLine, const char* aLabel, void* aObject, uintptr_t aOffset)
{
AppendPointerWithRva(aLine, aLabel, ReadPointerField(aObject, aOffset));
}
void AppendUint32Field(std::ostringstream& aLine, const char* aLabel, void* aObject, uintptr_t aOffset)
{
uint32_t value = 0;
const auto hasValue = aObject && TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aObject) + aOffset),
value);
aLine << " " << aLabel << "=";
if (hasValue)
{
aLine << value << "/0x" << std::hex << value << std::dec;
}
else
{
aLine << "<unreadable>";
}
}
void AppendByteField(std::ostringstream& aLine, const char* aLabel, void* aObject, uintptr_t aOffset)
{
uint8_t value = 0;
const auto hasValue = aObject && TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aObject) + aOffset),
value);
aLine << " " << aLabel << "=";
if (hasValue)
{
aLine << static_cast<uint32_t>(value);
}
else
{
aLine << "<unreadable>";
}
}
void AppendRouteRefFields(std::ostringstream& aLine, const char* aPrefix, void* aRouteRef)
{
AppendPointerWithRva(aLine, aPrefix, aRouteRef);
if (!aRouteRef)
{
return;
}
std::array<uintptr_t, 7> offsets{0x00, 0x08, 0x10, 0x18, 0x30, 0x40, 0x48};
for (const auto offset : offsets)
{
std::ostringstream label;
label << aPrefix << "_f" << std::hex << offset;
const auto labelText = label.str();
AppendPointerField(aLine, labelText.c_str(), aRouteRef, offset);
}
}
void LogMappinRouteCollections(const char* aName, uint32_t aCount, void* aSystem)
{
if (!aSystem)
{
return;
}
void* activeRouteMap = nullptr;
uint32_t activeRouteCount = 0;
uint32_t activeRouteFlags = 0;
void* observerBase = nullptr;
uint32_t observerCount = 0;
TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aSystem) + 0x1A0), activeRouteMap);
TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aSystem) + 0x1AC), activeRouteCount);
TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aSystem) + 0x1B0), activeRouteFlags);
TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aSystem) + 0x288), observerBase);
TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aSystem) + 0x294), observerCount);
std::ostringstream line;
line << "mappin-route-state " << aName << " call=" << aCount;
AppendPointerWithRva(line, "system", aSystem);
AppendPointerWithRva(line, "activeRouteMap", activeRouteMap);
line << " activeRouteCount=" << activeRouteCount << " activeRouteFlags=0x" << std::hex << activeRouteFlags
<< std::dec;
AppendPointerWithRva(line, "observerBase", observerBase);
line << " observerCount=" << observerCount;
LogRed4ext(line.str());
if (!observerBase || observerCount == 0 || observerCount > 128)
{
return;
}
const auto cappedCount = std::min(observerCount, 4u);
for (uint32_t index = 0; index < cappedCount; ++index)
{
auto* observer = ReadPointerField(observerBase, index * 0x10);
std::ostringstream observerLine;
observerLine << "mappin-route-observer " << aName << " call=" << aCount << " index=" << index;
AppendPointerWithRva(observerLine, "observer", observer);
AppendObjectVtableSlots(observerLine, observer, {0x48, 0x50, 0x210, 0x260});
LogRed4ext(observerLine.str());
}
}
void LogRouteEventFields(const char* aName, uint32_t aCount, void* aEvent)
{
uint32_t routeId = 0;
uint8_t active = 0;
void* system = nullptr;
if (aEvent)
{
TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aEvent) + 0x18), routeId);
TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aEvent) + 0x20), system);
TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aEvent) + 0x28), active);
}
std::ostringstream line;
line << "route-event-fields " << aName << " call=" << aCount << " routeId=" << routeId << " active="
<< static_cast<uint32_t>(active);
AppendPointerWithRva(line, "event", aEvent);
AppendPointerWithRva(line, "system", system);
AppendObjectVtableSlots(line, aEvent, {0x20, 0x28, 0x30});
LogRed4ext(line.str());
}
void LogJournalListenerContext(const char* aName, uint32_t aCount, void* aListener, void* aEvent)
{
std::ostringstream line;
@@ -863,6 +1016,177 @@ void DetourJournalListenerTrackerVt28(void* aListener, void* aEvent)
gOriginalJournalListenerTrackerVt28, aListener, aEvent);
}
bool DetourJournalRouteBridge(void* aThis, void* aEntryHandle)
{
constexpr uint32_t kMaxCalls = 64;
const auto callCount = gJournalRouteBridgeLogCount;
const auto shouldLog = callCount < kMaxCalls;
if (shouldLog)
{
std::ostringstream line;
line << "hook JournalRouteBridge::Update candidate call=" << callCount;
AppendPointerWithRva(line, "this", aThis);
AppendPointerWithRva(line, "entryHandle", aEntryHandle);
AppendReturnRva(line, __builtin_return_address(0));
AppendPointerField(line, "field10", aThis, 0x10);
AppendPointerField(line, "field18", aThis, 0x18);
AppendPointerField(line, "field20", aThis, 0x20);
AppendByteField(line, "field30", aThis, 0x30);
AppendUint32Field(line, "field34", aThis, 0x34);
AppendUint32Field(line, "field38", aThis, 0x38);
LogRed4ext(line.str());
++gJournalRouteBridgeLogCount;
}
bool result = false;
if (gOriginalJournalRouteBridge)
{
result = gOriginalJournalRouteBridge(aThis, aEntryHandle);
}
if (shouldLog)
{
std::ostringstream line;
line << "hook JournalRouteBridge::Update candidate result call=" << callCount << " value="
<< static_cast<uint32_t>(result);
LogRed4ext(line.str());
}
return result;
}
void DetourMappinRouteEventEnqueue(void* aSystem, uint32_t aRouteId, uint8_t aActive)
{
constexpr uint32_t kMaxCalls = 64;
if (gMappinRouteEventEnqueueLogCount < kMaxCalls)
{
const auto callCount = gMappinRouteEventEnqueueLogCount;
std::ostringstream line;
line << "hook MappinSystem::RouteEventEnqueue slot240 call=" << callCount << " routeId=" << aRouteId
<< " active=" << static_cast<uint32_t>(aActive);
AppendPointerWithRva(line, "system", aSystem);
AppendReturnRva(line, __builtin_return_address(0));
LogRed4ext(line.str());
LogMappinRouteCollections("enqueue-before", callCount, aSystem);
++gMappinRouteEventEnqueueLogCount;
}
if (gOriginalMappinRouteEventEnqueue)
{
gOriginalMappinRouteEventEnqueue(aSystem, aRouteId, aActive);
}
}
void DetourMappinRouteEventHandler(void* aEvent, void* aRouteHandle)
{
constexpr uint32_t kMaxCalls = 64;
if (gMappinRouteEventHandlerLogCount < kMaxCalls)
{
const auto callCount = gMappinRouteEventHandlerLogCount;
void* routeOwner = ReadPointerField(aRouteHandle, 0);
void* routeRef = ReadPointerField(routeOwner, 0x40);
std::ostringstream line;
line << "hook MappinRouteEvent::Handle call=" << callCount;
AppendPointerWithRva(line, "event", aEvent);
AppendPointerWithRva(line, "routeHandle", aRouteHandle);
AppendPointerWithRva(line, "routeOwner", routeOwner);
AppendRouteRefFields(line, "routeRef", routeRef);
AppendReturnRva(line, __builtin_return_address(0));
LogRed4ext(line.str());
LogRouteEventFields("handler-before", callCount, aEvent);
++gMappinRouteEventHandlerLogCount;
}
if (gOriginalMappinRouteEventHandler)
{
gOriginalMappinRouteEventHandler(aEvent, aRouteHandle);
}
}
void DetourMappinRouteActivate(void* aSystem, void* aRouteRef)
{
constexpr uint32_t kMaxCalls = 64;
if (gMappinRouteActivateLogCount < kMaxCalls)
{
const auto callCount = gMappinRouteActivateLogCount;
std::ostringstream line;
line << "hook MappinSystem::RouteActivate call=" << callCount;
AppendPointerWithRva(line, "system", aSystem);
AppendRouteRefFields(line, "routeRef", aRouteRef);
AppendReturnRva(line, __builtin_return_address(0));
LogRed4ext(line.str());
LogMappinRouteCollections("activate-before", callCount, aSystem);
++gMappinRouteActivateLogCount;
}
if (gOriginalMappinRouteActivate)
{
gOriginalMappinRouteActivate(aSystem, aRouteRef);
}
}
void DetourMappinRouteDeactivate(void* aSystem, void* aRouteRef)
{
constexpr uint32_t kMaxCalls = 64;
if (gMappinRouteDeactivateLogCount < kMaxCalls)
{
const auto callCount = gMappinRouteDeactivateLogCount;
std::ostringstream line;
line << "hook MappinSystem::RouteDeactivate call=" << callCount;
AppendPointerWithRva(line, "system", aSystem);
AppendRouteRefFields(line, "routeRef", aRouteRef);
AppendReturnRva(line, __builtin_return_address(0));
LogRed4ext(line.str());
LogMappinRouteCollections("deactivate-before", callCount, aSystem);
++gMappinRouteDeactivateLogCount;
}
if (gOriginalMappinRouteDeactivate)
{
gOriginalMappinRouteDeactivate(aSystem, aRouteRef);
}
}
void DetourRouteBuildCandidate(void* aThis, uint32_t aRouteId, uint32_t aAltRouteId, uint32_t aPreviousRouteId,
void* aRoutePositionData, void* aPrimaryDescriptor, void* aSecondaryDescriptor,
void* aRouteData88, void* aRouteData108, void* aRouteData114, uint8_t aFlag,
uint32_t aMode)
{
constexpr uint32_t kMaxCalls = 64;
if (gRouteBuildCandidateLogCount < kMaxCalls)
{
std::ostringstream line;
line << "hook RouteBuildCandidate 0x5625a4 call=" << gRouteBuildCandidateLogCount
<< " routeId=" << aRouteId << " altRouteId=" << aAltRouteId
<< " previousRouteId=" << aPreviousRouteId << " flag=" << static_cast<uint32_t>(aFlag)
<< " mode=" << aMode;
AppendPointerWithRva(line, "this", aThis);
AppendPointerWithRva(line, "routePositionData", aRoutePositionData);
AppendPointerWithRva(line, "primaryDescriptor", aPrimaryDescriptor);
AppendPointerWithRva(line, "secondaryDescriptor", aSecondaryDescriptor);
AppendPointerWithRva(line, "routeData88", aRouteData88);
AppendPointerWithRva(line, "routeData108", aRouteData108);
AppendPointerWithRva(line, "routeData114", aRouteData114);
AppendReturnRva(line, __builtin_return_address(0));
LogRed4ext(line.str());
++gRouteBuildCandidateLogCount;
}
if (gOriginalRouteBuildCandidate)
{
gOriginalRouteBuildCandidate(aThis, aRouteId, aAltRouteId, aPreviousRouteId, aRoutePositionData,
aPrimaryDescriptor, aSecondaryDescriptor, aRouteData88, aRouteData108,
aRouteData114, aFlag, aMode);
}
}
void DetourFrameMappinPathWrapper(void* aThis, void* aStack, void* aResult)
{
if (gFrameMappinPathWrapperLogCount < 64)
@@ -1487,6 +1811,24 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e
AttachProbeHook("JournalListener::Tracker vt28", kJournalListenerTrackerVt28Rva,
reinterpret_cast<void*>(&DetourJournalListenerTrackerVt28),
reinterpret_cast<void**>(&gOriginalJournalListenerTrackerVt28));
AttachProbeHook("JournalRouteBridge::Update candidate", kJournalRouteBridgeRva,
reinterpret_cast<void*>(&DetourJournalRouteBridge),
reinterpret_cast<void**>(&gOriginalJournalRouteBridge));
AttachProbeHook("MappinSystem::RouteEventEnqueue slot240", kMappinRouteEventEnqueueRva,
reinterpret_cast<void*>(&DetourMappinRouteEventEnqueue),
reinterpret_cast<void**>(&gOriginalMappinRouteEventEnqueue));
AttachProbeHook("MappinRouteEvent::Handle", kMappinRouteEventHandlerRva,
reinterpret_cast<void*>(&DetourMappinRouteEventHandler),
reinterpret_cast<void**>(&gOriginalMappinRouteEventHandler));
AttachProbeHook("MappinSystem::RouteActivate", kMappinRouteActivateRva,
reinterpret_cast<void*>(&DetourMappinRouteActivate),
reinterpret_cast<void**>(&gOriginalMappinRouteActivate));
AttachProbeHook("MappinSystem::RouteDeactivate", kMappinRouteDeactivateRva,
reinterpret_cast<void*>(&DetourMappinRouteDeactivate),
reinterpret_cast<void**>(&gOriginalMappinRouteDeactivate));
AttachProbeHook("RouteBuildCandidate 0x5625a4", kRouteBuildCandidateRva,
reinterpret_cast<void*>(&DetourRouteBuildCandidate),
reinterpret_cast<void**>(&gOriginalRouteBuildCandidate));
AttachProbeHook("FrameMappinPathWrapper", kFrameMappinPathWrapperRva,
reinterpret_cast<void*>(&DetourFrameMappinPathWrapper),
reinterpret_cast<void**>(&gOriginalFrameMappinPathWrapper));
@@ -1537,6 +1879,12 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e
DetachProbeHook("JournalListener::UI vt28", kJournalListenerUiVt28Rva);
DetachProbeHook("JournalListener::UI vt30", kJournalListenerUiVt30Rva);
DetachProbeHook("JournalListener::Tracker vt28", kJournalListenerTrackerVt28Rva);
DetachProbeHook("JournalRouteBridge::Update candidate", kJournalRouteBridgeRva);
DetachProbeHook("MappinSystem::RouteEventEnqueue slot240", kMappinRouteEventEnqueueRva);
DetachProbeHook("MappinRouteEvent::Handle", kMappinRouteEventHandlerRva);
DetachProbeHook("MappinSystem::RouteActivate", kMappinRouteActivateRva);
DetachProbeHook("MappinSystem::RouteDeactivate", kMappinRouteDeactivateRva);
DetachProbeHook("RouteBuildCandidate 0x5625a4", kRouteBuildCandidateRva);
DetachProbeHook("FrameMappinPathWrapper", kFrameMappinPathWrapperRva);
DetachProbeHook("FrameMappinPathCore", kFrameMappinPathCoreRva);
DetachProbeHook("SetSelectedMappinWrapper", kSetSelectedMappinWrapperRva);