From 4497b991be65ec7402d291f625c375d1882a3ea3 Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 7 Sep 2021 16:45:59 +0200 Subject: [PATCH] Fix characters or cursor vanishing when cursor is in escaped color code that is expanded --- src/Components/Modules/TextRenderer.cpp | 77 ++++++++++++++++++++----- src/Components/Modules/TextRenderer.hpp | 3 + src/Game/Structs.hpp | 11 ++++ 3 files changed, 76 insertions(+), 15 deletions(-) diff --git a/src/Components/Modules/TextRenderer.cpp b/src/Components/Modules/TextRenderer.cpp index 80022711..73ae7dd8 100644 --- a/src/Components/Modules/TextRenderer.cpp +++ b/src/Components/Modules/TextRenderer.cpp @@ -372,6 +372,12 @@ namespace Components while(*curText && maxLengthRemaining) { + if (passes[passIndex] == Game::FONTPASS_NORMAL && renderFlags & Game::TEXT_RENDERFLAG_CURSOR && count == cursorPos) + { + RotateXY(cosAngle, sinAngle, startX, startY, xa, xy, &xRot, &yRot); + Game::RB_DrawCursor(material, cursorLetter, xRot, yRot, sinAngle, cosAngle, font, xScale, yScale, color.packed); + } + auto letter = Game::SEH_ReadCharFromString(&curText, nullptr); if(letter == '^' && *curText >= COLOR_FIRST_CHAR && *curText <= COLOR_LAST_CHAR) @@ -427,16 +433,9 @@ namespace Components RotateXY(cosAngle, sinAngle, startX, startY, xa, xy, &xRot, &yRot); if(passes[passIndex] == Game::FONTPASS_NORMAL) - { - const auto fontIconWidth = DrawFontIcon(fontIconInfo, xRot, yRot, sinAngle, cosAngle, font, xScale, yScale, ColorRgba(255, 255, 255, finalColor.array[3])); - if (renderFlags & Game::TEXT_RENDERFLAG_CURSOR && count == cursorPos) - Game::RB_DrawCursor(material, cursorLetter, xRot, yRot, sinAngle, cosAngle, font, xScale, yScale, color.packed); - xa += fontIconWidth; - } + xa += DrawFontIcon(fontIconInfo, xRot, yRot, sinAngle, cosAngle, font, xScale, yScale, ColorRgba(255, 255, 255, finalColor.array[3])); else - { xa += GetFontIconWidth(fontIconInfo, font, xScale); - } if (renderFlags & Game::TEXT_RENDERFLAG_PADDING) xa += xScale * padding; @@ -513,12 +512,6 @@ namespace Components DrawTextFxExtraCharacter(fxMaterial, extraFxChar, xRot, yRot, static_cast(glyph->pixelWidth) * xScale, static_cast(glyph->pixelHeight) * yScale, sinAngle, cosAngle, finalColor.packed); else Game::RB_DrawChar(material, xRot, yRot, static_cast(glyph->pixelWidth) * xScale, static_cast(glyph->pixelHeight) * yScale, sinAngle, cosAngle, glyph, finalColor.packed); - - if (renderFlags & Game::TEXT_RENDERFLAG_CURSOR && count == cursorPos) - { - RotateXY(cosAngle, sinAngle, startX, startY, xa, xy, &xRot, &yRot); - Game::RB_DrawCursor(material, cursorLetter, xRot, yRot, sinAngle, cosAngle, font, xScale, yScale, color.packed); - } } else if(passes[passIndex] == Game::FONTPASS_OUTLINE) { @@ -785,10 +778,61 @@ namespace Components return std::string(buffer); } + int TextRenderer::SEH_PrintStrlenWithCursor(const char* string, const Game::field_t* field) + { + if (!string) + return 0; + + const auto cursorPos = field->cursor; + auto len = 0; + auto lenWithInvisibleTail = 0; + auto count = 0; + const auto* curText = string; + while(*curText) + { + const auto c = Game::SEH_ReadCharFromString(&curText, nullptr); + lenWithInvisibleTail = len; + + if (c == '^' && *curText >= COLOR_FIRST_CHAR && *curText <= COLOR_LAST_CHAR && !(cursorPos > count && cursorPos < count + 2)) + { + curText++; + count++; + } + else if(c != '\r' && c != '\n') + { + len++; + } + + count++; + lenWithInvisibleTail++; + } + + return lenWithInvisibleTail; + } + + __declspec(naked) void TextRenderer::Field_AdjustScroll_PrintLen_Stub() + { + __asm + { + push eax + pushad + + push esi + push [esp + 0x8 + 0x24] + call SEH_PrintStrlenWithCursor + add esp, 0x8 + mov [esp + 0x20], eax + + popad + pop eax + ret + } + } + void TextRenderer::PatchColorLimit(const char limit) { Utils::Hook::Set(0x535629, limit); // DrawText2d - Utils::Hook::Set(0x4C1BE4, limit); // No idea :P + Utils::Hook::Set(0x4C1BE4, limit); // SEH_PrintStrlen Utils::Hook::Set(0x4863DD, limit); // No idea :P Utils::Hook::Set(0x486429, limit); // No idea :P Utils::Hook::Set(0x49A5A8, limit); // No idea :P @@ -894,6 +938,9 @@ namespace Components // Replace team colors with colorblind team colors when colorblind is enabled Utils::Hook(0x406530, GetUnpackedColorByNameStub, HOOK_JUMP).install()->quick(); + // Consider the cursor being inside the color escape sequence when getting the print length for a field + Utils::Hook(0x488CBD, Field_AdjustScroll_PrintLen_Stub, HOOK_CALL).install()->quick(); + PatchColorLimit(COLOR_LAST_CHAR); } } \ No newline at end of file diff --git a/src/Components/Modules/TextRenderer.hpp b/src/Components/Modules/TextRenderer.hpp index 619150d1..357cf6d1 100644 --- a/src/Components/Modules/TextRenderer.hpp +++ b/src/Components/Modules/TextRenderer.hpp @@ -94,6 +94,9 @@ namespace Components private: static unsigned HsvToRgb(HsvColor hsv); + static int SEH_PrintStrlenWithCursor(const char* string, const Game::field_t* field); + static void Field_AdjustScroll_PrintLen_Stub(); + static void PatchColorLimit(char limit); static bool Dvar_GetUnpackedColorByName(const char* name, float* expandedColor); static void GetUnpackedColorByNameStub(); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index f4e8e8ca..d9a6e2a6 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -5287,6 +5287,17 @@ namespace Game FONTPASS_COUNT = 0x3, }; + struct field_t + { + int cursor; + int scroll; + int drawWidth; + int widthInPixels; + float charHeight; + int fixedSize; + char buffer[256]; + }; + #pragma endregion #ifndef IDA