Initial Mangos Three Commit

This commit is contained in:
Antz 2020-02-16 01:55:18 +00:00
parent bb91aa5933
commit 7665a09232
No known key found for this signature in database
GPG key ID: 0DF907270598C85F
2444 changed files with 625144 additions and 0 deletions

View file

@ -0,0 +1,156 @@
# MaNGOS is a full featured server for World of Warcraft, supporting
# the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
#
# Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Used for install targets
set(TOOLS_DIR "tools")
set(SHARED_SRCS
shared/dbcfile.cpp
shared/ExtractorCommon.cpp
shared/dbcfile.h
shared/ExtractorCommon.h
)
#=======================================================#
#map-extractor
#=======================================================#
add_executable(map-extractor
map-extractor/System.cpp
${SHARED_SRCS}
$<$<BOOL:${WIN32}>:map-extractor/map-extractor.rc>
)
target_include_directories(map-extractor
PUBLIC
shared
map-extractor
)
target_link_libraries(map-extractor
PUBLIC
loadlib
)
install(
TARGETS map-extractor
DESTINATION "${BIN_DIR}/${TOOLS_DIR}"
)
if(WIN32 AND MSVC)
install(
FILES $<TARGET_PDB_FILE:map-extractor>
DESTINATION ${BIN_DIR}/${TOOLS_DIR}
OPTIONAL)
endif()
#=======================================================#
#vmap-extractor
#=======================================================#
add_executable(vmap-extractor
vmap-extractor/adtfile.cpp
vmap-extractor/adtfile.h
vmap-extractor/assembler.cpp
vmap-extractor/model.cpp
vmap-extractor/model.h
vmap-extractor/modelheaders.h
vmap-extractor/vec3d.h
vmap-extractor/vmapexport.cpp
vmap-extractor/vmapexport.h
vmap-extractor/wdtfile.cpp
vmap-extractor/wdtfile.h
vmap-extractor/wmo.cpp
vmap-extractor/wmo.h
${SHARED_SRCS}
$<$<BOOL:${WIN32}>:vmap-extractor/vmap-extractor.rc>
)
target_include_directories(vmap-extractor
PUBLIC
shared
vmap-extractor
)
target_link_libraries(vmap-extractor
PUBLIC
loadlib
vmap2
)
install(
TARGETS vmap-extractor
DESTINATION ${BIN_DIR}/${TOOLS_DIR}
)
if(WIN32 AND MSVC)
install(
FILES $<TARGET_PDB_FILE:vmap-extractor>
DESTINATION ${BIN_DIR}/${TOOLS_DIR}
OPTIONAL
)
endif()
#=======================================================#
#mmap-extractor
#=======================================================#
add_executable(mmap-extractor
Movemap-Generator/generator.cpp
Movemap-Generator/IntermediateValues.cpp
Movemap-Generator/IntermediateValues.h
Movemap-Generator/MangosMap.h
Movemap-Generator/MapBuilder.cpp
Movemap-Generator/MapBuilder.h
Movemap-Generator/MMapCommon.h
Movemap-Generator/TerrainBuilder.cpp
Movemap-Generator/TerrainBuilder.h
Movemap-Generator/TileMsgBlock.h
Movemap-Generator/TileThreadPool.cpp
Movemap-Generator/TileThreadPool.h
Movemap-Generator/VMapExtensions.cpp
shared/ExtractorCommon.cpp
shared/ExtractorCommon.h
$<$<BOOL:${WIN32}>:Movemap-Generator/Movemap-Generator.rc>
)
target_include_directories(mmap-extractor
PUBLIC
shared
Movemap-Generator
)
target_link_libraries(mmap-extractor
PUBLIC
vmap2
shared
RecastNavigation::Recast
Threads::Threads
DL::DL
)
install(
TARGETS mmap-extractor
DESTINATION ${BIN_DIR}/${TOOLS_DIR}
)
if(WIN32 AND MSVC)
install(
FILES $<TARGET_PDB_FILE:mmap-extractor>
DESTINATION ${BIN_DIR}/${TOOLS_DIR}
OPTIONAL
)
endif()

View file

@ -0,0 +1,327 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#include "IntermediateValues.h"
namespace MMAP
{
IntermediateValues::~IntermediateValues()
{
rcFreeCompactHeightfield(compactHeightfield);
rcFreeHeightField(heightfield);
rcFreeContourSet(contours);
rcFreePolyMesh(polyMesh);
rcFreePolyMeshDetail(polyMeshDetail);
}
void IntermediateValues::writeIV(int mapID, int tileX, int tileY)
{
char fileName[255];
char tileString[25];
sprintf(tileString, "[%02u,%02u]: ", tileX, tileY);
printf("%sWriting debug output... \r", tileString);
string name("meshes/%03u%02i%02i.");
#define DEBUG_WRITE(fileExtension,data) \
do { \
sprintf(fileName, (name + fileExtension).c_str(), mapID, tileY, tileX); \
FILE* file = fopen(fileName, "wb"); \
if (!file) \
{ \
char message[1024]; \
sprintf(message, "%sFailed to open %s for writing!\n", tileString, fileName); \
perror(message); \
} \
else \
debugWrite(file, data); \
if(file) fclose(file); \
printf("%sWriting debug output... \r", tileString); \
} while (false)
if (heightfield)
{
DEBUG_WRITE("hf", heightfield);
}
if (compactHeightfield)
{
DEBUG_WRITE("chf", compactHeightfield);
}
if (contours)
{
DEBUG_WRITE("cs", contours);
}
if (polyMesh)
{
DEBUG_WRITE("pmesh", polyMesh);
}
if (polyMeshDetail)
{
DEBUG_WRITE("dmesh", polyMeshDetail);
}
#undef DEBUG_WRITE
}
void IntermediateValues::debugWrite(FILE* file, const rcHeightfield* mesh)
{
if (!file || !mesh)
{
return;
}
fwrite(&(mesh->cs), sizeof(float), 1, file);
fwrite(&(mesh->ch), sizeof(float), 1, file);
fwrite(&(mesh->width), sizeof(int), 1, file);
fwrite(&(mesh->height), sizeof(int), 1, file);
fwrite(mesh->bmin, sizeof(float), 3, file);
fwrite(mesh->bmax, sizeof(float), 3, file);
for (int y = 0; y < mesh->height; ++y)
for (int x = 0; x < mesh->width; ++x)
{
rcSpan* span = mesh->spans[x + y * mesh->width];
// first, count the number of spans
int spanCount = 0;
while (span)
{
spanCount++;
span = span->next;
}
// write the span count
fwrite(&spanCount, sizeof(int), 1, file);
// write the spans
span = mesh->spans[x + y * mesh->width];
while (span)
{
fwrite(span, sizeof(rcSpan), 1, file);
span = span->next;
}
}
}
void IntermediateValues::debugWrite(FILE* file, const rcCompactHeightfield* chf)
{
if (!file | !chf)
{
return;
}
fwrite(&(chf->width), sizeof(chf->width), 1, file);
fwrite(&(chf->height), sizeof(chf->height), 1, file);
fwrite(&(chf->spanCount), sizeof(chf->spanCount), 1, file);
fwrite(&(chf->walkableHeight), sizeof(chf->walkableHeight), 1, file);
fwrite(&(chf->walkableClimb), sizeof(chf->walkableClimb), 1, file);
fwrite(&(chf->maxDistance), sizeof(chf->maxDistance), 1, file);
fwrite(&(chf->maxRegions), sizeof(chf->maxRegions), 1, file);
fwrite(chf->bmin, sizeof(chf->bmin), 1, file);
fwrite(chf->bmax, sizeof(chf->bmax), 1, file);
fwrite(&(chf->cs), sizeof(chf->cs), 1, file);
fwrite(&(chf->ch), sizeof(chf->ch), 1, file);
int tmp = 0;
if (chf->cells)
{
tmp |= 1;
}
if (chf->spans)
{
tmp |= 2;
}
if (chf->dist)
{
tmp |= 4;
}
if (chf->areas)
{
tmp |= 8;
}
fwrite(&tmp, sizeof(tmp), 1, file);
if (chf->cells)
{
fwrite(chf->cells, sizeof(rcCompactCell), chf->width * chf->height, file);
}
if (chf->spans)
{
fwrite(chf->spans, sizeof(rcCompactSpan), chf->spanCount, file);
}
if (chf->dist)
{
fwrite(chf->dist, sizeof(unsigned short), chf->spanCount, file);
}
if (chf->areas)
{
fwrite(chf->areas, sizeof(unsigned char), chf->spanCount, file);
}
}
void IntermediateValues::debugWrite(FILE* file, const rcContourSet* cs)
{
if (!file || !cs)
{
return;
}
fwrite(&(cs->cs), sizeof(float), 1, file);
fwrite(&(cs->ch), sizeof(float), 1, file);
fwrite(cs->bmin, sizeof(float), 3, file);
fwrite(cs->bmax, sizeof(float), 3, file);
fwrite(&(cs->nconts), sizeof(int), 1, file);
for (int i = 0; i < cs->nconts; ++i)
{
fwrite(&cs->conts[i].area, sizeof(unsigned char), 1, file);
fwrite(&cs->conts[i].reg, sizeof(unsigned short), 1, file);
fwrite(&cs->conts[i].nverts, sizeof(int), 1, file);
fwrite(cs->conts[i].verts, sizeof(int), cs->conts[i].nverts * 4, file);
fwrite(&cs->conts[i].nrverts, sizeof(int), 1, file);
fwrite(cs->conts[i].rverts, sizeof(int), cs->conts[i].nrverts * 4, file);
}
}
void IntermediateValues::debugWrite(FILE* file, const rcPolyMesh* mesh)
{
if (!file || !mesh)
{
return;
}
fwrite(&(mesh->cs), sizeof(float), 1, file);
fwrite(&(mesh->ch), sizeof(float), 1, file);
fwrite(&(mesh->nvp), sizeof(int), 1, file);
fwrite(mesh->bmin, sizeof(float), 3, file);
fwrite(mesh->bmax, sizeof(float), 3, file);
fwrite(&(mesh->nverts), sizeof(int), 1, file);
fwrite(mesh->verts, sizeof(unsigned short), mesh->nverts * 3, file);
fwrite(&(mesh->npolys), sizeof(int), 1, file);
fwrite(mesh->polys, sizeof(unsigned short), mesh->npolys * mesh->nvp * 2, file);
fwrite(mesh->flags, sizeof(unsigned short), mesh->npolys, file);
fwrite(mesh->areas, sizeof(unsigned char), mesh->npolys, file);
fwrite(mesh->regs, sizeof(unsigned short), mesh->npolys, file);
}
void IntermediateValues::debugWrite(FILE* file, const rcPolyMeshDetail* mesh)
{
if (!file || !mesh)
{
return;
}
fwrite(&(mesh->nverts), sizeof(int), 1, file);
fwrite(mesh->verts, sizeof(float), mesh->nverts * 3, file);
fwrite(&(mesh->ntris), sizeof(int), 1, file);
fwrite(mesh->tris, sizeof(char), mesh->ntris * 4, file);
fwrite(&(mesh->nmeshes), sizeof(int), 1, file);
fwrite(mesh->meshes, sizeof(int), mesh->nmeshes * 4, file);
}
void IntermediateValues::generateObjFile(int mapID, int tileX, int tileY, MeshData& meshData)
{
char objFileName[255];
sprintf(objFileName, "meshes/map%03u%02u%02u.obj", mapID, tileY, tileX);
FILE* objFile = fopen(objFileName, "wb");
if (!objFile)
{
char message[1024];
sprintf(message, "Failed to open %s for writing!\n", objFileName);
perror(message);
return;
}
G3D::Array<float> allVerts;
G3D::Array<int> allTris;
allTris.append(meshData.liquidTris);
allVerts.append(meshData.liquidVerts);
TerrainBuilder::copyIndices(meshData.solidTris, allTris, allVerts.size() / 3);
allVerts.append(meshData.solidVerts);
float* verts = allVerts.getCArray();
int vertCount = allVerts.size() / 3;
int* tris = allTris.getCArray();
int triCount = allTris.size() / 3;
for (int i = 0; i < allVerts.size() / 3; i++)
{
fprintf(objFile, "v %f %f %f\n", verts[i * 3], verts[i * 3 + 1], verts[i * 3 + 2]);
}
for (int i = 0; i < allTris.size() / 3; i++)
{
fprintf(objFile, "f %i %i %i\n", tris[i * 3] + 1, tris[i * 3 + 1] + 1, tris[i * 3 + 2] + 1);
}
fclose(objFile);
char tileString[25];
sprintf(tileString, "[%02u,%02u]: ", tileY, tileX);
printf("%sWriting debug output... \r", tileString);
sprintf(objFileName, "meshes/%03u.map", mapID);
objFile = fopen(objFileName, "wb");
if (!objFile)
{
char message[1024];
sprintf(message, "Failed to open %s for writing!\n", objFileName);
perror(message);
return;
}
char b = '\0';
fwrite(&b, sizeof(char), 1, objFile);
fclose(objFile);
sprintf(objFileName, "meshes/%03u%02u%02u.mesh", mapID, tileY, tileX);
objFile = fopen(objFileName, "wb");
if (!objFile)
{
char message[1024];
sprintf(message, "Failed to open %s for writing!\n", objFileName);
perror(message);
return;
}
fwrite(&vertCount, sizeof(int), 1, objFile);
fwrite(verts, sizeof(float), vertCount * 3, objFile);
fflush(objFile);
fwrite(&triCount, sizeof(int), 1, objFile);
fwrite(tris, sizeof(int), triCount * 3, objFile);
fflush(objFile);
fclose(objFile);
}
}

View file

@ -0,0 +1,116 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#ifndef MANGOS_H_INTERMEDIATE_VALUES
#define MANGOS_H_INTERMEDIATE_VALUES
#include <Recast.h>
#include <DetourNavMesh.h>
#include "MMapCommon.h"
#include "TerrainBuilder.h"
namespace MMAP
{
/**
* @brief this class gathers all debug info holding and output
*
*/
struct IntermediateValues
{
rcHeightfield* heightfield; /**< TODO */
rcCompactHeightfield* compactHeightfield; /**< TODO */
rcContourSet* contours; /**< TODO */
rcPolyMesh* polyMesh; /**< TODO */
rcPolyMeshDetail* polyMeshDetail; /**< TODO */
/**
* @brief
*
*/
IntermediateValues() : compactHeightfield(NULL), heightfield(NULL),
contours(NULL), polyMesh(NULL), polyMeshDetail(NULL) {}
/**
* @brief
*
*/
~IntermediateValues();
/**
* @brief
*
* @param mapID
* @param tileX
* @param tileY
*/
void writeIV(int mapID, int tileX, int tileY);
/**
* @brief
*
* @param file
* @param mesh
*/
void debugWrite(FILE* file, const rcHeightfield* mesh);
/**
* @brief
*
* @param file
* @param chf
*/
void debugWrite(FILE* file, const rcCompactHeightfield* chf);
/**
* @brief
*
* @param file
* @param cs
*/
void debugWrite(FILE* file, const rcContourSet* cs);
/**
* @brief
*
* @param file
* @param mesh
*/
void debugWrite(FILE* file, const rcPolyMesh* mesh);
/**
* @brief
*
* @param file
* @param mesh
*/
void debugWrite(FILE* file, const rcPolyMeshDetail* mesh);
/**
* @brief
*
* @param mapID
* @param tileX
* @param tileY
* @param meshData
*/
void generateObjFile(int mapID, int tileX, int tileY, MeshData& meshData);
};
}
#endif

View file

