TrinityCore
vmapexport.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "adtfile.h"
19#include "Banner.h"
20#include "Common.h"
21#include "cascfile.h"
22#include "DB2CascFileSource.h"
24#include "StringFormat.h"
25#include "VMapDefinitions.h"
26#include "vmapexport.h"
27#include "Locales.h"
28#include "Util.h"
29#include "wdtfile.h"
30#include "wmo.h"
31#include <algorithm>
32#include <CascLib.h>
33#include <boost/filesystem/operations.hpp>
34#include <fstream>
35#include <list>
36#include <map>
37#include <unordered_map>
38#include <unordered_set>
39#include <vector>
40#include <cstdio>
41#include <cerrno>
42#include <sys/stat.h>
43
44#ifdef _WIN32
45 #include <direct.h>
46 #define mkdir _mkdir
47#endif
48
49//-----------------------------------------------------------------------------
50
51std::shared_ptr<CASC::Storage> CascStorage;
52
53struct MapEntry
54{
55 uint32 Id = 0;
58 std::string Name;
59 std::string Directory;
60};
61
62std::vector<MapEntry> map_ids; // partitioned by parent maps first
63std::unordered_set<uint32> maps_that_are_parents;
64boost::filesystem::path input_path;
65bool preciseVectorData = false;
66char const* CascProduct = "wow";
67char const* CascRegion = "eu";
68bool UseRemoteCasc = false;
70std::unordered_map<std::string, WMODoodadData> WmoDoodads;
71
72// Constants
73
74char const* szWorkDirWmo = "./Buildings";
75
76#define CASC_LOCALES_COUNT 17
78{
79 "none", "enUS",
80 "koKR", "unknown",
81 "frFR", "deDE",
82 "zhCN", "esES",
83 "zhTW", "enGB",
84 "enCN", "enTW",
85 "esMX", "ruRU",
86 "ptBR", "itIT",
87 "ptPT"
88};
89
91{
92 CASC_LOCALE_ENUS | CASC_LOCALE_ENGB,
93 CASC_LOCALE_KOKR,
94 CASC_LOCALE_FRFR,
95 CASC_LOCALE_DEDE,
96 CASC_LOCALE_ZHCN,
97 CASC_LOCALE_ZHTW,
98 CASC_LOCALE_ESES,
99 CASC_LOCALE_ESMX,
100 CASC_LOCALE_RURU,
101 0,
102 CASC_LOCALE_PTBR | CASC_LOCALE_PTPT,
103 CASC_LOCALE_ITIT,
104};
105
106bool OpenCascStorage(int locale)
107{
108 try
109 {
110 if (UseRemoteCasc)
111 {
112 boost::filesystem::path const casc_cache_dir(boost::filesystem::canonical(input_path) / "CascCache");
114 if (CascStorage)
115 return true;
116
117 printf("Unable to open remote casc fallback to local casc\n");
118 }
119
120 boost::filesystem::path const storage_dir(boost::filesystem::canonical(input_path) / "Data");
122 if (!CascStorage)
123 {
124 printf("error opening casc storage '%s' locale %s\n", storage_dir.string().c_str(), localeNames[locale]);
125 return false;
126 }
127
128 return true;
129 }
130 catch (boost::filesystem::filesystem_error const& error)
131 {
132 printf("error opening casc storage : %s\n", error.what());
133 return false;
134 }
135}
136
138{
139 try
140 {
141 if (UseRemoteCasc)
142 {
143 boost::filesystem::path const casc_cache_dir(boost::filesystem::canonical(input_path) / "CascCache");
144
145 std::unique_ptr<CASC::Storage> storage(CASC::Storage::OpenRemote(casc_cache_dir, 0, CascProduct, CascRegion));
146 if (storage)
147 return CASC_LOCALE_ALL_WOW;
148
149 printf("Unable to open remote casc fallback to local casc\n");
150 }
151
152 boost::filesystem::path const storage_dir(boost::filesystem::canonical(input_path) / "Data");
153 std::unique_ptr<CASC::Storage> storage(CASC::Storage::Open(storage_dir, 0, CascProduct));
154 if (!storage)
155 return false;
156
157 return storage->GetInstalledLocalesMask();
158 }
159 catch (boost::filesystem::filesystem_error const& error)
160 {
161 printf("Unable to determine installed locales mask: %s\n", error.what());
162 }
163
164 return 0;
165}
166
167uint32 uniqueObjectIdGenerator = std::numeric_limits<uint32>::max() - 1;
168std::map<std::pair<uint32, uint16>, uint32> uniqueObjectIds;
169
170uint32 GenerateUniqueObjectId(uint32 clientId, uint16 clientDoodadId, bool isWmo)
171{
172 // WMO client ids must be preserved, they are used in DB2 files
173 uint32 newId = isWmo ? clientId : uniqueObjectIdGenerator--;
174 return uniqueObjectIds.emplace(std::make_pair(clientId, clientDoodadId), newId).first->second;
175}
176
177// Local testing functions
178bool FileExists(char const* file)
179{
180 if (FILE* n = fopen(file, "rb"))
181 {
182 fclose(n);
183 return true;
184 }
185 return false;
186}
187
188bool ExtractSingleWmo(std::string& fname)
189{
190 // Copy files from archive
191 std::string originalName = fname;
192
193 char* plain_name = GetPlainName(&fname[0]);
194 NormalizeFileName(plain_name, strlen(plain_name));
195 std::string szLocalFile = Trinity::StringFormat("{}/{}", szWorkDirWmo, plain_name);
196
197 if (FileExists(szLocalFile.c_str()))
198 return true;
199
200 int p = 0;
201 // Select root wmo files
202 char const* rchr = strrchr(plain_name, '_');
203 if (rchr != nullptr)
204 for (int i = 0; i < 4; ++i)
205 if (isdigit(rchr[i]))
206 p++;
207
208 if (p == 3)
209 return true;
210
211 bool file_ok = true;
212 WMORoot froot(originalName);
213 if (!froot.open())
214 {
215 printf("Couldn't open RootWmo!!!\n");
216 return true;
217 }
218 FILE *output = fopen(szLocalFile.c_str(),"wb");
219 if(!output)
220 {
221 printf("couldn't open %s for writing!\n", szLocalFile.c_str());
222 return false;
223 }
224 froot.ConvertToVMAPRootWmo(output);
225 WMODoodadData& doodads = WmoDoodads[plain_name];
226 std::swap(doodads, froot.DoodadData);
227 int Wmo_nVertices = 0;
228 uint32 groupCount = 0;
229 //printf("root has %d groups\n", froot->nGroups);
230 for (std::size_t i = 0; i < froot.groupFileDataIDs.size(); ++i)
231 {
232 std::string s = Trinity::StringFormat("FILE{:08X}.xxx", froot.groupFileDataIDs[i]);
233 WMOGroup fgroup(s);
234 if (!fgroup.open(&froot))
235 {
236 printf("Could not open all Group file for: %s\n", plain_name);
237 file_ok = false;
238 break;
239 }
240
241 if (fgroup.ShouldSkip(&froot))
242 continue;
243
244 Wmo_nVertices += fgroup.ConvertToVMAPGroupWmo(output, preciseVectorData);
245 ++groupCount;
246 for (uint16 groupReference : fgroup.DoodadReferences)
247 {
248 if (groupReference >= doodads.Spawns.size())
249 continue;
250
251 uint32 doodadNameIndex = doodads.Spawns[groupReference].NameIndex;
252 if (froot.ValidDoodadNames.find(doodadNameIndex) == froot.ValidDoodadNames.end())
253 continue;
254
255 doodads.References.insert(groupReference);
256 }
257 }
258
259 fseek(output, 8, SEEK_SET); // store the correct no of vertices
260 fwrite(&Wmo_nVertices, sizeof(int), 1, output);
261 // store the correct no of groups
262 fwrite(&groupCount, sizeof(uint32), 1, output);
263 fclose(output);
264
265 // Delete the extracted file in the case of an error
266 if (!file_ok)
267 remove(szLocalFile.c_str());
268 return true;
269}
270
272{
273 std::unordered_map<uint32, WDTFile> wdts;
274 auto getWDT = [&wdts](uint32 mapId) -> WDTFile*
275 {
276 auto itr = wdts.find(mapId);
277 if (itr == wdts.end())
278 {
279 auto mapEntryItr = std::find_if(map_ids.begin(), map_ids.end(), [mapId](MapEntry const& mapEntry) { return mapEntry.Id == mapId; });
280 if (mapEntryItr == map_ids.end())
281 return nullptr;
282
283 uint32 fileDataId = mapEntryItr->WdtFileDataId;
284 if (!fileDataId)
285 return nullptr;
286
287 std::string description = Trinity::StringFormat("WDT for map {} - {} (FileDataID {})", mapId, mapEntryItr->Name, fileDataId);
288 std::string directory = mapEntryItr->Directory;
289 itr = wdts.emplace(std::piecewise_construct, std::forward_as_tuple(mapId), std::forward_as_tuple(fileDataId, description, std::move(directory), maps_that_are_parents.count(mapId) > 0)).first;
290 if (!itr->second.init(mapId))
291 {
292 wdts.erase(itr);
293 return nullptr;
294 }
295 }
296
297 return &itr->second;
298 };
299
300 for (MapEntry const& mapEntry : map_ids)
301 {
302 if (WDTFile* WDT = getWDT(mapEntry.Id))
303 {
304 WDTFile* parentWDT = mapEntry.ParentMapID >= 0 ? getWDT(mapEntry.ParentMapID) : nullptr;
305 printf("Processing Map %u\n[", mapEntry.Id);
306 for (int32 x = 0; x < 64; ++x)
307 {
308 for (int32 y = 0; y < 64; ++y)
309 {
310 bool success = false;
311 if (ADTFile* ADT = WDT->GetMap(x, y))
312 {
313 success = ADT->init(mapEntry.Id, mapEntry.Id);
314 WDT->FreeADT(ADT);
315 }
316 if (!success && parentWDT)
317 {
318 if (ADTFile* ADT = parentWDT->GetMap(x, y))
319 {
320 ADT->init(mapEntry.Id, mapEntry.ParentMapID);
321 parentWDT->FreeADT(ADT);
322 }
323 }
324 }
325 printf("#");
326 fflush(stdout);
327 }
328 printf("]\n");
329 }
330 }
331}
332
333bool processArgv(int argc, char ** argv, const char *versionString)
334{
335 bool result = true;
336 preciseVectorData = false;
337
338 for (int i = 1; i < argc; ++i)
339 {
340 if (strcmp("-s", argv[i]) == 0)
341 {
342 preciseVectorData = false;
343 }
344 else if (strcmp("-d", argv[i]) == 0)
345 {
346 if ((i + 1) < argc)
347 {
348 input_path = boost::filesystem::path(argv[i + 1]);
349 ++i;
350 }
351 else
352 {
353 result = false;
354 }
355 }
356 else if (strcmp("-?", argv[1]) == 0)
357 {
358 result = false;
359 }
360 else if (strcmp("-l", argv[i]) == 0)
361 {
362 preciseVectorData = true;
363 }
364 else if (strcmp("-p", argv[i]) == 0)
365 {
366 if (i + 1 < argc && strlen(argv[i + 1]))
367 CascProduct = argv[++i];
368 else
369 result = false;
370 }
371 else if (strcmp("-c", argv[i]) == 0)
372 {
373 UseRemoteCasc = true;
374 }
375 else if (strcmp("-r", argv[i]) == 0)
376 {
377 if (i + 1 < argc && strlen(argv[i + 1]))
378 CascRegion = argv[++i];
379 else
380 result = false;
381 }
382 else if (strcmp("-dl", argv[i]) == 0)
383 {
384 if (i + 1 < argc && strlen(argv[i + 1]))
385 {
386 for (uint32 l = 0; l < TOTAL_LOCALES; ++l)
387 if (!strcmp(argv[i + 1], localeNames[l]))
388 DbcLocale = 1 << l;
389 i++;
390 }
391 else
392 result = false;
393 }
394 else
395 {
396 result = false;
397 break;
398 }
399 }
400
401 if (!result)
402 {
403 printf("Extract %s.\n",versionString);
404 printf("%s [-?][-s][-l][-d <path>][-p <product>]\n", argv[0]);
405 printf(" -s : (default) small size (data size optimization), ~500MB less vmap data.\n");
406 printf(" -l : large size, ~500MB more vmap data. (might contain more details)\n");
407 printf(" -d <path>: Path to the vector data source folder.\n");
408 printf(" -p <product>: which installed product to open (wow/wowt/wow_beta)\n");
409 printf(" -c use remote casc\n");
410 printf(" -r set remote casc region - standard: eu\n");
411 printf(" -dl dbc locale\n");
412 printf(" -? : This message.\n");
413 }
414
415 return result;
416}
417
418static bool RetardCheck()
419{
420 try
421 {
422 if (UseRemoteCasc)
423 return true;
424
425 boost::filesystem::path storageDir(boost::filesystem::canonical(input_path) / "Data");
426 boost::filesystem::directory_iterator end;
427 for (boost::filesystem::directory_iterator itr(storageDir); itr != end; ++itr)
428 {
429 if (itr->path().extension() == ".MPQ")
430 {
431 printf("MPQ files found in Data directory!\n");
432 printf("This tool works only with World of Warcraft: Battle for Azeroth\n");
433 printf("\n");
434 printf("To extract maps for Wrath of the Lich King, rebuild tools using 3.3.5 branch!\n");
435 printf("\n");
436 printf("Press ENTER to exit...\n");
437 getchar();
438 return false;
439 }
440 }
441 }
442 catch (std::exception const& error)
443 {
444 printf("Error checking client version: %s\n", error.what());
445 }
446
447 return true;
448}
449
450int main(int argc, char ** argv)
451{
453
455
456 Trinity::Banner::Show("VMAP data extractor", [](char const* text) { printf("%s\n", text); }, nullptr);
457
458 bool success = true;
459
460 // Use command line arguments, when some
461 if (!processArgv(argc, argv, VMAP::VMAP_MAGIC))
462 return 1;
463
464 if (!RetardCheck())
465 return 1;
466
467 // some simple check if working dir is dirty
468 else
469 {
470 std::string sdir = std::string(szWorkDirWmo) + "/dir";
471 std::string sdir_bin = std::string(szWorkDirWmo) + "/dir_bin";
472 struct stat status;
473 if (!stat(sdir.c_str(), &status) || !stat(sdir_bin.c_str(), &status))
474 {
475 printf("Your output directory seems to be polluted, please use an empty directory!\n");
476 printf("<press return to exit>");
477 char garbage[2];
478 return scanf("%c", garbage);
479 }
480 }
481
482 printf("Extract %s. Beginning work ....\n", VMAP::VMAP_MAGIC);
483 //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
484 // Create the working directory
485 if (mkdir(szWorkDirWmo
486#if defined(__linux__) || defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
487 , 0711
488#endif
489 ))
490 success = (errno == EEXIST);
491
492 uint32 installedLocalesMask = GetInstalledLocalesMask();
493 int32 FirstLocale = -1;
494 for (int i = 0; i < TOTAL_LOCALES; ++i)
495 {
496 if (DbcLocale && !(DbcLocale & (1 << i)))
497 continue;
498
499 if (i == LOCALE_none)
500 continue;
501
502 if (!(installedLocalesMask & WowLocaleToCascLocaleFlags[i]))
503 continue;
504
505 if (!OpenCascStorage(i))
506 continue;
507
508 FirstLocale = i;
509 uint32 build = CascStorage->GetBuildNumber();
510 if (!build)
511 {
512 CascStorage.reset();
513 continue;
514 }
515
516 printf("Detected client build %u for locale %s\n\n", build, localeNames[i]);
517 break;
518 }
519
520 if (FirstLocale == -1)
521 {
522 printf("FATAL ERROR: No locales defined, unable to continue.\n");
523 return 1;
524 }
525
526 // Extract models, listed in GameObjectDisplayInfo.dbc
528
529 //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
530 //map.dbc
531 if (success)
532 {
533 printf("Read Map.dbc file... ");
534
535 DB2CascFileSource source(CascStorage, MapLoadInfo::Instance.Meta->FileDataId);
536 DB2FileLoader db2;
537 try
538 {
539 db2.Load(&source, &MapLoadInfo::Instance);
540 }
541 catch (std::exception const& e)
542 {
543 printf("Fatal error: Invalid Map.db2 file format! %s\n%s\n", CASC::HumanReadableCASCError(GetCascError()), e.what());
544 exit(1);
545 }
546
547 map_ids.reserve(db2.GetRecordCount());
548 std::unordered_map<uint32, std::size_t> idToIndex;
549 for (uint32 x = 0; x < db2.GetRecordCount(); ++x)
550 {
551 DB2Record record = db2.GetRecord(x);
552 if (!record)
553 continue;
554
555 MapEntry map;
556 map.Id = record.GetId();
557 map.WdtFileDataId = record.GetInt32("WdtFileDataID");
558 map.ParentMapID = int16(record.GetUInt16("ParentMapID"));
559 map.Name = record.GetString("MapName");
560 map.Directory = record.GetString("Directory");
561
562 if (map.ParentMapID < 0)
563 map.ParentMapID = int16(record.GetUInt16("CosmeticParentMapID"));
564
565 if (map.ParentMapID >= 0)
567
568 idToIndex[map.Id] = map_ids.size();
569 map_ids.push_back(map);
570 }
571
572 for (uint32 x = 0; x < db2.GetRecordCopyCount(); ++x)
573 {
574 DB2RecordCopy copy = db2.GetRecordCopy(x);
575 auto itr = idToIndex.find(copy.SourceRowId);
576 if (itr != idToIndex.end())
577 {
578 MapEntry map;
579 map.Id = copy.NewRowId;
580 map.WdtFileDataId = map_ids[itr->second].WdtFileDataId;
581 map.ParentMapID = map_ids[itr->second].ParentMapID;
582 map.Name = map_ids[itr->second].Name;
583 map.Directory = map_ids[itr->second].Directory;
584 map_ids.push_back(map);
585 }
586 }
587
588 map_ids.erase(std::remove_if(map_ids.begin(), map_ids.end(), [](MapEntry const& map) { return !map.WdtFileDataId; }), map_ids.end());
589
590 // force parent maps to be extracted first
591 std::stable_partition(map_ids.begin(), map_ids.end(), [](MapEntry const& map) { return maps_that_are_parents.count(map.Id) > 0; });
592
593 printf("Done! (" SZFMTD " maps loaded)\n", map_ids.size());
594 ParsMapFiles();
595 }
596
597 CascStorage.reset();
598
599 printf("\n");
600 if (!success)
601 {
602 printf("ERROR: Extract %s. Work NOT complete.\n Precise vector data=%d.\nPress any key.\n", VMAP::VMAP_MAGIC, preciseVectorData);
603 getchar();
604 }
605
606 printf("Extract %s. Work complete. No errors.\n", VMAP::VMAP_MAGIC);
607 return 0;
608}
char const * localeNames[TOTAL_LOCALES]
Definition: Common.cpp:20
@ LOCALE_none
Definition: Common.h:58
@ TOTAL_LOCALES
Definition: Common.h:62
int16_t int16
Definition: Define.h:139
int32_t int32
Definition: Define.h:138
uint16_t uint16
Definition: Define.h:143
uint32_t uint32
Definition: Define.h:142
#define SZFMTD
Definition: Define.h:132
char const * GetPlainName(char const *FileName)
Definition: adtfile.cpp:24
void NormalizeFileName(char *name, size_t len)
Definition: adtfile.cpp:69
static Storage * OpenRemote(boost::filesystem::path const &path, uint32 localeMask, char const *product, char const *region)
static Storage * Open(boost::filesystem::path const &path, uint32 localeMask, char const *product)
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
uint16 GetUInt16(uint32 field, uint32 arrayIndex) const
uint32 GetId() const
char const * GetString(uint32 field, uint32 arrayIndex) const
void FreeADT(ADTFile *adt)
Definition: wdtfile.cpp:165
ADTFile * GetMap(int32 x, int32 y)
Definition: wdtfile.cpp:141
Definition: wmo.h:121
std::vector< uint16 > DoodadReferences
Definition: wmo.h:151
bool open(WMORoot *rootWMO)
Definition: wmo.cpp:219
int ConvertToVMAPGroupWmo(FILE *output, bool preciseVectorData)
Definition: wmo.cpp:352
bool ShouldSkip(WMORoot const *root) const
Definition: wmo.cpp:557
Definition: wmo.h:79
WMODoodadData DoodadData
Definition: wmo.h:90
std::vector< uint32 > groupFileDataIDs
Definition: wmo.h:92
bool open()
Definition: wmo.cpp:42
std::unordered_set< uint32 > ValidDoodadNames
Definition: wmo.h:91
bool ConvertToVMAPRootWmo(FILE *output)
Definition: wmo.cpp:194
void ExtractGameobjectModels()
Definition: adtfile.h:27
char const * HumanReadableCASCError(uint32 error)
Definition: CascHandles.cpp:30
TC_COMMON_API void Show(char const *applicationName, void(*log)(char const *text), void(*logExtraInfo)())
Definition: Banner.cpp:22
TC_COMMON_API void Init()
Definition: Locales.cpp:28
TC_COMMON_API void VerifyOsVersion()
Definition: Util.cpp:34
std::string StringFormat(FormatString< Args... > fmt, Args &&... args)
Default TC string format function.
Definition: StringFormat.h:38
const char VMAP_MAGIC[]
Definition: wdtfile.h:31
uint32 SourceRowId
uint32 Id
Definition: System.cpp:53
char const * Directory
int32 WdtFileDataId
Definition: System.cpp:54
std::string Name
Definition: System.cpp:55
int16 ParentMapID
static constexpr DB2LoadInfo Instance
Definition: DB2LoadInfo.h:3676
std::unordered_set< uint16 > References
Definition: wmo.h:75
std::vector< WMO::MODD > Spawns
Definition: wmo.h:74
uint32 GetInstalledLocalesMask()
Definition: vmapexport.cpp:137
static bool RetardCheck()
Definition: vmapexport.cpp:418
std::unordered_map< std::string, WMODoodadData > WmoDoodads
Definition: vmapexport.cpp:70
bool preciseVectorData
Definition: vmapexport.cpp:65
bool UseRemoteCasc
Definition: vmapexport.cpp:68
int main(int argc, char **argv)
Definition: vmapexport.cpp:450
std::shared_ptr< CASC::Storage > CascStorage
Definition: vmapexport.cpp:51
char const * CascLocaleNames[CASC_LOCALES_COUNT]
Definition: vmapexport.cpp:77
void ParsMapFiles()
Definition: vmapexport.cpp:271
char const * CascProduct
Definition: vmapexport.cpp:66
uint32 DbcLocale
Definition: vmapexport.cpp:69
uint32 uniqueObjectIdGenerator
Definition: vmapexport.cpp:167
uint32 WowLocaleToCascLocaleFlags[12]
Definition: vmapexport.cpp:90
boost::filesystem::path input_path
Definition: vmapexport.cpp:64
std::map< std::pair< uint32, uint16 >, uint32 > uniqueObjectIds
Definition: vmapexport.cpp:168
bool FileExists(char const *file)
Definition: vmapexport.cpp:178
bool processArgv(int argc, char **argv, const char *versionString)
Definition: vmapexport.cpp:333
uint32 GenerateUniqueObjectId(uint32 clientId, uint16 clientDoodadId, bool isWmo)
Definition: vmapexport.cpp:170
char const * CascRegion
Definition: vmapexport.cpp:67
bool OpenCascStorage(int locale)
Definition: vmapexport.cpp:106
std::vector< MapEntry > map_ids
Definition: vmapexport.cpp:62
#define CASC_LOCALES_COUNT
Definition: vmapexport.cpp:76
char const * szWorkDirWmo
Definition: vmapexport.cpp:74
std::unordered_set< uint32 > maps_that_are_parents
Definition: vmapexport.cpp:63
bool ExtractSingleWmo(std::string &fname)
Definition: vmapexport.cpp:188