From c0b642adc33e852df9f6adb832275067be9404ef Mon Sep 17 00:00:00 2001 From: tmp64 Date: Sat, 18 May 2024 21:57:24 +0700 Subject: [PATCH] Client: Check that sprites are loaded from hud.txt (#222) Resolves #222 --- src/game/client/cdll_int.cpp | 15 ++++- src/game/client/cl_dll.h | 4 ++ src/game/client/hud.cpp | 106 ++++++++++++++++++++--------------- 3 files changed, 79 insertions(+), 46 deletions(-) diff --git a/src/game/client/cdll_int.cpp b/src/game/client/cdll_int.cpp index 95db50e5..9936fa27 100644 --- a/src/game/client/cdll_int.cpp +++ b/src/game/client/cdll_int.cpp @@ -226,8 +226,7 @@ int CL_DLLEXPORT Initialize(cl_enginefunc_t *pEnginefuncs, int iVersion) if (bIsInitialized) { - GetSDL()->ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "BugfixedHL Fatal Error", "The engine failed to unload client library during restart."); - std::abort(); + HUD_FatalError("The engine failed to unload client library during restart."); return 0; } @@ -508,6 +507,18 @@ extern "C" void CL_DLLEXPORT F(void *pv) *pcldll_func = cldll_func; } +void HUD_FatalError(const char *fmt, ...) +{ + char buf[4096]; + va_list va; + va_start(va, fmt); + vsnprintf(buf, sizeof(buf), fmt, va); + va_end(va); + + GetSDL()->ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "BugfixedHL Fatal Error", buf); + std::abort(); +} + //----------------------------------------------------------------------------- // Purpose: Exports functions that are used by the gameUI for UI dialogs //----------------------------------------------------------------------------- diff --git a/src/game/client/cl_dll.h b/src/game/client/cl_dll.h index 74159765..c102ee56 100644 --- a/src/game/client/cl_dll.h +++ b/src/game/client/cl_dll.h @@ -45,3 +45,7 @@ typedef float vec_t; #include "cdll_dll.h" extern cl_enginefunc_t gEngfuncs; + +//! Causes a fatal client error. Shows a dialog and crashes the game. +//! @param fmt Printf arguments. +[[noreturn]] void HUD_FatalError(const char *fmt, ...); diff --git a/src/game/client/hud.cpp b/src/game/client/hud.cpp index 2967a45c..2b50502e 100644 --- a/src/game/client/hud.cpp +++ b/src/game/client/hud.cpp @@ -246,6 +246,19 @@ static int GetHudSize(const SCREENINFO &screenInfo, EHudScale maxScale) return fallbackInfo.iRes; } +static int CountSpritesOfRes(const client_sprite_t* pSpriteList, int nTotalCount, int iRes) +{ + int count = 0; + + for (int i = 0; i < nTotalCount; i++) + { + if (pSpriteList[i].iRes == iRes) + count++; + } + + return count; +} + CHud::CHud() { } @@ -441,56 +454,58 @@ void CHud::VidInit(void) // we need to load the hud.txt, and all sprites within m_pSpriteList = SPR_GetList("sprites/hud.txt", &m_iSpriteCountAllRes); - if (m_pSpriteList) - { - // count the number of sprites of the appropriate res - m_iSpriteCount = 0; - client_sprite_t *p = m_pSpriteList; - int j; - for (j = 0; j < m_iSpriteCountAllRes; j++) - { - if (p->iRes == m_iRes) - m_iSpriteCount++; - p++; - } + if (!m_pSpriteList) + HUD_FatalError("Failed to load sprites/hud.txt.\nYour game is corrupted."); - // allocated memory for sprite handle arrays - m_rghSprites.resize(m_iSpriteCount); - m_rgrcRects.resize(m_iSpriteCount); - m_rgszSpriteNames.resize(m_iSpriteCount); - m_rgSpritePaths.resize(m_iSpriteCount); + // count the number of sprites of the appropriate res + m_iSpriteCount = CountSpritesOfRes(m_pSpriteList, m_iSpriteCountAllRes, m_iRes); - p = m_pSpriteList; - int index = 0; - for (j = 0; j < m_iSpriteCountAllRes; j++) - { - if (p->iRes == m_iRes) - { - char sz[256]; - sprintf(sz, "sprites/%s.spr", p->szSprite); - m_rghSprites[index] = SPR_Load(sz); - m_rgrcRects[index] = p->rc; - Q_strncpy(m_rgszSpriteNames[index].name, p->szName, MAX_SPRITE_NAME_LENGTH); - m_rgSpritePaths[index] = sz; - - index++; - } + if (m_iSpriteCount == 0) + { + Warning("Found no sprites with resolution of %d. Defaulting to %d.\n", m_iRes, HUD_FALLBACK_RES); + m_iRes = HUD_FALLBACK_RES; + m_iSpriteCount = CountSpritesOfRes(m_pSpriteList, m_iSpriteCountAllRes, m_iRes); + } - p++; - } + if (m_iSpriteCount == 0) + HUD_FatalError("Failed to find sprites with resolution of %d in sprites/hud.txt.\nYour game is corrupted.", m_iRes); + + // allocated memory for sprite handle arrays + m_rghSprites.resize(m_iSpriteCount); + m_rgrcRects.resize(m_iSpriteCount); + m_rgszSpriteNames.resize(m_iSpriteCount); + m_rgSpritePaths.resize(m_iSpriteCount); - // Add AG CTF sprites on non-AG clients - // AG has them in hud.txt - if (!IsAG()) + client_sprite_t *p = m_pSpriteList; + int index = 0; + for (int j = 0; j < m_iSpriteCountAllRes; j++) + { + if (p->iRes == m_iRes) { - AddSprite(client_sprite_t { "item_flag_team1", "ag_ctf", 0, 640, wrect_t { 120, 160, 0, 40 } }); - AddSprite(client_sprite_t { "item_flag_team2", "ag_ctf", 0, 640, wrect_t { 120, 160, 0, 40 } }); - AddSprite(client_sprite_t { "icon_ctf_home", "ag_ctf", 0, 640, wrect_t { 0, 40, 0, 40 } }); - AddSprite(client_sprite_t { "icon_ctf_stolen", "ag_ctf", 0, 640, wrect_t { 40, 80, 0, 40 } }); - AddSprite(client_sprite_t { "icon_ctf_lost", "ag_ctf", 0, 640, wrect_t { 80, 120, 0, 40 } }); - AddSprite(client_sprite_t { "icon_ctf_carry", "ag_ctf", 0, 640, wrect_t { 120, 160, 0, 40 } }); - AddSprite(client_sprite_t { "icon_ctf_score", "ag_ctf_score", 0, 640, wrect_t { 0, 16, 0, 16 } }); + char sz[256]; + sprintf(sz, "sprites/%s.spr", p->szSprite); + m_rghSprites[index] = SPR_Load(sz); + m_rgrcRects[index] = p->rc; + Q_strncpy(m_rgszSpriteNames[index].name, p->szName, MAX_SPRITE_NAME_LENGTH); + m_rgSpritePaths[index] = sz; + + index++; } + + p++; + } + + // Add AG CTF sprites on non-AG clients + // AG has them in hud.txt + if (!IsAG()) + { + AddSprite(client_sprite_t { "item_flag_team1", "ag_ctf", 0, 640, wrect_t { 120, 160, 0, 40 } }); + AddSprite(client_sprite_t { "item_flag_team2", "ag_ctf", 0, 640, wrect_t { 120, 160, 0, 40 } }); + AddSprite(client_sprite_t { "icon_ctf_home", "ag_ctf", 0, 640, wrect_t { 0, 40, 0, 40 } }); + AddSprite(client_sprite_t { "icon_ctf_stolen", "ag_ctf", 0, 640, wrect_t { 40, 80, 0, 40 } }); + AddSprite(client_sprite_t { "icon_ctf_lost", "ag_ctf", 0, 640, wrect_t { 80, 120, 0, 40 } }); + AddSprite(client_sprite_t { "icon_ctf_carry", "ag_ctf", 0, 640, wrect_t { 120, 160, 0, 40 } }); + AddSprite(client_sprite_t { "icon_ctf_score", "ag_ctf_score", 0, 640, wrect_t { 0, 16, 0, 16 } }); } } else @@ -511,6 +526,9 @@ void CHud::VidInit(void) // assumption: number_1, number_2, etc, are all listed and loaded sequentially m_HUD_number_0 = GetSpriteIndex("number_0"); + if (m_HUD_number_0 == -1) + HUD_FatalError("Failed to find sprite 'number_0' in the sprite list.\nYour game is corrupted."); + m_iFontHeight = m_rgrcRects[m_HUD_number_0].bottom - m_rgrcRects[m_HUD_number_0].top; for (CHudElem *i : m_HudList)