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

[SA] There's no way to add extra train tracks? #204

Open
nathan130200 opened this issue Oct 19, 2024 · 0 comments
Open

[SA] There's no way to add extra train tracks? #204

nathan130200 opened this issue Oct 19, 2024 · 0 comments

Comments

@nathan130200
Copy link

nathan130200 commented Oct 19, 2024

I'm creating some scripts in GTA SA that I need to add more tracks because of some areas on the map I'm creating, from what I've noticed the train tracks are statically allocated in the game during compilation:

extern unsigned int *NumTrackNodes; // unsigned int NumTrackNodes[4]
extern CTrainNode **pTrackNodes; // CTrainNode *pTrackNodes[4]

unsigned int *NumTrackNodes = (unsigned int *)0xC38014;
CTrainNode **pTrackNodes = (CTrainNode **)0xC38024;

I've tried everything I know to try to reallocate this pointer to expand the number of supported tracks, but it's bizarre, nothing is working. Either the game crashes due to a corrupted heap or no effect appears (it seems like nothing has been changed).

I even thought about the last alternative of injecting small parts into assembly full of jumps to change these variables that are accessed, but it wouldn't make much sense. Searching on IDA there are many places that access this.

Does anyone know of a way to do this?

My last attempt (not effect / game ignores, or seems nothing has changed):

// global var to control if was extended or not, reset on init called again (eg: reload game, load new game, load save, ...)

static volatile char init = false;

// call this in gameProcess to and check once if was initialized
void Init();

// plguni class here, call Init() on gameProcess

class ExtendedTracks
{
 // in ctor call Events::gameProcessEvent += ....
} g_ExtendedTracksInstance;

// pre allocating 4 fixed tracks to copy from game
static std::vector<uint> newNumTrackNodes = { 0,0,0,0 };
static std::vector<CTrainNode*> newTrackNodes = { 0,0,0,0 };

// create new node at runtime, testing only,
// because CTrain::ReadAndInterpretTrackFile is always crashing.

// oh yes, thanks MSVC to delete this CTrainNode after function return. i need force nodiscard here
[[nodiscard]]
static CTrainNode* NewTrainNode(float x, float y, float z)
{
	auto* result = new CTrainNode();
	result->SetX(x);
	result->SetY(y);
	result->SetZ(z);
	return result;
}

// ensure new unique track index will be generated after track 4 (index: 3)
uint NewTrainTrack() {
	static volatile uint newTrackIndex = 4;

	uint trackId = newTrackIndex++;
	newNumTrackNodes.emplace_back();
	newTrackNodes.emplace_back();
	return trackId;
}

void Init()
{
	// sample nodes, no crash
	static std::array<CTrainNode, 25> g_TestNodes
	{
		NewTrainNode(2098.71, -1293.85, 23.9742),
		NewTrainNode(2090.46, -1293.62, 23.9739),
		NewTrainNode(2080.06, -1296.16, 23.8203),
		NewTrainNode(2079.9, -1301.55, 23.8203),
		NewTrainNode(2079.57, -1307.41, 23.9844),
		NewTrainNode(2082.61, -1308.37, 23.9844),
		NewTrainNode(2087.18, -1308.38, 23.9844),
		NewTrainNode(2091.38, -1308.46, 23.9844),
		NewTrainNode(2098.91, -1308.51, 23.9844),
		NewTrainNode(2104.63, -1308.54, 23.9844),
		NewTrainNode(2114.08, -1308.97, 23.9844),
		NewTrainNode(2131.04, -1308.28, 23.9861),
		NewTrainNode(2138.84, -1308.23, 23.9929),
		NewTrainNode(2145.38, -1308.2, 23.9928),
		NewTrainNode(2156.59, -1308.28, 23.9848),
		NewTrainNode(2158.36, -1304.41, 23.8203),
		NewTrainNode(2158.41, -1300.6, 23.8281),
		NewTrainNode(2158.23, -1293.34, 23.9766),
		NewTrainNode(2152.52, -1293.36, 23.9764),
		NewTrainNode(2145.53, -1293.98, 23.9799),
		NewTrainNode(2139.37, -1293.7, 23.9801),
		NewTrainNode(2133.85, -1293.75, 23.98),
		NewTrainNode(2127.8, -1293.12, 23.9823),
		NewTrainNode(2120.73, -1293.17, 23.9842),
		NewTrainNode(2111.67, -1293.31, 23.9730)
	};

	if (init)
			return;

	init = true;

	int trackId = 0;

	for (trackId = 0; trackId < 4; trackId++) {
		uint numNodes = NumTrackNodes[trackId];
		newNumTrackNodes[trackId] = numNodes;
		newTrackNodes[trackId] = new CTrainNode[numNodes + 1];

		for (int j = 0; j < numNodes; j++) {
			auto srcNode = &pTrackNodes[trackId][j];

			if (!srcNode)
				break;

			auto& newNode = newTrackNodes[trackId] = new CTrainNode;
			newNode->SetX(srcNode->GetX());
			newNode->SetY(srcNode->GetY());
			newNode->SetZ(srcNode->GetZ());
			newNode->m_bSurfLightingFound = srcNode->m_bSurfLightingFound;
			newNode->m_nSurfaceLighting = srcNode->m_nSurfaceLighting;
			newNode->SetDistanceFromStart(srcNode->GetDistanceFromStart());
		}
	}

	{
		auto nextTrackId = NewTrainTrack();

		auto nextTrack = newTrackNodes[nextTrackId] = new CTrainNode[g_TestNodes.size()];
		newNumTrackNodes[nextTrackId] = g_TestNodes.size();

		for (int nodeId = 0; nodeId < g_TestNodes.size(); nodeId++)
			nextTrack[nodeId] = *g_TestNodes[nodeId];
	}

        // printf works fine here, displaying all tracks with number of nodes in each track
	for (int i = 0; i < newTrackNodes.size(); i++) {
		printf("track: %d | nodes: %d\n", i, newNumTrackNodes[i]);
	}


// this was suposed to be "replace" variable content i guess?
// dont crash but nothing change.
	{
		trace("patch num tracks\n");
		DWORD oldProtect;
		VirtualProtect(NumTrackNodes, sizeof(uint) * 4, PAGE_EXECUTE_READWRITE, &oldProtect);
		NumTrackNodes = newNumTrackNodes.data();
		VirtualProtect(NumTrackNodes, sizeof(uint) * newNumTrackNodes.size(), oldProtect, &oldProtect);
	}

	{
		trace("patch tracks nodes\n");
		DWORD oldProtect;
		VirtualProtect(pTrackNodes, sizeof(CTrainNode*) * 4, PAGE_EXECUTE_READWRITE, &oldProtect);
		pTrackNodes = newTrackNodes.data();
		VirtualProtect(pTrackNodes, sizeof(CTrainNode*) * newTrackNodes.size(), oldProtect, &oldProtect);
	}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant