diff options
| author | Shauren <shauren.trinity@gmail.com> | 2016-01-24 15:56:10 +0100 |
|---|---|---|
| committer | Shauren <shauren.trinity@gmail.com> | 2016-05-20 23:46:17 +0200 |
| commit | 5c2c9a684f1458da0cea1f3536622add77ef1324 (patch) | |
| tree | d3a2a349e8bed9a31cf417ce93830d508ecea46b /src/server/shared/DataStores | |
| parent | 65c0a0ee4d5c299f3caab04b6cb3fcd7a4a93e2e (diff) | |
Core/DataStores: Updated dbc/db2 to 7.0.1.20810
Diffstat (limited to 'src/server/shared/DataStores')
| -rw-r--r-- | src/server/shared/DataStores/DB2SparseStorageLoader.cpp | 683 | ||||
| -rw-r--r-- | src/server/shared/DataStores/DB2SparseStorageLoader.h | 117 | ||||
| -rw-r--r-- | src/server/shared/DataStores/DB2StorageLoader.cpp | 180 | ||||
| -rw-r--r-- | src/server/shared/DataStores/DB2StorageLoader.h | 40 | ||||
| -rw-r--r-- | src/server/shared/DataStores/DB2Store.h | 226 | ||||
| -rw-r--r-- | src/server/shared/DataStores/DBCFileLoader.cpp | 20 | ||||
| -rw-r--r-- | src/server/shared/DataStores/DBStorageIterator.h | 7 |
7 files changed, 1137 insertions, 136 deletions
diff --git a/src/server/shared/DataStores/DB2SparseStorageLoader.cpp b/src/server/shared/DataStores/DB2SparseStorageLoader.cpp new file mode 100644 index 00000000000..2e7e974cb5d --- /dev/null +++ b/src/server/shared/DataStores/DB2SparseStorageLoader.cpp @@ -0,0 +1,683 @@ +/* + * 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 "Common.h" +#include "DB2SparseStorageLoader.h" +#include "Database/DatabaseEnv.h" +#include "Log.h" + +DB2SparseFileLoader::DB2SparseFileLoader() +{ + fileName = nullptr; + + recordCount = 0; + fieldCount = 0; + recordSize = 0; + offsetsPos = 0; + tableHash = 0; + build = 0; + unk1 = 0; + minIndex = 0; + maxIndex = 0; + localeMask = 0; + copyIdSize = 0; + + dataStart = 0; + data = nullptr; + offsets = nullptr; +} + +bool DB2SparseFileLoader::Load(const char *filename) +{ + if (data) + { + delete[] data; + data = nullptr; + } + + FILE* f = fopen(filename, "rb"); + if (!f) + return false; + + fileName = filename; + uint32 header; + if (fread(&header, 4, 1, f) != 1) // Signature + { + fclose(f); + return false; + } + + EndianConvert(header); + + if (header != 0x34424457) + { + fclose(f); + return false; //'WDB4' + } + + if (fread(&recordCount, 4, 1, f) != 1) // Number of records + { + fclose(f); + return false; + } + + EndianConvert(recordCount); + + if (fread(&fieldCount, 4, 1, f) != 1) // Number of fields + { + fclose(f); + return false; + } + + EndianConvert(fieldCount); + + if (fread(&recordSize, 4, 1, f) != 1) // Size of a record + { + fclose(f); + return false; + } + + EndianConvert(recordSize); + + if (fread(&offsetsPos, 4, 1, f) != 1) // String size + { + fclose(f); + return false; + } + + EndianConvert(offsetsPos); + + if (fread(&tableHash, 4, 1, f) != 1) // Table hash + { + fclose(f); + return false; + } + + EndianConvert(tableHash); + + if (fread(&build, 4, 1, f) != 1) // Build + { + fclose(f); + return false; + } + + EndianConvert(build); + + if (fread(&unk1, 4, 1, f) != 1) // Unknown WDB2 + { + fclose(f); + return false; + } + + EndianConvert(unk1); + + if (fread(&minIndex, 4, 1, f) != 1) // MinIndex WDB2 + { + fclose(f); + return false; + } + + EndianConvert(minIndex); + + if (fread(&maxIndex, 4, 1, f) != 1) // MaxIndex WDB2 + { + fclose(f); + return false; + } + + EndianConvert(maxIndex); + + if (fread(&localeMask, 4, 1, f) != 1) // Locales + { + fclose(f); + return false; + } + + EndianConvert(localeMask); + + if (fread(©IdSize, 4, 1, f) != 1) + { + fclose(f); + return false; + } + + EndianConvert(copyIdSize); + + if (fread(&metaFlags, 4, 1, f) != 1) + { + fclose(f); + return false; + } + + EndianConvert(metaFlags); + + dataStart = ftell(f); + + data = new unsigned char[offsetsPos - dataStart]; + + if (fread(data, offsetsPos - dataStart, 1, f) != 1) + { + fclose(f); + return false; + } + + offsets = new OffsetTableEntry[maxIndex - minIndex + 1]; + if (fread(offsets, (maxIndex - minIndex + 1) * 6, 1, f) != 1) + { + fclose(f); + return false; + } + + fclose(f); + return true; +} + +DB2SparseFileLoader::~DB2SparseFileLoader() +{ + delete[] data; + delete[] offsets; +} + +uint32 DB2SparseFileLoader::GetFormatRecordSize(const char * format) +{ + uint32 recordsize = 0; + for (uint32 x = 0; format[x]; ++x) + { + switch (format[x]) + { + case FT_FLOAT: + case FT_INT: + recordsize += 4; + break; + case FT_STRING: + case FT_STRING_NOT_LOCALIZED: + recordsize += sizeof(char*); + break; + case FT_BYTE: + recordsize += 1; + break; + case FT_LONG: + recordsize += 8; + break; + case FT_SHORT: + recordsize += 2; + break; + } + } + + return recordsize; +} + +uint32 DB2SparseFileLoader::GetFormatStringFieldCount(const char* format) +{ + uint32 stringfields = 0; + for (uint32 x = 0; format[x]; ++x) + if (format[x] == FT_STRING || format[x] == FT_STRING_NOT_LOCALIZED) + ++stringfields; + + return stringfields; +} + +uint32 DB2SparseFileLoader::GetFormatLocalizedStringFieldCount(char const* format) +{ + uint32 stringfields = 0; + for (uint32 x = 0; format[x]; ++x) + if (format[x] == FT_STRING) + ++stringfields; + + return stringfields; +} + +static char const* const nullStr = ""; + +char* DB2SparseFileLoader::AutoProduceData(const char* format, IndexTable const& indexTable, uint32 locale, std::vector<char*>& stringPool) +{ + typedef char* ptr; + if (strlen(format) != fieldCount) + return NULL; + + //get struct size and index pos + uint32 recordsize = GetFormatRecordSize(format); + + uint32 offsetCount = maxIndex - minIndex + 1; + uint32 records = 0; + uint32 expandedDataSize = 0; + for (uint32 i = 0; i < offsetCount; ++i) + { + if (offsets[i].FileOffset && offsets[i].RecordSize) + { + ++records; + expandedDataSize += offsets[i].RecordSize; + } + } + + char* dataTable = new char[records * recordsize]; + + // we store flat holders pool as single memory block + std::size_t stringFields = GetFormatStringFieldCount(format); + std::size_t localizedStringFields = GetFormatLocalizedStringFieldCount(format); + + // each string field at load have array of string for each locale + std::size_t stringHolderSize = sizeof(char*) * TOTAL_LOCALES; + std::size_t stringHoldersRecordPoolSize = localizedStringFields * stringHolderSize + (stringFields - localizedStringFields) * sizeof(char*); + std::size_t stringHoldersPoolSize = stringHoldersRecordPoolSize * records; + + char* stringHoldersPool = new char[stringHoldersPoolSize]; + stringPool.push_back(stringHoldersPool); + + // DB2 strings expected to have at least empty string + for (std::size_t i = 0; i < stringHoldersPoolSize / sizeof(char*); ++i) + ((char const**)stringHoldersPool)[i] = nullStr; + + char* stringTable = new char[expandedDataSize - records * (recordsize - stringFields * sizeof(char*))]; + memset(stringTable, 0, expandedDataSize - records * (recordsize - stringFields * sizeof(char*))); + stringPool.push_back(stringTable); + char* stringPtr = stringTable; + + uint32 offset = 0; + uint32 recordNum = 0; + for (uint32 y = 0; y < offsetCount; ++y) + { + if (!offsets[y].FileOffset || !offsets[y].RecordSize) + continue; + + indexTable.Insert(y + minIndex, &dataTable[offset]); + uint32 fieldOffset = 0; + uint32 stringFieldOffset = 0; + for (uint32 x = 0; x < fieldCount; x++) + { + switch (format[x]) + { + case FT_FLOAT: + *((float*)(&dataTable[offset])) = *reinterpret_cast<float*>(&data[offsets[y].FileOffset - dataStart + fieldOffset]); + offset += 4; + fieldOffset += 4; + break; + case FT_IND: + case FT_INT: + *((uint32*)(&dataTable[offset])) = *reinterpret_cast<uint32*>(&data[offsets[y].FileOffset - dataStart + fieldOffset]); + offset += 4; + fieldOffset += 4; + break; + case FT_BYTE: + *((uint8*)(&dataTable[offset])) = *reinterpret_cast<uint8*>(&data[offsets[y].FileOffset - dataStart + fieldOffset]); + offset += 1; + fieldOffset += 1; + break; + case FT_LONG: + *((uint64*)(&dataTable[offset])) = *reinterpret_cast<uint64*>(&data[offsets[y].FileOffset - dataStart + fieldOffset]); + offset += 8; + fieldOffset += 8; + break; + case FT_SHORT: + *((uint16*)(&dataTable[offset])) = *reinterpret_cast<uint16*>(&data[offsets[y].FileOffset - dataStart + fieldOffset]); + offset += 2; + fieldOffset += 2; + break; + case FT_STRING: + { + LocalizedString** slot = (LocalizedString**)(&dataTable[offset]); + *slot = (LocalizedString*)(&stringHoldersPool[stringHoldersRecordPoolSize * recordNum + stringFieldOffset]); + (*slot)->Str[locale] = stringPtr; + strcpy(stringPtr, (char*)&data[offsets[y].FileOffset - dataStart + fieldOffset]); + fieldOffset += strlen(stringPtr) + 1; + stringPtr += strlen(stringPtr) + 1; + stringFieldOffset += stringHolderSize; + offset += sizeof(LocalizedString*); + break; + } + case FT_STRING_NOT_LOCALIZED: + { + char const*** slot = (char const***)(&dataTable[offset]); + *slot = (char const**)(&stringHoldersPool[stringHoldersRecordPoolSize * recordNum + stringFieldOffset]); + **slot = stringPtr; + strcpy(stringPtr, (char*)&data[offsets[y].FileOffset - dataStart + fieldOffset]); + fieldOffset += strlen(stringPtr) + 1; + stringPtr += strlen(stringPtr) + 1; + ++stringFieldOffset; + offset += sizeof(char*); + break; + } + } + } + + ++recordNum; + } + + return dataTable; +} + +char* DB2SparseFileLoader::AutoProduceStrings(const char* format, char* dataTable, uint32 locale) +{ + if (strlen(format) != fieldCount) + return nullptr; + + if (!(localeMask & (1 << locale))) + { + char const* sep = ""; + std::ostringstream str; + for (uint32 i = 0; i < TOTAL_LOCALES; ++i) + { + if (localeMask & (1 << i)) + { + str << sep << localeNames[i]; + sep = ", "; + } + } + + TC_LOG_ERROR("", "Attempted to load %s which has locales %s as %s. Check if you placed your localized db2 files in correct directory.", fileName, str.str().c_str(), localeNames[locale]); + return nullptr; + } + + uint32 offsetCount = maxIndex - minIndex + 1; + uint32 records = 0; + for (uint32 i = 0; i < offsetCount; ++i) + if (offsets[i].FileOffset && offsets[i].RecordSize) + ++records; + + uint32 recordsize = GetFormatRecordSize(format); + std::size_t stringFields = GetFormatStringFieldCount(format); + char* stringTable = new char[offsetsPos - dataStart - records * (recordsize - stringFields * sizeof(char*))]; + memset(stringTable, 0, offsetsPos - dataStart - records * (recordsize - stringFields * sizeof(char*))); + char* stringPtr = stringTable; + + uint32 offset = 0; + + for (uint32 y = 0; y < offsetCount; y++) + { + if (!offsets[y].FileOffset || !offsets[y].RecordSize) + continue; + + uint32 fieldOffset; + for (uint32 x = 0; x < fieldCount; x++) + { + switch (format[x]) + { + case FT_FLOAT: + case FT_IND: + case FT_INT: + offset += 4; + fieldOffset += 4; + break; + case FT_BYTE: + offset += 1; + fieldOffset += 1; + break; + case FT_LONG: + offset += 8; + fieldOffset += 8; + break; + case FT_SHORT: + offset += 2; + fieldOffset += 2; + break; + case FT_STRING: + { + // fill only not filled entries + LocalizedString* db2str = *(LocalizedString**)(&dataTable[offset]); + db2str->Str[locale] = stringPtr; + strcpy(stringPtr, (char*)&data[offsets[y].FileOffset - dataStart + fieldOffset]); + fieldOffset += strlen(stringPtr) + 1; + stringPtr += strlen(stringPtr) + 1; + offset += sizeof(char*); + break; + } + case FT_STRING_NOT_LOCALIZED: + { + char** db2str = (char**)(&dataTable[offset]); + *db2str = stringPtr; + strcpy(stringPtr, (char*)&data[offsets[y].FileOffset - dataStart + fieldOffset]); + fieldOffset += strlen(stringPtr) + 1; + stringPtr += strlen(stringPtr) + 1; + offset += sizeof(char*); + break; + } + } + } + } + + return stringTable; +} + +char* DB2SparseDatabaseLoader::Load(const char* format, HotfixDatabaseStatements preparedStatement, IndexTable const& indexTable, std::vector<char*>& stringPool) +{ + // Even though this query is executed only once, prepared statement is used to send data from mysql server in binary format + PreparedQueryResult result = HotfixDatabase.Query(HotfixDatabase.GetPreparedStatement(preparedStatement)); + if (!result) + return nullptr; + + uint32 const fieldCount = strlen(format); + if (fieldCount != result->GetFieldCount()) + return nullptr; + + // get struct size and index pos + uint32 recordSize = DB2SparseFileLoader::GetFormatRecordSize(format); + + // we store flat holders pool as single memory block + std::size_t stringFields = DB2SparseFileLoader::GetFormatStringFieldCount(format); + std::size_t localizedStringFields = DB2SparseFileLoader::GetFormatLocalizedStringFieldCount(format); + + // each string field at load have array of string for each locale + std::size_t stringHolderSize = sizeof(char*) * TOTAL_LOCALES; + std::size_t stringHoldersRecordPoolSize = localizedStringFields * stringHolderSize + (stringFields - localizedStringFields) * sizeof(char*); + + char* stringHolders = nullptr; + if (stringFields) + { + std::size_t stringHoldersPoolSize = stringHoldersRecordPoolSize * result->GetRowCount(); + stringHolders = new char[stringHoldersPoolSize]; + stringPool.push_back(stringHolders); + + // DB2 strings expected to have at least empty string + for (std::size_t i = 0; i < stringHoldersPoolSize / sizeof(char*); ++i) + ((char const**)stringHolders)[i] = nullStr; + } + + char* tempDataTable = new char[result->GetRowCount() * recordSize]; + uint32* newIndexes = new uint32[result->GetRowCount()]; + uint32 rec = 0; + uint32 newRecords = 0; + + do + { + Field* fields = result->Fetch(); + uint32 offset = 0; + uint32 stringFieldOffset = 0; + + uint32 indexValue = fields[0].GetUInt32(); + + // Attempt to overwrite existing data + char* dataValue = indexTable.Get(indexValue); + if (!dataValue) + { + newIndexes[newRecords] = indexValue; + dataValue = &tempDataTable[newRecords++ * recordSize]; + } + + for (uint32 f = 0; f < fieldCount; f++) + { + switch (format[f]) + { + case FT_FLOAT: + *((float*)(&dataValue[offset])) = fields[f].GetFloat(); + offset += 4; + break; + case FT_IND: + case FT_INT: + *((int32*)(&dataValue[offset])) = fields[f].GetInt32(); + offset += 4; + break; + case FT_BYTE: + *((int8*)(&dataValue[offset])) = fields[f].GetInt8(); + offset += 1; + break; + case FT_LONG: + *((int64*)(&dataValue[offset])) = fields[f].GetInt64(); + offset += 8; + break; + case FT_SHORT: + *((int16*)(&dataValue[offset])) = fields[f].GetInt16(); + offset += 2; + break; + case FT_STRING: + { + LocalizedString** slot = (LocalizedString**)(&dataValue[offset]); + *slot = (LocalizedString*)(&stringHolders[stringHoldersRecordPoolSize * rec + stringFieldOffset]); + ASSERT(*slot); + + // Value in database in main table field must be for enUS locale + if (char* str = AddString(&(*slot)->Str[LOCALE_enUS], fields[f].GetString())) + stringPool.push_back(str); + + stringFieldOffset += stringHolderSize; + offset += sizeof(char*); + break; + } + case FT_STRING_NOT_LOCALIZED: + { + char const** slot = (char const**)(&dataValue[offset]); + *slot = (char*)(&stringHolders[stringHoldersRecordPoolSize * rec + stringFieldOffset]); + ASSERT(*slot); + + // Value in database in main table field must be for enUS locale + if (char* str = AddString(slot, fields[f].GetString())) + stringPool.push_back(str); + + ++stringFieldOffset; + offset += sizeof(char*); + break; + } + } + } + + ASSERT(offset == recordSize); + ++rec; + } while (result->NextRow()); + + if (!newRecords) + { + delete[] tempDataTable; + delete[] newIndexes; + return nullptr; + } + + // Compact new data table to only contain new records not previously loaded from file + char* dataTable = new char[newRecords * recordSize]; + memcpy(dataTable, tempDataTable, newRecords * recordSize); + + // insert new records to index table + for (uint32 i = 0; i < newRecords; ++i) + indexTable.Insert(newIndexes[i], &dataTable[i * recordSize]); + + delete[] tempDataTable; + delete[] newIndexes; + + return dataTable; +} + +void DB2SparseDatabaseLoader::LoadStrings(const char* format, HotfixDatabaseStatements preparedStatement, uint32 locale, IndexTable const& indexTable, std::vector<char*>& stringPool) +{ + PreparedStatement* stmt = HotfixDatabase.GetPreparedStatement(preparedStatement); + stmt->setString(0, localeNames[locale]); + PreparedQueryResult result = HotfixDatabase.Query(stmt); + if (!result) + return; + + size_t stringFields = DB2SparseFileLoader::GetFormatLocalizedStringFieldCount(format); + if (result->GetFieldCount() != stringFields + 1 /*ID*/) + return; + + uint32 const fieldCount = strlen(format); + uint32 recordSize = DB2SparseFileLoader::GetFormatRecordSize(format); + ASSERT(0 >= 0, "DB2Storage must be indexed to load localized strings"); + + do + { + Field* fields = result->Fetch(); + uint32 offset = 0; + uint32 stringFieldNumInRecord = 0; + uint32 indexValue = fields[0].GetUInt32(); + + // Attempt to overwrite existing data + if (char* dataValue = indexTable.Get(indexValue)) + { + for (uint32 x = 0; x < fieldCount; x++) + { + switch (format[x]) + { + case FT_FLOAT: + case FT_IND: + case FT_INT: + offset += 4; + break; + case FT_BYTE: + offset += 1; + break; + case FT_LONG: + offset += 8; + break; + case FT_SHORT: + offset += 2; + break; + case FT_STRING: + { + // fill only not filled entries + LocalizedString* db2str = *(LocalizedString**)(&dataValue[offset]); + if (db2str->Str[locale] == nullStr) + if (char* str = AddString(&db2str->Str[locale], fields[1 + stringFieldNumInRecord].GetString())) + stringPool.push_back(str); + + ++stringFieldNumInRecord; + offset += sizeof(char*); + break; + } + } + } + + ASSERT(offset == recordSize); + } + else + TC_LOG_ERROR("sql.sql", "Hotfix locale table for storage %s references row that does not exist %u!", _storageName.c_str(), indexValue); + + } while (result->NextRow()); + + return; +} + +char* DB2SparseDatabaseLoader::AddString(char const** holder, std::string const& value) +{ + if (!value.empty()) + { + std::size_t existingLength = strlen(*holder); + if (existingLength >= value.length()) + { + // Reuse existing storage if there is enough space + char* str = const_cast<char*>(*holder); + memcpy(str, value.c_str(), value.length()); + str[value.length()] = '\0'; + return nullptr; + } + + char* str = new char[value.length() + 1]; + memcpy(str, value.c_str(), value.length()); + str[value.length()] = '\0'; + *holder = str; + return str; + } + + return nullptr; +} diff --git a/src/server/shared/DataStores/DB2SparseStorageLoader.h b/src/server/shared/DataStores/DB2SparseStorageLoader.h new file mode 100644 index 00000000000..13f31e12d56 --- /dev/null +++ b/src/server/shared/DataStores/DB2SparseStorageLoader.h @@ -0,0 +1,117 @@ +/* + * 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 DB2_SPARSE_FILE_LOADER_H +#define DB2_SPARSE_FILE_LOADER_H + +#include "Define.h" +#include "Utilities/ByteConverter.h" +#include "Implementation/HotfixDatabase.h" +#include <unordered_map> +#include <vector> + +class IndexTable +{ +public: + virtual void Insert(uint32 index, char* data) const = 0; + virtual char* Get(uint32 index) const = 0; +}; + +template<typename T> +class IndexTableAdapter : public IndexTable +{ +public: + IndexTableAdapter(std::unordered_map<uint32, T const*>& indexTable) : _indexTable(indexTable) { } + + void Insert(uint32 index, char* data) const override + { + _indexTable[index] = (T const*)data; + } + + char* Get(uint32 index) const override + { + auto itr = _indexTable.find(index); + if (itr != _indexTable.end()) + return (char*)itr->second; + return nullptr; + } + +private: + std::unordered_map<uint32, T const*>& _indexTable; +}; + +class DB2SparseFileLoader +{ + public: + DB2SparseFileLoader(); + ~DB2SparseFileLoader(); + + bool Load(const char *filename); + + uint32 GetNumRows() const { return recordCount; } + uint32 GetCols() const { return fieldCount; } + uint32 GetHash() const { return tableHash; } + bool IsLoaded() const { return (data != NULL); } + char* AutoProduceData(const char* fmt, IndexTable const& indexTable, uint32 locale, std::vector<char*>& stringPool); + char* AutoProduceStrings(const char* fmt, char* dataTable, uint32 locale); + static uint32 GetFormatRecordSize(const char * format); + static uint32 GetFormatStringFieldCount(const char * format); + static uint32 GetFormatLocalizedStringFieldCount(const char * format); +private: +#pragma pack(push, 1) + struct OffsetTableEntry + { + uint32 FileOffset; + uint16 RecordSize; + }; +#pragma pack(pop) + + char const* fileName; + + // WDB2 / WCH2 fields + uint32 recordSize; + uint32 recordCount; + uint32 fieldCount; + uint32 offsetsPos; + uint32 tableHash; + uint32 build; + uint32 unk1; + uint32 minIndex; + uint32 maxIndex; + uint32 localeMask; + uint32 copyIdSize; + uint32 metaFlags; + + uint32 dataStart; + unsigned char* data; + OffsetTableEntry* offsets; +}; + +class DB2SparseDatabaseLoader +{ +public: + explicit DB2SparseDatabaseLoader(std::string const& storageName) : _storageName(storageName) { } + + char* Load(const char* format, HotfixDatabaseStatements preparedStatement, IndexTable const& indexTable, std::vector<char*>& stringPool); + void LoadStrings(const char* format, HotfixDatabaseStatements preparedStatement, uint32 locale, IndexTable const& indexTable, std::vector<char*>& stringPool); + static char* AddString(char const** holder, std::string const& value); + +private: + std::string _storageName; +}; + +#endif diff --git a/src/server/shared/DataStores/DB2StorageLoader.cpp b/src/server/shared/DataStores/DB2StorageLoader.cpp index d2be433806f..d101d12a52e 100644 --- a/src/server/shared/DataStores/DB2StorageLoader.cpp +++ b/src/server/shared/DataStores/DB2StorageLoader.cpp @@ -23,22 +23,25 @@ DB2FileLoader::DB2FileLoader() { fileName = nullptr; + recordSize = 0; recordCount = 0; fieldCount = 0; stringSize = 0; - fieldsOffset = nullptr; - data = nullptr; - stringTable = nullptr; - tableHash = 0; build = 0; - unk1 = 0; minIndex = 0; maxIndex = 0; localeMask = 0; - unk5 = 0; + copyIdSize = 0; + + fieldsOffset = nullptr; + data = nullptr; + stringTable = nullptr; + idTable = nullptr; + idTableSize = 0; + copyTable = nullptr; } bool DB2FileLoader::Load(const char *filename, const char *fmt) @@ -55,7 +58,7 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) fileName = filename; uint32 header; - if (fread(&header, 4, 1, f) != 1) // Signature + if (fread(&header, 4, 1, f) != 1) // Signature { fclose(f); return false; @@ -63,13 +66,13 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) EndianConvert(header); - if (header != 0x32424457) + if (header != 0x34424457) { fclose(f); - return false; //'WDB2' + return false; //'WDB4' } - if (fread(&recordCount, 4, 1, f) != 1) // Number of records + if (fread(&recordCount, 4, 1, f) != 1) // Number of records { fclose(f); return false; @@ -77,7 +80,7 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) EndianConvert(recordCount); - if (fread(&fieldCount, 4, 1, f) != 1) // Number of fields + if (fread(&fieldCount, 4, 1, f) != 1) // Number of fields { fclose(f); return false; @@ -85,7 +88,7 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) EndianConvert(fieldCount); - if (fread(&recordSize, 4, 1, f) != 1) // Size of a record + if (fread(&recordSize, 4, 1, f) != 1) // Size of a record { fclose(f); return false; @@ -93,7 +96,7 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) EndianConvert(recordSize); - if (fread(&stringSize, 4, 1, f) != 1) // String size + if (fread(&stringSize, 4, 1, f) != 1) // String size { fclose(f); return false; @@ -101,8 +104,7 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) EndianConvert(stringSize); - /* NEW WDB2 FIELDS*/ - if (fread(&tableHash, 4, 1, f) != 1) // Table hash + if (fread(&tableHash, 4, 1, f) != 1) // Table hash { fclose(f); return false; @@ -110,7 +112,7 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) EndianConvert(tableHash); - if (fread(&build, 4, 1, f) != 1) // Build + if (fread(&build, 4, 1, f) != 1) // Build { fclose(f); return false; @@ -118,7 +120,7 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) EndianConvert(build); - if (fread(&unk1, 4, 1, f) != 1) // Unknown WDB2 + if (fread(&unk1, 4, 1, f) != 1) // Unknown WDB2 { fclose(f); return false; @@ -126,7 +128,7 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) EndianConvert(unk1); - if (fread(&minIndex, 4, 1, f) != 1) // MinIndex WDB2 + if (fread(&minIndex, 4, 1, f) != 1) // MinIndex WDB2 { fclose(f); return false; @@ -134,7 +136,7 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) EndianConvert(minIndex); - if (fread(&maxIndex, 4, 1, f) != 1) // MaxIndex WDB2 + if (fread(&maxIndex, 4, 1, f) != 1) // MaxIndex WDB2 { fclose(f); return false; @@ -142,7 +144,7 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) EndianConvert(maxIndex); - if (fread(&localeMask, 4, 1, f) != 1) // Locales + if (fread(&localeMask, 4, 1, f) != 1) // Locales { fclose(f); return false; @@ -150,18 +152,26 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) EndianConvert(localeMask); - if (fread(&unk5, 4, 1, f) != 1) // Unknown WDB2 + if (fread(©IdSize, 4, 1, f) != 1) { fclose(f); return false; } - EndianConvert(unk5); + EndianConvert(copyIdSize); - if (maxIndex != 0) + if (fread(&metaFlags, 4, 1, f) != 1) { - int32 diff = maxIndex - minIndex + 1; - fseek(f, diff * 4 + diff * 2, SEEK_CUR); // diff * 4: an index for rows, diff * 2: a memory allocation bank + fclose(f); + return false; + } + + EndianConvert(metaFlags); + + if (fmt[0] == FT_SORT) + { + idTableSize = recordCount * sizeof(uint32); + ++fmt; } fieldsOffset = new uint32[fieldCount]; @@ -173,6 +183,8 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) fieldsOffset[i] += 1; else if (fmt[i - 1] == FT_LONG) fieldsOffset[i] += 8; + else if (fmt[i - 1] == FT_SHORT) + fieldsOffset[i] += 2; else // 4 byte fields (int32/float/strings) fieldsOffset[i] += 4; } @@ -186,29 +198,49 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) return false; } + if (idTableSize) + { + idTable = new unsigned char[idTableSize]; + if (fread(idTable, idTableSize, 1, f) != 1) + { + fclose(f); + return false; + } + } + + if (copyIdSize) + { + copyTable = new unsigned char[copyIdSize]; + if (fread(copyTable, copyIdSize, 1, f) != 1) + { + fclose(f); + return false; + } + } + fclose(f); return true; } DB2FileLoader::~DB2FileLoader() { - if (data) - delete [] data; - if (fieldsOffset) - delete [] fieldsOffset; + delete[] data; + delete[] idTable; + delete[] copyTable; + delete[] fieldsOffset; } DB2FileLoader::Record DB2FileLoader::getRecord(size_t id) { assert(data); - return Record(*this, data + id*recordSize); + return Record(*this, data + id * recordSize); } uint32 DB2FileLoader::GetFormatRecordSize(const char * format, int32* index_pos) { uint32 recordsize = 0; int32 i = -1; - for (uint32 x=0; format[x]; ++x) + for (uint32 x = 0; format[x]; ++x) { switch (format[x]) { @@ -233,6 +265,9 @@ uint32 DB2FileLoader::GetFormatRecordSize(const char * format, int32* index_pos) case FT_LONG: recordsize += 8; break; + case FT_SHORT: + recordsize += 2; + break; } } @@ -264,8 +299,8 @@ uint32 DB2FileLoader::GetFormatLocalizedStringFieldCount(char const* format) char* DB2FileLoader::AutoProduceData(const char* format, uint32& records, char**& indexTable) { - typedef char * ptr; - if (strlen(format) != fieldCount) + typedef char* ptr; + if (strlen(format) != fieldCount + (format[0] == FT_SORT ? 1 : 0)) return NULL; //get struct size and index pos @@ -276,9 +311,28 @@ char* DB2FileLoader::AutoProduceData(const char* format, uint32& records, char** { uint32 maxi = 0; //find max index - for (uint32 y = 0; y < recordCount; y++) + if (!idTableSize) + { + for (uint32 y = 0; y < recordCount; ++y) + { + uint32 ind = getRecord(y).getUInt(indexField); + if (ind > maxi) + maxi = ind; + } + } + else { - uint32 ind = getRecord(y).getUInt(indexField); + ASSERT(indexField == 0); + for (uint32 y = 0; y < recordCount; ++y) + { + uint32 ind = ((uint32*)idTable)[y]; + if (ind > maxi) + maxi = ind; + } + } + for (uint32 y = 0; y < copyIdSize; y += 8) + { + uint32 ind = *((uint32*)(copyTable + y)); if (ind > maxi) maxi = ind; } @@ -290,18 +344,25 @@ char* DB2FileLoader::AutoProduceData(const char* format, uint32& records, char** } else { + ASSERT(!copyIdSize, "Storage %s uses id copy table - must be indexed!", fileName); records = recordCount; indexTable = new ptr[recordCount]; } - char* dataTable = new char[recordCount * recordsize]; + char* dataTable = new char[(recordCount + (copyIdSize / 8)) * recordsize]; uint32 offset = 0; + if (idTableSize) + { + ASSERT(format[0] == 'd'); + ++format; + } + for (uint32 y = 0; y < recordCount; y++) { if (indexField >= 0) - indexTable[getRecord(y).getUInt(indexField)] = &dataTable[offset]; + indexTable[!idTableSize ? getRecord(y).getUInt(indexField) : ((uint32*)idTable)[y]] = &dataTable[offset]; else indexTable[y] = &dataTable[offset]; @@ -326,6 +387,10 @@ char* DB2FileLoader::AutoProduceData(const char* format, uint32& records, char** *((uint64*)(&dataTable[offset])) = getRecord(y).getUInt64(x); offset += 8; break; + case FT_SHORT: + *((uint16*)(&dataTable[offset])) = getRecord(y).getUInt16(x); + offset += 2; + break; case FT_STRING: case FT_STRING_NOT_LOCALIZED: *((char**)(&dataTable[offset])) = nullptr; // will be replaces non-empty or "" strings in AutoProduceStrings @@ -335,6 +400,20 @@ char* DB2FileLoader::AutoProduceData(const char* format, uint32& records, char** } } + uint32* copyIds = (uint32*)copyTable; + for (uint32 c = 0; c < copyIdSize / 4; c += 2) + { + uint32 to = copyIds[c]; + uint32 from = copyIds[c + 1]; + + if (from && from < records && indexTable[from]) + { + indexTable[to] = &dataTable[offset]; + memcpy(indexTable[to], indexTable[from], recordsize); + offset += recordsize; + } + } + return dataTable; } @@ -342,7 +421,7 @@ static char const* const nullStr = ""; char* DB2FileLoader::AutoProduceStringsArrayHolders(const char* format, char* dataTable) { - if (strlen(format) != fieldCount) + if (strlen(format) != fieldCount + (format[0] == FT_SORT ? 1 : 0)) return nullptr; // we store flat holders pool as single memory block @@ -365,6 +444,9 @@ char* DB2FileLoader::AutoProduceStringsArrayHolders(const char* format, char* da uint32 offset = 0; + if (idTableSize) + ++format; + // assign string holders to string field slots for (uint32 y = 0; y < recordCount; y++) { @@ -385,6 +467,9 @@ char* DB2FileLoader::AutoProduceStringsArrayHolders(const char* format, char* da case FT_LONG: offset += 8; break; + case FT_SHORT: + offset += 2; + break; case FT_STRING: case FT_STRING_NOT_LOCALIZED: { @@ -411,7 +496,7 @@ char* DB2FileLoader::AutoProduceStringsArrayHolders(const char* format, char* da char* DB2FileLoader::AutoProduceStrings(const char* format, char* dataTable, uint32 locale) { - if (strlen(format) != fieldCount) + if (strlen(format) != fieldCount + (format[0] == FT_SORT ? 1 : 0)) return nullptr; if (!(localeMask & (1 << locale))) @@ -436,6 +521,9 @@ char* DB2FileLoader::AutoProduceStrings(const char* format, char* dataTable, uin uint32 offset = 0; + if (idTableSize) + ++format; + for (uint32 y = 0; y < recordCount; y++) { for (uint32 x = 0; x < fieldCount; x++) @@ -453,6 +541,9 @@ char* DB2FileLoader::AutoProduceStrings(const char* format, char* dataTable, uin case FT_LONG: offset += 8; break; + case FT_SHORT: + offset += 2; + break; case FT_STRING: { // fill only not filled entries @@ -481,7 +572,7 @@ char* DB2FileLoader::AutoProduceStrings(const char* format, char* dataTable, uin return stringPool; } -char* DB2DatabaseLoader::Load(const char* format, HotfixDatabaseStatements preparedStatement, uint32& records, char**& indexTable, char*& stringHolders, std::list<char*>& stringPool) +char* DB2DatabaseLoader::Load(const char* format, HotfixDatabaseStatements preparedStatement, uint32& records, char**& indexTable, char*& stringHolders, std::vector<char*>& stringPool) { // Even though this query is executed only once, prepared statement is used to send data from mysql server in binary format PreparedQueryResult result = HotfixDatabase.Query(HotfixDatabase.GetPreparedStatement(preparedStatement)); @@ -582,6 +673,10 @@ char* DB2DatabaseLoader::Load(const char* format, HotfixDatabaseStatements prepa *((int64*)(&dataValue[offset])) = fields[f].GetInt64(); offset += 8; break; + case FT_SHORT: + *((int16*)(&dataValue[offset])) = fields[f].GetInt16(); + offset += 2; + break; case FT_STRING: { LocalizedString** slot = (LocalizedString**)(&dataValue[offset]); @@ -640,7 +735,7 @@ char* DB2DatabaseLoader::Load(const char* format, HotfixDatabaseStatements prepa return dataTable; } -void DB2DatabaseLoader::LoadStrings(const char* format, HotfixDatabaseStatements preparedStatement, uint32 locale, char**& indexTable, std::list<char*>& stringPool) +void DB2DatabaseLoader::LoadStrings(const char* format, HotfixDatabaseStatements preparedStatement, uint32 locale, char**& indexTable, std::vector<char*>& stringPool) { PreparedStatement* stmt = HotfixDatabase.GetPreparedStatement(preparedStatement); stmt->setString(0, localeNames[locale]); @@ -682,6 +777,9 @@ void DB2DatabaseLoader::LoadStrings(const char* format, HotfixDatabaseStatements case FT_LONG: offset += 8; break; + case FT_SHORT: + offset += 2; + break; case FT_STRING: { // fill only not filled entries diff --git a/src/server/shared/DataStores/DB2StorageLoader.h b/src/server/shared/DataStores/DB2StorageLoader.h index 41705c67f19..67525779ef5 100644 --- a/src/server/shared/DataStores/DB2StorageLoader.h +++ b/src/server/shared/DataStores/DB2StorageLoader.h @@ -22,7 +22,7 @@ #include "Utilities/ByteConverter.h" #include "Implementation/HotfixDatabase.h" #include <cassert> -#include <list> +#include <vector> class TC_SHARED_API DB2FileLoader { @@ -61,6 +61,13 @@ class TC_SHARED_API DB2FileLoader EndianConvert(val); return val; } + uint16 getUInt16(size_t field) const + { + assert(field < file.fieldCount); + uint16 val = *reinterpret_cast<uint16*>(offset + file.GetOffset(field)); + EndianConvert(val); + return val; + } const char *getString(size_t field) const { assert(field < file.fieldCount); @@ -95,23 +102,26 @@ class TC_SHARED_API DB2FileLoader private: char const* fileName; + // WDB2 / WCH2 fields uint32 recordSize; uint32 recordCount; uint32 fieldCount; uint32 stringSize; - uint32 *fieldsOffset; - unsigned char *data; - unsigned char *stringTable; + uint32 tableHash; + uint32 build; + uint32 unk1; + uint32 minIndex; + uint32 maxIndex; + uint32 localeMask; + uint32 copyIdSize; + uint32 metaFlags; - // WDB2 / WCH2 fields - uint32 tableHash; // WDB2 - uint32 build; // WDB2 - - int unk1; // WDB2 (Unix time in WCH2) - int minIndex; // WDB2 - int maxIndex; // WDB2 (index table) - int localeMask; // WDB2 - int unk5; // WDB2 + uint32 *fieldsOffset; + unsigned char* data; + unsigned char* stringTable; + unsigned char* idTable; + uint32 idTableSize; + unsigned char* copyTable; }; class TC_SHARED_API DB2DatabaseLoader @@ -119,8 +129,8 @@ class TC_SHARED_API DB2DatabaseLoader public: explicit DB2DatabaseLoader(std::string const& storageName) : _storageName(storageName) { } - char* Load(const char* format, HotfixDatabaseStatements preparedStatement, uint32& records, char**& indexTable, char*& stringHolders, std::list<char*>& stringPool); - void LoadStrings(const char* format, HotfixDatabaseStatements preparedStatement, uint32 locale, char**& indexTable, std::list<char*>& stringPool); + char* Load(const char* format, HotfixDatabaseStatements preparedStatement, uint32& records, char**& indexTable, char*& stringHolders, std::vector<char*>& stringPool); + void LoadStrings(const char* format, HotfixDatabaseStatements preparedStatement, uint32 locale, char**& indexTable, std::vector<char*>& stringPool); static char* AddString(char const** holder, std::string const& value); private: diff --git a/src/server/shared/DataStores/DB2Store.h b/src/server/shared/DataStores/DB2Store.h index 72271ce507b..570e8174a39 100644 --- a/src/server/shared/DataStores/DB2Store.h +++ b/src/server/shared/DataStores/DB2Store.h @@ -20,6 +20,7 @@ #include "Common.h" #include "DB2StorageLoader.h" +#include "DB2SparseStorageLoader.h" #include "DBStorageIterator.h" #include "ByteBuffer.h" @@ -27,7 +28,16 @@ class DB2StorageBase { public: - virtual ~DB2StorageBase() { } + DB2StorageBase(char const* fileName, char const* format, HotfixDatabaseStatements preparedStmtIndex) + : _tableHash(0), _fileName(fileName), _fieldCount(0), _format(format), _dataTable(nullptr), _dataTableEx(nullptr), _hotfixStatement(preparedStmtIndex) { } + + virtual ~DB2StorageBase() + { + delete[] reinterpret_cast<char*>(_dataTable); + delete[] reinterpret_cast<char*>(_dataTableEx); + for (char* strings : _stringPool) + delete[] strings; + } uint32 GetHash() const { return _tableHash; } @@ -37,39 +47,20 @@ public: virtual void EraseRecord(uint32 id) = 0; -protected: - uint32 _tableHash; -}; + std::string const& GetFileName() const { return _fileName; } -template<class T> -class DB2Storage : public DB2StorageBase -{ - typedef std::list<char*> StringPoolList; -public: - typedef DBStorageIterator<T> iterator; + uint32 GetFieldCount() const { return _fieldCount; } - DB2Storage(char const* fileName, char const* format, HotfixDatabaseStatements preparedStmtIndex) - : _fileName(fileName), _indexTableSize(0), _fieldCount(0), _format(format), _dataTable(nullptr), _dataTableEx(nullptr), _hotfixStatement(preparedStmtIndex) - { - _indexTable.AsT = NULL; - } + char const* GetFormat() const { return _format; } - ~DB2Storage() - { - delete[] reinterpret_cast<char*>(_indexTable.AsT); - delete[] reinterpret_cast<char*>(_dataTable); - delete[] reinterpret_cast<char*>(_dataTableEx); - for (char* stringPool : _stringPoolList) - delete[] stringPool; - } + virtual bool Load(std::string const& path, uint32 locale) = 0; + virtual bool LoadStringsFrom(std::string const& path, uint32 locale) = 0; + virtual void LoadFromDB() = 0; + virtual void LoadStringsFromDB(uint32 locale) = 0; - bool HasRecord(uint32 id) const override { return id < _indexTableSize && _indexTable.AsT[id] != nullptr; } - void WriteRecord(uint32 id, uint32 locale, ByteBuffer& buffer) const override +protected: + void WriteRecordData(char const* entry, uint32 locale, ByteBuffer& buffer) const { - ASSERT(id < _indexTableSize); - char const* entry = _indexTable.AsChar[id]; - ASSERT(entry); - std::size_t fields = strlen(_format); for (uint32 i = 0; i < fields; ++i) { @@ -122,16 +113,48 @@ public: } } + uint32 _tableHash; + std::string _fileName; + uint32 _fieldCount; + char const* _format; + char* _dataTable; + char* _dataTableEx; + std::vector<char*> _stringPool; + HotfixDatabaseStatements _hotfixStatement; +}; + +template<class T> +class DB2Storage : public DB2StorageBase +{ + static_assert(std::is_standard_layout<T>::value, "T in DB2Storage must have standard layout."); + +public: + typedef DBStorageIterator<T> iterator; + + DB2Storage(char const* fileName, char const* format, HotfixDatabaseStatements preparedStmtIndex) : DB2StorageBase(fileName, format, preparedStmtIndex), + _indexTableSize(0) + { + _indexTable.AsT = NULL; + } + + ~DB2Storage() + { + delete[] reinterpret_cast<char*>(_indexTable.AsT); + } + + bool HasRecord(uint32 id) const override { return id < _indexTableSize && _indexTable.AsT[id] != nullptr; } + void WriteRecord(uint32 id, uint32 locale, ByteBuffer& buffer) const override + { + WriteRecordData(reinterpret_cast<char const*>(AssertEntry(id)), locale, buffer); + } + void EraseRecord(uint32 id) override { if (id < _indexTableSize) _indexTable.AsT[id] = nullptr; } T const* LookupEntry(uint32 id) const { return (id >= _indexTableSize) ? nullptr : _indexTable.AsT[id]; } T const* AssertEntry(uint32 id) const { return ASSERT_NOTNULL(LookupEntry(id)); } - std::string const& GetFileName() const { return _fileName; } uint32 GetNumRows() const { return _indexTableSize; } - char const* GetFormat() const { return _format; } - uint32 GetFieldCount() const { return _fieldCount; } - bool Load(std::string const& path, uint32 locale) + bool Load(std::string const& path, uint32 locale) override { DB2FileLoader db2; // Check if load was successful, only then continue @@ -142,23 +165,23 @@ public: _tableHash = db2.GetHash(); // load raw non-string data - _dataTable = reinterpret_cast<T*>(db2.AutoProduceData(_format, _indexTableSize, _indexTable.AsChar)); + _dataTable = db2.AutoProduceData(_format, _indexTableSize, _indexTable.AsChar); // create string holders for loaded string fields - if (char* stringHolders = db2.AutoProduceStringsArrayHolders(_format, (char*)_dataTable)) + if (char* stringHolders = db2.AutoProduceStringsArrayHolders(_format, _dataTable)) { - _stringPoolList.push_back(stringHolders); + _stringPool.push_back(stringHolders); // load strings from db2 data - if (char* stringBlock = db2.AutoProduceStrings(_format, (char*)_dataTable, locale)) - _stringPoolList.push_back(stringBlock); + if (char* stringBlock = db2.AutoProduceStrings(_format, _dataTable, locale)) + _stringPool.push_back(stringBlock); } // error in db2 file at loading if NULL return _indexTable.AsT != NULL; } - bool LoadStringsFrom(std::string const& path, uint32 locale) + bool LoadStringsFrom(std::string const& path, uint32 locale) override { // DB2 must be already loaded using Load if (!_indexTable.AsT) @@ -171,54 +194,129 @@ public: // load strings from another locale db2 data if (DB2FileLoader::GetFormatLocalizedStringFieldCount(_format)) - if (char* stringBlock = db2.AutoProduceStrings(_format, (char*)_dataTable, locale)) - _stringPoolList.push_back(stringBlock); + if (char* stringBlock = db2.AutoProduceStrings(_format, _dataTable, locale)) + _stringPool.push_back(stringBlock); return true; } - void LoadFromDB() + void LoadFromDB() override { char* extraStringHolders = nullptr; - if (char* dataTable = DB2DatabaseLoader(_fileName).Load(_format, _hotfixStatement, _indexTableSize, _indexTable.AsChar, extraStringHolders, _stringPoolList)) - _dataTableEx = reinterpret_cast<T*>(dataTable); - + _dataTableEx = DB2DatabaseLoader(_fileName).Load(_format, _hotfixStatement, _indexTableSize, _indexTable.AsChar, extraStringHolders, _stringPool); if (extraStringHolders) - _stringPoolList.push_back(extraStringHolders); + _stringPool.push_back(extraStringHolders); } - void LoadStringsFromDB(uint32 locale) + void LoadStringsFromDB(uint32 locale) override { if (!DB2FileLoader::GetFormatLocalizedStringFieldCount(_format)) return; - DB2DatabaseLoader(_fileName).LoadStrings(_format, HotfixDatabaseStatements(_hotfixStatement + 1), locale, _indexTable.AsChar, _stringPoolList); - } - - typedef bool(*SortFunc)(T const* left, T const* right); - - void Sort(SortFunc pred) - { - ASSERT(strpbrk(_format, "nd") == nullptr, "Only non-indexed storages can be sorted"); - std::sort(_indexTable.AsT, _indexTable.AsT + _indexTableSize, pred); + DB2DatabaseLoader(_fileName).LoadStrings(_format, HotfixDatabaseStatements(_hotfixStatement + 1), locale, _indexTable.AsChar, _stringPool); } iterator begin() { return iterator(_indexTable.AsT, _indexTableSize); } iterator end() { return iterator(_indexTable.AsT, _indexTableSize, _indexTableSize); } private: - std::string _fileName; - uint32 _indexTableSize; - uint32 _fieldCount; - char const* _format; union { T** AsT; char** AsChar; } _indexTable; - T* _dataTable; - T* _dataTableEx; - StringPoolList _stringPoolList; - HotfixDatabaseStatements _hotfixStatement; + uint32 _indexTableSize; +}; + +template<class T> +class DB2SparseStorage : public DB2StorageBase +{ + static_assert(std::is_pod<T>::value, "T in DB2SparseStorage must be POD-type."); + +public: + typedef typename std::unordered_map<uint32, T const*>::const_iterator iterator; + + DB2SparseStorage(char const* fileName, char const* format, HotfixDatabaseStatements preparedStmtIndex) + : DB2StorageBase(fileName, format, preparedStmtIndex) + { + } + + ~DB2SparseStorage() + { + } + + bool HasRecord(uint32 id) const override { return _indexTable.count(id) > 0; } + void WriteRecord(uint32 id, uint32 locale, ByteBuffer& buffer) const override + { + WriteRecordData(reinterpret_cast<char const*>(AssertEntry(id)), locale, buffer); + } + + void EraseRecord(uint32 id) override { _indexTable.erase(id); } + + T const* LookupEntry(uint32 id) const + { + auto itr = _indexTable.find(id); + if (itr != _indexTable.end()) + return itr->second; + return nullptr; + } + + T const* AssertEntry(uint32 id) const { return ASSERT_NOTNULL(LookupEntry(id)); } + + uint32 GetNumRows() const { return _indexTable.size(); } + + bool Load(std::string const& path, uint32 locale) override + { + DB2SparseFileLoader db2; + // Check if load was successful, only then continue + if (!db2.Load((path + _fileName).c_str())) + return false; + + _fieldCount = db2.GetCols(); + _tableHash = db2.GetHash(); + + // load raw non-string data + _dataTable = db2.AutoProduceData(_format, IndexTableAdapter<T>(_indexTable), locale, _stringPool); + + // error in db2 file at loading if NULL + return !_indexTable.empty(); + } + + bool LoadStringsFrom(std::string const& path, uint32 locale) override + { + // DB2 must be already loaded using Load + if (_indexTable.empty()) + return false; + + DB2SparseFileLoader db2; + // Check if load was successful, only then continue + if (!db2.Load((path + _fileName).c_str())) + return false; + + // load strings from another locale db2 data + if (DB2SparseFileLoader::GetFormatLocalizedStringFieldCount(_format)) + if (char* stringBlock = db2.AutoProduceStrings(_format, _dataTable, locale)) + _stringPool.push_back(stringBlock); + return true; + } + + void LoadFromDB() override + { + _dataTableEx = DB2SparseDatabaseLoader(_fileName).Load(_format, _hotfixStatement, IndexTableAdapter<T>(_indexTable), _stringPool); + } + + void LoadStringsFromDB(uint32 locale) override + { + if (!DB2SparseFileLoader::GetFormatLocalizedStringFieldCount(_format)) + return; + + DB2SparseDatabaseLoader(_fileName).LoadStrings(_format, HotfixDatabaseStatements(_hotfixStatement + 1), locale, IndexTableAdapter<T>(_indexTable), _stringPool); + } + + iterator begin() const { return _indexTable.begin(); } + iterator end() const { return _indexTable.end(); } + +private: + std::unordered_map<uint32, T const*> _indexTable; }; #endif diff --git a/src/server/shared/DataStores/DBCFileLoader.cpp b/src/server/shared/DataStores/DBCFileLoader.cpp index 829c3708221..ec3a95f74b9 100644 --- a/src/server/shared/DataStores/DBCFileLoader.cpp +++ b/src/server/shared/DataStores/DBCFileLoader.cpp @@ -172,17 +172,6 @@ uint32 DBCFileLoader::GetFormatRecordSize(const char* format, int32* index_pos) char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char**& indexTable, uint32 sqlRecordCount, uint32 sqlHighestIndex, char*& sqlDataTable) { - /* - format STRING, NA, FLOAT, NA, INT <=> - struct{ - char* field0, - float field1, - int field2 - }entry; - - this func will generate entry[rows] data; - */ - typedef char* ptr; if (strlen(format) != fieldCount) return NULL; @@ -193,11 +182,11 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char** if (i >= 0) { - uint32 maxi = 0; + int32 maxi = 0; //find max index for (uint32 y = 0; y < recordCount; ++y) { - uint32 ind = getRecord(y).getUInt(i); + int32 ind = int32(getRecord(y).getUInt(i)); if (ind > maxi) maxi = ind; } @@ -224,7 +213,10 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char** for (uint32 y = 0; y < recordCount; ++y) { if (i >= 0) - indexTable[getRecord(y).getUInt(i)] = &dataTable[offset]; + { + if (int32(getRecord(y).getUInt(i)) >= 0) + indexTable[getRecord(y).getUInt(i)] = &dataTable[offset]; + } else indexTable[y] = &dataTable[offset]; diff --git a/src/server/shared/DataStores/DBStorageIterator.h b/src/server/shared/DataStores/DBStorageIterator.h index 8148a2a5300..8e7d03f2756 100644 --- a/src/server/shared/DataStores/DBStorageIterator.h +++ b/src/server/shared/DataStores/DBStorageIterator.h @@ -35,8 +35,11 @@ public: } } - T* operator->() { return _index[_pos]; } - T* operator*() { return _index[_pos]; } + uint32 Key() const { return _pos; } + T const* Value() const { return _index[_pos]; } + + T const* operator->() { return _index[_pos]; } + T const* operator*() { return _index[_pos]; } bool operator==(DBStorageIterator const& right) const { /*ASSERT(_index == right._index, "Iterator belongs to a different container")*/ return _pos == right._pos; } bool operator!=(DBStorageIterator const& right) const { return !(*this == right); } |
