Friday, March 21, 2014

Identifying Variables and Functions

Continued from the previous entry

In this post, we'll be focusing on variables, function calls and the structure of the decompiled function, SetActorLocalAlpha.

In the previous entry, we examined the Lua script that calls this function and identified its arguments. Applying that knowledge to the disassembled code indicates that the first 4 calls to lua_lua2C are actually calls to get the parameters for the function. As such, this code can be re-written with descriptive variable names. Additionally, the types suggested by these parameters suggest similarities with code that's already been written.

Starting with the first parameter, we see that in the script, the function is called by itself, with no colon or period operator. This indicates that the function is standalone and not a member of any class. Next, we see that the first parameter is self.hActor, as seen in the call to SetActorLocalAlpha in the previous post. Since we know that the variable is a member of the "self" object, we need to identify what this variable is used for. Often, it's possible to tell the object's type by looking at what sets the member variable. Searching through the scripts for "hActor =" will identify where the hActor variable was set, and we're in luck! The file _actors.lua is the only file in the scripts directory that matches this criteria. Let's take a look at where it's used.

The first hit we get is in the actorTemplate, a structure that is used in the Lua script as a template for all new actor objects. While this is useful for identifying members of the Actor class, this doesn't help with identifying the type for hActor. Let's move on to the next instance.

In this function, Actor.create, we see that the actorTemplate is copied into the variable local1. The variable later has the hActor member set by saving the return value of the function LoadActor. From the source for LoadActor found in engines/grim/lua_v1_actor.cpp (line 37), we can see that this function creates a new Actor, and therefore, the type for hActor is most likely an Actor. While this might seem a bit obvious, when things are less obvious, you'll still follow the same basic steps.

With the knowledge that this variable is an Actor object, we can improve our translated code by naming the variable that we've saved the 1st parameter actorObj. We can also continue through the code and simplify functions that use this a parameter. It is also a good idea to compare other uses of Actor objects to see if already rewritten code matches what has been found so far. In this case, we see a call to lua_isuserdata and lua_tag:
lua_userdata and lua_tag

The call to lua_isuserdata is checking that the 1st parameter contains an object with the type UserData. If the 1st parameter doesn't have a UserData object, the whole function will just return. In the second box, the call to lua_tag is comparing the UserData's tag with the number 52544341h. While this might seem like a random number, if we interpret this value as a string of four characters, they spell out 'RTCA', or 'ACTR' in little endian.

Before we continue, let's look at what we mean by Lua UserData and tags. In the ResidualVM wiki we see that in the modified version of Lua used in this engine, variables are saved in a pool and are identified by their pool id number and tagged with an identifier. The id number is used to retrieve the data from the pool, while the tag is used to identify the type of the object. Going back to engines/grim/lua_v1_actor.cpp, when the engine loads the Actor, it creates a new Actor object instance, then adds this instance into the pool. It is also applying the tag using a macro: MKTAG.

So, applying this information, we interpret these two lines of code as checking to see if the variable is UserData, and if so, does it have the tag 'ACTR'. If not, the code will return. This bit of assembly can now be converted into C++:
The C++ code, translated from assembly
To this point, we haven't really added anything to the project yet since this code had already been worked out by one of the previous developers. In the next post, we'll start working on filling in the missing parts of the code with the information that we've learned so far.

No comments:

Post a Comment