I have been trying to reverse the db struct for the 2.4.3 spell class for the last 2 days. At first, I was thinking I was looking at the wrong list but after dumping the db names it looks to be the correct one.
Code:
DBFilesClient\Spell.dbc = 0x00BA0BE0
The 1st thing that threw me off was the Spell header was a little different from all others. (from game memory)
Spell
Code:
struct WoWClientDb2
{
PVOID _vtable; //0x00
int IsLoaded; //0x04
int NumRows; //0x08
int MaxIndex; //0x0C
int MinIndex; //0x10
PVOID StringTable; //0x14
PVOID FirstRow; //0x18
PVOID UnorderedRows; //0x1C
PVOID OrderedRows; //0x20
};
All others
Code:
struct WoWClientDb
{
PVOID _vtable; //0x00
int IsLoaded; //0x04
int NumRows; //0x08
int MaxIndex; //0x0C
int MinIndex; //0x10
PVOID StringTable; //0x14
PVOID FirstRow; //0x18
PVOID Rows; //0x1C
};
The next problem I ran into was the struct its self was much different than the 1.12.1 client. The struct was aligned on a byte boundary (from game memory). After looking over some emulator source code I concluded the file struct was different than how it was being saved into game memory, as mangos was reading on a 4 byte boundary. I started diffing the class with different spells and found some basic offsets. Soon after diffing more spells, I found the offsets stopped lining up.So I started to look into how everything was being loaded,
0x005746E0, the start of the spelldb loading process
Code:
.text:005746E0 68 6B 01 00 00 push 16Bh
.text:005746E5 68 CC BE 8B 00 push offset a_Dbclient_cpp ; ".\\DBClient.cpp"
.text:005746EA B9 E0 0B BA 00 mov ecx, offset off_BA0BE0 //WoWClientDb2* Spell
.text:005746EF E8 5C B1 FF FF call sub_56F850
56F850 does some basic error checking (version check, file handling, and row count)
After that it moves on to a virtual function that allocates and populates the WoWClientDb2 object,
Code:
SpellObjectSetup = 0x0057BA70
Code:
.text:0056F9A8 8B 16 mov edx, [esi]
.text:0056F9AA 8B 12 mov edx, [edx]
.text:0056F9AC 57 push edi
.text:0056F9AD 89 46 14 mov [esi+14h], eax
.text:0056F9B0 8B 45 FC mov eax, [ebp+var_4]
.text:0056F9B3 53 push ebx
.text:0056F9B4 50 push eax
.text:0056F9B5 8B CE mov ecx, esi
.text:0056F9B7 C7 46 0C 00 00 00 00 mov dword ptr [esi+0Ch], 0
.text:0056F9BE C7 46 10 FF FF FF 0F mov dword ptr [esi+10h], 0FFFFFFFh
.text:0056F9C5 FF D2 call edx //0x0057BA70
A little into the function you will see a call to 76B370. 76B370 is a function that reads each row from the spell db (mapped in file) into the heap. ecx has the address of the write/out buffer. Looking at it after it's called I found that the spell struct looked normal, everything was alined like in 1.12.1, on a 4 byte boundary. So I could bootstrap the process, hook and create my own copy, try loading the table my self or understand what is going on. The odd thing is it looks to take up the same amount of memory for both versions (0x260). I should also note that at this point I can see its not just alined differntly, comparing the 2 objects they have different vlaues even if aliment was changed.
Code:
.text:0057BAC7 8B 46 14 mov eax, [esi+14h]
.text:0057BACA 8B 4D 08 mov ecx, [ebp+arg_0]
.text:0057BACD 50 push eax ; int
.text:0057BACE 51 push ecx ; int
.text:0057BACF 74 3F jz short loc_57BB10
.text:0057BAD1 8D 8D 9C FD FF FF lea ecx, [ebp+var_264] ; void * //ecx = out_buffer
.text:0057BAD7 E8 94 F8 1E 00 call sub_76B370 //Read Spell Data
The next function call is to 0x00792D80. This function must be the one that is re-alining the struct
Code:
.text:0057BADC 6A 00 push 0
.text:0057BADE 8D 95 9C FD FF FF lea edx, [ebp+var_264]
.text:0057BAE4 68 60 02 00 00 push 260h
.text:0057BAE9 52 push edx
.text:0057BAEA E8 91 72 21 00 call sub_792D80
Code:
signed int __cdecl sub_792D80(_BYTE *pSpellObj, int size (0x260), _BYTE *pUnknown (NULL))
{
_BYTE *pUnknown2; // esi@1
unsigned int v4; // edi@1
signed int result; // eax@3
_BYTE *i; // ecx@3
char v7; // bl@6
unsigned int j; // edx@7
pUnknown2 = pUnknown;
v4 = (unsigned int)&pSpellObj[size]; //Get end address
if ( pUnknown ) // is NULL, skip
{
*pUnknown = *pSpellObj;
pUnknown2 = pUnknown + 1;
}
result = 1;
for ( i = pSpellObj + 1; (unsigned int)i < v4; ++i ) //loop over object
{
if ( pUnknown2 ) // is NULL, skip
*pUnknown2++ = *i;
v7 = *i;
++result;
if ( *i == *(i - 1) )
{
++i;
for ( j = 0; (unsigned int)i < v4; ++j )
{
if ( *i != v7 )
break;
if ( j >= 0xFF )
break;
++i;
}
if ( pUnknown2 ) // is NULL, skip
*pUnknown2++ = j;
++result;
if ( i == (_BYTE *)v4 )
return result;
if ( pUnknown2 ) // is NULL, skip
*pUnknown2++ = *i;
++result;
}
}
return result;
}
I can't really tell what it's doing, looks like its calculating blank space?
I cant find where its getting copied over to from the temp object. I get hits from,
All from, 792D80
Code:
.text:00792DC6 38 19 cmp [ecx], bl
..
.text:00792DB8 3A 59 FF cmp bl, [ecx-1]
..
.text:00792DB3 8A 19 mov bl, [ecx]
Some mem copy function, called from ReadSpell (0x0076B370) -> ReadData (0x00654E00)
Code:
.text:0040B6CC 89 44 8F FC mov [edi+ecx*4-4], eax
Another odd thing is when the game accesses the spell object list, it always makes a copy of it, and not just returning a pointer.
Code:
signed int __thiscall sub_466680(int this, int a2, void *a3)
{
int v3; // edx@1
const void **v4; // eax@3
signed int result; // eax@5
v3 = *(_DWORD *)(this + 0x10);
if ( a2 >= v3
&& a2 <= *(_DWORD *)(this + 0xC)
&& (v4 = (const void **)(*(_DWORD *)(this + 0x20) + 4 * (a2 - v3)), *v4) )
{
if ( byte_D2A084 )
{
call 004664C0(*v4, 0x260, a3); //<---
result = 1;
}
else
{
qmemcpy(a3, *v4, 0x260u);
result = 1;
}
}
else
{
result = 0;
}
return result;
}
I must be missing something... For a quick fix I'm going to try calling it my self, and hooking the function. Hope someone can give me some insight. Thanks!
Download: Spell.dbc dump from memory
----edit
Temp fix works
Code:
PCHAR errorString = ".\\DBClient.cpp";
WoWClientDb2* newSpellDBC = new WoWClientDb2;
DWORD SpellDBCall = 0x56F850;
memset(newSpellDBC, 0, sizeof(WoWClientDb2));
newSpellDBC->_vtable = (PVOID)0x08BC6D0;
newSpellDBC->MaxIndex = 0xFFFFFFFF;
newSpellDBC->MinIndex = 0x0FFFFFFF;
WoWClass::hook(0x0057BAD7, (DWORD)SpellCave);
__asm
{
push 0x16B;
push errorString;
mov ecx, newSpellDBC;
call SpellDBCall;
};
----edit
nvm. I'm just crazy...
Code:
typedef bool (__thiscall* dGetRow2)(PVOID _this, int index, PVOID buffer /*size = 0x260*/);
bool GetRow(int index, PVOID buffer)
{
dGetRow2 _GetRow = (dGetRow2)0x00466680;
return _GetRow(this, index, buffer);
}
found this the other day, noticed wow called it, I was like fuck that, I dont need a copy of the object... Just looked into it again, and it reverses what 466680 does with 4664C0... If only I trusted wow more... So 00466680 returns the real uncompressed object that we all know and love.