@ -0,0 +1,173 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#ifndef MANGOS_H_MMAP_COMMON
#define MANGOS_H_MMAP_COMMON
// stop warning spam from ACE includes
#ifdef WIN32
# pragma warning( disable : 4996 )
#endif
#include <string>
#include <vector>
#include <errno.h>
#include "Platform/Define.h"
#ifndef WIN32
#include <stddef.h>
#include <dirent.h>
#endif
using namespace std;
namespace MMAP
{
/**
* @brief
*
* @param filter
* @param str
* @return bool
*/
inline bool matchWildcardFilter(const char* filter, const char* str)
{
if (!filter || !str)
{
return false;
}
// end on null character
while (*filter && *str)
{
if (*filter == '*')
{
if (*++filter == '\0') // wildcard at end of filter means all remaing chars match
{
return true;
}
while (true)
{
if (*filter == *str)
{
break;
}
if (*str == '\0')
{ return false; } // reached end of string without matching next filter character
str++;
}
}
else if (*filter != *str)
{ return false; } // mismatch
filter++;
str++;
}
return ((*filter == '\0' || (*filter == '*' && *++filter == '\0')) && *str == '\0');
}
/**
* @brief
*
*/
enum ListFilesResult
{
LISTFILE_DIRECTORY_NOT_FOUND = 0,
LISTFILE_OK = 1
};
/**
* @brief
*
* @param fileList
* @param dirpath
* @param filter
* @param includeSubDirs
* @return ListFilesResult
*/
inline ListFilesResult getDirContents(vector<string>& fileList, string dirpath = ".", string filter = "*", bool includeSubDirs = false)
{
#ifdef WIN32
HANDLE hFind;
WIN32_FIND_DATA findFileInfo;
string directory;
directory = dirpath + "/" + filter;
hFind = FindFirstFile(directory.c_str(), &findFileInfo);
if (hFind == INVALID_HANDLE_VALUE)
{
return LISTFILE_DIRECTORY_NOT_FOUND;
}
do
{
if (includeSubDirs || (findFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
{
fileList.push_back(string(findFileInfo.cFileName));
}
}
while (FindNextFile(hFind, &findFileInfo));
FindClose(hFind);
#else
const char* p = dirpath.c_str();
DIR* dirp = opendir(p);
struct dirent* dp;
while (dirp)
{
errno = 0;
if ((dp = readdir(dirp)) != NULL)
{
if (matchWildcardFilter(filter.c_str(), dp->d_name))
{
fileList.push_back(string(dp->d_name));
}
}
else
{
break;
}
}
if (dirp)
{
closedir(dirp);
}
else
{
return LISTFILE_DIRECTORY_NOT_FOUND;
}
#endif
return LISTFILE_OK;
}
}
#endif

View file

@ -0,0 +1,123 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#ifndef MANGOS_H_MMAP_MANGOS_MAP
#define MANGOS_H_MMAP_MANGOS_MAP
// following is copied from src/game/GridMap.h (too many useless includes there to use original file)
namespace MaNGOS
{
/**
* @brief
*
*/
struct GridMapFileHeader
{
uint32 mapMagic; /**< TODO */
uint32 versionMagic; /**< TODO */
uint32 buildMagic;
uint32 areaMapOffset; /**< TODO */
uint32 areaMapSize; /**< TODO */
uint32 heightMapOffset; /**< TODO */
uint32 heightMapSize; /**< TODO */
uint32 liquidMapOffset; /**< TODO */
uint32 liquidMapSize; /**< TODO */
uint32 holesOffset; /**< TODO */
uint32 holesSize; /**< TODO */
};
// ==============mmaps don't use area==============
//#define MAP_AREA_NO_AREA 0x0001
//struct GridMapAreaHeader
//{
// uint32 fourcc;
// uint16 flags;
// uint16 gridArea;
//};
#define MAP_HEIGHT_NO_HEIGHT 0x0001
#define MAP_HEIGHT_AS_INT16 0x0002
#define MAP_HEIGHT_AS_INT8 0x0004
/**
* @brief
*
*/
struct GridMapHeightHeader
{
uint32 fourcc; /**< TODO */
uint32 flags; /**< TODO */
float gridHeight; /**< TODO */
float gridMaxHeight; /**< TODO */
};
#define MAP_LIQUID_NO_TYPE 0x0001
#define MAP_LIQUID_NO_HEIGHT 0x0002
/**
* @brief
*
*/
struct GridMapLiquidHeader
{
uint32 fourcc; /**< TODO */
uint16 flags; /**< TODO */
uint16 liquidType; /**< TODO */
uint8 offsetX; /**< TODO */
uint8 offsetY; /**< TODO */
uint8 width; /**< TODO */
uint8 height; /**< TODO */
float liquidLevel; /**< TODO */
};
//enum GridMapLiquidStatus
//{
// LIQUID_MAP_NO_WATER = 0x00000000,
// LIQUID_MAP_ABOVE_WATER = 0x00000001,
// LIQUID_MAP_WATER_WALK = 0x00000002,
// LIQUID_MAP_IN_WATER = 0x00000004,
// LIQUID_MAP_UNDER_WATER = 0x00000008
//};
#define MAP_LIQUID_TYPE_NO_WATER 0x00
#define MAP_LIQUID_TYPE_WATER 0x01
#define MAP_LIQUID_TYPE_OCEAN 0x02
#define MAP_LIQUID_TYPE_MAGMA 0x04
#define MAP_LIQUID_TYPE_SLIME 0x08
#define MAP_ALL_LIQUIDS (MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN | MAP_LIQUID_TYPE_MAGMA | MAP_LIQUID_TYPE_SLIME)
#define MAP_LIQUID_TYPE_DARK_WATER 0x10
#define MAP_LIQUID_TYPE_WMO_WATER 0x20
//struct GridMapLiquidData
//{
// uint32 type;
// float level;
// float depth_level;
//};
}
#endif

View file

@ -0,0 +1,922 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#include <DetourNavMeshBuilder.h>
#include <DetourCommon.h>
#include "MMapCommon.h"
#include "MapBuilder.h"
#include "MapTree.h"
#include "ModelInstance.h"
#include "ExtractorCommon.h"
#include "TileMsgBlock.h"
using namespace VMAP;
namespace MMAP
{
MapBuilder::MapBuilder(char const* magic, float maxWalkableAngle, bool skipLiquid,
bool skipContinents, bool skipJunkMaps, bool skipBattlegrounds,
bool debugOutput, bool bigBaseUnit, const char* offMeshFilePath) :
m_terrainBuilder(NULL),
m_debugOutput(debugOutput),
m_skipContinents(skipContinents),
m_skipJunkMaps(skipJunkMaps),
m_skipBattlegrounds(skipBattlegrounds),
m_maxWalkableAngle(maxWalkableAngle),
m_bigBaseUnit(bigBaseUnit),
m_rcContext(NULL),
m_offMeshFilePath(offMeshFilePath),
m_magic(magic),
m_numThreads(-1), m_threadPool(NULL), m_poolActivated(false)
{
m_terrainBuilder = new TerrainBuilder(skipLiquid);
m_rcContext = new rcContext(false);
discoverTiles();
}
/**************************************************************************/
MapBuilder::~MapBuilder()
{
if (activated())
{
delete m_threadPool;
m_poolActivated = false;
}
for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it)
{
(*it).second->clear();
delete(*it).second;
}
delete m_terrainBuilder;
delete m_rcContext;
}
/**************************************************************************/
int MapBuilder::activate(int num_threads)
{
int result = -1;
m_numThreads = num_threads;
m_threadPool = new TileThreadPool();
if (m_threadPool && m_numThreads && !m_poolActivated)
{
result = m_threadPool->start(m_numThreads);
if (result != -1)
{
m_poolActivated = true;
}
}
return result;
}
/**************************************************************************/
void MapBuilder::discoverTiles()
{
vector<string> files;
int mapID;
uint32 tileX, tileY, tileID, count = 0;
char filter[12];
printf(" Discovering maps... ");
getDirContents(files, "maps");
for (uint32 i = 0; i < files.size(); ++i)
{
mapID = uint32(atoi(files[i].substr(0, 3).c_str()));
if (m_tiles.find(mapID) == m_tiles.end())
{
m_tiles.insert(pair<uint32, set<uint32>*>(mapID, new set<uint32>));
count++;
}
}
files.clear();
getDirContents(files, "vmaps", "*.vmtree");
for (uint32 i = 0; i < files.size(); ++i)
{
mapID = uint32(atoi(files[i].substr(0, 3).c_str()));
m_tiles.insert(pair<uint32, set<uint32>*>(mapID, new set<uint32>));
count++;
}
printf(" found %u.\n", count);
count = 0;
printf(" Discovering tiles... ");
for (TileList::iterator itr = m_tiles.begin(); itr != m_tiles.end(); ++itr)
{
set<uint32>* tiles = (*itr).second;
mapID = (*itr).first;
sprintf(filter, "%03u*.vmtile", mapID);
files.clear();
getDirContents(files, "vmaps", filter);
for (uint32 i = 0; i < files.size(); ++i)
{
tileX = uint32(atoi(files[i].substr(7, 2).c_str()));
tileY = uint32(atoi(files[i].substr(4, 2).c_str()));
tileID = StaticMapTree::packTileID(tileY, tileX);
tiles->insert(tileID);
count++;
}
sprintf(filter, "%03u*", mapID);
files.clear();
getDirContents(files, "maps", filter);
for (uint32 i = 0; i < files.size(); ++i)
{
tileY = uint32(atoi(files[i].substr(3, 2).c_str()));
tileX = uint32(atoi(files[i].substr(5, 2).c_str()));
tileID = StaticMapTree::packTileID(tileX, tileY);
if (tiles->insert(tileID).second)
{
count++;
}
}
}
printf(" found %u.\n\n", count);
}
/**************************************************************************/
set<uint32>* MapBuilder::getTileList(int mapID)
{
TileList::iterator itr = m_tiles.find(mapID);
if (itr != m_tiles.end())
{
return (*itr).second;
}
set<uint32>* tiles = new set<uint32>();
m_tiles.insert(pair<uint32, set<uint32>*>(mapID, tiles));
return tiles;
}
/**************************************************************************/
void MapBuilder::buildAllMaps()
{
for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it)
{
uint32 mapID = (*it).first;
if (!shouldSkipMap(mapID,m_skipContinents,m_skipJunkMaps,m_skipBattlegrounds))
{
buildMap(mapID, false);
}
}
if (activated())
{
Tile_Message_Block *finish_mb = new Tile_Message_Block(NULL);
finish_mb->msg_type(ACE_Message_Block::MB_HANGUP);
m_threadPool->putq(finish_mb);
m_threadPool->wait();
}
}
/**************************************************************************/
void MapBuilder::buildMap(int mapID, bool standAlone)
{
set<uint32>* tiles = getTileList(mapID);
// make sure we process maps which don't have tiles
if (!tiles->size())
{
// convert coord bounds to grid bounds
uint32 minX, minY, maxX, maxY;
getGridBounds(mapID, minX, minY, maxX, maxY);
// add all tiles within bounds to tile list.
for (uint32 i = minX; i <= maxX; ++i)
for (uint32 j = minY; j <= maxY; ++j)
{
tiles->insert(StaticMapTree::packTileID(i, j));
}
}
if (!tiles->size())
{
return;
}
// build navMesh
dtNavMesh* navMesh = NULL;
dtNavMeshParams* meshParams = NULL;
buildNavMesh(mapID, navMesh, meshParams);
if (!navMesh)
{
printf("Failed creating navmesh for map %03u! \n", mapID);
if (meshParams)
{
delete meshParams;
}
return;
}
if (activated())
{ dtFreeNavMesh(navMesh); } // each tile will get it's own pointer to navMesh
// now start building/scheduling mmtiles for each tile
printf(" %s map %03u [%u tiles]\n", activated() ? "Scheduling" : "Building", mapID, (unsigned int)tiles->size());
for (set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it)
{
uint32 tileX, tileY;
// unpack tile coords
StaticMapTree::unpackTileID((*it), tileX, tileY);
if (shouldSkipTile(mapID, tileX, tileY))
{
continue;
}
if (!activated())
{
buildTile(mapID, tileX, tileY, navMesh);
}
else
{
dtNavMesh *mesh = NULL;
buildNavMesh(mapID, mesh, meshParams); //meshParams is not null, so we get a new pointer to dtNavMesh
if (mesh)
{
TileBuilder* tb = new TileBuilder(this, mapID, tileX, tileY, mesh);
Tile_Message_Block *mb = new Tile_Message_Block(tb);
if (m_threadPool->putq(mb) == -1)
{
break;
}
}
}
}
if (activated() && standAlone)
{
Tile_Message_Block *finish_mb = new Tile_Message_Block(NULL);
finish_mb->msg_type(ACE_Message_Block::MB_HANGUP);
m_threadPool->putq(finish_mb);
m_threadPool->wait();
}
if (!activated())
{
dtFreeNavMesh(navMesh);
}
if (meshParams)
{
delete meshParams;
}
if (!activated())
{
printf(" Map %03u complete!\n\n", mapID);
}
}
/**************************************************************************/
void MapBuilder::buildSingleTile(int mapID, int tileX, int tileY)
{
dtNavMesh* navMesh = NULL;
dtNavMeshParams* meshParams = NULL;
buildNavMesh(mapID, navMesh, meshParams);
if (!navMesh)
{
printf("Failed creating navmesh! \n");
return;
}
buildTile(mapID, tileX, tileY, navMesh);
dtFreeNavMesh(navMesh);
if (meshParams)
{
delete meshParams;
}
}
/**************************************************************************/
void MapBuilder::buildTile(int mapID, int tileX, int tileY, dtNavMesh* navMesh)
{
MeshData meshData;
// get heightmap data
m_terrainBuilder->loadMap(mapID, tileX, tileY, meshData, m_magic);
// get model data
m_terrainBuilder->loadVMap(mapID, tileY, tileX, meshData);
// if there is no data, give up now
if (!meshData.solidVerts.size() && !meshData.liquidVerts.size())
{
return;
}
// remove unused vertices
TerrainBuilder::cleanVertices(meshData.solidVerts, meshData.solidTris);
TerrainBuilder::cleanVertices(meshData.liquidVerts, meshData.liquidTris);
// gather all mesh data for final data check, and bounds calculation
G3D::Array<float> allVerts;
allVerts.append(meshData.liquidVerts);
allVerts.append(meshData.solidVerts);
if (!allVerts.size())
{
return;
}
// get bounds of current tile
float bmin[3], bmax[3];
getTileBounds(tileX, tileY, allVerts.getCArray(), allVerts.size() / 3, bmin, bmax);
m_terrainBuilder->loadOffMeshConnections(mapID, tileX, tileY, meshData, m_offMeshFilePath);
printf(" Building map %03u - Tile [%02u,%02u]\n", mapID, tileX, tileY);
buildMoveMapTile(mapID, tileX, tileY, meshData, bmin, bmax, navMesh);
}
/**************************************************************************/
void MapBuilder::getGridBounds(int mapID, uint32& minX, uint32& minY, uint32& maxX, uint32& maxY)
{
maxX = INT_MAX;
maxY = INT_MAX;
minX = INT_MIN;
minY = INT_MIN;
float bmin[3] = { 0 };
float bmax[3] = { 0 };
float lmin[3] = { 0 };
float lmax[3] = { 0 };
MeshData meshData;
// make sure we process maps which don't have tiles
// initialize the static tree, which loads WDT models
if (!m_terrainBuilder->loadVMap(mapID, 64, 64, meshData))
{
return;
}
// get the coord bounds of the model data
if (meshData.solidVerts.size() + meshData.liquidVerts.size() == 0)
{
return;
}
// get the coord bounds of the model data
if (meshData.solidVerts.size() && meshData.liquidVerts.size())
{
rcCalcBounds(meshData.solidVerts.getCArray(), meshData.solidVerts.size() / 3, bmin, bmax);
rcCalcBounds(meshData.liquidVerts.getCArray(), meshData.liquidVerts.size() / 3, lmin, lmax);
rcVmin(bmin, lmin);
rcVmax(bmax, lmax);
}
else if (meshData.solidVerts.size())
{
rcCalcBounds(meshData.solidVerts.getCArray(), meshData.solidVerts.size() / 3, bmin, bmax);
}
else
{
rcCalcBounds(meshData.liquidVerts.getCArray(), meshData.liquidVerts.size() / 3, lmin, lmax);
}
// convert coord bounds to grid bounds
maxX = 32 - bmin[0] / GRID_SIZE;
maxY = 32 - bmin[2] / GRID_SIZE;
minX = 32 - bmax[0] / GRID_SIZE;
minY = 32 - bmax[2] / GRID_SIZE;
}
/**************************************************************************/
void MapBuilder::buildNavMesh(int mapID, dtNavMesh*& navMesh, dtNavMeshParams*& navMeshParams)
{
bool isFirstNavMesh = navMeshParams ? false : true;
if (isFirstNavMesh)
{
set<uint32>* tiles = getTileList(mapID);
int polyBits = DT_POLY_BITS;
int maxTiles = tiles->size();
int maxPolysPerTile = 1 << polyBits;
/*** calculate bounds of map ***/
uint32 tileXMin = 64, tileYMin = 64, tileXMax = 0, tileYMax = 0, tileX, tileY;
for (set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it)
{
StaticMapTree::unpackTileID((*it), tileX, tileY);
if (tileX > tileXMax)
{
tileXMax = tileX;
}
else if (tileX < tileXMin)
{
tileXMin = tileX;
}
if (tileY > tileYMax)
{
tileYMax = tileY;
}
else if (tileY < tileYMin)
{
tileYMin = tileY;
}
}
// use Max because '32 - tileX' is negative for values over 32
float bmin[3], bmax[3];
getTileBounds(tileXMax, tileYMax, NULL, 0, bmin, bmax);
/*** now create the navmesh ***/
// navmesh creation params
navMeshParams = new dtNavMeshParams();
memset(navMeshParams, 0, sizeof(dtNavMeshParams));
navMeshParams->tileWidth = GRID_SIZE;
navMeshParams->tileHeight = GRID_SIZE;
rcVcopy(navMeshParams->orig, bmin);
navMeshParams->maxTiles = maxTiles;
navMeshParams->maxPolys = maxPolysPerTile;
}
navMesh = dtAllocNavMesh();
if (!navMesh->init(navMeshParams))
{
printf("Failed creating navmesh! \n");
return;
}
if (isFirstNavMesh)
{
char fileName[25];
sprintf(fileName, "mmaps/%03u.mmap", mapID);
FILE* file = fopen(fileName, "wb");
if (!file)
{
dtFreeNavMesh(navMesh);
char message[1024];
sprintf(message, "Failed to open %s for writing!\n", fileName);
perror(message);
return;
}
// now that we know navMesh params are valid, we can write them to file
fwrite(navMeshParams, sizeof(dtNavMeshParams), 1, file);
fclose(file);
}
}
/**************************************************************************/
void MapBuilder::buildMoveMapTile(int mapID, int tileX, int tileY,
MeshData& meshData, float bmin[3], float bmax[3],
dtNavMesh* navMesh)
{
// console output
char tileString[10];
sprintf(tileString, "[%02i,%02i] ", tileX, tileY);
IntermediateValues iv;
float* tVerts = meshData.solidVerts.getCArray();
int tVertCount = meshData.solidVerts.size() / 3;
int* tTris = meshData.solidTris.getCArray();
int tTriCount = meshData.solidTris.size() / 3;
float* lVerts = meshData.liquidVerts.getCArray();
int lVertCount = meshData.liquidVerts.size() / 3;
int* lTris = meshData.liquidTris.getCArray();
int lTriCount = meshData.liquidTris.size() / 3;
uint8* lTriFlags = meshData.liquidType.getCArray();
// these are WORLD UNIT based metrics
// this are basic unit dimentions
// value have to divide GRID_SIZE(533.33333f) ( aka: 0.5333, 0.2666, 0.3333, 0.1333, etc )
const static float BASE_UNIT_DIM = m_bigBaseUnit ? 0.533333f : 0.266666f;
// All are in UNIT metrics!
const static int VERTEX_PER_MAP = int(GRID_SIZE / BASE_UNIT_DIM + 0.5f);
const static int VERTEX_PER_TILE = m_bigBaseUnit ? 40 : 80; // must divide VERTEX_PER_MAP
const static int TILES_PER_MAP = VERTEX_PER_MAP / VERTEX_PER_TILE;
rcConfig config;
memset(&config, 0, sizeof(rcConfig));
rcVcopy(config.bmin, bmin);
rcVcopy(config.bmax, bmax);
config.maxVertsPerPoly = DT_VERTS_PER_POLYGON;
config.cs = BASE_UNIT_DIM;
config.ch = BASE_UNIT_DIM;
config.walkableSlopeAngle = m_maxWalkableAngle;
config.tileSize = VERTEX_PER_TILE;
config.walkableRadius = m_bigBaseUnit ? 1 : 2;
config.borderSize = config.walkableRadius + 3;
config.maxEdgeLen = VERTEX_PER_TILE + 1; //anything bigger than tileSize
config.walkableHeight = m_bigBaseUnit ? 3 : 6;
config.walkableClimb = m_bigBaseUnit ? 2 : 4; // keep less than walkableHeight
config.minRegionArea = rcSqr(60);
config.mergeRegionArea = rcSqr(50);
config.maxSimplificationError = 2.0f; // eliminates most jagged edges (tinny polygons)
config.detailSampleDist = config.cs * 64;
config.detailSampleMaxError = config.ch * 2;
// this sets the dimensions of the heightfield - should maybe happen before border padding
rcCalcGridSize(config.bmin, config.bmax, config.cs, &config.width, &config.height);
// allocate subregions : tiles
Tile* tiles = new Tile[TILES_PER_MAP * TILES_PER_MAP];
// Initialize per tile config.
rcConfig tileCfg;
memcpy(&tileCfg, &config, sizeof(rcConfig));
tileCfg.width = config.tileSize + config.borderSize * 2;
tileCfg.height = config.tileSize + config.borderSize * 2;
// build all tiles
for (int y = 0; y < TILES_PER_MAP; ++y)
{
for (int x = 0; x < TILES_PER_MAP; ++x)
{
Tile& tile = tiles[x + y * TILES_PER_MAP];
// Calculate the per tile bounding box.
tileCfg.bmin[0] = config.bmin[0] + (x * config.tileSize - config.borderSize) * config.cs;
tileCfg.bmin[2] = config.bmin[2] + (y * config.tileSize - config.borderSize) * config.cs;
tileCfg.bmax[0] = config.bmin[0] + ((x + 1) * config.tileSize + config.borderSize) * config.cs;
tileCfg.bmax[2] = config.bmin[2] + ((y + 1) * config.tileSize + config.borderSize) * config.cs;
float tbmin[2], tbmax[2];
tbmin[0] = tileCfg.bmin[0];
tbmin[1] = tileCfg.bmin[2];
tbmax[0] = tileCfg.bmax[0];
tbmax[1] = tileCfg.bmax[2];
// build heightfield
tile.solid = rcAllocHeightfield();
if (!tile.solid || !rcCreateHeightfield(m_rcContext, *tile.solid, tileCfg.width, tileCfg.height, tileCfg.bmin, tileCfg.bmax, tileCfg.cs, tileCfg.ch))
{
printf("%s Failed building heightfield! \n", tileString);
continue;
}
// mark all walkable tiles, both liquids and solids
unsigned char* triFlags = new unsigned char[tTriCount];
memset(triFlags, NAV_GROUND, tTriCount * sizeof(unsigned char));
rcClearUnwalkableTriangles(m_rcContext, tileCfg.walkableSlopeAngle, tVerts, tVertCount, tTris, tTriCount, triFlags);
rcRasterizeTriangles(m_rcContext, tVerts, tVertCount, tTris, triFlags, tTriCount, *tile.solid, config.walkableClimb);
delete [] triFlags;
rcFilterLowHangingWalkableObstacles(m_rcContext, config.walkableClimb, *tile.solid);
rcFilterLedgeSpans(m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid);
rcFilterWalkableLowHeightSpans(m_rcContext, tileCfg.walkableHeight, *tile.solid);
rcRasterizeTriangles(m_rcContext, lVerts, lVertCount, lTris, lTriFlags, lTriCount, *tile.solid, config.walkableClimb);
// compact heightfield spans
tile.chf = rcAllocCompactHeightfield();
if (!tile.chf || !rcBuildCompactHeightfield(m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid, *tile.chf))
{
printf("%s Failed compacting heightfield! \n", tileString);
continue;
}
// build polymesh intermediates
if (!rcErodeWalkableArea(m_rcContext, config.walkableRadius, *tile.chf))
{
printf("%s Failed eroding area! \n", tileString);
continue;
}
if (!rcBuildDistanceField(m_rcContext, *tile.chf))
{
printf("%s Failed building distance field! \n", tileString);
continue;
}
if (!rcBuildRegions(m_rcContext, *tile.chf, tileCfg.borderSize, tileCfg.minRegionArea, tileCfg.mergeRegionArea))
{
printf("%s Failed building regions! \n", tileString);
continue;
}
tile.cset = rcAllocContourSet();
if (!tile.cset || !rcBuildContours(m_rcContext, *tile.chf, tileCfg.maxSimplificationError, tileCfg.maxEdgeLen, *tile.cset))
{
printf("%s Failed building contours! \n", tileString);
continue;
}
// build polymesh
tile.pmesh = rcAllocPolyMesh();
if (!tile.pmesh || !rcBuildPolyMesh(m_rcContext, *tile.cset, tileCfg.maxVertsPerPoly, *tile.pmesh))
{
printf("%s Failed building polymesh! \n", tileString);
continue;
}
tile.dmesh = rcAllocPolyMeshDetail();
if (!tile.dmesh || !rcBuildPolyMeshDetail(m_rcContext, *tile.pmesh, *tile.chf, tileCfg.detailSampleDist, tileCfg .detailSampleMaxError, *tile.dmesh))
{
printf("%s Failed building polymesh detail! \n", tileString);
continue;
}
// free those up
// we may want to keep them in the future for debug
// but right now, we don't have the code to merge them
rcFreeHeightField(tile.solid);
tile.solid = NULL;
rcFreeCompactHeightfield(tile.chf);
tile.chf = NULL;
rcFreeContourSet(tile.cset);
tile.cset = NULL;
}
}
// merge per tile poly and detail meshes
rcPolyMesh** pmmerge = new rcPolyMesh*[TILES_PER_MAP * TILES_PER_MAP];
if (!pmmerge)
{
printf("%s alloc pmmerge FAILED! \n", tileString);
delete [] tiles;
return;
}
rcPolyMeshDetail** dmmerge = new rcPolyMeshDetail*[TILES_PER_MAP * TILES_PER_MAP];
if (!dmmerge)
{
printf("%s alloc dmmerge FAILED! \n", tileString);
delete [] pmmerge;
delete [] tiles;
return;
}
int nmerge = 0;
for (int y = 0; y < TILES_PER_MAP; ++y)
{
for (int x = 0; x < TILES_PER_MAP; ++x)
{
Tile& tile = tiles[x + y * TILES_PER_MAP];
if (tile.pmesh)
{
pmmerge[nmerge] = tile.pmesh;
dmmerge[nmerge] = tile.dmesh;
nmerge++;
}
}
}
iv.polyMesh = rcAllocPolyMesh();
if (!iv.polyMesh)
{
printf("%s alloc iv.polyMesh FAILED! \n", tileString);
delete [] pmmerge;
delete [] dmmerge;
delete [] tiles;
return;
}
rcMergePolyMeshes(m_rcContext, pmmerge, nmerge, *iv.polyMesh);
iv.polyMeshDetail = rcAllocPolyMeshDetail();
if (!iv.polyMeshDetail)
{
printf("%s alloc m_dmesh FAILED! \n", tileString);
delete [] pmmerge;
delete [] dmmerge;
delete [] tiles;
return;
}
rcMergePolyMeshDetails(m_rcContext, dmmerge, nmerge, *iv.polyMeshDetail);
// free things up
delete [] pmmerge;
delete [] dmmerge;
delete [] tiles;
#if defined (CATA)
// remove padding for extraction
for (int i = 0; i < iv.polyMesh->nverts; ++i)
{
unsigned short* v = &iv.polyMesh->verts[i * 3];
v[0] -= (unsigned short)config.borderSize;
v[2] -= (unsigned short)config.borderSize;
}
#endif
// set polygons as walkable
// TODO: special flags for DYNAMIC polygons, ie surfaces that can be turned on and off
for (int i = 0; i < iv.polyMesh->npolys; ++i)
if (iv.polyMesh->areas[i] & RC_WALKABLE_AREA)
{
iv.polyMesh->flags[i] = iv.polyMesh->areas[i];
}
// setup mesh parameters
dtNavMeshCreateParams params;
memset(&params, 0, sizeof(params));
params.verts = iv.polyMesh->verts;
params.vertCount = iv.polyMesh->nverts;
params.polys = iv.polyMesh->polys;
params.polyAreas = iv.polyMesh->areas;
params.polyFlags = iv.polyMesh->flags;
params.polyCount = iv.polyMesh->npolys;
params.nvp = iv.polyMesh->nvp;
params.detailMeshes = iv.polyMeshDetail->meshes;
params.detailVerts = iv.polyMeshDetail->verts;
params.detailVertsCount = iv.polyMeshDetail->nverts;
params.detailTris = iv.polyMeshDetail->tris;
params.detailTriCount = iv.polyMeshDetail->ntris;
params.offMeshConVerts = meshData.offMeshConnections.getCArray();
params.offMeshConCount = meshData.offMeshConnections.size() / 6;
params.offMeshConRad = meshData.offMeshConnectionRads.getCArray();
params.offMeshConDir = meshData.offMeshConnectionDirs.getCArray();
params.offMeshConAreas = meshData.offMeshConnectionsAreas.getCArray();
params.offMeshConFlags = meshData.offMeshConnectionsFlags.getCArray();
params.walkableHeight = BASE_UNIT_DIM * config.walkableHeight; // agent height
params.walkableRadius = BASE_UNIT_DIM * config.walkableRadius; // agent radius
params.walkableClimb = BASE_UNIT_DIM * config.walkableClimb; // keep less that walkableHeight (aka agent height)!
params.tileX = (((bmin[0] + bmax[0]) / 2) - navMesh->getParams()->orig[0]) / GRID_SIZE;
params.tileY = (((bmin[2] + bmax[2]) / 2) - navMesh->getParams()->orig[2]) / GRID_SIZE;
rcVcopy(params.bmin, bmin);
rcVcopy(params.bmax, bmax);
params.cs = config.cs;
params.ch = config.ch;
params.tileLayer = 0;
params.buildBvTree = true;
// will hold final navmesh
unsigned char* navData = NULL;
int navDataSize = 0;
do
{
// these values are checked within dtCreateNavMeshData - handle them here
// so we have a clear error message
if (params.nvp > DT_VERTS_PER_POLYGON)
{
printf("%s Invalid verts-per-polygon value! \n", tileString);
continue;
}
if (params.vertCount >= 0xffff)
{
printf("%s Too many vertices! \n", tileString);
continue;
}
if (!params.vertCount || !params.verts)
{
// occurs mostly when adjacent tiles have models
// loaded but those models don't span into this tile
// message is an annoyance
//printf("%sNo vertices to build tile! \n", tileString);
continue;
}
if (!params.polyCount || !params.polys ||
TILES_PER_MAP * TILES_PER_MAP == params.polyCount)
{
// we have flat tiles with no actual geometry - don't build those, its useless
// keep in mind that we do output those into debug info
// drop tiles with only exact count - some tiles may have geometry while having less tiles
printf(" No polygons to build on tile - %s \n", tileString);
continue;
}
if (!params.detailMeshes || !params.detailVerts || !params.detailTris)
{
printf(" No detail mesh to build tile - %s \n", tileString);
continue;
}
if (!dtCreateNavMeshData(&params, &navData, &navDataSize))
{
printf(" Failed building navmesh tile - %s \n", tileString);
continue;
}
dtTileRef tileRef = 0;
// DT_TILE_FREE_DATA tells detour to unallocate memory when the tile
// is removed via removeTile()
dtStatus dtResult = navMesh->addTile(navData, navDataSize, DT_TILE_FREE_DATA, 0, &tileRef);
if (!tileRef || dtStatusFailed(dtResult))
{
printf(" Failed adding tile %s to navmesh ! \n", tileString);
continue;
}
// file output
char fileName[255];
sprintf(fileName, "mmaps/%03u%02i%02i.mmtile", mapID, tileY, tileX);
FILE* file = fopen(fileName, "wb");
if (!file)
{
char message[1024];
sprintf(message, "Failed to open %s for writing!\n", fileName);
perror(message);
navMesh->removeTile(tileRef, NULL, NULL);
continue;
}
// write header
MmapTileHeader header;
header.usesLiquids = m_terrainBuilder->usesLiquids();
header.size = uint32(navDataSize);
fwrite(&header, sizeof(MmapTileHeader), 1, file);
// write data
fwrite(navData, sizeof(unsigned char), navDataSize, file);
fclose(file);
// now that tile is written to disk, we can unload it
navMesh->removeTile(tileRef, NULL, NULL);
}
while (0);
if (m_debugOutput)
{
// restore padding so that the debug visualization is correct
for (int i = 0; i < iv.polyMesh->nverts; ++i)
{
unsigned short* v = &iv.polyMesh->verts[i * 3];
v[0] += (unsigned short)config.borderSize;
v[2] += (unsigned short)config.borderSize;
}
iv.generateObjFile(mapID, tileX, tileY, meshData);
iv.writeIV(mapID, tileX, tileY);
}
}
/**************************************************************************/
void MapBuilder::getTileBounds(int tileX, int tileY, float* verts, int vertCount, float* bmin, float* bmax)
{
// this is for elevation
if (verts && vertCount)
{
rcCalcBounds(verts, vertCount, bmin, bmax);
}
else
{
bmin[1] = FLT_MIN;
bmax[1] = FLT_MAX;
}
// this is for width and depth
bmax[0] = (32 - int(tileX)) * GRID_SIZE;
bmax[2] = (32 - int(tileY)) * GRID_SIZE;
bmin[0] = bmax[0] - GRID_SIZE;
bmin[2] = bmax[2] - GRID_SIZE;
}
/**************************************************************************/
bool MapBuilder::shouldSkipTile(int mapID, int tileX, int tileY)
{
char fileName[255];
sprintf(fileName, "mmaps/%03u%02i%02i.mmtile", mapID, tileY, tileX);
FILE* file = fopen(fileName, "rb");
if (!file)
{
return false;
}
MmapTileHeader header;
size_t file_read = fread(&header, sizeof(MmapTileHeader), 1, file);
fclose(file);
if (header.mmapMagic != MMAP_MAGIC || header.dtVersion != DT_NAVMESH_VERSION || file_read <= 0)
{
return false;
}
if (header.mmapVersion != MMAP_VERSION)
{
return false;
}
return true;
}
}

View file

@ -0,0 +1,253 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#ifndef MANGOS_H_MAP_BUILDER
#define MANGOS_H_MAP_BUILDER
#include <vector>
#include <set>
#include <map>
#include <Recast.h>
#include <DetourNavMesh.h>
#include "TerrainBuilder.h"
#include "IntermediateValues.h"
#include "IVMapManager.h"
#include "WorldModel.h"
#include "TileThreadPool.h"
using namespace std;
using namespace VMAP;
// G3D namespace typedefs conflicts with ACE typedefs
namespace MMAP
{
/**
* @brief
*
*/
typedef map<uint32, set<uint32>*> TileList;
/**
* @brief
*
*/
struct Tile
{
/**
* @brief
*
*/
Tile() : chf(NULL), solid(NULL), cset(NULL), pmesh(NULL), dmesh(NULL) {}
/**
* @brief
*
*/
~Tile()
{
rcFreeCompactHeightfield(chf);
rcFreeContourSet(cset);
rcFreeHeightField(solid);
rcFreePolyMesh(pmesh);
rcFreePolyMeshDetail(dmesh);
}
rcCompactHeightfield* chf; /**< TODO */
rcHeightfield* solid; /**< TODO */
rcContourSet* cset; /**< TODO */
rcPolyMesh* pmesh; /**< TODO */
rcPolyMeshDetail* dmesh; /**< TODO */
};
/**
* @brief
*
*/
class MapBuilder
{
public:
/**
* @brief
*
* @param maxWalkableAngle
* @param skipLiquid
* @param skipContinents
* @param skipJunkMaps
* @param skipBattlegrounds
* @param debugOutput
* @param bigBaseUnit
* @param offMeshFilePath
*/
MapBuilder(const char* magic,
float maxWalkableAngle = 60.f,
bool skipLiquid = false,
bool skipContinents = false,
bool skipJunkMaps = true,
bool skipBattlegrounds = false,
bool debugOutput = false,
bool bigBaseUnit = false,
const char* offMeshFilePath = NULL);
/**
* @brief
*
*/
~MapBuilder();
/**
* @brief builds all mmap tiles for the specified map id (ignores skip settings)
*
* @param mapID
*/
void buildMap(int mapID, bool standAlone = true);
/**
* @brief builds an mmap tile for the specified map and its mesh
*
* @param mapID
* @param tileX
* @param tileY
*/
void buildSingleTile(int mapID, int tileX, int tileY);
/**
* @brief builds list of maps, then builds all of mmap tiles (based on the skip settings)
*
*/
void buildAllMaps();
int activate(int num_threads);
bool activated() const { return m_poolActivated; }
/**
* @brief
*
* @param mapID
* @param tileX
* @param tileY
* @param navMesh
*/
void buildTile(int mapID, int tileX, int tileY, dtNavMesh* navMesh);
private:
/**
* @brief detect maps and tiles
*
*/
void discoverTiles();
/**
* @brief
*
* @param mapID
* @return set<uint32>
*/
set<uint32>* getTileList(int mapID);
/**
* @brief
*
* @param mapID
* @param navMesh
*/
void buildNavMesh(int mapID, dtNavMesh*& navMesh, dtNavMeshParams*& navMeshParams);
/**
* @brief move map building
*
* @param mapID
* @param tileX
* @param tileY
* @param meshData
* @param bmin[]
* @param bmax[]
* @param navMesh
*/
void buildMoveMapTile(int mapID,
int tileX,
int tileY,
MeshData& meshData,
float bmin[3],
float bmax[3],
dtNavMesh* navMesh);
/**
* @brief
*
* @param tileX
* @param tileY
* @param verts
* @param vertCount
* @param bmin
* @param bmax
*/
void getTileBounds(int tileX, int tileY,
float* verts, int vertCount,
float* bmin, float* bmax);
/**
* @brief
*
* @param mapID
* @param minX
* @param minY
* @param maxX
* @param maxY
*/
void getGridBounds(int mapID, uint32& minX, uint32& minY, uint32& maxX, uint32& maxY);
/**
* @brief
*
* @param mapID
* @param tileX
* @param tileY
* @return bool
*/
bool shouldSkipTile(int mapID, int tileX, int tileY);
TerrainBuilder* m_terrainBuilder; /**< TODO */
TileList m_tiles; /**< TODO */
bool m_debugOutput; /**< TODO */
const char* m_offMeshFilePath; /**< TODO */
bool m_skipContinents; /**< TODO */
bool m_skipJunkMaps; /**< TODO */
bool m_skipBattlegrounds; /**< TODO */
float m_maxWalkableAngle; /**< TODO */
bool m_bigBaseUnit; /**< TODO */
char const* m_magic;
int m_numThreads;
TileThreadPool* m_threadPool;
bool m_poolActivated;
rcContext* m_rcContext; /**< build performance - not really used for now */
};
}
#endif

