Skip to content

Commit

Permalink
tweak(extra-natives/five): sanitize mission train creation
Browse files Browse the repository at this point in the history
  • Loading branch information
Ehbw committed Dec 5, 2024
1 parent f3958ce commit 6a690f8
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 47 deletions.
59 changes: 59 additions & 0 deletions code/components/extra-natives-five/include/Train.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#pragma once

#include <atArray.h>

namespace rage
{
struct CTrackNode
{
public:
float m_x, m_y, m_z;
float m_unk;
uint8_t m_unk1;
int m_station;
};

struct CTrainTrack
{
public:
// This has never changed.
static const int kMaxTracks = 27;

uint32_t m_hash;
bool m_enabled;
bool m_isLooped;
bool m_stopsAtStation;
bool m_MPStopsAtStation;
uint32_t m_speed;
uint32_t m_brakeDistance;

int m_nodeCount;
CTrackNode* m_nodes;

uint8_t m_pad[8];

bool m_disableAmbientTrains;
uint8_t m_pad2[0x220];

// Helper functions
static bool AreAllTracksDisabled();
};

struct CTrainConfig
{
struct CarriageData
{
uint32_t m_hash;
uint8_t m_pad[0xE];
};

uint32_t m_hash;
uint8_t m_pad[0x1A];
atArray<CarriageData> m_carriages;
};

struct CTrainConfigData
{
atArray<CTrainConfig> m_trainConfigs;
};
}
60 changes: 60 additions & 0 deletions code/components/extra-natives-five/src/NativeFixes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "RageParser.h"
#include "Resource.h"
#include "ScriptWarnings.h"
#include <Train.h>

static void FixVehicleWindowNatives()
{
Expand Down Expand Up @@ -453,11 +454,68 @@ static void FixApplyForceToEntity()
});
}

static hook::cdecl_stub<bool(uint32_t* mi)> hasModelLoaded([]()
{
return hook::get_call(hook::get_pattern("25 FF FF FF 3F 89 45 6F E8 ? ? ? ? 84 C0", 8));
});

static rage::CTrainConfigData* g_trainConfigData;

static void FixMissionTrain()
{
constexpr uint64_t CREATE_MISSION_TRAIN = 0x63C6CCA8E68AE8C8;

auto originalHandler = fx::ScriptEngine::GetNativeHandler(CREATE_MISSION_TRAIN);
if (!originalHandler)
{
return;
}

auto handler = *originalHandler;
fx::ScriptEngine::RegisterNativeHandler(CREATE_MISSION_TRAIN, [handler](fx::ScriptContext& ctx)
{
auto variation = ctx.GetArgument<int>(0);

if (variation < 0 || variation >= g_trainConfigData->m_trainConfigs.GetCount())
{
fx::scripting::Warningf("natives", "Invalid train variation index was passed to CREATE_MISSION_TRAIN (%i), should be from 0 to %i\n", variation, g_trainConfigData->m_trainConfigs.GetCount() - 1);
ctx.SetResult<int>(0);
return;
}

rage::CTrainConfig config = g_trainConfigData->m_trainConfigs.Get(variation);

// Prevent the native from executing if one any of the required models are not in memory
for (auto& carriage : config.m_carriages)
{
rage::fwModelId idx{ 0 };
rage::fwArchetypeManager::GetArchetypeFromHashKey(carriage.m_hash, idx);
if (!hasModelLoaded(&idx.value))
{
fx::scripting::Warningf("natives", "Failed to spawn mission train as carriage hash '%i' is not loaded\n", carriage.m_hash);
ctx.SetResult<int>(0);
return;
}
}

// Prevent the native from executing if there are no tracks available. This won't crash the game but can give a confusing error.
if (rage::CTrainTrack::AreAllTracksDisabled())
{
fx::scripting::Warningf("natives", "Failed to spawn mission train as there are no tracks enabled\n");
ctx.SetResult<int>(0);
return;
}

handler(ctx);
});
}

