From 2c3665bbe375f624c9ed6dae95b6ceb33707d557 Mon Sep 17 00:00:00 2001 From: tintinhamans <5984296+tintinhamans@users.noreply.github.com> Date: Mon, 7 Sep 2026 22:34:17 +0200 Subject: [PATCH 1/2] bugfix(gamefont): Size glyph buffers to the glyph to prevent an overflow --- Core/GameEngine/Include/GameClient/GameFont.h | 2 ++ .../Source/GameClient/GUI/GameFont.cpp | 6 +++++- .../Source/GameClient/GlobalLanguage.cpp | 9 ++++++++- .../Source/WWVegas/WW3D2/render2dsentence.cpp | 20 +++++++++++++++++-- .../Source/WWVegas/WW3D2/render2dsentence.h | 8 ++++++-- 5 files changed, 39 insertions(+), 6 deletions(-) diff --git a/Core/GameEngine/Include/GameClient/GameFont.h b/Core/GameEngine/Include/GameClient/GameFont.h index 3b67bd19bb6..5e47f2aa7f8 100644 --- a/Core/GameEngine/Include/GameClient/GameFont.h +++ b/Core/GameEngine/Include/GameClient/GameFont.h @@ -34,6 +34,8 @@ #include "Common/AsciiString.h" #include "Common/GameMemory.h" +enum { FONT_POINT_SIZE_MAX = 512 }; + //------------------------------------------------------------------------------------------------- /** A font for use in the device independent game */ //------------------------------------------------------------------------------------------------- diff --git a/Core/GameEngine/Source/GameClient/GUI/GameFont.cpp b/Core/GameEngine/Source/GameClient/GUI/GameFont.cpp index e8dbc96bb4f..61bbc0db04c 100644 --- a/Core/GameEngine/Source/GameClient/GUI/GameFont.cpp +++ b/Core/GameEngine/Source/GameClient/GUI/GameFont.cpp @@ -178,11 +178,15 @@ void FontLibrary::reset() //------------------------------------------------------------------------------------------------- GameFont *FontLibrary::getFont( AsciiString name, Int pointSize, Bool bold ) { - // TheSuperHackers @fix No longer creates fonts with zero size. And allows fonts with size larger than 100. + // TheSuperHackers @fix No longer creates fonts with zero size and clamps oversized fonts. if (pointSize < 1) { return nullptr; } + if (pointSize > FONT_POINT_SIZE_MAX) + { + pointSize = FONT_POINT_SIZE_MAX; + } GameFont *font; diff --git a/Core/GameEngine/Source/GameClient/GlobalLanguage.cpp b/Core/GameEngine/Source/GameClient/GlobalLanguage.cpp index a9bc586d494..73d39d80707 100644 --- a/Core/GameEngine/Source/GameClient/GlobalLanguage.cpp +++ b/Core/GameEngine/Source/GameClient/GlobalLanguage.cpp @@ -59,6 +59,7 @@ #include "Common/OptionPreferences.h" #include "GameClient/Display.h" +#include "GameClient/GameFont.h" #include "GameClient/GlobalLanguage.h" //----------------------------------------------------------------------------- @@ -281,7 +282,13 @@ Int GlobalLanguage::adjustFontSize(Int theFontSize) // Therefore cache the adjustFactor on resolution change to not recompute it on every call. const Real resolutionScaler = getResolutionFontSizeAdjustment(); const Real adjustFactor = getResolutionFontSizeScale(m_resolutionFontSizeMethod, resolutionScaler); - const Int pointSize = REAL_TO_INT_FLOOR(theFontSize * adjustFactor); + Int pointSize = REAL_TO_INT_FLOOR(theFontSize * adjustFactor); + + // TheSuperHackers @fix arcticdolphin 07/09/2026 Keep the scaled size within what getFont can build. + if (pointSize > FONT_POINT_SIZE_MAX) + { + pointSize = FONT_POINT_SIZE_MAX; + } return pointSize; } diff --git a/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp b/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp index 5fe9bf9a01a..86041bd2514 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp +++ b/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp @@ -1155,6 +1155,18 @@ Render2DSentenceClass::Build_Sentence (const WCHAR *text, int *hkX, int *hkY) } +FontCharsBuffer::FontCharsBuffer (int length) : + Length( length ), + Buffer( W3DNEWARRAY uint16[length] ) +{ +} + +FontCharsBuffer::~FontCharsBuffer () +{ + delete [] Buffer; +} + + //////////////////////////////////////////////////////////////////////////////////// // // FontCharsClass @@ -1441,6 +1453,8 @@ FontCharsClass::Store_GDI_Char (WCHAR ch) void FontCharsClass::Update_Current_Buffer (int char_width) { + const int char_len = char_width * CharHeight; + // // Check to see if we need to allocate a new buffer // @@ -1450,7 +1464,7 @@ FontCharsClass::Update_Current_Buffer (int char_width) // // Would we extend past this buffer? // - if ( (CurrPixelOffset + (char_width * CharHeight)) > CHAR_BUFFER_LEN ) { + if ( (CurrPixelOffset + char_len) > BufferList[BufferList.Count () - 1]->Length ) { needs_new_buffer = true; } } @@ -1460,7 +1474,9 @@ FontCharsClass::Update_Current_Buffer (int char_width) // if (needs_new_buffer) { - FontCharsBuffer* new_buffer = W3DNEW FontCharsBuffer; + // TheSuperHackers @fix arcticdolphin 07/09/2026 Length may exceed CHAR_BUFFER_LEN to fit this glyph. + const int length = (char_len > CHAR_BUFFER_LEN) ? char_len : CHAR_BUFFER_LEN; + FontCharsBuffer* new_buffer = W3DNEW FontCharsBuffer( length ); BufferList.Add( new_buffer ); CurrPixelOffset = 0; } diff --git a/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.h b/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.h index 15426c1e950..429008a2b8a 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.h +++ b/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.h @@ -62,11 +62,15 @@ class FontCharsClassCharDataStruct enum { CHAR_BUFFER_LEN = 32768 }; +// TheSuperHackers @fix arcticdolphin 07/09/2026 Buffer length matches the glyph so a large glyph cannot overrun it. class FontCharsBuffer { - W3DMPO_CODE(FontCharsBuffer) public: - uint16 Buffer[CHAR_BUFFER_LEN]; + FontCharsBuffer( int length ); + ~FontCharsBuffer(); + + int Length; + uint16 * Buffer; }; From 099cb78610487e4a8973d63b88dfe599027dd161 Mon Sep 17 00:00:00 2001 From: tintinhamans <5984296+tintinhamans@users.noreply.github.com> Date: Tue, 8 Sep 2026 18:30:27 +0200 Subject: [PATCH 2/2] refactor(gamefont): Store glyph buffer descriptors by value --- Core/GameEngine/Include/GameClient/GameFont.h | 2 -- .../Source/GameClient/GUI/GameFont.cpp | 2 ++ .../Source/GameClient/GlobalLanguage.cpp | 9 +------ .../Source/WWVegas/WW3D2/render2dsentence.cpp | 25 +++++-------------- .../Source/WWVegas/WW3D2/render2dsentence.h | 10 +++++--- 5 files changed, 15 insertions(+), 33 deletions(-) diff --git a/Core/GameEngine/Include/GameClient/GameFont.h b/Core/GameEngine/Include/GameClient/GameFont.h index 5e47f2aa7f8..3b67bd19bb6 100644 --- a/Core/GameEngine/Include/GameClient/GameFont.h +++ b/Core/GameEngine/Include/GameClient/GameFont.h @@ -34,8 +34,6 @@ #include "Common/AsciiString.h" #include "Common/GameMemory.h" -enum { FONT_POINT_SIZE_MAX = 512 }; - //------------------------------------------------------------------------------------------------- /** A font for use in the device independent game */ //------------------------------------------------------------------------------------------------- diff --git a/Core/GameEngine/Source/GameClient/GUI/GameFont.cpp b/Core/GameEngine/Source/GameClient/GUI/GameFont.cpp index 61bbc0db04c..15ac9115628 100644 --- a/Core/GameEngine/Source/GameClient/GUI/GameFont.cpp +++ b/Core/GameEngine/Source/GameClient/GUI/GameFont.cpp @@ -179,6 +179,8 @@ void FontLibrary::reset() GameFont *FontLibrary::getFont( AsciiString name, Int pointSize, Bool bold ) { // TheSuperHackers @fix No longer creates fonts with zero size and clamps oversized fonts. + // The upper bound caps glyph buffer memory when a resolution-scaled point size gets very large. + enum { FONT_POINT_SIZE_MAX = 512 }; if (pointSize < 1) { return nullptr; diff --git a/Core/GameEngine/Source/GameClient/GlobalLanguage.cpp b/Core/GameEngine/Source/GameClient/GlobalLanguage.cpp index 73d39d80707..a9bc586d494 100644 --- a/Core/GameEngine/Source/GameClient/GlobalLanguage.cpp +++ b/Core/GameEngine/Source/GameClient/GlobalLanguage.cpp @@ -59,7 +59,6 @@ #include "Common/OptionPreferences.h" #include "GameClient/Display.h" -#include "GameClient/GameFont.h" #include "GameClient/GlobalLanguage.h" //----------------------------------------------------------------------------- @@ -282,13 +281,7 @@ Int GlobalLanguage::adjustFontSize(Int theFontSize) // Therefore cache the adjustFactor on resolution change to not recompute it on every call. const Real resolutionScaler = getResolutionFontSizeAdjustment(); const Real adjustFactor = getResolutionFontSizeScale(m_resolutionFontSizeMethod, resolutionScaler); - Int pointSize = REAL_TO_INT_FLOOR(theFontSize * adjustFactor); - - // TheSuperHackers @fix arcticdolphin 07/09/2026 Keep the scaled size within what getFont can build. - if (pointSize > FONT_POINT_SIZE_MAX) - { - pointSize = FONT_POINT_SIZE_MAX; - } + const Int pointSize = REAL_TO_INT_FLOOR(theFontSize * adjustFactor); return pointSize; } diff --git a/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp b/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp index 86041bd2514..29d1a22a82d 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp +++ b/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp @@ -1155,18 +1155,6 @@ Render2DSentenceClass::Build_Sentence (const WCHAR *text, int *hkX, int *hkY) } -FontCharsBuffer::FontCharsBuffer (int length) : - Length( length ), - Buffer( W3DNEWARRAY uint16[length] ) -{ -} - -FontCharsBuffer::~FontCharsBuffer () -{ - delete [] Buffer; -} - - //////////////////////////////////////////////////////////////////////////////////// // // FontCharsClass @@ -1200,7 +1188,7 @@ FontCharsClass::FontCharsClass () : FontCharsClass::~FontCharsClass () { while ( BufferList.Count() ) { - delete BufferList[0]; + delete [] BufferList[0].Buffer; BufferList.Delete(0); } @@ -1348,7 +1336,7 @@ FontCharsClass::Store_GDI_Char (WCHAR ch) // Get a pointer to the surface that this character should use // Update_Current_Buffer( char_size.cx ); - uint16* curr_buffer_p = BufferList[BufferList.Count () - 1]->Buffer; + uint16* curr_buffer_p = BufferList[BufferList.Count () - 1].Buffer; curr_buffer_p += CurrPixelOffset; // @@ -1422,7 +1410,7 @@ FontCharsClass::Store_GDI_Char (WCHAR ch) FontCharsClassCharDataStruct *char_data = W3DNEW FontCharsClassCharDataStruct; char_data->Value = ch; char_data->Width = char_size.cx; - char_data->Buffer = BufferList[BufferList.Count () - 1]->Buffer + CurrPixelOffset; + char_data->Buffer = BufferList[BufferList.Count () - 1].Buffer + CurrPixelOffset; // // Insert this character into our array @@ -1464,7 +1452,7 @@ FontCharsClass::Update_Current_Buffer (int char_width) // // Would we extend past this buffer? // - if ( (CurrPixelOffset + char_len) > BufferList[BufferList.Count () - 1]->Length ) { + if ( (CurrPixelOffset + char_len) > BufferList[BufferList.Count () - 1].Length ) { needs_new_buffer = true; } } @@ -1475,9 +1463,8 @@ FontCharsClass::Update_Current_Buffer (int char_width) if (needs_new_buffer) { // TheSuperHackers @fix arcticdolphin 07/09/2026 Length may exceed CHAR_BUFFER_LEN to fit this glyph. - const int length = (char_len > CHAR_BUFFER_LEN) ? char_len : CHAR_BUFFER_LEN; - FontCharsBuffer* new_buffer = W3DNEW FontCharsBuffer( length ); - BufferList.Add( new_buffer ); + const int length = max( (int)CHAR_BUFFER_LEN, char_len ); + BufferList.Add( FontCharsBuffer( length, W3DNEWARRAY uint16[length] ) ); CurrPixelOffset = 0; } } diff --git a/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.h b/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.h index 429008a2b8a..a6c19625cfb 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.h +++ b/Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.h @@ -62,12 +62,14 @@ class FontCharsClassCharDataStruct enum { CHAR_BUFFER_LEN = 32768 }; -// TheSuperHackers @fix arcticdolphin 07/09/2026 Buffer length matches the glyph so a large glyph cannot overrun it. class FontCharsBuffer { public: - FontCharsBuffer( int length ); - ~FontCharsBuffer(); + FontCharsBuffer() : Length( 0 ), Buffer( nullptr ) {} + FontCharsBuffer( int length, uint16 *buffer ) : Length( length ), Buffer( buffer ) {} + + bool operator== (const FontCharsBuffer &src) const { return Length == src.Length && Buffer == src.Buffer; } + bool operator!= (const FontCharsBuffer &src) const { return !(*this == src); } int Length; uint16 * Buffer; @@ -116,7 +118,7 @@ class FontCharsClass : public RefCountClass // Private member data // StringClass Name; - DynamicVectorClass BufferList; + DynamicVectorClass BufferList; int CurrPixelOffset; int CharHeight; int CharAscent;