I’ve spent some time recently to have a look at the script VM in V as a preparation for its upcoming release. Since IV is pretty old chances are the scripting engine got some updates, as it was the case for Max Payne 3. One example of a change in MP3 is the native context, which is used to send parameters of a native function from a script to the engine and to pass back return values. Every native function basically has a wrapper function whose only argument is the native context.
Native context in GTA IV
In IV it looked like this (excerpt from C++ ScriptHook by aru)
class scrNativeCallContext { public: ptr m_pReturn; u32 m_nArgCount; ptr m_pArgs; };
So we have two pointers here that point at memory containing the (possible) return values and the arguments. We also have an argument specifying the argument’s count. This is really simple to understand and to implement in your own code. Note that to return values, R* makes heavy use of arguments passed by reference instead of using the return section of the native context.
Native context in Max Payne 3
Things became a little different in Max Payne 3. One of the changes involved the passing by reference. R* now almost always used the return section instead of pointers. So void GetMaxAmmo(Ped ped, int weapon, u32 *pMaxAmmo) became int GetMaxAmmo(Ped ped, int weapon). While this is definitely a nice change, it doesn’t really affect the implementation and thus can be neglected when creating a script hook. One other change however, may definitely not. The native context‘s structure has changed:
class scrNativeCallContext { public: GtaThread* m_pCurrentThread; ptr m_pReturn; u32 m_nArgCount; ptr m_pArgs; };
Right, it now also has a pointer to a GtaThread. This alone however wouldn’t be something to worry about, if this didn’t introduce some other major change: Pointers in arguments are now passed via the thread’s stack. Say one wants to pass a char* to a native function, it will no longer just push the string. However it will generate an offset, which is a 32-bit value whose first 3 bit indicate the type of the offset/pointer and the other 29-bit specify the offset from the beginning of the stack in bytes. Yes, in bytes, so the stack is definitely not a 4 bytes type array or anything, but looks more like a plain byte array. Maybe even used to pass smaller types/data or prevent alignment to save space. For a string at offset zero (because it’s the first pointer pushed this way this call), the value would look like this: 0xA0000000 The first three bits have the value of five, which is the type for strings. The remaining 29 bits have the value of zero, since it should point at the beginning of the stack.
In the wrapper of a native function, this value is then retrieved like this (before calling the real native):
INT32* stack = thread->Stack; switch (type) { case STACK_TYPE_STRING: // Return the actual value return stack[byteOffset / 4]; break; }
This will return the char* pointer stored at stack[offset]. So this is a rather major change one has to take into account when trying to invoke native functions in Max Payne 3.
Native context in V
During my research (note that I’m far away from being a PowerPC expert, so please bear with me in case I got something wrong) on V, I found out that this seems not to be used by V. While the wrapper of a native in Max Payne would call a function similar to the code shown above, this is not the case in V. Take a look at the wrapper of GET_MAX_AMMO in V:
.text:82DFC948 n_GET_MAX_AMMO: # DATA XREF: sub_82E0A3E0+2F0o .text:82DFC948 .text:82DFC948 .set var_10, -0x10 .text:82DFC948 .set var_8, -8 .text:82DFC948 .text:82DFC948 mflr r12 # set up stack .text:82DFC94C stw r12, var_8(r1) .text:82DFC950 std r31, var_10(r1) .text:82DFC954 stwu r1, -0x60(r1) # end stack setup .text:82DFC958 lwz r11, NativeContext.m_pArguments(r3) # load native arguments pointer (load [arg_0+8] into r11. this is the native arguments pointer, used to be +0xC in MP3) .text:82DFC95C mr r31, r3 # move native context into r31 .text:82DFC960 lwz r3, NativeArguments(r11) # move first native argument into r3 .text:82DFC964 lwz r4, NativeArguments.argument_2(r11) # move second native argument into r4 .text:82DFC968 lwz r5, NativeArguments.argument_3(r11) # move third native argument into r5 .text:82DFC96C bl GET_MAX_AMMO .text:82DFC970 lwz r10, 0(r31) # move native context back into r10 .text:82DFC974 clrlwi r9, r3, 24 # Store result in r9 .text:82DFC978 stw r9, NativeContext(r10) # move return value to pReturnValue in native context .text:82DFC97C addi r1, r1, 0x60 # destroy stack frame .text:82DFC980 lwz r12, var_8(r1) .text:82DFC984 mtlr r12 .text:82DFC988 ld r31, var_10(r1) .text:82DFC98C blr # return
There is no call prior to calling the native and the arguments are just retrieved from the native context and pushed to the real native. So for some reason, R* didn’t implement this into GTA V, this might be due to different teams working on Max Payne 3 and GTA V, but it might also have proven not to be of any use, I don’t really know.
Script VM native invocation
The script VM now features a whole of 127 instructions, up from 77 in Max Payne 3. Invoking natives however (opcode 45) hasn’t really changed and seems to work the same way it did before. So if this is to remain the same for V’s PC release, there probably isn’t much work needed to adjust our current tool’s (ScriptHook) to work with V.
Hello,
Great blog! It is very good to see how they develop engines and that stuff. My question is: In your point of view, as you are looking into the engine of GTA V, how do you think it will be to modding? I mean, we know that R* doesn’t support mods or either care about them, but my question is related to GTA IV as well as everyone knows, it is very bugged to mod, using static lists and other stuffs causing crashes all time…
Do you think it will be the same on GTA V or it will be more friendly?
Thanks! I find IV pretty good to mod, what exactly crashes for you? As it will probably use a similar engine it will be as friendly as IV I suppose. The big thing to worry about really is the protection R* will throw in, that can be a game changer. But we have to wait and see how much they want to protect MP.
Hi LMS, will you be using the same method in GTA V to stop the damage bug in ELS like you did in IV? I created a script similar to ELS called EVL. I created in the hopes it would take the pressure off everyone involved in ELS and hoped we would see more ELS enabled cars ready for when ELS came out. I spoke to Lt.Caine a while back about the bug and he said you were still working on Reverse Engineering V. Was curious if the old way would work because of the new protection or do you have to do something different?
Hi Ben,
I didn’t really look into this specific part of the game yet, so I can’t give any comments on that. But I assume it would work similarly, yes.
Hi again LMS, i have another question for you. When/if you do get round to doing some reverse engineering on GTA V, im wondering if possible you would be able to see what triggers an emergency vehicles tail lights to start flashing when the siren is activated and if possible find a way to stop that happening. Its personally a really annoying thing for me because i just think it ruins the emergency vehicles. Anyway let me know if you think this is possible mate, thanks.
Hi Ben,
I’m pretty sure that this can be done rather easily. From my experience with vehicle lights in IV (and a little bit in V), it’s mostly functions calls per light, so it should be possible. However, I can’t tell you when/if I will ever look into it. If you need any guidance for finding it yourself, I’d be more than happy to assist, though.
Hi, yeah if you could assist me that would be awesome. Thanks
Hello mate. Me again. Sorry to keep bombarding you with lots of questions. A while ago I asked you about the door bug for ELS, I’ve noticed that in the latest GTA V ELS videos that the vehicles don’t automatically repair themselves so they no longer have the door bug. I’m just wondering if this is down to you as I’m not sure that there’s anyone else in the community that has the knowledge to fix the issue and if it is you that sorted the door bug for GTA V would you be kind enough to tell me how I could go about using it as well please. Is there a piece of code that I could add into my script or maybe you have your own .dll that has the code included that I could download and use as a reference? Look forward to hearing from you. Thanks
Given that I have indeed fixed the door bug a while ago, chances are that you see the outcome of my changes. However, it’s a memory patch that is still in development and only works on certain game versions so far (due to updates coming in). I’ve also developed it closely with Lt.Caine’s requirements, so no, sorry, there won’t be any public version of the code soon.
Ah fair enough mate. Would you still be able to assist me with what i need to do in order to find out what triggers the tail lights to flash and how to disable them please.
Sure, what are you currently having issues with?
Hello LMS, i saw that you and Lt.Caine may be ditching the extras you are using for ELS and may be going doing the route of using the sirens. I suppose it would be easier and harder at the same time. Im currently still working on my script EVL. I’m trying to find out information on reverse engineering so i can make a patch to stop the door bug. I was wondering if you could give me some pointers on what software you use. Also i was wondering, in GTA, when you turn an extra on, the vehicle fixes itself and thats what causes the door bug, so if i were to make a patch saying when the vehicle has an extra that is turned on, dont fix the car. Would that possible at all? I know you wont give out your code but any help you can give would be very much appreciated. Thanks
Yes, that is perfectly possible as I already did it. I mostly use IDA (static) and Cheat Engine (dynamic) with my own anti anti debugging code enabled. In your case, have a look at the TOGGLE_EXTRAS function to learn which calls resets the vehicle. Then try to work around that by disabling parts of it. It was actually much easier than in IV this time.
Okay thanks mate. Il take a look. Hopefully il be able to figure it out. It’s the last thing I need to do for my script.