#include "Render/TextContext.h" #include "UI/PixelTextWrapping.h" #include #include #include namespace { int Add(int left, int right) { return std::bit_cast(static_cast(left) + static_cast(right)); } unsigned Digit(unsigned char value) { if (value >= '0' && value <= '9') { return value - '0'; } if (value >= 'a' && value <= 'f') { return value - 'a' + 10; } if (value >= 'A' && value <= 'F') { return value - 'A' + 10; } return 0; } } // OFFSET: 0x00A82310 // FUNCTION: fn_16993 void WrapPixelText( CDC& surface, FontCache& fonts, const TextLocale& locale, unsigned short codePage, const char* text, std::vector& lines, int width ) { if (!text || *text == '\0') { return; } // Each original measurement constructs its own scoped text context. const auto measure = [&](const char* part, int bytes) { if (bytes == 0) { bytes = static_cast(std::strlen(part)); } if (bytes == 0) { return 0; } TextContext context(surface, fonts, locale); context.SetFont(0, 12, 0, 0); return static_cast(context.Measure(part, bytes).cx); }; const int bytes = static_cast(std::strlen(text)); if (measure(text, bytes) <= 0) { return; } const char* end = text + bytes; int offset = 0; for (int row = 0; offset < bytes; ++row) { if (row > 1000) { return; } const char* begin = text + offset; const char* current = begin; int count = 0; int extent = 0; bool newline = false; for (int step = 0; current < end; ++step) { if (step > 1000) { break; } const int previous = count; const char* next = CharNextExA(codePage, current, 0); const int length = static_cast(next - current); extent = Add(extent, measure(current, length)); count = Add(count, length); if (length == 1 && (*current == '\r' || *current == '\n')) { measure("\n", 1); newline = true; if (current + 1 < end && current[1] == '\n') { ++count; } break; } if (*current == '^') { const unsigned color = (Digit(current[2]) + 16 * Digit(current[1])) | ((Digit(current[4]) + 16 * Digit(current[3])) << 8) | ((Digit(current[6]) + 16 * Digit(current[5])) << 16); if (color != 0 || std::strncmp(current, "^000000", 7) == 0) { // The original visits each byte; it subtracts the seven-byte color // marker's width here rather than advancing over the marker. extent = Add(extent, -measure("^000000", 7)); } } if (*current == '[') { const auto* close = _mbsstr(reinterpret_cast(current), reinterpret_cast("]")); if (close) { const int bracketBytes = static_cast(reinterpret_cast(close) - current) + 1; const int bracketWidth = measure(current, bracketBytes); const int characterWidth = measure(current, length); if (Add(Add(extent, bracketWidth), -characterWidth) > width && bracketWidth <= width) { count = previous; break; } } } if (extent > width) { count = previous; break; } current = next; } if (count <= 0) { // Original retries the same byte until its row guard expires. continue; } std::string line(begin, static_cast(count)); if (newline) { line[static_cast(count - 1)] = '\0'; line.resize(std::strlen(line.c_str())); lines.push_back(line.empty() ? " " : line); } else { lines.push_back(std::move(line)); } offset = Add(offset, count); } }