Don't want to see articles from a certain category? When logged in, go to your User Settings and adjust your feed in the Content Preferences section where you can block tags!
We do often include affiliate links to earn us some pennies. See more here.
xoreos is a FLOSS project aiming to reimplement BioWare's Aurora engine (and derivatives), covering their games starting with Neverwinter Nights and potentially up to Dragon Age II. This post gives a short update on the current progress.

Note: This is a cross-post of a news item on the xoreos website.


Continuing with my quest to make the engines display areas (as I did with Neverwinter Nights 2), I turned to Jade Empire the last two weeks. There was just one tiny issue: xoreos didn't yet support the model format. While I could make use of other's people reverse-engineering work for the model formats of other engines (Neverwinter Nights (Torlack), Neverwinter Nights 2 (Tazpn) and Knights of the Old Republic (cchargin), apparently barely anybody bothered to look into Jade Empire. A person called Maian tried to figure out a few thing with just a hexeditor, and while that was a great start (and confirmed my suspicions that the format is similar to Knights of the Old Republic's), it wasn't enough for full support in xoreos.

So, with no other place to look, I buckled down and opened the Jade Empire binary in a disassembler.

Models

The loader function was quickly found, and in combination with Maian's findings, the header was a cakewalk. The model nodes, the structures containing the mesh data, however, proved to be more tricky: the engine simply read the whole shebang into memory, to be used later.

Trying to find a shortcut, I remembered that Neverwinter Nights was able to load ASCII representations of its models. I searched for some common keywords like "verts" and "faces". I had luck: there still is a ASCII model loader in Jade Empire, and not just a remnant of older code; it was updated to load ASCII representations of Jade Empire models. Moreover, for the most part, it actually parsed the model values into the same struct it reads the binary data into. Meaning: I could directly map data from the model file to their ASCII name, getting their meaning handed on a platter.

image image image
Untextured Geometry

This basically gave me the entirety of the general node header, and a good part of the TriMesh node (that contain model geometry in form of a triangle mesh) header. What it did not help with was the format of the vertex data and face indices; those were parsed into a different structure.

Chasing down the place where the binary data was put into that structure wasn't too difficult, and soon I had enough information to render basic, untextured geometry:

image
Materials and Textures

While there was a texture field in the TriMesh node, I found it was more often then not empty. Instead, there was a numerical material field, and a matching .mab file in the resource. A material definition in binary format. I also found several .mat files in the resources, ASCII material definitions. Trying my luck again for finding a loader of these in the binary, I searched for the string in the disassembly, and yes: there is a loader for the ASCII material files, parsing them into exactly the layout of the binary .mab files.

With that, and some fiddling with the offset into the vertex structure for the texture coordinates, I managed to render textured geometry:

image
Triangle lists, strips and fans, oh my!

People familiar with Jade Empire might recognize that model: that's Silk Fox (SPOILER WARNING on that link!). And for some reason, she usually wears a veil in front of face. So, where's that veil?

Turns out, while meshes in Knights of the Old Republic's models are always triangle lists (simple, unconnected triangles), Jade Empire models also contain other mesh types: triangle strips and triangle fans. For the sake of simplicity, I decided to unroll them into triangle lists for now, and that made the veil visible:

image
Now, about that vertex structure...

I said above that I fiddled a bit to find the texture coordinates inside the vertex structure. Well, in fact, what I did was a big, fat hack: I hard-coded the offset based on the size of the structure. Obviously, that's wrong. And it did fail for a lot of models, most prominently the models used for the area geometry.

Wasting quite some time trying to find how the vertex structure is interpreted in the disassembly (and even trying to trace it with WineDbg), I eventually found the answer where I should have looked way earlier: in the Knights of the Old Republic model loader. Since the two model formats are similar, I could just apply what the one did to find the offset in the other. With that, the area geometry renders more correctly:

image
Please note a few things. Yes, not all fields in the material definition are used yet (in fact, only the 4 base textures are used). As such, the rendering looks a bit off, especially where environment mapping should happen. That's why the roof is semi-transparent, for example.

Also, while I did find the offsets for the textures, I'm not yet sure about the other pieces of data. Specifically, the normals and data found after the texture offsets. For example, take a look at this hexdump of the vertex array for Silk Fox's head (click for a bigger view):

image
Each colored section is a new vertex. The red box, i.e. the first twelve bytes of each vertex, is the vertex position, stored as 3 IEEE floats in little endian byte order. The green box, i.e. 8 bytes at offset 0x14 within each vertex, is the first set of texture coordinates, stored as 2 IEEE floats.

Now, the field that in Knights of the Old Republic models denote the offset to the normals, points to 0xC. Unfortunately, there's only space for 2 floats here, while the normal usually needs 3 floats. I have frankly no idea what's going in there. In the case of the first vertex, the data could intentionally overlap, since the 0.0f would fit, but that breaks down not much later.

The data after the texture coordinates looks pretty un-floaty, more like several uint16 values. Again, no idea what that could be yet.

I am, of course, always open to suggestions if you should know what this could mean. :)

In either case, this brings Jade Empire model support close to the other model formats. I found that enough for now; I did spent about 50 hours staring at disassembly to get this far, after all.

Areas

Like a lot of things, areas in Jade Empire are structurally similar to areas in Knights of the Old Republic. A .lyt file specifies the layout: the positions of several models (called "rooms") that make up the area geometry. And a .vis file specifies which rooms is visible from where, so that, in-game, rooms that are not visible from the current location can be culled from the render queue.

Unlike Knights of the Old Republic (and also unlike Neverwinter Nights), the Jade Empire areas don't have .are and .git files to describe static and dynamic elements of the area, like the name, the music playing in the background, and objects and creatures found within. Instead, there's another array in the .lyt file for simple, state-machine driven objects like doors, and an .art file gives some general information for each room (mostly wind, fog, camera perspective). Everything else seems to be placed or started by the game scripts.

A script system is not something I want to bind to the Jade Empire engine just yet, but loading the layout file and placing the room models, that I can do:

image image
Again, the same caveats as above apply: no proper material support yet. And due to these issues of missing environment mapping and wrong blending, those two screenshots are actually less ugly than others. See for yourselves:

image image image
(The last image shows a different problem: I suspect that's a bug in one of the texture loaders.)

Still, apart from the missing NPCs and objects, Jade Empire areas are now in a similar state to areas in Knights of the Old Republic. That counts for something, no? :) Article taken from GamingOnLinux.com.
0 Likes
About the author -
Geek. Atheist+. Leftist. Metal-Head. Discordian. Lefty.
ScummVM dev, xoreos lead.
Free software zealot.
See more from me
The comments on this article are closed.
6 comments

Bumadar Feb 16, 2015
impressive stuff, I can imagine the time flies one you dig into it.
Ivancillo Feb 16, 2015
Hello.

I saw the work you are doing, Sven.

Seems a lot of reverse engineering.

I want to learn a bit about it.

In a tutorial that I'm following, I was told to use [EDB](https://code.google.com/p/edb-debugger/) as it's similar to OllyDBG.

And I see you are using IDA.

How much good it is? Which is better?
DrMcCoy Feb 16, 2015
Quoting: IvancilloSeems a lot of reverse engineering.

Yeah.

Quoting: IvancilloHow much good it is? Which is better?

Frankly, I've never used EDB. I did try OllyDbg a few times in VirtualBox, but it didn't work that well.

Since I'm mostly taking apart DOS games (for ScummVM) and Windows games (for xoreos), I couldn't actually use the debugging capabilities of EDB. Or does it support hooking into winedbg for debugging Windows application running on Linux using wine? IDA theoretically suports that (through a gdb interface), but I couldn't get it to work lately either.

Otherwise, I am pretty content with IDA, but I do have a license, after all, and can therefore use the native Linux version instead of having to run it in Wine.
Anorelsan Feb 16, 2015
Awesome and interesting work as always!
sub Feb 16, 2015
Fantastic work, Sven!

Quoting: DrMcCoyOtherwise, I am pretty content with IDA, but I do have a license, after all, and can therefore use the native Linux version instead of having to run it in Wine.

I just checked the price for IDA Pro and it costs a fortune... :/
Unshra Feb 16, 2015
Very impressive work!
While you're here, please consider supporting GamingOnLinux on:

Reward Tiers: Patreon. Plain Donations: PayPal.

This ensures all of our main content remains totally free for everyone! Patreon supporters can also remove all adverts and sponsors! Supporting us helps bring good, fresh content. Without your continued support, we simply could not continue!

You can find even more ways to support us on this dedicated page any time. If you already are, thank you!
The comments on this article are closed.