mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-15 23:20:36 +01:00
Tools: Extractor updates
* VMAP extractor does not work due to a bug in CascLib
This commit is contained in:
@@ -58,7 +58,7 @@
|
||||
#define BLTE_HEADER_DELTA 0x1E // Distance of BLTE header from begin of the header area
|
||||
#define MAX_HEADER_AREA_SIZE 0x2A // Length of the file header area
|
||||
|
||||
// File header area in the data.xxx:
|
||||
// File header area in the data.nnn:
|
||||
// BYTE HeaderHash[MD5_HASH_SIZE]; // MD5 of the frame array
|
||||
// DWORD dwFileSize; // Size of the file (see comment before CascGetFileSize for details)
|
||||
// BYTE SomeSize[4]; // Some size (big endian)
|
||||
@@ -205,6 +205,7 @@ typedef struct _TCascStorage
|
||||
QUERY_KEY ArchivesGroup; // Key array of the "archive-group"
|
||||
QUERY_KEY ArchivesKey; // Key array of the "archives"
|
||||
QUERY_KEY PatchArchivesKey; // Key array of the "patch-archives"
|
||||
QUERY_KEY PatchArchivesGroup; // Key array of the "patch-archive-group"
|
||||
QUERY_KEY RootKey;
|
||||
QUERY_KEY PatchKey;
|
||||
QUERY_KEY DownloadKey;
|
||||
|
||||
@@ -255,7 +255,7 @@ int CascDecrypt(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWOR
|
||||
// Check if we know the key
|
||||
pbKey = FindCascKey(KeyName);
|
||||
if(pbKey == NULL)
|
||||
return ERROR_UNKNOWN_FILE_KEY;
|
||||
return ERROR_FILE_ENCRYPTED;
|
||||
|
||||
// Shuffle the Vector with the block index
|
||||
// Note that there's no point to go beyond 32 bits, unless the file has
|
||||
|
||||
@@ -696,7 +696,7 @@ static int LoadCdnConfigFile(TCascStorage * hs, void * pvListFile)
|
||||
szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "patch-archive-group");
|
||||
if(szVarBegin != NULL)
|
||||
{
|
||||
LoadSingleBlob(&hs->PatchArchivesKey, szVarBegin, szLineEnd);
|
||||
LoadSingleBlob(&hs->PatchArchivesGroup, szVarBegin, szLineEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,35 +32,12 @@ extern "C" {
|
||||
#define CASCLIB_VERSION 0x0100 // Current version of CascLib (1.0)
|
||||
#define CASCLIB_VERSION_STRING "1.00" // String version of CascLib version
|
||||
|
||||
#define ERROR_UNKNOWN_FILE_KEY 10001 // Returned by encrypted stream when can't find file key
|
||||
#define ERROR_FILE_INCOMPLETE 10006 // The required file part is missing
|
||||
|
||||
// Values for CascOpenStorage
|
||||
#define CASC_STOR_XXXXX 0x00000001 // Not used
|
||||
|
||||
// Values for CascOpenFile
|
||||
#define CASC_OPEN_BY_ENCODING_KEY 0x00000001 // The name is just the encoding key; skip ROOT file processing
|
||||
|
||||
// Flags for file stream
|
||||
#define BASE_PROVIDER_FILE 0x00000000 // Base data source is a file
|
||||
#define BASE_PROVIDER_MAP 0x00000001 // Base data source is memory-mapped file
|
||||
#define BASE_PROVIDER_HTTP 0x00000002 // Base data source is a file on web server
|
||||
#define BASE_PROVIDER_MASK 0x0000000F // Mask for base provider value
|
||||
|
||||
#define STREAM_PROVIDER_FLAT 0x00000000 // Stream is linear with no offset mapping
|
||||
#define STREAM_PROVIDER_PARTIAL 0x00000010 // Stream is partial file (.part)
|
||||
#define STREAM_PROVIDER_ENCRYPTED 0x00000020 // Stream is an encrypted archive
|
||||
#define STREAM_PROVIDER_BLOCK4 0x00000030 // 0x4000 per block, text MD5 after each block, max 0x2000 blocks per file
|
||||
#define STREAM_PROVIDER_MASK 0x000000F0 // Mask for stream provider value
|
||||
|
||||
#define STREAM_FLAG_READ_ONLY 0x00000100 // Stream is read only
|
||||
#define STREAM_FLAG_WRITE_SHARE 0x00000200 // Allow write sharing when open for write
|
||||
#define STREAM_FLAG_USE_BITMAP 0x00000400 // If the file has a file bitmap, load it and use it
|
||||
#define STREAM_OPTIONS_MASK 0x0000FF00 // Mask for stream options
|
||||
|
||||
#define STREAM_PROVIDERS_MASK 0x000000FF // Mask to get stream providers
|
||||
#define STREAM_FLAGS_MASK 0x0000FFFF // Mask for all stream flags (providers+options)
|
||||
|
||||
#define CASC_LOCALE_ALL 0xFFFFFFFF
|
||||
#define CASC_LOCALE_NONE 0x00000000
|
||||
#define CASC_LOCALE_UNKNOWN1 0x00000001
|
||||
|
||||
@@ -507,7 +507,7 @@ static int LoadKeyMapping(PCASC_MAPPING_TABLE pKeyMapping, DWORD KeyIndex)
|
||||
{
|
||||
// Retrieve the file size
|
||||
FileStream_GetSize(pStream, &FileSize);
|
||||
if(0 < FileSize && FileSize <= 0x100000)
|
||||
if(0 < FileSize && FileSize <= 0x200000)
|
||||
{
|
||||
// WoW6 actually reads THE ENTIRE file to memory
|
||||
// Verified on Mac build (x64)
|
||||
@@ -991,6 +991,7 @@ static TCascStorage * FreeCascStorage(TCascStorage * hs)
|
||||
FreeCascBlob(&hs->ArchivesGroup);
|
||||
FreeCascBlob(&hs->ArchivesKey);
|
||||
FreeCascBlob(&hs->PatchArchivesKey);
|
||||
FreeCascBlob(&hs->PatchArchivesGroup);
|
||||
FreeCascBlob(&hs->RootKey);
|
||||
FreeCascBlob(&hs->PatchKey);
|
||||
FreeCascBlob(&hs->DownloadKey);
|
||||
|
||||
@@ -216,6 +216,11 @@
|
||||
#define ERROR_HANDLE_EOF 1002 // No such error code under Linux
|
||||
#define ERROR_CAN_NOT_COMPLETE 1003 // No such error code under Linux
|
||||
#define ERROR_FILE_CORRUPT 1004 // No such error code under Linux
|
||||
#define ERROR_FILE_ENCRYPTED 1005 // Returned by encrypted stream when can't find file key
|
||||
#endif
|
||||
|
||||
#ifndef ERROR_FILE_INCOMPLETE
|
||||
#define ERROR_FILE_INCOMPLETE 1006 // The required file part is missing
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@@ -231,7 +231,7 @@ static const DIABLO3_ASSET_INFO Assets[] =
|
||||
{"Accolade", "aco"}, // 0x42
|
||||
};
|
||||
|
||||
static const DIABLO3_ASSET_INFO UnknownAsset = {"Unknown", "xxx"};
|
||||
static const DIABLO3_ASSET_INFO UnknownAsset = {"Unknown", "unk"};
|
||||
|
||||
#define DIABLO3_ASSET_COUNT (sizeof(Assets) / sizeof(Assets[0]))
|
||||
|
||||
@@ -327,7 +327,7 @@ static size_t CreateShortName(
|
||||
}
|
||||
}
|
||||
|
||||
// If we havent't found the package, we either use the default asset extension or "xxx"
|
||||
// If we havent't found the package, we either use the default asset extension or "unk"
|
||||
if(szPackageName == NULL)
|
||||
{
|
||||
if(dwSubIndex == DIABLO3_INVALID_INDEX)
|
||||
@@ -337,7 +337,7 @@ static size_t CreateShortName(
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(szBuffer + nLength, "xxx");
|
||||
strcpy(szBuffer + nLength, "unk");
|
||||
nLength += 3;
|
||||
}
|
||||
}
|
||||
@@ -887,7 +887,7 @@ static int ParseCoreTOC(
|
||||
// Find out the entry with the maximum index
|
||||
for(DWORD n = 0; n < pTocHeader->EntryCounts[i]; n++)
|
||||
{
|
||||
if(pTocEntry->FileIndex > dwFileIndexes)
|
||||
if(pTocEntry->FileIndex >= dwFileIndexes)
|
||||
dwFileIndexes = pTocEntry->FileIndex + 1;
|
||||
pTocEntry++;
|
||||
}
|
||||
|
||||
@@ -381,7 +381,7 @@ static LPBYTE WowHandler_GetKey(TRootHandler_WoW6 * pRootHandler, const char * s
|
||||
BYTE FileDataIdLE[4];
|
||||
|
||||
// Open by FileDataId. The file name must be as following:
|
||||
// File########.xxx, where '#' are hexa-decimal numbers (case insensitive).
|
||||
// File########.unk, where '#' are hexa-decimal numbers (case insensitive).
|
||||
// Extension is ignored in that case
|
||||
if(IsFileDataIdName(szFileName))
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*****************************************************************************/
|
||||
/* FileStream.cpp Copyright (c) Ladislav Zezula 2010 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* File stream support for CascLib */
|
||||
/* File stream support */
|
||||
/* */
|
||||
/* Windows support: Written by Ladislav Zezula */
|
||||
/* Mac support: Written by Sam Wilkins */
|
||||
@@ -162,7 +162,7 @@ static bool BaseFile_Read(
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
{
|
||||
// Note: CascLib no longer supports Windows 9x.
|
||||
// Note: We no longer support Windows 9x.
|
||||
// Thus, we can use the OVERLAPPED structure to specify
|
||||
// file offset to read from file. This allows us to skip
|
||||
// one system call to SetFilePointer
|
||||
@@ -233,7 +233,7 @@ static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
{
|
||||
// Note: CascLib no longer supports Windows 9x.
|
||||
// Note: We no longer support Windows 9x.
|
||||
// Thus, we can use the OVERLAPPED structure to specify
|
||||
// file offset to read from file. This allows us to skip
|
||||
// one system call to SetFilePointer
|
||||
@@ -2097,7 +2097,7 @@ static TFileStream * EncrStream_Open(const TCHAR * szFileName, DWORD dwStreamFla
|
||||
|
||||
// Cleanup the stream and return
|
||||
FileStream_Close(pStream);
|
||||
SetLastError(ERROR_UNKNOWN_FILE_KEY);
|
||||
SetLastError(ERROR_FILE_ENCRYPTED);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,28 @@
|
||||
#ifndef __FILESTREAM_H__
|
||||
#define __FILESTREAM_H__
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Flags for file stream
|
||||
|
||||
#define BASE_PROVIDER_FILE 0x00000000 // Base data source is a file
|
||||
#define BASE_PROVIDER_MAP 0x00000001 // Base data source is memory-mapped file
|
||||
#define BASE_PROVIDER_HTTP 0x00000002 // Base data source is a file on web server
|
||||
#define BASE_PROVIDER_MASK 0x0000000F // Mask for base provider value
|
||||
|
||||
#define STREAM_PROVIDER_FLAT 0x00000000 // Stream is linear with no offset mapping
|
||||
#define STREAM_PROVIDER_PARTIAL 0x00000010 // Stream is partial file (.part)
|
||||
#define STREAM_PROVIDER_ENCRYPTED 0x00000020 // Stream is an encrypted archive
|
||||
#define STREAM_PROVIDER_BLOCK4 0x00000030 // 0x4000 per block, text MD5 after each block, max 0x2000 blocks per file
|
||||
#define STREAM_PROVIDER_MASK 0x000000F0 // Mask for stream provider value
|
||||
|
||||
#define STREAM_FLAG_READ_ONLY 0x00000100 // Stream is read only
|
||||
#define STREAM_FLAG_WRITE_SHARE 0x00000200 // Allow write sharing when open for write
|
||||
#define STREAM_FLAG_USE_BITMAP 0x00000400 // If the file has a file bitmap, load it and use it
|
||||
#define STREAM_OPTIONS_MASK 0x0000FF00 // Mask for stream options
|
||||
|
||||
#define STREAM_PROVIDERS_MASK 0x000000FF // Mask to get stream providers
|
||||
#define STREAM_FLAGS_MASK 0x0000FFFF // Mask for all stream flags (providers+options)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Function prototypes
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ recastnavigation (Recast is state of the art navigation mesh construction toolse
|
||||
|
||||
CascLib (An open-source implementation of library for reading CASC storage from Blizzard games since 2014)
|
||||
https://github.com/ladislav-zezula/CascLib
|
||||
Version: d477d30f7c2b3a306fd07c92929950639335fb3f
|
||||
Version: fe97d8cf7fd2deb08db7e8c14990a6420a3b47b4
|
||||
|
||||
rapidjson (A fast JSON parser/generator for C++ with both SAX/DOM style API http://rapidjson.org/)
|
||||
https://github.com/miloyip/rapidjson
|
||||
|
||||
199
src/tools/map_extractor/DB2.cpp
Normal file
199
src/tools/map_extractor/DB2.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "DB2.h"
|
||||
|
||||
DB2FileLoader::DB2FileLoader()
|
||||
{
|
||||
meta = nullptr;
|
||||
|
||||
recordSize = 0;
|
||||
recordCount = 0;
|
||||
fieldCount = 0;
|
||||
stringSize = 0;
|
||||
tableHash = 0;
|
||||
layoutHash = 0;
|
||||
minIndex = 0;
|
||||
maxIndex = 0;
|
||||
localeMask = 0;
|
||||
copyIdSize = 0;
|
||||
|
||||
data = nullptr;
|
||||
stringTable = nullptr;
|
||||
idTable = nullptr;
|
||||
idTableSize = 0;
|
||||
copyTable = nullptr;
|
||||
fields = nullptr;
|
||||
}
|
||||
|
||||
bool DB2FileLoader::Load(HANDLE db2Handle, DB2Meta const* meta)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
delete[] data;
|
||||
data = nullptr;
|
||||
}
|
||||
|
||||
DWORD bytesRead = 0;
|
||||
this->meta = meta;
|
||||
std::uint32_t header;
|
||||
CascReadFile(db2Handle, &header, sizeof(header), &bytesRead);
|
||||
if (bytesRead != sizeof(header)) // Signature
|
||||
return false;
|
||||
|
||||
EndianConvert(header);
|
||||
|
||||
if (header != 0x35424457)
|
||||
return false; //'WDB5'
|
||||
|
||||
CascReadFile(db2Handle, &recordCount, sizeof(recordCount), &bytesRead);
|
||||
if (bytesRead != sizeof(recordCount)) // Number of records
|
||||
return false;
|
||||
|
||||
EndianConvert(recordCount);
|
||||
|
||||
CascReadFile(db2Handle, &fieldCount, sizeof(fieldCount), &bytesRead);
|
||||
if (bytesRead != sizeof(fieldCount)) // Number of fields
|
||||
return false;
|
||||
|
||||
EndianConvert(fieldCount);
|
||||
|
||||
CascReadFile(db2Handle, &recordSize, sizeof(recordSize), &bytesRead);
|
||||
if (bytesRead != sizeof(recordSize)) // Size of a record
|
||||
return false;
|
||||
|
||||
EndianConvert(recordSize);
|
||||
|
||||
CascReadFile(db2Handle, &stringSize, sizeof(stringSize), &bytesRead);
|
||||
if (bytesRead != sizeof(stringSize)) // String size
|
||||
return false;
|
||||
|
||||
EndianConvert(stringSize);
|
||||
|
||||
CascReadFile(db2Handle, &tableHash, sizeof(tableHash), &bytesRead);
|
||||
if (bytesRead != sizeof(tableHash)) // Table hash
|
||||
return false;
|
||||
|
||||
EndianConvert(tableHash);
|
||||
|
||||
CascReadFile(db2Handle, &layoutHash, sizeof(layoutHash), &bytesRead);
|
||||
if (bytesRead != sizeof(layoutHash)) // Layout hash
|
||||
return false;
|
||||
|
||||
if (layoutHash != meta->LayoutHash)
|
||||
return false;
|
||||
|
||||
EndianConvert(layoutHash);
|
||||
|
||||
CascReadFile(db2Handle, &minIndex, sizeof(minIndex), &bytesRead);
|
||||
if (bytesRead != sizeof(minIndex)) // MinIndex WDB2
|
||||
return false;
|
||||
|
||||
EndianConvert(minIndex);
|
||||
|
||||
CascReadFile(db2Handle, &maxIndex, sizeof(maxIndex), &bytesRead);
|
||||
if (bytesRead != sizeof(maxIndex)) // MaxIndex WDB2
|
||||
return false;
|
||||
|
||||
EndianConvert(maxIndex);
|
||||
|
||||
CascReadFile(db2Handle, &localeMask, sizeof(localeMask), &bytesRead);
|
||||
if (bytesRead != sizeof(localeMask)) // Locales
|
||||
return false;
|
||||
|
||||
EndianConvert(localeMask);
|
||||
|
||||
CascReadFile(db2Handle, ©IdSize, sizeof(copyIdSize), &bytesRead);
|
||||
if (bytesRead != sizeof(copyIdSize))
|
||||
return false;
|
||||
|
||||
EndianConvert(copyIdSize);
|
||||
|
||||
CascReadFile(db2Handle, &metaFlags, sizeof(metaFlags), &bytesRead);
|
||||
if (bytesRead != sizeof(metaFlags))
|
||||
return false;
|
||||
|
||||
EndianConvert(metaFlags);
|
||||
|
||||
ASSERT((metaFlags & 0x1) == 0);
|
||||
ASSERT((meta->IndexField == -1) || (meta->IndexField == (metaFlags >> 16)));
|
||||
|
||||
fields = new FieldEntry[fieldCount];
|
||||
CascReadFile(db2Handle, fields, fieldCount * sizeof(FieldEntry), &bytesRead);
|
||||
if (bytesRead != fieldCount * sizeof(FieldEntry))
|
||||
return false;
|
||||
|
||||
if (!meta->HasIndexFieldInData())
|
||||
idTableSize = recordCount * sizeof(std::uint32_t);
|
||||
|
||||
data = new unsigned char[recordSize * recordCount + stringSize];
|
||||
stringTable = data + recordSize * recordCount;
|
||||
|
||||
CascReadFile(db2Handle, data, recordSize * recordCount + stringSize, &bytesRead);
|
||||
if (bytesRead != recordSize * recordCount + stringSize)
|
||||
return false;
|
||||
|
||||
if (idTableSize)
|
||||
{
|
||||
idTable = new unsigned char[idTableSize];
|
||||
CascReadFile(db2Handle, idTable, idTableSize, &bytesRead);
|
||||
if (bytesRead != idTableSize)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (copyIdSize)
|
||||
{
|
||||
copyTable = new unsigned char[copyIdSize];
|
||||
CascReadFile(db2Handle, copyTable, copyIdSize, &bytesRead);
|
||||
if (bytesRead != copyIdSize)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DB2FileLoader::~DB2FileLoader()
|
||||
{
|
||||
delete[] data;
|
||||
delete[] idTable;
|
||||
delete[] copyTable;
|
||||
delete[] fields;
|
||||
}
|
||||
|
||||
DB2FileLoader::Record DB2FileLoader::getRecord(size_t id)
|
||||
{
|
||||
assert(data);
|
||||
return Record(*this, data + id * recordSize);
|
||||
}
|
||||
|
||||
std::pair<std::uint32_t, std::uint32_t> DB2FileLoader::GetRowCopy(std::uint32_t i) const
|
||||
{
|
||||
std::uint32_t* copyIds = (std::uint32_t*)copyTable;
|
||||
std::uint32_t to = copyIds[i];
|
||||
std::uint32_t from = copyIds[i + 1];
|
||||
return{ from, to };
|
||||
}
|
||||
|
||||
std::uint32_t DB2FileLoader::GetMaxId() const
|
||||
{
|
||||
std::uint32_t j = maxIndex;
|
||||
for (std::uint32_t i = 0; i < GetNumRowCopies(); ++i)
|
||||
if (j < GetRowCopy(i).second)
|
||||
j = GetRowCopy(i).second;
|
||||
|
||||
return j;
|
||||
}
|
||||
178
src/tools/map_extractor/DB2.h
Normal file
178
src/tools/map_extractor/DB2.h
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MapExtractor_DB2_h__
|
||||
#define MapExtractor_DB2_h__
|
||||
|
||||
#include "DB2Meta.h"
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
#undef PLATFORM_WINDOWS
|
||||
#endif
|
||||
#include "CascLib.h"
|
||||
#include "Utilities/ByteConverter.h"
|
||||
#include "Errors.h"
|
||||
|
||||
class DB2FileLoader
|
||||
{
|
||||
public:
|
||||
DB2FileLoader();
|
||||
~DB2FileLoader();
|
||||
|
||||
bool Load(HANDLE db2Handle, DB2Meta const* meta);
|
||||
|
||||
class Record
|
||||
{
|
||||
public:
|
||||
float getFloat(std::uint32_t field, std::uint32_t arrayIndex) const
|
||||
{
|
||||
ASSERT(field < file.fieldCount);
|
||||
float val = *reinterpret_cast<float*>(offset + GetOffset(field) + arrayIndex * sizeof(float));
|
||||
EndianConvert(val);
|
||||
return val;
|
||||
}
|
||||
|
||||
std::uint32_t getUInt(std::uint32_t field, std::uint32_t arrayIndex) const
|
||||
{
|
||||
ASSERT(field < file.fieldCount);
|
||||
return GetVarInt(field, GetByteSize(field), arrayIndex);
|
||||
}
|
||||
|
||||
std::uint8_t getUInt8(std::uint32_t field, std::uint32_t arrayIndex) const
|
||||
{
|
||||
ASSERT(field < file.fieldCount);
|
||||
ASSERT(GetByteSize(field) == 1);
|
||||
return *reinterpret_cast<std::uint8_t*>(offset + GetOffset(field) + arrayIndex * sizeof(std::uint8_t));
|
||||
}
|
||||
|
||||
std::uint16_t getUInt16(std::uint32_t field, std::uint32_t arrayIndex) const
|
||||
{
|
||||
ASSERT(field < file.fieldCount);
|
||||
ASSERT(GetByteSize(field) == 2);
|
||||
std::uint16_t val = *reinterpret_cast<std::uint16_t*>(offset + GetOffset(field) + arrayIndex * sizeof(std::uint16_t));
|
||||
EndianConvert(val);
|
||||
return val;
|
||||
}
|
||||
|
||||
char const* getString(std::uint32_t field, std::uint32_t arrayIndex) const
|
||||
{
|
||||
ASSERT(field < file.fieldCount);
|
||||
std::uint32_t stringOffset = *reinterpret_cast<std::uint32_t*>(offset + GetOffset(field) + arrayIndex * sizeof(std::uint32_t));
|
||||
EndianConvert(stringOffset);
|
||||
ASSERT(stringOffset < file.stringSize);
|
||||
return reinterpret_cast<char*>(file.stringTable + stringOffset);
|
||||
}
|
||||
|
||||
private:
|
||||
std::uint16_t GetOffset(std::uint32_t field) const
|
||||
{
|
||||
ASSERT(field < file.fieldCount);
|
||||
return file.fields[field].Offset;
|
||||
}
|
||||
|
||||
std::uint16_t GetByteSize(std::uint32_t field) const
|
||||
{
|
||||
ASSERT(field < file.fieldCount);
|
||||
return 4 - file.fields[field].UnusedBits / 8;
|
||||
}
|
||||
|
||||
std::uint32_t GetVarInt(std::uint32_t field, std::uint16_t size, std::uint32_t arrayIndex) const
|
||||
{
|
||||
ASSERT(field < file.fieldCount);
|
||||
switch (size)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
return *reinterpret_cast<std::uint8_t*>(offset + GetOffset(field) + arrayIndex * sizeof(std::uint8_t));
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
std::uint16_t val = *reinterpret_cast<std::uint16_t*>(offset + GetOffset(field) + arrayIndex * sizeof(std::uint16_t));
|
||||
EndianConvert(val);
|
||||
return val;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
#pragma pack(push, 1)
|
||||
struct dbcint24 { std::uint8_t v[3]; };
|
||||
#pragma pack(pop)
|
||||
dbcint24 val = *reinterpret_cast<dbcint24*>(offset + GetOffset(field) + arrayIndex * sizeof(dbcint24));
|
||||
EndianConvert(val);
|
||||
return std::uint32_t(val.v[0]) | (std::uint32_t(val.v[1]) << 8) | (std::uint32_t(val.v[2]) << 16);
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
std::uint32_t val = *reinterpret_cast<std::uint32_t*>(offset + GetOffset(field) + arrayIndex * sizeof(std::uint32_t));
|
||||
EndianConvert(val);
|
||||
return val;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ASSERT(false, "GetByteSize(field) < 4");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Record(DB2FileLoader &file_, unsigned char *offset_): offset(offset_), file(file_) {}
|
||||
unsigned char *offset;
|
||||
DB2FileLoader &file;
|
||||
|
||||
friend class DB2FileLoader;
|
||||
};
|
||||
|
||||
// Get record by id
|
||||
Record getRecord(size_t id);
|
||||
std::uint32_t getId(size_t row) { return ((std::uint32_t*)idTable)[row]; }
|
||||
std::pair<std::uint32_t, std::uint32_t> GetRowCopy(std::uint32_t i) const;
|
||||
|
||||
std::uint32_t GetNumRows() const { return recordCount; }
|
||||
std::uint32_t GetNumRowCopies() const { return copyIdSize / 8; }
|
||||
std::uint32_t GetMaxId() const;
|
||||
|
||||
private:
|
||||
#pragma pack(push, 1)
|
||||
struct FieldEntry
|
||||
{
|
||||
std::uint16_t UnusedBits;
|
||||
std::uint16_t Offset;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
DB2Meta const* meta;
|
||||
|
||||
// WDB2 / WCH2 fields
|
||||
std::uint32_t recordSize;
|
||||
std::uint32_t recordCount;
|
||||
std::uint32_t fieldCount;
|
||||
std::uint32_t stringSize;
|
||||
std::uint32_t tableHash;
|
||||
std::uint32_t layoutHash;
|
||||
std::uint32_t minIndex;
|
||||
std::uint32_t maxIndex;
|
||||
std::uint32_t localeMask;
|
||||
std::uint32_t copyIdSize;
|
||||
std::uint32_t metaFlags;
|
||||
|
||||
unsigned char* data;
|
||||
unsigned char* stringTable;
|
||||
unsigned char* idTable;
|
||||
std::uint32_t idTableSize;
|
||||
unsigned char* copyTable;
|
||||
FieldEntry* fields;
|
||||
};
|
||||
|
||||
#endif // MapExtractor_DB2_h__
|
||||
@@ -34,7 +34,7 @@
|
||||
#endif
|
||||
#include "DBFilesClientList.h"
|
||||
#include "CascLib.h"
|
||||
#include "dbcfile.h"
|
||||
#include "DB2.h"
|
||||
#include "Banner.h"
|
||||
#include "StringFormat.h"
|
||||
|
||||
@@ -75,12 +75,34 @@ typedef struct
|
||||
uint32 id;
|
||||
} map_id;
|
||||
|
||||
map_id *map_ids;
|
||||
uint16 *LiqType;
|
||||
std::vector<map_id> map_ids;
|
||||
std::vector<uint16> LiqType;
|
||||
#define MAX_PATH_LENGTH 128
|
||||
char output_path[MAX_PATH_LENGTH];
|
||||
char input_path[MAX_PATH_LENGTH];
|
||||
|
||||
struct LiquidTypeMeta
|
||||
{
|
||||
static DB2Meta const* Instance()
|
||||
{
|
||||
static char const* types = "sifffffSifihhbbbbbi";
|
||||
static uint8 const arraySizes[19] = { 1, 1, 1, 1, 1, 1, 1, 6, 2, 18, 4, 1, 1, 1, 1, 1, 1, 6, 1 };
|
||||
static DB2Meta instance(-1, 19, 0x28B44DCB, types, arraySizes);
|
||||
return &instance;
|
||||
}
|
||||
};
|
||||
|
||||
struct MapMeta
|
||||
{
|
||||
static DB2Meta const* Instance()
|
||||
{
|
||||
static char const* types = "siffssshhhhhhhbbbbb";
|
||||
static uint8 const arraySizes[19] = { 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
|
||||
static DB2Meta instance(-1, 19, 0xB32E648C, types, arraySizes);
|
||||
return &instance;
|
||||
}
|
||||
};
|
||||
|
||||
// **************************************************
|
||||
// Extractor options
|
||||
// **************************************************
|
||||
@@ -276,31 +298,34 @@ uint32 ReadBuild(int locale)
|
||||
return build;
|
||||
}
|
||||
|
||||
uint32 ReadMapDBC()
|
||||
void ReadMapDBC()
|
||||
{
|
||||
printf("Read Map.dbc file... ");
|
||||
|
||||
HANDLE dbcFile;
|
||||
if (!CascOpenFile(CascStorage, "DBFilesClient\\Map.dbc", CASC_LOCALE_NONE, 0, &dbcFile))
|
||||
if (!CascOpenFile(CascStorage, "DBFilesClient\\Map.db2", CASC_LOCALE_NONE, 0, &dbcFile))
|
||||
{
|
||||
printf("Fatal error: Cannot find Map.dbc in archive! %s\n", HumanReadableCASCError(GetLastError()));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DBCFile dbc(dbcFile);
|
||||
if (!dbc.open())
|
||||
DB2FileLoader db2;
|
||||
if (!db2.Load(dbcFile, MapMeta::Instance()))
|
||||
{
|
||||
printf("Fatal error: Invalid Map.dbc file format!\n");
|
||||
printf("Fatal error: Invalid Map.db2 file format! %s\n", HumanReadableCASCError(GetLastError()));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
size_t map_count = dbc.getRecordCount();
|
||||
map_ids = new map_id[map_count];
|
||||
for(uint32 x = 0; x < map_count; ++x)
|
||||
map_ids.resize(db2.GetNumRows());
|
||||
std::unordered_map<uint32, uint32> idToIndex;
|
||||
for (uint32 x = 0; x < db2.GetNumRows(); ++x)
|
||||
{
|
||||
map_ids[x].id = dbc.getRecord(x).getUInt(0);
|
||||
if (MapMeta::Instance()->HasIndexFieldInData())
|
||||
map_ids[x].id = db2.getRecord(x).getUInt(MapMeta::Instance()->GetIndexField(), 0);
|
||||
else
|
||||
map_ids[x].id = db2.getId(x);
|
||||
|
||||
const char* map_name = dbc.getRecord(x).getString(1);
|
||||
const char* map_name = db2.getRecord(x).getString(0, 0);
|
||||
size_t max_map_name_length = sizeof(map_ids[x].name);
|
||||
if (strlen(map_name) >= max_map_name_length)
|
||||
{
|
||||
@@ -310,40 +335,62 @@ uint32 ReadMapDBC()
|
||||
|
||||
strncpy(map_ids[x].name, map_name, max_map_name_length);
|
||||
map_ids[x].name[max_map_name_length - 1] = '\0';
|
||||
idToIndex[map_ids[x].id] = x;
|
||||
}
|
||||
|
||||
for (uint32 x = 0; x < db2.GetNumRowCopies(); ++x)
|
||||
{
|
||||
uint32 from = db2.GetRowCopy(x).first;
|
||||
uint32 to = db2.GetRowCopy(x).second;
|
||||
auto itr = idToIndex.find(from);
|
||||
if (itr != idToIndex.end())
|
||||
{
|
||||
map_id id;
|
||||
id.id = to;
|
||||
strcpy(id.name, map_ids[itr->second].name);
|
||||
map_ids.push_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
CascCloseFile(dbcFile);
|
||||
printf("Done! (" SZFMTD " maps loaded)\n", map_count);
|
||||
return map_count;
|
||||
printf("Done! (" SZFMTD " maps loaded)\n", map_ids.size());
|
||||
}
|
||||
|
||||
void ReadLiquidTypeTableDBC()
|
||||
{
|
||||
printf("Read LiquidType.dbc file...");
|
||||
HANDLE dbcFile;
|
||||
if (!CascOpenFile(CascStorage, "DBFilesClient\\LiquidType.dbc", CASC_LOCALE_NONE, 0, &dbcFile))
|
||||
if (!CascOpenFile(CascStorage, "DBFilesClient\\LiquidType.db2", CASC_LOCALE_NONE, 0, &dbcFile))
|
||||
{
|
||||
printf("Fatal error: Cannot find LiquidType.dbc in archive! %s\n", HumanReadableCASCError(GetLastError()));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DBCFile dbc(dbcFile);
|
||||
if(!dbc.open())
|
||||
DB2FileLoader db2;
|
||||
if (!db2.Load(dbcFile, LiquidTypeMeta::Instance()))
|
||||
{
|
||||
printf("Fatal error: Invalid LiquidType.dbc file format!\n");
|
||||
printf("Fatal error: Invalid LiquidType.db2 file format!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
size_t liqTypeCount = dbc.getRecordCount();
|
||||
size_t liqTypeMaxId = dbc.getMaxId();
|
||||
LiqType = new uint16[liqTypeMaxId + 1];
|
||||
memset(LiqType, 0xff, (liqTypeMaxId + 1) * sizeof(uint16));
|
||||
LiqType.resize(db2.GetMaxId(), 0xFFFF);
|
||||
|
||||
for(uint32 x = 0; x < liqTypeCount; ++x)
|
||||
LiqType[dbc.getRecord(x).getUInt(0)] = dbc.getRecord(x).getUInt(3);
|
||||
for (uint32 x = 0; x < db2.GetNumRows(); ++x)
|
||||
{
|
||||
uint32 liquidTypeId;
|
||||
if (LiquidTypeMeta::Instance()->HasIndexFieldInData())
|
||||
liquidTypeId = db2.getRecord(x).getUInt(LiquidTypeMeta::Instance()->GetIndexField(), 0);
|
||||
else
|
||||
liquidTypeId = db2.getId(x);
|
||||
|
||||
LiqType[liquidTypeId] = db2.getRecord(x).getUInt8(13, 0);
|
||||
}
|
||||
|
||||
for (uint32 x = 0; x < db2.GetNumRowCopies(); ++x)
|
||||
LiqType[db2.GetRowCopy(x).second] = LiqType[db2.GetRowCopy(x).first];
|
||||
|
||||
CascCloseFile(dbcFile);
|
||||
printf("Done! (" SZFMTD " LiqTypes loaded)\n", liqTypeCount);
|
||||
printf("Done! (" SZFMTD " LiqTypes loaded)\n", LiqType.size());
|
||||
}
|
||||
|
||||
//
|
||||
@@ -1034,7 +1081,7 @@ void ExtractMaps(uint32 build)
|
||||
|
||||
printf("Extracting maps...\n");
|
||||
|
||||
uint32 map_count = ReadMapDBC();
|
||||
ReadMapDBC();
|
||||
|
||||
ReadLiquidTypeTableDBC();
|
||||
|
||||
@@ -1045,9 +1092,9 @@ void ExtractMaps(uint32 build)
|
||||
std::set<std::string> wmoList;
|
||||
|
||||
printf("Convert map files\n");
|
||||
for (uint32 z = 0; z < map_count; ++z)
|
||||
for (std::size_t z = 0; z < map_ids.size(); ++z)
|
||||
{
|
||||
printf("Extract %s (%d/%u) \n", map_ids[z].name, z+1, map_count);
|
||||
printf("Extract %s (" SZFMTD "/" SZFMTD ") \n", map_ids[z].name, z+1, map_ids.size());
|
||||
// Loadup map grid data
|
||||
storagePath = Trinity::StringFormat("World\\Maps\\%s\\%s.wdt", map_ids[z].name, map_ids[z].name);
|
||||
ChunkedFile wdt;
|
||||
@@ -1091,7 +1138,6 @@ void ExtractMaps(uint32 build)
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
delete[] map_ids;
|
||||
}
|
||||
|
||||
bool ExtractFile(HANDLE fileInArchive, std::string filename)
|
||||
@@ -1138,8 +1184,7 @@ void ExtractDBFilesClient(int l)
|
||||
while (fileName)
|
||||
{
|
||||
std::string filename = fileName;
|
||||
if (CascOpenFile(CascStorage, (filename = (filename + ".db2")).c_str(), WowLocaleToCascLocaleFlags[l], 0, &dbcFile) ||
|
||||
CascOpenFile(CascStorage, (filename = (filename.substr(0, filename.length() - 4) + ".dbc")).c_str(), WowLocaleToCascLocaleFlags[l], 0, &dbcFile))
|
||||
if (CascOpenFile(CascStorage, filename.c_str(), WowLocaleToCascLocaleFlags[l], 0, &dbcFile))
|
||||
{
|
||||
filename = outputPath + filename.substr(filename.rfind('\\') + 1);
|
||||
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define _CRT_SECURE_NO_DEPRECATE
|
||||
|
||||
#include "dbcfile.h"
|
||||
|
||||
DBCFile::DBCFile(HANDLE file) :
|
||||
_file(file), _recordSize(0), _recordCount(0), _fieldCount(0),
|
||||
_stringSize(0), _data(NULL), _stringTable(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
bool DBCFile::open()
|
||||
{
|
||||
char header[4];
|
||||
unsigned int na, nb, es, ss;
|
||||
|
||||
DWORD readBytes = 0;
|
||||
CascReadFile(_file, header, 4, &readBytes);
|
||||
if (readBytes != 4) // Number of records
|
||||
return false;
|
||||
|
||||
if (header[0] != 'W' || header[1] != 'D' || header[2] != 'B' || header[3] != 'C')
|
||||
return false;
|
||||
|
||||
CascReadFile(_file, &na, 4, &readBytes);
|
||||
if (readBytes != 4) // Number of records
|
||||
return false;
|
||||
|
||||
CascReadFile(_file, &nb, 4, &readBytes);
|
||||
if (readBytes != 4) // Number of fields
|
||||
return false;
|
||||
|
||||
CascReadFile(_file, &es, 4, &readBytes);
|
||||
if (readBytes != 4) // Size of a record
|
||||
return false;
|
||||
|
||||
CascReadFile(_file, &ss, 4, &readBytes);
|
||||
if (readBytes != 4) // String size
|
||||
return false;
|
||||
|
||||
_recordSize = es;
|
||||
_recordCount = na;
|
||||
_fieldCount = nb;
|
||||
_stringSize = ss;
|
||||
if (_fieldCount * 4 != _recordSize)
|
||||
return false;
|
||||
|
||||
_data = new unsigned char[_recordSize * _recordCount + _stringSize];
|
||||
_stringTable = _data + _recordSize*_recordCount;
|
||||
|
||||
size_t data_size = _recordSize * _recordCount + _stringSize;
|
||||
CascReadFile(_file, _data, data_size, &readBytes);
|
||||
if (readBytes != data_size)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DBCFile::~DBCFile()
|
||||
{
|
||||
delete [] _data;
|
||||
}
|
||||
|
||||
DBCFile::Record DBCFile::getRecord(size_t id)
|
||||
{
|
||||
assert(_data);
|
||||
return Record(*this, _data + id*_recordSize);
|
||||
}
|
||||
|
||||
size_t DBCFile::getMaxId()
|
||||
{
|
||||
assert(_data);
|
||||
|
||||
size_t maxId = 0;
|
||||
for(size_t i = 0; i < getRecordCount(); ++i)
|
||||
if (maxId < getRecord(i).getUInt(0))
|
||||
maxId = getRecord(i).getUInt(0);
|
||||
|
||||
return maxId;
|
||||
}
|
||||
|
||||
DBCFile::Iterator DBCFile::begin()
|
||||
{
|
||||
assert(_data);
|
||||
return Iterator(*this, _data);
|
||||
}
|
||||
|
||||
DBCFile::Iterator DBCFile::end()
|
||||
{
|
||||
assert(_data);
|
||||
return Iterator(*this, _stringTable);
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DBCFILE_H
|
||||
#define DBCFILE_H
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include "CascLib.h"
|
||||
|
||||
class DBCFile
|
||||
{
|
||||
public:
|
||||
DBCFile(HANDLE file);
|
||||
~DBCFile();
|
||||
|
||||
// Open database. It must be openened before it can be used.
|
||||
bool open();
|
||||
|
||||
// Database exceptions
|
||||
class Exception
|
||||
{
|
||||
public:
|
||||
Exception(const std::string &message) : message(message) { }
|
||||
virtual ~Exception() { }
|
||||
const std::string &getMessage() { return message; }
|
||||
private:
|
||||
std::string message;
|
||||
};
|
||||
|
||||
class NotFound: public Exception
|
||||
{
|
||||
public:
|
||||
NotFound(): Exception("Key was not found") { }
|
||||
};
|
||||
|
||||
// Iteration over database
|
||||
class Iterator;
|
||||
class Record
|
||||
{
|
||||
public:
|
||||
float getFloat(size_t field) const
|
||||
{
|
||||
assert(field < file._fieldCount);
|
||||
return *reinterpret_cast<float*>(offset + field * 4);
|
||||
}
|
||||
|
||||
unsigned int getUInt(size_t field) const
|
||||
{
|
||||
assert(field < file._fieldCount);
|
||||
return *reinterpret_cast<unsigned int*>(offset + field * 4);
|
||||
}
|
||||
|
||||
int getInt(size_t field) const
|
||||
{
|
||||
assert(field < file._fieldCount);
|
||||
return *reinterpret_cast<int*>(offset + field * 4);
|
||||
}
|
||||
|
||||
char const* getString(size_t field) const
|
||||
{
|
||||
assert(field < file._fieldCount);
|
||||
size_t stringOffset = getUInt(field);
|
||||
assert(stringOffset < file._stringSize);
|
||||
return reinterpret_cast<char*>(file._stringTable + stringOffset);
|
||||
}
|
||||
|
||||
private:
|
||||
Record(DBCFile& file, unsigned char* offset): file(file), offset(offset) {}
|
||||
DBCFile& file;
|
||||
unsigned char* offset;
|
||||
|
||||
friend class DBCFile;
|
||||
friend class DBCFile::Iterator;
|
||||
|
||||
Record& operator=(Record const& right);
|
||||
};
|
||||
/** Iterator that iterates over records
|
||||
*/
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
Iterator(DBCFile &file, unsigned char* offset) : record(file, offset) { }
|
||||
|
||||
/// Advance (prefix only)
|
||||
Iterator& operator++()
|
||||
{
|
||||
record.offset += record.file._recordSize;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Return address of current instance
|
||||
Record const& operator*() const { return record; }
|
||||
Record const* operator->() const { return &record; }
|
||||
|
||||
/// Comparison
|
||||
bool operator==(Iterator const& b) const
|
||||
{
|
||||
return record.offset == b.record.offset;
|
||||
}
|
||||
|
||||
bool operator!=(Iterator const& b) const
|
||||
{
|
||||
return record.offset != b.record.offset;
|
||||
}
|
||||
|
||||
private:
|
||||
Record record;
|
||||
|
||||
Iterator& operator=(Iterator const& right);
|
||||
};
|
||||
|
||||
// Get record by id
|
||||
Record getRecord(size_t id);
|
||||
/// Get begin iterator over records
|
||||
Iterator begin();
|
||||
/// Get begin iterator over records
|
||||
Iterator end();
|
||||
/// Trivial
|
||||
size_t getRecordCount() const { return _recordCount; }
|
||||
size_t getFieldCount() const { return _fieldCount; }
|
||||
size_t getMaxId();
|
||||
|
||||
private:
|
||||
HANDLE _file;
|
||||
size_t _recordSize;
|
||||
size_t _recordCount;
|
||||
size_t _fieldCount;
|
||||
size_t _stringSize;
|
||||
unsigned char* _data;
|
||||
unsigned char* _stringTable;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
199
src/tools/vmap4_extractor/DB2.cpp
Normal file
199
src/tools/vmap4_extractor/DB2.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "DB2.h"
|
||||
|
||||
DB2FileLoader::DB2FileLoader()
|
||||
{
|
||||
meta = nullptr;
|
||||
|
||||
recordSize = 0;
|
||||
recordCount = 0;
|
||||
fieldCount = 0;
|
||||
stringSize = 0;
|
||||
tableHash = 0;
|
||||
layoutHash = 0;
|
||||
minIndex = 0;
|
||||
maxIndex = 0;
|
||||
localeMask = 0;
|
||||
copyIdSize = 0;
|
||||
|
||||
data = nullptr;
|
||||
stringTable = nullptr;
|
||||
idTable = nullptr;
|
||||
idTableSize = 0;
|
||||
copyTable = nullptr;
|
||||
fields = nullptr;
|
||||
}
|
||||
|
||||
bool DB2FileLoader::Load(HANDLE db2Handle, DB2Meta const* meta)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
delete[] data;
|
||||
data = nullptr;
|
||||
}
|
||||
|
||||
DWORD bytesRead = 0;
|
||||
this->meta = meta;
|
||||
std::uint32_t header;
|
||||
CascReadFile(db2Handle, &header, sizeof(header), &bytesRead);
|
||||
if (bytesRead != sizeof(header)) // Signature
|
||||
return false;
|
||||
|
||||
EndianConvert(header);
|
||||
|
||||
if (header != 0x35424457)
|
||||
return false; //'WDB5'
|
||||
|
||||
CascReadFile(db2Handle, &recordCount, sizeof(recordCount), &bytesRead);
|
||||
if (bytesRead != sizeof(recordCount)) // Number of records
|
||||
return false;
|
||||
|
||||
EndianConvert(recordCount);
|
||||
|
||||
CascReadFile(db2Handle, &fieldCount, sizeof(fieldCount), &bytesRead);
|
||||
if (bytesRead != sizeof(fieldCount)) // Number of fields
|
||||
return false;
|
||||
|
||||
EndianConvert(fieldCount);
|
||||
|
||||
CascReadFile(db2Handle, &recordSize, sizeof(recordSize), &bytesRead);
|
||||
if (bytesRead != sizeof(recordSize)) // Size of a record
|
||||
return false;
|
||||
|
||||
EndianConvert(recordSize);
|
||||
|
||||
CascReadFile(db2Handle, &stringSize, sizeof(stringSize), &bytesRead);
|
||||
if (bytesRead != sizeof(stringSize)) // String size
|
||||
return false;
|
||||
|
||||
EndianConvert(stringSize);
|
||||
|
||||
CascReadFile(db2Handle, &tableHash, sizeof(tableHash), &bytesRead);
|
||||
if (bytesRead != sizeof(tableHash)) // Table hash
|
||||
return false;
|
||||
|
||||
EndianConvert(tableHash);
|
||||
|
||||
CascReadFile(db2Handle, &layoutHash, sizeof(layoutHash), &bytesRead);
|
||||
if (bytesRead != sizeof(layoutHash)) // Layout hash
|
||||
return false;
|
||||
|
||||
if (layoutHash != meta->LayoutHash)
|
||||
return false;
|
||||
|
||||
EndianConvert(layoutHash);
|
||||
|
||||
CascReadFile(db2Handle, &minIndex, sizeof(minIndex), &bytesRead);
|
||||
if (bytesRead != sizeof(minIndex)) // MinIndex WDB2
|
||||
return false;
|
||||
|
||||
EndianConvert(minIndex);
|
||||
|
||||
CascReadFile(db2Handle, &maxIndex, sizeof(maxIndex), &bytesRead);
|
||||
if (bytesRead != sizeof(maxIndex)) // MaxIndex WDB2
|
||||
return false;
|
||||
|
||||
EndianConvert(maxIndex);
|
||||
|
||||
CascReadFile(db2Handle, &localeMask, sizeof(localeMask), &bytesRead);
|
||||
if (bytesRead != sizeof(localeMask)) // Locales
|
||||
return false;
|
||||
|
||||
EndianConvert(localeMask);
|
||||
|
||||
CascReadFile(db2Handle, ©IdSize, sizeof(copyIdSize), &bytesRead);
|
||||
if (bytesRead != sizeof(copyIdSize))
|
||||
return false;
|
||||
|
||||
EndianConvert(copyIdSize);
|
||||
|
||||
CascReadFile(db2Handle, &metaFlags, sizeof(metaFlags), &bytesRead);
|
||||
if (bytesRead != sizeof(metaFlags))
|
||||
return false;
|
||||
|
||||
EndianConvert(metaFlags);
|
||||
|
||||
ASSERT((metaFlags & 0x1) == 0);
|
||||
ASSERT((meta->IndexField == -1) || (meta->IndexField == (metaFlags >> 16)));
|
||||
|
||||
fields = new FieldEntry[fieldCount];
|
||||
CascReadFile(db2Handle, fields, fieldCount * sizeof(FieldEntry), &bytesRead);
|
||||
if (bytesRead != fieldCount * sizeof(FieldEntry))
|
||||
return false;
|
||||
|
||||
if (!meta->HasIndexFieldInData())
|
||||
idTableSize = recordCount * sizeof(std::uint32_t);
|
||||
|
||||
data = new unsigned char[recordSize * recordCount + stringSize];
|
||||
stringTable = data + recordSize * recordCount;
|
||||
|
||||
CascReadFile(db2Handle, data, recordSize * recordCount + stringSize, &bytesRead);
|
||||
if (bytesRead != recordSize * recordCount + stringSize)
|
||||
return false;
|
||||
|
||||
if (idTableSize)
|
||||
{
|
||||
idTable = new unsigned char[idTableSize];
|
||||
CascReadFile(db2Handle, idTable, idTableSize, &bytesRead);
|
||||
if (bytesRead != idTableSize)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (copyIdSize)
|
||||
{
|
||||
copyTable = new unsigned char[copyIdSize];
|
||||
CascReadFile(db2Handle, copyTable, copyIdSize, &bytesRead);
|
||||
if (bytesRead != copyIdSize)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DB2FileLoader::~DB2FileLoader()
|
||||
{
|
||||
delete[] data;
|
||||
delete[] idTable;
|
||||
delete[] copyTable;
|
||||
delete[] fields;
|
||||
}
|
||||
|
||||
DB2FileLoader::Record DB2FileLoader::getRecord(size_t id)
|
||||
{
|
||||
assert(data);
|
||||
return Record(*this, data + id * recordSize);
|
||||
}
|
||||
|
||||
std::pair<std::uint32_t, std::uint32_t> DB2FileLoader::GetRowCopy(std::uint32_t i) const
|
||||
{
|
||||
std::uint32_t* copyIds = (std::uint32_t*)copyTable;
|
||||
std::uint32_t to = copyIds[i];
|
||||
std::uint32_t from = copyIds[i + 1];
|
||||
return{ from, to };
|
||||
}
|
||||
|
||||
std::uint32_t DB2FileLoader::GetMaxId() const
|
||||
{
|
||||
std::uint32_t j = maxIndex;
|
||||
for (std::uint32_t i = 0; i < GetNumRowCopies(); ++i)
|
||||
if (j < GetRowCopy(i).second)
|
||||
j = GetRowCopy(i).second;
|
||||
|
||||
return j;
|
||||
}
|
||||
178
src/tools/vmap4_extractor/DB2.h
Normal file
178
src/tools/vmap4_extractor/DB2.h
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MapExtractor_DB2_h__
|
||||
#define MapExtractor_DB2_h__
|
||||
|
||||
#include "DB2Meta.h"
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
#undef PLATFORM_WINDOWS
|
||||
#endif
|
||||
#include "CascLib.h"
|
||||
#include "Utilities/ByteConverter.h"
|
||||
#include "Errors.h"
|
||||
|
||||
class DB2FileLoader
|
||||
{
|
||||
public:
|
||||
DB2FileLoader();
|
||||
~DB2FileLoader();
|
||||
|
||||
bool Load(HANDLE db2Handle, DB2Meta const* meta);
|
||||
|
||||
class Record
|
||||
{
|
||||
public:
|
||||
float getFloat(std::uint32_t field, std::uint32_t arrayIndex) const
|
||||
{
|
||||
ASSERT(field < file.fieldCount);
|
||||
float val = *reinterpret_cast<float*>(offset + GetOffset(field) + arrayIndex * sizeof(float));
|
||||
EndianConvert(val);
|
||||
return val;
|
||||
}
|
||||
|
||||
std::uint32_t getUInt(std::uint32_t field, std::uint32_t arrayIndex) const
|
||||
{
|
||||
ASSERT(field < file.fieldCount);
|
||||
return GetVarInt(field, GetByteSize(field), arrayIndex);
|
||||
}
|
||||
|
||||
std::uint8_t getUInt8(std::uint32_t field, std::uint32_t arrayIndex) const
|
||||
{
|
||||
ASSERT(field < file.fieldCount);
|
||||
ASSERT(GetByteSize(field) == 1);
|
||||
return *reinterpret_cast<std::uint8_t*>(offset + GetOffset(field) + arrayIndex * sizeof(std::uint8_t));
|
||||
}
|
||||
|
||||
std::uint16_t getUInt16(std::uint32_t field, std::uint32_t arrayIndex) const
|
||||
{
|
||||
ASSERT(field < file.fieldCount);
|
||||
ASSERT(GetByteSize(field) == 2);
|
||||
std::uint16_t val = *reinterpret_cast<std::uint16_t*>(offset + GetOffset(field) + arrayIndex * sizeof(std::uint16_t));
|
||||
EndianConvert(val);
|
||||
return val;
|
||||
}
|
||||
|
||||
char const* getString(std::uint32_t field, std::uint32_t arrayIndex) const
|
||||
{
|
||||
ASSERT(field < file.fieldCount);
|
||||
std::uint32_t stringOffset = *reinterpret_cast<std::uint32_t*>(offset + GetOffset(field) + arrayIndex * sizeof(std::uint32_t));
|
||||
EndianConvert(stringOffset);
|
||||
ASSERT(stringOffset < file.stringSize);
|
||||
return reinterpret_cast<char*>(file.stringTable + stringOffset);
|
||||
}
|
||||
|
||||
private:
|
||||
std::uint16_t GetOffset(std::uint32_t field) const
|
||||
{
|
||||
ASSERT(field < file.fieldCount);
|
||||
return file.fields[field].Offset;
|
||||
}
|
||||
|
||||
std::uint16_t GetByteSize(std::uint32_t field) const
|
||||
{
|
||||
ASSERT(field < file.fieldCount);
|
||||
return 4 - file.fields[field].UnusedBits / 8;
|
||||
}
|
||||
|
||||
std::uint32_t GetVarInt(std::uint32_t field, std::uint16_t size, std::uint32_t arrayIndex) const
|
||||
{
|
||||
ASSERT(field < file.fieldCount);
|
||||
switch (size)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
return *reinterpret_cast<std::uint8_t*>(offset + GetOffset(field) + arrayIndex * sizeof(std::uint8_t));
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
std::uint16_t val = *reinterpret_cast<std::uint16_t*>(offset + GetOffset(field) + arrayIndex * sizeof(std::uint16_t));
|
||||
EndianConvert(val);
|
||||
return val;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
#pragma pack(push, 1)
|
||||
struct dbcint24 { std::uint8_t v[3]; };
|
||||
#pragma pack(pop)
|
||||
dbcint24 val = *reinterpret_cast<dbcint24*>(offset + GetOffset(field) + arrayIndex * sizeof(dbcint24));
|
||||
EndianConvert(val);
|
||||
return std::uint32_t(val.v[0]) | (std::uint32_t(val.v[1]) << 8) | (std::uint32_t(val.v[2]) << 16);
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
std::uint32_t val = *reinterpret_cast<std::uint32_t*>(offset + GetOffset(field) + arrayIndex * sizeof(std::uint32_t));
|
||||
EndianConvert(val);
|
||||
return val;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ASSERT(false, "GetByteSize(field) < 4");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Record(DB2FileLoader &file_, unsigned char *offset_): offset(offset_), file(file_) {}
|
||||
unsigned char *offset;
|
||||
DB2FileLoader &file;
|
||||
|
||||
friend class DB2FileLoader;
|
||||
};
|
||||
|
||||
// Get record by id
|
||||
Record getRecord(size_t id);
|
||||
std::uint32_t getId(size_t row) { return ((std::uint32_t*)idTable)[row]; }
|
||||
std::pair<std::uint32_t, std::uint32_t> GetRowCopy(std::uint32_t i) const;
|
||||
|
||||
std::uint32_t GetNumRows() const { return recordCount; }
|
||||
std::uint32_t GetNumRowCopies() const { return copyIdSize / 8; }
|
||||
std::uint32_t GetMaxId() const;
|
||||
|
||||
private:
|
||||
#pragma pack(push, 1)
|
||||
struct FieldEntry
|
||||
{
|
||||
std::uint16_t UnusedBits;
|
||||
std::uint16_t Offset;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
DB2Meta const* meta;
|
||||
|
||||
// WDB2 / WCH2 fields
|
||||
std::uint32_t recordSize;
|
||||
std::uint32_t recordCount;
|
||||
std::uint32_t fieldCount;
|
||||
std::uint32_t stringSize;
|
||||
std::uint32_t tableHash;
|
||||
std::uint32_t layoutHash;
|
||||
std::uint32_t minIndex;
|
||||
std::uint32_t maxIndex;
|
||||
std::uint32_t localeMask;
|
||||
std::uint32_t copyIdSize;
|
||||
std::uint32_t metaFlags;
|
||||
|
||||
unsigned char* data;
|
||||
unsigned char* stringTable;
|
||||
unsigned char* idTable;
|
||||
std::uint32_t idTableSize;
|
||||
unsigned char* copyTable;
|
||||
FieldEntry* fields;
|
||||
};
|
||||
|
||||
#endif // MapExtractor_DB2_h__
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define _CRT_SECURE_NO_DEPRECATE
|
||||
|
||||
#include "dbcfile.h"
|
||||
|
||||
DBCFile::DBCFile(HANDLE mpq, const char* filename) :
|
||||
_mpq(mpq), _filename(filename), _file(NULL), _recordSize(0), _recordCount(0),
|
||||
_fieldCount(0), _stringSize(0), _data(NULL), _stringTable(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
bool DBCFile::open()
|
||||
{
|
||||
if (!CascOpenFile(_mpq, _filename, CASC_LOCALE_NONE, 0, &_file))
|
||||
return false;
|
||||
|
||||
char header[4];
|
||||
unsigned int na, nb, es, ss;
|
||||
|
||||
DWORD readBytes = 0;
|
||||
CascReadFile(_file, header, 4, &readBytes);
|
||||
if (readBytes != 4) // Number of records
|
||||
return false;
|
||||
|
||||
if (header[0] != 'W' || header[1] != 'D' || header[2] != 'B' || header[3] != 'C')
|
||||
return false;
|
||||
|
||||
readBytes = 0;
|
||||
CascReadFile(_file, &na, 4, &readBytes);
|
||||
if (readBytes != 4) // Number of records
|
||||
return false;
|
||||
|
||||
readBytes = 0;
|
||||
CascReadFile(_file, &nb, 4, &readBytes);
|
||||
if (readBytes != 4) // Number of fields
|
||||
return false;
|
||||
|
||||
readBytes = 0;
|
||||
CascReadFile(_file, &es, 4, &readBytes);
|
||||
if (readBytes != 4) // Size of a record
|
||||
return false;
|
||||
|
||||
readBytes = 0;
|
||||
CascReadFile(_file, &ss, 4, &readBytes);
|
||||
if (readBytes != 4) // String size
|
||||
return false;
|
||||
|
||||
_recordSize = es;
|
||||
_recordCount = na;
|
||||
_fieldCount = nb;
|
||||
_stringSize = ss;
|
||||
if (_fieldCount * 4 != _recordSize)
|
||||
return false;
|
||||
|
||||
_data = new unsigned char[_recordSize * _recordCount + _stringSize];
|
||||
_stringTable = _data + _recordSize*_recordCount;
|
||||
|
||||
size_t data_size = _recordSize * _recordCount + _stringSize;
|
||||
readBytes = 0;
|
||||
CascReadFile(_file, _data, data_size, &readBytes);
|
||||
if (readBytes != data_size)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DBCFile::~DBCFile()
|
||||
{
|
||||
delete [] _data;
|
||||
if (_file != NULL)
|
||||
CascCloseFile(_file);
|
||||
}
|
||||
|
||||
DBCFile::Record DBCFile::getRecord(size_t id)
|
||||
{
|
||||
assert(_data);
|
||||
return Record(*this, _data + id*_recordSize);
|
||||
}
|
||||
|
||||
size_t DBCFile::getMaxId()
|
||||
{
|
||||
assert(_data);
|
||||
|
||||
size_t maxId = 0;
|
||||
for(size_t i = 0; i < getRecordCount(); ++i)
|
||||
if (maxId < getRecord(i).getUInt(0))
|
||||
maxId = getRecord(i).getUInt(0);
|
||||
|
||||
return maxId;
|
||||
}
|
||||
|
||||
DBCFile::Iterator DBCFile::begin()
|
||||
{
|
||||
assert(_data);
|
||||
return Iterator(*this, _data);
|
||||
}
|
||||
|
||||
DBCFile::Iterator DBCFile::end()
|
||||
{
|
||||
assert(_data);
|
||||
return Iterator(*this, _stringTable);
|
||||
}
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DBCFILE_H
|
||||
#define DBCFILE_H
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include "CascLib.h"
|
||||
|
||||
class DBCFile
|
||||
{
|
||||
public:
|
||||
DBCFile(HANDLE mpq, const char* filename);
|
||||
~DBCFile();
|
||||
|
||||
// Open database. It must be openened before it can be used.
|
||||
bool open();
|
||||
|
||||
// Database exceptions
|
||||
class Exception
|
||||
{
|
||||
public:
|
||||
Exception(const std::string &message) : message(message) { }
|
||||
virtual ~Exception() { }
|
||||
const std::string &getMessage() { return message; }
|
||||
private:
|
||||
std::string message;
|
||||
};
|
||||
|
||||
class NotFound: public Exception
|
||||
{
|
||||
public:
|
||||
NotFound(): Exception("Key was not found") { }
|
||||
};
|
||||
|
||||
// Iteration over database
|
||||
class Iterator;
|
||||
class Record
|
||||
{
|
||||
public:
|
||||
float getFloat(size_t field) const
|
||||
{
|
||||
assert(field < file._fieldCount);
|
||||
return *reinterpret_cast<float*>(offset + field * 4);
|
||||
}
|
||||
|
||||
unsigned int getUInt(size_t field) const
|
||||
{
|
||||
assert(field < file._fieldCount);
|
||||
return *reinterpret_cast<unsigned int*>(offset + field * 4);
|
||||
}
|
||||
|
||||
int getInt(size_t field) const
|
||||
{
|
||||
assert(field < file._fieldCount);
|
||||
return *reinterpret_cast<int*>(offset + field * 4);
|
||||
}
|
||||
|
||||
char const* getString(size_t field) const
|
||||
{
|
||||
assert(field < file._fieldCount);
|
||||
size_t stringOffset = getUInt(field);
|
||||
assert(stringOffset < file._stringSize);
|
||||
return reinterpret_cast<char*>(file._stringTable + stringOffset);
|
||||
}
|
||||
|
||||
private:
|
||||
Record(DBCFile& file, unsigned char* offset): file(file), offset(offset) {}
|
||||
DBCFile& file;
|
||||
unsigned char* offset;
|
||||
|
||||
friend class DBCFile;
|
||||
friend class DBCFile::Iterator;
|
||||
|
||||
Record& operator=(Record const&);
|
||||
Record(Record const& right) : file(right.file), offset(right.offset)
|
||||
{
|
||||
}
|
||||
};
|
||||
/** Iterator that iterates over records
|
||||
*/
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
Iterator(DBCFile &file, unsigned char* offset) : record(file, offset) { }
|
||||
|
||||
Iterator(Iterator const& right) : record(right.record)
|
||||
{
|
||||
}
|
||||
|
||||
/// Advance (prefix only)
|
||||
Iterator& operator++()
|
||||
{
|
||||
record.offset += record.file._recordSize;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Return address of current instance
|
||||
Record const& operator*() const { return record; }
|
||||
Record const* operator->() const { return &record; }
|
||||
|
||||
/// Comparison
|
||||
bool operator==(Iterator const& b) const
|
||||
{
|
||||
return record.offset == b.record.offset;
|
||||
}
|
||||
|
||||
bool operator!=(Iterator const& b) const
|
||||
{
|
||||
return record.offset != b.record.offset;
|
||||
}
|
||||
|
||||
Iterator& operator=(Iterator const& right)
|
||||
{
|
||||
record.offset = right.record.offset;
|
||||
return *this;
|
||||
}
|
||||
private:
|
||||
Record record;
|
||||
|
||||
};
|
||||
|
||||
// Get record by id
|
||||
Record getRecord(size_t id);
|
||||
/// Get begin iterator over records
|
||||
Iterator begin();
|
||||
/// Get begin iterator over records
|
||||
Iterator end();
|
||||
/// Trivial
|
||||
size_t getRecordCount() const { return _recordCount; }
|
||||
size_t getFieldCount() const { return _fieldCount; }
|
||||
size_t getMaxId();
|
||||
|
||||
private:
|
||||
HANDLE _mpq;
|
||||
const char* _filename;
|
||||
HANDLE _file;
|
||||
size_t _recordSize;
|
||||
size_t _recordCount;
|
||||
size_t _fieldCount;
|
||||
size_t _stringSize;
|
||||
unsigned char *_data;
|
||||
unsigned char* _stringTable;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -16,28 +16,17 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "DB2.h"
|
||||
#include "model.h"
|
||||
#include "dbcfile.h"
|
||||
#include "adtfile.h"
|
||||
#include "vmapexport.h"
|
||||
#include "StringFormat.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdio.h>
|
||||
|
||||
bool ExtractSingleModel(std::string& fname)
|
||||
bool ExtractSingleModel(std::string& name)
|
||||
{
|
||||
if (fname.substr(fname.length() - 4, 4) == ".mdx")
|
||||
{
|
||||
fname.erase(fname.length() - 2, 2);
|
||||
fname.append("2");
|
||||
}
|
||||
|
||||
std::string originalName = fname;
|
||||
|
||||
char* name = GetPlainName((char*)fname.c_str());
|
||||
FixNameCase(name, strlen(name));
|
||||
FixNameSpaces(name, strlen(name));
|
||||
|
||||
std::string output(szWorkDirWmo);
|
||||
output += "/";
|
||||
output += name;
|
||||
@@ -45,7 +34,7 @@ bool ExtractSingleModel(std::string& fname)
|
||||
if (FileExists(output.c_str()))
|
||||
return true;
|
||||
|
||||
Model mdl(originalName);
|
||||
Model mdl(name);
|
||||
if (!mdl.open())
|
||||
return false;
|
||||
|
||||
@@ -54,26 +43,64 @@ bool ExtractSingleModel(std::string& fname)
|
||||
|
||||
extern HANDLE CascStorage;
|
||||
|
||||
struct GameObjectDisplayInfoMeta
|
||||
{
|
||||
static DB2Meta const* Instance()
|
||||
{
|
||||
static char const* types = "ifffh";
|
||||
static uint8 const arraySizes[5] = { 1, 6, 1, 1, 1 };
|
||||
static DB2Meta instance(-1, 5, 0xDD4432B9, types, arraySizes);
|
||||
return &instance;
|
||||
}
|
||||
};
|
||||
|
||||
struct CascFileHandleDeleter
|
||||
{
|
||||
typedef HANDLE pointer;
|
||||
void operator()(HANDLE handle) const { CascCloseFile(handle); }
|
||||
};
|
||||
|
||||
enum ModelTypes : uint32
|
||||
{
|
||||
MODEL_MD20 = '02DM',
|
||||
MODEL_MD21 = '12DM',
|
||||
MODEL_WMO = 'REVM'
|
||||
};
|
||||
|
||||
uint32 GetHeaderMagic(std::string const& fileName)
|
||||
{
|
||||
HANDLE file;
|
||||
if (!CascOpenFile(CascStorage, fileName.c_str(), CASC_LOCALE_ALL, 0, &file))
|
||||
return 0;
|
||||
|
||||
std::unique_ptr<HANDLE, CascFileHandleDeleter> modelFile(file);
|
||||
uint32 magic = 0;
|
||||
DWORD bytesRead = 0;
|
||||
if (!CascReadFile(file, &magic, 4, &bytesRead) || bytesRead != 4)
|
||||
return 0;
|
||||
|
||||
return magic;
|
||||
}
|
||||
|
||||
void ExtractGameobjectModels()
|
||||
{
|
||||
printf("Extracting GameObject models...");
|
||||
DBCFile dbc(CascStorage, "DBFilesClient\\GameObjectDisplayInfo.dbc");
|
||||
if(!dbc.open())
|
||||
HANDLE dbcFile;
|
||||
if (!CascOpenFile(CascStorage, "DBFilesClient\\GameObjectDisplayInfo.db2", CASC_LOCALE_NONE, 0, &dbcFile))
|
||||
{
|
||||
printf("Fatal error: Invalid GameObjectDisplayInfo.dbc file format!\n");
|
||||
printf("Fatal error: Cannot find GameObjectDisplayInfo.db2 in archive!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DBCFile fileData(CascStorage, "DBFilesClient\\FileData.dbc");
|
||||
if (!fileData.open())
|
||||
DB2FileLoader db2;
|
||||
if (!db2.Load(dbcFile, GameObjectDisplayInfoMeta::Instance()))
|
||||
{
|
||||
printf("Fatal error: Invalid FileData.dbc file format!\n");
|
||||
printf("Fatal error: Invalid GameObjectDisplayInfo.db2 file format!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
std::string basepath = szWorkDirWmo;
|
||||
basepath += "/";
|
||||
std::string path;
|
||||
|
||||
std::string modelListPath = basepath + "temp_gameobject_models";
|
||||
FILE* model_list = fopen(modelListPath.c_str(), "wb");
|
||||
@@ -83,62 +110,30 @@ void ExtractGameobjectModels()
|
||||
return;
|
||||
}
|
||||
|
||||
size_t maxFileId = fileData.getMaxId() + 1;
|
||||
uint32* fileDataIndex = new uint32[maxFileId];
|
||||
memset(fileDataIndex, 0, maxFileId * sizeof(uint32));
|
||||
size_t files = fileData.getRecordCount();
|
||||
for (uint32 i = 0; i < files; ++i)
|
||||
fileDataIndex[fileData.getRecord(i).getUInt(0)] = i;
|
||||
|
||||
for (DBCFile::Iterator it = dbc.begin(); it != dbc.end(); ++it)
|
||||
for (uint32 rec = 0; rec < db2.GetNumRows(); ++rec)
|
||||
{
|
||||
uint32 fileId = it->getUInt(1);
|
||||
uint32 fileId = db2.getRecord(rec).getUInt(0, 0);
|
||||
if (!fileId)
|
||||
continue;
|
||||
|
||||
uint32 fileIndex = fileDataIndex[fileId];
|
||||
if (!fileIndex)
|
||||
continue;
|
||||
|
||||
std::string filename = fileData.getRecord(fileIndex).getString(1);
|
||||
std::string filepath = fileData.getRecord(fileIndex).getString(2);
|
||||
|
||||
path = filepath + filename;
|
||||
|
||||
if (path.length() < 4)
|
||||
continue;
|
||||
|
||||
FixNameCase((char*)path.c_str(), path.size());
|
||||
char * name = GetPlainName((char*)path.c_str());
|
||||
FixNameSpaces(name, strlen(name));
|
||||
|
||||
char * ch_ext = GetExtension(name);
|
||||
if (!ch_ext)
|
||||
continue;
|
||||
|
||||
strToLower(ch_ext);
|
||||
|
||||
std::string fileName = Trinity::StringFormat("FILE%08X", fileId);
|
||||
bool result = false;
|
||||
if (!strcmp(ch_ext, ".wmo"))
|
||||
result = ExtractSingleWmo(path);
|
||||
else if (!strcmp(ch_ext, ".mdl")) // TODO: extract .mdl files, if needed
|
||||
continue;
|
||||
else if (!strcmp(ch_ext, ".mdx") || !strcmp(ch_ext, ".m2"))
|
||||
result = ExtractSingleModel(path);
|
||||
if (GetHeaderMagic(fileName) == MODEL_WMO)
|
||||
result = ExtractSingleWmo(fileName);
|
||||
else
|
||||
result = ExtractSingleModel(fileName);
|
||||
|
||||
if (result)
|
||||
{
|
||||
uint32 displayId = it->getUInt(0);
|
||||
uint32 path_length = strlen(name);
|
||||
uint32 displayId = db2.getId(rec);
|
||||
uint32 path_length = fileName.length();
|
||||
fwrite(&displayId, sizeof(uint32), 1, model_list);
|
||||
fwrite(&path_length, sizeof(uint32), 1, model_list);
|
||||
fwrite(name, sizeof(char), path_length, model_list);
|
||||
fwrite(fileName.c_str(), sizeof(char), path_length, model_list);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(model_list);
|
||||
|
||||
delete[] fileDataIndex;
|
||||
|
||||
printf("Done!\n");
|
||||
}
|
||||
|
||||
@@ -45,16 +45,24 @@ bool Model::open()
|
||||
|
||||
_unload();
|
||||
|
||||
int32 m2start = 0;
|
||||
char const* ptr = f.getBuffer();
|
||||
while (m2start + 4 < f.getSize() && *reinterpret_cast<uint32 const*>(ptr) != '02DM')
|
||||
{
|
||||
++m2start;
|
||||
++ptr;
|
||||
}
|
||||
|
||||
memcpy(&header, f.getBuffer(), sizeof(ModelHeader));
|
||||
if (header.nBoundingTriangles > 0)
|
||||
{
|
||||
f.seek(0);
|
||||
f.seek(m2start);
|
||||
f.seekRelative(header.ofsBoundingVertices);
|
||||
vertices = new Vec3D[header.nBoundingVertices];
|
||||
f.read(vertices,header.nBoundingVertices*12);
|
||||
for (uint32 i=0; i<header.nBoundingVertices; i++)
|
||||
vertices[i] = fixCoordSystem(vertices[i]);
|
||||
f.seek(0);
|
||||
f.seek(m2start);
|
||||
f.seekRelative(header.ofsBoundingTriangles);
|
||||
indices = new uint16[header.nBoundingTriangles];
|
||||
f.read(indices,header.nBoundingTriangles*2);
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
//From Extractor
|
||||
#include "adtfile.h"
|
||||
#include "wdtfile.h"
|
||||
#include "dbcfile.h"
|
||||
#include "DB2.h"
|
||||
#include "wmo.h"
|
||||
#include "mpqfile.h"
|
||||
|
||||
@@ -68,13 +68,35 @@ typedef struct
|
||||
unsigned int id;
|
||||
}map_id;
|
||||
|
||||
map_id * map_ids;
|
||||
uint16 *LiqType = 0;
|
||||
std::vector<map_id> map_ids;
|
||||
std::vector<uint16> LiqType;
|
||||
uint32 map_count;
|
||||
char output_path[128] = ".";
|
||||
char input_path[1024] = ".";
|
||||
bool preciseVectorData = false;
|
||||
|
||||
struct LiquidTypeMeta
|
||||
{
|
||||
static DB2Meta const* Instance()
|
||||
{
|
||||
static char const* types = "sifffffSifihhbbbbbi";
|
||||
static uint8 const arraySizes[19] = { 1, 1, 1, 1, 1, 1, 1, 6, 2, 18, 4, 1, 1, 1, 1, 1, 1, 6, 1 };
|
||||
static DB2Meta instance(-1, 19, 0x28B44DCB, types, arraySizes);
|
||||
return &instance;
|
||||
}
|
||||
};
|
||||
|
||||
struct MapMeta
|
||||
{
|
||||
static DB2Meta const* Instance()
|
||||
{
|
||||
static char const* types = "siffssshhhhhhhbbbbb";
|
||||
static uint8 const arraySizes[19] = { 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
|
||||
static DB2Meta instance(-1, 19, 0xB32E648C, types, arraySizes);
|
||||
return &instance;
|
||||
}
|
||||
};
|
||||
|
||||
// Constants
|
||||
|
||||
//static const char * szWorkDirMaps = ".\\Maps";
|
||||
@@ -228,23 +250,38 @@ void strToLower(char* str)
|
||||
void ReadLiquidTypeTableDBC()
|
||||
{
|
||||
printf("Read LiquidType.dbc file...");
|
||||
|
||||
DBCFile dbc(CascStorage, "DBFilesClient\\LiquidType.dbc");
|
||||
if(!dbc.open())
|
||||
HANDLE dbcFile;
|
||||
if (!CascOpenFile(CascStorage, "DBFilesClient\\LiquidType.db2", CASC_LOCALE_NONE, 0, &dbcFile))
|
||||
{
|
||||
printf("Fatal error: Invalid LiquidType.dbc file format!\n");
|
||||
printf("Fatal error: Cannot find LiquidType.dbc in archive! %s\n", HumanReadableCASCError(GetLastError()));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
size_t LiqType_count = dbc.getRecordCount();
|
||||
size_t LiqType_maxid = dbc.getRecord(LiqType_count - 1).getUInt(0);
|
||||
LiqType = new uint16[LiqType_maxid + 1];
|
||||
memset(LiqType, 0xff, (LiqType_maxid + 1) * sizeof(uint16));
|
||||
DB2FileLoader db2;
|
||||
if (!db2.Load(dbcFile, LiquidTypeMeta::Instance()))
|
||||
{
|
||||
printf("Fatal error: Invalid LiquidType.db2 file format!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for(uint32 x = 0; x < LiqType_count; ++x)
|
||||
LiqType[dbc.getRecord(x).getUInt(0)] = dbc.getRecord(x).getUInt(3);
|
||||
LiqType.resize(db2.GetMaxId(), 0xFFFF);
|
||||
|
||||
printf("Done! (%u LiqTypes loaded)\n", (unsigned int)LiqType_count);
|
||||
for (uint32 x = 0; x < db2.GetNumRows(); ++x)
|
||||
{
|
||||
uint32 liquidTypeId;
|
||||
if (LiquidTypeMeta::Instance()->HasIndexFieldInData())
|
||||
liquidTypeId = db2.getRecord(x).getUInt(LiquidTypeMeta::Instance()->GetIndexField(), 0);
|
||||
else
|
||||
liquidTypeId = db2.getId(x);
|
||||
|
||||
LiqType[liquidTypeId] = db2.getRecord(x).getUInt8(13, 0);
|
||||
}
|
||||
|
||||
for (uint32 x = 0; x < db2.GetNumRowCopies(); ++x)
|
||||
LiqType[db2.GetRowCopy(x).second] = LiqType[db2.GetRowCopy(x).first];
|
||||
|
||||
CascCloseFile(dbcFile);
|
||||
printf("Done! (" SZFMTD " LiqTypes loaded)\n", LiqType.size());
|
||||
}
|
||||
|
||||
bool ExtractWmo()
|
||||
@@ -325,15 +362,12 @@ bool ExtractSingleWmo(std::string& fname)
|
||||
froot.ConvertToVMAPRootWmo(output);
|
||||
int Wmo_nVertices = 0;
|
||||
//printf("root has %d groups\n", froot->nGroups);
|
||||
if (froot.nGroups !=0)
|
||||
if (!froot.groupFileDataIDs.empty())
|
||||
{
|
||||
for (uint32 i = 0; i < froot.nGroups; ++i)
|
||||
for (std::size_t i = 0; i < froot.groupFileDataIDs.size(); ++i)
|
||||
{
|
||||
char temp[1024];
|
||||
strncpy(temp, fname.c_str(), 1024);
|
||||
temp[fname.length()-4] = 0;
|
||||
char groupFileName[1024];
|
||||
sprintf(groupFileName, "%s_%03u.wmo", temp, i);
|
||||
sprintf(groupFileName, "FILE%08X", froot.groupFileDataIDs[i]);
|
||||
//printf("Trying to open groupfile %s\n",groupFileName);
|
||||
|
||||
std::string s = groupFileName;
|
||||
@@ -364,7 +398,7 @@ void ParsMapFiles()
|
||||
char fn[512];
|
||||
//char id_filename[64];
|
||||
char id[10];
|
||||
for (unsigned int i=0; i<map_count; ++i)
|
||||
for (unsigned int i = 0; i < map_ids.size(); ++i)
|
||||
{
|
||||
sprintf(id, "%04u", map_ids[i].id);
|
||||
sprintf(fn,"World\\Maps\\%s\\%s.wdt", map_ids[i].name, map_ids[i].name);
|
||||
@@ -538,38 +572,61 @@ int main(int argc, char ** argv)
|
||||
//map.dbc
|
||||
if (success)
|
||||
{
|
||||
DBCFile * dbc = new DBCFile(CascStorage, "DBFilesClient\\Map.dbc");
|
||||
if (!dbc->open())
|
||||
printf("Read Map.dbc file... ");
|
||||
|
||||
HANDLE dbcFile;
|
||||
if (!CascOpenFile(CascStorage, "DBFilesClient\\Map.db2", CASC_LOCALE_NONE, 0, &dbcFile))
|
||||
{
|
||||
delete dbc;
|
||||
printf("FATAL ERROR: Map.dbc not found in data file.\n");
|
||||
return 1;
|
||||
printf("Fatal error: Cannot find Map.dbc in archive! %s\n", HumanReadableCASCError(GetLastError()));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
map_count = dbc->getRecordCount();
|
||||
map_ids = new map_id[map_count];
|
||||
for (unsigned int x = 0; x < map_count; ++x)
|
||||
DB2FileLoader db2;
|
||||
if (!db2.Load(dbcFile, MapMeta::Instance()))
|
||||
{
|
||||
map_ids[x].id = dbc->getRecord(x).getUInt(0);
|
||||
printf("Fatal error: Invalid Map.db2 file format! %s\n", HumanReadableCASCError(GetLastError()));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char* map_name = dbc->getRecord(x).getString(1);
|
||||
map_ids.resize(db2.GetNumRows());
|
||||
std::unordered_map<uint32, uint32> idToIndex;
|
||||
for (uint32 x = 0; x < db2.GetNumRows(); ++x)
|
||||
{
|
||||
if (MapMeta::Instance()->HasIndexFieldInData())
|
||||
map_ids[x].id = db2.getRecord(x).getUInt(MapMeta::Instance()->GetIndexField(), 0);
|
||||
else
|
||||
map_ids[x].id = db2.getId(x);
|
||||
|
||||
const char* map_name = db2.getRecord(x).getString(0, 0);
|
||||
size_t max_map_name_length = sizeof(map_ids[x].name);
|
||||
if (strlen(map_name) >= max_map_name_length)
|
||||
{
|
||||
delete dbc;
|
||||
delete[] map_ids;
|
||||
printf("FATAL ERROR: Map name too long.\n");
|
||||
return 1;
|
||||
printf("Fatal error: Map name too long!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
strncpy(map_ids[x].name, map_name, max_map_name_length);
|
||||
map_ids[x].name[max_map_name_length - 1] = '\0';
|
||||
printf("Map - %s\n", map_ids[x].name);
|
||||
idToIndex[map_ids[x].id] = x;
|
||||
}
|
||||
|
||||
delete dbc;
|
||||
for (uint32 x = 0; x < db2.GetNumRowCopies(); ++x)
|
||||
{
|
||||
uint32 from = db2.GetRowCopy(x).first;
|
||||
uint32 to = db2.GetRowCopy(x).second;
|
||||
auto itr = idToIndex.find(from);
|
||||
if (itr != idToIndex.end())
|
||||
{
|
||||
map_id id;
|
||||
id.id = to;
|
||||
strcpy(id.name, map_ids[itr->second].name);
|
||||
map_ids.push_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
CascCloseFile(dbcFile);
|
||||
printf("Done! (" SZFMTD " maps loaded)\n", map_ids.size());
|
||||
ParsMapFiles();
|
||||
delete [] map_ids;
|
||||
}
|
||||
|
||||
CascCloseStorage(CascStorage);
|
||||
@@ -582,6 +639,5 @@ int main(int argc, char ** argv)
|
||||
}
|
||||
|
||||
printf("Extract %s. Work complete. No errors.\n", versionString);
|
||||
delete [] LiqType;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -29,11 +29,11 @@
|
||||
#include "mpqfile.h"
|
||||
|
||||
using namespace std;
|
||||
extern uint16 *LiqType;
|
||||
extern std::vector<uint16> LiqType;
|
||||
|
||||
WMORoot::WMORoot(std::string &filename)
|
||||
: filename(filename), col(0), nTextures(0), nGroups(0), nP(0), nLights(0),
|
||||
nModels(0), nDoodads(0), nDoodadSets(0), RootWMOID(0), liquidType(0)
|
||||
: filename(filename), color(0), nTextures(0), nGroups(0), nPortals(0), nLights(0),
|
||||
nDoodadNames(0), nDoodadDefs(0), nDoodadSets(0), RootWMOID(0), flags(0)
|
||||
{
|
||||
memset(bbcorn1, 0, sizeof(bbcorn1));
|
||||
memset(bbcorn2, 0, sizeof(bbcorn2));
|
||||
@@ -67,18 +67,30 @@ bool WMORoot::open()
|
||||
{
|
||||
f.read(&nTextures, 4);
|
||||
f.read(&nGroups, 4);
|
||||
f.read(&nP, 4);
|
||||
f.read(&nPortals, 4);
|
||||
f.read(&nLights, 4);
|
||||
f.read(&nModels, 4);
|
||||
f.read(&nDoodads, 4);
|
||||
f.read(&nDoodadNames, 4);
|
||||
f.read(&nDoodadDefs, 4);
|
||||
f.read(&nDoodadSets, 4);
|
||||
f.read(&col, 4);
|
||||
f.read(&color, 4);
|
||||
f.read(&RootWMOID, 4);
|
||||
f.read(bbcorn1, 12);
|
||||
f.read(bbcorn2, 12);
|
||||
f.read(&liquidType, 4);
|
||||
f.read(&flags, 4);
|
||||
break;
|
||||
}
|
||||
else if (!strcmp(fourcc, "GFID"))
|
||||
{
|
||||
for (uint32 gp = 0; gp < nGroups; ++gp)
|
||||
{
|
||||
uint32 fileDataId;
|
||||
f.read(&fileDataId, 4);
|
||||
groupFileDataIDs.push_back(fileDataId);
|
||||
|
||||
if (flags & 16) // LOD related
|
||||
f.seekRelative(8);
|
||||
}
|
||||
}
|
||||
/*
|
||||
else if (!strcmp(fourcc,"MOTX"))
|
||||
{
|
||||
@@ -409,7 +421,7 @@ int WMOGroup::ConvertToVMAPGroupWmo(FILE *output, WMORoot *rootWMO, bool precise
|
||||
|
||||
// according to WoW.Dev Wiki:
|
||||
uint32 liquidEntry;
|
||||
if (rootWMO->liquidType & 4)
|
||||
if (rootWMO->flags & 4)
|
||||
liquidEntry = liquidType;
|
||||
else if (liquidType == 15)
|
||||
liquidEntry = 0;
|
||||
@@ -460,7 +472,7 @@ int WMOGroup::ConvertToVMAPGroupWmo(FILE *output, WMORoot *rootWMO, bool precise
|
||||
|
||||
/* std::ofstream llog("Buildings/liquid.log", ios_base::out | ios_base::app);
|
||||
llog << filename;
|
||||
llog << ":\nliquidEntry: " << liquidEntry << " type: " << hlq->type << " (root:" << rootWMO->liquidType << " group:" << liquidType << ")\n";
|
||||
llog << ":\nliquidEntry: " << liquidEntry << " type: " << hlq->type << " (root:" << rootWMO->flags << " group:" << flags << ")\n";
|
||||
llog.close(); */
|
||||
|
||||
fwrite(hlq, sizeof(WMOLiquidHeader), 1, output);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include "vec3d.h"
|
||||
#include "mpqfile.h"
|
||||
|
||||
@@ -47,11 +48,13 @@ class WMORoot
|
||||
private:
|
||||
std::string filename;
|
||||
public:
|
||||
unsigned int col;
|
||||
uint32 nTextures, nGroups, nP, nLights, nModels, nDoodads, nDoodadSets, RootWMOID, liquidType;
|
||||
unsigned int color;
|
||||
uint32 nTextures, nGroups, nPortals, nLights, nDoodadNames, nDoodadDefs, nDoodadSets, RootWMOID, flags;
|
||||
float bbcorn1[3];
|
||||
float bbcorn2[3];
|
||||
|
||||
std::vector<uint32> groupFileDataIDs;
|
||||
|
||||
WMORoot(std::string& filename);
|
||||
|
||||
bool open();
|
||||
|
||||
Reference in New Issue
Block a user