Quantcast

Jump to content

» «
Photo

Get RPM For Actual Vehicle [C++]

11 replies to this topic
AxelDV
  • AxelDV

    Player Hater

  • Members
  • Joined: 16 Jun 2016
  • Mexico

#1

Posted 4 weeks ago

I need the pattern or function to get rpm please


Unknown_Modder
  • Unknown_Modder

    Staff at GTA5-Mods.com

  • Members
  • Joined: 07 May 2015
  • Germany

#2

Posted 4 weeks ago Edited by Unknown_Modder, 4 weeks ago.

Just read the float at CVehicle + 0x844 and multiply it by 10000.0f.


uNiverselEgacy
  • uNiverselEgacy

    Player Hater

  • Members
  • Joined: 15 Jul 2015
  • United-States

#3

Posted 4 weeks ago

I don't think there's a function for that. You just need to read the float value in the CVehicle struct.

The offset can be found using this pattern: F3 44 0F 10 93 ? ? ? ? F3 0F 10 0D. For 1180 it's at 0x844.

You get a value from 0.0 to 1.0. You need to adjust it depending on which vehicle it is if you want the real rpm number.


kagikn
  • kagikn

    Ghillie

  • Members
  • Joined: 05 Jun 2014
  • Japan

#4

Posted 4 weeks ago Edited by kagikn, 4 weeks ago.

It's surprising that uNiverselEgacy told us the pattern for RPM faster than Unknown Modder. I believe Unknown Modder also knows the pattern, though.
Anyway, now we don't need to track the offset of RPM when a GTA update changed the vehicle class unless the pattern is wrong.

ikt
  • ikt

    _

  • Members
  • Joined: 02 Oct 2006
  • None

#5

Posted 3 weeks ago

How do you find these patterns and what are they used in? Or do you just search for code that uses that offset and use that as pattern?

uNiverselEgacy
  • uNiverselEgacy

    Player Hater

  • Members
  • Joined: 15 Jul 2015
  • United-States

#6

Posted 3 weeks ago

How do you find these patterns and what are they used in? Or do you just search for code that uses that offset and use that as pattern?

Yea just use a debugger to find the code that accesses that address.

  • ikt likes this

kagikn
  • kagikn

    Ghillie

  • Members
  • Joined: 05 Jun 2014
  • Japan

#7

Posted 3 weeks ago Edited by kagikn, 3 weeks ago.

So, could you show us usage? We can use it by finding the pattern and reading value as a int pointer?

Cuky
  • Cuky

    Player Hater

  • Members
  • Joined: 3 weeks ago
  • Germany

#8

Posted 3 weeks ago

Hi all,

uNiverselEgacy could you please do a short tutorial how to get these offsets?


ikt
  • ikt

    _

  • Members
  • Joined: 02 Oct 2006
  • None

#9

Posted 3 weeks ago

So, could you show us usage? We can use it by finding the pattern and reading value as a int pointer?


I just implemented something similar once I realized this is a much better way than a bunch of offsets :D

uint64_t getHandlingOffset() {
	int gameVersion = getGameVersion();
	auto offset = gameVersion >= 24 ? 0x830 : 0;
	offset = gameVersion >= 26 ? 0x850 : offset;
	offset = gameVersion >= 28 ? 0x878 : offset;
	offset = gameVersion >= 34 ? 0x888 : offset;
	offset = gameVersion >= 36 ? 0x8A8 : offset;
	return offset;
}
becomes

uint64_t getHandlingOffset() {
	auto addr = MemoryAccess::FindPattern("\x48\x8b\x87\x00\x00\x00\x00\xf3\x0f\x10\xc8", "xxx????xxxx");
	uint64_t offset = *(int*)(addr + 3);
	return offset;
}
For the pattern I just chose some instruction that accessed 0x8A8. I hope it's not an exact science, and any sufficiently distinct pattern of any function accessing this address should work.
  • kagikn likes this

K^2
  • K^2

    Vidi Vici Veni

  • Moderator
  • Joined: 14 Apr 2004
  • United-States
  • Best Poster [Technology / Programming] 2016
    Best Poster [Programming] 2015
    Most Knowledgeable [Web Development/Programming] 2013
    Most Knowledgeable [GTA Series] 2011
    Best Debater 2010

#10

Posted 3 weeks ago

uint64_t getHandlingOffset() {
	int gameVersion = getGameVersion();
	auto offset = gameVersion >= 24 ? 0x830 : 0;
	offset = gameVersion >= 26 ? 0x850 : offset;
	offset = gameVersion >= 28 ? 0x878 : offset;
	offset = gameVersion >= 34 ? 0x888 : offset;
	offset = gameVersion >= 36 ? 0x8A8 : offset;
	return offset;
}

I know it hardly matters in this particular case, but ternary operator chains are about the worst thing you can do for performance, readability, and debugging in general. I know this particular snippet is defunct anyhow, but in the future, if you encounter a similar need, consider doing something like the following.

