/* * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Affero General Public License as published by the * Free Software Foundation; either version 3 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 Affero General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "Cache.h" #include "Constants.h" #include "ContinentBuilder.h" #include "DBC.h" #include "MPQMgr.h" #include "Model.h" #include "WDT.h" #include "DetourNavMesh.h" #include "DetourNavMeshQuery.h" #include "Recast.h" #include #include MPQMgr* MPQHandler; CacheClass* Cache; void ExtractMMaps(std::set& mapIds, uint32 threads) { DBC* dbc = MPQHandler->GetDBC("Map"); printf("Map.dbc contains %u rows.\n", dbc->Records.size()); for (std::vector::iterator itr = dbc->Records.begin(); itr != dbc->Records.end(); ++itr) { uint32 mapId = (*itr)->Values[0]; // Skip this map if a list of specific maps was provided and this one is not contained in it. if (!mapIds.empty() && mapIds.find(mapId) == mapIds.end()) { if (Constants::Debug) printf("Map %u will not be built.\n", mapId); continue; } std::string name = (*itr)->Get(1); WDT wdt("World\\maps\\" + name + "\\" + name + ".wdt"); if (!wdt.IsValid) { printf("Could not find WDT data for map %u (%s)\n", mapId, name.c_str()); continue; } printf("Building %s MapId %u\n", name.c_str(), mapId); ContinentBuilder builder(name, mapId, &wdt, threads); builder.Build(); } } void ExtractDBCs() { printf("Extracting DBCs\n"); // Create the file system structure std::string baseDBCPath = "dbc/"; Utils::CreateDir(baseDBCPath); std::set DBCFiles; const size_t extLen = strlen(".dbc"); // Populate list of DBC files // We get the DBC names by going over the (guaranteed to exist) default locale files // Then we look in other locale files in case that they are available. for (std::vector::iterator itr = MPQHandler->LocaleFiles[MPQHandler->BaseLocale]->Files.begin(); itr != MPQHandler->LocaleFiles[MPQHandler->BaseLocale]->Files.end(); ++itr) if (itr->rfind(".dbc") == itr->length() - extLen) // Check if the extension is ".dbc" DBCFiles.insert(*itr); const size_t folderLen = strlen("DBFilesClient\\"); // Iterate over all available locales for (std::set::iterator itr = MPQHandler->AvailableLocales.begin(); itr != MPQHandler->AvailableLocales.end(); ++itr) { printf("Extracting DBCs for locale %s\n", MPQMgr::Languages[*itr]); std::string path = baseDBCPath; if (*itr != uint32(MPQHandler->BaseLocale)) { path += std::string(MPQMgr::Languages[*itr]) + "/"; Utils::CreateDir(path); } std::string component = "component.wow-" + std::string(MPQMgr::Languages[*itr]) + ".txt"; // Extract the component file Utils::SaveToDisk(MPQHandler->GetFileFrom(component, MPQHandler->LocaleFiles[*itr]), path + component); // Extract the DBC files for the given locale for (std::set::iterator itr2 = DBCFiles.begin(); itr2 != DBCFiles.end(); ++itr2) Utils::SaveToDisk(MPQHandler->GetFileFrom(*itr2, MPQHandler->LocaleFiles[*itr]), path + (itr2->c_str() + folderLen)); } printf("DBC extraction finished!\n"); } void ExtractGameobjectModels() { Constants::ToWoWCoords = true; printf("Extracting GameObject models\n"); std::string baseBuildingsPath = "Buildings/"; std::string baseVmapsPath = "vmaps/"; Utils::CreateDir(baseVmapsPath); Utils::CreateDir(baseBuildingsPath); FILE* modelList = fopen((baseVmapsPath + "GameObjectModels.list").c_str(), "wb"); if (!modelList) { printf("Could not create file vmaps/GameObjectModels.list, please make sure that you have the write permissions in the folder\n"); return; } DBC* dbc = MPQHandler->GetDBC("GameObjectDisplayInfo"); for (std::vector::iterator itr = dbc->Records.begin(); itr != dbc->Records.end(); ++itr) { std::string path = (*itr)->Get(1); std::string fileName = Utils::GetPlainName(path.c_str()); std::string extension = Utils::GetExtension(fileName); // Convert the extension to lowercase std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); if (extension == "mdx" || extension == "m2") { fileName = Utils::FixModelPath(fileName); Model model(path); if (model.IsBad) continue; FILE* output = fopen((baseBuildingsPath + fileName).c_str(), "wb"); if (!output) { printf("Could not create file %s, please check that you have write permissions\n", (baseBuildingsPath + fileName).c_str()); continue; } // Placeholder for 0 values int Nop[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; fwrite(Constants::VMAPMagic, 8, 1, output); uint32 numVerts = model.Header.CountBoundingVertices; fwrite(&numVerts, sizeof(uint32), 1, output); uint32 numGroups = 1; fwrite(&numGroups, sizeof(uint32), 1, output); fwrite(Nop, 4 * 3, 1, output); // rootwmoid, flags, groupid fwrite(Nop, sizeof(float), 3 * 2, output);//bbox, only needed for WMO currently fwrite(Nop, 4, 1, output);// liquidflags fwrite("GRP ", 4, 1, output); uint32 branches = 1; uint32 wsize = sizeof(branches) + sizeof(uint32) * branches; fwrite(&wsize, sizeof(uint32), 1, output); fwrite(&branches, sizeof(branches), 1, output); uint32 numTris = model.Header.CountBoundingTriangles; fwrite(&numTris, sizeof(uint32), 1, output); fwrite("INDX", 4, 1, output); wsize = sizeof(uint32) + sizeof(unsigned short) * numTris; fwrite(&wsize, sizeof(int), 1, output); fwrite(&numTris, sizeof(uint32), 1, output); uint16* indices = new uint16[numTris]; if (numTris > 0) { uint32 i = 0; for (std::vector>::iterator itr2 = model.Triangles.begin(); itr2 != model.Triangles.end(); ++itr2, ++i) { indices[i * 3 + 0] = itr2->V0; indices[i * 3 + 1] = itr2->V1; indices[i * 3 + 2] = itr2->V2; } fwrite(indices, sizeof(uint16), numTris, output); } fwrite("VERT", 4, 1, output); wsize = sizeof(int) + sizeof(float) * 3 * numVerts; fwrite(&wsize, sizeof(int), 1, output); fwrite(&numVerts, sizeof(int), 1, output); float* vertices = new float[numVerts * 3]; if (numVerts > 0) { uint32 i = 0; for (std::vector::iterator itr2 = model.Vertices.begin(); itr2 != model.Vertices.end(); ++itr2, ++i) { vertices[i * 3 + 0] = itr2->x; vertices[i * 3 + 1] = itr2->y; vertices[i * 3 + 2] = itr2->z; } fwrite(vertices, sizeof(float), numVerts * 3, output); } fclose(output); delete[] indices; delete[] vertices; uint32 displayId = (*itr)->Values[0]; uint32 pathLength = fileName.size(); fwrite(&displayId, sizeof(uint32), 1, modelList); fwrite(&pathLength, sizeof(uint32), 1, modelList); fwrite(fileName.c_str(), sizeof(char), pathLength, modelList); } else if (extension == "wmo") { WorldModelRoot model(path); FILE* output = fopen((baseBuildingsPath + fileName).c_str(), "wb"); if (!output) { printf("Could not create file %s, please check that you have write permissions\n", (baseBuildingsPath + fileName).c_str()); continue; } fwrite(Constants::VMAPMagic, 1, 8, output); uint32 numVertices = 0; fwrite(&numVertices, sizeof(uint32), 1, output); // will be filled later fwrite(&model.Header.CountGroups, sizeof(uint32), 1, output); fwrite(&model.Header.WmoId, sizeof(uint32), 1, output); const char grp[] = { 'G', 'R', 'P', ' ' }; for (std::vector::iterator itr2 = model.Groups.begin(); itr2 != model.Groups.end(); ++itr2) { const WMOGroupHeader& header = itr2->Header; fwrite(&header.Flags, sizeof(uint32), 1, output); fwrite(&header.WmoId, sizeof(uint32), 1, output); fwrite(&header.BoundingBox[0], sizeof(uint32), 1, output); fwrite(&header.BoundingBox[1], sizeof(uint32), 1, output); uint32 LiquidFlags = itr2->HasLiquidData ? 1 : 0; fwrite(&LiquidFlags, sizeof(uint32), 1, output); fwrite(grp, sizeof(char), sizeof(grp), output); uint32 k = 0; uint32 mobaBatch = itr2->MOBALength / 12; uint32* MobaEx = new uint32[mobaBatch * 4]; for (uint32 i = 8; i < itr2->MOBALength; i += 12) MobaEx[k++] = itr2->MOBA[i]; int mobaSizeGrp = mobaBatch * 4 + 4; fwrite(&mobaSizeGrp, 4, 1, output); fwrite(&mobaBatch, 4, 1, output); fwrite(MobaEx, 4, k, output); delete[] MobaEx; //@TODO: Finish this. } fclose(output); } } fclose(modelList); printf("GameObject models extraction finished!"); Constants::ToWoWCoords = false; } bool HandleArgs(int argc, char** argv, uint32& threads, std::set& mapList, bool& debugOutput, uint32& extractFlags) { char* param = nullptr; extractFlags = 0; for (int i = 1; i < argc; ++i) { if (strcmp(argv[i], "--threads") == 0) { param = argv[++i]; if (!param) return false; threads = atoi(param); printf("Using %u threads\n", threads); } else if (strcmp(argv[i], "--maps") == 0) { param = argv[++i]; if (!param) return false; char* copy = strdup(param); char* token = strtok(copy, ","); while (token) { mapList.insert(atoi(token)); token = strtok(nullptr, ","); } free(copy); printf("Extracting only provided list of maps (%u).\n", uint32(mapList.size())); } else if (strcmp(argv[i], "--debug") == 0) { param = argv[++i]; if (!param) return false; debugOutput = atoi(param); if (debugOutput) printf("Output will contain debug information (.obj files)\n"); } else if (strcmp(argv[i], "--extract") == 0) { param = argv[++i]; if (!param) return false; extractFlags = atoi(param); if (!(extractFlags & Constants::EXTRACT_FLAG_ALLOWED)) // Tried to use an invalid flag return false; printf("Detected flags: \n"); printf("* Extract DBCs: %s\n", (extractFlags & Constants::EXTRACT_FLAG_DBC) ? "Yes" : "No"); printf("* Extract Maps: %s\n", (extractFlags & Constants::EXTRACT_FLAG_MAPS) ? "Yes" : "No"); printf("* Extract VMaps: %s\n", (extractFlags & Constants::EXTRACT_FLAG_VMAPS) ? "Yes" : "No"); printf("* Extract GameObject Models: %s\n", (extractFlags & Constants::EXTRACT_FLAG_GOB_MODELS) ? "Yes" : "No"); printf("* Extract MMaps: %s\n", (extractFlags & Constants::EXTRACT_FLAG_MMAPS) ? "Yes" : "No"); } } return true; } void PrintUsage() { printf("MeshExtractor help.\n"); printf("* Use \"--threads \" to specify threads, default to 4 (Only available when extracting MMaps)\n"); printf("* Use \"--maps a,b,c,d,e\" to extract only the maps specified (Do not use spaces)\n"); printf("* Use \"--debug 1\" to generate debug information of the tiles (Only available when extracting MMaps)\n"); printf("* Use \"--extract X\" to extract the data specified by the flag X (Note: You can combine the flags with the bitwise OR operator |). Available flags are: \n"); { printf("- %u to extract DBCs\n", Constants::EXTRACT_FLAG_DBC); printf("- %u to extract Maps (Not yet implemented)\n", Constants::EXTRACT_FLAG_MAPS); printf("- %u to extract VMaps (Not yet implemented)\n", Constants::EXTRACT_FLAG_VMAPS); printf("- %u to extract GameObject models (Not yet finished, you need to run VMapAssembler on the extracted files)\n", Constants::EXTRACT_FLAG_GOB_MODELS); printf("- %u to extract MMaps (Not yet finished)\n", Constants::EXTRACT_FLAG_MMAPS); } } void LoadTile(dtNavMesh*& navMesh, const char* tile) { FILE* f = fopen(tile, "rb"); if (!f) return; MmapTileHeader header; if (fread(&header, sizeof(MmapTileHeader), 1, f) != 1) return; uint8* nav = new uint8[header.size]; if (fread(nav, header.size, 1, f) != 1) return; navMesh->addTile(nav, header.size, DT_TILE_FREE_DATA, 0, nullptr); fclose(f); } int main(int argc, char* argv[]) { _setmaxstdio(2048); uint32 threads = 4, extractFlags = 0; std::set mapIds; if (!HandleArgs(argc, argv, threads, mapIds, Constants::Debug, extractFlags)) { PrintUsage(); return -1; } if (extractFlags == 0) { printf("You must provide valid extract flags.\n"); PrintUsage(); return -1; } Cache = new CacheClass(); MPQHandler = new MPQMgr(); MPQHandler->Initialize(); if (extractFlags & Constants::EXTRACT_FLAG_DBC) ExtractDBCs(); if (extractFlags & Constants::EXTRACT_FLAG_MMAPS) ExtractMMaps(mapIds, threads); if (extractFlags & Constants::EXTRACT_FLAG_GOB_MODELS) ExtractGameobjectModels(); if (extractFlags & Constants::EXTRACT_FLAG_TEST) { float start[] = { 16226.200195f, 16257.000000f, 13.202200f }; float end[] = { 16245.725586f, 16382.465820f, 47.384956f }; // float m_spos[3]; m_spos[0] = -start[1]; m_spos[1] = start[2]; m_spos[2] = -start[0]; // float m_epos[3]; m_epos[0] = -end[1]; m_epos[1] = end[2]; m_epos[2] = -end[0]; // dtQueryFilterExt m_filter; m_filter.setIncludeFlags(Constants::POLY_AREA_ROAD | Constants::POLY_AREA_TERRAIN); m_filter.setExcludeFlags(Constants::POLY_AREA_WATER); // float m_polyPickExt[3]; m_polyPickExt[0] = 2.5f; m_polyPickExt[1] = 2.5f; m_polyPickExt[2] = 2.5f; // dtPolyRef m_startRef; dtPolyRef m_endRef; FILE* mmap = fopen("mmaps/001.mmap", "rb"); dtNavMeshParams params; int count = fread(¶ms, sizeof(dtNavMeshParams), 1, mmap); fclose(mmap); if (count != 1) { printf("main: Error reading from .mmap\n"); return 0; } dtNavMesh* navMesh = new dtNavMesh(); dtNavMeshQuery* navMeshQuery = new dtNavMeshQuery(); navMesh->init(¶ms); for (int i = 0; i <= 32; ++i) { for (int j = 0; j <= 32; ++j) { char buff[100]; sprintf(buff, "mmaps/001%02i%02i.mmtile", i, j); LoadTile(navMesh, buff); } } navMeshQuery->init(navMesh, 2048); float nearestPt[3]; navMeshQuery->findNearestPoly(m_spos, m_polyPickExt, &m_filter, &m_startRef, nearestPt); navMeshQuery->findNearestPoly(m_epos, m_polyPickExt, &m_filter, &m_endRef, nearestPt); if ( !m_startRef || !m_endRef ) { std::cerr << "Could not find any nearby poly's (" << m_startRef << "," << m_endRef << ")" << std::endl; return 0; } int hops; dtPolyRef* hopBuffer = new dtPolyRef[8192]; dtStatus status = navMeshQuery->findPath(m_startRef, m_endRef, m_spos, m_epos, &m_filter, hopBuffer, &hops, 8192); int resultHopCount; float* straightPath = new float[2048 * 3]; unsigned char* pathFlags = new unsigned char[2048]; dtPolyRef* pathRefs = new dtPolyRef[2048]; status = navMeshQuery->findStraightPath(m_spos, m_epos, hopBuffer, hops, straightPath, pathFlags, pathRefs, &resultHopCount, 2048); std::vector FinalPath; FinalPath.reserve(resultHopCount); for (int32 i = 0; i < resultHopCount; ++i) { Vector3 finalV = Utils::ToWoWCoords(Vector3(straightPath[i * 3 + 0], straightPath[i * 3 + 1], straightPath[i * 3 + 2])); FinalPath.push_back(finalV); printf("Point %f %f %f\n", finalV.x, finalV.y, finalV.z); } } return 0; }