This is a read only copy without any forum functionality of the old Modcraft forum.
If there is anything that you would like to have removed, message me on Discord via Kaev#5208.
Big thanks to Alastor for making this copy!

Menu

Author Topic: infodump: pm4 files  (Read 2146 times)

schlumpf

  • Administrator
  • Creator of Worlds
  • *****
  • Posts: 2967
    • View Profile
infodump: pm4 files
« on: June 12, 2014, 10:26:48 pm »
Code: [Select]
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

Code: [Select]
#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;
}

« Last Edit: January 01, 1970, 01:00:00 am by Admin »

schlumpf

  • Administrator
  • Creator of Worlds
  • *****
  • Posts: 2967
    • View Profile
Re: infodump: pm4 files
« Reply #1 on: June 12, 2014, 10:39:50 pm »
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.
« Last Edit: January 01, 1970, 01:00:00 am by Admin »

akspa420

  • Registred Member
  • Polygonshifter
  • *****
  • Posts: 65
    • View Profile
Re: infodump: pm4 files
« Reply #2 on: June 14, 2014, 03:47:08 am »
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.
« Last Edit: January 01, 1970, 01:00:00 am by Admin »

schlumpf

  • Administrator
  • Creator of Worlds
  • *****
  • Posts: 2967
    • View Profile
Re: infodump: pm4 files
« Reply #3 on: June 14, 2014, 10:47:49 am »
Not really more information, but a 010 editor template

Code: [Select]
//--------------------------------------
//--- 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];
  }
}
« Last Edit: January 01, 1970, 01:00:00 am by Admin »

schlumpf

  • Administrator
  • Creator of Worlds
  • *****
  • Posts: 2967
    • View Profile
Re: infodump: pm4 files
« Reply #4 on: June 14, 2014, 10:52:07 am »
Quote from: "akspa420"
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?

Quote from: "akspa420"
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.

Quote from: "akspa420"
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.

Quote from: "akspa420"
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.
« Last Edit: January 01, 1970, 01:00:00 am by Admin »

akspa420

  • Registred Member
  • Polygonshifter
  • *****
  • Posts: 65
    • View Profile
Re: infodump: pm4 files
« Reply #5 on: June 19, 2014, 01:44:12 am »
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.
« Last Edit: January 01, 1970, 01:00:00 am by Admin »