TrinityCore
System.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 "Banner.h"
19#include "CascHandles.h"
20#include "Common.h"
21#include "DB2CascFileSource.h"
22#include "DB2Meta.h"
23#include "DBFilesClientList.h"
25#include "IteratorPair.h"
26#include "Locales.h"
27#include "MapDefines.h"
28#include "StringFormat.h"
29#include "Util.h"
30#include "adt.h"
31#include "wdt.h"
32#include <CascLib.h>
33#include <boost/filesystem/path.hpp>
34#include <boost/filesystem/operations.hpp>
35#include <bitset>
36#include <cstdio>
37#include <deque>
38#include <fstream>
39#include <set>
40#include <unordered_map>
41#include <cstdlib>
42#include <cstring>
43#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
44#include <io.h>
45#else
46#include <unistd.h>
47#endif
48
49std::shared_ptr<CASC::Storage> CascStorage;
50
51struct MapEntry
52{
55 std::string Name;
56 std::string Directory;
57};
58
60{
61 int8 LVF = 0;
62};
63
65{
67};
68
69struct LiquidTypeEntry
70{
71 uint8 SoundBank = 0;
72 uint8 MaterialID = 0;
73};
74
75std::vector<MapEntry> map_ids;
76std::unordered_map<uint32, LiquidMaterialEntry> LiquidMaterials;
77std::unordered_map<uint32, LiquidObjectEntry> LiquidObjects;
78std::unordered_map<uint32, LiquidTypeEntry> LiquidTypes;
79std::set<uint32> CameraFileDataIds;
80bool PrintProgress = true;
81boost::filesystem::path input_path;
82boost::filesystem::path output_path;
83
84// **************************************************
85// Extractor options
86// **************************************************
88{
93
95};
96
97// Select data for extract
99
100// This option allow limit minimum height to some value (Allow save some memory)
102float CONF_use_minHeight = -2000.0f;
103
104// This option allow use float to int conversion
106float CONF_float_to_int8_limit = 2.0f; // Max accuracy = val/256
107float CONF_float_to_int16_limit = 2048.0f; // Max accuracy = val/65536
108float CONF_flat_height_delta_limit = 0.005f; // If max - min less this value - surface is flat
109float CONF_flat_liquid_delta_limit = 0.001f; // If max - min less this value - liquid surface is flat
110
112
113char const* CONF_Product = "wow";
114char const* CONF_Region = "eu";
116
117#define CASC_LOCALES_COUNT 17
118
120{
121 "none", "enUS",
122 "koKR", "unknown",
123 "frFR", "deDE",
124 "zhCN", "esES",
125 "zhTW", "enGB",
126 "enCN", "enTW",
127 "esMX", "ruRU",
128 "ptBR", "itIT",
129 "ptPT"
130};
131
133{
134 CASC_LOCALE_ENUS | CASC_LOCALE_ENGB,
135 CASC_LOCALE_KOKR,
136 CASC_LOCALE_FRFR,
137 CASC_LOCALE_DEDE,
138 CASC_LOCALE_ZHCN,
139 CASC_LOCALE_ZHTW,
140 CASC_LOCALE_ESES,
141 CASC_LOCALE_ESMX,
142 CASC_LOCALE_RURU,
143 0,
144 CASC_LOCALE_PTBR | CASC_LOCALE_PTPT,
145 CASC_LOCALE_ITIT,
146};
147
148void CreateDir(boost::filesystem::path const& path)
149{
150 namespace fs = boost::filesystem;
151 if (fs::exists(path))
152 return;
153
154 boost::system::error_code err;
155 if (!fs::create_directory(path, err) || err)
156 throw std::runtime_error("Unable to create directory" + path.string());
157}
158
159void Usage(char const* prg)
160{
161 printf(
162 "Usage:\n"\
163 "%s -[var] [value]\n"\
164 "-i set input path\n"\
165 "-o set output path\n"\
166 "-e extract only MAP(1)/DBC(2)/Camera(4)/gt(8) - standard: all(15)\n"\
167 "-f height stored as int (less map size but lost some accuracy) 1 by default\n"\
168 "-l dbc locale\n"\
169 "-p which installed product to open (wow/wowt/wow_beta)\n"\
170 "-c use remote casc\n"\
171 "-r set remote casc region - standard: eu\n"\
172 "Example: %s -f 0 -i \"c:\\games\\game\"\n", prg, prg);
173 exit(1);
174}
175
176void HandleArgs(int argc, char* arg[])
177{
178 for (int c = 1; c < argc; ++c)
179 {
180 // i - input path
181 // o - output path
182 // e - extract only MAP(1)/DBC(2)/Camera(4)/gt(8) - standard: all(11)
183 // f - use float to int conversion
184 // h - limit minimum height
185 // l - dbc locale
186 // c - use remote casc
187 // r - set casc remote region - standard: eu
188 if (arg[c][0] != '-')
189 Usage(arg[0]);
190
191 switch (arg[c][1])
192 {
193 case 'i':
194 if (c + 1 < argc && strlen(arg[c + 1])) // all ok
195 input_path = boost::filesystem::path(arg[c++ + 1]);
196 else
197 Usage(arg[0]);
198 break;
199 case 'o':
200 if (c + 1 < argc && strlen(arg[c + 1])) // all ok
201 output_path = boost::filesystem::path(arg[c++ + 1]);
202 else
203 Usage(arg[0]);
204 break;
205 case 'f':
206 if (c + 1 < argc) // all ok
207 CONF_allow_float_to_int = atoi(arg[c++ + 1]) != 0;
208 else
209 Usage(arg[0]);
210 break;
211 case 'e':
212 if (c + 1 < argc) // all ok
213 {
214 CONF_extract = atoi(arg[c++ + 1]);
215 if (!(CONF_extract > 0 && CONF_extract <= EXTRACT_ALL))
216 Usage(arg[0]);
217 }
218 else
219 Usage(arg[0]);
220 break;
221 case 'l':
222 if (c + 1 < argc) // all ok
223 {
224 for (uint32 i = 0; i < TOTAL_LOCALES; ++i)
225 if (!strcmp(arg[c + 1], localeNames[i]))
226 CONF_Locale = 1 << i;
227 ++c;
228 }
229 else
230 Usage(arg[0]);
231 break;
232 case 'p':
233 if (c + 1 < argc && strlen(arg[c + 1])) // all ok
234 CONF_Product = arg[++c];
235 else
236 Usage(arg[0]);
237 break;
238 case 'c':
239 if (c + 1 < argc) // all ok
240 CONF_UseRemoteCasc = atoi(arg[c++ + 1]) != 0;
241 else
242 Usage(arg[0]);
243 break;
244 case 'r':
245 if (c + 1 < argc && strlen(arg[c + 1])) // all ok
246 CONF_Region = arg[c++ + 1];
247 else
248 Usage(arg[0]);
249 break;
250 case 'h':
251 Usage(arg[0]);
252 break;
253 default:
254 break;
255 }
256 }
257}
258
259void TryLoadDB2(char const* name, DB2CascFileSource* source, DB2FileLoader* db2, DB2FileLoadInfo const* loadInfo)
260{
261 try
262 {
263 db2->Load(source, loadInfo);
264 }
265 catch (std::exception const& e)
266 {
267 printf("Fatal error: Invalid %s file format! %s\n%s\n", name, CASC::HumanReadableCASCError(GetCascError()), e.what());
268 exit(1);
269 }
270}
271
273{
274 printf("Read Map.db2 file...\n");
275
276 DB2CascFileSource source(CascStorage, MapLoadInfo::Instance.Meta->FileDataId);
277 DB2FileLoader db2;
278 TryLoadDB2("Map.db2", &source, &db2, &MapLoadInfo::Instance);
279
280 map_ids.reserve(db2.GetRecordCount());
281 std::unordered_map<uint32, std::size_t> idToIndex;
282 for (uint32 x = 0; x < db2.GetRecordCount(); ++x)
283 {
284 DB2Record record = db2.GetRecord(x);
285 if (!record)
286 continue;
287
288 MapEntry map;
289 map.Id = record.GetId();
290 map.WdtFileDataId = record.GetInt32("WdtFileDataID");
291 map.Name = record.GetString("MapName");
292 map.Directory = record.GetString("Directory");
293 idToIndex[map.Id] = map_ids.size();
294 map_ids.push_back(map);
295 }
296
297 for (uint32 x = 0; x < db2.GetRecordCopyCount(); ++x)
298 {
299 DB2RecordCopy copy = db2.GetRecordCopy(x);
300 auto itr = idToIndex.find(copy.SourceRowId);
301 if (itr != idToIndex.end())
302 {
303 MapEntry map;
304 map.Id = copy.NewRowId;
305 map.WdtFileDataId = map_ids[itr->second].WdtFileDataId;
306 map.Name = map_ids[itr->second].Name;
307 map.Directory = map_ids[itr->second].Directory;
308 map_ids.push_back(map);
309 }
310 }
311
312 map_ids.erase(std::remove_if(map_ids.begin(), map_ids.end(), [](MapEntry const& map) { return !map.WdtFileDataId; }), map_ids.end());
313
314 printf("Done! (" SZFMTD " maps loaded)\n", map_ids.size());
315}
316
318{
319 printf("Read LiquidMaterial.db2 file...\n");
320
322 DB2FileLoader db2;
323 TryLoadDB2("LiquidMaterial.db2", &source, &db2, &LiquidMaterialLoadInfo::Instance);
324
325 for (uint32 x = 0; x < db2.GetRecordCount(); ++x)
326 {
327 DB2Record record = db2.GetRecord(x);
328 if (!record)
329 continue;
330
331 LiquidMaterialEntry& liquidType = LiquidMaterials[record.GetId()];
332 liquidType.LVF = record.GetUInt8("LVF");
333 }
334
335 for (uint32 x = 0; x < db2.GetRecordCopyCount(); ++x)
337
338 printf("Done! (" SZFMTD " LiquidMaterials loaded)\n", LiquidMaterials.size());
339}
340
342{
343 printf("Read LiquidObject.db2 file...\n");
344
346 DB2FileLoader db2;
347 TryLoadDB2("LiquidObject.db2", &source, &db2, &LiquidObjectLoadInfo::Instance);
348
349 for (uint32 x = 0; x < db2.GetRecordCount(); ++x)
350 {
351 DB2Record record = db2.GetRecord(x);
352 if (!record)
353 continue;
354
355 LiquidObjectEntry& liquidType = LiquidObjects[record.GetId()];
356 liquidType.LiquidTypeID = record.GetUInt16("LiquidTypeID");
357 }
358
359 for (uint32 x = 0; x < db2.GetRecordCopyCount(); ++x)
361
362 printf("Done! (" SZFMTD " LiquidObjects loaded)\n", LiquidObjects.size());
363}
364
366{
367 printf("Read LiquidType.db2 file...\n");
368
370 DB2FileLoader db2;
371 TryLoadDB2("LiquidType.db2", &source, &db2, &LiquidTypeLoadInfo::Instance);
372
373 for (uint32 x = 0; x < db2.GetRecordCount(); ++x)
374 {
375 DB2Record record = db2.GetRecord(x);
376 if (!record)
377 continue;
378
379 LiquidTypeEntry& liquidType = LiquidTypes[record.GetId()];
380 liquidType.SoundBank = record.GetUInt8("SoundBank");
381 liquidType.MaterialID = record.GetUInt8("MaterialID");
382 }
383
384 for (uint32 x = 0; x < db2.GetRecordCopyCount(); ++x)
386
387 printf("Done! (" SZFMTD " LiquidTypes loaded)\n", LiquidTypes.size());
388}
389
391{
392 printf("Read CinematicCamera.db2 file...\n");
393
395 DB2FileLoader db2;
396 TryLoadDB2("CinematicCamera.db2", &source, &db2, &CinematicCameraLoadInfo::Instance);
397
398 // get camera file list from DB2
399 for (size_t i = 0; i < db2.GetRecordCount(); ++i)
400 {
401 DB2Record record = db2.GetRecord(i);
402 if (!record)
403 continue;
404
405 CameraFileDataIds.insert(record.GetUInt32("FileDataID"));
406 }
407
408 printf("Done! (" SZFMTD " CinematicCameras loaded)\n", CameraFileDataIds.size());
409 return true;
410}
411
412//
413// Adt file convertor function and data
414//
415
416float selectUInt8StepStore(float maxDiff)
417{
418 return 255 / maxDiff;
419}
420
421float selectUInt16StepStore(float maxDiff)
422{
423 return 65535 / maxDiff;
424}
425// Temporary grid data store
427
434
440
443
445{
446 if (liquidInstance->LiquidVertexFormat < 42)
447 return static_cast<LiquidVertexFormatType>(liquidInstance->LiquidVertexFormat);
448
449 if (liquidInstance->LiquidType == 2)
451
452 auto liquidType = LiquidTypes.find(liquidInstance->LiquidType);
453 if (liquidType != LiquidTypes.end())
454 {
455 auto liquidMaterial = LiquidMaterials.find(liquidType->second.MaterialID);
456 if (liquidMaterial != LiquidMaterials.end())
457 return static_cast<LiquidVertexFormatType>(liquidMaterial->second.LVF);
458 }
459
460 return static_cast<LiquidVertexFormatType>(-1);
461}
462
463bool TransformToHighRes(uint16 lowResHoles, uint8 hiResHoles[8])
464{
465 for (uint8 i = 0; i < 8; i++)
466 {
467 for (uint8 j = 0; j < 8; j++)
468 {
469 int32 holeIdxL = (i / 2) * 4 + (j / 2);
470 if (((lowResHoles >> holeIdxL) & 1) == 1)
471 hiResHoles[i] |= (1 << j);
472 }
473 }
474
475 return *((uint64*)hiResHoles) != 0;
476}
477
478bool ConvertADT(ChunkedFile& adt, std::string const& mapName, std::string const& outputPath, int gx, int gy, uint32 build, bool ignoreDeepWater)
479{
480 // Prepare map header
481 map_fileheader map{};
482 map.mapMagic = MapMagic;
483 map.versionMagic = MapVersionMagic;
484 map.buildMagic = build;
485
486 // Get area flags data
487 memset(area_ids, 0, sizeof(area_ids));
488 memset(V9, 0, sizeof(V9));
489 memset(V8, 0, sizeof(V8));
490
491 memset(liquid_show, 0, sizeof(liquid_show));
492 memset(liquid_flags, 0, sizeof(liquid_flags));
493 memset(liquid_entry, 0, sizeof(liquid_entry));
494
495 memset(holes, 0, sizeof(holes));
496
497 bool hasHoles = false;
498 bool hasFlightBox = false;
499
500 for (auto const& [_, rawChunk] : Trinity::Containers::MapEqualRange(adt.chunks, "MCNK"))
501 {
502 adt_MCNK* mcnk = rawChunk->As<adt_MCNK>();
503
504 // Area data
505 area_ids[mcnk->iy][mcnk->ix] = mcnk->areaid;
506
507 // Height
508 // Height values for triangles stored in order:
509 // 1 2 3 4 5 6 7 8 9
510 // 10 11 12 13 14 15 16 17
511 // 18 19 20 21 22 23 24 25 26
512 // 27 28 29 30 31 32 33 34
513 // . . . . . . . .
514 // For better get height values merge it to V9 and V8 map
515 // V9 height map:
516 // 1 2 3 4 5 6 7 8 9
517 // 18 19 20 21 22 23 24 25 26
518 // . . . . . . . .
519 // V8 height map:
520 // 10 11 12 13 14 15 16 17
521 // 27 28 29 30 31 32 33 34
522 // . . . . . . . .
523
524 // Set map height as grid height
525 for (int y = 0; y <= ADT_CELL_SIZE; y++)
526 {
527 // edge V9s are overlapping between cells (i * ADT_CELL_SIZE is correct, otherwise we would be missing a row/column of V8s between)
528 int cy = mcnk->iy * ADT_CELL_SIZE + y;
529 for (int x = 0; x <= ADT_CELL_SIZE; x++)
530 {
531 int cx = mcnk->ix * ADT_CELL_SIZE + x;
532 V9[cy][cx] = mcnk->ypos;
533 }
534 }
535
536 for (int y = 0; y < ADT_CELL_SIZE; y++)
537 {
538 int cy = mcnk->iy * ADT_CELL_SIZE + y;
539 for (int x = 0; x < ADT_CELL_SIZE; x++)
540 {
541 int cx = mcnk->ix * ADT_CELL_SIZE + x;
542 V8[cy][cx] = mcnk->ypos;
543 }
544 }
545
546 // Get custom height
547 if (FileChunk* chunk = rawChunk->GetSubChunk("MCVT"))
548 {
549 adt_MCVT* mcvt = chunk->As<adt_MCVT>();
550 // get V9 height map
551 for (int y = 0; y <= ADT_CELL_SIZE; y++)
552 {
553 // edge V9s are overlapping between cells (i * ADT_CELL_SIZE is correct, otherwise we would be missing a row/column of V8s between)
554 int cy = mcnk->iy * ADT_CELL_SIZE + y;
555 for (int x = 0; x <= ADT_CELL_SIZE; x++)
556 {
557 int cx = mcnk->ix * ADT_CELL_SIZE + x;
558 V9[cy][cx] += mcvt->height_map[y*(ADT_CELL_SIZE * 2 + 1) + x];
559 }
560 }
561 // get V8 height map
562 for (int y = 0; y < ADT_CELL_SIZE; y++)
563 {
564 int cy = mcnk->iy * ADT_CELL_SIZE + y;
565 for (int x = 0; x < ADT_CELL_SIZE; x++)
566 {
567 int cx = mcnk->ix * ADT_CELL_SIZE + x;
568 V8[cy][cx] += mcvt->height_map[y*(ADT_CELL_SIZE * 2 + 1) + ADT_CELL_SIZE + 1 + x];
569 }
570 }
571 }
572
573 // Liquid data
574 if (mcnk->sizeMCLQ > 8)
575 {
576 if (FileChunk* chunk = rawChunk->GetSubChunk("MCLQ"))
577 {
578 adt_MCLQ* liquid = chunk->As<adt_MCLQ>();
579 int count = 0;
580 for (int y = 0; y < ADT_CELL_SIZE; ++y)
581 {
582 int cy = mcnk->iy * ADT_CELL_SIZE + y;
583 for (int x = 0; x < ADT_CELL_SIZE; ++x)
584 {
585 int cx = mcnk->ix * ADT_CELL_SIZE + x;
586 if (liquid->flags[y][x] != 0x0F)
587 {
588 liquid_show[cy][cx] = true;
589 if (!ignoreDeepWater && liquid->flags[y][x] & (1 << 7))
591 ++count;
592 }
593 }
594 }
595
596 uint32 c_flag = mcnk->flags;
597 if (c_flag & (1 << 2))
598 {
599 liquid_entry[mcnk->iy][mcnk->ix] = 1;
600 liquid_flags[mcnk->iy][mcnk->ix] |= map_liquidHeaderTypeFlags::Water; // water
601 }
602 if (c_flag & (1 << 3))
603 {
604 liquid_entry[mcnk->iy][mcnk->ix] = 2;
605 liquid_flags[mcnk->iy][mcnk->ix] |= map_liquidHeaderTypeFlags::Ocean; // ocean
606 }
607 if (c_flag & (1 << 4))
608 {
609 liquid_entry[mcnk->iy][mcnk->ix] = 3;
610 liquid_flags[mcnk->iy][mcnk->ix] |= map_liquidHeaderTypeFlags::Magma; // magma/slime
611 }
612
613 if (!count && liquid_flags[mcnk->iy][mcnk->ix] != map_liquidHeaderTypeFlags::NoWater)
614 fprintf(stderr, "Wrong liquid detect in MCLQ chunk");
615
616 for (int y = 0; y <= ADT_CELL_SIZE; ++y)
617 {
618 int cy = mcnk->iy * ADT_CELL_SIZE + y;
619 for (int x = 0; x <= ADT_CELL_SIZE; ++x)
620 {
621 int cx = mcnk->ix * ADT_CELL_SIZE + x;
622 liquid_height[cy][cx] = liquid->liquid[y][x].height;
623 }
624 }
625 }
626 }
627
628 // Hole data
629 if (!(mcnk->flags & 0x10000))
630 {
631 if (uint16 hole = mcnk->holes)
632 if (TransformToHighRes(hole, holes[mcnk->iy][mcnk->ix]))
633 hasHoles = true;
634 }
635 else
636 {
637 memcpy(holes[mcnk->iy][mcnk->ix], mcnk->union_5_3_0.HighResHoles, sizeof(uint64));
638 if (*((uint64*)holes[mcnk->iy][mcnk->ix]) != 0)
639 hasHoles = true;
640 }
641 }
642
643 // Get liquid map for grid (in WOTLK used MH2O chunk)
644 if (FileChunk* chunk = adt.GetChunk("MH2O"))
645 {
646 adt_MH2O* h2o = chunk->As<adt_MH2O>();
647 for (int32 i = 0; i < ADT_CELLS_PER_GRID; i++)
648 {
649 for (int32 j = 0; j < ADT_CELLS_PER_GRID; j++)
650 {
651 adt_liquid_instance const* h = h2o->GetLiquidInstance(i, j);
652 if (!h)
653 continue;
654
656
657 int32 count = 0;
658 uint64 existsMask = h2o->GetLiquidExistsBitmap(h);
659 for (int32 y = 0; y < h->GetHeight(); y++)
660 {
661 int32 cy = i * ADT_CELL_SIZE + y + h->GetOffsetY();
662 for (int32 x = 0; x < h->GetWidth(); x++)
663 {
664 int32 cx = j * ADT_CELL_SIZE + x + h->GetOffsetX();
665 if (existsMask & 1)
666 {
667 liquid_show[cy][cx] = true;
668 ++count;
669 }
670 existsMask >>= 1;
671 }
672 }
673
674 liquid_entry[i][j] = h2o->GetLiquidType(h);
675 switch (LiquidTypes.at(liquid_entry[i][j]).SoundBank)
676 {
681 default:
682 printf("\nCan't find Liquid type %u for map %s [%u,%u]\nchunk %d,%d\n", h->LiquidType, mapName.c_str(), gx, gy, i, j);
683 break;
684 }
685
687 printf("Wrong liquid detect in MH2O chunk");
688
689 int32 pos = 0;
690 for (int32 y = 0; y <= h->GetHeight(); y++)
691 {
692 int32 cy = i * ADT_CELL_SIZE + y + h->GetOffsetY();
693 for (int32 x = 0; x <= h->GetWidth(); x++)
694 {
695 int32 cx = j * ADT_CELL_SIZE + x + h->GetOffsetX();
696 liquid_height[cy][cx] = h2o->GetLiquidHeight(h, pos);
697 pos++;
698 }
699 }
700 }
701 }
702 }
703
704 if (FileChunk* chunk = adt.GetChunk("MFBO"))
705 {
706 adt_MFBO* mfbo = chunk->As<adt_MFBO>();
707 memcpy(flight_box_max, &mfbo->max, sizeof(flight_box_max));
708 memcpy(flight_box_min, &mfbo->min, sizeof(flight_box_min));
709 hasFlightBox = true;
710 }
711
712 //============================================
713 // Try pack area data
714 //============================================
715 bool fullAreaData = false;
716 uint32 areaId = area_ids[0][0];
717 for (int y = 0; y < ADT_CELLS_PER_GRID; ++y)
718 {
719 for (int x = 0; x < ADT_CELLS_PER_GRID; ++x)
720 {
721 if (area_ids[y][x] != areaId)
722 {
723 fullAreaData = true;
724 break;
725 }
726 }
727 }
728
729 map.areaMapOffset = sizeof(map);
730 map.areaMapSize = sizeof(map_areaHeader);
731
732 map_areaHeader areaHeader;
733 areaHeader.areaMagic = MapAreaMagic;
734 areaHeader.flags = map_areaHeaderFlags::None;
735 if (fullAreaData)
736 {
737 areaHeader.gridArea = 0;
738 map.areaMapSize += sizeof(area_ids);
739 }
740 else
741 {
743 areaHeader.gridArea = static_cast<uint16>(areaId);
744 }
745
746 //============================================
747 // Try pack height data
748 //============================================
749 float maxHeight = -20000;
750 float minHeight = 20000;
751 for (int y=0; y<ADT_GRID_SIZE; y++)
752 {
753 for(int x=0;x<ADT_GRID_SIZE;x++)
754 {
755 float h = V8[y][x];
756 if (maxHeight < h) maxHeight = h;
757 if (minHeight > h) minHeight = h;
758 }
759 }
760 for (int y=0; y<=ADT_GRID_SIZE; y++)
761 {
762 for(int x=0;x<=ADT_GRID_SIZE;x++)
763 {
764 float h = V9[y][x];
765 if (maxHeight < h) maxHeight = h;
766 if (minHeight > h) minHeight = h;
767 }
768 }
769
770 // Check for allow limit minimum height (not store height in deep ochean - allow save some memory)
772 {
773 for (int y=0; y<ADT_GRID_SIZE; y++)
774 for(int x=0;x<ADT_GRID_SIZE;x++)
775 if (V8[y][x] < CONF_use_minHeight)
776 V8[y][x] = CONF_use_minHeight;
777 for (int y=0; y<=ADT_GRID_SIZE; y++)
778 for(int x=0;x<=ADT_GRID_SIZE;x++)
779 if (V9[y][x] < CONF_use_minHeight)
780 V9[y][x] = CONF_use_minHeight;
781 if (minHeight < CONF_use_minHeight)
782 minHeight = CONF_use_minHeight;
783 if (maxHeight < CONF_use_minHeight)
784 maxHeight = CONF_use_minHeight;
785 }
786
787 map.heightMapOffset = map.areaMapOffset + map.areaMapSize;
788 map.heightMapSize = sizeof(map_heightHeader);
789
790 map_heightHeader heightHeader;
791 heightHeader.heightMagic = MapHeightMagic;
792 heightHeader.flags = map_heightHeaderFlags::None;
793 heightHeader.gridHeight = minHeight;
794 heightHeader.gridMaxHeight = maxHeight;
795
796 if (maxHeight == minHeight)
798
799 // Not need store if flat surface
800 if (CONF_allow_float_to_int && (maxHeight - minHeight) < CONF_flat_height_delta_limit)
802
803 if (hasFlightBox)
804 {
806 map.heightMapSize += sizeof(flight_box_max) + sizeof(flight_box_min);
807 }
808
809 // Try store as packed in uint16 or uint8 values
811 {
812 float step = 0;
813 // Try Store as uint values
815 {
816 float diff = maxHeight - minHeight;
817 if (diff < CONF_float_to_int8_limit) // As uint8 (max accuracy = CONF_float_to_int8_limit/256)
818 {
820 step = selectUInt8StepStore(diff);
821 }
822 else if (diff < CONF_float_to_int16_limit) // As uint16 (max accuracy = CONF_float_to_int16_limit/65536)
823 {
825 step = selectUInt16StepStore(diff);
826 }
827 }
828
829 // Pack it to int values if need
831 {
832 for (int y=0; y<ADT_GRID_SIZE; y++)
833 for(int x=0;x<ADT_GRID_SIZE;x++)
834 uint8_V8[y][x] = uint8((V8[y][x] - minHeight) * step + 0.5f);
835 for (int y=0; y<=ADT_GRID_SIZE; y++)
836 for(int x=0;x<=ADT_GRID_SIZE;x++)
837 uint8_V9[y][x] = uint8((V9[y][x] - minHeight) * step + 0.5f);
838 map.heightMapSize+= sizeof(uint8_V9) + sizeof(uint8_V8);
839 }
841 {
842 for (int y=0; y<ADT_GRID_SIZE; y++)
843 for(int x=0;x<ADT_GRID_SIZE;x++)
844 uint16_V8[y][x] = uint16((V8[y][x] - minHeight) * step + 0.5f);
845 for (int y=0; y<=ADT_GRID_SIZE; y++)
846 for(int x=0;x<=ADT_GRID_SIZE;x++)
847 uint16_V9[y][x] = uint16((V9[y][x] - minHeight) * step + 0.5f);
848 map.heightMapSize+= sizeof(uint16_V9) + sizeof(uint16_V8);
849 }
850 else
851 map.heightMapSize+= sizeof(V9) + sizeof(V8);
852 }
853
854 //============================================
855 // Pack liquid data
856 //============================================
857 uint16 firstLiquidType = liquid_entry[0][0];
858 map_liquidHeaderTypeFlags firstLiquidFlag = liquid_flags[0][0];
859 bool fullType = false;
860 for (int y = 0; y < ADT_CELLS_PER_GRID; y++)
861 {
862 for (int x = 0; x < ADT_CELLS_PER_GRID; x++)
863 {
864 if (liquid_entry[y][x] != firstLiquidType || liquid_flags[y][x] != firstLiquidFlag)
865 {
866 fullType = true;
868 break;
869 }
870 }
871 }
872
873 map_liquidHeader liquidHeader;
874
875 // no water data (if all grid have 0 liquid type)
876 if (firstLiquidFlag == map_liquidHeaderTypeFlags::NoWater && !fullType)
877 {
878 // No liquid data
879 map.liquidMapOffset = 0;
880 map.liquidMapSize = 0;
881 }
882 else
883 {
884 int minX = 255, minY = 255;
885 int maxX = 0, maxY = 0;
886 maxHeight = -20000;
887 minHeight = 20000;
888 for (int y=0; y<ADT_GRID_SIZE; y++)
889 {
890 for(int x=0; x<ADT_GRID_SIZE; x++)
891 {
892 if (liquid_show[y][x])
893 {
894 if (minX > x) minX = x;
895 if (maxX < x) maxX = x;
896 if (minY > y) minY = y;
897 if (maxY < y) maxY = y;
898 float h = liquid_height[y][x];
899 if (maxHeight < h) maxHeight = h;
900 if (minHeight > h) minHeight = h;
901 }
902 else
903 {
905 if (minHeight > CONF_use_minHeight) minHeight = CONF_use_minHeight;
906 }
907 }
908 }
909 map.liquidMapOffset = map.heightMapOffset + map.heightMapSize;
910 map.liquidMapSize = sizeof(map_liquidHeader);
911 liquidHeader.liquidMagic = MapLiquidMagic;
912 liquidHeader.flags = map_liquidHeaderFlags::None;
913 liquidHeader.liquidType = 0;
914 liquidHeader.offsetX = minX;
915 liquidHeader.offsetY = minY;
916 liquidHeader.width = maxX - minX + 1 + 1;
917 liquidHeader.height = maxY - minY + 1 + 1;
918 liquidHeader.liquidLevel = minHeight;
919
920 if (maxHeight == minHeight)
922
923 // Not need store if flat surface
924 if (CONF_allow_float_to_int && (maxHeight - minHeight) < CONF_flat_liquid_delta_limit)
926
927 if (!fullType)
928 liquidHeader.flags |= map_liquidHeaderFlags::NoType;
929
931 {
932 liquidHeader.liquidFlags = firstLiquidFlag;
933 liquidHeader.liquidType = firstLiquidType;
934 }
935 else
936 map.liquidMapSize += sizeof(liquid_entry) + sizeof(liquid_flags);
937
939 map.liquidMapSize += sizeof(float)*liquidHeader.width*liquidHeader.height;
940 }
941
942 if (hasHoles)
943 {
944 if (map.liquidMapOffset)
945 map.holesOffset = map.liquidMapOffset + map.liquidMapSize;
946 else
947 map.holesOffset = map.heightMapOffset + map.heightMapSize;
948
949 map.holesSize = sizeof(holes);
950 }
951 else
952 {
953 map.holesOffset = 0;
954 map.holesSize = 0;
955 }
956
957 // Ok all data prepared - store it
958 std::ofstream outFile(outputPath, std::ofstream::out | std::ofstream::binary);
959 if (!outFile)
960 {
961 printf("Can't create the output file '%s'\n", outputPath.c_str());
962 return false;
963 }
964
965 outFile.write(reinterpret_cast<char const*>(&map), sizeof(map));
966 // Store area data
967 outFile.write(reinterpret_cast<char const*>(&areaHeader), sizeof(areaHeader));
969 outFile.write(reinterpret_cast<char const*>(area_ids), sizeof(area_ids));
970
971 // Store height data
972 outFile.write(reinterpret_cast<char const*>(&heightHeader), sizeof(heightHeader));
974 {
976 {
977 outFile.write(reinterpret_cast<char const*>(uint16_V9), sizeof(uint16_V9));
978 outFile.write(reinterpret_cast<char const*>(uint16_V8), sizeof(uint16_V8));
979 }
981 {
982 outFile.write(reinterpret_cast<char const*>(uint8_V9), sizeof(uint8_V9));
983 outFile.write(reinterpret_cast<char const*>(uint8_V8), sizeof(uint8_V8));
984 }
985 else
986 {
987 outFile.write(reinterpret_cast<char const*>(V9), sizeof(V9));
988 outFile.write(reinterpret_cast<char const*>(V8), sizeof(V8));
989 }
990 }
991
993 {
994 outFile.write(reinterpret_cast<char*>(flight_box_max), sizeof(flight_box_max));
995 outFile.write(reinterpret_cast<char*>(flight_box_min), sizeof(flight_box_min));
996 }
997
998 // Store liquid data if need
999 if (map.liquidMapOffset)
1000 {
1001 outFile.write(reinterpret_cast<char const*>(&liquidHeader), sizeof(liquidHeader));
1002 if (!liquidHeader.flags.HasFlag(map_liquidHeaderFlags::NoType))
1003 {
1004 outFile.write(reinterpret_cast<char const*>(liquid_entry), sizeof(liquid_entry));
1005 outFile.write(reinterpret_cast<char const*>(liquid_flags), sizeof(liquid_flags));
1006 }
1007
1009 {
1010 for (int y = 0; y < liquidHeader.height; y++)
1011 outFile.write(reinterpret_cast<char const*>(&liquid_height[y + liquidHeader.offsetY][liquidHeader.offsetX]), sizeof(float) * liquidHeader.width);
1012 }
1013 }
1014
1015 // store hole data
1016 if (hasHoles)
1017 outFile.write(reinterpret_cast<char const*>(holes), map.holesSize);
1018
1019 outFile.close();
1020
1021 return true;
1022}
1023
1024bool ConvertADT(std::string const& fileName, std::string const& mapName, std::string const& outputPath, int gx, int gy, uint32 build, bool ignoreDeepWater)
1025{
1026 ChunkedFile adt;
1027
1028 if (!adt.loadFile(CascStorage, fileName))
1029 return false;
1030
1031 return ConvertADT(adt, mapName, outputPath, gx, gy, build, ignoreDeepWater);
1032}
1033
1034bool ConvertADT(uint32 fileDataId, std::string const& mapName, std::string const& outputPath, int gx, int gy, uint32 build, bool ignoreDeepWater)
1035{
1036 ChunkedFile adt;
1037
1038 if (!adt.loadFile(CascStorage, fileDataId, Trinity::StringFormat("Map {} grid [{},{}]", mapName, gx, gy)))
1039 return false;
1040
1041 return ConvertADT(adt, mapName, outputPath, gx, gy, build, ignoreDeepWater);
1042}
1043
1045{
1046 if (mapId == 0)
1047 {
1048 // GRID(39, 24) || GRID(39, 25) || GRID(39, 26) ||
1049 // GRID(40, 24) || GRID(40, 25) || GRID(40, 26) ||
1050 //GRID(41, 18) || GRID(41, 19) || GRID(41, 20) || GRID(41, 21) || GRID(41, 22) || GRID(41, 23) || GRID(41, 24) || GRID(41, 25) || GRID(41, 26) ||
1051 //GRID(42, 18) || GRID(42, 19) || GRID(42, 20) || GRID(42, 21) || GRID(42, 22) || GRID(42, 23) || GRID(42, 24) || GRID(42, 25) || GRID(42, 26) ||
1052 //GRID(43, 18) || GRID(43, 19) || GRID(43, 20) || GRID(43, 21) || GRID(43, 22) || GRID(43, 23) || GRID(43, 24) || GRID(43, 25) || GRID(43, 26) ||
1053 //GRID(44, 18) || GRID(44, 19) || GRID(44, 20) || GRID(44, 21) || GRID(44, 22) || GRID(44, 23) || GRID(44, 24) || GRID(44, 25) || GRID(44, 26) ||
1054 //GRID(45, 18) || GRID(45, 19) || GRID(45, 20) || GRID(45, 21) || GRID(45, 22) || GRID(45, 23) || GRID(45, 24) || GRID(45, 25) || GRID(45, 26) ||
1055 //GRID(46, 18) || GRID(46, 19) || GRID(46, 20) || GRID(46, 21) || GRID(46, 22) || GRID(46, 23) || GRID(46, 24) || GRID(46, 25) || GRID(46, 26)
1056
1057 // Vashj'ir grids completely ignore fatigue
1058 return (x >= 39 && x <= 40 && y >= 24 && y <= 26) || (x >= 41 && x <= 46 && y >= 18 && y <= 26);
1059 }
1060
1061 if (mapId == 1)
1062 {
1063 // GRID(43, 39) || GRID(43, 40)
1064 // Thousand Needles
1065 return x == 43 && (y == 39 || y == 40);
1066 }
1067
1068 return false;
1069}
1070
1072{
1073 std::string outputFileName;
1074
1075 printf("Extracting maps...\n");
1076
1077 ReadMapDBC();
1078
1082
1083 CreateDir(output_path / "maps");
1084
1085 printf("Convert map files\n");
1086 for (std::size_t z = 0; z < map_ids.size(); ++z)
1087 {
1088 printf("Extract %s (" SZFMTD "/" SZFMTD ") \n", map_ids[z].Name.c_str(), z + 1, map_ids.size());
1089 // Loadup map grid data
1090 ChunkedFile wdt;
1091 std::bitset<(WDT_MAP_SIZE) * (WDT_MAP_SIZE)> existingTiles;
1092 if (wdt.loadFile(CascStorage, map_ids[z].WdtFileDataId, Trinity::StringFormat("WDT for map {}", map_ids[z].Id), false))
1093 {
1094 FileChunk* mphd = wdt.GetChunk("MPHD");
1095 FileChunk* main = wdt.GetChunk("MAIN");
1096 FileChunk* maid = wdt.GetChunk("MAID");
1097 for (uint32 y = 0; y < WDT_MAP_SIZE; ++y)
1098 {
1099 for (uint32 x = 0; x < WDT_MAP_SIZE; ++x)
1100 {
1101 if (!(main->As<wdt_MAIN>()->adt_list[y][x].flag & 0x1))
1102 continue;
1103
1104 outputFileName = Trinity::StringFormat("{}/maps/{:04}_{:02}_{:02}.map", output_path.string(), map_ids[z].Id, y, x);
1105 bool ignoreDeepWater = IsDeepWaterIgnored(map_ids[z].Id, y, x);
1106 if (mphd && mphd->As<wdt_MPHD>()->flags & 0x200)
1107 {
1108 existingTiles[y * WDT_MAP_SIZE + x] = ConvertADT(maid->As<wdt_MAID>()->adt_files[y][x].rootADT, map_ids[z].Name, outputFileName, y, x, build, ignoreDeepWater);
1109 }
1110 else
1111 {
1112 std::string storagePath = Trinity::StringFormat(R"(World\Maps\{}\{}_{}_{}.adt)", map_ids[z].Directory, map_ids[z].Directory, x, y);
1113 existingTiles[y * WDT_MAP_SIZE + x] = ConvertADT(storagePath, map_ids[z].Name, outputFileName, y, x, build, ignoreDeepWater);
1114 }
1115 }
1116
1117 // draw progress bar
1118 if (PrintProgress)
1119 printf("Processing........................%d%%\r", (100 * (y + 1)) / WDT_MAP_SIZE);
1120 }
1121 }
1122
1123 if (FILE* tileList = fopen(Trinity::StringFormat("{}/maps/{:04}.tilelist", output_path.string(), map_ids[z].Id).c_str(), "wb"))
1124 {
1125 fwrite(MapMagic.data(), 1, MapMagic.size(), tileList);
1126 fwrite(&MapVersionMagic, 1, sizeof(MapVersionMagic), tileList);
1127 fwrite(&build, sizeof(build), 1, tileList);
1128 fwrite(existingTiles.to_string().c_str(), 1, existingTiles.size(), tileList);
1129 fclose(tileList);
1130 }
1131 }
1132
1133 printf("\n");
1134}
1135
1136bool ExtractFile(CASC::File* fileInArchive, std::string const& filename)
1137{
1138 int64 fileSize = fileInArchive->GetSize();
1139 if (fileSize == -1)
1140 {
1141 printf("Can't read file size of '%s'\n", filename.c_str());
1142 return false;
1143 }
1144
1145 FILE* output = fopen(filename.c_str(), "wb");
1146 if (!output)
1147 {
1148 printf("Can't create the output file '%s'\n", filename.c_str());
1149 return false;
1150 }
1151
1152 char buffer[0x10000];
1153 uint32 readBytes;
1154
1155 do
1156 {
1157 readBytes = 0;
1158 if (!fileInArchive->ReadFile(buffer, std::min<uint32>(fileSize, sizeof(buffer)), &readBytes))
1159 {
1160 printf("Can't read file '%s'\n", filename.c_str());
1161 fclose(output);
1162 boost::filesystem::remove(filename);
1163 return false;
1164 }
1165
1166 if (!readBytes)
1167 break;
1168
1169 fwrite(buffer, 1, readBytes, output);
1170 fileSize -= readBytes;
1171 if (!fileSize) // now we have read entire file
1172 break;
1173
1174 } while (true);
1175
1176 fclose(output);
1177 return true;
1178}
1179
1180bool ExtractDB2File(uint32 fileDataId, char const* cascFileName, int locale, boost::filesystem::path const& outputPath)
1181{
1182 DB2CascFileSource source(CascStorage, fileDataId, false);
1183 if (!source.IsOpen())
1184 {
1185 printf("Unable to open file %s in the archive for locale %s: %s\n", cascFileName, localeNames[locale], CASC::HumanReadableCASCError(GetCascError()));
1186 return false;
1187 }
1188
1189 int64 fileSize = source.GetFileSize();
1190 if (fileSize == -1)
1191 {
1192 printf("Can't read file size of '%s'\n", cascFileName);
1193 return false;
1194 }
1195
1196 DB2FileLoader db2;
1197 try
1198 {
1199 db2.LoadHeaders(&source, nullptr);
1200 }
1201 catch (std::exception const& e)
1202 {
1203 printf("Can't read DB2 headers of '%s': %s\n", cascFileName, e.what());
1204 return false;
1205 }
1206
1207 std::string outputFileName = outputPath.string();
1208 FILE* output = fopen(outputFileName.c_str(), "wb");
1209 if (!output)
1210 {
1211 printf("Can't create the output file '%s'\n", outputFileName.c_str());
1212 return false;
1213 }
1214
1215 DB2Header header = db2.GetHeader();
1216
1217 int64 posAfterHeaders = 0;
1218 posAfterHeaders += fwrite(&header, 1, sizeof(header), output);
1219
1220 // erase TactId from header if key is known
1221 for (uint32 i = 0; i < header.SectionCount; ++i)
1222 {
1223 DB2SectionHeader sectionHeader = db2.GetSectionHeader(i);
1224 if (sectionHeader.TactId && CascStorage->HasTactKey(sectionHeader.TactId))
1225 sectionHeader.TactId = DUMMY_KNOWN_TACT_ID;
1226
1227 posAfterHeaders += fwrite(&sectionHeader, 1, sizeof(sectionHeader), output);
1228 }
1229
1230 char buffer[0x10000];
1231 uint32 readBatchSize = 0x10000;
1232 uint32 readBytes;
1233 source.SetPosition(posAfterHeaders);
1234
1235 do
1236 {
1237 readBytes = 0;
1238 if (!source.GetNativeHandle()->ReadFile(buffer, std::min<uint32>(fileSize, readBatchSize), &readBytes))
1239 {
1240 printf("Can't read file '%s'\n", outputFileName.c_str());
1241 fclose(output);
1242 boost::filesystem::remove(outputPath);
1243 return false;
1244 }
1245
1246 if (!readBytes)
1247 break;
1248
1249 fwrite(buffer, 1, readBytes, output);
1250 fileSize -= readBytes;
1251 readBatchSize = 0x10000;
1252 if (!fileSize) // now we have read entire file
1253 break;
1254
1255 } while (true);
1256
1257 fclose(output);
1258 return true;
1259}
1260
1261char const* GetCascFilenamePart(char const* cascPath)
1262{
1263 if (char const* lastSep = strrchr(cascPath, '\\'))
1264 return lastSep + 1;
1265
1266 return cascPath;
1267}
1268
1270{
1271 printf("Extracting dbc/db2 files...\n");
1272
1273 boost::filesystem::path localePath = output_path / "dbc" / localeNames[l];
1274
1275 CreateDir(output_path / "dbc");
1276 CreateDir(localePath);
1277
1278 printf("locale %s output path %s\n", localeNames[l], localePath.string().c_str());
1279
1280 uint32 count = 0;
1281 for (DB2FileInfo const& db2 : DBFilesClientList)
1282 {
1283 boost::filesystem::path filePath = localePath / db2.Name;
1284
1285 if (!boost::filesystem::exists(filePath))
1286 if (ExtractDB2File(db2.FileDataId, db2.Name, l, filePath.string()))
1287 ++count;
1288
1289 }
1290
1291 printf("Extracted %u files\n\n", count);
1292}
1293
1295{
1296 printf("Extracting camera files...\n");
1297
1299 return;
1300
1301 boost::filesystem::path outputPath = output_path / "cameras";
1302
1303 CreateDir(outputPath);
1304
1305 printf("output path %s\n", outputPath.string().c_str());
1306
1307 // extract M2s
1308 uint32 count = 0;
1309 for (uint32 cameraFileDataId : CameraFileDataIds)
1310 {
1311 std::unique_ptr<CASC::File> cameraFile(CascStorage->OpenFile(cameraFileDataId, CASC_LOCALE_NONE));
1312 if (cameraFile)
1313 {
1314 boost::filesystem::path filePath = outputPath / Trinity::StringFormat("FILE{:08X}.xxx", cameraFileDataId);
1315
1316 if (!boost::filesystem::exists(filePath))
1317 if (ExtractFile(cameraFile.get(), filePath.string()))
1318 ++count;
1319 }
1320 else
1321 printf("Unable to open file %u in the archive: %s\n", cameraFileDataId, CASC::HumanReadableCASCError(GetCascError()));
1322 }
1323
1324 printf("Extracted %u camera files\n", count);
1325}
1326
1328{
1329 printf("Extracting game tables...\n");
1330
1331 boost::filesystem::path outputPath = output_path / "gt";
1332
1333 CreateDir(outputPath);
1334
1335 printf("output path %s\n", outputPath.string().c_str());
1336
1337 DB2FileInfo GameTables[] =
1338 {
1339 { 1582086, "ArtifactKnowledgeMultiplier.txt" },
1340 { 1391662, "ArtifactLevelXP.txt" },
1341 { 1391663, "BarberShopCostBase.txt" },
1342 { 1391664, "BaseMp.txt" },
1343 { 4494528, "BaseProfessionRatings.txt" },
1344 { 1391665, "BattlePetTypeDamageMod.txt" },
1345 { 1391666, "BattlePetXP.txt" },
1346 { 1391669, "CombatRatings.txt" },
1347 { 1391670, "CombatRatingsMultByILvl.txt" },
1348 { 1391671, "HonorLevel.txt" },
1349 { 1391642, "HpPerSta.txt" },
1350 { 2012881, "ItemLevelByLevel.txt" },
1351 { 1726830, "ItemLevelSquish.txt" },
1352 { 1391643, "ItemSocketCostPerLevel.txt" },
1353 { 1391651, "NPCManaCostScaler.txt" },
1354 { 4492239, "ProfessionRatings.txt" },
1355 { 1391659, "SandboxScaling.txt" },
1356 { 1391660, "SpellScaling.txt" },
1357 { 1980632, "StaminaMultByILvl.txt" },
1358 { 1391661, "xp.txt" }
1359 };
1360
1361 uint32 count = 0;
1362 for (DB2FileInfo const& gt : GameTables)
1363 {
1364 std::unique_ptr<CASC::File> dbcFile(CascStorage->OpenFile(gt.FileDataId, CASC_LOCALE_NONE));
1365 if (dbcFile)
1366 {
1367 boost::filesystem::path filePath = outputPath / gt.Name;
1368
1369 if (!boost::filesystem::exists(filePath))
1370 if (ExtractFile(dbcFile.get(), filePath.string()))
1371 ++count;
1372 }
1373 else
1374 printf("Unable to open file %s in the archive: %s\n", gt.Name, CASC::HumanReadableCASCError(GetCascError()));
1375 }
1376
1377 printf("Extracted %u files\n\n", count);
1378}
1379
1380bool OpenCascStorage(int locale)
1381{
1382 try
1383 {
1385 {
1386 boost::filesystem::path const cache_dir(boost::filesystem::canonical(input_path) / "CascCache");
1388 if (CascStorage)
1389 return true;
1390
1391 printf("Unable to open remote casc fallback to local casc\n");
1392 }
1393
1394 boost::filesystem::path const storage_dir(boost::filesystem::canonical(input_path) / "Data");
1396 if (!CascStorage)
1397 {
1398 printf("error opening casc storage '%s' locale %s\n", storage_dir.string().c_str(), localeNames[locale]);
1399 return false;
1400 }
1401
1402 return true;
1403 }
1404 catch (boost::filesystem::filesystem_error const& error)
1405 {
1406 printf("Error opening CASC storage: %s\n", error.what());
1407 return false;
1408 }
1409}
1410
1412{
1413 try
1414 {
1416 {
1417 boost::filesystem::path const cache_dir(boost::filesystem::canonical(input_path) / "CascCache");
1418 std::unique_ptr<CASC::Storage> storage(CASC::Storage::OpenRemote(cache_dir, CASC_LOCALE_ALL_WOW, CONF_Product, CONF_Region));
1419 if (storage)
1420 return CASC_LOCALE_ALL_WOW;
1421
1422 printf("Unable to open remote casc fallback to local casc\n");
1423 }
1424
1425 boost::filesystem::path const storage_dir(boost::filesystem::canonical(input_path) / "Data");
1426 std::unique_ptr<CASC::Storage> storage(CASC::Storage::Open(storage_dir, CASC_LOCALE_ALL_WOW, CONF_Product));
1427 if (!storage)
1428 return false;
1429
1430 return storage->GetInstalledLocalesMask();
1431 }
1432 catch (boost::filesystem::filesystem_error const& error)
1433 {
1434 printf("Unable to determine installed locales mask: %s\n", error.what());
1435 }
1436
1437 return 0;
1438}
1439
1440static bool RetardCheck()
1441{
1443 return true;
1444
1445 try
1446 {
1447 boost::filesystem::path storageDir(boost::filesystem::canonical(input_path) / "Data");
1448 boost::filesystem::directory_iterator end;
1449 for (boost::filesystem::directory_iterator itr(storageDir); itr != end; ++itr)
1450 {
1451 if (itr->path().extension() == ".MPQ")
1452 {
1453 printf("MPQ files found in Data directory!\n");
1454 printf("This tool works only with World of Warcraft: Battle for Azeroth\n");
1455 printf("\n");
1456 printf("To extract maps for Wrath of the Lich King, rebuild tools using 3.3.5 branch!\n");
1457 printf("\n");
1458 printf("Press ENTER to exit...\n");
1459 getchar();
1460 return false;
1461 }
1462 }
1463 }
1464 catch (std::exception const& error)
1465 {
1466 printf("Error checking client version: %s\n", error.what());
1467 }
1468
1469 return true;
1470}
1471
1472int main(int argc, char * arg[])
1473{
1475
1477
1478 Trinity::Banner::Show("Map & DBC Extractor", [](char const* text) { printf("%s\n", text); }, nullptr);
1479
1480 PrintProgress = isatty(fileno(stdout));
1481 input_path = boost::filesystem::current_path();
1482 output_path = boost::filesystem::current_path();
1483
1484 HandleArgs(argc, arg);
1485
1486 if (!RetardCheck())
1487 return 1;
1488
1489 uint32 installedLocalesMask = GetInstalledLocalesMask();
1490 int32 firstInstalledLocale = -1;
1491 uint32 build = 0;
1492
1493 for (int i = 0; i < TOTAL_LOCALES; ++i)
1494 {
1495 if (CONF_Locale && !(CONF_Locale & (1 << i)))
1496 continue;
1497
1498 if (i == LOCALE_none)
1499 continue;
1500
1501 if (!(installedLocalesMask & WowLocaleToCascLocaleFlags[i]))
1502 continue;
1503
1504 if (!OpenCascStorage(i))
1505 continue;
1506
1507 if ((CONF_extract & EXTRACT_DBC) == 0)
1508 {
1509 firstInstalledLocale = i;
1510 build = CascStorage->GetBuildNumber();
1511 if (!build)
1512 {
1513 CascStorage.reset();
1514 continue;
1515 }
1516
1517 printf("Detected client build: %u\n\n", build);
1518 break;
1519 }
1520
1521 //Extract DBC files
1522 uint32 tempBuild = CascStorage->GetBuildNumber();
1523 if (!tempBuild)
1524 {
1525 CascStorage.reset();
1526 continue;
1527 }
1528
1529 printf("Detected client build %u for locale %s\n\n", tempBuild, localeNames[i]);
1531 CascStorage.reset();
1532
1533 if (firstInstalledLocale < 0)
1534 {
1535 firstInstalledLocale = i;
1536 build = tempBuild;
1537 }
1538 }
1539
1540 if (firstInstalledLocale < 0)
1541 {
1542 printf("No locales detected\n");
1543 return 0;
1544 }
1545
1547 {
1548 OpenCascStorage(firstInstalledLocale);
1550 CascStorage.reset();
1551 }
1552
1554 {
1555 OpenCascStorage(firstInstalledLocale);
1557 CascStorage.reset();
1558 }
1559
1561 {
1562 OpenCascStorage(firstInstalledLocale);
1563 ExtractMaps(build);
1564 CascStorage.reset();
1565 }
1566
1567 return 0;
1568}
char const * localeNames[TOTAL_LOCALES]
Definition: Common.cpp:20
@ LOCALE_none
Definition: Common.h:58
@ TOTAL_LOCALES
Definition: Common.h:62
constinit uint64 DUMMY_KNOWN_TACT_ID
Definition: DB2FileLoader.h:71
DB2FileInfo const DBFilesClientList[]
uint8_t uint8
Definition: Define.h:144
int64_t int64
Definition: Define.h:137
int16_t int16
Definition: Define.h:139
int8_t int8
Definition: Define.h:140
int32_t int32
Definition: Define.h:138
uint64_t uint64
Definition: Define.h:141
uint16_t uint16
Definition: Define.h:143
uint32_t uint32
Definition: Define.h:142
#define SZFMTD
Definition: Define.h:132
uint32 const MapVersionMagic
Definition: MapDefines.cpp:21
u_map_magic const MapAreaMagic
Definition: MapDefines.cpp:22
u_map_magic const MapLiquidMagic
Definition: MapDefines.cpp:24
u_map_magic const MapHeightMagic
Definition: MapDefines.cpp:23
u_map_magic const MapMagic
Definition: MapDefines.cpp:20
map_liquidHeaderTypeFlags
Definition: MapDefines.h:97
if(posix_memalign(&__mallocedMemory, __align, __size)) return NULL
bool CONF_UseRemoteCasc
Definition: System.cpp:115
float selectUInt8StepStore(float maxDiff)
Definition: System.cpp:416
float CONF_float_to_int8_limit
Definition: System.cpp:106
char const * CONF_Product
Definition: System.cpp:113
uint16 uint16_V8[ADT_GRID_SIZE][ADT_GRID_SIZE]
Definition: System.cpp:430
uint32 GetInstalledLocalesMask()
Definition: System.cpp:1411
bool CONF_allow_height_limit
Definition: System.cpp:101
bool PrintProgress
Definition: System.cpp:80
static bool RetardCheck()
Definition: System.cpp:1440
void ReadMapDBC()
Definition: System.cpp:272
void HandleArgs(int argc, char *arg[])
Definition: System.cpp:176
boost::filesystem::path output_path
Definition: System.cpp:82
uint8 holes[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID][8]
Definition: System.cpp:439
bool CONF_allow_float_to_int
Definition: System.cpp:105
void ReadLiquidObjectTable()
Definition: System.cpp:341
std::shared_ptr< CASC::Storage > CascStorage
Definition: System.cpp:49
void CreateDir(boost::filesystem::path const &path)
Definition: System.cpp:148
char const * CascLocaleNames[CASC_LOCALES_COUNT]
Definition: System.cpp:119
std::unordered_map< uint32, LiquidTypeEntry > LiquidTypes
Definition: System.cpp:78
map_liquidHeaderTypeFlags liquid_flags[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID]
Definition: System.cpp:436
std::unordered_map< uint32, LiquidObjectEntry > LiquidObjects
Definition: System.cpp:77
uint16 uint16_V9[ADT_GRID_SIZE+1][ADT_GRID_SIZE+1]
Definition: System.cpp:431
void ExtractGameTables()
Definition: System.cpp:1327
bool ExtractFile(CASC::File *fileInArchive, std::string const &filename)
Definition: System.cpp:1136
uint32 WowLocaleToCascLocaleFlags[12]
Definition: System.cpp:132
void ExtractDBFilesClient(int l)
Definition: System.cpp:1269
Extract
Definition: System.cpp:88
@ EXTRACT_CAMERA
Definition: System.cpp:91
@ EXTRACT_MAP
Definition: System.cpp:89
@ EXTRACT_DBC
Definition: System.cpp:90
@ EXTRACT_GT
Definition: System.cpp:92
@ EXTRACT_ALL
Definition: System.cpp:94
bool TransformToHighRes(uint16 lowResHoles, uint8 hiResHoles[8])
Definition: System.cpp:463
float CONF_flat_liquid_delta_limit
Definition: System.cpp:109
float CONF_use_minHeight
Definition: System.cpp:102
uint32 CONF_Locale
Definition: System.cpp:111
boost::filesystem::path input_path
Definition: System.cpp:81
void ReadLiquidMaterialTable()
Definition: System.cpp:317
uint16 liquid_entry[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID]
Definition: System.cpp:435
bool ReadCinematicCameraDBC()
Definition: System.cpp:390
void ExtractCameraFiles()
Definition: System.cpp:1294
int16 flight_box_min[3][3]
Definition: System.cpp:442
float selectUInt16StepStore(float maxDiff)
Definition: System.cpp:421
uint16 area_ids[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID]
Definition: System.cpp:426
char const * CONF_Region
Definition: System.cpp:114
std::unordered_map< uint32, LiquidMaterialEntry > LiquidMaterials
Definition: System.cpp:76
int16 flight_box_max[3][3]
Definition: System.cpp:441
float liquid_height[ADT_GRID_SIZE+1][ADT_GRID_SIZE+1]
Definition: System.cpp:438
char const * GetCascFilenamePart(char const *cascPath)
Definition: System.cpp:1261
bool OpenCascStorage(int locale)
Definition: System.cpp:1380
std::set< uint32 > CameraFileDataIds
Definition: System.cpp:79
bool IsDeepWaterIgnored(uint32 mapId, uint32 x, uint32 y)
Definition: System.cpp:1044
void ExtractMaps(uint32 build)
Definition: System.cpp:1071
float V8[ADT_GRID_SIZE][ADT_GRID_SIZE]
Definition: System.cpp:428
std::vector< MapEntry > map_ids
Definition: System.cpp:75
int CONF_extract
Definition: System.cpp:98
uint8 uint8_V9[ADT_GRID_SIZE+1][ADT_GRID_SIZE+1]
Definition: System.cpp:433
int main(int argc, char *arg[])
Definition: System.cpp:1472
void TryLoadDB2(char const *name, DB2CascFileSource *source, DB2FileLoader *db2, DB2FileLoadInfo const *loadInfo)
Definition: System.cpp:259
bool ExtractDB2File(uint32 fileDataId, char const *cascFileName, int locale, boost::filesystem::path const &outputPath)
Definition: System.cpp:1180
void ReadLiquidTypeTable()
Definition: System.cpp:365
#define CASC_LOCALES_COUNT
Definition: System.cpp:117
void Usage(char const *prg)
Definition: System.cpp:159
float V9[ADT_GRID_SIZE+1][ADT_GRID_SIZE+1]
Definition: System.cpp:429
bool ConvertADT(ChunkedFile &adt, std::string const &mapName, std::string const &outputPath, int gx, int gy, uint32 build, bool ignoreDeepWater)
Definition: System.cpp:478
float CONF_float_to_int16_limit
Definition: System.cpp:107
uint8 uint8_V8[ADT_GRID_SIZE][ADT_GRID_SIZE]
Definition: System.cpp:432
bool liquid_show[ADT_GRID_SIZE][ADT_GRID_SIZE]
Definition: System.cpp:437
float CONF_flat_height_delta_limit
Definition: System.cpp:108
#define ADT_CELL_SIZE
Definition: adt.h:39
LiquidVertexFormatType
Definition: adt.h:139
@ LIQUID_TYPE_WATER
Definition: adt.h:29
@ LIQUID_TYPE_SLIME
Definition: adt.h:32
@ LIQUID_TYPE_MAGMA
Definition: adt.h:31
@ LIQUID_TYPE_OCEAN
Definition: adt.h:30
#define ADT_GRID_SIZE
Definition: adt.h:40
#define ADT_CELLS_PER_GRID
Definition: adt.h:38
int64 GetSize() const
bool ReadFile(void *buffer, uint32 bytes, uint32 *bytesRead)
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)
FileChunk * GetChunk(std::string const &name)
Definition: loadlib.cpp:168
std::multimap< std::string, FileChunk * > chunks
Definition: loadlib.h:90
bool loadFile(std::shared_ptr< CASC::Storage const > mpq, std::string const &fileName, bool log=true)
Definition: loadlib.cpp:36
void Load(DB2FileSource *source, DB2FileLoadInfo const *loadInfo)
DB2Record GetRecord(uint32 recordNumber) const
DB2RecordCopy GetRecordCopy(uint32 copyNumber) const
DB2Header const & GetHeader() const
void LoadHeaders(DB2FileSource *source, DB2FileLoadInfo const *loadInfo)
DB2SectionHeader const & GetSectionHeader(uint32 section) 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
uint32 GetId() const
char const * GetString(uint32 field, uint32 arrayIndex) const
constexpr bool HasFlag(T flag) const
Definition: EnumFlag.h:106
T * As()
Definition: loadlib.h:67
FileChunk * GetSubChunk(std::string const &name)
Definition: loadlib.cpp:212
Definition: wdt.h:67
uint32 rootADT
Definition: wdt.h:78
struct wdt_MAID::@374 adt_files[64][64]
Definition: wdt.h:50
struct wdt_MAIN::adtData adt_list[64][64]
Definition: wdt.h:30
uint32 flags
Definition: wdt.h:39
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
auto MapEqualRange(M &map, typename M::key_type const &key)
Definition: IteratorPair.h:60
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
static constexpr DB2LoadInfo Instance
Definition: DB2LoadInfo.h:1236
bool IsOpen() const override
int64 GetFileSize() const override
bool SetPosition(int64 position) override
CASC::File * GetNativeHandle() const
uint32 SectionCount
Definition: DB2FileLoader.h:53
uint32 SourceRowId
static constexpr DB2FileLoadInfo Instance
int16 LiquidTypeID
Definition: System.cpp:66
static constexpr DB2FileLoadInfo Instance
static constexpr DB2LoadInfo Instance
Definition: DB2LoadInfo.h:3585
uint32 Id
Definition: System.cpp:53
std::string Directory
Definition: System.cpp:56
int32 WdtFileDataId
Definition: System.cpp:54
std::string Name
Definition: System.cpp:55
static constexpr DB2LoadInfo Instance
Definition: DB2LoadInfo.h:3676
Definition: adt.h:61
struct adt_MCLQ::liquid_data liquid[ADT_CELL_SIZE+1][ADT_CELL_SIZE+1]
uint8 flags[ADT_CELL_SIZE][ADT_CELL_SIZE]
Definition: adt.h:80
Definition: adt.h:88
uint8 HighResHoles[8]
Definition: adt.h:106
uint32 sizeMCLQ
Definition: adt.h:126
uint32 holes
Definition: adt.h:116
uint32 ix
Definition: adt.h:95
uint32 iy
Definition: adt.h:96
union adt_MCNK::@360 union_5_3_0
uint32 flags
Definition: adt.h:94
uint32 areaid
Definition: adt.h:114
float ypos
Definition: adt.h:129
Definition: adt.h:48
float height_map[(ADT_CELL_SIZE+1) *(ADT_CELL_SIZE+1)+ADT_CELL_SIZE *ADT_CELL_SIZE]
Definition: adt.h:54
Definition: adt.h:300
plane min
Definition: adt.h:312
plane max
Definition: adt.h:311
Definition: adt.h:177
adt_liquid_attributes GetLiquidAttributes(int32 x, int32 y) const
Definition: adt.h:197
adt_liquid_instance const * GetLiquidInstance(int32 x, int32 y) const
Definition: adt.h:190
float GetLiquidHeight(adt_liquid_instance const *h, int32 pos) const
Definition: adt.h:216
LiquidVertexFormatType GetLiquidVertexFormat(adt_liquid_instance const *liquidInstance) const
Definition: System.cpp:444
uint64 GetLiquidExistsBitmap(adt_liquid_instance const *h) const
Definition: adt.h:288
uint16 GetLiquidType(adt_liquid_instance const *h) const
Definition: adt.h:208
uint8 GetWidth() const
Definition: adt.h:163
uint8 GetOffsetX() const
Definition: adt.h:161
uint16 LiquidType
Definition: adt.h:150
uint8 GetHeight() const
Definition: adt.h:164
uint8 GetOffsetY() const
Definition: adt.h:162
uint16 LiquidVertexFormat
Definition: adt.h:151
EnumFlag< map_areaHeaderFlags > flags
Definition: MapDefines.h:64
uint16 gridArea
Definition: MapDefines.h:65
u_map_magic areaMagic
Definition: MapDefines.h:63
u_map_magic mapMagic
Definition: MapDefines.h:40
float gridMaxHeight
Definition: MapDefines.h:84
EnumFlag< map_heightHeaderFlags > flags
Definition: MapDefines.h:82
u_map_magic heightMagic
Definition: MapDefines.h:81
u_map_magic liquidMagic
Definition: MapDefines.h:113
EnumFlag< map_liquidHeaderFlags > flags
Definition: MapDefines.h:114
EnumFlag< map_liquidHeaderTypeFlags > liquidFlags
Definition: MapDefines.h:115
uint32 flag
Definition: wdt.h:61
#define WDT_MAP_SIZE
Definition: wdt.h:25