Figuring Out What to Do

Continued from the previous entry

In this post, I’ll show you what my work flow generally looks like for reverse engineering. In the past, I’ve worked on a few reverse engineering projects like the Broadcom BCM43xx and the Collie SD Card interface for Zaurus. In any reverse engineering project, the first thing to figure out is where to begin! For this post, I’ll take a stubbed function from the ResidualVM code, explain how to find the original implementation, figure out what it does and then re-implement it.

After getting EMI running, I noticed that there were a lot of debug messages about stubbed functions in the console window.

Console window, filled with debug messages

Picking one at random, I decided to look into the function Lua_V2::SetActorLocalAlpha, but first, I needed to do a little bit of research!

Understanding what the structure of the program is before diving into the assembly is usually a good idea. From the documentation at the ResidualVM wiki, I saw that Lua was the scripting language running the game and that EMI’s engine was structurally similar to the one used in Grim Fandango. Let’s take a quick look at the source code for ResidualVM and investigate the structure some more.

In engines/grim/emi/lua_v2.h (line 39), we can see the list of Lua script functions that the engine provides. The actual code that implements these functions can be found in the rest of the files in engines/grim/emi/. Of note, the function we’re interested in, SetActorLocalAlpha can be found in engines/grim/emi/lua_v2_actor.cpp at line 38. Helpfully, this code is partially completed, waiting to be finished!

So, to summarize what we have so far:

  • We have identified the function we’d like to work on
  • We have information about how the EMI engine was put together
  • We have found where the implementation will go once we’ve written it, and some helpful information from the stub function.

Next, we’ll take a look at the binary from the original version of EMI. I’ll be working with the patched version if you’d like to follow along.

I like working with IDA, it’s a great reverse engineering tool! Luckily for poor students like me, a version of the tool is provided for free for non-commercial use. While all of the features of later versions would be nice, including a native Linux build, this will do for now. If you’re not using windows, Wine can be used to run IDA with almost no issues.

To begin with, I first checked to see if the function name we were interested in was in the executable at all. Some binaries are stripped or obfuscated, making this job a lot harder.

  • strings Monkey4.exe  | grep SetActorLocalAlpha

This returned two instances:

SetActorLocalAlpha
SetActorLocalAlpha: Actor isn't wearing any primitives!

This looked promising! I put the Monkey4.exe binary into IDA and let it process the file. Once this was complete, I searched for the text SetActorLocalAlpha. Lucky for us, there’s a jump table with the function name in ASCII, likely for the Lua scripting engine to convert the text into the actual function call. The entry for SetActorLocalAlpha is found at 0x004C06D8, and points to a function call at 0x00413570. We now have the entry point for the function we’re interested in!

In the next post, we’ll investigate what can be learned from the Lua scripts that actually call this function and how it can be used to improve our code.

Introduction, Unpacking and Moving in

Hi! My name is Joe Jezak and I’ve decided to start working on fixing the issues with Escape from Monkey Island (EMI) in ResidualVM.

Usually, when I start working on a new project, the first step is to see what happens when you run it! So, to get started, I first found my EMI discs:

The Discs were hanging out with some old friends!

To prepare the discs for use in ResidualVM, I copied the contents of the Monkey4 directory on both discs to my hard drive. As noted here, you must rename Textures/FullMonkeyMap.imt however, I found that both CD’s files must be renamed, not just the file on disc 2. So, for disc 1, the file must be renamed to Textures/FullMonkeyMap1.imt and for disc 2, the file must be renamed to Textures/FullMonkeyMap2.imt. When copying the files, make sure that you copy the .m4b files from the MonkeyInstall directory as well! Finally, I found that the voiceAll.m4b file must be copied from disc 1, the copy from disc 2 causes an MD5 error. Also remember that you may need to copy the patch (if needed) and the data file from the Residual project into your EMI data directory.

Okay, so now, we have the game data files. Great! The next step is to fetch the ResidualVM source code and build it by following these directions. That wasn’t too bad! It compiled cleanly on the first try with no issues. I started up the build and set it up by adding a game and pointing it at the location I stored the data files.

Setting Up the Game

Now, I crossed my fingers and started up the game and it worked! Kind of:

There’s something missing here…

It was apparent that there is an issue with fading between images, resulting in weird output like this. A bug to add to the list! However, things actually work a whole lot better than this first impression would seem. I was able to complete the whole first act (Act 0?) without any game breaking bugs. There were plenty of issues, such as the hot coal floating around like mad and the wick on the cannon not burning away, but the game was playable. Great!

Guybrush Threepwood at his polygonal best

So, where to from here? Helpfully, the current build prints out a large number of debugging messages from the Lua interpreter pointing out places where there is missing code. I decided to pick one and see if I could figure out what needed to be in this function. But that’s for the next entry, this one has gone on long enough.