Temple.DLL

Discussion in 'TemplePlus' started by Sitra Achara, Feb 7, 2015.

Remove all ads!
Thread Status:
Not open for further replies.
  1. Sitra Achara

    Sitra Achara Senior Member

    Joined:
    Sep 1, 2003
    Messages:
    3,613
    Likes Received:
    537
    Notice: With TemplePlus, the focus has shifted from DLL analysis to DLL replacement :)
    I am going to discontinue updating this thread and the IDA database with functions signatures and data strctures for the simple reason that I'm pretty much doing that from within the TemplePlus source code, if not replacing the functions entirely. We may do a compilation of header files and such if there's demand however.

    ------------------

    I've been spelunking a bit lately... this thread is for documenting what I've found. Hopefully others will find something useful out of it, and maybe it could encourage someone to spelunk themselves :) Hopefully we can get more Spellslinger-type DLL fixes out of this.

    I'm using IDA Pro 5.1 to do this (freely available at hex-rays.com), and I'll be uploading the database once in a while here.

    Questions and discussions are best left to another thread - feel free to open one yourself!

    ~~~~~~~~~~~~~~~~~~~~~~~~

    General remarks

    Unless noted otherwise all values /elements are stated in terms of Bytes (contrary to my earlier, lazier notation).
    e.g.
    [0h: 3h] int32 A
    [4h: 7h] int32 B
    [8h:Fh] int64 C
    [10h: 43h] int D[0xDh] // array of size 0xDh
    etc.

    Objects (ref: MOB file specs)
    Note: among the object types are Protos, MOBs and Static Objects.

    Memory Storage: the ObjectPool

    Code:
    10BCAC50 ObjectArray * [b]pObjectPool_MstrArray[100h][/b] // a 100h size array of pointers to ObjectArrays.
    
    [B]ObjectArray[/B]: An array of Objects. 
         Size - defined in [I]10BCAC60  SizeOf_2000h_Objects__Bytes[/I] (which is 2000h * ObjectSizeBytes = 158000h).
    
    Init :  100C2E90 ObjectPool_NewItem // finds a usable slot in the ObjectPool. Returns an ObjectPool_idx, and writes an Object Handle into the input pointer. See below on Object Handle details.
    Object Handle
    MOBs are usually represented by a 64-bit Int Handle (long). It's the number you see in the in-game console when typing an object's python handle e.g. game.party[0] -> "Regdar(17848395498463)". I'll be referring to this as ObjHnd in function descriptions. Since this value is loaded into 32bit registers, I call the MSB part "ObjHnd_Ext" inside the IDA function descriptions.



    This handle may be used to fetch the Object's actual memory pointer.
    Bits [0:2h] of the handle are the Object's Type. 1 means it's a Prototype Object, 2 means it's a MOB Object (an instance of a prototype).
    Bits [3h: 19h] - ??? but they are identical to an Object header bits [8h:1Eh].
    Bits [1Dh:2Dh] of the handle give the Object's index in the list of all Object_Structs.

    struct ToEEObject specs:
    nObjHeaderSize (102D0010) - global value for object header size (Bytes). Seems to be always 4 in ToEE (int32 header).
    ToEEObject_Size (10BCAC34) - global value for object size (Bytes). Including the size of the header (i.e. subtract the header size from this to get the body size).

    The fields below refer to the data after the header (it would be more accurate to write them as pObject[ n + nObjHeaderSize] but fuck that - ToEE always fetches a pointer to the body anyway).

    Code:
    pObject[0:3h] - int object_type // (e.g. 0xDh for PCs, 0xEh for NPCs, etc. see Agetian's MOB specs thread)
    pObject[4h:7h] - ?
    
    The next fields are different for Protos and Instanced Objects, but in principle they are two 18h sized ObjectIdentifiers - these structs are what's used to look up Object Handles. The 2nd one is a copy of the originating Proto's ObjectIdentifier and is therefore essentially null for Proto objects.
    
    For Protos: 
    ObjectID:
      pObject[8h:9h] int16 Subtype. Always 1 in ToEE (see Ag's MOB specs)
      pObject[Ah:Bh] ???
      pObject[Ch:Fh] - ???
      pObject[10h:13h] - Proto Number
      pObject[14h:1Bh] - GUID type? It's always 2,0 in any case that I've seen.
      pObject[1Ch:1Fh] - ???
    ProtoID:
      pObject[20h:21h] - FFFFh for Protos
      pObject[22h:38h] - all zero
    
    Instanced (Mobs + Statics):
    
    ObjectID:
    pObject[8h:Fh] (2 Dwords) GUID_type
    pObject[10h:1Fh] (4 Dwords) - GUID // again, see Ag's thread for details
    pObject[20h:37h] ObjectID ProtoID // copied from the originating Proto's ObjectIdentifier
    pObject[38h:3Fh] ObjHndl  ObjHnd_Proto // the originating Proto's ObjHnd
    
    
    pObject[40h:43h] (1 DWord) - 0 for new object
    pObject[44h:45h] (1 Word)- number of items in the Object_Internal_Data_Stack
    pObject[46h:47h] (1 Word) - flag that says there is data in the Object_Internal_Data_Stack methinks (as opposed to a 'virgin' object I guess); initially 0 for new object
    pObject[48h:4Bh] (1 DWord) - pPropertyBitmap1 // pointer to a bit array[0:428h bits]. The [I]nth[/I] bit signifies whether field [I]n+1[/I] exists in the object - e.g. bit[0] corresponds to obj_f_aid. You can see the exact offset for each field by taking  Column[2] * Column[4] in [B]pObject_Fields_Storage_Lookup_Table[/B].
    pObject[4Ch:4Fh] (1 DWord) - pPropertyBitmap2 // almost the same as the above, not yet sure what's the difference - one seems to be related to inheritance from the Protos methinks?
    pObject[50h:53h] (1 DWord)- pPropertyDataStack
    pObject[54h:A7h] - obj_transient data fields (graphics / animation related data).
    
    Header details:
    Bits 0:7h (2 LSB bytes) - should be 48h for active Objects, 50h for inactives (ones that are 'free to overwrite in memory').
    Bits 8h:1Eh are a serial number (which resets at when going over 7FFFFFh back to 1; stored globally at 10BCAC40 object_header_serial_number). It is the same as bits 3h:19h in the Object Handle.

    Constants and tables

    pObject_Fields_Storage_Lookup_Table (10B3D7D8)
    A pointer to a 0x1AE rows by 7 columns table (each column a DWord).
    The table contains information on each object field's properties / definitions:
    Code:
    column 0 - location of the default field data in the Proto Object's Internal Data Stack
    column 2 - data_index_1    - index_1 for "field existence" bit array
    column 3 - bitmask for the object field's bit
    column 4 - data_index_2    subindex for "field_existence" bit array
    
    By "field existence" I am referring to pObject[0x12h] - an array of bits pointed to from every object struct's [0x12h] memory location (See below for details). 
    index_1 is a Double Word index, and index_2 is a subindex within that Double Word (i.e. overall offset is index_1 * 0x20h + index_2).
    
    column[6] - seems to be a code for the type of field
    	01 - "begin" (dummy field)
    	02 - "end" (dummy field)
    	03 - plain int value - data in Object_Stack is the desired value
    	04 - 64bit value - data in Object_Stack is a pointer to a Long
    	05 - a list index - data in Object_Stack points to an offset within some global array (example: critter_abilities_idx). Needs a field_subindex input.
    	07 - list??? (example: level idx)
            08 - a 64bit list??? (example: obj_f_npc_standpoints)
    	0C - object GUID struct (example: obj_f_npc_leader)
    	0D - example ai_list_idx
    pHandles_List (10BCAC54)

    A list of handles for all Objects (Protos, MOBs and Static Objects). Its length is stored at HndLstLen (10BCAC3C)
    There are 9 fields here for each entry - named Column 0 thru Column 8. Note that the protos.tab properties are not directly stored here.

    Code:
    For all types:
    Column 0 [0h:1h] (1 Word) GUID type. 
       1 is for Proto
       2 is for MOB
       3 is for Static Object instances
    Column 1 [2h:3h]- (1 Word) ??? it is always zero for MOBs and static objects, sometimes non-zero for Protos.
    Column 2 [4h:7h] (1 DWord) type/properties? 
    
    -----------------
    
    Columns 3-6 [8h:17h]  [b]depends on type[/b]:
    
    [b]For Protos[/b]:
    Column 3   [8h:Bh] (1 Dword) the Proto number. [B]The Protos are sorted by this value.[/B]
    Columns 4-5 [Ch:13h] (2 DWords) GUID type? (it's always 2,0 for Protos it seems)
    Column 6   [14h: 17h] (1 DWord) ??
    
    -----------------
    
    [b]For MOBs[/b]:
    Columns 3-6 [8h:17h] (4 DWords) The GUID
    
    -----------------------
    
    [b]For Static Objects[/b]:
    These are sorted by Column 4, and then Column 3, unlike the Protos and MOBs.
    Column 3 [8h:Bh] - numbers in the range of FFFF (typically about 200h). Sector coordinates?
    Column 4 [Ch:Fh] - numbers in the range of FFFF (typically about 200h). Sector coordinates?
    Column 5 [10:13h] - map number
    
    ----------------------
    
    Column 7,8 [14h:1Bh] - ObjHnd
    
    This table exposes a pattern in the MSB of the handle, in that it is a running index that increments for every 8 objects (the MOBs continue from where the Protos stop at). For the protos, it is ordered in clumps of 8, but for the MOBs it's seemingly random (as a side effect of the sorting by Column 3; whereas for protos, they must have been generated in the same sequences as the proto number).
    
    The list is first of all sorted by object type - first protos, then MOBs, then Static objects.
     
    Last edited: Apr 12, 2015
  2. Sitra Achara

    Sitra Achara Senior Member

    Joined:
    Sep 1, 2003
    Messages:
    3,613
    Likes Received:
    537
    Basic Functions

    Commonly appearing functions. Understanding these is key to deciphering other more specific ones.

    Unless noted otherwise the inputs are in order of appearance.
    e.g. if I list
    Function
    Inputs: A, B, C

    It corresponds to

    Code:
    push A
    push B
    push C
    call [I]Function[/I]
    ~~~~~~~~~~~~~~~~~

    Obj_Get_Internal_Field_32bit_Int (1009E1D0)
    Input: int Field_Index, long ObjHnd
    Output: EAX (32 bit value)

    Used for retrieving an object's stored field values of the 32bit variety.
    This function is what's behind the python function .obj_get_int( Field_Index ).

    This is most often called for checking obj_f_type (Field_Index = 0x1ACh) when differentiating between PC (0xDh), NPC (0xEh), item, etc.

    Alas, the implementation of the function is such that when you try to read 64bit values, let alone more complicated data types, it overwrites the function's return address and yields a crash. This is why .obj_get_int crashes the game when trying to read certain fields. This is a bit of a shame, because there's lots of interesting data to be read and scripted for this way, e.g. the famous obj_f_npc_who_hit_me_last from Ted's tutorials. Thing is, it shouldn't be that complicated to write a obj_get_int_64 or whatever that is just a copy of the other internal functions! Something for another day perhaps.

    Obj_Get_Internal_Field_Float (1009E260)
    Input: int Field_Index, long ObjHnd
    Output: Push to FPU stack

    Similar to the function above, except that it's used for double-precision floating point fields such as obj_f_offset_x, obj_f_rotation, obj_f_radius, etc. It also does not output to EAX, but to the FPU stack.

    Obj_Get_Internal_Field_64bit (1009E2E0 )
    Input: int Field_Index, ObjHnd
    Output: EAX , EDX

    Like I said, there's a 64bit version of that thing :)

    Uses I've seen for this were for obj_f_current_aid, obj_f_npc_ai_flags64 and obj_f_critter_monster_category.


    Obj_Get_Memory_Address (100C2A70 )
    Input: long ObjHnd
    Output: EAX (Pointer to memory location)

    To be precise, it fetches the address of the object's body - there's also a 4 byte header before that.

    Object_Field_Fetcher (1009CD40)
    Inputs:
    EAX - int Object_Field_Index
    ECX - &pOut
    EDX - &pObject
    Output: depends on the field, but generally writes the values to *pOut

    This is the actual low-level implementation for reading object fields. The above functions all use this one inside. As you can see it takes the input directly from the registers.

    1009C190 ; bool __usercall ObjField_Exists@<eax>(int nFieldIdx@<eax>, int nObjType@<ecx>)

    Checks whether an object of type nObjType has a field of index nFieldIdx. e.g. whether an nObjType = obj_t_npc has the field nFieldIdx = obj_f_npc_leader.


    101EC990 int __cdecl Packet_Write(PacketStruc *, _PktKey, _PktIO *)
    This function writes into a PacketStruc.

    DiceRoller (10038B60)
    Input: int Dice_Bonus, int Dice_Type, int Num_Dice
    Output: EAX (Roll result)

    Very useful to set up a breakpoint here when in combat.
     
    Last edited: Mar 4, 2015
  3. Sitra Achara

    Sitra Achara Senior Member

    Joined:
    Sep 1, 2003
    Messages:
    3,613
    Likes Received:
    537
    Core Systems

    Look forward to a breakdown of the messaging / event system and action/animation sequencers and stuff :)

    Dispatchers and Conditions

    ToEE stores object status information and various functions associated with it in a structure called a "dispatcher". A Dispatcher Object is generated and linked to the object when loading up a map, ultimately using information colected from the Object's internal fields, various functions and definitions in the game files, global variables / lists, etc.

    The pointer to this object is stored in the object field obj_f_dispatcher ( = 0x2Dh ). You will see many functions in ToEE fetch this value from an object and proceed to use it for various things such as "d20 queries", sending signals, applying conditions, spells, etc.

    The Dispatcher is essentially all about processing Conditions. Conditions are a variety of qualities, from basic stuff like Races and Classes (e.g. the "Halforc" and "Fighter" conditions), to feats and abilities (e.g. "AoO", "Improved Two Weapon Fighting", "Forge Ring"), to spell and status effects ("sp-Stinking Cloud", "Barbarian Raged", "SurpriseRound") and so on. The list is pretty huge and contains just about anything you can think of that can affect characters / objects.

    Condition Object Structure:
    Condition Structs are defined inside the temple.dll data (i.e. hardcoded data, nothing you can change in a .mes file or something like that). They follow a fairly simple structure:
    Code:
    [b]CondStruct[/b]
      CondStruct[0h:3h] - char **pCondName // pointer to a pointer to string bearing the condition's name, e.g. "Halforc"Dying"
      CondStruct[4h:7h] - int nArgs // number of arguments associated with this condition. e.g. the "sp-Detect Magic" condition has 3 arguments associated - spellID, length, and one dummy argument (or at least I think it's a dummy..).
      SubDispDef_Array[] // An array of what I call "Sub Dispatcher Definition" structs, 5 DWords each. (I may come up with a better name at some point ;))
      [b]SubDispDef[/b]
          SubDispDef[0h:3h] - int nDispatcherType
          SubDispDef[4h:7h] - int nKey
          SubDispDef[8h:Bh] - void *pFunction  // pointer to a function
          SubDispDef[Ch:Fh] - arg1 // Usually null. May be a pointer or integer. May be used in various ways, such as acting as a condition's default argument, or pointer to a mutually exclusive condition, or pointing to a secondary function to execute.
          SubDispDefStruct[10h:13h] - arg2
      The struct array is terminated by an all-0 struct. I don't remember if there's a size limit, but if there is it's probably sufficiently very large to accommodate any practically conceivable condition. Sometimes there's another null following that, probably just alignment.
    
    These properties come into play when interacting with Dispatchers and their chief function the Dispatcher Processor.

    Dispatcher Object structure:

    It is a 138h bytes sized struct. Initialized by 100E1F10 Dispatcher_Init.
    Code:
    [0h:7h] ObjHndl ObjHnd__Caller 
    
    Condition lists:
     [8h:Bh] CondNode *pAttributeCondList // pointer to a linked list of permanent attribute Conditions such as Class levels, Race, Feats, etc.
     [Ch:Fh] CondNode *pItemCondList // pointer to a linked list of Conditions granted by the object's items/inventory, e.g. Armor Bonuses, To Hit Bonuses from magical weapons, etc.
     [10h:13h] CondNode *pOtherCondList // pointer to linked list of Conditions granted by other things, like Spell Effect conditions, definitions from obj_f_conditions and obj_f_permanent_mods, direct application from Obj.Condition_Add() command, etc.
    
    "SubDispatcher" array:
    [14h:137h] aSubDispatcher_Array  // An array of pointers to "SubDispatchers". Array size is 49h (73d).

    The condition list nodes are made up of at least 3 fields:
    Code:
           pCondNode[0h:3h] CondStruct *pCondStruct  // a pointer to the condition's definitive struct, which contains all the information about the condition's definition such as number of arguments and what Dispatchers it possesses.
           pCondNode[4h:7h] CondNode *pNextNode // pointer to next condition node
           pCondNode[8h:Bh] (1 DWord) ??? initalized to 0, always zero from what I've seen 
           pCondNode.args[]: (if nArgs > 0)
              pCondNode[Ch : Ch + 4*(nArgs-1) -1] (nArgs DWords) condition arguments, if any. nArgs is defined at pCondStruct[4h:7h]. If it's zero, then these fields don't exist.
    

    SubDispatchers
    As mentioned, there's an array of "SubDispatchers". The index of each one corresponds to nDispatcherType in the SubDisDef structs from the Condition Structs. Confusing, I know, it takes some getting used to.

    Each SubDispatcher is a linked list (though many are null, as a default initialization).

    SubDispLstNode structure:
    Code:
    [0h:3h] SubDispDef *pSubDispDef // a pointer to the 5-element SubDispatcher Definition Struct inside the Condition_Struct
    [4h:7h] CondNode *pRootCondNode // the Condition Node that originated this SubDispatcher
    [8h:Bh] SubDispLstNode *pNextNode // 0 if none
    When a new condition is added, all of its SubDispDef Structs will be linked by appending new Nodes to the SubDispatchers.


    Dispatcher_Processor (100E2120)
    Inputs: (this time relative to return address! i.e. from last pushed to first push)

    Code:
    struct *pDispatcher
    int   nDispatcherType
    int   nKey
    struct *pOut
    Outputs:
    At least for d20_queries, it seems to write a 1 into *(pOut+4h).

    This appears to be the central pillar of ToEE's signaling system.

    Note: pDispatcher is usually taken from an object's field obj_f_dispatcher.

    What it does: it cycles through the SubDispatcher list of index nDispatcherType.
    Each SubDispatcher contains a reference to a Root Condition Node (pRootCondNode), and to the SubDispatcher Definition taken from that condition's CondStruct.

    If nKey matches (or the SubDispDef has a null key), it executes the function defined in SubDispDef. It cycles through all the SubDisp list nodes this way.

    A simple example is a Query - a query dispatch simply cycles through the Dispatcher Type 0x1Dh list, and nKey is the query code. To be precise, there's an offset of 0xCFh, e.g. Q_Helpless ( = 0 ) corresponds to nKey = 0xCFh. The function inside the query may return a boolean value, data, or both.


    Dispatcher Types I've at least minimally explored so far:
    1 - initialization function - runs when the Condition is initially added or when an object is initialized (this includes things like map transfer since technically Objects get initalized "from scratch").
    3 - precondition functions - these dispatchers run right before adding a new condition. That is, the "Condition Add" function runs a Dispatcher with dispatcher_type 3, key = 0 before it proceeds to add the condition. A condition will only then be added if the "Ok to add" flag remains as 1 after processing this dispatcher. Included here are mutual exclusions and stacking-prevention measures for various conditions.
    0x1C for d20 signals (as in the d20_send_signal functions)
    0x1D for d20 queries (as in the d20_query_[...] functions)


    The following are a bit more speculative:
    0x6 - ping (probably executed every frame)
    0x9 - time related. I initially thought it might be for the onset of a new day. I no longer do.
    0xA - stat_level_get
    0x12 - attack / trip / touch attack (one or more of them...)
    0x14 - dealing damage? (see e.g. inside Damage_Critter)
    0x15 - taking damage? (see e.g. inside Damage_Critter)
    0x19 - well being?
    0x1A - Global Dispatch for {E4} query (corresponding to stats.mes entry E4 -> HP)
    0x1B - enter combat

    0x1E for skill_level_get
    0x25 - perform_activate_item_via_radial_menu_action (I've no idea what this is)
    0x28 - some spell resistance/immunity thing (seems like there's lots of those)
    0x2A - trip
    0x2D - another spell resistance/immunity thing
    0x30 - begin round
    0x31 - reflex and save damage
    0x3B - begin restoration spell



    More on this later as I'll continue to explore!
     
    Last edited: Mar 18, 2015
  4. Sitra Achara

    Sitra Achara Senior Member

    Joined:
    Sep 1, 2003
    Messages:
    3,613
    Likes Received:
    537
    Functions, functions, functions...


    Reserved!
     
  5. Sitra Achara

    Sitra Achara Senior Member

    Joined:
    Sep 1, 2003
    Messages:
    3,613
    Likes Received:
    537
    Misc 1

    Reserved!



    Functions with non-standard calling conventions:
    Code:
    1009C190 ; bool __usercall [B]ObjField_Exists[/B]@<eax>(int nFieldIdx@<eax>, int nObjType@<ecx>)
    1009CD40 ; void __usercall [B]ObjectPropFetcher[/B](int nFIeldIdx@<eax>, void *@<ecx>, ToEEObject *pObject@<edx>)
    100C1060 ; int __usercall [B]obj_find_node_detach[/B]@<eax>(void *pFind_Node@<eax>)
    1009BF70 ; int __usercall [B]PropBitmap_GetLen[/B]@<eax>(int nObjType@<eax>)
    1009DA90 ; int __usercall [B]ObjProps_DoFunc[/B]@<eax>(void *pFunc@<edi>, ToEEObjBody *@<esi>)
    10077670 ; int __usercall [B]Spell__spell_get_next_spell_id[/B]@<eax>(int pSthg, void *@<ebx>)
    100A0500 ; void __usercall [B]Object_Set_Field_With_Subindex[/B](int nFieldIdx, int nFieldSubIdx, ToEEObject *@<ecx>, _SourceData *@<eax>)
     
    Last edited: Mar 5, 2015
  6. Sitra Achara

    Sitra Achara Senior Member

    Joined:
    Sep 1, 2003
    Messages:
    3,613
    Likes Received:
    537
    Misc 2

    Animal Companion Tables
    A little nugget I think people would appreciate:

    102EF14C Animal_Companion_Proto_Array - Size 10 array (each entry is 4 bytes) containing the proto numbers for the Animal Companions you can summon.

    102EF174 Animal_Companion_Level_Restrictions - Likewise, except it contains the minimum level for each animal companion.

    So for instance if you wanted to have the Dire Bear companion at level 13, you could replace it with, say, the Chicken.


    Hotkey Binding

    The table that defines the hotkeys is at

    .data:10BD0248 pHotkey_Bindings_Array

    It is 39 rows by 18 columns (DWords).
    The Row index is the same as the QWERTY character map, i.e. 0 <-> 'Q', 1<-> 'W', 2<-> 'E' and so on. You can see the exact order in the mes\hotkey.mes file (look it up in the vanilla files as it hasn't been touched by Co8).

    Column[0] - pString for the hotkey-bound action

    Note that you can't bind rows that correspond to reserved keys such as 'R' (Rest), 'M' (Map) etc.

    Spell Number Influence vis a vis hardcoding:


    10097C20 Action_Frame_Action_Check
    Spells with spell number >= 600 will have the following code executed:
    Code:
    .text:10097C55 83 8A 18 0B+        or      [edx+ActionSequences.CAF_Flags], D20CAF_NEED_PROJECTILE_HIT
    .text:10097C5C A1 F0 A8 86+        mov     eax, pActSeq_Cur
    .text:10097C61 8B 88 58 0B+        mov     ecx, [eax+ActionSequences.SpellPktData.SpellIdx]
    .text:10097C67 89 88 5C 0B+        mov     [eax+ActionSequences.SpellPktData.SpellIdx_Original__used_for_spontns_cast], ecx
     
    Last edited: Mar 16, 2015
  7. Sitra Achara

    Sitra Achara Senior Member

    Joined:
    Sep 1, 2003
    Messages:
    3,613
    Likes Received:
    537
    Last edited: Mar 14, 2015
  8. Sitra Achara

    Sitra Achara Senior Member

    Joined:
    Sep 1, 2003
    Messages:
    3,613
    Likes Received:
    537
    Last edited: Mar 18, 2015
  9. Sitra Achara

    Sitra Achara Senior Member

    Joined:
    Sep 1, 2003
    Messages:
    3,613
    Likes Received:
    537
Thread Status:
Not open for further replies.
Our Host!