This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/engine/common/titles.c

360 lines
8.0 KiB
C

//=======================================================================
// Copyright XashXT Group 2010 ©
// titles.c - implementation of titles.txt parser
//=======================================================================
#include "common.h"
#include "client.h"
#define MAX_MESSAGES 2048
#define MSGFILE_NAME 0
#define MSGFILE_TEXT 1
client_textmessage_t gMessageParms;
// the string "pText" is assumed to have all whitespace from both ends cut out
static int IsComment( const char *pText )
{
if( pText )
{
int length = com.strlen( pText );
if( length >= 2 && pText[0] == '/' && pText[1] == '/' )
return 1;
// no text?
if( length > 0 )
return 0;
}
// no text is a comment too
return 1;
}
// the string "pText" is assumed to have all whitespace from both ends cut out
static int IsStartOfText( const char *pText )
{
if( pText )
{
if( pText[0] == '{' )
return 1;
}
return 0;
}
// the string "pText" is assumed to have all whitespace from both ends cut out
static int IsEndOfText( const char *pText )
{
if( pText )
{
if( pText[0] == '}' )
return 1;
}
return 0;
}
static int IsWhiteSpace( char space )
{
if( space == ' ' || space == '\t' || space == '\r' || space == '\n' )
return 1;
return 0;
}
static const char *SkipSpace( const char *pText )
{
if( pText )
{
int pos = 0;
while( pText[pos] && IsWhiteSpace( pText[pos] ))
pos++;
return pText + pos;
}
return NULL;
}
static const char *SkipText( const char *pText )
{
if( pText )
{
int pos = 0;
while( pText[pos] && !IsWhiteSpace( pText[pos] ))
pos++;
return pText + pos;
}
return NULL;
}
static int ParseFloats( const char *pText, float *pFloat, int count )
{
const char *pTemp = pText;
int index = 0;
while( pTemp && count > 0 )
{
// skip current token / float
pTemp = SkipText( pTemp );
// skip any whitespace in between
pTemp = SkipSpace( pTemp );
if( pTemp )
{
// parse a float
pFloat[index] = com.atof( pTemp );
count--;
index++;
}
}
if( count == 0 )
return 1;
return 0;
}
// trims all whitespace from the front and end of a string
static void TrimSpace( const char *source, char *dest )
{
int start, end, length;
start = 0;
end = com.strlen( source );
while( source[start] && IsWhiteSpace( source[start] ))
start++;
end--;
while( end > 0 && IsWhiteSpace( source[end] ))
end--;
end++;
length = end - start;
if( length > 0 )
Mem_Copy( dest, source + start, length );
else length = 0;
// terminate the dest string
dest[length] = 0;
}
static int IsToken( const char *pText, const char *pTokenName )
{
if( !pText || !pTokenName )
return 0;
if( !com.strnicmp( pText+1, pTokenName, com.strlen( pTokenName )))
return 1;
return 0;
}
static int ParseDirective( const char *pText )
{
if( pText && pText[0] == '$' )
{
float tempFloat[8];
if( IsToken( pText, "position" ))
{
if( ParseFloats( pText, tempFloat, 2 ))
{
gMessageParms.x = tempFloat[0];
gMessageParms.y = tempFloat[1];
}
}
else if( IsToken( pText, "effect" ))
{
if( ParseFloats( pText, tempFloat, 1 ))
{
gMessageParms.effect = (int)tempFloat[0];
}
}
else if( IsToken( pText, "fxtime" ))
{
if( ParseFloats( pText, tempFloat, 1 ))
{
gMessageParms.fxtime = tempFloat[0];
}
}
else if( IsToken( pText, "color2" ))
{
if( ParseFloats( pText, tempFloat, 3 ))
{
gMessageParms.r2 = (int)tempFloat[0];
gMessageParms.g2 = (int)tempFloat[1];
gMessageParms.b2 = (int)tempFloat[2];
}
}
else if( IsToken( pText, "color" ))
{
if( ParseFloats( pText, tempFloat, 3 ))
{
gMessageParms.r1 = (int)tempFloat[0];
gMessageParms.g1 = (int)tempFloat[1];
gMessageParms.b1 = (int)tempFloat[2];
}
}
else if( IsToken( pText, "fadein" ))
{
if( ParseFloats( pText, tempFloat, 1 ))
{
gMessageParms.fadein = tempFloat[0];
}
}
else if( IsToken( pText, "fadeout" ))
{
if( ParseFloats( pText, tempFloat, 3 ))
{
gMessageParms.fadeout = tempFloat[0];
}
}
else if( IsToken( pText, "holdtime" ))
{
if( ParseFloats( pText, tempFloat, 3 ))
{
gMessageParms.holdtime = tempFloat[0];
}
}
else
{
MsgDev( D_ERROR, "unknown token: %s\n", pText );
}
return 1;
}
return 0;
}
void CL_TextMessageParse( byte *pMemFile, int fileSize )
{
char buf[512], trim[512];
char *pCurrentText = NULL, *pNameHeap;
char currentName[512], nameHeap[16384];
int mode = MSGFILE_NAME; // searching for a message name
int lineNumber, filePos, lastLinePos;
client_textmessage_t textMessages[MAX_MESSAGES];
int i, nameHeapSize, textHeapSize, messageSize, nameOffset;
int messageCount, lastNamePos;
lastNamePos = 0;
lineNumber = 0;
filePos = 0;
lastLinePos = 0;
messageCount = 0;
while( pfnMemFgets( pMemFile, fileSize, &filePos, buf, 512 ) != NULL )
{
TrimSpace( buf, trim );
switch( mode )
{
case MSGFILE_NAME:
// skip comment lines
if( IsComment( trim ))
break;
// Is this a directive "$command"?, if so parse it and break
if( ParseDirective( trim ))
break;
if( IsStartOfText( trim ))
{
mode = MSGFILE_TEXT;
pCurrentText = (char*)(pMemFile + filePos);
break;
}
if( IsEndOfText( trim ))
{
MsgDev( D_ERROR, "TextMessage: unexpected '}' found, line %d\n", lineNumber );
return;
}
com.strcpy( currentName, trim );
break;
case MSGFILE_TEXT:
if( IsEndOfText( trim ))
{
int length = com.strlen( currentName );
// save name on name heap
if( lastNamePos + length > 8192 )
{
MsgDev( D_ERROR, "TextMessage: error while parsing!\n" );
return;
}
com.strcpy( nameHeap + lastNamePos, currentName );
// terminate text in-place in the memory file
// (it's temporary memory that will be deleted)
pMemFile[lastLinePos-1] = 0;
// Save name/text on heap
textMessages[messageCount] = gMessageParms;
textMessages[messageCount].pName = nameHeap + lastNamePos;
lastNamePos += com.strlen( currentName ) + 1;
textMessages[messageCount].pMessage = pCurrentText;
messageCount++;
// reset parser to search for names
mode = MSGFILE_NAME;
break;
}
if( IsStartOfText( trim ))
{
MsgDev( D_ERROR, "TextMessage: unexpected '{' found, line %d\n", lineNumber );
return;
}
break;
}
lineNumber++;
lastLinePos = filePos;
if( messageCount >= MAX_MESSAGES )
{
MsgDev( D_WARN, "Too many messages in titles.txt, max is %d\n", MAX_MESSAGES );
break;
}
}
MsgDev( D_NOTE, "TextMessage: parsed %d text messages\n", messageCount );
nameHeapSize = lastNamePos;
textHeapSize = 0;
for( i = 0; i < messageCount; i++ )
textHeapSize += com.strlen( textMessages[i].pMessage ) + 1;
messageSize = ( messageCount * sizeof( client_textmessage_t ));
// must malloc because we need to be able to clear it after initialization
clgame.titles = (client_textmessage_t *)Mem_Alloc( cls.mempool, textHeapSize + nameHeapSize + messageSize );
// copy table over
Mem_Copy( clgame.titles, textMessages, messageSize );
// copy Name heap
pNameHeap = ((char *)clgame.titles) + messageSize;
Mem_Copy( pNameHeap, nameHeap, nameHeapSize );
nameOffset = pNameHeap - clgame.titles[0].pName;
// copy text & fixup pointers
pCurrentText = pNameHeap + nameHeapSize;
for( i = 0; i < messageCount; i++ )
{
clgame.titles[i].pName += nameOffset; // adjust name pointer (parallel buffer)
com.strcpy( pCurrentText, clgame.titles[i].pMessage ); // copy text over
clgame.titles[i].pMessage = pCurrentText;
pCurrentText += com.strlen( pCurrentText ) + 1;
}
#if _DEBUG
if(( pCurrentText - (char *)clgame.titles ) != ( textHeapSize + nameHeapSize + messageSize ))
MsgDev( D_ERROR, "TextMessage: overflow text message buffer!\n" );
#endif
clgame.numTitles = messageCount;
}