View file

@ -0,0 +1,25 @@
/*
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
IDI_APPICON ICON DISCARDABLE "../tools.ico"

View file

@ -0,0 +1,64 @@
mmap generator
==============
The *movement map generator* will extract pathfinding information from the
game client in order to enable the *mangos* server to let creatures walk
on the terrain as naturally as possible.
Please note that due to the complex nature of the process, the extraction and
generation process may require a high amount of computing power any time.
Depending on your system, you should except ranges from four hours up to a full
day for a full map generation of all in-game zones. Reducing the amount of zones
generated will also reduce the time needed to wait.
Requirements
------------
You will need a working installation of the [World of Warcraft][1] client patched
to version 1.12.x.
Usage
-----
The `mmap-generator` command can be used with a set of parameters to fine-tune the
process of movement map generation.
* `--offMeshInput [file.*]`: Path to file containing off mesh connections data,
with a single mesh connection per line. Format must be:
`map_id tile_x,tile_y (start_x start_y start_z) (end_x end_y end_z) size //optional comments`
* `--silent`: Make us script friendly. Do not wait for user input on error or
completion.
* `--bigBaseUnit [true|false]`: Generate tile/map using bigger basic unit. Use this
option only if you have unexpected gaps. If set to `false`, we will use normal
metrics.
* `--maxAngle [#]`: the maximum walkable inclination angle. By default this is set
to `60`. Float values between 45 and 90 degrees are allowed.
* `--skipLiquid`: skip liquid data for maps. Skipping liquid maps is disabled by
default.
* `--skipContinents [true|false]`: skip building continents. Disabled by default.
Available continents are `0` (*Eastern Kingdoms*), and `1` (*Kalimdor*).
* `--skipJunkMaps [true|false]`: skip junk maps, including some unused maps,
transport maps, and some other maps. Junk maps are skipped by default.
* `--skipBattlegrounds [true|false]`: skip battleground maps. By default battleground
maps are included.
* `--debugOutput [true|false]`: create debugging files for use with RecastDemo. If you
are only creating movement maps for use with MaNGOS, you do not need debugging
files built. By default, debugging files are not created.
* `--tile [#,#]`: Build the specified tile seperate number with a comma ','.
Must specify a map number (see below). If this option is not used, all tiles are
built. If only one number is specified, builds the map specified by it.
This command will build the map regardless of --skip* option settings. If you do
not specify a map number, builds all maps that pass the filters specified by
`--skip*` options.
* `-h`, `--help`: show usage information.
Examples
--------
* `mmap-generator`: builds maps using the default settings (see above for defaults)
* `mmap-generator --skipContinents true`: builds the default maps, except continents
* `mmap-generator 0`: builds all tiles of map 0
* `mmap-generator 0 --tile 34,46`: builds only tile 34,46 of map 0 (this is the southern face of blackrock mountain)
[1]: http://blizzard.com/games/wow/ "World of Warcraft"

View file

@ -0,0 +1,997 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#include "TerrainBuilder.h"
#include "MMapCommon.h"
#include "MapBuilder.h"
#include "VMapManager2.h"
#include "MapTree.h"
#include "ModelInstance.h"
namespace MMAP
{
TerrainBuilder::TerrainBuilder(bool skipLiquid) : m_skipLiquid(skipLiquid) { }
TerrainBuilder::~TerrainBuilder() { }
/**************************************************************************/
void TerrainBuilder::getLoopVars(Spot portion, int& loopStart, int& loopEnd, int& loopInc)
{
switch (portion)
{
case ENTIRE:
loopStart = 0;
loopEnd = V8_SIZE_SQ;
loopInc = 1;
break;
case TOP:
loopStart = 0;
loopEnd = V8_SIZE;
loopInc = 1;
break;
case LEFT:
loopStart = 0;
loopEnd = V8_SIZE_SQ - V8_SIZE + 1;
loopInc = V8_SIZE;
break;
case RIGHT:
loopStart = V8_SIZE - 1;
loopEnd = V8_SIZE_SQ;
loopInc = V8_SIZE;
break;
case BOTTOM:
loopStart = V8_SIZE_SQ - V8_SIZE;
loopEnd = V8_SIZE_SQ;
loopInc = 1;
break;
}
}
/**************************************************************************/
void TerrainBuilder::loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData,char const* MAP_VERSION_MAGIC)
{
if (loadMap(mapID, tileX, tileY, meshData, ENTIRE, MAP_VERSION_MAGIC))
{
loadMap(mapID, tileX + 1, tileY, meshData, LEFT, MAP_VERSION_MAGIC);
loadMap(mapID, tileX - 1, tileY, meshData, RIGHT, MAP_VERSION_MAGIC);
loadMap(mapID, tileX, tileY + 1, meshData, TOP, MAP_VERSION_MAGIC);
loadMap(mapID, tileX, tileY - 1, meshData, BOTTOM, MAP_VERSION_MAGIC);
}
}
/**************************************************************************/
bool TerrainBuilder::loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData, Spot portion,char const* MAP_VERSION_MAGIC)
{
char mapFileName[255];
sprintf(mapFileName, "maps/%03u%02u%02u.map", mapID, tileY, tileX);
FILE* mapFile = fopen(mapFileName, "rb");
if (!mapFile)
{
return false;
}
GridMapFileHeader fheader;
size_t file_read = fread(&fheader, sizeof(GridMapFileHeader), 1, mapFile);
if (file_read <= 0)
{
fclose(mapFile);
printf("Could not read map data from %s.\n", mapFileName);
return false;
}
if (fheader.versionMagic != *((uint32 const*)(MAP_VERSION_MAGIC)))
{
fclose(mapFile);
printf("%s is the wrong version, please extract new .map files\n", mapFileName);
return false;
}
GridMapHeightHeader hheader;
fseek(mapFile, fheader.heightMapOffset, SEEK_SET);
file_read = fread(&hheader, sizeof(GridMapHeightHeader), 1, mapFile);
if (file_read <= 0)
{
fclose(mapFile);
printf("Could not read map data from %s.\n", mapFileName);
return false;
}
bool haveTerrain = !(hheader.flags & MAP_HEIGHT_NO_HEIGHT);
bool haveLiquid = fheader.liquidMapOffset && !m_skipLiquid;
// no data in this map file
if (!haveTerrain && !haveLiquid)
{
fclose(mapFile);
return false;
}
// data used later
uint16 holes[16][16];
memset(holes, 0, sizeof(holes));
uint8 liquid_type[16][16];
memset(liquid_type, 0, sizeof(liquid_type));
G3D::Array<int> ltriangles;
G3D::Array<int> ttriangles;
// terrain data
if (haveTerrain)
{
int i;
float heightMultiplier;
float V9[V9_SIZE_SQ], V8[V8_SIZE_SQ];
if (hheader.flags & MAP_HEIGHT_AS_INT8)
{
uint8 v9[V9_SIZE_SQ];
uint8 v8[V8_SIZE_SQ];
file_read = fread(v9, sizeof(uint8), V9_SIZE_SQ, mapFile);
if (file_read <= 0)
{
fclose(mapFile);
printf("Could not read map data from %s.\n", mapFileName);
return false;
}
file_read = fread(v8, sizeof(uint8), V8_SIZE_SQ, mapFile);
if (file_read <= 0)
{
fclose(mapFile);
printf("Could not read map data from %s.\n", mapFileName);
return false;
}
heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 255;
for (i = 0; i < V9_SIZE_SQ; ++i)
{
V9[i] = (float)v9[i] * heightMultiplier + hheader.gridHeight;
}
for (i = 0; i < V8_SIZE_SQ; ++i)
{
V8[i] = (float)v8[i] * heightMultiplier + hheader.gridHeight;
}
}
else if (hheader.flags & MAP_HEIGHT_AS_INT16)
{
uint16 v9[V9_SIZE_SQ];
uint16 v8[V8_SIZE_SQ];
file_read = fread(v9, sizeof(uint16), V9_SIZE_SQ, mapFile);
if (file_read <= 0)
{
fclose(mapFile);
printf("Could not read map data from %s.\n", mapFileName);
return false;
}
file_read = fread(v8, sizeof(uint16), V8_SIZE_SQ, mapFile);
if (file_read <= 0)
{
fclose(mapFile);
printf("Could not read map data from %s.\n", mapFileName);
return false;
}
heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 65535;
for (i = 0; i < V9_SIZE_SQ; ++i)
{
V9[i] = (float)v9[i] * heightMultiplier + hheader.gridHeight;
}
for (i = 0; i < V8_SIZE_SQ; ++i)
{
V8[i] = (float)v8[i] * heightMultiplier + hheader.gridHeight;
}
}
else
{
file_read = fread(V9, sizeof(float), V9_SIZE_SQ, mapFile);
if (file_read <= 0)
{
fclose(mapFile);
printf("Could not read map data from %s.\n", mapFileName);
return false;
}
file_read = fread(V8, sizeof(float), V8_SIZE_SQ, mapFile);
if (file_read <= 0)
{
fclose(mapFile);
printf("Could not read map data from %s.\n", mapFileName);
return false;
}
}
// hole data
memset(holes, 0, fheader.holesSize);
fseek(mapFile, fheader.holesOffset, SEEK_SET);
file_read = fread(holes, fheader.holesSize, 1, mapFile);
if (file_read <= 0)
{
fclose(mapFile);
printf("Could not read map data from %s.\n", mapFileName);
return false;
}
int count = meshData.solidVerts.size() / 3;
float xoffset = (float(tileX) - 32) * GRID_SIZE;
float yoffset = (float(tileY) - 32) * GRID_SIZE;
float coord[3];
for (i = 0; i < V9_SIZE_SQ; ++i)
{
getHeightCoord(i, GRID_V9, xoffset, yoffset, coord, V9);
meshData.solidVerts.append(coord[0]);
meshData.solidVerts.append(coord[2]);
meshData.solidVerts.append(coord[1]);
}
for (i = 0; i < V8_SIZE_SQ; ++i)
{
getHeightCoord(i, GRID_V8, xoffset, yoffset, coord, V8);
meshData.solidVerts.append(coord[0]);
meshData.solidVerts.append(coord[2]);
meshData.solidVerts.append(coord[1]);
}
int j, indices[3], loopStart, loopEnd, loopInc;
getLoopVars(portion, loopStart, loopEnd, loopInc);
for (i = loopStart; i < loopEnd; i += loopInc)
for (j = TOP; j <= BOTTOM; j += 1)
{
getHeightTriangle(i, Spot(j), indices);
ttriangles.append(indices[2] + count);
ttriangles.append(indices[1] + count);
ttriangles.append(indices[0] + count);
}
}
// liquid data
if (haveLiquid)
{
GridMapLiquidHeader lheader;
fseek(mapFile, fheader.liquidMapOffset, SEEK_SET);
file_read = fread(&lheader, sizeof(GridMapLiquidHeader), 1, mapFile);
if (file_read <= 0)
{
fclose(mapFile);
printf("Could not read map data from %s.\n", mapFileName);
return false;
}
float* liquid_map = NULL;
if (!(lheader.flags & MAP_LIQUID_NO_TYPE))
{
file_read = fread(liquid_type, sizeof(liquid_type), 1, mapFile);
if (file_read <= 0)
{
fclose(mapFile);
printf("Could not read map data from %s.\n", mapFileName);
return false;
}
}
if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT))
{
liquid_map = new float [lheader.width * lheader.height];
file_read = fread(liquid_map, sizeof(float), lheader.width * lheader.height, mapFile);
if (file_read <= 0)
{
fclose(mapFile);
delete [] liquid_map;
printf("Could not read map data from %s.\n", mapFileName);
return false;
}
}
if (liquid_type && liquid_map)
{
int count = meshData.liquidVerts.size() / 3;
float xoffset = (float(tileX) - 32) * GRID_SIZE;
float yoffset = (float(tileY) - 32) * GRID_SIZE;
float coord[3];
int row, col;
// generate coordinates
if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT))
{
int j = 0;
for (int i = 0; i < V9_SIZE_SQ; ++i)
{
row = i / V9_SIZE;
col = i % V9_SIZE;
if (row < lheader.offsetY || row >= lheader.offsetY + lheader.height ||
col < lheader.offsetX || col >= lheader.offsetX + lheader.width)
{
// dummy vert using invalid height
meshData.liquidVerts.append((xoffset + col * GRID_PART_SIZE) * -1, INVALID_MAP_LIQ_HEIGHT, (yoffset + row * GRID_PART_SIZE) * -1);
continue;
}
getLiquidCoord(i, j, xoffset, yoffset, coord, liquid_map);
meshData.liquidVerts.append(coord[0]);
meshData.liquidVerts.append(coord[2]);
meshData.liquidVerts.append(coord[1]);
j++;
}
}
else
{
for (int i = 0; i < V9_SIZE_SQ; ++i)
{
row = i / V9_SIZE;
col = i % V9_SIZE;
meshData.liquidVerts.append((xoffset + col * GRID_PART_SIZE) * -1, lheader.liquidLevel, (yoffset + row * GRID_PART_SIZE) * -1);
}
}
delete [] liquid_map;
int indices[3], loopStart, loopEnd, loopInc, triInc;
getLoopVars(portion, loopStart, loopEnd, loopInc);
triInc = BOTTOM - TOP;
// generate triangles
for (int i = loopStart; i < loopEnd; i += loopInc)
for (int j = TOP; j <= BOTTOM; j += triInc)
{
getHeightTriangle(i, Spot(j), indices, true);
ltriangles.append(indices[2] + count);
ltriangles.append(indices[1] + count);
ltriangles.append(indices[0] + count);
}
}
}
fclose(mapFile);
// now that we have gathered the data, we can figure out which parts to keep:
// liquid above ground, ground above liquid
int loopStart, loopEnd, loopInc, tTriCount = 4;
bool useTerrain, useLiquid;
float* lverts = meshData.liquidVerts.getCArray();
int* ltris = ltriangles.getCArray();
float* tverts = meshData.solidVerts.getCArray();
int* ttris = ttriangles.getCArray();
if (ltriangles.size() + ttriangles.size() == 0)
{
return false;
}
// make a copy of liquid vertices
// used to pad right-bottom frame due to lost vertex data at extraction
float* lverts_copy = NULL;
if (meshData.liquidVerts.size())
{
lverts_copy = new float[meshData.liquidVerts.size()];
memcpy(lverts_copy, lverts, sizeof(float)*meshData.liquidVerts.size());
}
getLoopVars(portion, loopStart, loopEnd, loopInc);
for (int i = loopStart; i < loopEnd; i += loopInc)
{
for (int j = 0; j < 2; ++j)
{
// default is true, will change to false if needed
useTerrain = true;
useLiquid = true;
uint8 liquidType = MAP_LIQUID_TYPE_NO_WATER;
// if there is no liquid, don't use liquid
if (!liquid_type || !meshData.liquidVerts.size() || !ltriangles.size())
{
useLiquid = false;
}
else
{
liquidType = getLiquidType(i, liquid_type);
switch (liquidType)
{
default:
useLiquid = false;
break;
case MAP_LIQUID_TYPE_WATER:
case MAP_LIQUID_TYPE_OCEAN:
// merge different types of water
liquidType = NAV_WATER;
break;
case MAP_LIQUID_TYPE_MAGMA:
liquidType = NAV_MAGMA;
break;
case MAP_LIQUID_TYPE_SLIME:
liquidType = NAV_SLIME;
break;
case MAP_LIQUID_TYPE_DARK_WATER:
// players should not be here, so logically neither should creatures
useTerrain = false;
useLiquid = false;
break;
}
}
// if there is no terrain, don't use terrain
if (!ttriangles.size())
{
useTerrain = false;
}
// while extracting ADT data we are losing right-bottom vertices
// this code adds fair approximation of lost data
if (useLiquid)
{
float quadHeight = 0;
uint32 validCount = 0;
for (uint32 idx = 0; idx < 3; idx++)
{
float h = lverts_copy[ltris[idx] * 3 + 1];
if (h != INVALID_MAP_LIQ_HEIGHT && h < INVALID_MAP_LIQ_HEIGHT_MAX)
{
quadHeight += h;
validCount++;
}
}
// update vertex height data
if (validCount > 0 && validCount < 3)
{
quadHeight /= validCount;
for (uint32 idx = 0; idx < 3; idx++)
{
float h = lverts[ltris[idx] * 3 + 1];
if (h == INVALID_MAP_LIQ_HEIGHT || h > INVALID_MAP_LIQ_HEIGHT_MAX)
{
lverts[ltris[idx] * 3 + 1] = quadHeight;
}
}
}
// no valid vertexes - don't use this poly at all
if (validCount == 0)
{
useLiquid = false;
}
}
// if there is a hole here, don't use the terrain
if (useTerrain)
{
useTerrain = !isHole(i, holes);
}
// we use only one terrain kind per quad - pick higher one
if (useTerrain && useLiquid)
{
float minLLevel = INVALID_MAP_LIQ_HEIGHT_MAX;
float maxLLevel = INVALID_MAP_LIQ_HEIGHT;
for (uint32 x = 0; x < 3; x++)
{
float h = lverts[ltris[x] * 3 + 1];
if (minLLevel > h)
{
minLLevel = h;
}
if (maxLLevel < h)
{
maxLLevel = h;
}
}
float maxTLevel = INVALID_MAP_LIQ_HEIGHT;
float minTLevel = INVALID_MAP_LIQ_HEIGHT_MAX;
for (uint32 x = 0; x < 6; x++)
{
float h = tverts[ttris[x] * 3 + 1];
if (maxTLevel < h)
{
maxTLevel = h;
}
if (minTLevel > h)
{
minTLevel = h;
}
}
// terrain under the liquid?
if (minLLevel > maxTLevel)
{
useTerrain = false;
}
//liquid under the terrain?
if (minTLevel > maxLLevel)
{
useLiquid = false;
}
}
// store the result
if (useLiquid)
{
meshData.liquidType.append(liquidType);
for (int k = 0; k < 3; ++k)
{
meshData.liquidTris.append(ltris[k]);
}
}
if (useTerrain)
for (int k = 0; k < 3 * tTriCount / 2; ++k)
{
meshData.solidTris.append(ttris[k]);
}
// advance to next set of triangles
ltris += 3;
ttris += 3 * tTriCount / 2;
}
}
if (lverts_copy)
{
delete [] lverts_copy;
}
return meshData.solidTris.size() || meshData.liquidTris.size();
}
/**************************************************************************/
void TerrainBuilder::getHeightCoord(int index, Grid grid, float xOffset, float yOffset, float* coord, float* v)
{
// wow coords: x, y, height
// coord is mirroed about the horizontal axes
switch (grid)
{
case GRID_V9:
coord[0] = (xOffset + index % (V9_SIZE) * GRID_PART_SIZE) * -1.f;
coord[1] = (yOffset + (int)(index / (V9_SIZE)) * GRID_PART_SIZE) * -1.f;
coord[2] = v[index];
break;
case GRID_V8:
coord[0] = (xOffset + index % (V8_SIZE) * GRID_PART_SIZE + GRID_PART_SIZE / 2.f) * -1.f;
coord[1] = (yOffset + (int)(index / (V8_SIZE)) * GRID_PART_SIZE + GRID_PART_SIZE / 2.f) * -1.f;
coord[2] = v[index];
break;
}
}
/**************************************************************************/
void TerrainBuilder::getHeightTriangle(int square, Spot triangle, int* indices, bool liquid/* = false*/)
{
int rowOffset = square / V8_SIZE;
if (!liquid)
switch (triangle)
{
case TOP:
indices[0] = square + rowOffset; // 0-----1 .... 128
indices[1] = square + 1 + rowOffset; // |\ T /|
indices[2] = (V9_SIZE_SQ) + square; // | \ / |
break; // |L 0 R| .. 127
case LEFT: // | / \ |
indices[0] = square + rowOffset; // |/ B \|
indices[1] = (V9_SIZE_SQ) + square; // 129---130 ... 386
indices[2] = square + V9_SIZE + rowOffset; // |\ /|
break; // | \ / |
case RIGHT: // | 128 | .. 255
indices[0] = square + 1 + rowOffset; // | / \ |
indices[1] = square + V9_SIZE + 1 + rowOffset; // |/ \|
indices[2] = (V9_SIZE_SQ) + square; // 258---259 ... 515
break;
case BOTTOM:
indices[0] = (V9_SIZE_SQ) + square;
indices[1] = square + V9_SIZE + 1 + rowOffset;
indices[2] = square + V9_SIZE + rowOffset;
break;
default: break;
}
else
switch (triangle)
{
// 0-----1 .... 128
case TOP: // |\ |
indices[0] = square + rowOffset; // | \ T |
indices[1] = square + 1 + rowOffset; // | \ |
indices[2] = square + V9_SIZE + 1 + rowOffset; // | B \ |
break; // | \|
case BOTTOM: // 129---130 ... 386
indices[0] = square + rowOffset; // |\ |
indices[1] = square + V9_SIZE + 1 + rowOffset; // | \ |
indices[2] = square + V9_SIZE + rowOffset; // | \ |
break; // | \ |
default: break; // | \|
} // 258---259 ... 515
}
/**************************************************************************/
void TerrainBuilder::getLiquidCoord(int index, int index2, float xOffset, float yOffset, float* coord, float* v)
{
// wow coords: x, y, height
// coord is mirroed about the horizontal axes
coord[0] = (xOffset + index % (V9_SIZE) * GRID_PART_SIZE) * -1.f;
coord[1] = (yOffset + (int)(index / (V9_SIZE)) * GRID_PART_SIZE) * -1.f;
coord[2] = v[index2];
}
static uint16 holetab_h[4] = {0x1111, 0x2222, 0x4444, 0x8888};
static uint16 holetab_v[4] = {0x000F, 0x00F0, 0x0F00, 0xF000};
/**************************************************************************/
bool TerrainBuilder::isHole(int square, const uint16 holes[16][16])
{
int row = square / 128;
int col = square % 128;
int cellRow = row / 8; // 8 squares per cell
int cellCol = col / 8;
int holeRow = row % 8 / 2;
int holeCol = (square - (row * 128 + cellCol * 8)) / 2;
uint16 hole = holes[cellRow][cellCol];
return (hole & holetab_h[holeCol] & holetab_v[holeRow]) != 0;
}
/**************************************************************************/
uint8 TerrainBuilder::getLiquidType(int square, const uint8 liquid_type[16][16])
{
int row = square / 128;
int col = square % 128;
int cellRow = row / 8; // 8 squares per cell
int cellCol = col / 8;
return liquid_type[cellRow][cellCol];
}
/**************************************************************************/
bool TerrainBuilder::loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData)
{
IVMapManager* vmapManager = new VMapManager2();
VMAPLoadResult result = vmapManager->loadMap("vmaps", mapID, tileX, tileY);
bool retval = false;
do
{
if (result == VMAP_LOAD_RESULT_ERROR)
{
break;
}
InstanceTreeMap instanceTrees;
((VMapManager2*)vmapManager)->getInstanceMapTree(instanceTrees);
if (!instanceTrees[mapID])
{
break;
}
ModelInstance* models = NULL;
uint32 count = 0;
instanceTrees[mapID]->getModelInstances(models, count);
if (!models)
{
break;
}
for (uint32 i = 0; i < count; ++i)
{
ModelInstance instance = models[i];
// model instances exist in tree even though there are instances of that model in this tile
WorldModel* worldModel = instance.getWorldModel();
if (!worldModel)
{
continue;
}
// now we have a model to add to the meshdata
retval = true;
vector<GroupModel> groupModels;
worldModel->getGroupModels(groupModels);
// all M2s need to have triangle indices reversed
bool isM2 = instance.name.find(".m2") != instance.name.npos || instance.name.find(".M2") != instance.name.npos;
// transform data
float scale = instance.iScale;
G3D::Matrix3 rotation = G3D::Matrix3::fromEulerAnglesXYZ(G3D::pi() * instance.iRot.z / -180.f, G3D::pi() * instance.iRot.x / -180.f, G3D::pi() * instance.iRot.y / -180.f);
Vector3 position = instance.iPos;
position.x -= 32 * GRID_SIZE;
position.y -= 32 * GRID_SIZE;
for (vector<GroupModel>::iterator it = groupModels.begin(); it != groupModels.end(); ++it)
{
vector<Vector3> tempVertices;
vector<Vector3> transformedVertices;
vector<MeshTriangle> tempTriangles;
WmoLiquid* liquid = NULL;
(*it).getMeshData(tempVertices, tempTriangles, liquid);
// first handle collision mesh
transform(tempVertices, transformedVertices, scale, rotation, position);
int offset = meshData.solidVerts.size() / 3;
copyVertices(transformedVertices, meshData.solidVerts);
copyIndices(tempTriangles, meshData.solidTris, offset, isM2);
// now handle liquid data
if (liquid)
{
vector<Vector3> liqVerts;
vector<int> liqTris;
uint32 tilesX, tilesY, vertsX, vertsY;
Vector3 corner;
liquid->getPosInfo(tilesX, tilesY, corner);
vertsX = tilesX + 1;
vertsY = tilesY + 1;
uint8* flags = liquid->GetFlagsStorage();
float* data = liquid->GetHeightStorage();
uint8 type = NAV_EMPTY;
// convert liquid type to NavTerrain
switch (liquid->GetType())
{
case 0:
case 1:
type = NAV_WATER;
break;
case 2:
type = NAV_MAGMA;
break;
case 3:
type = NAV_SLIME;
break;
}
// indexing is weird...
// after a lot of trial and error, this is what works:
// vertex = y*vertsX+x
// tile = x*tilesY+y
// flag = y*tilesY+x
Vector3 vert;
for (uint32 x = 0; x < vertsX; ++x)
for (uint32 y = 0; y < vertsY; ++y)
{
vert = Vector3(corner.x + x * GRID_PART_SIZE, corner.y + y * GRID_PART_SIZE, data[y * vertsX + x]);
vert = vert * rotation * scale + position;
vert.x *= -1.f;
vert.y *= -1.f;
liqVerts.push_back(vert);
}
int idx1, idx2, idx3, idx4;
uint32 square;
for (uint32 x = 0; x < tilesX; ++x)
for (uint32 y = 0; y < tilesY; ++y)
if ((flags[x + y * tilesX] & 0x0f) != 0x0f)
{
square = x * tilesY + y;
idx1 = square + x;
idx2 = square + 1 + x;
idx3 = square + tilesY + 1 + 1 + x;
idx4 = square + tilesY + 1 + x;
// top triangle
liqTris.push_back(idx3);
liqTris.push_back(idx2);
liqTris.push_back(idx1);
// bottom triangle
liqTris.push_back(idx4);
liqTris.push_back(idx3);
liqTris.push_back(idx1);
}
uint32 liqOffset = meshData.liquidVerts.size() / 3;
for (uint32 i = 0; i < liqVerts.size(); ++i)
{
meshData.liquidVerts.append(liqVerts[i].y, liqVerts[i].z, liqVerts[i].x);
}
for (uint32 i = 0; i < liqTris.size() / 3; ++i)
{
meshData.liquidTris.append(liqTris[i * 3 + 1] + liqOffset, liqTris[i * 3 + 2] + liqOffset, liqTris[i * 3] + liqOffset);
meshData.liquidType.append(type);
}
}
}
}
}
while (false);
vmapManager->unloadMap(mapID, tileX, tileY);
delete vmapManager;
return retval;
}
/**************************************************************************/
void TerrainBuilder::transform(vector<Vector3>& source, vector<Vector3>& transformedVertices, float scale, G3D::Matrix3& rotation, Vector3& position)
{
for (vector<Vector3>::iterator it = source.begin(); it != source.end(); ++it)
{
// apply tranform, then mirror along the horizontal axes
Vector3 v((*it) * rotation * scale + position);
v.x *= -1.f;
v.y *= -1.f;
transformedVertices.push_back(v);
}
}
/**************************************************************************/
void TerrainBuilder::copyVertices(vector<Vector3>& source, G3D::Array<float>& dest)
{
for (vector<Vector3>::iterator it = source.begin(); it != source.end(); ++it)
{
dest.push_back((*it).y);
dest.push_back((*it).z);
dest.push_back((*it).x);
}
}
/**************************************************************************/
void TerrainBuilder::copyIndices(vector<MeshTriangle>& source, G3D::Array<int>& dest, int offset, bool flip)
{
if (flip)
{
for (vector<MeshTriangle>::iterator it = source.begin(); it != source.end(); ++it)
{
dest.push_back((*it).idx2 + offset);
dest.push_back((*it).idx1 + offset);
dest.push_back((*it).idx0 + offset);
}
}
else
{
for (vector<MeshTriangle>::iterator it = source.begin(); it != source.end(); ++it)
{
dest.push_back((*it).idx0 + offset);
dest.push_back((*it).idx1 + offset);
dest.push_back((*it).idx2 + offset);
}
}
}
/**************************************************************************/
void TerrainBuilder::copyIndices(G3D::Array<int>& source, G3D::Array<int>& dest, int offset)
{
int* src = source.getCArray();
for (int32 i = 0; i < source.size(); ++i)
{
dest.append(src[i] + offset);
}
}
/**************************************************************************/
void TerrainBuilder::cleanVertices(G3D::Array<float>& verts, G3D::Array<int>& tris)
{
map<int, int> vertMap;
int* t = tris.getCArray();
float* v = verts.getCArray();
// collect all the vertex indices from triangle
for (int i = 0; i < tris.size(); ++i)
{
if (vertMap.find(t[i]) != vertMap.end())
{
continue;
}
vertMap.insert(std::pair<int, int>(t[i], 0));
}
// collect the vertices
G3D::Array<float> cleanVerts;
int index, count = 0;
for (map<int, int>::iterator it = vertMap.begin(); it != vertMap.end(); ++it)
{
index = (*it).first;
(*it).second = count;
cleanVerts.append(v[index * 3], v[index * 3 + 1], v[index * 3 + 2]);
count++;
}
verts.fastClear();
verts.append(cleanVerts);
cleanVerts.clear();
// update triangles to use new indices
for (int i = 0; i < tris.size(); ++i)
{
map<int, int>::iterator it;
if ((it = vertMap.find(t[i])) == vertMap.end())
{
continue;
}
t[i] = (*it).second;
}
vertMap.clear();
}
/**************************************************************************/
void TerrainBuilder::loadOffMeshConnections(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData, const char* offMeshFilePath)
{
// no meshfile input given?
if (offMeshFilePath == NULL)
{
return;
}
FILE* fp = fopen(offMeshFilePath, "rb");
if (!fp)
{
printf(" loadOffMeshConnections:: input file %s not found!\n", offMeshFilePath);
return;
}
// pretty silly thing, as we parse entire file and load only the tile we need
// but we don't expect this file to be too large
char* buf = new char[512];
while (fgets(buf, 512, fp))
{
float p0[3], p1[3];
int mid, tx, ty;
float size;
if (10 != sscanf(buf, "%d %d,%d (%f %f %f) (%f %f %f) %f", &mid, &tx, &ty,
&p0[0], &p0[1], &p0[2], &p1[0], &p1[1], &p1[2], &size))
{ continue; }
if ((mapID == mid) && (tileX == tx) && (tileY == ty))
{
meshData.offMeshConnections.append(p0[1]);
meshData.offMeshConnections.append(p0[2]);
meshData.offMeshConnections.append(p0[0]);
meshData.offMeshConnections.append(p1[1]);
meshData.offMeshConnections.append(p1[2]);
meshData.offMeshConnections.append(p1[0]);
meshData.offMeshConnectionDirs.append(1); // 1 - both direction, 0 - one sided
meshData.offMeshConnectionRads.append(size); // agent size equivalent
// can be used same way as polygon flags
meshData.offMeshConnectionsAreas.append((unsigned char)0xFF);
meshData.offMeshConnectionsFlags.append((unsigned short)0xFF); // all movement masks can make this path
}
}
delete [] buf;
fclose(fp);
}
}

