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