Owner-authorized source migration: 31 helper-only translation units were merged into their callers or existing inline headers; both build manifests updated. Includes the preserved source payload, Lua configuration matching wowdecomp, and exact malloca expansion without diagnostic pragmas. 449 native units compiled locally and 449 staged units compiled by the pinned server toolchain. No previously byte-matched function regressed. Compiler mapping diagnostics remain recorded as needs_cleanup, not accepted or peer-confirmed. Existing independent review records are preserved. README.md is intentionally unchanged.
208 lines
4.9 KiB
C++
208 lines
4.9 KiB
C++
#include "UI/Text/TextMarkup.h"
|
|
|
|
#include <windows.h>
|
|
|
|
namespace {
|
|
bool Hex(unsigned char value) {
|
|
return (value >= '0' && value <= '9') || (value >= 'a' && value <= 'f') || (value >= 'A' && value <= 'F');
|
|
}
|
|
|
|
bool Color(const char* text, const char* end) {
|
|
if (*text != '^' || end - text < 6) {
|
|
return false;
|
|
}
|
|
|
|
for (int index = 1; index <= 6; ++index) {
|
|
if (!Hex(static_cast<unsigned char>(text[index]))) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// OFFSET: 0x00A81A70
|
|
// FUNCTION: fn_16980
|
|
bool HasTextLinks(const std::string& text) {
|
|
return text.find("<URL>") != std::string::npos || text.find("<NAVI>") != std::string::npos || text.find("<INFO>") != std::string::npos ||
|
|
text.find("<ITEML>") != std::string::npos || text.find("<TIPBOX>") != std::string::npos || text.find("<QUEST>") != std::string::npos;
|
|
}
|
|
|
|
// OFFSET: 0x008791E0
|
|
// FUNCTION: fn_11214
|
|
std::string ExpandTextEscapes(std::string text) {
|
|
auto position = text.find("\\n");
|
|
|
|
while (position != std::string::npos) {
|
|
text.replace(position, 2, "\n");
|
|
position = text.find("\\n");
|
|
}
|
|
|
|
position = text.find("\\r");
|
|
|
|
while (position != std::string::npos) {
|
|
text.replace(position, 2, "\r");
|
|
position = text.find("\\r");
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
// OFFSET: 0x00A7EDC0
|
|
// FUNCTION: fn_16961
|
|
std::string RemoveTextColors(const char* text, unsigned short codePage) {
|
|
const char* end = text + std::char_traits<char>::length(text);
|
|
const char* current = text;
|
|
std::string result;
|
|
|
|
while (current < end) {
|
|
const char* next = current;
|
|
|
|
while (next < end && !Color(next, end)) {
|
|
next = CharNextExA(codePage, next, 0);
|
|
}
|
|
|
|
result.append(current, next);
|
|
|
|
if (next >= end) {
|
|
break;
|
|
}
|
|
|
|
current = next + 7;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// OFFSET: 0x00A7F660
|
|
// FUNCTION: fn_16966
|
|
std::string DisplayTextTag(std::string text, const std::string& begin, const std::string& end) {
|
|
auto position = text.find(begin);
|
|
|
|
if (position == std::string::npos) {
|
|
return text;
|
|
}
|
|
|
|
std::string result;
|
|
|
|
do {
|
|
if (position > 0) {
|
|
result.append(text, 0, position);
|
|
}
|
|
|
|
const auto content = position + begin.size();
|
|
std::string tail = text.substr(content);
|
|
const auto close = tail.find(end);
|
|
|
|
if (close == std::string::npos) {
|
|
position = content;
|
|
} else {
|
|
result.append(tail, 0, close);
|
|
text = tail.substr(close + end.size());
|
|
position = text.find(begin);
|
|
}
|
|
} while (position != std::string::npos);
|
|
|
|
result += text;
|
|
return result;
|
|
}
|
|
|
|
// OFFSET: 0x00A7EF40
|
|
// FUNCTION: fn_16962
|
|
std::string RemoveTextLinkInfo(std::string text) {
|
|
auto position = text.find("<INFO>");
|
|
|
|
if (position == std::string::npos) {
|
|
return text;
|
|
}
|
|
|
|
std::string result;
|
|
|
|
do {
|
|
if (position > 0) {
|
|
result.append(text, 0, position);
|
|
}
|
|
|
|
const auto close = text.find("</INFO>");
|
|
|
|
if (close != std::string::npos) {
|
|
text = text.substr(close + 7);
|
|
position = text.find("<INFO>");
|
|
}
|
|
} while (position != std::string::npos);
|
|
|
|
result += text;
|
|
return result;
|
|
}
|
|
|
|
// OFFSET: 0x00A7CAB0
|
|
// FUNCTION: fn_16950
|
|
void ExtractTextLinks(std::string text, const std::string& begin, const std::string& end, std::vector<TextLink>& links) {
|
|
auto position = text.find(begin);
|
|
|
|
if (position == std::string::npos) {
|
|
return;
|
|
}
|
|
|
|
std::string prefix = RemoveTextLinkInfo(text.substr(0, position));
|
|
std::uint32_t offset = static_cast<std::uint32_t>(prefix.size());
|
|
|
|
do {
|
|
const auto content = position + begin.size();
|
|
std::string tail = text.substr(content);
|
|
const auto close = tail.find(end);
|
|
|
|
if (close == std::string::npos) {
|
|
position = content;
|
|
continue;
|
|
}
|
|
|
|
std::string display = tail.substr(0, close);
|
|
std::string metadata;
|
|
const auto info = display.find("<INFO>");
|
|
|
|
if (info != std::string::npos) {
|
|
display = tail.substr(0, info);
|
|
metadata = tail.substr(info + 6);
|
|
const auto infoEnd = metadata.find("</INFO>");
|
|
|
|
if (infoEnd != std::string::npos) {
|
|
metadata.resize(infoEnd);
|
|
}
|
|
}
|
|
|
|
TextLink link;
|
|
link.position = offset;
|
|
link.display = display;
|
|
link.metadata = metadata;
|
|
links.push_back(std::move(link));
|
|
offset += static_cast<std::uint32_t>(display.size());
|
|
|
|
text = tail.substr(close + end.size());
|
|
position = text.find(begin);
|
|
|
|
if (position != std::string::npos) {
|
|
prefix = RemoveTextLinkInfo(text.substr(0, position));
|
|
offset += static_cast<std::uint32_t>(prefix.size());
|
|
}
|
|
} while (position != std::string::npos);
|
|
}
|
|
|
|
// OFFSET: 0x00877650
|
|
// FUNCTION: fn_11210
|
|
void SplitTextLinkArguments(std::string text, std::vector<std::string>& arguments) {
|
|
if (text.empty()) {
|
|
return;
|
|
}
|
|
|
|
auto comma = text.find(',');
|
|
|
|
while (comma != std::string::npos) {
|
|
arguments.push_back(text.substr(0, comma));
|
|
text = text.substr(comma + 1);
|
|
comma = text.find(',');
|
|
}
|
|
|
|
arguments.push_back(text);
|
|
}
|