Fix Android arm64 crashes and improve GLES/Input stability

- Fixed 64-bit pointer truncation in CUtlMemoryAligned affecting shadows and meshes.
- Resolved heap corruption in GameUI and GamepadUI music systems (mismatched new[]/delete).
- Fixed out-of-bounds touch input crashes by mapping 64-bit SDL finger IDs.
- Lowered OpenGL requirement to 3.0 on Android and added fallbacks for GLES 3.2+ functions.
- Fixed GamepadUI text wrapping and lifecycle crashes.
This commit is contained in:
Star1xr 2026-05-22 19:48:25 +03:00
parent 8420c329c6
commit 067036f6e9
13 changed files with 147 additions and 51 deletions

View File

@ -450,7 +450,7 @@ void CTouchControls::CreateAtlasTexture()
int atlasSize = 0;
stbrp_rect *rects = (stbrp_rect*)malloc(textureList.Count()*sizeof(stbrp_rect));
memset(rects, 0, sizeof(stbrp_node)*textureList.Count());
memset(rects, 0, sizeof(stbrp_rect)*textureList.Count());
if( touchTextureID )
vgui::surface()->DeleteTextureByID( touchTextureID );

View File

@ -156,7 +156,7 @@ bool GamepadUIBasePanel::StartBackgroundMusic( float flVolume )
GamepadUI::GetInstance().GetEngineClient()->ClientCmd_Unrestricted( found );
}
fileNames.PurgeAndDeleteElements();
fileNames.PurgeAndDeleteElementsArray();
return m_nBackgroundMusicGUID != 0;
}

View File

