January 19th, 2006, 10:22 PM
I've been thinking for a long time about creating .MOB format specs. However, it is a really hard job to create comprehensive specs for this file format.
This thread has been created as a placeholder for the MOB specs. Here is what I'm going to do: every day or two, I'll try to add some information about the MOB file format here, starting with the very basics (and the very first offset in the MOB file), and going up to more and more complicated things with each post. In the end I will try to come up with comprehensive specs of all known intrinsics of the MOB format.
This thread is closed, so if you'd like to express your opinion about what is said in this thread or if you'd like to ask a question about the .MOB format, please use the old .MOB format breakdown thread, which has been unstickied. Thanks.
January 20th, 2006, 11:32 AM
Lesson 1: Object File Format 101
Historical and Background Information
First of all I'd like to make it clear that the so-called MOB format is not really a standalone format, and even the very word combination "MOB format" is somewhat incorrect. In fact, MOB files represent instances of the Object File Format (will be called "OFF" later in our discussions), which is also used by the game for embedded static objects in sectors and player characters (PCs). ToEE's predecessor, Arcanum, also used OFF for the prototypes (protos). Later those prototype (.PRO) files were replaced in ToEE with the protos.tab file that we're all familiar with.
The Object File Format is super convoluted. In fact, for their very small size (MOB files' size range from only about 100 bytes to a few kilobytes), OFF-based files are incredibly complicated. That's why if you would like to understand these specs, get ready to face a challenge really soon. It took me four months to decipher the basics of the OFF format and create an algorithm that could load and write back valid mobile object (.MOB) files that don't make the game crash.
The General Structure
The goal of the OFF is to store a game object, that's why apparently it stores a collection of that object's properties. Generally speaking, the OFF structure can be subdivided into the following big groups:
The Header - stores the most general information about the object, such as the version of the OFF used, the prototype identifier that the object is based on, the object type, its globally unique identifier that makes the object unique from all other game objects on the same map, and a bunch of other stuff.
The Property Bitmap - a huge field that defines which properties exactly the OFF file stores. Don't let the word "bitmap" mislead you here: it has nothing to do with graphics. The property bitmap is just a big field where one bit stands for one property. Most obviously, if the bit is set (=1), then the property should be present in the object file. If the bit is not set (=0), then the property is not even stored in the OFF file, and is taken from the base prototype (from the corresponding entry in protos.tab). We'll speak about the property bitmap in more detail later on, so don't worry if you don't understand something at this point.
The Property Collection - this part contains all the properties that are defined in the property bitmap. Each property has its own subformat, which is hardcoded and defined by the identification number of the property (so there are NO offsets in OFF files, the size and the format of individual subfields are defined by the temple.dll).
And so, for a starter, let's begin with the easiest part - the header.
As I said before, the header of an OFF file stores the most general information about the object. The header structure is as follows:
The order of explanation: OFFSET - SIZE - MEANING
1. 0x0000 - INT32 (4 bytes) - Version of the OFF format. Always 0x77h. The version of the OFF format is the same for both ToEE and Arcanum. I don't really know why exactly this number was taken.
2. 0x0004 - INT16 (2 bytes) - Subtype of the OFF format. In ToEE, it's always 0x1h, which stands for an instance of a prototype object. In Arcanum this field could also have the value 0xFFFFh, which meant the prototype object itself (a proto). Since ToEE does not use the OFF in order to define prototypes (protos.tab is used instead), the subtype 0xFFFFh is not applicable for this game, and will make the game CTD.
3. 0x0006 - INT16 (2 bytes)+INT32 (4 bytes) - In-memory handle part 1. Used mostly when an object is already in memory, possibly in order to create a way to access this object in memory. It has little effect when stored on the disk. Earlier versions of ToEEWB used to store these fields as 0x0, but some files stored in this way used to CTD for no apparent reason, that's why an algorithm was devised to create a random value in these fields within known limits characteristic of the game original OFF files.
4. 0x000C - INT32 (4 bytes) - prototype ID. This is an ID of the prototype (an entry in protos.tab) that the object is based on. All properties which are not stored in an OFF file will be inherited from this prototype.
5. 0x0010 - INT32 (4 bytes)+INT32 (4 bytes)+INT32 (4 bytes) - In-memory handle part 2. Essentially this is a continuation of field . What is interesting is that the two parts are separated by the prototype identifier. I presume the prototype ID (field ) is actually the part of the in-memory handle structure, or at least it was, at a certain point of the game development.
6. 0x001C - INT64 (8 bytes) - type of the object GUID (globally unique identifier). This field defines the type of the globally unique identifier that is to follow. Basically it defines how the memory for the object should be allocated. Setting an incorrect value for the GUID type causes a memory leak, which may cause massive game slowdowns and even CTDs. The general rule is: for mobile objects (MOBs) and player characters (which essentially are mobile objects as well), this field must have a value of 2 ("mobile object GUID"). For static objects that are embedded in sectors, this field must have a value of 0 ("no GUID"). Static objects' GUID doesn't really matter, because they are embedded into sector files and highly depend on them. Other GUID types (besides 0 and 2) are not applicable for ToEE, at least not for objects stored on disk. Arcanum used to have more GUID types.
7. 0x0024 - G_GUID (16 bytes) - the object's globally unique identifier. Every OFF object (no matter whether it's static or mobile) is supposed to have a globally unique identifier, which is basically a huge 256-bit number that can be used to identify *this* specific object as opposed to all other objects currently present on the game map. This identifier is stored in a structure which I call the G_GUID, which has the following form:
INT32 (4 bytes) // part 1
INT16 (2 bytes) // part 2
INT16 (2 bytes) // part 3
BYTE (1 byte) // part 4
BYTE (1 byte)
BYTE (1 byte) // part 5
BYTE (1 byte)
BYTE (1 byte)
BYTE (1 byte)
BYTE (1 byte)
BYTE (1 byte)
Note that I divided the structure into five parts. This has been done intentionally in order to show one interesting and important feature of the mobile object files. In fact, the file name of the MOB files also contains this same GUID, in the following form:
So, those parts are separated with underscore signs ("_"). Here you must mind that the byte order on Intel platforms is little-endian, so 4-byte and 2-byte sequences (which are found in parts 1 through 3) will be stored in reverse order, while 1-byte sequences (parts 4 and 5) will be stored in direct order. Also, please note that the G_GUID structure in the MOB file and the globally unique identifier in the MOB file name must be the same. If they are not the same, the game may behave incorrectly or may CTD. Ah well, I guess it's kind of hard to understand, let me give you an example. Say, you have an object with the following G_GUID field:
(the G_GUID structure is divided into parts with a "|" sign):
9E 0D 26 1B | 2E 7C | 21 44 | 82 E9 | 81 9B AE 8A 7F 67
In that case, the corresponding file name must be the following (keeping the little endian order in mind):
Well, that's all about the GUID. So, let's move on to the next field in the OFF header.
8. 0x0034 - INT32 (4 bytes) - Object type. Well, exactly what it is. It's worth mentioning that this type must correspond to the type of the prototype ID defined in the field . If it doesn't correspond to the type of the base proto, the game will CTD. The type should be one of the following:
0x0 - Portal (obj_t_portal)
0x1 - Container (obj_t_container)
0x2 - Scenery (obj_t_scenery)
0x3 - Projectile (obj_t_projectile)
0x4 - Weapon (obj_t_weapon)
0x5 - Ammo (obj_t_ammo)
0x6 - Armor (obj_t_armor)
0x7 - Money (obj_t_money)
0x8 - Food (obj_t_food)
0x9 - Scroll (obj_t_scroll)
0xA - Key (obj_t_key)
0xB - Written (obj_t_written)
0xC - Generic (obj_t_generic)
0xD - PC (obj_t_pc)
0xE - NPC (obj_t_npc)
0xF - Trap (obj_t_trap)
0x10 - Bag (obj_t_bag)
So, for example, if your object has the proto ID 14083 (a NPC), the object type must be defined as 0xE (obj_t_npc), otherwise the game will crash.
Phew! That wasn't bad for a starter. In the next lesson we'll discuss the property bitmap and its dependence on the object type.
vBulletin® v3.8.7, Copyright ©2000-2013, vBulletin Solutions, Inc.