diff options
author | Shauren <shauren.trinity@gmail.com> | 2021-10-31 23:07:56 +0100 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2021-10-31 23:07:56 +0100 |
commit | 8423382fb034d3f1734590f7f246b73f3aa646aa (patch) | |
tree | 7ac27a5e8fc53dae8a44a3efcfaca4395f73fe9d /src/server/game/Chat/Hyperlinks.cpp | |
parent | 70a62c39785e65711c5068289e2eaba7fc0880c3 (diff) |
Core/Chat: Implement hyperlink validation for new link types
Diffstat (limited to 'src/server/game/Chat/Hyperlinks.cpp')
-rw-r--r-- | src/server/game/Chat/Hyperlinks.cpp | 435 |
1 files changed, 397 insertions, 38 deletions
diff --git a/src/server/game/Chat/Hyperlinks.cpp b/src/server/game/Chat/Hyperlinks.cpp index f66df9b4348..08d1bd37ea3 100644 --- a/src/server/game/Chat/Hyperlinks.cpp +++ b/src/server/game/Chat/Hyperlinks.cpp @@ -25,6 +25,7 @@ #include "SharedDefines.h" #include "SpellInfo.h" #include "SpellMgr.h" +#include "StringFormat.h" #include "World.h" #include "advstd.h" @@ -99,6 +100,49 @@ static bool equal_with_len(char const* str1, char const* str2, size_t len) return !len && !*str1; } +static bool IsCreatureNameValid(uint32 creatureId, char const* pos, size_t len) +{ + if (CreatureTemplate const* creatureTemplate = sObjectMgr->GetCreatureTemplate(creatureId)) + { + CreatureLocale const* locale = sObjectMgr->GetCreatureLocale(creatureId); + if (!locale) + return equal_with_len(creatureTemplate->Name.c_str(), pos, len); + + for (uint8 i = 0; i < TOTAL_LOCALES; ++i) + { + std::string const& name = (i == DEFAULT_LOCALE) ? creatureTemplate->Name : locale->Name[i]; + if (name.empty()) + continue; + if (equal_with_len(name.c_str(), pos, len)) + return true; + } + } + + return false; +} + +template <> +struct LinkValidator<LinkTags::spell> +{ + static bool IsTextValid(SpellLinkData const& data, char const* pos, size_t len) + { + return IsTextValid(data.Spell, pos, len); + } + + static bool IsTextValid(SpellInfo const* info, char const* pos, size_t len) + { + for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1)) + if (equal_with_len((*info->SpellName)[i], pos, len)) + return true; + return false; + } + + static bool IsColorValid(SpellLinkData const&, HyperlinkColor c) + { + return c == CHAT_LINK_COLOR_SPELL; + } +}; + template <> struct LinkValidator<LinkTags::achievement> { @@ -119,6 +163,186 @@ struct LinkValidator<LinkTags::achievement> }; template <> +struct LinkValidator<LinkTags::apower> +{ + static bool IsTextValid(ArtifactPowerLinkData const& data, char const* pos, size_t len) + { + if (SpellInfo const* info = sSpellMgr->GetSpellInfo(data.ArtifactPower->SpellID, DIFFICULTY_NONE)) + return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len); + return false; + } + + static bool IsColorValid(ArtifactPowerLinkData const&, HyperlinkColor c) + { + return c == CHAT_LINK_COLOR_ARTIFACT_POWER; + } +}; + +template <> +struct LinkValidator<LinkTags::azessence> +{ + static bool IsTextValid(AzeriteEssenceLinkData const& data, char const* pos, size_t len) + { + for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1)) + if (equal_with_len(data.Essence->Name[i], pos, len)) + return true; + return false; + } + + static bool IsColorValid(AzeriteEssenceLinkData const& data, HyperlinkColor c) + { + return c == ItemQualityColors[data.Rank + 1]; + } +}; + +template <> +struct LinkValidator<LinkTags::battlepet> +{ + static bool IsTextValid(BattlePetLinkData const& data, char const* pos, size_t len) + { + return IsCreatureNameValid(data.Species->CreatureID, pos, len); + } + + static bool IsColorValid(BattlePetLinkData const& data, HyperlinkColor c) + { + return c == ItemQualityColors[data.Quality]; + } +}; + +template <> +struct LinkValidator<LinkTags::conduit> +{ + static bool IsTextValid(SoulbindConduitRankEntry const* rank, char const* pos, size_t len) + { + if (SpellInfo const* info = sSpellMgr->GetSpellInfo(rank->SpellID, DIFFICULTY_NONE)) + return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len); + return false; + } + + static bool IsColorValid(SoulbindConduitRankEntry const*, HyperlinkColor c) + { + return c == CHAT_LINK_COLOR_SPELL; + } +}; + +template <> +struct LinkValidator<LinkTags::currency> +{ + static bool IsTextValid(CurrencyLinkData const& data, char const* pos, size_t len) + { + LocalizedString const* name = data.Container ? &data.Container->ContainerName : &data.Currency->Name; + for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1)) + if (equal_with_len((*name)[i], pos, len)) + return true; + return false; + } + + static bool IsColorValid(CurrencyLinkData const& data, HyperlinkColor c) + { + return c == ItemQualityColors[(data.Container ? data.Container->ContainerQuality : data.Currency->Quality)]; + } +}; + +template <> +struct LinkValidator<LinkTags::enchant> +{ + static bool IsTextValid(SpellInfo const* info, char const* pos, size_t len) + { + SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(info->Id); + if (bounds.first == bounds.second) + return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len); + + SkillLineEntry const* skill = sSkillLineStore.LookupEntry(bounds.first->second->SkillupSkillLineID + ? bounds.first->second->SkillupSkillLineID + : bounds.first->second->SkillLine); + if (!skill) + return false; + + for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1)) + { + char const* skillName = skill->DisplayName[i]; + size_t skillLen = strlen(skillName); + if (len > skillLen + 2 && // or of form [Skill Name: Spell Name] + !strncmp(pos, skillName, skillLen) && !strncmp(pos + skillLen, ": ", 2) && + equal_with_len((*info->SpellName)[i], pos + (skillLen + 2), len - (skillLen + 2))) + return true; + } + return false; + } + + static bool IsColorValid(SpellInfo const*, HyperlinkColor c) + { + return c == CHAT_LINK_COLOR_ENCHANT; + } +}; + +template <> +struct LinkValidator<LinkTags::garrfollower> +{ + static bool IsTextValid(GarrisonFollowerLinkData const& data, char const* pos, size_t len) + { + return IsCreatureNameValid(data.Follower->HordeCreatureID, pos, len) + || IsCreatureNameValid(data.Follower->AllianceCreatureID, pos, len); + } + + static bool IsColorValid(GarrisonFollowerLinkData const& data, HyperlinkColor c) + { + return c == ItemQualityColors[data.Quality]; + } +}; + +template <> +struct LinkValidator<LinkTags::garrfollowerability> +{ + static bool IsTextValid(GarrAbilityEntry const* ability, char const* pos, size_t len) + { + for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1)) + if (equal_with_len(ability->Name[i], pos, len)) + return true; + return false; + } + + static bool IsColorValid(GarrAbilityEntry const*, HyperlinkColor c) + { + return c == CHAT_LINK_COLOR_GARR_ABILITY; + } +}; + +template <> +struct LinkValidator<LinkTags::garrmission> +{ + static bool IsTextValid(GarrisonMissionLinkData const& data, char const* pos, size_t len) + { + for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1)) + if (equal_with_len(data.Mission->Name[i], pos, len)) + return true; + return false; + } + + static bool IsColorValid(GarrisonMissionLinkData const&, HyperlinkColor c) + { + return c == QuestDifficultyColors[2]; + } +}; + +template <> +struct LinkValidator<LinkTags::instancelock> +{ + static bool IsTextValid(InstanceLockLinkData const& data, char const* pos, size_t len) + { + for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1)) + if (equal_with_len(data.Map->MapName[i], pos, len)) + return true; + return false; + } + + static bool IsColorValid(InstanceLockLinkData const&, HyperlinkColor c) + { + return c == CHAT_LINK_COLOR_INSTANCE_LOCK; + } +}; + +template <> struct LinkValidator<LinkTags::item> { static bool IsTextValid(ItemLinkData const& data, char const* pos, size_t len) @@ -127,17 +351,22 @@ struct LinkValidator<LinkTags::item> if (!(data.Item->GetFlags3() & ITEM_FLAG3_HIDE_NAME_SUFFIX) && data.Suffix) suffixStrings = &data.Suffix->Description; + return IsTextValid(data.Item, suffixStrings, pos, len); + } + + static bool IsTextValid(ItemTemplate const* itemTemplate, LocalizedString const* suffixStrings, char const* pos, size_t len) + { for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1)) { - std::string name = data.Item->GetName(i); + std::string name = itemTemplate->GetName(i); if (name.empty()) continue; if (suffixStrings) { if (len > name.length() + 1 && - (strncmp(name.c_str(), pos, name.length()) == 0) && - (*(pos + name.length()) == ' ') && - equal_with_len((*suffixStrings)[i], pos + name.length() + 1, len - name.length() - 1)) + (strncmp(name.c_str(), pos, name.length()) == 0) && + (*(pos + name.length()) == ' ') && + equal_with_len((*suffixStrings)[i], pos + name.length() + 1, len - name.length() - 1)) return true; } else if (equal_with_len(name.c_str(), pos, len)) @@ -153,6 +382,52 @@ struct LinkValidator<LinkTags::item> }; template <> +struct LinkValidator<LinkTags::journal> +{ + static bool IsTextValid(JournalLinkData const& data, char const* pos, size_t len) + { + for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1)) + if (equal_with_len((*data.ExpectedText)[i], pos, len)) + return true; + return false; + } + + static bool IsColorValid(JournalLinkData const&, HyperlinkColor c) + { + return c == CHAT_LINK_COLOR_JOURNAL; + } +}; + +template <> +struct LinkValidator<LinkTags::keystone> +{ + static bool IsTextValid(KeystoneLinkData const& data, char const* pos, size_t len) + { + // Skip "Keystone" prefix - not loading GlobalStrings.db2 + char const* validateStartPos = strstr(pos, ": "); + if (!validateStartPos) + return false; + + // skip ": " too + validateStartPos += 2; + size_t validateLen = len - (validateStartPos - pos); + + for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1)) + { + std::string expectedText = Trinity::StringFormat("%s (%u)", data.Map->Name[i], data.Level); + if (equal_with_len(expectedText.c_str(), validateStartPos, validateLen)) + return true; + } + return false; + } + + static bool IsColorValid(KeystoneLinkData const&, HyperlinkColor c) + { + return c == ItemQualityColors[ITEM_QUALITY_EPIC]; + } +}; + +template <> struct LinkValidator<LinkTags::quest> { static bool IsTextValid(QuestLinkData const& data, char const* pos, size_t len) @@ -183,66 +458,57 @@ struct LinkValidator<LinkTags::quest> }; template <> -struct LinkValidator<LinkTags::spell> +struct LinkValidator<LinkTags::mawpower> { - static bool IsTextValid(SpellLinkData const& data, char const* pos, size_t len) + static bool IsTextValid(MawPowerEntry const* mawPower, char const* pos, size_t len) { - return IsTextValid(data.Spell, pos, len); - } - - static bool IsTextValid(SpellInfo const* info, char const* pos, size_t len) - { - for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1)) - if (equal_with_len((*info->SpellName)[i], pos, len)) - return true; + if (SpellInfo const* info = sSpellMgr->GetSpellInfo(mawPower->SpellID, DIFFICULTY_NONE)) + return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len); return false; } - static bool IsColorValid(SpellLinkData const&, HyperlinkColor c) + static bool IsColorValid(MawPowerEntry const*, HyperlinkColor c) { return c == CHAT_LINK_COLOR_SPELL; } }; template <> -struct LinkValidator<LinkTags::enchant> +struct LinkValidator<LinkTags::outfit> { - static bool IsTextValid(SpellInfo const* info, char const* pos, size_t len) + static bool IsTextValid(std::string const&, char const*, size_t) { - SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(info->Id); - if (bounds.first == bounds.second) - return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len); + return true; + } - SkillLineEntry const* skill = sSkillLineStore.LookupEntry(bounds.first->second->SkillupSkillLineID - ? bounds.first->second->SkillupSkillLineID - : bounds.first->second->SkillLine); - if (!skill) - return false; + static bool IsColorValid(std::string const&, HyperlinkColor c) + { + return c == CHAT_LINK_COLOR_TRANSMOG; + } +}; - for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1)) - { - char const* skillName = skill->DisplayName[i]; - size_t skillLen = strlen(skillName); - if (len > skillLen + 2 && // or of form [Skill Name: Spell Name] - !strncmp(pos, skillName, skillLen) && !strncmp(pos + skillLen, ": ", 2) && - equal_with_len((*info->SpellName)[i], pos + (skillLen + 2), len - (skillLen + 2))) - return true; - } +template <> +struct LinkValidator<LinkTags::pvptal> +{ + static bool IsTextValid(PvpTalentEntry const* mawPower, char const* pos, size_t len) + { + if (SpellInfo const* info = sSpellMgr->GetSpellInfo(mawPower->SpellID, DIFFICULTY_NONE)) + return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len); return false; } - static bool IsColorValid(SpellInfo const*, HyperlinkColor c) + static bool IsColorValid(PvpTalentEntry const*, HyperlinkColor c) { - return c == CHAT_LINK_COLOR_ENCHANT; + return c == CHAT_LINK_COLOR_TALENT; } }; template <> struct LinkValidator<LinkTags::talent> { - static bool IsTextValid(TalentEntry const* talent, char const* pos, size_t len) + static bool IsTextValid(TalentEntry const* mawPower, char const* pos, size_t len) { - if (SpellInfo const* info = sSpellMgr->GetSpellInfo(talent->SpellID, DIFFICULTY_NONE)) + if (SpellInfo const* info = sSpellMgr->GetSpellInfo(mawPower->SpellID, DIFFICULTY_NONE)) return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len); return false; } @@ -267,6 +533,81 @@ struct LinkValidator<LinkTags::trade> } }; +template <> +struct LinkValidator<LinkTags::transmogappearance> +{ + static bool IsTextValid(ItemModifiedAppearanceEntry const* enchantment, char const* pos, size_t len) + { + if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(enchantment->ItemID)) + return LinkValidator<LinkTags::item>::IsTextValid(itemTemplate, nullptr, pos, len); + return false; + } + + static bool IsColorValid(ItemModifiedAppearanceEntry const*, HyperlinkColor c) + { + return c == CHAT_LINK_COLOR_TRANSMOG; + } +}; + +template <> +struct LinkValidator<LinkTags::transmogillusion> +{ + static bool IsTextValid(SpellItemEnchantmentEntry const* enchantment, char const* pos, size_t len) + { + for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1)) + if (equal_with_len(enchantment->Name[i], pos, len)) + return true; + for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1)) + if (equal_with_len(enchantment->HordeName[i], pos, len)) + return true; + return false; + } + + static bool IsColorValid(SpellItemEnchantmentEntry const*, HyperlinkColor c) + { + return c == CHAT_LINK_COLOR_TRANSMOG; + } +}; + +template <> +struct LinkValidator<LinkTags::transmogset> +{ + static bool IsTextValid(TransmogSetEntry const* set, char const* pos, size_t len) + { + for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1)) + { + if (ItemNameDescriptionEntry const* itemNameDescription = sItemNameDescriptionStore.LookupEntry(set->ItemNameDescriptionID)) + { + std::string expectedText = Trinity::StringFormat("%s (%s)", set->Name[i], itemNameDescription->Description[i]); + if (equal_with_len(expectedText.c_str(), pos, len)) + return true; + } + else if (equal_with_len(set->Name[i], pos, len)) + return true; + } + return false; + } + + static bool IsColorValid(TransmogSetEntry const*, HyperlinkColor c) + { + return c == CHAT_LINK_COLOR_TRANSMOG; + } +}; + +template <> +struct LinkValidator<LinkTags::worldmap> +{ + static bool IsTextValid(WorldMapLinkData const&, char const*, size_t) + { + return true; + } + + static bool IsColorValid(WorldMapLinkData const&, HyperlinkColor c) + { + return c == CHAT_LINK_COLOR_ACHIEVEMENT; + } +}; + #define TryValidateAs(tagname) \ { \ ASSERT(!strcmp(LinkTags::tagname::tag(), #tagname)); \ @@ -288,17 +629,31 @@ struct LinkValidator<LinkTags::trade> static bool ValidateLinkInfo(HyperlinkInfo const& info) { TryValidateAs(achievement); + TryValidateAs(apower); + TryValidateAs(azessence); TryValidateAs(area); TryValidateAs(areatrigger); + TryValidateAs(battlepet); + TryValidateAs(conduit); TryValidateAs(creature); TryValidateAs(creature_entry); + TryValidateAs(currency); TryValidateAs(enchant); TryValidateAs(gameevent); TryValidateAs(gameobject); TryValidateAs(gameobject_entry); + TryValidateAs(garrfollower); + TryValidateAs(garrfollowerability); + TryValidateAs(garrmission); + TryValidateAs(instancelock); TryValidateAs(item); TryValidateAs(itemset); + TryValidateAs(journal); + TryValidateAs(keystone); + TryValidateAs(mawpower); + TryValidateAs(outfit); TryValidateAs(player); + TryValidateAs(pvptal); TryValidateAs(quest); TryValidateAs(skill); TryValidateAs(spell); @@ -307,6 +662,10 @@ static bool ValidateLinkInfo(HyperlinkInfo const& info) TryValidateAs(tele); TryValidateAs(title); TryValidateAs(trade); + TryValidateAs(transmogappearance); + TryValidateAs(transmogillusion); + TryValidateAs(transmogset); + TryValidateAs(worldmap); return false; } |