From b786408f89c54bbfe146c7e4859f8f6f67ec5b42 Mon Sep 17 00:00:00 2001 From: Ehbw Date: Sun, 23 Feb 2025 16:32:18 +0000 Subject: [PATCH] feat(five/streaming): expand train track array --- .../extra-natives-five/include/Train.h | 10 +- .../extra-natives-five/src/TrackNatives.cpp | 52 +++++- .../src/PatchTrackLimits.cpp | 160 ++++++++++++++++++ 3 files changed, 214 insertions(+), 8 deletions(-) create mode 100644 code/components/gta-streaming-five/src/PatchTrackLimits.cpp diff --git a/code/components/extra-natives-five/include/Train.h b/code/components/extra-natives-five/include/Train.h index c4efc87056..216dd822b9 100644 --- a/code/components/extra-natives-five/include/Train.h +++ b/code/components/extra-natives-five/include/Train.h @@ -16,9 +16,6 @@ struct CTrackNode struct CTrainTrack { public: - // This has never changed. - static const int kMaxTracks = 27; - uint32_t m_hash; bool m_enabled; bool m_isLooped; @@ -33,7 +30,12 @@ struct CTrainTrack uint8_t m_pad[8]; bool m_disableAmbientTrains; - uint8_t m_pad2[0x220]; + + uint8_t m_pad2[0x17]; + + bool m_isActive; + + uint8_t m_pad3[0x20C]; // Helper functions static bool AreAllTracksDisabled(); diff --git a/code/components/extra-natives-five/src/TrackNatives.cpp b/code/components/extra-natives-five/src/TrackNatives.cpp index ab190babbd..d0ec164de9 100644 --- a/code/components/extra-natives-five/src/TrackNatives.cpp +++ b/code/components/extra-natives-five/src/TrackNatives.cpp @@ -96,13 +96,16 @@ static hook::cdecl_stub CTrainTrack__getTrainTrack return hook::get_call(hook::get_pattern("E8 ? ? ? ? 33 DB 45 0F 57 DB")); }); + +static int kMaxTracks = xbr::IsGameBuildOrGreater<2545>() ? 127 : 27; + static int32_t FindClosestTrack(rage::Vector3& position, int8_t* outTrack) { *outTrack = -1; float closestDistance = std::numeric_limits::infinity(); int closestNode = -1; - for (int i = 0; i < rage::CTrainTrack::kMaxTracks; i++) + for (int i = 0; i < kMaxTracks; i++) { rage::CTrainTrack* track = CTrainTrack__getTrainTrack(i); @@ -132,7 +135,7 @@ static int32_t FindClosestTrack(rage::Vector3& position, int8_t* outTrack) static std::vector GetTrackNodesInRadius(const float& x, const float& y, const float& z, float radius, bool includeDisabledTracks = false) { std::vector nearbyNodes; - for (int8_t i = 0; i < rage::CTrainTrack::kMaxTracks; i++) + for (int8_t i = 0; i < kMaxTracks; i++) { rage::CTrainTrack* track = CTrainTrack__getTrainTrack(i); @@ -202,7 +205,7 @@ template static rage::CTrainTrack* GetAndCheckTrack(fx::ScriptContext& context, std::string_view nn) { int trackIndex = context.GetArgument(ArgumentIndex); - if (trackIndex < 0 || trackIndex > rage::CTrainTrack::kMaxTracks) + if (trackIndex < 0 || trackIndex > kMaxTracks) { fx::scripting::Warningf("natives", "%s: Invalid track index %i\n", nn, trackIndex); context.SetResult(0); @@ -234,7 +237,7 @@ static rage::CTrackNode* GetAndCheckTrackNode(rage::CTrainTrack* track, int8_t t bool rage::CTrainTrack::AreAllTracksDisabled() { - for (int i = 0; i < rage::CTrainTrack::kMaxTracks; i++) + for (int i = 0; i < kMaxTracks; i++) { CTrainTrack* track = CTrainTrack__getTrainTrack(i); @@ -447,11 +450,52 @@ static InitFunction initFunction([]() }); }); +static int32_t FindTrackFromNode(void* node) +{ + for (int i = 0; i < kMaxTracks; i++) + { + rage::CTrainTrack* track = CTrainTrack__getTrainTrack(i); + + if (!track || !track->m_enabled) + { + continue; + } + + void* maxNode = &track->m_nodes[track->m_nodeCount - 1]; + if (node >= track->m_nodes && node <= maxNode) + { + return i; + } + } + + return -1; +} + +static bool CTrain__IsTrackActive(int32_t trackIndex) +{ + if (trackIndex <= 0 || trackIndex > kMaxTracks) + { + return false; + } + + rage::CTrainTrack* track = CTrainTrack__getTrainTrack(trackIndex); + + if (!track || !track->m_enabled || !track->m_isActive) + { + return false; + } + + return true; +} + static HookFunction hookFunction([]() { MH_Initialize(); // add missing enabled check for script created trains. MH_CreateHook(hook::get_call(hook::get_pattern("E8 ? ? ? ? 8B D8 E8 ? ? ? ? 44 8A CF")), FindClosestTrack, NULL); + MH_CreateHook(hook::get_call(hook::get_pattern("E8 ? ? ? ? 4C 63 E0 41 83 FC ? 0F 84 ? ? ? ? 49 8B FC")), FindTrackFromNode, NULL); + // Doesn't account for negative indexes or disabled tracks. + MH_CreateHook(hook::get_call(hook::get_pattern("E8 ? ? ? ? 84 C0 74 ? 33 D2 8B CF")), CTrain__IsTrackActive, NULL); MH_EnableHook(MH_ALL_HOOKS); // Prevent game code from constantly setting the trains speed while in moving state if it has the "stopsAtStations" flag enabled from setting the train speed to the tracks max speed while moving. diff --git a/code/components/gta-streaming-five/src/PatchTrackLimits.cpp b/code/components/gta-streaming-five/src/PatchTrackLimits.cpp new file mode 100644 index 0000000000..17e1be1a39 --- /dev/null +++ b/code/components/gta-streaming-five/src/PatchTrackLimits.cpp @@ -0,0 +1,160 @@ +/* +* This file is part of the CitizenFX project - http://citizen.re/ +* +* See LICENSE and MENTIONS in the root of the source tree for information +* regarding licensing. +*/ +#include "StdInc.h" + +#include "CrossBuildRuntime.h" +#include +#include +#include + +struct PatternPair +{ + std::string_view pattern; + int offset; +}; + +struct ClampPatternPair : PatternPair +{ + bool clamp; +}; + +struct CTrainTrack +{ + char pad[0x250]; +}; + +struct CTrackData +{ + char pad[0xA0]; +}; + +constexpr int kNumTracks = 127; + +static CTrackData** trackStore2; +static CTrainTrack** trackStore; + +static void RelocateRelative(std::initializer_list list) +{ + void* oldAddress = nullptr; + + for (auto& entry : list) + { + auto location = hook::get_pattern(entry.pattern, entry.offset); + + if (!oldAddress) + { + oldAddress = hook::get_address(location); + } + + auto curTarget = hook::get_address(location); + assert(curTarget == oldAddress); + + hook::put(location, (intptr_t)trackStore - (intptr_t)location - 4); + } +} + +static HookFunction initFunction([]() +{ + if (!xbr::IsGameBuildOrGreater<2545>()) + { + return; + } + + // patching hardcoded max available train tracks + { + std::initializer_list list = { + {"41 80 FC ? 0F 8D", 3, false}, + {"48 8B C1 48 69 C0 ? ? ? ? 44 88 74 30", -2, false}, + {"41 83 FE ? 0F 8C ? ? ? ? 44 8B 64 24", 3, false}, + {"41 80 F9 ? 7C", 3, false}, + {"48 8B C8 8B 05 ? ? ? ? 48 69 C9", -3, false}, + {"83 FB ? 0F 8C ? ? ? ? 48 83 C4 ? 41 5F 41 5E 41 5C", 2, false}, + {"83 F9 ? 7D ? 4C 8D 05", 2, false}, + {"83 F9 ? 7D ? 42 88 54 00", 2, false}, + {"83 FA ? 7C ? 83 C8 ? C3 8B C2", 2, false}, + {"83 F9 ? 77 ? 48 63 C1 48 8D 0D ? ? ? ? 48 69 C0 ? ? ? ? 48 03 C1 44 39 48", 2, true}, + {"FF C3 83 FB ? 0F 8C ? ? ? ? 48 83 C4", 4, true}, + {"83 FA ? 0F 87 ? ? ? ? F3 0F 10 0E", 2, true}, + {"83 FA ? 0F 87 ? ? ? ? F3 0F 10 35",2, true}, + {"80 FA ? 77 ? 45 85 C0", 2, true} + }; + + for (auto& entry : list) + { + auto location = hook::pattern(entry.pattern).count(1).get(0).get(entry.offset); + hook::put(location, (entry.clamp ? (kNumTracks - 1) : kNumTracks)); + } + } + + // patching pointers to static track array + { + trackStore = (CTrainTrack**)hook::AllocateStubMemory(sizeof(CTrainTrack) * kNumTracks); + + RelocateRelative({ + {"48 8D 0D ? ? ? ? 45 8A C7 48 8B C7", 3}, + {"4B 8D 14 64", 7}, + {"48 8D 05 ? ? ? ? 48 69 C9 ? ? ? ? 48 8B 44 01", 3}, + {"4C 8D 15 ? ? ? ? 33 FF", 3}, + {"66 0F 6E 4C 08", -4}, + {"42 38 44 02", -11}, + {"48 8D 35 ? ? ? ? 44 8B 78", 3}, + {"49 0F BE CC", 7}, + {"48 8D 35 ? ? ? ? 41 BB", 3}, + {"F3 0F 11 4D ? F3 0F 10 54 C8", -14}, + {"89 44 11 ? 48 83 7C 24", -11}, + {"8B CB E8 ? ? ? ? 83 25 ? ? ? ? ? E9", -21}, + {"48 8D 2D ? ? ? ? 49 8B D3", 3}, + {"48 69 FF ? ? ? ? 44 8B 64 07", -6}, + {"48 8D 2D ? ? ? ? 48 85 F6 74 ? 85 DB", 3}, + {"48 89 5C 24 ? 48 0F BE C1", 5 + 4 + 3}, + {"49 63 C9 48 8D 35", 6}, + {"84 C9 78 ? 48 0F BE C1 4C 8D 05", 11}, + {"4C 0F BE C9", 7}, + {"74 ? F3 43 0F 10 5C 3B", -17}, + {"41 83 38 ? 74 ? 48 63 C2", 12}, + {"48 0F BE D2 4C 8D 15", 7}, + {"48 8D 3D ? ? ? ? 0F 29 74 24 ? 4C 8D 14 40", 3}, + {"74 ? 48 0F BE 81", 13}, + {"4C 8D 25 ? ? ? ? 44 0F 28 C6", 3}, + {"48 8D 05 ? ? ? ? 48 69 FF ? ? ? ? 48 03 F8 0F 84", 3}, + {"48 85 DB 0F 84 ? ? ? ? 48 8D 3D", 12}, + {"4C 8D 3D ? ? ? ? 48 8D 15 ? ? ? ? 84 C0", 3}, + {"48 8D 3D ? ? ? ? 48 8B 5B", 3}, + {"48 8D 0D ? ? ? ? 83 B8", 3}, + {"48 8D 0D ? ? ? ? 8A 83 ? ? ? ? 84 C0", 3}, + {"EB ? 33 C9 84 C9 75 ? 8A 83", -15}, + {"48 8D 0D ? ? ? ? 48 69 C0 ? ? ? ? 8A 4C 08 ? EB ? 0F 2F F2", 3}, + {"48 83 EC ? 48 63 F9 48 8D 35", 10}, + {"4C 8D 05 ? ? ? ? 48 69 C0 ? ? ? ? 42 C6 44 00", 3}, + {"48 69 C9 ? ? ? ? 42 88 54 01", -7}, + {"33 F6 45 8B D8 48 8B F9", 11}, + {"48 03 C1 83 78", -11}, + {"33 C0 84 C9 78 ? 38 05", 19}, + {"48 03 C1 44 39 48", -11} + }); + + // Patch getTrainTrack function + if (xbr::IsGameBuildOrGreater<2944>()) + { + auto location = (hook::get_call(hook::get_pattern("E8 ? ? ? ? 33 DB 45 0F 57 DB")) + 0x5); + hook::put(location, (intptr_t)trackStore - (intptr_t)location - 4); + } + else + { + RelocateRelative({ + {"48 8D 0D ? ? ? ? 48 69 C0 ? ? ? ? 48 03 C1 C3 90", 3} + }); + } + } + + // Patch another static array related to trains + { + trackStore2 = (CTrackData**)hook::AllocateStubMemory((sizeof(CTrackData) * kNumTracks) * 20); + auto location = hook::get_pattern("48 8D 05 ? ? ? ? 49 C1 E0 ? 48 69 F6", 3); + hook::put(location, (intptr_t)trackStore2 - (intptr_t)location - 4); + } +});