#include "ui/CSimpleHTML.hpp" #include "ui/CSimpleFontedFrameFont.hpp" #include "ui/CSimpleFontString.hpp" #include "ui/CSimpleHTMLScript.hpp" #include "ui/CSimpleTop.hpp" #include "util/CStatus.hpp" #include "util/SFile.hpp" #include "util/StringTo.hpp" #include int32_t CSimpleHTML::s_metatable; int32_t CSimpleHTML::s_objectType; CONTENTNODE::~CONTENTNODE() { if (this->string) { delete string; } } void CSimpleHTML::CreateScriptMetaTable() { lua_State* L = FrameScript_GetContext(); int32_t ref = FrameScript_Object::CreateScriptMetaTable(L, &CSimpleHTML::RegisterScriptMethods); CSimpleHTML::s_metatable = ref; } int32_t CSimpleHTML::GetObjectType() { if (!CSimpleHTML::s_objectType) { CSimpleHTML::s_objectType = ++FrameScript_Object::s_objectTypes; } return CSimpleHTML::s_objectType; } void CSimpleHTML::RegisterScriptMethods(lua_State* L) { CSimpleFrame::RegisterScriptMethods(L); FrameScript_Object::FillScriptMethodTable(L, SimpleHTMLMethods, NUM_SIMPLE_HTML_SCRIPT_METHODS); } CSimpleHTML::CSimpleHTML(CSimpleFrame* parent) : CSimpleHyperlinkedFrame(parent) { // TODO for (int32_t i = 0; i < NUM_HTML_TEXT_TYPES; i++) { auto m = SMemAlloc(sizeof(CSimpleFontedFrameFont), __FILE__, __LINE__, 0x0); auto font = new (m) CSimpleFontedFrameFont(this); this->m_fonts[i] = font; } // TODO } void CSimpleHTML::AddText(const char* text, HTML_TEXT_TYPE type, uint32_t justify) { // TODO auto stringMem = CDataAllocator::GetData(CSimpleFontString::s_allocator, 0x0, __FILE__, __LINE__); auto stringMem = SMemAlloc(sizeof(CSimpleFontString), __FILE__, __LINE__, 0x0); auto string = new (stringMem) CSimpleFontString(this, DRAWLAYER_ARTWORK, 1); if (this->m_layoutAnchor) { string->SetPoint( FRAMEPOINT_TOPLEFT, this->m_layoutAnchor, FRAMEPOINT_BOTTOMLEFT, 0.0f, this->m_layoutOffset, 1 ); } else { string->SetPoint( FRAMEPOINT_TOPLEFT, this, FRAMEPOINT_TOPLEFT, 0.0f, 0.0f, 1 ); } string->SetWidth(this->GetWidth()); auto font = this->m_fonts[type]; if (!font->m_attributes.m_font.GetString()) { font = this->m_fonts[0]; } string->SetFontObject(font); string->SetJustifyH(justify); // TODO something with style flag 0x1000? string->SetText(text, 1); this->m_layoutAnchor = string; this->m_layoutOffset = -CSimpleTop::RoundToPixelHeight(string->m_spacing); auto contentNode = this->m_content.NewNode(2, 0, 0x0); contentNode->string = string; // TODO contentNode->dword8 = SStrStr(text, "|H") != 0; } void CSimpleHTML::ClearContent() { this->m_content.DeleteAll(); this->m_layoutAnchor = nullptr; this->m_layoutOffset = 0.0f; // TODO clear hyperlinks } void CSimpleHTML::FontUpdated(CSimpleFontedFrameFont* font, int32_t a3) { // TODO } int32_t CSimpleHTML::GetScriptMetaTable() { return CSimpleHTML::s_metatable; } bool CSimpleHTML::IsA(int32_t type) { return type == CSimpleHTML::s_objectType || type == CSimpleFrame::s_objectType || type == CScriptRegion::s_objectType || type == CScriptObject::s_objectType; } void CSimpleHTML::LoadXML(XMLNode* node, CStatus* status) { CSimpleFrame::LoadXML(node, status); const char* fontAttr = node->GetAttributeByName("font"); if (fontAttr && *fontAttr) { auto font = CSimpleFont::GetFont(fontAttr, 0); if (font) { for (int32_t i = 0; i < NUM_HTML_TEXT_TYPES; i++) { this->m_fonts[i]->SetFontObject(font); } } else { status->Add( STATUS_WARNING, "%s %s: Couldn't find font object named %s", this->GetObjectTypeName(), this->GetDisplayName(), fontAttr ); } } for (XMLNode* child = node->m_child; child; child = child->m_next) { if (!SStrCmpI(child->GetName(), "FontString", STORM_MAX_STR)) { this->m_fonts[HTML_TEXT_NORMAL]->LoadXML(child, status); } else if (!SStrCmpI(child->GetName(), "FontStringHeader1", STORM_MAX_STR)) { this->m_fonts[HTML_TEXT_HEADER1]->LoadXML(child, status); } else if (!SStrCmpI(child->GetName(), "FontStringHeader2", STORM_MAX_STR)) { this->m_fonts[HTML_TEXT_HEADER2]->LoadXML(child, status); } else if (!SStrCmpI(child->GetName(), "FontStringHeader3", STORM_MAX_STR)) { this->m_fonts[HTML_TEXT_HEADER3]->LoadXML(child, status); } } const char* fileAttr = node->GetAttributeByName("file"); void* fileData; if (fileAttr && SFile::Load(nullptr, fileAttr, &fileData, nullptr, 1, 0x1, nullptr)) { this->SetText(static_cast(fileData), nullptr); SFile::Unload(fileData); } // TODO hyperlinkFormat } void CSimpleHTML::ParseBODY(XMLNode* node, CStatus* status) { for (auto child = node->m_child; child; child = child->m_next) { if (!SStrCmpI(child->GetName(), "H1", STORM_MAX_STR)) { this->ParseP(child, HTML_TEXT_HEADER1, status); } else if (!SStrCmpI(child->GetName(), "H2", STORM_MAX_STR)) { this->ParseP(child, HTML_TEXT_HEADER2, status); } else if (!SStrCmpI(child->GetName(), "H3", STORM_MAX_STR)) { this->ParseP(child, HTML_TEXT_HEADER3, status); } else if (!SStrCmpI(child->GetName(), "P", STORM_MAX_STR)) { this->ParseP(child, HTML_TEXT_NORMAL, status); } else if (!SStrCmpI(child->GetName(), "BR", STORM_MAX_STR)) { this->AddText("\n", HTML_TEXT_NORMAL, 1); } else if (!SStrCmpI(child->GetName(), "IMG", STORM_MAX_STR)) { this->ParseIMG(child, status); } else { status->Add( STATUS_WARNING, "Frame %s: Unknown element type: %s", this->GetDisplayName(), child->GetName() ); } } } void CSimpleHTML::ParseIMG(XMLNode* node, CStatus* status) { // TODO } void CSimpleHTML::ParseP(XMLNode* node, HTML_TEXT_TYPE type, CStatus* status) { uint32_t justify = 1; auto justifyAttr = node->GetAttributeByName("align"); if (justifyAttr && *justifyAttr) { StringToJustify(justifyAttr, justify); } auto body = node->GetBody(); if (!body) { body = ""; } auto text = SStrDupA(body, __FILE__, __LINE__); uint32_t offset = 0; for (auto child = node->m_child; child; child = child->m_next) { if (!SStrCmpI(child->GetName(), "BR", STORM_MAX_STR)) { char* newText = static_cast(SMemAlloc(SStrLen(text) + 3, __FILE__, __LINE__, 0x0)); SStrCopy(newText, text, child->m_offset + offset + 1); SStrPack(newText, "|n", STORM_MAX_STR); SStrPack(newText, &text[child->m_offset + offset], STORM_MAX_STR); SMemFree(text, __FILE__, __LINE__, 0x0); text = newText; offset += 2; } else if (!SStrCmpI(child->GetName(), "A", STORM_MAX_STR)) { // TODO handle anchor child } else { status->Add( STATUS_WARNING, "Frame %s: Unknown element type: %s", this->GetDisplayName(), child->GetName() ); } } int32_t v22 = 0; int32_t v36 = 1; auto v23 = text; auto v24 = text; if (*text) { do { switch (*v23) { case '\t': case '\n': case '\r': case ' ': { if (!v36 && !v22) { v22 = 1; *v24 = ' '; v24++; } break; } case '|': { if (v23[1] != 'n') { v22 = 0; v36 = 0; *v24 = *v23; v24++; break; } if (v24 > text && v22) { v24--; } *v24 = '|'; v24++; *v24 = 'n'; v24++; v22 = 0; v36 = 1; v23++; break; } default: { v22 = 0; v36 = 0; *v24 = *v23; v24++; break; } } v23++; } while (*v23); if (v24 > text && v22) { v24--; } } *v24 = '\0'; this->AddText(text, type, justify); SMemFree(text, __FILE__, __LINE__, 0x0); } bool CSimpleHTML::SetText(const char* text, CStatus* status) { static auto s_nullStatus = CStatus(); if (!status) { status = &s_nullStatus; } if (!text) { text = ""; } // TODO some sort of guard condition this->ClearContent(); auto textLength = SStrLen(text); auto tree = XMLTree_Load(text, textLength); bool success = false; if (tree) { auto node = XMLTree_GetRoot(tree); if (!SStrCmpI(node->GetName(), "HTML", STORM_MAX_STR)) { for (auto child = node->m_child; child; child = child->m_next) { if (!SStrCmpI(child->GetName(), "BODY", STORM_MAX_STR)) { success = true; this->ParseBODY(child, status); break; } status->Add( STATUS_WARNING, "Frame %s: Unknown element type: %s (expected BODY)", this->GetDisplayName(), node->GetName() ); } } else { status->Add( STATUS_WARNING, "Frame %s: Unknown element type: %s (expected HTML)", this->GetDisplayName(), node->GetName() ); } XMLTree_Free(tree); } if (success) { // TODO set flag return true; } this->AddText(text, HTML_TEXT_NORMAL, 1); return false; }