Tuesday, March 25, 2014

Detour: Fixing the Segfault - Part 2

Continued from the previous entry

With the segfault resolved, the bug no longer crashes ResidualVM, but instead, the game gets stuck, preventing the player from continuing. In the game log, we see some messages that might help us to determine the problem:

New Error in the Log, Exposed by Fixing the Segfault
In this error, we see first that the Lua interpreter is printing lua: (null). This indicates that a value was unexpectedly null. The Active Stack tells us where this error occurred, much like the backtrace in the previous entry, but for the Lua script. Finally, we see the warning that we added in the previous entry, telling us that there's a registry key read, SpewOnError, which doesn't cause the segfault anymore because of our fix. So that's good!

In the stack trace, we see that this failed on a call to a tag method named (helpfully) function, so let's see if we can find that code. In the EMI Demo, the scripts of interest are located in the MagDemo.lab file. Following the same directions as before, unlab the file and delua the Lua scripts.

Inside, _options.lua, there's a comment with the original line number, 1503. Search for that and we find that the function called was main_menu.return_to_game, which kind of makes sense as to why things are getting fouled up. In the original demo on Windows, pressing F1 does not bring up the main menu, but rather, does nothing, while pressing ESC skips the cutscene.

It appears that the game is in a wrong state, but it would be helpful to have more information about the problem and more details as to what was run. Let's enable debugging information in ResidualVM to see if there's anything else that can help us track this down.

In ResidualVM, there are debug flags that can be enabled from the command line, like this:
  • ./residualvm --debugflags=<flag list separated by commas>
In addition, there's an in game debug mode you can enter by pressing CTRL-D. This will bring up a console from which you can turn on debug flags. For both, individual classes of flags may be enabled instead of all flags if you're working on a specific area of the engine.

Let's tackle the part of the bug where pressing ESC doesn't skip the cutscene first. With all of the debugging messages on, we get these messages in the log when we press ESC during the opening cutscene:

Debug Output of Pressing ESC in the Cutscene
Following the path of execution, we see a call to GetControlState(). This function returns the state of the key being passed in. From common/keyboard.h, we see that the keys its checking are:
  • KEYCODE_LCTRL (306)
  • KEYCODE_LALT (308)
  • KEYCODE_BACKSPACE (8)
  • KEYCODE_LCTRL (306)
  • KEYCODE_LALT (308)
From this sequence, it appears that the script being run is the SampleButtonHandler (in _control.lua), which includes the first three calls, then the CommonButtonHandler (in _control.lua) which does the second two.

We then see that the script reported that the Override key was hit. This code is in the CommonButtonHandler which then calls the call_override function, which can be found in the _system.lua file. This function is supposed to stop the current script if the system override is active. Let's check the value of this variable.

Using the console, type:
  • lua_do if(system.override.is_active) then PrintDebug("Active") else PrintDebug("Inactive") end
We find that the override is inactive after starting the game up again and checking the log. So this function call does nothing, control is handed back to the button handler and the movie continues. This reflects the behavior that we see when playing the game, the ESC key is ignored.

Also, in the previous screenshot, we see a repeated series of functions:
  • function: IsMoviePlaying()
  • function: break_here()
These function calls either come from a function in the _system.lua script called wait_for_movie which does these checks repeatedly until the movie is finished, or from the StartMovie function which contains similar logic.

In the actual game, in _cut_scenes.lua, there's a call to EscapeMovie when the override key is pressed during the playback. In the demo, RunFullscreenMovie is much simpler, without this logic. In the Demo, the BOOTTWO function, which is part of the game scripts' startup sequence. In this function, there's a call to a function called StartMovie this function begins playing the intro movie. So the demo doesn't use RunFullscreenMovie at all! We can confirm that there is a movie named intro by checking in the movies directory, so we are sure that this is the code that starts the demo and plays the movie.

So, how did it work in the original interpreter in Windows and what can we do to fix it in ResidualVM? We'll keep digging in the next blog post!

No comments:

Post a Comment