#include #include #include #include #include #include #include namespace nw4r { namespace lyt { NW4R_UT_RTTI_DEF_DERIVED(TextBox, Pane); namespace { // ReverseYAxis__Q34nw4r3lyt25@unnamed@lyt_textBox_cpp@FPQ34nw4r4math5MTX34 void ReverseYAxis(math::MTX34 *pMtx) { // Based on impl in LoadMtx pMtx->_01 = -pMtx->_01; pMtx->_11 = -pMtx->_11; pMtx->_21 = -pMtx->_21; } // ClampColor__Q34nw4r3lyt25@unnamed@lyt_textBox_cpp@Fs u8 ClampColor(s16 colVal) { // The CLAMP macro was reversed for this -_- return colVal < 0 ? 0 : (colVal > 0xFF ? 0xFF : colVal); } // GetColor__Q34nw4r3lyt25@unnamed@lyt_textBox_cpp@FRC11_GXColorS10 ut::Color GetColor(const GXColorS10 &src) { GXColor dst; dst.r = ClampColor(src.r); dst.g = ClampColor(src.g); dst.b = ClampColor(src.b); dst.a = ClampColor(src.a); return dst; } template void CalcStringRect(ut::Rect *pRect, ut::TextWriterBase *pTextWriter, const T *str, int length, f32 maxWidth) { ut::TextWriterBase myCopy = *pTextWriter; CalcStringRectImpl(pRect, &myCopy, str, length, maxWidth); } template int CalcLineStrNum(f32 *pWidth, ut::TextWriterBase *pTextWriter, const T *str, int length, f32 maxWidth, bool *pbOver) { ut::Rect rect; ut::TextWriterBase myCopy = *pTextWriter; myCopy.SetCursor(0.0f, 0.0f); int ret = CalcLineRectImpl(&rect, &myCopy, str, length, maxWidth, pbOver); *pWidth = rect.GetWidth(); return ret; } template int CalcLineRectImpl(ut::Rect *pRect, ut::TextWriterBase *pTextWriter, const T *str, int length, f32 maxWidth, bool *pbOver) { ut::PrintContext context = {pTextWriter, str, 0.0f, 0.0f, 0}; f32 x = 0.0f; const ut::Font *font = pTextWriter->GetFont(); bool bCharSpace = false; ut::CharStrmReader reader = font->GetCharStrmReader(); const T *prStrPos = (const T *)reader.GetCurrentPos(); pRect->left = 0.0f; pRect->right = 0.0f; pRect->top = ut::Min(0.0f, pTextWriter->GetLineHeight()); pRect->bottom = ut::Max(0.0f, pTextWriter->GetLineHeight()); *pbOver = false; reader.Set(str); ut::Rect prMaxRect = *pRect; u16 code = reader.Next(); while (((const T *)reader.GetCurrentPos() - str) <= length) { if (code < ' ') { ut::Operation operation; ut::Rect rect(x, 0.0f, 0.0f, 0.0f); context.str = (const T *)reader.GetCurrentPos(); context.flags = !bCharSpace; pTextWriter->SetCursorX(x); operation = pTextWriter->GetTagProcessor()->CalcRect(&rect, code, &context); reader.Set(context.str); pRect->left = ut::Min(pRect->left, rect.left); pRect->top = ut::Min(pRect->top, rect.top); pRect->right = ut::Max(pRect->right, rect.right); pRect->bottom = ut::Max(pRect->bottom, rect.bottom); x = pTextWriter->GetCursorX(); if (pRect->GetWidth() > maxWidth) { *pbOver = true; break; } if (operation == ut::OPERATION_END_DRAW) { return length; } else if (operation == ut::OPERATION_NO_CHAR_SPACE) { bCharSpace = false; } else if (operation == ut::OPERATION_CHAR_SPACE) { bCharSpace = true; } else if (operation == ut::OPERATION_NEXT_LINE) { break; } } else { if (bCharSpace) { x += pTextWriter->GetCharSpace(); } bCharSpace = true; if (pTextWriter->IsWidthFixed()) { x += pTextWriter->GetFixedWidth(); } else { x += pTextWriter->GetFont()->GetCharWidth(code) * pTextWriter->GetScaleH(); } pRect->left = ut::Min(pRect->left, x); pRect->right = ut::Max(pRect->right, x); if (pRect->GetWidth() > maxWidth) { *pbOver = true; break; } } prStrPos = (const T *)reader.GetCurrentPos(); code = reader.Next(); prMaxRect = *pRect; } if (*pbOver) { if (prStrPos) { *pRect = prMaxRect; return (prStrPos - str); } } return ((const T *)reader.GetCurrentPos() - str); } template void CalcStringRectImpl(ut::Rect *pRect, ut::TextWriterBase *pTextWriter, const T *str, int length, f32 maxWidth) { pRect->left = 0.0f; pRect->right = 0.0f; pRect->top = 0.0f; pRect->bottom = 0.0f; pTextWriter->SetCursor(0.0f, 0.0f); int remain = length; const T *pos = str; do { ut::Rect rect; bool bOver; int read = CalcLineRectImpl(&rect, pTextWriter, pos, remain, maxWidth, &bOver); pos += read; remain -= read; pRect->left = ut::Min(pRect->left, rect.left); pRect->top = ut::Min(pRect->top, rect.top); pRect->right = ut::Max(pRect->right, rect.right); pRect->bottom = ut::Max(pRect->bottom, rect.bottom); if (bOver) { CalcLineRectImpl(&rect, pTextWriter, L"\n", 1, maxWidth, &bOver); pRect->left = ut::Min(pRect->left, rect.left); pRect->top = ut::Min(pRect->top, rect.top); pRect->right = ut::Max(pRect->right, rect.right); pRect->bottom = ut::Max(pRect->bottom, rect.bottom); } } while (remain > 0); } } // namespace // __ct__Q34nw4r3lyt7TextBoxFUs TextBox::TextBox(u16 allocStrLen) { // TODO: Not in SS } // __ct__Q34nw4r3lyt7TextBoxFUsPCwPCQ34nw4r2ut4Font TextBox::TextBox(const wchar_t *str, const ut::Font *pFont) { // TODO: Not in SS } // __ct__Q34nw4r3lyt7TextBoxFUsPCwUsPCQ34nw4r2ut4Font TextBox::TextBox(u16, const wchar_t *str, u16, const ut::Font *pFont) { // TODO: Not in SS } // __ct__Q34nw4r3lyt7TextBoxFPCQ44nw4r3lyt3res7TextBoxRCQ34nw4r3lyt11ResBlockSet TextBox::TextBox(const res::TextBox *pBlock, const ResBlockSet &resBlockSet) : Pane(pBlock), mTextColors(), mFontSize() { u16 allocStrBufferLen = pBlock->textBufBytes / 2; if (allocStrBufferLen > 0) { allocStrBufferLen -= 1; } Init(allocStrBufferLen); if (pBlock->textStrBytes >= 2 && mTextBuf) { u16 resStringLen = pBlock->textStrBytes / 2 - 1; const wchar_t *pBlockText = detail::ConvertOffsToPtr(pBlock, pBlock->textStrOffset); SetString(pBlockText, 0, resStringLen); } mTextColors[0] = pBlock->textCols[0]; mTextColors[1] = pBlock->textCols[1]; mFontSize = pBlock->fontSize; mTextPosition = pBlock->textPosition; mBits.textAlignment = pBlock->textAlignment; mCharSpace = pBlock->charSpace; mLineSpace = pBlock->lineSpace; int i = pBlock->fontIdx; const res::Font *fonts = detail::ConvertOffsToPtr(resBlockSet.pFontList, sizeof(res::FontList)); const char *fontName = detail::ConvertOffsToPtr(fonts, fonts[i].nameStrOffset); ut::Font *pFont = resBlockSet.pResAccessor->GetFont(fontName); if (pFont) { mpFont = pFont; } else { void *fontRes = resBlockSet.pResAccessor->GetResource('font', fontName, nullptr); if (fontRes) { ut::ResFont *pResFont = Layout::NewObj(); if (pResFont) { bool bSucces = pResFont->SetResource(fontRes); mpFont = pResFont; mBits.bAllocFont = true; } } } const u32 *matOffsTbl = detail::ConvertOffsToPtr(resBlockSet.pMaterialList, sizeof(res::MaterialList)); const res::Material *pResMaterial = detail::ConvertOffsToPtr(resBlockSet.pMaterialList, matOffsTbl[pBlock->materialIdx]); mpMaterial = Layout::NewObj(pResMaterial, resBlockSet); } // Init__Q34nw4r3lyt7TextBoxFUs void TextBox::Init(u16 allocStrLen) { mTextBuf = nullptr; mTextBufBytes = 0; mTextLen = 0; mpFont = nullptr; mFontSize = Size(0.0f, 0.0f); mLineSpace = 0.0f; mCharSpace = 0.0f; mpTagProcessor = nullptr; SetTextPositionH(1); SetTextPositionV(1); memset(&mBits, 0, sizeof(mBits)); if (allocStrLen != 0) { AllocStringBuffer(allocStrLen); } } // __dt__Q34nw4r3lyt7TextBoxFv TextBox::~TextBox() { SetFont(nullptr); if (mpMaterial && !mpMaterial->IsUserAllocated()) { Layout::DeleteObj(mpMaterial); mpMaterial = nullptr; } FreeStringBuffer(); } // GetVtxColor__Q34nw4r3lyt7TextBoxCFUl ut::Color TextBox::GetVtxColor(u32 idx) const { return GetTextColor(idx); } // SetVtxColor__Q34nw4r3lyt7TextBoxFUlQ34nw4r2ut5Color void TextBox::SetVtxColor(u32 idx, ut::Color value) { SetTextColor(idx / 2, value); } // GetVtxColorElement__Q34nw4r3lyt7TextBoxCFUl u8 TextBox::GetVtxColorElement(u32 idx) const { return ((u8 *)mTextColors)[((idx / 2) & ~3) + (idx & 3)]; } // SetVtxColorElement__Q34nw4r3lyt7TextBoxFUlUc void TextBox::SetVtxColorElement(u32 idx, u8 value) { ((u8 *)mTextColors)[((idx / 2) & ~3) + (idx & 3)] = value; } // 1] IDK which one is accurate, both symbols exist // GetTextDrawRect__Q34nw4r3lyt7TextBoxCFv ut::Rect TextBox::GetTextDrawRect() const { return GetTextDrawRect(&ut::TextWriterBase()); } // 2] IDK which one is accurate, both symbols exist // GetTextDrawRect__Q34nw4r3lyt7TextBoxCFRCQ34nw4r3lyt8DrawInfo ut::Rect TextBox::GetTextDrawRect(const DrawInfo &drawInfo) const { // Not in SS return GetTextDrawRect(); } // DrawSelf__Q34nw4r3lyt7TextBoxFRCQ34nw4r3lyt8DrawInfo void TextBox::DrawSelf(const DrawInfo &drawInfo) { if (!mTextBuf || !mpFont || !mpMaterial) { return; } LoadMtx(drawInfo); ut::TextWriterBase writer; ut::Rect textRect = GetTextDrawRect(&writer); ut::Color topCol = detail::MultipleAlpha(mTextColors[0], mGlbAlpha); ut::Color btmCol = detail::MultipleAlpha(mTextColors[1], mGlbAlpha); writer.SetGradationMode(topCol != btmCol ? ut::CharWriter::GRADMODE_V : ut::CharWriter::GRADMODE_NONE); writer.SetTextColor(topCol, btmCol); ut::Color minCol = GetColor(mpMaterial->GetTevColor(0)); ut::Color maxCol = GetColor(mpMaterial->GetTevColor(1)); writer.SetColorMapping(minCol, maxCol); writer.SetupGX(); GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); f32 hMag = GetTextAlignMag(); const wchar_t *strPos = mTextBuf; writer.SetCursor(textRect.left, -textRect.top); f32 texWidth = textRect.GetWidth(); int remain = mTextLen; while (remain > 0) { bool bOver; f32 lineWidth; int lineStrNum = CalcLineStrNum(&lineWidth, &writer, strPos, remain, mSize.width, &bOver); f32 textPosX = textRect.left + hMag * (texWidth - lineWidth); writer.SetCursorX(textPosX); writer.PrintMutable(strPos, lineStrNum); if (bOver) { writer.PrintMutable(L"\n", 1); } strPos += lineStrNum; remain -= lineStrNum; } } // CalcLineStrNum__Q34nw4r3lyt25@unnamed@lyt_textBox_cpp@FPfPQ34nw4r2ut17TextWriterBasePCwifPb_i // CalcLineRectImpl__Q34nw4r3lyt25@unnamed@lyt_textBox_cpp@FPQ34nw4r2ut4RectPQ34nw4r2ut17TextWriterBasePCwifPb_i // GetStringBufferLength__Q34nw4r3lyt7TextBoxCFv u16 TextBox::GetStringBufferLength() const { if (mTextBufBytes == 0) { return 0; } return mTextBufBytes / sizeof(wchar_t) - 1; } // AllocStringBuffer__Q34nw4r3lyt7TextBoxFUs void TextBox::AllocStringBuffer(u16 minLen) { if (minLen == 0) { return; } u32 allocLen = minLen; u32 texBufBytes = (minLen + 1) * sizeof(wchar_t); if (texBufBytes > mTextBufBytes) { FreeStringBuffer(); mTextBuf = Layout::NewArray(allocLen + 1); if (mTextBuf) { mTextBufBytes = texBufBytes; } } } // FreeStringBuffer__Q34nw4r3lyt7TextBoxFv void TextBox::FreeStringBuffer() { if (mTextBuf) { Layout::DeletePrimArray(mTextBuf); mTextBuf = nullptr; mTextBufBytes = 0; } } // SetString__Q34nw4r3lyt7TextBoxFPCwUs u16 TextBox::SetString(const wchar_t *str, u16 dstIdx) { return SetStringImpl(str, dstIdx, wcslen(str)); } // SetString__Q34nw4r3lyt7TextBoxFPCwUsUs u16 TextBox::SetString(const wchar_t *str, u16 dstIdx, u16 strLen) { return SetStringImpl(str, dstIdx, strLen); } // SetStringImpl__Q34nw4r3lyt7TextBoxFPCwUsUl u16 TextBox::SetStringImpl(const wchar_t *str, u16 dstIdx, u32 strLen) { if (!mTextBuf) { return 0; } u16 bufLen = GetStringBufferLength(); if (dstIdx >= bufLen) { return 0; } u32 cpLen = ut::Min(strLen, bufLen - dstIdx); memcpy(&mTextBuf[dstIdx], str, cpLen * sizeof(wchar_t)); mTextLen = dstIdx + cpLen; mTextBuf[mTextLen] = L'\0'; return cpLen; } // GetFont__Q34nw4r3lyt7TextBoxCFv const ut::Font *TextBox::GetFont() const { return mpFont; } // SetFont__Q34nw4r3lyt7TextBoxFPCQ34nw4r2ut4Font void TextBox::SetFont(const ut::Font *pFont) { if (mBits.bAllocFont) { Layout::DeleteObj(const_cast(mpFont)); mBits.bAllocFont = false; } mpFont = pFont; if (mpFont) { SetFontSize(Size(mpFont->GetWidth(), mpFont->GetHeight())); } else { SetFontSize(Size(0.0f, 0.0f)); } } // LoadMtx__Q34nw4r3lyt7TextBoxFRCQ34nw4r3lyt8DrawInfo void TextBox::LoadMtx(const DrawInfo &drawInfo) { math::MTX34 mtx; if (drawInfo.IsMultipleViewMtxOnDraw()) { PSMTXConcat(drawInfo.GetViewMtx(), mGlbMtx, mtx); } else { PSMTXCopy(mGlbMtx, mtx); } ReverseYAxis(&mtx); GXLoadPosMtxImm(mtx, GX_PNMTX0); GXSetCurrentMtx(GX_PNMTX0); } // GetTextDrawRect__Q34nw4r3lyt7TextBoxCFPQ34nw4r2ut17TextWriterBase // BBA map failed me here xD, probably different impl ut::Rect TextBox::GetTextDrawRect(ut::TextWriterBase *pWriter) const { ut::Rect textRect; pWriter->SetCursor(0.0f, 0.0f); pWriter->SetFont(*mpFont); pWriter->SetFontSize(mFontSize.width, mFontSize.height); pWriter->SetLineSpace(mLineSpace); pWriter->SetCharSpace(mCharSpace); if (mpTagProcessor) { pWriter->SetTagProcessor(mpTagProcessor); } CalcStringRect(&textRect, pWriter, mTextBuf, mTextLen, mSize.width); Size textSize(textRect.GetWidth(), textRect.GetHeight()); math::VEC2 ltPos = GetVtxPos(); // Adjust for Alignment (left, center, right) ltPos.x += (mSize.width - textSize.width) * GetTextMagH(); ltPos.y -= (mSize.height - textSize.height) * GetTextMagV(); // Apply the new pos textRect.left = ltPos.x; textRect.top = ltPos.y; textRect.right = ltPos.x + textSize.width; textRect.bottom = ltPos.y - textSize.height; return textRect; } // CalcStringRect__Q34nw4r3lyt25@unnamed@lyt_textBox_cpp@FPQ34nw4r2ut4RectPQ34nw4r2ut17TextWriterBasePCwif_v // CalcStringRectImpl__Q34nw4r3lyt25@unnamed@lyt_textBox_cpp@FPQ34nw4r2ut4RectPQ34nw4r2ut17TextWriterBasePCwif_v // GetTextMagH__Q34nw4r3lyt7TextBoxCFv f32 TextBox::GetTextMagH() const { switch (GetTextPositionH()) { default: return 0.0f; case 1: return 0.5f; case 2: return 1.0f; } } // GetTextMagV__Q34nw4r3lyt7TextBoxCFv f32 TextBox::GetTextMagV() const { switch (GetTextPositionV()) { default: return 0.0f; case 1: return 0.5f; case 2: return 1.0f; } } // GetTextAlignMag__Q34nw4r3lyt7TextBoxCFv f32 TextBox::GetTextAlignMag() const { switch (mBits.textAlignment) { default: return GetTextMagH(); case 1: return 0.0f; case 2: return 0.5f; case 3: return 1.0f; } } // MakeDrawFlag__Q34nw4r3lyt7TextBoxCFv UNKTYPE TextBox::MakeDrawFlag() const { // TODO: Not In SS // Calls GetTextPositionH() } } // namespace lyt } // namespace nw4r