View file

@ -0,0 +1,302 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#ifndef MANGOS_H_MMAP_TERRAIN_BUILDER
#define MANGOS_H_MMAP_TERRAIN_BUILDER
#include "MMapCommon.h"
#include "MangosMap.h"
#include "MoveMapSharedDefines.h"
#include "WorldModel.h"
#include "G3D/Array.h"
#include "G3D/Vector3.h"
#include "G3D/Matrix3.h"
using namespace MaNGOS;
namespace MMAP
{
/**
* @brief
*
*/
enum Spot
{
TOP = 1,
RIGHT = 2,
LEFT = 3,
BOTTOM = 4,
ENTIRE = 5
};
/**
* @brief
*
*/
enum Grid
{
GRID_V8,
GRID_V9
};
static const int V9_SIZE = 129; /**< TODO */
static const int V9_SIZE_SQ = V9_SIZE* V9_SIZE; /**< TODO */
static const int V8_SIZE = 128; /**< TODO */
static const int V8_SIZE_SQ = V8_SIZE* V8_SIZE; /**< TODO */
static const float GRID_SIZE = 533.33333f; /**< TODO */
static const float GRID_PART_SIZE = GRID_SIZE / V8_SIZE; /**< TODO */
// see contrib/extractor/system.cpp, CONF_use_minHeight
static const float INVALID_MAP_LIQ_HEIGHT = -500.f; /**< TODO */
static const float INVALID_MAP_LIQ_HEIGHT_MAX = 5000.0f; /**< TODO */
// see following files:
// src/tools/map-extractor/system.cpp
// src/game/GridMap.cpp
/**
* @brief
*
*/
struct MeshData
{
G3D::Array<float> solidVerts; /**< TODO */
G3D::Array<int> solidTris; /**< TODO */
G3D::Array<float> liquidVerts; /**< TODO */
G3D::Array<int> liquidTris; /**< TODO */
G3D::Array<uint8> liquidType; /**< TODO */
// offmesh connection data
G3D::Array<float> offMeshConnections; // [p0y,p0z,p0x,p1y,p1z,p1x] - per connection /**< TODO */
G3D::Array<float> offMeshConnectionRads; /**< TODO */
G3D::Array<unsigned char> offMeshConnectionDirs; /**< TODO */
G3D::Array<unsigned char> offMeshConnectionsAreas; /**< TODO */
G3D::Array<unsigned short> offMeshConnectionsFlags; /**< TODO */
};
/**
* @brief
*
*/
class TerrainBuilder
{
public:
/**
* @brief
*
* @param skipLiquid
*/
TerrainBuilder(bool skipLiquid);
/**
* @brief
*
*/
~TerrainBuilder();
/**
* @brief
*
* @param mapID
* @param tileX
* @param tileY
* @param meshData
*/
void loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData, char const* MAP_VERSION_MAGIC);
/**
* @brief
*
* @param mapID
* @param tileX
* @param tileY
* @param meshData
* @return bool
*/
bool loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData);
/**
* @brief
*
* @param mapID
* @param tileX
* @param tileY
* @param meshData
* @param offMeshFilePath
*/
void loadOffMeshConnections(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData, const char* offMeshFilePath);
/**
* @brief
*
* @return bool
*/
bool usesLiquids() { return !m_skipLiquid; }
/**
* @brief vert and triangle methods
*
* @param original
* @param transformed
* @param scale
* @param rotation
* @param position
*/
static void transform(vector<G3D::Vector3>& original, vector<G3D::Vector3>& transformed,
float scale, G3D::Matrix3& rotation, G3D::Vector3& position);
/**
* @brief
*
* @param source
* @param dest
*/
static void copyVertices(vector<G3D::Vector3>& source, G3D::Array<float>& dest);
/**
* @brief
*
* @param source
* @param dest
* @param offest
* @param flip
*/
static void copyIndices(vector<VMAP::MeshTriangle>& source, G3D::Array<int>& dest, int offest, bool flip);
/**
* @brief
*
* @param src
* @param dest
* @param offset
*/
static void copyIndices(G3D::Array<int>& src, G3D::Array<int>& dest, int offset);
/**
* @brief
*
* @param verts
* @param tris
*/
static void cleanVertices(G3D::Array<float>& verts, G3D::Array<int>& tris);
private:
/**
* @brief Loads a portion of a map's terrain
*
* @param mapID
* @param tileX
* @param tileY
* @param meshData
* @param portion
* @return bool
*/
bool loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData, Spot portion, char const* MAP_VERSION_MAGIC);
/**
* @brief Sets loop variables for selecting only certain parts of a map's terrain
*
* @param portion
* @param loopStart
* @param loopEnd
* @param loopInc
*/
void getLoopVars(Spot portion, int& loopStart, int& loopEnd, int& loopInc);
bool m_skipLiquid; /**< Controls whether liquids are loaded */
/**
* @brief Load the map terrain from file
*
* @param mapID
* @param tileX
* @param tileY
* @param vertices
* @param triangles
* @param portion
* @return bool
*/
bool loadHeightMap(uint32 mapID, uint32 tileX, uint32 tileY, G3D::Array<float>& vertices, G3D::Array<int>& triangles, Spot portion);
/**
* @brief Get the vector coordinate for a specific position
*
* @param index
* @param grid
* @param xOffset
* @param yOffset
* @param coord
* @param v
*/
void getHeightCoord(int index, Grid grid, float xOffset, float yOffset, float* coord, float* v);
/**
* @brief Get the triangle's vector indices for a specific position
*
* @param square
* @param triangle
* @param indices
* @param liquid
*/
void getHeightTriangle(int square, Spot triangle, int* indices, bool liquid = false);
/**
* @brief Determines if the specific position's triangles should be rendered
*
* @param square
* @param holes[][]
* @return bool
*/
bool isHole(int square, const uint16 holes[16][16]);
/**
* @brief Get the liquid vector coordinate for a specific position
*
* @param index
* @param index2
* @param xOffset
* @param yOffset
* @param coord
* @param v
*/
void getLiquidCoord(int index, int index2, float xOffset, float yOffset, float* coord, float* v);
/**
* @brief Get the liquid type for a specific position
*
* @param square
* @param liquid_type[][]
* @return uint8
*/
uint8 getLiquidType(int square, const uint8 liquid_type[16][16]);
/**
* @brief hide parameterless constructor
*
*/
TerrainBuilder();
/**
* @brief hide copy constructor
*
* @param tb
*/
TerrainBuilder(const TerrainBuilder& tb);
};
}
#endif

View file

@ -0,0 +1,64 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#ifndef TILE_MESSAGE_BLOCK_H
#define TILE_MESSAGE_BLOCK_H
#include "ace/Message_Block.h"
#include "MapBuilder.h"
class TileBuilder
{
public:
TileBuilder(MMAP::MapBuilder* builder, int mapID, int tileX, int tileY, dtNavMesh* mesh) :
m_navMesh(mesh), m_tileY(tileY), m_tileX(tileX), m_mapID(mapID), m_builder(builder) {}
~TileBuilder() { delete m_navMesh; }
void Work() { m_builder->buildTile(m_mapID, m_tileX, m_tileY, m_navMesh); }
private:
int m_mapID;
int m_tileX;
int m_tileY;
MMAP::MapBuilder* m_builder;
dtNavMesh* m_navMesh;
};
class Tile_Message_Block : public ACE_Message_Block
{
public:
typedef ACE_Message_Block BASE;
Tile_Message_Block(TileBuilder* _tileBuilder, size_t size = 0) : BASE(size), m_tileBuilder(_tileBuilder) {}
~Tile_Message_Block() { delete m_tileBuilder; }
TileBuilder* GetTileBuilder() const { return m_tileBuilder; }
protected:
Tile_Message_Block& operator=(const Tile_Message_Block&);
Tile_Message_Block(const Tile_Message_Block&);
TileBuilder* m_tileBuilder;
};
#endif

View file

@ -0,0 +1,72 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#include "TileThreadPool.h"
#include "TileMsgBlock.h"
TileThreadPool::TileThreadPool(): m_barrier(0)
{
}
TileThreadPool::~TileThreadPool()
{
ACE_Message_Block *msg;
this->getq(msg);
msg->release();
delete m_barrier;
}
int TileThreadPool::start(int threads)
{
m_barrier = new ACE_Barrier(threads);
return this->activate(THR_NEW_LWP, threads);
}
int TileThreadPool::svc(void)
{
m_barrier->wait();
ACE_Message_Block *msg;
while (1)
{
if (this->getq(msg) == -1)
{
ACE_ERROR_RETURN((LM_ERROR, "%p\n", "getq"), -1);
}
if (msg->msg_type() == ACE_Message_Block::MB_HANGUP)
{
this->putq(msg);
break;
}
Tile_Message_Block *mb = (Tile_Message_Block*)msg;
mb->GetTileBuilder()->Work();
msg->release ();
}
return 0;
}

View file

@ -0,0 +1,44 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#ifndef TILE_THREAD_POOL_H
#define TILE_THREAD_POOL_H
#include "ace/Task.h"
#include "ace/Barrier.h"
class TileThreadPool : public ACE_Task<ACE_MT_SYNCH>
{
public:
TileThreadPool();
~TileThreadPool();
int start(int threads = 1);
virtual int svc(void);
protected:
ACE_Barrier *m_barrier;
};
#endif

View file

@ -0,0 +1,79 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#include <vector>
#include "MapTree.h"
#include "VMapManager2.h"
#include "WorldModel.h"
#include "ModelInstance.h"
using namespace std;
namespace VMAP
{
// Need direct access to encapsulated VMAP data, so we add functions for MMAP generator
// maybe add MapBuilder as friend to all of the below classes would be better?
// declared in src/shared/vmap/MapTree.h
void StaticMapTree::getModelInstances(ModelInstance*& models, uint32& count)
{
models = iTreeValues;
count = iNTreeValues;
}
// declared in src/shared/vmap/VMapManager2.h
void VMapManager2::getInstanceMapTree(InstanceTreeMap& instanceMapTree)
{
instanceMapTree = iInstanceMapTrees;
}
// declared in src/shared/vmap/WorldModel.h
void WorldModel::getGroupModels(vector<GroupModel>& groupModels)
{
groupModels = this->groupModels;
}
// declared in src/shared/vmap/WorldModel.h
void GroupModel::getMeshData(vector<Vector3>& vertices, vector<MeshTriangle>& triangles, WmoLiquid*& liquid)
{
vertices = this->vertices;
triangles = this->triangles;
liquid = iLiquid;
}
// declared in src/shared/vmap/ModelInstance.h
WorldModel* const ModelInstance::getWorldModel()
{
return iModel;
}
// declared in src/shared/vmap/WorldModel.h
void WmoLiquid::getPosInfo(uint32& tilesX, uint32& tilesY, Vector3& corner) const
{
tilesX = iTilesX;
tilesY = iTilesY;
corner = iCorner;
}
}

View file

@ -0,0 +1,443 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#include "ace/High_Res_Timer.h"
#include "MMapCommon.h"
#include "MapBuilder.h"
#include "ExtractorCommon.h"
using namespace MMAP;
bool checkDirectories(bool debugOutput)
{
vector<string> dirFiles;
if (getDirContents(dirFiles, "maps") == LISTFILE_DIRECTORY_NOT_FOUND || !dirFiles.size())
{
printf(" 'maps' directory is empty or does not exist\n");
return false;
}
dirFiles.clear();
if (getDirContents(dirFiles, "vmaps", "*.vmtree") == LISTFILE_DIRECTORY_NOT_FOUND || !dirFiles.size())
{
printf(" 'vmaps' directory is empty or does not exist\n");
return false;
}
dirFiles.clear();
if (getDirContents(dirFiles, "mmaps") == LISTFILE_DIRECTORY_NOT_FOUND)
{
//printf(" 'mmaps' directory does not exist\n");
CreateDir(std::string("mmaps"));
}
dirFiles.clear();
if (debugOutput)
{
if (getDirContents(dirFiles, "meshes") == LISTFILE_DIRECTORY_NOT_FOUND)
{
printf(" 'meshes' directory does not exist (no place to put debugOutput files)\n");
return false;
}
}
return true;
}
void printUsage(char* prg)
{
printf(" Usage: %s [OPTION]\n\n", prg);
printf(" Generate movement maps from extracted client maps.\n");
printf(" -h, --help show the usage\n");
printf(" --threads [#] number of worker threads (default 0).\n");
printf(" --maxAngle [#] max walkable inclination angle.\n");
printf(" --tile [#,#] build the specified tile.\n");
printf(" --skipLiquid [true|false] skip liquid data for maps.\n");
printf(" --skipContinents [true|false] skip continents.\n");
printf(" --skipJunkMaps [true|false] skip unused junk maps.\n");
printf(" --skipBattlegrounds [true|false] skip battleground maps.\n");
printf(" --bigBaseUnit [true|false] generate tile/map using bigger basic unit.\n");
printf(" --offMeshInput [file.*] path to file containing off mesh.\n");
printf(" connections data\n");
printf(" --debugOutput [true|false] create debugging files for use with\n");
printf(" RecastDemo.\n");
printf(" --silent No questions asked.\n");
printf(" [#] Build only the map specified by #.\n");
printf("\n");
printf(" Examples:\n");
printf(" - generate all movement maps:\n");
printf(" %s\n", prg);
printf(" - generate only map 0:\n");
printf(" %s 0\n", prg);
printf(" - build tile 34,46 of map 0:\n");
printf(" %s --tile 34,46\n", prg);
}
bool handleArgs(int argc, char** argv,
int& mapnum,
int& tileX,
int& tileY,
float& maxAngle,
bool& skipLiquid,
bool& skipContinents,
bool& skipJunkMaps,
bool& skipBattlegrounds,
bool& debugOutput,
bool& silent,
bool& bigBaseUnit,
int& num_threads,
char*& offMeshInputPath)
{
char* param = NULL;
for (int i = 1; i < argc; ++i)
{
if (strcmp(argv[i], "--maxAngle") == 0)
{
param = argv[++i];
if (!param)
{
return false;
}
float maxangle = atof(param);
if (maxangle <= 90.f && maxangle >= 45.f)
{
maxAngle = maxangle;
}
else
{
printf("invalid option for '--maxAngle', using default\n");
}
}
else if (strcmp(argv[i], "--threads") == 0)
{
param = argv[++i];
if (!param)
{
return false;
}
int nThreads = atoi(param);
if (nThreads)
{
num_threads = nThreads;
}
else
{
printf("invalid option for '--threads', using single threaded build\n");
}
}
else if (strcmp(argv[i], "--tile") == 0)
{
param = argv[++i];
if (!param)
{
return false;
}
char* stileX = strtok(param, ",");
char* stileY = strtok(NULL, ",");
int tilex = atoi(stileX);
int tiley = atoi(stileY);
if ((tilex > 0 && tilex < 64) || (tilex == 0 && strcmp(stileX, "0") == 0))
{
tileX = tilex;
}
if ((tiley > 0 && tiley < 64) || (tiley == 0 && strcmp(stileY, "0") == 0))
{
tileY = tiley;
}
if (tileX < 0 || tileY < 0)
{
printf("invalid tile coords.\n");
return false;
}
}
else if (strcmp(argv[i], "--skipLiquid") == 0)
{
param = argv[++i];
if (!param)
{
return false;
}
if (strcmp(param, "true") == 0)
{
skipLiquid = true;
}
else if (strcmp(param, "false") == 0)
{
skipLiquid = false;
}
else
{
printf("invalid option for '--skipLiquid', using default\n");
}
}
else if (strcmp(argv[i], "--skipContinents") == 0)
{
param = argv[++i];
if (!param)
{
return false;
}
if (strcmp(param, "true") == 0)
{
skipContinents = true;
}
else if (strcmp(param, "false") == 0)
{
skipContinents = false;
}
else
{
printf("invalid option for '--skipContinents', using default\n");
}
}
else if (strcmp(argv[i], "--skipJunkMaps") == 0)
{
param = argv[++i];
if (!param)
{
return false;
}
if (strcmp(param, "true") == 0)
{
skipJunkMaps = true;
}
else if (strcmp(param, "false") == 0)
{
skipJunkMaps = false;
}
else
{
printf("invalid option for '--skipJunkMaps', using default\n");
}
}
else if (strcmp(argv[i], "--skipBattlegrounds") == 0)
{
param = argv[++i];
if (!param)
{
return false;
}
if (strcmp(param, "true") == 0)
{
skipBattlegrounds = true;
}
else if (strcmp(param, "false") == 0)
{
skipBattlegrounds = false;
}
else
{
printf("invalid option for '--skipBattlegrounds', using default\n");
}
}
else if (strcmp(argv[i], "--debugOutput") == 0)
{
param = argv[++i];
if (!param)
{
return false;
}
if (strcmp(param, "true") == 0)
{
debugOutput = true;
}
else if (strcmp(param, "false") == 0)
{
debugOutput = false;
}
else
{
printf("invalid option for '--debugOutput', using default true\n");
}
}
else if (strcmp(argv[i], "--silent") == 0)
{
silent = true;
}
else if (strcmp(argv[i], "--bigBaseUnit") == 0)
{
param = argv[++i];
if (!param)
{
return false;
}
if (strcmp(param, "true") == 0)
{
bigBaseUnit = true;
}
else if (strcmp(param, "false") == 0)
{
bigBaseUnit = false;
}
else
{
printf("invalid option for '--bigBaseUnit', using default false\n");
}
}
else if (strcmp(argv[i], "--offMeshInput") == 0)
{
param = argv[++i];
if (!param)
{
return false;
}
offMeshInputPath = param;
}
else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0)
{
printUsage(argv[0]);
exit(1);
}
else
{
int map = atoi(argv[i]);
if (map > 0 || (map == 0 && (strcmp(argv[i], "0") == 0)))
{
mapnum = map;
}
else
{
printf("invalid map id\n");
return false;
}
}
}
return true;
}
int finish(const char* message, int returnValue)
{
printf("%s", message);
(void)getchar();
return returnValue;
}
int main(int argc, char** argv)
{
char map_magic[16];
int thisBuild = getBuildNumber();
int iCoreNumber = getCoreNumberFromBuild(thisBuild);
showBanner("Movement Map Generator", iCoreNumber);
setMapMagicVersion(iCoreNumber, map_magic);
showWebsiteBanner();
int mapnum = -1;
float maxAngle = 60.0f;
int tileX = -1, tileY = -1;
bool skipLiquid = false,
skipContinents = false,
skipJunkMaps = true,
skipBattlegrounds = false,
debugOutput = false,
silent = false,
bigBaseUnit = false;
int num_threads = 0;
char* offMeshInputPath = NULL;
bool validParam = handleArgs(argc, argv, mapnum,
tileX, tileY, maxAngle,
skipLiquid, skipContinents, skipJunkMaps, skipBattlegrounds,
debugOutput, silent, bigBaseUnit, num_threads, offMeshInputPath);
if (!validParam)
{
return silent ? -1 : finish(" You have specified invalid parameters (use -h for more help)", -1);
}
if (mapnum == -1 && debugOutput)
{
if (silent)
{
return -2;
}
printf(" You have specifed debug output, but didn't specify a map to generate.\n");
printf(" This will generate debug output for ALL maps.\n");
printf(" Are you sure you want to continue? (y/n) ");
if (getchar() != 'y')
{
return 0;
}
}
if (!checkDirectories(debugOutput))
{
return silent ? -3 : finish(" Press any key to close...", -3);
}
MapBuilder builder(map_magic, maxAngle, skipLiquid, skipContinents, skipJunkMaps,
skipBattlegrounds, debugOutput, bigBaseUnit, offMeshInputPath);
ACE_Time_Value elapsed;
ACE_High_Res_Timer timer;
timer.start();
if (tileX > -1 && tileY > -1 && mapnum >= 0)
{
builder.buildSingleTile(mapnum, tileX, tileY);
}
else
{
if (num_threads && builder.activate(num_threads)== -1)
{
if (!silent)
{
printf(" Thread initialization was not ok. The build is single threaded\n");
}
}
if (builder.activated())
{
printf(" Using %d thread(s) for building\n", num_threads);
}
if (mapnum >= 0)
{
builder.buildMap(uint32(mapnum), true);
}
else
{
builder.buildAllMaps();
}
}
timer.stop();
timer.elapsed_time(elapsed);
printf(" \n Total build time: %ld seconds\n\n", elapsed.sec());
return silent ? 1 : finish(" Movemap build is complete! Press enter to exit\n", 1);
}

