Probe GPS route cost multipliers

This commit is contained in:
2026-06-20 14:56:36 -05:00
parent 0891a6265a
commit 2a5d4a6473
+186
View File
@@ -159,6 +159,8 @@ constexpr uintptr_t kGpsRouteJobBuildRva = 0x0818BA8;
constexpr uintptr_t kGpsRouteProducerRva = 0x044CC7C;
constexpr uintptr_t kGpsSearchRva = 0x044F054;
constexpr uintptr_t kGpsEdgeCostRva = 0x044F838;
constexpr uintptr_t kGpsAuxMultiplierRva = 0x040BB00;
constexpr uintptr_t kGpsNodeMultiplierRva = 0x040BB40;
HMODULE gModule = nullptr;
std::mutex gLogMutex;
@@ -228,6 +230,8 @@ uint32_t gGpsRouteJobBuildLogCount = 0;
uint32_t gGpsRouteProducerLogCount = 0;
uint32_t gGpsSearchLogCount = 0;
uint32_t gGpsEdgeCostLogCount = 0;
uint32_t gGpsAuxMultiplierLogCount = 0;
uint32_t gGpsNodeMultiplierLogCount = 0;
std::array<uint32_t, 64> gGpsEdgeCostClassLogCounts{};
std::mutex gGpsCostLogMutex;
std::mutex gGpsAsyncLogMutex;
@@ -287,8 +291,28 @@ struct GpsEdgeCostStats
uint32_t sampleCount = 0;
};
struct GpsMultiplierSignature
{
bool active = false;
bool auxiliary = false;
uint32_t mode = 0;
uint32_t retry = 0;
uint32_t flagsC0 = 0;
uint8_t c8 = 0;
uint16_t node86 = 0;
uint16_t flags88 = 0;
uint16_t node8c = 0;
uint16_t node8e = 0;
uint8_t node90 = 0;
uint8_t node93 = 0;
uint32_t valueBits = 0;
uint32_t count = 0;
};
thread_local GpsEdgeCostStats* gActiveGpsEdgeCostStats = nullptr;
thread_local uint32_t gActiveGpsRouteProducerCall = UINT32_MAX;
std::array<GpsMultiplierSignature, 256> gGpsMultiplierSignatures{};
uint32_t gGpsMultiplierOverflowCount = 0;
using ProbeFunc = void (*)(void* aThis, void* a2, void* a3, void* a4);
using JournalTrackEntryFunc = void (*)(void* aThis, void* aEntryHandle);
@@ -339,6 +363,7 @@ using GpsEdgeCostFunc = float (*)(void* aCostProvider, void* aCurrentState, void
uint64_t aPreviousHandle, void* aPreviousSegment, void* aPreviousPoint,
uint64_t aCurrentHandle, void* aCurrentSegment, void* aCurrentPoint,
uint64_t aNeighborHandle, void* aNeighborSegment, void* aNeighborPoint);
using GpsMultiplierFunc = float (*)(void* aJob, void* aNode);
ProbeFunc gOriginalGpsSystemTick = nullptr;
JournalTrackEntryFunc gOriginalJournalTrackEntry = nullptr;
ScriptNativeVoidFunc gOriginalFrameMappinPathWrapper = nullptr;
@@ -399,6 +424,8 @@ GpsRouteJobBuildFunc gOriginalGpsRouteJobBuild = nullptr;
GpsRouteProducerFunc gOriginalGpsRouteProducer = nullptr;
GpsSearchFunc gOriginalGpsSearch = nullptr;
GpsEdgeCostFunc gOriginalGpsEdgeCost = nullptr;
GpsMultiplierFunc gOriginalGpsAuxMultiplier = nullptr;
GpsMultiplierFunc gOriginalGpsNodeMultiplier = nullptr;
std::filesystem::path GetLogPath()
{
@@ -531,6 +558,13 @@ float FloatFromBits(uint32_t aBits)
return value;
}
uint32_t FloatToBits(float aValue)
{
uint32_t bits = 0;
std::memcpy(&bits, &aValue, sizeof(bits));
return bits;
}
void LogHookCall(const char* aName, uint32_t aCount, void* aThis, void* a2, void* a3, void* a4)
{
std::ostringstream line;
@@ -1250,6 +1284,96 @@ void NoteGpsEdgeCostStats(GpsEdgeCostStats& aStats, uint8_t aSourceClass, uint8_
}
}
bool ReadGpsMultiplierSignature(bool aAuxiliary, void* aJob, void* aNode, float aValue,
GpsMultiplierSignature& aSignature)
{
if (!aJob || !aNode)
{
return false;
}
aSignature = {};
aSignature.active = true;
aSignature.auxiliary = aAuxiliary;
aSignature.valueBits = FloatToBits(aValue);
const auto jobAddress = reinterpret_cast<uintptr_t>(aJob);
const auto nodeAddress = reinterpret_cast<uintptr_t>(aNode);
if (!TryReadMemory(reinterpret_cast<const void*>(jobAddress + 0xC4), aSignature.mode) ||
aSignature.mode != 2)
{
return false;
}
TryReadMemory(reinterpret_cast<const void*>(jobAddress + 0x54), aSignature.retry);
TryReadMemory(reinterpret_cast<const void*>(jobAddress + 0xC0), aSignature.flagsC0);
TryReadMemory(reinterpret_cast<const void*>(jobAddress + 0xC8), aSignature.c8);
TryReadMemory(reinterpret_cast<const void*>(nodeAddress + 0x86), aSignature.node86);
TryReadMemory(reinterpret_cast<const void*>(nodeAddress + 0x88), aSignature.flags88);
TryReadMemory(reinterpret_cast<const void*>(nodeAddress + 0x8C), aSignature.node8c);
TryReadMemory(reinterpret_cast<const void*>(nodeAddress + 0x8E), aSignature.node8e);
TryReadMemory(reinterpret_cast<const void*>(nodeAddress + 0x90), aSignature.node90);
TryReadMemory(reinterpret_cast<const void*>(nodeAddress + 0x93), aSignature.node93);
return true;
}
bool SameGpsMultiplierSignature(const GpsMultiplierSignature& aLeft, const GpsMultiplierSignature& aRight)
{
return aLeft.active == aRight.active && aLeft.auxiliary == aRight.auxiliary &&
aLeft.mode == aRight.mode && aLeft.retry == aRight.retry && aLeft.flagsC0 == aRight.flagsC0 &&
aLeft.c8 == aRight.c8 && aLeft.node86 == aRight.node86 && aLeft.flags88 == aRight.flags88 &&
aLeft.node8c == aRight.node8c && aLeft.node8e == aRight.node8e &&
aLeft.node90 == aRight.node90 && aLeft.node93 == aRight.node93 &&
aLeft.valueBits == aRight.valueBits;
}
bool NoteGpsMultiplierSignature(GpsMultiplierSignature aSignature, uint32_t& aCallCount, uint32_t& aSignatureCount)
{
std::lock_guard lock(gGpsCostLogMutex);
auto& logCount = aSignature.auxiliary ? gGpsAuxMultiplierLogCount : gGpsNodeMultiplierLogCount;
aCallCount = logCount++;
for (auto& existing : gGpsMultiplierSignatures)
{
if (!existing.active || !SameGpsMultiplierSignature(existing, aSignature))
{
continue;
}
aSignatureCount = ++existing.count;
return false;
}
for (auto& existing : gGpsMultiplierSignatures)
{
if (existing.active)
{
continue;
}
existing = aSignature;
existing.count = 1;
aSignatureCount = 1;
return true;
}
++gGpsMultiplierOverflowCount;
return false;
}
void AppendGpsMultiplierSignature(std::ostringstream& aLine, const GpsMultiplierSignature& aSignature)
{
aLine << " mode=" << aSignature.mode << " retry=" << aSignature.retry << " flagsC0=0x" << std::hex
<< aSignature.flagsC0 << " c8=0x" << static_cast<uint32_t>(aSignature.c8)
<< " node86=0x" << aSignature.node86 << " flags88=0x" << aSignature.flags88
<< " node8c=0x" << aSignature.node8c << " node8e=0x" << aSignature.node8e
<< " node90=0x" << static_cast<uint32_t>(aSignature.node90)
<< " node93=0x" << static_cast<uint32_t>(aSignature.node93) << std::dec
<< " value=" << FloatFromBits(aSignature.valueBits);
}
void AppendGpsRouteProducerQuerySummary(std::ostringstream& aLine, void* aQuery)
{
AppendVector4Field(aLine, "query_start00", aQuery, 0x00);
@@ -3403,6 +3527,60 @@ float DetourGpsEdgeCost(void* aCostProvider, void* aCurrentState, void* aNeighbo
return result;
}
void LogGpsMultiplierSignature(const char* aName, uintptr_t aRva, bool aAuxiliary, void* aJob, void* aNode,
float aValue, const void* aReturnAddress)
{
GpsMultiplierSignature signature{};
if (!ReadGpsMultiplierSignature(aAuxiliary, aJob, aNode, aValue, signature))
{
return;
}
uint32_t callCount = 0;
uint32_t signatureCount = 0;
const auto isNew = NoteGpsMultiplierSignature(signature, callCount, signatureCount);
const auto shouldLog = isNew || signatureCount == 8 || signatureCount == 64 || signatureCount == 512;
if (!shouldLog)
{
return;
}
std::ostringstream line;
line << "hook " << aName << " 0x" << std::hex << aRva << std::dec << " call=" << callCount
<< " signatureCount=" << signatureCount << " new=" << static_cast<uint32_t>(isNew);
AppendPointerWithRva(line, "job", aJob);
AppendPointerWithRva(line, "node", aNode);
AppendGpsMultiplierSignature(line, signature);
AppendReturnRva(line, aReturnAddress);
LogRed4ext(line.str());
}
float DetourGpsAuxMultiplier(void* aJob, void* aNode)
{
float result = 1.0f;
if (gOriginalGpsAuxMultiplier)
{
result = gOriginalGpsAuxMultiplier(aJob, aNode);
}
LogGpsMultiplierSignature("GPSAuxMultiplier", kGpsAuxMultiplierRva, true, aJob, aNode, result,
__builtin_return_address(0));
return result;
}
float DetourGpsNodeMultiplier(void* aJob, void* aNode)
{
float result = 1.0f;
if (gOriginalGpsNodeMultiplier)
{
result = gOriginalGpsNodeMultiplier(aJob, aNode);
}
LogGpsMultiplierSignature("GPSNodeMultiplier", kGpsNodeMultiplierRva, false, aJob, aNode, result,
__builtin_return_address(0));
return result;
}
void AttachProbeHook(const char* aName, uintptr_t aRva, void* aDetour, void** aOriginal)
{
if (!gSdk || !gSdk->hooking || !gSdk->hooking->Attach)
@@ -3754,6 +3932,12 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e
reinterpret_cast<void**>(&gOriginalGpsSearch));
AttachProbeHook("GPSEdgeCost 0x44f838", kGpsEdgeCostRva, reinterpret_cast<void*>(&DetourGpsEdgeCost),
reinterpret_cast<void**>(&gOriginalGpsEdgeCost));
AttachProbeHook("GPSAuxMultiplier 0x40bb00", kGpsAuxMultiplierRva,
reinterpret_cast<void*>(&DetourGpsAuxMultiplier),
reinterpret_cast<void**>(&gOriginalGpsAuxMultiplier));
AttachProbeHook("GPSNodeMultiplier 0x40bb40", kGpsNodeMultiplierRva,
reinterpret_cast<void*>(&DetourGpsNodeMultiplier),
reinterpret_cast<void**>(&gOriginalGpsNodeMultiplier));
AttachProbeHook("GPSSystem/Tick", kGpsSystemTickRva, reinterpret_cast<void*>(&DetourGpsSystemTick),
reinterpret_cast<void**>(&gOriginalGpsSystemTick));
AttachProbeHook("JournalManager::TrackEntry impl", kJournalTrackEntryImplRva,
@@ -3876,6 +4060,8 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e
DetachProbeHook("GPSRouteProducer 0x44cc7c", kGpsRouteProducerRva);
DetachProbeHook("GPSSearch 0x44f054", kGpsSearchRva);
DetachProbeHook("GPSEdgeCost 0x44f838", kGpsEdgeCostRva);
DetachProbeHook("GPSAuxMultiplier 0x40bb00", kGpsAuxMultiplierRva);
DetachProbeHook("GPSNodeMultiplier 0x40bb40", kGpsNodeMultiplierRva);
DetachProbeHook("GPSSystem/Tick", kGpsSystemTickRva);
DetachProbeHook("JournalManager::TrackEntry impl", kJournalTrackEntryImplRva);
if (kEnableVerboseJournalListenerHooks)