diff options
Diffstat (limited to 'src/server/shared')
| -rw-r--r-- | src/server/shared/DataStores/DB2Meta.cpp | 101 | ||||
| -rw-r--r-- | src/server/shared/DataStores/DB2Meta.h | 47 | ||||
| -rw-r--r-- | src/server/shared/DataStores/DB2SparseStorageLoader.cpp | 708 | ||||
| -rw-r--r-- | src/server/shared/DataStores/DB2SparseStorageLoader.h | 123 | ||||
| -rw-r--r-- | src/server/shared/DataStores/DB2StorageLoader.cpp | 648 | ||||
| -rw-r--r-- | src/server/shared/DataStores/DB2StorageLoader.h | 159 | ||||
| -rw-r--r-- | src/server/shared/DataStores/DB2Store.h | 315 | ||||
| -rw-r--r-- | src/server/shared/DataStores/DBCFileLoader.cpp | 325 | ||||
| -rw-r--r-- | src/server/shared/DataStores/DBCFileLoader.h | 105 | ||||
| -rw-r--r-- | src/server/shared/DataStores/DBCStore.h | 325 | ||||
| -rw-r--r-- | src/server/shared/DataStores/DBStorageIterator.h | 4 |
11 files changed, 1649 insertions, 1211 deletions
diff --git a/src/server/shared/DataStores/DB2Meta.cpp b/src/server/shared/DataStores/DB2Meta.cpp new file mode 100644 index 00000000000..bf119b52150 --- /dev/null +++ b/src/server/shared/DataStores/DB2Meta.cpp @@ -0,0 +1,101 @@ +/* + * 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 "DB2Meta.h" + +DB2Meta::DB2Meta(int32 indexField, uint32 fieldCount, uint32 layoutHash, char const* types, uint8 const* arraySizes) + : IndexField(indexField), FieldCount(fieldCount), LayoutHash(layoutHash), Types(types), ArraySizes(arraySizes) +{ +} + +bool DB2Meta::HasIndexFieldInData() const +{ + return IndexField != -1; +} + +uint32 DB2Meta::GetIndexField() const +{ + return IndexField == -1 ? 0 : uint32(IndexField); +} + +uint32 DB2Meta::GetRecordSize() const +{ + uint32 size = 0; + for (uint32 i = 0; i < FieldCount; ++i) + { + for (uint8 j = 0; j < ArraySizes[i]; ++j) + { + switch (Types[i]) + { + case FT_BYTE: + size += 1; + break; + case FT_SHORT: + size += 2; + break; + case FT_FLOAT: + case FT_INT: + size += 4; + break; + case FT_STRING: + case FT_STRING_NOT_LOCALIZED: + size += sizeof(char*); + break; + } + } + } + + if (!HasIndexFieldInData()) + size += 4; + + return size; +} + +uint32 DB2Meta::GetDbIndexField() const +{ + if (IndexField == -1) + return 0; + + uint32 index = 0; + for (uint32 i = 0; i < FieldCount && i < uint32(IndexField); ++i) + index += ArraySizes[i]; + + return index; +} + +uint32 DB2Meta::GetDbFieldCount() const +{ + uint32 fields = 0; + for (uint32 i = 0; i < FieldCount; ++i) + fields += ArraySizes[i]; + + if (!HasIndexFieldInData()) + ++fields; + + return fields; +} + +uint32 DB2Meta::GetStringFieldCount(bool localizedOnly) const +{ + uint32 stringFields = 0; + for (uint32 i = 0; i < FieldCount; ++i) + if (Types[i] == FT_STRING || (Types[i] == FT_STRING_NOT_LOCALIZED && !localizedOnly)) + for (uint8 j = 0; j < ArraySizes[i]; ++j) + ++stringFields; + + return stringFields; +} diff --git a/src/server/shared/DataStores/DB2Meta.h b/src/server/shared/DataStores/DB2Meta.h new file mode 100644 index 00000000000..2c165b52d86 --- /dev/null +++ b/src/server/shared/DataStores/DB2Meta.h @@ -0,0 +1,47 @@ +/* + * 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 DB2Meta_h__ +#define DB2Meta_h__ + +#include "Define.h" + +struct DB2Meta +{ + DB2Meta(int32 indexField, uint32 fieldCount, uint32 layoutHash, char const* types, uint8 const* arraySizes); + + bool HasIndexFieldInData() const; + + // Returns field index for data loaded in our structures (ID field is appended in the front if not present in db2 file data section) + uint32 GetIndexField() const; + + // Returns size of final loaded structure + uint32 GetRecordSize() const; + + uint32 GetDbIndexField() const; + uint32 GetDbFieldCount() const; + + uint32 GetStringFieldCount(bool localizedOnly) const; + + int32 IndexField; + uint32 FieldCount; + uint32 LayoutHash; + char const* Types; + uint8 const* ArraySizes; +}; + +#endif // DB2Meta_h__ diff --git a/src/server/shared/DataStores/DB2SparseStorageLoader.cpp b/src/server/shared/DataStores/DB2SparseStorageLoader.cpp new file mode 100644 index 00000000000..c6e1c495b00 --- /dev/null +++ b/src/server/shared/DataStores/DB2SparseStorageLoader.cpp @@ -0,0 +1,708 @@ +/* + * 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 "DatabaseEnv.h" +#include "Log.h" + +DB2SparseFileLoader::DB2SparseFileLoader() +{ + fileName = nullptr; + meta = nullptr; + + recordCount = 0; + fieldCount = 0; + recordSize = 0; + offsetsPos = 0; + tableHash = 0; + layoutHash = 0; + minIndex = 0; + maxIndex = 0; + localeMask = 0; + copyIdSize = 0; + + dataStart = 0; + data = nullptr; + offsets = nullptr; + fields = nullptr; +} + +bool DB2SparseFileLoader::Load(const char *filename, DB2Meta const* meta) +{ + if (data) + { + delete[] data; + data = nullptr; + } + + FILE* f = fopen(filename, "rb"); + if (!f) + return false; + + fileName = filename; + this->meta = meta; + uint32 header; + if (fread(&header, 4, 1, f) != 1) // Signature + { + fclose(f); + return false; + } + + EndianConvert(header); + + if (header != 0x35424457) + { + fclose(f); + return false; //'WDB5' + } + + 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(&layoutHash, 4, 1, f) != 1) // Layout hash + { + fclose(f); + return false; + } + + EndianConvert(layoutHash); + + if (layoutHash != meta->LayoutHash) + { + fclose(f); + return false; + } + + 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); + + fields = new FieldEntry[fieldCount]; + if (fread(fields, fieldCount * sizeof(FieldEntry), 1, f) != 1) + { + fclose(f); + return false; + } + + 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; + delete[] fields; +} + +static char const* const nullStr = ""; + +char* DB2SparseFileLoader::AutoProduceData(IndexTable const& indexTable, uint32 locale, std::vector<char*>& stringPool) +{ + typedef char* ptr; + if (meta->FieldCount != fieldCount) + return NULL; + + //get struct size and index pos + uint32 recordsize = meta->GetRecordSize(); + + 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 = meta->GetStringFieldCount(false); + std::size_t localizedStringFields = meta->GetStringFieldCount(true); + + // each string field at load have array of string for each locale + std::size_t stringHoldersRecordPoolSize = localizedStringFields * sizeof(LocalizedString) + (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 - (!meta->HasIndexFieldInData() ? 4 : 0)) - stringFields * sizeof(char*))]; + memset(stringTable, 0, expandedDataSize - records * ((recordsize - (!meta->HasIndexFieldInData() ? 4 : 0)) - 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; + + if (!meta->HasIndexFieldInData()) + { + *((uint32*)(&dataTable[offset])) = y + minIndex; + offset += 4; + } + + for (uint32 x = 0; x < fieldCount; ++x) + { + uint16 fieldBytes = 4 - fields[x].UnusedBits / 8; + for (uint32 z = 0; z < meta->ArraySizes[x]; ++z) + { + switch (meta->Types[x]) + { + case FT_FLOAT: + { + float val = *reinterpret_cast<float*>(&data[offsets[y].FileOffset - dataStart + fieldOffset]); + EndianConvert(val); + *((float*)(&dataTable[offset])) = val; + offset += 4; + fieldOffset += 4; + break; + } + case FT_INT: + { + ASSERT(fieldBytes && fieldBytes <= 4); + uint32 val; + switch (fieldBytes) + { + case 1: + val = *reinterpret_cast<uint8*>(&data[offsets[y].FileOffset - dataStart + fieldOffset]); + break; + case 2: + { + uint16 val16 = *reinterpret_cast<uint16*>(&data[offsets[y].FileOffset - dataStart + fieldOffset]); + EndianConvert(val16); + val = val16; + break; + } + case 3: + { +#pragma pack(push, 1) + struct dbcint24 { uint8 v[3]; }; +#pragma pack(pop) + dbcint24 i24v = *reinterpret_cast<dbcint24*>(&data[offsets[y].FileOffset - dataStart + fieldOffset]); + EndianConvert(i24v); + val = uint32(i24v.v[0]) | (uint32(i24v.v[1]) << 8) | (uint32(i24v.v[2]) << 16); + break; + } + case 4: + val = *reinterpret_cast<uint32*>(&data[offsets[y].FileOffset - dataStart + fieldOffset]); + EndianConvert(val); + break; + default: + break; + } + *((uint32*)(&dataTable[offset])) = val; + offset += 4; + fieldOffset += fieldBytes; + break; + } + case FT_BYTE: + { + ASSERT(fieldBytes == 1); + *((uint8*)(&dataTable[offset])) = *reinterpret_cast<uint8*>(&data[offsets[y].FileOffset - dataStart + fieldOffset]); + offset += 1; + fieldOffset += 1; + break; + } + case FT_SHORT: + { + ASSERT(fieldBytes == 2); + uint16 val = *reinterpret_cast<uint16*>(&data[offsets[y].FileOffset - dataStart + fieldOffset]); + EndianConvert(val); + *((uint16*)(&dataTable[offset])) = val; + 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 += sizeof(LocalizedString); + 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 += sizeof(char*); + offset += sizeof(char*); + break; + } + default: + ASSERT(false, "Unknown format character '%c' found in %s meta", meta->Types[x], fileName); + break; + } + } + } + + ++recordNum; + } + + return dataTable; +} + +char* DB2SparseFileLoader::AutoProduceStrings(char* dataTable, uint32 locale) +{ + if (meta->FieldCount != 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 = meta->GetRecordSize(); + std::size_t stringFields = meta->GetStringFieldCount(true); + char* stringTable = new char[offsetsPos - dataStart - records * ((recordsize - (!meta->HasIndexFieldInData() ? 4 : 0)) - stringFields * sizeof(char*))]; + memset(stringTable, 0, offsetsPos - dataStart - records * ((recordsize - (!meta->HasIndexFieldInData() ? 4 : 0)) - stringFields * sizeof(char*))); + char* stringPtr = stringTable; + + uint32 offset = 0; + + for (uint32 y = 0; y < offsetCount; y++) + { + if (!offsets[y].FileOffset || !offsets[y].RecordSize) + continue; + + if (!meta->HasIndexFieldInData()) + offset += 4; + + uint32 fieldOffset = 0; + for (uint32 x = 0; x < fieldCount; ++x) + { + for (uint32 z = 0; z < meta->ArraySizes[x]; ++z) + { + switch (meta->Types[x]) + { + case FT_FLOAT: + offset += 4; + fieldOffset += 4; + break; + case FT_INT: + offset += 4; + fieldOffset += 4 - fields[x].UnusedBits / 8; + break; + case FT_BYTE: + offset += 1; + fieldOffset += 1; + break; + case FT_SHORT: + offset += 2; + fieldOffset += 2; + break; + case FT_STRING: + { + 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: + { + fieldOffset += strlen((char*)&data[offsets[y].FileOffset - dataStart + fieldOffset]) + 1; + offset += sizeof(char*); + break; + } + default: + ASSERT(false, "Unknown format character '%c' found in %s meta", meta->Types[x], fileName); + break; + } + } + } + } + + return stringTable; +} + +char* DB2SparseDatabaseLoader::Load(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; + + if (_meta->GetDbFieldCount() != result->GetFieldCount()) + return nullptr; + + // get struct size and index pos + uint32 indexField = _meta->GetDbIndexField(); + uint32 recordSize = _meta->GetRecordSize(); + + // we store flat holders pool as single memory block + std::size_t stringFields = _meta->GetStringFieldCount(false); + std::size_t localizedStringFields = _meta->GetStringFieldCount(true); + + // each string field at load have array of string for each locale + std::size_t stringHoldersRecordPoolSize = localizedStringFields * sizeof(LocalizedString) + (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[indexField].GetUInt32(); + + // Attempt to overwrite existing data + char* dataValue = indexTable.Get(indexValue); + if (!dataValue) + { + newIndexes[newRecords] = indexValue; + dataValue = &tempDataTable[newRecords++ * recordSize]; + } + + uint32 f = 0; + if (!_meta->HasIndexFieldInData()) + { + *((uint32*)(&dataValue[offset])) = indexValue; + offset += 4; + ++f; + } + + for (uint32 x = 0; x < _meta->FieldCount; ++x) + { + for (uint32 z = 0; z < _meta->ArraySizes[x]; ++z) + { + switch (_meta->Types[x]) + { + case FT_FLOAT: + *((float*)(&dataValue[offset])) = fields[f].GetFloat(); + offset += 4; + break; + 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_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 += sizeof(LocalizedString); + 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 += sizeof(char*); + offset += sizeof(char*); + break; + } + default: + ASSERT(false, "Unknown format character '%c' found in %s meta", _meta->Types[x], _storageName.c_str()); + break; + } + ++f; + } + } + + 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(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 = _meta->GetStringFieldCount(true); + if (result->GetFieldCount() != stringFields + 1 /*ID*/) + return; + + uint32 fieldCount = _meta->FieldCount; + uint32 recordSize = _meta->GetRecordSize(); + + 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)) + { + if (!_meta->HasIndexFieldInData()) + offset += 4; + + for (uint32 x = 0; x < fieldCount; ++x) + { + for (uint32 z = 0; z < _meta->ArraySizes[x]; ++z) + { + switch (_meta->Types[x]) + { + case FT_FLOAT: + case FT_INT: + offset += 4; + break; + case FT_BYTE: + offset += 1; + 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; + } + default: + ASSERT(false, "Unknown format character '%c' found in %s meta", _meta->Types[x], _storageName.c_str()); + 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..0936947ea6a --- /dev/null +++ b/src/server/shared/DataStores/DB2SparseStorageLoader.h @@ -0,0 +1,123 @@ +/* + * 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 "DB2Meta.h" +#include "Utilities/ByteConverter.h" +#include "Implementation/HotfixDatabase.h" +#include <unordered_map> +#include <vector> + +class TC_SHARED_API 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 TC_SHARED_API DB2SparseFileLoader +{ + public: + DB2SparseFileLoader(); + ~DB2SparseFileLoader(); + + bool Load(char const* filename, DB2Meta const* meta); + + uint32 GetNumRows() const { return recordCount; } + uint32 GetCols() const { return fieldCount; } + uint32 GetTableHash() const { return tableHash; } + uint32 GetLayoutHash() const { return layoutHash; } + bool IsLoaded() const { return (data != NULL); } + char* AutoProduceData(IndexTable const& indexTable, uint32 locale, std::vector<char*>& stringPool); + char* AutoProduceStrings(char* dataTable, uint32 locale); + +private: +#pragma pack(push, 1) + struct OffsetTableEntry + { + uint32 FileOffset; + uint16 RecordSize; + }; + struct FieldEntry + { + uint16 UnusedBits; + uint16 Offset; + }; +#pragma pack(pop) + + char const* fileName; + DB2Meta const* meta; + + // WDB2 / WCH2 fields + uint32 recordSize; + uint32 recordCount; + uint32 fieldCount; + uint32 offsetsPos; + uint32 tableHash; + uint32 layoutHash; + uint32 minIndex; + uint32 maxIndex; + uint32 localeMask; + uint32 copyIdSize; + uint32 metaFlags; + FieldEntry* fields; + + uint32 dataStart; + unsigned char* data; + OffsetTableEntry* offsets; +}; + +class TC_SHARED_API DB2SparseDatabaseLoader +{ +public: + DB2SparseDatabaseLoader(std::string const& storageName, DB2Meta const* meta) : _storageName(storageName), _meta(meta) { } + + char* Load(HotfixDatabaseStatements preparedStatement, IndexTable const& indexTable, std::vector<char*>& stringPool); + void LoadStrings(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; + DB2Meta const* _meta; +}; + +#endif diff --git a/src/server/shared/DataStores/DB2StorageLoader.cpp b/src/server/shared/DataStores/DB2StorageLoader.cpp index a1acd3b5eac..fd3e0a257cd 100644 --- a/src/server/shared/DataStores/DB2StorageLoader.cpp +++ b/src/server/shared/DataStores/DB2StorageLoader.cpp @@ -23,25 +23,28 @@ DB2FileLoader::DB2FileLoader() { fileName = nullptr; + meta = nullptr; + recordSize = 0; recordCount = 0; fieldCount = 0; stringSize = 0; - fieldsOffset = nullptr; - data = nullptr; - stringTable = nullptr; - tableHash = 0; - build = 0; - - unk1 = 0; + layoutHash = 0; minIndex = 0; maxIndex = 0; localeMask = 0; - unk5 = 0; + copyIdSize = 0; + + data = nullptr; + stringTable = nullptr; + idTable = nullptr; + idTableSize = 0; + copyTable = nullptr; + fields = nullptr; } -bool DB2FileLoader::Load(const char *filename, const char *fmt) +bool DB2FileLoader::Load(char const* filename, DB2Meta const* meta) { if (data) { @@ -54,8 +57,9 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) return false; fileName = filename; + this->meta = meta; uint32 header; - if (fread(&header, 4, 1, f) != 1) // Signature + if (fread(&header, 4, 1, f) != 1) // Signature { fclose(f); return false; @@ -63,13 +67,13 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) EndianConvert(header); - if (header != 0x32424457) + if (header != 0x35424457) { fclose(f); - return false; //'WDB2' + return false; //'WDB5' } - 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 +81,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 +89,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 +97,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 +105,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,23 +113,21 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) EndianConvert(tableHash); - if (fread(&build, 4, 1, f) != 1) // Build + if (fread(&layoutHash, 4, 1, f) != 1) // Build { fclose(f); return false; } - EndianConvert(build); - - if (fread(&unk1, 4, 1, f) != 1) // Unknown WDB2 + if (layoutHash != meta->LayoutHash) { fclose(f); return false; } - EndianConvert(unk1); + EndianConvert(layoutHash); - if (fread(&minIndex, 4, 1, f) != 1) // MinIndex WDB2 + if (fread(&minIndex, 4, 1, f) != 1) // MinIndex WDB2 { fclose(f); return false; @@ -134,7 +135,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 +143,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,33 +151,34 @@ 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; } - fieldsOffset = new uint32[fieldCount]; - fieldsOffset[0] = 0; - for (uint32 i = 1; i < fieldCount; i++) - { - fieldsOffset[i] = fieldsOffset[i - 1]; - if (fmt[i - 1] == FT_BYTE) // byte fields - fieldsOffset[i] += 1; - else if (fmt[i - 1] == FT_LONG) - fieldsOffset[i] += 8; - else // 4 byte fields (int32/float/strings) - fieldsOffset[i] += 4; + EndianConvert(metaFlags); + + ASSERT((meta->IndexField == -1) || (meta->IndexField == (metaFlags >> 16))); + + fields = new FieldEntry[fieldCount]; + if (fread(fields, fieldCount * sizeof(FieldEntry), 1, f) != 1) + { + fclose(f); + return false; } + if (!meta->HasIndexFieldInData()) + idTableSize = recordCount * sizeof(uint32); + data = new unsigned char[recordSize * recordCount + stringSize]; stringTable = data + recordSize * recordCount; @@ -186,151 +188,135 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) return false; } - fclose(f); - return true; -} - -DB2FileLoader::~DB2FileLoader() -{ - if (data) - delete [] data; - if (fieldsOffset) - delete [] fieldsOffset; -} - -DB2FileLoader::Record DB2FileLoader::getRecord(size_t id) -{ - assert(data); - 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) + if (idTableSize) { - switch (format[x]) + idTable = new unsigned char[idTableSize]; + if (fread(idTable, idTableSize, 1, f) != 1) { - case FT_FLOAT: - case FT_INT: - recordsize += 4; - break; - case FT_STRING: - case FT_STRING_NOT_LOCALIZED: - recordsize += sizeof(char*); - break; - case FT_SORT: - i = x; - break; - case FT_IND: - i = x; - recordsize += 4; - break; - case FT_BYTE: - recordsize += 1; - break; - case FT_LONG: - recordsize += 8; - break; + fclose(f); + return false; } } - if (index_pos) - *index_pos = i; + if (copyIdSize) + { + copyTable = new unsigned char[copyIdSize]; + if (fread(copyTable, copyIdSize, 1, f) != 1) + { + fclose(f); + return false; + } + } - return recordsize; + fclose(f); + return true; } -uint32 DB2FileLoader::GetFormatStringFieldCount(const char* format) +DB2FileLoader::~DB2FileLoader() { - uint32 stringfields = 0; - for (uint32 x = 0; format[x]; ++x) - if (format[x] == FT_STRING || format[x] == FT_STRING_NOT_LOCALIZED) - ++stringfields; - - return stringfields; + delete[] data; + delete[] idTable; + delete[] copyTable; + delete[] fields; } -uint32 DB2FileLoader::GetFormatLocalizedStringFieldCount(char const* format) +DB2FileLoader::Record DB2FileLoader::getRecord(size_t id) { - uint32 stringfields = 0; - for (uint32 x = 0; format[x]; ++x) - if (format[x] == FT_STRING) - ++stringfields; - - return stringfields; + assert(data); + return Record(*this, data + id * recordSize); } -char* DB2FileLoader::AutoProduceData(const char* format, uint32& records, char**& indexTable) +char* DB2FileLoader::AutoProduceData(uint32& records, char**& indexTable) { - typedef char * ptr; - if (strlen(format) != fieldCount) + typedef char* ptr; + if (meta->FieldCount != fieldCount) return NULL; //get struct size and index pos - int32 indexField; - uint32 recordsize = GetFormatRecordSize(format, &indexField); + uint32 indexField = meta->GetIndexField(); + uint32 recordsize = meta->GetRecordSize(); - if (indexField >= 0) + uint32 maxi = 0; + //find max index + if (!idTableSize) { - uint32 maxi = 0; - //find max index - for (uint32 y = 0; y < recordCount; y++) + for (uint32 y = 0; y < recordCount; ++y) { - uint32 ind = getRecord(y).getUInt(indexField); + uint32 ind = getRecord(y).getUInt(indexField, 0); if (ind > maxi) maxi = ind; } - - ++maxi; - records = maxi; - indexTable = new ptr[maxi]; - memset(indexTable, 0, maxi * sizeof(ptr)); } else { - records = recordCount; - indexTable = new ptr[recordCount]; + for (uint32 y = 0; y < recordCount; ++y) + { + uint32 ind = ((uint32*)idTable)[y]; + if (ind > maxi) + maxi = ind; + } } - char* dataTable = new char[recordCount * recordsize]; + for (uint32 y = 0; y < copyIdSize; y += 8) + { + uint32 ind = *((uint32*)(copyTable + y)); + if (ind > maxi) + maxi = ind; + } + + ++maxi; + records = maxi; + indexTable = new ptr[maxi]; + memset(indexTable, 0, maxi * sizeof(ptr)); + + char* dataTable = new char[(recordCount + (copyIdSize / 8)) * recordsize]; uint32 offset = 0; for (uint32 y = 0; y < recordCount; y++) { - if (indexField >= 0) - indexTable[getRecord(y).getUInt(indexField)] = &dataTable[offset]; - else - indexTable[y] = &dataTable[offset]; + uint32 indexVal = meta->HasIndexFieldInData() ? getRecord(y).getUInt(indexField, 0) : ((uint32*)idTable)[y]; + + indexTable[indexVal] = &dataTable[offset]; - for (uint32 x = 0; x < fieldCount; x++) + if (!meta->HasIndexFieldInData()) { - switch (format[x]) + *((uint32*)(&dataTable[offset])) = indexVal; + offset += 4; + } + + Record rec = getRecord(y); + for (uint32 x = 0; x < fieldCount; ++x) + { + for (uint32 z = 0; z < meta->ArraySizes[x]; ++z) { - case FT_FLOAT: - *((float*)(&dataTable[offset])) = getRecord(y).getFloat(x); - offset += 4; - break; - case FT_IND: - case FT_INT: - *((uint32*)(&dataTable[offset])) = getRecord(y).getUInt(x); - offset += 4; - break; - case FT_BYTE: - *((uint8*)(&dataTable[offset])) = getRecord(y).getUInt8(x); - offset += 1; - break; - case FT_LONG: - *((uint64*)(&dataTable[offset])) = getRecord(y).getUInt64(x); - offset += 8; - break; - case FT_STRING: - case FT_STRING_NOT_LOCALIZED: - *((char**)(&dataTable[offset])) = nullptr; // will be replaces non-empty or "" strings in AutoProduceStrings - offset += sizeof(char*); - break; + switch (meta->Types[x]) + { + case FT_FLOAT: + *((float*)(&dataTable[offset])) = rec.getFloat(x, z); + offset += 4; + break; + case FT_INT: + *((uint32*)(&dataTable[offset])) = rec.getUInt(x, z); + offset += 4; + break; + case FT_BYTE: + *((uint8*)(&dataTable[offset])) = rec.getUInt8(x, z); + offset += 1; + break; + case FT_SHORT: + *((uint16*)(&dataTable[offset])) = rec.getUInt16(x, z); + offset += 2; + break; + case FT_STRING: + case FT_STRING_NOT_LOCALIZED: + *((char**)(&dataTable[offset])) = nullptr; // will be replaced by non-empty or "" strings in AutoProduceStrings + offset += sizeof(char*); + break; + default: + ASSERT(false, "Unknown format character '%c' found in %s meta", meta->Types[x], fileName); + break; + } } } } @@ -340,17 +326,17 @@ char* DB2FileLoader::AutoProduceData(const char* format, uint32& records, char** static char const* const nullStr = ""; -char* DB2FileLoader::AutoProduceStringsArrayHolders(const char* format, char* dataTable) +char* DB2FileLoader::AutoProduceStringsArrayHolders(char* dataTable) { - if (strlen(format) != fieldCount) + if (meta->FieldCount != fieldCount) return nullptr; // we store flat holders pool as single memory block - std::size_t stringFields = GetFormatStringFieldCount(format); + std::size_t stringFields = meta->GetStringFieldCount(false); if (!stringFields) return nullptr; - std::size_t localizedStringFields = GetFormatLocalizedStringFieldCount(format); + std::size_t localizedStringFields = meta->GetStringFieldCount(true); // each string field at load have array of string for each locale std::size_t stringHoldersRecordPoolSize = localizedStringFields * sizeof(LocalizedString) + (stringFields - localizedStringFields) * sizeof(char*); @@ -369,37 +355,43 @@ char* DB2FileLoader::AutoProduceStringsArrayHolders(const char* format, char* da { uint32 stringFieldOffset = 0; - for (uint32 x = 0; x < fieldCount; x++) + if (!meta->HasIndexFieldInData()) + offset += 4; + + for (uint32 x = 0; x < fieldCount; ++x) { - switch (format[x]) + for (uint32 z = 0; z < meta->ArraySizes[x]; ++z) { - 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_STRING: - case FT_STRING_NOT_LOCALIZED: + switch (meta->Types[x]) { - // init db2 string field slots by pointers to string holders - char const*** slot = (char const***)(&dataTable[offset]); - *slot = (char const**)(&stringHoldersPool[stringHoldersRecordPoolSize * y + stringFieldOffset]); - if (format[x] == FT_STRING) - stringFieldOffset += sizeof(LocalizedString); - else - stringFieldOffset += sizeof(char*); + case FT_FLOAT: + case FT_INT: + offset += 4; + break; + case FT_BYTE: + offset += 1; + break; + case FT_SHORT: + offset += 2; + break; + case FT_STRING: + case FT_STRING_NOT_LOCALIZED: + { + // init db2 string field slots by pointers to string holders + char const*** slot = (char const***)(&dataTable[offset]); + *slot = (char const**)(&stringHoldersPool[stringHoldersRecordPoolSize * y + stringFieldOffset]); + if (meta->Types[x] == FT_STRING) + stringFieldOffset += sizeof(LocalizedString); + else + stringFieldOffset += sizeof(char*); - offset += sizeof(char*); - break; + offset += sizeof(char*); + break; + } + default: + ASSERT(false, "Unknown format character '%c' found in %s meta", meta->Types[x], fileName); + break; } - default: - ASSERT(false, "unknown format character %c", format[x]); } } } @@ -408,9 +400,9 @@ char* DB2FileLoader::AutoProduceStringsArrayHolders(const char* format, char* da return stringHoldersPool; } -char* DB2FileLoader::AutoProduceStrings(const char* format, char* dataTable, uint32 locale) +char* DB2FileLoader::AutoProduceStrings(char* dataTable, uint32 locale) { - if (strlen(format) != fieldCount) + if (meta->FieldCount != fieldCount) return nullptr; if (!(localeMask & (1 << locale))) @@ -437,41 +429,49 @@ char* DB2FileLoader::AutoProduceStrings(const char* format, char* dataTable, uin for (uint32 y = 0; y < recordCount; y++) { - for (uint32 x = 0; x < fieldCount; x++) + if (!meta->HasIndexFieldInData()) + offset += 4; + + for (uint32 x = 0; x < fieldCount; ++x) { - switch (format[x]) + for (uint32 z = 0; z < meta->ArraySizes[x]; ++z) { - 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_STRING: + switch (meta->Types[x]) { - // fill only not filled entries - LocalizedString* db2str = *(LocalizedString**)(&dataTable[offset]); - if (db2str->Str[locale] == nullStr) + case FT_FLOAT: + case FT_INT: + offset += 4; + break; + case FT_BYTE: + offset += 1; + break; + case FT_SHORT: + offset += 2; + break; + case FT_STRING: { - char const* st = getRecord(y).getString(x); - db2str->Str[locale] = stringPool + (st - (char const*)stringTable); - } + // fill only not filled entries + LocalizedString* db2str = *(LocalizedString**)(&dataTable[offset]); + if (db2str->Str[locale] == nullStr) + { + char const* st = getRecord(y).getString(x, z); + db2str->Str[locale] = stringPool + (st - (char const*)stringTable); + } - offset += sizeof(char*); - break; - } - case FT_STRING_NOT_LOCALIZED: - { - char** db2str = (char**)(&dataTable[offset]); - char const* st = getRecord(y).getString(x); - *db2str = stringPool + (st - (char const*)stringTable); - offset += sizeof(char*); - break; + offset += sizeof(char*); + break; + } + case FT_STRING_NOT_LOCALIZED: + { + char** db2str = (char**)(&dataTable[offset]); + char const* st = getRecord(y).getString(x, z); + *db2str = stringPool + (st - (char const*)stringTable); + offset += sizeof(char*); + break; + } + default: + ASSERT(false, "Unknown format character '%c' found in %s meta", meta->Types[x], fileName); + break; } } } @@ -480,24 +480,48 @@ 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) +void DB2FileLoader::AutoProduceRecordCopies(uint32 records, char** indexTable, char* dataTable) +{ + uint32 recordsize = meta->GetRecordSize(); + uint32 offset = recordCount * recordsize; + 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); + + if (meta->HasIndexFieldInData()) + *((uint32*)(&dataTable[offset + fields[meta->GetIndexField()].Offset])) = to; + else + *((uint32*)(&dataTable[offset])) = to; + + offset += recordsize; + } + } +} + +char* DB2DatabaseLoader::Load(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)); if (!result) return nullptr; - uint32 const fieldCount = strlen(format); - if (fieldCount != result->GetFieldCount()) + if (_meta->GetDbFieldCount() != result->GetFieldCount()) return nullptr; // get struct size and index pos - int32 indexField; - uint32 recordSize = DB2FileLoader::GetFormatRecordSize(format, &indexField); + uint32 indexField = _meta->GetDbIndexField(); + uint32 recordSize = _meta->GetRecordSize(); // we store flat holders pool as single memory block - std::size_t stringFields = DB2FileLoader::GetFormatStringFieldCount(format); - std::size_t localizedStringFields = DB2FileLoader::GetFormatLocalizedStringFieldCount(format); + std::size_t stringFields = _meta->GetStringFieldCount(false); + std::size_t localizedStringFields = _meta->GetStringFieldCount(true); // each string field at load have array of string for each locale std::size_t stringHoldersRecordPoolSize = localizedStringFields * sizeof(LocalizedString) + (stringFields - localizedStringFields) * sizeof(char*); @@ -516,15 +540,9 @@ char* DB2DatabaseLoader::Load(const char* format, HotfixDatabaseStatements prepa // Resize index table // database query *MUST* contain ORDER BY `index_field` DESC clause - uint32 indexTableSize; - if (indexField >= 0) - { - indexTableSize = (*result)[indexField].GetUInt32() + 1; - if (indexTableSize < records) - indexTableSize = records; - } - else - indexTableSize = records + result->GetRowCount(); + uint32 indexTableSize = (*result)[indexField].GetUInt32() + 1; + if (indexTableSize < records) + indexTableSize = records; if (indexTableSize > records) { @@ -546,11 +564,7 @@ char* DB2DatabaseLoader::Load(const char* format, HotfixDatabaseStatements prepa uint32 offset = 0; uint32 stringFieldOffset = 0; - uint32 indexValue; - if (indexField >= 0) - indexValue = fields[indexField].GetUInt32(); - else - indexValue = records + rec; + uint32 indexValue = fields[indexField].GetUInt32(); // Attempt to overwrite existing data char* dataValue = indexTable[indexValue]; @@ -560,55 +574,69 @@ char* DB2DatabaseLoader::Load(const char* format, HotfixDatabaseStatements prepa dataValue = &tempDataTable[newRecords++ * recordSize]; } - for (uint32 f = 0; f < fieldCount; f++) + uint32 f = 0; + if (!_meta->HasIndexFieldInData()) { - switch (format[f]) + *((uint32*)(&dataValue[offset])) = indexValue; + offset += 4; + ++f; + } + + for (uint32 x = 0; x < _meta->FieldCount; ++x) + { + for (uint32 z = 0; z < _meta->ArraySizes[x]; ++z) { - 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_STRING: + switch (_meta->Types[x]) { - LocalizedString** slot = (LocalizedString**)(&dataValue[offset]); - *slot = (LocalizedString*)(&stringHolders[stringHoldersRecordPoolSize * rec + stringFieldOffset]); - ASSERT(*slot); + case FT_FLOAT: + *((float*)(&dataValue[offset])) = fields[f].GetFloat(); + offset += 4; + break; + 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_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); + // 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 += sizeof(LocalizedString); - offset += sizeof(char*); - break; - } - case FT_STRING_NOT_LOCALIZED: - { - char const** slot = (char const**)(&dataValue[offset]); - *slot = (char*)(&stringHolders[stringHoldersRecordPoolSize * rec + sizeof(LocalizedString) * stringFieldOffset]); - ASSERT(*slot); + stringFieldOffset += sizeof(LocalizedString); + 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); + // 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 += sizeof(char*); - offset += sizeof(char*); - break; + stringFieldOffset += sizeof(char*); + offset += sizeof(char*); + break; + } + default: + ASSERT(false, "Unknown format character '%c' found in %s meta", _meta->Types[x], _storageName.c_str()); + break; } + ++f; } } @@ -639,7 +667,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(HotfixDatabaseStatements preparedStatement, uint32 locale, uint32 records, char** indexTable, std::vector<char*>& stringPool) { PreparedStatement* stmt = HotfixDatabase.GetPreparedStatement(preparedStatement); stmt->setString(0, localeNames[locale]); @@ -647,14 +675,12 @@ void DB2DatabaseLoader::LoadStrings(const char* format, HotfixDatabaseStatements if (!result) return; - size_t stringFields = DB2FileLoader::GetFormatLocalizedStringFieldCount(format); + std::size_t stringFields = _meta->GetStringFieldCount(true); if (result->GetFieldCount() != stringFields + 1 /*ID*/) return; - uint32 const fieldCount = strlen(format); - int32 indexField; - uint32 recordSize = DB2FileLoader::GetFormatRecordSize(format, &indexField); - ASSERT(indexField >= 0, "DB2Storage must be indexed to load localized strings"); + uint32 fieldCount = _meta->FieldCount; + uint32 recordSize = _meta->GetRecordSize(); do { @@ -663,35 +689,49 @@ void DB2DatabaseLoader::LoadStrings(const char* format, HotfixDatabaseStatements uint32 stringFieldNumInRecord = 0; uint32 indexValue = fields[0].GetUInt32(); + if (indexValue >= records) + continue; + // Attempt to overwrite existing data if (char* dataValue = indexTable[indexValue]) { - for (uint32 x = 0; x < fieldCount; x++) + if (!_meta->HasIndexFieldInData()) + offset += 4; + + for (uint32 x = 0; x < fieldCount; ++x) { - switch (format[x]) + for (uint32 z = 0; z < _meta->ArraySizes[x]; ++z) { - 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_STRING: + switch (_meta->Types[x]) { - // 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; + case FT_FLOAT: + case FT_INT: + offset += 4; + break; + case FT_BYTE: + offset += 1; + 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(LocalizedString*); + break; + } + case FT_STRING_NOT_LOCALIZED: + offset += sizeof(char*); + break; + default: + ASSERT(false, "Unknown format character '%c' found in %s meta", _meta->Types[x], _storageName.c_str()); + break; } } } @@ -699,7 +739,7 @@ void DB2DatabaseLoader::LoadStrings(const char* format, HotfixDatabaseStatements 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); + TC_LOG_ERROR("sql.sql", "Hotfix locale table for storage %s references row that does not exist %u locale %s!", _storageName.c_str(), indexValue, localeNames[locale]); } while (result->NextRow()); diff --git a/src/server/shared/DataStores/DB2StorageLoader.h b/src/server/shared/DataStores/DB2StorageLoader.h index 41705c67f19..b302a1afe7d 100644 --- a/src/server/shared/DataStores/DB2StorageLoader.h +++ b/src/server/shared/DataStores/DB2StorageLoader.h @@ -18,11 +18,11 @@ #ifndef DB2_FILE_LOADER_H #define DB2_FILE_LOADER_H -#include "Define.h" +#include "DB2Meta.h" #include "Utilities/ByteConverter.h" #include "Implementation/HotfixDatabase.h" -#include <cassert> -#include <list> +#include "Errors.h" +#include <vector> class TC_SHARED_API DB2FileLoader { @@ -30,46 +30,101 @@ class TC_SHARED_API DB2FileLoader DB2FileLoader(); ~DB2FileLoader(); - bool Load(const char *filename, const char *fmt); + bool Load(char const* filename, DB2Meta const* meta); class Record { public: - float getFloat(size_t field) const + float getFloat(uint32 field, uint32 arrayIndex) const { - assert(field < file.fieldCount); - float val = *reinterpret_cast<float*>(offset + file.GetOffset(field)); + ASSERT(field < file.fieldCount); + float val = *reinterpret_cast<float*>(offset + GetOffset(field) + arrayIndex * sizeof(float)); EndianConvert(val); return val; } - uint32 getUInt(size_t field) const + + uint32 getUInt(uint32 field, uint32 arrayIndex) const { - assert(field < file.fieldCount); - uint32 val = *reinterpret_cast<uint32*>(offset + file.GetOffset(field)); - EndianConvert(val); - return val; + ASSERT(field < file.fieldCount); + return GetVarInt(field, GetByteSize(field), arrayIndex); } - uint8 getUInt8(size_t field) const + + uint8 getUInt8(uint32 field, uint32 arrayIndex) const { - assert(field < file.fieldCount); - return *reinterpret_cast<uint8*>(offset + file.GetOffset(field)); + ASSERT(field < file.fieldCount); + ASSERT(GetByteSize(field) == 1); + return *reinterpret_cast<uint8*>(offset + GetOffset(field) + arrayIndex * sizeof(uint8)); } - uint64 getUInt64(size_t field) const + + uint16 getUInt16(uint32 field, uint32 arrayIndex) const { - assert(field < file.fieldCount); - uint64 val = *reinterpret_cast<uint64*>(offset + file.GetOffset(field)); + ASSERT(field < file.fieldCount); + ASSERT(GetByteSize(field) == 2); + uint16 val = *reinterpret_cast<uint16*>(offset + GetOffset(field) + arrayIndex * sizeof(uint16)); EndianConvert(val); return val; } - const char *getString(size_t field) const + + char const* getString(uint32 field, uint32 arrayIndex) const { - assert(field < file.fieldCount); - size_t stringOffset = getUInt(field); - assert(stringOffset < file.stringSize); + ASSERT(field < file.fieldCount); + uint32 stringOffset = *reinterpret_cast<uint32*>(offset + GetOffset(field) + arrayIndex * sizeof(uint32)); + EndianConvert(stringOffset); + ASSERT(stringOffset < file.stringSize); return reinterpret_cast<char*>(file.stringTable + stringOffset); } private: + uint16 GetOffset(uint32 field) const + { + ASSERT(field < file.fieldCount); + return file.fields[field].Offset; + } + + uint16 GetByteSize(uint32 field) const + { + ASSERT(field < file.fieldCount); + return 4 - file.fields[field].UnusedBits / 8; + } + + uint32 GetVarInt(uint32 field, uint16 size, uint32 arrayIndex) const + { + ASSERT(field < file.fieldCount); + switch (size) + { + case 1: + { + return *reinterpret_cast<uint8*>(offset + GetOffset(field) + arrayIndex * sizeof(uint8)); + } + case 2: + { + uint16 val = *reinterpret_cast<uint16*>(offset + GetOffset(field) + arrayIndex * sizeof(uint16)); + EndianConvert(val); + return val; + } + case 3: + { +#pragma pack(push, 1) + struct dbcint24 { uint8 v[3]; }; +#pragma pack(pop) + dbcint24 val = *reinterpret_cast<dbcint24*>(offset + GetOffset(field) + arrayIndex * sizeof(dbcint24)); + EndianConvert(val); + return uint32(val.v[0]) | (uint32(val.v[1]) << 8) | (uint32(val.v[2]) << 16); + } + case 4: + { + uint32 val = *reinterpret_cast<uint32*>(offset + GetOffset(field) + arrayIndex * sizeof(uint32)); + 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; @@ -79,52 +134,62 @@ class TC_SHARED_API DB2FileLoader // Get record by id Record getRecord(size_t id); - /// Get begin iterator over records uint32 GetNumRows() const { return recordCount;} uint32 GetCols() const { return fieldCount; } - uint32 GetOffset(size_t id) const { return (fieldsOffset != NULL && id < fieldCount) ? fieldsOffset[id] : 0; } - uint32 GetHash() const { return tableHash; } + uint32 GetTableHash() const { return tableHash; } + uint32 GetLayoutHash() const { return layoutHash; } bool IsLoaded() const { return (data != NULL); } - char* AutoProduceData(const char* fmt, uint32& count, char**& indexTable); - char* AutoProduceStringsArrayHolders(const char* fmt, char* dataTable); - char* AutoProduceStrings(const char* fmt, char* dataTable, uint32 locale); - static uint32 GetFormatRecordSize(const char * format, int32 * index_pos = NULL); - static uint32 GetFormatStringFieldCount(const char * format); - static uint32 GetFormatLocalizedStringFieldCount(const char * format); + char* AutoProduceData(uint32& count, char**& indexTable); + char* AutoProduceStringsArrayHolders(char* dataTable); + char* AutoProduceStrings(char* dataTable, uint32 locale); + void AutoProduceRecordCopies(uint32 records, char** indexTable, char* dataTable); + private: +#pragma pack(push, 1) + struct FieldEntry + { + uint16 UnusedBits; + uint16 Offset; + }; +#pragma pack(pop) + char const* fileName; + DB2Meta const* meta; + // WDB2 / WCH2 fields uint32 recordSize; uint32 recordCount; uint32 fieldCount; uint32 stringSize; - uint32 *fieldsOffset; - unsigned char *data; - unsigned char *stringTable; - - // 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 tableHash; + uint32 layoutHash; + uint32 minIndex; + uint32 maxIndex; + uint32 localeMask; + uint32 copyIdSize; + uint32 metaFlags; + + unsigned char* data; + unsigned char* stringTable; + unsigned char* idTable; + uint32 idTableSize; + unsigned char* copyTable; + FieldEntry* fields; }; class TC_SHARED_API DB2DatabaseLoader { public: - explicit DB2DatabaseLoader(std::string const& storageName) : _storageName(storageName) { } + DB2DatabaseLoader(std::string const& storageName, DB2Meta const* meta) : _storageName(storageName), _meta(meta) { } - 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(HotfixDatabaseStatements preparedStatement, uint32& records, char**& indexTable, char*& stringHolders, std::vector<char*>& stringPool); + void LoadStrings(HotfixDatabaseStatements preparedStatement, uint32 locale, uint32 records, char** indexTable, std::vector<char*>& stringPool); static char* AddString(char const** holder, std::string const& value); private: std::string _storageName; + DB2Meta const* _meta; }; #endif diff --git a/src/server/shared/DataStores/DB2Store.h b/src/server/shared/DataStores/DB2Store.h index 72271ce507b..bf10d78f37b 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,9 +28,20 @@ class DB2StorageBase { public: - virtual ~DB2StorageBase() { } + DB2StorageBase(char const* fileName, DB2Meta const* meta, HotfixDatabaseStatements preparedStmtIndex) + : _tableHash(0), _layoutHash(0), _fileName(fileName), _fieldCount(0), _meta(meta), _dataTable(nullptr), _dataTableEx(nullptr), _hotfixStatement(preparedStmtIndex) { } - uint32 GetHash() const { return _tableHash; } + virtual ~DB2StorageBase() + { + delete[] reinterpret_cast<char*>(_dataTable); + delete[] reinterpret_cast<char*>(_dataTableEx); + for (char* strings : _stringPool) + delete[] strings; + } + + uint32 GetTableHash() const { return _tableHash; } + + uint32 GetLayoutHash() const { return _layoutHash; } virtual bool HasRecord(uint32 id) const = 0; @@ -37,19 +49,87 @@ public: virtual void EraseRecord(uint32 id) = 0; + std::string const& GetFileName() const { return _fileName; } + + uint32 GetFieldCount() const { return _fieldCount; } + + DB2Meta const* GetMeta() const { return _meta; } + + 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; + protected: + void WriteRecordData(char const* entry, uint32 locale, ByteBuffer& buffer) const + { + if (!_meta->HasIndexFieldInData()) + entry += 4; + + for (uint32 i = 0; i < _meta->FieldCount; ++i) + { + for (uint32 a = 0; a < _meta->ArraySizes[i]; ++a) + { + switch (_meta->Types[i]) + { + case FT_INT: + buffer << *(uint32*)entry; + entry += 4; + break; + case FT_FLOAT: + buffer << *(float*)entry; + entry += 4; + break; + case FT_BYTE: + buffer << *(uint8*)entry; + entry += 1; + break; + case FT_SHORT: + buffer << *(uint16*)entry; + entry += 2; + break; + case FT_STRING: + { + LocalizedString* locStr = *(LocalizedString**)entry; + if (locStr->Str[locale][0] == '\0') + locale = 0; + + buffer << locStr->Str[locale]; + entry += sizeof(LocalizedString*); + break; + } + case FT_STRING_NOT_LOCALIZED: + { + buffer << *(char const**)entry; + entry += sizeof(char const*); + break; + } + } + } + } + } + uint32 _tableHash; + uint32 _layoutHash; + std::string _fileName; + uint32 _fieldCount; + DB2Meta const* _meta; + char* _dataTable; + char* _dataTableEx; + std::vector<char*> _stringPool; + HotfixDatabaseStatements _hotfixStatement; }; template<class T> class DB2Storage : public DB2StorageBase { - typedef std::list<char*> StringPoolList; + 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) - : _fileName(fileName), _indexTableSize(0), _fieldCount(0), _format(format), _dataTable(nullptr), _dataTableEx(nullptr), _hotfixStatement(preparedStmtIndex) + DB2Storage(char const* fileName, DB2Meta const* meta, HotfixDatabaseStatements preparedStmtIndex) : DB2StorageBase(fileName, meta, preparedStmtIndex), + _indexTableSize(0) { _indexTable.AsT = NULL; } @@ -57,69 +137,12 @@ public: ~DB2Storage() { delete[] reinterpret_cast<char*>(_indexTable.AsT); - delete[] reinterpret_cast<char*>(_dataTable); - delete[] reinterpret_cast<char*>(_dataTableEx); - for (char* stringPool : _stringPoolList) - delete[] stringPool; } bool HasRecord(uint32 id) const override { return id < _indexTableSize && _indexTable.AsT[id] != nullptr; } void WriteRecord(uint32 id, uint32 locale, ByteBuffer& buffer) const override { - ASSERT(id < _indexTableSize); - char const* entry = _indexTable.AsChar[id]; - ASSERT(entry); - - std::size_t fields = strlen(_format); - for (uint32 i = 0; i < fields; ++i) - { - switch (_format[i]) - { - case FT_IND: - case FT_INT: - buffer << *(uint32*)entry; - entry += 4; - break; - case FT_FLOAT: - buffer << *(float*)entry; - entry += 4; - break; - case FT_BYTE: - buffer << *(uint8*)entry; - entry += 1; - break; - case FT_STRING: - { - LocalizedString* locStr = *(LocalizedString**)entry; - if (locStr->Str[locale][0] == '\0') - locale = 0; - - char const* str = locStr->Str[locale]; - std::size_t len = strlen(str); - buffer << uint16(len ? len + 1 : 0); - if (len) - { - buffer.append(str, len); - buffer << uint8(0); - } - entry += sizeof(LocalizedString*); - break; - } - case FT_STRING_NOT_LOCALIZED: - { - char const* str = *(char const**)entry; - std::size_t len = strlen(str); - buffer << uint16(len ? len + 1 : 0); - if (len) - { - buffer.append(str, len); - buffer << uint8(0); - } - entry += sizeof(char const*); - break; - } - } - } + WriteRecordData(reinterpret_cast<char const*>(AssertEntry(id)), locale, buffer); } void EraseRecord(uint32 id) override { if (id < _indexTableSize) _indexTable.AsT[id] = nullptr; } @@ -127,38 +150,38 @@ public: 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 - if (!db2.Load((path + _fileName).c_str(), _format)) + if (!db2.Load((path + _fileName).c_str(), _meta)) return false; _fieldCount = db2.GetCols(); - _tableHash = db2.GetHash(); + _tableHash = db2.GetTableHash(); + _layoutHash = db2.GetLayoutHash(); // load raw non-string data - _dataTable = reinterpret_cast<T*>(db2.AutoProduceData(_format, _indexTableSize, _indexTable.AsChar)); + _dataTable = db2.AutoProduceData(_indexTableSize, _indexTable.AsChar); // create string holders for loaded string fields - if (char* stringHolders = db2.AutoProduceStringsArrayHolders(_format, (char*)_dataTable)) + if (char* stringHolders = db2.AutoProduceStringsArrayHolders(_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(_dataTable, locale)) + _stringPool.push_back(stringBlock); } + db2.AutoProduceRecordCopies(_indexTableSize, _indexTable.AsChar, _dataTable); + // 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) @@ -166,59 +189,145 @@ public: DB2FileLoader db2; // Check if load was successful, only then continue - if (!db2.Load((path + _fileName).c_str(), _format)) + if (!db2.Load((path + _fileName).c_str(), _meta)) return false; // 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 (_meta->GetStringFieldCount(true)) + if (char* stringBlock = db2.AutoProduceStrings(_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, _meta).Load(_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)) + if (!_meta->GetStringFieldCount(true)) 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, _meta).LoadStrings(HotfixDatabaseStatements(_hotfixStatement + 1), locale, _indexTableSize, _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 struct iterator_wrapper : public std::unordered_map<uint32, T const*>::const_iterator + { + typedef typename std::unordered_map<uint32, T const*>::const_iterator Base; + + iterator_wrapper() = default; + iterator_wrapper(iterator_wrapper const& right) = default; + iterator_wrapper(Base const& baseItr) : Base(baseItr) { } + + T const* operator->() const { return Base::operator->()->second; } + T const* operator*() const { return Base::operator*().second; } + } iterator; + + DB2SparseStorage(char const* fileName, DB2Meta const* meta, HotfixDatabaseStatements preparedStmtIndex) + : DB2StorageBase(fileName, meta, 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(), _meta)) + return false; + + _fieldCount = db2.GetCols(); + _tableHash = db2.GetTableHash(); + _layoutHash = db2.GetLayoutHash(); + + // load raw non-string data + _dataTable = db2.AutoProduceData(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(), _meta)) + return false; + + // load strings from another locale db2 data + if (_meta->GetStringFieldCount(true)) + if (char* stringBlock = db2.AutoProduceStrings(_dataTable, locale)) + _stringPool.push_back(stringBlock); + return true; + } + + void LoadFromDB() override + { + _dataTableEx = DB2SparseDatabaseLoader(_fileName, _meta).Load(_hotfixStatement, IndexTableAdapter<T>(_indexTable), _stringPool); + } + + void LoadStringsFromDB(uint32 locale) override + { + if (!_meta->GetStringFieldCount(true)) + return; + + DB2SparseDatabaseLoader(_fileName, _meta).LoadStrings(HotfixDatabaseStatements(_hotfixStatement + 1), locale, IndexTableAdapter<T>(_indexTable), _stringPool); + } + + iterator begin() const { return iterator(_indexTable.begin()); } + iterator end() const { return iterator(_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 deleted file mode 100644 index 829c3708221..00000000000 --- a/src/server/shared/DataStores/DBCFileLoader.cpp +++ /dev/null @@ -1,325 +0,0 @@ -/* - * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2009 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/>. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "DBCFileLoader.h" -#include "Errors.h" - -DBCFileLoader::DBCFileLoader() : recordSize(0), recordCount(0), fieldCount(0), stringSize(0), fieldsOffset(NULL), data(NULL), stringTable(NULL) { } - -bool DBCFileLoader::Load(const char* filename, const char* fmt) -{ - uint32 header; - if (data) - { - delete [] data; - data = NULL; - } - - FILE* f = fopen(filename, "rb"); - if (!f) - return false; - - if (fread(&header, 4, 1, f) != 1) // Number of records - { - fclose(f); - return false; - } - - - EndianConvert(header); - - if (header != 0x43424457) //'WDBC' - { - fclose(f); - return false; - } - - 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(&stringSize, 4, 1, f) != 1) // String size - { - fclose(f); - return false; - } - - EndianConvert(stringSize); - - fieldsOffset = new uint32[fieldCount]; - fieldsOffset[0] = 0; - for (uint32 i = 1; i < fieldCount; ++i) - { - fieldsOffset[i] = fieldsOffset[i - 1]; - if (fmt[i - 1] == FT_BYTE || fmt[i - 1] == FT_NA_BYTE) // byte fields - fieldsOffset[i] += sizeof(uint8); - else if (fmt[i - 1] == FT_LONG) - fieldsOffset[i] += sizeof(uint64); - else // 4 byte fields (int32/float/strings) - fieldsOffset[i] += sizeof(uint32); - } - - data = new unsigned char[recordSize * recordCount + stringSize]; - stringTable = data + recordSize*recordCount; - - if (fread(data, recordSize * recordCount + stringSize, 1, f) != 1) - { - fclose(f); - return false; - } - - fclose(f); - - return true; -} - -DBCFileLoader::~DBCFileLoader() -{ - delete[] data; - - delete[] fieldsOffset; -} - -DBCFileLoader::Record DBCFileLoader::getRecord(size_t id) -{ - assert(data); - return Record(*this, data + id * recordSize); -} - -uint32 DBCFileLoader::GetFormatRecordSize(const char* format, int32* index_pos) -{ - uint32 recordsize = 0; - int32 i = -1; - for (uint32 x = 0; format[x]; ++x) - { - switch (format[x]) - { - case FT_FLOAT: - recordsize += sizeof(float); - break; - case FT_INT: - recordsize += sizeof(uint32); - break; - case FT_STRING: - recordsize += sizeof(char*); - break; - case FT_SORT: - i = x; - break; - case FT_IND: - i = x; - recordsize += sizeof(uint32); - break; - case FT_BYTE: - recordsize += sizeof(uint8); - break; - case FT_LONG: - recordsize += sizeof(uint64); - break; - case FT_NA: - case FT_NA_BYTE: - break; - default: - ASSERT(false && "Unknown field format character in DBCfmt.h"); - break; - } - } - - if (index_pos) - *index_pos = i; - - return recordsize; -} - -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; - - //get struct size and index pos - int32 i; - uint32 recordsize = GetFormatRecordSize(format, &i); - - if (i >= 0) - { - uint32 maxi = 0; - //find max index - for (uint32 y = 0; y < recordCount; ++y) - { - uint32 ind = getRecord(y).getUInt(i); - if (ind > maxi) - maxi = ind; - } - - // If higher index avalible from sql - use it instead of dbcs - if (sqlHighestIndex > maxi) - maxi = sqlHighestIndex; - - ++maxi; - records = maxi; - indexTable = new ptr[maxi]; - memset(indexTable, 0, maxi * sizeof(ptr)); - } - else - { - records = recordCount + sqlRecordCount; - indexTable = new ptr[recordCount + sqlRecordCount]; - } - - char* dataTable = new char[(recordCount + sqlRecordCount) * recordsize]; - - uint32 offset = 0; - - for (uint32 y = 0; y < recordCount; ++y) - { - if (i >= 0) - indexTable[getRecord(y).getUInt(i)] = &dataTable[offset]; - else - indexTable[y] = &dataTable[offset]; - - for (uint32 x=0; x < fieldCount; ++x) - { - switch (format[x]) - { - case FT_FLOAT: - *((float*)(&dataTable[offset])) = getRecord(y).getFloat(x); - offset += sizeof(float); - break; - case FT_IND: - case FT_INT: - *((uint32*)(&dataTable[offset])) = getRecord(y).getUInt(x); - offset += sizeof(uint32); - break; - case FT_BYTE: - *((uint8*)(&dataTable[offset])) = getRecord(y).getUInt8(x); - offset += sizeof(uint8); - break; - case FT_LONG: - *((uint64*)(&dataTable[offset])) = getRecord(y).getUInt64(x); - offset += sizeof(uint64); - break; - case FT_STRING: - *((char**)(&dataTable[offset])) = NULL; // will replace non-empty or "" strings in AutoProduceStrings - offset += sizeof(char*); - break; - case FT_NA: - case FT_NA_BYTE: - case FT_SORT: - break; - default: - ASSERT(false && "Unknown field format character in DBCfmt.h"); - break; - } - } - } - - sqlDataTable = dataTable + offset; - - return dataTable; -} - -char* DBCFileLoader::AutoProduceStrings(const char* format, char* dataTable) -{ - if (strlen(format) != fieldCount) - return NULL; - - char* stringPool = new char[stringSize]; - memcpy(stringPool, stringTable, stringSize); - - uint32 offset = 0; - - for (uint32 y = 0; y < recordCount; ++y) - { - for (uint32 x = 0; x < fieldCount; ++x) - { - switch (format[x]) - { - case FT_FLOAT: - offset += sizeof(float); - break; - case FT_IND: - case FT_INT: - offset += sizeof(uint32); - break; - case FT_BYTE: - offset += sizeof(uint8); - break; - case FT_LONG: - offset += sizeof(uint64); - break; - case FT_STRING: - { - // fill only not filled entries - char** slot = (char**)(&dataTable[offset]); - if (!*slot || !**slot) - { - const char * st = getRecord(y).getString(x); - *slot=stringPool+(st-(const char*)stringTable); - } - offset += sizeof(char*); - break; - } - case FT_NA: - case FT_NA_BYTE: - case FT_SORT: - break; - default: - ASSERT(false && "Unknown field format character in DBCfmt.h"); - break; - } - } - } - - return stringPool; -} diff --git a/src/server/shared/DataStores/DBCFileLoader.h b/src/server/shared/DataStores/DBCFileLoader.h deleted file mode 100644 index e58031e6ccc..00000000000 --- a/src/server/shared/DataStores/DBCFileLoader.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2009 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 DBC_FILE_LOADER_H -#define DBC_FILE_LOADER_H - -#include "Define.h" -#include "Utilities/ByteConverter.h" -#include <cassert> - -class TC_SHARED_API DBCFileLoader -{ - public: - DBCFileLoader(); - ~DBCFileLoader(); - - bool Load(const char *filename, const char *fmt); - - class Record - { - public: - float getFloat(size_t field) const - { - assert(field < file.fieldCount); - float val = *reinterpret_cast<float*>(offset + file.GetOffset(field)); - EndianConvert(val); - return val; - } - uint32 getUInt(size_t field) const - { - assert(field < file.fieldCount); - uint32 val = *reinterpret_cast<uint32*>(offset + file.GetOffset(field)); - EndianConvert(val); - return val; - } - uint8 getUInt8(size_t field) const - { - assert(field < file.fieldCount); - return *reinterpret_cast<uint8*>(offset + file.GetOffset(field)); - } - uint64 getUInt64(size_t field) const - { - assert(field < file.fieldCount); - uint64 val = *reinterpret_cast<uint64*>(offset + file.GetOffset(field)); - EndianConvert(val); - return val; - } - const char *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(DBCFileLoader &file_, unsigned char *offset_): offset(offset_), file(file_) { } - unsigned char* offset; - DBCFileLoader& file; - - friend class DBCFileLoader; - - }; - - // Get record by id - Record getRecord(size_t id); - /// Get begin iterator over records - - uint32 GetNumRows() const { return recordCount; } - uint32 GetRowSize() const { return recordSize; } - uint32 GetCols() const { return fieldCount; } - uint32 GetOffset(size_t id) const { return (fieldsOffset != NULL && id < fieldCount) ? fieldsOffset[id] : 0; } - bool IsLoaded() const { return data != NULL; } - char* AutoProduceData(const char* fmt, uint32& count, char**& indexTable, uint32 sqlRecordCount, uint32 sqlHighestIndex, char *& sqlDataTable); - char* AutoProduceStrings(const char* fmt, char* dataTable); - static uint32 GetFormatRecordSize(const char * format, int32 * index_pos = NULL); - private: - - uint32 recordSize; - uint32 recordCount; - uint32 fieldCount; - uint32 stringSize; - uint32 *fieldsOffset; - unsigned char *data; - unsigned char *stringTable; - - DBCFileLoader(DBCFileLoader const& right) = delete; - DBCFileLoader& operator=(DBCFileLoader const& right) = delete; -}; -#endif diff --git a/src/server/shared/DataStores/DBCStore.h b/src/server/shared/DataStores/DBCStore.h deleted file mode 100644 index c53d2a563fe..00000000000 --- a/src/server/shared/DataStores/DBCStore.h +++ /dev/null @@ -1,325 +0,0 @@ -/* - * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2009 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 DBCSTORE_H -#define DBCSTORE_H - -#include "DBCFileLoader.h" -#include "DBStorageIterator.h" -#include "Log.h" -#include "Field.h" -#include "DatabaseWorkerPool.h" -#include "Implementation/WorldDatabase.h" -#include "DatabaseEnv.h" - -struct SqlDbc -{ - std::string const* formatString; - std::string const* indexName; - std::string sqlTableName; - int32 indexPos; - int32 sqlIndexPos; - SqlDbc(std::string const* _filename, std::string const* _format, std::string const* _idname, char const* fmt) - : formatString(_format), indexName (_idname), sqlIndexPos(0) - { - // Convert dbc file name to sql table name - sqlTableName = *_filename; - for (uint32 i = 0; i< sqlTableName.size(); ++i) - { - if (isalpha(sqlTableName[i])) - sqlTableName[i] = char(tolower(sqlTableName[i])); - else if (sqlTableName[i] == '.') - sqlTableName[i] = '_'; - } - - // Get sql index position - DBCFileLoader::GetFormatRecordSize(fmt, &indexPos); - if (indexPos >= 0) - { - uint32 uindexPos = uint32(indexPos); - for (uint32 x = 0; x < formatString->size(); ++x) - { - // Count only fields present in sql - if ((*formatString)[x] == FT_SQL_PRESENT) - { - if (x == uindexPos) - break; - ++sqlIndexPos; - } - } - } - } - -private: - SqlDbc(SqlDbc const& right) = delete; - SqlDbc& operator=(SqlDbc const& right) = delete; -}; - -template<class T> -class DBCStorage -{ - typedef std::list<char*> StringPoolList; - - public: - typedef DBStorageIterator<T> iterator; - - explicit DBCStorage(char const* f) - : fmt(f), nCount(0), fieldCount(0), dataTable(NULL) - { - indexTable.asT = NULL; - } - - ~DBCStorage() { Clear(); } - - T const* LookupEntry(uint32 id) const - { - return (id >= nCount) ? NULL : indexTable.asT[id]; - } - - T const* AssertEntry(uint32 id) const - { - T const* entry = LookupEntry(id); - ASSERT(entry); - return entry; - } - - uint32 GetNumRows() const { return nCount; } - char const* GetFormat() const { return fmt; } - uint32 GetFieldCount() const { return fieldCount; } - - bool Load(char const* fn, SqlDbc* sql) - { - DBCFileLoader dbc; - // Check if load was successful, only then continue - if (!dbc.Load(fn, fmt)) - return false; - - uint32 sqlRecordCount = 0; - uint32 sqlHighestIndex = 0; - Field* fields = NULL; - QueryResult result = QueryResult(NULL); - // Load data from sql - if (sql) - { - std::string query = "SELECT * FROM " + sql->sqlTableName; - if (sql->indexPos >= 0) - query +=" ORDER BY " + *sql->indexName + " DESC"; - query += ';'; - - - result = WorldDatabase.Query(query.c_str()); - if (result) - { - sqlRecordCount = uint32(result->GetRowCount()); - if (sql->indexPos >= 0) - { - fields = result->Fetch(); - sqlHighestIndex = fields[sql->sqlIndexPos].GetUInt32(); - } - - // Check if sql index pos is valid - if (int32(result->GetFieldCount() - 1) < sql->sqlIndexPos) - { - TC_LOG_ERROR("server.loading", "Invalid index pos for dbc:'%s'", sql->sqlTableName.c_str()); - return false; - } - } - } - - char* sqlDataTable = NULL; - fieldCount = dbc.GetCols(); - - dataTable = reinterpret_cast<T*>(dbc.AutoProduceData(fmt, nCount, indexTable.asChar, - sqlRecordCount, sqlHighestIndex, sqlDataTable)); - - stringPoolList.push_back(dbc.AutoProduceStrings(fmt, reinterpret_cast<char*>(dataTable))); - - // Insert sql data into arrays - if (result) - { - if (indexTable.asT) - { - uint32 offset = 0; - uint32 rowIndex = dbc.GetNumRows(); - do - { - if (!fields) - fields = result->Fetch(); - - if (sql->indexPos >= 0) - { - uint32 id = fields[sql->sqlIndexPos].GetUInt32(); - if (indexTable.asT[id]) - { - TC_LOG_ERROR("server.loading", "Index %d already exists in dbc:'%s'", id, sql->sqlTableName.c_str()); - return false; - } - - indexTable.asT[id] = reinterpret_cast<T*>(&sqlDataTable[offset]); - } - else - indexTable.asT[rowIndex]= reinterpret_cast<T*>(&sqlDataTable[offset]); - - uint32 columnNumber = 0; - uint32 sqlColumnNumber = 0; - - for (; columnNumber < sql->formatString->size(); ++columnNumber) - { - if ((*sql->formatString)[columnNumber] == FT_SQL_ABSENT) - { - switch (fmt[columnNumber]) - { - case FT_FLOAT: - *reinterpret_cast<float*>(&sqlDataTable[offset]) = 0.0f; - offset += 4; - break; - case FT_IND: - case FT_INT: - *reinterpret_cast<uint32*>(&sqlDataTable[offset]) = uint32(0); - offset += 4; - break; - case FT_BYTE: - *reinterpret_cast<uint8*>(&sqlDataTable[offset]) = uint8(0); - offset += 1; - break; - case FT_LONG: - *reinterpret_cast<uint64*>(&sqlDataTable[offset]) = uint64(0); - offset += 8; - break; - case FT_STRING: - // Beginning of the pool - empty string - *reinterpret_cast<char**>(&sqlDataTable[offset]) = stringPoolList.back(); - offset += sizeof(char*); - break; - } - } - else if ((*sql->formatString)[columnNumber] == FT_SQL_PRESENT) - { - bool validSqlColumn = true; - switch (fmt[columnNumber]) - { - case FT_FLOAT: - *reinterpret_cast<float*>(&sqlDataTable[offset]) = fields[sqlColumnNumber].GetFloat(); - offset += 4; - break; - case FT_IND: - case FT_INT: - *reinterpret_cast<uint32*>(&sqlDataTable[offset]) = fields[sqlColumnNumber].GetUInt32(); - offset += 4; - break; - case FT_BYTE: - *reinterpret_cast<uint8*>(&sqlDataTable[offset]) = fields[sqlColumnNumber].GetUInt8(); - offset += 1; - break; - case FT_LONG: - *reinterpret_cast<uint64*>(&sqlDataTable[offset]) = fields[sqlColumnNumber].GetUInt64(); - offset += 8; - break; - case FT_STRING: - TC_LOG_ERROR("server.loading", "Unsupported data type in table '%s' at char %d", sql->sqlTableName.c_str(), columnNumber); - return false; - case FT_SORT: - break; - default: - validSqlColumn = false; - break; - } - if (validSqlColumn && (columnNumber != (sql->formatString->size()-1))) - sqlColumnNumber++; - } - else - { - TC_LOG_ERROR("server.loading", "Incorrect sql format string '%s' at char %d", sql->sqlTableName.c_str(), columnNumber); - return false; - } - } - - if (sqlColumnNumber != (result->GetFieldCount() - 1)) - { - TC_LOG_ERROR("server.loading", "SQL and DBC format strings are not matching for table: '%s'", sql->sqlTableName.c_str()); - return false; - } - - fields = NULL; - ++rowIndex; - } while (result->NextRow()); - } - } - - // error in dbc file at loading if NULL - return indexTable.asT != NULL; - } - - bool LoadStringsFrom(char const* fn) - { - // DBC must be already loaded using Load - if (!indexTable.asT) - return false; - - DBCFileLoader dbc; - // Check if load was successful, only then continue - if (!dbc.Load(fn, fmt)) - return false; - - stringPoolList.push_back(dbc.AutoProduceStrings(fmt, reinterpret_cast<char*>(dataTable))); - - return true; - } - - void Clear() - { - if (!indexTable.asT) - return; - - delete[] reinterpret_cast<char*>(indexTable.asT); - indexTable.asT = NULL; - delete[] reinterpret_cast<char*>(dataTable); - dataTable = NULL; - - while (!stringPoolList.empty()) - { - delete[] stringPoolList.front(); - stringPoolList.pop_front(); - } - - nCount = 0; - } - - iterator begin() { return iterator(indexTable.asT, nCount); } - iterator end() { return iterator(indexTable.asT, nCount, nCount); } - - private: - char const* fmt; - uint32 nCount; - uint32 fieldCount; - - union - { - T** asT; - char** asChar; - } - indexTable; - - T* dataTable; - StringPoolList stringPoolList; - - DBCStorage(DBCStorage const& right) = delete; - DBCStorage& operator=(DBCStorage const& right) = delete; -}; - -#endif diff --git a/src/server/shared/DataStores/DBStorageIterator.h b/src/server/shared/DataStores/DBStorageIterator.h index 8148a2a5300..5568397b245 100644 --- a/src/server/shared/DataStores/DBStorageIterator.h +++ b/src/server/shared/DataStores/DBStorageIterator.h @@ -35,8 +35,8 @@ public: } } - T* operator->() { return _index[_pos]; } - T* operator*() { 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); } |