uint64_t getHandlingOffset() {
	static const std::map<int, uint64_t, std::greater<int>> kVersionOffsets {
		{0, 0},
		{24, 0x830},
		{26, 0x850},
		{28, 0x878},
		{34, 0x888},
		{36, 0x8A8}
	};
	
	const int gameVersion = getGameVersion();
	return kVersionOffsets.lower_bound(gameVersion)->second;
}
Believe it or not, map will actually perform slightly better even in this simple case. It will perform way better if you get to 10-20 versions. But more importantly, it's easier to edit, can be loaded dynamically from a data file, and won't make hopeless C++ nerds like myself choke on a drink while casually reading a forum.

Oh, and const qualifiers. Use them. They help compiler do its job, and make code easier to read.
  • ikt and kagikn like this

ikt
  • ikt

    _

  • Members
  • Joined: 02 Oct 2006
  • None

#11

Posted 3 weeks ago Edited by ikt, 3 weeks ago.

I know it hardly matters in this particular case, but ternary operator chains are about the worst thing you can do for performance, readability, and debugging in general. I know this particular snippet is defunct anyhow, but in the future, if you encounter a similar need, consider doing something like the following.
[...]
Believe it or not, map will actually perform slightly better even in this simple case. It will perform way better if you get to 10-20 versions. But more importantly, it's easier to edit, can be loaded dynamically from a data file, and won't make hopeless C++ nerds like myself choke on a drink while casually reading a forum.

Oh, and const qualifiers. Use them. They help compiler do its job, and make code easier to read.


Thanks, I never thought about using maps like this. Can you give some background information on why/how this is faster? I did find something about ternary operator chains being slightly slower than if-else chains, but not much about using maps for this.

K^2
  • K^2

    Vidi Vici Veni

  • Moderator
  • Joined: 14 Apr 2004
  • United-States
  • Best Poster [Technology / Programming] 2016
    Best Poster [Programming] 2015
    Most Knowledgeable [Web Development/Programming] 2013
    Most Knowledgeable [GTA Series] 2011
    Best Debater 2010

#12

Posted 3 weeks ago

Ordered maps, such as std::map, are O(ln N) order of complexity, because they are organized as a tree. If you wanted to achieve the same effect with if-else statements, you'd write something like this.

uint64_t getHandlingOffset() {
	const int gameVersion = getGameVersion();
	if (gameVersion >= 28)
	{
		if (gameVersion >= 34)
		{
			if (gameVersion >= 36)
				return 0x8A8;
			else
				return 0x888;
		}
		else
			return 0x878;
	}
	else
	{
		if (gameVersion >= 24)
		{
			if (gameVersion >= 26)
				return 0x850;
			else
				return 0x830;
		}
		else
			return 0;
	}
}
This is probably the most efficient method that doesn't involve writing a redundant lookup table, but it's absolutely horrible for readability or editing. Maps effectively build a tree like that for you, including doing all of the heavy lifting on balancing it. There is an added cost of function calls there, but that's still not as bad as having a bunch of extra branches. Your biggest potential loss with maps is cache misses, but you minimize that exposure by setting the map up all at once, which will mean that all of it is nearly guaranteed to be allocated in continuous chunk of memory.

Now, the reason if-else blocks are more efficient to begin with than ternary operators is because of how much work went into optimization. Both CPU and compiler optimizers are geared towards them, and they expect certain layout. When you throw ternary operators at such a compiler, they don't do so well. And the reason for that is that, in general, you might not want to evaluate both branches of the ternary operator, which means that decision on which branch to evaluate has to be made upfront. And that greatly limits how much rearranging the optimizer can do and how much look-ahead work the CPU can do.

Which leads us to another trick. Sometimes, it's cheaper to do extra math. These two are equivalent in most cases.

int z = (a > b) ? x : y;
const bool choose = a > b;
int z = choose * x + (1 - choose) * y;
And despite the later being a lot more instructions, if you have to do a lot of such decisions, it's going to run a hell of a lot faster. Because math is something CPU does well, and branching is a touch harder. As an added bonus, while branching has to involve a CPU core checking each condition one at a time, if you are running this code via SIMD or shaders, you can usually do them in bulk. So instead of hitting a prediction miss that interrupts your entire pipeline for a hundred cycles, you end up doing eight of these in 3 cycles on the same core without slowing down for a beat.

Not that you'd ever want to use something like this to choose an offset in a file. The realistic scenario where you pull out heavy guns like that is running animation on tends of thousands of characters in a complex scene, where you have to apply different joint constraints depending on what's happening with the animation.

So at the end of the day, the main reason to use maps still comes back to how easy it is for somebody to follow the code. If I have a forest of ternary operators, a person seeing that code for the first time has to figure out how everything works to be able to make the change. If you have a map initialized from a list of pairs, somebody can come in and edit that list without having any idea how the rest of the code works. And even if you are working on a piece of code alone, a month later, you might as well be a completely different person. It's worth it to be kind to your future self.

Sorry that it turned into a rant. This just happens to touch on so many subjects.
  • Wesser and ikt like this




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users