View file

@ -0,0 +1,11 @@
## Unified Extractor Projects
The submodule contains the unified Extractors for MaNGOS.
This initially Supports:-
* MaNGOS Zero
* MaNGOS One
* MaNGOS Two

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,25 @@
/*
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
IDI_APPICON ICON DISCARDABLE "../tools.ico"

View file

@ -0,0 +1,605 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#include <stdio.h>
#include <deque>
#include <set>
#include <cstdlib>
#include <cstring>
#include "ExtractorCommon.h"
#ifdef WIN32
#include <direct.h>
#else
#include <sys/stat.h>
#endif
#include <fcntl.h>
#ifndef WIN32
#include <unistd.h>
/* This isn't the nicest way to do things..
* TODO: Fix this with snprintf instead and check that it still works
*/
#define sprintf_s sprintf
#endif
#if defined( __GNUC__ )
#define _open open
#define _close close
#ifndef O_BINARY
#define O_BINARY 0
#endif
#else
#include <io.h>
#endif
#ifdef O_LARGEFILE
#define OPEN_FLAGS (O_RDONLY | O_BINARY | O_LARGEFILE)
#else
#define OPEN_FLAGS (O_RDONLY | O_BINARY)
#endif
/**
* This function searches for and opens the WoW exe file, using all known variations on its spelling
*
* @RETURN pFile the pointer to the file, so that it can be worked on
*/
FILE* openWoWExe()
{
FILE *pFile;
const char* ExeFileName[] = { "WoW.exe", "Wow.exe", "wow.exe" ,"World of Warcraft.exe", "World of Warcraft.app/Contents/MacOS/World of Warcraft"};
int iExeSpelling = 5; ///> WoW.exe (Classic, CATA), Wow.exe (TBC, MoP, WoD), wow.exe (WOTLK) and a variant
/// loop through all possible file names
for (int iFileCount = 0; iFileCount < iExeSpelling; iFileCount++)
{
#ifdef WIN32
if (fopen_s(&pFile, ExeFileName[iFileCount], "rb") == 0)
return pFile; ///< successfully located the WoW executable
#else
if ((pFile = fopen(ExeFileName[iFileCount], "rb")))
return pFile; ///< successfully located the WoW executable
#endif
}
return 0; ///< failed to locate WoW executable
}
/**
* This function loads up a binary file (WoW executable), then searches for and returns
* the build number of the file. The build number is searched for in hex form.
*
* @PARAM sFilename is the filename of the WoW executable to be loaded
* @RETURN iBuild the build number of the WoW executable, or 0 if failed
*/
int getBuildNumber()
{
int iBuild = -1; ///< build version # of the WoW executable (returned value)
/// buffers used for working on the file's bytes
unsigned char byteSearchBuffer[1]; ///< used for reading in a single character, ready to be
///< tested for the required text we are searching for: 1, 5, 6, or 8
unsigned char jumpBytesBuffer[128]; ///< used for skipping past the bytes from the file's start
///< to the base # area, before we start searching for the base #, for faster processing
unsigned char preWOTLKbuildNumber[3]; ///< will hold the last 3 digits of the build number
unsigned char postTBCbuildNumber[4]; ///< will hold the last 4 digits of the build number
// These do not include the first digit
// as the first digit is used to locate the possible location of the build number
// then the following bytes are grabbed, 3 for pre WOTLK, 4 for WOTLK and later.
// Those grabbed bytes are then compared with the below variables in order to idenity the exe's build
unsigned char vanillaBuild1[3] = { 0x38, 0x37, 0x35 }; // (5)875
unsigned char vanillaBuild2[3] = { 0x30, 0x30, 0x35 }; // (6)005
unsigned char vanillaBuild3[3] = { 0x31, 0x34, 0x31 }; // (6)141
unsigned char tbcBuild[3] = { 0x36, 0x30, 0x36 }; // (8)606
unsigned char wotlkBuild[4] = { 0x32, 0x33, 0x34, 0x30 }; // (1)2340
unsigned char cataBuild[4] = { 0x35, 0x35, 0x39, 0x35 }; // (1)5595
unsigned char mopBuild[4] = { 0x38, 0x34, 0x31, 0x34 }; // (1)8414
FILE *pFile;
if (!(pFile = openWoWExe()))
{
printf("\nFatal Error: failed to locate the WoW executable!\n\n");
printf("\nExiting program!!\n");
exit(0); ///> failed to locate exe file
}
/// jump over as much of the file as possible, before we start searching for the base #
for (int i = 0; i < 3300; i++)
{
fread(jumpBytesBuffer, sizeof(jumpBytesBuffer), 1, pFile);
}
/// Search for the build #
while (fread(byteSearchBuffer, 1, 1, pFile))
{
/// we are looking for 1, 5, 6, or 8
/// these values are the first digit of the build versions we are interested in
// Vanilla and TBC
if (byteSearchBuffer[0] == 0x35 || byteSearchBuffer[0] == 0x36 || byteSearchBuffer[0] == 0x38)
{
/// grab the next 4 bytes
fread(preWOTLKbuildNumber, sizeof(preWOTLKbuildNumber), 1, pFile);
if (!memcmp(preWOTLKbuildNumber, vanillaBuild1, sizeof(preWOTLKbuildNumber))) /// build is Vanilla?
{
return 5875;
}
else if (!memcmp(preWOTLKbuildNumber, vanillaBuild2, sizeof(preWOTLKbuildNumber))) /// build is Vanilla?
{
return 6005;
}
else if (!memcmp(preWOTLKbuildNumber, vanillaBuild3, sizeof(preWOTLKbuildNumber))) /// build is Vanilla?
{
return 6141;
}
else if (!memcmp(preWOTLKbuildNumber, tbcBuild, sizeof(preWOTLKbuildNumber))) /// build is TBC?
{
return 8606;
}
}
/// WOTLK, CATA, MoP
if (byteSearchBuffer[0] == 0x31)
{
/// grab the next 4 bytes
fread(postTBCbuildNumber, sizeof(postTBCbuildNumber), 1, pFile);
if (!memcmp(postTBCbuildNumber, wotlkBuild, sizeof(postTBCbuildNumber))) /// build is WOTLK?
{
return 12340;
}
else if (!memcmp(postTBCbuildNumber, cataBuild, sizeof(postTBCbuildNumber))) /// build is CATA?
{
return 15595;
}
else if (!memcmp(postTBCbuildNumber, mopBuild, sizeof(postTBCbuildNumber))) /// build is MoP?
{
return 18414;
}
}
}
printf("\nFatal Error: failed to identify build version!\n\n");
printf("\nSupported build versions:\n");
printf("\nVanilla: 5875, 6005, 6141\n");
printf("TBC: 8606\n");
printf("WOTLK: 12340\n");
printf("CATA: 15595\n");
printf("MOP: 18414\n");
printf("\n\nExiting program!!\n");
exit(0);
}
/**
* This function looks up the Core Version based in the found build Number
*
* @RETURN iCoreNumber the build number of the WoW executable, or -1 if failed
*/
int getCoreNumber()
{
return getCoreNumberFromBuild(getBuildNumber());
}
/**
* This function looks up the Core Version based in the found build Number
*
* @PARAM iBuildNumber is the build number of the WoW executable
* @RETURN iCoreNumber the build number of the WoW executable, or -1 if failed
*/
int getCoreNumberFromBuild(int iBuildNumber)
{
switch (iBuildNumber)
{
case 5875: //CLASSIC
case 6005: //CLASSIC
case 6141: //CLASSIC
return CLIENT_CLASSIC;
break;
case 8606: //TBC
return CLIENT_TBC;
break;
case 12340: //WOTLK
return CLIENT_WOTLK;
break;
case 15595: //CATA
return CLIENT_CATA;
break;
case 18414: //MOP
return CLIENT_MOP;
break;
case 21355: //WOD
return CLIENT_WOD;
break;
case 20740: //LEGION ALPHA
return CLIENT_LEGION;
break;
default:
return -1;
break;
}
}
/**
* This function displays the standard mangos banner to the console
*
* @PARAM sTitle is the Title text (directly under the MaNGOS logo)
* @PARAM iCoreNumber is the Core Number
*/
void showBanner(const std::string& sTitle, int iCoreNumber)
{
printf(
" __ __ _ _ ___ ___ ___ \n"
" | \\/ |__ _| \\| |/ __|/ _ \\/ __| \n"
" | |\\/| / _` | .` | (_ | (_) \\__ \\ \n"
" |_| |_\\__,_|_|\\_|\\___|\\___/|___/ \n"
" %s for ", sTitle.c_str());
switch (iCoreNumber)
{
case CLIENT_CLASSIC:
printf("MaNGOSZero\n");
break;
case CLIENT_TBC:
printf("MaNGOSOne\n");
break;
case CLIENT_WOTLK:
printf("MaNGOSTwo\n");
break;
case CLIENT_CATA:
printf("MaNGOSThree\n");
break;
case CLIENT_MOP:
printf("MaNGOSFour\n");
break;
case CLIENT_WOD:
printf("MaNGOSFive\n");
break;
case CLIENT_LEGION:
printf("MaNGOSSix\n");
break;
default:
printf("Unknown Version\n");
break;
}
printf(" ________________________________________________\n");
}
/**
* This function displays the standard mangos help banner to the console
*/
void showWebsiteBanner()
{
printf(
" ________________________________________________\n\n"
" For help and support please visit: \n"
" Website / Forum / Wiki: https://getmangos.eu \n"
" ________________________________________________\n"
);
}
/**
* This function returns the .map file 'magic' number based on the core number
*
* @PARAM iCoreNumber is the Core Number
*/
void setMapMagicVersion(int iCoreNumber, char* magic)
{
switch (iCoreNumber)
{
case CLIENT_CLASSIC:
std::strcpy(magic,"z1.5");
break;
case CLIENT_TBC:
std::strcpy(magic,"s1.5");
break;
case CLIENT_WOTLK:
std::strcpy(magic,"v1.5");
break;
case CLIENT_CATA:
std::strcpy(magic,"c1.5");
break;
case CLIENT_MOP:
std::strcpy(magic,"p1.5");
break;
case CLIENT_WOD:
std::strcpy(magic,"w1.5");
break;
case CLIENT_LEGION:
std::strcpy(magic,"l1.5");
break;
default:
std::strcpy(magic,"UNKN");
break;
}
}
/**
* This function returns the .vmap file 'magic' number based on the core number
*
* @PARAM iCoreNumber is the Core Number
*/
void setVMapMagicVersion(int iCoreNumber, char* magic)
{
switch (iCoreNumber)
{
case CLIENT_CLASSIC:
std::strcpy(magic,"VMAPz07");
break;
case CLIENT_TBC:
std::strcpy(magic,"VMAPs07");
break;
case CLIENT_WOTLK:
std::strcpy(magic,"VMAPt07");
break;
case CLIENT_CATA:
std::strcpy(magic,"VMAPc07");
break;
case CLIENT_MOP:
std::strcpy(magic,"VMAPp07");
break;
case CLIENT_WOD:
std::strcpy(magic,"VMAPw07");
break;
case CLIENT_LEGION:
std::strcpy(magic,"VMAPl07");
break;
default:
std::strcpy(magic,"VMAPUNK");
break;
}
}
/**
* This function returns the .mmap file 'magic' number based on the core number
*
* @PARAM iCoreNumber is the Core Number
*/
void setMMapMagicVersion(int iCoreNumber, char* magic)
{
switch (iCoreNumber)
{
case CLIENT_CLASSIC:
std::strcpy(magic, "z06");
break;
case CLIENT_TBC:
std::strcpy(magic, "s06");
break;
case CLIENT_WOTLK:
std::strcpy(magic, "t06");
break;
case CLIENT_CATA:
std::strcpy(magic, "c06");
break;
case CLIENT_MOP:
std::strcpy(magic, "p06");
break;
case CLIENT_WOD:
std::strcpy(magic, "w06");
break;
case CLIENT_LEGION:
std::strcpy(magic, "l06");
break;
default:
std::strcpy(magic, "UNK");
break;
}
}
//#define MMAP_VERSION 4
/**
* @Create Folders based on the path provided
*
* @param sPath
*/
void CreateDir(const std::string& sPath)
{
#ifdef WIN32
_mkdir(sPath.c_str());
#else
mkdir(sPath.c_str(), 0777);
#endif
}
/**
* @Checks whether the Filename in the client exists
*
* @param sFileName
* @return bool
*/
bool ClientFileExists(const char* sFileName)
{
int fp = _open(sFileName, OPEN_FLAGS);
if (fp != -1)
{
_close(fp);
return true;
}
return false;
}
/**************************************************************************/
bool isTransportMap(int mapID)
{
switch (mapID)
{
// transport maps
case 582: // Transport: Rut'theran to Auberdine
case 584: // Transport: Menethil to Theramore
case 586: // Transport: Exodar to Auberdine
case 587: // Transport: Feathermoon Ferry - (TBC / WOTLK / CATA / MOP)
case 588: // Transport: Menethil to Auberdine - (TBC / WOTLK / CATA / MOP)
case 589: // Transport: Orgrimmar to Grom'Gol - (TBC / WOTLK / CATA / MOP)
case 590: // Transport: Grom'Gol to Undercity - (TBC / WOTLK / CATA / MOP)
case 591: // Transport: Undercity to Orgrimmar - (TBC / WOTLK / CATA / MOP)
case 592: // Transport: Borean Tundra Test - (WOTLK / CATA / MOP)
case 593: // Transport: Booty Bay to Ratchet - (TBC / WOTLK / CATA / MOP)
case 594: // Transport: Howling Fjord Sister Mercy (Quest) - (WOTLK / CATA / MOP)
case 596: // Transport: Naglfar - (WOTLK / CATA / MOP)
case 610: // Transport: Tirisfal to Vengeance Landing - (WOTLK / CATA / MOP)
case 612: // Transport: Menethil to Valgarde - (WOTLK / CATA / MOP)
case 613: // Transport: Orgrimmar to Warsong Hold - (WOTLK / CATA / MOP)
case 614: // Transport: Stormwind to Valiance Keep - (WOTLK / CATA / MOP)
case 620: // Transport: Moa'ki to Unu'pe - (WOTLK / CATA / MOP)
case 621: // Transport: Moa'ki to Kamagua - (WOTLK / CATA / MOP)
case 622: // Transport: Orgrim's Hammer - (WOTLK / CATA / MOP)
case 623: // Transport: The Skybreaker - (WOTLK / CATA / MOP)
case 641: // Transport: Alliance Airship BG - (WOTLK / CATA / MOP)
case 642: // Transport: HordeAirshipBG - (WOTLK / CATA / MOP)
case 647: // Transport: Orgrimmar to Thunder Bluff - (WOTLK / CATA / MOP)
case 662: // Transport: Alliance Vashj'ir Ship - (WOTLK / CATA / MOP)
case 672: // Transport: The Skybreaker (Icecrown Citadel Raid) - (WOTLK / CATA / MOP)
case 673: // Transport: Orgrim's Hammer (Icecrown Citadel Raid) - (WOTLK / CATA / MOP)
case 674: // Transport: Ship to Vashj'ir - (WOTLK / CATA / MOP)
case 712: // Transport: The Skybreaker (IC Dungeon) - (WOTLK / CATA / MOP)
case 713: // Transport: Orgrim's Hammer (IC Dungeon) - (WOTLK / CATA / MOP)
case 718: // Transport: The Mighty Wind (Icecrown Citadel Raid) - (WOTLK / CATA / MOP)
case 738: // Ship to Vashj'ir (Orgrimmar -> Vashj'ir) - (CATA / MOP)
case 739: // Vashj'ir Sub - Horde - (CATA / MOP)
case 740: // Vashj'ir Sub - Alliance - (CATA / MOP)
case 741: // Twilight Highlands Horde Transport - (CATA / MOP)
case 742: // Vashj'ir Sub - Horde - Circling Abyssal Maw - (CATA / MOP)
case 743: // Vashj'ir Sub - Alliance circling Abyssal Maw - (CATA / MOP)
case 746: // Uldum Phase Oasis - (CATA / MOP)
case 747: // Transport: Deepholm Gunship - (CATA / MOP)
case 748: // Transport: Onyxia/Nefarian Elevator - (CATA / MOP)
case 749: // Transport: Gilneas Moving Gunship - (CATA / MOP)
case 750: // Transport: Gilneas Static Gunship - (CATA / MOP)
case 762: // Twilight Highlands Zeppelin 1 - (CATA / MOP)
case 763: // Twilight Highlands Zeppelin 2 - (CATA / MOP)
case 765: // Krazzworks Attack Zeppelin - (CATA / MOP)
case 766: // Transport: Gilneas Moving Gunship 02 - (CATA / MOP)
case 767: // Transport: Gilneas Moving Gunship 03 - (CATA / MOP)
case 1113: // Transport: DarkmoonCarousel - (MOP)
case 1132: // Transport218599 - The Skybag (Brawl'gar Arena) - (MOP)
case 1133: // Transport218600 - Zandalari Ship (Mogu Island) - (MOP)
case 1172: // Transport: Siege of Orgrimmar (Alliance) - (MOP)
case 1173: // Transport: Siege of Orgrimmar (Horde) - (MOP)
case 1192: // Transport: Iron_Horde_Gorgrond_Train - (WOD)
case 1231: // Transport: Wavemurder Barge - (WOD)
return true;
default: // no transport maps
return false;
}
}
/**************************************************************************/
bool shouldSkipMap(int mapID,bool m_skipContinents, bool m_skipJunkMaps, bool m_skipBattlegrounds)
{
if (m_skipContinents)
switch (mapID)
{
case 0: // Eastern Kingdoms - (CLASSIC / TBC / WOTLK / CATA / MOP)
case 1: // Kalimdor - (CLASSIC - TBC / WOTLK / CATA / MOP)
case 530: // Outland - (TBC / WOTLK / CATA / MOP)
case 571: // Northrend - (WOTLK / CATA / MOP)
case 870: // Pandaria - (MOP)
case 1116: // Draenor - (WOD)
return true;
default:
break;
}
if (m_skipJunkMaps)
switch (mapID)
{
case 13: // test.wdt
case 25: // ScottTest.wdt
case 29: // Test.wdt
case 35: // StornWind Crypt (Unused Instance)
case 37: // Ashara.wdt (Unused Raid Area)
case 42: // Colin.wdt
case 44: // Monastry.wdt (Unused Old SM)
case 169: // EmeraldDream.wdt (unused, and very large)
case 451: // development.wdt
case 573: // ExteriorTest.wdt - (WOTLK / CATA / MOP)
case 597: // CraigTest.wdt - (WOTLK / CATA / MOP)
case 605: // development_nonweighted.wdt - (WOTLK / CATA / MOP)
case 606: // QA_DVD.wdt - (WOTLK / CATA / MOP)
case 627: // unused.wdt - (CATA / MOP)
case 930: // (UNUSED) Scenario: Alcaz Island - (MOP)
case 995: // The Depths [UNUSED] - (MOP)
case 1010: // MistsCTF3
case 1014: // (UNUSED) Peak of Serenity Scenario - (MOP)
case 1028: // (UNUSED) Scenario: Mogu Ruins - (MOP)
case 1029: // (UNUSED) Scenario: Mogu Crypt - (MOP)
case 1049: // (UNUSED) Scenario: Black Ox Temple - (MOP)
case 1060: // Level Design Land - Dev Only - (MOP)
case 1181: // PattyMack Test Garrison Bldg Map - (WOD)
case 1250: // Alliance - Garrison - Herb Garden 1 (UNUSED) - (WOD)
case 1251: // Alliance - Garrison - Inn 1 DONT USE - (WOD)
case 1264: // Propland - Dev Only - (WOD)
case 1270: // Development Land 3 - (WOD)
case 1310: // Expansion 5 QA Model Map - (WOD)
case 1407: // GorgrondFinaleScenarioMap(zzzOld) - (WOD)
case 1427: // PattyMack Test Garrison Bld Map2 - (WOD)
return true;
default:
if (isTransportMap(mapID))
{
return true;
}
break;
}
if (m_skipBattlegrounds)
switch (mapID)
{
case 30: // AV
case 37: // AC
case 489: // WSG
case 529: // AB
case 566: // EotS - (TBC / WOTLK / CATA / MOP)
case 607: // SotA - (WOTLK / CATA / MOP)
case 628: // IoC - (WOTLK / CATA / MOP)
case 726: // TP - (CATA / MOP)
case 727: // SM - (CATA / MOP)
case 728: // BfG - (CATA / MOP)
case 761: // BfG2 - (CATA / MOP)
case 968: // EotS2 - (CATA / MOP)
case 998: // VOP - (MOP)
case 1010: // CTF3 - (MOP)
case 1101: // DOTA - (MOP)
case 1105: // GR - (MOP)
case 1166: // Small Battleground - (WOD)
case 1431: // SparringArenaLevel3Stadium 'The Coliseum' - (WOD)
return true;
default:
break;
}
return false;
}

View file

@ -0,0 +1,57 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <sstream>
FILE* openWoWExe();
int getBuildNumber();
int getCoreNumber();
int getCoreNumberFromBuild(int iBuildNumber);
void showBanner(const std::string& title, int iCoreNumber);
void showWebsiteBanner();
void setMapMagicVersion(int iCoreNumber, char* magic);
void setVMapMagicVersion(int iCoreNumber, char* magic);
void setMMapMagicVersion(int iCoreNumber, char* magic);
void CreateDir(const std::string& sPath);
bool ClientFileExists(const char* sFileName);
bool isTransportMap(int mapID);
bool shouldSkipMap(int mapID, bool m_skipContinents, bool m_skipJunkMaps, bool m_skipBattlegrounds);
static const char *langs[12] = { "enGB", "enUS", "deDE", "esES", "frFR", "koKR", "zhCN", "zhTW", "enCN", "enTW", "esMX", "ruRU" };
/// Enumerated Core Numbers
enum CoreNumber
{
CLIENT_CLASSIC = 0,
CLIENT_TBC = 1,
CLIENT_WOTLK = 2,
CLIENT_CATA = 3,
CLIENT_MOP = 4,
CLIENT_WOD = 5,
CLIENT_LEGION = 6
};

View file

@ -0,0 +1,165 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#include "dbcfile.h"
#undef min
#undef max
#include <ml/mpq.h>
#include <cstdio>
DBCFile::DBCFile(const std::string& filename):
filename(filename),
data(0)
{
}
bool DBCFile::open()
{
MPQFile f(filename.c_str());
// Need some error checking, otherwise an unhandled exception error occurs
// if people screw with the data path.
if (f.isEof() == true)
{
f.close();
data = NULL;
printf("Could not open DBCFile %s.\n", filename.c_str());
return false;
}
unsigned char header[4];
unsigned int na, nb, es, ss;
if (f.read(header, 4) != 4) // Number of records
{
f.close();
data = NULL;
printf("Could not read header in DBCFile %s.\n", filename.c_str());
return false;
}
if (header[0] != 'W' || header[1] != 'D' || header[2] != 'B' || header[3] != 'C')
{
f.close();
data = NULL;
printf("The header in DBCFile %s did not match.\n", filename.c_str());
return false;
}
if (f.read(&na, 4) != 4) // Number of records
{
f.close();
data = NULL;
printf("Could not read number of records from DBCFile %s.\n", filename.c_str());
return false;
}
if (f.read(&nb, 4) != 4) // Number of fields
{
f.close();
data = NULL;
printf("Could not read number of fields from DBCFile %s.\n", filename.c_str());
return false;
}
if (f.read(&es, 4) != 4) // Size of a record
{
f.close();
data = NULL;
printf("Could not read record size from DBCFile %s.\n", filename.c_str());
return false;
}
if (f.read(&ss, 4) != 4) // String size
{
f.close();
data = NULL;
printf("Could not read string block size from DBCFile %s.\n", filename.c_str());
return false;
}
recordSize = es;
recordCount = na;
fieldCount = nb;
stringSize = ss;
if (fieldCount * 4 != recordSize)
{
f.close();
data = NULL;
printf("Field count and record size in DBCFile %s do not match.\n", filename.c_str());
return false;
}
data = new unsigned char[recordSize * recordCount + stringSize];
stringTable = data + recordSize * recordCount;
size_t data_size = recordSize * recordCount + stringSize;
if (f.read(data, data_size) != data_size)
{
f.close();
data = NULL;
printf("DBCFile %s did not contain expected amount of data for records.\n", filename.c_str());
return false;
}
f.close();
return true;
}
DBCFile::~DBCFile()
{
delete [] data;
}
DBCFile::Record DBCFile::getRecord(size_t id)
{
assert(data);
return Record(*this, data + id * recordSize);
}
size_t DBCFile::getMaxId()
{
assert(data);
size_t maxId = 0;
for (size_t i = 0; i < getRecordCount(); ++i)
{
if (maxId < getRecord(i).getUInt(0))
{
maxId = getRecord(i).getUInt(0);
}
}
return maxId;
}
DBCFile::Iterator DBCFile::begin()
{
assert(data);
return Iterator(*this, data);
}
DBCFile::Iterator DBCFile::end()
{
assert(data);
return Iterator(*this, stringTable);
}

View file

