Tools: Extractor updates

* VMAP extractor does not work due to a bug in CascLib
This commit is contained in:
Shauren
2016-07-18 23:37:19 +02:00
parent a9a13d10f7
commit fe4d11c65a
28 changed files with 1634 additions and 1297 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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
//-----------------------------------------------------------------------------

View File

@@ -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++;
}

View File

@@ -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))
{

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View 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, &copyIdSize, 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;
}

View 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__

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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

View 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, &copyIdSize, 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;
}

View 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__

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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");
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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();