diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 2bc817ab..45a5d54e 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -583,7 +583,7 @@ static void CL_InitTitles( const char *filename ) pMemFile = FS_LoadFile( filename, &fileSize, false ); if( !pMemFile ) return; - CL_TextMessageParse( pMemFile, fileSize ); + clgame.titles = CL_TextMessageParse( clgame.mempool, pMemFile, fileSize, &clgame.numTitles ); Mem_Free( pMemFile ); } diff --git a/engine/client/client.h b/engine/client/client.h index 53585a80..e4682363 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -851,7 +851,7 @@ void CL_ClearWorld( void ); void CL_DrawCenterPrint( void ); void CL_ClearSpriteTextures( void ); void CL_CenterPrint( const char *text, float y ); -void CL_TextMessageParse( byte *pMemFile, int fileSize ); +client_textmessage_t *CL_TextMessageParse( poolhandle_t mempool, byte *pMemFile, int fileSize, int *numTitles ); client_textmessage_t *CL_TextMessageGet( const char *pName ); void NetAPI_CancelAllRequests( void ); model_t *CL_LoadClientSprite( const char *filename ); diff --git a/engine/client/titles.c b/engine/client/titles.c index 1a0746dd..68727411 100644 --- a/engine/client/titles.c +++ b/engine/client/titles.c @@ -15,6 +15,7 @@ GNU General Public License for more details. #include "common.h" #include "client.h" +#include "tests.h" #define MAX_MESSAGES 2048 @@ -208,7 +209,7 @@ static int ParseDirective( const char *pText ) return 0; } -void CL_TextMessageParse( byte *pMemFile, int fileSize ) +client_textmessage_t *CL_TextMessageParse( poolhandle_t mempool, byte *pMemFile, int fileSize, int *numTitles ) { char buf[512], trim[512], currentName[512]; char *pCurrentText = NULL, *pNameHeap; @@ -251,7 +252,7 @@ void CL_TextMessageParse( byte *pMemFile, int fileSize ) if( IsEndOfText( trim )) { Con_Reportf( "%s: unexpected '}' found, line %d\n", __func__, lineNumber ); - return; + return NULL; } Q_strncpy( currentName, trim, sizeof( currentName )); break; @@ -264,7 +265,7 @@ void CL_TextMessageParse( byte *pMemFile, int fileSize ) if( lastNamePos + length > 32768 ) { Con_Reportf( "%s: error while parsing!\n", __func__ ); - return; + return NULL; } Q_strncpy( nameHeap + lastNamePos, currentName, sizeof( nameHeap ) - lastNamePos ); @@ -287,7 +288,7 @@ void CL_TextMessageParse( byte *pMemFile, int fileSize ) if( IsStartOfText( trim )) { Con_Reportf( "%s: unexpected '{' found, line %d\n", __func__, lineNumber ); - return; + return NULL; } break; } @@ -312,22 +313,19 @@ void CL_TextMessageParse( byte *pMemFile, int fileSize ) if(( textHeapSize + nameHeapSize + messageSize ) <= 0 ) { - clgame.titles = NULL; - clgame.numTitles = 0; - return; + *numTitles = 0; + return NULL; } // must malloc because we need to be able to clear it after initialization - clgame.titles = (client_textmessage_t *)Mem_Calloc( cls.mempool, textHeapSize + nameHeapSize + messageSize ); + client_textmessage_t *out = Mem_Calloc( mempool, textHeapSize + nameHeapSize + messageSize ); // copy table over - memcpy( clgame.titles, textMessages, messageSize ); + memcpy( out, textMessages, messageSize ); // copy Name heap - pNameHeap = ((char *)clgame.titles) + messageSize; + pNameHeap = ((char *)out) + messageSize; memcpy( pNameHeap, nameHeap, nameHeapSize ); - //nameOffset = pNameHeap - clgame.titles[0].pName; //undefined on amd64 - // copy text & fixup pointers textHeapSizeRemaining = textHeapSize; @@ -335,19 +333,112 @@ void CL_TextMessageParse( byte *pMemFile, int fileSize ) for( i = 0; i < messageCount; i++ ) { - size_t currentTextSize = Q_strlen( clgame.titles[i].pMessage ) + 1; + size_t currentTextSize = Q_strlen( out[i].pMessage ) + 1; - clgame.titles[i].pName = pNameHeap; // adjust name pointer (parallel buffer) - Q_strncpy( pCurrentText, clgame.titles[i].pMessage, textHeapSizeRemaining ); // copy text over - clgame.titles[i].pMessage = pCurrentText; + out[i].pName = pNameHeap; // adjust name pointer (parallel buffer) + Q_strncpy( pCurrentText, out[i].pMessage, textHeapSizeRemaining ); // copy text over + out[i].pMessage = pCurrentText; pNameHeap += Q_strlen( pNameHeap ) + 1; pCurrentText += currentTextSize; textHeapSizeRemaining -= currentTextSize; } - if(( pCurrentText - (char *)clgame.titles ) != ( textHeapSize + nameHeapSize + messageSize )) + if(( pCurrentText - (char *)out ) != ( textHeapSize + nameHeapSize + messageSize )) Con_DPrintf( S_ERROR "%s: overflow text message buffer!\n", __func__ ); - clgame.numTitles = messageCount; + *numTitles = messageCount; + + return out; } + +#if XASH_ENGINE_TESTS + +void Test_RunTitles( void ) +{ + poolhandle_t mempool = Mem_AllocPool( __func__ ); + client_textmessage_t *tmessages; + client_textmessage_t *null; + int num_titles = 0, num_null_titles = 0; + char titles[] = + "// this is a comment\n" + "$effect 2\n" + "$color 1 2 3\n" + "$color2 4 5 6\n" + "$position 7 8\n" + "$fadein 0.1\n" + "$fadeout 0.5\n" + "$holdtime 321\n" + "$fxtime 123\n" + "TITLE\n" + "{\n" + "Hello to anybody reading test data\n" + "Hope you have a good time\n" + "}\n" + "\n" + "TITLE2\n" + "{\n" + "Still reading that nonsense huh?\n" + "}\n" + "// let's override some things\n" + "$cats are\n" + "$cute\n" + "$color 10 10 10\n" + "$position -1 -1\n" + "UwU\n" + "{\n" + "OwO\n" + "}\n" + "Technically titles can have spaces\n" + "{\n" + "Oh yeah!\n" + "}\n"; + char broken_titles[] = "}\n"; + char broken_titles2[] = "{\n{\n"; + + null = CL_TextMessageParse( mempool, broken_titles, Q_strlen( broken_titles ), &num_null_titles ); + TASSERT_EQi( num_null_titles, 0 ); + TASSERT_EQp( null, NULL ); + + null = CL_TextMessageParse( mempool, broken_titles2, Q_strlen( broken_titles2 ), &num_null_titles ); + TASSERT_EQi( num_null_titles, 0 ); + TASSERT_EQp( null, NULL ); + + tmessages = CL_TextMessageParse( mempool, titles, Q_strlen( titles ), &num_titles ); + TASSERT_EQi( num_titles, 4 ); + TASSERT_NEQp( tmessages, NULL ); + + for( int i = 0; i < 4; i++ ) + { + TASSERT_EQi( tmessages[i].effect, 2 ); + TASSERT_EQi( tmessages[i].r1, i >= 2 ? 10 : 1 ); + TASSERT_EQi( tmessages[i].g1, i >= 2 ? 10 : 2 ); + TASSERT_EQi( tmessages[i].b1, i >= 2 ? 10 : 3 ); + TASSERT_EQi( tmessages[i].a1, 0 ); + TASSERT_EQi( tmessages[i].r2, 4 ); + TASSERT_EQi( tmessages[i].g2, 5 ); + TASSERT_EQi( tmessages[i].b2, 6 ); + TASSERT_EQi( tmessages[i].a2, 0 ); + TASSERT_EQi( tmessages[i].x, i >= 2 ? -1 : 7 ); + TASSERT_EQi( tmessages[i].y, i >= 2 ? -1 : 8 ); + TASSERT_EQi( tmessages[i].fadein, 0.1f ); + TASSERT_EQi( tmessages[i].fadeout, 0.5f ); + TASSERT_EQi( tmessages[i].holdtime, 321.f ); + TASSERT_EQi( tmessages[i].fxtime, 123.f ); + } + + TASSERT_STR( tmessages[0].pName, "TITLE" ); + TASSERT_STR( tmessages[1].pName, "TITLE2" ); + TASSERT_STR( tmessages[2].pName, "UwU" ); + TASSERT_STR( tmessages[3].pName, "Technically titles can have spaces" ); + + TASSERT_STR( tmessages[0].pMessage, "Hello to anybody reading test data\nHope you have a good time" ); + TASSERT_STR( tmessages[1].pMessage, "Still reading that nonsense huh?" ); + TASSERT_STR( tmessages[2].pMessage, "OwO" ); + TASSERT_STR( tmessages[3].pMessage, "Oh yeah!" ); + + Mem_Free( tmessages ); + Mem_FreePool( &mempool ); +} + +#endif // XASH_ENGINE_TESTS diff --git a/engine/common/tests.h b/engine/common/tests.h index 69ebcff7..7e4ffc25 100644 --- a/engine/common/tests.h +++ b/engine/common/tests.h @@ -29,6 +29,8 @@ extern struct tests_stats_s tests_stats; _TASSERT( ( val1 ) != ( val2 ), Msg( S_ERROR "assert failed at %s:%i, \"%d\" != \"%d\"\n", __FILE__, __LINE__, (int)val1, (int)val2 )) #define TASSERT_EQp( val1, val2 ) \ _TASSERT( ( val1 ) != ( val2 ), Msg( S_ERROR "assert failed at %s:%i, \"%p\" != \"%p\"\n", __FILE__, __LINE__, val1, val2 )) +#define TASSERT_NEQp( val1, val2 ) \ + _TASSERT( ( val1 ) == ( val2 ), Msg( S_ERROR "assert failed at %s:%i, \"%p\" == \"%p\"\n", __FILE__, __LINE__, val1, val2 )) #define TASSERT_STR( str1, str2 ) \ _TASSERT( Q_strcmp(( str1 ), ( str2 )), Msg( S_ERROR "assert failed at %s:%i, \"%s\" != \"%s\"\n", __FILE__, __LINE__, ( str1 ), ( str2 ))) @@ -44,6 +46,7 @@ void Test_RunGamma( void ); void Test_RunDelta( void ); void Test_RunBuffer( void ); void Test_RunMunge( void ); +void Test_RunTitles( void ); #define TEST_LIST_0 \ Test_RunLibCommon(); \ @@ -63,7 +66,8 @@ void Test_RunMunge( void ); Test_RunImagelib(); #define TEST_LIST_1_CLIENT \ - Test_RunVOX(); + Test_RunVOX(); \ + Test_RunTitles(); #endif