@ -0,0 +1,303 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#ifndef DBCFILE_H
#define DBCFILE_H
#include <cassert>
#include <string>
/**
* @brief
*
*/
class DBCFile
{
public:
/**
* @brief
*
* @param filename
*/
DBCFile(const std::string& filename);
/**
* @brief
*
*/
~DBCFile();
/**
* @brief Open database. It must be openened before it can be used.
*
* @return bool
*/
bool open();
/**
* @brief Database exceptions
*
*/
class Exception
{
public:
/**
* @brief
*
* @param message
*/
Exception(const std::string& message): message(message)
{ }
/**
* @brief
*
*/
virtual ~Exception()
{ }
/**
* @brief
*
* @return const std::string
*/
const std::string& getMessage() {return message;}
private:
std::string message; /**< TODO */
};
/**
* @brief
*
*/
class NotFound: public Exception
{
public:
/**
* @brief
*
*/
NotFound(): Exception("Key was not found")
{ }
};
class Iterator;
/**
* @brief Iteration over database
*
*/
class Record
{
public:
/**
* @brief
*
* @param r
* @return Record &operator
*/
Record& operator= (const Record& r)
{
file = r.file;
offset = r.offset;
return *this;
}
/**
* @brief
*
* @param field
* @return float
*/
float getFloat(size_t field) const
{
assert(field < file.fieldCount);
return *reinterpret_cast<float*>(offset + (field * 4));
}
/**
* @brief
*
* @param field
* @return unsigned int
*/
unsigned int getUInt(size_t field) const
{
assert(field < file.fieldCount);
return *reinterpret_cast<unsigned int*>(offset + (field * 4));
}
/**
* @brief
*
* @param field
* @return int
*/
int getInt(size_t field) const
{
assert(field < file.fieldCount);
return *reinterpret_cast<int*>(offset + (field * 4));
}
/**
* @brief
*
* @param ofs
* @return unsigned char
*/
unsigned char getByte(size_t ofs) const
{
assert(ofs < file.recordSize);
return *reinterpret_cast<unsigned char*>(offset + ofs);
}
/**
* @brief
*
* @param field
* @return const char
*/
const char* getString(size_t field) const
{
assert(field < file.fieldCount);
size_t stringOffset = getUInt(field);
assert(stringOffset < file.stringSize);
return reinterpret_cast<char*>(file.stringTable + stringOffset);
}
private:
/**
* @brief
*
* @param file
* @param offset
*/
Record(DBCFile& file, unsigned char* offset): file(file), offset(offset) {}
DBCFile& file; /**< TODO */
unsigned char* offset; /**< TODO */
friend class DBCFile;
friend class DBCFile::Iterator;
};
/**
* @brief Iterator that iterates over records
*
*/
class Iterator
{
public:
/**
* @brief
*
* @param file
* @param offset
*/
Iterator(DBCFile& file, unsigned char* offset):
record(file, offset) {}
/**
* @brief Advance (prefix only)
*
* @return Iterator &operator
*/
Iterator& operator++()
{
record.offset += record.file.recordSize;
return *this;
}
/**
* @brief Return address of current instance
*
* @return const Record &operator
*/
Record const& operator*() const { return record; }
/**
* @brief
*
* @return const Record *operator ->
*/
const Record* operator->() const
{
return &record;
}
/**
* @brief Comparison
*
* @param b
* @return bool operator
*/
bool operator==(const Iterator& b) const
{
return record.offset == b.record.offset;
}
/**
* @brief
*
* @param b
* @return bool operator
*/
bool operator!=(const Iterator& b) const
{
return record.offset != b.record.offset;
}
private:
Record record; /**< TODO */
};
/**
* @brief Get record by id
*
* @param id
* @return Record
*/
Record getRecord(size_t id);
/**
* @brief Get begin iterator over records
*
* @return Iterator
*/
Iterator begin();
/**
* @brief Get begin iterator over records
*
* @return Iterator
*/
Iterator end();
/**
* @brief Trivial
*
* @return size_t
*/
size_t getRecordCount() const { return recordCount;}
/**
* @brief
*
* @return size_t
*/
size_t getFieldCount() const { return fieldCount; }
/**
* @brief
*
* @return size_t
*/
size_t getMaxId();
private:
std::string filename; /**< TODO */
size_t recordSize; /**< TODO */
size_t recordCount; /**< TODO */
size_t fieldCount; /**< TODO */
size_t stringSize; /**< TODO */
unsigned char* data; /**< TODO */
unsigned char* stringTable; /**< TODO */
};
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

View file

@ -0,0 +1,40 @@
vmap extractor
--------------
The *vmap extractor* will extract model information from the game client.
Requirements
------------
You will need a working installation of the [World of Warcraft][1] client patched
to version 1.12.x.
Instructions - Linux
--------------------
Use the created executable to extract model information. Change the data path if
needed.
$ vmap-extractor -d /mnt/windows/games/world of warcraft/
Resulting files will be in `./Buildings`.
Instructions - Windows
----------------------
Use the created executable (from command prompt) to extract model information.
It should find the data path for your client installation through the Windows
registry, but the data path can be specified with the -d option.
Resulting files will be in `.\Buildings`.
Parameters
----------
The *vmap extractor* can be used with a few parameters to customize input, output
and generated output.
* `-d PATH`, `--data PATH`: set the path for reading the client's MPQ archives to the given
path.
* `-s`, `--small`: small size (data size optimization), ~500MB less vmap data. This is the
default setting.
* `-l`, `--large`: large size, ~500MB more vmap data. Stores additional details in vmap data.
* `-h`, `--help`: display the usage message, and an example call.
[1]: http://blizzard.com/games/wow/ "World of Warcraft"

View file

@ -0,0 +1,161 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#include <algorithm>
#include <cstdio>
#include "vmapexport.h"
#include "adtfile.h"
ADTFile::ADTFile(char* filename): ADT(filename)
{
AdtFilename.assign(filename);
}
bool ADTFile::init(uint32 map_num, uint32 tileX, uint32 tileY, StringSet& failedPaths,int iCoreNumber, const void *szRawVMAPMagic)
{
if (ADT.isEof())
{
return false;
}
uint32 size;
std::string xMap;
std::string yMap;
AdtFilename.erase(AdtFilename.find(".adt"), 4);
std::string TempMapNumber;
TempMapNumber = AdtFilename.substr(AdtFilename.length() - 6, 6);
xMap = TempMapNumber.substr(TempMapNumber.find("_") + 1, (TempMapNumber.find_last_of("_") - 1) - (TempMapNumber.find("_")));
yMap = TempMapNumber.substr(TempMapNumber.find_last_of("_") + 1, (TempMapNumber.length()) - (TempMapNumber.find_last_of("_")));
AdtFilename.erase((AdtFilename.length() - xMap.length() - yMap.length() - 2), (xMap.length() + yMap.length() + 2));
std::string AdtMapNumber = xMap + ' ' + yMap + ' ' + GetUniformName(AdtFilename);
std::string dirname = std::string(szWorkDirWmo) + "/dir_bin";
FILE* dirfile;
dirfile = fopen(dirname.c_str(), "ab");
if (!dirfile)
{
printf("Can't open dirfile!'%s'\n", dirname.c_str());
return false;
}
while (!ADT.isEof())
{
char fourcc[5];
ADT.read(&fourcc, 4);
ADT.read(&size, 4);
flipcc(fourcc);
fourcc[4] = 0;
size_t nextpos = ADT.getPos() + size;
if (!strcmp(fourcc, "MCIN"))
{
}
else if (!strcmp(fourcc, "MTEX"))
{
}
else if (!strcmp(fourcc, "MMDX"))
{
if (size)
{
char* buf = new char[size];
ADT.read(buf, size);
char* p = buf;
int t = 0;
ModelInstansName = new std::string[size];
while (p < buf + size)
{
std::string path(p); // Store copy after name fixed
std::string uName;
ExtractSingleModel(path, uName, failedPaths, iCoreNumber, szRawVMAPMagic);
ModelInstansName[t++] = uName;
p = p + strlen(p) + 1;
}
delete[] buf;
}
}
else if (!strcmp(fourcc, "MWMO"))
{
if (size)
{
char* buf = new char[size];
ADT.read(buf, size);
char* p = buf;
int q = 0;
WmoInstansName = new std::string[size];
while (p < buf + size)
{
std::string path(p);
WmoInstansName[q++] = GetUniformName(path);
p = p + strlen(p) + 1;
}
delete[] buf;
}
}
//======================
else if (!strcmp(fourcc, "MDDF"))
{
if (size)
{
nMDX = (int)size / 36;
for (int i = 0; i < nMDX; ++i)
{
uint32 id;
ADT.read(&id, 4);
ModelInstance inst(ADT, ModelInstansName[id], map_num, tileX, tileY, dirfile, iCoreNumber);
}
delete[] ModelInstansName;
}
}
else if (!strcmp(fourcc, "MODF"))
{
if (size)
{
nWMO = (int)size / 64;
for (int i = 0; i < nWMO; ++i)
{
uint32 id;
ADT.read(&id, 4);
WMOInstance inst(ADT, WmoInstansName[id], map_num, tileX, tileY, dirfile);
}
delete[] WmoInstansName;
}
}
//======================
ADT.seek(nextpos);
}
ADT.close();
fclose(dirfile);
return true;
}
ADTFile::~ADTFile()
{
ADT.close();
}

View file

@ -0,0 +1,179 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#ifndef ADT_H
#define ADT_H
#include <ml/mpq.h>
#include "wmo.h"
#include "vmapexport.h"
#include "model.h"
#define TILESIZE (533.33333f)
#define CHUNKSIZE ((TILESIZE) / 16.0f)
#define UNITSIZE (CHUNKSIZE / 8.0f)
/**
* @brief
*
*/
typedef struct
{
float x; /**< TODO */
float y; /**< TODO */
float z; /**< TODO */
} svec;
/**
* @brief
*
*/
struct vec
{
double x; /**< TODO */
double y; /**< TODO */
double z; /**< TODO */
};
/**
* @brief
*
*/
struct triangle
{
vec v[3]; /**< TODO */
};
/**
* @brief
*
*/
typedef struct
{
float v9[16 * 8 + 1][16 * 8 + 1]; /**< TODO */
float v8[16 * 8][16 * 8]; /**< TODO */
} Cell;
/**
* @brief
*
*/
typedef struct
{
double v9[9][9]; /**< TODO */
double v8[8][8]; /**< TODO */
uint16 area_id; /**< TODO */
//Liquid *lq;
float waterlevel[9][9]; /**< TODO */
uint8 flag; /**< TODO */
} chunk;
/**
* @brief
*
*/
typedef struct
{
chunk ch[16][16]; /**< TODO */
} mcell;
/**
* @brief
*
*/
struct MapChunkHeader
{
uint32 flags; /**< TODO */
uint32 ix; /**< TODO */
uint32 iy; /**< TODO */
uint32 nLayers; /**< TODO */
uint32 nDoodadRefs; /**< TODO */
uint32 ofsHeight; /**< TODO */
uint32 ofsNormal; /**< TODO */
uint32 ofsLayer; /**< TODO */
uint32 ofsRefs; /**< TODO */
uint32 ofsAlpha; /**< TODO */
uint32 sizeAlpha; /**< TODO */
uint32 ofsShadow; /**< TODO */
uint32 sizeShadow; /**< TODO */
uint32 areaid; /**< TODO */
uint32 nMapObjRefs; /**< TODO */
uint32 holes; /**< TODO */
uint16 s1; /**< TODO */
uint16 s2; /**< TODO */
uint32 d1; /**< TODO */
uint32 d2; /**< TODO */
uint32 d3; /**< TODO */
uint32 predTex; /**< TODO */
uint32 nEffectDoodad; /**< TODO */
uint32 ofsSndEmitters; /**< TODO */
uint32 nSndEmitters; /**< TODO */
uint32 ofsLiquid; /**< TODO */
uint32 sizeLiquid; /**< TODO */
float zpos; /**< TODO */
float xpos; /**< TODO */
float ypos; /**< TODO */
uint32 textureId; /**< TODO */
uint32 props; /**< TODO */
uint32 effectId; /**< TODO */
};
/**
* @brief
*
*/
class ADTFile
{
public:
/**
* @brief
*
* @param filename
*/
ADTFile(char* filename);
/**
* @brief
*
*/
~ADTFile();
int nWMO; /**< TODO */
int nMDX; /**< TODO */
string* WmoInstansName; /**< TODO */
string* ModelInstansName; /**< TODO */
/**
* @brief
*
* @param map_num
* @param tileX
* @param tileY
* @param failedPaths
* @return bool
*/
bool init(uint32 map_num, uint32 tileX, uint32 tileY, StringSet& failedPaths,int iCoreNumber, const void *szRawVMAPMagic);
private:
MPQFile ADT; /**< TODO */
string AdtFilename; /**< TODO */
};
#endif

View file

@ -0,0 +1,40 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#include "TileAssembler.h"
#include <string>
bool AssembleVMAP(std::string src, std::string dest, const char* szMagic)
{
bool success = true;
VMAP::TileAssembler* ta = new VMAP::TileAssembler(src, dest);
if (!ta->convertWorld2(szMagic))
{
success = false;
}
delete ta;
return success;
}

View file

