The DTI and MtFramework 2.0 - AniBullet/MonsterHunterWorldModding GitHub Wiki

The DTI and MtFramework 2.0

This guide covers the DTI and general information about MtFramework 2.0 (The Engine the game runs on). The information is mostly focused on memory layout of classes.

Table of contents

What is the DTI

"DTI" stands for Data Type Information and is data embedded into the exe that contains information about the different class types in the framework/game.

The DTI can contain information about:

  • The Class itself
    • Name
    • Size
    • Parent Classes
  • Class members
    • Name
    • Offset
    • Type

The DTI does not provide all information about all classes as this information has to be added manually for each class by the devs. So it only has that information which the devs bothered to add. Also of note is that, if a class A has some fields listed in its DTI, then any class X that inherits from A will also have those fields listed. (And possibly some extra fields)

DTI Dumps

Now of course having to look in the exe for all of this information would be a pain. Fortunately Ando was so nice as to publish his DTI dumping code, and has a github repo for storing the DTI Dump (+ a couple of other things) for the current game version.

You can find them here: https://github.com/Andoryuuta/MHW-DTI-Dumps/

And the dumper can be found here: https://github.com/Andoryuuta/MHW-ClassPropDump

The DTI Dump is in the form of a C++ header file, with pseudo class declarations for each object type.

Class Prefixes

(Almost) all classes in the game have some sort of one-letter prefix to their name.

