4484 lines
169 KiB
C++
4484 lines
169 KiB
C++
#include <RED4ext/Api/ApiVersion.hpp>
|
|
#include <RED4ext/Api/v1/EMainReason.hpp>
|
|
#include <RED4ext/Api/v1/PluginHandle.hpp>
|
|
#include <RED4ext/Api/v1/PluginInfo.hpp>
|
|
#include <RED4ext/Api/v1/Runtime.hpp>
|
|
#include <RED4ext/Api/v1/Version.hpp>
|
|
#include <RED4ext/Common.hpp>
|
|
|
|
#include <windows.h>
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <chrono>
|
|
#include <cstring>
|
|
#include <cstdint>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iomanip>
|
|
#include <initializer_list>
|
|
#include <mutex>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <string_view>
|
|
|
|
namespace RED4ext::v1
|
|
{
|
|
struct Logger
|
|
{
|
|
void (*Trace)(PluginHandle aHandle, const char* aMessage);
|
|
void (*TraceF)(PluginHandle aHandle, const char* aFormat, ...);
|
|
void (*TraceW)(PluginHandle aHandle, const wchar_t* aMessage);
|
|
void (*TraceWF)(PluginHandle aHandle, const wchar_t* aFormat, ...);
|
|
void (*Debug)(PluginHandle aHandle, const char* aMessage);
|
|
void (*DebugF)(PluginHandle aHandle, const char* aFormat, ...);
|
|
void (*DebugW)(PluginHandle aHandle, const wchar_t* aMessage);
|
|
void (*DebugWF)(PluginHandle aHandle, const wchar_t* aFormat, ...);
|
|
void (*Info)(PluginHandle aHandle, const char* aMessage);
|
|
void (*InfoF)(PluginHandle aHandle, const char* aFormat, ...);
|
|
void (*InfoW)(PluginHandle aHandle, const wchar_t* aMessage);
|
|
void (*InfoWF)(PluginHandle aHandle, const wchar_t* aFormat, ...);
|
|
void (*Warn)(PluginHandle aHandle, const char* aMessage);
|
|
void (*WarnF)(PluginHandle aHandle, const char* aFormat, ...);
|
|
void (*WarnW)(PluginHandle aHandle, const wchar_t* aMessage);
|
|
void (*WarnWF)(PluginHandle aHandle, const wchar_t* aFormat, ...);
|
|
void (*Error)(PluginHandle aHandle, const char* aMessage);
|
|
void (*ErrorF)(PluginHandle aHandle, const char* aFormat, ...);
|
|
void (*ErrorW)(PluginHandle aHandle, const wchar_t* aMessage);
|
|
void (*ErrorWF)(PluginHandle aHandle, const wchar_t* aFormat, ...);
|
|
void (*Critical)(PluginHandle aHandle, const char* aMessage);
|
|
void (*CriticalF)(PluginHandle aHandle, const char* aFormat, ...);
|
|
void (*CriticalW)(PluginHandle aHandle, const wchar_t* aMessage);
|
|
void (*CriticalWF)(PluginHandle aHandle, const wchar_t* aFormat, ...);
|
|
};
|
|
|
|
struct GameState
|
|
{
|
|
bool (*OnEnter)(void* aApp);
|
|
bool (*OnUpdate)(void* aApp);
|
|
bool (*OnExit)(void* aApp);
|
|
};
|
|
|
|
struct GameStates
|
|
{
|
|
bool (*Add)(PluginHandle aHandle, uint32_t aType, GameState* aState);
|
|
};
|
|
|
|
struct Hooking
|
|
{
|
|
bool (*Attach)(PluginHandle aHandle, void* aTarget, void* aDetour, void** aOriginal);
|
|
bool (*Detach)(PluginHandle aHandle, void* aTarget);
|
|
};
|
|
|
|
struct Scripts
|
|
{
|
|
bool (*Add)(PluginHandle aHandle, const wchar_t* aPath);
|
|
bool (*RegisterNeverRefType)(const char* aType);
|
|
bool (*RegisterMixedRefType)(const char* aType);
|
|
};
|
|
|
|
struct Sdk;
|
|
|
|
struct Sdk
|
|
{
|
|
SemVer* runtime;
|
|
Logger* logger;
|
|
Hooking* hooking;
|
|
GameStates* gameStates;
|
|
Scripts* scripts;
|
|
};
|
|
}
|
|
|
|
namespace
|
|
{
|
|
constexpr uint32_t kGameStateRunning = 2;
|
|
constexpr bool kEnableVerboseJournalListenerHooks = false;
|
|
constexpr bool kEnableVerboseRouteObserverHooks = false;
|
|
constexpr bool kEnableVerboseMappinRouteState = false;
|
|
constexpr bool kEnableGpsMultiplierHooks = false;
|
|
constexpr bool kEnableGpsCostTablePatch = false;
|
|
constexpr bool kEnableGpsProviderClassPatch = true;
|
|
constexpr bool kEnableGpsProviderClassPatchTrace = true;
|
|
constexpr bool kEnableGpsTraceHooks = false;
|
|
constexpr bool kEnableGpsRouteJobStartTrace = false;
|
|
constexpr bool kEnableGpsLocalSearchHooks = false;
|
|
constexpr bool kEnableRouteUiTraceHooks = false;
|
|
constexpr uintptr_t kGpsSystemTickRva = 0x0E6B62C;
|
|
constexpr uintptr_t kJournalTrackEntryImplRva = 0x05944FC;
|
|
constexpr uintptr_t kJournalListenerArrayOffset = 0x210;
|
|
constexpr uintptr_t kJournalListenerCountOffset = 0x21C;
|
|
constexpr uintptr_t kMappinSystemResolverRva = 0x02BB840;
|
|
constexpr uintptr_t kFrameMappinPathWrapperRva = 0x27C4314;
|
|
constexpr uintptr_t kFrameMappinPathCoreRva = 0x27BC1EC;
|
|
constexpr uintptr_t kSetSelectedMappinWrapperRva = 0x27C4A38;
|
|
constexpr uintptr_t kSetSelectedMappinByIdWrapperRva = 0x27C4944;
|
|
constexpr uintptr_t kSetSelectedMappinByPositionWrapperRva = 0x27C49C4;
|
|
constexpr uintptr_t kSetSelectedMappinCoreRva = 0x27C1684;
|
|
constexpr uintptr_t kTrackCustomPositionMappinWrapperRva = 0x27C4AAC;
|
|
constexpr uintptr_t kTrackCustomPositionMappinCoreRva = 0x27C2318;
|
|
constexpr uintptr_t kUntrackCustomPositionMappinWrapperRva = 0x27C4BA0;
|
|
constexpr uintptr_t kTrackMappinWrapperRva = 0x27C4AB4;
|
|
constexpr uintptr_t kTrackMappinCoreRva = 0x27C11D0;
|
|
constexpr uintptr_t kSetMappinTrackingAlternativeWrapperRva = 0x1246794;
|
|
constexpr uintptr_t kMappinUpdateCustomPositionSlotOffset = 0x1F0;
|
|
constexpr uintptr_t kMappinSetTrackedSlotOffset = 0x220;
|
|
constexpr uintptr_t kMappinClearTrackedSlotOffset = 0x228;
|
|
constexpr uintptr_t kMappinFramePathSlotOffset = 0x280;
|
|
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;
|
|
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;
|
|
constexpr uintptr_t kRouteObserver0ActivateRva = 0x0AA6610;
|
|
constexpr uintptr_t kRouteObserver1ActivateRva = 0x0AA6628;
|
|
constexpr uintptr_t kRouteObserver23ActivateRva = 0x0AA63E0;
|
|
constexpr uintptr_t kRouteObserver0DeactivateRva = 0x27B10C0;
|
|
constexpr uintptr_t kRouteObserver1DeactivateRva = 0x295D4A0;
|
|
constexpr uintptr_t kRouteObserver23DeactivateRva = 0x286A85C;
|
|
constexpr uintptr_t kObserver1SystemTypeGlobalRva = 0x342F6A8;
|
|
constexpr uintptr_t kObserver1ServiceLookupRva = 0x0287C44;
|
|
constexpr uintptr_t kObserver1ServiceRouteLookupSlotOffset = 0x220;
|
|
constexpr uintptr_t kRunGpsQueryBodyRva = 0x29BD128;
|
|
constexpr uintptr_t kUpdateGpsQueryBodyRva = 0x29BD254;
|
|
constexpr uintptr_t kGpsQuerySubmitRva = 0x070A42C;
|
|
constexpr uintptr_t kGpsQueryDispatchRva = 0x070A570;
|
|
constexpr uintptr_t kGpsQueryResultFetchRva = 0x07094B8;
|
|
constexpr uintptr_t kGpsQueryStatusRva = 0x0AA5704;
|
|
constexpr uintptr_t kGpsDispatchAdvanceRva = 0x08D17D8;
|
|
constexpr uintptr_t kGpsAsyncWorkerTickRva = 0x0126B28;
|
|
constexpr uintptr_t kGpsRouteJobStartRva = 0x0818928;
|
|
constexpr uintptr_t kGpsRouteJobPollRva = 0x0883CD8;
|
|
constexpr uintptr_t kGpsRouteFinishRva = 0x11F5F60;
|
|
constexpr uintptr_t kGpsRouteJobBuildRva = 0x0818BA8;
|
|
constexpr uintptr_t kGpsRouteProducerRva = 0x044CC7C;
|
|
constexpr uintptr_t kGpsSearchRva = 0x044F054;
|
|
constexpr uintptr_t kGpsEdgeCostRva = 0x044F838;
|
|
constexpr uintptr_t kGpsBaseCostProviderVtableRva = 0x2AE6120;
|
|
constexpr uintptr_t kGpsFilteredCostProviderVtableRva = 0x2AE60F8;
|
|
constexpr uintptr_t kGpsAuxMultiplierRva = 0x040BB00;
|
|
constexpr uintptr_t kGpsNodeMultiplierRva = 0x040BB40;
|
|
constexpr uintptr_t kGpsCostMultiplierTableRva = 0x3154D28;
|
|
constexpr std::array<float, 7> kGpsPatchedCostMultipliers = {
|
|
12.0f, 7.0f, 2.5f, 0.0f, 3.0f, 2.0f, 1.25f,
|
|
};
|
|
constexpr size_t kGpsProviderClassCount = 64;
|
|
|
|
struct GpsProviderClassOverride
|
|
{
|
|
size_t classId;
|
|
float multiplier;
|
|
};
|
|
|
|
constexpr std::array<GpsProviderClassOverride, 5> kGpsProviderClassOverrides = {{
|
|
{1, 0.70f},
|
|
{3, 1.45f},
|
|
{4, 1.25f},
|
|
{5, 1.75f},
|
|
{15, 0.80f},
|
|
}};
|
|
|
|
constexpr std::array<size_t, 6> kGpsProviderClassProbeIds = {1, 3, 4, 5, 14, 15};
|
|
|
|
HMODULE gModule = nullptr;
|
|
std::mutex gLogMutex;
|
|
RED4ext::v1::PluginHandle gHandle = nullptr;
|
|
const RED4ext::v1::Sdk* gSdk = nullptr;
|
|
uint32_t gUpdateLogCount = 0;
|
|
bool gScannedExecutable = false;
|
|
uint32_t gGpsTickLogCount = 0;
|
|
uint32_t gJournalTrackEntryLogCount = 0;
|
|
uint32_t gJournalTrackEntryListenerDumpCount = 0;
|
|
uint32_t gFrameMappinPathWrapperLogCount = 0;
|
|
uint32_t gFrameMappinPathCoreLogCount = 0;
|
|
uint32_t gSetSelectedMappinWrapperLogCount = 0;
|
|
uint32_t gSetSelectedMappinByIdWrapperLogCount = 0;
|
|
uint32_t gSetSelectedMappinByPositionWrapperLogCount = 0;
|
|
uint32_t gSetSelectedMappinCoreLogCount = 0;
|
|
uint32_t gTrackCustomPositionMappinWrapperLogCount = 0;
|
|
uint32_t gTrackCustomPositionMappinCoreLogCount = 0;
|
|
uint32_t gUntrackCustomPositionMappinWrapperLogCount = 0;
|
|
uint32_t gTrackMappinWrapperLogCount = 0;
|
|
uint32_t gTrackMappinCoreLogCount = 0;
|
|
uint32_t gSetMappinTrackingAlternativeLogCount = 0;
|
|
uint32_t gMappinSystemSlotLogCount = 0;
|
|
uint32_t gMappinUpdateCustomPositionLogCount = 0;
|
|
uint32_t gMappinSetTrackedLogCount = 0;
|
|
uint32_t gMappinClearTrackedLogCount = 0;
|
|
uint32_t gMappinFramePathLogCount = 0;
|
|
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;
|
|
uint32_t gJournalRouteBridgeLogCount = 0;
|
|
uint32_t gMappinRouteEventEnqueueLogCount = 0;
|
|
uint32_t gMappinRouteEventHandlerLogCount = 0;
|
|
uint32_t gMappinRouteActivateLogCount = 0;
|
|
uint32_t gMappinRouteDeactivateLogCount = 0;
|
|
uint32_t gRouteBuildCandidateLogCount = 0;
|
|
uint32_t gRouteObserver0ActivateLogCount = 0;
|
|
uint32_t gRouteObserver1ActivateLogCount = 0;
|
|
uint32_t gRouteObserver23ActivateLogCount = 0;
|
|
uint32_t gRouteObserver0DeactivateLogCount = 0;
|
|
uint32_t gRouteObserver1DeactivateLogCount = 0;
|
|
uint32_t gRouteObserver23DeactivateLogCount = 0;
|
|
uint32_t gObserver1ServiceLookupLogCount = 0;
|
|
uint32_t gObserver1ServiceRouteLookupLogCount = 0;
|
|
uint32_t gRunGpsQueryBodyLogCount = 0;
|
|
uint32_t gUpdateGpsQueryBodyLogCount = 0;
|
|
uint32_t gGpsQuerySubmitLogCount = 0;
|
|
uint32_t gGpsQueryDispatchLogCount = 0;
|
|
uint32_t gGpsQueryResultFetchLogCount = 0;
|
|
uint32_t gGpsQueryStatusLogCount = 0;
|
|
uint32_t gGpsDispatchAdvanceLogCount = 0;
|
|
uint32_t gGpsAsyncWorkerTickLogCount = 0;
|
|
uint32_t gGpsRouteJobStartLogCount = 0;
|
|
uint32_t gGpsRouteJobPollLogCount = 0;
|
|
uint32_t gGpsRouteFinishLogCount = 0;
|
|
uint32_t gGpsRouteJobBuildLogCount = 0;
|
|
uint32_t gGpsRouteProducerLogCount = 0;
|
|
uint32_t gGpsSearchLogCount = 0;
|
|
uint32_t gGpsEdgeCostLogCount = 0;
|
|
uint32_t gGpsAuxMultiplierLogCount = 0;
|
|
uint32_t gGpsNodeMultiplierLogCount = 0;
|
|
uint32_t gGpsCostTablePatchLogCount = 0;
|
|
uint32_t gGpsProviderClassPatchLogCount = 0;
|
|
std::array<uint32_t, 64> gGpsEdgeCostClassLogCounts{};
|
|
std::mutex gGpsCostLogMutex;
|
|
std::mutex gGpsAsyncLogMutex;
|
|
std::mutex gGpsCostTablePatchMutex;
|
|
uint64_t gLogStartTick = 0;
|
|
uint64_t gLogFrequency = 0;
|
|
void* gMappinSetTrackedTarget = nullptr;
|
|
void* gMappinClearTrackedTarget = nullptr;
|
|
void* gMappinUpdateCustomPositionTarget = nullptr;
|
|
void* gMappinFramePathTarget = nullptr;
|
|
void* gMappinPositionQueryTarget = nullptr;
|
|
void* gMappinCreateCustomPositionTarget = nullptr;
|
|
void* gMappinReleaseQueryTarget = nullptr;
|
|
void* gMappinActiveRouteTarget = nullptr;
|
|
void* gObserver1ServiceRouteLookupTarget = nullptr;
|
|
bool gMappinSlotHookAttachAttempted = false;
|
|
bool gObserver1ServiceRouteLookupHookAttachAttempted = false;
|
|
|
|
struct TrackedGpsQuery
|
|
{
|
|
bool active = false;
|
|
uint32_t id = 0;
|
|
void* manager = nullptr;
|
|
uintptr_t submitReturnRva = 0;
|
|
uint32_t submitCall = 0;
|
|
uint32_t resultFetchCalls = 0;
|
|
uint32_t statusCalls = 0;
|
|
};
|
|
|
|
std::mutex gTrackedGpsQueryMutex;
|
|
std::array<TrackedGpsQuery, 32> gTrackedGpsQueries{};
|
|
uint32_t gTrackedGpsQueryNext = 0;
|
|
|
|
struct GpsEdgeCostSample
|
|
{
|
|
uint64_t currentHandle = 0;
|
|
uint64_t neighborHandle = 0;
|
|
uintptr_t returnRva = 0;
|
|
float cost = 0.0f;
|
|
uint8_t sourceClass = 0xFF;
|
|
uint8_t neighborClass = 0xFF;
|
|
};
|
|
|
|
struct GpsEdgeCostStats
|
|
{
|
|
uint32_t total = 0;
|
|
uint32_t invalidSourceClass = 0;
|
|
uint32_t invalidNeighborClass = 0;
|
|
float costSum = 0.0f;
|
|
float minCost = 0.0f;
|
|
float maxCost = 0.0f;
|
|
std::array<uint32_t, 64> sourceClasses{};
|
|
std::array<uint32_t, 64> neighborClasses{};
|
|
std::array<uintptr_t, 8> returnRvas{};
|
|
std::array<uint32_t, 8> returnRvaCounts{};
|
|
uint32_t otherReturnRvas = 0;
|
|
std::array<GpsEdgeCostSample, 8> samples{};
|
|
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);
|
|
using ScriptNativeVoidFunc = void (*)(void* aThis, void* aStack, void* aResult);
|
|
using MappinSystemResolverFunc = void* (*)(void* aThis);
|
|
using FrameMappinPathCoreFunc = void (*)(void* aThis, uint32_t aPathKind, void* a3, void* aPathRect);
|
|
using SetSelectedMappinCoreFunc = void (*)(void* aThis, void* aMappinHandle);
|
|
using TrackCustomPositionMappinCoreFunc = void (*)(void* aThis);
|
|
using TrackMappinCoreFunc = void (*)(void* aThis, void* aMappinHandle);
|
|
using MappinUpdateCustomPositionFunc = void (*)(void* aSystem, void* aHandle, void* aPositionData);
|
|
using MappinSetTrackedFunc = void (*)(void* aSystem, void* aMappinId);
|
|
using MappinClearTrackedFunc = void (*)(void* aSystem);
|
|
using MappinFramePathFunc = void (*)(void* aSystem, uint32_t aPathKind, void* aResult);
|
|
using MappinPositionQueryFunc = void* (*)(void* aSystem, void* a2, void* a3, void* a4, void* a5);
|
|
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);
|
|
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, uint64_t aRouteKey);
|
|
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);
|
|
using RouteObserverCallbackFunc = void (*)(void* aObserver, void* aRouteEntry);
|
|
using Observer1ServiceLookupFunc = void* (*)(void* aServiceOwner, void* aSystemType);
|
|
using Observer1ServiceRouteLookupFunc = void (*)(void* aService, void* aOut, uint32_t aRouteId);
|
|
using RunGpsQueryBodyFunc = bool (*)(void* aThis, void* aStart, void* aEnd, int32_t* aOutQueryId);
|
|
using UpdateGpsQueryBodyFunc = bool (*)(void* aThis, uint32_t aQueryId, void* aOutPoints, void* aOutError);
|
|
using GpsQuerySubmitFunc = int32_t (*)(void* aManager, void* aQuery);
|
|
using GpsQueryDispatchFunc = void (*)(void* aRuntimeQuery, void* aTargetPosition, void* aArg3, void* aArg4);
|
|
using GpsQueryResultFetchFunc = bool (*)(void* aManager, uint32_t aQueryId, void* aOutPath);
|
|
using GpsQueryStatusFunc = int32_t (*)(void* aManager, uint32_t aQueryId);
|
|
using GpsDispatchAdvanceFunc = void (*)(void* aRouteSystem, void* aRuntimeQuery, void* aOutTicket,
|
|
void* aTargetPosition, void* aSourcePosition);
|
|
using GpsAsyncWorkerTickFunc = void (*)(void* aRouteSystem);
|
|
using GpsRouteJobStartFunc = bool (*)(void* aRouteSystem, void* aActiveEntry, float aDelta, void* aOutLocal);
|
|
using GpsRouteJobPollFunc = bool (*)(void* aRouteSystem, void* aActiveEntry);
|
|
using GpsRouteFinishFunc = void* (*)(void* aResultQueue, void* aOutEntry, void* aActiveEntry, void* aLocalResult);
|
|
using GpsRouteJobBuildFunc = void* (*)(void* aJob, void* aOut, void* aRequest);
|
|
using GpsRouteProducerFunc = int32_t (*)(void* aGraph, void* aQuery, void* aOutResult);
|
|
using GpsSearchFunc = int32_t (*)(void* aGraph, uint64_t aStartHandle, uint64_t aTargetHandle, void* aStartPoint,
|
|
void* aTargetPoint, void* aCostProvider, void* aOutPath, uint32_t* aOutCount);
|
|
using GpsEdgeCostFunc = float (*)(void* aCostProvider, void* aCurrentState, void* aNeighborState,
|
|
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;
|
|
FrameMappinPathCoreFunc gOriginalFrameMappinPathCore = nullptr;
|
|
ScriptNativeVoidFunc gOriginalSetSelectedMappinWrapper = nullptr;
|
|
ScriptNativeVoidFunc gOriginalSetSelectedMappinByIdWrapper = nullptr;
|
|
ScriptNativeVoidFunc gOriginalSetSelectedMappinByPositionWrapper = nullptr;
|
|
SetSelectedMappinCoreFunc gOriginalSetSelectedMappinCore = nullptr;
|
|
ScriptNativeVoidFunc gOriginalTrackCustomPositionMappinWrapper = nullptr;
|
|
TrackCustomPositionMappinCoreFunc gOriginalTrackCustomPositionMappinCore = nullptr;
|
|
ScriptNativeVoidFunc gOriginalUntrackCustomPositionMappinWrapper = nullptr;
|
|
ScriptNativeVoidFunc gOriginalTrackMappinWrapper = nullptr;
|
|
TrackMappinCoreFunc gOriginalTrackMappinCore = nullptr;
|
|
ScriptNativeVoidFunc gOriginalSetMappinTrackingAlternativeWrapper = nullptr;
|
|
MappinUpdateCustomPositionFunc gOriginalMappinUpdateCustomPosition = nullptr;
|
|
MappinSetTrackedFunc gOriginalMappinSetTracked = nullptr;
|
|
MappinClearTrackedFunc gOriginalMappinClearTracked = nullptr;
|
|
MappinFramePathFunc gOriginalMappinFramePath = nullptr;
|
|
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;
|
|
JournalRouteBridgeFunc gOriginalJournalRouteBridge = nullptr;
|
|
MappinRouteEventEnqueueFunc gOriginalMappinRouteEventEnqueue = nullptr;
|
|
MappinRouteEventHandlerFunc gOriginalMappinRouteEventHandler = nullptr;
|
|
MappinRouteToggleFunc gOriginalMappinRouteActivate = nullptr;
|
|
MappinRouteToggleFunc gOriginalMappinRouteDeactivate = nullptr;
|
|
RouteBuildCandidateFunc gOriginalRouteBuildCandidate = nullptr;
|
|
RouteObserverCallbackFunc gOriginalRouteObserver0Activate = nullptr;
|
|
RouteObserverCallbackFunc gOriginalRouteObserver1Activate = nullptr;
|
|
RouteObserverCallbackFunc gOriginalRouteObserver23Activate = nullptr;
|
|
RouteObserverCallbackFunc gOriginalRouteObserver0Deactivate = nullptr;
|
|
RouteObserverCallbackFunc gOriginalRouteObserver1Deactivate = nullptr;
|
|
RouteObserverCallbackFunc gOriginalRouteObserver23Deactivate = nullptr;
|
|
Observer1ServiceLookupFunc gOriginalObserver1ServiceLookup = nullptr;
|
|
Observer1ServiceRouteLookupFunc gOriginalObserver1ServiceRouteLookup = nullptr;
|
|
RunGpsQueryBodyFunc gOriginalRunGpsQueryBody = nullptr;
|
|
UpdateGpsQueryBodyFunc gOriginalUpdateGpsQueryBody = nullptr;
|
|
GpsQuerySubmitFunc gOriginalGpsQuerySubmit = nullptr;
|
|
GpsQueryDispatchFunc gOriginalGpsQueryDispatch = nullptr;
|
|
GpsQueryResultFetchFunc gOriginalGpsQueryResultFetch = nullptr;
|
|
GpsQueryStatusFunc gOriginalGpsQueryStatus = nullptr;
|
|
GpsDispatchAdvanceFunc gOriginalGpsDispatchAdvance = nullptr;
|
|
GpsAsyncWorkerTickFunc gOriginalGpsAsyncWorkerTick = nullptr;
|
|
GpsRouteJobStartFunc gOriginalGpsRouteJobStart = nullptr;
|
|
GpsRouteJobPollFunc gOriginalGpsRouteJobPoll = nullptr;
|
|
GpsRouteFinishFunc gOriginalGpsRouteFinish = nullptr;
|
|
GpsRouteJobBuildFunc gOriginalGpsRouteJobBuild = nullptr;
|
|
GpsRouteProducerFunc gOriginalGpsRouteProducer = nullptr;
|
|
GpsSearchFunc gOriginalGpsSearch = nullptr;
|
|
GpsEdgeCostFunc gOriginalGpsEdgeCost = nullptr;
|
|
GpsMultiplierFunc gOriginalGpsAuxMultiplier = nullptr;
|
|
GpsMultiplierFunc gOriginalGpsNodeMultiplier = nullptr;
|
|
|
|
std::filesystem::path GetLogPath()
|
|
{
|
|
wchar_t modulePath[MAX_PATH]{};
|
|
if (gModule && GetModuleFileNameW(gModule, modulePath, MAX_PATH) > 0)
|
|
{
|
|
auto path = std::filesystem::path(modulePath);
|
|
return path.parent_path() / L"EdgeWeightGPS.log";
|
|
}
|
|
|
|
return std::filesystem::path(L"red4ext") / L"plugins" / L"EdgeWeightGPS" / L"EdgeWeightGPS.log";
|
|
}
|
|
|
|
void Log(std::string_view aMessage)
|
|
{
|
|
std::lock_guard lock(gLogMutex);
|
|
|
|
auto now = std::chrono::system_clock::now();
|
|
auto time = std::chrono::system_clock::to_time_t(now);
|
|
std::tm localTime{};
|
|
localtime_s(&localTime, &time);
|
|
|
|
std::ofstream out(GetLogPath(), std::ios::app);
|
|
if (!out)
|
|
{
|
|
return;
|
|
}
|
|
|
|
uint64_t elapsedMs = 0;
|
|
if (gLogStartTick && gLogFrequency)
|
|
{
|
|
LARGE_INTEGER counter{};
|
|
QueryPerformanceCounter(&counter);
|
|
elapsedMs = static_cast<uint64_t>((counter.QuadPart - gLogStartTick) * 1000 / gLogFrequency);
|
|
}
|
|
|
|
out << std::put_time(&localTime, "%Y-%m-%d %H:%M:%S") << "." << std::setw(3) << std::setfill('0')
|
|
<< elapsedMs % 1000 << std::setfill(' ') << " +" << elapsedMs << "ms " << aMessage << "\n";
|
|
}
|
|
|
|
void LogRed4ext(std::string_view aMessage)
|
|
{
|
|
Log(aMessage);
|
|
|
|
if (gSdk && gSdk->logger && gSdk->logger->Info && gHandle)
|
|
{
|
|
std::string text(aMessage);
|
|
gSdk->logger->Info(gHandle, text.c_str());
|
|
}
|
|
}
|
|
|
|
uintptr_t GetImageBase()
|
|
{
|
|
return reinterpret_cast<uintptr_t>(GetModuleHandleW(nullptr));
|
|
}
|
|
|
|
uintptr_t GetImageEnd()
|
|
{
|
|
const auto imageBase = GetImageBase();
|
|
if (!imageBase)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
const auto* dos = reinterpret_cast<const IMAGE_DOS_HEADER*>(imageBase);
|
|
if (dos->e_magic != IMAGE_DOS_SIGNATURE)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
const auto* nt = reinterpret_cast<const IMAGE_NT_HEADERS*>(imageBase + dos->e_lfanew);
|
|
if (nt->Signature != IMAGE_NT_SIGNATURE)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return imageBase + nt->OptionalHeader.SizeOfImage;
|
|
}
|
|
|
|
bool IsInImage(const void* aPointer)
|
|
{
|
|
const auto imageBase = GetImageBase();
|
|
const auto imageEnd = GetImageEnd();
|
|
const auto address = reinterpret_cast<uintptr_t>(aPointer);
|
|
return imageBase && imageEnd && address >= imageBase && address < imageEnd;
|
|
}
|
|
|
|
bool TryGetImageRva(const void* aPointer, uintptr_t& aRva)
|
|
{
|
|
if (!IsInImage(aPointer))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
aRva = reinterpret_cast<uintptr_t>(aPointer) - GetImageBase();
|
|
return true;
|
|
}
|
|
|
|
void AppendPointerWithRva(std::ostringstream& aLine, const char* aLabel, const void* aPointer)
|
|
{
|
|
aLine << " " << aLabel << "=" << aPointer;
|
|
|
|
if (IsInImage(aPointer))
|
|
{
|
|
const auto imageBase = GetImageBase();
|
|
const auto address = reinterpret_cast<uintptr_t>(aPointer);
|
|
aLine << " " << aLabel << "_rva=0x" << std::hex << (address - imageBase) << std::dec;
|
|
}
|
|
}
|
|
|
|
void AppendReturnRva(std::ostringstream& aLine, const void* aReturnAddress)
|
|
{
|
|
if (IsInImage(aReturnAddress))
|
|
{
|
|
const auto imageBase = GetImageBase();
|
|
const auto address = reinterpret_cast<uintptr_t>(aReturnAddress);
|
|
aLine << " ret_rva=0x" << std::hex << (address - imageBase) << std::dec;
|
|
}
|
|
}
|
|
|
|
void AppendHexValue(std::ostringstream& aLine, const char* aLabel, uint64_t aValue)
|
|
{
|
|
aLine << " " << aLabel << "=0x" << std::hex << aValue << std::dec;
|
|
}
|
|
|
|
float FloatFromBits(uint32_t aBits)
|
|
{
|
|
float value = 0.0f;
|
|
std::memcpy(&value, &aBits, sizeof(value));
|
|
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;
|
|
line << "hook " << aName << " call=" << aCount << " this=" << aThis << " a2=" << a2 << " a3=" << a3
|
|
<< " a4=" << a4;
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
void LogScriptNativeCall(const char* aName, uint32_t aCount, void* aThis, void* aStack, void* aResult)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook " << aName << " call=" << aCount << " this=" << aThis << " stack=" << aStack
|
|
<< " result=" << aResult;
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
void LogFrameMappinPathCoreCall(uint32_t aCount, void* aThis, uint32_t aPathKind, void* a3, void* aPathRect)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook FrameMappinPathCore call=" << aCount << " this=" << aThis << " pathKind=" << aPathKind
|
|
<< " a3=" << a3 << " pathRect=" << aPathRect;
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
void LogSetSelectedMappinCoreCall(uint32_t aCount, void* aThis, void* aMappinHandle)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook SetSelectedMappinCore call=" << aCount << " this=" << aThis
|
|
<< " mappinHandle=" << aMappinHandle;
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
void LogTrackCustomPositionMappinCoreCall(uint32_t aCount, void* aThis)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook TrackCustomPositionMappinCore call=" << aCount << " this=" << aThis;
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
void LogTrackMappinCoreCall(uint32_t aCount, void* aThis, void* aMappinHandle)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook TrackMappinCore call=" << aCount << " this=" << aThis << " mappinHandle=" << aMappinHandle;
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
void LogMappinSystemSlots(uint32_t aCount, void* aSystem, void* aVtable)
|
|
{
|
|
auto* vtable = reinterpret_cast<uint8_t*>(aVtable);
|
|
auto readSlot = [vtable](uintptr_t aOffset) -> void* {
|
|
if (!vtable)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
return *reinterpret_cast<void**>(vtable + aOffset);
|
|
};
|
|
|
|
std::ostringstream line;
|
|
line << "mappin-system slots call=" << aCount;
|
|
AppendPointerWithRva(line, "system", aSystem);
|
|
AppendPointerWithRva(line, "vtable", aVtable);
|
|
AppendPointerWithRva(line, "slot1f0", readSlot(kMappinUpdateCustomPositionSlotOffset));
|
|
AppendPointerWithRva(line, "slot220", readSlot(kMappinSetTrackedSlotOffset));
|
|
AppendPointerWithRva(line, "slot228", readSlot(kMappinClearTrackedSlotOffset));
|
|
AppendPointerWithRva(line, "slot250", readSlot(0x250));
|
|
AppendPointerWithRva(line, "slot280", readSlot(kMappinFramePathSlotOffset));
|
|
AppendPointerWithRva(line, "slot2e0", readSlot(kMappinPositionQuerySlotOffset));
|
|
AppendPointerWithRva(line, "slot2f0", readSlot(kMappinCreateCustomPositionSlotOffset));
|
|
AppendPointerWithRva(line, "slot368", readSlot(kMappinReleaseQuerySlotOffset));
|
|
AppendPointerWithRva(line, "slot3a8", readSlot(kMappinActiveRouteSlotOffset));
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
void InspectAndHookMappinSystem(void* aThis);
|
|
|
|
const char* ReasonToString(RED4ext::v1::EMainReason aReason)
|
|
{
|
|
switch (aReason)
|
|
{
|
|
case RED4ext::v1::EMainReason::Load:
|
|
return "Load";
|
|
case RED4ext::v1::EMainReason::Unload:
|
|
return "Unload";
|
|
}
|
|
|
|
return "Unknown";
|
|
}
|
|
|
|
bool IsReadablePage(DWORD aProtect)
|
|
{
|
|
if ((aProtect & PAGE_GUARD) != 0 || (aProtect & PAGE_NOACCESS) != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const auto baseProtect = aProtect & 0xFF;
|
|
return baseProtect == PAGE_READONLY || baseProtect == PAGE_READWRITE || baseProtect == PAGE_WRITECOPY ||
|
|
baseProtect == PAGE_EXECUTE_READ || baseProtect == PAGE_EXECUTE_READWRITE ||
|
|
baseProtect == PAGE_EXECUTE_WRITECOPY;
|
|
}
|
|
|
|
bool IsReadableMemory(const void* aPointer, size_t aSize)
|
|
{
|
|
if (!aPointer || aSize == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const auto begin = reinterpret_cast<uintptr_t>(aPointer);
|
|
const auto end = begin + aSize;
|
|
if (end < begin)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
auto cursor = begin;
|
|
while (cursor < end)
|
|
{
|
|
MEMORY_BASIC_INFORMATION mbi{};
|
|
if (!VirtualQuery(reinterpret_cast<const void*>(cursor), &mbi, sizeof(mbi)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (mbi.State != MEM_COMMIT || !IsReadablePage(mbi.Protect))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const auto regionEnd = reinterpret_cast<uintptr_t>(mbi.BaseAddress) + mbi.RegionSize;
|
|
if (regionEnd <= cursor)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
cursor = regionEnd;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template<typename T>
|
|
bool TryReadMemory(const void* aPointer, T& aValue)
|
|
{
|
|
if (!IsReadableMemory(aPointer, sizeof(T)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
aValue = *reinterpret_cast<const T*>(aPointer);
|
|
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* GetObserver1SystemType()
|
|
{
|
|
return ReadPointerField(reinterpret_cast<void*>(GetImageBase() + kObserver1SystemTypeGlobalRva), 0x00);
|
|
}
|
|
|
|
void AppendObjectVtableSlots(std::ostringstream& aLine, void* aObject, std::initializer_list<uintptr_t> aSlots)
|
|
{
|
|
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;
|
|
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 AppendUint16Field(std::ostringstream& aLine, const char* aLabel, void* aObject, uintptr_t aOffset)
|
|
{
|
|
uint16_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 AppendUint64Field(std::ostringstream& aLine, const char* aLabel, void* aObject, uintptr_t aOffset)
|
|
{
|
|
uint64_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 AppendBytesField(std::ostringstream& aLine, const char* aLabel, void* aObject, uintptr_t aOffset, size_t aSize)
|
|
{
|
|
const auto* address = aObject ? reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aObject) + aOffset) : nullptr;
|
|
aLine << " " << aLabel << "=";
|
|
if (!address || !IsReadableMemory(address, aSize))
|
|
{
|
|
aLine << "<unreadable>";
|
|
return;
|
|
}
|
|
|
|
const auto flags = aLine.flags();
|
|
const auto fill = aLine.fill();
|
|
aLine << std::hex << std::setfill('0');
|
|
for (size_t index = 0; index < aSize; ++index)
|
|
{
|
|
const auto byte = *(reinterpret_cast<const uint8_t*>(address) + index);
|
|
aLine << std::setw(2) << static_cast<uint32_t>(byte);
|
|
}
|
|
aLine.flags(flags);
|
|
aLine.fill(fill);
|
|
}
|
|
|
|
void AppendBytesPointer(std::ostringstream& aLine, const char* aLabel, const void* aPointer, size_t aSize)
|
|
{
|
|
aLine << " " << aLabel << "=";
|
|
if (!aPointer || reinterpret_cast<uintptr_t>(aPointer) < 0x10000 || !IsReadableMemory(aPointer, aSize))
|
|
{
|
|
aLine << "<unreadable>";
|
|
return;
|
|
}
|
|
|
|
const auto flags = aLine.flags();
|
|
const auto fill = aLine.fill();
|
|
aLine << std::hex << std::setfill('0');
|
|
for (size_t index = 0; index < aSize; ++index)
|
|
{
|
|
const auto byte = *(reinterpret_cast<const uint8_t*>(aPointer) + index);
|
|
aLine << std::setw(2) << static_cast<uint32_t>(byte);
|
|
}
|
|
aLine.flags(flags);
|
|
aLine.fill(fill);
|
|
}
|
|
|
|
struct Vector4Value
|
|
{
|
|
float x;
|
|
float y;
|
|
float z;
|
|
float w;
|
|
};
|
|
|
|
struct Vector3Value
|
|
{
|
|
float x;
|
|
float y;
|
|
float z;
|
|
};
|
|
|
|
void AppendVector3Pointer(std::ostringstream& aLine, const char* aLabel, const void* aVector)
|
|
{
|
|
Vector3Value value{};
|
|
aLine << " " << aLabel << "=";
|
|
if (aVector && TryReadMemory(aVector, value))
|
|
{
|
|
aLine << "(" << value.x << "," << value.y << "," << value.z << ")";
|
|
}
|
|
else
|
|
{
|
|
aLine << "<unreadable>";
|
|
}
|
|
}
|
|
|
|
void AppendVector4Pointer(std::ostringstream& aLine, const char* aLabel, const void* aVector)
|
|
{
|
|
Vector4Value value{};
|
|
aLine << " " << aLabel << "=";
|
|
if (aVector && TryReadMemory(aVector, value))
|
|
{
|
|
aLine << "(" << value.x << "," << value.y << "," << value.z << "," << value.w << ")";
|
|
}
|
|
else
|
|
{
|
|
aLine << "<unreadable>";
|
|
}
|
|
}
|
|
|
|
void AppendVector4Field(std::ostringstream& aLine, const char* aLabel, void* aObject, uintptr_t aOffset)
|
|
{
|
|
if (!aObject)
|
|
{
|
|
aLine << " " << aLabel << "=<null>";
|
|
return;
|
|
}
|
|
|
|
AppendVector4Pointer(aLine, aLabel, reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aObject) + aOffset));
|
|
}
|
|
|
|
bool TryReadFloatField(void* aObject, uintptr_t aOffset, float& aValue)
|
|
{
|
|
return aObject && TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aObject) + aOffset),
|
|
aValue);
|
|
}
|
|
|
|
void AppendFloatField(std::ostringstream& aLine, const char* aLabel, void* aObject, uintptr_t aOffset)
|
|
{
|
|
float value = 0.0f;
|
|
const auto hasValue = TryReadFloatField(aObject, aOffset, value);
|
|
aLine << " " << aLabel << "=";
|
|
if (hasValue)
|
|
{
|
|
const auto flags = aLine.flags();
|
|
const auto precision = aLine.precision();
|
|
aLine << std::fixed << std::setprecision(3) << value;
|
|
aLine.flags(flags);
|
|
aLine.precision(precision);
|
|
}
|
|
else
|
|
{
|
|
aLine << "<unreadable>";
|
|
}
|
|
}
|
|
|
|
uint8_t ReadGpsPointClass(void* aPoint)
|
|
{
|
|
uint8_t value = 0xFF;
|
|
if (!aPoint || !TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aPoint) + 0x13), value))
|
|
{
|
|
return 0xFF;
|
|
}
|
|
|
|
return value & 0x3F;
|
|
}
|
|
|
|
uint16_t ReadGpsPointMask(void* aPoint)
|
|
{
|
|
uint16_t value = 0;
|
|
if (!aPoint)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aPoint) + 0x10), value);
|
|
return value;
|
|
}
|
|
|
|
void AppendGpsPointSummary(std::ostringstream& aLine, const char* aPrefix, void* aPoint)
|
|
{
|
|
const auto prefix = std::string(aPrefix);
|
|
AppendPointerWithRva(aLine, prefix.c_str(), aPoint);
|
|
if (!aPoint)
|
|
{
|
|
return;
|
|
}
|
|
|
|
uint32_t adjacency = 0;
|
|
uint16_t mask = 0;
|
|
uint8_t byte12 = 0;
|
|
uint8_t byte13 = 0;
|
|
TryReadMemory(aPoint, adjacency);
|
|
TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aPoint) + 0x10), mask);
|
|
TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aPoint) + 0x12), byte12);
|
|
TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aPoint) + 0x13), byte13);
|
|
|
|
aLine << " " << prefix << "_adj=" << adjacency;
|
|
aLine << " " << prefix << "_mask10=0x" << std::hex << mask << std::dec;
|
|
aLine << " " << prefix << "_b12=0x" << std::hex << static_cast<uint32_t>(byte12);
|
|
aLine << " " << prefix << "_class13=" << std::dec << static_cast<uint32_t>(byte13 & 0x3F);
|
|
aLine << " " << prefix << "_raw13=0x" << std::hex << static_cast<uint32_t>(byte13) << std::dec;
|
|
}
|
|
|
|
void AppendGpsCostProviderSummary(std::ostringstream& aLine, const char* aPrefix, void* aProvider)
|
|
{
|
|
const auto prefix = std::string(aPrefix);
|
|
AppendPointerWithRva(aLine, prefix.c_str(), aProvider);
|
|
if (!aProvider)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto* vtable = ReadPointerField(aProvider, 0x00);
|
|
AppendPointerWithRva(aLine, (prefix + "_vtable").c_str(), vtable);
|
|
if (IsInImage(vtable))
|
|
{
|
|
AppendPointerWithRva(aLine, (prefix + "_slot08").c_str(), ReadPointerField(vtable, 0x08));
|
|
AppendPointerWithRva(aLine, (prefix + "_slot10").c_str(), ReadPointerField(vtable, 0x10));
|
|
}
|
|
|
|
AppendUint16Field(aLine, (prefix + "_mask108").c_str(), aProvider, 0x108);
|
|
AppendUint16Field(aLine, (prefix + "_mask10a").c_str(), aProvider, 0x10A);
|
|
|
|
const auto flags = aLine.flags();
|
|
const auto precision = aLine.precision();
|
|
aLine << " " << prefix << "_mul=[";
|
|
for (uint32_t index = 0; index < 64; ++index)
|
|
{
|
|
float value = 0.0f;
|
|
const auto hasValue = TryReadFloatField(aProvider, 0x08 + static_cast<uintptr_t>(index) * sizeof(float), value);
|
|
if (index > 0)
|
|
{
|
|
aLine << ",";
|
|
}
|
|
|
|
aLine << index << ":";
|
|
if (hasValue)
|
|
{
|
|
aLine << std::fixed << std::setprecision(3) << value;
|
|
}
|
|
else
|
|
{
|
|
aLine << "?";
|
|
}
|
|
}
|
|
aLine << "]";
|
|
aLine.flags(flags);
|
|
aLine.precision(precision);
|
|
}
|
|
|
|
void AppendGpsCostProviderCompact(std::ostringstream& aLine, const char* aPrefix, void* aProvider)
|
|
{
|
|
const auto prefix = std::string(aPrefix);
|
|
AppendPointerWithRva(aLine, prefix.c_str(), aProvider);
|
|
if (!aProvider)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto* vtable = ReadPointerField(aProvider, 0x00);
|
|
AppendPointerWithRva(aLine, (prefix + "_vtable").c_str(), vtable);
|
|
if (IsInImage(vtable))
|
|
{
|
|
AppendPointerWithRva(aLine, (prefix + "_slot08").c_str(), ReadPointerField(vtable, 0x08));
|
|
AppendPointerWithRva(aLine, (prefix + "_slot10").c_str(), ReadPointerField(vtable, 0x10));
|
|
}
|
|
|
|
AppendUint16Field(aLine, (prefix + "_mask108").c_str(), aProvider, 0x108);
|
|
AppendUint16Field(aLine, (prefix + "_mask10a").c_str(), aProvider, 0x10A);
|
|
|
|
const auto flags = aLine.flags();
|
|
const auto precision = aLine.precision();
|
|
aLine << " " << prefix << "_nonunitMul=[";
|
|
bool first = true;
|
|
for (uint32_t index = 0; index < 64; ++index)
|
|
{
|
|
float value = 0.0f;
|
|
if (!TryReadFloatField(aProvider, 0x08 + static_cast<uintptr_t>(index) * sizeof(float), value))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (value > 0.999f && value < 1.001f)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!first)
|
|
{
|
|
aLine << ",";
|
|
}
|
|
first = false;
|
|
aLine << index << ":" << std::fixed << std::setprecision(3) << value;
|
|
}
|
|
if (first)
|
|
{
|
|
aLine << "none";
|
|
}
|
|
aLine << "]";
|
|
aLine.flags(flags);
|
|
aLine.precision(precision);
|
|
}
|
|
|
|
void AppendClassCounts(std::ostringstream& aLine, const char* aLabel, const std::array<uint32_t, 64>& aCounts,
|
|
uint32_t aInvalidCount)
|
|
{
|
|
aLine << " " << aLabel << "=[";
|
|
bool first = true;
|
|
for (uint32_t index = 0; index < aCounts.size(); ++index)
|
|
{
|
|
if (aCounts[index] == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!first)
|
|
{
|
|
aLine << ",";
|
|
}
|
|
first = false;
|
|
aLine << index << ":" << aCounts[index];
|
|
}
|
|
|
|
if (aInvalidCount > 0)
|
|
{
|
|
if (!first)
|
|
{
|
|
aLine << ",";
|
|
}
|
|
first = false;
|
|
aLine << "invalid:" << aInvalidCount;
|
|
}
|
|
|
|
if (first)
|
|
{
|
|
aLine << "none";
|
|
}
|
|
|
|
aLine << "]";
|
|
}
|
|
|
|
void AppendGpsEdgeCostStats(std::ostringstream& aLine, const GpsEdgeCostStats& aStats)
|
|
{
|
|
aLine << " edgeCostCalls=" << aStats.total;
|
|
if (aStats.total > 0)
|
|
{
|
|
const auto flags = aLine.flags();
|
|
const auto precision = aLine.precision();
|
|
aLine << " edgeCostMin=" << std::fixed << std::setprecision(3) << aStats.minCost;
|
|
aLine << " edgeCostMax=" << std::fixed << std::setprecision(3) << aStats.maxCost;
|
|
aLine << " edgeCostAvg=" << std::fixed << std::setprecision(3)
|
|
<< (aStats.costSum / static_cast<float>(aStats.total));
|
|
aLine.flags(flags);
|
|
aLine.precision(precision);
|
|
}
|
|
|
|
AppendClassCounts(aLine, "edgeSrcClasses", aStats.sourceClasses, aStats.invalidSourceClass);
|
|
AppendClassCounts(aLine, "edgeDstClasses", aStats.neighborClasses, aStats.invalidNeighborClass);
|
|
|
|
aLine << " edgeRetRvas=[";
|
|
bool first = true;
|
|
for (uint32_t index = 0; index < aStats.returnRvas.size(); ++index)
|
|
{
|
|
if (aStats.returnRvas[index] == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!first)
|
|
{
|
|
aLine << ",";
|
|
}
|
|
first = false;
|
|
aLine << "0x" << std::hex << aStats.returnRvas[index] << std::dec << ":" << aStats.returnRvaCounts[index];
|
|
}
|
|
if (aStats.otherReturnRvas > 0)
|
|
{
|
|
if (!first)
|
|
{
|
|
aLine << ",";
|
|
}
|
|
first = false;
|
|
aLine << "other:" << aStats.otherReturnRvas;
|
|
}
|
|
if (first)
|
|
{
|
|
aLine << "none";
|
|
}
|
|
aLine << "]";
|
|
|
|
aLine << " edgeSamples=[";
|
|
const auto sampleCount = std::min<uint32_t>(aStats.sampleCount, static_cast<uint32_t>(aStats.samples.size()));
|
|
for (uint32_t index = 0; index < sampleCount; ++index)
|
|
{
|
|
const auto& sample = aStats.samples[index];
|
|
if (index > 0)
|
|
{
|
|
aLine << ";";
|
|
}
|
|
|
|
const auto flags = aLine.flags();
|
|
const auto precision = aLine.precision();
|
|
aLine << index << ":" << static_cast<uint32_t>(sample.sourceClass) << ">"
|
|
<< static_cast<uint32_t>(sample.neighborClass) << " cost=" << std::fixed << std::setprecision(3)
|
|
<< sample.cost << " ret=0x" << std::hex << sample.returnRva << " cur=0x" << sample.currentHandle
|
|
<< " dst=0x" << sample.neighborHandle << std::dec;
|
|
aLine.flags(flags);
|
|
aLine.precision(precision);
|
|
}
|
|
if (sampleCount == 0)
|
|
{
|
|
aLine << "none";
|
|
}
|
|
aLine << "]";
|
|
}
|
|
|
|
void NoteGpsEdgeCostStats(GpsEdgeCostStats& aStats, uint8_t aSourceClass, uint8_t aNeighborClass, float aCost,
|
|
uintptr_t aReturnRva, uint64_t aCurrentHandle, uint64_t aNeighborHandle)
|
|
{
|
|
++aStats.total;
|
|
aStats.costSum += aCost;
|
|
if (aStats.total == 1)
|
|
{
|
|
aStats.minCost = aCost;
|
|
aStats.maxCost = aCost;
|
|
}
|
|
else
|
|
{
|
|
aStats.minCost = std::min(aStats.minCost, aCost);
|
|
aStats.maxCost = std::max(aStats.maxCost, aCost);
|
|
}
|
|
|
|
if (aSourceClass < aStats.sourceClasses.size())
|
|
{
|
|
++aStats.sourceClasses[aSourceClass];
|
|
}
|
|
else
|
|
{
|
|
++aStats.invalidSourceClass;
|
|
}
|
|
|
|
if (aNeighborClass < aStats.neighborClasses.size())
|
|
{
|
|
++aStats.neighborClasses[aNeighborClass];
|
|
}
|
|
else
|
|
{
|
|
++aStats.invalidNeighborClass;
|
|
}
|
|
|
|
if (aReturnRva != 0)
|
|
{
|
|
bool recorded = false;
|
|
for (uint32_t index = 0; index < aStats.returnRvas.size(); ++index)
|
|
{
|
|
if (aStats.returnRvas[index] == aReturnRva)
|
|
{
|
|
++aStats.returnRvaCounts[index];
|
|
recorded = true;
|
|
break;
|
|
}
|
|
|
|
if (aStats.returnRvas[index] == 0)
|
|
{
|
|
aStats.returnRvas[index] = aReturnRva;
|
|
aStats.returnRvaCounts[index] = 1;
|
|
recorded = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!recorded)
|
|
{
|
|
++aStats.otherReturnRvas;
|
|
}
|
|
}
|
|
|
|
if (aStats.sampleCount < aStats.samples.size())
|
|
{
|
|
auto& sample = aStats.samples[aStats.sampleCount++];
|
|
sample.currentHandle = aCurrentHandle;
|
|
sample.neighborHandle = aNeighborHandle;
|
|
sample.returnRva = aReturnRva;
|
|
sample.cost = aCost;
|
|
sample.sourceClass = aSourceClass;
|
|
sample.neighborClass = aNeighborClass;
|
|
}
|
|
}
|
|
|
|
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);
|
|
AppendVector4Field(aLine, "query_end10", aQuery, 0x10);
|
|
AppendFloatField(aLine, "query_f68", aQuery, 0x68);
|
|
AppendFloatField(aLine, "query_f78", aQuery, 0x78);
|
|
AppendFloatField(aLine, "query_f88", aQuery, 0x88);
|
|
AppendUint32Field(aLine, "query_f8c", aQuery, 0x8C);
|
|
AppendUint32Field(aLine, "query_f90", aQuery, 0x90);
|
|
AppendUint32Field(aLine, "query_f94", aQuery, 0x94);
|
|
AppendPointerField(aLine, "query_ptr98", aQuery, 0x98);
|
|
AppendPointerField(aLine, "query_ptra0", aQuery, 0xA0);
|
|
AppendUint32Field(aLine, "query_flagsa8", aQuery, 0xA8);
|
|
}
|
|
|
|
void AppendVector3ListCandidate(std::ostringstream& aLine, const char* aPrefix, void* aObject, uintptr_t aDataOffset,
|
|
uintptr_t aCountOffset, uintptr_t aStride)
|
|
{
|
|
uint32_t count = 0;
|
|
auto* data = ReadPointerField(aObject, aDataOffset);
|
|
TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aObject) + aCountOffset), count);
|
|
|
|
const auto dataLabel = std::string(aPrefix) + "_data";
|
|
const auto countLabel = std::string(aPrefix) + "_count";
|
|
AppendPointerWithRva(aLine, dataLabel.c_str(), data);
|
|
aLine << " " << countLabel << "=" << count;
|
|
|
|
if (!data || reinterpret_cast<uintptr_t>(data) < 0x10000 || count == 0 || count > 10000)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AppendVector3Pointer(aLine, (std::string(aPrefix) + "_first3").c_str(), data);
|
|
if (count > 1)
|
|
{
|
|
AppendVector3Pointer(aLine, (std::string(aPrefix) + "_last3").c_str(),
|
|
reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(data) +
|
|
static_cast<uintptr_t>(count - 1) * aStride));
|
|
}
|
|
}
|
|
|
|
void AppendVectorListCandidate(std::ostringstream& aLine, const char* aPrefix, void* aObject, uintptr_t aDataOffset,
|
|
uintptr_t aCountOffset, uintptr_t aStride)
|
|
{
|
|
uint32_t count = 0;
|
|
auto* data = ReadPointerField(aObject, aDataOffset);
|
|
TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aObject) + aCountOffset), count);
|
|
|
|
const auto dataLabel = std::string(aPrefix) + "_data";
|
|
const auto countLabel = std::string(aPrefix) + "_count";
|
|
AppendPointerWithRva(aLine, dataLabel.c_str(), data);
|
|
aLine << " " << countLabel << "=" << count;
|
|
|
|
if (!data || reinterpret_cast<uintptr_t>(data) < 0x10000 || count == 0 || count > 10000)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AppendVector4Pointer(aLine, (std::string(aPrefix) + "_first").c_str(), data);
|
|
if (count > 1)
|
|
{
|
|
AppendVector4Pointer(aLine, (std::string(aPrefix) + "_last").c_str(),
|
|
reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(data) +
|
|
static_cast<uintptr_t>(count - 1) * aStride));
|
|
}
|
|
}
|
|
|
|
void AppendPathBufferSummary(std::ostringstream& aLine, const char* aPrefix, void* aPathBuffer)
|
|
{
|
|
AppendPointerWithRva(aLine, aPrefix, aPathBuffer);
|
|
if (!aPathBuffer)
|
|
{
|
|
return;
|
|
}
|
|
|
|
uint32_t count = 0;
|
|
auto* data = ReadPointerField(aPathBuffer, 0x30);
|
|
TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aPathBuffer) + 0x3C), count);
|
|
|
|
aLine << " " << aPrefix << "_count=" << count;
|
|
AppendPointerWithRva(aLine, (std::string(aPrefix) + "_data").c_str(), data);
|
|
|
|
if (!data || count == 0 || count > 100000)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AppendVector4Pointer(aLine, (std::string(aPrefix) + "_first").c_str(), data);
|
|
if (count > 1)
|
|
{
|
|
AppendVector4Pointer(aLine, (std::string(aPrefix) + "_last").c_str(),
|
|
reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(data) +
|
|
static_cast<uintptr_t>(count - 1) * 12));
|
|
}
|
|
}
|
|
|
|
void AppendGpsResultElementArraySummary(std::ostringstream& aLine, const char* aPrefix, void* aResultObject)
|
|
{
|
|
uint32_t capacity = 0;
|
|
uint32_t count = 0;
|
|
auto* data = ReadPointerField(aResultObject, 0x28);
|
|
TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aResultObject) + 0x30), capacity);
|
|
TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aResultObject) + 0x34), count);
|
|
|
|
const auto prefix = std::string(aPrefix);
|
|
aLine << " " << prefix << "_ptr28_capacity30=" << capacity;
|
|
aLine << " " << prefix << "_ptr28_count34=" << count;
|
|
|
|
if (!data || reinterpret_cast<uintptr_t>(data) < 0x10000 || count == 0 || count > 256)
|
|
{
|
|
return;
|
|
}
|
|
|
|
constexpr uintptr_t kRouteRecordStride = 0x28;
|
|
const auto rawSize = std::min<size_t>(0xA0, static_cast<size_t>(count) * kRouteRecordStride);
|
|
AppendBytesPointer(aLine, (prefix + "_ptr28_rec40raw").c_str(), data, rawSize);
|
|
|
|
const auto flags = aLine.flags();
|
|
const auto fill = aLine.fill();
|
|
aLine << " " << prefix << "_ptr28_rec40=[";
|
|
for (uint32_t index = 0; index < count; ++index)
|
|
{
|
|
const auto recordAddress = reinterpret_cast<uintptr_t>(data) + static_cast<uintptr_t>(index) * kRouteRecordStride;
|
|
const auto* record = reinterpret_cast<const void*>(recordAddress);
|
|
|
|
uint64_t h00 = 0;
|
|
uint32_t u08 = 0;
|
|
uint32_t u0c = 0;
|
|
uint32_t u10 = 0;
|
|
uint32_t u14 = 0;
|
|
uint64_t h18 = 0;
|
|
uint64_t h20 = 0;
|
|
const auto hasRecord = TryReadMemory(record, h00) &&
|
|
TryReadMemory(reinterpret_cast<const void*>(recordAddress + 0x08), u08) &&
|
|
TryReadMemory(reinterpret_cast<const void*>(recordAddress + 0x0C), u0c) &&
|
|
TryReadMemory(reinterpret_cast<const void*>(recordAddress + 0x10), u10) &&
|
|
TryReadMemory(reinterpret_cast<const void*>(recordAddress + 0x14), u14) &&
|
|
TryReadMemory(reinterpret_cast<const void*>(recordAddress + 0x18), h18) &&
|
|
TryReadMemory(reinterpret_cast<const void*>(recordAddress + 0x20), h20);
|
|
|
|
if (index > 0)
|
|
{
|
|
aLine << ";";
|
|
}
|
|
|
|
aLine << index << ":";
|
|
if (!hasRecord)
|
|
{
|
|
aLine << "<unreadable>";
|
|
continue;
|
|
}
|
|
|
|
const auto h18Lo = static_cast<uint32_t>(h18);
|
|
const auto h18Hi = static_cast<uint32_t>(h18 >> 32);
|
|
aLine << std::hex << h00 << ",0x" << std::setw(8) << std::setfill('0') << u08 << std::setfill(fill)
|
|
<< std::dec << "," << u0c << "," << u10 << "," << u14 << "," << std::hex << h18 << std::dec
|
|
<< "," << FloatFromBits(h18Lo) << "," << FloatFromBits(h18Hi) << "," << std::hex << h20
|
|
<< std::dec;
|
|
}
|
|
aLine << "]";
|
|
aLine.flags(flags);
|
|
aLine.fill(fill);
|
|
}
|
|
|
|
void AppendGpsResultObjectSummary(std::ostringstream& aLine, const char* aPrefix, void* aResultObject)
|
|
{
|
|
if (!aResultObject)
|
|
{
|
|
aLine << " " << aPrefix << "_result=<null>";
|
|
return;
|
|
}
|
|
|
|
AppendBytesField(aLine, (std::string(aPrefix) + "_bytes20").c_str(), aResultObject, 0x20, 0x40);
|
|
AppendUint32Field(aLine, (std::string(aPrefix) + "_u20").c_str(), aResultObject, 0x20);
|
|
AppendUint32Field(aLine, (std::string(aPrefix) + "_u24").c_str(), aResultObject, 0x24);
|
|
AppendPointerField(aLine, (std::string(aPrefix) + "_ptr28").c_str(), aResultObject, 0x28);
|
|
AppendVectorListCandidate(aLine, (std::string(aPrefix) + "_vec20").c_str(), aResultObject, 0x20, 0x2C, 12);
|
|
AppendVectorListCandidate(aLine, (std::string(aPrefix) + "_vec20s16").c_str(), aResultObject, 0x20, 0x2C, 16);
|
|
AppendUint32Field(aLine, (std::string(aPrefix) + "_u2c").c_str(), aResultObject, 0x2C);
|
|
AppendUint64Field(aLine, (std::string(aPrefix) + "_u30").c_str(), aResultObject, 0x30);
|
|
AppendUint32Field(aLine, (std::string(aPrefix) + "_u30lo").c_str(), aResultObject, 0x30);
|
|
AppendUint32Field(aLine, (std::string(aPrefix) + "_u34hi").c_str(), aResultObject, 0x34);
|
|
AppendGpsResultElementArraySummary(aLine, aPrefix, aResultObject);
|
|
AppendVectorListCandidate(aLine, (std::string(aPrefix) + "_vec38").c_str(), aResultObject, 0x38, 0x3C, 12);
|
|
AppendUint16Field(aLine, (std::string(aPrefix) + "_flags40").c_str(), aResultObject, 0x40);
|
|
AppendByteField(aLine, (std::string(aPrefix) + "_state42").c_str(), aResultObject, 0x42);
|
|
AppendUint64Field(aLine, (std::string(aPrefix) + "_u48").c_str(), aResultObject, 0x48);
|
|
AppendUint64Field(aLine, (std::string(aPrefix) + "_u50").c_str(), aResultObject, 0x50);
|
|
}
|
|
|
|
uint32_t NextGpsAsyncLogCall(uint32_t& aCounter)
|
|
{
|
|
std::lock_guard lock(gGpsAsyncLogMutex);
|
|
return aCounter++;
|
|
}
|
|
|
|
bool TryReadUint32FieldValue(void* aObject, uintptr_t aOffset, uint32_t& aValue)
|
|
{
|
|
return aObject && TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aObject) + aOffset),
|
|
aValue);
|
|
}
|
|
|
|
bool IsDrivingRouteJob(void* aRouteSystem, void* aActiveEntry)
|
|
{
|
|
uint32_t mode = 0;
|
|
if (TryReadUint32FieldValue(aActiveEntry, 0x80, mode) && mode == 2)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
auto* job = ReadPointerField(aRouteSystem, 0x90D0);
|
|
return TryReadUint32FieldValue(job, 0xC4, mode) && mode == 2;
|
|
}
|
|
|
|
void AppendFloatArray(std::ostringstream& aLine, const char* aLabel, const float* aValues, size_t aCount)
|
|
{
|
|
const auto flags = aLine.flags();
|
|
const auto precision = aLine.precision();
|
|
aLine << " " << aLabel << "=[";
|
|
for (size_t index = 0; index < aCount; ++index)
|
|
{
|
|
if (index > 0)
|
|
{
|
|
aLine << ",";
|
|
}
|
|
aLine << std::fixed << std::setprecision(2) << aValues[index];
|
|
}
|
|
aLine << "]";
|
|
aLine.flags(flags);
|
|
aLine.precision(precision);
|
|
}
|
|
|
|
void AppendGpsProviderClassSamples(std::ostringstream& aLine, const char* aLabel, const float* aValues,
|
|
size_t aCount)
|
|
{
|
|
const auto flags = aLine.flags();
|
|
const auto precision = aLine.precision();
|
|
aLine << " " << aLabel << "=[";
|
|
bool first = true;
|
|
for (const auto classId : kGpsProviderClassProbeIds)
|
|
{
|
|
if (classId >= aCount)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!first)
|
|
{
|
|
aLine << ",";
|
|
}
|
|
first = false;
|
|
aLine << classId << ":" << std::fixed << std::setprecision(2) << aValues[classId];
|
|
}
|
|
aLine << "]";
|
|
aLine.flags(flags);
|
|
aLine.precision(precision);
|
|
}
|
|
|
|
void AppendGpsProviderClassOverrides(std::ostringstream& aLine)
|
|
{
|
|
const auto flags = aLine.flags();
|
|
const auto precision = aLine.precision();
|
|
aLine << " overrides=[";
|
|
for (size_t index = 0; index < kGpsProviderClassOverrides.size(); ++index)
|
|
{
|
|
if (index > 0)
|
|
{
|
|
aLine << ",";
|
|
}
|
|
|
|
const auto& item = kGpsProviderClassOverrides[index];
|
|
aLine << item.classId << ":" << std::fixed << std::setprecision(2) << item.multiplier;
|
|
}
|
|
aLine << "]";
|
|
aLine.flags(flags);
|
|
aLine.precision(precision);
|
|
}
|
|
|
|
class ScopedGpsCostTablePatch
|
|
{
|
|
public:
|
|
ScopedGpsCostTablePatch(bool aShouldPatch, uint32_t aRouteJobStartCall)
|
|
: m_routeJobStartCall(aRouteJobStartCall)
|
|
{
|
|
if (!kEnableGpsCostTablePatch || !aShouldPatch)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_table = reinterpret_cast<float*>(GetImageBase() + kGpsCostMultiplierTableRva);
|
|
if (!IsReadableMemory(m_table, sizeof(m_original)))
|
|
{
|
|
LogPatch("gps-cost-table-patch skipped", "reason=unreadable-table");
|
|
return;
|
|
}
|
|
|
|
std::memcpy(m_original.data(), m_table, sizeof(m_original));
|
|
|
|
DWORD oldProtect = 0;
|
|
if (!VirtualProtect(m_table, sizeof(m_original), PAGE_EXECUTE_READWRITE, &oldProtect))
|
|
{
|
|
LogPatch("gps-cost-table-patch skipped", "reason=protect-failed");
|
|
return;
|
|
}
|
|
|
|
std::memcpy(m_table, kGpsPatchedCostMultipliers.data(), sizeof(m_original));
|
|
|
|
DWORD ignoredProtect = 0;
|
|
VirtualProtect(m_table, sizeof(m_original), oldProtect, &ignoredProtect);
|
|
FlushInstructionCache(GetCurrentProcess(), m_table, sizeof(m_original));
|
|
|
|
m_active = true;
|
|
LogPatch("gps-cost-table-patch applied", nullptr);
|
|
}
|
|
|
|
~ScopedGpsCostTablePatch()
|
|
{
|
|
if (!m_active || !m_table)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DWORD oldProtect = 0;
|
|
if (!VirtualProtect(m_table, sizeof(m_original), PAGE_EXECUTE_READWRITE, &oldProtect))
|
|
{
|
|
LogPatch("gps-cost-table-patch restore-failed", "reason=protect-failed");
|
|
return;
|
|
}
|
|
|
|
std::memcpy(m_table, m_original.data(), sizeof(m_original));
|
|
|
|
DWORD ignoredProtect = 0;
|
|
VirtualProtect(m_table, sizeof(m_original), oldProtect, &ignoredProtect);
|
|
FlushInstructionCache(GetCurrentProcess(), m_table, sizeof(m_original));
|
|
LogPatch("gps-cost-table-patch restored", nullptr);
|
|
}
|
|
|
|
private:
|
|
void LogPatch(const char* aMessage, const char* aReason)
|
|
{
|
|
const auto patchLogCall = NextGpsAsyncLogCall(gGpsCostTablePatchLogCount);
|
|
if (patchLogCall >= 16)
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::ostringstream line;
|
|
line << aMessage << " patchCall=" << patchLogCall << " routeJobStartCall=" << m_routeJobStartCall;
|
|
AppendPointerWithRva(line, "table", m_table);
|
|
if (aReason)
|
|
{
|
|
line << " " << aReason;
|
|
}
|
|
if (m_table && IsReadableMemory(m_table, sizeof(m_original)))
|
|
{
|
|
AppendFloatArray(line, "current", m_table, m_original.size());
|
|
}
|
|
AppendFloatArray(line, "original", m_original.data(), m_original.size());
|
|
AppendFloatArray(line, "patched", kGpsPatchedCostMultipliers.data(), kGpsPatchedCostMultipliers.size());
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
uint32_t m_routeJobStartCall = 0;
|
|
bool m_active = false;
|
|
float* m_table = nullptr;
|
|
std::array<float, kGpsPatchedCostMultipliers.size()> m_original{};
|
|
};
|
|
|
|
bool IsKnownGpsCostProvider(void* aProvider, uintptr_t& aVtableRva)
|
|
{
|
|
aVtableRva = 0;
|
|
auto* vtable = ReadPointerField(aProvider, 0x00);
|
|
if (!TryGetImageRva(vtable, aVtableRva))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return aVtableRva == kGpsBaseCostProviderVtableRva || aVtableRva == kGpsFilteredCostProviderVtableRva;
|
|
}
|
|
|
|
class ScopedGpsProviderClassPatch
|
|
{
|
|
public:
|
|
ScopedGpsProviderClassPatch(void* aProvider, uint32_t aSearchCall)
|
|
: m_provider(aProvider)
|
|
, m_searchCall(aSearchCall)
|
|
{
|
|
if (!kEnableGpsProviderClassPatch || !m_provider)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!IsKnownGpsCostProvider(m_provider, m_vtableRva))
|
|
{
|
|
LogPatch("gps-provider-class-patch skipped", "reason=unknown-provider");
|
|
return;
|
|
}
|
|
|
|
m_table = reinterpret_cast<float*>(reinterpret_cast<uintptr_t>(m_provider) + 0x08);
|
|
if (!IsReadableMemory(m_table, sizeof(m_original)))
|
|
{
|
|
LogPatch("gps-provider-class-patch skipped", "reason=unreadable-table");
|
|
return;
|
|
}
|
|
|
|
std::memcpy(m_original.data(), m_table, sizeof(m_original));
|
|
for (const auto& item : kGpsProviderClassOverrides)
|
|
{
|
|
if (item.classId < m_original.size())
|
|
{
|
|
m_table[item.classId] = item.multiplier;
|
|
}
|
|
}
|
|
m_active = true;
|
|
LogPatch("gps-provider-class-patch applied", nullptr);
|
|
}
|
|
|
|
~ScopedGpsProviderClassPatch()
|
|
{
|
|
if (!m_active || !m_table)
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::memcpy(m_table, m_original.data(), sizeof(m_original));
|
|
LogPatch("gps-provider-class-patch restored", nullptr);
|
|
}
|
|
|
|
private:
|
|
void LogPatch(const char* aMessage, const char* aReason)
|
|
{
|
|
if (!kEnableGpsProviderClassPatchTrace)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const auto patchLogCall = NextGpsAsyncLogCall(gGpsProviderClassPatchLogCount);
|
|
if (patchLogCall >= 128)
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::ostringstream line;
|
|
line << aMessage << " patchCall=" << patchLogCall << " searchCall=" << m_searchCall;
|
|
AppendPointerWithRva(line, "provider", m_provider);
|
|
line << " provider_vtable_rva=0x" << std::hex << m_vtableRva << std::dec;
|
|
if (aReason)
|
|
{
|
|
line << " " << aReason;
|
|
}
|
|
if (m_table && IsReadableMemory(m_table, sizeof(m_original)))
|
|
{
|
|
AppendGpsProviderClassSamples(line, "current", m_table, m_original.size());
|
|
}
|
|
AppendGpsProviderClassSamples(line, "original", m_original.data(), m_original.size());
|
|
AppendGpsProviderClassOverrides(line);
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
void* m_provider = nullptr;
|
|
float* m_table = nullptr;
|
|
uint32_t m_searchCall = 0;
|
|
uintptr_t m_vtableRva = 0;
|
|
bool m_active = false;
|
|
std::array<float, kGpsProviderClassCount> m_original{};
|
|
};
|
|
|
|
void AppendRouteJobSummary(std::ostringstream& aLine, const char* aPrefix, void* aJob)
|
|
{
|
|
const auto prefix = std::string(aPrefix);
|
|
AppendPointerWithRva(aLine, prefix.c_str(), aJob);
|
|
if (!aJob)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AppendUint32Field(aLine, (prefix + "_type10").c_str(), aJob, 0x10);
|
|
AppendPointerField(aLine, (prefix + "_result18").c_str(), aJob, 0x18);
|
|
AppendPointerField(aLine, (prefix + "_result30").c_str(), aJob, 0x30);
|
|
AppendUint32Field(aLine, (prefix + "_u4c").c_str(), aJob, 0x4C);
|
|
AppendUint32Field(aLine, (prefix + "_count50").c_str(), aJob, 0x50);
|
|
AppendUint32Field(aLine, (prefix + "_retry54").c_str(), aJob, 0x54);
|
|
AppendUint32Field(aLine, (prefix + "_flagsc0").c_str(), aJob, 0xC0);
|
|
AppendUint32Field(aLine, (prefix + "_statec4").c_str(), aJob, 0xC4);
|
|
AppendPointerField(aLine, (prefix + "_chunkse0").c_str(), aJob, 0xE0);
|
|
AppendUint32Field(aLine, (prefix + "_chunks_cap_e8").c_str(), aJob, 0xE8);
|
|
AppendUint32Field(aLine, (prefix + "_chunks_count_ec").c_str(), aJob, 0xEC);
|
|
AppendPointerField(aLine, (prefix + "_nodesf8").c_str(), aJob, 0xF8);
|
|
}
|
|
|
|
void AppendRouteSystemSummary(std::ostringstream& aLine, const char* aPrefix, void* aRouteSystem)
|
|
{
|
|
const auto prefix = std::string(aPrefix);
|
|
AppendPointerWithRva(aLine, prefix.c_str(), aRouteSystem);
|
|
if (!aRouteSystem)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AppendUint32Field(aLine, (prefix + "_queued8fc0").c_str(), aRouteSystem, 0x8FC0);
|
|
AppendUint32Field(aLine, (prefix + "_active8fd0").c_str(), aRouteSystem, 0x8FD0);
|
|
AppendPointerField(aLine, (prefix + "_job90d0").c_str(), aRouteSystem, 0x90D0);
|
|
|
|
auto* job = ReadPointerField(aRouteSystem, 0x90D0);
|
|
if (!job)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AppendRouteJobSummary(aLine, (prefix + "_job").c_str(), job);
|
|
}
|
|
|
|
bool HasPendingRouteWork(void* aRouteSystem)
|
|
{
|
|
uint32_t queued = 0;
|
|
uint32_t active = 0;
|
|
TryReadUint32FieldValue(aRouteSystem, 0x8FC0, queued);
|
|
TryReadUint32FieldValue(aRouteSystem, 0x8FD0, active);
|
|
return queued != 0 || active != 0 || ReadPointerField(aRouteSystem, 0x90D0) != nullptr;
|
|
}
|
|
|
|
void AppendRouteRequestFields(std::ostringstream& aLine, const char* aPrefix, void* aObject, uintptr_t aOffset)
|
|
{
|
|
const auto prefix = std::string(aPrefix);
|
|
auto* base = aObject ? reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(aObject) + aOffset) : nullptr;
|
|
AppendPointerWithRva(aLine, prefix.c_str(), base);
|
|
if (!base)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AppendBytesPointer(aLine, (prefix + "_raw40").c_str(), base, 0x40);
|
|
AppendVector4Field(aLine, (prefix + "_vec00").c_str(), base, 0x00);
|
|
AppendVector4Field(aLine, (prefix + "_vec10").c_str(), base, 0x10);
|
|
AppendUint32Field(aLine, (prefix + "_u00").c_str(), base, 0x00);
|
|
AppendUint32Field(aLine, (prefix + "_u08").c_str(), base, 0x08);
|
|
AppendUint32Field(aLine, (prefix + "_u1c").c_str(), base, 0x1C);
|
|
AppendUint16Field(aLine, (prefix + "_u1e").c_str(), base, 0x1E);
|
|
AppendUint32Field(aLine, (prefix + "_u20").c_str(), base, 0x20);
|
|
AppendUint16Field(aLine, (prefix + "_u20w").c_str(), base, 0x20);
|
|
AppendUint16Field(aLine, (prefix + "_u24").c_str(), base, 0x24);
|
|
AppendUint16Field(aLine, (prefix + "_u26").c_str(), base, 0x26);
|
|
AppendUint16Field(aLine, (prefix + "_u28").c_str(), base, 0x28);
|
|
AppendUint32Field(aLine, (prefix + "_u70").c_str(), base, 0x70);
|
|
AppendByteField(aLine, (prefix + "_b74").c_str(), base, 0x74);
|
|
AppendByteField(aLine, (prefix + "_b75").c_str(), base, 0x75);
|
|
AppendByteField(aLine, (prefix + "_b76").c_str(), base, 0x76);
|
|
AppendByteField(aLine, (prefix + "_b77").c_str(), base, 0x77);
|
|
}
|
|
|
|
void AppendRouteQueueEntrySummary(std::ostringstream& aLine, const char* aPrefix, void* aEntry)
|
|
{
|
|
const auto prefix = std::string(aPrefix);
|
|
AppendPointerWithRva(aLine, prefix.c_str(), aEntry);
|
|
if (!aEntry)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AppendUint32Field(aLine, (prefix + "_id00").c_str(), aEntry, 0x00);
|
|
AppendRouteRequestFields(aLine, (prefix + "_payload10").c_str(), aEntry, 0x10);
|
|
AppendPointerField(aLine, (prefix + "_vtable40").c_str(), aEntry, 0x40);
|
|
AppendPointerField(aLine, (prefix + "_self78").c_str(), aEntry, 0x78);
|
|
AppendUint32Field(aLine, (prefix + "_u80").c_str(), aEntry, 0x80);
|
|
AppendByteField(aLine, (prefix + "_b84").c_str(), aEntry, 0x84);
|
|
AppendByteField(aLine, (prefix + "_b85").c_str(), aEntry, 0x85);
|
|
AppendByteField(aLine, (prefix + "_b86").c_str(), aEntry, 0x86);
|
|
AppendByteField(aLine, (prefix + "_b87").c_str(), aEntry, 0x87);
|
|
}
|
|
|
|
void AppendRouteOutputPointerSummary(std::ostringstream& aLine, const char* aPrefix, void* aOut)
|
|
{
|
|
const auto prefix = std::string(aPrefix);
|
|
AppendPointerWithRva(aLine, prefix.c_str(), aOut);
|
|
if (!aOut)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto* value = ReadPointerField(aOut, 0x00);
|
|
AppendPointerWithRva(aLine, (prefix + "_value").c_str(), value);
|
|
if (value && IsReadableMemory(value, 0x58))
|
|
{
|
|
AppendGpsResultObjectSummary(aLine, (prefix + "_result").c_str(), value);
|
|
}
|
|
}
|
|
|
|
void TrackGpsQuery(uint32_t aQueryId, void* aManager, uintptr_t aSubmitReturnRva, uint32_t aSubmitCall)
|
|
{
|
|
if (aQueryId == UINT32_MAX)
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::lock_guard lock(gTrackedGpsQueryMutex);
|
|
|
|
for (auto& query : gTrackedGpsQueries)
|
|
{
|
|
if (query.active && query.id == aQueryId && query.manager == aManager)
|
|
{
|
|
query.submitReturnRva = aSubmitReturnRva;
|
|
query.submitCall = aSubmitCall;
|
|
return;
|
|
}
|
|
}
|
|
|
|
auto& query = gTrackedGpsQueries[gTrackedGpsQueryNext++ % gTrackedGpsQueries.size()];
|
|
query.active = true;
|
|
query.id = aQueryId;
|
|
query.manager = aManager;
|
|
query.submitReturnRva = aSubmitReturnRva;
|
|
query.submitCall = aSubmitCall;
|
|
query.resultFetchCalls = 0;
|
|
query.statusCalls = 0;
|
|
}
|
|
|
|
bool NoteTrackedGpsQueryResultFetch(uint32_t aQueryId, void* aManager, TrackedGpsQuery& aSnapshot,
|
|
uint32_t& aPerQueryCall)
|
|
{
|
|
std::lock_guard lock(gTrackedGpsQueryMutex);
|
|
|
|
for (auto& query : gTrackedGpsQueries)
|
|
{
|
|
if (!query.active || query.id != aQueryId || query.manager != aManager)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
aPerQueryCall = query.resultFetchCalls++;
|
|
aSnapshot = query;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool NoteTrackedGpsQueryStatus(uint32_t aQueryId, void* aManager, TrackedGpsQuery& aSnapshot,
|
|
uint32_t& aPerQueryCall)
|
|
{
|
|
std::lock_guard lock(gTrackedGpsQueryMutex);
|
|
|
|
for (auto& query : gTrackedGpsQueries)
|
|
{
|
|
if (!query.active || query.id != aQueryId || query.manager != aManager)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
aPerQueryCall = query.statusCalls++;
|
|
aSnapshot = query;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void AppendTrackedGpsQuery(std::ostringstream& aLine, const TrackedGpsQuery& aQuery)
|
|
{
|
|
aLine << " submitCall=" << aQuery.submitCall << " submitRetRva=0x" << std::hex << aQuery.submitReturnRva
|
|
<< std::dec;
|
|
}
|
|
|
|
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 (!kEnableVerboseMappinRouteState)
|
|
{
|
|
return;
|
|
}
|
|
|
|
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});
|
|
LogRed4ext(observerLine.str());
|
|
}
|
|
}
|
|
|
|
void LogRouteObserverContext(const char* aName, uint32_t aCount, void* aObserver, void* aRouteEntry)
|
|
{
|
|
auto* observerInner = ReadPointerField(aObserver, 0x08);
|
|
auto* observerInnerServiceOwner = ReadPointerField(observerInner, 0x18);
|
|
auto* observer1SystemType = GetObserver1SystemType();
|
|
auto* routeObject = ReadPointerField(aRouteEntry, 0x00);
|
|
|
|
std::ostringstream line;
|
|
line << "hook " << aName << " call=" << aCount;
|
|
AppendPointerWithRva(line, "observer", aObserver);
|
|
AppendPointerWithRva(line, "observerInner", observerInner);
|
|
AppendPointerWithRva(line, "observerInnerServiceOwner", observerInnerServiceOwner);
|
|
AppendPointerWithRva(line, "observer1SystemType342f6a8", observer1SystemType);
|
|
AppendPointerWithRva(line, "routeEntry", aRouteEntry);
|
|
AppendPointerWithRva(line, "routeObject", routeObject);
|
|
AppendPointerField(line, "routeEntry_f08", aRouteEntry, 0x08);
|
|
AppendPointerField(line, "routeEntry_f10", aRouteEntry, 0x10);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
|
|
std::ostringstream observerLine;
|
|
observerLine << "route-observer-object " << aName << " call=" << aCount;
|
|
AppendPointerWithRva(observerLine, "observer", aObserver);
|
|
AppendObjectVtableSlots(observerLine, aObserver, {0x48, 0x50});
|
|
LogRed4ext(observerLine.str());
|
|
|
|
if (observerInner)
|
|
{
|
|
std::ostringstream innerLine;
|
|
innerLine << "route-observer-inner " << aName << " call=" << aCount;
|
|
AppendPointerWithRva(innerLine, "observerInner", observerInner);
|
|
AppendObjectVtableSlots(innerLine, observerInner, {0x08});
|
|
AppendPointerField(innerLine, "field18", observerInner, 0x18);
|
|
AppendPointerField(innerLine, "field180", observerInner, 0x180);
|
|
AppendPointerField(innerLine, "field1a0", observerInner, 0x1A0);
|
|
AppendPointerField(innerLine, "field1c0", observerInner, 0x1C0);
|
|
AppendPointerField(innerLine, "field1e0", observerInner, 0x1E0);
|
|
AppendPointerField(innerLine, "field1e8", observerInner, 0x1E8);
|
|
AppendPointerField(innerLine, "field1f0", observerInner, 0x1F0);
|
|
LogRed4ext(innerLine.str());
|
|
}
|
|
|
|
if (observerInnerServiceOwner)
|
|
{
|
|
std::ostringstream serviceOwnerLine;
|
|
serviceOwnerLine << "route-observer-service-owner " << aName << " call=" << aCount;
|
|
AppendPointerWithRva(serviceOwnerLine, "serviceOwner", observerInnerServiceOwner);
|
|
AppendObjectVtableSlots(serviceOwnerLine, observerInnerServiceOwner, {0x08});
|
|
LogRed4ext(serviceOwnerLine.str());
|
|
}
|
|
|
|
if (!routeObject)
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::ostringstream routeLine;
|
|
routeLine << "route-observer-route-object " << aName << " call=" << aCount;
|
|
AppendPointerWithRva(routeLine, "routeObject", routeObject);
|
|
AppendObjectVtableSlots(routeLine, routeObject, {0x140, 0x188, 0x198, 0x1A8});
|
|
AppendPointerField(routeLine, "field40", routeObject, 0x40);
|
|
AppendPointerField(routeLine, "field48", routeObject, 0x48);
|
|
AppendPointerField(routeLine, "field78", routeObject, 0x78);
|
|
AppendUint32Field(routeLine, "field80", routeObject, 0x80);
|
|
AppendByteField(routeLine, "active84", routeObject, 0x84);
|
|
AppendUint32Field(routeLine, "field8C", routeObject, 0x8C);
|
|
AppendUint32Field(routeLine, "field90", routeObject, 0x90);
|
|
AppendPointerField(routeLine, "field98", routeObject, 0x98);
|
|
AppendPointerField(routeLine, "field88", routeObject, 0x88);
|
|
AppendPointerField(routeLine, "fieldF8", routeObject, 0xF8);
|
|
AppendPointerField(routeLine, "field120", routeObject, 0x120);
|
|
AppendByteField(routeLine, "fieldE2", routeObject, 0xE2);
|
|
LogRed4ext(routeLine.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;
|
|
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)
|
|
{
|
|
constexpr uint32_t kMaxMatchesPerNeedle = 8;
|
|
|
|
const auto needleSize = std::strlen(aNeedle);
|
|
if (needleSize == 0 || aRegionSize < needleSize || aMatches >= kMaxMatchesPerNeedle)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const auto* cursor = aRegionBegin;
|
|
const auto* regionEnd = aRegionBegin + aRegionSize;
|
|
|
|
while (cursor + needleSize <= regionEnd && aMatches < kMaxMatchesPerNeedle)
|
|
{
|
|
if (std::memcmp(cursor, aNeedle, needleSize) == 0)
|
|
{
|
|
const auto rva = reinterpret_cast<uintptr_t>(cursor) - aImageBase;
|
|
std::ostringstream line;
|
|
line << "exe-string match needle=\"" << aNeedle << "\" rva=0x" << std::hex << rva << std::dec;
|
|
LogRed4ext(line.str());
|
|
++aMatches;
|
|
cursor += needleSize;
|
|
continue;
|
|
}
|
|
|
|
++cursor;
|
|
}
|
|
}
|
|
|
|
void ScanExecutableStrings()
|
|
{
|
|
if (gScannedExecutable)
|
|
{
|
|
return;
|
|
}
|
|
gScannedExecutable = true;
|
|
|
|
const auto imageBase = reinterpret_cast<uintptr_t>(GetModuleHandleW(nullptr));
|
|
if (!imageBase)
|
|
{
|
|
LogRed4ext("exe-string scan skipped: main module unavailable");
|
|
return;
|
|
}
|
|
|
|
const auto* dos = reinterpret_cast<const IMAGE_DOS_HEADER*>(imageBase);
|
|
if (dos->e_magic != IMAGE_DOS_SIGNATURE)
|
|
{
|
|
LogRed4ext("exe-string scan skipped: bad DOS header");
|
|
return;
|
|
}
|
|
|
|
const auto* nt = reinterpret_cast<const IMAGE_NT_HEADERS*>(imageBase + dos->e_lfanew);
|
|
if (nt->Signature != IMAGE_NT_SIGNATURE)
|
|
{
|
|
LogRed4ext("exe-string scan skipped: bad NT header");
|
|
return;
|
|
}
|
|
|
|
const auto imageSize = static_cast<size_t>(nt->OptionalHeader.SizeOfImage);
|
|
std::ostringstream header;
|
|
header << "exe-string scan begin base=0x" << std::hex << imageBase << " size=0x" << imageSize << std::dec;
|
|
LogRed4ext(header.str());
|
|
|
|
constexpr std::array<const char*, 21> kNeedles{
|
|
"GPSSystem/Tick",
|
|
"GPSSystem",
|
|
"GPSSettings",
|
|
"RunGPSQuery",
|
|
"UpdateGPSQuery",
|
|
"gameuiGPSGameController",
|
|
"OnSetHasGPSPortal",
|
|
"IsGPSPortal",
|
|
"m_cookedGpsData",
|
|
"gameCookedGpsMappinData",
|
|
"GetNavigationFunctionalTests",
|
|
"TrafficSystem_Pathfinding",
|
|
"TrafficSystem_PathMap",
|
|
"Pathfinding Algorithm Failed",
|
|
"No Path Found in Traffic",
|
|
"PathFindingFailed",
|
|
"worldTrafficLanePlayerGPSInfo",
|
|
"worldTrafficPersistentResource",
|
|
"worldTrafficConnectivityOutLane",
|
|
};
|
|
|
|
for (const auto* needle : kNeedles)
|
|
{
|
|
uint32_t matches = 0;
|
|
uintptr_t cursor = imageBase;
|
|
const uintptr_t end = imageBase + imageSize;
|
|
|
|
while (cursor < end)
|
|
{
|
|
MEMORY_BASIC_INFORMATION mbi{};
|
|
if (!VirtualQuery(reinterpret_cast<const void*>(cursor), &mbi, sizeof(mbi)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
const auto regionBegin = reinterpret_cast<uintptr_t>(mbi.BaseAddress);
|
|
const auto regionEnd = regionBegin + mbi.RegionSize;
|
|
if (mbi.State == MEM_COMMIT && mbi.Type == MEM_IMAGE && IsReadablePage(mbi.Protect))
|
|
{
|
|
const auto scanBegin = std::max(regionBegin, imageBase);
|
|
const auto scanEnd = std::min(regionEnd, end);
|
|
if (scanBegin < scanEnd)
|
|
{
|
|
ScanRegionForString(reinterpret_cast<const uint8_t*>(scanBegin), scanEnd - scanBegin, imageBase,
|
|
needle, matches);
|
|
}
|
|
}
|
|
|
|
if (regionEnd <= cursor)
|
|
{
|
|
break;
|
|
}
|
|
cursor = regionEnd;
|
|
}
|
|
|
|
if (matches == 0)
|
|
{
|
|
std::ostringstream line;
|
|
line << "exe-string no match needle=\"" << needle << "\"";
|
|
LogRed4ext(line.str());
|
|
}
|
|
}
|
|
|
|
LogRed4ext("exe-string scan end");
|
|
}
|
|
|
|
void DetourGpsSystemTick(void* aThis, void* a2, void* a3, void* a4)
|
|
{
|
|
if (gGpsTickLogCount < 8)
|
|
{
|
|
LogHookCall("GPSSystem/Tick", gGpsTickLogCount, aThis, a2, a3, a4);
|
|
++gGpsTickLogCount;
|
|
}
|
|
|
|
if (gOriginalGpsSystemTick)
|
|
{
|
|
gOriginalGpsSystemTick(aThis, a2, a3, a4);
|
|
}
|
|
}
|
|
|
|
void LogJournalListeners(uint32_t aCallCount, void* aThis)
|
|
{
|
|
constexpr uint32_t kMaxListenerDumps = 16;
|
|
constexpr uint32_t kMaxListenersPerDump = 24;
|
|
constexpr uint32_t kSuspiciousListenerCount = 512;
|
|
|
|
if (gJournalTrackEntryListenerDumpCount >= kMaxListenerDumps || !aThis)
|
|
{
|
|
return;
|
|
}
|
|
|
|
uintptr_t listenerBase = 0;
|
|
uint32_t listenerCount = 0;
|
|
const auto objectAddress = reinterpret_cast<uintptr_t>(aThis);
|
|
const auto hasBase = TryReadMemory(reinterpret_cast<const void*>(objectAddress + kJournalListenerArrayOffset),
|
|
listenerBase);
|
|
const auto hasCount = TryReadMemory(reinterpret_cast<const void*>(objectAddress + kJournalListenerCountOffset),
|
|
listenerCount);
|
|
|
|
std::ostringstream header;
|
|
header << "journal-listeners dump=" << gJournalTrackEntryListenerDumpCount << " call=" << aCallCount
|
|
<< " hasBase=" << hasBase << " hasCount=" << hasCount << " count=" << listenerCount;
|
|
AppendPointerWithRva(header, "base", reinterpret_cast<const void*>(listenerBase));
|
|
LogRed4ext(header.str());
|
|
|
|
++gJournalTrackEntryListenerDumpCount;
|
|
|
|
if (!hasBase || !hasCount || !listenerBase || listenerCount > kSuspiciousListenerCount)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const auto cappedCount = std::min(listenerCount, kMaxListenersPerDump);
|
|
for (uint32_t index = 0; index < cappedCount; ++index)
|
|
{
|
|
void* listener = nullptr;
|
|
void* vtable = nullptr;
|
|
void* callback28 = nullptr;
|
|
void* callback30 = nullptr;
|
|
void* callback50 = nullptr;
|
|
|
|
const auto listenerAddress = reinterpret_cast<const void*>(listenerBase + index * sizeof(void*));
|
|
const auto hasListener = TryReadMemory(listenerAddress, listener);
|
|
const auto hasVtable = hasListener && TryReadMemory(listener, vtable);
|
|
if (hasVtable && vtable)
|
|
{
|
|
const auto vtableAddress = reinterpret_cast<uintptr_t>(vtable);
|
|
TryReadMemory(reinterpret_cast<const void*>(vtableAddress + 0x28), callback28);
|
|
TryReadMemory(reinterpret_cast<const void*>(vtableAddress + 0x30), callback30);
|
|
TryReadMemory(reinterpret_cast<const void*>(vtableAddress + 0x50), callback50);
|
|
}
|
|
|
|
std::ostringstream line;
|
|
line << "journal-listener call=" << aCallCount << " index=" << index << " hasListener=" << hasListener
|
|
<< " hasVtable=" << hasVtable;
|
|
AppendPointerWithRva(line, "listener", listener);
|
|
AppendPointerWithRva(line, "vtable", vtable);
|
|
AppendPointerWithRva(line, "vt28", callback28);
|
|
AppendPointerWithRva(line, "vt30", callback30);
|
|
AppendPointerWithRva(line, "vt50", callback50);
|
|
LogRed4ext(line.str());
|
|
}
|
|
}
|
|
|
|
void DetourJournalTrackEntry(void* aThis, void* aEntryHandle)
|
|
{
|
|
constexpr uint32_t kMaxCalls = 96;
|
|
|
|
const auto callCount = gJournalTrackEntryLogCount;
|
|
if (callCount < kMaxCalls)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook JournalManager::TrackEntry impl call=" << callCount;
|
|
AppendPointerWithRva(line, "this", aThis);
|
|
AppendPointerWithRva(line, "entryHandle", aEntryHandle);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
|
|
++gJournalTrackEntryLogCount;
|
|
}
|
|
|
|
if (gOriginalJournalTrackEntry)
|
|
{
|
|
gOriginalJournalTrackEntry(aThis, 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);
|
|
}
|
|
|
|
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);
|
|
const auto routeKey = reinterpret_cast<uint64_t>(ReadPointerField(routeOwner, 0x40));
|
|
|
|
std::ostringstream line;
|
|
line << "hook MappinRouteEvent::Handle call=" << callCount;
|
|
AppendPointerWithRva(line, "event", aEvent);
|
|
AppendPointerWithRva(line, "routeHandle", aRouteHandle);
|
|
AppendPointerWithRva(line, "routeOwner", routeOwner);
|
|
AppendHexValue(line, "routeKey", routeKey);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
LogRouteEventFields("handler-before", callCount, aEvent);
|
|
++gMappinRouteEventHandlerLogCount;
|
|
}
|
|
|
|
if (gOriginalMappinRouteEventHandler)
|
|
{
|
|
gOriginalMappinRouteEventHandler(aEvent, aRouteHandle);
|
|
}
|
|
}
|
|
|
|
void DetourMappinRouteActivate(void* aSystem, uint64_t aRouteKey)
|
|
{
|
|
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);
|
|
AppendHexValue(line, "routeKey", aRouteKey);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
LogMappinRouteCollections("activate-before", callCount, aSystem);
|
|
++gMappinRouteActivateLogCount;
|
|
}
|
|
|
|
if (gOriginalMappinRouteActivate)
|
|
{
|
|
gOriginalMappinRouteActivate(aSystem, aRouteKey);
|
|
}
|
|
}
|
|
|
|
void DetourMappinRouteDeactivate(void* aSystem, uint64_t aRouteKey)
|
|
{
|
|
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);
|
|
AppendHexValue(line, "routeKey", aRouteKey);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
LogMappinRouteCollections("deactivate-before", callCount, aSystem);
|
|
++gMappinRouteDeactivateLogCount;
|
|
}
|
|
|
|
if (gOriginalMappinRouteDeactivate)
|
|
{
|
|
gOriginalMappinRouteDeactivate(aSystem, aRouteKey);
|
|
}
|
|
}
|
|
|
|
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 DispatchRouteObserverCallback(const char* aName, uint32_t& aCounter, RouteObserverCallbackFunc aOriginal,
|
|
void* aObserver, void* aRouteEntry)
|
|
{
|
|
constexpr uint32_t kMaxCalls = 96;
|
|
|
|
if (aCounter < kMaxCalls)
|
|
{
|
|
LogRouteObserverContext(aName, aCounter, aObserver, aRouteEntry);
|
|
++aCounter;
|
|
}
|
|
|
|
if (aOriginal)
|
|
{
|
|
aOriginal(aObserver, aRouteEntry);
|
|
}
|
|
}
|
|
|
|
void DetourRouteObserver0Activate(void* aObserver, void* aRouteEntry)
|
|
{
|
|
DispatchRouteObserverCallback("RouteObserver0::Activate slot48", gRouteObserver0ActivateLogCount,
|
|
gOriginalRouteObserver0Activate, aObserver, aRouteEntry);
|
|
}
|
|
|
|
void DetourRouteObserver1Activate(void* aObserver, void* aRouteEntry)
|
|
{
|
|
DispatchRouteObserverCallback("RouteObserver1::Activate slot48", gRouteObserver1ActivateLogCount,
|
|
gOriginalRouteObserver1Activate, aObserver, aRouteEntry);
|
|
}
|
|
|
|
void DetourRouteObserver23Activate(void* aObserver, void* aRouteEntry)
|
|
{
|
|
DispatchRouteObserverCallback("RouteObserver23::Activate slot48", gRouteObserver23ActivateLogCount,
|
|
gOriginalRouteObserver23Activate, aObserver, aRouteEntry);
|
|
}
|
|
|
|
void DetourRouteObserver0Deactivate(void* aObserver, void* aRouteEntry)
|
|
{
|
|
DispatchRouteObserverCallback("RouteObserver0::Deactivate slot50", gRouteObserver0DeactivateLogCount,
|
|
gOriginalRouteObserver0Deactivate, aObserver, aRouteEntry);
|
|
}
|
|
|
|
void DetourRouteObserver1Deactivate(void* aObserver, void* aRouteEntry)
|
|
{
|
|
DispatchRouteObserverCallback("RouteObserver1::Deactivate slot50", gRouteObserver1DeactivateLogCount,
|
|
gOriginalRouteObserver1Deactivate, aObserver, aRouteEntry);
|
|
}
|
|
|
|
void DetourRouteObserver23Deactivate(void* aObserver, void* aRouteEntry)
|
|
{
|
|
DispatchRouteObserverCallback("RouteObserver23::Deactivate slot50", gRouteObserver23DeactivateLogCount,
|
|
gOriginalRouteObserver23Deactivate, aObserver, aRouteEntry);
|
|
}
|
|
|
|
void DetourFrameMappinPathWrapper(void* aThis, void* aStack, void* aResult)
|
|
{
|
|
if (gFrameMappinPathWrapperLogCount < 64)
|
|
{
|
|
LogScriptNativeCall("FrameMappinPathWrapper", gFrameMappinPathWrapperLogCount, aThis, aStack, aResult);
|
|
++gFrameMappinPathWrapperLogCount;
|
|
}
|
|
|
|
InspectAndHookMappinSystem(aThis);
|
|
|
|
if (gOriginalFrameMappinPathWrapper)
|
|
{
|
|
gOriginalFrameMappinPathWrapper(aThis, aStack, aResult);
|
|
}
|
|
}
|
|
|
|
void DetourFrameMappinPathCore(void* aThis, uint32_t aPathKind, void* a3, void* aPathRect)
|
|
{
|
|
if (gFrameMappinPathCoreLogCount < 64)
|
|
{
|
|
LogFrameMappinPathCoreCall(gFrameMappinPathCoreLogCount, aThis, aPathKind, a3, aPathRect);
|
|
++gFrameMappinPathCoreLogCount;
|
|
}
|
|
|
|
InspectAndHookMappinSystem(aThis);
|
|
|
|
if (gOriginalFrameMappinPathCore)
|
|
{
|
|
gOriginalFrameMappinPathCore(aThis, aPathKind, a3, aPathRect);
|
|
}
|
|
}
|
|
|
|
void DetourSetSelectedMappinWrapper(void* aThis, void* aStack, void* aResult)
|
|
{
|
|
if (gSetSelectedMappinWrapperLogCount < 64)
|
|
{
|
|
LogScriptNativeCall("SetSelectedMappinWrapper", gSetSelectedMappinWrapperLogCount, aThis, aStack, aResult);
|
|
++gSetSelectedMappinWrapperLogCount;
|
|
}
|
|
|
|
InspectAndHookMappinSystem(aThis);
|
|
|
|
if (gOriginalSetSelectedMappinWrapper)
|
|
{
|
|
gOriginalSetSelectedMappinWrapper(aThis, aStack, aResult);
|
|
}
|
|
}
|
|
|
|
void DetourSetSelectedMappinByIdWrapper(void* aThis, void* aStack, void* aResult)
|
|
{
|
|
if (gSetSelectedMappinByIdWrapperLogCount < 64)
|
|
{
|
|
LogScriptNativeCall("SetSelectedMappinByIdWrapper", gSetSelectedMappinByIdWrapperLogCount, aThis, aStack,
|
|
aResult);
|
|
++gSetSelectedMappinByIdWrapperLogCount;
|
|
}
|
|
|
|
InspectAndHookMappinSystem(aThis);
|
|
|
|
if (gOriginalSetSelectedMappinByIdWrapper)
|
|
{
|
|
gOriginalSetSelectedMappinByIdWrapper(aThis, aStack, aResult);
|
|
}
|
|
}
|
|
|
|
void DetourSetSelectedMappinByPositionWrapper(void* aThis, void* aStack, void* aResult)
|
|
{
|
|
if (gSetSelectedMappinByPositionWrapperLogCount < 64)
|
|
{
|
|
LogScriptNativeCall("SetSelectedMappinByPositionWrapper", gSetSelectedMappinByPositionWrapperLogCount, aThis,
|
|
aStack, aResult);
|
|
++gSetSelectedMappinByPositionWrapperLogCount;
|
|
}
|
|
|
|
InspectAndHookMappinSystem(aThis);
|
|
|
|
if (gOriginalSetSelectedMappinByPositionWrapper)
|
|
{
|
|
gOriginalSetSelectedMappinByPositionWrapper(aThis, aStack, aResult);
|
|
}
|
|
}
|
|
|
|
void DetourSetSelectedMappinCore(void* aThis, void* aMappinHandle)
|
|
{
|
|
if (gSetSelectedMappinCoreLogCount < 64)
|
|
{
|
|
LogSetSelectedMappinCoreCall(gSetSelectedMappinCoreLogCount, aThis, aMappinHandle);
|
|
++gSetSelectedMappinCoreLogCount;
|
|
}
|
|
|
|
InspectAndHookMappinSystem(aThis);
|
|
|
|
if (gOriginalSetSelectedMappinCore)
|
|
{
|
|
gOriginalSetSelectedMappinCore(aThis, aMappinHandle);
|
|
}
|
|
}
|
|
|
|
void DetourTrackCustomPositionMappinWrapper(void* aThis, void* aStack, void* aResult)
|
|
{
|
|
if (gTrackCustomPositionMappinWrapperLogCount < 64)
|
|
{
|
|
LogScriptNativeCall("TrackCustomPositionMappinWrapper", gTrackCustomPositionMappinWrapperLogCount, aThis,
|
|
aStack, aResult);
|
|
++gTrackCustomPositionMappinWrapperLogCount;
|
|
}
|
|
|
|
InspectAndHookMappinSystem(aThis);
|
|
|
|
if (gOriginalTrackCustomPositionMappinWrapper)
|
|
{
|
|
gOriginalTrackCustomPositionMappinWrapper(aThis, aStack, aResult);
|
|
}
|
|
}
|
|
|
|
void DetourTrackCustomPositionMappinCore(void* aThis)
|
|
{
|
|
if (gTrackCustomPositionMappinCoreLogCount < 64)
|
|
{
|
|
LogTrackCustomPositionMappinCoreCall(gTrackCustomPositionMappinCoreLogCount, aThis);
|
|
++gTrackCustomPositionMappinCoreLogCount;
|
|
}
|
|
|
|
InspectAndHookMappinSystem(aThis);
|
|
|
|
if (gOriginalTrackCustomPositionMappinCore)
|
|
{
|
|
gOriginalTrackCustomPositionMappinCore(aThis);
|
|
}
|
|
}
|
|
|
|
void DetourUntrackCustomPositionMappinWrapper(void* aThis, void* aStack, void* aResult)
|
|
{
|
|
if (gUntrackCustomPositionMappinWrapperLogCount < 64)
|
|
{
|
|
LogScriptNativeCall("UntrackCustomPositionMappinWrapper", gUntrackCustomPositionMappinWrapperLogCount, aThis,
|
|
aStack, aResult);
|
|
++gUntrackCustomPositionMappinWrapperLogCount;
|
|
}
|
|
|
|
InspectAndHookMappinSystem(aThis);
|
|
|
|
if (gOriginalUntrackCustomPositionMappinWrapper)
|
|
{
|
|
gOriginalUntrackCustomPositionMappinWrapper(aThis, aStack, aResult);
|
|
}
|
|
}
|
|
|
|
void DetourTrackMappinWrapper(void* aThis, void* aStack, void* aResult)
|
|
{
|
|
if (gTrackMappinWrapperLogCount < 64)
|
|
{
|
|
LogScriptNativeCall("TrackMappinWrapper", gTrackMappinWrapperLogCount, aThis, aStack, aResult);
|
|
++gTrackMappinWrapperLogCount;
|
|
}
|
|
|
|
if (gOriginalTrackMappinWrapper)
|
|
{
|
|
gOriginalTrackMappinWrapper(aThis, aStack, aResult);
|
|
}
|
|
}
|
|
|
|
void DetourTrackMappinCore(void* aThis, void* aMappinHandle)
|
|
{
|
|
if (gTrackMappinCoreLogCount < 64)
|
|
{
|
|
LogTrackMappinCoreCall(gTrackMappinCoreLogCount, aThis, aMappinHandle);
|
|
++gTrackMappinCoreLogCount;
|
|
}
|
|
|
|
InspectAndHookMappinSystem(aThis);
|
|
|
|
if (gOriginalTrackMappinCore)
|
|
{
|
|
gOriginalTrackMappinCore(aThis, aMappinHandle);
|
|
}
|
|
}
|
|
|
|
void DetourSetMappinTrackingAlternativeWrapper(void* aThis, void* aStack, void* aResult)
|
|
{
|
|
if (gSetMappinTrackingAlternativeLogCount < 64)
|
|
{
|
|
LogScriptNativeCall("SetMappinTrackingAlternativeWrapper", gSetMappinTrackingAlternativeLogCount, aThis,
|
|
aStack, aResult);
|
|
++gSetMappinTrackingAlternativeLogCount;
|
|
}
|
|
|
|
if (gOriginalSetMappinTrackingAlternativeWrapper)
|
|
{
|
|
gOriginalSetMappinTrackingAlternativeWrapper(aThis, aStack, aResult);
|
|
}
|
|
}
|
|
|
|
void DetourMappinUpdateCustomPosition(void* aSystem, void* aHandle, void* aPositionData)
|
|
{
|
|
if (gMappinUpdateCustomPositionLogCount < 32)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook MappinSystem::UpdateCustomPosition slot1f0 call=" << gMappinUpdateCustomPositionLogCount;
|
|
AppendPointerWithRva(line, "system", aSystem);
|
|
AppendPointerWithRva(line, "handle", aHandle);
|
|
AppendPointerWithRva(line, "positionData", aPositionData);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
++gMappinUpdateCustomPositionLogCount;
|
|
}
|
|
|
|
if (gOriginalMappinUpdateCustomPosition)
|
|
{
|
|
gOriginalMappinUpdateCustomPosition(aSystem, aHandle, aPositionData);
|
|
}
|
|
}
|
|
|
|
void DetourMappinSetTracked(void* aSystem, void* aMappinId)
|
|
{
|
|
if (gMappinSetTrackedLogCount < 64)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook MappinSystem::SetTracked slot220 call=" << gMappinSetTrackedLogCount;
|
|
AppendPointerWithRva(line, "system", aSystem);
|
|
AppendPointerWithRva(line, "mappinId", aMappinId);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
++gMappinSetTrackedLogCount;
|
|
}
|
|
|
|
if (gOriginalMappinSetTracked)
|
|
{
|
|
gOriginalMappinSetTracked(aSystem, aMappinId);
|
|
}
|
|
}
|
|
|
|
void DetourMappinClearTracked(void* aSystem)
|
|
{
|
|
if (gMappinClearTrackedLogCount < 64)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook MappinSystem::ClearTracked slot228 call=" << gMappinClearTrackedLogCount;
|
|
AppendPointerWithRva(line, "system", aSystem);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
++gMappinClearTrackedLogCount;
|
|
}
|
|
|
|
if (gOriginalMappinClearTracked)
|
|
{
|
|
gOriginalMappinClearTracked(aSystem);
|
|
}
|
|
}
|
|
|
|
void DetourMappinFramePath(void* aSystem, uint32_t aPathKind, void* aResult)
|
|
{
|
|
if (gMappinFramePathLogCount < 32)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook MappinSystem::FramePath slot280 call=" << gMappinFramePathLogCount
|
|
<< " pathKind=" << aPathKind;
|
|
AppendPointerWithRva(line, "system", aSystem);
|
|
AppendPointerWithRva(line, "result", aResult);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
++gMappinFramePathLogCount;
|
|
}
|
|
|
|
if (gOriginalMappinFramePath)
|
|
{
|
|
gOriginalMappinFramePath(aSystem, aPathKind, aResult);
|
|
}
|
|
}
|
|
|
|
void* DetourMappinPositionQuery(void* aSystem, void* a2, void* a3, void* a4, void* a5)
|
|
{
|
|
const auto shouldLog = gMappinPositionQueryLogCount < 32;
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook MappinSystem::PositionQuery slot2e0 call=" << gMappinPositionQueryLogCount;
|
|
AppendPointerWithRva(line, "system", aSystem);
|
|
AppendPointerWithRva(line, "a2", a2);
|
|
AppendPointerWithRva(line, "a3", a3);
|
|
AppendPointerWithRva(line, "a4", a4);
|
|
AppendPointerWithRva(line, "a5", a5);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
++gMappinPositionQueryLogCount;
|
|
}
|
|
|
|
void* result = nullptr;
|
|
if (gOriginalMappinPositionQuery)
|
|
{
|
|
result = gOriginalMappinPositionQuery(aSystem, a2, a3, a4, a5);
|
|
}
|
|
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook MappinSystem::PositionQuery slot2e0 result";
|
|
AppendPointerWithRva(line, "value", result);
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void* DetourMappinCreateCustomPosition(void* aSystem, void* aName, void* aParams, void* aPositionData)
|
|
{
|
|
const auto shouldLog = gMappinCreateCustomPositionLogCount < 32;
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook MappinSystem::CreateCustomPosition slot2f0 call=" << gMappinCreateCustomPositionLogCount;
|
|
AppendPointerWithRva(line, "system", aSystem);
|
|
AppendPointerWithRva(line, "name", aName);
|
|
AppendPointerWithRva(line, "params", aParams);
|
|
AppendPointerWithRva(line, "positionData", aPositionData);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
++gMappinCreateCustomPositionLogCount;
|
|
}
|
|
|
|
void* result = nullptr;
|
|
if (gOriginalMappinCreateCustomPosition)
|
|
{
|
|
result = gOriginalMappinCreateCustomPosition(aSystem, aName, aParams, aPositionData);
|
|
}
|
|
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook MappinSystem::CreateCustomPosition slot2f0 result";
|
|
AppendPointerWithRva(line, "value", result);
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void DetourMappinReleaseQuery(void* aSystem, void* aHandle)
|
|
{
|
|
if (gMappinReleaseQueryLogCount < 32)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook MappinSystem::ReleaseQuery slot368 call=" << gMappinReleaseQueryLogCount;
|
|
AppendPointerWithRva(line, "system", aSystem);
|
|
AppendPointerWithRva(line, "handle", aHandle);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
++gMappinReleaseQueryLogCount;
|
|
}
|
|
|
|
if (gOriginalMappinReleaseQuery)
|
|
{
|
|
gOriginalMappinReleaseQuery(aSystem, aHandle);
|
|
}
|
|
}
|
|
|
|
void* DetourMappinActiveRoute(void* aSystem)
|
|
{
|
|
const auto shouldLog = gMappinActiveRouteLogCount < 32;
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook MappinSystem::ActiveRoute slot3a8 call=" << gMappinActiveRouteLogCount;
|
|
AppendPointerWithRva(line, "system", aSystem);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
++gMappinActiveRouteLogCount;
|
|
}
|
|
|
|
void* result = nullptr;
|
|
if (gOriginalMappinActiveRoute)
|
|
{
|
|
result = gOriginalMappinActiveRoute(aSystem);
|
|
}
|
|
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook MappinSystem::ActiveRoute slot3a8 result";
|
|
AppendPointerWithRva(line, "value", result);
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool DetourRunGpsQueryBody(void* aThis, void* aStart, void* aEnd, int32_t* aOutQueryId)
|
|
{
|
|
constexpr uint32_t kMaxCalls = 96;
|
|
|
|
const auto callCount = gRunGpsQueryBodyLogCount;
|
|
const auto shouldLog = callCount < kMaxCalls;
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook RunGPSQuery body 0x29bd128 call=" << callCount;
|
|
AppendPointerWithRva(line, "this", aThis);
|
|
AppendPointerWithRva(line, "outQueryId", aOutQueryId);
|
|
AppendVector4Pointer(line, "start", aStart);
|
|
AppendVector4Pointer(line, "end", aEnd);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
++gRunGpsQueryBodyLogCount;
|
|
}
|
|
|
|
bool result = false;
|
|
if (gOriginalRunGpsQueryBody)
|
|
{
|
|
result = gOriginalRunGpsQueryBody(aThis, aStart, aEnd, aOutQueryId);
|
|
}
|
|
|
|
if (shouldLog)
|
|
{
|
|
int32_t queryId = 0;
|
|
const auto hasQueryId = aOutQueryId && TryReadMemory(aOutQueryId, queryId);
|
|
|
|
std::ostringstream line;
|
|
line << "hook RunGPSQuery body result call=" << callCount << " value=" << static_cast<uint32_t>(result)
|
|
<< " hasQueryId=" << hasQueryId;
|
|
if (hasQueryId)
|
|
{
|
|
line << " queryId=" << queryId;
|
|
}
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool DetourUpdateGpsQueryBody(void* aThis, uint32_t aQueryId, void* aOutPoints, void* aOutError)
|
|
{
|
|
constexpr uint32_t kMaxCalls = 128;
|
|
|
|
const auto callCount = gUpdateGpsQueryBodyLogCount;
|
|
const auto shouldLog = callCount < kMaxCalls;
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook UpdateGPSQuery body 0x29bd254 call=" << callCount << " queryId=" << aQueryId;
|
|
AppendPointerWithRva(line, "this", aThis);
|
|
AppendPointerWithRva(line, "outPoints", aOutPoints);
|
|
AppendPointerWithRva(line, "outError", aOutError);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
++gUpdateGpsQueryBodyLogCount;
|
|
}
|
|
|
|
bool result = false;
|
|
if (gOriginalUpdateGpsQueryBody)
|
|
{
|
|
result = gOriginalUpdateGpsQueryBody(aThis, aQueryId, aOutPoints, aOutError);
|
|
}
|
|
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook UpdateGPSQuery body result call=" << callCount << " value=" << static_cast<uint32_t>(result);
|
|
AppendPathBufferSummary(line, "cachedPath", ReadPointerField(aThis, 0x58));
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int32_t DetourGpsQuerySubmit(void* aManager, void* aQuery)
|
|
{
|
|
constexpr uint32_t kMaxCalls = 128;
|
|
|
|
const auto returnAddress = __builtin_return_address(0);
|
|
uintptr_t returnRva = 0;
|
|
TryGetImageRva(returnAddress, returnRva);
|
|
|
|
const auto callCount = gGpsQuerySubmitLogCount;
|
|
const auto shouldLog = callCount < kMaxCalls;
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSQuerySubmit 0x70a42c call=" << callCount;
|
|
AppendPointerWithRva(line, "manager", aManager);
|
|
AppendPointerWithRva(line, "query", aQuery);
|
|
AppendUint32Field(line, "query_f00", aQuery, 0x00);
|
|
AppendUint32Field(line, "query_f04", aQuery, 0x04);
|
|
AppendUint32Field(line, "query_f08", aQuery, 0x08);
|
|
AppendUint32Field(line, "query_f0c", aQuery, 0x0C);
|
|
AppendUint32Field(line, "query_fcc", aQuery, 0xCC);
|
|
AppendVector4Field(line, "query_pos58", aQuery, 0x58);
|
|
AppendReturnRva(line, returnAddress);
|
|
LogRed4ext(line.str());
|
|
++gGpsQuerySubmitLogCount;
|
|
}
|
|
|
|
int32_t result = -1;
|
|
if (gOriginalGpsQuerySubmit)
|
|
{
|
|
result = gOriginalGpsQuerySubmit(aManager, aQuery);
|
|
}
|
|
|
|
if (result >= 0)
|
|
{
|
|
TrackGpsQuery(static_cast<uint32_t>(result), aManager, returnRva, callCount);
|
|
}
|
|
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSQuerySubmit result call=" << callCount << " queryId=" << result;
|
|
AppendUint32Field(line, "manager_nextIdD4", aManager, 0xD4);
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void DetourGpsQueryDispatch(void* aRuntimeQuery, void* aTargetPosition, void* aArg3, void* aArg4)
|
|
{
|
|
constexpr uint32_t kMaxCalls = 128;
|
|
|
|
if (gGpsQueryDispatchLogCount < kMaxCalls)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSQueryDispatch 0x70a570 call=" << gGpsQueryDispatchLogCount;
|
|
AppendPointerWithRva(line, "runtimeQuery", aRuntimeQuery);
|
|
AppendPointerWithRva(line, "arg3", aArg3);
|
|
AppendPointerWithRva(line, "arg4", aArg4);
|
|
AppendUint32Field(line, "runtimeQuery_state138", aRuntimeQuery, 0x138);
|
|
AppendUint32Field(line, "runtimeQuery_bank13c", aRuntimeQuery, 0x13C);
|
|
AppendVector4Pointer(line, "target", aTargetPosition);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
++gGpsQueryDispatchLogCount;
|
|
}
|
|
|
|
if (gOriginalGpsQueryDispatch)
|
|
{
|
|
gOriginalGpsQueryDispatch(aRuntimeQuery, aTargetPosition, aArg3, aArg4);
|
|
}
|
|
}
|
|
|
|
bool DetourGpsQueryResultFetch(void* aManager, uint32_t aQueryId, void* aOutPath)
|
|
{
|
|
constexpr uint32_t kMaxUntrackedSamples = 32;
|
|
constexpr uint32_t kMaxTrackedSamplesPerQuery = 24;
|
|
|
|
const auto callCount = gGpsQueryResultFetchLogCount++;
|
|
const auto returnAddress = __builtin_return_address(0);
|
|
|
|
bool result = false;
|
|
if (gOriginalGpsQueryResultFetch)
|
|
{
|
|
result = gOriginalGpsQueryResultFetch(aManager, aQueryId, aOutPath);
|
|
}
|
|
|
|
TrackedGpsQuery trackedQuery{};
|
|
uint32_t perQueryCall = 0;
|
|
const auto isTracked = NoteTrackedGpsQueryResultFetch(aQueryId, aManager, trackedQuery, perQueryCall);
|
|
const auto shouldLog = (isTracked && (perQueryCall < kMaxTrackedSamplesPerQuery || result)) ||
|
|
(!isTracked && callCount < kMaxUntrackedSamples && aQueryId < 2);
|
|
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSQueryResultFetch 0x7094b8 call=" << callCount << " queryId=" << aQueryId
|
|
<< " tracked=" << static_cast<uint32_t>(isTracked) << " perQueryCall=" << perQueryCall
|
|
<< " value=" << static_cast<uint32_t>(result);
|
|
if (isTracked)
|
|
{
|
|
AppendTrackedGpsQuery(line, trackedQuery);
|
|
}
|
|
AppendPointerWithRva(line, "manager", aManager);
|
|
AppendPointerWithRva(line, "outPath", aOutPath);
|
|
AppendReturnRva(line, returnAddress);
|
|
AppendPathBufferSummary(line, "outPath", aOutPath);
|
|
if (result && isTracked && trackedQuery.submitReturnRva == 0x8D20D4)
|
|
{
|
|
AppendGpsResultObjectSummary(line, "gpsResult", aOutPath);
|
|
}
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int32_t DetourGpsQueryStatus(void* aManager, uint32_t aQueryId)
|
|
{
|
|
constexpr uint32_t kMaxUntrackedSamples = 32;
|
|
constexpr uint32_t kMaxTrackedSamplesPerQuery = 24;
|
|
|
|
const auto callCount = gGpsQueryStatusLogCount++;
|
|
const auto returnAddress = __builtin_return_address(0);
|
|
|
|
int32_t result = 0;
|
|
if (gOriginalGpsQueryStatus)
|
|
{
|
|
result = gOriginalGpsQueryStatus(aManager, aQueryId);
|
|
}
|
|
|
|
TrackedGpsQuery trackedQuery{};
|
|
uint32_t perQueryCall = 0;
|
|
const auto isTracked = NoteTrackedGpsQueryStatus(aQueryId, aManager, trackedQuery, perQueryCall);
|
|
const auto shouldLog = (isTracked && (perQueryCall < kMaxTrackedSamplesPerQuery || result != 0)) ||
|
|
(!isTracked && callCount < kMaxUntrackedSamples && aQueryId < 2);
|
|
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSQueryStatus 0xaa5704 call=" << callCount << " queryId=" << aQueryId
|
|
<< " tracked=" << static_cast<uint32_t>(isTracked) << " perQueryCall=" << perQueryCall
|
|
<< " value=" << result;
|
|
if (isTracked)
|
|
{
|
|
AppendTrackedGpsQuery(line, trackedQuery);
|
|
}
|
|
AppendPointerWithRva(line, "manager", aManager);
|
|
AppendReturnRva(line, returnAddress);
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void DetourGpsDispatchAdvance(void* aRouteSystem, void* aRuntimeQuery, void* aOutTicket, void* aTargetPosition,
|
|
void* aSourcePosition)
|
|
{
|
|
constexpr uint32_t kMaxCalls = 128;
|
|
|
|
const auto callCount = NextGpsAsyncLogCall(gGpsDispatchAdvanceLogCount);
|
|
const auto shouldLog = callCount < kMaxCalls;
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSDispatchAdvance 0x8d17d8 call=" << callCount;
|
|
AppendRouteSystemSummary(line, "routeSystem", aRouteSystem);
|
|
AppendPointerWithRva(line, "runtimeQuery", aRuntimeQuery);
|
|
AppendPointerWithRva(line, "outTicket", aOutTicket);
|
|
AppendUint32Field(line, "runtimeQuery_state138", aRuntimeQuery, 0x138);
|
|
AppendUint32Field(line, "runtimeQuery_bank13c", aRuntimeQuery, 0x13C);
|
|
AppendVector4Pointer(line, "target", aTargetPosition);
|
|
AppendVector4Pointer(line, "source", aSourcePosition);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
if (gOriginalGpsDispatchAdvance)
|
|
{
|
|
gOriginalGpsDispatchAdvance(aRouteSystem, aRuntimeQuery, aOutTicket, aTargetPosition, aSourcePosition);
|
|
}
|
|
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSDispatchAdvance result call=" << callCount;
|
|
AppendUint32Field(line, "outTicket_value", aOutTicket, 0x00);
|
|
AppendRouteSystemSummary(line, "routeSystem", aRouteSystem);
|
|
LogRed4ext(line.str());
|
|
}
|
|
}
|
|
|
|
void DetourGpsAsyncWorkerTick(void* aRouteSystem)
|
|
{
|
|
constexpr uint32_t kMaxCalls = 192;
|
|
|
|
const auto callCount = NextGpsAsyncLogCall(gGpsAsyncWorkerTickLogCount);
|
|
const auto shouldLogBefore = callCount < 16 || (callCount < kMaxCalls && HasPendingRouteWork(aRouteSystem));
|
|
if (shouldLogBefore)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSAsyncWorkerTick 0x126b28 call=" << callCount;
|
|
AppendRouteSystemSummary(line, "routeSystem", aRouteSystem);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
if (gOriginalGpsAsyncWorkerTick)
|
|
{
|
|
gOriginalGpsAsyncWorkerTick(aRouteSystem);
|
|
}
|
|
|
|
const auto shouldLogAfter = shouldLogBefore || (callCount < kMaxCalls && HasPendingRouteWork(aRouteSystem));
|
|
if (shouldLogAfter)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSAsyncWorkerTick result call=" << callCount;
|
|
AppendRouteSystemSummary(line, "routeSystem", aRouteSystem);
|
|
LogRed4ext(line.str());
|
|
}
|
|
}
|
|
|
|
bool DetourGpsRouteJobStart(void* aRouteSystem, void* aActiveEntry, float aDelta, void* aOutLocal)
|
|
{
|
|
constexpr uint32_t kMaxCalls = 128;
|
|
|
|
const auto callCount = NextGpsAsyncLogCall(gGpsRouteJobStartLogCount);
|
|
const auto shouldLog = kEnableGpsRouteJobStartTrace && callCount < kMaxCalls;
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSRouteJobStart 0x818928 call=" << callCount << " dt=" << aDelta;
|
|
AppendRouteSystemSummary(line, "routeSystem", aRouteSystem);
|
|
AppendRouteQueueEntrySummary(line, "activeEntry", aActiveEntry);
|
|
AppendRouteOutputPointerSummary(line, "outLocal", aOutLocal);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
bool result = false;
|
|
if (gOriginalGpsRouteJobStart)
|
|
{
|
|
if (kEnableGpsCostTablePatch && IsDrivingRouteJob(aRouteSystem, aActiveEntry))
|
|
{
|
|
std::lock_guard patchLock(gGpsCostTablePatchMutex);
|
|
ScopedGpsCostTablePatch patch(true, callCount);
|
|
result = gOriginalGpsRouteJobStart(aRouteSystem, aActiveEntry, aDelta, aOutLocal);
|
|
}
|
|
else
|
|
{
|
|
result = gOriginalGpsRouteJobStart(aRouteSystem, aActiveEntry, aDelta, aOutLocal);
|
|
}
|
|
}
|
|
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSRouteJobStart result call=" << callCount << " value=" << static_cast<uint32_t>(result);
|
|
AppendRouteSystemSummary(line, "routeSystem", aRouteSystem);
|
|
AppendRouteQueueEntrySummary(line, "activeEntry", aActiveEntry);
|
|
AppendRouteOutputPointerSummary(line, "outLocal", aOutLocal);
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool DetourGpsRouteJobPoll(void* aRouteSystem, void* aActiveEntry)
|
|
{
|
|
constexpr uint32_t kMaxCalls = 128;
|
|
|
|
const auto callCount = NextGpsAsyncLogCall(gGpsRouteJobPollLogCount);
|
|
const auto shouldLog = callCount < kMaxCalls;
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSRouteJobPoll 0x883cd8 call=" << callCount;
|
|
AppendRouteSystemSummary(line, "routeSystem", aRouteSystem);
|
|
AppendRouteQueueEntrySummary(line, "activeEntry", aActiveEntry);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
bool result = false;
|
|
if (gOriginalGpsRouteJobPoll)
|
|
{
|
|
result = gOriginalGpsRouteJobPoll(aRouteSystem, aActiveEntry);
|
|
}
|
|
|
|
if (shouldLog || result)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSRouteJobPoll result call=" << callCount << " value=" << static_cast<uint32_t>(result);
|
|
AppendRouteSystemSummary(line, "routeSystem", aRouteSystem);
|
|
AppendRouteQueueEntrySummary(line, "activeEntry", aActiveEntry);
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void* DetourGpsRouteFinish(void* aResultQueue, void* aOutEntry, void* aActiveEntry, void* aLocalResult)
|
|
{
|
|
constexpr uint32_t kMaxCalls = 128;
|
|
|
|
const auto callCount = NextGpsAsyncLogCall(gGpsRouteFinishLogCount);
|
|
const auto shouldLog = callCount < kMaxCalls;
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSRouteFinish 0x11f5f60 call=" << callCount;
|
|
AppendPointerWithRva(line, "resultQueue", aResultQueue);
|
|
AppendRouteQueueEntrySummary(line, "activeEntry", aActiveEntry);
|
|
AppendRouteOutputPointerSummary(line, "localResult", aLocalResult);
|
|
AppendPointerWithRva(line, "outEntry", aOutEntry);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
void* result = nullptr;
|
|
if (gOriginalGpsRouteFinish)
|
|
{
|
|
result = gOriginalGpsRouteFinish(aResultQueue, aOutEntry, aActiveEntry, aLocalResult);
|
|
}
|
|
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSRouteFinish result call=" << callCount;
|
|
AppendPointerWithRva(line, "value", result);
|
|
AppendRouteOutputPointerSummary(line, "localResult", aLocalResult);
|
|
AppendPointerWithRva(line, "outEntry", aOutEntry);
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void* DetourGpsRouteJobBuild(void* aJob, void* aOut, void* aRequest)
|
|
{
|
|
constexpr uint32_t kMaxCalls = 128;
|
|
|
|
const auto callCount = NextGpsAsyncLogCall(gGpsRouteJobBuildLogCount);
|
|
const auto shouldLog = callCount < kMaxCalls;
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSRouteJobBuild 0x818ba8 call=" << callCount;
|
|
AppendRouteJobSummary(line, "job", aJob);
|
|
AppendRouteOutputPointerSummary(line, "out", aOut);
|
|
AppendRouteRequestFields(line, "request", aRequest, 0x00);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
void* result = nullptr;
|
|
if (gOriginalGpsRouteJobBuild)
|
|
{
|
|
result = gOriginalGpsRouteJobBuild(aJob, aOut, aRequest);
|
|
}
|
|
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSRouteJobBuild result call=" << callCount;
|
|
AppendPointerWithRva(line, "value", result);
|
|
AppendRouteOutputPointerSummary(line, "out", aOut);
|
|
AppendRouteJobSummary(line, "job", aJob);
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int32_t DetourGpsRouteProducer(void* aGraph, void* aQuery, void* aOutResult)
|
|
{
|
|
constexpr uint32_t kMaxCalls = 512;
|
|
|
|
uint32_t callCount = 0;
|
|
bool shouldLog = false;
|
|
{
|
|
std::lock_guard lock(gGpsCostLogMutex);
|
|
callCount = gGpsRouteProducerLogCount++;
|
|
shouldLog = callCount < kMaxCalls;
|
|
}
|
|
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSRouteProducer 0x44cc7c call=" << callCount;
|
|
AppendPointerWithRva(line, "graph", aGraph);
|
|
AppendPointerWithRva(line, "query", aQuery);
|
|
AppendPointerWithRva(line, "outResult", aOutResult);
|
|
AppendGpsRouteProducerQuerySummary(line, aQuery);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
GpsEdgeCostStats stats{};
|
|
auto* previousStats = gActiveGpsEdgeCostStats;
|
|
const auto previousRouteProducerCall = gActiveGpsRouteProducerCall;
|
|
gActiveGpsEdgeCostStats = &stats;
|
|
gActiveGpsRouteProducerCall = callCount;
|
|
|
|
int32_t result = 0x80000008;
|
|
if (gOriginalGpsRouteProducer)
|
|
{
|
|
result = gOriginalGpsRouteProducer(aGraph, aQuery, aOutResult);
|
|
}
|
|
|
|
gActiveGpsEdgeCostStats = previousStats;
|
|
gActiveGpsRouteProducerCall = previousRouteProducerCall;
|
|
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSRouteProducer result call=" << callCount << " value=0x" << std::hex
|
|
<< static_cast<uint32_t>(result) << std::dec;
|
|
AppendUint32Field(line, "out_u0c", aOutResult, 0x0C);
|
|
AppendUint32Field(line, "out_u1c", aOutResult, 0x1C);
|
|
AppendUint32Field(line, "out_u20", aOutResult, 0x20);
|
|
AppendUint32Field(line, "out_u24", aOutResult, 0x24);
|
|
AppendUint32Field(line, "out_u2c", aOutResult, 0x2C);
|
|
AppendUint32Field(line, "out_u30lo", aOutResult, 0x30);
|
|
AppendUint32Field(line, "out_u34hi", aOutResult, 0x34);
|
|
AppendUint16Field(line, "out_flags40", aOutResult, 0x40);
|
|
AppendByteField(line, "out_state42", aOutResult, 0x42);
|
|
AppendGpsEdgeCostStats(line, stats);
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int32_t DetourGpsSearch(void* aGraph, uint64_t aStartHandle, uint64_t aTargetHandle, void* aStartPoint,
|
|
void* aTargetPoint, void* aCostProvider, void* aOutPath, uint32_t* aOutCount)
|
|
{
|
|
constexpr uint32_t kMaxCalls = 384;
|
|
|
|
uint32_t callCount = 0;
|
|
bool shouldLog = false;
|
|
{
|
|
std::lock_guard lock(gGpsCostLogMutex);
|
|
callCount = gGpsSearchLogCount++;
|
|
shouldLog = kEnableGpsTraceHooks && callCount < kMaxCalls;
|
|
}
|
|
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook GPSSearch 0x44f054 call=" << callCount << " startHandle=0x" << std::hex << aStartHandle
|
|
<< " targetHandle=0x" << aTargetHandle << std::dec;
|
|
AppendPointerWithRva(line, "graph", aGraph);
|
|
AppendVector3Pointer(line, "startPoint", aStartPoint);
|
|
AppendVector3Pointer(line, "targetPoint", aTargetPoint);
|
|
AppendPointerWithRva(line, "outPath", aOutPath);
|
|
AppendPointerWithRva(line, "outCount", aOutCount);
|
|
AppendGpsCostProviderCompact(line, "provider", aCostProvider);
|
|
if (gActiveGpsRouteProducerCall != UINT32_MAX)
|
|
{
|
|
line << " routeProducerCall=" << gActiveGpsRouteProducerCall;
|
|
}
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
int32_t result = 0x80000008;
|
|
if (gOriginalGpsSearch)
|
|
{
|
|
ScopedGpsProviderClassPatch providerPatch(aCostProvider, callCount);
|
|
result = gOriginalGpsSearch(aGraph, aStartHandle, aTargetHandle, aStartPoint, aTargetPoint, aCostProvider,
|
|
aOutPath, aOutCount);
|
|
}
|
|
|
|
if (shouldLog)
|
|
{
|
|
uint32_t outCount = 0;
|
|
const auto hasOutCount = aOutCount && TryReadMemory(aOutCount, outCount);
|
|
|
|
std::ostringstream line;
|
|
line << "hook GPSSearch result call=" << callCount << " value=0x" << std::hex
|
|
<< static_cast<uint32_t>(result) << std::dec << " hasOutCount=" << static_cast<uint32_t>(hasOutCount);
|
|
if (hasOutCount)
|
|
{
|
|
line << " outCount=" << outCount;
|
|
}
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
float DetourGpsEdgeCost(void* aCostProvider, void* aCurrentState, void* aNeighborState, uint64_t aPreviousHandle,
|
|
void* aPreviousSegment, void* aPreviousPoint, uint64_t aCurrentHandle, void* aCurrentSegment,
|
|
void* aCurrentPoint, uint64_t aNeighborHandle, void* aNeighborSegment, void* aNeighborPoint)
|
|
{
|
|
const auto returnAddress = __builtin_return_address(0);
|
|
uintptr_t returnRva = 0;
|
|
TryGetImageRva(returnAddress, returnRva);
|
|
|
|
float result = 0.0f;
|
|
if (gOriginalGpsEdgeCost)
|
|
{
|
|
result = gOriginalGpsEdgeCost(aCostProvider, aCurrentState, aNeighborState, aPreviousHandle, aPreviousSegment,
|
|
aPreviousPoint, aCurrentHandle, aCurrentSegment, aCurrentPoint, aNeighborHandle,
|
|
aNeighborSegment, aNeighborPoint);
|
|
}
|
|
|
|
const auto sourceClass = ReadGpsPointClass(aCurrentPoint);
|
|
const auto neighborClass = ReadGpsPointClass(aNeighborPoint);
|
|
|
|
if (gActiveGpsEdgeCostStats)
|
|
{
|
|
NoteGpsEdgeCostStats(*gActiveGpsEdgeCostStats, sourceClass, neighborClass, result, returnRva, aCurrentHandle,
|
|
aNeighborHandle);
|
|
}
|
|
|
|
float multiplier = 0.0f;
|
|
const auto hasMultiplier = sourceClass < 64 &&
|
|
TryReadFloatField(aCostProvider, 0x08 + static_cast<uintptr_t>(sourceClass) * sizeof(float),
|
|
multiplier);
|
|
|
|
uint32_t callCount = 0;
|
|
uint32_t classCallCount = 0;
|
|
bool shouldLog = false;
|
|
{
|
|
std::lock_guard lock(gGpsCostLogMutex);
|
|
callCount = gGpsEdgeCostLogCount++;
|
|
if (sourceClass < gGpsEdgeCostClassLogCounts.size())
|
|
{
|
|
classCallCount = gGpsEdgeCostClassLogCounts[sourceClass]++;
|
|
}
|
|
shouldLog = callCount < 32 || (sourceClass < 64 && classCallCount < 1);
|
|
}
|
|
|
|
if (shouldLog)
|
|
{
|
|
const auto baseCost = hasMultiplier && multiplier != 0.0f ? result / multiplier : 0.0f;
|
|
|
|
std::ostringstream line;
|
|
line << "hook GPSEdgeCost 0x44f838 call=" << callCount << " classCall=" << classCallCount
|
|
<< " sourceClass=" << static_cast<uint32_t>(sourceClass)
|
|
<< " neighborClass=" << static_cast<uint32_t>(neighborClass) << " value=" << result;
|
|
if (hasMultiplier)
|
|
{
|
|
line << " multiplier=" << multiplier << " baseCost=" << baseCost;
|
|
}
|
|
else
|
|
{
|
|
line << " multiplier=<unreadable>";
|
|
}
|
|
line << " previousHandle=0x" << std::hex << aPreviousHandle << " currentHandle=0x" << aCurrentHandle
|
|
<< " neighborHandle=0x" << aNeighborHandle << std::dec;
|
|
AppendPointerWithRva(line, "provider", aCostProvider);
|
|
AppendVector3Pointer(line, "currentState", aCurrentState);
|
|
AppendVector3Pointer(line, "neighborState", aNeighborState);
|
|
AppendPointerWithRva(line, "previousSegment", aPreviousSegment);
|
|
AppendGpsPointSummary(line, "previousPoint", aPreviousPoint);
|
|
AppendPointerWithRva(line, "currentSegment", aCurrentSegment);
|
|
AppendGpsPointSummary(line, "currentPoint", aCurrentPoint);
|
|
AppendPointerWithRva(line, "neighborSegment", aNeighborSegment);
|
|
AppendGpsPointSummary(line, "neighborPoint", aNeighborPoint);
|
|
AppendReturnRva(line, returnAddress);
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
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)
|
|
{
|
|
LogRed4ext("hook attach skipped: RED4ext hooking API unavailable");
|
|
return;
|
|
}
|
|
|
|
auto* target = reinterpret_cast<void*>(GetImageBase() + aRva);
|
|
const auto attached = gSdk->hooking->Attach(gHandle, target, aDetour, aOriginal);
|
|
|
|
std::ostringstream line;
|
|
line << "hook attach " << aName << " target=" << target << " rva=0x" << std::hex << aRva << std::dec
|
|
<< " result=" << (attached ? "ok" : "failed") << " original=" << (aOriginal ? *aOriginal : nullptr);
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
void AttachAbsoluteHook(const char* aName, void* aTarget, void* aDetour, void** aOriginal)
|
|
{
|
|
if (!gSdk || !gSdk->hooking || !gSdk->hooking->Attach)
|
|
{
|
|
LogRed4ext("absolute hook attach skipped: RED4ext hooking API unavailable");
|
|
return;
|
|
}
|
|
|
|
if (!aTarget)
|
|
{
|
|
std::ostringstream line;
|
|
line << "absolute hook attach skipped " << aName << ": target=null";
|
|
LogRed4ext(line.str());
|
|
return;
|
|
}
|
|
|
|
const auto attached = gSdk->hooking->Attach(gHandle, aTarget, aDetour, aOriginal);
|
|
|
|
std::ostringstream line;
|
|
line << "absolute hook attach " << aName;
|
|
AppendPointerWithRva(line, "target", aTarget);
|
|
line << " result=" << (attached ? "ok" : "failed");
|
|
AppendPointerWithRva(line, "original", aOriginal ? *aOriginal : nullptr);
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
void DetachProbeHook(const char* aName, uintptr_t aRva)
|
|
{
|
|
if (!gSdk || !gSdk->hooking || !gSdk->hooking->Detach)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto* target = reinterpret_cast<void*>(GetImageBase() + aRva);
|
|
const auto detached = gSdk->hooking->Detach(gHandle, target);
|
|
|
|
std::ostringstream line;
|
|
line << "hook detach " << aName << " target=" << target << " result=" << (detached ? "ok" : "failed");
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
void DetachAbsoluteHook(const char* aName, void* aTarget)
|
|
{
|
|
if (!gSdk || !gSdk->hooking || !gSdk->hooking->Detach || !aTarget)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const auto detached = gSdk->hooking->Detach(gHandle, aTarget);
|
|
|
|
std::ostringstream line;
|
|
line << "absolute hook detach " << aName;
|
|
AppendPointerWithRva(line, "target", aTarget);
|
|
line << " result=" << (detached ? "ok" : "failed");
|
|
LogRed4ext(line.str());
|
|
}
|
|
|
|
void DetourObserver1ServiceRouteLookup(void* aService, void* aOut, uint32_t aRouteId)
|
|
{
|
|
const auto shouldLog = gObserver1ServiceRouteLookupLogCount < 96;
|
|
const auto callCount = gObserver1ServiceRouteLookupLogCount;
|
|
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook Observer1Service::RouteLookup slot220 call=" << callCount << " routeId=" << aRouteId
|
|
<< "/0x" << std::hex << aRouteId << std::dec;
|
|
AppendPointerWithRva(line, "service", aService);
|
|
AppendPointerWithRva(line, "out", aOut);
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
++gObserver1ServiceRouteLookupLogCount;
|
|
}
|
|
|
|
if (gOriginalObserver1ServiceRouteLookup)
|
|
{
|
|
gOriginalObserver1ServiceRouteLookup(aService, aOut, aRouteId);
|
|
}
|
|
|
|
if (shouldLog)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook Observer1Service::RouteLookup slot220 result call=" << callCount;
|
|
AppendPointerField(line, "out_f00", aOut, 0x00);
|
|
AppendPointerField(line, "out_f08", aOut, 0x08);
|
|
AppendPointerField(line, "out_f10", aOut, 0x10);
|
|
|
|
auto* routeData = ReadPointerField(aOut, 0x00);
|
|
if (routeData)
|
|
{
|
|
AppendPointerWithRva(line, "routeData", routeData);
|
|
AppendPointerField(line, "routeData_f40", routeData, 0x40);
|
|
AppendPointerField(line, "routeData_f48", routeData, 0x48);
|
|
AppendPointerField(line, "routeData_f50", routeData, 0x50);
|
|
AppendPointerField(line, "routeData_f58", routeData, 0x58);
|
|
}
|
|
|
|
LogRed4ext(line.str());
|
|
}
|
|
}
|
|
|
|
void MaybeAttachObserver1ServiceRouteLookup(void* aService)
|
|
{
|
|
if (gObserver1ServiceRouteLookupHookAttachAttempted || !aService)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto* vtable = ReadPointerField(aService, 0x00);
|
|
if (!IsInImage(vtable))
|
|
{
|
|
return;
|
|
}
|
|
|
|
gObserver1ServiceRouteLookupTarget =
|
|
ReadPointerField(vtable, kObserver1ServiceRouteLookupSlotOffset);
|
|
if (!gObserver1ServiceRouteLookupTarget)
|
|
{
|
|
return;
|
|
}
|
|
|
|
gObserver1ServiceRouteLookupHookAttachAttempted = true;
|
|
AttachAbsoluteHook("Observer1Service::RouteLookup slot220", gObserver1ServiceRouteLookupTarget,
|
|
reinterpret_cast<void*>(&DetourObserver1ServiceRouteLookup),
|
|
reinterpret_cast<void**>(&gOriginalObserver1ServiceRouteLookup));
|
|
}
|
|
|
|
void* DetourObserver1ServiceLookup(void* aServiceOwner, void* aSystemType)
|
|
{
|
|
void* result = nullptr;
|
|
if (gOriginalObserver1ServiceLookup)
|
|
{
|
|
result = gOriginalObserver1ServiceLookup(aServiceOwner, aSystemType);
|
|
}
|
|
|
|
auto* observer1SystemType = GetObserver1SystemType();
|
|
if (!observer1SystemType || aSystemType != observer1SystemType)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (gObserver1ServiceLookupLogCount < 96)
|
|
{
|
|
std::ostringstream line;
|
|
line << "hook Observer1ServiceLookup 0x287c44 call=" << gObserver1ServiceLookupLogCount;
|
|
AppendPointerWithRva(line, "serviceOwner", aServiceOwner);
|
|
AppendPointerWithRva(line, "systemType", aSystemType);
|
|
AppendPointerWithRva(line, "result", result);
|
|
AppendObjectVtableSlots(line, result, {0x218, 0x220, 0x228, 0x230, 0x238, 0x240});
|
|
AppendReturnRva(line, __builtin_return_address(0));
|
|
LogRed4ext(line.str());
|
|
++gObserver1ServiceLookupLogCount;
|
|
}
|
|
|
|
MaybeAttachObserver1ServiceRouteLookup(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
void InspectAndHookMappinSystem(void* aThis)
|
|
{
|
|
auto* resolver = reinterpret_cast<MappinSystemResolverFunc>(GetImageBase() + kMappinSystemResolverRva);
|
|
void* system = resolver ? resolver(aThis) : nullptr;
|
|
auto* vtable = system ? *reinterpret_cast<void***>(system) : nullptr;
|
|
|
|
if (gMappinSystemSlotLogCount < 16)
|
|
{
|
|
LogMappinSystemSlots(gMappinSystemSlotLogCount, system, vtable);
|
|
++gMappinSystemSlotLogCount;
|
|
}
|
|
|
|
if (gMappinSlotHookAttachAttempted || !vtable)
|
|
{
|
|
return;
|
|
}
|
|
|
|
gMappinSlotHookAttachAttempted = true;
|
|
gMappinUpdateCustomPositionTarget =
|
|
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(vtable) + kMappinUpdateCustomPositionSlotOffset);
|
|
gMappinSetTrackedTarget =
|
|
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(vtable) + kMappinSetTrackedSlotOffset);
|
|
gMappinClearTrackedTarget =
|
|
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(vtable) + kMappinClearTrackedSlotOffset);
|
|
gMappinFramePathTarget =
|
|
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(vtable) + kMappinFramePathSlotOffset);
|
|
gMappinPositionQueryTarget =
|
|
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(vtable) + kMappinPositionQuerySlotOffset);
|
|
gMappinCreateCustomPositionTarget =
|
|
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(vtable) + kMappinCreateCustomPositionSlotOffset);
|
|
gMappinReleaseQueryTarget =
|
|
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(vtable) + kMappinReleaseQuerySlotOffset);
|
|
gMappinActiveRouteTarget =
|
|
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(vtable) + kMappinActiveRouteSlotOffset);
|
|
|
|
AttachAbsoluteHook("MappinSystem::UpdateCustomPosition slot1f0", gMappinUpdateCustomPositionTarget,
|
|
reinterpret_cast<void*>(&DetourMappinUpdateCustomPosition),
|
|
reinterpret_cast<void**>(&gOriginalMappinUpdateCustomPosition));
|
|
AttachAbsoluteHook("MappinSystem::SetTracked slot220", gMappinSetTrackedTarget,
|
|
reinterpret_cast<void*>(&DetourMappinSetTracked),
|
|
reinterpret_cast<void**>(&gOriginalMappinSetTracked));
|
|
AttachAbsoluteHook("MappinSystem::ClearTracked slot228", gMappinClearTrackedTarget,
|
|
reinterpret_cast<void*>(&DetourMappinClearTracked),
|
|
reinterpret_cast<void**>(&gOriginalMappinClearTracked));
|
|
AttachAbsoluteHook("MappinSystem::FramePath slot280", gMappinFramePathTarget,
|
|
reinterpret_cast<void*>(&DetourMappinFramePath),
|
|
reinterpret_cast<void**>(&gOriginalMappinFramePath));
|
|
AttachAbsoluteHook("MappinSystem::PositionQuery slot2e0", gMappinPositionQueryTarget,
|
|
reinterpret_cast<void*>(&DetourMappinPositionQuery),
|
|
reinterpret_cast<void**>(&gOriginalMappinPositionQuery));
|
|
AttachAbsoluteHook("MappinSystem::CreateCustomPosition slot2f0", gMappinCreateCustomPositionTarget,
|
|
reinterpret_cast<void*>(&DetourMappinCreateCustomPosition),
|
|
reinterpret_cast<void**>(&gOriginalMappinCreateCustomPosition));
|
|
AttachAbsoluteHook("MappinSystem::ReleaseQuery slot368", gMappinReleaseQueryTarget,
|
|
reinterpret_cast<void*>(&DetourMappinReleaseQuery),
|
|
reinterpret_cast<void**>(&gOriginalMappinReleaseQuery));
|
|
AttachAbsoluteHook("MappinSystem::ActiveRoute slot3a8", gMappinActiveRouteTarget,
|
|
reinterpret_cast<void*>(&DetourMappinActiveRoute),
|
|
reinterpret_cast<void**>(&gOriginalMappinActiveRoute));
|
|
}
|
|
|
|
bool OnRunningEnter(void* aApp)
|
|
{
|
|
std::ostringstream line;
|
|
line << "GameState Running OnEnter app=" << aApp;
|
|
LogRed4ext(line.str());
|
|
return true;
|
|
}
|
|
|
|
bool OnRunningUpdate(void* aApp)
|
|
{
|
|
if (gUpdateLogCount < 5)
|
|
{
|
|
std::ostringstream line;
|
|
line << "GameState Running OnUpdate app=" << aApp << " count=" << gUpdateLogCount;
|
|
LogRed4ext(line.str());
|
|
++gUpdateLogCount;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool OnRunningExit(void* aApp)
|
|
{
|
|
std::ostringstream line;
|
|
line << "GameState Running OnExit app=" << aApp;
|
|
LogRed4ext(line.str());
|
|
return true;
|
|
}
|
|
|
|
RED4ext::v1::GameState gRunningState{
|
|
.OnEnter = OnRunningEnter,
|
|
.OnUpdate = OnRunningUpdate,
|
|
.OnExit = OnRunningExit,
|
|
};
|
|
}
|
|
|
|
BOOL APIENTRY DllMain(HMODULE aModule, DWORD aReason, LPVOID)
|
|
{
|
|
if (aReason == DLL_PROCESS_ATTACH)
|
|
{
|
|
gModule = aModule;
|
|
LARGE_INTEGER frequency{};
|
|
LARGE_INTEGER counter{};
|
|
QueryPerformanceFrequency(&frequency);
|
|
QueryPerformanceCounter(&counter);
|
|
gLogFrequency = static_cast<uint64_t>(frequency.QuadPart);
|
|
gLogStartTick = static_cast<uint64_t>(counter.QuadPart);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4ext::v1::EMainReason aReason,
|
|
const RED4ext::v1::Sdk* aSdk)
|
|
{
|
|
gHandle = aHandle;
|
|
gSdk = aSdk;
|
|
|
|
std::ostringstream line;
|
|
line << "EdgeWeightGPS Main reason=" << ReasonToString(aReason) << " handle=" << aHandle << " sdk=" << aSdk;
|
|
if (aSdk && aSdk->runtime)
|
|
{
|
|
line << " runtime=" << static_cast<uint32_t>(aSdk->runtime->major) << "." << aSdk->runtime->minor << "."
|
|
<< aSdk->runtime->patch;
|
|
}
|
|
LogRed4ext(line.str());
|
|
|
|
if (aReason == RED4ext::v1::EMainReason::Load && aSdk && aSdk->gameStates && aSdk->gameStates->Add)
|
|
{
|
|
const auto added = aSdk->gameStates->Add(aHandle, kGameStateRunning, &gRunningState);
|
|
LogRed4ext(added ? "registered Running game-state callbacks" : "failed to register Running game-state callbacks");
|
|
|
|
if (kEnableGpsTraceHooks)
|
|
{
|
|
AttachProbeHook("RunGPSQuery body 0x29bd128", kRunGpsQueryBodyRva,
|
|
reinterpret_cast<void*>(&DetourRunGpsQueryBody),
|
|
reinterpret_cast<void**>(&gOriginalRunGpsQueryBody));
|
|
AttachProbeHook("UpdateGPSQuery body 0x29bd254", kUpdateGpsQueryBodyRva,
|
|
reinterpret_cast<void*>(&DetourUpdateGpsQueryBody),
|
|
reinterpret_cast<void**>(&gOriginalUpdateGpsQueryBody));
|
|
AttachProbeHook("GPSQuerySubmit 0x70a42c", kGpsQuerySubmitRva,
|
|
reinterpret_cast<void*>(&DetourGpsQuerySubmit),
|
|
reinterpret_cast<void**>(&gOriginalGpsQuerySubmit));
|
|
AttachProbeHook("GPSQueryDispatch 0x70a570", kGpsQueryDispatchRva,
|
|
reinterpret_cast<void*>(&DetourGpsQueryDispatch),
|
|
reinterpret_cast<void**>(&gOriginalGpsQueryDispatch));
|
|
AttachProbeHook("GPSQueryResultFetch 0x7094b8", kGpsQueryResultFetchRva,
|
|
reinterpret_cast<void*>(&DetourGpsQueryResultFetch),
|
|
reinterpret_cast<void**>(&gOriginalGpsQueryResultFetch));
|
|
AttachProbeHook("GPSQueryStatus 0xaa5704", kGpsQueryStatusRva,
|
|
reinterpret_cast<void*>(&DetourGpsQueryStatus),
|
|
reinterpret_cast<void**>(&gOriginalGpsQueryStatus));
|
|
AttachProbeHook("GPSDispatchAdvance 0x8d17d8", kGpsDispatchAdvanceRva,
|
|
reinterpret_cast<void*>(&DetourGpsDispatchAdvance),
|
|
reinterpret_cast<void**>(&gOriginalGpsDispatchAdvance));
|
|
AttachProbeHook("GPSAsyncWorkerTick 0x126b28", kGpsAsyncWorkerTickRva,
|
|
reinterpret_cast<void*>(&DetourGpsAsyncWorkerTick),
|
|
reinterpret_cast<void**>(&gOriginalGpsAsyncWorkerTick));
|
|
AttachProbeHook("GPSRouteJobPoll 0x883cd8", kGpsRouteJobPollRva,
|
|
reinterpret_cast<void*>(&DetourGpsRouteJobPoll),
|
|
reinterpret_cast<void**>(&gOriginalGpsRouteJobPoll));
|
|
AttachProbeHook("GPSRouteFinish 0x11f5f60", kGpsRouteFinishRva,
|
|
reinterpret_cast<void*>(&DetourGpsRouteFinish),
|
|
reinterpret_cast<void**>(&gOriginalGpsRouteFinish));
|
|
AttachProbeHook("GPSRouteJobBuild 0x818ba8", kGpsRouteJobBuildRva,
|
|
reinterpret_cast<void*>(&DetourGpsRouteJobBuild),
|
|
reinterpret_cast<void**>(&gOriginalGpsRouteJobBuild));
|
|
}
|
|
if (kEnableGpsCostTablePatch || kEnableGpsRouteJobStartTrace)
|
|
{
|
|
AttachProbeHook("GPSRouteJobStart 0x818928", kGpsRouteJobStartRva,
|
|
reinterpret_cast<void*>(&DetourGpsRouteJobStart),
|
|
reinterpret_cast<void**>(&gOriginalGpsRouteJobStart));
|
|
}
|
|
if (kEnableGpsProviderClassPatch || (kEnableGpsTraceHooks && kEnableGpsLocalSearchHooks))
|
|
{
|
|
AttachProbeHook("GPSSearch 0x44f054", kGpsSearchRva, reinterpret_cast<void*>(&DetourGpsSearch),
|
|
reinterpret_cast<void**>(&gOriginalGpsSearch));
|
|
}
|
|
if (kEnableGpsTraceHooks && kEnableGpsLocalSearchHooks)
|
|
{
|
|
AttachProbeHook("GPSRouteProducer 0x44cc7c", kGpsRouteProducerRva,
|
|
reinterpret_cast<void*>(&DetourGpsRouteProducer),
|
|
reinterpret_cast<void**>(&gOriginalGpsRouteProducer));
|
|
AttachProbeHook("GPSEdgeCost 0x44f838", kGpsEdgeCostRva, reinterpret_cast<void*>(&DetourGpsEdgeCost),
|
|
reinterpret_cast<void**>(&gOriginalGpsEdgeCost));
|
|
}
|
|
if (kEnableGpsMultiplierHooks)
|
|
{
|
|
AttachProbeHook("GPSAuxMultiplier 0x40bb00", kGpsAuxMultiplierRva,
|
|
reinterpret_cast<void*>(&DetourGpsAuxMultiplier),
|
|
reinterpret_cast<void**>(&gOriginalGpsAuxMultiplier));
|
|
AttachProbeHook("GPSNodeMultiplier 0x40bb40", kGpsNodeMultiplierRva,
|
|
reinterpret_cast<void*>(&DetourGpsNodeMultiplier),
|
|
reinterpret_cast<void**>(&gOriginalGpsNodeMultiplier));
|
|
}
|
|
if (kEnableRouteUiTraceHooks)
|
|
{
|
|
AttachProbeHook("GPSSystem/Tick", kGpsSystemTickRva, reinterpret_cast<void*>(&DetourGpsSystemTick),
|
|
reinterpret_cast<void**>(&gOriginalGpsSystemTick));
|
|
AttachProbeHook("JournalManager::TrackEntry impl", kJournalTrackEntryImplRva,
|
|
reinterpret_cast<void*>(&DetourJournalTrackEntry),
|
|
reinterpret_cast<void**>(&gOriginalJournalTrackEntry));
|
|
if (kEnableVerboseJournalListenerHooks)
|
|
{
|
|
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("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));
|
|
if (kEnableVerboseRouteObserverHooks)
|
|
{
|
|
AttachProbeHook("RouteObserver0::Activate slot48", kRouteObserver0ActivateRva,
|
|
reinterpret_cast<void*>(&DetourRouteObserver0Activate),
|
|
reinterpret_cast<void**>(&gOriginalRouteObserver0Activate));
|
|
AttachProbeHook("RouteObserver1::Activate slot48", kRouteObserver1ActivateRva,
|
|
reinterpret_cast<void*>(&DetourRouteObserver1Activate),
|
|
reinterpret_cast<void**>(&gOriginalRouteObserver1Activate));
|
|
AttachProbeHook("RouteObserver23::Activate slot48", kRouteObserver23ActivateRva,
|
|
reinterpret_cast<void*>(&DetourRouteObserver23Activate),
|
|
reinterpret_cast<void**>(&gOriginalRouteObserver23Activate));
|
|
AttachProbeHook("RouteObserver0::Deactivate slot50", kRouteObserver0DeactivateRva,
|
|
reinterpret_cast<void*>(&DetourRouteObserver0Deactivate),
|
|
reinterpret_cast<void**>(&gOriginalRouteObserver0Deactivate));
|
|
AttachProbeHook("RouteObserver1::Deactivate slot50", kRouteObserver1DeactivateRva,
|
|
reinterpret_cast<void*>(&DetourRouteObserver1Deactivate),
|
|
reinterpret_cast<void**>(&gOriginalRouteObserver1Deactivate));
|
|
AttachProbeHook("RouteObserver23::Deactivate slot50", kRouteObserver23DeactivateRva,
|
|
reinterpret_cast<void*>(&DetourRouteObserver23Deactivate),
|
|
reinterpret_cast<void**>(&gOriginalRouteObserver23Deactivate));
|
|
}
|
|
AttachProbeHook("FrameMappinPathWrapper", kFrameMappinPathWrapperRva,
|
|
reinterpret_cast<void*>(&DetourFrameMappinPathWrapper),
|
|
reinterpret_cast<void**>(&gOriginalFrameMappinPathWrapper));
|
|
AttachProbeHook("FrameMappinPathCore", kFrameMappinPathCoreRva,
|
|
reinterpret_cast<void*>(&DetourFrameMappinPathCore),
|
|
reinterpret_cast<void**>(&gOriginalFrameMappinPathCore));
|
|
AttachProbeHook("SetSelectedMappinWrapper", kSetSelectedMappinWrapperRva,
|
|
reinterpret_cast<void*>(&DetourSetSelectedMappinWrapper),
|
|
reinterpret_cast<void**>(&gOriginalSetSelectedMappinWrapper));
|
|
AttachProbeHook("SetSelectedMappinByIdWrapper", kSetSelectedMappinByIdWrapperRva,
|
|
reinterpret_cast<void*>(&DetourSetSelectedMappinByIdWrapper),
|
|
reinterpret_cast<void**>(&gOriginalSetSelectedMappinByIdWrapper));
|
|
AttachProbeHook("SetSelectedMappinByPositionWrapper", kSetSelectedMappinByPositionWrapperRva,
|
|
reinterpret_cast<void*>(&DetourSetSelectedMappinByPositionWrapper),
|
|
reinterpret_cast<void**>(&gOriginalSetSelectedMappinByPositionWrapper));
|
|
AttachProbeHook("SetSelectedMappinCore", kSetSelectedMappinCoreRva,
|
|
reinterpret_cast<void*>(&DetourSetSelectedMappinCore),
|
|
reinterpret_cast<void**>(&gOriginalSetSelectedMappinCore));
|
|
AttachProbeHook("TrackCustomPositionMappinWrapper", kTrackCustomPositionMappinWrapperRva,
|
|
reinterpret_cast<void*>(&DetourTrackCustomPositionMappinWrapper),
|
|
reinterpret_cast<void**>(&gOriginalTrackCustomPositionMappinWrapper));
|
|
AttachProbeHook("TrackCustomPositionMappinCore", kTrackCustomPositionMappinCoreRva,
|
|
reinterpret_cast<void*>(&DetourTrackCustomPositionMappinCore),
|
|
reinterpret_cast<void**>(&gOriginalTrackCustomPositionMappinCore));
|
|
AttachProbeHook("UntrackCustomPositionMappinWrapper", kUntrackCustomPositionMappinWrapperRva,
|
|
reinterpret_cast<void*>(&DetourUntrackCustomPositionMappinWrapper),
|
|
reinterpret_cast<void**>(&gOriginalUntrackCustomPositionMappinWrapper));
|
|
AttachProbeHook("TrackMappinWrapper", kTrackMappinWrapperRva,
|
|
reinterpret_cast<void*>(&DetourTrackMappinWrapper),
|
|
reinterpret_cast<void**>(&gOriginalTrackMappinWrapper));
|
|
AttachProbeHook("TrackMappinCore", kTrackMappinCoreRva, reinterpret_cast<void*>(&DetourTrackMappinCore),
|
|
reinterpret_cast<void**>(&gOriginalTrackMappinCore));
|
|
AttachProbeHook("SetMappinTrackingAlternativeWrapper", kSetMappinTrackingAlternativeWrapperRva,
|
|
reinterpret_cast<void*>(&DetourSetMappinTrackingAlternativeWrapper),
|
|
reinterpret_cast<void**>(&gOriginalSetMappinTrackingAlternativeWrapper));
|
|
}
|
|
}
|
|
|
|
if (aReason == RED4ext::v1::EMainReason::Unload)
|
|
{
|
|
if (kEnableGpsTraceHooks)
|
|
{
|
|
DetachProbeHook("RunGPSQuery body 0x29bd128", kRunGpsQueryBodyRva);
|
|
DetachProbeHook("UpdateGPSQuery body 0x29bd254", kUpdateGpsQueryBodyRva);
|
|
DetachProbeHook("GPSQuerySubmit 0x70a42c", kGpsQuerySubmitRva);
|
|
DetachProbeHook("GPSQueryDispatch 0x70a570", kGpsQueryDispatchRva);
|
|
DetachProbeHook("GPSQueryResultFetch 0x7094b8", kGpsQueryResultFetchRva);
|
|
DetachProbeHook("GPSQueryStatus 0xaa5704", kGpsQueryStatusRva);
|
|
DetachProbeHook("GPSDispatchAdvance 0x8d17d8", kGpsDispatchAdvanceRva);
|
|
DetachProbeHook("GPSAsyncWorkerTick 0x126b28", kGpsAsyncWorkerTickRva);
|
|
DetachProbeHook("GPSRouteJobPoll 0x883cd8", kGpsRouteJobPollRva);
|
|
DetachProbeHook("GPSRouteFinish 0x11f5f60", kGpsRouteFinishRva);
|
|
DetachProbeHook("GPSRouteJobBuild 0x818ba8", kGpsRouteJobBuildRva);
|
|
}
|
|
if (kEnableGpsCostTablePatch || kEnableGpsRouteJobStartTrace)
|
|
{
|
|
DetachProbeHook("GPSRouteJobStart 0x818928", kGpsRouteJobStartRva);
|
|
}
|
|
if (kEnableGpsProviderClassPatch || (kEnableGpsTraceHooks && kEnableGpsLocalSearchHooks))
|
|
{
|
|
DetachProbeHook("GPSSearch 0x44f054", kGpsSearchRva);
|
|
}
|
|
if (kEnableGpsTraceHooks && kEnableGpsLocalSearchHooks)
|
|
{
|
|
DetachProbeHook("GPSRouteProducer 0x44cc7c", kGpsRouteProducerRva);
|
|
DetachProbeHook("GPSEdgeCost 0x44f838", kGpsEdgeCostRva);
|
|
}
|
|
if (kEnableGpsMultiplierHooks)
|
|
{
|
|
DetachProbeHook("GPSAuxMultiplier 0x40bb00", kGpsAuxMultiplierRva);
|
|
DetachProbeHook("GPSNodeMultiplier 0x40bb40", kGpsNodeMultiplierRva);
|
|
}
|
|
if (kEnableRouteUiTraceHooks)
|
|
{
|
|
DetachProbeHook("GPSSystem/Tick", kGpsSystemTickRva);
|
|
DetachProbeHook("JournalManager::TrackEntry impl", kJournalTrackEntryImplRva);
|
|
if (kEnableVerboseJournalListenerHooks)
|
|
{
|
|
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("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);
|
|
if (kEnableVerboseRouteObserverHooks)
|
|
{
|
|
DetachProbeHook("RouteObserver0::Activate slot48", kRouteObserver0ActivateRva);
|
|
DetachProbeHook("RouteObserver1::Activate slot48", kRouteObserver1ActivateRva);
|
|
DetachProbeHook("RouteObserver23::Activate slot48", kRouteObserver23ActivateRva);
|
|
DetachProbeHook("RouteObserver0::Deactivate slot50", kRouteObserver0DeactivateRva);
|
|
DetachProbeHook("RouteObserver1::Deactivate slot50", kRouteObserver1DeactivateRva);
|
|
DetachProbeHook("RouteObserver23::Deactivate slot50", kRouteObserver23DeactivateRva);
|
|
}
|
|
DetachProbeHook("FrameMappinPathWrapper", kFrameMappinPathWrapperRva);
|
|
DetachProbeHook("FrameMappinPathCore", kFrameMappinPathCoreRva);
|
|
DetachProbeHook("SetSelectedMappinWrapper", kSetSelectedMappinWrapperRva);
|
|
DetachProbeHook("SetSelectedMappinByIdWrapper", kSetSelectedMappinByIdWrapperRva);
|
|
DetachProbeHook("SetSelectedMappinByPositionWrapper", kSetSelectedMappinByPositionWrapperRva);
|
|
DetachProbeHook("SetSelectedMappinCore", kSetSelectedMappinCoreRva);
|
|
DetachProbeHook("TrackCustomPositionMappinWrapper", kTrackCustomPositionMappinWrapperRva);
|
|
DetachProbeHook("TrackCustomPositionMappinCore", kTrackCustomPositionMappinCoreRva);
|
|
DetachProbeHook("UntrackCustomPositionMappinWrapper", kUntrackCustomPositionMappinWrapperRva);
|
|
DetachProbeHook("TrackMappinWrapper", kTrackMappinWrapperRva);
|
|
DetachProbeHook("TrackMappinCore", kTrackMappinCoreRva);
|
|
DetachProbeHook("SetMappinTrackingAlternativeWrapper", kSetMappinTrackingAlternativeWrapperRva);
|
|
DetachAbsoluteHook("MappinSystem::UpdateCustomPosition slot1f0", gMappinUpdateCustomPositionTarget);
|
|
DetachAbsoluteHook("MappinSystem::SetTracked slot220", gMappinSetTrackedTarget);
|
|
DetachAbsoluteHook("MappinSystem::ClearTracked slot228", gMappinClearTrackedTarget);
|
|
DetachAbsoluteHook("MappinSystem::FramePath slot280", gMappinFramePathTarget);
|
|
DetachAbsoluteHook("MappinSystem::PositionQuery slot2e0", gMappinPositionQueryTarget);
|
|
DetachAbsoluteHook("MappinSystem::CreateCustomPosition slot2f0", gMappinCreateCustomPositionTarget);
|
|
DetachAbsoluteHook("MappinSystem::ReleaseQuery slot368", gMappinReleaseQueryTarget);
|
|
DetachAbsoluteHook("MappinSystem::ActiveRoute slot3a8", gMappinActiveRouteTarget);
|
|
DetachAbsoluteHook("Observer1Service::RouteLookup slot220", gObserver1ServiceRouteLookupTarget);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
RED4EXT_C_EXPORT void RED4EXT_CALL Query(RED4ext::v1::PluginInfo* aInfo)
|
|
{
|
|
aInfo->name = L"EdgeWeightGPS";
|
|
aInfo->author = L"salt+Codex";
|
|
aInfo->version = RED4ext::v1::SemVer{0, 1, 0, {0, 0}};
|
|
aInfo->runtime = RED4ext::v1::FileVer{3, 0, 80, 51928};
|
|
aInfo->sdk = RED4ext::v1::SemVer{0, 5, 0, {0, 0}};
|
|
}
|
|
|
|
RED4EXT_C_EXPORT uint32_t RED4EXT_CALL Supports()
|
|
{
|
|
return RED4EXT_API_VERSION_1_COMPAT_0;
|
|
}
|