O'Reilly Network: What's one interesting example of usecode's intrinsic functions that the Exult team figured out, and how did you do it?
Jeff Freedman: One of the most challenging was
path_run_usecode (whose name, of course, we didn't know). Certain
animation sequences in the original, like walking to a bucket and picking it
up, weren't happening in Exult, and this was the "unknown" intrinsic common to
those situations. It's taken a lot of trial and error, but we eventually
figured out that when this function is called, the NPC [non-player character]
should be scheduled to walk to the given location, and, when he gets there, the
specified usecode function should be called with that final parameter as the
Ryan Nunn: The
intrinsics are reasonably interesting. To figure them out is trial and error
and looking over lots of disassembled usecode. We pretty much just have to
watch the usecode executing to see what it's trying to do. Then all that can be
done is to take a guess. Sometimes these guesses are correct; other times they
are not. There are numerous cases where we can get an intrinsic working, but
then there will be one or two cases where things just don't work. It's annoying,
and many times we had to implement workarounds for the cases that just didn't
ORN: So what are your impressions of the usecode language that Origin developed for the Ultima games?
JF: It does the job nicely, and was a really smart way for them to control the game. Also, designing their virtual machine to be purely stack-based makes it easy to write a compiler that targets it, as we've done in ExultStudio.
RN: It's a fairly functional language, but it does have some flaws. In particular, in order to execute timed events, another language had to be used. So there were actually two languages.
Another is that the usecode must finish executing before the game can go on. It would have been more flexible if functions could be suspended, and resumed later.
ORN: It was basically a hit-or-miss method when it came to figuring out the way game stat data is interpreted by the Ultima VII engine. Describe the steps involved that the Exult team devised in order to decipher the stat data.
Read other Linux Interviews:
JF: The general statistics weren't that hard, since they're shown in the "stat" displays in the original. What we've guessed at is the way they're interpreted during combat. And it's probable that we are not doing things the same way as the original. In particular, I don't think we're using dexterity to determine speed, and I'm pretty sure the original did.
RN: The simplest way is to use the cheat menu system in the original game and toggle flags/change data and then compare savegames before and after the changes were made. Lucky for us, most of the data had already been figured out by others, but there was still a fair amount that we needed to figure out ourselves.
ORN: Exult uses the ScummVM project's FM synthesizer. Why was this particular sound code used? And was there much difficulty involved in integrating it with Exult?
RN: The FMOPL FM Synth (which I did grab from ScummVM) is an optional method to play music with. By default, we use the standard Windows MIDIOut on Windows, Timidity on Linux, and QuickTime on MacOS. I used the FM Synth code from ScummVM because porting it wasn't going to be difficult. The idea of why I did it was to allow PDA devices, such as the Zaurus (which doesn't have any native method of MIDI playback) a method of playing music without requiring an excessive amount of storage space.
ORN: How similar are the graphic scalers used in Exult to those in Snes9x?
JF: They're essentially the same, since both were done by Derek Liauw. I'm always amazed at how good they look, even compared to the scaling done by image-editing programs.
RN: I would say they are "exactly" the same, but that isn't really true. We do use the same scalers as Snes9x, but the code of our scalers is slightly modified to perform clipping (the original code by Derek didn't have any clipping), and are also in C++ templates so they can easily work in both 16-BPP and 32-BPP display modes. Other than that, they are the same.
ORN: Any plans to add additional graphics and sound improvements to the original Ultima VII titles? Or, through ExultStudio, how extensively will the user be able to modify the games' graphics and sound?
JF: ExultStudio is useful for changing graphics, and makes it easy to import PNG images into Ultima VII, or to export Ultima VII images as PNGs. You can also bring up dialogs to set weapon, armor, and monster attributes. Theoretically, you could create an original game with our tools.
RN: The idea is for everything to be changeable. As it currently is, the sound effects are WAV files and music can be supplied as both MIDs and OGGs. Graphics need to be in the Ultima VII format, but we supply plug-ins for The GIMP and Photoshop, as well as a conversion tool.
ORN: Any plans to create versions of Exult that support the other Ultima titles (i.e. Ultima VIII, IX)? How similar are the various Ultima engines, or are they vastly different?
RN: There are no plans for Exult to support any other Ultima titles. All of the engines are just far too different. Ultima VII and Ultima VIII have a number of similarities, but there are just too many differences. The Ultima VIII usecode format is so vastly different from Ultima VII that supporting it would be as much work as writing a new game engine.
However, a few people from the Exult team (including myself) are working on Pentagram, a re-creation of the Ultima VIII engine. There is going to be very little code reuse from Exult.
ORN: Any advice to those looking into reverse-engineering a game engine -- either technical or legal advice?
RN: Find out as much about the original game as possible. Knowing cheats (especially anything that cause minor state changes, such as toggling flags), bugs, and other engine limitations can be a great help in decoding file formats and understanding the internal structure of the original engine.
Try modifying the [game's] original data files and observing the result to the game. This can be a good way for decoding file formats, especially opcodes in compiled scripts. If the files have checksums, though, this may not work.
When examining various files, look for patterns in the individual files and between files. Arrays are often very obvious to notice.
There may be similarities between the file formats of a game and other games made by the same company. For instance, the Flex file formats used in Ultima VII, Ultima VIII, and Ultima IX are all fairly similar. If another game by the same company has been reverse-engineered, it may have code that might be useful to you. Alternatively, if you reverse engineer one game, it may be "trivial" to add support for another game.
Last and not least, always -- and I repeat, always -- use platform and byte-order independent methods to read and write files.
JF: So far, we haven't had any legal trouble. I'd like to think this is due to our emphasis that users have to own a legal copy of Ultima VII in order to use Exult. So we're helping to sell copies of the game. But it's just as likely that we're merely lucky.
Howard Wen is a freelance writer who has contributed frequently to O'Reilly Network and written for Salon.com, Playboy.com, and Wired, among others.
Return to the Linux DevCenter.
Copyright © 2009 O'Reilly Media, Inc.