#include "d/lyt/d_textbox.h" #include "common.h" #include "d/d_font_manager.h" #include "d/d_gfx.h" #include "d/d_message.h" #include "d/d_tag_processor.h" #include "d/d_lyt_hio.h" #include "d/lyt/msg_window/d_lyt_msg_window.h" #include "libms/msgfile.h" #include "m/m_color.h" #include "nw4r/lyt/lyt_common.h" #include "nw4r/lyt/lyt_drawInfo.h" #include "nw4r/lyt/lyt_material.h" #include "nw4r/lyt/lyt_resources.h" #include "nw4r/lyt/lyt_textBox.h" #include "nw4r/lyt/lyt_types.h" #include "nw4r/ut/ut_CharWriter.h" #include "nw4r/ut/ut_Font.h" #include "nw4r/ut/ut_Rect.h" #include "rvl/GX/GXPixel.h" #include "rvl/GX/GXTev.h" #include "rvl/GX/GXTypes.h" #include dTextBox_c::dTextBox_c(const nw4r::lyt::res::TextBox *pBlock, const nw4r::lyt::ResBlockSet &resBlockSet) : nw4r::lyt::TextBox(pBlock, resBlockSet), mMyFontSize(0.0f, 0.0f), mMyTextScale(0.0f, 0.0f) { const nw4r::lyt::res::Font *fonts = nw4r::lyt::detail::ConvertOffsToPtr( resBlockSet.pFontList, sizeof(nw4r::lyt::res::FontList) ); const char *fontName = nw4r::lyt::detail::ConvertOffsToPtr(fonts, fonts[pBlock->fontIdx].nameStrOffset); for (u32 i = 0; i < 5; i++) { const char *name = dFontMng_c::getFontName(i); if (!std::strcmp(fontName, name)) { mFontIdx = i; break; } } init(); } /////////////////////////////////////////////////////////////////////////// // Begin largely copied from nw4r::lyt::TextBox /////////////////////////////////////////////////////////////////////////// inline u8 ClampColor(s16 colVal) { // The CLAMP macro was reversed for this -_- return colVal < 0 ? 0 : (colVal > 0xFF ? 0xFF : colVal); } nw4r::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; } static void CalcStringRect( nw4r::ut::Rect *pRect, nw4r::ut::TextWriterBase &textWriter, const wchar_t *str, int length, bool *pbWideScreenUnk, f32 maxWidth ); static void CalcStringRectImpl( nw4r::ut::Rect *pRect, nw4r::ut::TextWriterBase *pTextWriter, const wchar_t *str, int length, f32 maxWidth, bool *pbWideScreenUnk ); static int CalcLineRectImpl( nw4r::ut::Rect *pRect, nw4r::ut::TextWriterBase *pTextWriter, const wchar_t *str, int length, f32 maxWidth, bool *pbOver, bool *pbWideScreenUnk ); static int CalcLineStrNum( f32 *pWidth, nw4r::ut::TextWriterBase *pTextWriter, const wchar_t *str, int length, f32 maxWidth, bool *pbOver ); nw4r::ut::Rect dTextBox_c::GetMyTextDrawRect(nw4r::ut::TextWriterBase *pWriter, bool *pbWideScreenUnk) const { nw4r::ut::Rect textRect; pWriter->SetCursor(0.0f, 0.0f); pWriter->SetFont(*mpFont); pWriter->SetFontSize(GetFontSize().width, GetFontSize().height); pWriter->SetLineSpace(mLineSpace); pWriter->SetCharSpace(mCharSpace); if (mIsWidthFixed == true) { pWriter->EnableFixedWidth(true); pWriter->SetFixedWidth(mFixedWidth); } if (GetTagProcessor() != nullptr) { pWriter->SetTagProcessor(GetTagProcessor()); } CalcStringRect(&textRect, *pWriter, mTextBuf, mTextLen, pbWideScreenUnk, FLT_MAX); nw4r::lyt::Size textSize(textRect.GetWidth(), textRect.GetHeight()); nw4r::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; } static void CalcStringRect( nw4r::ut::Rect *pRect, nw4r::ut::TextWriterBase &textWriter, const wchar_t *str, int length, bool *pbWideScreenUnk, f32 maxWidth ) { nw4r::ut::TextWriterBase writerCpy(textWriter); CalcStringRectImpl(pRect, &writerCpy, str, length, maxWidth, pbWideScreenUnk); } static void CalcStringRectImpl( nw4r::ut::Rect *pRect, nw4r::ut::TextWriterBase *pTextWriter, const wchar_t *str, int length, f32 maxWidth, bool *pbWideScreenUnk ) { 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 wchar_t *pos = str; int i = 0; do { nw4r::ut::Rect rect; bool bOver; int read = CalcLineRectImpl(&rect, pTextWriter, pos, remain, maxWidth, &bOver, i == 0 ? pbWideScreenUnk : nullptr); pos += read; remain -= read; pRect->left = nw4r::ut::Min(pRect->left, rect.left); pRect->top = nw4r::ut::Min(pRect->top, rect.top); pRect->right = nw4r::ut::Max(pRect->right, rect.right); pRect->bottom = nw4r::ut::Max(pRect->bottom, rect.bottom); if (bOver) { CalcLineRectImpl(&rect, pTextWriter, L"\n", 1, maxWidth, &bOver, nullptr); pRect->left = nw4r::ut::Min(pRect->left, rect.left); pRect->top = nw4r::ut::Min(pRect->top, rect.top); pRect->right = nw4r::ut::Max(pRect->right, rect.right); pRect->bottom = nw4r::ut::Max(pRect->bottom, rect.bottom); } i++; } while (remain > 0); } static int CalcLineRectImpl( nw4r::ut::Rect *pRect, nw4r::ut::TextWriterBase *pTextWriter, const wchar_t *str, int length, f32 maxWidth, bool *pbOver, bool *pbWideScreenUnk ) { nw4r::ut::PrintContext context = {pTextWriter, str, 0.0f, 0.0f, 0}; f32 x = 0.0f; const nw4r::ut::Font *font = pTextWriter->GetFont(); bool bCharSpace = false; nw4r::ut::CharStrmReader reader = font->GetCharStrmReader(); const wchar_t *prStrPos = (const wchar_t *)reader.GetCurrentPos(); pRect->left = 0.0f; pRect->right = 0.0f; pRect->top = nw4r::ut::Min(0.0f, pTextWriter->GetLineHeight()); pRect->bottom = nw4r::ut::Max(0.0f, pTextWriter->GetLineHeight()); *pbOver = false; reader.Set(str); nw4r::ut::Rect prMaxRect = *pRect; u16 code = reader.Next(); while (((const wchar_t *)reader.GetCurrentPos() - str) <= length) { if (code < ' ') { nw4r::ut::Operation operation; nw4r::ut::Rect rect(x, 0.0f, 0.0f, 0.0f); context.str = (const wchar_t *)reader.GetCurrentPos(); context.flags = !bCharSpace; pTextWriter->SetCursorX(x); // ?????????? if (pbWideScreenUnk != nullptr && ((u32 *)context.str)[0] == 0 && dGfx_c::isTvModeWidescreen()) { *pbWideScreenUnk = true; } operation = pTextWriter->GetTagProcessor()->CalcRect(&rect, code, &context); reader.Set(context.str); pRect->left = nw4r::ut::Min(pRect->left, rect.left); pRect->top = nw4r::ut::Min(pRect->top, rect.top); pRect->right = nw4r::ut::Max(pRect->right, rect.right); pRect->bottom = nw4r::ut::Max(pRect->bottom, rect.bottom); x = pTextWriter->GetCursorX(); if (pRect->GetWidth() > maxWidth) { *pbOver = true; break; } if (operation == nw4r::ut::OPERATION_END_DRAW) { return length; } else if (operation == nw4r::ut::OPERATION_NO_CHAR_SPACE) { bCharSpace = false; } else if (operation == nw4r::ut::OPERATION_CHAR_SPACE) { bCharSpace = true; } else if (operation == nw4r::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 = nw4r::ut::Min(pRect->left, x); pRect->right = nw4r::ut::Max(pRect->right, x); if (pRect->GetWidth() > maxWidth) { *pbOver = true; break; } } prStrPos = (const wchar_t *)reader.GetCurrentPos(); code = reader.Next(); prMaxRect = *pRect; } if (*pbOver) { if (prStrPos) { *pRect = prMaxRect; return (prStrPos - str); } } return ((const wchar_t *)reader.GetCurrentPos() - str); } /////////////////////////////////////////////////////////////////////////// // End largely copied from nw4r::lyt::TextBox /////////////////////////////////////////////////////////////////////////// void dTextBox_c::setTextWithGlobalTextProcessor(const wchar_t *str, void *unk, ...) { va_list l; va_start(l, str); setTextWithGlobalTextProcessorV(str, unk, l); va_end(l); } void dTextBox_c::setTextWithGlobalTextProcessorV(const wchar_t *str, void *unk, va_list list) { SetFontSize(mMyTextScale); mHasTextWriter = true; mpMyTagProcessor = dMessage_c::getGlobalTagProcessor(); u32 outLen = 0; mWindowSubtype = mpMyTagProcessor->getMsgWindowSubtype(); mpMyTagProcessor->setIsShadowText(mIsShadowTextBox); mpMyTagProcessor->setField_0xEE2(0); mpMyTagProcessor->setField_0xEE3(1); static wchar_t destBuffer[0x200]; mpMyTagProcessor->formatV(nullptr, str, destBuffer, ARRAY_LENGTH(destBuffer), &outLen, unk, list); SetString(destBuffer, 0, outLen); } void dTextBox_c::setTextWithTextProcessor(const wchar_t *str, dTagProcessor_c *tagProcessor, void *unk, ...) { va_list l; va_start(l, str); setTextWithTextProcessorV(str, tagProcessor, unk, l); va_end(l); } void dTextBox_c::setTextWithTextProcessorV(const wchar_t *str, dTagProcessor_c *tagProcessor, void *unk, va_list list) { SetFontSize(mMyTextScale); mHasTextWriter = true; u32 outLen = 0; mpMyTagProcessor = tagProcessor; mWindowSubtype = mpMyTagProcessor->getMsgWindowSubtype(); mpMyTagProcessor->setIsShadowText(mIsShadowTextBox); mpMyTagProcessor->setField_0xEE2(0); mpMyTagProcessor->setField_0xEE3(1); static wchar_t destBuffer[0x200]; mpMyTagProcessor->formatV(nullptr, str, destBuffer, ARRAY_LENGTH(destBuffer), &outLen, unk, list); SetString(destBuffer, 0, outLen); } s32 dTextBox_c::calcTextLines(const wchar_t *src, dTagProcessor_c *tagProcessor) { static wchar_t destBuffer[0x200]; u32 outLen = 0; tagProcessor->format(this, src, destBuffer, ARRAY_LENGTH(destBuffer), &outLen, nullptr); return tagProcessor->getNumLinesMaybe(); } void dTextBox_c::setMessageWithGlobalTextProcessor(const char *labelId, void *unused, void *unk, ...) { va_list l; va_start(l, str); setMessageWithGlobalTextProcessorV(labelId, unused, unk, l); va_end(l); } void dTextBox_c::setMessageWithGlobalTextProcessorV(const char *labelId, void *unused, void *unk, va_list list) { const wchar_t *text = dMessage_c::getTextMessageByLabel(labelId, true, nullptr, 0); setTextWithGlobalTextProcessorV(text, unk, list); if (GetFont() != nullptr) { resizeTextToFit(GetSize().width); } } void dTextBox_c::setMessageWithGlobalTextProcessorAndMsbtInfo( MsbtInfo *info, const char *labelId, wchar_t *destBuf, u32 maxLen ) { const wchar_t *src = LMS_GetTextByLabel(info, labelId); SetFontSize(mMyTextScale); mHasTextWriter = true; field_0x1FA = 1; u32 outLen = 0; mpMyTagProcessor = dMessage_c::getGlobalTagProcessor(); mpMyTagProcessor->setMsgWindowSubtype(dLytMsgWindow_c::MSG_WINDOW_34); mWindowSubtype = mpMyTagProcessor->getMsgWindowSubtype(); mpMyTagProcessor->setIsShadowText(mIsShadowTextBox); mpMyTagProcessor->setField_0xEE2(0); mpMyTagProcessor->setField_0xEE3(1); mpMyTagProcessor->format(nullptr, src, destBuf, maxLen, &outLen, 0); SetString(destBuf, 0, outLen); } void dTextBox_c::setTextWithGlobalTextProcessor(const wchar_t *str) { setTextWithGlobalTextProcessor(str, nullptr); if (GetFont() != nullptr) { resizeTextToFit(GetSize().width); } } f32 dTextBox_c::GetLineWidth(f32 *pOutSpacing) const { if ((int)mTextLen <= 0) { return 0.0f; } f32 widthThisLine = 0.0f; int numCharsThisLine = 0; int maxNumCharacters = 0; const wchar_t *buf = GetString(); f32 maxWidth = widthThisLine; const nw4r::ut::Font *f = GetFont(); f32 charBaseWidth = mFontSize.width / f->GetWidth(); wchar_t c; while ((c = *buf, c != '\0')) { if (c == L'\x0E') { u8 commandLen = buf[3]; buf += commandLen / 2 + 4; } else if (c == L'\x0F') { buf += 3; } else if (c == L'\n') { if (widthThisLine > maxWidth) { maxWidth = widthThisLine; maxNumCharacters = numCharsThisLine; } widthThisLine = 0.0f; numCharsThisLine = 0; buf++; } else { if (mIsWidthFixed == true) { widthThisLine += mFixedWidth; } else { int charWid = f->GetCharWidth(c); widthThisLine += charBaseWidth * charWid; } numCharsThisLine++; buf++; } } if (widthThisLine > maxWidth) { maxWidth = widthThisLine; maxNumCharacters = numCharsThisLine; } f32 maxSpaceWidth = (maxNumCharacters - 1) * GetCharSpace(); maxWidth += maxSpaceWidth + 1.0f; if (pOutSpacing != nullptr) { *pOutSpacing = maxSpaceWidth; } return maxWidth; } f32 dTextBox_c::GetLinesHeight() const { if ((int)mTextLen <= 0) { return 0.0f; } const wchar_t *buf = GetString(); const nw4r::ut::Font *f = GetFont(); f32 assumedDefaultSize = 35.0f; f32 f3 = dLyt_HIO_c::getFn800B1F70(); nw4r::lyt::Size size = mMyFontSize; int fontHeight = f->GetHeight(); f3 *= (size.height / assumedDefaultSize) * fontHeight; f32 lineBaseHeight = f3; f32 totalHeight = 0.0f; wchar_t c; while ((c = *buf, c != '\0')) { if (c == L'\x0E') { u8 commandLen = buf[3]; s32 skip = commandLen / 2 + 4; u32 command = *((u32 *)(buf + 1)); if (command == 0x10008) { f32 nullHeight = dTagProcessor_c::fn_800B8040(0, mWindowSubtype); f32 cmdHeight = dTagProcessor_c::fn_800B8040(*((u8 *)(buf + 4)), mWindowSubtype); lineBaseHeight = f3 * (cmdHeight / nullHeight); } buf += skip; } else if (c == L'\x0F') { buf += 3; } else if (c == L'\n') { totalHeight += lineBaseHeight + GetLineSpace(); buf++; } else { buf++; } } totalHeight += lineBaseHeight; return totalHeight; } f32 dTextBox_c::getLineDrawWidth() const { return GetLineWidth(nullptr) / dGfx_c::get16x9to4x3WidthScaleF(); } f32 dTextBox_c::resizeTextToFit(f32 maxWidth) { if (!hasDynamicText()) { return 0.0f; } nw4r::lyt::Size size; size = GetFontSize(); f32 spacing = 0.0f; f32 computedTextWidth = GetLineWidth(&spacing); if (maxWidth >= computedTextWidth) { return 0.0f; } if (spacing < 0.0f) { spacing = 0.0f; } f32 resizeFactor = (maxWidth - spacing) / computedTextWidth; size.width *= resizeFactor; size.height = size.height; SetFontSize(size); return resizeFactor * 100.0f; } void dTextBox_c::init() { mpLytBase = nullptr; mIsWidthFixed = false; mFixedWidth = 0.0f; mHasTextWriter = true; mIsShadowTextBox = false; field_0x1F7 = 0; field_0x1F9 = false; field_0x1FA = 0; field_0x1F8 = 2; mpMyTagProcessor = nullptr; MySetFontSize(GetFontSize()); for (int i = 0; i < 10; i++) { mLineStrNums[i] = 0; mLineWidths[i] = 0.0f; mbOvers[i] = 0; } f32 f1 = 35.0f; if (mFontIdx > 1 && mFontIdx < 4) { f1 = 41.0f; } nw4r::lyt::Size sz = GetFontSize(); f32 scale = dLyt_HIO_c::getFn800B1F70(); f32 height = sz.height / f1; sz.width = GetFont()->GetWidth() * height * scale; sz.height = GetFont()->GetHeight() * height * scale; SetFontSize(sz); mMyTextScale = sz; mMyScale = 1.0f; mWindowSubtype = dLytMsgWindow_c::MSG_WINDOW_36; field_0x201 = true; } bool dTextBox_c::hasDynamicText() { u16 myNum = GetExtUserDataNum(); if (myNum == 0) { return false; } const nw4r::lyt::res::ExtUserData *myList = GetExtUserData(); for (int i = 0; i < myNum; i++) { SizedString<0x40> userDatName; userDatName = myList->GetName(); if (userDatName == "embed" && myList->GetType() == nw4r::lyt::res::TYPE_INT) { return true; } if (userDatName == "textScale" && myList->GetType() == nw4r::lyt::res::TYPE_INT) { return true; } if (userDatName == "copy" && myList->GetType() == nw4r::lyt::res::TYPE_STRING) { mIsShadowTextBox = true; dTextBox_c *other = mpLytBase->getTextBox(myList->GetString()); if (other != nullptr) { u16 otherNum = other->GetExtUserDataNum(); if (otherNum == 0) { break; } const nw4r::lyt::res::ExtUserData *otherList = other->GetExtUserData(); for (int j = 0; j < otherNum; j++) { userDatName = otherList->GetName(); if (userDatName == "embed") { return true; } if (userDatName == "textScale") { return true; } otherList++; } } } myList++; } return false; } void dTextBox_c::someDebugCheckMaybe() { field_0x1F7 = 0; // Everything below this is effectively no-ops, since // we check and compare a bunch of things but don't actually // do anything. u16 myNum = GetExtUserDataNum(); if (!myNum) { return; } const nw4r::lyt::res::ExtUserData *myList = GetExtUserData(); for (int i = 0; i < myNum; myList++, i++) { SizedString<0x40> userDatName; userDatName = myList->GetName(); if (userDatName == "copy" && myList->GetType() == nw4r::lyt::res::TYPE_STRING) { dTextBox_c *other = mpLytBase->getTextBox(myList->GetString()); if (other != nullptr) { u16 otherNum = other->GetExtUserDataNum(); if (otherNum == 0) { continue; } const nw4r::lyt::res::ExtUserData *otherList = other->GetExtUserData(); for (int j = 0; j < otherNum; j++) { SizedString<0x40> otherName; otherName = otherList->GetName(); otherList++; } } } } return; } // Also largely copied from nw4r::lyt::TextBox void dTextBox_c::DrawSelf(const nw4r::lyt::DrawInfo &drawInfo) { if (!mTextBuf || !mpFont || !mpMaterial) { return; } LoadMtx(drawInfo); nw4r::ut::TextWriterBase writer; nw4r::ut::Rect textRect; if (mHasTextWriter) { field_0x1F9 = false; someDebugCheckMaybe(); nw4r::ut::Rect resultRect = GetMyTextDrawRect(&writer, &field_0x1F9); textRect = resultRect; mTextWriter = writer; field_0x188 = textRect; } else { writer = mTextWriter; textRect = field_0x188; } if (mpMyTagProcessor != nullptr) { mpMyTagProcessor->setMsgWindowSubtype(mWindowSubtype); mpMyTagProcessor->setIsShadowText(mIsShadowTextBox); mpMyTagProcessor->setField_0xEE2(0); if (mAlpha != 0) { mpMyTagProcessor->setField_0xEE3(1); } else { mpMyTagProcessor->setField_0xEE3(0); } } bool unk1; if (field_0x1F8 == 2) { unk1 = field_0x1F9; } else { unk1 = field_0x1F8 == 1; } if (field_0x1F7 == 1 || (field_0x1F7 == 2 && unk1)) { f32 f = GetFontSize().height * dLyt_HIO_c::getField0x75C(); textRect.top -= f; textRect.bottom -= f; } else if (field_0x1F7 == 3 && unk1) { f32 f = GetFontSize().height * dLyt_HIO_c::getField0x760(); textRect.top -= f; textRect.bottom -= f; } nw4r::ut::Color topCol = nw4r::lyt::detail::MultipleAlpha(mTextColors[0], mGlbAlpha); nw4r::ut::Color btmCol = nw4r::lyt::detail::MultipleAlpha(mTextColors[1], mGlbAlpha); writer.SetGradationMode(topCol != btmCol ? nw4r::ut::CharWriter::GRADMODE_V : nw4r::ut::CharWriter::GRADMODE_NONE); writer.SetTextColor(topCol, btmCol); nw4r::ut::Color minCol = GetColor(mpMaterial->GetTevColor(0)); nw4r::ut::Color maxCol = GetColor(mpMaterial->GetTevColor(1)); writer.SetColorMapping(minCol, maxCol); if (mFontIdx <= 3) { setupGX(); if (mpMyTagProcessor != nullptr) { mpMyTagProcessor->setTextbox(this); } } else { writer.SetupGX(); } GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); f32 hMag = GetTextAlignMag(); const wchar_t *strPos = GetString(); writer.SetCursor(textRect.left, -textRect.top); f32 texWidth = textRect.GetWidth(); int i = 0; if (field_0x1FA != 0) { // The original variant from nw4r::lyt 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; } } else if (!mHasTextWriter && field_0x201 == true) { // Taking parameters from the three arrays, instead of calculating them int remain = mTextLen; while (remain > 0 && i < 10) { bool bOver; f32 lineWidth; s32 lineStrNum = GetStoredLineStrNum(i, &lineWidth, &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; i++; } } else { // Copying parameters to the three arrays int remain = mTextLen; while (remain > 0 && i < 10) { bool bOver; f32 lineWidth; s32 lineStrNum = CalcLineStrNum(&lineWidth, &writer, strPos, remain, mSize.width, &bOver); f32 textPosX = textRect.left + hMag * (texWidth - lineWidth); writer.SetCursorX(textPosX); writer.PrintMutable(strPos, lineStrNum); if (mHasTextWriter && field_0x201 == true && i < 10) { SetStoredLineStrNum(i, lineWidth, lineStrNum, bOver); } if (bOver) { writer.PrintMutable(L"\n", 1); } strPos += lineStrNum; remain -= lineStrNum; i++; } } mHasTextWriter = false; } static int CalcLineStrNum( f32 *pWidth, nw4r::ut::TextWriterBase *pTextWriter, const wchar_t *str, int length, f32 maxWidth, bool *pbOver ) { nw4r::ut::Rect rect; nw4r::ut::TextWriterBase writerCpy(*pTextWriter); writerCpy.SetCursor(0.0f, 0.0f); int ret = CalcLineRectImpl(&rect, &writerCpy, str, length, maxWidth, pbOver, nullptr); *pWidth = rect.right - rect.left; return ret; } void dTextBox_c::loadTextFormatVars() { u16 myNum = GetExtUserDataNum(); if (myNum == 0) { return; } const nw4r::lyt::res::ExtUserData *myList = GetExtUserData(); for (int i = 0; i < myNum; i++) { SizedString<0x40> userDatName; userDatName = myList->GetName(); if (userDatName == "fontWidth" && myList->GetType() == nw4r::lyt::res::TYPE_FLOAT) { // If this text box has a "fontWidth" user datum of type float, // we use that as a fixed font width. mIsWidthFixed = true; mFixedWidth = myList->GetFloat(); return; } // If this text box has a "copy" user datum, find a text box // of the given name and use its font width, if any if (userDatName == "copy") { dTextBox_c *other = mpLytBase->getTextBox(myList->GetString()); if (other != nullptr) { u16 otherNum = other->GetExtUserDataNum(); if (otherNum == 0) { return; } const nw4r::lyt::res::ExtUserData *otherList = other->GetExtUserData(); for (int j = 0; j < otherNum; j++) { userDatName = otherList->GetName(); if (userDatName == "fontWidth" && otherList->GetType() == nw4r::lyt::res::TYPE_FLOAT) { mIsWidthFixed = true; mFixedWidth = otherList->GetFloat(); break; } otherList++; } } } myList++; } } void dTextBox_c::setupGX() const { static mColor sFogColor = mColor(0); GXSetFog(GX_FOG_NONE, sFogColor, 0.0f, 0.0f, 0.0f, 0.0f); GXSetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); GXSetZTexture(0, GX_TF_Z8, 0); GXSetNumChans(1); GXSetChanCtrl(GX_COLOR0A0, false, GX_SRC_REG, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE); GXSetChanCtrl(GX_COLOR1A1, false, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE); GXSetNumTexGens(1); GXSetTexCoordGen2(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, 0x3c, 0, 0x7d); GXSetNumIndStages(0); GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_SET); GXSetNumTevStages(2); GXSetTevDirect(GX_TEVSTAGE0); GXSetTevDirect(GX_TEVSTAGE1); GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0); GXSetTevSwapMode(GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP0); GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL); GXColorS10 c0 = mpMaterial->GetTevColor(0); GXColorS10 c1 = mpMaterial->GetTevColor(1); GXColorS10 c2 = c1; c2.r /= -2, c2.g /= -2, c2.b /= -2, c2.a /= -2; GXSetTevColorS10(GX_TEVREG0, c0); GXSetTevColorS10(GX_TEVREG1, c1); GXSetTevColorS10(GX_TEVREG2, c2); if ((mFontIdx == 1) || (mFontIdx == 3)) { GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_C0, GX_CC_C1, GX_CC_TEXC, GX_CC_C2); GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_A0, GX_CA_A1, GX_CA_TEXA, GX_CA_ZERO); } else if ((mFontIdx == 0) || (mFontIdx == 2)) { GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_C0, GX_CC_C1, GX_CC_TEXC, GX_CC_C2); GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_A0, GX_CA_A1, GX_CA_TEXA, GX_CA_A2); } GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, 1, GX_TEVPREV); GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, 1, GX_TEVPREV); GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0); GXSetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_CPREV, GX_CC_RASC, GX_CC_ZERO); GXSetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_APREV, GX_CA_RASA, GX_CA_ZERO); GXSetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV); GXSetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_CLR_RGBA, GX_RGBX8, 0xf); GXClearVtxDesc(); GXSetVtxDesc(GX_VA_POS, GX_DIRECT); GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT); GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT); } void dTextBox_c::MySetFontSize(const nw4r::lyt::Size &value) { mMyFontSize = value; } void dTextBox_c::MySetScale(const nw4r::lyt::Size &value) { mMyTextScale = value; }