diff options
-rw-r--r-- | src/server/database/Database/Implementation/HotfixDatabase.cpp | 6 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/HotfixDatabase.h | 4 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.cpp | 6 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.h | 5 | ||||
-rw-r--r-- | src/server/shared/DataStores/DB2SparseStorageLoader.cpp | 709 | ||||
-rw-r--r-- | src/server/shared/DataStores/DB2SparseStorageLoader.h | 123 | ||||
-rw-r--r-- | src/server/shared/DataStores/DB2StorageLoader.cpp | 934 | ||||
-rw-r--r-- | src/server/shared/DataStores/DB2StorageLoader.h | 177 | ||||
-rw-r--r-- | src/server/shared/DataStores/DB2Store.h | 116 |
9 files changed, 748 insertions, 1332 deletions
diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index beff06ff757..71a874d7198 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -378,13 +378,13 @@ void HotfixDatabaseConnection::DoPrepareStatements() PrepareStatement(HOTFIX_SEL_ITEM_BONUS_TREE_NODE, "SELECT ID, BonusTreeID, SubTreeID, BonusListID, BonusTreeModID FROM item_bonus_tree_node" " ORDER BY ID DESC", CONNECTION_SYNCH); + // ItemChildEquipment.db2 + PrepareStatement(HOTFIX_SEL_ITEM_CHILD_EQUIPMENT, "SELECT ID, ItemID, AltItemID, AltEquipmentSlot FROM item_child_equipment ORDER BY ID DESC", CONNECTION_SYNCH); + // ItemClass.db2 PrepareStatement(HOTFIX_SEL_ITEM_CLASS, "SELECT ID, PriceMod, Name, Flags FROM item_class ORDER BY ID DESC", CONNECTION_SYNCH); PREPARE_LOCALE_STMT(HOTFIX_SEL_ITEM_CLASS, "SELECT ID, Name_lang FROM item_class_locale WHERE locale = ?", CONNECTION_SYNCH); - // ItemChildEquipment.db2 - PrepareStatement(HOTFIX_SEL_ITEM_CHILD_EQUIPMENT, "SELECT ID, ItemID, AltItemID, AltEquipmentSlot FROM item_child_equipment ORDER BY ID DESC", CONNECTION_SYNCH); - // ItemCurrencyCost.db2 PrepareStatement(HOTFIX_SEL_ITEM_CURRENCY_COST, "SELECT ID, ItemId FROM item_currency_cost ORDER BY ID DESC", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h index 4fc4d796f58..66d9d65cb07 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.h +++ b/src/server/database/Database/Implementation/HotfixDatabase.h @@ -215,11 +215,11 @@ enum HotfixDatabaseStatements HOTFIX_SEL_ITEM_BONUS_TREE_NODE, + HOTFIX_SEL_ITEM_CHILD_EQUIPMENT, + HOTFIX_SEL_ITEM_CLASS, HOTFIX_SEL_ITEM_CLASS_LOCALE, - HOTFIX_SEL_ITEM_CHILD_EQUIPMENT, - HOTFIX_SEL_ITEM_CURRENCY_COST, HOTFIX_SEL_ITEM_DAMAGE_AMMO, diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 050fda2fa25..f86cc7da260 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -100,8 +100,8 @@ DB2Storage<ItemArmorTotalEntry> sItemArmorTotalStore("ItemArmorT DB2Storage<ItemBagFamilyEntry> sItemBagFamilyStore("ItemBagFamily.db2", ItemBagFamilyMeta::Instance(), HOTFIX_SEL_ITEM_BAG_FAMILY); DB2Storage<ItemBonusEntry> sItemBonusStore("ItemBonus.db2", ItemBonusMeta::Instance(), HOTFIX_SEL_ITEM_BONUS); DB2Storage<ItemBonusTreeNodeEntry> sItemBonusTreeNodeStore("ItemBonusTreeNode.db2", ItemBonusTreeNodeMeta::Instance(), HOTFIX_SEL_ITEM_BONUS_TREE_NODE); -DB2Storage<ItemClassEntry> sItemClassStore("ItemClass.db2", ItemClassMeta::Instance(), HOTFIX_SEL_ITEM_CLASS); DB2Storage<ItemChildEquipmentEntry> sItemChildEquipmentStore("ItemChildEquipment.db2", ItemChildEquipmentMeta::Instance(), HOTFIX_SEL_ITEM_CHILD_EQUIPMENT); +DB2Storage<ItemClassEntry> sItemClassStore("ItemClass.db2", ItemClassMeta::Instance(), HOTFIX_SEL_ITEM_CLASS); DB2Storage<ItemCurrencyCostEntry> sItemCurrencyCostStore("ItemCurrencyCost.db2", ItemCurrencyCostMeta::Instance(), HOTFIX_SEL_ITEM_CURRENCY_COST); DB2Storage<ItemDamageAmmoEntry> sItemDamageAmmoStore("ItemDamageAmmo.db2", ItemDamageAmmoMeta::Instance(), HOTFIX_SEL_ITEM_DAMAGE_AMMO); DB2Storage<ItemDamageOneHandEntry> sItemDamageOneHandStore("ItemDamageOneHand.db2", ItemDamageOneHandMeta::Instance(), HOTFIX_SEL_ITEM_DAMAGE_ONE_HAND); @@ -120,7 +120,7 @@ DB2Storage<ItemRandomSuffixEntry> sItemRandomSuffixStore("ItemRand DB2Storage<ItemSearchNameEntry> sItemSearchNameStore("ItemSearchName.db2", ItemSearchNameMeta::Instance(), HOTFIX_SEL_ITEM_SEARCH_NAME); DB2Storage<ItemSetEntry> sItemSetStore("ItemSet.db2", ItemSetMeta::Instance(), HOTFIX_SEL_ITEM_SET); DB2Storage<ItemSetSpellEntry> sItemSetSpellStore("ItemSetSpell.db2", ItemSetSpellMeta::Instance(), HOTFIX_SEL_ITEM_SET_SPELL); -DB2SparseStorage<ItemSparseEntry> sItemSparseStore("Item-sparse.db2", ItemSparseMeta::Instance(), HOTFIX_SEL_ITEM_SPARSE); +DB2Storage<ItemSparseEntry> sItemSparseStore("Item-sparse.db2", ItemSparseMeta::Instance(), HOTFIX_SEL_ITEM_SPARSE); DB2Storage<ItemSpecEntry> sItemSpecStore("ItemSpec.db2", ItemSpecMeta::Instance(), HOTFIX_SEL_ITEM_SPEC); DB2Storage<ItemSpecOverrideEntry> sItemSpecOverrideStore("ItemSpecOverride.db2", ItemSpecOverrideMeta::Instance(), HOTFIX_SEL_ITEM_SPEC_OVERRIDE); DB2Storage<ItemUpgradeEntry> sItemUpgradeStore("ItemUpgrade.db2", ItemUpgradeMeta::Instance(), HOTFIX_SEL_ITEM_UPGRADE); @@ -362,8 +362,8 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale) LOAD_DB2(sItemBagFamilyStore); LOAD_DB2(sItemBonusStore); LOAD_DB2(sItemBonusTreeNodeStore); - LOAD_DB2(sItemClassStore); LOAD_DB2(sItemChildEquipmentStore); + LOAD_DB2(sItemClassStore); LOAD_DB2(sItemCurrencyCostStore); LOAD_DB2(sItemDamageAmmoStore); LOAD_DB2(sItemDamageOneHandStore); diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index efe9bef5754..35550f9ce0d 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -108,7 +108,7 @@ TC_GAME_API extern DB2Storage<ItemRandomSuffixEntry> sItemRandomS TC_GAME_API extern DB2Storage<ItemSearchNameEntry> sItemSearchNameStore; TC_GAME_API extern DB2Storage<ItemSetEntry> sItemSetStore; TC_GAME_API extern DB2Storage<ItemSetSpellEntry> sItemSetSpellStore; -TC_GAME_API extern DB2SparseStorage<ItemSparseEntry> sItemSparseStore; +TC_GAME_API extern DB2Storage<ItemSparseEntry> sItemSparseStore; TC_GAME_API extern DB2Storage<ItemSpecEntry> sItemSpecStore; TC_GAME_API extern DB2Storage<ItemSpecOverrideEntry> sItemSpecOverrideStore; TC_GAME_API extern DB2Storage<ItemUpgradeEntry> sItemUpgradeStore; @@ -304,7 +304,8 @@ public: std::vector<QuestPackageItemEntry const*> const* GetQuestPackageItems(uint32 questPackageID) const; uint32 GetQuestUniqueBitFlag(uint32 questId); uint32 GetRulesetItemUpgrade(uint32 itemId) const; - SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_); std::vector<SpecializationSpellsEntry const*> const* GetSpecializationSpells(uint32 specId) const; + SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_); + std::vector<SpecializationSpellsEntry const*> const* GetSpecializationSpells(uint32 specId) const; std::vector<SpellPowerEntry const*> GetSpellPowers(uint32 spellId, Difficulty difficulty = DIFFICULTY_NONE, bool* hasDifficultyPowers = nullptr) const; std::vector<SpellProcsPerMinuteModEntry const*> GetSpellProcsPerMinuteMods(uint32 spellprocsPerMinuteId) const; std::vector<TalentEntry const*> const& GetTalentsByPosition(uint32 class_, uint32 tier, uint32 column) const; diff --git a/src/server/shared/DataStores/DB2SparseStorageLoader.cpp b/src/server/shared/DataStores/DB2SparseStorageLoader.cpp deleted file mode 100644 index 5b93d588965..00000000000 --- a/src/server/shared/DataStores/DB2SparseStorageLoader.cpp +++ /dev/null @@ -1,709 +0,0 @@ -/* - * 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); - - ASSERT((metaFlags & 0x1) != 0, "%s is not a sparse storage, use DB2Storage!", filename); - - 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) -{ - 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 = 0; - 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 deleted file mode 100644 index 0936947ea6a..00000000000 --- a/src/server/shared/DataStores/DB2SparseStorageLoader.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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 e1f02a350af..d63217e5e1c 100644 --- a/src/server/shared/DataStores/DB2StorageLoader.cpp +++ b/src/server/shared/DataStores/DB2StorageLoader.cpp @@ -15,205 +15,257 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "Common.h" #include "DB2StorageLoader.h" #include "Database/DatabaseEnv.h" -#include "Log.h" +#include "DB2Meta.h" +#include "Utilities/ByteConverter.h" -DB2FileLoader::DB2FileLoader() +class DB2FileLoaderImpl { - fileName = nullptr; - meta = nullptr; - - recordSize = 0; - recordCount = 0; - fieldCount = 0; - stringSize = 0; - tableHash = 0; - layoutHash = 0; - minIndex = 0; - maxIndex = 0; - localeMask = 0; - copyIdSize = 0; - - data = nullptr; - stringTable = nullptr; - idTable = nullptr; - idTableSize = 0; - copyTable = nullptr; - fields = nullptr; -} - -bool DB2FileLoader::Load(char const* filename, DB2Meta const* meta) +public: + virtual ~DB2FileLoaderImpl() { } + virtual bool Load(char const* filename, FILE* file, DB2Meta const* meta, DB2Header const* header) = 0; + virtual char* AutoProduceData(uint32& count, char**& indexTable, std::vector<char*>& stringPool) = 0; + virtual char* AutoProduceStrings(char* dataTable, uint32 locale) = 0; + virtual void AutoProduceRecordCopies(uint32 records, char** indexTable, char* dataTable) = 0; +}; + +class DB2FileLoaderRegularImpl : public DB2FileLoaderImpl { - 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; - } +public: + DB2FileLoaderRegularImpl(); + ~DB2FileLoaderRegularImpl(); - EndianConvert(recordCount); + bool Load(char const* filename, FILE* file, DB2Meta const* meta, DB2Header const* header) override; + char* AutoProduceData(uint32& count, char**& indexTable, std::vector<char*>& stringPool) override; + char* AutoProduceStrings(char* dataTable, uint32 locale) override; + void AutoProduceRecordCopies(uint32 records, char** indexTable, char* dataTable) override; - if (fread(&fieldCount, 4, 1, f) != 1) // Number of fields +private: + class Record { - 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 + public: + float getFloat(uint32 field, uint32 arrayIndex) const; + uint32 getUInt(uint32 field, uint32 arrayIndex) const; + uint8 getUInt8(uint32 field, uint32 arrayIndex) const; + uint16 getUInt16(uint32 field, uint32 arrayIndex) const; + char const* getString(uint32 field, uint32 arrayIndex) const; + + private: + uint16 GetOffset(uint32 field) const; + uint16 GetByteSize(uint32 field) const; + uint32 GetVarInt(uint32 field, uint16 size, uint32 arrayIndex) const; + + Record(DB2FileLoaderRegularImpl &file_, unsigned char *offset_); + unsigned char *offset; + DB2FileLoaderRegularImpl &file; + + friend class DB2FileLoaderRegularImpl; + }; + + // Get record by id + Record getRecord(size_t id); + +#pragma pack(push, 1) + struct FieldEntry { - fclose(f); - return false; - } - - EndianConvert(stringSize); - - if (fread(&tableHash, 4, 1, f) != 1) // Table hash + uint16 UnusedBits; + uint16 Offset; + }; +#pragma pack(pop) + + char const* fileName; + DB2Meta const* meta; + DB2Header const* header; + + unsigned char* data; + unsigned char* stringTable; + unsigned char* idTable; + uint32 idTableSize; + unsigned char* copyTable; + FieldEntry* fields; +}; + +class DB2FileLoaderSparseImpl : public DB2FileLoaderImpl +{ +public: + DB2FileLoaderSparseImpl(); + ~DB2FileLoaderSparseImpl(); + + bool Load(char const* filename, FILE* file, DB2Meta const* meta, DB2Header const* header) override; + char* AutoProduceData(uint32& records, char**& indexTable, std::vector<char*>& stringPool); + char* AutoProduceStrings(char* dataTable, uint32 locale) override; + void AutoProduceRecordCopies(uint32 /*records*/, char** /*indexTable*/, char* /*dataTable*/) override { } + +private: +#pragma pack(push, 1) + struct OffsetTableEntry { - fclose(f); - return false; - } - - EndianConvert(tableHash); - - if (fread(&layoutHash, 4, 1, f) != 1) // Build + uint32 FileOffset; + uint16 RecordSize; + }; + struct FieldEntry { - fclose(f); - return false; - } + uint16 UnusedBits; + uint16 Offset; + }; +#pragma pack(pop) + + char const* fileName; + DB2Meta const* meta; + DB2Header const* header; + FieldEntry* fields; + + uint32 dataStart; + unsigned char* data; + OffsetTableEntry* offsets; +}; + +float DB2FileLoaderRegularImpl::Record::getFloat(uint32 field, uint32 arrayIndex) const +{ + ASSERT(field < file.header->FieldCount); + float val = *reinterpret_cast<float*>(offset + GetOffset(field) + arrayIndex * sizeof(float)); + EndianConvert(val); + return val; +} - if (layoutHash != meta->LayoutHash) - { - fclose(f); - return false; - } +uint32 DB2FileLoaderRegularImpl::Record::getUInt(uint32 field, uint32 arrayIndex) const +{ + ASSERT(field < file.header->FieldCount); + return GetVarInt(field, GetByteSize(field), arrayIndex); +} - EndianConvert(layoutHash); +uint8 DB2FileLoaderRegularImpl::Record::getUInt8(uint32 field, uint32 arrayIndex) const +{ + ASSERT(field < file.header->FieldCount); + ASSERT(GetByteSize(field) == 1); + return *reinterpret_cast<uint8*>(offset + GetOffset(field) + arrayIndex * sizeof(uint8)); +} - if (fread(&minIndex, 4, 1, f) != 1) // MinIndex WDB2 - { - fclose(f); - return false; - } +uint16 DB2FileLoaderRegularImpl::Record::getUInt16(uint32 field, uint32 arrayIndex) const +{ + ASSERT(field < file.header->FieldCount); + ASSERT(GetByteSize(field) == 2); + uint16 val = *reinterpret_cast<uint16*>(offset + GetOffset(field) + arrayIndex * sizeof(uint16)); + EndianConvert(val); + return val; +} - EndianConvert(minIndex); +char const* DB2FileLoaderRegularImpl::Record::getString(uint32 field, uint32 arrayIndex) const +{ + ASSERT(field < file.header->FieldCount); + uint32 stringOffset = *reinterpret_cast<uint32*>(offset + GetOffset(field) + arrayIndex * sizeof(uint32)); + EndianConvert(stringOffset); + ASSERT(stringOffset < file.header->StringTableSize); + return reinterpret_cast<char*>(file.stringTable + stringOffset); +} - if (fread(&maxIndex, 4, 1, f) != 1) // MaxIndex WDB2 - { - fclose(f); - return false; - } +uint16 DB2FileLoaderRegularImpl::Record::GetOffset(uint32 field) const +{ + ASSERT(field < file.header->FieldCount); + return file.fields[field].Offset; +} - EndianConvert(maxIndex); +uint16 DB2FileLoaderRegularImpl::Record::GetByteSize(uint32 field) const +{ + ASSERT(field < file.header->FieldCount); + return 4 - file.fields[field].UnusedBits / 8; +} - if (fread(&localeMask, 4, 1, f) != 1) // Locales +uint32 DB2FileLoaderRegularImpl::Record::GetVarInt(uint32 field, uint16 size, uint32 arrayIndex) const +{ + ASSERT(field < file.header->FieldCount); + switch (size) { - fclose(f); - return false; + 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; } - EndianConvert(localeMask); - - if (fread(©IdSize, 4, 1, f) != 1) - { - fclose(f); - return false; - } + ASSERT(false, "GetByteSize(field) < 4"); + return 0; +} - EndianConvert(copyIdSize); +DB2FileLoaderRegularImpl::Record::Record(DB2FileLoaderRegularImpl &file_, unsigned char *offset_) : offset(offset_), file(file_) +{ - if (fread(&metaFlags, 4, 1, f) != 1) - { - fclose(f); - return false; - } +} - EndianConvert(metaFlags); +DB2FileLoaderRegularImpl::DB2FileLoaderRegularImpl() +{ + fileName = nullptr; + meta = nullptr; + header = nullptr; + data = nullptr; + stringTable = nullptr; + idTable = nullptr; + idTableSize = 0; + copyTable = nullptr; + fields = nullptr; +} - ASSERT((metaFlags & 0x1) == 0, "%s is a sparse storage, use DB2SparseStorage!", filename); - ASSERT((meta->IndexField == -1) || (meta->IndexField == int32(metaFlags >> 16))); +bool DB2FileLoaderRegularImpl::Load(char const* filename, FILE* file, DB2Meta const* meta_, DB2Header const* header_) +{ + meta = meta_; + header = header_; + ASSERT(meta->IndexField == -1 || meta->IndexField == int32(header->IndexField)); - fields = new FieldEntry[fieldCount]; - if (fread(fields, fieldCount * sizeof(FieldEntry), 1, f) != 1) - { - fclose(f); + fileName = filename; + fields = new FieldEntry[header->FieldCount]; + if (fread(fields, header->FieldCount * sizeof(FieldEntry), 1, file) != 1) return false; - } if (!meta->HasIndexFieldInData()) - idTableSize = recordCount * sizeof(uint32); + idTableSize = header->RecordCount * sizeof(uint32); - data = new unsigned char[recordSize * recordCount + stringSize]; - stringTable = data + recordSize * recordCount; + data = new unsigned char[header->RecordSize * header->RecordCount + header->StringTableSize]; + stringTable = data + header->RecordSize * header->RecordCount; - if (fread(data, recordSize * recordCount + stringSize, 1, f) != 1) - { - fclose(f); + if (fread(data, header->RecordSize * header->RecordCount + header->StringTableSize, 1, file) != 1) return false; - } if (idTableSize) { idTable = new unsigned char[idTableSize]; - if (fread(idTable, idTableSize, 1, f) != 1) - { - fclose(f); + if (fread(idTable, idTableSize, 1, file) != 1) return false; - } } - if (copyIdSize) + if (header->CopyTableSize) { - copyTable = new unsigned char[copyIdSize]; - if (fread(copyTable, copyIdSize, 1, f) != 1) - { - fclose(f); + copyTable = new unsigned char[header->CopyTableSize]; + if (fread(copyTable, header->CopyTableSize, 1, file) != 1) return false; - } } - fclose(f); return true; } -DB2FileLoader::~DB2FileLoader() +DB2FileLoaderRegularImpl::~DB2FileLoaderRegularImpl() { delete[] data; delete[] idTable; @@ -221,16 +273,17 @@ DB2FileLoader::~DB2FileLoader() delete[] fields; } -DB2FileLoader::Record DB2FileLoader::getRecord(size_t id) +DB2FileLoaderRegularImpl::Record DB2FileLoaderRegularImpl::getRecord(size_t id) { assert(data); - return Record(*this, data + id * recordSize); + return Record(*this, data + id * header->RecordSize); } -char* DB2FileLoader::AutoProduceData(uint32& records, char**& indexTable) +static char const* const nullStr = ""; + +char* DB2FileLoaderRegularImpl::AutoProduceData(uint32& records, char**& indexTable, std::vector<char*>& stringPool) { - typedef char* ptr; - if (meta->FieldCount != fieldCount) + if (meta->FieldCount != header->FieldCount) return NULL; //get struct size and index pos @@ -241,7 +294,7 @@ char* DB2FileLoader::AutoProduceData(uint32& records, char**& indexTable) //find max index if (!idTableSize) { - for (uint32 y = 0; y < recordCount; ++y) + for (uint32 y = 0; y < header->RecordCount; ++y) { uint32 ind = getRecord(y).getUInt(indexField, 0); if (ind > maxi) @@ -250,7 +303,7 @@ char* DB2FileLoader::AutoProduceData(uint32& records, char**& indexTable) } else { - for (uint32 y = 0; y < recordCount; ++y) + for (uint32 y = 0; y < header->RecordCount; ++y) { uint32 ind = ((uint32*)idTable)[y]; if (ind > maxi) @@ -258,23 +311,44 @@ char* DB2FileLoader::AutoProduceData(uint32& records, char**& indexTable) } } - for (uint32 y = 0; y < copyIdSize; y += 8) + for (uint32 y = 0; y < header->CopyTableSize; y += 8) { uint32 ind = *((uint32*)(copyTable + y)); if (ind > maxi) maxi = ind; } + using index_entry_t = char*; + ++maxi; records = maxi; - indexTable = new ptr[maxi]; - memset(indexTable, 0, maxi * sizeof(ptr)); + indexTable = new index_entry_t[maxi]; + memset(indexTable, 0, maxi * sizeof(index_entry_t)); + + char* dataTable = new char[(header->RecordCount + (header->CopyTableSize / 8)) * recordsize]; + + // we store flat holders pool as single memory block + std::size_t stringFields = meta->GetStringFieldCount(false); + std::size_t localizedStringFields = meta->GetStringFieldCount(true); - char* dataTable = new char[(recordCount + (copyIdSize / 8)) * recordsize]; + // each string field at load have array of string for each locale + std::size_t stringHoldersRecordPoolSize = localizedStringFields * sizeof(LocalizedString) + (stringFields - localizedStringFields) * sizeof(char*); + char* stringHoldersPool = nullptr; + if (stringFields) + { + std::size_t stringHoldersPoolSize = stringHoldersRecordPoolSize * header->RecordCount; + + 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; + } uint32 offset = 0; - for (uint32 y = 0; y < recordCount; y++) + for (uint32 y = 0; y < header->RecordCount; y++) { Record rec = getRecord(y); uint32 indexVal = meta->HasIndexFieldInData() ? rec.getUInt(indexField, 0) : ((uint32*)idTable)[y]; @@ -287,7 +361,9 @@ char* DB2FileLoader::AutoProduceData(uint32& records, char**& indexTable) offset += 4; } - for (uint32 x = 0; x < fieldCount; ++x) + uint32 stringFieldOffset = 0; + + for (uint32 x = 0; x < header->FieldCount; ++x) { for (uint32 z = 0; z < meta->ArraySizes[x]; ++z) { @@ -311,9 +387,18 @@ char* DB2FileLoader::AutoProduceData(uint32& records, char**& indexTable) break; case FT_STRING: case FT_STRING_NOT_LOCALIZED: - *((char**)(&dataTable[offset])) = nullptr; // will be replaced by non-empty or "" strings in AutoProduceStrings + { + // 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; + } default: ASSERT(false, "Unknown format character '%c' found in %s meta", meta->Types[x], fileName); break; @@ -325,67 +410,314 @@ char* DB2FileLoader::AutoProduceData(uint32& records, char**& indexTable) return dataTable; } -static char const* const nullStr = ""; - -char* DB2FileLoader::AutoProduceStringsArrayHolders(char* dataTable) +char* DB2FileLoaderRegularImpl::AutoProduceStrings(char* dataTable, uint32 locale) { - if (meta->FieldCount != fieldCount) + if (meta->FieldCount != header->FieldCount) return nullptr; - // we store flat holders pool as single memory block - std::size_t stringFields = meta->GetStringFieldCount(false); - if (!stringFields) + if (!(header->Locale & (1 << locale))) + { + char const* sep = ""; + std::ostringstream str; + for (uint32 i = 0; i < TOTAL_LOCALES; ++i) + { + if (header->Locale & (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; + } + + char* stringPool = new char[header->StringTableSize]; + memcpy(stringPool, stringTable, header->StringTableSize); + + uint32 offset = 0; + + for (uint32 y = 0; y < header->RecordCount; y++) + { + if (!meta->HasIndexFieldInData()) + offset += 4; + + for (uint32 x = 0; x < header->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**)(&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, 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; + } + } + } + } + + return stringPool; +} + +void DB2FileLoaderRegularImpl::AutoProduceRecordCopies(uint32 records, char** indexTable, char* dataTable) +{ + uint32 recordsize = meta->GetRecordSize(); + uint32 offset = header->RecordCount * recordsize; + uint32* copyIds = (uint32*)copyTable; + for (uint32 c = 0; c < header->CopyTableSize / 4; c += 2) + { + uint32 to = copyIds[c]; + uint32 from = copyIds[c + 1]; + + if (from && from < records && to < 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; + } + } +} + +DB2FileLoaderSparseImpl::DB2FileLoaderSparseImpl() +{ + fileName = nullptr; + meta = nullptr; + header = nullptr; + fields = nullptr; + dataStart = 0; + data = nullptr; + offsets = nullptr; +} + +bool DB2FileLoaderSparseImpl::Load(char const* filename, FILE* file, DB2Meta const* meta_, DB2Header const* header_) +{ + meta = meta_; + header = header_; + fileName = filename; + + fields = new FieldEntry[header->FieldCount]; + if (fread(fields, header->FieldCount * sizeof(FieldEntry), 1, file) != 1) + return false; + + dataStart = ftell(file); + + data = new unsigned char[header->StringTableSize - dataStart]; + + if (fread(data, header->StringTableSize - dataStart, 1, file) != 1) + return false; + + offsets = new OffsetTableEntry[header->MaxId - header->MinId + 1]; + if (fread(offsets, (header->MaxId - header->MinId + 1) * 6, 1, file) != 1) + return false; + + return true; +} + +DB2FileLoaderSparseImpl::~DB2FileLoaderSparseImpl() +{ + delete[] data; + delete[] offsets; + delete[] fields; +} + +char* DB2FileLoaderSparseImpl::AutoProduceData(uint32& maxId, char**& indexTable, std::vector<char*>& stringPool) +{ + if (meta->FieldCount != header->FieldCount) + return NULL; + + //get struct size and index pos + uint32 recordsize = meta->GetRecordSize(); + + uint32 offsetCount = header->MaxId - header->MinId + 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; + } + } + + using index_entry_t = char*; + maxId = header->MaxId + 1; + indexTable = new index_entry_t[maxId]; + memset(indexTable, 0, maxId * sizeof(index_entry_t)); + + 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 * recordCount; + 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; - uint32 offset = 0; + 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; - // assign string holders to string field slots - for (uint32 y = 0; y < recordCount; y++) + uint32 offset = 0; + uint32 recordNum = 0; + for (uint32 y = 0; y < offsetCount; ++y) { + if (!offsets[y].FileOffset || !offsets[y].RecordSize) + continue; + + indexTable[y + header->MinId] = &dataTable[offset]; + ASSERT(indexTable[y + header->MinId]); + std::size_t fieldOffset = 0; uint32 stringFieldOffset = 0; if (!meta->HasIndexFieldInData()) + { + *((uint32*)(&dataTable[offset])) = y + header->MinId; offset += 4; + } - for (uint32 x = 0; x < fieldCount; ++x) + for (uint32 x = 0; x < header->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 = 0; + 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]); + for (uint32 locale = 0; locale < TOTAL_LOCALES; ++locale) + if (header->Locale & (1 << locale)) + (*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: { - // 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*); - + *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; } @@ -395,24 +727,25 @@ char* DB2FileLoader::AutoProduceStringsArrayHolders(char* dataTable) } } } + + ++recordNum; } - //send as char* for store in char* pool list for free at unload - return stringHoldersPool; + return dataTable; } -char* DB2FileLoader::AutoProduceStrings(char* dataTable, uint32 locale) +char* DB2FileLoaderSparseImpl::AutoProduceStrings(char* dataTable, uint32 locale) { - if (meta->FieldCount != fieldCount) + if (meta->FieldCount != header->FieldCount) return nullptr; - if (!(localeMask & (1 << locale))) + if (!(header->Locale & (1 << locale))) { char const* sep = ""; std::ostringstream str; for (uint32 i = 0; i < TOTAL_LOCALES; ++i) { - if (localeMask & (1 << i)) + if (header->Locale & (1 << i)) { str << sep << localeNames[i]; sep = ", "; @@ -423,50 +756,64 @@ char* DB2FileLoader::AutoProduceStrings(char* dataTable, uint32 locale) return nullptr; } - char* stringPool = new char[stringSize]; - memcpy(stringPool, stringTable, stringSize); + uint32 offsetCount = header->MaxId - header->MinId + 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[header->StringTableSize - dataStart - records * ((recordsize - (!meta->HasIndexFieldInData() ? 4 : 0)) - stringFields * sizeof(char*))]; + memset(stringTable, 0, header->StringTableSize - dataStart - records * ((recordsize - (!meta->HasIndexFieldInData() ? 4 : 0)) - stringFields * sizeof(char*))); + char* stringPtr = stringTable; uint32 offset = 0; - for (uint32 y = 0; y < recordCount; y++) + for (uint32 y = 0; y < offsetCount; y++) { + if (!offsets[y].FileOffset || !offsets[y].RecordSize) + continue; + if (!meta->HasIndexFieldInData()) offset += 4; - for (uint32 x = 0; x < fieldCount; ++x) + std::size_t fieldOffset = 0; + for (uint32 x = 0; x < header->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: { - // 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); - } - + db2str->Str[locale] = stringPtr; + strcpy(stringPtr, (char*)&data[offsets[y].FileOffset - dataStart + fieldOffset]); + fieldOffset += strlen(stringPtr) + 1; + stringPtr += strlen(stringPtr) + 1; offset += sizeof(char*); break; } case FT_STRING_NOT_LOCALIZED: { - char** db2str = (char**)(&dataTable[offset]); - char const* st = getRecord(y).getString(x, z); - *db2str = stringPool + (st - (char const*)stringTable); + fieldOffset += strlen((char*)&data[offsets[y].FileOffset - dataStart + fieldOffset]) + 1; offset += sizeof(char*); break; } @@ -478,32 +825,7 @@ char* DB2FileLoader::AutoProduceStrings(char* dataTable, uint32 locale) } } - return 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; - } - } + return stringTable; } char* DB2DatabaseLoader::Load(HotfixDatabaseStatements preparedStatement, uint32& records, char**& indexTable, char*& stringHolders, std::vector<char*>& stringPool) @@ -770,3 +1092,151 @@ char* DB2DatabaseLoader::AddString(char const** holder, std::string const& value return nullptr; } + +DB2FileLoader::DB2FileLoader() : _impl(nullptr) +{ +} + +DB2FileLoader::~DB2FileLoader() +{ + delete _impl; +} + +bool DB2FileLoader::Load(char const* filename, DB2Meta const* meta) +{ + FILE* f = fopen(filename, "rb"); + if (!f) + return false; + + if (fread(&_header.Signature, 4, 1, f) != 1) // Signature + { + fclose(f); + return false; + } + + EndianConvert(_header.Signature); + + if (_header.Signature != 0x35424457) + { + fclose(f); + return false; //'WDB5' + } + + if (fread(&_header.RecordCount, 4, 1, f) != 1) // Number of records + { + fclose(f); + return false; + } + + EndianConvert(_header.RecordCount); + + if (fread(&_header.FieldCount, 4, 1, f) != 1) // Number of fields + { + fclose(f); + return false; + } + + EndianConvert(_header.FieldCount); + + if (fread(&_header.RecordSize, 4, 1, f) != 1) // Size of a record + { + fclose(f); + return false; + } + + EndianConvert(_header.RecordSize); + + if (fread(&_header.StringTableSize, 4, 1, f) != 1) // String size + { + fclose(f); + return false; + } + + EndianConvert(_header.StringTableSize); + + if (fread(&_header.TableHash, 4, 1, f) != 1) // Table hash + { + fclose(f); + return false; + } + + EndianConvert(_header.TableHash); + + if (fread(&_header.LayoutHash, 4, 1, f) != 1) // Layout hash + { + fclose(f); + return false; + } + + EndianConvert(_header.LayoutHash); + + if (_header.LayoutHash != meta->LayoutHash) + { + fclose(f); + return false; + } + + if (fread(&_header.MinId, 4, 1, f) != 1) // MinIndex WDB2 + { + fclose(f); + return false; + } + + EndianConvert(_header.MinId); + + if (fread(&_header.MaxId, 4, 1, f) != 1) // MaxIndex WDB2 + { + fclose(f); + return false; + } + + EndianConvert(_header.MaxId); + + if (fread(&_header.Locale, 4, 1, f) != 1) // Locales + { + fclose(f); + return false; + } + + EndianConvert(_header.Locale); + + if (fread(&_header.CopyTableSize, 4, 1, f) != 1) + { + fclose(f); + return false; + } + + EndianConvert(_header.CopyTableSize); + + if (fread(&_header.Flags, 4, 1, f) != 1) + { + fclose(f); + return false; + } + + EndianConvert(_header.Flags); + + if (!(_header.Flags & 0x1)) + _impl = new DB2FileLoaderRegularImpl(); + else + _impl = new DB2FileLoaderSparseImpl(); + + bool result = _impl->Load(filename, f, meta, &_header); + fclose(f); + return result; +} + +char* DB2FileLoader::AutoProduceData(uint32& count, char**& indexTable, std::vector<char*>& stringPool) +{ + return _impl->AutoProduceData(count, indexTable, stringPool); +} + +char* DB2FileLoader::AutoProduceStrings(char* dataTable, uint32 locale) +{ + return _impl->AutoProduceStrings(dataTable, locale); +} + +void DB2FileLoader::AutoProduceRecordCopies(uint32 records, char** indexTable, char* dataTable) +{ + _impl->AutoProduceRecordCopies(records, indexTable, dataTable); +} diff --git a/src/server/shared/DataStores/DB2StorageLoader.h b/src/server/shared/DataStores/DB2StorageLoader.h index b302a1afe7d..4cf55becc8d 100644 --- a/src/server/shared/DataStores/DB2StorageLoader.h +++ b/src/server/shared/DataStores/DB2StorageLoader.h @@ -18,164 +18,49 @@ #ifndef DB2_FILE_LOADER_H #define DB2_FILE_LOADER_H -#include "DB2Meta.h" -#include "Utilities/ByteConverter.h" +#include "Define.h" #include "Implementation/HotfixDatabase.h" -#include "Errors.h" -#include <vector> + +class DB2FileLoaderImpl; +struct DB2Meta; + +#pragma pack(push, 1) +struct DB2Header +{ + uint32 Signature; + uint32 RecordCount; + uint32 FieldCount; + uint32 RecordSize; + uint32 StringTableSize; + uint32 TableHash; + uint32 LayoutHash; + uint32 MinId; + uint32 MaxId; + uint32 Locale; + uint32 CopyTableSize; + uint16 Flags; + int16 IndexField; +}; +#pragma pack(pop) class TC_SHARED_API DB2FileLoader { - public: +public: DB2FileLoader(); ~DB2FileLoader(); bool Load(char const* filename, DB2Meta const* meta); - - class Record - { - public: - float getFloat(uint32 field, uint32 arrayIndex) const - { - ASSERT(field < file.fieldCount); - float val = *reinterpret_cast<float*>(offset + GetOffset(field) + arrayIndex * sizeof(float)); - EndianConvert(val); - return val; - } - - uint32 getUInt(uint32 field, uint32 arrayIndex) const - { - ASSERT(field < file.fieldCount); - return GetVarInt(field, GetByteSize(field), arrayIndex); - } - - uint8 getUInt8(uint32 field, uint32 arrayIndex) const - { - ASSERT(field < file.fieldCount); - ASSERT(GetByteSize(field) == 1); - return *reinterpret_cast<uint8*>(offset + GetOffset(field) + arrayIndex * sizeof(uint8)); - } - - uint16 getUInt16(uint32 field, uint32 arrayIndex) const - { - ASSERT(field < file.fieldCount); - ASSERT(GetByteSize(field) == 2); - uint16 val = *reinterpret_cast<uint16*>(offset + GetOffset(field) + arrayIndex * sizeof(uint16)); - EndianConvert(val); - return val; - } - - char const* getString(uint32 field, uint32 arrayIndex) const - { - 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; - - friend class DB2FileLoader; - }; - - // Get record by id - Record getRecord(size_t id); - - 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(uint32& count, char**& indexTable); - char* AutoProduceStringsArrayHolders(char* dataTable); + char* AutoProduceData(uint32& count, char**& indexTable, std::vector<char*>& stringPool); 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) + uint32 GetCols() const { return _header.FieldCount; } + uint32 GetTableHash() const { return _header.TableHash; } + uint32 GetLayoutHash() const { return _header.LayoutHash; } - char const* fileName; - DB2Meta const* meta; - - // WDB2 / WCH2 fields - uint32 recordSize; - uint32 recordCount; - uint32 fieldCount; - uint32 stringSize; - 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; +private: + DB2FileLoaderImpl* _impl; + DB2Header _header; }; class TC_SHARED_API DB2DatabaseLoader diff --git a/src/server/shared/DataStores/DB2Store.h b/src/server/shared/DataStores/DB2Store.h index bf10d78f37b..0c63feba93c 100644 --- a/src/server/shared/DataStores/DB2Store.h +++ b/src/server/shared/DataStores/DB2Store.h @@ -19,8 +19,8 @@ #define DB2STORE_H #include "Common.h" +#include "DB2Meta.h" #include "DB2StorageLoader.h" -#include "DB2SparseStorageLoader.h" #include "DBStorageIterator.h" #include "ByteBuffer.h" @@ -163,17 +163,12 @@ public: _layoutHash = db2.GetLayoutHash(); // load raw non-string data - _dataTable = db2.AutoProduceData(_indexTableSize, _indexTable.AsChar); + _dataTable = db2.AutoProduceData(_indexTableSize, _indexTable.AsChar, _stringPool); - // create string holders for loaded string fields - if (char* stringHolders = db2.AutoProduceStringsArrayHolders(_dataTable)) - { - _stringPool.push_back(stringHolders); - - // load strings from db2 data + // load strings from db2 data + if (!_stringPool.empty()) if (char* stringBlock = db2.AutoProduceStrings(_dataTable, locale)) _stringPool.push_back(stringBlock); - } db2.AutoProduceRecordCopies(_indexTableSize, _indexTable.AsChar, _dataTable); @@ -227,107 +222,4 @@ private: 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 |