Modcraft - The community dedicated to quality WoW modding!
Wrath of the Lich King Modding => Miscellaneous => Topic started by: schlumpf on June 12, 2014, 10:26:48 pm
-
MPRL: 59832 0x0E9B8
MPRR: 327744 0x50040
MSHD: 32 0x00020 // M S HeaDer
MSPV: 105336 0x19B78
MSPI: 105832 0x19D68
MSCN: 119880 0x1D448
MSLK: 256400 0x3E990
MSVT: 75816 0x12828
MSVI: 62408 0x0F3C8
MSUR: 131520 0x201C0
MDOS: 200 0x000C8
MDSF: 21472 0x053E0
MDBH: Destructible Building Header
MDBI: Destructible Building I
MDBF: Destructible Building Filename
#include <cstdlib>
#include <cstdio>
#include <cassert>
#include <stdint.h>
struct CHUNK
{
uint32_t magic;
uint32_t size;
};
struct MVER : CHUNK
{
uint32_t version;
};
struct MPRLentry
{
uint16_t a; // 0
int16_t b; // -1
uint16_t c;
uint16_t d;
float position[3];
int16_t e;
uint16_t f;
};
struct MPRL : CHUNK
{
MPRLentry entries[0];
};
struct MDBF : CHUNK
{
char name[0];
};
struct MDBI : CHUNK
{
uint32_t index;
};
struct MDBH : CHUNK
{
uint32_t number;
};
struct MSHD : CHUNK
{
float unk[8];
};
struct MDOSentry
{
uint32_t a;
uint32_t b;
};
struct MDOS : CHUNK
{
MDOSentry entries[0];
};
int main(int na,char** va)
{
assert(na == 2 && *va );
FILE* input = fopen( va[1], "rb" );
fseek( input, 0, SEEK_END );
size_t size = ftell( input );
char* file = new char[size];
fseek( input, 0, SEEK_SET );
fread( file, 1, size, input );
fclose( input );
size_t position = 0;
// MVER ______________________________________________________________________
MVER* mver = ( MVER* )( file + position );
assert( mver->magic == 'MVER' && mver->size == 4 );
printf( "Build: %in", mver->version );
position += mver->size + 8;
printf( "n -- %xn", position );
// MPRL ______________________________________________________________________
MPRL* mprl = ( MPRL* )( file + position );
assert( mprl->magic == 'MPRL' );
size_t nMPRLEntries = mprl->size / sizeof( MPRLentry );
printf( "MPRL entries: %ldnn", nMPRLEntries );
for( size_t i = 0; i < nMPRLEntries; ++i )
{
printf( "{%f,%f,%f}:t%xt%it%xt%xt%it%xn", mprl->entries[i].position[0],
mprl->entries[i].position[1], mprl->entries[i].position[2],
mprl->entries[i].a, mprl->entries[i].b, mprl->entries[i].c,
mprl->entries[i].d, mprl->entries[i].e, mprl->entries[i].f );
assert( mprl->entries[i].d == 0x8000 );
assert( mprl->entries[i].b == -1 );
}
position += mprl->size + 8;
printf( "n -- %xn", position );
// ???? ______________________________________________________________________
{
CHUNK* unk = ( CHUNK* )( file + position );
printf( "Magic: %c%c%c%c, size: %in", ((char*)(&(unk->magic)))[3], ((char*)(&(unk->magic)))[2], ((char*)(&(unk->magic)))[1], ((char*)(&(unk->magic)))[0], unk->size );
position += unk->size + 8;
printf( " -- %xn", position );
}
// MDBH ______________________________________________________________________
MDBH* mdbh = ( MDBH* )( file + position );
assert( mdbh->magic == 'MDBH' && mdbh->size == 4 );
if( mdbh->number )
{
printf("There are %i destructible buildings:n", mdbh->number );
}
position += mdbh->size + 8;
// MDB* ______________________________________________________________________
for( size_t i = 0; i < mdbh->number; ++i )
{
MDBI* mdbi = ( MDBI* )( file + position );
assert( mdbi->magic == 'MDBI' && mdbh->size == 4 );
printf("- Building %i:n", mdbi->index );
position += mdbi->size + 8;
for( size_t i = 0; i < 3; ++i )
{
MDBF* mdbf = ( MDBF* )( file + position );
assert( mdbf->magic == 'MDBF' );
if( mdbf->size )
printf(" %i: %sn", i, mdbf->name );
position += mdbf->size + 8;
}
}
// MSHD ______________________________________________________________________
/* MSHD* mshd = ( MSHD* )( file + position );
assert( mshd->magic == 'MSHD' && mshd->size == 32 );
printf("nMSHD: n");
for( size_t i = 0; i < 8; ++i )
{
printf(" %i: %fn", i, mshd->unk[i] );
}
position += mshd->size + 8;*/
// ???? ______________________________________________________________________
while( position < size )
{
CHUNK* unk = ( CHUNK* )( file + position );
printf( "Magic: %c%c%c%c, size: %in", ((char*)(&(unk->magic)))[3], ((char*)(&(unk->magic)))[2], ((char*)(&(unk->magic)))[1], ((char*)(&(unk->magic)))[0], unk->size );
if( unk->magic == 'MShHD' && unk->size )
{
float* f = ( float* )( file + position + 8 );
int* n = ( int* )( file + position + 8 );
for( size_t i = 0; i < unk->size / 4; ++i )
{
printf("%x: %f %i %xn",i*4, *f, *n, *n );
f++;
n++;
}
}
position += unk->size + 8;
printf( " -- %xn", position );
}
delete[] file;
return 0;
}
(http://marlamin.com/u/2014-06-12_20-39-48-4l8f16zH.png)
-
note that these files are only server side and are not likely to be found in-client and are of little use. they contain information about destructible buildings and some other stuff.
-
Wow, you've decoded what these files actually are. I'm seriously impressed - I've looked at the files before, but couldn't make heads or tails as to what the files contained.
On the subject, sorta, do you think noggit could eventually have a way to grab all the known locations of model/wmo data, sort of in the same format as these files, so you could effectively convert a map from one version of the game, fix the offsets, and then dump the objects back into the map? (specifically the issue I'm facing while trying to get the old Alpha->3.3.5 maps working in Noggit - running the offset fix will fix the maps and make them work in Noggit, for the most part, but all objects - m2,wmo, etc, will be removed)
Just a thought, since you've managed to decode what I thought was just some leftover files from the internal WowEdit app. I imagine the same sort of reason for the pm4 files is because the map objects are indexed in a database and then exported to these files temporarily for the editor to use. (does that make sense?)
On another note, would it be possible to make noggit save each type of map chunk data into a set of project files, instead of a complete ADT, so that additional changes could be made without having to re-decode the ADT data every time you work on a map? Is this a design step you'd consider taking, should noggit3's development continue?
In any case, awesome work figuring this out. What other data do the files describe? I've only taken a quick look at what you've posted, but will study it a bit to fully comprehend what you've decoded from these files.
-
Not really more information, but a 010 editor template
//--------------------------------------
//--- 010 Editor v5.0 Binary Template
//
// File:
// Author:
// Revision:
// Purpose:
//--------------------------------------
typedef struct
{
float x;
float y;
float z;
} vec3f<read=readvec3f, optimize=false>;
string readvec3f (vec3f& rec)
{
string a;
SPrintf (a, "(%f %f %f)", rec.x, rec.y, rec.z);
return a;
}
typedef struct
{
float x;
float y;
float z;
float w;
} vec4f<read=readvec4f, optimize=false>;
string readvec4f (vec4f& rec)
{
string a;
SPrintf (a, "(%f %f %f %f)", rec.x, rec.y, rec.z, rec.w);
return a;
}
typedef struct
{
int x;
int y;
} vec2i<read=readvec2i, optimize=false>;
string readvec2i (vec2i& rec)
{
string a;
SPrintf (a, "(%i %i)", rec.x, rec.y);
return a;
}
typedef struct
{
int x;
int y;
int z;
int w;
} vec4i<read=readvec4i, optimize=false>;
string readvec4i (vec4i& rec)
{
string a;
SPrintf (a, "(%i %i %i %i)", rec.x, rec.y, rec.z, rec.w);
return a;
}
typedef struct
{
union
{
struct
{
char _0;
char _1;
char _2;
char _3;
} raw;
unsigned int _;
} _;
} CHUNK_MAGIC<read=readCHUNK_MAGIC, optimize=false>;
string readCHUNK_MAGIC(CHUNK_MAGIC& rec)
{
string a;
SPrintf(a, "%c%c%c%c", rec._.raw._3, rec._.raw._2, rec._.raw._1, rec._.raw._0);
return a;
}
typedef struct
{
CHUNK_MAGIC magic;
unsigned int size;
} CHUNK_header <read=readCHUNK_header, optimize=false>;
typedef struct
{
CHUNK_header header;
chunk_contents (header);
} CHUNK <read=readCHUNK, optimize=false>;
string readCHUNK(CHUNK& rec)
{
string a;
SPrintf (a, "%s", readCHUNK_header(rec.header));
return a;
}
string readCHUNK_header(CHUNK_header& rec)
{
string a;
SPrintf (a, "%s: %x", readCHUNK_MAGIC(rec.magic), rec.size);
return a;
}
while (FTell() < FileSize())
{
CHUNK chunk;
}
struct MVER
{
CHUNK _;
unsigned int version;
};
struct MDBF
{
CHUNK _;
char name[0];
};
struct MDBI
{
CHUNK _;
unsigned int index;
};
struct MDBH
{
CHUNK _;
unsigned int number;
};
struct MSHD
{
CHUNK _;
float unk[8];
};
struct MDOSentry
{
CHUNK _;
unsigned int a;
unsigned int b;
};
struct MDOS
{
CHUNK _;
MDOSentry entries[0];
};
void chunk_contents (CHUNK_header& header)
{
if (header.magic._._ == 1297499474) // MVER
{
unsigned int version;
}
else if (header.magic._._ == 1297303620) // MSHD -- s? header
{
int unk[header.size/4];
}
else if (header.magic._._ == 1297305686) // MSPV -- s? p? vertices
{
vec3f unk[header.size/sizeof(vec3f)];
}
else if (header.magic._._ == 1297305673) // MSPI --- s? p? indices
{
// todo: actual number of indices per record?
struct entry_MSPI
{
int _[2];
} unk[header.size/sizeof(entry_MSPI)];
}
else if (header.magic._._ == 1297302350) // MSCN -- s? c? normals?
{
vec3f unk[header.size/sizeof(vec3f)];
}
else if (header.magic._._ == 1297304651) // MSLK -- s? l? k?
{
struct entry_MSLK
{
int id;
int unk0;
short unk1a;
short unk1b;
int unk2;
unsigned short unk3;
short unk4;
} unk[header.size/sizeof (entry_MSLK)];
}
else if (header.magic._._ == 1297307220) // MSVT -- s? v? t?
{
vec3f unk[header.size/sizeof (vec3f)];
}
else if (header.magic._._ == 1297307209) // MSVI --- s? v? indices
{
// todo: actual number of indices per record?
struct entry_MSVI
{
int unk[1];
} unk[header.size/sizeof (entry_MSVI)];
}
else if (header.magic._._ == 1297306962) // MSUR --- s? u? r?
{
// todo: actual number of indices per record?
struct entry_MSUR
{
int id;
vec3f unk;
float unk1;
vec2i unk2;
float unk3;
} unk[header.size/sizeof (entry_MSUR)];
}
else if (header.magic._._ == 1297109580) // MPRL --- p? r? l?
{
struct entry_MPRL
{
unsigned short a; // 0
short b; // -1
unsigned short c;
unsigned short d;
vec3f position;
short e;
unsigned short f;
} unk[header.size/sizeof (entry_MPRL)];
}
else if (header.magic._._ == 1297109586) // MPRR --- p? r? r?
{
struct entry_MPRR
{
short unk0;
short unk1;
} unk[header.size/sizeof (entry_MPRR)];
}
else if (header.magic._._ == 1296319048) // MDBH --- destructible building header
{
unsigned int count;
struct entry_MDBH
{
CHUNK index;
CHUNK filename[3];
} entries[count]<optimize=false>;
}
else if (header.magic._._ == 1296319049) // MDBI --- destructible building index
{
unsigned int index;
}
else if (header.magic._._ == 1296322387) // MDOS --- d? o? s?
{
struct entry_MDOS
{
int unk0;
int unk1;
} unk[header.size/sizeof (entry_MDOS)];
}
else if (header.magic._._ == 1296323398) // MDSF --- d? s? f?
{
struct entry_MDSF
{
int unk0;
int unk1;
} unk[header.size/sizeof (entry_MDSF)];
}
else
{
char _[header.size];
}
}
-
On the subject, sorta, do you think noggit could eventually have a way to grab all the known locations of model/wmo data, sort of in the same format as these files, so you could effectively convert a map from one version of the game, fix the offsets, and then dump the objects back into the map?
That's what Cryect's fileinfo/loadinfo tools do?
Just a thought, since you've managed to decode what I thought was just some leftover files from the internal WowEdit app. I imagine the same sort of reason for the pm4 files is because the map objects are indexed in a database and then exported to these files temporarily for the editor to use. (does that make sense?)
They are used by the server and are not just some leftovers from wowedit.
On another note, would it be possible to make noggit save each type of map chunk data into a set of project files, instead of a complete ADT, so that additional changes could be made without having to re-decode the ADT data every time you work on a map? Is this a design step you'd consider taking, should noggit3's development continue?
I don't really see an advantage in that. Opening the ADTs is not really hard and in the end, those split up files will only be the single chunks, without any changes. I don't think having them split would be an improvement for editing them.
In any case, awesome work figuring this out. What other data do the files describe? I've only taken a quick look at what you've posted, but will study it a bit to fully comprehend what you've decoded from these files.
They seem to have pathfinding data, and some other terrain related stuff. I can't really figure that out, though. Any work on these files has no use anyway.
-
Thanks for answering my questions. They were mostly a bunch of random thoughts I had, floating in my mind. The only reason why I brought up saving the map data as individual chunks outside of the ADT is because of work I did 10 years ago helping to decode the Halo map format - in the end, we found that the map files were actually more or less databases of 'tags', very much like the chunked structure that ADT's have. When it came to editing the maps, we started with by-hand offset hacks, which worked ok initially, but eventually if we wanted to do more extensive edits, we needed to extract each 'tag' from the map files. The end result was a database of usable chunks of data that we could swap between different maps.
Things got a bit better, though, when Gearbox decided to release editing tools for the PC version, which allowed people to use a neutered version of the original Halo editing toolset. Gorilla was the main editing environment - it basically acted as a tag manager, and metadata editor. I suspect that WoWEdit is very much the same, except that it allows terrain editing within itself, whereas Bungie/Gearbox's tools required an external 3d editor to create and texture the terrain. I got about 30% into creating a map in 3dsmax, before my hard drive failed and I gave up on the project. Ah, those were the days... no backups to speak of :/
At least with Noggit, we have a fully functional editor that, when given proper new maps, seems to work correctly without the overhead of a database or tags. All the work you and everyone else here has put into the tool, including research and reverse engineering of the various file formats is beyond what was done with Halo. Well beyond any other game reversal projects that I've ever seen.