@ -93,6 +93,10 @@ void GamepadUI::Shutdown()
if ( m_pBasePanel )
m_pBasePanel->DeletePanel();
if ( m_pAnimationController )
delete m_pAnimationController;
m_pAnimationController = NULL;
#ifdef HL2_RETAIL // not necessary on SDK2013 (Madi)
m_SteamAPIContext.Clear();
#endif
@ -116,8 +120,10 @@ void GamepadUI::OnLevelInitializePreEntity()
void GamepadUI::OnLevelInitializePostEntity()
{
m_pBasePanel->OnMenuStateChanged();
GetMainMenu()->OnMenuStateChanged();
if ( m_pBasePanel )
m_pBasePanel->OnMenuStateChanged();
if ( GetMainMenu() )
GetMainMenu()->OnMenuStateChanged();
}
void GamepadUI::OnLevelShutdown()
@ -128,12 +134,17 @@ void GamepadUI::OnLevelShutdown()
m_pAnimationController->RunAllAnimationsToCompletion();
}
m_pBasePanel->OnMenuStateChanged();
GetMainMenu()->OnMenuStateChanged();
if ( m_pBasePanel )
m_pBasePanel->OnMenuStateChanged();
if ( GetMainMenu() )
GetMainMenu()->OnMenuStateChanged();
}
void GamepadUI::VidInit()
{
if ( !m_pBasePanel )
return;
int w, h;
vgui::surface()->GetScreenSize( w, h );

View File

@ -35,6 +35,9 @@ const char *COM_GetModDirectory()
int DrawPrintWrappedText(vgui::HFont font, int pX, int pY, const wchar_t* pszText, int nLength, int nMaxWidth, bool bDraw)
{
if ( !pszText || nLength <= 0 || nMaxWidth <= 0 )
return 0;
float x = 0.0f;
int extraY = 0;
const int nFontTall = vgui::surface()->GetFontTall(font);
@ -42,53 +45,68 @@ int DrawPrintWrappedText(vgui::HFont font, int pX, int pY, const wchar_t* pszTex
const wchar_t* wszLastSpace = NULL;
const wchar_t* wszEnd = pszText + nLength;
for (const wchar_t* wsz = pszText; *wsz; wsz++)
for (const wchar_t* wsz = pszText; wsz < wszEnd; wsz++)
{
wchar_t ch = *wsz;
if (ch == L' ' || ch == L'\n')
wszLastSpace = wsz;
#if USE_GETKERNEDCHARWIDTH
wchar_t chBefore = 0;
wchar_t chAfter = 0;
if (wsz > pszText)
chBefore = wsz[-1];
chAfter = wsz[1];
float flWide = 0.0f, flabcA = 0.0f;
vgui::surface()->GetKernedCharWidth(font, ch, chBefore, chAfter, flWide, flabcA);
if (ch == L' ')
x = ceil(x);
surface()->DrawSetTextPos(x + px, y + py);
surface()->DrawUnicodeChar(ch);
x += floor(flWide + 0.6);
#else
x += vgui::surface()->GetCharacterWidth(font, ch);
#endif
if (x >= nMaxWidth || ch == L'\n')
{
const wchar_t* wszNewStart = wszLastSpace ? wszLastSpace : wsz;
const wchar_t* wszBreak;
const wchar_t* wszNextStart;
if ( ch == L'\n' )
{
wszBreak = wsz;
wszNextStart = wsz + 1;
}
else if ( wszLastSpace )
{
wszBreak = wszLastSpace;
wszNextStart = wszLastSpace + 1;
}
else
{
// Break mid-word
if ( wsz == wszStrStart )
{
// Even a single character doesn't fit, just force it
wszBreak = wsz + 1;
wszNextStart = wsz + 1;
}
else
{
wszBreak = wsz;
wszNextStart = wsz;
}
}
if ( bDraw )
{
vgui::surface()->DrawSetTextPos(pX, pY);
vgui::surface()->DrawPrintText(wszStrStart, (int)(intp)(wszNewStart - wszStrStart));
vgui::surface()->DrawPrintText(wszStrStart, (int)(wszBreak - wszStrStart));
}
wszStrStart = wszNewStart + 1;
wsz = wszStrStart;
if ( ch == L'\n' )
wsz--;
wszStrStart = wszNextStart;
wsz = wszNextStart - 1; // loop increment will move it to wszNextStart
wszLastSpace = NULL;
x = 0;
pY += nFontTall;
extraY += nFontTall;
}
}
if (wszStrStart != wszEnd && bDraw)
if (wszStrStart < wszEnd )
{
vgui::surface()->DrawSetTextPos(pX, pY);
vgui::surface()->DrawPrintText(wszStrStart, (int)(intp)(wszEnd - wszStrStart));
if ( bDraw )
{
vgui::surface()->DrawSetTextPos(pX, pY);
vgui::surface()->DrawPrintText(wszStrStart, (int)(wszEnd - wszStrStart));
}
}
return extraY;

View File

@ -507,7 +507,7 @@ void CGameUI::PlayGameStartupSound()
engine->ClientCmd_Unrestricted( found );
}
fileNames.PurgeAndDeleteElements();
fileNames.PurgeAndDeleteElementsArray();
}
}

View File

@ -461,6 +461,8 @@ public:
int m_mouseRawAccumX, m_mouseRawAccumY;
float m_touchAccumX[TOUCH_FINGER_MAX_COUNT], m_touchAccumY[TOUCH_FINGER_MAX_COUNT];
int m_touchFingerIds[TOUCH_FINGER_MAX_COUNT];
int GetFingerIndex( int fingerId, bool bAdd );
// For the 'SleepUntilInput' feature
HANDLE m_hEvent;

View File

