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