Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(five/streaming): expand train track array #3191

Merged
merged 1 commit into from
Feb 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
});
Loading