@ -50,8 +50,9 @@ void CInputSystem::InitializeTouch( void )
memset( m_touchAccumX, 0, sizeof(m_touchAccumX) );
memset( m_touchAccumY, 0, sizeof(m_touchAccumY) );
for( int i = 0; i < TOUCH_FINGER_MAX_COUNT; i++ ) m_touchFingerIds[i] = -1;
m_bJoystickInitialized = true;
m_bTouchInitialized = true;
SDL_AddEventWatch(TouchSDLWatcher, this);
}
@ -64,35 +65,64 @@ void CInputSystem::ShutdownTouch()
m_bTouchInitialized = false;
}
bool CInputSystem::GetTouchAccumulators( int fingerId, float &dx, float &dy )
int CInputSystem::GetFingerIndex( int fingerId, bool bAdd )
{
dx = m_touchAccumX[fingerId];
dy = m_touchAccumY[fingerId];
for( int i = 0; i < TOUCH_FINGER_MAX_COUNT; i++ )
{
if( m_touchFingerIds[i] == fingerId )
return i;
}
m_touchAccumX[fingerId] = m_touchAccumY[fingerId] = 0.f;
if( bAdd )
{
for( int i = 0; i < TOUCH_FINGER_MAX_COUNT; i++ )
{
if( m_touchFingerIds[i] == -1 )
{
m_touchFingerIds[i] = fingerId;
return i;
}
}
}
return -1;
}
bool CInputSystem::GetTouchAccumulators( int index, float &dx, float &dy )
{
if( index < 0 || index >= TOUCH_FINGER_MAX_COUNT )
return false;
dx = m_touchAccumX[index];
dy = m_touchAccumY[index];
m_touchAccumX[index] = m_touchAccumY[index] = 0.f;
return true;
}
void CInputSystem::FingerEvent(int eventType, int fingerId, float x, float y, float dx, float dy)
{
if( fingerId >= TOUCH_FINGER_MAX_COUNT )
int index = GetFingerIndex( fingerId, eventType != IE_FingerUp );
if( index < 0 || index >= TOUCH_FINGER_MAX_COUNT )
return;
if( eventType == IE_FingerUp )
{
m_touchAccumX[fingerId] = 0.f;
m_touchAccumY[fingerId] = 0.f;
m_touchAccumX[index] = 0.f;
m_touchAccumY[index] = 0.f;
m_touchFingerIds[index] = -1;
}
else
{
m_touchAccumX[fingerId] += dx;
m_touchAccumY[fingerId] += dy;
m_touchAccumX[index] += dx;
m_touchAccumY[index] += dy;
}
int _x,_y;
memcpy( &_x, &x, sizeof(float) );
memcpy( &_y, &y, sizeof(float) );
PostEvent(eventType, m_nLastSampleTick, fingerId, _x, _y);
PostEvent(eventType, m_nLastSampleTick, index, _x, _y);
}

View File

