Skip to content

Commit cd6a1a8

Browse files
committed
fix(chat): prevent input exceeding utf-8 byte limit
Because of the `Send` method, max char limit should be based on utf-8 byte count, not utf-16 chars.
1 parent 81536c3 commit cd6a1a8

2 files changed

Lines changed: 63 additions & 0 deletions

File tree

src/game/client/hud_basechat.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,8 @@ CBaseHudChatInputLine::CBaseHudChatInputLine( CBaseHudChat *parent, char const *
466466
m_pPrompt = new vgui::Label( this, "ChatInputPrompt", L"Enter text:" );
467467
m_pInput = new CBaseHudChatEntry( this, "ChatInput", parent );
468468
m_pInput->SetMaximumCharCount( 127 );
469+
// Send converts text to utf-8
470+
m_pInput->m_iMaxByteCount = 127;
469471
}
470472

471473
void CBaseHudChatInputLine::ApplySchemeSettings(vgui::IScheme *pScheme)
@@ -2519,3 +2521,59 @@ void CBaseHudChat::FireGameEvent( IGameEvent *event )
25192521
ChatPrintf( player->entindex(), CHAT_FILTER_NONE, "(SourceTV) %s", event->GetString( "text" ) );
25202522
}
25212523
}
2524+
2525+
// Prevent player from inserting text over utf-8 byte limit
2526+
void CBaseHudChatEntry::InsertChar(wchar_t ch)
2527+
{
2528+
if ( m_iMaxByteCount == -1 )
2529+
{
2530+
BaseClass::InsertChar(ch);
2531+
return;
2532+
}
2533+
2534+
// single utf-16 char converted to utf-8 is 3 byte long in worst case
2535+
const int iBufLen = BaseClass::GetTextLength() * 3;
2536+
2537+
// Shortcut: fitting max byte count even in worst case
2538+
if ( iBufLen + 4 <= m_iMaxByteCount )
2539+
{
2540+
BaseClass::InsertChar(ch);
2541+
return;
2542+
}
2543+
2544+
// Count bytes of converted utf-8 str
2545+
m_szCharBuf.EnsureCapacity( iBufLen );
2546+
BaseClass::GetText( m_szCharBuf.Base(), m_szCharBuf.Count() );
2547+
const int iCurrentByteLen = Q_strlen( m_szCharBuf.Base() );
2548+
2549+
// Shortcut: average case
2550+
if ( iCurrentByteLen + 4 <= m_iMaxByteCount )
2551+
{
2552+
BaseClass::InsertChar(ch);
2553+
return;
2554+
}
2555+
2556+
// Edge case: check if current wchar_t will cause Send to truncate message
2557+
int iCharByteLen; // utf-8
2558+
if ( ch < 0x80 ) iCharByteLen = 1;
2559+
else if ( ch < 0x800 ) iCharByteLen = 2;
2560+
else if ( ch >= 0xD800 && ch <= 0xDBFF ) iCharByteLen = 4; // high surrogate, assume pair
2561+
else if ( ch >= 0xDC00 && ch <= 0xDFFF ) // low surrogate
2562+
{
2563+
// Check if previous wchar was a high surrogate
2564+
wchar_t wszHigh[2];
2565+
const int iLast = BaseClass::GetTextLength() - 1;
2566+
BaseClass::GetTextRange( wszHigh, iLast, 1 );
2567+
if ( wszHigh[0] >= 0xD800 && wszHigh[0] <= 0xDBFF ) { BaseClass::InsertChar(ch); return; }
2568+
else return; // it was not a hight surrogate, reject
2569+
}
2570+
else iCharByteLen = 3;
2571+
2572+
if ( iCurrentByteLen + iCharByteLen <= m_iMaxByteCount )
2573+
{
2574+
BaseClass::InsertChar(ch);
2575+
return;
2576+
}
2577+
2578+
return; // do not insert anything
2579+
}

src/game/client/hud_basechat.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,8 @@ class CBaseHudChatEntry : public vgui::TextEntry
324324
{
325325
typedef vgui::TextEntry BaseClass;
326326
public:
327+
int m_iMaxByteCount = -1; // utf-8 string length
328+
327329
CBaseHudChatEntry( vgui::Panel *parent, char const *panelName, CBaseHudChat *pChat )
328330
: BaseClass( parent, panelName )
329331
{
@@ -340,6 +342,8 @@ class CBaseHudChatEntry : public vgui::TextEntry
340342
SetPaintBorderEnabled( false );
341343
}
342344

345+
virtual void InsertChar(wchar_t ch);
346+
343347
virtual void OnKeyCodeTyped(vgui::KeyCode code)
344348
{
345349
if ( code == KEY_ENTER || code == KEY_PAD_ENTER || code == KEY_ESCAPE )
@@ -371,6 +375,7 @@ class CBaseHudChatEntry : public vgui::TextEntry
371375

372376
private:
373377
CBaseHudChat *m_pHudChat;
378+
CUtlMemory<char> m_szCharBuf{ 0, 64 }; // tmp buffer
374379
};
375380

376381
//-----------------------------------------------------------------------------

0 commit comments

Comments
 (0)