diff options
author | Shauren <shauren.trinity@gmail.com> | 2015-08-30 00:17:08 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2015-08-30 00:17:08 +0200 |
commit | b564c10b13effed63c7b7dae25117e2e0a2c64e4 (patch) | |
tree | ea79cf0c9b254aac517f1024602b39615eedb010 /src | |
parent | b0107802064a19d69ce06f7bcf8b8394695e6faf (diff) |
Core/Players: Implemented serverside validation of reserved/profane names
Closes #15357
New library dependency: Boost.Regex
Diffstat (limited to 'src')
-rw-r--r-- | src/common/Define.h | 1 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/HotfixDatabase.cpp | 9 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/HotfixDatabase.h | 6 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.cpp | 41 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.h | 4 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Structure.h | 20 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2fmt.h | 3 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.cpp | 4 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.h | 2 | ||||
-rw-r--r-- | src/server/game/Handlers/CharacterHandler.cpp | 8 | ||||
-rw-r--r-- | src/server/game/Tools/PlayerDump.cpp | 2 | ||||
-rw-r--r-- | src/server/scripts/Commands/cs_character.cpp | 4 | ||||
-rw-r--r-- | src/server/shared/DataStores/DB2StorageLoader.cpp | 94 | ||||
-rw-r--r-- | src/server/shared/DataStores/DB2StorageLoader.h | 6 | ||||
-rw-r--r-- | src/server/shared/DataStores/DB2Store.h | 17 |
16 files changed, 188 insertions, 35 deletions
diff --git a/src/common/Define.h b/src/common/Define.h index cf288c3053f..92962bfab79 100644 --- a/src/common/Define.h +++ b/src/common/Define.h @@ -111,6 +111,7 @@ enum DBCFormer FT_NA = 'x', //not used or unknown, 4 byte size FT_NA_BYTE = 'X', //not used or unknown, byte FT_STRING = 's', //char* + FT_STRING_NOT_LOCALIZED = 'S', //char* but without locale in DB2 FT_FLOAT = 'f', //float FT_INT = 'i', //uint32 FT_BYTE = 'b', //uint8 diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index ff3f8e906c7..927e7f623d8 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -291,6 +291,15 @@ void HotfixDatabaseConnection::DoPrepareStatements() PrepareStatement(HOTFIX_SEL_NAME_GEN, "SELECT ID, Name, Race, Sex FROM name_gen ORDER BY ID DESC", CONNECTION_SYNCH); PREPARE_LOCALE_STMT(HOTFIX_SEL_NAME_GEN, "SELECT ID, Name_lang FROM name_gen_locale WHERE locale = ?", CONNECTION_SYNCH); + // NamesProfanity.db2 + PrepareStatement(HOTFIX_SEL_NAMES_PROFANITY, "SELECT ID, Name, Language FROM names_profanity ORDER BY ID DESC", CONNECTION_SYNCH); + + // NamesReserved.db2 + PrepareStatement(HOTFIX_SEL_NAMES_RESERVED, "SELECT ID, Name FROM names_reserved ORDER BY ID DESC", CONNECTION_SYNCH); + + // NamesReservedLocale.db2 + PrepareStatement(HOTFIX_SEL_NAMES_RESERVED_LOCALE, "SELECT ID, Name, LocaleMask FROM names_reserved_locale ORDER BY ID DESC", CONNECTION_SYNCH); + // OverrideSpellData.db2 PrepareStatement(HOTFIX_SEL_OVERRIDE_SPELL_DATA, "SELECT ID, SpellID1, SpellID2, SpellID3, SpellID4, SpellID5, SpellID6, SpellID7, SpellID8, " "SpellID9, SpellID10, Flags, PlayerActionbarFileDataID FROM override_spell_data 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 abc584a8b20..c181ef9e9e6 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.h +++ b/src/server/database/Database/Implementation/HotfixDatabase.h @@ -168,6 +168,12 @@ enum HotfixDatabaseStatements HOTFIX_SEL_NAME_GEN, HOTFIX_SEL_NAME_GEN_LOCALE, + HOTFIX_SEL_NAMES_PROFANITY, + + HOTFIX_SEL_NAMES_RESERVED, + + HOTFIX_SEL_NAMES_RESERVED_LOCALE, + HOTFIX_SEL_OVERRIDE_SPELL_DATA, HOTFIX_SEL_PHASE_X_PHASE_GROUP, diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 88ed3989efa..66321a1c6b1 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -82,6 +82,9 @@ DB2Storage<MountCapabilityEntry> sMountCapabilityStore("MountCapa DB2Storage<MountEntry> sMountStore("Mount.db2", MountFormat, HOTFIX_SEL_MOUNT); DB2Storage<MountTypeXCapabilityEntry> sMountTypeXCapabilityStore("MountTypeXCapability.db2", MountTypeXCapabilityFormat, HOTFIX_SEL_MOUNT_TYPE_X_CAPABILITY); DB2Storage<NameGenEntry> sNameGenStore("NameGen.db2", NameGenFormat, HOTFIX_SEL_NAME_GEN); +DB2Storage<NamesProfanityEntry> sNamesProfanityStore("NamesProfanity.db2", NamesProfanityFormat, HOTFIX_SEL_NAMES_PROFANITY); +DB2Storage<NamesReservedEntry> sNamesReservedStore("NamesReserved.db2", NamesReservedFormat, HOTFIX_SEL_NAMES_RESERVED); +DB2Storage<NamesReservedLocaleEntry> sNamesReservedLocaleStore("NamesReservedLocale.db2", NamesReservedLocaleFormat, HOTFIX_SEL_NAMES_RESERVED_LOCALE); DB2Storage<OverrideSpellDataEntry> sOverrideSpellDataStore("OverrideSpellData.db2", OverrideSpellDataFormat, HOTFIX_SEL_OVERRIDE_SPELL_DATA); DB2Storage<PhaseXPhaseGroupEntry> sPhaseXPhaseGroupStore("PhaseXPhaseGroup.db2", PhaseXPhaseGroupFormat, HOTFIX_SEL_PHASE_X_PHASE_GROUP); DB2Storage<QuestMoneyRewardEntry> sQuestMoneyRewardStore("QuestMoneyReward.db2", QuestMoneyRewardFormat, HOTFIX_SEL_QUEST_MONEY_REWARD); @@ -244,6 +247,9 @@ void DB2Manager::LoadStores(std::string const& dataPath) LOAD_DB2(sMountStore); LOAD_DB2(sMountTypeXCapabilityStore); LOAD_DB2(sNameGenStore); + LOAD_DB2(sNamesProfanityStore); + LOAD_DB2(sNamesReservedStore); + LOAD_DB2(sNamesReservedLocaleStore); LOAD_DB2(sOverrideSpellDataStore); LOAD_DB2(sPhaseXPhaseGroupStore); LOAD_DB2(sQuestMoneyRewardStore); @@ -349,6 +355,27 @@ void DB2Manager::LoadStores(std::string const& dataPath) for (NameGenEntry const* entry : sNameGenStore) _nameGenData[entry->Race][entry->Sex].push_back(entry); + for (NamesProfanityEntry const* namesProfanity : sNamesProfanityStore) + { + ASSERT(namesProfanity->Language < TOTAL_LOCALES || namesProfanity->Language == -1); + if (namesProfanity->Language != -1) + _nameValidators[namesProfanity->Language].emplace_back(namesProfanity->Name, boost::regex::perl | boost::regex::icase | boost::regex::optimize); + else + for (uint32 i = 0; i < TOTAL_LOCALES; ++i) + _nameValidators[i].emplace_back(namesProfanity->Name, boost::regex::perl | boost::regex::icase | boost::regex::optimize); + } + + for (NamesReservedEntry const* namesReserved : sNamesReservedStore) + _nameValidators[TOTAL_LOCALES].emplace_back(namesReserved->Name, boost::regex::perl | boost::regex::icase | boost::regex::optimize); + + for (NamesReservedLocaleEntry const* namesReserved : sNamesReservedLocaleStore) + { + ASSERT(!(namesReserved->LocaleMask & ~((1 << TOTAL_LOCALES) - 1))); + for (uint32 i = 0; i < TOTAL_LOCALES; ++i) + if (namesReserved->LocaleMask & (1 << i)) + _nameValidators[i].emplace_back(namesReserved->Name, boost::regex::perl | boost::regex::icase | boost::regex::optimize); + } + for (PhaseXPhaseGroupEntry const* group : sPhaseXPhaseGroupStore) if (PhaseEntry const* phase = sPhaseStore.LookupEntry(group->PhaseID)) _phasesByGroup[group->PhaseGroupID].insert(phase->ID); @@ -711,6 +738,20 @@ DB2Manager::MountTypeXCapabilitySet const* DB2Manager::GetMountCapabilities(uint return nullptr; } +ResponseCodes DB2Manager::ValidateName(std::string const& name, LocaleConstant locale) const +{ + for (boost::regex const& regex : _nameValidators[locale]) + if (boost::regex_search(name, regex)) + return CHAR_NAME_PROFANE; + + // regexes at TOTAL_LOCALES are loaded from NamesReserved which is not locale specific + for (boost::regex const& regex : _nameValidators[TOTAL_LOCALES]) + if (boost::regex_search(name, regex)) + return CHAR_NAME_RESERVED; + + return CHAR_NAME_SUCCESS; +} + std::vector<QuestPackageItemEntry const*> const* DB2Manager::GetQuestPackageItems(uint32 questPackageID) const { auto itr = _questPackages.find(questPackageID); diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index 98c36fe4b1d..6ccf3933984 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -21,6 +21,7 @@ #include "DB2Store.h" #include "DB2Structure.h" #include "SharedDefines.h" +#include <boost/regex.hpp> #include <array> extern DB2Storage<AuctionHouseEntry> sAuctionHouseStore; @@ -142,6 +143,7 @@ public: typedef std::set<MountTypeXCapabilityEntry const*, MountTypeXCapabilityEntryComparator> MountTypeXCapabilitySet; typedef std::unordered_map<uint32, MountTypeXCapabilitySet> MountCapabilitiesByTypeContainer; typedef std::unordered_map<uint32, std::array<std::vector<NameGenEntry const*>, 2>> NameGenContainer; + typedef std::array<std::vector<boost::regex>, TOTAL_LOCALES + 1> NameValidationRegexContainer; typedef std::unordered_map<uint32, std::set<uint32>> PhaseGroupContainer; typedef std::unordered_map<uint32, std::vector<QuestPackageItemEntry const*>> QuestPackageItemContainer; typedef std::unordered_map<uint32, std::vector<SpecializationSpellsEntry const*>> SpecializationSpellsContainer; @@ -176,6 +178,7 @@ public: MountEntry const* GetMount(uint32 spellId) const; MountEntry const* GetMountById(uint32 id) const; MountTypeXCapabilitySet const* GetMountCapabilities(uint32 mountType) const; + ResponseCodes ValidateName(std::string const& name, LocaleConstant locale) const; std::vector<QuestPackageItemEntry const*> const* GetQuestPackageItems(uint32 questPackageID) const; uint32 GetQuestUniqueBitFlag(uint32 questId); std::set<uint32> GetPhasesForGroup(uint32 group) const; @@ -200,6 +203,7 @@ private: MountContainer _mountsBySpellId; MountCapabilitiesByTypeContainer _mountCapabilitiesByType; NameGenContainer _nameGenData; + NameValidationRegexContainer _nameValidators; PhaseGroupContainer _phasesByGroup; QuestPackageItemContainer _questPackages; SpecializationSpellsContainer _specializationSpellsBySpec; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index b8bb0f2a94d..b0b99b20959 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -727,6 +727,26 @@ struct NameGenEntry uint32 Sex; // 3 }; +struct NamesProfanityEntry +{ + uint32 ID; // 0 + char const* Name; // 1 + int32 Language; // 2 +}; + +struct NamesReservedEntry +{ + uint32 ID; // 0 + char const* Name; // 1 +}; + +struct NamesReservedLocaleEntry +{ + uint32 ID; // 0 + char const* Name; // 1 + uint32 LocaleMask; // 2 +}; + #define MAX_OVERRIDE_SPELL 10 struct OverrideSpellDataEntry diff --git a/src/server/game/DataStores/DB2fmt.h b/src/server/game/DataStores/DB2fmt.h index a9da1ae646a..6088ff82ec0 100644 --- a/src/server/game/DataStores/DB2fmt.h +++ b/src/server/game/DataStores/DB2fmt.h @@ -76,6 +76,9 @@ char const MountCapabilityFormat[] = "niiiiiii"; char const MountFormat[] = "niiiisssii"; char const MountTypeXCapabilityFormat[] = "niii"; char const NameGenFormat[] = "nsii"; +char const NamesProfanityFormat[] = "nSi"; +char const NamesReservedFormat[] = "nS"; +char const NamesReservedLocaleFormat[] = "nSi"; char const OverrideSpellDataFormat[] = "niiiiiiiiiiii"; char const PhaseXPhaseGroupFormat[] = "nii"; char const QuestMoneyRewardFormat[] = "niiiiiiiiii"; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 5003cd52d74..9012be79547 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -16717,7 +16717,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) m_name = fields[2].GetString(); // check name limitations - if (ObjectMgr::CheckPlayerName(m_name) != CHAR_NAME_SUCCESS || + if (ObjectMgr::CheckPlayerName(m_name, GetSession()->GetSessionDbcLocale()) != CHAR_NAME_SUCCESS || (!GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RESERVEDNAME) && sObjectMgr->IsReservedName(m_name))) { diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 459de1bdcd9..463158762c2 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -7509,7 +7509,7 @@ bool isValidString(const std::wstring& wstr, uint32 strictMask, bool numericOrSp return false; } -ResponseCodes ObjectMgr::CheckPlayerName(const std::string& name, bool create) +ResponseCodes ObjectMgr::CheckPlayerName(std::string const& name, LocaleConstant locale, bool create /*= false*/) { std::wstring wname; if (!Utf8toWStr(name, wname)) @@ -7531,7 +7531,7 @@ ResponseCodes ObjectMgr::CheckPlayerName(const std::string& name, bool create) if (wname[i] == wname[i-1] && wname[i] == wname[i-2]) return CHAR_NAME_THREE_CONSECUTIVE; - return CHAR_NAME_SUCCESS; + return sDB2Manager.ValidateName(name, locale); } bool ObjectMgr::IsValidCharterName(const std::string& name) diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 1ae3e113331..8e660e3f32c 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -1229,7 +1229,7 @@ class ObjectMgr bool IsReservedName(std::string const& name) const; // name with valid structure and symbols - static ResponseCodes CheckPlayerName(std::string const& name, bool create = false); + static ResponseCodes CheckPlayerName(std::string const& name, LocaleConstant locale, bool create = false); static PetNameInvalidReason CheckPetName(std::string const& name); static bool IsValidCharterName(std::string const& name); diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 3dc6b72157a..dfbd48f0dec 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -458,7 +458,7 @@ void WorldSession::HandleCharCreateOpcode(WorldPackets::Character::CreateCharact } // check name limitations - ResponseCodes res = ObjectMgr::CheckPlayerName(charCreate.CreateInfo->Name, true); + ResponseCodes res = ObjectMgr::CheckPlayerName(charCreate.CreateInfo->Name, GetSessionDbcLocale(), true); if (res != CHAR_NAME_SUCCESS) { SendCharCreate(res); @@ -1271,7 +1271,7 @@ void WorldSession::HandleCharRenameOpcode(WorldPackets::Character::CharacterRena return; } - ResponseCodes res = ObjectMgr::CheckPlayerName(request.RenameInfo->NewName, true); + ResponseCodes res = ObjectMgr::CheckPlayerName(request.RenameInfo->NewName, GetSessionDbcLocale(), true); if (res != CHAR_NAME_SUCCESS) { SendCharRename(res, request.RenameInfo.get()); @@ -1567,7 +1567,7 @@ void WorldSession::HandleCharCustomizeCallback(PreparedQueryResult result, World return; } - ResponseCodes res = ObjectMgr::CheckPlayerName(customizeInfo->CharName, true); + ResponseCodes res = ObjectMgr::CheckPlayerName(customizeInfo->CharName, GetSessionDbcLocale(), true); if (res != CHAR_NAME_SUCCESS) { SendCharCustomize(res, customizeInfo); @@ -1811,7 +1811,7 @@ void WorldSession::HandleCharRaceOrFactionChangeCallback(PreparedQueryResult res return; } - ResponseCodes res = ObjectMgr::CheckPlayerName(factionChangeInfo->Name, true); + ResponseCodes res = ObjectMgr::CheckPlayerName(factionChangeInfo->Name, GetSessionDbcLocale(), true); if (res != CHAR_NAME_SUCCESS) { SendCharFactionChange(res, factionChangeInfo); diff --git a/src/server/game/Tools/PlayerDump.cpp b/src/server/game/Tools/PlayerDump.cpp index 71719c19b3a..900be2ba44a 100644 --- a/src/server/game/Tools/PlayerDump.cpp +++ b/src/server/game/Tools/PlayerDump.cpp @@ -435,7 +435,7 @@ DumpReturn PlayerDumpReader::LoadDump(std::string const& file, uint32 account, s if (!normalizePlayerName(name)) name.clear(); - if (ObjectMgr::CheckPlayerName(name, true) == CHAR_NAME_SUCCESS) + if (ObjectMgr::CheckPlayerName(name, sWorld->GetDefaultDbcLocale(), true) == CHAR_NAME_SUCCESS) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_NAME); stmt->setString(0, name); diff --git a/src/server/scripts/Commands/cs_character.cpp b/src/server/scripts/Commands/cs_character.cpp index 93a4a526786..ef39e2feb98 100644 --- a/src/server/scripts/Commands/cs_character.cpp +++ b/src/server/scripts/Commands/cs_character.cpp @@ -336,7 +336,7 @@ public: return false; } - if (ObjectMgr::CheckPlayerName(newName, true) != CHAR_NAME_SUCCESS) + if (ObjectMgr::CheckPlayerName(newName, target ? target->GetSession()->GetSessionDbcLocale() : sWorld->GetDefaultDbcLocale(), true) != CHAR_NAME_SUCCESS) { handler->SendSysMessage(LANG_BAD_VALUE); handler->SetSentErrorMessage(true); @@ -896,7 +896,7 @@ public: return false; } - if (ObjectMgr::CheckPlayerName(name, true) != CHAR_NAME_SUCCESS) + if (ObjectMgr::CheckPlayerName(name, sWorld->GetDefaultDbcLocale(), true) != CHAR_NAME_SUCCESS) { handler->PSendSysMessage(LANG_INVALID_CHARACTER_NAME); handler->SetSentErrorMessage(true); diff --git a/src/server/shared/DataStores/DB2StorageLoader.cpp b/src/server/shared/DataStores/DB2StorageLoader.cpp index 763819695b7..a67d921ba90 100644 --- a/src/server/shared/DataStores/DB2StorageLoader.cpp +++ b/src/server/shared/DataStores/DB2StorageLoader.cpp @@ -22,13 +22,14 @@ DB2FileLoader::DB2FileLoader() { + fileName = nullptr; recordSize = 0; recordCount = 0; fieldCount = 0; stringSize = 0; - fieldsOffset = NULL; - data = NULL; - stringTable = NULL; + fieldsOffset = nullptr; + data = nullptr; + stringTable = nullptr; tableHash = 0; build = 0; @@ -36,7 +37,7 @@ DB2FileLoader::DB2FileLoader() unk1 = 0; minIndex = 0; maxIndex = 0; - locale = 0; + localeMask = 0; unk5 = 0; } @@ -44,14 +45,15 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) { if (data) { - delete [] data; - data = NULL; + delete[] data; + data = nullptr; } FILE* f = fopen(filename, "rb"); if (!f) return false; + fileName = filename; uint32 header; if (fread(&header, 4, 1, f) != 1) // Signature { @@ -140,13 +142,13 @@ bool DB2FileLoader::Load(const char *filename, const char *fmt) EndianConvert(maxIndex); - if (fread(&locale, 4, 1, f) != 1) // Locales + if (fread(&localeMask, 4, 1, f) != 1) // Locales { fclose(f); return false; } - EndianConvert(locale); + EndianConvert(localeMask); if (fread(&unk5, 4, 1, f) != 1) // Unknown WDB2 { @@ -213,6 +215,7 @@ uint32 DB2FileLoader::GetFormatRecordSize(const char * format, int32* index_pos) recordsize += 4; break; case FT_STRING: + case FT_STRING_NOT_LOCALIZED: recordsize += sizeof(char*); break; case FT_SORT: @@ -234,7 +237,17 @@ uint32 DB2FileLoader::GetFormatRecordSize(const char * format, int32* index_pos) return recordsize; } -uint32 DB2FileLoader::GetFormatStringFieldCount(char const* format) +uint32 DB2FileLoader::GetFormatStringFieldCount(const char* format) +{ + uint32 stringfields = 0; + for (uint32 x = 0; format[x]; ++x) + if (format[x] == FT_STRING || format[x] == FT_STRING_NOT_LOCALIZED) + ++stringfields; + + return stringfields; +} + +uint32 DB2FileLoader::GetFormatLocalizedStringFieldCount(char const* format) { uint32 stringfields = 0; for (uint32 x = 0; format[x]; ++x) @@ -305,7 +318,8 @@ char* DB2FileLoader::AutoProduceData(const char* format, uint32& records, char** offset += 1; break; case FT_STRING: - *((char**)(&dataTable[offset])) = NULL; // will be replaces non-empty or "" strings in AutoProduceStrings + case FT_STRING_NOT_LOCALIZED: + *((char**)(&dataTable[offset])) = nullptr; // will be replaces non-empty or "" strings in AutoProduceStrings offset += sizeof(char*); break; } @@ -358,6 +372,7 @@ char* DB2FileLoader::AutoProduceStringsArrayHolders(const char* format, char* da offset += 1; 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]); @@ -379,7 +394,24 @@ char* DB2FileLoader::AutoProduceStringsArrayHolders(const char* format, char* da char* DB2FileLoader::AutoProduceStrings(const char* format, char* dataTable, uint32 locale) { if (strlen(format) != fieldCount) - return NULL; + 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; + } char* stringPool = new char[stringSize]; memcpy(stringPool, stringTable, stringSize); @@ -406,13 +438,21 @@ char* DB2FileLoader::AutoProduceStrings(const char* format, char* dataTable, uin LocalizedString* db2str = *(LocalizedString**)(&dataTable[offset]); if (db2str->Str[locale] == nullStr) { - const char * st = getRecord(y).getString(x); - db2str->Str[locale] = stringPool + (st - (const char*)stringTable); + char const* st = getRecord(y).getString(x); + 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; + } } } } @@ -524,7 +564,21 @@ char* DB2DatabaseLoader::Load(const char* format, HotfixDatabaseStatements prepa ASSERT(*slot); // Value in database in main table field must be for enUS locale - if (char* str = AddLocaleString(*slot, LOCALE_enUS, fields[f].GetString())) + if (char* str = AddString(&(*slot)->Str[LOCALE_enUS], fields[f].GetStringView())) + stringPool.push_back(str); + + ++stringFieldNumInRecord; + offset += sizeof(char*); + break; + } + case FT_STRING_NOT_LOCALIZED: + { + char const** slot = (char const**)(&dataValue[offset]); + *slot = (char*)(&stringHolders[stringHoldersRecordPoolSize * rec + stringHolderSize * stringFieldNumInRecord]); + ASSERT(*slot); + + // Value in database in main table field must be for enUS locale + if (char* str = AddString(slot, fields[f].GetStringView())) stringPool.push_back(str); ++stringFieldNumInRecord; @@ -569,7 +623,7 @@ void DB2DatabaseLoader::LoadStrings(const char* format, HotfixDatabaseStatements if (!result) return; - size_t stringFields = DB2FileLoader::GetFormatStringFieldCount(format); + size_t stringFields = DB2FileLoader::GetFormatLocalizedStringFieldCount(format); if (result->GetFieldCount() != stringFields + 1 /*ID*/) return; @@ -605,7 +659,7 @@ void DB2DatabaseLoader::LoadStrings(const char* format, HotfixDatabaseStatements // fill only not filled entries LocalizedString* db2str = *(LocalizedString**)(&dataValue[offset]); if (db2str->Str[locale] == nullStr) - if (char* str = AddLocaleString(db2str, locale, fields[1 + stringFieldNumInRecord].GetString())) + if (char* str = AddString(&db2str->Str[locale], fields[1 + stringFieldNumInRecord].GetStringView())) stringPool.push_back(str); ++stringFieldNumInRecord; @@ -625,15 +679,15 @@ void DB2DatabaseLoader::LoadStrings(const char* format, HotfixDatabaseStatements return; } -char* DB2DatabaseLoader::AddLocaleString(LocalizedString* holder, uint32 locale, std::string const& value) +char* DB2DatabaseLoader::AddString(char const** holder, std::string const& value) { if (!value.empty()) { - std::size_t existingLength = strlen(holder->Str[locale]); + std::size_t existingLength = strlen(*holder); if (existingLength >= value.length()) { // Reuse existing storage if there is enough space - char* str = const_cast<char*>(holder->Str[locale]); + char* str = const_cast<char*>(*holder); memcpy(str, value.c_str(), value.length()); str[value.length()] = '\0'; return nullptr; @@ -642,7 +696,7 @@ char* DB2DatabaseLoader::AddLocaleString(LocalizedString* holder, uint32 locale, char* str = new char[value.length() + 1]; memcpy(str, value.c_str(), value.length()); str[value.length()] = '\0'; - holder->Str[locale] = str; + *holder = str; return str; } diff --git a/src/server/shared/DataStores/DB2StorageLoader.h b/src/server/shared/DataStores/DB2StorageLoader.h index e209955025a..587174a804b 100644 --- a/src/server/shared/DataStores/DB2StorageLoader.h +++ b/src/server/shared/DataStores/DB2StorageLoader.h @@ -85,7 +85,9 @@ class DB2FileLoader 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); private: + char const* fileName; uint32 recordSize; uint32 recordCount; @@ -102,7 +104,7 @@ private: int unk1; // WDB2 (Unix time in WCH2) int minIndex; // WDB2 int maxIndex; // WDB2 (index table) - int locale; // WDB2 + int localeMask; // WDB2 int unk5; // WDB2 }; @@ -113,7 +115,7 @@ public: 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); - static char* AddLocaleString(LocalizedString* holder, uint32 locale, std::string const& value); + static char* AddString(char const** holder, std::string const& value); private: std::string _storageName; diff --git a/src/server/shared/DataStores/DB2Store.h b/src/server/shared/DataStores/DB2Store.h index c27d32df136..5ae7be4d046 100644 --- a/src/server/shared/DataStores/DB2Store.h +++ b/src/server/shared/DataStores/DB2Store.h @@ -105,6 +105,19 @@ public: 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; + } } } } @@ -157,7 +170,7 @@ public: return false; // load strings from another locale db2 data - if (DB2FileLoader::GetFormatStringFieldCount(_format)) + if (DB2FileLoader::GetFormatLocalizedStringFieldCount(_format)) if (char* stringBlock = db2.AutoProduceStrings(_format, (char*)_dataTable, locale)) _stringPoolList.push_back(stringBlock); return true; @@ -175,7 +188,7 @@ public: void LoadStringsFromDB(uint32 locale) { - if (!DB2FileLoader::GetFormatStringFieldCount(_format)) + if (!DB2FileLoader::GetFormatLocalizedStringFieldCount(_format)) return; DB2DatabaseLoader(_fileName).LoadStrings(_format, HotfixDatabaseStatements(_hotfixStatement + 1), locale, _indexTable.AsChar, _stringPoolList); |