diff --git a/config/SOUE01/config.yml b/config/SOUE01/config.yml index bbe8cde0..74626e27 100644 --- a/config/SOUE01/config.yml +++ b/config/SOUE01/config.yml @@ -19,6 +19,7 @@ force_active: [ "RegistFont__Q34nw4r3lyt24MultiArcResourceAccessorFPQ34nw4r3lyt11FontRefLink", "GetFont__Q34nw4r3lyt24MultiArcResourceAccessorFPCc", "GetPaneRect__Q34nw4r3lyt4PaneCFRCQ34nw4r3lyt8DrawInfo", + "SetFont__Q34nw4r3lyt7TextBoxFPCQ34nw4r2ut4Font", ] # modules: diff --git a/config/SOUE01/splits.txt b/config/SOUE01/splits.txt index f9938f3d..5c8a14e5 100644 --- a/config/SOUE01/splits.txt +++ b/config/SOUE01/splits.txt @@ -345,6 +345,14 @@ nw4r/lyt/lyt_picture.cpp: .sbss start:0x80576700 end:0x80576704 .sdata2 start:0x8057F248 end:0x8057F250 +nw4r/lyt/lyt_textBox.cpp: + .text start:0x80489800 end:0x8048B16C + .ctors start:0x804DB97C end:0x804DB980 + .data start:0x8056E570 end:0x8056E5F4 + .sdata start:0x80574EB8 end:0x80574EBC + .sbss start:0x80576708 end:0x8057670C + .sdata2 start:0x8057F250 end:0x8057F268 + nw4r/lyt/lyt_resourceAccessor.cpp: .text start:0x80492000 end:0x80492058 .data start:0x8056E7E0 end:0x8056E7F8 diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt index 164e97b6..b97b3b4a 100644 --- a/config/SOUE01/symbols.txt +++ b/config/SOUE01/symbols.txt @@ -3860,7 +3860,7 @@ fn_800B0F40 = .text:0x800B0F40; // type:function size:0x240 fn_800B1180 = .text:0x800B1180; // type:function size:0x3F0 fn_800B1570 = .text:0x800B1570; // type:function size:0x14 fn_800B1590 = .text:0x800B1590; // type:function size:0x14 -fn_800B15B0 = .text:0x800B15B0; // type:function size:0x8 +GetRuntimeTypeInfo__Q34nw4r3lyt7TextBoxCFv = .text:0x800B15B0; // type:function size:0x8 scope:weak fn_800B15C0 = .text:0x800B15C0; // type:function size:0x68 fn_800B1630 = .text:0x800B1630; // type:function size:0x3C fn_800B1670 = .text:0x800B1670; // type:function size:0x134 @@ -25941,10 +25941,10 @@ SetFont__Q34nw4r3lyt7TextBoxFPCQ34nw4r2ut4Font = .text:0x8048A9E0; // type:funct LoadMtx__Q34nw4r3lyt7TextBoxFRCQ34nw4r3lyt8DrawInfo = .text:0x8048AB10; // type:function size:0x84 GetTextDrawRect__Q34nw4r3lyt7TextBoxCFPQ34nw4r2ut17TextWriterBase = .text:0x8048ABA0; // type:function size:0x2A4 CalcStringRectImpl__Q34nw4r3lyt25@unnamed@lyt_textBox_cpp@FPQ34nw4r2ut4RectPQ34nw4r2ut17TextWriterBasePCwif_v = .text:0x8048AE50; // type:function size:0x1D4 -FUN_8048b030 = .text:0x8048B030; // type:function size:0x4C -FUN_8048b080 = .text:0x8048B080; // type:function size:0x44 -FUN_8048b0d0 = .text:0x8048B0D0; // type:function size:0x84 -__sinit_lyt_textBox_cpp = .text:0x8048B160; // type:function size:0xC +GetTextMagH__Q34nw4r3lyt7TextBoxCFv = .text:0x8048B030; // type:function size:0x4C +GetTextMagV__Q34nw4r3lyt7TextBoxCFv = .text:0x8048B080; // type:function size:0x44 +GetTextAlignMag__Q34nw4r3lyt7TextBoxCFv = .text:0x8048B0D0; // type:function size:0x84 +__sinit_\lyt_textBox_cpp = .text:0x8048B160; // type:function size:0xC scope:local __ct__Q34nw4r3lyt6WindowFPCQ44nw4r3lyt3res6WindowRCQ34nw4r3lyt11ResBlockSet = .text:0x8048B170; // type:function size:0x284 __dt__Q34nw4r3lyt6WindowFv = .text:0x8048B400; // type:function size:0x144 FindMaterialByName__Q34nw4r3lyt6WindowFPCcb = .text:0x8048B550; // type:function size:0xFC @@ -37076,7 +37076,7 @@ __vt__Q34nw4r3lyt4Pane = .data:0x8056E430; // type:object size:0x74 __vt__Q34nw4r3lyt5Group = .data:0x8056E4A8; // type:object size:0xC __vt__Q34nw4r3lyt6Layout = .data:0x8056E4B8; // type:object size:0x40 __vt__Q34nw4r3lyt7Picture = .data:0x8056E4F8; // type:object size:0x78 -lbl_8056E570 = .data:0x8056E570; // type:object size:0x88 +__vt__Q34nw4r3lyt7TextBox = .data:0x8056E570; // type:object size:0x84 lbl_8056E5F8 = .data:0x8056E5F8; // type:object size:0x3C lbl_8056E634 = .data:0x8056E634; // type:object size:0x8C lbl_8056E6C0 = .data:0x8056E6C0; // type:object size:0x78 @@ -39585,7 +39585,7 @@ lbl_80574E98 = .sdata:0x80574E98; // type:object size:0x8 lbl_80574EA0 = .sdata:0x80574EA0; // type:object size:0x8 data:4byte lbl_80574EA8 = .sdata:0x80574EA8; // type:object size:0x8 data:4byte NW4R_LYT_Version___22@unnamed@lyt_init_cpp@ = .sdata:0x80574EB0; // type:object size:0x4 scope:local data:4byte -lbl_80574EB8 = .sdata:0x80574EB8; // type:object size:0x8 +lbl_80574EB8 = .sdata:0x80574EB8; // type:object size:0x4 data:string lbl_80574EC0 = .sdata:0x80574EC0; // type:object size:0x2 data:string lbl_80574EC4 = .sdata:0x80574EC4; // type:object size:0x3 data:string lbl_80574EC8 = .sdata:0x80574EC8; // type:object size:0x8 @@ -47567,10 +47567,10 @@ lbl_8057F238 = .sdata2:0x8057F238; // type:object size:0x8 data:double lbl_8057F240 = .sdata2:0x8057F240; // type:object size:0x4 data:float lbl_8057F244 = .sdata2:0x8057F244; // type:object size:0x4 data:float lbl_8057F248 = .sdata2:0x8057F248; // type:object size:0x4 data:float -lbl_8057F250 = .sdata2:0x8057F250; // type:object size:0x4 data:float -lbl_8057F258 = .sdata2:0x8057F258; // type:object size:0x8 data:double -lbl_8057F260 = .sdata2:0x8057F260; // type:object size:0x4 data:float -lbl_8057F264 = .sdata2:0x8057F264; // type:object size:0x4 data:float +lbl_8057F250 = .sdata2:0x8057F250; // type:object size:0x4 scope:local data:float +lbl_8057F258 = .sdata2:0x8057F258; // type:object size:0x8 scope:local data:double +lbl_8057F260 = .sdata2:0x8057F260; // type:object size:0x4 scope:local data:float +lbl_8057F264 = .sdata2:0x8057F264; // type:object size:0x4 scope:local data:float lbl_8057F268 = .sdata2:0x8057F268; // type:object size:0x4 data:float lbl_8057F270 = .sdata2:0x8057F270; // type:object size:0x8 data:double lbl_8057F278 = .sdata2:0x8057F278; // type:object size:0x8 data:double diff --git a/configure.py b/configure.py index 63abe94c..ab74faa9 100644 --- a/configure.py +++ b/configure.py @@ -203,7 +203,6 @@ cflags_nw4r = [ *cflags_base, "-ipa file", "-fp_contract off", - ] # REL flags @@ -357,6 +356,7 @@ config.libs = [ Object(Matching, "nw4r/lyt/lyt_group.cpp"), Object(Matching, "nw4r/lyt/lyt_layout.cpp"), Object(Matching, "nw4r/lyt/lyt_picture.cpp"), + Object(Matching, "nw4r/lyt/lyt_textBox.cpp"), Object(Matching, "nw4r/lyt/lyt_resourceAccessor.cpp"), Object(Matching, "nw4r/lyt/lyt_arcResourceAccessor.cpp"), Object(Matching, "nw4r/lyt/lyt_common.cpp"), diff --git a/include/nw4r/lyt/lyt_common.h b/include/nw4r/lyt/lyt_common.h index 3023d73b..81b40ae5 100644 --- a/include/nw4r/lyt/lyt_common.h +++ b/include/nw4r/lyt/lyt_common.h @@ -28,6 +28,19 @@ inline void SetVtxColElement(ut::Color *cols, u32 idx, u8 value) { ((u8 *)cols)[(idx & ~3) + (idx & 3)] = value; } +inline u8 GetHorizontalPosition(u8 var) { + return var % 3; +} +inline u8 GetVerticalPosition(u8 var) { + return var / 3; +} +inline void SetHorizontalPosition(u8 *pVar, u8 newVal) { + *pVar = newVal + GetVerticalPosition(*pVar) * 3; +} +inline void SetVerticalPosition(u8 *pVar, u8 newVal) { + *pVar = newVal * 3 + GetHorizontalPosition(*pVar); +} + typedef math::VEC2 TexCoordData[TEXCOORD_VTX_COUNT]; class TexCoordAry { diff --git a/include/nw4r/lyt/lyt_layout.h b/include/nw4r/lyt/lyt_layout.h index 3eebfff9..b7d801c7 100644 --- a/include/nw4r/lyt/lyt_layout.h +++ b/include/nw4r/lyt/lyt_layout.h @@ -4,6 +4,7 @@ #include #include #include +#include #include namespace nw4r { @@ -70,6 +71,9 @@ public: template static T *NewArray(size_t n) { T *array = (T *)AllocMemory(n * sizeof(T)); + if (!array) { + return nullptr; + } for (size_t i = 0; i < n; i++) { new (&array[i]) T(); @@ -86,6 +90,13 @@ public: } } + template + static void DeletePrimArray(T *objAry) { + if (objAry) { + FreeMemory(objAry); + } + } + template static T *NewObj() { T *pMem = (T *)AllocMemory(sizeof(T)); diff --git a/include/nw4r/lyt/lyt_material.h b/include/nw4r/lyt/lyt_material.h index b0566173..970fe84f 100644 --- a/include/nw4r/lyt/lyt_material.h +++ b/include/nw4r/lyt/lyt_material.h @@ -81,6 +81,14 @@ public: TexMap *GetTexMapAry(); TexCoordGen *GetTexCoordGenAry(); + GXColorS10 GetTevColor(u32 idx) const { + return mTevCols[idx]; + } + + ut::Color GetTevKColor(u32 idx) const { + return mTevKCols[idx]; + } + void SetTexture(u32 texMapIdx, const TexMap &texMap) { GetTexMapAry()[texMapIdx] = texMap; } diff --git a/include/nw4r/lyt/lyt_resources.h b/include/nw4r/lyt/lyt_resources.h index d4cb6a76..03c6ea73 100644 --- a/include/nw4r/lyt/lyt_resources.h +++ b/include/nw4r/lyt/lyt_resources.h @@ -110,6 +110,12 @@ struct FontList { u8 padding[2]; // at 0x0A }; +struct Font { + u32 nameStrOffset; // at 0x0 + u8 type; // at 0x4 + u8 padding[3]; // at 0x5 +}; + struct TextureList { DataBlockHeader blockHeader; // at 0x00 u16 texNum; // at 0x08 @@ -117,6 +123,7 @@ struct TextureList { }; struct TextBox : public Pane { u16 textBufBytes; // at 0x4C + u16 textStrBytes; // at 0x4E u16 materialIdx; // at 0x50 u16 fontIdx; // at 0x52 u8 textPosition; // at 0x54 diff --git a/include/nw4r/lyt/lyt_textBox.h b/include/nw4r/lyt/lyt_textBox.h index f2fab1ba..6a56a0fc 100644 --- a/include/nw4r/lyt/lyt_textBox.h +++ b/include/nw4r/lyt/lyt_textBox.h @@ -8,17 +8,75 @@ namespace nw4r { namespace lyt { class TextBox : public Pane { public: + TextBox(u16 allocStrLen); + TextBox(const wchar_t *str, const ut::Font *pFont); + TextBox(u16, const wchar_t *str, u16, const ut::Font *pFont); + TextBox(const res::TextBox *pBlock, const ResBlockSet &resBlockSet); + + void Init(u16 allocStrLen); + + ut::Rect GetTextDrawRect() const; + ut::Rect GetTextDrawRect(const DrawInfo &drawInfo) const; + u16 GetStringBufferLength() const; + u16 SetStringImpl(const wchar_t *str, u16 dstIdx, u32 strLen); + const ut::Font *GetFont() const; + void SetFont(const ut::Font *pFont); + ut::Rect GetTextDrawRect(ut::TextWriterBase *pWriter) const; + f32 GetTextMagH() const; + f32 GetTextMagV() const; + f32 GetTextAlignMag() const; + UNKTYPE MakeDrawFlag() const; + + ut::Color GetTextColor(u32 type) const { + return mTextColors[type / 2]; + } + void SetTextColor(u32 type, ut::Color value) { + mTextColors[type] = value; + } + u8 GetTextPositionH() const { + return detail::GetHorizontalPosition(mTextPosition); + } + void SetTextPositionH(u8 val) { + detail::SetHorizontalPosition(&mTextPosition, val); + } + u8 GetTextPositionV() const { + return detail::GetVerticalPosition(mTextPosition); + } + void SetTextPositionV(u8 val) { + detail::SetVerticalPosition(&mTextPosition, val); + } + + const Size &GetFontSize() const { + return mFontSize; + } + + void SetFontSize(const Size &fontSize) { + mFontSize = fontSize; + } + ut::TagProcessorBase *GetTagProcessor() const { + return mpTagProcessor; + } void SetTagProcessor(ut::TagProcessorBase *pTagProcessor) { mpTagProcessor = pTagProcessor; } - TextBox(const res::TextBox *, const ResBlockSet &resBlockSet); virtual ~TextBox(); NW4R_UT_RTTI_DECL(TextBox); + virtual void DrawSelf(const DrawInfo &drawInfo); // at 0x18 + virtual ut::Color GetVtxColor(u32 idx) const; // at 0x24 + virtual void SetVtxColor(u32 idx, ut::Color value); // at 0x28 + virtual u8 GetVtxColorElement(u32 idx) const; // at 0x34 + virtual void SetVtxColorElement(u32 idx, u8 value); // at 0x38 + virtual void LoadMtx(const DrawInfo &drawInfo); // at 0x70 + virtual void AllocStringBuffer(u16 minLen); // at 0x + virtual void FreeStringBuffer(); // at 0x + virtual u16 SetString(const wchar_t *str, u16 dstIdx); // at 0x + virtual u16 SetString(const wchar_t *str, u16 dstIdx, u16 strLen); // at 0x +protected: wchar_t *mTextBuf; // at 0xD8 // ptr to is Guess ut::Color mTextColors[2]; // at 0xDC - ut::Font *mpFont; // at 0xE4 + const ut::Font *mpFont; // at 0xE4 Size mFontSize; // at 0xE8 f32 mLineSpace; // at 0xF0 f32 mCharSpace; // at 0xF4 @@ -28,7 +86,7 @@ public: u8 mTextPosition; // at 0x100 struct { // u8 bAllocFont : 1; // - u8 textAligntment : 1; // + u8 textAlignment : 2; // } mBits; // at 0x101 }; diff --git a/include/nw4r/lyt/lyt_types.h b/include/nw4r/lyt/lyt_types.h index 1acade59..31f75a7f 100644 --- a/include/nw4r/lyt/lyt_types.h +++ b/include/nw4r/lyt/lyt_types.h @@ -20,7 +20,7 @@ inline void SetBit(T *bits, int pos, bool val) { } template -T *ConvertOffsetToPtr(const void *baseAddress, unsigned int offset) { +T *ConvertOffsToPtr(const void *baseAddress, unsigned int offset) { return (T *)((u32)baseAddress + offset); } } // namespace detail diff --git a/include/nw4r/ut/ut_Color.h b/include/nw4r/ut/ut_Color.h index b9112d10..b9616f14 100644 --- a/include/nw4r/ut/ut_Color.h +++ b/include/nw4r/ut/ut_Color.h @@ -17,6 +17,9 @@ public: Color(int red, int green, int blue, int alpha) { Set(red, green, blue, alpha); } + Color(const GXColor &clr) { + *this = *(u32 *)&clr; + } ~Color() {} void Set(int red, int green, int blue, int alpha) { diff --git a/src/nw4r/lyt/lyt_common.cpp b/src/nw4r/lyt/lyt_common.cpp index 922cf77b..d5fc6b8b 100644 --- a/src/nw4r/lyt/lyt_common.cpp +++ b/src/nw4r/lyt/lyt_common.cpp @@ -42,8 +42,8 @@ void TexCoordAry::Free() { void TexCoordAry::Reserve(u8 num) { if (mCap < num) { Free(); - const TexCoordData *pData = (TexCoordData *)Layout::NewArray(num * TEXCOORD_VTX_COUNT); - this->mpData = pData ? const_cast(pData) : nullptr; + TexCoordData *pData = (TexCoordData *)Layout::NewArray(num * TEXCOORD_VTX_COUNT); + this->mpData = pData; if (mpData != NULL) { mCap = num; } diff --git a/src/nw4r/lyt/lyt_group.cpp b/src/nw4r/lyt/lyt_group.cpp index 82433fdf..75876728 100644 --- a/src/nw4r/lyt/lyt_group.cpp +++ b/src/nw4r/lyt/lyt_group.cpp @@ -10,7 +10,7 @@ Group::Group(const res::Group *pResGroup, Pane *pRootPane) : mLink(), mPaneListL Init(); strncpy(this->mName, pResGroup->mName, NW4R_RES_NAME_SIZE); this->mName[NW4R_RES_NAME_SIZE] = '\0'; - const char *paneNameBase = detail::ConvertOffsetToPtr(pResGroup, sizeof(res::Group)); + const char *paneNameBase = detail::ConvertOffsToPtr(pResGroup, sizeof(res::Group)); for (int i = 0; i < pResGroup->paneNum; i++) { Pane *pFindPane = pRootPane->FindPaneByName(paneNameBase + i * NW4R_RES_NAME_SIZE, true); diff --git a/src/nw4r/lyt/lyt_pane.cpp b/src/nw4r/lyt/lyt_pane.cpp index e0ef53f8..58ad61a2 100644 --- a/src/nw4r/lyt/lyt_pane.cpp +++ b/src/nw4r/lyt/lyt_pane.cpp @@ -514,7 +514,7 @@ u16 Pane::GetExtUserDataNum() const { } res::ExtUserData *Pane::GetExtUserData() const { if (this->mpExtUserDataList) { - return detail::ConvertOffsetToPtr(this->mpExtUserDataList, sizeof(res::ExtUserDataList)); + return detail::ConvertOffsToPtr(this->mpExtUserDataList, sizeof(res::ExtUserDataList)); } return nullptr; } @@ -530,7 +530,7 @@ res::ExtUserData *Pane::FindExtUserDataByName(const char *name) { u32 offset = pUserData->nameOffs; const char *str = 0; if (offset != 0) { - str = detail::ConvertOffsetToPtr(pUserData, offset); + str = detail::ConvertOffsToPtr(pUserData, offset); } else { str = nullptr; } diff --git a/src/nw4r/lyt/lyt_picture.cpp b/src/nw4r/lyt/lyt_picture.cpp index 83e567d3..e6a7c581 100644 --- a/src/nw4r/lyt/lyt_picture.cpp +++ b/src/nw4r/lyt/lyt_picture.cpp @@ -30,9 +30,9 @@ Picture::Picture(const res::Picture *pResPic, const ResBlockSet &resBlockSet) if (texCoordNum != 0 && !mTexCoordAry.IsEmpty()) { mTexCoordAry.Copy((void *)&pResPic[1], texCoordNum); } - const u32 *matOffsTbl = detail::ConvertOffsetToPtr(resBlockSet.pMaterialList, sizeof(res::MaterialList)); + const u32 *matOffsTbl = detail::ConvertOffsToPtr(resBlockSet.pMaterialList, sizeof(res::MaterialList)); const res::Material *pResMaterial = - detail::ConvertOffsetToPtr(resBlockSet.pMaterialList, matOffsTbl[pResPic->materialIdx]); + detail::ConvertOffsToPtr(resBlockSet.pMaterialList, matOffsTbl[pResPic->materialIdx]); mpMaterial = Layout::NewObj(pResMaterial, resBlockSet); } diff --git a/src/nw4r/lyt/lyt_textBox.cpp b/src/nw4r/lyt/lyt_textBox.cpp index e69de29b..6ce2e7fc 100644 --- a/src/nw4r/lyt/lyt_textBox.cpp +++ b/src/nw4r/lyt/lyt_textBox.cpp @@ -0,0 +1,509 @@ +#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