static HookFunction hookFunction([]()
{
g_fireInstances = (std::array<FireInfoEntry, 128>*)(hook::get_address<uintptr_t>(hook::get_pattern("74 47 48 8D 0D ? ? ? ? 48 8B D3", 2), 3, 7) + 0x10);
g_maxHudColours = *hook::get_pattern<int32_t>("81 F9 ? ? ? ? 77 5A 48 89 5C 24", 2);
g_numMarkerTypes = *hook::get_pattern<int32_t>("BE FF FF FF DF 41 BF 00 00 FF 0F 41 BC FF FF FF BF", -4);
g_trainConfigData = hook::get_address<rage::CTrainConfigData*>(hook::get_pattern<rage::CTrainConfigData>("48 8B 05 ? ? ? ? 4D 03 C0 4A 8B 4C C0 ? 80 7C 91 ? ? 74", 3));

rage::scrEngine::OnScriptInit.Connect([]()
{
Expand Down Expand Up @@ -489,5 +547,7 @@ static HookFunction hookFunction([]()
FixDrawMarker();

FixApplyForceToEntity();

FixMissionTrain();
});
});
78 changes: 31 additions & 47 deletions code/components/extra-natives-five/src/TrackNatives.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,37 +11,10 @@
#include <limits>
#include <MinHook.h>
#include <rageVectors.h>
#include "ScriptWarnings.h"
#include <Train.h>

struct TrackNode
{
public:
float m_x, m_y, m_z;
float m_unk;
uint8_t m_unk1;
int m_station;
};

struct TrackData
{
public:
uint32_t m_hash;
bool m_enabled;
bool m_isLooped;
bool m_stopsAtStation;
bool m_MPStopsAtStation;
uint32_t m_speed;
uint32_t m_brakeDistance;

int m_nodeCount;
TrackNode* m_nodes;

uint8_t m_pad[8];

bool m_disableAmbientTrains;
// and a bunch of other fields...
};

static hook::cdecl_stub<TrackData* (uint32_t)> getTrainTrack([]
static hook::cdecl_stub<rage::CTrainTrack* (uint32_t)> getTrainTrack([]
{
return hook::get_call(hook::get_pattern("E8 ? ? ? ? 33 DB 45 0F 57 DB"));
});
Expand All @@ -51,18 +24,15 @@ static float calculateDistance(const rage::Vector3& point1, const float& x, cons
return (point1.x - x) * (point1.x - x) + (point1.y - y) * (point1.y - y) + (point1.z - z) * (point1.z - z);
}

// This has never changed.
static constexpr int kMaxTrainTracks = 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 < kMaxTrainTracks; i++)
for (int i = 0; i < rage::CTrainTrack::kMaxTracks; i++)
{
TrackData* track = getTrainTrack(i);
rage::CTrainTrack* track = getTrainTrack(i);

// Skip if this track is a nullptr or is currently disabled. The game doesn't check for this.
if (!track || !track->m_enabled)
Expand All @@ -72,7 +42,7 @@ static int32_t FindClosestTrack(rage::Vector3& position, int8_t* outTrack)

for (int n = 0; n < track->m_nodeCount; n++)
{
TrackNode node = track->m_nodes[n];
rage::CTrackNode node = track->m_nodes[n];

float Distance = calculateDistance(position, node.m_x, node.m_y, node.m_z);

Expand All @@ -88,7 +58,6 @@ static int32_t FindClosestTrack(rage::Vector3& position, int8_t* outTrack)
return closestNode;
}


static HookFunction hookFunction([]()
{
MH_Initialize();
Expand All @@ -100,17 +69,17 @@ static HookFunction hookFunction([]()
hook::nop(hook::get_pattern("F3 0F 10 75 ? 8B 55"), 5);
});

static TrackData* getAndCheckTrack(fx::ScriptContext& context, std::string_view nn)
static rage::CTrainTrack* getAndCheckTrack(fx::ScriptContext& context, std::string_view nn)
{
int trackIndex = context.GetArgument<int>(0);
if (trackIndex < 0 || trackIndex > kMaxTrainTracks)
if (trackIndex < 0 || trackIndex > rage::CTrainTrack::kMaxTracks)
{
trace("Invalid track index %i passed to %s\n", trackIndex, nn);
context.SetResult(0);
return NULL;
}

TrackData* track = getTrainTrack(trackIndex);
rage::CTrainTrack* track = getTrainTrack(trackIndex);

if (!track || track->m_hash == 0)
{
Expand All @@ -122,29 +91,44 @@ static TrackData* getAndCheckTrack(fx::ScriptContext& context, std::string_view
return track;
}

bool rage::CTrainTrack::AreAllTracksDisabled()
{
for (int i = 0; i < rage::CTrainTrack::kMaxTracks; i++)
{
CTrainTrack* track = getTrainTrack(i);

if (track && track->m_enabled)
{
return false;
}
}

return true;
}

static InitFunction initFunction([]()
{
fx::ScriptEngine::RegisterNativeHandler("SET_TRACK_MAX_SPEED", [](fx::ScriptContext& context)
{
int maxSpeed = context.CheckArgument<int>(1);

if (TrackData* track = getAndCheckTrack(context, "SET_TRACK_MAX_SPEED"))
if (rage::CTrainTrack* track = getAndCheckTrack(context, "SET_TRACK_MAX_SPEED"))
{
track->m_speed = maxSpeed;
}
});

fx::ScriptEngine::RegisterNativeHandler("GET_TRACK_MAX_SPEED", [](fx::ScriptContext& context)
{
if (TrackData* track = getAndCheckTrack(context, "GET_TRACK_MAX_SPEED"))
if (rage::CTrainTrack* track = getAndCheckTrack(context, "GET_TRACK_MAX_SPEED"))
{
context.SetResult<int>(track->m_speed);
}
});

fx::ScriptEngine::RegisterNativeHandler("GET_TRACK_BRAKING_DISTANCE", [](fx::ScriptContext& context)
{
if (TrackData* track = getAndCheckTrack(context, "GET_TRACK_BRAKING_DISTANCE"))
if (rage::CTrainTrack* track = getAndCheckTrack(context, "GET_TRACK_BRAKING_DISTANCE"))
{
context.SetResult<int>(track->m_brakeDistance);
}
Expand All @@ -153,7 +137,7 @@ static InitFunction initFunction([]()
fx::ScriptEngine::RegisterNativeHandler("SET_TRACK_BRAKING_DISTANCE", [](fx::ScriptContext& context)
{
int brakeDistance = context.CheckArgument<int>(1);
if (TrackData* track = getAndCheckTrack(context, "SET_TRACK_BRAKING_DISTANCE"))
if (rage::CTrainTrack* track = getAndCheckTrack(context, "SET_TRACK_BRAKING_DISTANCE"))
{
track->m_brakeDistance = brakeDistance;
}
Expand All @@ -162,23 +146,23 @@ static InitFunction initFunction([]()
fx::ScriptEngine::RegisterNativeHandler("SET_TRACK_ENABLED", [](fx::ScriptContext& context)
{
bool state = context.GetArgument<bool>(1);
if (TrackData* track = getAndCheckTrack(context, "SET_TRACK_ENABLED"))
if (rage::CTrainTrack* track = getAndCheckTrack(context, "SET_TRACK_ENABLED"))
{
track->m_enabled = state;
}
});

fx::ScriptEngine::RegisterNativeHandler("IS_TRACK_ENABLED", [](fx::ScriptContext& context)
{
if (TrackData* track = getAndCheckTrack(context, "IS_TRACK_ENABLED"))
if (rage::CTrainTrack* track = getAndCheckTrack(context, "IS_TRACK_ENABLED"))
{
context.SetResult<bool>(track->m_enabled);
}
});

fx::ScriptEngine::RegisterNativeHandler("IS_TRACK_SWITCHED_OFF", [](fx::ScriptContext& context)
{
if (TrackData* track = getAndCheckTrack(context, "IS_TRACK_SWITCHED_OFF"))
if (rage::CTrainTrack* track = getAndCheckTrack(context, "IS_TRACK_SWITCHED_OFF"))
{
context.SetResult<bool>(track->m_disableAmbientTrains);
}
Expand Down

0 comments on commit 6a690f8

Please sign in to comment.