Jump to content
    1. Welcome to GTAForums!

    1. GTANet.com

    1. GTA Online

      1. Los Santos Drug Wars
      2. Updates
      3. Find Lobbies & Players
      4. Guides & Strategies
      5. Vehicles
      6. Content Creator
      7. Help & Support
    2. Red Dead Online

      1. Blood Money
      2. Frontier Pursuits
      3. Find Lobbies & Outlaws
      4. Help & Support
    3. Crews

    1. Grand Theft Auto Series

      1. Bugs*
      2. St. Andrews Cathedral
    2. GTA VI

    3. GTA V

      1. Guides & Strategies
      2. Help & Support
    4. GTA IV

      1. The Lost and Damned
      2. The Ballad of Gay Tony
      3. Guides & Strategies
      4. Help & Support
    5. GTA San Andreas

      1. Classic GTA SA
      2. Guides & Strategies
      3. Help & Support
    6. GTA Vice City

      1. Classic GTA VC
      2. Guides & Strategies
      3. Help & Support
    7. GTA III

      1. Classic GTA III
      2. Guides & Strategies
      3. Help & Support
    8. Portable Games

      1. GTA Chinatown Wars
      2. GTA Vice City Stories
      3. GTA Liberty City Stories
    9. Top-Down Games

      1. GTA Advance
      2. GTA 2
      3. GTA
    1. Red Dead Redemption 2

      1. PC
      2. Help & Support
    2. Red Dead Redemption

    1. GTA Mods

      1. GTA V
      2. GTA IV
      3. GTA III, VC & SA
      4. Tutorials
    2. Red Dead Mods

      1. Documentation
    3. Mod Showroom

      1. Scripts & Plugins
      2. Maps
      3. Total Conversions
      4. Vehicles
      5. Textures
      6. Characters
      7. Tools
      8. Other
      9. Workshop
    4. Featured Mods

      1. Design Your Own Mission
      2. OpenIV
      3. GTA: Underground
      4. GTA: Liberty City
      5. GTA: State of Liberty
    1. Rockstar Games

    2. Rockstar Collectors

    1. Off-Topic

      1. General Chat
      2. Gaming
      3. Technology
      4. Movies & TV
      5. Music
      6. Sports
      7. Vehicles
    2. Expression

      1. Graphics / Visual Arts
      2. GFX Requests & Tutorials
      3. Writers' Discussion
      4. Debates & Discussion
    1. Announcements

    2. Forum Support

    3. Suggestions

Get RPM For Actual Vehicle [C++]


AxelDV
 Share

Recommended Posts

uNiverselEgacy

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.

Link to comment
Share on other sites

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.

Edited by kagikn
Link to comment
Share on other sites

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?

Link to comment
Share on other sites

uNiverselEgacy

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.

Link to comment
Share on other sites

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

Edited by kagikn
Link to comment
Share on other sites

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.
Link to comment
Share on other sites

 

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.

  • Like 2

Prior to filing a bug against any of my code, please consider this response to common concerns.

Link to comment
Share on other sites

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.

Edited by ikt
Link to comment
Share on other sites

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.

  • Like 2

Prior to filing a bug against any of my code, please consider this response to common concerns.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • 1 User Currently Viewing
    0 members, 0 Anonymous, 1 Guest

×
×
  • Create New...

Important Information

By using GTAForums.com, you agree to our Terms of Use and Privacy Policy.