#include "Runtime/GuiSys/CTextParser.hpp" #include "Runtime/GuiSys/CFontImageDef.hpp" #include "Runtime/GuiSys/CTextExecuteBuffer.hpp" namespace metaforce { static float u16stof(const char16_t* str) { char cstr[16]; int i; for (i = 0; i < 15 && str[i] != u'\0'; ++i) cstr[i] = str[i]; cstr[i] = '\0'; return strtof(cstr, nullptr); } CTextColor CTextParser::ParseColor(const char16_t* str, int len) { u8 r = GetColorValue(str + 1); u8 g = GetColorValue(str + 3); u8 b = GetColorValue(str + 5); u8 a = 0xff; if (len == 9) a = GetColorValue(str + 7); CTextColor ret; ret.fromRGBA8(r, g, b, a); return ret; } u8 CTextParser::GetColorValue(const char16_t* str) { return (FromHex(str[0]) << 4) + FromHex(str[1]); } u32 CTextParser::FromHex(char16_t ch) { if (ch >= u'0' && ch <= u'9') return ch - u'0'; if (ch >= u'A' && ch <= u'F') return ch - u'A' + 10; if (ch >= u'a' && ch <= u'f') return ch - u'a' + 10; return 0; } s32 CTextParser::ParseInt(const char16_t* str, int len, bool signVal) { bool neg = false; int procCur = 0; if (signVal && len && *str == u'-') { neg = true; procCur = 1; } int val = 0; while (len > procCur) { val *= 10; char16_t ch = str[procCur]; val += ch - u'0'; ++procCur; } return neg ? -val : val; } bool CTextParser::Equals(const char16_t* str, int len, const char16_t* other) { for (int i = 0; *other && i < len; ++i, ++str, ++other) { if (*str != *other) return false; } return *other == u'\0'; } bool CTextParser::BeginsWith(const char16_t* str, int len, const char16_t* other) { for (int i = 0; *other && i < len; ++i, ++str, ++other) { if (*str != *other) return false; } return true; } void CTextParser::ParseTag(CTextExecuteBuffer& out, const char16_t* str, int len, const std::vector>* txtrMap) { if (BeginsWith(str, len, u"font=")) { TToken font = GetFont(str + 5, len - 5); out.AddFont(font); } else if (BeginsWith(str, len, u"image=")) { CFontImageDef image = GetImage(str + 6, len - 6, txtrMap); out.AddImage(image); } else if (BeginsWith(str, len, u"fg-color=")) { CTextColor color = ParseColor(str + 9, len - 9); out.AddColor(EColorType::Foreground, color); } else if (BeginsWith(str, len, u"main-color=")) { CTextColor color = ParseColor(str + 11, len - 11); out.AddColor(EColorType::Main, color); } else if (BeginsWith(str, len, u"geometry-color=")) { CTextColor color = ParseColor(str + 15, len - 15); out.AddColor(EColorType::Geometry, color); } else if (BeginsWith(str, len, u"outline-color=")) { CTextColor color = ParseColor(str + 14, len - 14); out.AddColor(EColorType::Outline, color); } else if (BeginsWith(str, len, u"color")) { const char16_t* valCur = str + 7; len -= 7; int val = str[6] - u'0'; if (str[7] >= u'0' && str[7] <= u'9') { ++valCur; --len; val *= 10; val += str[7] - u'0'; } if (Equals(valCur + 10, len - 10, u"no")) out.AddRemoveColorOverride(val); else { CTextColor color = ParseColor(str + 10, len - 10); out.AddColorOverride(val, color); } } else if (BeginsWith(str, len, u"line-spacing=")) { out.AddLineSpacing(ParseInt(str + 13, len - 13, true) / 100.0f); } else if (BeginsWith(str, len, u"line-extra-space=")) { out.AddLineExtraSpace(ParseInt(str + 17, len - 17, true)); } else if (BeginsWith(str, len, u"just=")) { if (Equals(str + 5, len - 5, u"left")) out.AddJustification(EJustification::Left); else if (Equals(str + 5, len - 5, u"center")) out.AddJustification(EJustification::Center); else if (Equals(str + 5, len - 5, u"right")) out.AddJustification(EJustification::Right); else if (Equals(str + 5, len - 5, u"full")) out.AddJustification(EJustification::Full); else if (Equals(str + 5, len - 5, u"nleft")) out.AddJustification(EJustification::NLeft); else if (Equals(str + 5, len - 5, u"ncenter")) out.AddJustification(EJustification::NCenter); else if (Equals(str + 5, len - 5, u"nright")) out.AddJustification(EJustification::NRight); } else if (BeginsWith(str, len, u"vjust=")) { if (Equals(str + 6, len - 6, u"top")) out.AddVerticalJustification(EVerticalJustification::Top); else if (Equals(str + 6, len - 6, u"center")) out.AddVerticalJustification(EVerticalJustification::Center); else if (Equals(str + 6, len - 6, u"bottom")) out.AddVerticalJustification(EVerticalJustification::Bottom); else if (Equals(str + 6, len - 6, u"full")) out.AddVerticalJustification(EVerticalJustification::Full); else if (Equals(str + 6, len - 6, u"ntop")) out.AddVerticalJustification(EVerticalJustification::NTop); else if (Equals(str + 6, len - 6, u"ncenter")) out.AddVerticalJustification(EVerticalJustification::NCenter); else if (Equals(str + 6, len - 6, u"nbottom")) out.AddVerticalJustification(EVerticalJustification::NBottom); } else if (Equals(str, len, u"push")) { out.AddPushState(); } else if (Equals(str, len, u"pop")) { out.AddPopState(); } } CFontImageDef CTextParser::GetImage(const char16_t* str, int len, const std::vector>* txtrMap) { int commaCount = 0; for (int i = 0; i < len; ++i) if (str[i] == u',') ++commaCount; if (commaCount) { std::u16string iterable(str, len); size_t tokenPos; size_t commaPos; commaPos = iterable.find(u','); iterable[commaPos] = u'\0'; tokenPos = commaPos + 1; auto AdvanceCommaPos = [&]() { commaPos = iterable.find(u',', tokenPos); if (commaPos == std::u16string::npos) commaPos = iterable.size(); iterable[commaPos] = u'\0'; }; auto AdvanceTokenPos = [&]() { tokenPos = commaPos + 1; }; if (BeginsWith(str, len, u"A")) { /* Animated texture array */ AdvanceCommaPos(); float interval = u16stof(&iterable[tokenPos]); AdvanceTokenPos(); std::vector> texs; texs.reserve(commaCount - 1); do { AdvanceCommaPos(); texs.emplace_back(x0_store.GetObj({SBIG('TXTR'), GetAssetIdFromString(&iterable[tokenPos], len, txtrMap)})); AdvanceTokenPos(); } while (commaPos != iterable.size()); return CFontImageDef(texs, interval, zeus::CVector2f(1.f, 1.f)); } else if (BeginsWith(str, len, u"SA")) { /* Scaled and animated texture array */ AdvanceCommaPos(); float interval = u16stof(&iterable[tokenPos]); AdvanceTokenPos(); AdvanceCommaPos(); float cropX = u16stof(&iterable[tokenPos]); AdvanceTokenPos(); AdvanceCommaPos(); float cropY = u16stof(&iterable[tokenPos]); AdvanceTokenPos(); std::vector> texs; texs.reserve(commaCount - 3); do { AdvanceCommaPos(); texs.emplace_back(x0_store.GetObj({SBIG('TXTR'), GetAssetIdFromString(&iterable[tokenPos], len, txtrMap)})); AdvanceTokenPos(); } while (commaPos != iterable.size()); return CFontImageDef(texs, interval, zeus::CVector2f(cropX, cropY)); } else if (BeginsWith(str, len, u"SI")) { /* Scaled single texture */ AdvanceCommaPos(); float cropX = u16stof(&iterable[tokenPos]); AdvanceTokenPos(); AdvanceCommaPos(); float cropY = u16stof(&iterable[tokenPos]); AdvanceTokenPos(); AdvanceCommaPos(); TToken tex = x0_store.GetObj({SBIG('TXTR'), GetAssetIdFromString(&iterable[tokenPos], len, txtrMap)}); AdvanceTokenPos(); return CFontImageDef(tex, zeus::CVector2f(cropX, cropY)); } } TToken tex = x0_store.GetObj({SBIG('TXTR'), GetAssetIdFromString(str, len, txtrMap)}); return CFontImageDef(tex, zeus::CVector2f(1.f, 1.f)); } CAssetId CTextParser::GetAssetIdFromString(const char16_t* str, int len, const std::vector>* txtrMap) { u8 r = GetColorValue(str); u8 g = GetColorValue(str + 2); u8 b = GetColorValue(str + 4); u8 a = GetColorValue(str + 6); CAssetId id = ((r << 24) | (g << 16) | (b << 8) | a) & 0xffffffff; if (len == 16) { r = GetColorValue(str + 8); g = GetColorValue(str + 10); b = GetColorValue(str + 12); a = GetColorValue(str + 14); id = (id.Value() << 32) | (((r << 24) | (g << 16) | (b << 8) | a) & 0xffffffff); } if (txtrMap) { auto search = rstl::binary_find(txtrMap->begin(), txtrMap->end(), id, [](const std::pair& a) { return a.first; }); if (search != txtrMap->end()) id = search->second; } return id; } TToken CTextParser::GetFont(const char16_t* str, int len) { return x0_store.GetObj({SBIG('FONT'), GetAssetIdFromString(str, len, nullptr)}); } void CTextParser::ParseText(CTextExecuteBuffer& out, const char16_t* str, int len, const std::vector>* txtrMap) { int b = 0, e = 0; for (b = 0, e = 0; str[e] && (len == -1 || e < len);) { if (str[e] != u'&') { ++e; continue; } if ((len == -1 || e + 1 < len) && str[e + 1] != u'&') { if (e > b) out.AddString(str + b, e - b); ++e; b = e; while (str[e] && (len == -1 || e < len) && str[e] != u';') ++e; ParseTag(out, str + b, e - b, txtrMap); b = e + 1; } else { out.AddString(str + b, e + 1 - b); e += 2; b = e; } } if (e > b) out.AddString(str + b, e - b); } } // namespace metaforce