35#include <boost/filesystem/directory.hpp>
36#include <boost/filesystem/operations.hpp>
41#include <unordered_map>
86#define CASC_LOCALES_COUNT 17
102 CASC_LOCALE_ENUS | CASC_LOCALE_ENGB,
112 CASC_LOCALE_PTBR | CASC_LOCALE_PTPT,
122 boost::filesystem::path
const casc_cache_dir(boost::filesystem::canonical(
input_path) /
"CascCache");
127 printf(
"Unable to open remote casc fallback to local casc\n");
130 boost::filesystem::path
const storage_dir(boost::filesystem::canonical(
input_path) /
"Data");
134 printf(
"error opening casc storage '%s' locale %s\n", storage_dir.string().c_str(),
localeNames[locale]);
140 catch (std::exception
const& error)
142 printf(
"error opening casc storage : %s\n", error.what());
153 boost::filesystem::path
const casc_cache_dir(boost::filesystem::canonical(
input_path) /
"CascCache");
157 return CASC_LOCALE_ALL_WOW;
159 printf(
"Unable to open remote casc fallback to local casc\n");
162 boost::filesystem::path
const storage_dir(boost::filesystem::canonical(
input_path) /
"Data");
167 return storage->GetInstalledLocalesMask();
169 catch (std::exception
const& error)
171 printf(
"Unable to determine installed locales mask: %s\n", error.what());
186 return UniqueObjectIds.emplace(std::make_pair(clientId, clientDoodadId), newId).first->second;
196 return { &itr->second, isNew };
202 std::string originalName = fname;
211 switch (model->State.load(std::memory_order::relaxed))
221 auto stateGuard = Trinity::make_unique_ptr_with_deleter<&ExtractedModelData::Fail>(model);
225 std::size_t rchr = fname.find_last_of(
'_');
226 if (rchr != std::string::npos)
227 for (std::size_t i = 0; i < 4 && rchr + i < fname.length(); ++i)
228 if (isdigit(fname[rchr + i]))
238 printf(
"Couldn't open RootWmo!!!\n");
242 FILE* output = fopen(szLocalFile.c_str(),
"wb");
245 printf(
"couldn't open %s for writing!\n", szLocalFile.c_str());
249 WMODoodadData& doodads = *(model->Doodads = std::make_unique<WMODoodadData>());
251 int Wmo_nVertices = 0;
254 std::vector<WMOGroup> groups;
259 WMOGroup& fgroup = groups.emplace_back(s);
260 if (!fgroup.
open(&froot))
262 printf(
"Could not open all Group file for: %s\n", fname.c_str());
270 if (fgroup.ShouldSkip(&froot))
273 if (fgroup.mogpFlags2 & 0x80
274 && fgroup.parentOrFirstChildSplitGroupIndex >= 0
275 &&
size_t(fgroup.parentOrFirstChildSplitGroupIndex) < groups.size())
276 fgroup.groupWMOID = groups[fgroup.parentOrFirstChildSplitGroupIndex].groupWMOID;
280 for (
uint16 groupReference : fgroup.DoodadReferences)
282 if (groupReference >= doodads.
Spawns.size())
285 uint32 doodadNameIndex = doodads.
Spawns[groupReference].NameIndex;
293 fseek(output, 8, SEEK_SET);
294 fwrite(&Wmo_nVertices,
sizeof(
int), 1, output);
296 fwrite(&groupCount,
sizeof(
uint32), 1, output);
299 if (!Wmo_nVertices && (doodads.
Sets.empty() || doodads.
References.empty()))
303 if (!file_ok || !Wmo_nVertices)
304 remove(szLocalFile.c_str());
310 return stateGuard.release();
325 std::unordered_map<uint32, WDTFile> wdts;
326 std::map<uint32, std::vector<MapEntry const*>> steps;
329 steps[mapEntry.ChildDepth].push_back(&mapEntry);
332 std::string description =
Trinity::StringFormat(
"WDT for map {} - {} (FileDataID {})", mapEntry.Id, mapEntry.Name, mapEntry.WdtFileDataId);
333 auto itr = wdts.try_emplace(mapEntry.Id, mapEntry.WdtFileDataId, description, mapEntry.Directory, mapEntry.IsParent).first;
334 if (!itr->second.init(mapEntry.Id))
338 for (
auto const& [_, maps] : steps)
342 for (
MapEntry const* mapEntry : maps)
344 threadPool.
PostWork([mapEntry, &wdts]
348 int16 parentMapId = mapEntry->ParentMapID;
349 std::vector<WDTFile*> parentWDTs;
350 while (parentMapId >= 0)
355 if (parentMapItr ==
map_ids.end())
358 parentMapId = parentMapItr->ParentMapID;
361 printf(
"Processing Map %u\n", mapEntry->Id);
362 for (
int32 x = 0; x < 64; ++x)
364 for (
int32 y = 0; y < 64; ++y)
366 bool success =
false;
369 success =
ADT->init(mapEntry->Id, mapEntry->Id);
375 for (
WDTFile* parentWDT : parentWDTs)
377 if (
ADTFile*
ADT = parentWDT->GetMap(x, y,
false))
379 success =
ADT->init(mapEntry->Id, mapEntry->ParentMapID);
380 parentWDT->FreeADT(
ADT);
389 printf(
"Processing Map %u Done\n", mapEntry->Id);
402 db2->
Load(source, loadInfo);
404 catch (std::exception
const& e)
413 printf(
"Read Map.dbc file... ");
420 std::unordered_map<uint32, std::size_t> idToIndex;
444 if (itr != idToIndex.end())
454 int16 parentMapId = map.ParentMapID;
455 while (parentMapId >= 0)
473 printf(
"Read LiquidMaterial.db2 file...\n");
497 printf(
"Read LiquidType.db2 file...\n");
519bool processArgv(
int argc,
char ** argv,
const char *versionString)
524 for (
int i = 1; i < argc; ++i)
526 if (strcmp(
"-s", argv[i]) == 0)
530 else if (strcmp(
"-d", argv[i]) == 0)
534 input_path = boost::filesystem::path(argv[i + 1]);
542 else if (strcmp(
"-?", argv[1]) == 0)
546 else if (strcmp(
"-l", argv[i]) == 0)
550 else if (strcmp(
"-p", argv[i]) == 0)
552 if (i + 1 < argc && strlen(argv[i + 1]))
557 else if (strcmp(
"-c", argv[i]) == 0)
561 else if (strcmp(
"-r", argv[i]) == 0)
563 if (i + 1 < argc && strlen(argv[i + 1]))
568 else if (strcmp(
"-dl", argv[i]) == 0)
570 if (i + 1 < argc && strlen(argv[i + 1]))
580 else if (strcmp(
"--threads", argv[i]) == 0)
582 if (i + 1 < argc && strlen(argv[i + 1]))
583 Threads = Trinity::StringTo<uint32>(argv[++i]).value_or(std::thread::hardware_concurrency());
596 printf(
"Extract %s.\n",versionString);
597 printf(
"%s [-?][-s][-l][-d <path>][-p <product>]\n", argv[0]);
598 printf(
" -s : (default) small size (data size optimization), ~500MB less vmap data.\n");
599 printf(
" -l : large size, ~500MB more vmap data. (might contain more details)\n");
600 printf(
" -d <path>: Path to the vector data source folder.\n");
601 printf(
" -p <product>: which installed product to open (wow/wowt/wow_beta)\n");
602 printf(
" -c use remote casc\n");
603 printf(
" -r set remote casc region - standard: eu\n");
604 printf(
" -dl dbc locale\n");
605 printf(
" --threads <N> number of threads to use, default: all cpu cores\n");
606 printf(
" -? : This message.\n");
619 boost::filesystem::path storageDir(boost::filesystem::canonical(
input_path) /
"Data");
620 boost::filesystem::directory_iterator end;
621 for (boost::filesystem::directory_iterator itr(storageDir); itr != end; ++itr)
623 if (itr->path().extension() ==
".MPQ")
625 printf(
"MPQ files found in Data directory!\n");
626 printf(
"This tool works only with World of Warcraft: Dragonflight\n");
628 printf(
"To extract maps for Wrath of the Lich King, rebuild tools using 3.3.5 branch!\n");
630 printf(
"Press ENTER to exit...\n");
636 catch (std::exception
const& error)
638 printf(
"Error checking client version: %s\n", error.what());
644int main(
int argc,
char ** argv)
650 Trinity::Banner::Show(
"VMAP data extractor", [](
char const* text) { printf(
"%s\n", text); },
nullptr);
662 boost::filesystem::path sdir_bin = boost::filesystem::path(
szWorkDirWmo) /
"dir_bin";
664 boost::system::error_code ec;
665 if (boost::filesystem::exists(sdir_bin, ec) && !boost::filesystem::is_empty(sdir_bin, ec))
667 printf(
"Your output directory seems to be polluted, please use an empty directory!\n");
668 printf(
"<press return to exit>");
670 return scanf(
"%c", garbage);
677 success = boost::filesystem::create_directories(sdir_bin) || boost::filesystem::is_directory(sdir_bin);
680 int32 FirstLocale = -1;
703 printf(
"Detected client build %u for locale %s\n\n", build,
localeNames[i]);
707 if (FirstLocale == -1)
709 printf(
"FATAL ERROR: No locales defined, unable to continue.\n");
739#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
char const * localeNames[TOTAL_LOCALES]
void NormalizeFileName(std::string &name)
std::string_view GetPlainName(std::string_view fileName)
static Storage * Open(boost::filesystem::path const &path, uint32 localeMask, char const *product)
static Storage * OpenRemote(boost::filesystem::path const &path, uint32 localeMask, char const *product, char const *region)
void Load(DB2FileSource *source, DB2FileLoadInfo const *loadInfo)
DB2Record GetRecord(uint32 recordNumber) const
DB2RecordCopy GetRecordCopy(uint32 copyNumber) const
uint32 GetRecordCopyCount() const
uint32 GetRecordCount() const
int32 GetInt32(uint32 field, uint32 arrayIndex) const
uint32 GetUInt32(uint32 field, uint32 arrayIndex) const
uint16 GetUInt16(uint32 field, uint32 arrayIndex) const
uint8 GetUInt8(uint32 field, uint32 arrayIndex) const
char const * GetString(uint32 field, uint32 arrayIndex) const
decltype(auto) PostWork(T &&work)
bool open(WMORoot *rootWMO)
std::vector< uint32 > groupFileDataIDs
std::unordered_set< uint32 > ValidDoodadNames
bool ConvertToVMAPRootWmo(FILE *output)
char const * HumanReadableCASCError(uint32 error)
TC_COMMON_API void Show(char const *applicationName, void(*log)(char const *text), void(*logExtraInfo)())
auto MapGetValuePtr(M &map, typename M::key_type const &key)
TC_COMMON_API void Init()
TC_COMMON_API void VerifyOsVersion()
std::string StringFormat(FormatString< Args... > fmt, Args &&... args) noexcept
Default TC string format function.
EnumFlag< LiquidMaterialFlags > Flags
static constexpr DB2FileLoadInfo Instance
static constexpr DB2LoadInfo Instance
static constexpr DB2LoadInfo Instance
std::unordered_set< uint16 > References
std::vector< WMO::MODS > Sets
std::vector< WMO::MODD > Spawns
uint32 GetInstalledLocalesMask()
static std::mutex ExtractedModelsMutex
static bool RetardCheck()
static std::map< std::pair< uint32, uint16 >, uint32 > UniqueObjectIds
static std::atomic< uint32 > UniqueObjectIdGenerator
int main(int argc, char **argv)
std::shared_ptr< CASC::Storage > CascStorage
bool IsLiquidIgnored(uint32 liquidTypeId)
static std::mutex UniqueObjectIdsMutex
char const * CascLocaleNames[CASC_LOCALES_COUNT]
std::unordered_map< uint32, LiquidTypeEntry > LiquidTypes
uint32 WowLocaleToCascLocaleFlags[12]
boost::filesystem::path input_path
void ReadLiquidMaterialTable()
bool processArgv(int argc, char **argv, const char *versionString)
std::unordered_map< std::string, ExtractedModelData > ExtractedModels
std::unordered_map< uint32, LiquidMaterialEntry > LiquidMaterials
uint32 GenerateUniqueObjectId(uint32 clientId, uint16 clientDoodadId, bool isWmo)
bool OpenCascStorage(int locale)
std::vector< MapEntry > map_ids
void TryLoadDB2(char const *name, DB2CascFileSource *source, DB2FileLoader *db2, DB2FileLoadInfo const *loadInfo)
ExtractedModelData const * ExtractSingleWmo(std::string &fname)
void ReadLiquidTypeTable()
#define CASC_LOCALES_COUNT
char const * szWorkDirWmo
std::pair< ExtractedModelData *, bool > BeginModelExtraction(std::string const &outputName)