33#include <boost/filesystem/path.hpp>
34#include <boost/filesystem/operations.hpp>
40#include <unordered_map>
43#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
117#define CASC_LOCALES_COUNT 17
134 CASC_LOCALE_ENUS | CASC_LOCALE_ENGB,
144 CASC_LOCALE_PTBR | CASC_LOCALE_PTPT,
151 if (fs::exists(path))
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());
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"\
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);
178 for (
int c = 1; c < argc; ++c)
188 if (arg[c][0] !=
'-')
194 if (c + 1 < argc && strlen(arg[c + 1]))
195 input_path = boost::filesystem::path(arg[c++ + 1]);
200 if (c + 1 < argc && strlen(arg[c + 1]))
201 output_path = boost::filesystem::path(arg[c++ + 1]);
233 if (c + 1 < argc && strlen(arg[c + 1]))
245 if (c + 1 < argc && strlen(arg[c + 1]))
263 db2->
Load(source, loadInfo);
265 catch (std::exception
const& e)
274 printf(
"Read Map.db2 file...\n");
281 std::unordered_map<uint32, std::size_t> idToIndex;
301 if (itr != idToIndex.end())
319 printf(
"Read LiquidMaterial.db2 file...\n");
343 printf(
"Read LiquidObject.db2 file...\n");
367 printf(
"Read LiquidType.db2 file...\n");
392 printf(
"Read CinematicCamera.db2 file...\n");
418 return 255 / maxDiff;
423 return 65535 / maxDiff;
455 auto liquidMaterial =
LiquidMaterials.find(liquidType->second.MaterialID);
465 for (
uint8 i = 0; i < 8; i++)
467 for (
uint8 j = 0; j < 8; j++)
469 int32 holeIdxL = (i / 2) * 4 + (j / 2);
470 if (((lowResHoles >> holeIdxL) & 1) == 1)
471 hiResHoles[i] |= (1 << j);
475 return *((
uint64*)hiResHoles) != 0;
484 map.buildMagic = build;
488 memset(
V9, 0,
sizeof(
V9));
489 memset(
V8, 0,
sizeof(
V8));
497 bool hasHoles =
false;
498 bool hasFlightBox =
false;
586 if (liquid->
flags[y][x] != 0x0F)
589 if (!ignoreDeepWater && liquid->
flags[y][x] & (1 << 7))
597 if (c_flag & (1 << 2))
602 if (c_flag & (1 << 3))
607 if (c_flag & (1 << 4))
614 fprintf(stderr,
"Wrong liquid detect in MCLQ chunk");
629 if (!(mcnk->
flags & 0x10000))
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);
687 printf(
"Wrong liquid detect in MH2O chunk");
715 bool fullAreaData =
false;
729 map.areaMapOffset =
sizeof(map);
738 map.areaMapSize +=
sizeof(
area_ids);
749 float maxHeight = -20000;
750 float minHeight = 20000;
756 if (maxHeight < h) maxHeight = h;
757 if (minHeight > h) minHeight = h;
765 if (maxHeight < h) maxHeight = h;
766 if (minHeight > h) minHeight = h;
787 map.heightMapOffset = map.areaMapOffset + map.areaMapSize;
796 if (maxHeight == minHeight)
816 float diff = maxHeight - minHeight;
851 map.heightMapSize+=
sizeof(
V9) +
sizeof(
V8);
859 bool fullType =
false;
879 map.liquidMapOffset = 0;
880 map.liquidMapSize = 0;
884 int minX = 255, minY = 255;
885 int maxX = 0, maxY = 0;
894 if (minX > x) minX = x;
895 if (maxX < x) maxX = x;
896 if (minY > y) minY = y;
897 if (maxY < y) maxY = y;
899 if (maxHeight < h) maxHeight = h;
900 if (minHeight > h) minHeight = h;
909 map.liquidMapOffset = map.heightMapOffset + map.heightMapSize;
916 liquidHeader.
width = maxX - minX + 1 + 1;
917 liquidHeader.
height = maxY - minY + 1 + 1;
920 if (maxHeight == minHeight)
939 map.liquidMapSize +=
sizeof(
float)*liquidHeader.
width*liquidHeader.
height;
944 if (map.liquidMapOffset)
945 map.holesOffset = map.liquidMapOffset + map.liquidMapSize;
947 map.holesOffset = map.heightMapOffset + map.heightMapSize;
949 map.holesSize =
sizeof(
holes);
958 std::ofstream outFile(outputPath, std::ofstream::out | std::ofstream::binary);
961 printf(
"Can't create the output file '%s'\n", outputPath.c_str());
965 outFile.write(
reinterpret_cast<char const*
>(&map),
sizeof(map));
967 outFile.write(
reinterpret_cast<char const*
>(&areaHeader),
sizeof(areaHeader));
972 outFile.write(
reinterpret_cast<char const*
>(&heightHeader),
sizeof(heightHeader));
987 outFile.write(
reinterpret_cast<char const*
>(
V9),
sizeof(
V9));
988 outFile.write(
reinterpret_cast<char const*
>(
V8),
sizeof(
V8));
999 if (map.liquidMapOffset)
1001 outFile.write(
reinterpret_cast<char const*
>(&liquidHeader),
sizeof(liquidHeader));
1010 for (
int y = 0; y < liquidHeader.
height; y++)
1017 outFile.write(
reinterpret_cast<char const*
>(
holes), map.holesSize);
1024bool ConvertADT(std::string
const& fileName, std::string
const& mapName, std::string
const& outputPath,
int gx,
int gy,
uint32 build,
bool ignoreDeepWater)
1031 return ConvertADT(adt, mapName, outputPath, gx, gy, build, ignoreDeepWater);
1034bool ConvertADT(
uint32 fileDataId, std::string
const& mapName, std::string
const& outputPath,
int gx,
int gy,
uint32 build,
bool ignoreDeepWater)
1041 return ConvertADT(adt, mapName, outputPath, gx, gy, build, ignoreDeepWater);
1058 return (x >= 39 && x <= 40 && y >= 24 && y <= 26) || (x >= 41 && x <= 46 && y >= 18 && y <= 26);
1065 return x == 43 && (y == 39 || y == 40);
1073 std::string outputFileName;
1075 printf(
"Extracting maps...\n");
1085 printf(
"Convert map files\n");
1086 for (std::size_t z = 0; z <
map_ids.size(); ++z)
1119 printf(
"Processing........................%d%%\r", (100 * (y + 1)) /
WDT_MAP_SIZE);
1127 fwrite(&build,
sizeof(build), 1, tileList);
1128 fwrite(existingTiles.to_string().c_str(), 1, existingTiles.size(), tileList);
1141 printf(
"Can't read file size of '%s'\n", filename.c_str());
1145 FILE* output = fopen(filename.c_str(),
"wb");
1148 printf(
"Can't create the output file '%s'\n", filename.c_str());
1152 char buffer[0x10000];
1158 if (!fileInArchive->
ReadFile(buffer, std::min<uint32>(fileSize,
sizeof(buffer)), &readBytes))
1160 printf(
"Can't read file '%s'\n", filename.c_str());
1162 boost::filesystem::remove(filename);
1169 fwrite(buffer, 1, readBytes, output);
1170 fileSize -= readBytes;
1180bool ExtractDB2File(
uint32 fileDataId,
char const* cascFileName,
int locale, boost::filesystem::path
const& outputPath)
1192 printf(
"Can't read file size of '%s'\n", cascFileName);
1201 catch (std::exception
const& e)
1203 printf(
"Can't read DB2 headers of '%s': %s\n", cascFileName, e.what());
1207 std::string outputFileName = outputPath.string();
1208 FILE* output = fopen(outputFileName.c_str(),
"wb");
1211 printf(
"Can't create the output file '%s'\n", outputFileName.c_str());
1217 int64 posAfterHeaders = 0;
1218 posAfterHeaders += fwrite(&header, 1,
sizeof(header), output);
1227 posAfterHeaders += fwrite(§ionHeader, 1,
sizeof(sectionHeader), output);
1230 char buffer[0x10000];
1231 uint32 readBatchSize = 0x10000;
1240 printf(
"Can't read file '%s'\n", outputFileName.c_str());
1242 boost::filesystem::remove(outputPath);
1249 fwrite(buffer, 1, readBytes, output);
1250 fileSize -= readBytes;
1251 readBatchSize = 0x10000;
1263 if (
char const* lastSep = strrchr(cascPath,
'\\'))
1271 printf(
"Extracting dbc/db2 files...\n");
1278 printf(
"locale %s output path %s\n",
localeNames[l], localePath.string().c_str());
1283 boost::filesystem::path filePath = localePath / db2.Name;
1285 if (!boost::filesystem::exists(filePath))
1286 if (
ExtractDB2File(db2.FileDataId, db2.Name, l, filePath.string()))
1291 printf(
"Extracted %u files\n\n", count);
1296 printf(
"Extracting camera files...\n");
1301 boost::filesystem::path outputPath =
output_path /
"cameras";
1305 printf(
"output path %s\n", outputPath.string().c_str());
1311 std::unique_ptr<CASC::File> cameraFile(
CascStorage->OpenFile(cameraFileDataId, CASC_LOCALE_NONE));
1314 boost::filesystem::path filePath = outputPath /
Trinity::StringFormat(
"FILE{:08X}.xxx", cameraFileDataId);
1316 if (!boost::filesystem::exists(filePath))
1317 if (
ExtractFile(cameraFile.get(), filePath.string()))
1324 printf(
"Extracted %u camera files\n", count);
1329 printf(
"Extracting game tables...\n");
1331 boost::filesystem::path outputPath =
output_path /
"gt";
1335 printf(
"output path %s\n", outputPath.string().c_str());
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" }
1364 std::unique_ptr<CASC::File> dbcFile(
CascStorage->OpenFile(gt.FileDataId, CASC_LOCALE_NONE));
1367 boost::filesystem::path filePath = outputPath / gt.Name;
1369 if (!boost::filesystem::exists(filePath))
1370 if (
ExtractFile(dbcFile.get(), filePath.string()))
1377 printf(
"Extracted %u files\n\n", count);
1386 boost::filesystem::path
const cache_dir(boost::filesystem::canonical(
input_path) /
"CascCache");
1391 printf(
"Unable to open remote casc fallback to local casc\n");
1394 boost::filesystem::path
const storage_dir(boost::filesystem::canonical(
input_path) /
"Data");
1398 printf(
"error opening casc storage '%s' locale %s\n", storage_dir.string().c_str(),
localeNames[locale]);
1404 catch (boost::filesystem::filesystem_error
const& error)
1406 printf(
"Error opening CASC storage: %s\n", error.what());
1417 boost::filesystem::path
const cache_dir(boost::filesystem::canonical(
input_path) /
"CascCache");
1420 return CASC_LOCALE_ALL_WOW;
1422 printf(
"Unable to open remote casc fallback to local casc\n");
1425 boost::filesystem::path
const storage_dir(boost::filesystem::canonical(
input_path) /
"Data");
1430 return storage->GetInstalledLocalesMask();
1432 catch (boost::filesystem::filesystem_error
const& error)
1434 printf(
"Unable to determine installed locales mask: %s\n", error.what());
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)
1451 if (itr->path().extension() ==
".MPQ")
1453 printf(
"MPQ files found in Data directory!\n");
1454 printf(
"This tool works only with World of Warcraft: Battle for Azeroth\n");
1456 printf(
"To extract maps for Wrath of the Lich King, rebuild tools using 3.3.5 branch!\n");
1458 printf(
"Press ENTER to exit...\n");
1464 catch (std::exception
const& error)
1466 printf(
"Error checking client version: %s\n", error.what());
1478 Trinity::Banner::Show(
"Map & DBC Extractor", [](
char const* text) { printf(
"%s\n", text); },
nullptr);
1481 input_path = boost::filesystem::current_path();
1490 int32 firstInstalledLocale = -1;
1509 firstInstalledLocale = i;
1517 printf(
"Detected client build: %u\n\n", build);
1529 printf(
"Detected client build %u for locale %s\n\n", tempBuild,
localeNames[i]);
1533 if (firstInstalledLocale < 0)
1535 firstInstalledLocale = i;
1540 if (firstInstalledLocale < 0)
1542 printf(
"No locales detected\n");
char const * localeNames[TOTAL_LOCALES]
constinit uint64 DUMMY_KNOWN_TACT_ID
DB2FileInfo const DBFilesClientList[]
uint32 const MapVersionMagic
u_map_magic const MapAreaMagic
u_map_magic const MapLiquidMagic
u_map_magic const MapHeightMagic
u_map_magic const MapMagic
map_liquidHeaderTypeFlags
if(posix_memalign(&__mallocedMemory, __align, __size)) return NULL
float selectUInt8StepStore(float maxDiff)
float CONF_float_to_int8_limit
char const * CONF_Product
uint16 uint16_V8[ADT_GRID_SIZE][ADT_GRID_SIZE]
uint32 GetInstalledLocalesMask()
bool CONF_allow_height_limit
static bool RetardCheck()
void HandleArgs(int argc, char *arg[])
boost::filesystem::path output_path
uint8 holes[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID][8]
bool CONF_allow_float_to_int
void ReadLiquidObjectTable()
std::shared_ptr< CASC::Storage > CascStorage
void CreateDir(boost::filesystem::path const &path)
char const * CascLocaleNames[CASC_LOCALES_COUNT]
std::unordered_map< uint32, LiquidTypeEntry > LiquidTypes
map_liquidHeaderTypeFlags liquid_flags[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID]
std::unordered_map< uint32, LiquidObjectEntry > LiquidObjects
uint16 uint16_V9[ADT_GRID_SIZE+1][ADT_GRID_SIZE+1]
bool ExtractFile(CASC::File *fileInArchive, std::string const &filename)
uint32 WowLocaleToCascLocaleFlags[12]
void ExtractDBFilesClient(int l)
bool TransformToHighRes(uint16 lowResHoles, uint8 hiResHoles[8])
float CONF_flat_liquid_delta_limit
boost::filesystem::path input_path
void ReadLiquidMaterialTable()
uint16 liquid_entry[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID]
bool ReadCinematicCameraDBC()
void ExtractCameraFiles()
int16 flight_box_min[3][3]
float selectUInt16StepStore(float maxDiff)
uint16 area_ids[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID]
std::unordered_map< uint32, LiquidMaterialEntry > LiquidMaterials
int16 flight_box_max[3][3]
float liquid_height[ADT_GRID_SIZE+1][ADT_GRID_SIZE+1]
char const * GetCascFilenamePart(char const *cascPath)
bool OpenCascStorage(int locale)
std::set< uint32 > CameraFileDataIds
bool IsDeepWaterIgnored(uint32 mapId, uint32 x, uint32 y)
void ExtractMaps(uint32 build)
float V8[ADT_GRID_SIZE][ADT_GRID_SIZE]
std::vector< MapEntry > map_ids
uint8 uint8_V9[ADT_GRID_SIZE+1][ADT_GRID_SIZE+1]
int main(int argc, char *arg[])
void TryLoadDB2(char const *name, DB2CascFileSource *source, DB2FileLoader *db2, DB2FileLoadInfo const *loadInfo)
bool ExtractDB2File(uint32 fileDataId, char const *cascFileName, int locale, boost::filesystem::path const &outputPath)
void ReadLiquidTypeTable()
#define CASC_LOCALES_COUNT
void Usage(char const *prg)
float V9[ADT_GRID_SIZE+1][ADT_GRID_SIZE+1]
bool ConvertADT(ChunkedFile &adt, std::string const &mapName, std::string const &outputPath, int gx, int gy, uint32 build, bool ignoreDeepWater)
float CONF_float_to_int16_limit
uint8 uint8_V8[ADT_GRID_SIZE][ADT_GRID_SIZE]
bool liquid_show[ADT_GRID_SIZE][ADT_GRID_SIZE]
float CONF_flat_height_delta_limit
#define ADT_CELLS_PER_GRID
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)
std::multimap< std::string, FileChunk * > chunks
bool loadFile(std::shared_ptr< CASC::Storage const > mpq, std::string const &fileName, bool log=true)
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
char const * GetString(uint32 field, uint32 arrayIndex) const
constexpr bool HasFlag(T flag) const
FileChunk * GetSubChunk(std::string const &name)
struct wdt_MAID::@374 adt_files[64][64]
struct wdt_MAIN::adtData adt_list[64][64]
char const * HumanReadableCASCError(uint32 error)
TC_COMMON_API void Show(char const *applicationName, void(*log)(char const *text), void(*logExtraInfo)())
auto MapEqualRange(M &map, typename M::key_type const &key)
TC_COMMON_API void Init()
TC_COMMON_API void VerifyOsVersion()
std::string StringFormat(FormatString< Args... > fmt, Args &&... args)
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
static constexpr DB2FileLoadInfo Instance
static constexpr DB2FileLoadInfo Instance
static constexpr DB2LoadInfo Instance
static constexpr DB2LoadInfo Instance
struct adt_MCLQ::liquid_data liquid[ADT_CELL_SIZE+1][ADT_CELL_SIZE+1]
uint8 flags[ADT_CELL_SIZE][ADT_CELL_SIZE]
union adt_MCNK::@360 union_5_3_0
float height_map[(ADT_CELL_SIZE+1) *(ADT_CELL_SIZE+1)+ADT_CELL_SIZE *ADT_CELL_SIZE]
adt_liquid_attributes GetLiquidAttributes(int32 x, int32 y) const
adt_liquid_instance const * GetLiquidInstance(int32 x, int32 y) const
float GetLiquidHeight(adt_liquid_instance const *h, int32 pos) const
LiquidVertexFormatType GetLiquidVertexFormat(adt_liquid_instance const *liquidInstance) const
uint64 GetLiquidExistsBitmap(adt_liquid_instance const *h) const
uint16 GetLiquidType(adt_liquid_instance const *h) const
uint16 LiquidVertexFormat