@ -988,7 +988,7 @@ CUtlMemoryAligned<T, nAlignment>::CUtlMemoryAligned( T* pMemory, int numElements
CUtlMemory<T>::m_nGrowSize = CUtlMemory<T>::EXTERNAL_BUFFER_MARKER;
CUtlMemory<T>::m_pMemory = (T*)Align( pMemory );
CUtlMemory<T>::m_nAllocationCount = ( (int)(pMemory + numElements) - (int)CUtlMemory<T>::m_pMemory ) / sizeof(T);
CUtlMemory<T>::m_nAllocationCount = ( (T*)pMemory + numElements ) - (T*)CUtlMemory<T>::m_pMemory;
}
template< class T, int nAlignment >
@ -998,7 +998,7 @@ CUtlMemoryAligned<T, nAlignment>::CUtlMemoryAligned( const T* pMemory, int numEl
CUtlMemory<T>::m_nGrowSize = CUtlMemory<T>::EXTERNAL_CONST_BUFFER_MARKER;
CUtlMemory<T>::m_pMemory = (T*)Align( pMemory );
CUtlMemory<T>::m_nAllocationCount = ( (int)(pMemory + numElements) - (int)CUtlMemory<T>::m_pMemory ) / sizeof(T);
CUtlMemory<T>::m_nAllocationCount = ( (T*)pMemory + numElements ) - (T*)CUtlMemory<T>::m_pMemory;
}
template< class T, int nAlignment >
@ -1018,7 +1018,7 @@ void CUtlMemoryAligned<T, nAlignment>::SetExternalBuffer( T* pMemory, int numEle
Purge();
CUtlMemory<T>::m_pMemory = (T*)Align( pMemory );
CUtlMemory<T>::m_nAllocationCount = ( (int)(pMemory + numElements) - (int)CUtlMemory<T>::m_pMemory ) / sizeof(T);
CUtlMemory<T>::m_nAllocationCount = ( (T*)pMemory + numElements ) - (T*)CUtlMemory<T>::m_pMemory;
// Indicate that we don't own the memory
CUtlMemory<T>::m_nGrowSize = CUtlMemory<T>::EXTERNAL_BUFFER_MARKER;
@ -1031,7 +1031,7 @@ void CUtlMemoryAligned<T, nAlignment>::SetExternalBuffer( const T* pMemory, int
Purge();
CUtlMemory<T>::m_pMemory = (T*)Align( pMemory );
CUtlMemory<T>::m_nAllocationCount = ( (int)(pMemory + numElements) - (int)CUtlMemory<T>::m_pMemory ) / sizeof(T);
CUtlMemory<T>::m_nAllocationCount = ( (T*)pMemory + numElements ) - (T*)CUtlMemory<T>::m_pMemory;
// Indicate that we don't own the memory
CUtlMemory<T>::m_nGrowSize = CUtlMemory<T>::EXTERNAL_CONST_BUFFER_MARKER;

View File

@ -1433,7 +1433,7 @@ inline void CUtlVector<T, A>::PurgeAndDeleteElementsArray()
{
delete[] Element(i);
}
RemoveAll();
Purge();
}

View File

@ -67,7 +67,7 @@ GL_FUNC_VOID(OpenGL,true,glDeleteSync,(GLsync a),(a))
GL_FUNC(OpenGL,true,GLsync,glFenceSync,(GLenum a, GLbitfield b),(a,b))
#if 1 //ifndef OSX // 10.6/GL 2.1 compatability
GL_FUNC_VOID(OpenGL,true,glDrawRangeElementsBaseVertex,(GLenum a,GLuint b,GLuint c,GLsizei d,GLenum e,const GLvoid *f, GLenum g),(a,b,c,d,e,f,g))
GL_FUNC_VOID(OpenGL,false,glDrawRangeElementsBaseVertex,(GLenum a,GLuint b,GLuint c,GLsizei d,GLenum e,const GLvoid *f, GLenum g),(a,b,c,d,e,f,g))
#endif
GL_FUNC_VOID(OpenGL,true,glEnable,(GLenum a),(a))
GL_FUNC_VOID(OpenGL,true,glEnableVertexAttribArray,(GLuint a),(a))

View File

@ -1900,8 +1900,22 @@ FORCEINLINE void GLMContext::DrawRangeElements( GLenum mode, GLuint start, GLuin
#endif
// do the drawing
if (hasVP && hasFP)
{
if ( gGL->glDrawRangeElementsBaseVertex )
{
gGL->glDrawRangeElementsBaseVertex( mode, start, end, count, type, indicesActual, baseVertex );
}
else
{
if ( baseVertex == 0 )
{
gGL->glDrawRangeElements( mode, start, end, count, type, indicesActual );
}
else
{
Assert( !"glDrawRangeElementsBaseVertex not supported but baseVertex != 0" );
}
}
if ( m_slowCheckEnable )
{
@ -1916,7 +1930,21 @@ FORCEINLINE void GLMContext::DrawRangeElements( GLenum mode, GLuint start, GLuin
if ( m_pBoundPair )
{
gGL->glDrawRangeElementsBaseVertex( mode, start, end, count, type, indicesActual, baseVertex );
if ( gGL->glDrawRangeElementsBaseVertex )
{
gGL->glDrawRangeElementsBaseVertex( mode, start, end, count, type, indicesActual, baseVertex );
}
else
{
if ( baseVertex == 0 )
{
gGL->glDrawRangeElements( mode, start, end, count, type, indicesActual );
}
else
{
Assert( !"glDrawRangeElementsBaseVertex not supported but baseVertex != 0" );
}
}
#if GLMDEBUG
if ( m_slowCheckEnable )

View File

@ -540,6 +540,8 @@ DBG_INTERFACE bool DoNewAssertDialog( const tchar *pFilename, int line, const tc
{
#ifdef OSX
void *ret = dlopen( "libSDL2-2.0.0.dylib", RTLD_LAZY );
#elif defined( __ANDROID__ )
void *ret = dlopen( "libSDL2.so", RTLD_LAZY );
#else
void *ret = dlopen( "libSDL2-2.0.so.0", RTLD_LAZY );
#endif

View File

@ -249,8 +249,13 @@ static int GetOpenGLVersionPatch()
static bool CheckBaseOpenGLVersion()
{
#ifdef __ANDROID__
const int NEED_MAJOR = 3;
const int NEED_MINOR = 0;
#else
const int NEED_MAJOR = 3;
const int NEED_MINOR = 2;
#endif
const int NEED_PATCH = 0;
int major, minor, patch;