Skip to content

Commit

Permalink
Merge Expand train track array (pr-3191)
Browse files Browse the repository at this point in the history
b786408 - feat(five/streaming): expand train track array
  • Loading branch information
prikolium-cfx committed Feb 25, 2025
2 parents 5357457 + b786408 commit 67742ee
Show file tree
Hide file tree
Showing 3 changed files with 214 additions and 8 deletions.
10 changes: 6 additions & 4 deletions code/components/extra-natives-five/include/Train.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
Expand Down
52 changes: 48 additions & 4 deletions code/components/extra-natives-five/src/TrackNatives.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,16 @@ static hook::cdecl_stub<rage::CTrainTrack*(uint32_t)> 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<float>::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);

Expand Down Expand Up @@ -132,7 +135,7 @@ static int32_t FindClosestTrack(rage::Vector3& position, int8_t* outTrack)
static std::vector<scrTrackNodeInfo> GetTrackNodesInRadius(const float& x, const float& y, const float& z, float radius, bool includeDisabledTracks = false)
{
std::vector<scrTrackNodeInfo> 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);

Expand Down Expand Up @@ -202,7 +205,7 @@ template<int ArgumentIndex>
static rage::CTrainTrack* GetAndCheckTrack(fx::ScriptContext& context, std::string_view nn)
{
int trackIndex = context.GetArgument<int>(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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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.
Expand Down
160 changes: 160 additions & 0 deletions code/components/gta-streaming-five/src/PatchTrackLimits.cpp
Original file line number Diff line number Diff line change
@@ -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 <Local.h>
#include <Hooking.h>
#include <Hooking.Stubs.h>

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<PatternPair> list)
{
void* oldAddress = nullptr;

for (auto& entry : list)
{
auto location = hook::get_pattern<int32_t>(entry.pattern, entry.offset);

if (!oldAddress)
{
oldAddress = hook::get_address<void*>(location);
}

auto curTarget = hook::get_address<void*>(location);
assert(curTarget == oldAddress);

hook::put<int32_t>(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<ClampPatternPair> 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<int>(entry.offset);
hook::put<uint8_t>(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<int32_t>("E8 ? ? ? ? 33 DB 45 0F 57 DB")) + 0x5);
hook::put<int32_t>(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<int32_t>("48 8D 05 ? ? ? ? 49 C1 E0 ? 48 69 F6", 3);
hook::put<int32_t>(location, (intptr_t)trackStore2 - (intptr_t)location - 4);
}
});

0 comments on commit 67742ee

Please sign in to comment.