TrinityCore
Loading...
Searching...
No Matches
Hyperlinks.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "Hyperlinks.h"
19#include "Common.h"
20#include "DB2Stores.h"
21#include "Errors.h"
22#include "ItemTemplate.h"
23#include "ObjectMgr.h"
24#include "QuestDef.h"
25#include "SharedDefines.h"
26#include "SpellInfo.h"
27#include "SpellMgr.h"
28#include "StringFormat.h"
29#include "World.h"
30
31using namespace Trinity::Hyperlinks;
32
34{
35 return data.starts_with("IQ") && q < MAX_ITEM_QUALITY && Trinity::StringTo<uint32>(data.substr(2)) == uint32(q);
36}
37
38// Validates a single hyperlink
40{
41 std::string_view color;
42 std::string_view tag;
43 std::string_view data;
44 std::string_view text;
45
46 //color tag
47 if (!str.starts_with("|c"sv))
48 return {};
49 str.remove_prefix(2);
50
51 if (str.length() < 8)
52 return {};
53
54 if (str[0] == 'n')
55 {
56 // numeric color id
57 str.remove_prefix(1);
58
59 if (size_t endOfColor = str.find(":"sv); endOfColor != std::string_view::npos)
60 {
61 color = str.substr(0, endOfColor);
62 str.remove_prefix(endOfColor + 1);
63 }
64 else
65 return {};
66 }
67 else
68 {
69 // hex color
70 color = str.substr(0, 8);
71 str.remove_prefix(8);
72 }
73
74 if (!str.starts_with("|H"sv))
75 return {};
76 str.remove_prefix(2);
77
78 // tag+data part follows
79 if (size_t delimPos = str.find('|'); delimPos != std::string_view::npos)
80 {
81 tag = str.substr(0, delimPos);
82 str.remove_prefix(delimPos+1);
83 }
84 else
85 return {};
86
87 // split tag if : is present (data separator)
88 if (size_t dataStart = tag.find(':'); dataStart != std::string_view::npos)
89 {
90 data = tag.substr(dataStart+1);
91 tag = tag.substr(0, dataStart);
92 }
93
94 // ok, next should be link data end tag...
95 if (!str.starts_with('h'))
96 return {};
97 str.remove_prefix(1);
98
99 // extract text, must be between []
100 if (str[0] != '[')
101 return {};
102
103 size_t openBrackets = 0;
104 for (size_t nameItr = 0; nameItr < str.length(); ++nameItr)
105 {
106 switch (str[nameItr])
107 {
108 case '[':
109 ++openBrackets;
110 break;
111 case ']':
112 --openBrackets;
113 break;
114 default:
115 break;
116 }
117
118 if (!openBrackets)
119 {
120 text = str.substr(1, nameItr - 1);
121 str.remove_prefix(nameItr + 1);
122 break;
123 }
124 }
125
126 // check end tag
127 if (!str.starts_with("|h|r"sv))
128 return {};
129 str.remove_prefix(4);
130
131 // ok, valid hyperlink, return info
132 return { str, color, tag, data, text };
133}
134
135template <typename T>
137{
138 static bool IsTextValid(typename T::value_type, std::string_view) { return true; }
139 static bool IsColorValid(typename T::value_type, HyperlinkColor) { return true; }
140};
141
142static bool IsCreatureNameValid(uint32 creatureId, std::string_view text)
143{
144 if (CreatureTemplate const* creatureTemplate = sObjectMgr->GetCreatureTemplate(creatureId))
145 {
146 CreatureLocale const* locale = sObjectMgr->GetCreatureLocale(creatureId);
147 if (!locale)
148 return creatureTemplate->Name == text;
149
150 for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
151 {
152 std::string const& name = (i == DEFAULT_LOCALE) ? creatureTemplate->Name : locale->Name[i];
153 if (name.empty())
154 continue;
155 if (name == text)
156 return true;
157 }
158 }
159
160 return false;
161}
162
163template <>
164struct LinkValidator<LinkTags::spell>
165{
166 static bool IsTextValid(SpellLinkData const& data, std::string_view text)
167 {
168 return IsTextValid(data.Spell, text);
169 }
170
171 static bool IsTextValid(SpellInfo const* info, std::string_view text)
172 {
173 for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
174 if ((*info->SpellName)[i] == text)
175 return true;
176 return false;
177 }
178
180 {
181 return c == CHAT_LINK_COLOR_SPELL;
182 }
183};
184
185template <>
186struct LinkValidator<LinkTags::achievement>
187{
188 static bool IsTextValid(AchievementLinkData const& data, std::string_view text)
189 {
190 if (text.empty())
191 return false;
192 for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
193 if (text == data.Achievement->Title[i])
194 return true;
195 return false;
196 }
197
199 {
200 return c == CHAT_LINK_COLOR_ACHIEVEMENT;
201 }
202};
203
204template <>
205struct LinkValidator<LinkTags::apower>
206{
207 static bool IsTextValid(ArtifactPowerLinkData const& data, std::string_view text)
208 {
209 if (SpellInfo const* info = sSpellMgr->GetSpellInfo(data.ArtifactPower->SpellID, DIFFICULTY_NONE))
211 return false;
212 }
213
215 {
217 }
218};
219
220template <>
221struct LinkValidator<LinkTags::azessence>
222{
223 static bool IsTextValid(AzeriteEssenceLinkData const& data, std::string_view text)
224 {
225 for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
226 if (data.Essence->Name[i] == text)
227 return true;
228 return false;
229 }
230
232 {
233 ItemQualities quality = ItemQualities(data.Rank + 1);
234 if (c == ItemQualityColors[quality])
235 return true;
236
237 if (c == ItemQualities(quality))
238 return true;
239
240 return false;
241 }
242};
243
244template <>
245struct LinkValidator<LinkTags::battlepet>
246{
247 static bool IsTextValid(BattlePetLinkData const& data, std::string_view text)
248 {
249 return IsCreatureNameValid(data.Species->CreatureID, text);
250 }
251
253 {
254 return c == ItemQualityColors[data.Quality];
255 }
256};
257
258template <>
259struct LinkValidator<LinkTags::battlePetAbil>
260{
261 static bool IsTextValid(BattlePetAbilLinkData const& data, std::string_view text)
262 {
263 for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
264 if (data.Ability->Name[i] == text)
265 return true;
266 return false;
267 }
268
270 {
272 }
273};
274
275template <>
276struct LinkValidator<LinkTags::conduit>
277{
278 static bool IsTextValid(SoulbindConduitRankEntry const* rank, std::string_view text)
279 {
280 if (SpellInfo const* info = sSpellMgr->GetSpellInfo(rank->SpellID, DIFFICULTY_NONE))
282 return false;
283 }
284
286 {
287 return c == CHAT_LINK_COLOR_SPELL;
288 }
289};
290
291template <>
292struct LinkValidator<LinkTags::curio>
293{
294 static bool IsTextValid(SpellInfo const* info, std::string_view text)
295 {
297 }
298
299 static bool IsColorValid(SpellInfo const*, HyperlinkColor c)
300 {
301 for (uint32 i = 0; i < MAX_ITEM_QUALITY; ++i)
302 if (c == ItemQualities(i))
303 return true;
304 return false;
305 }
306};
307
308template <>
309struct LinkValidator<LinkTags::currency>
310{
311 static bool IsTextValid(CurrencyLinkData const& data, std::string_view text)
312 {
313 LocalizedString const* name = data.Container ? &data.Container->ContainerName : &data.Currency->Name;
314 for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
315 if ((*name)[i] == text)
316 return true;
317 return false;
318 }
319
320 static bool IsColorValid(CurrencyLinkData const& data, HyperlinkColor c)
321 {
323 if (c == ItemQualityColors[quality])
324 return true;
325
326 if (c == quality)
327 return true;
328
329 return false;
330 }
331};
332
333template <>
334struct LinkValidator<LinkTags::enchant>
335{
336 static bool IsTextValid(SpellInfo const* info, std::string_view text)
337 {
339 return true;
340 SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(info->Id);
341 if (bounds.first == bounds.second)
342 return false;
343
344 for (auto pair = bounds.first; pair != bounds.second; ++pair)
345 {
346 SkillLineEntry const* skill = sSkillLineStore.LookupEntry(pair->second->SkillupSkillLineID
347 ? pair->second->SkillupSkillLineID
348 : pair->second->SkillLine);
349 if (!skill)
350 return false;
351
352 for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
353 {
354 std::string_view skillName = skill->DisplayName[i];
355 std::string_view spellName = (*info->SpellName)[i];
356 if ((text.length() == (skillName.length() + 2 + spellName.length())) &&
357 (text.substr(0, skillName.length()) == skillName) &&
358 (text.substr(skillName.length(), 2) == ": ") &&
359 (text.substr(skillName.length() + 2) == spellName))
360 return true;
361 }
362 }
363 return false;
364 }
365
366 static bool IsColorValid(SpellInfo const*, HyperlinkColor c)
367 {
368 return c == CHAT_LINK_COLOR_ENCHANT;
369 }
370};
371
372template <>
373struct LinkValidator<LinkTags::garrfollower>
374{
375 static bool IsTextValid(GarrisonFollowerLinkData const& data, std::string_view text)
376 {
379 }
380
382 {
383 if (c == ItemQualityColors[data.Quality])
384 return true;
385
386 if (c == ItemQualities(data.Quality))
387 return true;
388
389 return false;
390 }
391};
392
393template <>
394struct LinkValidator<LinkTags::garrfollowerability>
395{
396 static bool IsTextValid(GarrAbilityEntry const* ability, std::string_view text)
397 {
398 for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
399 if (ability->Name[i] == text)
400 return true;
401 return false;
402 }
403
405 {
407 }
408};
409
410template <>
411struct LinkValidator<LinkTags::garrmission>
412{
413 static bool IsTextValid(GarrisonMissionLinkData const& data, std::string_view text)
414 {
415 for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
416 if (data.Mission->Name[i] == text)
417 return true;
418 return false;
419 }
420
422 {
423 return c == QuestDifficultyColors[2];
424 }
425};
426
427template <>
428struct LinkValidator<LinkTags::instancelock>
429{
430 static bool IsTextValid(InstanceLockLinkData const& data, std::string_view text)
431 {
432 for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
433 if (data.Map->MapName[i] == text)
434 return true;
435 return false;
436 }
437
439 {
441 }
442};
443
444template <>
445struct LinkValidator<LinkTags::item>
446{
447 static constexpr std::array<std::string_view, 6> CRAFTING_QUALITY_ICON =
448 {
449 "",
450 " |A:Professions-ChatIcon-Quality-Tier1:17:15::1|a",
451 " |A:Professions-ChatIcon-Quality-Tier2:17:23::1|a",
452 " |A:Professions-ChatIcon-Quality-Tier3:17:18::1|a",
453 " |A:Professions-ChatIcon-Quality-Tier4:17:17::1|a",
454 " |A:Professions-ChatIcon-Quality-Tier5:17:17::1|a",
455 };
456
457 static bool IsTextValid(ItemLinkData const& data, std::string_view text)
458 {
459 LocalizedString const* suffixStrings = nullptr;
461 suffixStrings = &data.Suffix->Description;
462
463 Optional<int32> craftingQualityId;
464 auto craftingQualityIdItr = std::ranges::find(data.Modifiers, ITEM_MODIFIER_CRAFTING_QUALITY_ID, &ItemLinkData::Modifier::Type);
465 if (craftingQualityIdItr != data.Modifiers.end())
466 craftingQualityId = craftingQualityIdItr->Value;
467
468 return IsTextValid(data.Item, suffixStrings, craftingQualityId, text);
469 }
470
471 static bool IsTextValid(ItemTemplate const* itemTemplate, LocalizedString const* suffixStrings, Optional<int32> craftingQualityId, std::string_view text)
472 {
473 // default icon
474 if (!craftingQualityId)
475 if (ModifiedCraftingItemEntry const* modifiedCraftingItemEntry = sModifiedCraftingItemStore.LookupEntry(itemTemplate->GetId()))
476 craftingQualityId = modifiedCraftingItemEntry->CraftingQualityID;
477
478 std::string_view craftingQualityIcon = CRAFTING_QUALITY_ICON[0];
479 if (craftingQualityId)
480 if (CraftingQualityEntry const* craftingQualityEntry = sCraftingQualityStore.LookupEntry(*craftingQualityId))
481 if (craftingQualityEntry->QualityTier < std::ranges::ssize(CRAFTING_QUALITY_ICON))
482 craftingQualityIcon = CRAFTING_QUALITY_ICON[craftingQualityEntry->QualityTier];
483
484 for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
485 if (IsTextValid(text, itemTemplate->GetName(i), suffixStrings ? Optional<std::string_view>((*suffixStrings)[i]) : std::nullopt, craftingQualityIcon))
486 return true;
487
488 return false;
489 }
490
491 static bool IsTextValid(std::string_view toValidate, std::string_view name, Optional<std::string_view> suffix, std::string_view craftingQualityIcon)
492 {
493 if (name.empty())
494 return false;
495
496 if (!toValidate.starts_with(name))
497 return false;
498
499 toValidate.remove_prefix(name.length());
500 if (suffix)
501 {
502 if (toValidate.length() < suffix->length() + 1)
503 return false;
504
505 if (toValidate[0] != ' ')
506 return false;
507
508 toValidate.remove_prefix(1);
509 if (!toValidate.starts_with(*suffix))
510 return false;
511
512 toValidate.remove_prefix(suffix->length());
513 }
514
515 if (!toValidate.starts_with(craftingQualityIcon))
516 return false;
517
518 toValidate.remove_prefix(craftingQualityIcon.length());
519 return toValidate.empty();
520 }
521
522 static bool IsColorValid(ItemLinkData const& data, HyperlinkColor c)
523 {
524 if (c == ItemQualityColors[data.Quality])
525 return true;
526
527 if (c == ItemQualities(data.Quality))
528 return true;
529
530 return false;
531 }
532};
533
534template <>
535struct LinkValidator<LinkTags::journal>
536{
537 static bool IsTextValid(JournalLinkData const& data, std::string_view text)
538 {
539 for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
540 if ((*data.ExpectedText)[i] == text)
541 return true;
542 return false;
543 }
544
546 {
547 return c == CHAT_LINK_COLOR_JOURNAL;
548 }
549};
550
551template <>
552struct LinkValidator<LinkTags::keystone>
553{
554 static bool IsTextValid(KeystoneLinkData const& data, std::string_view text)
555 {
556 // Skip "Keystone" prefix - not loading GlobalStrings.db2
557 size_t validateStartPos = text.find(": ");
558 if (validateStartPos == std::string_view::npos)
559 return false;
560
561 text.remove_prefix(validateStartPos);
562 text.remove_prefix(2); // skip ": " too
563
564 for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
565 {
566 std::string expectedText = Trinity::StringFormat("{} ({})", data.Map->Name[i], data.Level);
567 if (expectedText == text)
568 return true;
569 }
570 return false;
571 }
572
574 {
576 return true;
577
578 if (c == ITEM_QUALITY_EPIC)
579 return true;
580
581 return false;
582 }
583};
584
585template <>
586struct LinkValidator<LinkTags::mawpower>
587{
588 static bool IsTextValid(MawPowerEntry const* mawPower, std::string_view text)
589 {
590 if (SpellInfo const* info = sSpellMgr->GetSpellInfo(mawPower->SpellID, DIFFICULTY_NONE))
592 return false;
593 }
594
596 {
597 return c == CHAT_LINK_COLOR_SPELL;
598 }
599};
600
601template <>
602struct LinkValidator<LinkTags::mount>
603{
604 static bool IsTextValid(MountLinkData const& data, std::string_view text)
605 {
607 }
608
610 {
611 return c == CHAT_LINK_COLOR_SPELL;
612 }
613};
614
615template <>
616struct LinkValidator<LinkTags::outfit>
617{
618 static bool IsTextValid(std::string_view, std::string_view)
619 {
620 return true;
621 }
622
623 static bool IsColorValid(std::string_view, HyperlinkColor c)
624 {
625 return c == CHAT_LINK_COLOR_TRANSMOG;
626 }
627};
628
629template <>
630struct LinkValidator<LinkTags::perksactivity>
631{
632 static bool IsTextValid(PerksActivityEntry const* perksActivity, std::string_view text)
633 {
634 for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
635 if (perksActivity->ActivityName[i] == text)
636 return true;
637 return false;
638 }
639
641 {
642 return c == CHAT_LINK_COLOR_NEUTRAL;
643 }
644};
645
646template <>
647struct LinkValidator<LinkTags::pvptal>
648{
649 static bool IsTextValid(PvpTalentEntry const* pvpTalent, std::string_view text)
650 {
651 if (SpellInfo const* info = sSpellMgr->GetSpellInfo(pvpTalent->SpellID, DIFFICULTY_NONE))
653 return false;
654 }
655
657 {
658 return c == CHAT_LINK_COLOR_TALENT;
659 }
660};
661
662template <>
663struct LinkValidator<LinkTags::quest>
664{
665 static bool IsTextValid(QuestLinkData const& data, std::string_view text)
666 {
667 if (text.empty())
668 return false;
669
670 if (text == data.Quest->GetLogTitle())
671 return true;
672
673 QuestTemplateLocale const* locale = sObjectMgr->GetQuestLocale(data.Quest->GetQuestId());
674 if (!locale)
675 return false;
676
677 for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
678 {
679 if (i == DEFAULT_LOCALE)
680 continue;
681
682 std::string_view name = ObjectMgr::GetLocaleString(locale->LogTitle, LocaleConstant(i));
683 if (!name.empty() && (text == name))
684 return true;
685 }
686
687 return false;
688 }
689
691 {
692 for (uint8 i = 0; i < MAX_QUEST_DIFFICULTY; ++i)
693 if (c == QuestDifficultyColors[i])
694 return true;
695 return false;
696 }
697};
698
699template <>
700struct LinkValidator<LinkTags::talent>
701{
702 static bool IsTextValid(TalentEntry const* talent, std::string_view text)
703 {
704 if (SpellInfo const* info = sSpellMgr->GetSpellInfo(talent->SpellID, DIFFICULTY_NONE))
706 return false;
707 }
708
710 {
711 return c == CHAT_LINK_COLOR_TALENT;
712 }
713};
714
715template <>
716struct LinkValidator<LinkTags::trade>
717{
718 static bool IsTextValid(TradeskillLinkData const& data, std::string_view text)
719 {
721 }
722
724 {
725 return c == CHAT_LINK_COLOR_TRADE;
726 }
727};
728
729template <>
730struct LinkValidator<LinkTags::transmogappearance>
731{
732 static bool IsTextValid(ItemModifiedAppearanceEntry const* enchantment, std::string_view text)
733 {
734 if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(enchantment->ItemID))
735 return LinkValidator<LinkTags::item>::IsTextValid(itemTemplate, nullptr, {}, text);
736 return false;
737 }
738
740 {
741 return c == CHAT_LINK_COLOR_TRANSMOG;
742 }
743};
744
745template <>
746struct LinkValidator<LinkTags::transmogillusion>
747{
748 static bool IsTextValid(SpellItemEnchantmentEntry const* enchantment, std::string_view text)
749 {
750 for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
751 if (enchantment->Name[i] == text)
752 return true;
753 for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
754 if (enchantment->HordeName[i] == text)
755 return true;
756 return false;
757 }
758
760 {
761 return c == CHAT_LINK_COLOR_TRANSMOG;
762 }
763};
764
765template <>
766struct LinkValidator<LinkTags::transmogset>
767{
768 static bool IsTextValid(TransmogSetEntry const* set, std::string_view text)
769 {
770 for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
771 {
772 if (ItemNameDescriptionEntry const* itemNameDescription = sItemNameDescriptionStore.LookupEntry(set->ItemNameDescriptionID))
773 {
774 std::string expectedText = Trinity::StringFormat("{} ({})", set->Name[i], itemNameDescription->Description[i]);
775 if (expectedText.c_str() == text)
776 return true;
777 }
778 else if (set->Name[i] == text)
779 return true;
780 }
781 return false;
782 }
783
785 {
786 return c == CHAT_LINK_COLOR_TRANSMOG;
787 }
788};
789
790template <>
791struct LinkValidator<LinkTags::worldmap>
792{
793 static bool IsTextValid(WorldMapLinkData const&, std::string_view)
794 {
795 return true;
796 }
797
799 {
800 return c == CHAT_LINK_COLOR_ACHIEVEMENT;
801 }
802};
803
804template <typename TAG>
805static bool ValidateAs(HyperlinkInfo const& info)
806{
807 std::decay_t<typename TAG::value_type> t;
808 if (!TAG::StoreTo(t, info.data))
809 return false;
810
811 int32 const severity = static_cast<int32>(sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY));
812 if (severity >= 0)
813 {
815 return false;
816 if (severity >= 1)
817 {
819 return false;
820 }
821 }
822 return true;
823}
824
825#define TryValidateAs(T) do { if (info.tag == T::tag()) return ValidateAs<T>(info); } while (0)
826
827static bool ValidateLinkInfo(HyperlinkInfo const& info)
828{
829 using namespace LinkTags;
830 TryValidateAs(achievement);
831 TryValidateAs(api);
832 TryValidateAs(apower);
833 TryValidateAs(azessence);
834 TryValidateAs(area);
835 TryValidateAs(areatrigger);
836 TryValidateAs(battlepet);
837 TryValidateAs(battlePetAbil);
838 TryValidateAs(clubFinder);
839 TryValidateAs(clubTicket);
840 TryValidateAs(conduit);
841 TryValidateAs(creature);
842 TryValidateAs(creature_entry);
843 TryValidateAs(curio);
844 TryValidateAs(currency);
845 TryValidateAs(dungeonScore);
846 TryValidateAs(enchant);
847 TryValidateAs(gameevent);
848 TryValidateAs(gameobject);
849 TryValidateAs(gameobject_entry);
850 TryValidateAs(garrfollower);
851 TryValidateAs(garrfollowerability);
852 TryValidateAs(garrmission);
853 TryValidateAs(instancelock);
854 TryValidateAs(item);
855 TryValidateAs(itemset);
856 TryValidateAs(journal);
857 TryValidateAs(keystone);
858 TryValidateAs(mawpower);
859 TryValidateAs(mount);
860 TryValidateAs(outfit);
861 TryValidateAs(perksactivity);
862 TryValidateAs(player);
863 TryValidateAs(pvptal);
864 TryValidateAs(quest);
865 TryValidateAs(skill);
866 TryValidateAs(spell);
867 TryValidateAs(talent);
868 TryValidateAs(talentbuild);
869 TryValidateAs(taxinode);
870 TryValidateAs(tele);
871 TryValidateAs(title);
872 TryValidateAs(trade);
873 TryValidateAs(transmogappearance);
874 TryValidateAs(transmogillusion);
875 TryValidateAs(transmogset);
876 TryValidateAs(worldmap);
877 return false;
878}
879
880// Validates all hyperlinks and control sequences contained in str
881bool Trinity::Hyperlinks::CheckAllLinks(std::string_view str)
882{
883 // Step 1: Disallow all control sequences except ||, |H, |h, |c, |A, |a and |r
884 {
885 std::string_view::size_type pos = 0;
886 while ((pos = str.find('|', pos)) != std::string::npos)
887 {
888 ++pos;
889 if (pos == str.length())
890 return false;
891 char next = str[pos];
892 if (next == 'H' || next == 'h' || next == 'c' || next == 'A' || next == 'a' || next == 'r' || next == '|')
893 ++pos;
894 else
895 return false;
896 }
897 }
898
899 // Step 2: Parse all link sequences
900 // They look like this: |c<color>|H<linktag>:<linkdata>|h[<linktext>]|h|r
901 // - <color> is 8 hex characters AARRGGBB
902 // - <linktag> is arbitrary length [a-z_]
903 // - <linkdata> is arbitrary length, no | contained
904 // - <linktext> is printable
905 {
906 std::string::size_type pos;
907 while ((pos = str.find('|')) != std::string::npos)
908 {
909 if (str[pos + 1] == '|') // this is an escaped pipe character (||)
910 {
911 str = str.substr(pos + 2);
912 continue;
913 }
914
915 HyperlinkInfo info = ParseSingleHyperlink(str.substr(pos));
916 if (!info || !ValidateLinkInfo(info))
917 return false;
918
919 // tag is fine, find the next one
920 str = info.tail;
921 }
922 }
923
924 // all tags are valid
925 return true;
926}
LocaleConstant
Definition Common.h:51
@ TOTAL_LOCALES
Definition Common.h:65
@ LOCALE_enUS
Definition Common.h:52
#define DEFAULT_LOCALE
Definition Common.h:69
DB2Storage< ItemNameDescriptionEntry > sItemNameDescriptionStore("ItemNameDescription.db2", &ItemNameDescriptionLoadInfo::Instance)
DB2Storage< SkillLineEntry > sSkillLineStore("SkillLine.db2", &SkillLineLoadInfo::Instance)
DB2Storage< ModifiedCraftingItemEntry > sModifiedCraftingItemStore("ModifiedCraftingItem.db2", &ModifiedCraftingItemLoadInfo::Instance)
DB2Storage< CraftingQualityEntry > sCraftingQualityStore("CraftingQuality.db2", &CraftingQualityLoadInfo::Instance)
@ DIFFICULTY_NONE
Definition DBCEnums.h:933
uint8_t uint8
Definition Define.h:156
int32_t int32
Definition Define.h:150
uint32_t uint32
Definition Define.h:154
@ ITEM_MODIFIER_CRAFTING_QUALITY_ID
@ ITEM_FLAG3_HIDE_NAME_SUFFIX
#define sObjectMgr
Definition ObjectMgr.h:1885
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
uint32 constexpr ItemQualityColors[MAX_ITEM_QUALITY]
ItemQualities
@ MAX_ITEM_QUALITY
@ ITEM_QUALITY_EPIC
@ CHAT_LINK_COLOR_TRADE
@ CHAT_LINK_COLOR_NEUTRAL
@ CHAT_LINK_COLOR_JOURNAL
@ CHAT_LINK_COLOR_TRANSMOG
@ CHAT_LINK_COLOR_SPELL
@ CHAT_LINK_COLOR_ACHIEVEMENT
@ CHAT_LINK_COLOR_ARTIFACT_POWER
@ CHAT_LINK_COLOR_GARR_ABILITY
@ CHAT_LINK_COLOR_TALENT
@ CHAT_LINK_COLOR_BATTLE_PET_ABIL
@ CHAT_LINK_COLOR_INSTANCE_LOCK
@ CHAT_LINK_COLOR_ENCHANT
uint32 constexpr QuestDifficultyColors[MAX_QUEST_DIFFICULTY]
size_t constexpr MAX_QUEST_DIFFICULTY
#define sSpellMgr
Definition SpellMgr.h:812
std::pair< SkillLineAbilityMap::const_iterator, SkillLineAbilityMap::const_iterator > SkillLineAbilityMapBounds
Definition SpellMgr.h:579
static std::string_view GetLocaleString(std::vector< std::string > const &data, LocaleConstant locale)
Definition ObjectMgr.h:1611
std::string const & GetLogTitle() const
Definition QuestDef.h:666
uint32 GetQuestId() const
Definition QuestDef.h:637
uint32 const Id
Definition SpellInfo.h:328
LocalizedString const * SpellName
Definition SpellInfo.h:409
#define sWorld
Definition World.h:916
@ CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY
Definition World.h:317
std::string StringFormat(FormatString< Args... > fmt, Args &&... args) noexcept
Default TC string format function.
LocalizedString Title
LocalizedString Name
LocalizedString Name
std::vector< std::string > Name
LocalizedString ContainerName
LocalizedString Name
LocalizedString Name
LocalizedString Name
LocalizedString Description
uint32 GetId() const
bool HasFlag(ItemFlags flag) const
char const * GetName(LocaleConstant locale) const
static bool IsColorValid(AchievementLinkData const &, HyperlinkColor c)
static bool IsTextValid(AchievementLinkData const &data, std::string_view text)
static bool IsColorValid(ArtifactPowerLinkData const &, HyperlinkColor c)
static bool IsTextValid(ArtifactPowerLinkData const &data, std::string_view text)
static bool IsTextValid(AzeriteEssenceLinkData const &data, std::string_view text)
static bool IsColorValid(AzeriteEssenceLinkData const &data, HyperlinkColor c)
static bool IsColorValid(BattlePetAbilLinkData const &, HyperlinkColor c)
static bool IsTextValid(BattlePetAbilLinkData const &data, std::string_view text)
static bool IsTextValid(BattlePetLinkData const &data, std::string_view text)
static bool IsColorValid(BattlePetLinkData const &data, HyperlinkColor c)
static bool IsTextValid(SoulbindConduitRankEntry const *rank, std::string_view text)
static bool IsColorValid(SoulbindConduitRankEntry const *, HyperlinkColor c)
static bool IsTextValid(SpellInfo const *info, std::string_view text)
static bool IsColorValid(SpellInfo const *, HyperlinkColor c)
static bool IsTextValid(CurrencyLinkData const &data, std::string_view text)
static bool IsColorValid(CurrencyLinkData const &data, HyperlinkColor c)
static bool IsTextValid(SpellInfo const *info, std::string_view text)
static bool IsColorValid(SpellInfo const *, HyperlinkColor c)
static bool IsColorValid(GarrisonFollowerLinkData const &data, HyperlinkColor c)
static bool IsTextValid(GarrisonFollowerLinkData const &data, std::string_view text)
static bool IsTextValid(GarrAbilityEntry const *ability, std::string_view text)
static bool IsColorValid(GarrAbilityEntry const *, HyperlinkColor c)
static bool IsColorValid(GarrisonMissionLinkData const &, HyperlinkColor c)
static bool IsTextValid(GarrisonMissionLinkData const &data, std::string_view text)
static bool IsTextValid(InstanceLockLinkData const &data, std::string_view text)
static bool IsColorValid(InstanceLockLinkData const &, HyperlinkColor c)
static bool IsTextValid(std::string_view toValidate, std::string_view name, Optional< std::string_view > suffix, std::string_view craftingQualityIcon)
static bool IsTextValid(ItemTemplate const *itemTemplate, LocalizedString const *suffixStrings, Optional< int32 > craftingQualityId, std::string_view text)
static bool IsColorValid(ItemLinkData const &data, HyperlinkColor c)
static bool IsTextValid(ItemLinkData const &data, std::string_view text)
static bool IsTextValid(JournalLinkData const &data, std::string_view text)
static bool IsColorValid(JournalLinkData const &, HyperlinkColor c)
static bool IsTextValid(KeystoneLinkData const &data, std::string_view text)
static bool IsColorValid(KeystoneLinkData const &, HyperlinkColor c)
static bool IsColorValid(MawPowerEntry const *, HyperlinkColor c)
static bool IsTextValid(MawPowerEntry const *mawPower, std::string_view text)
static bool IsColorValid(MountLinkData const &, HyperlinkColor c)
static bool IsTextValid(MountLinkData const &data, std::string_view text)
static bool IsColorValid(std::string_view, HyperlinkColor c)
static bool IsTextValid(std::string_view, std::string_view)
static bool IsTextValid(PerksActivityEntry const *perksActivity, std::string_view text)
static bool IsColorValid(PerksActivityEntry const *, HyperlinkColor c)
static bool IsTextValid(PvpTalentEntry const *pvpTalent, std::string_view text)
static bool IsColorValid(PvpTalentEntry const *, HyperlinkColor c)
static bool IsColorValid(QuestLinkData const &, HyperlinkColor c)
static bool IsTextValid(QuestLinkData const &data, std::string_view text)
static bool IsTextValid(SpellLinkData const &data, std::string_view text)
static bool IsTextValid(SpellInfo const *info, std::string_view text)
static bool IsColorValid(SpellLinkData const &, HyperlinkColor c)
static bool IsColorValid(TalentEntry const *, HyperlinkColor c)
static bool IsTextValid(TalentEntry const *talent, std::string_view text)
static bool IsColorValid(TradeskillLinkData const &, HyperlinkColor c)
static bool IsTextValid(TradeskillLinkData const &data, std::string_view text)
static bool IsColorValid(ItemModifiedAppearanceEntry const *, HyperlinkColor c)
static bool IsTextValid(ItemModifiedAppearanceEntry const *enchantment, std::string_view text)
static bool IsColorValid(SpellItemEnchantmentEntry const *, HyperlinkColor c)
static bool IsTextValid(SpellItemEnchantmentEntry const *enchantment, std::string_view text)
static bool IsColorValid(TransmogSetEntry const *, HyperlinkColor c)
static bool IsTextValid(TransmogSetEntry const *set, std::string_view text)
static bool IsColorValid(WorldMapLinkData const &, HyperlinkColor c)
static bool IsTextValid(WorldMapLinkData const &, std::string_view)
static bool IsTextValid(typename T::value_type, std::string_view)
static bool IsColorValid(typename T::value_type, HyperlinkColor)
LocalizedString Name
LocalizedString MapName
LocalizedString ActivityName
std::vector< std::string > LogTitle
Definition QuestDef.h:445
LocalizedString DisplayName
LocalizedString Name