@ -0,0 +1,348 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#include <cassert>
#include <algorithm>
#include <cstdio>
#include <ml/mpq.h>
#include "model.h"
#include "wmo.h"
#include "dbcfile.h"
#include "vmapexport.h"
#include <ExtractorCommon.h>
Model::Model(std::string& filename) : filename(filename), vertices(0), indices(0)
{
}
bool Model::open(StringSet& failedPaths, int iCoreNumber)
{
MPQFile f(filename.c_str());
ok = !f.isEof();
if (!ok)
{
f.close();
failedPaths.insert(filename);
return false;
}
_unload();
bool bBoundingTriangles = false;
uint32 uofsBoundingVertices = 0;
uint32 uofsBoundingTriangles = 0;
uint32 unBoundingVertices = 0;
uint32 unBoundingTriangles = 0;
if (iCoreNumber == CLIENT_CLASSIC || iCoreNumber == CLIENT_TBC)
{
memcpy(&headerClassicTBC, f.getBuffer(), sizeof(ModelHeaderClassicTBC));
if (headerClassicTBC.nBoundingTriangles > 0)
{
bBoundingTriangles = true;
uofsBoundingVertices = headerClassicTBC.ofsBoundingVertices;
uofsBoundingTriangles = headerClassicTBC.ofsBoundingTriangles;
unBoundingVertices = headerClassicTBC.nBoundingVertices;
unBoundingTriangles = headerClassicTBC.nBoundingTriangles;
}
}
if (iCoreNumber == CLIENT_WOTLK || iCoreNumber == CLIENT_CATA)
{
memcpy(&headerOthers, f.getBuffer(), sizeof(ModelHeaderOthers));
if (headerOthers.nBoundingTriangles > 0)
{
bBoundingTriangles = true;
uofsBoundingVertices = headerOthers.ofsBoundingVertices;
uofsBoundingTriangles = headerOthers.ofsBoundingTriangles;
unBoundingVertices = headerOthers.nBoundingVertices;
unBoundingTriangles = headerOthers.nBoundingTriangles;
}
}
if (bBoundingTriangles)
{
boundingVertices = (ModelBoundingVertex*)(f.getBuffer() + uofsBoundingVertices);
vertices = new Vec3D[unBoundingVertices];
for (size_t i = 0; i < unBoundingVertices; i++)
{
vertices[i] = boundingVertices[i].pos;
}
uint16* triangles = (uint16*)(f.getBuffer() + uofsBoundingTriangles);
nIndices = unBoundingTriangles; // refers to the number of int16's, not the number of triangles
indices = new uint16[nIndices];
memcpy(indices, triangles, nIndices * 2);
f.close();
}
else
{
//printf("not included %s\n", filename.c_str());
f.close();
return false;
}
return true;
}
bool Model::ConvertToVMAPModel(std::string& outfilename,int iCoreNumber, const void *szRawVMAPMagic)
{
int N[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
FILE* output = fopen(outfilename.c_str(), "wb");
if (!output)
{
printf("Can't create the output file '%s'\n", outfilename.c_str());
return false;
}
fwrite(szRawVMAPMagic, 8, 1, output);
uint32 nVertices = 0;
if (iCoreNumber == CLIENT_CLASSIC || iCoreNumber == CLIENT_TBC)
{
nVertices = headerClassicTBC.nBoundingVertices;
}
if (iCoreNumber == CLIENT_WOTLK || iCoreNumber == CLIENT_CATA)
{
nVertices = headerOthers.nBoundingVertices;
}
fwrite(&nVertices, sizeof(int), 1, output);
uint32 nofgroups = 1;
fwrite(&nofgroups, sizeof(uint32), 1, output);
fwrite(N, 4 * 3, 1, output); // rootwmoid, flags, groupid
fwrite(N, sizeof(float), 3 * 2, output); //bbox, only needed for WMO currently
fwrite(N, 4, 1, output); // liquidflags
fwrite("GRP ", 4, 1, output);
uint32 branches = 1;
int wsize;
wsize = sizeof(branches) + sizeof(uint32) * branches;
fwrite(&wsize, sizeof(int), 1, output);
fwrite(&branches, sizeof(branches), 1, output);
uint32 nIndexes = (uint32) nIndices;
fwrite(&nIndexes, sizeof(uint32), 1, output);
fwrite("INDX", 4, 1, output);
wsize = sizeof(uint32) + sizeof(unsigned short) * nIndexes;
fwrite(&wsize, sizeof(int), 1, output);
fwrite(&nIndexes, sizeof(uint32), 1, output);
if (nIndexes > 0)
{
for (uint32 i = 0; i < nIndices; ++i)
{
// index[0] -> x, index[1] -> y, index[2] -> z, index[3] -> x ...
if ((i % 3) - 1 == 0)
{
uint16 tmp = indices[i];
indices[i] = indices[i+1];
indices[i+1] = tmp;
}
}
fwrite(indices, sizeof(unsigned short), nIndexes, output);
}
fwrite("VERT", 4, 1, output);
wsize = sizeof(int) + sizeof(float) * 3 * nVertices;
fwrite(&wsize, sizeof(int), 1, output);
fwrite(&nVertices, sizeof(int), 1, output);
if (nVertices > 0)
{
fwrite(vertices, sizeof(float) * 3, nVertices, output);
}
fclose(output);
return true;
}
ModelInstance::ModelInstance(MPQFile& f, string& ModelInstName, uint32 mapID, uint32 tileX, uint32 tileY, FILE* pDirfile, int coreNumber)
{
float ff[3];
f.read(&id, 4);
f.read(ff, 12);
pos = fixCoords(Vec3D(ff[0], ff[1], ff[2]));
f.read(ff, 12);
rot = Vec3D(ff[0], ff[1], ff[2]);
if (coreNumber == CLIENT_TBC || coreNumber == CLIENT_WOTLK)
{
uint16 fFlags; // dummy var
f.read(&scaleOthers, 2);
f.read(&fFlags, 2); // unknown but flag 1 is used for biodome in Outland, currently this value is not used
sc = scaleOthers / 1024.0f; // scale factor - divide by 1024. why not just use a float?
}
if (coreNumber == CLIENT_CLASSIC)
{
f.read(&scaleZeroOnly,4); // The above three lines introduced a regression bug in Mangos Zero, is Fine for other cores.
sc = scaleZeroOnly / 1024.0f; // scale factor - divide by 1024. why not just use a float?
}
char tempname[512];
sprintf(tempname, "%s/%s", szWorkDirWmo, ModelInstName.c_str());
FILE* input;
input = fopen(tempname, "r+b");
if (!input)
{
//printf("ModelInstance::ModelInstance couldn't open %s\n", tempname);
return;
}
fseek(input, 8, SEEK_SET); // get the correct no of vertices
int nVertices;
size_t file_read = fread(&nVertices, sizeof(int), 1, input);
fclose(input);
if (nVertices == 0 || file_read <= 0)
{
return;
}
uint16 adtId = 0;// not used for models
uint32 flags = MOD_M2;
if (tileX == 65 && tileY == 65)
{
flags |= MOD_WORLDSPAWN;
}
//write mapID, tileX, tileY, Flags, ID, Pos, Rot, Scale, name
fwrite(&mapID, sizeof(uint32), 1, pDirfile);
fwrite(&tileX, sizeof(uint32), 1, pDirfile);
fwrite(&tileY, sizeof(uint32), 1, pDirfile);
fwrite(&flags, sizeof(uint32), 1, pDirfile);
fwrite(&adtId, sizeof(uint16), 1, pDirfile);
fwrite(&id, sizeof(uint32), 1, pDirfile);
fwrite(&pos, sizeof(float), 3, pDirfile);
fwrite(&rot, sizeof(float), 3, pDirfile);
fwrite(&sc, sizeof(float), 1, pDirfile);
uint32 nlen = ModelInstName.length();
fwrite(&nlen, sizeof(uint32), 1, pDirfile);
fwrite(ModelInstName.c_str(), sizeof(char), nlen, pDirfile);
}
bool ExtractSingleModel(std::string& origPath, std::string& fixedName, StringSet& failedPaths, int iCoreNumber, const void *szRawVMAPMagic)
{
string ext = GetExtension(origPath);
// < 3.1.0 ADT MMDX section store filename.mdx filenames for corresponded .m2 file
if ((ext == "mdx") || (ext=="mdl"))
{
// replace .md[l,x] -> .m2
origPath.erase(origPath.length() - 2, 2);
origPath.append("2");
}
// >= 3.1.0 ADT MMDX section store filename.m2 filenames for corresponded .m2 file
// nothing do
fixedName = GetUniformName(origPath);
std::string output(szWorkDirWmo); // Stores output filename
output += "/";
output += fixedName;
if (FileExists(output.c_str()))
{
return true;
}
Model mdl(origPath); // Possible changed fname
if (!mdl.open(failedPaths, iCoreNumber))
{
return false;
}
return mdl.ConvertToVMAPModel(output, iCoreNumber, szRawVMAPMagic);
}
void ExtractGameobjectModels(int iCoreNumber, const void *szRawVMAPMagic)
{
printf("\n");
printf("Extracting GameObject models...\n");
DBCFile dbc("DBFilesClient\\GameObjectDisplayInfo.dbc");
if (!dbc.open())
{
printf("Fatal error: Invalid GameObjectDisplayInfo.dbc file format!\n");
exit(1);
}
std::string basepath = szWorkDirWmo;
basepath += "/";
std::string path;
StringSet failedPaths;
FILE* model_list = fopen((basepath + "temp_gameobject_models").c_str(), "wb");
for (DBCFile::Iterator it = dbc.begin(); it != dbc.end(); ++it)
{
path = it->getString(1);
if (path.length() < 4)
{
continue;
}
string name;
string ch_ext = GetExtension(path);
if (ch_ext.empty())
{
continue;
}
bool result = false;
if (ch_ext == "wmo")
{
name = GetUniformName(path);
result = ExtractSingleWmo(path, iCoreNumber, szRawVMAPMagic);
}
else
{
result = ExtractSingleModel(path, name, failedPaths, iCoreNumber, szRawVMAPMagic);
}
if (result && FileExists((basepath + name).c_str()))
{
uint32 displayId = it->getUInt(0);
uint32 path_length = name.length();
fwrite(&displayId, sizeof(uint32), 1, model_list);
fwrite(&path_length, sizeof(uint32), 1, model_list);
fwrite(name.c_str(), sizeof(char), path_length, model_list);
}
}
fclose(model_list);
if (!failedPaths.empty())
{
printf("\n Warning: Some models could not be extracted, see below\n");
for (StringSet::const_iterator itr = failedPaths.begin(); itr != failedPaths.end(); ++itr)
{
printf(" Could not find file of model %s\n", itr->c_str());
}
printf("\n A few of these warnings are expected to happen, so be not alarmed!\n");
}
printf("\n Asset Extraction Complete !\n");
}

View file

@ -0,0 +1,144 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#ifndef MODEL_H
#define MODEL_H
#include <vector>
#include <ml/loadlib.h>
#include "vec3d.h"
#include "modelheaders.h"
#include "wmo.h"
/**
* @brief
*
*/
class Model
{
public:
ModelHeaderClassicTBC headerClassicTBC; /**< TODO */
ModelHeaderOthers headerOthers; /**< TODO */
ModelBoundingVertex* boundingVertices; /**< TODO */
Vec3D* vertices; /**< TODO */
uint16* indices; /**< TODO */
size_t nIndices; /**< TODO */
/**
* @brief
*
* @param failedPaths
* @return bool
*/
bool open(std::set<std::string>& failedPaths, int iCoreNumber);
/**
* @brief
*
* @param outfilename
* @return bool
*/
bool ConvertToVMAPModel(std::string& outfilename, int iCoreNumber, const void *szRawVMAPMagic);
bool ok; /**< TODO */
/**
* @brief
*
* @param filename
*/
Model(std::string& filename);
/**
* @brief
*
*/
~Model() {_unload();}
private:
/**
* @brief
*
*/
void _unload()
{
delete[] vertices;
delete[] indices;
vertices = NULL;
indices = NULL;
}
std::string filename; /**< TODO */
};
/**
* @brief
*
*/
class ModelInstance
{
public:
Model* model; /**< TODO */
uint32 id; /**< TODO */
Vec3D pos, rot; /**< TODO */
unsigned int d1;
float w, sc;
unsigned int scaleZeroOnly;
//unsigned int scale; // This line introduced a regression bug in Mangos Zero, is Fine for other cores.
uint16 scaleOthers;
/**
* @brief
*
*/
ModelInstance() {}
/**
* @brief
*
* @param f
* @param ModelInstName
* @param mapID
* @param tileX
* @param tileY
* @param pDirfile
*/
ModelInstance(MPQFile& f, std::string& ModelInstName, uint32 mapID, uint32 tileX, uint32 tileY, FILE* pDirfile, int iCoreNumber);
};
/**
* @brief
*
* @param origPath original path of the model, cleaned with fixnamen and fixname2
* @param fixedName will store the translated name (if changed)
* @param failedPaths Set to collect errors
* @return bool
*/
bool ExtractSingleModel(std::string& origPath, std::string& fixedName, std::set<std::string>& failedPaths, int iCoreNumber, const void *szRawVMAPMagic);
/**
* @brief
*
*/
void ExtractGameobjectModels(int iCoreNumber, const void *szRawVMAPMagic);
#endif

View file

@ -0,0 +1,187 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#ifndef MODELHEADERS_H
#define MODELHEADERS_H
/* typedef unsigned char uint8;
typedef char int8;
typedef unsigned short uint16;
typedef short int16;
typedef unsigned int uint32;
typedef int int32; */
#pragma pack(push,1)
/**
* @brief
*
*/
struct ModelHeaderClassicTBC
{
char id[4]; /**< TODO */
uint8 version[4]; /**< TODO */
uint32 nameLength; /**< TODO */
uint32 nameOfs; /**< TODO */
uint32 type; /**< TODO */
uint32 nGlobalSequences; /**< TODO */
uint32 ofsGlobalSequences; /**< TODO */
uint32 nAnimations; /**< TODO */
uint32 ofsAnimations; /**< TODO */
uint32 nAnimationLookup; /**< TODO */
uint32 ofsAnimationLookup; /**< TODO */
uint32 nD; /**< TODO */
uint32 ofsD; /**< TODO */
uint32 nBones; /**< TODO */
uint32 ofsBones; /**< TODO */
uint32 nKeyBoneLookup; /**< TODO */
uint32 ofsKeyBoneLookup; /**< TODO */
uint32 nVertices; /**< TODO */
uint32 ofsVertices; /**< TODO */
uint32 nViews; /**< TODO */
uint32 ofsViews; /**< TODO */
uint32 nColors; /**< TODO */
uint32 ofsColors; /**< TODO */
uint32 nTextures; /**< TODO */
uint32 ofsTextures; /**< TODO */
uint32 nTransparency; /**< TODO */
uint32 ofsTransparency; /**< TODO */
uint32 nI; /**< TODO */
uint32 ofsI; /**< TODO */
uint32 nTextureanimations; /**< TODO */
uint32 ofsTextureanimations; /**< TODO */
uint32 nTexReplace; /**< TODO */
uint32 ofsTexReplace; /**< TODO */
uint32 nRenderFlags; /**< TODO */
uint32 ofsRenderFlags; /**< TODO */
uint32 nBoneLookupTable; /**< TODO */
uint32 ofsBoneLookupTable; /**< TODO */
uint32 nTexLookup; /**< TODO */
uint32 ofsTexLookup; /**< TODO */
uint32 nTexUnits; /**< TODO */
uint32 ofsTexUnits; /**< TODO */
uint32 nTransLookup; /**< TODO */
uint32 ofsTransLookup; /**< TODO */
uint32 nTexAnimLookup; /**< TODO */
uint32 ofsTexAnimLookup; /**< TODO */
float floats[14]; /**< TODO */
uint32 nBoundingTriangles; /**< TODO */
uint32 ofsBoundingTriangles; /**< TODO */
uint32 nBoundingVertices; /**< TODO */
uint32 ofsBoundingVertices; /**< TODO */
uint32 nBoundingNormals; /**< TODO */
uint32 ofsBoundingNormals; /**< TODO */
uint32 nAttachments; /**< TODO */
uint32 ofsAttachments; /**< TODO */
uint32 nAttachLookup; /**< TODO */
uint32 ofsAttachLookup; /**< TODO */
uint32 nAttachments_2; /**< TODO */
uint32 ofsAttachments_2; /**< TODO */
uint32 nLights; /**< TODO */
uint32 ofsLights; /**< TODO */
uint32 nCameras; /**< TODO */
uint32 ofsCameras; /**< TODO */
uint32 nCameraLookup; /**< TODO */
uint32 ofsCameraLookup; /**< TODO */
uint32 nRibbonEmitters; /**< TODO */
uint32 ofsRibbonEmitters; /**< TODO */
uint32 nParticleEmitters; /**< TODO */
uint32 ofsParticleEmitters; /**< TODO */
};
struct ModelHeaderOthers
{
char id[4]; /**< TODO */
uint8 version[4]; /**< TODO */
uint32 nameLength; /**< TODO */
uint32 nameOfs; /**< TODO */
uint32 type; /**< TODO */
uint32 nGlobalSequences; /**< TODO */
uint32 ofsGlobalSequences; /**< TODO */
uint32 nAnimations; /**< TODO */
uint32 ofsAnimations; /**< TODO */
uint32 nAnimationLookup; /**< TODO */
uint32 ofsAnimationLookup; /**< TODO */
uint32 nBones; /**< TODO */
uint32 ofsBones; /**< TODO */
uint32 nKeyBoneLookup; /**< TODO */
uint32 ofsKeyBoneLookup; /**< TODO */
uint32 nVertices; /**< TODO */
uint32 ofsVertices; /**< TODO */
uint32 nViews; /**< TODO */
uint32 nColors; /**< TODO */
uint32 ofsColors; /**< TODO */
uint32 nTextures; /**< TODO */
uint32 ofsTextures; /**< TODO */
uint32 nTransparency; /**< TODO */
uint32 ofsTransparency; /**< TODO */
uint32 nTextureanimations; /**< TODO */
uint32 ofsTextureanimations; /**< TODO */
uint32 nTexReplace; /**< TODO */
uint32 ofsTexReplace; /**< TODO */
uint32 nRenderFlags; /**< TODO */
uint32 ofsRenderFlags; /**< TODO */
uint32 nBoneLookupTable; /**< TODO */
uint32 ofsBoneLookupTable; /**< TODO */
uint32 nTexLookup; /**< TODO */
uint32 ofsTexLookup; /**< TODO */
uint32 nTexUnits; /**< TODO */
uint32 ofsTexUnits; /**< TODO */
uint32 nTransLookup; /**< TODO */
uint32 ofsTransLookup; /**< TODO */
uint32 nTexAnimLookup; /**< TODO */
uint32 ofsTexAnimLookup; /**< TODO */
float floats[14]; /**< TODO */
uint32 nBoundingTriangles; /**< TODO */
uint32 ofsBoundingTriangles; /**< TODO */
uint32 nBoundingVertices; /**< TODO */
uint32 ofsBoundingVertices; /**< TODO */
uint32 nBoundingNormals; /**< TODO */
uint32 ofsBoundingNormals; /**< TODO */
uint32 nAttachments; /**< TODO */
uint32 ofsAttachments; /**< TODO */
uint32 nAttachLookup; /**< TODO */
uint32 ofsAttachLookup; /**< TODO */
uint32 nAttachments_2; /**< TODO */
uint32 ofsAttachments_2; /**< TODO */
uint32 nLights; /**< TODO */
uint32 ofsLights; /**< TODO */
uint32 nCameras; /**< TODO */
uint32 ofsCameras; /**< TODO */
uint32 nCameraLookup; /**< TODO */
uint32 ofsCameraLookup; /**< TODO */
uint32 nRibbonEmitters; /**< TODO */
uint32 ofsRibbonEmitters; /**< TODO */
uint32 nParticleEmitters; /**< TODO */
uint32 ofsParticleEmitters; /**< TODO */
};
struct ModelBoundingVertex
{
Vec3D pos; /**< TODO */
};
#pragma pack(pop)
#endif

View file

@ -0,0 +1,287 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#ifndef VEC3D_H
#define VEC3D_H
#include <iostream>
#include <cmath>
/**
* @brief
*
*/
class Vec3D
{
public:
float x, y, z; /**< TODO */
/**
* @brief
*
* @param x0
* @param y0
* @param z0
*/
Vec3D(float x0 = 0.0f, float y0 = 0.0f, float z0 = 0.0f) : x(x0), y(y0), z(z0) {}
/**
* @brief
*
* @param v
*/
Vec3D(const Vec3D& v) : x(v.x), y(v.y), z(v.z) {}
/**
* @brief
*
* @param v
* @return Vec3D &operator
*/
Vec3D& operator= (const Vec3D& v)
{
x = v.x;
y = v.y;
z = v.z;
return *this;
}
/**
* @brief
*
* @param v
* @return Vec3D operator
*/
Vec3D operator+ (const Vec3D& v) const
{
Vec3D r(x + v.x, y + v.y, z + v.z);
return r;
}
/**
* @brief
*
* @param v
* @return Vec3D operator
*/
Vec3D operator- (const Vec3D& v) const
{
Vec3D r(x - v.x, y - v.y, z - v.z);
return r;
}
/**
* @brief
*
* @param v
* @return float operator
*/
float operator* (const Vec3D& v) const
{
return x * v.x + y * v.y + z * v.z;
}
/**
* @brief
*
* @param d
* @return Vec3D operator
*/
Vec3D operator* (float d) const
{
Vec3D r(x * d, y * d, z * d);
return r;
}
/**
* @brief
*
* @param d
* @param v
* @return Vec3D operator
*/
friend Vec3D operator* (float d, const Vec3D& v)
{
return v * d;
}
/**
* @brief
*
* @param v
* @return Vec3D operator
*/
Vec3D operator% (const Vec3D& v) const
{
Vec3D r(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x);
return r;
}
/**
* @brief
*
* @param v
* @return Vec3D &operator
*/
Vec3D& operator+= (const Vec3D& v)
{
x += v.x;
y += v.y;
z += v.z;
return *this;
}
/**
* @brief
*
* @param v
* @return Vec3D &operator
*/
Vec3D& operator-= (const Vec3D& v)
{
x -= v.x;
y -= v.y;
z -= v.z;
return *this;
}
/**
* @brief
*
* @param d
* @return Vec3D &operator
*/
Vec3D& operator*= (float d)
{
x *= d;
y *= d;
z *= d;
return *this;
}
/**
* @brief
*
* @return float
*/
float lengthSquared() const
{
return x * x + y * y + z * z;
}
/**
* @brief
*
* @return float
*/
float length() const
{
return sqrt(x * x + y * y + z * z);
}
/**
* @brief
*
* @return Vec3D
*/
Vec3D& normalize()
{
this->operator*= (1.0f / length());
return *this;
}
/**
* @brief
*
* @return Vec3D operator
*/
Vec3D operator~() const
{
Vec3D r(*this);
r.normalize();
return r;
}
/**
* @brief
*
* @param in
* @param v
* @return std::istream &operator >>
*/
friend std::istream& operator>>(std::istream& in, Vec3D& v)
{
in >> v.x >> v.y >> v.z;
return in;
}
/**
* @brief
*
* @param out
* @param v
* @return std::ostream &operator
*/
friend std::ostream& operator<<(std::ostream& out, const Vec3D& v)
{
out << v.x << " " << v.y << " " << v.z;
return out;
}
/**
* @brief
*
* @return operator float
*/
operator float* ()
{
return (float*)this;
}
};
/**
* @brief
*
* @param x0
* @param y0
* @param x
* @param y
* @param angle
*/
inline void rotate(float x0, float y0, float* x, float* y, float angle)
{
float xa = *x - x0, ya = *y - y0;
*x = xa * cosf(angle) - ya * sinf(angle) + x0;
*y = xa * sinf(angle) + ya * cosf(angle) + y0;
}
/**
* @brief for whatever reason a certain company just can't stick to one coordinate system...
*
* @param v
* @return Vec3D
*/
inline Vec3D fixCoords(const Vec3D& v) { return Vec3D(v.z, v.x, v.y); }
#endif

View file

@ -0,0 +1,25 @@
/*
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
IDI_APPICON ICON DISCARDABLE "../tools.ico"

View file

@ -0,0 +1,592 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#include <cstdio>
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#include <errno.h>
#if defined WIN32
#include <Windows.h>
#include <sys/stat.h>
#include <direct.h>
#define mkdir _mkdir
#else
#include <sys/stat.h>
#endif
#undef min
#undef max
//#pragma warning(disable : 4505)
//#pragma comment(lib, "Winmm.lib")
#include <map>
//From Extractor
#include "adtfile.h"
#include "wdtfile.h"
#include "dbcfile.h"
#include "wmo.h"
#include <ml/mpq.h>
#include "vmapexport.h"
#include "Auth/md5.h"
#include "ExtractorCommon.h"
//------------------------------------------------------------------------------
// Defines
#define MPQ_BLOCK_SIZE 0x1000
//-----------------------------------------------------------------------------
bool AssembleVMAP(std::string src, std::string dest, const char* szMagic);
extern ArchiveSet gOpenArchives;
typedef struct
{
char name[64];
unsigned int id;
} map_id;
map_id* map_ids;
uint16* LiqType = 0;
uint32 map_count;
char output_path[128] = ".";
char input_path[1024] = ".";
bool hasInputPathParam = false;
bool preciseVectorData = true;
// Constants
//static const char * szWorkDirMaps = ".\\Maps";
char const szWorkDirWmo[] = "./Buildings";
char szRawVMAPMagic[] = "VMAP000";
// Local testing functions
bool FileExists(const char* file)
{
if (FILE* n = std::fopen(file, "rb"))
{
fclose(n);
return true;
}
return false;
}
void compute_md5(const char* value, char* result)
{
md5_byte_t digest[16];
md5_state_t ctx;
md5_init(&ctx);
md5_append(&ctx, (const unsigned char*)value, strlen(value));
md5_finish(&ctx, digest);
for(int i=0;i<16;i++)
{
sprintf(result+2*i,"%02x",digest[i]);
}
result[32]='\0';
}
std::string GetUniformName(std::string& path)
{
std::transform(path.begin(),path.end(),path.begin(),::tolower);
string tempPath;
string file;
char digest[33];
std::size_t found = path.find_last_of("/\\");
if (found != string::npos)
{
file = path.substr(found+1);
tempPath = path.substr(0,found);
}
else
{
file = tempPath = path;
}
if(!tempPath.empty())
compute_md5(tempPath.c_str(),digest);
else
compute_md5("\\",digest);
string result;
result = result.assign(digest) + "-" + file;
return result;
}
std::string GetExtension(std::string& path)
{
string ext;
size_t foundExt = path.find_last_of(".");
if (foundExt != std::string::npos)
{
ext=path.substr(foundExt+1);
}
else
{
ext.clear();
}
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
return ext;
}
// copied from contrib/extractor/System.cpp
void ReadLiquidTypeTableDBC()
{
printf(" Reading liquid types from LiquidType.dbc...");
DBCFile dbc("DBFilesClient\\LiquidType.dbc");
if (!dbc.open())
{
printf("Fatal error: Could not read LiquidType.dbc!\n");
exit(1);
}
size_t LiqType_count = dbc.getRecordCount();
size_t LiqType_maxid = dbc.getRecord(LiqType_count - 1).getUInt(0);
LiqType = new uint16[LiqType_maxid + 1];
memset(LiqType, 0xff, (LiqType_maxid + 1) * sizeof(uint16));
for (uint32 x = 0; x < LiqType_count; ++x)
{
LiqType[dbc.getRecord(x).getUInt(0)] = dbc.getRecord(x).getUInt(3);
}
printf(" Success! (%u Liquid Types loaded)\n", (unsigned int)LiqType_count);
}
void ParseMapFiles(int iCoreNumber)
{
char fn[512];
//char id_filename[64];
char id[10];
StringSet failedPaths;
printf("\n");
for (unsigned int i = 0; i < map_count; ++i)
{
sprintf(id, "%03u", map_ids[i].id);
sprintf(fn, "World\\Maps\\%s\\%s.wdt", map_ids[i].name, map_ids[i].name);
WDTFile WDT(fn, map_ids[i].name);
if (WDT.init(id, map_ids[i].id))
{
printf(" Processing Map %u (%s)\n[", map_ids[i].id, map_ids[i].name);
for (int x = 0; x < 64; ++x)
{
for (int y = 0; y < 64; ++y)
{
if (ADTFile* ADT = WDT.GetMap(x, y))
{
//sprintf(id_filename,"%02u %02u %03u",x,y,map_ids[i].id);//!!!!!!!!!
ADT->init(map_ids[i].id, x, y, failedPaths, iCoreNumber, szRawVMAPMagic);
delete ADT;
}
}
printf("#");
fflush(stdout);
}
printf("]\n");
}
}
if (!failedPaths.empty())
{
printf(" Warning: Some models could not be extracted, see below\n");
for (StringSet::const_iterator itr = failedPaths.begin(); itr != failedPaths.end(); ++itr)
{
printf("Could not find file of model %s\n", itr->c_str());
}
printf(" A few not found models can be expected and are not alarming.\n");
}
}
void getGamePath()
{
#ifdef _WIN32
strcpy(input_path, "Data\\");
#else
strcpy(input_path, "Data/");
#endif
}
bool scan_patches(char* scanmatch, std::vector<std::string>& pArchiveNames)
{
int i;
char path[512];
for (i = 1; i <= 99; i++)
{
if (i != 1)
{
sprintf(path, "%s-%d.MPQ", scanmatch, i);
}
else
{
sprintf(path, "%s.MPQ", scanmatch);
}
#ifdef __linux__
if (FILE* h = fopen64(path, "rb"))
#else
if (FILE* h = fopen(path, "rb"))
#endif
{
fclose(h);
//matches.push_back(path);
pArchiveNames.push_back(path);
}
}
return(true);
}
bool fillArchiveNameVector(std::vector<std::string>& pArchiveNames, int iCoreNumber)
{
if (iCoreNumber == CLIENT_TBC || iCoreNumber ==2)
{
if (!hasInputPathParam)
{
getGamePath();
}
}
printf("\n Game path: %s\n", input_path);
char path[512];
// open expansion and common files
if (iCoreNumber == CLIENT_CLASSIC)
{
printf(" Opening data files from data directory.\n");
sprintf(path, "%s/Data/terrain.MPQ", input_path);
pArchiveNames.push_back(path);
sprintf(path, "%s/Data/model.MPQ", input_path);
pArchiveNames.push_back(path);
sprintf(path, "%s/Data/texture.MPQ", input_path);
pArchiveNames.push_back(path);
sprintf(path, "%s/Data/wmo.MPQ", input_path);
pArchiveNames.push_back(path);
sprintf(path, "%s/Data/misc.MPQ", input_path);
pArchiveNames.push_back(path);
sprintf(path, "%s/Data/patch", input_path);
if (!scan_patches(path, pArchiveNames))
{
return(false);
}
}
if (iCoreNumber == CLIENT_TBC || iCoreNumber == CLIENT_WOTLK)
{
string in_path(input_path);
std::vector<std::string> locales, searchLocales;
searchLocales.push_back("enGB");
searchLocales.push_back("enUS");
searchLocales.push_back("deDE");
searchLocales.push_back("esES");
searchLocales.push_back("frFR");
searchLocales.push_back("koKR");
searchLocales.push_back("zhCN");
searchLocales.push_back("zhTW");
searchLocales.push_back("enCN");
searchLocales.push_back("enTW");
searchLocales.push_back("esMX");
searchLocales.push_back("ruRU");
for (std::vector<std::string>::iterator i = searchLocales.begin(); i != searchLocales.end(); ++i)
{
std::string localePath = in_path + *i;
// check if locale exists:
struct stat status;
if (stat(localePath.c_str(), &status))
continue;
if ((status.st_mode & S_IFDIR) == 0)
continue;
printf(" Found locale '%s'\n", i->c_str());
locales.push_back(*i);
}
printf("\n");
// open locale expansion and common files
printf(" Adding data files from locale directories.\n");
for (std::vector<std::string>::iterator i = locales.begin(); i != locales.end(); ++i)
{
pArchiveNames.push_back(in_path + *i + "/locale-" + *i + ".MPQ");
pArchiveNames.push_back(in_path + *i + "/expansion-locale-" + *i + ".MPQ");
if (iCoreNumber == CLIENT_WOTLK)
{
pArchiveNames.push_back(in_path + *i + "/lichking-locale-" + *i + ".MPQ");
}
}
printf(" Opening data files from data directory.\n");
pArchiveNames.push_back(input_path + string("common.MPQ"));
if (iCoreNumber == CLIENT_WOTLK)
{
pArchiveNames.push_back(input_path + string("common-2.MPQ"));
}
pArchiveNames.push_back(input_path + string("expansion.MPQ"));
if (iCoreNumber == CLIENT_WOTLK)
{
pArchiveNames.push_back(input_path + string("lichking.MPQ"));
}
// now, scan for the patch levels in the core dir
printf(" Scanning patch levels from data directory.\n");
sprintf(path, "%spatch", input_path);
if (!scan_patches(path, pArchiveNames))
{
return(false);
}
// now, scan for the patch levels in locale dirs
printf(" Scanning patch levels from locale directories.\n");
bool foundOne = false;
for (std::vector<std::string>::iterator i = locales.begin(); i != locales.end(); ++i)
{
printf(" Locale: %s\n", i->c_str());
sprintf(path, "%s%s/patch-%s", input_path, i->c_str(), i->c_str());
if (scan_patches(path, pArchiveNames))
foundOne = true;
}
printf("\n");
if (!foundOne)
{
printf("no locale found\n");
return false;
}
}
else
{
printf("\n");
}
return true;
}
void Usage(char* prg)
{
printf(" Usage: %s [OPTION]\n\n", prg);
printf(" Extract client database files and generate map files.\n");
printf(" -h, --help show the usage\n");
printf(" -d, --data <path> search path for game client archives\n");
printf(" -s, --small extract smaller vmaps by optimizing data. Reduces\n");
printf(" size by ~ 500MB\n");
printf("\n");
printf(" Example:\n");
printf(" - use data path and create larger vmaps:\n");
printf(" %s -l -d \"c:\\games\\world of warcraft\"\n", prg);
}
bool processArgv(int argc, char** argv)
{
bool result = true;
char* param = NULL;
for (int i = 1; i < argc; ++i)
{
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0 )
{
result = false;
break;
}
else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--small") == 0 )
{
result = true;
}
else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--data") == 0 )
{
param = argv[++i];
if (!param)
{
result = false;
break;
}
result = true;
strcpy(input_path, param);
if (input_path[strlen(input_path) - 1] != '\\' || input_path[strlen(input_path) - 1] != '/')
{
strcat(input_path, "/");
}
}
else
{
result = false;
break;
}
}
if (!result)
{
Usage(argv[0]);
}
return result;
}
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// Main
//
// The program must be run with two command line arguments
//
// Arg1 - The source MPQ name (for testing reading and file find)
// Arg2 - Listfile name
//
int main(int argc, char** argv)
{
int thisBuild = getBuildNumber();
int iCoreNumber = getCoreNumberFromBuild(thisBuild);
std::string outDir = std::string(output_path) + "/vmaps";
showBanner("Vertical Map Asset Extractor", iCoreNumber);
setVMapMagicVersion(iCoreNumber, szRawVMAPMagic);
showWebsiteBanner();
bool success = true;
// Use command line arguments, when some
if (!processArgv(argc, argv))
{
return 1;
}
// some simple check if working dir is dirty
else
{
std::string sdir = std::string(szWorkDirWmo) + "/dir";
std::string sdir_bin = std::string(szWorkDirWmo) + "/dir_bin";
struct stat status;
bool dirty = false;
if (!stat(sdir.c_str(), &status) || !stat(sdir_bin.c_str(), &status))
{
printf(" Your %s directory seems to exist, please delete it!\n", szWorkDirWmo);
dirty = true;
}
if (!stat(outDir.c_str(), &status))
{
printf(" Your %s directory seems to exist, please delete it!\n", outDir.c_str());
dirty = true;
}
if (dirty)
{
printf(" <press return to exit>");
char garbage[2];
int ret = scanf("%c", garbage);
return 1;
}
}
printf(" Beginning work ....\n");
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// Create the working and ouput directories
CreateDir(std::string(szWorkDirWmo));
CreateDir(outDir);
// prepare archive name list
std::vector<std::string> archiveNames;
fillArchiveNameVector(archiveNames, iCoreNumber);
for (size_t i = 0; i < archiveNames.size(); ++i)
{
MPQArchive* archive = new MPQArchive(archiveNames[i].c_str());
if (!gOpenArchives.size() || gOpenArchives.front() != archive)
{
delete archive;
}
}
if (gOpenArchives.empty())
{
printf("FATAL ERROR: None MPQ archive found by path '%s'. Use -d option with proper path.\n", input_path);
return 1;
}
if (iCoreNumber == CLIENT_CLASSIC)
{
ReadLiquidTypeTableDBC();
}
// extract data
if (success)
{
success = ExtractWmo(iCoreNumber, szRawVMAPMagic);
}
// Open map.dbc
if (success)
{
DBCFile* dbc = new DBCFile("DBFilesClient\\Map.dbc");
if (!dbc->open())
{
delete dbc;
printf("FATAL ERROR: Map.dbc not found in data file.\n");
return 1;
}
map_count = dbc->getRecordCount();
map_ids = new map_id[map_count];
for (unsigned int x = 0; x < map_count; ++x)
{
map_ids[x].id = dbc->getRecord(x).getUInt(0);
strcpy(map_ids[x].name, dbc->getRecord(x).getString(1));
printf(" Map %d - %s\n", map_ids[x].id, map_ids[x].name);
}
delete dbc;
ParseMapFiles(iCoreNumber);
delete [] map_ids;
//nError = ERROR_SUCCESS;
// Extract models, listed in DameObjectDisplayInfo.dbc
ExtractGameobjectModels(iCoreNumber, szRawVMAPMagic);
}
delete [] LiqType;
if (!success)
{
printf("ERROR: Extract for %s. Work NOT complete.\n Precise vector data=%d.\nPress any key.\n", szRawVMAPMagic, preciseVectorData);
getchar();
return 1;
}
success = AssembleVMAP(std::string(szWorkDirWmo), outDir, szRawVMAPMagic);
if (!success)
{
printf("ERROR: VMAP building for %s NOT completed", szRawVMAPMagic);
getchar();
return 1;
}
printf("\n");
printf(" VMAP building complete. No errors.\n");
return 0;
}

View file

@ -0,0 +1,75 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#ifndef VMAPEXPORT_H
#define VMAPEXPORT_H
#include <string>
#include <set>
/**
* @brief
*
*/
typedef std::set<std::string> StringSet;
/**
* @brief
*
*/
enum ModelFlags
{
MOD_M2 = 1,
MOD_WORLDSPAWN = 1 << 1,
MOD_HAS_BOUND = 1 << 2
};
extern char const szWorkDirWmo[]; /**< TODO */
//extern const char* szRawVMAPMagic; /**< vmap magic string for extracted raw vmap data */
/**
* @brief Test if the specified file exists in the building directory
*
* @param file
* @return bool
*/
bool FileExists(const char* file);
/**
* @brief Get "uniform" name for a path (a uniform name has the format <md5hash>-<filename>.<ext>)
*
* @param path
* @return string
*/
std::string GetUniformName(std::string& path);
/**
* @brief Get extension for a file
*
* @param file
* @return extension, if found, or empty string if not
*/
std::string GetExtension(std::string& file);
#endif

View file

@ -0,0 +1,129 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#include <cstdio>
#include "vmapexport.h"
#include "wdtfile.h"
WDTFile::WDTFile(char* file_name, char* file_name1): WDT(file_name)
{
filename.assign(file_name1);
}
bool WDTFile::init(char* map_id, unsigned int mapID)
{
if (WDT.isEof())
{
//printf("Can't find WDT file.\n");
return false;
}
char fourcc[5];
uint32 size;
std::string dirname = std::string(szWorkDirWmo) + "/dir_bin";
FILE* dirfile;
dirfile = fopen(dirname.c_str(), "ab");
if (!dirfile)
{
printf("Can't open dirfile!'%s'\n", dirname.c_str());
return false;
}
while (!WDT.isEof())
{
WDT.read(fourcc, 4);
WDT.read(&size, 4);
flipcc(fourcc);
fourcc[4] = 0;
size_t nextpos = WDT.getPos() + size;
if (!strcmp(fourcc, "MAIN"))
{
}
if (!strcmp(fourcc, "MWMO"))
{
// global map objects
if (size)
{
char* buf = new char[size];
WDT.read(buf, size);
char* p = buf;
int q = 0;
gWmoInstansName = new string[size];
while (p < buf + size)
{
string path(p);
gWmoInstansName[q++] = GetUniformName(path);
p = p + strlen(p) + 1;
}
delete[] buf;
}
}
else if (!strcmp(fourcc, "MODF"))
{
// global wmo instance data
if (size)
{
gnWMO = (int)size / 64;
string gWMO_mapname;
string fake_mapname;
fake_mapname = "65 65 ";
//gWMO_mapname = fake_mapname + filename;
gWMO_mapname = fake_mapname + std::string(map_id);
for (int i = 0; i < gnWMO; ++i)
{
int id;
WDT.read(&id, 4);
WMOInstance inst(WDT, gWmoInstansName[id], mapID, 65, 65, dirfile);
}
delete[] gWmoInstansName;
}
}
WDT.seek((int)nextpos);
}
fclose(dirfile);
return true;
}
WDTFile::~WDTFile(void)
{
WDT.close();
}
ADTFile* WDTFile::GetMap(int x, int z)
{
if (!(x >= 0 && z >= 0 && x < 64 && z < 64))
{
return NULL;
}
char name[512];
sprintf(name, "World\\Maps\\%s\\%s_%d_%d.adt", filename.c_str(), filename.c_str(), x, z);
return new ADTFile(name);
}

View file

@ -0,0 +1,79 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#ifndef WDTFILE_H
#define WDTFILE_H
#include <string>
#include <ml/mpq.h>
#include "wmo.h"
#include "adtfile.h"
/**
* @brief
*
*/
class WDTFile
{
public:
/**
* @brief
*
* @param file_name
* @param file_name1
*/
WDTFile(char* file_name, char* file_name1);
/**
* @brief
*
*/
~WDTFile(void);
/**
* @brief
*
* @param map_id
* @param mapID
* @return bool
*/
bool init(char* map_id, unsigned int mapID);
std::string* gWmoInstansName; /**< TODO */
int gnWMO, nMaps; /**< TODO */
/**
* @brief
*
* @param x
* @param z
* @return ADTFile
*/
ADTFile* GetMap(int x, int z);
private:
MPQFile WDT; /**< TODO */
bool maps[64][64]; /**< TODO */
std::string filename; /**< TODO */
};
#endif

View file

@ -0,0 +1,745 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#include "vmapexport.h"
#include "wmo.h"
#include "vec3d.h"
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <map>
#include <fstream>
#include <ExtractorCommon.h>
#undef min
#undef max
extern uint16* LiqType;
extern bool preciseVectorData;
extern ArchiveSet gOpenArchives;
WMORoot::WMORoot(std::string& filename) : filename(filename)
{
}
bool WMORoot::open()
{
MPQFile f(filename.c_str());
if (f.isEof())
{
printf(" No such file %s.\n", filename.c_str());
return false;
}
uint32 size;
char fourcc[5];
while (!f.isEof())
{
f.read(fourcc, 4);
f.read(&size, 4);
flipcc(fourcc);
fourcc[4] = 0;
size_t nextpos = f.getPos() + size;
if (!strcmp(fourcc, "MOHD")) //header
{
f.read(&nTextures, 4);
f.read(&nGroups, 4);
f.read(&nP, 4);
f.read(&nLights, 4);
f.read(&nModels, 4);
f.read(&nDoodads, 4);
f.read(&nDoodadSets, 4);
f.read(&col, 4);
f.read(&RootWMOID, 4);
f.read(bbcorn1, 12);
f.read(bbcorn2, 12);
f.read(&liquidType, 4);
break;
}
/*
else if (!strcmp(fourcc,"MOTX"))
{
}
else if (!strcmp(fourcc,"MOMT"))
{
}
else if (!strcmp(fourcc,"MOGN"))
{
}
else if (!strcmp(fourcc,"MOGI"))
{
}
else if (!strcmp(fourcc,"MOLT"))
{
}
else if (!strcmp(fourcc,"MODN"))
{
}
else if (!strcmp(fourcc,"MODS"))
{
}
else if (!strcmp(fourcc,"MODD"))
{
}
else if (!strcmp(fourcc,"MOSB"))
{
}
else if (!strcmp(fourcc,"MOPV"))
{
}
else if (!strcmp(fourcc,"MOPT"))
{
}
else if (!strcmp(fourcc,"MOPR"))
{
}
else if (!strcmp(fourcc,"MFOG"))
{
}
*/
f.seek((int)nextpos);
}
f.close();
return true;
}
bool WMORoot::ConvertToVMAPRootWmo(FILE* pOutfile, const void *szRawVMAPMagic)
{
//printf("Convert RootWmo...\n");
fwrite(szRawVMAPMagic, 1, 8, pOutfile);
unsigned int nVectors = 0;
fwrite(&nVectors, sizeof(nVectors), 1, pOutfile); // will be filled later
fwrite(&nGroups, 4, 1, pOutfile);
fwrite(&RootWMOID, 4, 1, pOutfile);
return true;
}
WMORoot::~WMORoot()
{
}
WMOGroup::WMOGroup(std::string& filename) : filename(filename),
MOPY(0), MOVI(0), MoviEx(0), MOVT(0), MOBA(0), MobaEx(0), hlq(0), LiquEx(0), LiquBytes(0)
{
}
bool WMOGroup::open()
{
MPQFile f(filename.c_str());
if (f.isEof())
{
printf(" No such file.\n");
return false;
}
uint32 size;
char fourcc[5];
while (!f.isEof())
{
f.read(fourcc, 4);
f.read(&size, 4);
flipcc(fourcc);
if (!strcmp(fourcc, "MOGP")) //Fix sizeoff = Data size.
{
size = 68;
}
fourcc[4] = 0;
size_t nextpos = f.getPos() + size;
LiquEx_size = 0;
liquflags = 0;
if (!strcmp(fourcc, "MOGP")) //header
{
f.read(&groupName, 4);
f.read(&descGroupName, 4);
f.read(&mogpFlags, 4);
f.read(bbcorn1, 12);
f.read(bbcorn2, 12);
f.read(&moprIdx, 2);
f.read(&moprNItems, 2);
f.read(&nBatchA, 2);
f.read(&nBatchB, 2);
f.read(&nBatchC, 4);
f.read(&fogIdx, 4);
f.read(&liquidType, 4);
f.read(&groupWMOID, 4);
}
else if (!strcmp(fourcc, "MOPY"))
{
MOPY = new char[size];
mopy_size = size;
nTriangles = (int)size / 2;
f.read(MOPY, size);
}
else if (!strcmp(fourcc, "MOVI"))
{
MOVI = new uint16[size / 2];
f.read(MOVI, size);
}
else if (!strcmp(fourcc, "MOVT"))
{
MOVT = new float[size / 4];
f.read(MOVT, size);
nVertices = (int)size / 12;
}
else if (!strcmp(fourcc, "MONR"))
{
}
else if (!strcmp(fourcc, "MOTV"))
{
}
else if (!strcmp(fourcc, "MOBA"))
{
MOBA = new uint16[size / 2];
moba_size = size / 2;
f.read(MOBA, size);
}
else if (!strcmp(fourcc, "MLIQ"))
{
liquflags |= 1;
hlq = new WMOLiquidHeader;
f.read(hlq, 0x1E);
LiquEx_size = sizeof(WMOLiquidVert) * hlq->xverts * hlq->yverts;
LiquEx = new WMOLiquidVert[hlq->xverts * hlq->yverts];
f.read(LiquEx, LiquEx_size);
int nLiquBytes = hlq->xtiles * hlq->ytiles;
LiquBytes = new char[nLiquBytes];
f.read(LiquBytes, nLiquBytes);
/* std::ofstream llog("Buildings/liquid.log", ios_base::out | ios_base::app);
llog << filename;
llog << "\nbbox: " << bbcorn1[0] << ", " << bbcorn1[1] << ", " << bbcorn1[2] << " | " << bbcorn2[0] << ", " << bbcorn2[1] << ", " << bbcorn2[2];
llog << "\nlpos: " << hlq->pos_x << ", " << hlq->pos_y << ", " << hlq->pos_z;
llog << "\nx-/yvert: " << hlq->xverts << "/" << hlq->yverts << " size: " << size << " expected size: " << 30 + hlq->xverts*hlq->yverts*8 + hlq->xtiles*hlq->ytiles << std::endl;
llog.close(); */
}
f.seek((int)nextpos);
}
f.close();
return true;
}
int WMOGroup::ConvertToVMAPGroupWmo(FILE* output, WMORoot* rootWMO, bool pPreciseVectorData, int iCoreNumber)
{
fwrite(&mogpFlags, sizeof(uint32), 1, output);
fwrite(&groupWMOID, sizeof(uint32), 1, output);
// group bound
fwrite(bbcorn1, sizeof(float), 3, output);
fwrite(bbcorn2, sizeof(float), 3, output);
fwrite(&liquflags, sizeof(uint32), 1, output);
int nColTriangles = 0;
if (pPreciseVectorData)
{
char GRP[] = "GRP ";
fwrite(GRP, 1, 4, output);
int k = 0;
int moba_batch = moba_size / 12;
MobaEx = new int[moba_batch * 4];
for (int i = 8; i < moba_size; i += 12)
{
MobaEx[k++] = MOBA[i];
}
int moba_size_grp = moba_batch * 4 + 4;
fwrite(&moba_size_grp, 4, 1, output);
fwrite(&moba_batch, 4, 1, output);
fwrite(MobaEx, 4, k, output);
delete [] MobaEx;
uint32 nIdexes = nTriangles * 3;
if (fwrite("INDX", 4, 1, output) != 1)
{
printf("Error while writing file nbraches ID");
exit(0);
}
int wsize = sizeof(uint32) + sizeof(unsigned short) * nIdexes;
if (fwrite(&wsize, sizeof(int), 1, output) != 1)
{
printf("Error while writing file wsize");
exit(0);
}
if (fwrite(&nIdexes, sizeof(uint32), 1, output) != 1)
{
printf("Error while writing file nIndexes");
exit(0);
}
if (nIdexes > 0)
{
if (fwrite(MOVI, sizeof(unsigned short), nIdexes, output) != nIdexes)
{
printf("Error while writing file indexarray");
exit(0);
}
}
if (fwrite("VERT", 4, 1, output) != 1)
{
printf("Error while writing file nbraches ID");
exit(0);
}
wsize = sizeof(int) + sizeof(float) * 3 * nVertices;
if (fwrite(&wsize, sizeof(int), 1, output) != 1)
{
printf("Error while writing file wsize");
exit(0);
}
if (fwrite(&nVertices, sizeof(int), 1, output) != 1)
{
printf("Error while writing file nVertices");
exit(0);
}
if (nVertices > 0)
{
if (fwrite(MOVT, sizeof(float) * 3, nVertices, output) != nVertices)
{
printf("Error while writing file vectors");
exit(0);
}
}
nColTriangles = nTriangles;
}
else
{
char GRP[] = "GRP ";
fwrite(GRP, 1, 4, output);
int k = 0;
int moba_batch = moba_size / 12;
MobaEx = new int[moba_batch * 4];
for (int i = 8; i < moba_size; i += 12)
{
MobaEx[k++] = MOBA[i];
}
int moba_size_grp = moba_batch * 4 + 4;
fwrite(&moba_size_grp, 4, 1, output);
fwrite(&moba_batch, 4, 1, output);
fwrite(MobaEx, 4, k, output);
delete [] MobaEx;
//-------INDX------------------------------------
//-------MOPY--------
MoviEx = new uint16[nTriangles * 3]; // "worst case" size...
int* IndexRenum = new int[nVertices];
memset(IndexRenum, 0xFF, nVertices * sizeof(int));
for (int i = 0; i < nTriangles; ++i)
{
// Skip no collision triangles
if (MOPY[2 * i]&WMO_MATERIAL_NO_COLLISION ||
!(MOPY[2 * i] & (WMO_MATERIAL_HINT | WMO_MATERIAL_COLLIDE_HIT)))
{ continue; }
// Use this triangle
for (int j = 0; j < 3; ++j)
{
IndexRenum[MOVI[3 * i + j]] = 1;
MoviEx[3 * nColTriangles + j] = MOVI[3 * i + j];
}
++nColTriangles;
}
// assign new vertex index numbers
int nColVertices = 0;
for (uint32 i = 0; i < nVertices; ++i)
{
if (IndexRenum[i] == 1)
{
IndexRenum[i] = nColVertices;
++nColVertices;
}
}
// translate triangle indices to new numbers
for (int i = 0; i < 3 * nColTriangles; ++i)
{
assert(MoviEx[i] < nVertices);
MoviEx[i] = IndexRenum[MoviEx[i]];
}
// write triangle indices
int INDX[] = {0x58444E49, nColTriangles * 6 + 4, nColTriangles * 3};
fwrite(INDX, 4, 3, output);
fwrite(MoviEx, 2, nColTriangles * 3, output);
// write vertices
int VERT[] = {0x54524556, nColVertices * 3 * sizeof(float) + 4, nColVertices}; // "VERT"
int check = 3 * nColVertices;
fwrite(VERT, 4, 3, output);
for (uint32 i = 0; i < nVertices; ++i)
if (IndexRenum[i] >= 0)
{
check -= fwrite(MOVT + 3 * i, sizeof(float), 3, output);
}
assert(check == 0);
delete [] MoviEx;
delete [] IndexRenum;
}
//------LIQU------------------------
if (LiquEx_size != 0)
{
int LIQU_h[] = {0x5551494C, sizeof(WMOLiquidHeader) + LiquEx_size + hlq->xtiles* hlq->ytiles}; // "LIQU"
fwrite(LIQU_h, 4, 2, output);
// according to WoW.Dev Wiki:
uint32 liquidEntry;
if (rootWMO->liquidType & 4)
{
liquidEntry = liquidType;
}
else if (liquidType == 15)
{
liquidEntry = 0;
}
else
{
liquidEntry = liquidType + 1;
}
if (!liquidEntry)
{
int v1; // edx@1
int v2; // eax@1
v1 = hlq->xtiles * hlq->ytiles;
v2 = 0;
if (v1 > 0)
{
while ((LiquBytes[v2] & 0xF) == 15)
{
++v2;
if (v2 >= v1)
{
break;
}
}
if (v2 < v1 && (LiquBytes[v2] & 0xF) != 15)
{
liquidEntry = (LiquBytes[v2] & 0xF) + 1;
}
}
}
if (liquidEntry && liquidEntry < 21)
{
switch (((uint8)liquidEntry - 1) & 3)
{
case 0:
if (iCoreNumber == CLIENT_WOTLK || iCoreNumber == CLIENT_CATA)
{
liquidEntry = ((mogpFlags & 0x80000) != 0) + 13;
}
if (iCoreNumber == CLIENT_CLASSIC || iCoreNumber == CLIENT_TBC)
{
liquidEntry = ((mogpFlags & 0x80000) != 0) + 1;
if (iCoreNumber == CLIENT_TBC)
{
if (liquidEntry == 1) // water type
{
if (filename.find("coilfang_raid") != string::npos)
{
// set water type to special coilfang raid water
liquidEntry = 41;
}
}
}
}
break;
case 1:
if (iCoreNumber == CLIENT_CLASSIC || iCoreNumber == CLIENT_TBC)
{
liquidEntry = 2; // ocean
}
if (iCoreNumber == CLIENT_WOTLK || iCoreNumber == CLIENT_CATA)
{
liquidEntry = 14;
}
break;
case 2:
if (iCoreNumber == CLIENT_CLASSIC || iCoreNumber == CLIENT_TBC)
{
liquidEntry = 3; // magma
}
if (iCoreNumber == CLIENT_WOTLK || iCoreNumber == CLIENT_CATA)
{
liquidEntry = 19;
}
break;
case 3:
if (iCoreNumber == CLIENT_CLASSIC || iCoreNumber == CLIENT_TBC)
{
if ((filename.find("stratholme_raid") != string::npos) || (filename.find("Stratholme_raid") != string::npos))
{
liquidEntry = 21; // Naxxramas slime
}
else
{
liquidEntry = 4;
} // Normal slime
}
if (iCoreNumber == CLIENT_WOTLK || iCoreNumber == CLIENT_CATA)
{
liquidEntry = 20;
}
break;
default:
break;
}
}
hlq->type = liquidEntry;
/* std::ofstream llog("Buildings/liquid.log", ios_base::out | ios_base::app);
llog << filename;
llog << ":\nliquidEntry: " << liquidEntry << " type: " << hlq->type << " (root:" << rootWMO->liquidType << " group:" << liquidType << ")\n";
llog.close(); */
fwrite(hlq, sizeof(WMOLiquidHeader), 1, output);
// only need height values, the other values are unknown anyway
for (uint32 i = 0; i < LiquEx_size / sizeof(WMOLiquidVert); ++i)
{
fwrite(&LiquEx[i].height, sizeof(float), 1, output);
}
// todo: compress to bit field
fwrite(LiquBytes, 1, hlq->xtiles * hlq->ytiles, output);
}
return nColTriangles;
}
WMOGroup::~WMOGroup()
{
delete [] MOPY;
delete [] MOVI;
delete [] MOVT;
delete [] MOBA;
delete hlq;
delete [] LiquEx;
delete [] LiquBytes;
}
//WmoInstName is in the form MD5/name.wmo
WMOInstance::WMOInstance(MPQFile& f, std::string& WmoInstName, uint32 mapID, uint32 tileX, uint32 tileY, FILE* pDirfile)
{
pos = Vec3D(0, 0, 0);
float ff[3];
f.read(&id, 4);
f.read(ff, 12);
pos = Vec3D(ff[0], ff[1], ff[2]);
f.read(ff, 12);
rot = Vec3D(ff[0], ff[1], ff[2]);
f.read(ff, 12);
pos2 = Vec3D(ff[0], ff[1], ff[2]);
f.read(ff, 12);
pos3 = Vec3D(ff[0], ff[1], ff[2]);
f.read(&d2, 4);
uint16 trash, adtId;
f.read(&adtId, 2);
f.read(&trash, 2);
//-----------add_in _dir_file----------------
char tempname[512];
sprintf(tempname, "%s/%s", szWorkDirWmo, WmoInstName.c_str());
FILE* input;
input = fopen(tempname, "r+b");
if (!input)
{
printf("WMOInstance::WMOInstance: couldn't open %s\n", tempname);
return;
}
fseek(input, 8, SEEK_SET); // get the correct no of vertices
int nVertices;
size_t file_read = fread(&nVertices, sizeof(int), 1, input);
fclose(input);
if (nVertices == 0 || file_read <= 0)
{
return;
}
float x, z;
x = pos.x;
z = pos.z;
if (x == 0 && z == 0)
{
pos.x = 533.33333f * 32;
pos.z = 533.33333f * 32;
}
pos = fixCoords(pos);
pos2 = fixCoords(pos2);
pos3 = fixCoords(pos3);
float scale = 1.0f;
uint32 flags = MOD_HAS_BOUND;
if (tileX == 65 && tileY == 65)
{
flags |= MOD_WORLDSPAWN;
}
//write mapID, tileX, tileY, Flags, ID, Pos, Rot, Scale, Bound_lo, Bound_hi, name
fwrite(&mapID, sizeof(uint32), 1, pDirfile);
fwrite(&tileX, sizeof(uint32), 1, pDirfile);
fwrite(&tileY, sizeof(uint32), 1, pDirfile);
fwrite(&flags, sizeof(uint32), 1, pDirfile);
fwrite(&adtId, sizeof(uint16), 1, pDirfile);
fwrite(&id, sizeof(uint32), 1, pDirfile);
fwrite(&pos, sizeof(float), 3, pDirfile);
fwrite(&rot, sizeof(float), 3, pDirfile);
fwrite(&scale, sizeof(float), 1, pDirfile);
fwrite(&pos2, sizeof(float), 3, pDirfile);
fwrite(&pos3, sizeof(float), 3, pDirfile);
uint32 nlen = WmoInstName.length();
fwrite(&nlen, sizeof(uint32), 1, pDirfile);
fwrite(WmoInstName.c_str(), sizeof(char), nlen, pDirfile);
}
bool ExtractSingleWmo(std::string& fname, int iCoreNumber, const void *szRawVMAPMagic)
{
// Copy files from archive
char szLocalFile[1024];
string plain_name = GetUniformName(fname);
sprintf(szLocalFile, "%s/%s", szWorkDirWmo, plain_name.c_str());
if (FileExists(szLocalFile))
{
return true;
}
int p = 0;
//Select root wmo files
const char* rchr = strrchr(plain_name.c_str(), '_');
if (rchr != NULL)
{
char cpy[4];
strncpy((char*)cpy, rchr, 4);
for (int i = 0; i < 4; ++i)
{
int m = cpy[i];
if (isdigit(m))
{
p++;
}
}
}
if (p == 3)
{
return true;
}
bool file_ok = true;
printf(" Extracting %s\n", fname.c_str());
WMORoot froot(fname);
if (!froot.open())
{
printf("Couldn't open RootWmo!!!\n");
return true;
}
FILE* output = fopen(szLocalFile, "wb");
if (!output)
{
printf("Couldn't open %s for writing!\n", szLocalFile);
return false;
}
froot.ConvertToVMAPRootWmo(output,szRawVMAPMagic);
int Wmo_nVertices = 0;
if (froot.nGroups != 0)
{
for (uint32 i = 0; i < froot.nGroups; ++i)
{
char temp[1024];
strcpy(temp, fname.c_str());
temp[fname.length() - 4] = 0;
char groupFileName[1024];
sprintf(groupFileName, "%s_%03d.wmo", temp, i);
string s(groupFileName);
WMOGroup fgroup(s);
if (!fgroup.open())
{
printf("Could not open all Group file for: %s\n", plain_name.c_str());
file_ok = false;
break;
}
Wmo_nVertices += fgroup.ConvertToVMAPGroupWmo(output, &froot, preciseVectorData, iCoreNumber);
}
}
fseek(output, 8, SEEK_SET); // store the correct no of vertices
fwrite(&Wmo_nVertices, sizeof(int), 1, output);
fclose(output);
// Delete the extracted file in the case of an error
if (!file_ok)
{
remove(szLocalFile);
}
return true;
}
bool ExtractWmo(int iCoreNumber, const void *szRawVMAPMagic)
{
bool success = true;
for (ArchiveSet::const_iterator ar_itr = gOpenArchives.begin(); ar_itr != gOpenArchives.end() && success; ++ar_itr)
{
vector<std::string> filelist;
(*ar_itr)->GetFileListTo(filelist);
for (vector<std::string>::iterator fname = filelist.begin(); fname != filelist.end() && success; ++fname)
{
if (fname->find(".wmo") != string::npos)
{
ExtractSingleWmo(*fname, iCoreNumber, szRawVMAPMagic);
}
}
}
if (success)
{
printf("\n Extraction of WMO's complete, No fatal errors\n");
}
{ printf("\n Reading Maps\n"); }
{ printf(" _______________________________________________________\n"); }
return success;
}

View file

@ -0,0 +1,227 @@
/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2020 MaNGOS <https://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#ifndef WMO_H
#define WMO_H
#define TILESIZE (533.33333f)
#define CHUNKSIZE ((TILESIZE) / 16.0f)
#include <string>
#include <set>
#include "vec3d.h"
#include <ml/mpq.h>
#include <ml/loadlib.h>
// MOPY flags
#define WMO_MATERIAL_NOCAMCOLLIDE 0x01
#define WMO_MATERIAL_DETAIL 0x02
#define WMO_MATERIAL_NO_COLLISION 0x04
#define WMO_MATERIAL_HINT 0x08
#define WMO_MATERIAL_RENDER 0x10
#define WMO_MATERIAL_COLLIDE_HIT 0x20
#define WMO_MATERIAL_WALL_SURFACE 0x40
/**
* @brief
*
*/
class WMORoot
{
public:
uint32 nTextures, nGroups, nP, nLights, nModels, nDoodads, nDoodadSets, RootWMOID, liquidType; /**< TODO */
unsigned int col; /**< TODO */
float bbcorn1[3]; /**< TODO */
float bbcorn2[3]; /**< TODO */
/**
* @brief
*
* @param filename
*/
WMORoot(std::string& filename);
/**
* @brief
*
*/
~WMORoot();
/**
* @brief
*
* @return bool
*/
bool open();
/**
* @brief
*
* @param output
* @return bool
*/
bool ConvertToVMAPRootWmo(FILE* output, const void *szRawVMAPMagic);
private:
std::string filename; /**< TODO */
};
/**
* @brief
*
*/
struct WMOLiquidHeader
{
int xverts, yverts, xtiles, ytiles; /**< TODO */
float pos_x; /**< TODO */
float pos_y; /**< TODO */
float pos_z; /**< TODO */
short type; /**< TODO */
};
/**
* @brief
*
*/
struct WMOLiquidVert
{
uint16 unk1; /**< TODO */
uint16 unk2; /**< TODO */
float height; /**< TODO */
};
/**
* @brief
*
*/
class WMOGroup
{
public:
// MOGP
int groupName, descGroupName, mogpFlags; /**< TODO */
float bbcorn1[3]; /**< TODO */
float bbcorn2[3]; /**< TODO */
uint16 moprIdx; /**< TODO */
uint16 moprNItems; /**< TODO */
uint16 nBatchA; /**< TODO */
uint16 nBatchB; /**< TODO */
uint32 nBatchC, fogIdx, liquidType, groupWMOID; /**< TODO */
int mopy_size, moba_size; /**< TODO */
int LiquEx_size; /**< TODO */
unsigned int nVertices; /**< number when loaded */
int nTriangles; /**< number when loaded */
char* MOPY; /**< TODO */
uint16* MOVI; /**< TODO */
uint16* MoviEx; /**< TODO */
float* MOVT; /**< TODO */
uint16* MOBA; /**< TODO */
int* MobaEx; /**< TODO */
WMOLiquidHeader* hlq; /**< TODO */
WMOLiquidVert* LiquEx; /**< TODO */
char* LiquBytes; /**< TODO */
uint32 liquflags; /**< TODO */
/**
* @brief
*
* @param filename
*/
WMOGroup(std::string& filename);
/**
* @brief
*
*/
~WMOGroup();
/**
* @brief
*
* @return bool
*/
bool open();
/**
* @brief
*
* @param output
* @param rootWMO
* @param pPreciseVectorData
* @return int
*/
int ConvertToVMAPGroupWmo(FILE* output, WMORoot* rootWMO, bool pPreciseVectorData, int iCoreNumber);
private:
std::string filename; /**< TODO */
};
/**
* @brief
*
*/
class WMOInstance
{
static std::set<int> ids; /**< TODO */
public:
std::string MapName; /**< TODO */
int currx; /**< TODO */
int curry; /**< TODO */
WMOGroup* wmo; /**< TODO */
Vec3D pos; /**< TODO */
Vec3D pos2, pos3, rot; /**< TODO */
uint32 indx, id, d2, d3; /**< TODO */
int doodadset; /**< TODO */
/**
* @brief
*
* @param f
* @param WmoInstName
* @param mapID
* @param tileX
* @param tileY
* @param pDirfile
*/
WMOInstance(MPQFile& f, std::string& WmoInstName, uint32 mapID, uint32 tileX, uint32 tileY, FILE* pDirfile);
/**
* @brief
*
*/
static void reset();
};
/**
* @brief
*
* @param fname
* @return bool
*/
bool ExtractSingleWmo(std::string& fname, int iCoreNumber, const void *szRawVMAPMagic);
/**
* @brief
*
* @param
* @return bool
*/
bool ExtractWmo(int iCoreNumber, const void *szRawVMAPMagic);
#endif