For example: uEm001, sMhCamera, rEmDtTune, cEmFecesManager (Yes, that's a thing).

The meanings of each of those prefixes are as follows:

u is Unit Resource (inherits from cUnit)
c is Class
n is Namespace
r is Resource (inherits from cResource)
s is Singleton
a is Area (inherits from cArea)

All files are loaded into r classes.

Class Information

As mentioned before, the DTI holds information about the classes itself:

  • Name: The full official name of the class
  • Size: The size in bytes that the class occupies when instantiated
  • The location of the Virtual Function Table
  • Its CRC32 hash (Which is used by the game for quick identification of types)
  • The classes it inherits from

As an example let's pick rArmorData:

// rArmorData vftable:0x143436A80, Size:0xD0, CRC32:0x7501E3C1
class rArmorData /*: cResource, MtObject*/ {
  // ...

Immediately we can tell that the name of the class is rArmorData, the virtual function table is located at 0x143436A80, and the class is 0xD0 (208) bytes large. We can also tell that the class inherits from cResource and MtObject. As you might have guessed, MtObject is the base-class for all classes in MtFramework. The exception being small structs and classes that don't inherit from anything at all.

Field Information

The DTI also holds information about specific fields in certain classes:

  • The data type
  • The full name of the field
  • The offset from the base of the class

If the offset of a field is not given, there are addresses to a Getter and a Setter respectively, usually the actual offset can be deduced from those. For example, let's take rArmorData again. The dumped information for this class looks like so:

// rArmorData vftable:0x143436A80, Size:0xD0, CRC32:0x7501E3C1
class rArmorData /*: cResource, MtObject*/ {
	s32 'mRefCount'    ; // Offset:0x5C, Var, CRC32:0x26C06A67, Flags:0x3000
	u32 'mAttr'        ; // Offset:0x60, Var, CRC32:0xDD77E828, Flags:0x3000
	u64 'mSize'        ; // Offset:0x70, Var, CRC32:0x2EC9EF56, Flags:0x3000
	u64 'mID'          ; // Offset:0x78, Var, CRC32:0x10BB0F5B, Flags:0x3000
	u64 'mCreateTime'  ; // Offset:0x80, Var, CRC32:0x3EA974DF, Flags:0x3000
	u32 'mQuality'     ; // Offset:0x7FFFFFFFFFFFFFFF, PSEUDO-PROP, Getter:0x1402B7C90, Setter:0x1402B8DF0, CRC32:0xFF50780E, Flags:0x91000
	u32 'mState'       ; // Offset:0x7FFFFFFFFFFFFFFF, PSEUDO-PROP, Getter:0x1404521A0, Setter:0x140452380, CRC32:0x5E27483A, Flags:0x83000
	u32 'mTag'         ; // Offset:0x7FFFFFFFFFFFFFFF, PSEUDO-PROP, Getter:0x1404521B0, Setter:0x140452390, CRC32:0xD372274C, Flags:0x83000
	cstring 'mPath'    ; // Offset:0x7FFFFFFFFFFFFFFF, PSEUDO-PROP, Getter:0x141986F00, Setter:0x14023D5D0, CRC32:0xD25D4033, Flags:0x83000
};

As you can see the offset for the mPath member is listed as 0x7FFFFFFFFFFFFFFF, however the class size is only 0xD0. Basically if the offset is unknown it will be displayed as 0x7FFFFFFFFFFFFFFF (INT64 Max) in the dump. However we can see that in the comment there are Getter and Setter addresses. Let's take a look at the Getter.

The function looks like this

lea rax,[rcx+0xc]
ret

This tells us that the mPath member is at offset 0xC. However some of these Getters/Setters might point to "unimplemented" functions in which case there is unfortunately no way of knowing the offset of that field.

These "unimplemented" functions usually look something like

xor eax,eax
ret

or

mov al,0x1
ret

MtFramework

MtFramework, specifically 2.0, is the engine the game runs on. It has a lot of interesting traits and features to take advantage of when reverse engineering.

Basic Types

MTF has a big list of basic types that are used in the DTI dump, some of them like u32, s8, f32, or even vector3 might be obvious, but some others like classref, color, sphere, or string might not be so obvious. Well it's obvious what they are but not what the underlying types might be. classref for example can be a pointer to any class, color is an unsigned 32-bit integer storing colors in the usual RGBA format.

Here is a header file that I put together which typedefs all of the types used in the DTI dump. I am still not 100% sure on all of the types, especially the geometric ones, so if you have any corrections please let me know at Fexty#4696 on discord.

DTI Instances

Each class has its own DTI instance somewhere in the exe. Yes, the instances themselves are in the exe, not just pointers to them. Each instance looks roughly like this:

+0x00: VFT
+0x08: Class Name
+0x10: Next
+0x18: Child
+0x20: Parent
+0x28: Link
+0x30: Flags and Size/4
+0x38: CRC32 Hash

And as an example, let's say we're looking at the DTI instance of class cIconData, then the VFT of that DTI instance would look like so:

+0x00: ~MtDTI<cIconData>();
+0x08: cIconData* NewInstance(); // Allocates and initializes a new object of type cIconData
+0x10: cIconData* CtorInstance(cIconData* obj); // Initializes the passed object
+0x18: cIconData** CtorInstanceArray(cIconData** arr, u32 count); // Initializes an array of cIconData with a given size

With this information we can declare our own MtDTI type to layer on-top of the game's instances:

class MtDTI {
public:
  const char* class_name;
  MtDTI* next;
  MtDTI* child;
  MtDTI* parent;
  MtDTI* link;
  u32 flags_and_size_div_4;
  u32 crc_hash;

  virtual ~MtDTI() = 0;
  virtual void* NewInstance() = 0;
  virtual void* CtorInstance(void* object) = 0;
  virtual void** CtorInstanceArray(void** objects, u32 count) = 0;
};

This means that using the header file I linked above (which has this MtDTI declaration as well), you can declare a variable of the correct type and instantiate objects from the game that way:

struct rEmAngry {}; // Empty declaration of type so we can use it

MtDTI& dti = *(MtDTI*)0x145069738; // declare reference to DTI
rEmAngry* agr = (rEmAngry*)dti.NewInstance(); // Use DTI to create new instance of rEmAngry

Virtual Function Tables

Interestingly, the virtual function tables ("VFT"s) of the MTF classes are one of the most useful things available.

The first 5 functions in the VFT of any class that inherits from MtObject look like so:

+0x00: ~Class(); // Destructor
+0x08: void CreateGUI(MtProperty&); // Useless function, mostly unimplemented
+0x10: bool IsInitialized(); // Only implemented for cUnit inheriting classes
+0x18: MtDTI& GetDTI(); // Returns a reference/pointer to the DTI instance of the class
+0x20: void PopulatePropertyList(MtPropertyList&); // Fills out the PropertyList (if implemented)

These five functions will always be there, whether they're implemented or not, they will always be there. This also means that class-specific virtual functions will always start at offset 0x28, i.e. entry 6 and higher.

Now you may ask: "But how can I find this virtual function table?"

If a class has any virtual functions at all, then a pointer to its VFT can always be found at the very start of the object, i.e. offset 0x0. For example:

class A : MtObject {
  int number;
  float decimal;
  uint64_t big_number;

  virtual ~A() {}
};

Will look like this in memory:

+0x00: VFT
+0x08: number
+0x0C: decimal
+0x10: big_number

Also, A's VFT will look like this:

+0x00: A::~A();
+0x08: void MtObject::CreateGUI(MtProperty&);
+0x10: bool MtObject::IsInitialized();
+0x18: MtDTI& MtObject::GetDTI();
+0x20: void MtObject::PopulatePropertyList(MtPropertyList&);

So if you need to know what an object you're looking at is called, you can simply look for its VFT in the DTI dump.

If you can't find the VFT in the DTI dump, there is a chance that the dumper failed to retrieve this class, in that case you can follow these steps and if you're lucky you'll find the class name:

  1. Head to the VFT in Cheat Engine/Ghidra/IDA
  2. Go to entry number 5 (+0x18)
  3. Find the lea rax,[blah] instruction
  4. Navigate to blah + 0x8
  5. Navigate to the pointer at this address
  6. If the class has any DTI then you'll find the class name here
⚠️ **GitHub.com Fallback** ⚠️