/*
* This file is part of the TrinityCore 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 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, see .
*/
#include "Banner.h"
#include "DB2FileLoader.h"
#include "DB2FileSystemSource.h"
#include "ExtractorDB2LoadInfo.h"
#include "Locales.h"
#include "MapBuilder.h"
#include "PathCommon.h"
#include "Timer.h"
#include "Util.h"
#include "VMapManager2.h"
#include
#include
#include
constexpr char Readme[] =
{
#include "Info/readme.txt"
};
namespace
{
std::unordered_map _liquidTypes;
std::unordered_map> _mapDataForVmapInitialization;
}
namespace MMAP
{
std::unordered_map sMapStore;
namespace VMapFactory
{
std::unique_ptr CreateVMapManager()
{
std::unique_ptr vmgr = std::make_unique();
vmgr->InitializeThreadUnsafe(_mapDataForVmapInitialization);
vmgr->GetLiquidFlagsPtr = [](uint32 liquidId) -> uint32
{
auto itr = _liquidTypes.find(liquidId);
return itr != _liquidTypes.end() ? (1 << itr->second) : 0;
};
return vmgr;
}
}
}
using namespace MMAP;
bool checkDirectories(bool debugOutput, std::vector& dbcLocales)
{
if (getDirContents(dbcLocales, "dbc") == LISTFILE_DIRECTORY_NOT_FOUND || dbcLocales.empty())
{
printf("'dbc' directory is empty or does not exist\n");
return false;
}
std::vector dirFiles;
if (getDirContents(dirFiles, "maps") == LISTFILE_DIRECTORY_NOT_FOUND || dirFiles.empty())
{
printf("'maps' directory is empty or does not exist\n");
return false;
}
dirFiles.clear();
if (getDirContents(dirFiles, "vmaps/0000", "*.vmtree") == LISTFILE_DIRECTORY_NOT_FOUND || dirFiles.empty())
{
printf("'vmaps' directory is empty or does not exist\n");
return false;
}
dirFiles.clear();
if (getDirContents(dirFiles, "mmaps") == LISTFILE_DIRECTORY_NOT_FOUND)
{
if (!boost::filesystem::create_directory("mmaps"))
{
printf("'mmaps' directory does not exist and failed to create it\n");
return false;
}
}
dirFiles.clear();
if (debugOutput)
{
if (getDirContents(dirFiles, "meshes") == LISTFILE_DIRECTORY_NOT_FOUND)
{
if (!boost::filesystem::create_directory("meshes"))
{
printf("'meshes' directory does not exist and failed to create it (no place to put debugOutput files)\n");
return false;
}
}
}
return true;
}
int finish(char const* message, int returnValue)
{
printf("%s", message);
getchar(); // Wait for user input
return returnValue;
}
bool handleArgs(int argc, char** argv,
int &mapnum,
int &tileX,
int &tileY,
Optional& maxAngle,
Optional& maxAngleNotSteep,
bool &skipLiquid,
bool &skipContinents,
bool &skipJunkMaps,
bool &skipBattlegrounds,
bool &debugOutput,
bool &silent,
bool &bigBaseUnit,
char* &offMeshInputPath,
char* &file,
unsigned int& threads)
{
char* param = nullptr;
[[maybe_unused]] bool allowDebug = false;
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 >= 0.f)
maxAngle = maxangle;
else
printf("invalid option for '--maxAngle', using default\n");
}
else if (strcmp(argv[i], "--maxAngleNotSteep") == 0)
{
param = argv[++i];
if (!param)
return false;
float maxangle = atof(param);
if (maxangle <= 90.f && maxangle >= 0.f)
maxAngleNotSteep = maxangle;
else
printf("invalid option for '--maxAngleNotSteep', using default\n");
}
else if (strcmp(argv[i], "--threads") == 0)
{
param = argv[++i];
if (!param)
return false;
threads = static_cast(std::max(0, atoi(param)));
}
else if (strcmp(argv[i], "--file") == 0)
{
param = argv[++i];
if (!param)
return false;
file = param;
}
else if (strcmp(argv[i], "--tile") == 0)
{
param = argv[++i];
if (!param)
return false;
char* stileX = strtok(param, ",");
char* stileY = strtok(nullptr, ",");
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], "--allowDebug") == 0)
{
allowDebug = true;
}
else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-?"))
{
printf("%s\n", Readme);
silent = true;
return false;
}
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;
}
}
}
#ifndef NDEBUG
if (!allowDebug)
{
finish("Build mmaps_generator in RelWithDebInfo or Release mode or it will take hours to complete!!!\nUse '--allowDebug' argument if you really want to run this tool in Debug.\n", -2);
silent = true;
return false;
}
#endif
return true;
}
std::unordered_map LoadLiquid(std::string const& locale, bool silent, int32 errorExitCode)
{
DB2FileLoader liquidDb2;
std::unordered_map liquidData;
DB2FileSystemSource liquidTypeSource((boost::filesystem::path("dbc") / locale / "LiquidType.db2").string());
try
{
liquidDb2.Load(&liquidTypeSource, &LiquidTypeLoadInfo::Instance);
for (uint32 x = 0; x < liquidDb2.GetRecordCount(); ++x)
{
DB2Record record = liquidDb2.GetRecord(x);
if (!record)
continue;
liquidData[record.GetId()] = record.GetUInt8("SoundBank");
}
}
catch (std::exception const& e)
{
if (silent)
exit(errorExitCode);
exit(finish(e.what(), errorExitCode));
}
return liquidData;
}
std::unordered_map> LoadMap(std::string const& locale, bool silent, int32 errorExitCode)
{
DB2FileLoader mapDb2;
std::unordered_map> mapData;
DB2FileSystemSource mapSource((boost::filesystem::path("dbc") / locale / "Map.db2").string());
try
{
mapDb2.Load(&mapSource, &MapLoadInfo::Instance);
for (uint32 x = 0; x < mapDb2.GetRecordCount(); ++x)
{
DB2Record record = mapDb2.GetRecord(x);
if (!record)
continue;
mapData.emplace(std::piecewise_construct, std::forward_as_tuple(record.GetId()), std::forward_as_tuple());
int16 parentMapId = int16(record.GetUInt16("ParentMapID"));
if (parentMapId < 0)
parentMapId = int16(record.GetUInt16("CosmeticParentMapID"));
if (parentMapId != -1)
mapData[parentMapId].push_back(record.GetId());
MapEntry& map = sMapStore[record.GetId()];
map.MapType = record.GetUInt8("MapType");
map.InstanceType = record.GetUInt8("InstanceType");
map.ParentMapID = parentMapId;
map.Flags = record.GetInt32("Flags1");
}
}
catch (std::exception const& e)
{
if (silent)
exit(errorExitCode);
exit(finish(e.what(), errorExitCode));
}
return mapData;
}
int main(int argc, char** argv)
{
Trinity::VerifyOsVersion();
Trinity::Locale::Init();
Trinity::Banner::Show("MMAP generator", [](char const* text) { printf("%s\n", text); }, nullptr);
unsigned int threads = std::thread::hardware_concurrency();
int mapnum = -1;
int tileX = -1, tileY = -1;
Optional maxAngle, maxAngleNotSteep;
bool skipLiquid = false,
skipContinents = false,
skipJunkMaps = true,
skipBattlegrounds = false,
debugOutput = false,
silent = false,
bigBaseUnit = false;
char* offMeshInputPath = nullptr;
char* file = nullptr;
bool validParam = handleArgs(argc, argv, mapnum,
tileX, tileY, maxAngle, maxAngleNotSteep,
skipLiquid, skipContinents, skipJunkMaps, skipBattlegrounds,
debugOutput, silent, bigBaseUnit, offMeshInputPath, file, threads);
if (!validParam)
return silent ? -1 : finish("You have specified invalid parameters", -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;
}
std::vector dbcLocales;
if (!checkDirectories(debugOutput, dbcLocales))
return silent ? -3 : finish("Press ENTER to close...", -3);
_liquidTypes = LoadLiquid(dbcLocales[0], silent, -5);
_mapDataForVmapInitialization = LoadMap(dbcLocales[0], silent, -4);
MapBuilder builder(maxAngle, maxAngleNotSteep, skipLiquid, skipContinents, skipJunkMaps,
skipBattlegrounds, debugOutput, bigBaseUnit, mapnum, offMeshInputPath, threads);
uint32 start = getMSTime();
if (file)
builder.buildMeshFromFile(file);
else if (tileX > -1 && tileY > -1 && mapnum >= 0)
builder.buildSingleTile(mapnum, tileX, tileY);
else if (mapnum >= 0)
builder.buildMaps(uint32(mapnum));
else
builder.buildMaps({});
if (!silent)
printf("Finished. MMAPS were built in %s\n", secsToTimeString(GetMSTimeDiffToNow(start) / 1000).c_str());
return 0;
}
#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
#include "WheatyExceptionReport.h"
// must be at end of file because of init_seg pragma
INIT_CRASH_HANDLER();
#endif