From 78520ee48f9b4276c538c078c8546c7d3c9e63dd Mon Sep 17 00:00:00 2001 From: Jacob Babor Date: Sat, 27 Jun 2026 04:36:28 -0500 Subject: [PATCH] Streamline GPS momentum plugin --- README.md | 72 +- packaging/EdgeWeightGPS-README.txt | 25 - packaging/MomentumGPS-README.txt | 22 + red4ext/EdgeWeightGPS/CMakeLists.txt | 16 +- red4ext/EdgeWeightGPS/src/Main.cpp | 6199 +------------------------- tools/build_red4ext_shim.sh | 4 +- tools/install_red4ext_shim.sh | 10 +- tools/package_red4ext_mod.sh | 12 +- 8 files changed, 176 insertions(+), 6184 deletions(-) delete mode 100644 packaging/EdgeWeightGPS-README.txt create mode 100644 packaging/MomentumGPS-README.txt diff --git a/README.md b/README.md index 31505b7..b9e8aed 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,41 @@ -# EdgeWeightGPS +# MomentumGPS Cyberpunk 2077 RED4ext plugin that improves player GPS routing by penalizing -short-segment churn in the native GPS solver. The current validated default -pushes the GPS toward smoother, higher-momentum driving corridors without -globally forcing every route onto highways. +short-segment churn in the native GPS solver. The validated default pushes the +GPS toward smoother, higher-momentum driving corridors without globally forcing +every route onto highways. ## Current Status -Working prototype, validated in-game. +Working RED4ext plugin, validated in-game. - Route scoring patch: enabled. -- Live tuning file: enabled. -- Current default fixed-edge penalty: `80.0`. -- Highway multiplier: neutral `1.0`; the earlier highway-speed/class - experiments are not part of the playable default. +- Current fixed ordinary-edge penalty: `80.0`. +- Live tuning: removed from the release build. +- Highway multiplier/class weighting experiments: not part of the playable + default. The active package is a RED4ext plugin only: ```text -red4ext/plugins/EdgeWeightGPS/EdgeWeightGPS.dll -red4ext/plugins/EdgeWeightGPS/momentum_weights.bin -red4ext/plugins/EdgeWeightGPS/solver_weights.bin +red4ext/plugins/MomentumGPS/MomentumGPS.dll +red4ext/plugins/MomentumGPS/README.txt ``` ## What It Patches Reverse engineering found the player GPS route search loop around the native solver relaxation cluster. The useful patch surface is not the script-facing -`RunGPSQuery`/`UpdateGPSQuery` helpers and not the generated traffic -`maxSpeed` fields. The playable hook patches the two ordinary adjacency -expansion sites feeding the native relax/update routine at `0x40ba58`. +`RunGPSQuery`/`UpdateGPSQuery` helpers and not generated traffic `maxSpeed` +fields. The playable hook patches the two ordinary adjacency expansion sites +feeding the native relax/update routine at `0x40ba58`. -The plugin adds a nonnegative fixed cost to ordinary edge expansion. In -practice, this makes many tiny street segments and repeated 90-degree -stair-steps less attractive, while still allowing local roads when they are the -right path. +The plugin adds a fixed cost to ordinary edge expansion. In practice, this makes +many tiny street segments and repeated 90-degree stair-steps less attractive, +while still allowing local roads when they are the right path. + +This is an inline native patch against tested Cyberpunk 2077 executable RVAs. A +future game update may require fresh verification. ## Validation Summary @@ -69,7 +70,7 @@ toolbox run -c 2077 ./tools/build_red4ext_shim.sh This writes: ```text -build/red4ext/EdgeWeightGPS-mingw64/EdgeWeightGPS.dll +build/red4ext/MomentumGPS-mingw64/MomentumGPS.dll ``` ## Install Locally @@ -80,13 +81,9 @@ Only install while the game is cold. toolbox run -c 2077 ./tools/install_red4ext_shim.sh ``` -The install script builds the DLL, installs it into the Flatpak Steam Cyberpunk -2077 folder, and writes the validated defaults: - -- `momentum_weights.bin = 80.0` -- `solver_weights.bin = 1.0` - -Override the game directory with `CYBERPUNK2077_GAME_DIR` if needed. +The install script builds the DLL and installs it into the Flatpak Steam +Cyberpunk 2077 folder. Override the game directory with +`CYBERPUNK2077_GAME_DIR` if needed. ## Package @@ -99,26 +96,6 @@ toolbox run -c 2077 ./tools/package_red4ext_mod.sh The package is written under `dist/` and contains the `red4ext/` folder layout expected by the game. -## Live Tuning - -`momentum_weights.bin` is a single little-endian float32 and is hot-reloaded -every 500 ms while the game is running. - -Useful presets: - -```bash -python3 tools/write_momentum_weights.py vanilla # 0.0 -python3 tools/write_momentum_weights.py default # 80.0 -python3 tools/write_momentum_weights.py overstrong # 160.0, known too high -``` - -`solver_weights.bin` is still present for older highway multiplier experiments, -but the playable default keeps it neutral: - -```bash -python3 tools/write_solver_weights.py vanilla # 1.0 -``` - ## Repository Notes This repository also contains older experiments and reverse-engineering tools: @@ -127,6 +104,7 @@ This repository also contains older experiments and reverse-engineering tools: - Traffic lane and connection probability patchers. - Static disassembly helpers. - RED4ext logging probes. +- Earlier live-tuning helpers and presets. Those tools are retained because they explain the route-finding system and document failed approaches, but they are not part of the current playable diff --git a/packaging/EdgeWeightGPS-README.txt b/packaging/EdgeWeightGPS-README.txt deleted file mode 100644 index 6a5a79a..0000000 --- a/packaging/EdgeWeightGPS-README.txt +++ /dev/null @@ -1,25 +0,0 @@ -EdgeWeightGPS RED4ext package - -Install: -Copy the red4ext folder from this package into the Cyberpunk 2077 game folder. -The final layout should include: - - Cyberpunk 2077/red4ext/plugins/EdgeWeightGPS/EdgeWeightGPS.dll - Cyberpunk 2077/red4ext/plugins/EdgeWeightGPS/momentum_weights.bin - Cyberpunk 2077/red4ext/plugins/EdgeWeightGPS/solver_weights.bin - -Default tuning: - momentum_weights.bin = 80.0 fixed ordinary-edge penalty - solver_weights.bin = 1.0 neutral highway multiplier - -Notes: -The plugin changes player GPS route scoring by adding a fixed cost to ordinary -edge expansion in the native GPS solver. This favors smoother routes with fewer -short-segment stair-steps and was validated in-game on long highway routes, -City Center routes, and local Watson/Westbrook routes. - -Tuning: -momentum_weights.bin is a single little-endian float32 and is hot-reloaded while -the game is running. Set it to 0.0 to disable the fixed-edge penalty. Values -around 80.0 are the current validated default. 160.0 was tested and is too high: -it can trigger bad hairpins or fallback-style routing. diff --git a/packaging/MomentumGPS-README.txt b/packaging/MomentumGPS-README.txt new file mode 100644 index 0000000..f039cb2 --- /dev/null +++ b/packaging/MomentumGPS-README.txt @@ -0,0 +1,22 @@ +MomentumGPS RED4ext package + +Install: +Copy the red4ext folder from this package into the Cyberpunk 2077 game folder. +The final layout should include: + + Cyberpunk 2077/red4ext/plugins/MomentumGPS/MomentumGPS.dll + Cyberpunk 2077/red4ext/plugins/MomentumGPS/README.txt + +What it does: +The plugin changes player GPS route scoring by adding a fixed 80.0 cost to +ordinary edge expansion in the native GPS solver. This favors smoother routes +with fewer short-segment stair-steps and was validated in-game on long highway +routes, City Center routes, and local Watson/Westbrook routes. + +Notes: +This release has no live tuning file. To change the fixed penalty, rebuild the +plugin with a different kFixedEdgePenalty value. + +The patch is version-specific native code. If a future Cyberpunk 2077 update +changes the executable around the patched RVAs, the plugin should fail closed +instead of patching unexpected bytes. diff --git a/red4ext/EdgeWeightGPS/CMakeLists.txt b/red4ext/EdgeWeightGPS/CMakeLists.txt index db4e739..6ed72d8 100644 --- a/red4ext/EdgeWeightGPS/CMakeLists.txt +++ b/red4ext/EdgeWeightGPS/CMakeLists.txt @@ -1,25 +1,25 @@ cmake_minimum_required(VERSION 3.21) -project(EdgeWeightGPS VERSION 0.1.0 LANGUAGES CXX) +project(MomentumGPS VERSION 0.2.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -add_library(EdgeWeightGPS SHARED src/Main.cpp) +add_library(MomentumGPS SHARED src/Main.cpp) target_include_directories( - EdgeWeightGPS + MomentumGPS PRIVATE "${CMAKE_CURRENT_LIST_DIR}/include" "${CMAKE_CURRENT_LIST_DIR}/../../vendor/RED4ext.SDK/include" ) -target_compile_definitions(EdgeWeightGPS PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX) -target_compile_options(EdgeWeightGPS PRIVATE -fmax-errors=12) -target_link_options(EdgeWeightGPS PRIVATE -static -static-libgcc -static-libstdc++) +target_compile_definitions(MomentumGPS PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX) +target_compile_options(MomentumGPS PRIVATE -fmax-errors=12) +target_link_options(MomentumGPS PRIVATE -static -static-libgcc -static-libstdc++) set_target_properties( - EdgeWeightGPS + MomentumGPS PROPERTIES PREFIX "" - OUTPUT_NAME "EdgeWeightGPS" + OUTPUT_NAME "MomentumGPS" ) diff --git a/red4ext/EdgeWeightGPS/src/Main.cpp b/red4ext/EdgeWeightGPS/src/Main.cpp index d37619c..7381b27 100644 --- a/red4ext/EdgeWeightGPS/src/Main.cpp +++ b/red4ext/EdgeWeightGPS/src/Main.cpp @@ -8,23 +8,12 @@ #include -#include #include -#include -#include -#include -#include #include -#include -#include -#include +#include #include -#include #include #include -#include - -#include "GeneratedSpatialRoadGrid.hpp" namespace RED4ext::v1 { @@ -56,6 +45,12 @@ struct Logger void (*CriticalWF)(PluginHandle aHandle, const wchar_t* aFormat, ...); }; +struct Hooking +{ + bool (*Attach)(PluginHandle aHandle, void* aTarget, void* aDetour, void** aOriginal); + bool (*Detach)(PluginHandle aHandle, void* aTarget); +}; + struct GameState { bool (*OnEnter)(void* aApp); @@ -68,12 +63,6 @@ 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); @@ -81,8 +70,6 @@ struct Scripts bool (*RegisterMixedRefType)(const char* aType); }; -struct Sdk; - struct Sdk { SemVer* runtime; @@ -95,696 +82,109 @@ struct Sdk namespace { -constexpr uint32_t kGameStateRunning = 2; -constexpr bool kEnableVerboseJournalListenerHooks = false; -constexpr bool kEnableVerboseRouteObserverHooks = false; -constexpr bool kEnableVerboseMappinRouteState = false; -constexpr bool kEnableGpsMultiplierHooks = false; -constexpr bool kEnableGpsAuxMultiplierHook = false; -constexpr bool kEnableGpsMultiplierSignatureTrace = false; -constexpr bool kEnableGpsNodeMultiplierInlinePatch = true; -constexpr bool kEnableGpsMomentumPenaltyInlinePatch = true; -constexpr bool kEnableGpsCostTablePatch = false; -constexpr bool kEnableGpsProviderClassPatch = false; -constexpr bool kEnableGpsProviderClassPatchTrace = false; -constexpr bool kEnableGpsTraceHooks = true; -constexpr bool kEnableGpsQueryLifecycleHooks = false; -constexpr bool kEnableGpsQueryResultTraceHooks = true; -constexpr bool kEnableGpsQueryResultRecordDump = false; -constexpr bool kEnableGpsAsyncTrafficProbe = false; -constexpr bool kEnableGpsTrafficEdgeWeightPatch = false; -constexpr bool kEnableGpsSpatialEdgeWeightPatch = false; -constexpr bool kEnableGpsSpatialEdgeWeightPatchTrace = false; -constexpr bool kEnableGpsSpatialEdgeWeightPatchStats = true; -constexpr bool kEnableGpsRouteJobLifecycleHooks = false; -constexpr bool kEnableGpsRouteJobStartTrace = false; -constexpr bool kEnableGpsLocalSearchHooks = false; -constexpr bool kEnableRouteUiTraceHooks = false; -constexpr bool kEnableGpsLocalSearchTraceHooks = kEnableGpsTraceHooks && kEnableGpsLocalSearchHooks; -constexpr bool kEnableGpsRouteProducerHook = kEnableGpsLocalSearchTraceHooks || kEnableGpsSpatialEdgeWeightPatch; -constexpr bool kEnableGpsEdgeCostHook = kEnableGpsLocalSearchTraceHooks || kEnableGpsSpatialEdgeWeightPatch; -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 kGpsMapRouteSubmitReturnRva = 0x08D20D4; -constexpr uintptr_t kGpsDispatchAdvanceRva = 0x08D17D8; -constexpr uintptr_t kGpsTrafficResultTakeRva = 0x0709D5C; -constexpr uintptr_t kGpsRoutePostprocessRva = 0x044830C; -constexpr uintptr_t kGpsTrafficEdgeScoreRva = 0x08D46CC; -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 kGpsResolveHandleRva = 0x044E1A8; -constexpr uintptr_t kGpsEdgeCostRva = 0x044F838; -constexpr uintptr_t kGpsFilteredProviderFilterRva = 0x044FF68; -constexpr uintptr_t kGpsBaseProviderFilterRva = 0x0450B08; -constexpr uintptr_t kGpsBaseCostProviderVtableRva = 0x2AE6120; -constexpr uintptr_t kGpsFilteredCostProviderVtableRva = 0x2AE60F8; -constexpr uintptr_t kGpsAuxMultiplierRva = 0x040BB00; -constexpr uintptr_t kGpsNodeMultiplierRva = 0x040BB40; -constexpr uintptr_t kGpsNodeMultiplierRoadTailRva = 0x040BB98; constexpr uintptr_t kGpsRelaxUpdateRva = 0x040BA58; constexpr uintptr_t kGpsExpandListARelaxPatchRva = 0x040B49A; constexpr uintptr_t kGpsExpandListBRelaxPatchRva = 0x040B6C3; -constexpr uintptr_t kGpsCostMultiplierTableRva = 0x3154D28; -constexpr uint64_t kGpsResolveTraceWindowMs = 1200; -constexpr uint32_t kGpsResolveSelectedRetLogLimit = 24; -constexpr uint32_t kGpsResolveStartupLogLimit = 8; -constexpr uint32_t kGpsResolveVerboseDumpLimit = 4; -constexpr std::array kGpsPatchedCostMultipliers = { - 12.0f, 7.0f, 2.5f, 0.0f, 3.0f, 2.0f, 1.25f, -}; -constexpr size_t kGpsProviderClassCount = 64; -constexpr uint16_t kTrafficLaneFlagPavement = 0x0008; -constexpr uint16_t kTrafficLaneFlagRoad = 0x0010; -constexpr uint16_t kTrafficLaneFlagGpsOnly = 0x0200; -constexpr uint16_t kTrafficLaneFlagNoAiDriving = 0x2000; -constexpr uint16_t kTrafficLaneFlagHighway = 0x4000; -constexpr uint16_t kTrafficLaneFlagNoAutodrive = 0x8000; -constexpr uintptr_t kMinimumReadableAddress = 0x10000; -constexpr float kGpsSolverHighwayMultiplierDefault = 0.80f; -constexpr float kGpsSolverGpsOnlyMultiplier = 1.10f; -constexpr float kGpsSolverPavementMultiplier = 1.25f; -constexpr size_t kGpsNodeMultiplierRoadTailPatchSize = 14; -constexpr size_t kGpsMomentumRelaxPatchSize = 23; -constexpr size_t kGpsMomentumPenaltyCaveSize = 96; -constexpr float kGpsMomentumFixedEdgePenaltyDefault = 80.0f; -constexpr float kGpsMomentumFixedEdgePenaltyMax = 1000.0f; -constexpr std::array kGpsSolverNonRoadMultipliers = { - 10.0f, 6.0f, 2.0f, 0.0f, 4.0f, 2.5f, 1.5f, 0.0f, -}; -constexpr bool kGpsSpatialPatchRequiresRouteProducer = true; -constexpr float kGpsSpatialHighwayMultiplier = 0.62f; -constexpr float kGpsSpatialRoadMultiplier = 1.0f; -constexpr float kGpsSpatialGpsOnlyMultiplier = 1.20f; -constexpr float kGpsSpatialPavementMultiplier = 1.35f; -constexpr float kGpsSpatialUnknownMultiplier = 1.05f; -constexpr uint64_t kGpsSolverWeightsReloadIntervalMs = 500; -constexpr uint64_t kGpsSpatialWeightsReloadIntervalMs = 500; -constexpr uint64_t kGpsMomentumWeightsReloadIntervalMs = 500; +constexpr size_t kRelaxPatchSize = 23; +constexpr size_t kPatchCaveSize = 80; +constexpr float kFixedEdgePenalty = 80.0f; -struct GpsProviderClassOverride -{ - size_t classId; - float multiplier; -}; - -constexpr std::array kGpsProviderClassOverrides = {{ - {1, 0.70f}, - {3, 1.45f}, - {4, 1.25f}, - {5, 1.75f}, - {15, 0.80f}, -}}; - -constexpr std::array kGpsProviderClassProbeIds = {1, 3, 4, 5, 14, 15}; - -struct GpsMomentumPenaltyPatchSite +struct PatchSite { const char* name; uintptr_t targetRva; - std::atomic* counter = nullptr; - std::array original{}; - void* cave = nullptr; - uint8_t* penaltyConstant = nullptr; + std::array expectedFirstInstruction; + std::array original{}; + uint8_t* cave = nullptr; bool applied = false; }; 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 gGpsTrafficResultTakeLogCount = 0; -uint32_t gGpsRoutePostprocessLogCount = 0; -LONG gGpsTrafficEdgeScoreLogCount = 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 gGpsResolveHandleLogCount = 0; -uint32_t gGpsEdgeCostLogCount = 0; -uint32_t gGpsAuxMultiplierLogCount = 0; -uint32_t gGpsNodeMultiplierLogCount = 0; -LONG gGpsSolverNodeWeightPatchLogCount = 0; -uint32_t gGpsCostTablePatchLogCount = 0; -uint32_t gGpsProviderClassPatchLogCount = 0; -std::array gGpsResolveHandleRetLogCounts{}; -uint64_t gGpsResolveTraceWindowUntilMs = 0; -uint32_t gGpsResolveTraceWindowSerial = 0; -std::array gGpsEdgeCostClassLogCounts{}; -std::array gGpsNodeMultiplierRoadTailOriginal{}; -void* gGpsNodeMultiplierRoadTailCave = nullptr; -uint8_t* gGpsNodeMultiplierRoadTailHighwayConstant = nullptr; -bool gGpsNodeMultiplierRoadTailPatchApplied = false; -alignas(8) std::atomic gGpsMomentumPenaltyCallsA{0}; -alignas(8) std::atomic gGpsMomentumPenaltyCallsB{0}; -std::array gGpsMomentumPenaltyPatchSites = {{ - {"expand-list-a", kGpsExpandListARelaxPatchRva, &gGpsMomentumPenaltyCallsA}, - {"expand-list-b", kGpsExpandListBRelaxPatchRva, &gGpsMomentumPenaltyCallsB}, + +std::array gPatchSites{{ + { + "expand-list-a", + kGpsExpandListARelaxPatchRva, + {0xF3, 0x41, 0x0F, 0x58, 0xF6}, // addss xmm6, xmm14 + }, + { + "expand-list-b", + kGpsExpandListBRelaxPatchRva, + {0xF3, 0x41, 0x0F, 0x58, 0xF4}, // addss xmm6, xmm12 + }, }}; -std::mutex gGpsCostLogMutex; -std::mutex gGpsAsyncLogMutex; -std::mutex gGpsCostTablePatchMutex; -std::mutex gGpsSolverWeightsMutex; -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 +uintptr_t GameImageBase() { - 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; - uint64_t momentumCallsAStart = 0; - uint64_t momentumCallsBStart = 0; - bool momentumSummaryLogged = false; -}; + return reinterpret_cast(GetModuleHandleW(nullptr)); +} -std::mutex gTrackedGpsQueryMutex; -std::array gTrackedGpsQueries{}; -uint32_t gTrackedGpsQueryNext = 0; - -struct GpsEdgeCostSample +void LogInfo(const std::string& aMessage) { - uint64_t currentHandle = 0; - uint64_t neighborHandle = 0; - uintptr_t returnRva = 0; - float cost = 0.0f; - float originalCost = 0.0f; - float spatialMultiplier = 1.0f; - uint8_t sourceClass = 0xFF; - uint8_t neighborClass = 0xFF; - char spatialClass = '.'; - bool spatialPatchAllowed = false; - bool spatialLookupOk = false; - bool spatialAdjusted = false; -}; - -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 sourceClasses{}; - std::array neighborClasses{}; - uint32_t spatialPatchAllowed = 0; - uint32_t spatialLookupOk = 0; - uint32_t spatialLookupFailed = 0; - uint32_t spatialAdjusted = 0; - std::array spatialClasses{}; - std::array spatialMultiplierSums{}; - std::array returnRvas{}; - std::array returnRvaCounts{}; - uint32_t otherReturnRvas = 0; - std::array samples{}; - uint32_t sampleCount = 0; -}; - -struct GpsProviderFilterStats -{ - uint32_t total = 0; - uint32_t passed = 0; - uint32_t failed = 0; - uint32_t baseTotal = 0; - uint32_t filteredTotal = 0; - std::array passClasses{}; - std::array failClasses{}; - uint32_t passInvalidClass = 0; - uint32_t failInvalidClass = 0; - std::array passMaskBits{}; - std::array failMaskBits{}; -}; - -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 GpsProviderFilterStats* gActiveGpsProviderFilterStats = nullptr; -thread_local uint32_t gActiveGpsRouteProducerCall = UINT32_MAX; -std::array 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 GpsTrafficResultTakeFunc = void* (*)(void* aTrafficService, void* aOutResult, uint32_t aTicket, - void* aOutMeta); -using GpsRoutePostprocessFunc = void (*)(void* aOutPath, void* aTrafficResult, void* aRouteRecords, float aScale); -using GpsTrafficEdgeScoreFunc = float (*)(void* aSearchContext, void* aCandidate, void* aReferencePoint); -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 GpsResolveHandleFunc = int32_t (*)(void* aGraph, uint64_t aHandle, void* aOutSegment, void* aOutPoint); -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 GpsProviderFilterFunc = bool (*)(void* aCostProvider, uint64_t aHandle, void* aSegment, void* aPoint); -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; -GpsTrafficResultTakeFunc gOriginalGpsTrafficResultTake = nullptr; -GpsRoutePostprocessFunc gOriginalGpsRoutePostprocess = nullptr; -GpsTrafficEdgeScoreFunc gOriginalGpsTrafficEdgeScore = nullptr; -GpsAsyncWorkerTickFunc gOriginalGpsAsyncWorkerTick = nullptr; -GpsRouteJobStartFunc gOriginalGpsRouteJobStart = nullptr; -GpsRouteJobPollFunc gOriginalGpsRouteJobPoll = nullptr; -GpsRouteFinishFunc gOriginalGpsRouteFinish = nullptr; -GpsRouteJobBuildFunc gOriginalGpsRouteJobBuild = nullptr; -GpsRouteProducerFunc gOriginalGpsRouteProducer = nullptr; -GpsSearchFunc gOriginalGpsSearch = nullptr; -GpsResolveHandleFunc gOriginalGpsResolveHandle = nullptr; -GpsEdgeCostFunc gOriginalGpsEdgeCost = nullptr; -GpsProviderFilterFunc gOriginalGpsFilteredProviderFilter = nullptr; -GpsProviderFilterFunc gOriginalGpsBaseProviderFilter = nullptr; -GpsMultiplierFunc gOriginalGpsAuxMultiplier = nullptr; -GpsMultiplierFunc gOriginalGpsNodeMultiplier = nullptr; -std::array, 5> gGpsSpatialMultipliers = { - kGpsSpatialHighwayMultiplier, - kGpsSpatialRoadMultiplier, - kGpsSpatialGpsOnlyMultiplier, - kGpsSpatialPavementMultiplier, - kGpsSpatialUnknownMultiplier, -}; -std::atomic gGpsSolverHighwayMultiplier{kGpsSolverHighwayMultiplierDefault}; -std::atomic gGpsMomentumFixedEdgePenalty{kGpsMomentumFixedEdgePenaltyDefault}; -std::mutex gGpsSpatialWeightsMutex; -std::mutex gGpsMomentumWeightsMutex; -std::atomic gGpsSolverWeightsNextReloadCheckMs = 0; -std::atomic gGpsSpatialWeightsNextReloadCheckMs = 0; -std::atomic gGpsMomentumWeightsNextReloadCheckMs = 0; -std::filesystem::file_time_type gGpsSolverWeightsLastWriteTime{}; -std::filesystem::file_time_type gGpsSpatialWeightsLastWriteTime{}; -std::filesystem::file_time_type gGpsMomentumWeightsLastWriteTime{}; -bool gGpsSolverWeightsFileKnown = false; -bool gGpsSpatialWeightsFileKnown = false; -bool gGpsMomentumWeightsFileKnown = false; - -uintptr_t GetImageBase(); - -std::filesystem::path GetPluginDirectory() -{ - wchar_t modulePath[MAX_PATH]{}; - if (gModule && GetModuleFileNameW(gModule, modulePath, MAX_PATH) > 0) + if (gSdk && gSdk->logger && gSdk->logger->Info) { - auto path = std::filesystem::path(modulePath); - return path.parent_path(); + gSdk->logger->Info(gHandle, aMessage.c_str()); } - - return std::filesystem::path(L"red4ext") / L"plugins" / L"EdgeWeightGPS"; } -std::filesystem::path GetLogPath() +void LogError(const std::string& aMessage) { - return GetPluginDirectory() / L"EdgeWeightGPS.log"; -} - -std::filesystem::path GetSpatialWeightsPath() -{ - return GetPluginDirectory() / L"spatial_weights.bin"; -} - -std::filesystem::path GetSolverWeightsPath() -{ - return GetPluginDirectory() / L"solver_weights.bin"; -} - -std::filesystem::path GetMomentumWeightsPath() -{ - return GetPluginDirectory() / L"momentum_weights.bin"; -} - -uint64_t GetElapsedMs() -{ - if (!gLogStartTick || !gLogFrequency) - { - return 0; - } - - LARGE_INTEGER counter{}; - QueryPerformanceCounter(&counter); - return static_cast((counter.QuadPart - gLogStartTick) * 1000 / gLogFrequency); -} - -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) + if (gSdk && gSdk->logger && gSdk->logger->Error) { + gSdk->logger->Error(gHandle, aMessage.c_str()); return; } - const auto elapsedMs = GetElapsedMs(); - - 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"; + LogInfo(aMessage); } -void LogRed4ext(std::string_view aMessage) +const char* ReasonToString(RED4ext::v1::EMainReason aReason) { - Log(aMessage); - - if (gSdk && gSdk->logger && gSdk->logger->Info && gHandle) + switch (aReason) { - std::string text(aMessage); - gSdk->logger->Info(gHandle, text.c_str()); + case RED4ext::v1::EMainReason::Load: + return "Load"; + case RED4ext::v1::EMainReason::Unload: + return "Unload"; + default: + return "Unknown"; } } -void WriteInt32(uint8_t* aTarget, int32_t aValue) +void WriteInt32(uint8_t* aAddress, int32_t aValue) { - std::memcpy(aTarget, &aValue, sizeof(aValue)); + std::memcpy(aAddress, &aValue, sizeof(aValue)); } -void WriteFloat(uint8_t* aTarget, float aValue) +void WriteFloat(uint8_t* aAddress, float aValue) { - std::memcpy(aTarget, &aValue, sizeof(aValue)); + std::memcpy(aAddress, &aValue, sizeof(aValue)); } -bool IsUsableSolverMultiplier(float aValue) +void EmitBytes(uint8_t* aCave, size_t& aOffset, std::initializer_list aBytes) { - return std::isfinite(aValue) && aValue > 0.0f && aValue < 20.0f; -} - -void StoreGpsSolverHighwayMultiplier(float aValue) -{ - gGpsSolverHighwayMultiplier.store(aValue, std::memory_order_relaxed); - - if (gGpsNodeMultiplierRoadTailHighwayConstant) + for (auto byte : aBytes) { - WriteFloat(gGpsNodeMultiplierRoadTailHighwayConstant, aValue); - FlushInstructionCache(GetCurrentProcess(), gGpsNodeMultiplierRoadTailHighwayConstant, sizeof(aValue)); + aCave[aOffset++] = byte; } } -void LogGpsSolverHighwayMultiplier(std::string_view aPrefix, float aValue) -{ - std::ostringstream line; - line << aPrefix << " highway=" << aValue; - LogRed4ext(line.str()); -} - -bool IsUsableMomentumPenalty(float aValue) -{ - return std::isfinite(aValue) && aValue >= 0.0f && aValue <= kGpsMomentumFixedEdgePenaltyMax; -} - -void StoreGpsMomentumFixedEdgePenalty(float aValue) -{ - gGpsMomentumFixedEdgePenalty.store(aValue, std::memory_order_relaxed); - - for (auto& site : gGpsMomentumPenaltyPatchSites) - { - if (!site.penaltyConstant) - { - continue; - } - - WriteFloat(site.penaltyConstant, aValue); - FlushInstructionCache(GetCurrentProcess(), site.penaltyConstant, sizeof(aValue)); - } -} - -void LogGpsMomentumFixedEdgePenalty(std::string_view aPrefix, float aValue) -{ - std::ostringstream line; - line << aPrefix << " fixedEdgePenalty=" << aValue; - LogRed4ext(line.str()); -} - void EmitAbsoluteRaxCall(uint8_t* aCave, size_t& aOffset, uintptr_t aTarget) { - aCave[aOffset++] = 0x48; // mov rax, imm64 - aCave[aOffset++] = 0xB8; + EmitBytes(aCave, aOffset, {0x48, 0xB8}); // mov rax, imm64 std::memcpy(aCave + aOffset, &aTarget, sizeof(aTarget)); aOffset += sizeof(aTarget); - aCave[aOffset++] = 0xFF; // call rax - aCave[aOffset++] = 0xD0; + EmitBytes(aCave, aOffset, {0xFF, 0xD0}); // call rax } void EmitAbsoluteRaxJump(uint8_t* aCave, size_t& aOffset, uintptr_t aTarget) { - aCave[aOffset++] = 0x48; // mov rax, imm64 - aCave[aOffset++] = 0xB8; + EmitBytes(aCave, aOffset, {0x48, 0xB8}); // mov rax, imm64 std::memcpy(aCave + aOffset, &aTarget, sizeof(aTarget)); aOffset += sizeof(aTarget); - aCave[aOffset++] = 0xFF; // jmp rax - aCave[aOffset++] = 0xE0; -} - -void EmitAbsoluteLockedIncrement(uint8_t* aCave, size_t& aOffset, uintptr_t aTarget) -{ - aCave[aOffset++] = 0x48; // mov rax, imm64 - aCave[aOffset++] = 0xB8; - std::memcpy(aCave + aOffset, &aTarget, sizeof(aTarget)); - aOffset += sizeof(aTarget); - aCave[aOffset++] = 0xF0; // lock inc qword ptr [rax] - aCave[aOffset++] = 0x48; - aCave[aOffset++] = 0xFF; - aCave[aOffset++] = 0x00; + EmitBytes(aCave, aOffset, {0xFF, 0xE0}); // jmp rax } void BuildAbsoluteJumpPatch(uint8_t* aPatch, size_t aPatchSize, uintptr_t aTarget) @@ -798,206 +198,53 @@ void BuildAbsoluteJumpPatch(uint8_t* aPatch, size_t aPatchSize, uintptr_t aTarge aPatch[offset++] = 0x00; std::memcpy(aPatch + offset, &aTarget, sizeof(aTarget)); offset += sizeof(aTarget); + while (offset < aPatchSize) { aPatch[offset++] = 0x90; } } -void MaybeReloadGpsSolverWeights(bool aForce = false) -{ - if (!kEnableGpsNodeMultiplierInlinePatch) - { - return; - } - - const auto nowMs = GetElapsedMs(); - if (!aForce && nowMs < gGpsSolverWeightsNextReloadCheckMs.load(std::memory_order_relaxed)) - { - return; - } - - std::lock_guard lock(gGpsSolverWeightsMutex); - if (!aForce && nowMs < gGpsSolverWeightsNextReloadCheckMs.load(std::memory_order_relaxed)) - { - return; - } - gGpsSolverWeightsNextReloadCheckMs.store(nowMs + kGpsSolverWeightsReloadIntervalMs, std::memory_order_relaxed); - - const auto path = GetSolverWeightsPath(); - std::error_code error; - const auto writeTime = std::filesystem::last_write_time(path, error); - if (error) - { - if (aForce || gGpsSolverWeightsFileKnown) - { - gGpsSolverWeightsFileKnown = false; - std::ostringstream line; - line << "solver weights file unavailable path=" << path.string() - << " using highway=" << gGpsSolverHighwayMultiplier.load(std::memory_order_relaxed); - LogRed4ext(line.str()); - } - return; - } - - if (!aForce && gGpsSolverWeightsFileKnown && writeTime == gGpsSolverWeightsLastWriteTime) - { - return; - } - - float highwayMultiplier = 0.0f; - std::ifstream input(path, std::ios::binary); - input.read(reinterpret_cast(&highwayMultiplier), static_cast(sizeof(highwayMultiplier))); - if (input.gcount() != static_cast(sizeof(highwayMultiplier))) - { - std::ostringstream line; - line << "solver weights reload skipped path=" << path.string() << " bytes=" << input.gcount() - << " expected=" << sizeof(highwayMultiplier); - LogRed4ext(line.str()); - gGpsSolverWeightsFileKnown = true; - gGpsSolverWeightsLastWriteTime = writeTime; - return; - } - - if (!IsUsableSolverMultiplier(highwayMultiplier)) - { - std::ostringstream line; - line << "solver weights reload skipped path=" << path.string() << " invalidHighway=" << highwayMultiplier; - LogRed4ext(line.str()); - gGpsSolverWeightsFileKnown = true; - gGpsSolverWeightsLastWriteTime = writeTime; - return; - } - - StoreGpsSolverHighwayMultiplier(highwayMultiplier); - gGpsSolverWeightsFileKnown = true; - gGpsSolverWeightsLastWriteTime = writeTime; - - std::ostringstream line; - line << "solver weights reloaded path=" << path.string() << " highway=" << highwayMultiplier; - LogRed4ext(line.str()); -} - -void MaybeReloadGpsMomentumWeights(bool aForce = false) -{ - if (!kEnableGpsMomentumPenaltyInlinePatch) - { - return; - } - - const auto nowMs = GetElapsedMs(); - if (!aForce && nowMs < gGpsMomentumWeightsNextReloadCheckMs.load(std::memory_order_relaxed)) - { - return; - } - - std::lock_guard lock(gGpsMomentumWeightsMutex); - if (!aForce && nowMs < gGpsMomentumWeightsNextReloadCheckMs.load(std::memory_order_relaxed)) - { - return; - } - gGpsMomentumWeightsNextReloadCheckMs.store(nowMs + kGpsMomentumWeightsReloadIntervalMs, - std::memory_order_relaxed); - - const auto path = GetMomentumWeightsPath(); - std::error_code error; - const auto writeTime = std::filesystem::last_write_time(path, error); - if (error) - { - if (aForce || gGpsMomentumWeightsFileKnown) - { - gGpsMomentumWeightsFileKnown = false; - std::ostringstream line; - line << "momentum weights file unavailable path=" << path.string() - << " using fixedEdgePenalty=" << gGpsMomentumFixedEdgePenalty.load(std::memory_order_relaxed); - LogRed4ext(line.str()); - } - return; - } - - if (!aForce && gGpsMomentumWeightsFileKnown && writeTime == gGpsMomentumWeightsLastWriteTime) - { - return; - } - - float fixedEdgePenalty = 0.0f; - std::ifstream input(path, std::ios::binary); - input.read(reinterpret_cast(&fixedEdgePenalty), static_cast(sizeof(fixedEdgePenalty))); - if (input.gcount() != static_cast(sizeof(fixedEdgePenalty))) - { - std::ostringstream line; - line << "momentum weights reload skipped path=" << path.string() << " bytes=" << input.gcount() - << " expected=" << sizeof(fixedEdgePenalty); - LogRed4ext(line.str()); - gGpsMomentumWeightsFileKnown = true; - gGpsMomentumWeightsLastWriteTime = writeTime; - return; - } - - if (!IsUsableMomentumPenalty(fixedEdgePenalty)) - { - std::ostringstream line; - line << "momentum weights reload skipped path=" << path.string() - << " invalidFixedEdgePenalty=" << fixedEdgePenalty; - LogRed4ext(line.str()); - gGpsMomentumWeightsFileKnown = true; - gGpsMomentumWeightsLastWriteTime = writeTime; - return; - } - - StoreGpsMomentumFixedEdgePenalty(fixedEdgePenalty); - gGpsMomentumWeightsFileKnown = true; - gGpsMomentumWeightsLastWriteTime = writeTime; - - std::ostringstream line; - line << "momentum weights reloaded path=" << path.string() << " fixedEdgePenalty=" << fixedEdgePenalty; - LogRed4ext(line.str()); -} - -void RemoveGpsMomentumPenaltyInlinePatch(); - -bool InstallGpsMomentumPenaltyPatchSite(GpsMomentumPenaltyPatchSite& aSite, - std::initializer_list aAddCurrentCostInstruction) +bool InstallPatchSite(PatchSite& aSite) { if (aSite.applied) { return true; } - auto* cave = static_cast( - VirtualAlloc(nullptr, kGpsMomentumPenaltyCaveSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)); + auto* target = reinterpret_cast(GameImageBase() + aSite.targetRva); + if (std::memcmp(target, aSite.expectedFirstInstruction.data(), aSite.expectedFirstInstruction.size()) != 0) + { + std::ostringstream line; + line << "GPS fixed-edge penalty skipped site=" << aSite.name + << " reason=unexpected-bytes rva=0x" << std::hex << aSite.targetRva; + LogError(line.str()); + return false; + } + + auto* cave = static_cast(VirtualAlloc(nullptr, kPatchCaveSize, MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE)); if (!cave) { std::ostringstream line; - line << "gps-momentum-penalty-inline-patch skipped site=" << aSite.name << " reason=alloc-failed"; - LogRed4ext(line.str()); + line << "GPS fixed-edge penalty skipped site=" << aSite.name << " reason=alloc-failed"; + LogError(line.str()); return false; } size_t offset = 0; - const auto emit = [&](std::initializer_list aBytes) { - for (auto byte : aBytes) - { - cave[offset++] = byte; - } - }; - - // Replaces the final g/f assembly before 0x40ba58. The overwritten - // vanilla block differs only in which XMM register stores current.g. - emit(aAddCurrentCostInstruction); - - const auto addPenaltyOffset = offset; - emit({0xF3, 0x0F, 0x58, 0x35, 0x00, 0x00, 0x00, 0x00}); // addss xmm6, [rip+disp32] - if (aSite.counter) + for (auto byte : aSite.expectedFirstInstruction) { - EmitAbsoluteLockedIncrement(cave, offset, reinterpret_cast(aSite.counter)); + cave[offset++] = byte; } - emit({0xF3, 0x0F, 0x58, 0xC6}); // addss xmm0, xmm6 - emit({0x0F, 0x28, 0xDE}); // movaps xmm3, xmm6 - emit({0xF3, 0x0F, 0x11, 0x44, 0x24, 0x20}); // movss [rsp+0x20], xmm0 - EmitAbsoluteRaxCall(cave, offset, GetImageBase() + kGpsRelaxUpdateRva); - EmitAbsoluteRaxJump(cave, offset, GetImageBase() + aSite.targetRva + kGpsMomentumRelaxPatchSize); + const auto addPenaltyOffset = offset; + EmitBytes(cave, offset, {0xF3, 0x0F, 0x58, 0x35, 0x00, 0x00, 0x00, 0x00}); // addss xmm6, [rip+disp32] + EmitBytes(cave, offset, {0xF3, 0x0F, 0x58, 0xC6}); // addss xmm0, xmm6 + EmitBytes(cave, offset, {0x0F, 0x28, 0xDE}); // movaps xmm3, xmm6 + EmitBytes(cave, offset, {0xF3, 0x0F, 0x11, 0x44, 0x24, 0x20}); // movss [rsp+0x20], xmm0 + EmitAbsoluteRaxCall(cave, offset, GameImageBase() + kGpsRelaxUpdateRva); + EmitAbsoluteRaxJump(cave, offset, GameImageBase() + aSite.targetRva + kRelaxPatchSize); while ((offset % alignof(float)) != 0) { @@ -1005,17 +252,16 @@ bool InstallGpsMomentumPenaltyPatchSite(GpsMomentumPenaltyPatchSite& aSite, } const auto penaltyConstantOffset = offset; - WriteFloat(cave + offset, gGpsMomentumFixedEdgePenalty.load(std::memory_order_relaxed)); + WriteFloat(cave + offset, kFixedEdgePenalty); offset += sizeof(float); const auto caveAddress = reinterpret_cast(cave); WriteInt32(cave + addPenaltyOffset + 4, static_cast((caveAddress + penaltyConstantOffset) - (caveAddress + addPenaltyOffset + 8))); - auto* target = reinterpret_cast(GetImageBase() + aSite.targetRva); std::memcpy(aSite.original.data(), target, aSite.original.size()); - std::array patch{}; + std::array patch{}; BuildAbsoluteJumpPatch(patch.data(), patch.size(), caveAddress); DWORD oldProtect = 0; @@ -1023,8 +269,8 @@ bool InstallGpsMomentumPenaltyPatchSite(GpsMomentumPenaltyPatchSite& aSite, { VirtualFree(cave, 0, MEM_RELEASE); std::ostringstream line; - line << "gps-momentum-penalty-inline-patch skipped site=" << aSite.name << " reason=protect-failed"; - LogRed4ext(line.str()); + line << "GPS fixed-edge penalty skipped site=" << aSite.name << " reason=protect-failed"; + LogError(line.str()); return false; } @@ -1035,50 +281,25 @@ bool InstallGpsMomentumPenaltyPatchSite(GpsMomentumPenaltyPatchSite& aSite, VirtualProtect(target, patch.size(), oldProtect, &ignoredProtect); aSite.cave = cave; - aSite.penaltyConstant = cave + penaltyConstantOffset; aSite.applied = true; std::ostringstream line; - line << "gps-momentum-penalty-inline-patch applied site=" << aSite.name << " target_rva=0x" << std::hex - << aSite.targetRva << " cave=" << static_cast(cave) << std::dec - << " fixedEdgePenalty=" << gGpsMomentumFixedEdgePenalty.load(std::memory_order_relaxed); - LogRed4ext(line.str()); + line << "GPS fixed-edge penalty applied site=" << aSite.name << " rva=0x" << std::hex << aSite.targetRva + << " cave=" << static_cast(cave) << std::dec << " penalty=" << kFixedEdgePenalty; + LogInfo(line.str()); return true; } -bool InstallGpsMomentumPenaltyInlinePatch() +void RemovePatchSites() { - if (!kEnableGpsMomentumPenaltyInlinePatch) - { - return true; - } - - const bool firstOk = - InstallGpsMomentumPenaltyPatchSite(gGpsMomentumPenaltyPatchSites[0], - {0xF3, 0x41, 0x0F, 0x58, 0xF6}); // addss xmm6, xmm14 - const bool secondOk = - InstallGpsMomentumPenaltyPatchSite(gGpsMomentumPenaltyPatchSites[1], - {0xF3, 0x41, 0x0F, 0x58, 0xF4}); // addss xmm6, xmm12 - - if (!firstOk || !secondOk) - { - RemoveGpsMomentumPenaltyInlinePatch(); - return false; - } - - return true; -} - -void RemoveGpsMomentumPenaltyInlinePatch() -{ - for (auto& site : gGpsMomentumPenaltyPatchSites) + for (auto& site : gPatchSites) { if (!site.applied) { continue; } - auto* target = reinterpret_cast(GetImageBase() + site.targetRva); + auto* target = reinterpret_cast(GameImageBase() + site.targetRva); DWORD oldProtect = 0; if (VirtualProtect(target, site.original.size(), PAGE_EXECUTE_READWRITE, &oldProtect)) { @@ -1089,15 +310,14 @@ void RemoveGpsMomentumPenaltyInlinePatch() VirtualProtect(target, site.original.size(), oldProtect, &ignoredProtect); std::ostringstream line; - line << "gps-momentum-penalty-inline-patch restored site=" << site.name; - LogRed4ext(line.str()); + line << "GPS fixed-edge penalty restored site=" << site.name; + LogInfo(line.str()); } else { std::ostringstream line; - line << "gps-momentum-penalty-inline-patch restore-failed site=" << site.name - << " reason=protect-failed"; - LogRed4ext(line.str()); + line << "GPS fixed-edge penalty restore failed site=" << site.name << " reason=protect-failed"; + LogError(line.str()); } if (site.cave) @@ -1106,4832 +326,25 @@ void RemoveGpsMomentumPenaltyInlinePatch() site.cave = nullptr; } - site.penaltyConstant = nullptr; site.applied = false; } } -bool InstallGpsNodeMultiplierInlinePatch() +bool InstallPatchSites() { - if (!kEnableGpsNodeMultiplierInlinePatch || gGpsNodeMultiplierRoadTailPatchApplied) + bool ok = true; + for (auto& site : gPatchSites) { - return true; + ok = InstallPatchSite(site) && ok; } - constexpr size_t kCaveSize = 80; - auto* cave = static_cast( - VirtualAlloc(nullptr, kCaveSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)); - if (!cave) + if (!ok) { - LogRed4ext("gps-node-multiplier-inline-patch skipped reason=alloc-failed"); - return false; + RemovePatchSites(); } - // Replaces the road-tail at 0x40bb98. Reaching this block already means - // vanilla selected one of the road-style solver modes (0/2/4), so keep - // the change broad enough for manual GPS and autodrive-like callers. - size_t offset = 0; - const auto emit = [&](std::initializer_list aBytes) { - for (auto byte : aBytes) - { - cave[offset++] = byte; - } - }; - - emit({0x80, 0xBA, 0x93, 0x00, 0x00, 0x00, 0x01}); // cmp byte ptr [rdx+0x93], 1 - emit({0x75, 0x08}); // jne +8 - - const auto mulNode93Offset = offset; - emit({0xF3, 0x0F, 0x59, 0x05, 0x00, 0x00, 0x00, 0x00}); // mulss xmm0, [rip+disp32] - - emit({0x66, 0xF7, 0x82, 0x88, 0x00, 0x00, 0x00, 0x00, 0x40}); // test word ptr [rdx+0x88], 0x4000 - emit({0x74, 0x08}); // je +8 - - const auto mulHighwayOffset = offset; - emit({0xF3, 0x0F, 0x59, 0x05, 0x00, 0x00, 0x00, 0x00}); // mulss xmm0, [rip+disp32] - emit({0xC3}); // ret - - while ((offset % alignof(float)) != 0) - { - cave[offset++] = 0x90; - } - - const auto node93ConstantOffset = offset; - WriteFloat(cave + offset, 1.1f); - offset += sizeof(float); - const auto highwayConstantOffset = offset; - const auto highwayMultiplier = gGpsSolverHighwayMultiplier.load(std::memory_order_relaxed); - WriteFloat(cave + offset, highwayMultiplier); - offset += sizeof(float); - - const auto caveAddress = reinterpret_cast(cave); - WriteInt32(cave + mulNode93Offset + 4, - static_cast((caveAddress + node93ConstantOffset) - (caveAddress + mulNode93Offset + 8))); - WriteInt32(cave + mulHighwayOffset + 4, - static_cast((caveAddress + highwayConstantOffset) - (caveAddress + mulHighwayOffset + 8))); - - auto* target = reinterpret_cast(GetImageBase() + kGpsNodeMultiplierRoadTailRva); - std::memcpy(gGpsNodeMultiplierRoadTailOriginal.data(), target, gGpsNodeMultiplierRoadTailOriginal.size()); - - uint8_t patch[kGpsNodeMultiplierRoadTailPatchSize]{}; - size_t patchOffset = 0; - patch[patchOffset++] = 0xFF; // jmp qword ptr [rip+0] - patch[patchOffset++] = 0x25; - patch[patchOffset++] = 0x00; - patch[patchOffset++] = 0x00; - patch[patchOffset++] = 0x00; - patch[patchOffset++] = 0x00; - const auto cavePointer = reinterpret_cast(cave); - std::memcpy(patch + patchOffset, &cavePointer, sizeof(cavePointer)); - patchOffset += sizeof(cavePointer); - while (patchOffset < sizeof(patch)) - { - patch[patchOffset++] = 0x90; - } - - DWORD oldProtect = 0; - if (!VirtualProtect(target, sizeof(patch), PAGE_EXECUTE_READWRITE, &oldProtect)) - { - VirtualFree(cave, 0, MEM_RELEASE); - LogRed4ext("gps-node-multiplier-inline-patch skipped reason=protect-failed"); - return false; - } - - std::memcpy(target, patch, sizeof(patch)); - FlushInstructionCache(GetCurrentProcess(), target, sizeof(patch)); - - DWORD ignoredProtect = 0; - VirtualProtect(target, sizeof(patch), oldProtect, &ignoredProtect); - - gGpsNodeMultiplierRoadTailCave = cave; - gGpsNodeMultiplierRoadTailHighwayConstant = cave + highwayConstantOffset; - gGpsNodeMultiplierRoadTailPatchApplied = true; - - std::ostringstream line; - line << "gps-node-multiplier-inline-patch applied target_rva=0x" << std::hex - << kGpsNodeMultiplierRoadTailRva << " cave=" << static_cast(cave) - << " highwayMultiplier=" << std::dec << highwayMultiplier << " modes=road-tail"; - LogRed4ext(line.str()); - return true; -} - -void RemoveGpsNodeMultiplierInlinePatch() -{ - if (!gGpsNodeMultiplierRoadTailPatchApplied) - { - return; - } - - auto* target = reinterpret_cast(GetImageBase() + kGpsNodeMultiplierRoadTailRva); - DWORD oldProtect = 0; - if (VirtualProtect(target, gGpsNodeMultiplierRoadTailOriginal.size(), PAGE_EXECUTE_READWRITE, &oldProtect)) - { - std::memcpy(target, gGpsNodeMultiplierRoadTailOriginal.data(), gGpsNodeMultiplierRoadTailOriginal.size()); - FlushInstructionCache(GetCurrentProcess(), target, gGpsNodeMultiplierRoadTailOriginal.size()); - - DWORD ignoredProtect = 0; - VirtualProtect(target, gGpsNodeMultiplierRoadTailOriginal.size(), oldProtect, &ignoredProtect); - LogRed4ext("gps-node-multiplier-inline-patch restored"); - } - else - { - LogRed4ext("gps-node-multiplier-inline-patch restore-failed reason=protect-failed"); - } - - if (gGpsNodeMultiplierRoadTailCave) - { - VirtualFree(gGpsNodeMultiplierRoadTailCave, 0, MEM_RELEASE); - gGpsNodeMultiplierRoadTailCave = nullptr; - } - - gGpsNodeMultiplierRoadTailHighwayConstant = nullptr; - gGpsNodeMultiplierRoadTailPatchApplied = false; -} - -bool IsUsableSpatialMultiplier(float aValue) -{ - return std::isfinite(aValue) && aValue > 0.0f && aValue < 20.0f; -} - -void StoreGpsSpatialMultipliers(const std::array& aValues) -{ - for (size_t index = 0; index < aValues.size(); ++index) - { - gGpsSpatialMultipliers[index].store(aValues[index], std::memory_order_relaxed); - } -} - -void LogGpsSpatialMultipliers(std::string_view aPrefix, const std::array& aValues) -{ - std::ostringstream line; - line << aPrefix << " highway=" << aValues[0] << " road=" << aValues[1] << " gpsonly=" << aValues[2] - << " pavement=" << aValues[3] << " unknown=" << aValues[4]; - LogRed4ext(line.str()); -} - -void MaybeReloadGpsSpatialWeights() -{ - if (!kEnableGpsSpatialEdgeWeightPatch) - { - return; - } - - const auto nowMs = GetElapsedMs(); - if (nowMs < gGpsSpatialWeightsNextReloadCheckMs.load(std::memory_order_relaxed)) - { - return; - } - - std::lock_guard lock(gGpsSpatialWeightsMutex); - if (nowMs < gGpsSpatialWeightsNextReloadCheckMs.load(std::memory_order_relaxed)) - { - return; - } - gGpsSpatialWeightsNextReloadCheckMs.store(nowMs + kGpsSpatialWeightsReloadIntervalMs, std::memory_order_relaxed); - - const auto path = GetSpatialWeightsPath(); - std::error_code error; - const auto writeTime = std::filesystem::last_write_time(path, error); - if (error) - { - if (gGpsSpatialWeightsFileKnown) - { - gGpsSpatialWeightsFileKnown = false; - std::ostringstream line; - line << "spatial weights file unavailable path=" << path.string() << " using current/default values"; - LogRed4ext(line.str()); - } - return; - } - - if (gGpsSpatialWeightsFileKnown && writeTime == gGpsSpatialWeightsLastWriteTime) - { - return; - } - - std::array values{}; - std::ifstream input(path, std::ios::binary); - input.read(reinterpret_cast(values.data()), static_cast(values.size() * sizeof(float))); - if (input.gcount() != static_cast(values.size() * sizeof(float))) - { - std::ostringstream line; - line << "spatial weights reload skipped path=" << path.string() << " bytes=" << input.gcount() - << " expected=" << values.size() * sizeof(float); - LogRed4ext(line.str()); - gGpsSpatialWeightsFileKnown = true; - gGpsSpatialWeightsLastWriteTime = writeTime; - return; - } - - for (const auto value : values) - { - if (!IsUsableSpatialMultiplier(value)) - { - std::ostringstream line; - line << "spatial weights reload skipped path=" << path.string() << " invalidValue=" << value; - LogRed4ext(line.str()); - gGpsSpatialWeightsFileKnown = true; - gGpsSpatialWeightsLastWriteTime = writeTime; - return; - } - } - - StoreGpsSpatialMultipliers(values); - gGpsSpatialWeightsFileKnown = true; - gGpsSpatialWeightsLastWriteTime = writeTime; - LogGpsSpatialMultipliers("spatial weights reloaded", values); -} - -uintptr_t GetImageBase() -{ - return reinterpret_cast(GetModuleHandleW(nullptr)); -} - -uintptr_t GetImageEnd() -{ - const auto imageBase = GetImageBase(); - if (!imageBase) - { - return 0; - } - - const auto* dos = reinterpret_cast(imageBase); - if (dos->e_magic != IMAGE_DOS_SIGNATURE) - { - return 0; - } - - const auto* nt = reinterpret_cast(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(aPointer); - return imageBase && imageEnd && address >= imageBase && address < imageEnd; -} - -bool TryGetImageRva(const void* aPointer, uintptr_t& aRva) -{ - if (!IsInImage(aPointer)) - { - return false; - } - - aRva = reinterpret_cast(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(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(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(aVtable); - auto readSlot = [vtable](uintptr_t aOffset) -> void* { - if (!vtable) - { - return nullptr; - } - - return *reinterpret_cast(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(aPointer); - const auto end = begin + aSize; - if (begin < kMinimumReadableAddress || end < begin) - { - return false; - } - - auto cursor = begin; - while (cursor < end) - { - MEMORY_BASIC_INFORMATION mbi{}; - if (!VirtualQuery(reinterpret_cast(cursor), &mbi, sizeof(mbi))) - { - return false; - } - - if (mbi.State != MEM_COMMIT || !IsReadablePage(mbi.Protect)) - { - return false; - } - - const auto regionEnd = reinterpret_cast(mbi.BaseAddress) + mbi.RegionSize; - if (regionEnd <= cursor) - { - return false; - } - - cursor = regionEnd; - } - - return true; -} - -template -bool TryReadMemory(const void* aPointer, T& aValue) -{ - if (!IsReadableMemory(aPointer, sizeof(T))) - { - return false; - } - - aValue = *reinterpret_cast(aPointer); - return true; -} - -void* ReadPointerField(void* aObject, uintptr_t aOffset) -{ - void* value = nullptr; - if (!aObject) - { - return nullptr; - } - - TryReadMemory(reinterpret_cast(reinterpret_cast(aObject) + aOffset), value); - return value; -} - -void* GetObserver1SystemType() -{ - return ReadPointerField(reinterpret_cast(GetImageBase() + kObserver1SystemTypeGlobalRva), 0x00); -} - -void AppendObjectVtableSlots(std::ostringstream& aLine, void* aObject, std::initializer_list 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(reinterpret_cast(aObject) + aOffset), - value); - aLine << " " << aLabel << "="; - if (hasValue) - { - aLine << value << "/0x" << std::hex << value << std::dec; - } - else - { - aLine << ""; - } -} - -void AppendUint16Field(std::ostringstream& aLine, const char* aLabel, void* aObject, uintptr_t aOffset) -{ - uint16_t value = 0; - const auto hasValue = aObject && TryReadMemory(reinterpret_cast(reinterpret_cast(aObject) + aOffset), - value); - aLine << " " << aLabel << "="; - if (hasValue) - { - aLine << value << "/0x" << std::hex << value << std::dec; - } - else - { - aLine << ""; - } -} - -void AppendUint64Field(std::ostringstream& aLine, const char* aLabel, void* aObject, uintptr_t aOffset) -{ - uint64_t value = 0; - const auto hasValue = aObject && TryReadMemory(reinterpret_cast(reinterpret_cast(aObject) + aOffset), - value); - aLine << " " << aLabel << "="; - if (hasValue) - { - aLine << value << "/0x" << std::hex << value << std::dec; - } - else - { - aLine << ""; - } -} - -void AppendByteField(std::ostringstream& aLine, const char* aLabel, void* aObject, uintptr_t aOffset) -{ - uint8_t value = 0; - const auto hasValue = aObject && TryReadMemory(reinterpret_cast(reinterpret_cast(aObject) + aOffset), - value); - aLine << " " << aLabel << "="; - if (hasValue) - { - aLine << static_cast(value); - } - else - { - aLine << ""; - } -} - -void AppendBytesField(std::ostringstream& aLine, const char* aLabel, void* aObject, uintptr_t aOffset, size_t aSize) -{ - const auto* address = aObject ? reinterpret_cast(reinterpret_cast(aObject) + aOffset) : nullptr; - aLine << " " << aLabel << "="; - if (!address || !IsReadableMemory(address, aSize)) - { - aLine << ""; - 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(address) + index); - aLine << std::setw(2) << static_cast(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(aPointer) < 0x10000 || !IsReadableMemory(aPointer, aSize)) - { - aLine << ""; - 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(aPointer) + index); - aLine << std::setw(2) << static_cast(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; -}; - -int GpsSpatialRoadClassRank(char aCode) -{ - switch (aCode) - { - case 'H': - return 4; - case 'R': - return 3; - case 'G': - return 2; - case 'P': - return 1; - default: - return 0; - } -} - -uint32_t GpsSpatialRoadClassIndex(char aCode) -{ - switch (aCode) - { - case 'H': - return 0; - case 'R': - return 1; - case 'G': - return 2; - case 'P': - return 3; - default: - return 4; - } -} - -const char* GpsSpatialRoadClassName(char aCode) -{ - switch (aCode) - { - case 'H': - return "highway"; - case 'R': - return "road"; - case 'G': - return "gpsonly"; - case 'P': - return "pavement"; - default: - return "unknown"; - } -} - -float GpsSpatialRoadClassMultiplier(char aCode) -{ - switch (aCode) - { - case 'H': - return gGpsSpatialMultipliers[0].load(std::memory_order_relaxed); - case 'R': - return gGpsSpatialMultipliers[1].load(std::memory_order_relaxed); - case 'G': - return gGpsSpatialMultipliers[2].load(std::memory_order_relaxed); - case 'P': - return gGpsSpatialMultipliers[3].load(std::memory_order_relaxed); - default: - return gGpsSpatialMultipliers[4].load(std::memory_order_relaxed); - } -} - -char LookupGpsSpatialRoadClass(float aX, float aMapY) -{ - using namespace EdgeWeightGPS::Generated; - - if (!std::isfinite(aX) || !std::isfinite(aMapY)) - { - return '.'; - } - - const auto columnFloat = (aX - kSpatialRoadGridMinX) / kSpatialRoadGridCellSize; - const auto rowFloat = (aMapY - kSpatialRoadGridMinY) / kSpatialRoadGridCellSize; - if (columnFloat < 0.0f || rowFloat < 0.0f) - { - return '.'; - } - - const auto column = static_cast(std::floor(columnFloat)); - const auto row = static_cast(std::floor(rowFloat)); - if (column < 0 || row < 0 || column >= static_cast(kSpatialRoadGridWidth) || - row >= static_cast(kSpatialRoadGridHeight)) - { - return '.'; - } - - const auto& rowData = kSpatialRoadGridRows[static_cast(row)]; - if (static_cast(column) >= rowData.size()) - { - return '.'; - } - - return rowData[static_cast(column)]; -} - -bool TryLookupGpsSpatialEdgeClass(void* aCurrentState, void* aNeighborState, char& aCode) -{ - Vector3Value current{}; - Vector3Value neighbor{}; - if (!aCurrentState || !aNeighborState || !TryReadMemory(aCurrentState, current) || - !TryReadMemory(aNeighborState, neighbor)) - { - return false; - } - - const auto midpointX = (current.x + neighbor.x) * 0.5f; - const auto midpointMapY = (current.z + neighbor.z) * 0.5f; - - // Search states are (world X, height, world Y); route producer query vectors use (X, Y, Z, W). - const auto midpointClass = LookupGpsSpatialRoadClass(midpointX, midpointMapY); - if (midpointClass != '.') - { - aCode = midpointClass; - return true; - } - - aCode = '.'; - const std::array candidates = { - LookupGpsSpatialRoadClass(current.x, current.z), - LookupGpsSpatialRoadClass(neighbor.x, neighbor.z), - }; - for (const auto candidate : candidates) - { - if (GpsSpatialRoadClassRank(candidate) > GpsSpatialRoadClassRank(aCode)) - { - aCode = candidate; - } - } - - return true; -} - -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 << ""; - } -} - -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 << ""; - } -} - -void AppendVector4Field(std::ostringstream& aLine, const char* aLabel, void* aObject, uintptr_t aOffset) -{ - if (!aObject) - { - aLine << " " << aLabel << "="; - return; - } - - AppendVector4Pointer(aLine, aLabel, reinterpret_cast(reinterpret_cast(aObject) + aOffset)); -} - -bool TryReadFloatField(void* aObject, uintptr_t aOffset, float& aValue) -{ - return aObject && TryReadMemory(reinterpret_cast(reinterpret_cast(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 << ""; - } -} - -uint8_t ReadGpsPointClass(void* aPoint) -{ - uint8_t value = 0xFF; - if (!aPoint || !TryReadMemory(reinterpret_cast(reinterpret_cast(aPoint) + 0x13), value)) - { - return 0xFF; - } - - return value & 0x3F; -} - -uint16_t ReadGpsPointMask(void* aPoint) -{ - uint16_t value = 0; - if (!aPoint) - { - return 0; - } - - TryReadMemory(reinterpret_cast(reinterpret_cast(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(reinterpret_cast(aPoint) + 0x10), mask); - TryReadMemory(reinterpret_cast(reinterpret_cast(aPoint) + 0x12), byte12); - TryReadMemory(reinterpret_cast(reinterpret_cast(aPoint) + 0x13), byte13); - - aLine << " " << prefix << "_adj=" << adjacency; - aLine << " " << prefix << "_mask10=0x" << std::hex << mask << std::dec; - aLine << " " << prefix << "_b12=0x" << std::hex << static_cast(byte12); - aLine << " " << prefix << "_class13=" << std::dec << static_cast(byte13 & 0x3F); - aLine << " " << prefix << "_raw13=0x" << std::hex << static_cast(byte13) << std::dec; -} - -void AppendGpsPackedHandleSummary(std::ostringstream& aLine, const char* aPrefix, uint64_t aHandle) -{ - const auto pointIndex = aHandle & 0xFFFFF; - const auto segmentIndex = (aHandle >> 20) & 0x0FFFFFFF; - const auto generation = aHandle >> 48; - - aLine << " " << aPrefix << "_pointIndex=" << pointIndex; - aLine << " " << aPrefix << "_segmentIndex=" << segmentIndex; - aLine << " " << aPrefix << "_generation=" << generation; -} - -bool IsSelectedGpsResolveReturnRva(uintptr_t aReturnRva) -{ - constexpr std::array kSelectedReturnRvas = { - 0x44CB9E, - 0x44DD97, - }; - - return std::find(kSelectedReturnRvas.begin(), kSelectedReturnRvas.end(), aReturnRva) != - kSelectedReturnRvas.end(); -} - -size_t GetGpsResolveReturnBucket(uintptr_t aReturnRva) -{ - switch (aReturnRva) - { - case 0x44CB9E: - return 0; - case 0x44DD97: - return 1; - default: - return gGpsResolveHandleRetLogCounts.size(); - } -} - -void ExtendGpsResolveTraceWindow(uint64_t aNowMs) -{ - std::lock_guard lock(gGpsCostLogMutex); - const auto untilMs = aNowMs + kGpsResolveTraceWindowMs; - std::fill(gGpsResolveHandleRetLogCounts.begin(), gGpsResolveHandleRetLogCounts.end(), 0); - ++gGpsResolveTraceWindowSerial; - if (untilMs > gGpsResolveTraceWindowUntilMs) - { - gGpsResolveTraceWindowUntilMs = untilMs; - } -} - -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(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(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& 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 AppendMaskBitCounts(std::ostringstream& aLine, const char* aLabel, const std::array& aCounts) -{ - 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 (first) - { - aLine << "none"; - } - - aLine << "]"; -} - -void AppendGpsProviderFilterStats(std::ostringstream& aLine, const GpsProviderFilterStats& aStats) -{ - aLine << " filterCalls=" << aStats.total << " filterPass=" << aStats.passed - << " filterFail=" << aStats.failed << " filterBase=" << aStats.baseTotal - << " filterFiltered=" << aStats.filteredTotal; - AppendClassCounts(aLine, "filterPassClasses", aStats.passClasses, aStats.passInvalidClass); - AppendClassCounts(aLine, "filterFailClasses", aStats.failClasses, aStats.failInvalidClass); - AppendMaskBitCounts(aLine, "filterPassMaskBits", aStats.passMaskBits); - AppendMaskBitCounts(aLine, "filterFailMaskBits", aStats.failMaskBits); -} - -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(aStats.total)); - aLine.flags(flags); - aLine.precision(precision); - } - - AppendClassCounts(aLine, "edgeSrcClasses", aStats.sourceClasses, aStats.invalidSourceClass); - AppendClassCounts(aLine, "edgeDstClasses", aStats.neighborClasses, aStats.invalidNeighborClass); - aLine << " spatialAllowed=" << aStats.spatialPatchAllowed << " spatialLookupOk=" << aStats.spatialLookupOk - << " spatialLookupFailed=" << aStats.spatialLookupFailed << " spatialAdjusted=" << aStats.spatialAdjusted; - - aLine << " spatialClasses=["; - constexpr std::array spatialClassCodes = {'H', 'R', 'G', 'P', '.'}; - bool firstSpatialClass = true; - for (uint32_t index = 0; index < aStats.spatialClasses.size(); ++index) - { - const auto count = aStats.spatialClasses[index]; - if (count == 0) - { - continue; - } - - if (!firstSpatialClass) - { - aLine << ","; - } - firstSpatialClass = false; - aLine << spatialClassCodes[index] << ":" << count; - const auto flags = aLine.flags(); - const auto precision = aLine.precision(); - aLine << ":avgMult=" << std::fixed << std::setprecision(3) - << (aStats.spatialMultiplierSums[index] / static_cast(count)); - aLine.flags(flags); - aLine.precision(precision); - } - if (firstSpatialClass) - { - aLine << "none"; - } - aLine << "]"; - - 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(aStats.sampleCount, static_cast(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(sample.sourceClass) << ">" - << static_cast(sample.neighborClass) << " cost=" << std::fixed << std::setprecision(3) - << sample.cost << " original=" << sample.originalCost << " spatial=" << sample.spatialClass - << " mult=" << sample.spatialMultiplier << " allowed=" << static_cast(sample.spatialPatchAllowed) - << " lookup=" << static_cast(sample.spatialLookupOk) - << " adjusted=" << static_cast(sample.spatialAdjusted) << " 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 NoteGpsProviderFilterStats(GpsProviderFilterStats& aStats, bool aFilteredProvider, bool aPassed, - uint8_t aPointClass, uint16_t aPointMask) -{ - ++aStats.total; - if (aPassed) - { - ++aStats.passed; - } - else - { - ++aStats.failed; - } - - if (aFilteredProvider) - { - ++aStats.filteredTotal; - } - else - { - ++aStats.baseTotal; - } - - auto& classCounts = aPassed ? aStats.passClasses : aStats.failClasses; - auto& invalidClassCount = aPassed ? aStats.passInvalidClass : aStats.failInvalidClass; - if (aPointClass < classCounts.size()) - { - ++classCounts[aPointClass]; - } - else - { - ++invalidClassCount; - } - - auto& maskCounts = aPassed ? aStats.passMaskBits : aStats.failMaskBits; - for (uint32_t bit = 0; bit < maskCounts.size(); ++bit) - { - if ((aPointMask & (1u << bit)) != 0) - { - ++maskCounts[bit]; - } - } -} - -void NoteGpsEdgeCostStats(GpsEdgeCostStats& aStats, uint8_t aSourceClass, uint8_t aNeighborClass, float aCost, - float aOriginalCost, uintptr_t aReturnRva, uint64_t aCurrentHandle, - uint64_t aNeighborHandle, char aSpatialClass, float aSpatialMultiplier, - bool aSpatialPatchAllowed, bool aSpatialLookupOk, bool aSpatialAdjusted) -{ - ++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 (aSpatialPatchAllowed) - { - ++aStats.spatialPatchAllowed; - if (aSpatialLookupOk) - { - ++aStats.spatialLookupOk; - const auto spatialIndex = GpsSpatialRoadClassIndex(aSpatialClass); - if (spatialIndex < aStats.spatialClasses.size()) - { - ++aStats.spatialClasses[spatialIndex]; - aStats.spatialMultiplierSums[spatialIndex] += aSpatialMultiplier; - } - } - else - { - ++aStats.spatialLookupFailed; - } - - if (aSpatialAdjusted) - { - ++aStats.spatialAdjusted; - } - } - - 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.originalCost = aOriginalCost; - sample.spatialMultiplier = aSpatialMultiplier; - sample.sourceClass = aSourceClass; - sample.neighborClass = aNeighborClass; - sample.spatialClass = aSpatialClass; - sample.spatialPatchAllowed = aSpatialPatchAllowed; - sample.spatialLookupOk = aSpatialLookupOk; - sample.spatialAdjusted = aSpatialAdjusted; - } -} - -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(aJob); - const auto nodeAddress = reinterpret_cast(aNode); - if (!TryReadMemory(reinterpret_cast(jobAddress + 0xC4), aSignature.mode) || - aSignature.mode != 2) - { - return false; - } - - TryReadMemory(reinterpret_cast(jobAddress + 0x54), aSignature.retry); - TryReadMemory(reinterpret_cast(jobAddress + 0xC0), aSignature.flagsC0); - TryReadMemory(reinterpret_cast(jobAddress + 0xC8), aSignature.c8); - TryReadMemory(reinterpret_cast(nodeAddress + 0x86), aSignature.node86); - TryReadMemory(reinterpret_cast(nodeAddress + 0x88), aSignature.flags88); - TryReadMemory(reinterpret_cast(nodeAddress + 0x8C), aSignature.node8c); - TryReadMemory(reinterpret_cast(nodeAddress + 0x8E), aSignature.node8e); - TryReadMemory(reinterpret_cast(nodeAddress + 0x90), aSignature.node90); - TryReadMemory(reinterpret_cast(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(aSignature.c8) - << " node86=0x" << aSignature.node86 << " flags88=0x" << aSignature.flags88 - << " node8c=0x" << aSignature.node8c << " node8e=0x" << aSignature.node8e - << " node90=0x" << static_cast(aSignature.node90) - << " node93=0x" << static_cast(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); -} - -bool ShouldLogGpsRouteProducerStats(void* aQuery) -{ - if (!aQuery) - { - return false; - } - - Vector4Value start{}; - Vector4Value end{}; - if (!TryReadMemory(reinterpret_cast(reinterpret_cast(aQuery) + 0x00), start) || - !TryReadMemory(reinterpret_cast(reinterpret_cast(aQuery) + 0x10), end)) - { - return false; - } - - float radius = 0.0f; - TryReadFloatField(aQuery, 0x78, radius); - const auto* routeContext = ReadPointerField(aQuery, 0x98); - const auto deltaX = end.x - start.x; - const auto deltaY = end.y - start.y; - const auto deltaZ = end.z - start.z; - const auto distanceSquared = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ; - - return distanceSquared >= 4.0f || radius != 0.0f || routeContext != nullptr; -} - -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(reinterpret_cast(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(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(reinterpret_cast(data) + - static_cast(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(reinterpret_cast(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(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(reinterpret_cast(data) + - static_cast(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(reinterpret_cast(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(reinterpret_cast(data) + - static_cast(count - 1) * 12)); - } -} - -void AppendGpsResultObjectLightCounts(std::ostringstream& aLine, const char* aPrefix, void* aResultObject) -{ - if (!aResultObject) - { - aLine << " " << aPrefix << "_result="; - return; - } - - uint32_t routeCapacity = 0; - uint32_t routeCount = 0; - uint32_t pointCount = 0; - auto* routeData = ReadPointerField(aResultObject, 0x28); - auto* pointData = ReadPointerField(aResultObject, 0x38); - TryReadMemory(reinterpret_cast(reinterpret_cast(aResultObject) + 0x30), routeCapacity); - TryReadMemory(reinterpret_cast(reinterpret_cast(aResultObject) + 0x34), routeCount); - TryReadMemory(reinterpret_cast(reinterpret_cast(aResultObject) + 0x3C), pointCount); - - AppendPointerWithRva(aLine, aPrefix, aResultObject); - AppendPointerWithRva(aLine, (std::string(aPrefix) + "_routeData").c_str(), routeData); - aLine << " " << aPrefix << "_routeCapacity=" << routeCapacity << " " << aPrefix - << "_routeCount=" << routeCount; - AppendPointerWithRva(aLine, (std::string(aPrefix) + "_pointData").c_str(), pointData); - aLine << " " << aPrefix << "_pointCount=" << pointCount; -} - -bool TryReadGpsResultRouteCount(void* aResultObject, uint32_t& aRouteCount) -{ - aRouteCount = 0; - return aResultObject && - TryReadMemory(reinterpret_cast(reinterpret_cast(aResultObject) + 0x34), - aRouteCount); -} - -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(reinterpret_cast(aResultObject) + 0x30), capacity); - TryReadMemory(reinterpret_cast(reinterpret_cast(aResultObject) + 0x34), count); - - const auto prefix = std::string(aPrefix); - aLine << " " << prefix << "_ptr28_capacity30=" << capacity; - aLine << " " << prefix << "_ptr28_count34=" << count; - - if (!data || reinterpret_cast(data) < 0x10000 || count == 0 || count > 256) - { - return; - } - - constexpr uintptr_t kRouteRecordStride = 0x28; - const auto rawSize = std::min(0xA0, static_cast(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(data) + static_cast(index) * kRouteRecordStride; - const auto* record = reinterpret_cast(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(recordAddress + 0x08), u08) && - TryReadMemory(reinterpret_cast(recordAddress + 0x0C), u0c) && - TryReadMemory(reinterpret_cast(recordAddress + 0x10), u10) && - TryReadMemory(reinterpret_cast(recordAddress + 0x14), u14) && - TryReadMemory(reinterpret_cast(recordAddress + 0x18), h18) && - TryReadMemory(reinterpret_cast(recordAddress + 0x20), h20); - - if (index > 0) - { - aLine << ";"; - } - - aLine << index << ":"; - if (!hasRecord) - { - aLine << ""; - continue; - } - - const auto h18Lo = static_cast(h18); - const auto h18Hi = static_cast(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="; - 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(reinterpret_cast(aObject) + aOffset), - aValue); -} - -bool IsGpsRoadStyleMode(uint32_t aMode) -{ - return aMode == 0 || aMode == 2 || aMode == 4; -} - -bool IsDrivingRouteJob(void* aRouteSystem, void* aActiveEntry) -{ - uint32_t mode = 0; - if (TryReadUint32FieldValue(aActiveEntry, 0x80, mode) && IsGpsRoadStyleMode(mode)) - { - return true; - } - - auto* job = ReadPointerField(aRouteSystem, 0x90D0); - return TryReadUint32FieldValue(job, 0xC4, mode) && IsGpsRoadStyleMode(mode); -} - -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(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 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(reinterpret_cast(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 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(reinterpret_cast(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 AppendTrafficResultSummary(std::ostringstream& aLine, const char* aPrefix, void* aResult) -{ - const auto prefix = std::string(aPrefix); - AppendPointerWithRva(aLine, prefix.c_str(), aResult); - if (!aResult) - { - return; - } - - AppendUint32Field(aLine, (prefix + "_count0c").c_str(), aResult, 0x0C); - AppendPointerField(aLine, (prefix + "_records10").c_str(), aResult, 0x10); - AppendUint32Field(aLine, (prefix + "_u2c").c_str(), aResult, 0x2C); - AppendUint32Field(aLine, (prefix + "_u30").c_str(), aResult, 0x30); - AppendUint32Field(aLine, (prefix + "_u34").c_str(), aResult, 0x34); - - uint32_t count = 0; - auto* records = ReadPointerField(aResult, 0x10); - if (TryReadUint32FieldValue(aResult, 0x0C, count) && records) - { - const auto sampleCount = std::min(count, 4); - AppendBytesPointer(aLine, (prefix + "_records10_raw").c_str(), records, - static_cast(sampleCount) * 0x10); - } -} - -void AppendTrafficResultHolderSummary(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); - AppendTrafficResultSummary(aLine, (prefix + "_value").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; - query.resultFetchCalls = 0; - query.statusCalls = 0; - query.momentumCallsAStart = gGpsMomentumPenaltyCallsA.load(std::memory_order_relaxed); - query.momentumCallsBStart = gGpsMomentumPenaltyCallsB.load(std::memory_order_relaxed); - query.momentumSummaryLogged = false; - 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; - query.momentumCallsAStart = gGpsMomentumPenaltyCallsA.load(std::memory_order_relaxed); - query.momentumCallsBStart = gGpsMomentumPenaltyCallsB.load(std::memory_order_relaxed); - query.momentumSummaryLogged = false; -} - -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 TryMarkTrackedGpsQueryMomentumSummary(uint32_t aQueryId, void* aManager, TrackedGpsQuery& aSnapshot, - uint64_t& aCallsA, uint64_t& aCallsB, uint64_t& aGlobalCallsA, - uint64_t& aGlobalCallsB) -{ - std::lock_guard lock(gTrackedGpsQueryMutex); - - for (auto& query : gTrackedGpsQueries) - { - if (!query.active || query.id != aQueryId || query.manager != aManager || query.momentumSummaryLogged) - { - continue; - } - - aGlobalCallsA = gGpsMomentumPenaltyCallsA.load(std::memory_order_relaxed); - aGlobalCallsB = gGpsMomentumPenaltyCallsB.load(std::memory_order_relaxed); - aCallsA = aGlobalCallsA - query.momentumCallsAStart; - aCallsB = aGlobalCallsB - query.momentumCallsBStart; - aSnapshot = query; - query.momentumSummaryLogged = true; - 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 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(reinterpret_cast(aSystem) + 0x1A0), activeRouteMap); - TryReadMemory(reinterpret_cast(reinterpret_cast(aSystem) + 0x1AC), activeRouteCount); - TryReadMemory(reinterpret_cast(reinterpret_cast(aSystem) + 0x1B0), activeRouteFlags); - TryReadMemory(reinterpret_cast(reinterpret_cast(aSystem) + 0x288), observerBase); - TryReadMemory(reinterpret_cast(reinterpret_cast(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(reinterpret_cast(aEvent) + 0x18), routeId); - TryReadMemory(reinterpret_cast(reinterpret_cast(aEvent) + 0x20), system); - TryReadMemory(reinterpret_cast(reinterpret_cast(aEvent) + 0x28), active); - } - - std::ostringstream line; - line << "route-event-fields " << aName << " call=" << aCount << " routeId=" << routeId << " active=" - << static_cast(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(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(GetModuleHandleW(nullptr)); - if (!imageBase) - { - LogRed4ext("exe-string scan skipped: main module unavailable"); - return; - } - - const auto* dos = reinterpret_cast(imageBase); - if (dos->e_magic != IMAGE_DOS_SIGNATURE) - { - LogRed4ext("exe-string scan skipped: bad DOS header"); - return; - } - - const auto* nt = reinterpret_cast(imageBase + dos->e_lfanew); - if (nt->Signature != IMAGE_NT_SIGNATURE) - { - LogRed4ext("exe-string scan skipped: bad NT header"); - return; - } - - const auto imageSize = static_cast(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 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(cursor), &mbi, sizeof(mbi))) - { - break; - } - - const auto regionBegin = reinterpret_cast(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(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(aThis); - const auto hasBase = TryReadMemory(reinterpret_cast(objectAddress + kJournalListenerArrayOffset), - listenerBase); - const auto hasCount = TryReadMemory(reinterpret_cast(objectAddress + kJournalListenerCountOffset), - listenerCount); - - std::ostringstream header; - header << "journal-listeners dump=" << gJournalTrackEntryListenerDumpCount << " call=" << aCallCount - << " hasBase=" << hasBase << " hasCount=" << hasCount << " count=" << listenerCount; - AppendPointerWithRva(header, "base", reinterpret_cast(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(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(vtable); - TryReadMemory(reinterpret_cast(vtableAddress + 0x28), callback28); - TryReadMemory(reinterpret_cast(vtableAddress + 0x30), callback30); - TryReadMemory(reinterpret_cast(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(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(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(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(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(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(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++; - uint32_t queryKind = 0; - const auto hasQueryKind = aQuery && TryReadMemory(reinterpret_cast(reinterpret_cast(aQuery) + 0xCC), - queryKind); - const auto shouldTraceResolver = - returnRva == kGpsMapRouteSubmitReturnRva && hasQueryKind && queryKind == 5; - const auto shouldLog = - callCount < kMaxCalls && - (kEnableGpsQueryLifecycleHooks || (returnRva == kGpsMapRouteSubmitReturnRva && hasQueryKind && queryKind == 5)); - if (shouldTraceResolver) - { - ExtendGpsResolveTraceWindow(GetElapsedMs()); - } - - 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); - if (shouldTraceResolver) - { - line << " resolveTraceWindowMs=" << kGpsResolveTraceWindowMs; - } - AppendReturnRva(line, returnAddress); - LogRed4ext(line.str()); - } - - int32_t result = -1; - if (gOriginalGpsQuerySubmit) - { - result = gOriginalGpsQuerySubmit(aManager, aQuery); - } - - if (result >= 0) - { - TrackGpsQuery(static_cast(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 isMapRouteQuery = isTracked && trackedQuery.submitReturnRva == kGpsMapRouteSubmitReturnRva; - const auto shouldLog = - kEnableGpsQueryLifecycleHooks - ? ((isTracked && (perQueryCall < kMaxTrackedSamplesPerQuery || result)) || - (!isTracked && callCount < kMaxUntrackedSamples && aQueryId < 2)) - : (result && isMapRouteQuery); - - uint32_t gpsResultRouteCount = 0; - const auto hasGpsResultRouteCount = TryReadGpsResultRouteCount(aOutPath, gpsResultRouteCount); - if (result && isMapRouteQuery && hasGpsResultRouteCount && gpsResultRouteCount > 0) - { - TrackedGpsQuery summaryQuery{}; - uint64_t callsA = 0; - uint64_t callsB = 0; - uint64_t globalCallsA = 0; - uint64_t globalCallsB = 0; - if (TryMarkTrackedGpsQueryMomentumSummary(aQueryId, aManager, summaryQuery, callsA, callsB, globalCallsA, - globalCallsB)) - { - const auto totalCalls = callsA + callsB; - const auto fixedPenalty = gGpsMomentumFixedEdgePenalty.load(std::memory_order_relaxed); - std::ostringstream line; - line << "gps-momentum-summary queryId=" << aQueryId << " resultFetchCall=" << perQueryCall; - AppendTrackedGpsQuery(line, summaryQuery); - line << " callsA=" << callsA << " callsB=" << callsB << " callsTotal=" << totalCalls - << " fixedPenalty=" << fixedPenalty - << " penaltyTotal=" << static_cast(totalCalls) * static_cast(fixedPenalty) - << " globalCallsA=" << globalCallsA << " globalCallsB=" << globalCallsB - << " gpsResultRouteCount=" << gpsResultRouteCount; - AppendGpsResultObjectLightCounts(line, "gpsResult", aOutPath); - LogRed4ext(line.str()); - } - } - - if (shouldLog) - { - std::ostringstream line; - line << "hook GPSQueryResultFetch 0x7094b8 call=" << callCount << " queryId=" << aQueryId - << " tracked=" << static_cast(isTracked) << " perQueryCall=" << perQueryCall - << " value=" << static_cast(result); - if (isTracked) - { - AppendTrackedGpsQuery(line, trackedQuery); - } - AppendPointerWithRva(line, "manager", aManager); - AppendPointerWithRva(line, "outPath", aOutPath); - AppendReturnRva(line, returnAddress); - AppendPathBufferSummary(line, "outPath", aOutPath); - if (kEnableGpsQueryResultRecordDump && result && isMapRouteQuery) - { - 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(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* DetourGpsTrafficResultTake(void* aTrafficService, void* aOutResult, uint32_t aTicket, void* aOutMeta) -{ - constexpr uint32_t kMaxCalls = 128; - - const auto callCount = NextGpsAsyncLogCall(gGpsTrafficResultTakeLogCount); - const auto shouldLog = callCount < kMaxCalls; - if (shouldLog) - { - std::ostringstream line; - line << "hook GPSTrafficResultTake 0x709d5c call=" << callCount << " ticket=" << aTicket; - AppendPointerWithRva(line, "trafficService", aTrafficService); - AppendTrafficResultHolderSummary(line, "outResultBefore", aOutResult); - AppendPointerWithRva(line, "outMeta", aOutMeta); - AppendBytesPointer(line, "outMeta_raw10_before", aOutMeta, 0x10); - AppendReturnRva(line, __builtin_return_address(0)); - LogRed4ext(line.str()); - } - - void* result = nullptr; - if (gOriginalGpsTrafficResultTake) - { - result = gOriginalGpsTrafficResultTake(aTrafficService, aOutResult, aTicket, aOutMeta); - } - - if (shouldLog) - { - std::ostringstream line; - line << "hook GPSTrafficResultTake result call=" << callCount; - AppendPointerWithRva(line, "value", result); - AppendTrafficResultHolderSummary(line, "outResultAfter", aOutResult); - AppendPointerWithRva(line, "outMeta", aOutMeta); - AppendBytesPointer(line, "outMeta_raw10_after", aOutMeta, 0x10); - LogRed4ext(line.str()); - } - - return result; -} - -void DetourGpsRoutePostprocess(void* aOutPath, void* aTrafficResult, void* aRouteRecords, float aScale) -{ - constexpr uint32_t kMaxCalls = 128; - - const auto callCount = NextGpsAsyncLogCall(gGpsRoutePostprocessLogCount); - const auto shouldLog = callCount < kMaxCalls; - if (shouldLog) - { - std::ostringstream line; - line << "hook GPSRoutePostprocess 0x44830c call=" << callCount << " scale=" << aScale; - AppendPointerWithRva(line, "outPath", aOutPath); - AppendTrafficResultSummary(line, "trafficResult", aTrafficResult); - AppendPointerWithRva(line, "routeRecords", aRouteRecords); - AppendReturnRva(line, __builtin_return_address(0)); - LogRed4ext(line.str()); - } - - if (gOriginalGpsRoutePostprocess) - { - gOriginalGpsRoutePostprocess(aOutPath, aTrafficResult, aRouteRecords, aScale); - } - - if (shouldLog) - { - std::ostringstream line; - line << "hook GPSRoutePostprocess result call=" << callCount; - AppendGpsResultObjectSummary(line, "outPath", aOutPath); - LogRed4ext(line.str()); - } -} - -const char* TrafficLaneCategory(uint16_t aFlags) -{ - if ((aFlags & kTrafficLaneFlagHighway) != 0) - { - return "highway"; - } - if ((aFlags & kTrafficLaneFlagRoad) != 0) - { - return "road"; - } - if ((aFlags & kTrafficLaneFlagPavement) != 0) - { - return "pavement"; - } - if ((aFlags & kTrafficLaneFlagGpsOnly) != 0) - { - return "gpsonly"; - } - - return "other"; -} - -float GpsSolverNodeWeightMultiplier(uint16_t aFlags) -{ - if ((aFlags & kTrafficLaneFlagHighway) != 0) - { - return gGpsSolverHighwayMultiplier.load(std::memory_order_relaxed); - } - if ((aFlags & kTrafficLaneFlagGpsOnly) != 0) - { - return kGpsSolverGpsOnlyMultiplier; - } - if ((aFlags & kTrafficLaneFlagPavement) != 0) - { - return kGpsSolverPavementMultiplier; - } - - return 1.0f; -} - -float ReadGpsSolverNonRoadMultiplier(uint32_t aRetry) -{ - if (aRetry < kGpsSolverNonRoadMultipliers.size()) - { - return kGpsSolverNonRoadMultipliers[aRetry]; - } - - return 1.0f; -} - -float TrafficEdgeScoreMultiplier(uint16_t aFlags, bool aDriving) -{ - if (!aDriving) - { - return 1.0f; - } - - if ((aFlags & kTrafficLaneFlagHighway) != 0) - { - return 0.35f; - } - if ((aFlags & kTrafficLaneFlagRoad) != 0) - { - return 0.70f; - } - if ((aFlags & kTrafficLaneFlagGpsOnly) != 0) - { - return 1.35f; - } - if ((aFlags & kTrafficLaneFlagPavement) != 0) - { - return 2.75f; - } - - return 1.50f; -} - -float DetourGpsTrafficEdgeScore(void* aSearchContext, void* aCandidate, void* aReferencePoint) -{ - float baseScore = 0.0f; - if (gOriginalGpsTrafficEdgeScore) - { - baseScore = gOriginalGpsTrafficEdgeScore(aSearchContext, aCandidate, aReferencePoint); - } - - auto* query = ReadPointerField(aSearchContext, 0x10); - auto* lane = ReadPointerField(aCandidate, 0x00); - uint8_t queryMode = 0xFF; - uint16_t laneFlags = 0; - if (query) - { - TryReadMemory(reinterpret_cast(reinterpret_cast(query) + 0x41), queryMode); - } - if (lane) - { - TryReadMemory(reinterpret_cast(reinterpret_cast(lane) + 0x88), laneFlags); - } - - const auto driving = queryMode != 0 && queryMode != 0xFF; - const auto multiplier = TrafficEdgeScoreMultiplier(laneFlags, driving); - const auto patchedScore = baseScore * multiplier; - - constexpr LONG kMaxLoggedCalls = 64; - LONG callCount = -1; - if (InterlockedCompareExchange(&gGpsTrafficEdgeScoreLogCount, 0, 0) < kMaxLoggedCalls) - { - callCount = InterlockedIncrement(&gGpsTrafficEdgeScoreLogCount) - 1; - } - - if (callCount >= 0 && callCount < kMaxLoggedCalls) - { - std::ostringstream line; - const auto flags = line.flags(); - const auto precision = line.precision(); - line << "hook GPSTrafficEdgeScore 0x8d46cc call=" << callCount - << " category=" << TrafficLaneCategory(laneFlags) - << " driving=" << static_cast(driving) - << " query41=" << static_cast(queryMode) - << " laneFlags88=0x" << std::hex << laneFlags << std::dec - << " base=" << std::fixed << std::setprecision(3) << baseScore - << " multiplier=" << multiplier - << " result=" << patchedScore; - line.flags(flags); - line.precision(precision); - AppendPointerWithRva(line, "ctx", aSearchContext); - AppendPointerWithRva(line, "query", query); - AppendPointerWithRva(line, "candidate", aCandidate); - AppendPointerWithRva(line, "lane", lane); - AppendReturnRva(line, __builtin_return_address(0)); - LogRed4ext(line.str()); - } - - return patchedScore; -} - -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(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(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 = kEnableGpsLocalSearchTraceHooks || - (kEnableGpsSpatialEdgeWeightPatchStats && callCount < kMaxCalls && ShouldLogGpsRouteProducerStats(aQuery)); - } - - 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()); - } - - MaybeReloadGpsSpatialWeights(); - - 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(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()); - } - - GpsProviderFilterStats filterStats{}; - auto* previousFilterStats = gActiveGpsProviderFilterStats; - gActiveGpsProviderFilterStats = &filterStats; - - int32_t result = 0x80000008; - if (gOriginalGpsSearch) - { - ScopedGpsProviderClassPatch providerPatch(aCostProvider, callCount); - result = gOriginalGpsSearch(aGraph, aStartHandle, aTargetHandle, aStartPoint, aTargetPoint, aCostProvider, - aOutPath, aOutCount); - } - - gActiveGpsProviderFilterStats = previousFilterStats; - - 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(result) << std::dec << " hasOutCount=" << static_cast(hasOutCount); - if (hasOutCount) - { - line << " outCount=" << outCount; - } - AppendGpsProviderFilterStats(line, filterStats); - LogRed4ext(line.str()); - } - - return result; -} - -bool DetourGpsFilteredProviderFilter(void* aCostProvider, uint64_t aHandle, void* aSegment, void* aPoint) -{ - bool result = false; - if (gOriginalGpsFilteredProviderFilter) - { - result = gOriginalGpsFilteredProviderFilter(aCostProvider, aHandle, aSegment, aPoint); - } - - if (gActiveGpsProviderFilterStats) - { - NoteGpsProviderFilterStats(*gActiveGpsProviderFilterStats, true, result, ReadGpsPointClass(aPoint), - ReadGpsPointMask(aPoint)); - } - - return result; -} - -bool DetourGpsBaseProviderFilter(void* aCostProvider, uint64_t aHandle, void* aSegment, void* aPoint) -{ - bool result = false; - if (gOriginalGpsBaseProviderFilter) - { - result = gOriginalGpsBaseProviderFilter(aCostProvider, aHandle, aSegment, aPoint); - } - - if (gActiveGpsProviderFilterStats) - { - NoteGpsProviderFilterStats(*gActiveGpsProviderFilterStats, false, result, ReadGpsPointClass(aPoint), - ReadGpsPointMask(aPoint)); - } - - return result; -} - -int32_t DetourGpsResolveHandle(void* aGraph, uint64_t aHandle, void* aOutSegment, void* aOutPoint) -{ - const auto returnAddress = __builtin_return_address(0); - uintptr_t returnRva = 0; - TryGetImageRva(returnAddress, returnRva); - - int32_t result = 0x80000008; - if (gOriginalGpsResolveHandle) - { - result = gOriginalGpsResolveHandle(aGraph, aHandle, aOutSegment, aOutPoint); - } - - void* segment = nullptr; - void* point = nullptr; - const auto hasSegment = aOutSegment && TryReadMemory(aOutSegment, segment); - const auto hasPoint = aOutPoint && TryReadMemory(aOutPoint, point); - - const auto pointIndex = aHandle & 0xFFFFF; - uint32_t callCount = 0; - uint32_t returnCallCount = 0; - uint32_t traceWindowSerial = 0; - bool inTraceWindow = false; - bool shouldLog = false; - { - std::lock_guard lock(gGpsCostLogMutex); - callCount = gGpsResolveHandleLogCount++; - const auto returnBucket = GetGpsResolveReturnBucket(returnRva); - inTraceWindow = GetElapsedMs() <= gGpsResolveTraceWindowUntilMs; - traceWindowSerial = gGpsResolveTraceWindowSerial; - if (inTraceWindow && returnBucket < gGpsResolveHandleRetLogCounts.size()) - { - returnCallCount = gGpsResolveHandleRetLogCounts[returnBucket]++; - } - shouldLog = result >= 0 && hasSegment && hasPoint && - ((IsSelectedGpsResolveReturnRva(returnRva) && inTraceWindow && - returnCallCount < kGpsResolveSelectedRetLogLimit) || - callCount < kGpsResolveStartupLogLimit); - } - - if (shouldLog) - { - auto* segmentMeta = ReadPointerField(segment, 0x08); - auto* pointArray = ReadPointerField(segment, 0x10); - auto* coordArray = ReadPointerField(segment, 0x18); - auto* aux20 = ReadPointerField(segment, 0x20); - auto* aux28 = ReadPointerField(segment, 0x28); - auto* aux30 = ReadPointerField(segment, 0x30); - auto* aux38 = ReadPointerField(segment, 0x38); - - const auto auxPointOffset = pointIndex * 12; - auto* aux28Point = aux28 ? reinterpret_cast(reinterpret_cast(aux28) + auxPointOffset) : nullptr; - - std::ostringstream line; - line << "hook GPSResolveHandle 0x44e1a8 call=" << callCount << " retCall=" << returnCallCount - << " traceWindow=" << traceWindowSerial << " inTraceWindow=" << (inTraceWindow ? 1 : 0) - << " value=0x" << std::hex << static_cast(result) << " handle=0x" << aHandle << std::dec; - AppendGpsPackedHandleSummary(line, "handle", aHandle); - AppendPointerWithRva(line, "graph", aGraph); - AppendPointerWithRva(line, "outSegment", aOutSegment); - AppendPointerWithRva(line, "outPoint", aOutPoint); - AppendPointerWithRva(line, "segment", segment); - AppendPointerWithRva(line, "point", point); - AppendPointerWithRva(line, "segment_p08", segmentMeta); - AppendPointerWithRva(line, "segment_p10", pointArray); - AppendPointerWithRva(line, "segment_p18", coordArray); - AppendPointerWithRva(line, "segment_p20", aux20); - AppendPointerWithRva(line, "segment_p28", aux28); - AppendPointerWithRva(line, "segment_p30", aux30); - AppendPointerWithRva(line, "segment_p38", aux38); - AppendGpsPointSummary(line, "pointSummary", point); - const auto shouldDumpBytes = - callCount < kGpsResolveVerboseDumpLimit || - (inTraceWindow && returnCallCount < kGpsResolveVerboseDumpLimit); - line << " dump=" << (shouldDumpBytes ? 1 : 0); - if (shouldDumpBytes) - { - AppendBytesPointer(line, "segment_raw68", segment, 0x68); - AppendBytesPointer(line, "point_raw14", point, 0x14); - AppendBytesPointer(line, "segment_p08_raw30", segmentMeta, 0x30); - AppendBytesPointer(line, "segment_p20_raw40", aux20, 0x40); - AppendBytesPointer(line, "segment_p28_point_raw18", aux28Point, 0x18); - AppendBytesPointer(line, "segment_p30_raw40", aux30, 0x40); - AppendBytesPointer(line, "segment_p38_raw40", aux38, 0x40); - } - if (gActiveGpsRouteProducerCall != UINT32_MAX) - { - line << " routeProducerCall=" << gActiveGpsRouteProducerCall; - } - AppendReturnRva(line, returnAddress); - 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); - const auto originalResult = result; - - char spatialClass = '.'; - float spatialMultiplier = 1.0f; - bool spatialAdjusted = false; - bool spatialLookupOk = false; - const auto spatialPatchAllowed = - kEnableGpsSpatialEdgeWeightPatch && - (!kGpsSpatialPatchRequiresRouteProducer || gActiveGpsRouteProducerCall != UINT32_MAX); - if (spatialPatchAllowed && result > 0.0f && std::isfinite(result) && - TryLookupGpsSpatialEdgeClass(aCurrentState, aNeighborState, spatialClass)) - { - spatialLookupOk = true; - spatialMultiplier = GpsSpatialRoadClassMultiplier(spatialClass); - result *= spatialMultiplier; - spatialAdjusted = spatialMultiplier != 1.0f; - } - - if (gActiveGpsEdgeCostStats) - { - NoteGpsEdgeCostStats(*gActiveGpsEdgeCostStats, sourceClass, neighborClass, result, originalResult, returnRva, - aCurrentHandle, aNeighborHandle, spatialClass, spatialMultiplier, spatialPatchAllowed, - spatialLookupOk, spatialAdjusted); - } - - float multiplier = 0.0f; - const auto hasMultiplier = - neighborClass < 64 && - TryReadFloatField(aCostProvider, 0x08 + static_cast(neighborClass) * sizeof(float), multiplier); - - uint32_t callCount = 0; - uint32_t classCallCount = 0; - bool shouldLog = false; - { - std::lock_guard lock(gGpsCostLogMutex); - callCount = gGpsEdgeCostLogCount++; - if (neighborClass < gGpsEdgeCostClassLogCounts.size()) - { - classCallCount = gGpsEdgeCostClassLogCounts[neighborClass]++; - } - shouldLog = - (kEnableGpsLocalSearchTraceHooks && (callCount < 32 || (neighborClass < 64 && classCallCount < 1))) || - (kEnableGpsSpatialEdgeWeightPatchTrace && spatialAdjusted && callCount < 128); - } - - if (shouldLog) - { - const auto baseCost = hasMultiplier && multiplier != 0.0f ? originalResult / multiplier : 0.0f; - - std::ostringstream line; - line << "hook GPSEdgeCost 0x44f838 call=" << callCount << " classCall=" << classCallCount - << " sourceClass=" << static_cast(sourceClass) - << " neighborClass=" << static_cast(neighborClass) << " value=" << result; - if (spatialPatchAllowed) - { - line << " originalValue=" << originalResult << " spatialClass=" << GpsSpatialRoadClassName(spatialClass) - << " spatialMultiplier=" << spatialMultiplier - << " spatialAdjusted=" << static_cast(spatialAdjusted); - } - if (hasMultiplier) - { - line << " multiplier=" << multiplier << " baseCost=" << baseCost; - } - else - { - line << " multiplier="; - } - 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(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); - } - - if (kEnableGpsMultiplierSignatureTrace) - { - LogGpsMultiplierSignature("GPSAuxMultiplier", kGpsAuxMultiplierRva, true, aJob, aNode, result, - __builtin_return_address(0)); - } - return result; -} - -float DetourGpsNodeMultiplier(void* aJob, void* aNode) -{ - float result = 1.0f; - uint32_t mode = 0; - const auto jobAddress = reinterpret_cast(aJob); - if (!aJob || !TryReadMemory(reinterpret_cast(jobAddress + 0xC4), mode)) - { - return result; - } - - uint16_t nodeFlags = 0; - const auto nodeAddress = reinterpret_cast(aNode); - - if (mode == 1) - { - if (!aNode || !TryReadMemory(reinterpret_cast(nodeAddress + 0x88), nodeFlags)) - { - return result; - } - - return (nodeFlags & kTrafficLaneFlagPavement) != 0 ? 1.0f : 1.5f; - } - - if (mode != 0 && mode != 2 && mode != 4) - { - return result; - } - - if (!aNode || !TryReadMemory(reinterpret_cast(nodeAddress + 0x88), nodeFlags)) - { - return result; - } - - if ((nodeFlags & kTrafficLaneFlagRoad) != 0) - { - result = (nodeFlags & 0x0001) != 0 ? 1.0f : 1.2f; - - uint8_t node93 = 0; - if (TryReadMemory(reinterpret_cast(nodeAddress + 0x93), node93) && node93 == 1) - { - result *= 1.1f; - } - } - else - { - uint32_t retry = 0; - TryReadMemory(reinterpret_cast(jobAddress + 0x54), retry); - result = ReadGpsSolverNonRoadMultiplier(retry); - } - - if (IsGpsRoadStyleMode(mode) && std::isfinite(result)) - { - result *= GpsSolverNodeWeightMultiplier(nodeFlags); - } - - if (kEnableGpsMultiplierSignatureTrace) - { - 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(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(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()); + return ok; } - -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(&DetourObserver1ServiceRouteLookup), - reinterpret_cast(&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(GetImageBase() + kMappinSystemResolverRva); - void* system = resolver ? resolver(aThis) : nullptr; - auto* vtable = system ? *reinterpret_cast(system) : nullptr; - - if (gMappinSystemSlotLogCount < 16) - { - LogMappinSystemSlots(gMappinSystemSlotLogCount, system, vtable); - ++gMappinSystemSlotLogCount; - } - - if (gMappinSlotHookAttachAttempted || !vtable) - { - return; - } - - gMappinSlotHookAttachAttempted = true; - gMappinUpdateCustomPositionTarget = - *reinterpret_cast(reinterpret_cast(vtable) + kMappinUpdateCustomPositionSlotOffset); - gMappinSetTrackedTarget = - *reinterpret_cast(reinterpret_cast(vtable) + kMappinSetTrackedSlotOffset); - gMappinClearTrackedTarget = - *reinterpret_cast(reinterpret_cast(vtable) + kMappinClearTrackedSlotOffset); - gMappinFramePathTarget = - *reinterpret_cast(reinterpret_cast(vtable) + kMappinFramePathSlotOffset); - gMappinPositionQueryTarget = - *reinterpret_cast(reinterpret_cast(vtable) + kMappinPositionQuerySlotOffset); - gMappinCreateCustomPositionTarget = - *reinterpret_cast(reinterpret_cast(vtable) + kMappinCreateCustomPositionSlotOffset); - gMappinReleaseQueryTarget = - *reinterpret_cast(reinterpret_cast(vtable) + kMappinReleaseQuerySlotOffset); - gMappinActiveRouteTarget = - *reinterpret_cast(reinterpret_cast(vtable) + kMappinActiveRouteSlotOffset); - - AttachAbsoluteHook("MappinSystem::UpdateCustomPosition slot1f0", gMappinUpdateCustomPositionTarget, - reinterpret_cast(&DetourMappinUpdateCustomPosition), - reinterpret_cast(&gOriginalMappinUpdateCustomPosition)); - AttachAbsoluteHook("MappinSystem::SetTracked slot220", gMappinSetTrackedTarget, - reinterpret_cast(&DetourMappinSetTracked), - reinterpret_cast(&gOriginalMappinSetTracked)); - AttachAbsoluteHook("MappinSystem::ClearTracked slot228", gMappinClearTrackedTarget, - reinterpret_cast(&DetourMappinClearTracked), - reinterpret_cast(&gOriginalMappinClearTracked)); - AttachAbsoluteHook("MappinSystem::FramePath slot280", gMappinFramePathTarget, - reinterpret_cast(&DetourMappinFramePath), - reinterpret_cast(&gOriginalMappinFramePath)); - AttachAbsoluteHook("MappinSystem::PositionQuery slot2e0", gMappinPositionQueryTarget, - reinterpret_cast(&DetourMappinPositionQuery), - reinterpret_cast(&gOriginalMappinPositionQuery)); - AttachAbsoluteHook("MappinSystem::CreateCustomPosition slot2f0", gMappinCreateCustomPositionTarget, - reinterpret_cast(&DetourMappinCreateCustomPosition), - reinterpret_cast(&gOriginalMappinCreateCustomPosition)); - AttachAbsoluteHook("MappinSystem::ReleaseQuery slot368", gMappinReleaseQueryTarget, - reinterpret_cast(&DetourMappinReleaseQuery), - reinterpret_cast(&gOriginalMappinReleaseQuery)); - AttachAbsoluteHook("MappinSystem::ActiveRoute slot3a8", gMappinActiveRouteTarget, - reinterpret_cast(&DetourMappinActiveRoute), - reinterpret_cast(&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; - } - - MaybeReloadGpsSolverWeights(); - MaybeReloadGpsMomentumWeights(); - - 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) @@ -5939,13 +352,9 @@ 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(frequency.QuadPart); - gLogStartTick = static_cast(counter.QuadPart); + DisableThreadLibraryCalls(aModule); } + return TRUE; } @@ -5956,407 +365,21 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e gSdk = aSdk; std::ostringstream line; - line << "EdgeWeightGPS Main reason=" << ReasonToString(aReason) << " handle=" << aHandle << " sdk=" << aSdk; + line << "MomentumGPS Main reason=" << ReasonToString(aReason) << " module=" << gModule; if (aSdk && aSdk->runtime) { line << " runtime=" << static_cast(aSdk->runtime->major) << "." << aSdk->runtime->minor << "." << aSdk->runtime->patch; } - LogRed4ext(line.str()); + LogInfo(line.str()); - if (aReason == RED4ext::v1::EMainReason::Load && aSdk && aSdk->gameStates && aSdk->gameStates->Add) + if (aReason == RED4ext::v1::EMainReason::Load) { - const auto added = aSdk->gameStates->Add(aHandle, kGameStateRunning, &gRunningState); - LogRed4ext(added ? "registered Running game-state callbacks" : "failed to register Running game-state callbacks"); - - if (kEnableGpsSpatialEdgeWeightPatch) - { - std::array values = { - gGpsSpatialMultipliers[0].load(std::memory_order_relaxed), - gGpsSpatialMultipliers[1].load(std::memory_order_relaxed), - gGpsSpatialMultipliers[2].load(std::memory_order_relaxed), - gGpsSpatialMultipliers[3].load(std::memory_order_relaxed), - gGpsSpatialMultipliers[4].load(std::memory_order_relaxed), - }; - std::ostringstream line; - line << "spatial weights path=" << GetSpatialWeightsPath().string(); - LogRed4ext(line.str()); - LogGpsSpatialMultipliers("spatial weights defaults", values); - } - - if (kEnableGpsNodeMultiplierInlinePatch) - { - std::ostringstream line; - line << "solver weights path=" << GetSolverWeightsPath().string(); - LogRed4ext(line.str()); - MaybeReloadGpsSolverWeights(true); - LogGpsSolverHighwayMultiplier("solver weights active", - gGpsSolverHighwayMultiplier.load(std::memory_order_relaxed)); - } - - if (kEnableGpsMomentumPenaltyInlinePatch) - { - std::ostringstream line; - line << "momentum weights path=" << GetMomentumWeightsPath().string(); - LogRed4ext(line.str()); - MaybeReloadGpsMomentumWeights(true); - LogGpsMomentumFixedEdgePenalty("momentum fixed-edge penalty active", - gGpsMomentumFixedEdgePenalty.load(std::memory_order_relaxed)); - } - - InstallGpsNodeMultiplierInlinePatch(); - InstallGpsMomentumPenaltyInlinePatch(); - - if (kEnableGpsQueryLifecycleHooks) - { - AttachProbeHook("RunGPSQuery body 0x29bd128", kRunGpsQueryBodyRva, - reinterpret_cast(&DetourRunGpsQueryBody), - reinterpret_cast(&gOriginalRunGpsQueryBody)); - AttachProbeHook("UpdateGPSQuery body 0x29bd254", kUpdateGpsQueryBodyRva, - reinterpret_cast(&DetourUpdateGpsQueryBody), - reinterpret_cast(&gOriginalUpdateGpsQueryBody)); - } - if (kEnableGpsQueryLifecycleHooks || kEnableGpsQueryResultTraceHooks) - { - AttachProbeHook("GPSQuerySubmit 0x70a42c", kGpsQuerySubmitRva, - reinterpret_cast(&DetourGpsQuerySubmit), - reinterpret_cast(&gOriginalGpsQuerySubmit)); - AttachProbeHook("GPSQueryResultFetch 0x7094b8", kGpsQueryResultFetchRva, - reinterpret_cast(&DetourGpsQueryResultFetch), - reinterpret_cast(&gOriginalGpsQueryResultFetch)); - } - if (kEnableGpsQueryLifecycleHooks) - { - AttachProbeHook("GPSQueryDispatch 0x70a570", kGpsQueryDispatchRva, - reinterpret_cast(&DetourGpsQueryDispatch), - reinterpret_cast(&gOriginalGpsQueryDispatch)); - AttachProbeHook("GPSQueryStatus 0xaa5704", kGpsQueryStatusRva, - reinterpret_cast(&DetourGpsQueryStatus), - reinterpret_cast(&gOriginalGpsQueryStatus)); - } - if (kEnableGpsAsyncTrafficProbe) - { - AttachProbeHook("GPSDispatchAdvance 0x8d17d8", kGpsDispatchAdvanceRva, - reinterpret_cast(&DetourGpsDispatchAdvance), - reinterpret_cast(&gOriginalGpsDispatchAdvance)); - AttachProbeHook("GPSTrafficResultTake 0x709d5c", kGpsTrafficResultTakeRva, - reinterpret_cast(&DetourGpsTrafficResultTake), - reinterpret_cast(&gOriginalGpsTrafficResultTake)); - AttachProbeHook("GPSRoutePostprocess 0x44830c", kGpsRoutePostprocessRva, - reinterpret_cast(&DetourGpsRoutePostprocess), - reinterpret_cast(&gOriginalGpsRoutePostprocess)); - } - if (kEnableGpsTrafficEdgeWeightPatch) - { - AttachProbeHook("GPSTrafficEdgeScore 0x8d46cc", kGpsTrafficEdgeScoreRva, - reinterpret_cast(&DetourGpsTrafficEdgeScore), - reinterpret_cast(&gOriginalGpsTrafficEdgeScore)); - } - if (kEnableGpsRouteJobLifecycleHooks) - { - AttachProbeHook("GPSDispatchAdvance 0x8d17d8", kGpsDispatchAdvanceRva, - reinterpret_cast(&DetourGpsDispatchAdvance), - reinterpret_cast(&gOriginalGpsDispatchAdvance)); - AttachProbeHook("GPSAsyncWorkerTick 0x126b28", kGpsAsyncWorkerTickRva, - reinterpret_cast(&DetourGpsAsyncWorkerTick), - reinterpret_cast(&gOriginalGpsAsyncWorkerTick)); - AttachProbeHook("GPSRouteJobPoll 0x883cd8", kGpsRouteJobPollRva, - reinterpret_cast(&DetourGpsRouteJobPoll), - reinterpret_cast(&gOriginalGpsRouteJobPoll)); - AttachProbeHook("GPSRouteFinish 0x11f5f60", kGpsRouteFinishRva, - reinterpret_cast(&DetourGpsRouteFinish), - reinterpret_cast(&gOriginalGpsRouteFinish)); - AttachProbeHook("GPSRouteJobBuild 0x818ba8", kGpsRouteJobBuildRva, - reinterpret_cast(&DetourGpsRouteJobBuild), - reinterpret_cast(&gOriginalGpsRouteJobBuild)); - } - if (kEnableGpsCostTablePatch || kEnableGpsRouteJobStartTrace) - { - AttachProbeHook("GPSRouteJobStart 0x818928", kGpsRouteJobStartRva, - reinterpret_cast(&DetourGpsRouteJobStart), - reinterpret_cast(&gOriginalGpsRouteJobStart)); - } - if (kEnableGpsProviderClassPatch || kEnableGpsLocalSearchTraceHooks) - { - AttachProbeHook("GPSSearch 0x44f054", kGpsSearchRva, reinterpret_cast(&DetourGpsSearch), - reinterpret_cast(&gOriginalGpsSearch)); - } - if (kEnableGpsRouteProducerHook) - { - AttachProbeHook("GPSRouteProducer 0x44cc7c", kGpsRouteProducerRva, - reinterpret_cast(&DetourGpsRouteProducer), - reinterpret_cast(&gOriginalGpsRouteProducer)); - } - if (kEnableGpsLocalSearchTraceHooks) - { - AttachProbeHook("GPSResolveHandle 0x44e1a8", kGpsResolveHandleRva, - reinterpret_cast(&DetourGpsResolveHandle), - reinterpret_cast(&gOriginalGpsResolveHandle)); - } - if (kEnableGpsEdgeCostHook) - { - AttachProbeHook("GPSEdgeCost 0x44f838", kGpsEdgeCostRva, reinterpret_cast(&DetourGpsEdgeCost), - reinterpret_cast(&gOriginalGpsEdgeCost)); - } - if (kEnableGpsLocalSearchTraceHooks) - { - AttachProbeHook("GPSFilteredProviderFilter 0x44ff68", kGpsFilteredProviderFilterRva, - reinterpret_cast(&DetourGpsFilteredProviderFilter), - reinterpret_cast(&gOriginalGpsFilteredProviderFilter)); - AttachProbeHook("GPSBaseProviderFilter 0x450b08", kGpsBaseProviderFilterRva, - reinterpret_cast(&DetourGpsBaseProviderFilter), - reinterpret_cast(&gOriginalGpsBaseProviderFilter)); - } - if (kEnableGpsMultiplierHooks) - { - if (kEnableGpsAuxMultiplierHook) - { - AttachProbeHook("GPSAuxMultiplier 0x40bb00", kGpsAuxMultiplierRva, - reinterpret_cast(&DetourGpsAuxMultiplier), - reinterpret_cast(&gOriginalGpsAuxMultiplier)); - } - AttachProbeHook("GPSNodeMultiplier 0x40bb40", kGpsNodeMultiplierRva, - reinterpret_cast(&DetourGpsNodeMultiplier), - reinterpret_cast(&gOriginalGpsNodeMultiplier)); - } - if (kEnableRouteUiTraceHooks) - { - AttachProbeHook("GPSSystem/Tick", kGpsSystemTickRva, reinterpret_cast(&DetourGpsSystemTick), - reinterpret_cast(&gOriginalGpsSystemTick)); - AttachProbeHook("JournalManager::TrackEntry impl", kJournalTrackEntryImplRva, - reinterpret_cast(&DetourJournalTrackEntry), - reinterpret_cast(&gOriginalJournalTrackEntry)); - if (kEnableVerboseJournalListenerHooks) - { - AttachProbeHook("JournalListener::Objective vt28", kJournalListenerObjectiveVt28Rva, - reinterpret_cast(&DetourJournalListenerObjectiveVt28), - reinterpret_cast(&gOriginalJournalListenerObjectiveVt28)); - AttachProbeHook("JournalListener::Objective vt30", kJournalListenerObjectiveVt30Rva, - reinterpret_cast(&DetourJournalListenerObjectiveVt30), - reinterpret_cast(&gOriginalJournalListenerObjectiveVt30)); - AttachProbeHook("JournalListener::Route vt28", kJournalListenerRouteVt28Rva, - reinterpret_cast(&DetourJournalListenerRouteVt28), - reinterpret_cast(&gOriginalJournalListenerRouteVt28)); - AttachProbeHook("JournalListener::Route vt30", kJournalListenerRouteVt30Rva, - reinterpret_cast(&DetourJournalListenerRouteVt30), - reinterpret_cast(&gOriginalJournalListenerRouteVt30)); - AttachProbeHook("JournalListener::RouteAlt vt28", kJournalListenerRouteAltVt28Rva, - reinterpret_cast(&DetourJournalListenerRouteAltVt28), - reinterpret_cast(&gOriginalJournalListenerRouteAltVt28)); - AttachProbeHook("JournalListener::RouteAlt vt30", kJournalListenerRouteAltVt30Rva, - reinterpret_cast(&DetourJournalListenerRouteAltVt30), - reinterpret_cast(&gOriginalJournalListenerRouteAltVt30)); - AttachProbeHook("JournalListener::UI vt28", kJournalListenerUiVt28Rva, - reinterpret_cast(&DetourJournalListenerUiVt28), - reinterpret_cast(&gOriginalJournalListenerUiVt28)); - AttachProbeHook("JournalListener::UI vt30", kJournalListenerUiVt30Rva, - reinterpret_cast(&DetourJournalListenerUiVt30), - reinterpret_cast(&gOriginalJournalListenerUiVt30)); - AttachProbeHook("JournalListener::Tracker vt28", kJournalListenerTrackerVt28Rva, - reinterpret_cast(&DetourJournalListenerTrackerVt28), - reinterpret_cast(&gOriginalJournalListenerTrackerVt28)); - } - AttachProbeHook("JournalRouteBridge::Update candidate", kJournalRouteBridgeRva, - reinterpret_cast(&DetourJournalRouteBridge), - reinterpret_cast(&gOriginalJournalRouteBridge)); - AttachProbeHook("MappinSystem::RouteEventEnqueue slot240", kMappinRouteEventEnqueueRva, - reinterpret_cast(&DetourMappinRouteEventEnqueue), - reinterpret_cast(&gOriginalMappinRouteEventEnqueue)); - AttachProbeHook("MappinRouteEvent::Handle", kMappinRouteEventHandlerRva, - reinterpret_cast(&DetourMappinRouteEventHandler), - reinterpret_cast(&gOriginalMappinRouteEventHandler)); - AttachProbeHook("MappinSystem::RouteActivate", kMappinRouteActivateRva, - reinterpret_cast(&DetourMappinRouteActivate), - reinterpret_cast(&gOriginalMappinRouteActivate)); - AttachProbeHook("MappinSystem::RouteDeactivate", kMappinRouteDeactivateRva, - reinterpret_cast(&DetourMappinRouteDeactivate), - reinterpret_cast(&gOriginalMappinRouteDeactivate)); - AttachProbeHook("RouteBuildCandidate 0x5625a4", kRouteBuildCandidateRva, - reinterpret_cast(&DetourRouteBuildCandidate), - reinterpret_cast(&gOriginalRouteBuildCandidate)); - if (kEnableVerboseRouteObserverHooks) - { - AttachProbeHook("RouteObserver0::Activate slot48", kRouteObserver0ActivateRva, - reinterpret_cast(&DetourRouteObserver0Activate), - reinterpret_cast(&gOriginalRouteObserver0Activate)); - AttachProbeHook("RouteObserver1::Activate slot48", kRouteObserver1ActivateRva, - reinterpret_cast(&DetourRouteObserver1Activate), - reinterpret_cast(&gOriginalRouteObserver1Activate)); - AttachProbeHook("RouteObserver23::Activate slot48", kRouteObserver23ActivateRva, - reinterpret_cast(&DetourRouteObserver23Activate), - reinterpret_cast(&gOriginalRouteObserver23Activate)); - AttachProbeHook("RouteObserver0::Deactivate slot50", kRouteObserver0DeactivateRva, - reinterpret_cast(&DetourRouteObserver0Deactivate), - reinterpret_cast(&gOriginalRouteObserver0Deactivate)); - AttachProbeHook("RouteObserver1::Deactivate slot50", kRouteObserver1DeactivateRva, - reinterpret_cast(&DetourRouteObserver1Deactivate), - reinterpret_cast(&gOriginalRouteObserver1Deactivate)); - AttachProbeHook("RouteObserver23::Deactivate slot50", kRouteObserver23DeactivateRva, - reinterpret_cast(&DetourRouteObserver23Deactivate), - reinterpret_cast(&gOriginalRouteObserver23Deactivate)); - } - AttachProbeHook("FrameMappinPathWrapper", kFrameMappinPathWrapperRva, - reinterpret_cast(&DetourFrameMappinPathWrapper), - reinterpret_cast(&gOriginalFrameMappinPathWrapper)); - AttachProbeHook("FrameMappinPathCore", kFrameMappinPathCoreRva, - reinterpret_cast(&DetourFrameMappinPathCore), - reinterpret_cast(&gOriginalFrameMappinPathCore)); - AttachProbeHook("SetSelectedMappinWrapper", kSetSelectedMappinWrapperRva, - reinterpret_cast(&DetourSetSelectedMappinWrapper), - reinterpret_cast(&gOriginalSetSelectedMappinWrapper)); - AttachProbeHook("SetSelectedMappinByIdWrapper", kSetSelectedMappinByIdWrapperRva, - reinterpret_cast(&DetourSetSelectedMappinByIdWrapper), - reinterpret_cast(&gOriginalSetSelectedMappinByIdWrapper)); - AttachProbeHook("SetSelectedMappinByPositionWrapper", kSetSelectedMappinByPositionWrapperRva, - reinterpret_cast(&DetourSetSelectedMappinByPositionWrapper), - reinterpret_cast(&gOriginalSetSelectedMappinByPositionWrapper)); - AttachProbeHook("SetSelectedMappinCore", kSetSelectedMappinCoreRva, - reinterpret_cast(&DetourSetSelectedMappinCore), - reinterpret_cast(&gOriginalSetSelectedMappinCore)); - AttachProbeHook("TrackCustomPositionMappinWrapper", kTrackCustomPositionMappinWrapperRva, - reinterpret_cast(&DetourTrackCustomPositionMappinWrapper), - reinterpret_cast(&gOriginalTrackCustomPositionMappinWrapper)); - AttachProbeHook("TrackCustomPositionMappinCore", kTrackCustomPositionMappinCoreRva, - reinterpret_cast(&DetourTrackCustomPositionMappinCore), - reinterpret_cast(&gOriginalTrackCustomPositionMappinCore)); - AttachProbeHook("UntrackCustomPositionMappinWrapper", kUntrackCustomPositionMappinWrapperRva, - reinterpret_cast(&DetourUntrackCustomPositionMappinWrapper), - reinterpret_cast(&gOriginalUntrackCustomPositionMappinWrapper)); - AttachProbeHook("TrackMappinWrapper", kTrackMappinWrapperRva, - reinterpret_cast(&DetourTrackMappinWrapper), - reinterpret_cast(&gOriginalTrackMappinWrapper)); - AttachProbeHook("TrackMappinCore", kTrackMappinCoreRva, reinterpret_cast(&DetourTrackMappinCore), - reinterpret_cast(&gOriginalTrackMappinCore)); - AttachProbeHook("SetMappinTrackingAlternativeWrapper", kSetMappinTrackingAlternativeWrapperRva, - reinterpret_cast(&DetourSetMappinTrackingAlternativeWrapper), - reinterpret_cast(&gOriginalSetMappinTrackingAlternativeWrapper)); - } + InstallPatchSites(); } - - if (aReason == RED4ext::v1::EMainReason::Unload) + else if (aReason == RED4ext::v1::EMainReason::Unload) { - RemoveGpsNodeMultiplierInlinePatch(); - RemoveGpsMomentumPenaltyInlinePatch(); - - if (kEnableGpsQueryLifecycleHooks) - { - DetachProbeHook("RunGPSQuery body 0x29bd128", kRunGpsQueryBodyRva); - DetachProbeHook("UpdateGPSQuery body 0x29bd254", kUpdateGpsQueryBodyRva); - } - if (kEnableGpsQueryLifecycleHooks || kEnableGpsQueryResultTraceHooks) - { - DetachProbeHook("GPSQuerySubmit 0x70a42c", kGpsQuerySubmitRva); - DetachProbeHook("GPSQueryResultFetch 0x7094b8", kGpsQueryResultFetchRva); - } - if (kEnableGpsQueryLifecycleHooks) - { - DetachProbeHook("GPSQueryDispatch 0x70a570", kGpsQueryDispatchRva); - DetachProbeHook("GPSQueryStatus 0xaa5704", kGpsQueryStatusRva); - } - if (kEnableGpsAsyncTrafficProbe) - { - DetachProbeHook("GPSDispatchAdvance 0x8d17d8", kGpsDispatchAdvanceRva); - DetachProbeHook("GPSTrafficResultTake 0x709d5c", kGpsTrafficResultTakeRva); - DetachProbeHook("GPSRoutePostprocess 0x44830c", kGpsRoutePostprocessRva); - } - if (kEnableGpsTrafficEdgeWeightPatch) - { - DetachProbeHook("GPSTrafficEdgeScore 0x8d46cc", kGpsTrafficEdgeScoreRva); - } - if (kEnableGpsRouteJobLifecycleHooks) - { - 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 || kEnableGpsLocalSearchTraceHooks) - { - DetachProbeHook("GPSSearch 0x44f054", kGpsSearchRva); - } - if (kEnableGpsRouteProducerHook) - { - DetachProbeHook("GPSRouteProducer 0x44cc7c", kGpsRouteProducerRva); - } - if (kEnableGpsLocalSearchTraceHooks) - { - DetachProbeHook("GPSResolveHandle 0x44e1a8", kGpsResolveHandleRva); - } - if (kEnableGpsEdgeCostHook) - { - DetachProbeHook("GPSEdgeCost 0x44f838", kGpsEdgeCostRva); - } - if (kEnableGpsLocalSearchTraceHooks) - { - DetachProbeHook("GPSFilteredProviderFilter 0x44ff68", kGpsFilteredProviderFilterRva); - DetachProbeHook("GPSBaseProviderFilter 0x450b08", kGpsBaseProviderFilterRva); - } - if (kEnableGpsMultiplierHooks) - { - if (kEnableGpsAuxMultiplierHook) - { - 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); - } + RemovePatchSites(); } return true; @@ -6364,9 +387,9 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::v1::PluginHandle aHandle, RED4e RED4EXT_C_EXPORT void RED4EXT_CALL Query(RED4ext::v1::PluginInfo* aInfo) { - aInfo->name = L"EdgeWeightGPS"; + aInfo->name = L"MomentumGPS"; aInfo->author = L"salt+Codex"; - aInfo->version = RED4ext::v1::SemVer{0, 1, 0, {0, 0}}; + aInfo->version = RED4ext::v1::SemVer{0, 2, 0, {0, 0}}; aInfo->runtime = RED4ext::v1::FileVer{3, 0, 80, 51928}; aInfo->sdk = RED4ext::v1::SemVer{0, 5, 0, {0, 0}}; } diff --git a/tools/build_red4ext_shim.sh b/tools/build_red4ext_shim.sh index 7a9ad82..a5a3b59 100755 --- a/tools/build_red4ext_shim.sh +++ b/tools/build_red4ext_shim.sh @@ -3,7 +3,7 @@ set -euo pipefail repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" plugin_dir="$repo_root/red4ext/EdgeWeightGPS" -build_dir="$repo_root/build/red4ext/EdgeWeightGPS-mingw64" +build_dir="$repo_root/build/red4ext/MomentumGPS-mingw64" sdk_dir="$repo_root/vendor/RED4ext.SDK" if [[ ! -f "$sdk_dir/include/RED4ext/Api/v1/PluginInfo.hpp" ]]; then @@ -16,4 +16,4 @@ cmake -S "$plugin_dir" -B "$build_dir" \ -DCMAKE_BUILD_TYPE=RelWithDebInfo cmake --build "$build_dir" --parallel -printf '%s\n' "$build_dir/EdgeWeightGPS.dll" +printf '%s\n' "$build_dir/MomentumGPS.dll" diff --git a/tools/install_red4ext_shim.sh b/tools/install_red4ext_shim.sh index 00e448a..cdda6f7 100755 --- a/tools/install_red4ext_shim.sh +++ b/tools/install_red4ext_shim.sh @@ -3,14 +3,10 @@ set -euo pipefail repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" game_dir="${CYBERPUNK2077_GAME_DIR:-/var/home/salt/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/Cyberpunk 2077}" -plugin_out="$game_dir/red4ext/plugins/EdgeWeightGPS" +plugin_out="$game_dir/red4ext/plugins/MomentumGPS" dll_path="$("$repo_root/tools/build_red4ext_shim.sh" | tail -n 1)" mkdir -p "$plugin_out" -install -m 0644 "$dll_path" "$plugin_out/EdgeWeightGPS.dll" -install -m 0644 "$repo_root/presets/momentum/default.bin" "$plugin_out/momentum_weights.bin" -install -m 0644 "$repo_root/presets/solver_weights/vanilla.bin" "$plugin_out/solver_weights.bin" +install -m 0644 "$dll_path" "$plugin_out/MomentumGPS.dll" -printf 'Installed %s\n' "$plugin_out/EdgeWeightGPS.dll" -printf 'Installed %s\n' "$plugin_out/momentum_weights.bin" -printf 'Installed %s\n' "$plugin_out/solver_weights.bin" +printf 'Installed %s\n' "$plugin_out/MomentumGPS.dll" diff --git a/tools/package_red4ext_mod.sh b/tools/package_red4ext_mod.sh index 6f3b964..4bad328 100755 --- a/tools/package_red4ext_mod.sh +++ b/tools/package_red4ext_mod.sh @@ -3,18 +3,16 @@ set -euo pipefail repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" timestamp="$(date +%Y%m%d-%H%M%S)" -staging="$repo_root/build/package/EdgeWeightGPS-$timestamp" +staging="$repo_root/build/package/MomentumGPS-$timestamp" dist_dir="$repo_root/dist" -zip_path="$dist_dir/EdgeWeightGPS-red4ext-$timestamp.zip" +zip_path="$dist_dir/MomentumGPS-red4ext-$timestamp.zip" dll_path="$("$repo_root/tools/build_red4ext_shim.sh" | tail -n 1)" -plugin_out="$staging/red4ext/plugins/EdgeWeightGPS" +plugin_out="$staging/red4ext/plugins/MomentumGPS" mkdir -p "$plugin_out" "$dist_dir" -install -m 0644 "$dll_path" "$plugin_out/EdgeWeightGPS.dll" -install -m 0644 "$repo_root/presets/momentum/default.bin" "$plugin_out/momentum_weights.bin" -install -m 0644 "$repo_root/presets/solver_weights/vanilla.bin" "$plugin_out/solver_weights.bin" -install -m 0644 "$repo_root/packaging/EdgeWeightGPS-README.txt" "$plugin_out/README.txt" +install -m 0644 "$dll_path" "$plugin_out/MomentumGPS.dll" +install -m 0644 "$repo_root/packaging/MomentumGPS-README.txt" "$plugin_out/README.txt" ( cd "$staging"