Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
vmapexport.cpp
Go to the documentation of this file.
1/*
2* This file is part of Project SkyFire https://www.projectskyfire.org.
3* See LICENSE.md file for Copyright information
4*/
5
6#define _CRT_SECURE_NO_DEPRECATE
7#include <cstdio>
8#include <iostream>
9#include <vector>
10#include <list>
11#include <errno.h>
12
13#ifdef _WIN64
14 #include <Windows.h>
15 #include <sys/stat.h>
16 #include <direct.h>
17 #define mkdir _mkdir
18#elif WIN32
19 #include <Windows.h>
20 #include <sys/stat.h>
21 #include <direct.h>
22 #define mkdir _mkdir
23#else
24 #include <sys/stat.h>
25 #define ERROR_PATH_NOT_FOUND ERROR_FILE_NOT_FOUND
26#endif
27
28#undef min
29#undef max
30
31//#pragma warning(disable : 4505)
32//#pragma comment(lib, "Winmm.lib")
33
34#include <map>
35
36//From Extractor
37#include "adtfile.h"
38#include "wdtfile.h"
39#include "dbcfile.h"
40#include "wmo.h"
41#include "mpqfile.h"
42
43#include "vmapexport.h"
44
45//------------------------------------------------------------------------------
46// Defines
47
48#define MPQ_BLOCK_SIZE 0x1000
49
50//-----------------------------------------------------------------------------
51
52HANDLE WorldMpq = NULL;
53HANDLE LocaleMpq = NULL;
54
55uint32 CONF_TargetBuild = 18273; // 5.4.8.18273
56
57// List MPQ for extract maps from
58char const* CONF_mpq_list[]=
59{
60 "world.MPQ",
61 "model.MPQ", // added in 5.x.x
62 "misc.MPQ", // added in 5.x.x
63 "expansion1.MPQ",
64 "expansion2.MPQ",
65 "expansion3.MPQ",
66 "expansion4.MPQ", // added in 5.x.x
67};
68
69uint32 const Builds[] = {16016, 16048, 16057, 16309, 16357, 16516, 16650, 16844, 16965, 17116, 17266, 17325, 17345, 17538, 17645, 17688, 17898, 18273, 0};
70#define LAST_DBC_IN_DATA_BUILD 15595 // after this build mpqs with dbc are back to locale folder
71#define NEW_BASE_SET_BUILD 16016 // 15211
72
73#define LOCALES_COUNT 15
74
75char const* Locales[LOCALES_COUNT] =
76{
77 "enGB", "enUS",
78 "deDE", "esES",
79 "frFR", "koKR",
80 "zhCN", "zhTW",
81 "enCN", "enTW",
82 "esMX", "ruRU",
83 "ptBR", "ptPT",
84 "itIT"
85};
86
87TCHAR const* LocalesT[LOCALES_COUNT] =
88{
89 _T("enGB"), _T("enUS"),
90 _T("deDE"), _T("esES"),
91 _T("frFR"), _T("koKR"),
92 _T("zhCN"), _T("zhTW"),
93 _T("enCN"), _T("enTW"),
94 _T("esMX"), _T("ruRU"),
95 _T("ptBR"), _T("ptPT"),
96 _T("itIT"),
97};
98
99typedef struct
100{
101 char name[64];
102 unsigned int id;
103}map_id;
104
108char output_path[128]=".";
109char input_path[1024]=".";
110bool preciseVectorData = false;
111
112// Constants
113
114//static const char * szWorkDirMaps = ".\\Maps";
115const char* szWorkDirWmo = "./Buildings";
116const char* szRawVMAPMagic = "VMAP053";
117
118bool LoadLocaleMPQFile(int locale)
119{
120 TCHAR buff[1024];
121 memset(buff, 0, sizeof(buff));
122 _stprintf(buff, _T("%s%s/locale-%s.MPQ"), input_path, LocalesT[locale], LocalesT[locale]);
123 if (!SFileOpenArchive(buff, 0, MPQ_OPEN_READ_ONLY, &LocaleMpq))
124 {
125 if (GetLastError() != ERROR_PATH_NOT_FOUND)
126 {
127 _tprintf(_T("Loading %s locale MPQs\n"), LocalesT[locale]);
128 _tprintf(_T("Cannot open archive %s\n"), buff);
129 }
130 return false;
131 }
132
133 _tprintf(_T("Loading %s locale MPQs\n"), LocalesT[locale]);
134 char const* prefix = NULL;
135 for (int i = 0; Builds[i] && Builds[i] <= CONF_TargetBuild; ++i)
136 {
137 // Do not attempt to read older MPQ patch archives past this build, they were merged with base
138 // and trying to read them together with new base will not end well
140 continue;
141
142 memset(buff, 0, sizeof(buff));
144 {
145 prefix = "";
146 _stprintf(buff, _T("%s%s/wow-update-%s-%u.MPQ"), input_path, LocalesT[locale], LocalesT[locale], Builds[i]);
147 }
148 else
149 {
150 prefix = Locales[locale];
151 _stprintf(buff, _T("%swow-update-base-%u.MPQ"), input_path, Builds[i]);
152 }
153
154 if (!SFileOpenPatchArchive(LocaleMpq, buff, prefix, 0))
155 {
156 if (GetLastError() != ERROR_FILE_NOT_FOUND)
157 _tprintf(_T("Cannot open patch archive %s\n"), buff);
158 continue;
159 }
160 }
161
162 printf("\n");
163 return true;
164}
165
167{
168 TCHAR filename[1024];
169 _stprintf(filename, _T("%sworld.MPQ"), input_path);
170 _tprintf(_T("Loading common MPQ files\n"));
171 if (!SFileOpenArchive(filename, 0, MPQ_OPEN_READ_ONLY, &WorldMpq))
172 {
173 if (GetLastError() != ERROR_PATH_NOT_FOUND)
174 _tprintf(_T("Cannot open archive %s\n"), filename);
175 return;
176 }
177
178 int count = sizeof(CONF_mpq_list) / sizeof(char*);
179 for (int i = 1; i < count; ++i)
180 {
181 if (build < 15211 && !strcmp("world2.MPQ", CONF_mpq_list[i])) // 4.3.2 and higher MPQ
182 continue;
183
184 _stprintf(filename, _T("%s%s"), input_path, CONF_mpq_list[i]);
185 if (!SFileOpenPatchArchive(WorldMpq, filename, "", 0))
186 {
187 if (GetLastError() != ERROR_PATH_NOT_FOUND)
188 _tprintf(_T("Cannot open archive %s\n"), filename);
189 else
190 _tprintf(_T("Not found %s\n"), filename);
191 }
192 else
193 _tprintf(_T("Loaded %s\n"), filename);
194 }
195
196 char const* prefix = NULL;
197 for (int i = 0; Builds[i] && Builds[i] <= CONF_TargetBuild; ++i)
198 {
199 // Do not attempt to read older MPQ patch archives past this build, they were merged with base
200 // and trying to read them together with new base will not end well
202 continue;
203
204 memset(filename, 0, sizeof(filename));
206 {
207 prefix = "";
208 _stprintf(filename, _T("%swow-update-base-%u.MPQ"), input_path, Builds[i]);
209 }
210 else
211 {
212 prefix = "base";
213 _stprintf(filename, _T("%swow-update-%u.MPQ"), input_path, Builds[i]);
214 }
215
216 if (!SFileOpenPatchArchive(WorldMpq, filename, prefix, 0))
217 {
218 if (GetLastError() != ERROR_PATH_NOT_FOUND)
219 _tprintf(_T("Cannot open patch archive %s\n"), filename);
220 else
221 _tprintf(_T("Not found %s\n"), filename);
222 continue;
223 }
224 else
225 _tprintf(_T("Loaded %s\n"), filename);
226 }
227
228 printf("\n");
229}
230
231
232// Local testing functions
233
234bool FileExists(const char* file)
235{
236 if (FILE* n = fopen(file, "rb"))
237 {
238 fclose(n);
239 return true;
240 }
241 return false;
242}
243
244void strToLower(char* str)
245{
246 while(*str)
247 {
248 *str=tolower(*str);
249 ++str;
250 }
251}
252
253// copied from contrib/extractor/System.cpp
255{
256 HANDLE localeFile;
257 char localMPQ[1024];
258
259 snprintf(localMPQ, sizeof(localMPQ), "%smisc.MPQ", input_path);
260 if (FileExists(localMPQ)==false)
261 { // Use misc.mpq
262 snprintf(localMPQ, sizeof(localMPQ), "%s/Data/%s/locale-%s.MPQ", input_path, LocalesT[locale], LocalesT[locale]);
263 }
264
265 if (!SFileOpenArchive(localMPQ, 0, MPQ_OPEN_READ_ONLY, &localeFile))
266 {
267 return false;
268 }
269
270 printf("Read LiquidType.dbc file...");
271
272 HANDLE dbcFile;
273 if (!SFileOpenFileEx(localeFile, "DBFilesClient\\LiquidType.dbc", SFILE_OPEN_FROM_MPQ, &dbcFile))
274 {
275 if (!SFileOpenFileEx(localeFile, "DBFilesClient\\LiquidType.dbc", SFILE_OPEN_FROM_MPQ, &dbcFile))
276 {
277 printf("Fatal error: Cannot find LiquidType.dbc in archive!\n");
278 return false;
279 }
280 }
281
282 DBCFile dbc(localeFile, "DBFilesClient\\LiquidType.dbc");
283 if (!dbc.open())
284 {
285 printf("Fatal error: Invalid LiquidType.dbc file format!\n");
286 return false;
287 }
288
289 size_t LiqType_count = dbc.getRecordCount();
290 size_t LiqType_maxid = dbc.getMaxId();
291 LiqType = new uint16[LiqType_maxid + 1];
292 memset(LiqType, 0xff, (LiqType_maxid + 1) * sizeof(uint16));
293
294 for (size_t x = 0; x < LiqType_count; ++x)
295 LiqType[dbc.getRecord(x).getUInt(0)] = dbc.getRecord(x).getUInt(3);
296
297 printf("Done! (%zu LiqTypes loaded)\n", LiqType_count);
298 return true;
299}
300
302{
303 bool success = false;
304
305 //const char* ParsArchiveNames[] = {"patch-2.MPQ", "patch.MPQ", "common.MPQ", "expansion.MPQ"};
306
307 SFILE_FIND_DATA data;
308 HANDLE find = SFileFindFirstFile(WorldMpq, "*.wmo", &data, NULL);
309 if (find != NULL)
310 {
311 do
312 {
313 std::string str = data.cFileName;
314 //printf("Extracting wmo %s\n", str.c_str());
315 success |= ExtractSingleWmo(str);
316 }
317 while (SFileFindNextFile(find, &data));
318 }
319 SFileFindClose(find);
320
321 if (success)
322 printf("\nExtract wmo complete (No (fatal) errors)\n");
323
324 return success;
325}
326
327bool ExtractSingleWmo(std::string& fname)
328{
329 // Copy files from archive
330
331 char szLocalFile[1024];
332 const char * plain_name = GetPlainName(fname.c_str());
333 snprintf(szLocalFile, sizeof(szLocalFile), "%s/%s", szWorkDirWmo, plain_name);
334 FixNameCase(szLocalFile,strlen(szLocalFile));
335
336 if (FileExists(szLocalFile))
337 return true;
338
339 int p = 0;
340 // Select root wmo files
341 char const* rchr = strrchr(plain_name, '_');
342 if (rchr != NULL)
343 {
344 char cpy[4];
345 strncpy((char*)cpy, rchr, 4);
346 for (int i = 0; i < 4; ++i)
347 {
348 int m = cpy[i];
349 if (isdigit(m))
350 p++;
351 }
352 }
353
354 if (p == 3)
355 return true;
356
357 bool file_ok = true;
358 std::cout << "Extracting " << fname << std::endl;
359 WMORoot froot(fname);
360 if(!froot.open())
361 {
362 printf("Couldn't open RootWmo!!!\n");
363 return true;
364 }
365 FILE *output = fopen(szLocalFile,"wb");
366 if(!output)
367 {
368 printf("couldn't open %s for writing!\n", szLocalFile);
369 return false;
370 }
371 froot.ConvertToVMAPRootWmo(output);
372 int Wmo_nVertices = 0;
373 //printf("root has %d groups\n", froot->nGroups);
374 if (froot.nGroups !=0)
375 {
376 for (uint32 i = 0; i < froot.nGroups; ++i)
377 {
378 char temp[1024];
379 strncpy(temp, fname.c_str(), 1024);
380 temp[fname.length()-4] = 0;
381 char groupFileName[1024];
382 snprintf(groupFileName, sizeof(groupFileName), "%s_%03u.wmo", temp, i);
383 //printf("Trying to open groupfile %s\n",groupFileName);
384
385 std::string s = groupFileName;
386 WMOGroup fgroup(s);
387 if(!fgroup.open())
388 {
389 printf("Could not open all Group file for: %s\n", plain_name);
390 file_ok = false;
391 break;
392 }
393
394 Wmo_nVertices += fgroup.ConvertToVMAPGroupWmo(output, &froot, preciseVectorData);
395 }
396 }
397
398 fseek(output, 8, SEEK_SET); // store the correct no of vertices
399 fwrite(&Wmo_nVertices,sizeof(int),1,output);
400 fclose(output);
401
402 // Delete the extracted file in the case of an error
403 if (!file_ok)
404 remove(szLocalFile);
405 return true;
406}
407
409{
410 char fn[512];
411 //char id_filename[64];
412 char id[10];
413 for (unsigned int i=0; i<map_count; ++i)
414 {
415 snprintf(id, sizeof(id), "%04u", map_ids[i].id);
416 snprintf(fn, sizeof(fn), "World\\Maps\\%s\\%s.wdt", map_ids[i].name, map_ids[i].name);
417 WDTFile WDT(fn,map_ids[i].name);
418 if(WDT.init(id, map_ids[i].id))
419 {
420 printf("Processing Map %u\n[", map_ids[i].id);
421 for (int x=0; x<64; ++x)
422 {
423 for (int y=0; y<64; ++y)
424 {
425 if (ADTFile *ADT = WDT.GetMap(x,y))
426 {
427 //sprintf(id_filename,"%02u %02u %03u",x,y,map_ids[i].id);//!!!!!!!!!
428 ADT->init(map_ids[i].id, x, y);
429 delete ADT;
430 }
431 }
432 printf("#");
433 fflush(stdout);
434 }
435 printf("]\n");
436 }
437 }
438}
439
441{
442#ifdef _WIN32
443 strcpy(input_path,"Data\\");
444#else
445 strcpy(input_path,"Data/");
446#endif
447}
448
449bool processArgv(int argc, char ** argv, const char *versionString)
450{
451 bool result = true;
452 bool hasInputPathParam = false;
453 preciseVectorData = false;
454
455 for(int i = 1; i < argc; ++i)
456 {
457 if(strcmp("-s",argv[i]) == 0)
458 {
459 preciseVectorData = false;
460 }
461 else if(strcmp("-d",argv[i]) == 0)
462 {
463 if((i+1)<argc)
464 {
465 hasInputPathParam = true;
466 strncpy(input_path, argv[i + 1], sizeof(input_path));
467 input_path[sizeof(input_path) - 1] = '\0';
468
469 if (input_path[strlen(input_path) - 1] != '\\' && input_path[strlen(input_path) - 1] != '/')
470 strcat(input_path, "/");
471 ++i;
472 }
473 else
474 {
475 result = false;
476 }
477 }
478 else if(strcmp("-?",argv[1]) == 0)
479 {
480 result = false;
481 }
482 else if(strcmp("-l",argv[i]) == 0)
483 {
484 preciseVectorData = true;
485 }
486 else if(strcmp("-b",argv[i]) == 0)
487 {
488 if (i + 1 < argc) // all ok
489 CONF_TargetBuild = atoi(argv[i++ + 1]);
490 }
491 else
492 {
493 result = false;
494 break;
495 }
496 }
497
498 if(!result)
499 {
500 printf("Extract %s.\n",versionString);
501 printf("%s [-?][-s][-l][-d <path>]\n", argv[0]);
502 printf(" -s : (default) small size (data size optimization), ~500MB less vmap data.\n");
503 printf(" -l : large size, ~500MB more vmap data. (might contain more details)\n");
504 printf(" -d <path>: Path to the vector data source folder.\n");
505 printf(" -b : target build (default %u)\n", CONF_TargetBuild);
506 printf(" -? : This message.\n");
507 }
508
509 if(!hasInputPathParam)
510 getGamePath();
511
512 return result;
513}
514
515
516//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
517// Main
518//
519// The program must be run with two command line arguments
520//
521// Arg1 - The source MPQ name (for testing reading and file find)
522// Arg2 - Listfile name
523//
524
525int main(int argc, char ** argv)
526{
527 bool success=true;
528 const char *versionString = "V5.03 2025_02_09";
529
530 // Use command line arguments, when some
531 if (!processArgv(argc, argv, versionString))
532 return 1;
533
534 // some simple check if working dir is dirty
535 else
536 {
537 std::string sdir = std::string(szWorkDirWmo) + "/dir";
538 std::string sdir_bin = std::string(szWorkDirWmo) + "/dir_bin";
539 struct stat status;
540 if (!stat(sdir.c_str(), &status) || !stat(sdir_bin.c_str(), &status))
541 {
542 printf("Your output directory seems to be polluted, please use an empty directory!\n");
543 printf("<press return to exit>");
544 char garbage[2];
545 return scanf("%c", garbage);
546 }
547 }
548
549 printf("Extract %s. Beginning work ....\n\n",versionString);
550 //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
551 // Create the working directory
552 if (mkdir(szWorkDirWmo
553#if defined(__linux__) || defined(__APPLE__)
554 , 0711
555#endif
556 ))
557 success = (errno == EEXIST);
558
560
561 for (int i = 0; i < LOCALES_COUNT; ++i)
562 {
563 //Open MPQs
564 if (!LoadLocaleMPQFile(i))
565 {
566 if (GetLastError() != ERROR_PATH_NOT_FOUND)
567 printf("Unable to load %s locale archives!\n", Locales[i]);
568 continue;
569 }
570
571 printf("Detected and using locale: %s\n", Locales[i]);
572 break;
573 }
574
575 for (int i = 0; i < LOCALES_COUNT; ++i)
576 {
577 //Open MPQs
579 {
580 if (GetLastError() != ERROR_PATH_NOT_FOUND)
581 printf("Unable to load Liquid %s locale archives!\n", Locales[i]);
582 continue;
583 }
584
585 printf("Detected and using Liquid locale: %s\n", Locales[i]);
586 break;
587 }
588
590
591 // extract data
592 if (success)
593 success = ExtractWmo();
594
595 //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
596 //map.dbc
597 if (success)
598 {
599 DBCFile * dbc = new DBCFile(LocaleMpq, "DBFilesClient\\Map.dbc");
600 if (!dbc->open())
601 {
602 delete dbc;
603 printf("FATAL ERROR: Map.dbc not found in data file.\n");
604 return 1;
605 }
608 for (unsigned int x=0;x<map_count;++x)
609 {
610 map_ids[x].id=dbc->getRecord (x).getUInt(0);
611 const char* map_name = dbc->getRecord(x).getString(1);
612 size_t max_map_name_length = sizeof(map_ids[x].name);
613 if (strlen(map_name) >= max_map_name_length)
614 {
615 delete dbc;
616 delete[] map_ids;
617 printf("FATAL ERROR: Map name too long.\n");
618 return 1;
619 }
620 strncpy(map_ids[x].name, map_name, max_map_name_length);
621 map_ids[x].name[max_map_name_length - 1] = '\0';
622 printf("Map - %s\n",map_ids[x].name);
623 }
624
625
626 delete dbc;
627 ParsMapFiles();
628 delete [] map_ids;
629 //nError = ERROR_SUCCESS;
630 }
631
632 SFileCloseArchive(LocaleMpq);
633 SFileCloseArchive(WorldMpq);
634
635 printf("\n");
636 if (!success)
637 {
638 printf("ERROR: Extract %s. Work NOT complete.\n Precise vector data=%d.\nPress any key.\n",versionString, preciseVectorData);
639 getchar();
640 }
641
642 printf("Extract %s. Work complete. No errors.\n",versionString);
643 delete [] LiqType;
644 return 0;
645}
std::uint32_t uint32
Definition Define.h:77
std::uint16_t uint16
Definition Define.h:78
map_id * map_ids
Definition System.cpp:58
HANDLE WorldMpq
Definition System.cpp:49
#define ERROR_PATH_NOT_FOUND
Definition System.cpp:23
uint32 const Builds[]
Definition System.cpp:103
uint32 CONF_TargetBuild
Definition System.cpp:89
#define LOCALES_COUNT
Definition System.cpp:106
#define NEW_BASE_SET_BUILD
Definition System.cpp:105
char input_path[128]
Definition System.cpp:62
uint16 * LiqType
Definition System.cpp:60
HANDLE LocaleMpq
Definition System.cpp:50
void ReadLiquidTypeTableDBC()
Definition System.cpp:334
TCHAR const * LocalesT[LOCALES_COUNT]
Definition System.cpp:120
char output_path[128]
Definition System.cpp:61
char const * CONF_mpq_list[]
Definition System.cpp:92
char const * Locales[LOCALES_COUNT]
Definition System.cpp:108
char const * GetPlainName(char const *FileName)
Definition adtfile.cpp:16
void FixNameCase(char *name, size_t len)
Definition adtfile.cpp:34
char const * getString(size_t field) const
Definition dbcfile.h:61
unsigned int getUInt(size_t field) const
Definition dbcfile.h:49
Record getRecord(size_t id)
Definition dbcfile.cpp:68
size_t getMaxId()
Definition dbcfile.cpp:74
virtual bool open()
Definition dbcfile.cpp:16
size_t getRecordCount() const
Trivial.
Definition dbcfile.h:121
bool init(char *map_id, unsigned int mapID)
Definition wdtfile.cpp:27
ADTFile * GetMap(int x, int z)
Definition wdtfile.cpp:106
Definition wmo.h:66
int ConvertToVMAPGroupWmo(FILE *output, WMORoot *rootWMO, bool preciseVectorData)
Definition wmo.cpp:238
bool open()
Definition wmo.cpp:143
Definition wmo.h:33
bool open()
Definition wmo.cpp:31
uint32 nGroups
Definition wmo.h:38
bool ConvertToVMAPRootWmo(FILE *output)
Definition wmo.cpp:116
void ExtractGameobjectModels()
uint32 id
Definition System.cpp:55
bool preciseVectorData
int main(int argc, char **argv)
void ParsMapFiles()
void LoadCommonMPQFiles(uint32 build)
bool LoadLocaleMPQFile(int locale)
#define LAST_DBC_IN_DATA_BUILD
void getGamePath()
uint32 map_count
bool ExtractWmo()
bool processArgv(int argc, char **argv, const char *versionString)
bool FileExists(const char *file)
const char * szWorkDirWmo
void strToLower(char *str)
const char * szRawVMAPMagic
bool ExtractSingleWmo(std::string &fname)