- Accumulation buffer motion blur, which should work on shader model 2 hardware with less memory footprint. Pixel shader moved to r_motionblur 3 (will do texture motionblur on 2) - Tried to make r_tvMode 2 half vertical resolution, and failed. - Q3MME's FX scripted particle system files, just dropped in there, because they can't compile at all atm. Procrastinplanning on integrating this to replace my cgame effects
2391 lines
74 KiB
C
2391 lines
74 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 2006 Sjoerd van der Berg ( harekiet @ gmail.com )
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
Quake III Arena source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
Quake III Arena source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Foobar; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
// fx_parse.c -- Parse and load FX Scripts and handle the fx related math
|
|
|
|
#include "client.h"
|
|
#include "fx_local.h"
|
|
|
|
fxMain_t fx;
|
|
|
|
#define parentOfs(m) ((size_t)&((((fxParent_t*)0)->m)))
|
|
#define parentSize(m) (sizeof((((fxParent_t*)0)->m)) / sizeof(float))
|
|
#define runOfs(m) ((size_t)&((((fxRun_t*)0)->m)))
|
|
#define runSize(m) (sizeof((((fxRun_t*)0)->m)) / sizeof(float))
|
|
|
|
static fxScript_t *fxFindScript( const char *name );
|
|
|
|
static unsigned int fxGenerateHash( const char *name ) {
|
|
unsigned int hash = 0;
|
|
while (*name) {
|
|
char c = tolower(name[0]);
|
|
hash = (hash << 5 ) ^ (hash >> 27) ^ c;
|
|
name++;
|
|
}
|
|
return (hash ^ (hash >> FX_HASH_SHIFT) ^ (hash >> (FX_HASH_SHIFT*2))) & FX_HASH_MASK;
|
|
}
|
|
|
|
typedef enum {
|
|
fxParentScript,
|
|
fxParentEmitter,
|
|
} fxParseParent_t;
|
|
|
|
typedef struct {
|
|
const char *last, *text, *name, *line;
|
|
int lineCount;
|
|
char token[1024];
|
|
fxParseParent_t parent;
|
|
fxRunEmitter_t *emitter;
|
|
int loopDepth;
|
|
unsigned int readMask, writeMask, vectorMask;
|
|
} fxParse_t;
|
|
|
|
typedef struct {
|
|
int size, used;
|
|
byte *data;
|
|
} fxParseOut_t;
|
|
|
|
|
|
static ID_INLINE void fxParseWrite( fxParse_t *parse, unsigned int mask ) {
|
|
parse->writeMask |= mask;
|
|
}
|
|
|
|
static ID_INLINE void fxParseRead( fxParse_t *parse, unsigned int mask ) {
|
|
mask &= ~parse->writeMask;
|
|
parse->readMask |= mask;
|
|
}
|
|
|
|
qboolean fxParseError( fxParse_t *parse, const char *fmt, ... ) {
|
|
va_list argptr;
|
|
char msg[512];
|
|
char line[512];
|
|
int i = 0;
|
|
|
|
if ( parse->line ) {
|
|
while (i < sizeof( line ) - 1) {
|
|
if ( !parse->line[i] )
|
|
break;
|
|
if ( parse->line[i] == '\n' )
|
|
break;
|
|
if ( parse->line[i] == '\r' )
|
|
break;
|
|
line[i] = parse->line[i];
|
|
i++;
|
|
}
|
|
}
|
|
line[i] = 0;
|
|
|
|
va_start (argptr,fmt);
|
|
Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
|
|
va_end (argptr);
|
|
|
|
Com_Printf( S_COLOR_YELLOW "fxError:'%s' in line '%s' in script '%s'\n", msg ,line, parse->name );
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean fxParseErrorIllegal( fxParse_t *parse ) {
|
|
return fxParseError( parse, "Illegal token %s", parse->token );
|
|
}
|
|
|
|
static void *fxParseAlloc( int size ) {
|
|
return Z_TagMalloc( size, TAG_FX );
|
|
}
|
|
static void fxParseFree( void *free ) {
|
|
Z_Free( free );
|
|
}
|
|
|
|
static qboolean fxParseCreateMask( fxParse_t *parse, byte *table, int tableLen, unsigned short *allocAdd ) {
|
|
byte modMask[ runOfs( stackUsed ) / 4 ];
|
|
unsigned int mask = parse->readMask;
|
|
int modeCount, maskIndex, maskLast, tableIndex = 0;
|
|
int totalCount;
|
|
byte checkVal;
|
|
|
|
memset( modMask, 0, sizeof( modMask ));
|
|
#define MODSET( m ) memset( modMask + (runOfs( m ) / 4), 1, runSize( m ) );
|
|
#if 0
|
|
if ( mask & FX_MASK_ORIGIN ) MODSET( origin );
|
|
if ( mask & FX_MASK_VELOCITY ) MODSET( velocity );
|
|
#endif
|
|
if ( mask & FX_MASK_SHADER ) MODSET( shader );
|
|
if ( mask & FX_MASK_SIZE ) MODSET( size );
|
|
if ( mask & FX_MASK_ROTATE ) MODSET( rotate );
|
|
if ( mask & FX_MASK_COLOR ) MODSET( color );
|
|
|
|
if ( mask & FX_MASK_PARENT ) MODSET( parent );
|
|
if ( mask & FX_MASK_DIR ) MODSET( dir );
|
|
if ( mask & FX_MASK_MODEL ) MODSET( model );
|
|
if ( mask & FX_MASK_AXIS ) MODSET( axis );
|
|
if ( mask & FX_MASK_ANGLES ) MODSET( angles );
|
|
if ( mask & FX_MASK_WIDTH ) MODSET( width );
|
|
if ( mask & FX_MASK_T0 ) MODSET( t0 );
|
|
if ( mask & FX_MASK_T1 ) MODSET( t1 );
|
|
if ( mask & FX_MASK_T2 ) MODSET( t2 );
|
|
if ( mask & FX_MASK_T3 ) MODSET( t3 );
|
|
if ( mask & FX_MASK_V0 ) MODSET( v0 );
|
|
if ( mask & FX_MASK_V1 ) MODSET( v1 );
|
|
if ( mask & FX_MASK_V2 ) MODSET( v2 );
|
|
if ( mask & FX_MASK_V3 ) MODSET( v3 );
|
|
#undef MODSET
|
|
|
|
//This is all somewhat hacky, but seems to work
|
|
maskIndex = 3 + runOfs( velocity ) / 4;
|
|
maskLast = runOfs( parent ) / 4 + runSize( parent );
|
|
modeCount = 0;
|
|
totalCount = 0;
|
|
checkVal = 0;
|
|
|
|
while ( maskIndex < maskLast ) {
|
|
if ( checkVal == modMask[maskIndex] ) {
|
|
maskIndex++;
|
|
modeCount++;
|
|
totalCount += checkVal;
|
|
} else {
|
|
if ( tableIndex >= tableLen )
|
|
return fxParseError( parse, "Ran out of write mask space" );
|
|
checkVal ^= 1;
|
|
table[tableIndex++] = modeCount;
|
|
modeCount = 0;
|
|
}
|
|
}
|
|
if ( tableIndex >= tableLen )
|
|
return fxParseError( parse, "Ran out of write mask space" );
|
|
table[tableIndex++] = modeCount;
|
|
if (modeCount) {
|
|
if ( tableIndex >= tableLen )
|
|
return fxParseError( parse, "Ran out of write mask space" );
|
|
table[tableIndex++] = 0;
|
|
}
|
|
*allocAdd += totalCount * 4;
|
|
return qtrue;
|
|
}
|
|
|
|
#define SEPERATOR(_T_) ((_T_ == ' ') || (_T_ == '\n') || (_T_ == '\t') || (_T_ == '\r'))
|
|
|
|
static qboolean fxParseToken( fxParse_t *parse, qboolean skipLine ) {
|
|
const char *p = parse->text;
|
|
char *token = parse->token;
|
|
qboolean haveQuote = qfalse;
|
|
int tokenLength = 0;
|
|
|
|
if (!parse->line)
|
|
parse->line = p;
|
|
|
|
parse->last = p;
|
|
while (1) {
|
|
if ( tokenLength >= sizeof( parse->token )) {
|
|
parse->token[ sizeof( parse->token ) -1 ] = 0;
|
|
return fxParseError( parse, "token %s too long", parse->token );
|
|
}
|
|
if ( haveQuote ) {
|
|
if (*p == '\n' || *p == '\r' || *p == 0) {
|
|
return fxParseError( parse, "token missing end quote" );
|
|
}
|
|
if ( *p == '\"' ) {
|
|
if (!SEPERATOR(p[1]) && !p[1])
|
|
return fxParseError( parse, "token quote followed by a character" );
|
|
haveQuote = qfalse;
|
|
p++;
|
|
break;
|
|
}
|
|
token[tokenLength++] = p[0];
|
|
} else if ( tokenLength ) {
|
|
if (SEPERATOR(p[0]) || !p[0])
|
|
break;
|
|
token[tokenLength++] = p[0];
|
|
} else if (!p[0]) {
|
|
break;
|
|
} else if (p[0] == '"' ) {
|
|
haveQuote = qtrue;
|
|
} else if (SEPERATOR(p[0])) {
|
|
if ( p[0] == '\n' || p[0] == '\r' ) {
|
|
if ( !skipLine )
|
|
break;
|
|
parse->line = p+1;
|
|
parse->last = 0;
|
|
}
|
|
} else {
|
|
token[tokenLength++] = p[0];
|
|
}
|
|
p++;
|
|
}
|
|
parse->text = p;
|
|
token[tokenLength] = 0;
|
|
if (!tokenLength )
|
|
return qfalse;
|
|
return qtrue;
|
|
}
|
|
|
|
static qboolean fxParseReverse( fxParse_t *parse ) {
|
|
if (!parse->last)
|
|
return fxParseError( parse, "Can't reverse a token" );
|
|
parse->text = parse->last;
|
|
return qtrue;
|
|
}
|
|
|
|
static qboolean fxParseWord( fxParse_t *parse, const char *type ) {
|
|
if (!fxParseToken( parse, qfalse )) {
|
|
if ( !type )
|
|
type = "word";
|
|
return fxParseError( parse, "Missing %s" );
|
|
}
|
|
return qtrue;
|
|
}
|
|
|
|
static qboolean fxParseCompare( fxParse_t *parse, const char *compare ) {
|
|
if (!fxParseWord( parse, compare )) {
|
|
return qfalse;
|
|
}
|
|
if (Q_stricmp( parse->token, compare ))
|
|
return fxParseError( parse, "%s doesn't match %s", parse->token, compare );
|
|
return qtrue;
|
|
}
|
|
|
|
typedef qboolean (*fxListMatch_t)( fxParse_t *, void * );
|
|
|
|
static qboolean fxParseList( fxParse_t *parse, const char *list, fxListMatch_t match, int dataSize, void *data, int countMax, unsigned int *count ) {
|
|
const char *token = parse->token;
|
|
|
|
if (!fxParseCompare( parse, "{"))
|
|
return qfalse;
|
|
*count = 0;
|
|
while ( 1 ) {
|
|
if (!fxParseToken( parse, qtrue )) {
|
|
return fxParseError( parse, "Expected %s entry", list );
|
|
}
|
|
if (!Q_stricmp( token, "}"))
|
|
break;
|
|
if (!match( parse, ((char *)data) + *count * dataSize ))
|
|
return qfalse;
|
|
(*count)++;
|
|
if ( *count >= countMax )
|
|
return fxParseError( parse, "Too many entries for %s (max %d)", list, countMax );
|
|
}
|
|
if (!*count)
|
|
return fxParseError( parse, "%s is empty", list );
|
|
return qtrue;
|
|
}
|
|
|
|
static qboolean fxMatchValidFloat( fxParse_t *parse ) {
|
|
int doneNumber = 0;
|
|
int donePoint = 0;
|
|
|
|
char *token = parse->token;
|
|
for ( ;1;token++) {
|
|
switch (*token) {
|
|
case 0:
|
|
if (!doneNumber)
|
|
return fxParseError( parse, "empty float", parse->token );
|
|
return qtrue;
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
doneNumber++;
|
|
continue;
|
|
case '.':
|
|
if ( donePoint )
|
|
return fxParseError( parse, "invalid float %s", parse->token );
|
|
donePoint++;
|
|
continue;
|
|
default:
|
|
return fxParseError( parse, "illegal char %c in float %s", *token, parse->token );
|
|
}
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
static qboolean fxParseFloat( fxParse_t *parse, float *data ) {
|
|
if (!fxParseToken( parse, qfalse )) {
|
|
return fxParseError( parse, "Expected float" );
|
|
}
|
|
if (!fxMatchValidFloat( parse ))
|
|
return qfalse;
|
|
//TODO check for valid float?
|
|
*data = atof( parse->token );
|
|
return qtrue;
|
|
}
|
|
|
|
/* Bit of a hack to parse the second 2 values */
|
|
static qboolean fxMatchColor( fxParse_t *parse, byte *data ) {
|
|
int i;
|
|
for (i = 0;i<3;i++) {
|
|
float f;
|
|
if (!i) {
|
|
if (!fxMatchValidFloat( parse ))
|
|
return qfalse;
|
|
f = atof( parse->token );
|
|
} else if (!fxParseFloat( parse, &f)) {
|
|
return qfalse;
|
|
}
|
|
if ( f < 0 )
|
|
f = 0;
|
|
else if ( f > 1)
|
|
f = 1;
|
|
data[i] = f * 255;
|
|
}
|
|
return qtrue;
|
|
}
|
|
|
|
static qboolean fxMatchShader( fxParse_t *parse, qhandle_t *data ) {
|
|
data[0] = re.RegisterShader( parse->token );
|
|
if (!data[0])
|
|
return fxParseError( parse, "shader %s not found", parse->token );
|
|
return qtrue;
|
|
}
|
|
|
|
static qboolean fxMatchModel( fxParse_t *parse, qhandle_t *data ) {
|
|
data[0] = re.RegisterModel( parse->token );
|
|
if (!data[0])
|
|
return fxParseError( parse, "model %s not found", parse->token );
|
|
return qtrue;
|
|
}
|
|
|
|
static qboolean fxMatchSound( fxParse_t *parse, sfxHandle_t *data ) {
|
|
data[0] = S_RegisterSound( parse->token, qfalse );
|
|
if (!data[0])
|
|
return fxParseError( parse, "Sound %s not found", parse->token );
|
|
return qtrue;
|
|
}
|
|
|
|
static qboolean fxParseColor( fxParse_t *parse, byte *data ) {
|
|
int i;
|
|
for (i = 0;i<3;i++) {
|
|
float f;
|
|
if (!fxParseFloat( parse, &f )) {
|
|
return qfalse;
|
|
}
|
|
if ( f < 0 )
|
|
f = 0;
|
|
else if ( f > 1)
|
|
f = 1;
|
|
data[i] = f * 255;
|
|
}
|
|
return qtrue;
|
|
}
|
|
|
|
static qboolean fxParseRenderFX( fxParse_t *parse, int *renderFX ) {
|
|
*renderFX = RF_NOSHADOW;
|
|
while (fxParseToken( parse, qfalse ) ) {
|
|
const char *token = parse->token;
|
|
if (!Q_stricmp(token, "firstPerson")) {
|
|
*renderFX |= RF_FIRST_PERSON;
|
|
} else if (!Q_stricmp(token, "thirdPerson")) {
|
|
*renderFX |= RF_THIRD_PERSON;
|
|
} else if (!Q_stricmp(token, "shadow")) {
|
|
*renderFX &= ~RF_NOSHADOW;
|
|
} else if (!Q_stricmp(token, "cullNear") || !Q_stricmp(token, "cullRadius" ) ) {
|
|
*renderFX |= RF_CULLRADIUS;
|
|
} else if (!Q_stricmp(token, "depthHack")) {
|
|
*renderFX |= RF_DEPTHHACK;
|
|
} else if (!Q_stricmp(token, "stencil")) {
|
|
*renderFX |= RF_STENCIL;
|
|
} else {
|
|
return fxParseError( parse, "Illegal render keyword %s", token );
|
|
}
|
|
}
|
|
return qtrue;
|
|
}
|
|
|
|
|
|
static qboolean fxParseOut( fxParse_t *parse, fxParseOut_t *out, const void *data, int size ) {
|
|
if ( out->used + size + sizeof( void *) > out->size ) {
|
|
fxParseError( parse, "Overflowing output buffer" );
|
|
return qfalse;
|
|
}
|
|
memcpy( out->data + out->used, data, size );
|
|
out->used += size;
|
|
return qtrue;
|
|
}
|
|
|
|
static qboolean fxParseOutCmd( fxParse_t *parse, fxParseOut_t *out, int cmd, const void *data, int size ) {
|
|
if (!fxParseOut( parse, out, &cmd, sizeof( cmd ) ))
|
|
return qfalse;
|
|
if (!fxParseOut( parse, out, data, size ))
|
|
return qfalse;
|
|
return qtrue;
|
|
}
|
|
|
|
typedef struct {
|
|
fxRun_t *run;
|
|
const int *data;
|
|
} fxMathInfo_t;
|
|
|
|
typedef float (*fxMathHandler)( fxMathInfo_t *info );
|
|
|
|
static float ID_INLINE fxMath( fxMathInfo_t *info ) {
|
|
fxMathHandler * handler = ( fxMathHandler *)info->data;
|
|
info->data++;
|
|
return (*handler)( info );
|
|
}
|
|
|
|
const void *fxRunMath( fxRun_t *run, const void *data, float *value ) {
|
|
fxMathInfo_t info;
|
|
info.run = run;
|
|
info.data = data;
|
|
|
|
*value = fxMath( &info );
|
|
return info.data;
|
|
}
|
|
static float fxMathNeg( fxMathInfo_t *info ) {
|
|
return -fxMath( info );
|
|
}
|
|
static float fxMathMul( fxMathInfo_t *info ) {
|
|
float v1 = fxMath( info );
|
|
float v2 = fxMath( info );
|
|
return v1 * v2;
|
|
}
|
|
static float fxMathDiv( fxMathInfo_t *info ) {
|
|
float v1 = fxMath( info );
|
|
float v2 = fxMath( info );
|
|
return v1 / v2;
|
|
}
|
|
static float fxMathAdd( fxMathInfo_t *info ) {
|
|
float v1 = fxMath( info );
|
|
float v2 = fxMath( info );
|
|
return v1 + v2;
|
|
}
|
|
static float fxMathSub( fxMathInfo_t *info ) {
|
|
float v1 = fxMath( info );
|
|
float v2 = fxMath( info );
|
|
return v1 - v2;
|
|
}
|
|
|
|
static float fxMathEqual( fxMathInfo_t *info ) {
|
|
float v1 = fxMath( info );
|
|
float v2 = fxMath( info );
|
|
return ((v1 != 0) == (v2 != 0)) ? 1 : 0;
|
|
}
|
|
static float fxMathNotEqual( fxMathInfo_t *info ) {
|
|
float v1 = fxMath( info );
|
|
float v2 = fxMath( info );
|
|
return ((v1 != 0) != (v2 != 0)) ? 1 : 0;
|
|
}
|
|
|
|
static float fxMathTE( fxMathInfo_t *info ) {
|
|
float v1 = fxMath( info );
|
|
float v2 = fxMath( info );
|
|
return ( v1 == v2 ) ? 1 : 0;
|
|
}
|
|
static float fxMathTNE( fxMathInfo_t *info ) {
|
|
float v1 = fxMath( info );
|
|
float v2 = fxMath( info );
|
|
return ( v1 != v2 ) ? 1 : 0;
|
|
}
|
|
static float fxMathTG( fxMathInfo_t *info ) {
|
|
float v1 = fxMath( info );
|
|
float v2 = fxMath( info );
|
|
return (v1 > v2) ? 1 : 0;
|
|
}
|
|
static float fxMathTGE( fxMathInfo_t *info ) {
|
|
float v1 = fxMath( info );
|
|
float v2 = fxMath( info );
|
|
return (v1 >= v2) ? 1 : 0;
|
|
}
|
|
static float fxMathTS( fxMathInfo_t *info ) {
|
|
float v1 = fxMath( info );
|
|
float v2 = fxMath( info );
|
|
return (v1 < v2) ? 1 : 0;
|
|
}
|
|
static float fxMathTSE( fxMathInfo_t *info ) {
|
|
float v1 = fxMath( info );
|
|
float v2 = fxMath( info );
|
|
return (v1 <= v2) ? 1 : 0;
|
|
}
|
|
|
|
static float fxMathAND( fxMathInfo_t *info ) {
|
|
float v1 = fxMath( info );
|
|
float v2 = fxMath( info );
|
|
return (v1 && v2) ? 1 : 0;
|
|
}
|
|
|
|
static float fxMathOR( fxMathInfo_t *info ) {
|
|
float v1 = fxMath( info );
|
|
float v2 = fxMath( info );
|
|
return (v1 || v2) ? 1 : 0;
|
|
}
|
|
|
|
static float fxMathSin( fxMathInfo_t *info ) {
|
|
return sinf( DEG2RAD( fxMath( info )));
|
|
}
|
|
static float fxMathCos( fxMathInfo_t *info ) {
|
|
return cosf( DEG2RAD( fxMath( info )));
|
|
}
|
|
static float fxMathWave( fxMathInfo_t *info ) {
|
|
return sinf( fxMath( info ) * 2 * M_PI );
|
|
}
|
|
static float fxMathSqrt( fxMathInfo_t *info ) {
|
|
return sqrtf( fxMath( info ));
|
|
}
|
|
static float fxMathCeil( fxMathInfo_t *info ) {
|
|
return ceilf( fxMath( info ));
|
|
}
|
|
static float fxMathFloor( fxMathInfo_t *info ) {
|
|
return floorf( fxMath( info ));
|
|
}
|
|
static float fxMathClip( fxMathInfo_t *info) {
|
|
float v = fxMath( info );
|
|
if ( v < 0.0f)
|
|
return 0.0f;
|
|
if ( v > 1.0f )
|
|
return 1.0f;
|
|
return v;
|
|
}
|
|
static float fxMathZero( fxMathInfo_t *info ) {
|
|
return 0;
|
|
}
|
|
static float fxMathPi( fxMathInfo_t *info ) {
|
|
return M_PI;
|
|
}
|
|
static float fxMathRed( fxMathInfo_t *info ) {
|
|
return info->run->color[0] * (1.0f/255);
|
|
}
|
|
static float fxMathGreen( fxMathInfo_t *info ) {
|
|
return info->run->color[1] * (1.0f/255);
|
|
}
|
|
static float fxMathBlue( fxMathInfo_t *info ) {
|
|
return info->run->color[2] * (1.0f/255);
|
|
}
|
|
static float fxMathAlpha( fxMathInfo_t *info ) {
|
|
return info->run->color[3] * (1.0f/255);
|
|
}
|
|
static float fxMathValue( fxMathInfo_t *info ) {
|
|
int ofs = info->data[0];
|
|
float *v = (float *)(((char *)info->run) + ofs);
|
|
info->data++;
|
|
return v[0];
|
|
}
|
|
static float fxMathLength( fxMathInfo_t *info ) {
|
|
int ofs = info->data[0];
|
|
const float *v = fxFloatSrc( info->run, ofs );
|
|
info->data++;
|
|
return VectorLength( v );
|
|
}
|
|
static float fxMathParent( fxMathInfo_t *info ) {
|
|
int ofs = info->data[0];
|
|
float *v = (float *)(((char *)info->run->parent) + ofs);
|
|
info->data++;
|
|
return v[0];
|
|
}
|
|
static float fxMathConst( fxMathInfo_t *info) {
|
|
float v1 = ((float *)info->data)[0];
|
|
info->data++;
|
|
return v1;
|
|
}
|
|
static float fxMathTime( fxMathInfo_t *info) {
|
|
return fx.time * 0.001f;
|
|
}
|
|
static float fxMathRand( fxMathInfo_t *info) {
|
|
fx.seed = fx.seed * 16091 + 1;
|
|
return (fx.seed & 0xffff) * (1.0f / 0x10000);
|
|
}
|
|
static float fxMathCrand( fxMathInfo_t *info) {
|
|
fx.seed = fx.seed * 16091 + 1;
|
|
return 1.0f - (2.0f / 0x10000) * (fx.seed & 0xffff);
|
|
}
|
|
static float fxMathCvar( fxMathInfo_t *info ) {
|
|
cvar_t *cvar;
|
|
memcpy( &cvar, info->data, sizeof( cvar_t *));
|
|
info->data += sizeof( cvar_t *) / sizeof( int );
|
|
return cvar->value;
|
|
}
|
|
|
|
static qboolean fxParseParentValue( const char *val, int *ofs ) {
|
|
if (0) {
|
|
} else if (!Q_stricmp( val, "parentorigin0")) {
|
|
*ofs = parentOfs( origin[0] );
|
|
} else if (!Q_stricmp( val, "parentorigin1")) {
|
|
*ofs = parentOfs( origin[1] );
|
|
} else if (!Q_stricmp( val, "parentorigin2")) {
|
|
*ofs = parentOfs( origin[2] );
|
|
} else if (!Q_stricmp( val, "parentvelocity0")) {
|
|
*ofs = parentOfs( velocity[0] );
|
|
} else if (!Q_stricmp( val, "parentvelocity1")) {
|
|
*ofs = parentOfs( velocity[1] );
|
|
} else if (!Q_stricmp( val, "parentvelocity2")) {
|
|
*ofs = parentOfs( velocity[2] );
|
|
} else if (!Q_stricmp( val, "parentangles0")) {
|
|
*ofs = parentOfs( angles[0] );
|
|
} else if (!Q_stricmp( val, "parentangles1")) {
|
|
*ofs = parentOfs( angles[1] );
|
|
} else if (!Q_stricmp( val, "parentangles2")) {
|
|
*ofs = parentOfs( angles[2] );
|
|
} else if (!Q_stricmp( val, "parentyaw")) {
|
|
*ofs = parentOfs( angles[YAW] );
|
|
} else if (!Q_stricmp( val, "parentpitch")) {
|
|
*ofs = parentOfs( angles[PITCH] );
|
|
} else if (!Q_stricmp( val, "parentroll")) {
|
|
*ofs = parentOfs( angles[ROLL] );
|
|
} else if (!Q_stricmp( val, "parentangle")) {
|
|
*ofs = parentOfs( angles[ROLL] );
|
|
} else if (!Q_stricmp( val, "parentsize")) {
|
|
*ofs = parentOfs( size );
|
|
} else {
|
|
return qfalse;
|
|
}
|
|
return qtrue;
|
|
}
|
|
|
|
|
|
#define PARSE_RUN_VECTOR_ENTRY( _VEC, _MASK, _NUM ) \
|
|
if (!Q_stricmp( val, #_VEC #_NUM)) { \
|
|
*ofs = runOfs( _VEC[_NUM] ); \
|
|
*mask = _MASK; \
|
|
}
|
|
|
|
|
|
#define PARSE_RUN_VECTOR( _VEC, _MASK ) \
|
|
PARSE_RUN_VECTOR_ENTRY( _VEC, _MASK, 0 ) \
|
|
else \
|
|
PARSE_RUN_VECTOR_ENTRY( _VEC, _MASK, 1 ) \
|
|
else \
|
|
PARSE_RUN_VECTOR_ENTRY( _VEC, _MASK, 2 )
|
|
|
|
|
|
static qboolean fxMatchFloat( const char *val, int *ofs, unsigned int *mask ) {
|
|
PARSE_RUN_VECTOR( origin, FX_MASK_ORIGIN )
|
|
else PARSE_RUN_VECTOR( velocity, FX_MASK_VELOCITY )
|
|
else PARSE_RUN_VECTOR( angles, FX_MASK_ANGLES )
|
|
else PARSE_RUN_VECTOR( dir, FX_MASK_DIR )
|
|
else PARSE_RUN_VECTOR( v0, FX_MASK_V0 )
|
|
else PARSE_RUN_VECTOR( v1, FX_MASK_V1 )
|
|
else PARSE_RUN_VECTOR( v2, FX_MASK_V2 )
|
|
else PARSE_RUN_VECTOR( v3, FX_MASK_V3 )
|
|
else if (!Q_stricmp( val, "rotate") || !Q_stricmp( val, "angle")) {
|
|
*ofs = runOfs( rotate );
|
|
*mask = FX_MASK_ROTATE;
|
|
} else if (!Q_stricmp( val, "size")) {
|
|
*ofs = runOfs( size );
|
|
*mask = FX_MASK_SIZE;
|
|
} else if (!Q_stricmp( val, "width")) {
|
|
*ofs = runOfs( width );
|
|
*mask = FX_MASK_WIDTH;
|
|
} else if (!Q_stricmp( val, "yaw")) {
|
|
*ofs = runOfs( angles[YAW] );
|
|
*mask = FX_MASK_ANGLES;
|
|
} else if (!Q_stricmp( val, "pitch")) {
|
|
*ofs = runOfs( angles[PITCH] );
|
|
*mask = FX_MASK_ANGLES;
|
|
} else if (!Q_stricmp( val, "roll")) {
|
|
*ofs = runOfs( angles[ROLL] );
|
|
*mask = FX_MASK_ANGLES;
|
|
} else if (!Q_stricmp( val, "t0")) {
|
|
*ofs = runOfs( t0 );
|
|
*mask = FX_MASK_T0;
|
|
} else if (!Q_stricmp( val, "t1")) {
|
|
*ofs = runOfs( t1 );
|
|
*mask = FX_MASK_T1;
|
|
} else if (!Q_stricmp( val, "t2")) {
|
|
*ofs = runOfs( t2 );
|
|
*mask = FX_MASK_T2;
|
|
} else if (!Q_stricmp( val, "t3")) {
|
|
*ofs = runOfs( t3 );
|
|
*mask = FX_MASK_T3;
|
|
} else {
|
|
return qfalse;
|
|
}
|
|
return qtrue;
|
|
}
|
|
|
|
static qboolean fxMatchVector( const char *val, int *ofs, unsigned int *mask ) {
|
|
if (!Q_stricmp( val, "origin")) {
|
|
*ofs = runOfs( origin );
|
|
*mask = FX_MASK_ORIGIN;
|
|
} else if (!Q_stricmp( val, "velocity")) {
|
|
*ofs = runOfs( velocity );
|
|
*mask = FX_MASK_VELOCITY;
|
|
} else if (!Q_stricmp( val, "angles")) {
|
|
*ofs = runOfs( angles );
|
|
*mask = FX_MASK_ANGLES;
|
|
} else if (!Q_stricmp( val, "dir")) {
|
|
*ofs = runOfs( dir );
|
|
*mask = FX_MASK_DIR;
|
|
} else if (!Q_stricmp( val, "v0")) {
|
|
*ofs = runOfs( v0 );
|
|
*mask = FX_MASK_V0;
|
|
} else if (!Q_stricmp( val, "v1")) {
|
|
*ofs = runOfs( v1 );
|
|
*mask = FX_MASK_V1;
|
|
} else if (!Q_stricmp( val, "v2")) {
|
|
*ofs = runOfs( v2 );
|
|
*mask = FX_MASK_V2;
|
|
} else if (!Q_stricmp( val, "v3")) {
|
|
*ofs = runOfs( v3 );
|
|
*mask = FX_MASK_V3;
|
|
} else if (!Q_stricmp( val, "parentOrigin" )) {
|
|
*ofs = parentOfs( origin ) | FX_VECTOR_PARENT;
|
|
*mask = FX_MASK_PARENT;
|
|
} else if (!Q_stricmp( val, "parentVelocity" )) {
|
|
*ofs = parentOfs( velocity ) | FX_VECTOR_PARENT;
|
|
*mask = FX_MASK_PARENT;
|
|
} else if (!Q_stricmp( val, "parentAngles" )) {
|
|
*ofs = parentOfs( angles ) | FX_VECTOR_PARENT;
|
|
*mask = FX_MASK_PARENT;
|
|
} else if (!Q_stricmp( val, "parentDir" )) {
|
|
*ofs = parentOfs( dir ) | FX_VECTOR_PARENT;
|
|
*mask = FX_MASK_PARENT;
|
|
} else {
|
|
return qfalse;
|
|
}
|
|
return qtrue;
|
|
}
|
|
|
|
static qboolean fxParseVectorOptional( fxParse_t *parse, int *ofs ) {
|
|
unsigned int mask;
|
|
|
|
if (!fxParseToken( parse, qfalse )) {
|
|
mask = parse->vectorMask;
|
|
} else if (!fxMatchVector( parse->token, ofs, &mask ) ) {
|
|
mask = parse->vectorMask;
|
|
if (!fxParseReverse( parse ))
|
|
return qfalse;
|
|
}
|
|
if ( mask & FX_MASK_PARENT ) {
|
|
return fxParseError( parse, "Can't write to a parent vector" );
|
|
}
|
|
fxParseWrite( parse, mask );
|
|
return qtrue;
|
|
}
|
|
|
|
static qboolean fxParseVectorRead( fxParse_t *parse, int *ofs ) {
|
|
unsigned int mask;
|
|
if (!fxParseToken( parse, qfalse )) {
|
|
return fxParseError( parse, "Expected read vector name" );
|
|
}
|
|
if ( !fxMatchVector( parse->token, ofs, &mask ) ) {
|
|
return fxParseError( parse, "Unknown read vector name %s", parse->token );
|
|
}
|
|
parse->vectorMask = mask;
|
|
fxParseRead( parse, mask );
|
|
return qtrue;
|
|
}
|
|
|
|
static qboolean fxParseVectorWrite( fxParse_t *parse, int *ofs ) {
|
|
unsigned int mask;
|
|
if (!fxParseToken( parse, qfalse )) {
|
|
return fxParseError( parse, "Expected write vector name" );
|
|
}
|
|
if ( !fxMatchVector( parse->token, ofs, &mask ) ) {
|
|
return fxParseError( parse, "Unknown write vector name %s", parse->token );
|
|
}
|
|
if ( mask & FX_MASK_PARENT ) {
|
|
return fxParseError( parse, "Can't write to a parent vector" );
|
|
}
|
|
fxParseWrite( parse, mask );
|
|
return qtrue;
|
|
}
|
|
|
|
typedef enum {
|
|
mathParseLine,
|
|
mathParseSingle,
|
|
mathParseMul,
|
|
} mathParseType_t;
|
|
|
|
typedef struct {
|
|
byte data[1024];
|
|
int used;
|
|
} fxMathOut_t;
|
|
|
|
|
|
static qboolean fxMathAppend( fxParse_t *parse, fxMathOut_t *out, const void *data, int size ) {
|
|
if ( size + out->used > sizeof( out->data ))
|
|
return fxParseError( parse, "Overflowing math parser" );
|
|
if ( !size || !data)
|
|
return fxParseError( parse, "math adding 0 sized data" );
|
|
memcpy( out->data + out->used, data, size );
|
|
out->used += size;
|
|
return qtrue;
|
|
}
|
|
|
|
static qboolean fxMathAppendOut( fxParse_t *parse, fxMathOut_t *out, const fxMathOut_t *add ) {
|
|
return fxMathAppend( parse, out, add->data, add->used );
|
|
}
|
|
|
|
static qboolean fxMathAppendInt( fxParse_t *parse, fxMathOut_t *out, int val ) {
|
|
return fxMathAppend( parse, out, &val, sizeof( int ));
|
|
}
|
|
|
|
static qboolean fxMathAppendHandler( fxParse_t *parse, fxMathOut_t *out, fxMathHandler handler ) {
|
|
//TODO for 64bit platforms use a relative 32bit offset
|
|
return fxMathAppend( parse, out, &handler, sizeof( handler ));
|
|
}
|
|
|
|
static qboolean fxMathParse( fxParse_t *parse, mathParseType_t parseType, char **line, const fxMathOut_t *in, fxMathOut_t *out );
|
|
|
|
static qboolean fxMathParseHandler( fxParse_t *parse, char **line, fxMathOut_t *out, fxMathHandler handler ) {
|
|
if (!fxMathAppendHandler( parse, out, handler))
|
|
return qfalse;
|
|
return fxMathParse( parse, mathParseSingle, line, 0, out );
|
|
}
|
|
|
|
|
|
static qboolean fxMathParse( fxParse_t *parse, mathParseType_t parseType, char **line, const fxMathOut_t *in, fxMathOut_t *out ) {
|
|
char buf[64];
|
|
int wordSize;
|
|
fxMathOut_t val;
|
|
fxMathHandler tempHandler;
|
|
|
|
wordSize = 0;
|
|
val.used = 0;
|
|
|
|
if ( parseType == mathParseLine && !in ) {
|
|
if (!fxMathParse( parse, mathParseMul, line, 0, &val ))
|
|
return qfalse;
|
|
return fxMathParse( parse, mathParseLine, line, &val, out );
|
|
}
|
|
|
|
while ( 1 ) {
|
|
char c = line[0][0];
|
|
|
|
if ( wordSize ) {
|
|
int i;
|
|
qboolean isNumber;
|
|
|
|
if ( !c || c == ')' || c == '(' || c == ' '
|
|
|| c == '-' || c =='+' || c == '*' || c == '/'
|
|
|| c == '!' || c == '<' || c == '>' || c == '=' ||
|
|
c == '&' || c == '|' ) {
|
|
/* Easier to just parse the value first incase of trouble */
|
|
buf[wordSize] = 0;
|
|
|
|
if ( val.used )
|
|
return fxParseError( parse, "math double value %s", buf );
|
|
|
|
isNumber = qtrue;
|
|
for ( i = 0; i < wordSize;i++) {
|
|
if (!(buf[i] == '.' || (buf[i] >= '0' && buf[i] <= '9'))) {
|
|
isNumber = qfalse;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( isNumber ) {
|
|
float value = atof( buf );
|
|
if (!fxMathAppendHandler( parse, &val, fxMathConst ))
|
|
return qfalse;
|
|
if (!fxMathAppend( parse, &val, &value, sizeof( value )))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( buf, "sin")) {
|
|
if (!fxMathParseHandler( parse, line, &val, &fxMathSin ))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( buf, "cos")) {
|
|
if (!fxMathParseHandler( parse, line, &val, &fxMathCos ))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( buf, "wave")) {
|
|
if (!fxMathParseHandler( parse, line, &val, &fxMathWave))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( buf, "sqrt")) {
|
|
if (!fxMathParseHandler( parse, line, &val, &fxMathSqrt ))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( buf, "ceil")) {
|
|
if (!fxMathParseHandler( parse, line, &val, &fxMathCeil ))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( buf, "floor")) {
|
|
if (!fxMathParseHandler( parse, line, &val, &fxMathFloor ))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( buf, "clip")) {
|
|
if (!fxMathParseHandler( parse, line, &val, &fxMathClip ))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( buf, "rand") || !Q_stricmp( buf, "rnd")) {
|
|
if (!fxMathAppendHandler( parse, &val, &fxMathRand))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( buf, "time")) {
|
|
if (!fxMathAppendHandler( parse, &val, &fxMathTime))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( buf, "crand")) {
|
|
if (!fxMathAppendHandler( parse, &val, &fxMathCrand))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( buf, "pi")) {
|
|
if (!fxMathAppendHandler( parse, &val, &fxMathPi))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( buf, "red")) {
|
|
if (!fxMathAppendHandler( parse, &val, &fxMathRed))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_COLOR);
|
|
} else if (!Q_stricmp( buf, "green")) {
|
|
if (!fxMathAppendHandler( parse, &val, &fxMathGreen))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_COLOR);
|
|
} else if (!Q_stricmp( buf, "blue")) {
|
|
if (!fxMathAppendHandler( parse, &val, &fxMathBlue))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_COLOR);
|
|
} else if (!Q_stricmp( buf, "alpha")) {
|
|
if (!fxMathAppendHandler( parse, &val, &fxMathAlpha))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_COLOR);
|
|
} else if (!Q_stricmp( buf, "life")) {
|
|
if ( !parse->emitter )
|
|
return fxParseError( parse, "Can't access life outside of emitter" );
|
|
if (!fxMathAppendHandler( parse, &val, &fxMathValue))
|
|
return qfalse;
|
|
if (!fxMathAppendInt( parse, &val, runOfs( life )))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( buf, "lerp")) {
|
|
if ( !parse->emitter )
|
|
return fxParseError( parse, "Can't access lerp outside of emitter" );
|
|
if (!fxMathAppendHandler( parse, &val, &fxMathValue))
|
|
return qfalse;
|
|
if (!fxMathAppendInt( parse, &val, runOfs( lerp )))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( buf, "loop")) {
|
|
if ( parse->loopDepth <= 0 )
|
|
return fxParseError( parse, "Can't access loop outside of a loop" );
|
|
if (!fxMathAppendHandler( parse, &val, &fxMathValue))
|
|
return qfalse;
|
|
if (!fxMathAppendInt( parse, &val, runOfs( loop )))
|
|
return qfalse;
|
|
} else {
|
|
int ofs;
|
|
unsigned int mask;
|
|
cvar_t *cv;
|
|
if (fxMatchFloat( buf, &ofs, &mask)) {
|
|
parse->readMask |= mask;
|
|
if (!fxMathAppendHandler( parse, &val, fxMathValue))
|
|
return qfalse;
|
|
if (!fxMathAppendInt( parse, &val, ofs ))
|
|
return qfalse;
|
|
} else if (fxMatchVector( buf, &ofs, &mask)) {
|
|
parse->readMask |= mask;
|
|
if (!fxMathAppendHandler( parse, &val, fxMathLength))
|
|
return qfalse;
|
|
if (!fxMathAppendInt( parse, &val, ofs ))
|
|
return qfalse;
|
|
} else if (fxParseParentValue( buf, &ofs)) {
|
|
parse->readMask |= FX_MASK_PARENT;
|
|
if (!fxMathAppendHandler( parse, &val, fxMathParent))
|
|
return qfalse;
|
|
if (!fxMathAppendInt( parse, &val, ofs ))
|
|
return qfalse;
|
|
} else if ( (cv = Cvar_FindVar( buf )) ) {
|
|
if (!fxMathAppendHandler( parse, &val, &fxMathCvar))
|
|
return qfalse;
|
|
if (!fxMathAppend( parse, &val, &cv, sizeof( cv )))
|
|
return qfalse;
|
|
} else {
|
|
return fxParseError( parse, "math unknown word %s", buf );
|
|
}
|
|
}
|
|
wordSize = 0;
|
|
} else {
|
|
if (wordSize >= sizeof( buf ) - 1)
|
|
return fxParseError( parse, "math too long word %s", buf );
|
|
buf[wordSize++] = c;
|
|
line[0]++;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
switch (c) {
|
|
case '+':
|
|
case '-':
|
|
if ( parseType != mathParseLine ) {
|
|
if ( val.used )
|
|
return fxMathAppendOut( parse, out, &val );
|
|
if ( c == '-' && !fxMathAppendHandler( parse, out, fxMathNeg ))
|
|
return qfalse;
|
|
line[0]++;
|
|
return fxMathParse( parse, mathParseMul, line, 0, out );
|
|
}
|
|
line[0]++;
|
|
if (!fxMathAppendHandler( parse, &val, c == '+' ? fxMathAdd : fxMathSub ))
|
|
return qfalse;
|
|
if (!fxMathAppendOut( parse, &val, in ))
|
|
return qfalse;
|
|
if (!fxMathParse( parse, mathParseMul, line, 0, &val ))
|
|
return qfalse;
|
|
return fxMathParse( parse, mathParseLine, line, &val, out );
|
|
case '!':
|
|
case '<':
|
|
case '>':
|
|
case '=':
|
|
case '&':
|
|
case '|':
|
|
if ( parseType != mathParseLine ) {
|
|
if (!val.used )
|
|
return fxParseError( parse, "Expected value before %c", c );
|
|
return fxMathAppendOut( parse, out, &val );
|
|
}
|
|
switch (c) {
|
|
case '&':
|
|
if ( line[0][1] == '&' )
|
|
line[0]++;
|
|
tempHandler = fxMathAND;
|
|
break;
|
|
case '|':
|
|
if ( line[0][1] == '|' )
|
|
line[0]++;
|
|
tempHandler = fxMathOR;
|
|
break;
|
|
case '<':
|
|
if ( line[0][1] == '=' ) {
|
|
line[0]++;
|
|
tempHandler = fxMathTSE;
|
|
} else {
|
|
tempHandler = fxMathTS;
|
|
}
|
|
break;
|
|
case '>':
|
|
if ( line[0][1] == '=' ) {
|
|
line[0]++;
|
|
tempHandler = fxMathTGE;
|
|
} else {
|
|
tempHandler = fxMathTG;
|
|
}
|
|
break;
|
|
case '!':
|
|
if ( line[0][1] == '=' ) {
|
|
line[0]++;
|
|
tempHandler = fxMathNotEqual;
|
|
} else {
|
|
tempHandler = fxMathTNE;
|
|
}
|
|
break;
|
|
case '=':
|
|
if ( line[0][1] == '=' ) {
|
|
line[0]++;
|
|
tempHandler = fxMathEqual;
|
|
} else {
|
|
tempHandler = fxMathTE;
|
|
}
|
|
break;
|
|
default:
|
|
return fxParseError( parse, "math unknown char '%c'", c );
|
|
}
|
|
line[0]++;
|
|
if (!fxMathAppendHandler( parse, out, tempHandler ))
|
|
return qfalse;
|
|
if (!fxMathAppendOut( parse, out, in ))
|
|
return qfalse;
|
|
return fxMathParse( parse, mathParseLine, line, 0, out );
|
|
case '*':
|
|
case '/':
|
|
if (!val.used) {
|
|
return fxParseError( parse, "math unexpected %c", c );
|
|
}
|
|
if ( parseType != mathParseMul )
|
|
return fxMathAppendOut( parse, out, &val );
|
|
line[0]++;
|
|
if (!fxMathAppendHandler( parse, out, c == '*' ? fxMathMul : fxMathDiv ))
|
|
return qfalse;
|
|
if (!fxMathAppendOut( parse, out, &val ))
|
|
return qfalse;
|
|
return fxMathParse( parse, mathParseMul, line, 0, out );
|
|
case '(':
|
|
if ( val.used || parseType == mathParseLine)
|
|
return fxParseError( parse, "math unexpected (" );
|
|
line[0]++;
|
|
if (!fxMathParse( parse, mathParseLine, line, 0, &val ))
|
|
return qfalse;
|
|
if (line[0][0] != ')' )
|
|
return fxParseError( parse, "math expected )" );
|
|
line[0]++;
|
|
continue;
|
|
case ')':
|
|
if ( parseType == mathParseLine ) {
|
|
if (!in || !in->used )
|
|
return fxParseError( parse, "math:unexpected )");
|
|
return fxMathAppendOut( parse, out, in );
|
|
}
|
|
return fxMathAppendOut( parse, out, &val );
|
|
case 0:
|
|
if ( parseType == mathParseLine ) {
|
|
if (!in )
|
|
return fxParseError( parse, "math empty line?" );
|
|
return fxMathAppendOut( parse, out, in );
|
|
}
|
|
if ( !val.used )
|
|
return fxParseError( parse, "math expected some value" );
|
|
return fxMathAppendOut( parse, out, &val );
|
|
case ' ':
|
|
line[0]++;
|
|
continue;
|
|
default:
|
|
if ( val.used )
|
|
return fxParseError( parse, "math unexpected char %c", c );
|
|
buf[wordSize++] = c;
|
|
line[0]++;
|
|
continue;
|
|
}
|
|
//Final break that only reaches when we end this parse
|
|
break;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
static qboolean fxParseMath( fxParse_t *parse, fxParseOut_t *out ) {
|
|
char buf[1024];
|
|
int i;
|
|
const char *p;
|
|
char *line;
|
|
fxMathOut_t mathOut;
|
|
|
|
p = parse->text;
|
|
parse->last = p;
|
|
i = 0;
|
|
|
|
for( ;; p++ ) {
|
|
switch (*p) {
|
|
case '\n':
|
|
case '\r':
|
|
case '{':
|
|
case 0:
|
|
break;
|
|
case '"':
|
|
continue;
|
|
default:
|
|
if ( i > sizeof( buf) - 2)
|
|
break;
|
|
buf[i++] = *p;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
parse->text = p;
|
|
buf[i] = 0;
|
|
|
|
line = buf;
|
|
mathOut.used = 0;
|
|
if (!fxMathParse( parse, mathParseLine, &line, 0, &mathOut ))
|
|
return qfalse;
|
|
if ( out->used + mathOut.used > out->size )
|
|
return fxParseError( parse, "Overflowing output buffer" );
|
|
memcpy( out->data + out->used, mathOut.data, mathOut.used );
|
|
out->used += mathOut.used;
|
|
return qtrue;
|
|
}
|
|
|
|
static qboolean fxParseStackName( fxParse_t *parse, int *ofs, int *count, unsigned int *mask ) {
|
|
|
|
const char *token = parse->token;
|
|
if (!fxParseWord( parse , "stack name" ))
|
|
return qfalse;
|
|
|
|
if ( fxMatchFloat( token, ofs, mask )) {
|
|
*count = 1;
|
|
return qtrue;
|
|
} else if ( fxMatchVector( token, ofs, mask )) {
|
|
if ( *mask & FX_MASK_PARENT )
|
|
return fxParseError( parse, "Can't access parent value %s", token );
|
|
*count = 3;
|
|
return qtrue;
|
|
} else if (!Q_stricmp( token, "color")) {
|
|
*ofs = runOfs( color );
|
|
*count = runSize( color );
|
|
*mask = FX_MASK_COLOR;
|
|
} else if (!Q_stricmp( token, "model")) {
|
|
*ofs = runOfs( model );
|
|
*count = runSize( model );
|
|
*mask = FX_MASK_MODEL;
|
|
} else if (!Q_stricmp( token, "shader")) {
|
|
*ofs = runOfs( shader );
|
|
*count = runSize( shader );
|
|
*mask = FX_MASK_SHADER;
|
|
} else {
|
|
return fxParseError( parse, "Unknown stack tag %s", token );
|
|
}
|
|
return qtrue;
|
|
}
|
|
|
|
static qboolean fxParseParentName( fxParse_t *parse, int *ofs, int *count ) {
|
|
const char *token = parse->token;
|
|
if (!fxParseWord( parse, "parentname" ))
|
|
return qfalse;
|
|
if (!Q_stricmp( token, "origin")) {
|
|
*ofs = parentOfs( origin );
|
|
*count = parentSize( origin );
|
|
} else if (!Q_stricmp( token, "velocity")) {
|
|
*ofs = parentOfs( velocity );
|
|
*count = parentSize( velocity );
|
|
} else if (!Q_stricmp( token, "dir")) {
|
|
*ofs = parentOfs( dir );
|
|
*count = parentSize( dir );
|
|
} else if (!Q_stricmp( token, "angles")) {
|
|
*ofs = parentOfs( angles );
|
|
*count = parentSize( angles );
|
|
} else if (!Q_stricmp( token, "shader")) {
|
|
*ofs = parentOfs( shader );
|
|
*count = parentSize( shader );
|
|
} else if (!Q_stricmp( token, "model")) {
|
|
*ofs = parentOfs( model );
|
|
*count = parentSize( model );
|
|
} else if (!Q_stricmp( token, "size")) {
|
|
*ofs = parentOfs( size );
|
|
*count = parentSize( size );
|
|
} else if (!Q_stricmp( token, "width")) {
|
|
*ofs = parentOfs( width );
|
|
*count = parentSize( width );
|
|
} else if (!Q_stricmp( token, "yaw")) {
|
|
*ofs = parentOfs( angles[YAW] );
|
|
*count = parentSize( angles[YAW] );
|
|
} else if (!Q_stricmp( token, "pitch")) {
|
|
*ofs = parentOfs( angles[PITCH] );
|
|
*count = parentSize( angles[PITCH] );
|
|
} else if (!Q_stricmp( token, "roll")) {
|
|
*ofs = parentOfs( angles[ROLL] );
|
|
*count = parentSize( angles[ROLL] );
|
|
} else if (!Q_stricmp( token, "angle")) {
|
|
*ofs = parentOfs( angles[ROLL] );
|
|
*count = parentSize( angles[ROLL] );
|
|
} else if (!Q_stricmp( token, "color")) {
|
|
*ofs = parentOfs( color );
|
|
*count = parentSize( color );
|
|
} else if (!Q_stricmp( token, "color2")) {
|
|
*ofs = parentOfs( color2 );
|
|
*count = parentSize( color2 );
|
|
} else {
|
|
return fxParseError( parse, "Unknown parent tag %s", token );
|
|
}
|
|
return qtrue;
|
|
}
|
|
|
|
static qboolean fxParseColorMathOut( fxParse_t *parse, fxParseOut_t *out, unsigned int index ) {
|
|
fxRunColorMath_t colorMath;
|
|
colorMath.index = index;
|
|
if (!fxParseOutCmd( parse, out, fxCmdColorMath, &colorMath, sizeof( colorMath )))
|
|
return qfalse;
|
|
if (!fxParseMath( parse, out ))
|
|
return qfalse;
|
|
fxParseWrite( parse, FX_MASK_COLOR );
|
|
return qtrue;
|
|
}
|
|
|
|
static qboolean fxParseBlock( fxParse_t *parse, fxParseOut_t *out );
|
|
|
|
static qboolean fxParseIfBlock( fxParse_t *parse, fxParseOut_t *out ) {
|
|
int *blockSize = (int *)(out->data + out->used);
|
|
/* Store an int to contain the block size */
|
|
if (!fxParseOut( parse, out, blockSize, sizeof( int )))
|
|
return qfalse;
|
|
blockSize[0] = out->used;
|
|
if (!fxParseBlock( parse, out ))
|
|
return qfalse;
|
|
/* Store the blocksize in front of the block */
|
|
blockSize[0] = out->used - blockSize[0];
|
|
return qtrue;
|
|
}
|
|
|
|
/* This is started up with a parsed token alread in the parse */
|
|
static qboolean fxParseBlock( fxParse_t *parse, fxParseOut_t *out ) {
|
|
byte temp[1024];
|
|
const char *token = parse->token;
|
|
|
|
if (!fxParseToken( parse, qtrue )) {
|
|
return fxParseError( parse, "missing opening paranthesis" );
|
|
}
|
|
if (Q_stricmp( token, "{" ))
|
|
return fxParseError( parse, "no opening paranthesis, got %s", token );
|
|
|
|
while ( 1 ) {
|
|
if (!fxParseToken( parse, qtrue )) {
|
|
return fxParseError( parse, "missing closing paranthesis" );
|
|
}
|
|
if ( parse->emitter ) {
|
|
fxRunEmitter_t *emitter = parse->emitter;
|
|
if (!Q_stricmp( token, "moveGravity" ) ) {
|
|
if (!fxParseFloat( parse, &emitter->gravity ))
|
|
return qfalse;
|
|
emitter->flags |= FX_EMIT_MOVE;
|
|
continue;
|
|
} else if (!Q_stricmp( token, "moveBounce" ) ) {
|
|
if (!fxParseFloat( parse, &emitter->gravity ))
|
|
return qfalse;
|
|
if (!fxParseFloat( parse, &emitter->bounceFactor ))
|
|
return qfalse;
|
|
emitter->flags |= FX_EMIT_MOVE | FX_EMIT_TRACE;
|
|
continue;
|
|
} else if (!Q_stricmp( token, "impactDeath" ) ) {
|
|
if (!fxParseFloat( parse, &emitter->gravity ))
|
|
return qfalse;
|
|
emitter->flags |= FX_EMIT_MOVE | FX_EMIT_TRACE | FX_EMIT_IMPACT;
|
|
continue;
|
|
} else if (!Q_stricmp( token, "sink" ) ) {
|
|
if (!fxParseFloat( parse, &emitter->sinkDelay ))
|
|
return qfalse;
|
|
if (!fxParseFloat( parse, &emitter->sinkDepth ))
|
|
return qfalse;
|
|
//Scale the sinkdepth up for easy multiplying later
|
|
emitter->sinkDepth *= 1.0f / ( 1.0f - emitter->sinkDelay );
|
|
emitter->flags |= FX_EMIT_MOVE | FX_EMIT_SINK;
|
|
continue;
|
|
} else if (!Q_stricmp( token, "impact" ) ) {
|
|
fxParseOut_t impactOut;
|
|
fxRunSkip_t *skip = (fxRunSkip_t *)temp;
|
|
|
|
if (!fxParseFloat( parse, &emitter->impactSpeed ))
|
|
return qfalse;
|
|
|
|
emitter->impactSpeed *= emitter->impactSpeed;
|
|
emitter->flags |= FX_EMIT_MOVE | FX_EMIT_TRACE;
|
|
|
|
impactOut.data = temp;
|
|
impactOut.size = sizeof( temp );
|
|
impactOut.used = sizeof( fxRunSkip_t );
|
|
if (!fxParseBlock( parse, &impactOut ))
|
|
return qfalse;
|
|
skip->size = impactOut.used;
|
|
|
|
emitter->impactRun = out->used + sizeof( void *) + sizeof( fxRunSkip_t );
|
|
if (!fxParseOutCmd( parse, out, fxCmdSkip, skip, skip->size ))
|
|
return qfalse;
|
|
continue;
|
|
} else if (!Q_stricmp( token, "death" ) ) {
|
|
fxParseOut_t deathOut;
|
|
fxRunSkip_t *skip = (fxRunSkip_t *)temp;
|
|
|
|
deathOut.data = temp;
|
|
deathOut.size = sizeof( temp );
|
|
deathOut.used = sizeof( fxRunSkip_t );
|
|
if (!fxParseBlock( parse, &deathOut ))
|
|
return qfalse;
|
|
skip->size = deathOut.used;
|
|
|
|
emitter->deathRun = out->used + sizeof( void *) + sizeof( fxRunSkip_t );
|
|
if (!fxParseOutCmd( parse, out, fxCmdSkip, skip, skip->size ))
|
|
return qfalse;
|
|
continue;
|
|
}
|
|
}
|
|
if (0) {
|
|
} else if (!Q_stricmp( token, "shaderClear" ) ) {
|
|
fxRunShader_t shader;
|
|
shader.shader = 0;
|
|
if (!fxParseOutCmd( parse, out, fxCmdShader, &shader, sizeof( shader )))
|
|
return qfalse;
|
|
fxParseWrite( parse, FX_MASK_SHADER );
|
|
} else if (!Q_stricmp( token, "shader" ) ) {
|
|
fxRunShader_t shader;
|
|
if (!fxParseWord( parse, "shadername" ))
|
|
return qfalse;
|
|
if (!fxMatchShader( parse, &shader.shader ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdShader, &shader, sizeof( shader )))
|
|
return qfalse;
|
|
fxParseWrite( parse, FX_MASK_SHADER );
|
|
} else if (!Q_stricmp( token, "model" ) ) {
|
|
fxRunModel_t model;
|
|
if (!fxParseWord( parse, "modelname" ))
|
|
return qfalse;
|
|
if (!fxMatchModel( parse, &model.model ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdModel, &model, sizeof( model )))
|
|
return qfalse;
|
|
fxParseWrite( parse, FX_MASK_MODEL );
|
|
} else if (!Q_stricmp( token, "sound" ) ) {
|
|
fxRunSound_t sound;
|
|
if (!fxParseWord( parse, "soundname" ))
|
|
return qfalse;
|
|
if (!fxMatchSound( parse, &sound.handle ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdSound, &sound, sizeof( sound )))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_ORIGIN );
|
|
} else if (!Q_stricmp( token, "shaderList" ) ) {
|
|
fxRunShaderList_t *shaderList = (fxRunShaderList_t *)&temp;
|
|
fxParseOut_t shaderListOut;
|
|
|
|
shaderListOut.data = temp;
|
|
shaderListOut.size = sizeof( temp );
|
|
shaderListOut.used = sizeof( fxRunShaderList_t );
|
|
if (!fxParseMath( parse, &shaderListOut ))
|
|
return qfalse;
|
|
if (!fxParseList( parse, "shaderList", fxMatchShader, sizeof (qhandle_t ),
|
|
temp + shaderListOut.used,
|
|
( shaderListOut.size - shaderListOut.used ) / sizeof (qhandle_t ),
|
|
&shaderList->count ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdShaderList, temp, shaderListOut.used + shaderList->count * sizeof( qhandle_t ) ))
|
|
return qfalse;
|
|
fxParseWrite( parse, FX_MASK_SHADER );
|
|
} else if (!Q_stricmp( token, "modelList" ) ) {
|
|
fxRunModelList_t *modelList = (fxRunModelList_t *)&temp;
|
|
fxParseOut_t modelListOut;
|
|
|
|
modelListOut.data = temp;
|
|
modelListOut.size = sizeof( temp );
|
|
modelListOut.used = sizeof( fxRunModelList_t );
|
|
if (!fxParseMath( parse, &modelListOut ))
|
|
return qfalse;
|
|
if (!fxParseList( parse, "modelList", fxMatchModel, sizeof (qhandle_t ),
|
|
temp + modelListOut.used,
|
|
( modelListOut.size - modelListOut.used ) / sizeof (qhandle_t ),
|
|
&modelList->count ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdModelList, temp, modelListOut.used + modelList->count * sizeof( qhandle_t ) ))
|
|
return qfalse;
|
|
fxParseWrite( parse, FX_MASK_MODEL );
|
|
} else if (!Q_stricmp( token, "colorList" ) ) {
|
|
fxRunColorList_t *colorList = (fxRunColorList_t *)&temp;
|
|
fxParseOut_t colorListOut;
|
|
|
|
colorListOut.data = temp;
|
|
colorListOut.size = sizeof( temp );
|
|
colorListOut.used = sizeof( fxRunColorList_t );
|
|
if (!fxParseMath( parse, &colorListOut ))
|
|
return qfalse;
|
|
if (!fxParseList( parse, "colorList", fxMatchColor, sizeof( color4ub_t ),
|
|
temp + colorListOut.used,
|
|
( colorListOut.size - colorListOut.used ) / sizeof( color4ub_t ),
|
|
&colorList->count ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdColorList, temp, colorListOut.used + colorList->count * sizeof( color4ub_t ) ))
|
|
return qfalse;
|
|
fxParseWrite( parse, FX_MASK_COLOR );
|
|
} else if (!Q_stricmp( token, "colorBlend" ) ) {
|
|
fxRunColorBlend_t *colorBlend = (fxRunColorBlend_t *)&temp;
|
|
fxParseOut_t colorBlendOut;
|
|
|
|
colorBlendOut.data = temp;
|
|
colorBlendOut.size = sizeof( temp );
|
|
colorBlendOut.used = sizeof( fxRunColorBlend_t );
|
|
if (!fxParseMath( parse, &colorBlendOut ))
|
|
return qfalse;
|
|
if (!fxParseList( parse, "colorBlend", fxMatchColor, sizeof( color4ub_t ),
|
|
temp + colorBlendOut.used,
|
|
( colorBlendOut.size - colorBlendOut.used ) / sizeof( color4ub_t ),
|
|
&colorBlend->count ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdColorBlend, temp, colorBlendOut.used + colorBlend->count * sizeof( color4ub_t ) ))
|
|
return qfalse;
|
|
fxParseWrite( parse, FX_MASK_COLOR );
|
|
} else if (!Q_stricmp( token, "soundList" ) ) {
|
|
fxRunSoundList_t *soundList = (fxRunSoundList_t *)&temp;
|
|
if (!fxParseList( parse, "SoundList", fxMatchSound, sizeof( sfxHandle_t ),
|
|
soundList + 1,
|
|
(sizeof(temp) - sizeof( *soundList)) / sizeof( sfxHandle_t ),
|
|
&soundList->count ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdSoundList, temp, sizeof(soundList) + soundList->count * sizeof( sfxHandle_t ) ))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_ORIGIN );
|
|
} else if (!Q_stricmp( token, "loopSound" ) ) {
|
|
fxRunLoopSound_t loopSound;
|
|
if (!fxParseWord( parse, "Soundname" )) {
|
|
return qfalse;
|
|
}
|
|
loopSound.handle = S_RegisterSound( token, qfalse );
|
|
if (!fxParseOutCmd( parse, out, fxCmdLoopSound, &loopSound, sizeof( loopSound )))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_ORIGIN | FX_MASK_VELOCITY );
|
|
} else if (!Q_stricmp( token, "Sprite" ) ) {
|
|
fxRunRender_t runRender;
|
|
if (!fxParseRenderFX( parse, &runRender.renderfx ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdSprite, &runRender, sizeof( runRender )))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_ORIGIN | FX_MASK_COLOR | FX_MASK_ROTATE | FX_MASK_SHADER | FX_MASK_SIZE );
|
|
} else if (!Q_stricmp( token, "Spark" ) ) {
|
|
fxRunRender_t runRender;
|
|
if (!fxParseRenderFX( parse, &runRender.renderfx ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdSpark, &runRender, sizeof( runRender )))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_ORIGIN | FX_MASK_VELOCITY | FX_MASK_COLOR | FX_MASK_SHADER | FX_MASK_SIZE | FX_MASK_WIDTH );
|
|
} else if (!Q_stricmp( token, "Quad" ) ) {
|
|
fxRunRender_t runRender;
|
|
if (!fxParseRenderFX( parse, &runRender.renderfx ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdQuad, &runRender, sizeof( runRender )))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_ORIGIN | FX_MASK_COLOR | FX_MASK_ROTATE | FX_MASK_SHADER | FX_MASK_SIZE | FX_MASK_DIR );
|
|
} else if (!Q_stricmp( token, "Beam" ) ) {
|
|
fxRunRender_t runRender;
|
|
if (!fxParseRenderFX( parse, &runRender.renderfx ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdBeam, &runRender, sizeof( runRender )))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_ORIGIN | FX_MASK_COLOR | FX_MASK_ROTATE | FX_MASK_SHADER | FX_MASK_SIZE | FX_MASK_DIR );
|
|
} else if (!Q_stricmp( token, "Rings" ) ) {
|
|
fxRunRender_t runRender;
|
|
if (!fxParseRenderFX( parse, &runRender.renderfx ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdRings, &runRender, sizeof( runRender )))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_ORIGIN | FX_MASK_COLOR | FX_MASK_ROTATE | FX_MASK_SHADER | FX_MASK_SIZE | FX_MASK_WIDTH | FX_MASK_DIR );
|
|
} else if (!Q_stricmp( token, "anglesModel" ) ) {
|
|
fxRunRender_t runRender;
|
|
if (!fxParseRenderFX( parse, &runRender.renderfx ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdAnglesModel, &runRender, sizeof( runRender )))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_ORIGIN | FX_MASK_COLOR | FX_MASK_SHADER | FX_MASK_SIZE | FX_MASK_ANGLES | FX_MASK_MODEL );
|
|
} else if (!Q_stricmp( token, "axisModel" ) ) {
|
|
fxRunRender_t runRender;
|
|
if (!fxParseRenderFX( parse, &runRender.renderfx ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdAxisModel, &runRender, sizeof( runRender )))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_ORIGIN | FX_MASK_COLOR | FX_MASK_SHADER | FX_MASK_SIZE | FX_MASK_AXIS | FX_MASK_MODEL );
|
|
} else if (!Q_stricmp( token, "dirModel" ) ) {
|
|
fxRunRender_t runRender;
|
|
if (!fxParseRenderFX( parse, &runRender.renderfx ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdDirModel, &runRender, sizeof( runRender )))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_ORIGIN | FX_MASK_COLOR | FX_MASK_SHADER | FX_MASK_SIZE | FX_MASK_DIR | FX_MASK_MODEL | FX_MASK_ROTATE );
|
|
} else if (!Q_stricmp( token, "Light" ) ) {
|
|
if (!fxParseOutCmd( parse, out, fxCmdLight, 0, 0 ))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_ORIGIN | FX_MASK_COLOR | FX_MASK_SIZE );
|
|
} else if (!Q_stricmp( token, "Decal" ) ) {
|
|
fxRunDecal_t decal;
|
|
decal.flags = 0;
|
|
decal.life = 10000;
|
|
|
|
while (fxParseToken( parse, qfalse ) ) {
|
|
if (!Q_stricmp(token, "temp")) {
|
|
decal.flags |= DECAL_TEMP;
|
|
} else if (!Q_stricmp(token, "alpha")) {
|
|
decal.flags |= DECAL_ALPHA;
|
|
} else if (!Q_stricmp(token, "energy")) {
|
|
decal.flags |= DECAL_ENERGY;
|
|
//Last one has to be a float life
|
|
} else {
|
|
if (!fxMatchValidFloat( parse ))
|
|
return qfalse;
|
|
decal.life = atof( token ) * 1000;
|
|
}
|
|
}
|
|
if ( decal.life <= 0 ) {
|
|
decal.life = 10000;
|
|
}
|
|
if (!fxParseOutCmd( parse, out, fxCmdDecal, &decal, sizeof( decal )))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_ORIGIN | FX_MASK_COLOR | FX_MASK_SIZE | FX_MASK_DIR );
|
|
} else if (!Q_stricmp( token, "Trace" ) ) {
|
|
if (!fxParseOutCmd( parse, out, fxCmdTrace, 0, 0))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_ORIGIN | FX_MASK_DIR );
|
|
fxParseWrite( parse, FX_MASK_ORIGIN | FX_MASK_DIR );
|
|
} else if (!Q_stricmp( token, "vibrate" ) ) {
|
|
fxRunVibrate_t vibrate;
|
|
if (!fxParseFloat( parse, &vibrate.strength ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdVibrate, &vibrate, sizeof( vibrate )))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_ORIGIN );
|
|
} else if (!Q_stricmp( token, "alphaFade" ) ) {
|
|
fxRunAlphaFade_t alphaFade;
|
|
if (!fxParseFloat( parse, &alphaFade.delay ))
|
|
return qfalse;
|
|
if ( alphaFade.delay < 0 || alphaFade.delay >= 1.0f )
|
|
return fxParseError( parse, "Fade value %f illegal", alphaFade.delay );
|
|
alphaFade.scale = -255.0f / (1.0f - alphaFade.delay );
|
|
if (!fxParseOutCmd( parse, out, fxCmdAlphaFade, &alphaFade, sizeof( alphaFade )))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_COLOR );
|
|
fxParseWrite( parse, FX_MASK_COLOR );
|
|
} else if (!Q_stricmp( token, "color" ) ) {
|
|
fxRunColor_t color;
|
|
if (!fxParseColor( parse, color.value ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdColor, &color, sizeof( color )))
|
|
return qfalse;
|
|
fxParseWrite( parse, FX_MASK_COLOR );
|
|
} else if (!Q_stricmp( token, "colorFade" ) ) {
|
|
fxRunColorFade_t colorFade;
|
|
if (!fxParseFloat( parse, &colorFade.delay ))
|
|
return qfalse;
|
|
if ( colorFade.delay < 0 || colorFade.delay >= 1.0f )
|
|
return fxParseError( parse, "Fade value %f illegal", colorFade.delay );
|
|
colorFade.scale = -255.0f / (1.0f - colorFade.delay );
|
|
if (!fxParseOutCmd( parse, out, fxCmdColorFade, &colorFade, sizeof( colorFade )))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_COLOR );
|
|
fxParseWrite( parse, FX_MASK_COLOR );
|
|
} else if (!Q_stricmp( token, "colorScale" ) ) {
|
|
if (!fxParseOutCmd( parse, out, fxCmdColorScale, 0, 0 ))
|
|
return qfalse;
|
|
if (!fxParseMath( parse, out ))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_COLOR );
|
|
fxParseWrite( parse, FX_MASK_COLOR );
|
|
} else if (!Q_stricmp( token, "colorHue" ) ) {
|
|
if (!fxParseOutCmd( parse, out, fxCmdColorHue, 0, 0))
|
|
return qfalse;
|
|
if (!fxParseMath( parse, out ))
|
|
return qfalse;
|
|
fxParseWrite( parse, FX_MASK_COLOR );
|
|
} else if (!Q_stricmp( token, "red" ) ) {
|
|
if (!fxParseColorMathOut( parse, out, 0 ))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "green" ) ) {
|
|
if (!fxParseColorMathOut( parse, out, 1 ))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "blue" ) ) {
|
|
if (!fxParseColorMathOut( parse, out, 2 ))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "alpha" ) ) {
|
|
if (!fxParseColorMathOut( parse, out, 3 ))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "wobble" ) ) {
|
|
fxRunWobble_t wobble;
|
|
|
|
if (!fxParseVectorRead( parse, &wobble.src ))
|
|
return qfalse;
|
|
if (!fxParseVectorWrite( parse, &wobble.dst ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdWobble, &wobble, sizeof( wobble )))
|
|
return qfalse;
|
|
if (!fxParseMath( parse, out ))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "makeAngles" ) ) {
|
|
fxRunMakeAngles_t makeAngles;
|
|
|
|
if (!fxParseVectorRead( parse, &makeAngles.src ))
|
|
return qfalse;
|
|
makeAngles.dst = makeAngles.src;
|
|
if (!fxParseVectorOptional( parse, &makeAngles.dst ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdMakeAngles, &makeAngles, sizeof( makeAngles )))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "inverse" ) ) {
|
|
fxRunInverse_t inverse;
|
|
|
|
if (!fxParseVectorRead( parse, &inverse.src ))
|
|
return qfalse;
|
|
inverse.dst = inverse.src;
|
|
fxParseVectorOptional( parse, &inverse.dst );
|
|
if (!fxParseOutCmd( parse, out, fxCmdInverse, &inverse, sizeof( inverse )))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "normalize" ) ) {
|
|
fxRunNormalize_t normalize;
|
|
|
|
if (!fxParseVectorRead( parse, &normalize.src ))
|
|
return qfalse;
|
|
normalize.dst = normalize.src;
|
|
fxParseVectorOptional( parse, &normalize.dst );
|
|
if (!fxParseOutCmd( parse, out, fxCmdNormalize, &normalize, sizeof( normalize )))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "perpendicular" ) ) {
|
|
fxRunPerpendicular_t perpendicular;
|
|
|
|
if (!fxParseVectorRead( parse, &perpendicular.src ))
|
|
return qfalse;
|
|
perpendicular.dst = perpendicular.src;
|
|
fxParseVectorOptional( parse, &perpendicular.dst );
|
|
if (!fxParseOutCmd( parse, out, fxCmdPerpendicular, &perpendicular, sizeof( perpendicular )))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "clear" ) ) {
|
|
fxRunClear_t clear;
|
|
if (!fxParseVectorWrite( parse, &clear.dst ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdClear, &clear, sizeof( clear )))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "copy" ) ) {
|
|
fxRunCopy_t copy;
|
|
if (!fxParseVectorRead( parse, ©.src ))
|
|
return qfalse;
|
|
if (!fxParseVectorWrite( parse, ©.dst ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdCopy, ©, sizeof( copy )))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "scale" ) ) {
|
|
fxRunScale_t scale;
|
|
|
|
if (!fxParseVectorRead( parse, &scale.src ))
|
|
return qfalse;
|
|
// scale.dst = scale.src;
|
|
if (!fxParseVectorWrite( parse, &scale.dst ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdScale, &scale, sizeof( scale )))
|
|
return qfalse;
|
|
if (!fxParseMath( parse, out ))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "add" ) ) {
|
|
fxRunAdd_t add;
|
|
|
|
if (!fxParseVectorRead( parse, &add.src1 ))
|
|
return qfalse;
|
|
if (!fxParseVectorRead( parse, &add.src2 ))
|
|
return qfalse;
|
|
if (!fxParseVectorWrite( parse, &add.dst ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdAdd, &add, sizeof( add )))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "sub" ) ) {
|
|
fxRunSub_t sub;
|
|
|
|
if (!fxParseVectorRead( parse, &sub.src1 ))
|
|
return qfalse;
|
|
if (!fxParseVectorRead( parse, &sub.src2 ))
|
|
return qfalse;
|
|
if (!fxParseVectorWrite( parse, &sub.dst ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdSub, &sub, sizeof( sub )))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "addScale" ) ) {
|
|
fxRunAddScale_t addScale;
|
|
|
|
if (!fxParseVectorRead( parse, &addScale.src ))
|
|
return qfalse;
|
|
if (!fxParseVectorRead( parse, &addScale.scale ))
|
|
return qfalse;
|
|
if (!fxParseVectorWrite( parse, &addScale.dst ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdAddScale, &addScale, sizeof( addScale )))
|
|
return qfalse;
|
|
if (!fxParseMath( parse, out ))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "subScale" ) ) {
|
|
fxRunSubScale_t subScale;
|
|
|
|
if (!fxParseVectorRead( parse, &subScale.src ))
|
|
return qfalse;
|
|
if (!fxParseVectorRead( parse, &subScale.scale ))
|
|
return qfalse;
|
|
if (!fxParseVectorWrite( parse, &subScale.dst ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdSubScale, &subScale, sizeof( subScale )))
|
|
return qfalse;
|
|
if (!fxParseMath( parse, out ))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "rotateAround" ) ) {
|
|
fxRunRotateAround_t rotateAround;
|
|
|
|
if (!fxParseVectorRead( parse, &rotateAround.src ))
|
|
return qfalse;
|
|
if (!fxParseVectorRead( parse, &rotateAround.dir ))
|
|
return qfalse;
|
|
if (!fxParseVectorWrite( parse, &rotateAround.dst ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdRotateAround, &rotateAround, sizeof( rotateAround )))
|
|
return qfalse;
|
|
if (!fxParseMath( parse, out ))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "random" ) ) {
|
|
fxRunRandom_t random;
|
|
if (!fxParseVectorWrite( parse, &random.dst ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdRandom, &random, sizeof( random )))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "kill" ) ) {
|
|
if (!fxParseOutCmd( parse, out, fxCmdKill, 0, 0 ))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "emitter" ) || !Q_stricmp( token, "entity" )) {
|
|
fxRunEmitter_t *oldEmitter;
|
|
fxRunEmitter_t *newEmitter = (fxRunEmitter_t *)temp;
|
|
fxParseOut_t emitterOut;
|
|
unsigned oldReadMask, oldWriteMask, newReadMask;
|
|
int oldLoopDepth;
|
|
|
|
memset( newEmitter, 0, sizeof( fxRunEmitter_t ) );
|
|
|
|
emitterOut.data = temp;
|
|
emitterOut.size = sizeof( temp );
|
|
emitterOut.used = sizeof( fxRunEmitter_t );
|
|
if (!fxParseMath( parse, &emitterOut ))
|
|
return qfalse;
|
|
newEmitter->emitRun = emitterOut.used;
|
|
oldEmitter = parse->emitter;
|
|
oldReadMask = parse->readMask;
|
|
oldWriteMask = parse->writeMask;
|
|
oldLoopDepth = parse->loopDepth;
|
|
|
|
parse->emitter = newEmitter;
|
|
parse->loopDepth = 0;
|
|
parse->readMask = 0;
|
|
parse->writeMask = 0;
|
|
if (!fxParseBlock( parse, &emitterOut ))
|
|
return qfalse;
|
|
//Create copy table and extra size requirements
|
|
if (!fxParseCreateMask( parse, newEmitter->mask, sizeof( newEmitter->mask ), &newEmitter->allocSize ))
|
|
return qfalse;
|
|
newEmitter->allocSize += sizeof( fxEntity_t );
|
|
newEmitter->size = emitterOut.used;
|
|
//Emitters only read, restore the rest of the previous version
|
|
newReadMask = parse->readMask;
|
|
parse->readMask = oldReadMask;
|
|
parse->writeMask = oldWriteMask;
|
|
parse->loopDepth = oldLoopDepth;
|
|
//forward readmask to caller
|
|
fxParseRead( parse, newReadMask );
|
|
parse->emitter = oldEmitter;
|
|
if (!fxParseOutCmd( parse, out, fxCmdEmitter, newEmitter, emitterOut.used ))
|
|
return qfalse;
|
|
|
|
} else if (!Q_stricmp( token, "interval" ) ) {
|
|
fxRunInterval_t *Interval = (fxRunInterval_t *)temp;
|
|
fxParseOut_t intervalOut;
|
|
|
|
parse->loopDepth++;
|
|
intervalOut.data = temp;
|
|
intervalOut.size = sizeof( temp );
|
|
intervalOut.used = sizeof( fxRunInterval_t );
|
|
if (!fxParseMath( parse, &intervalOut ))
|
|
return qfalse;
|
|
if (!fxParseBlock( parse, &intervalOut ))
|
|
return qfalse;
|
|
Interval->size = intervalOut.used;
|
|
if (!fxParseOutCmd( parse, out, fxCmdInterval, Interval, intervalOut.used ))
|
|
return qfalse;
|
|
parse->loopDepth--;
|
|
|
|
} else if (!Q_stricmp( token, "repeat" ) ) {
|
|
fxRunRepeat_t *repeat = (fxRunRepeat_t *)temp;
|
|
fxParseOut_t repeatOut;
|
|
|
|
repeatOut.data = temp;
|
|
repeatOut.size = sizeof( temp );
|
|
repeatOut.used = sizeof( fxRunRepeat_t );
|
|
parse->loopDepth++;
|
|
if (!fxParseMath( parse, &repeatOut ))
|
|
return qfalse;
|
|
if (!fxParseBlock( parse, &repeatOut ))
|
|
return qfalse;
|
|
repeat->size = repeatOut.used;
|
|
if (!fxParseOutCmd( parse, out, fxCmdRepeat, repeat, repeatOut.used ))
|
|
return qfalse;
|
|
parse->loopDepth--;
|
|
} else if (!Q_stricmp( token, "distance" ) ) {
|
|
fxRunDistance_t *distance = (fxRunDistance_t *)temp;
|
|
fxParseOut_t distanceOut;
|
|
|
|
parse->loopDepth++;
|
|
distanceOut.data = temp;
|
|
distanceOut.size = sizeof( temp );
|
|
distanceOut.used = sizeof( fxRunDistance_t );
|
|
if (!fxParseMath( parse, &distanceOut ))
|
|
return qfalse;
|
|
if (!fxParseBlock( parse, &distanceOut ))
|
|
return qfalse;
|
|
distance->size = distanceOut.used;
|
|
if (!fxParseOutCmd( parse, out, fxCmdDistance, distance, distanceOut.used ))
|
|
return qfalse;
|
|
parse->loopDepth--;
|
|
fxParseRead( parse, FX_MASK_ORIGIN );
|
|
} else if (!Q_stricmp( token, "if" ) ) {
|
|
fxRunIf_t *doIf = (fxRunIf_t *)temp;
|
|
fxParseOut_t ifOut;
|
|
|
|
doIf->testCount = 1;
|
|
doIf->elseStep = 0;
|
|
ifOut.data = temp;
|
|
ifOut.size = sizeof( temp );
|
|
ifOut.used = sizeof( fxRunIf_t );
|
|
if (!fxParseMath( parse, &ifOut ))
|
|
return qfalse;
|
|
if (!fxParseIfBlock( parse, &ifOut ))
|
|
return qfalse;
|
|
while (1) {
|
|
if (!fxParseToken( parse, qfalse ))
|
|
break;
|
|
if (!Q_stricmp( token, "elif")) {
|
|
doIf->testCount += 1;
|
|
if (!fxParseMath( parse, &ifOut ))
|
|
return qfalse;
|
|
if (!fxParseIfBlock( parse, &ifOut ))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "else" )) {
|
|
doIf->elseStep = qtrue;
|
|
if (!fxParseBlock( parse, &ifOut ))
|
|
return qfalse;
|
|
break;
|
|
}
|
|
}
|
|
/* Calculate the total size for the final exit */
|
|
doIf->size = ifOut.used;
|
|
if (!fxParseOutCmd( parse, out, fxCmdIf, doIf, ifOut.used ))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "once" ) ) {
|
|
fxRunOnce_t *once = (fxRunOnce_t *)temp;
|
|
fxParseOut_t onceOut;
|
|
|
|
onceOut.data = temp;
|
|
onceOut.size = sizeof( temp );
|
|
onceOut.used = sizeof( fxRunOnce_t );
|
|
if (!fxParseBlock( parse, &onceOut ))
|
|
return qfalse;
|
|
once->size = onceOut.used;
|
|
if (!fxParseOutCmd( parse, out, fxCmdOnce, once, onceOut.used ))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "pushParent" ) ) {
|
|
fxRunPushParent_t push;
|
|
if (!fxParseParentName( parse, &push.offset, &push.count ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdPushParent, &push, sizeof( push )))
|
|
return qfalse;
|
|
fxParseRead( parse, FX_MASK_PARENT );
|
|
} else if (!Q_stricmp( token, "push" ) ) {
|
|
int mask;
|
|
fxRunPush_t push;
|
|
if (!fxParseStackName( parse, &push.offset, &push.count, &mask ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdPush, &push, sizeof( push )))
|
|
return qfalse;
|
|
fxParseRead( parse, mask );
|
|
} else if (!Q_stricmp( token, "pop" ) ) {
|
|
int mask;
|
|
fxRunPop_t pop;
|
|
|
|
if (!fxParseStackName( parse, &pop.offset, &pop.count, &mask ))
|
|
return qfalse;
|
|
if (!fxParseOutCmd( parse, out, fxCmdPop, &pop, sizeof( pop )))
|
|
return qfalse;
|
|
fxParseWrite( parse, mask );
|
|
} else if (!Q_stricmp( token, "script" ) ) {
|
|
fxRunScript_t script;
|
|
fxScript_t *loadScript;
|
|
|
|
if (!fxParseWord( parse, "scriptname" ))
|
|
return qfalse;
|
|
loadScript = fxFindScript( token );
|
|
if (!loadScript )
|
|
return fxParseError( parse, "Can't load script %s", token );
|
|
script.data = loadScript->data;
|
|
fxParseRead( parse, loadScript->readMask );
|
|
if (!fxParseOutCmd( parse, out, fxCmdScript, &script, sizeof( script )))
|
|
return qfalse;
|
|
} else if (!Q_stricmp( token, "}" ) ) {
|
|
if (!fxParseOutCmd( parse, out, 0, 0, 0 ))
|
|
return qfalse;
|
|
return qtrue;
|
|
} else {
|
|
unsigned int mask;
|
|
fxRunValue_t value;
|
|
if (fxMatchFloat( token, &value.dst, &mask)) {
|
|
if (!fxParseOutCmd( parse, out, fxCmdValue, &value, sizeof( value ) ))
|
|
return qfalse;
|
|
if (!fxParseMath( parse, out ))
|
|
return qfalse;
|
|
fxParseWrite( parse, mask );
|
|
} else {
|
|
return fxParseError( parse, "Unknown token %s", parse->token );
|
|
}
|
|
}
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
static fxScript_t *fxParseScript( fxHashEntry_t *entry ) {
|
|
fxParse_t parse;
|
|
fxParseOut_t parseOut;
|
|
fxScript_t *script;
|
|
char parseData[ 32*1024];
|
|
byte *allocBuf;
|
|
int allocSize;
|
|
|
|
/* Check if this one has already been loaded */
|
|
if ( entry->script )
|
|
return entry->script;
|
|
|
|
if ( fx.scriptCount >= FX_MAX_SCRIPTS ) {
|
|
Com_Printf( "fx script limit reached" );
|
|
return 0;
|
|
}
|
|
|
|
/* Prepare variables for parsing the text */
|
|
memset( &parse, 0, sizeof( parse ));
|
|
parse.text = entry->text;
|
|
parse.name = entry->name;
|
|
|
|
parseOut.data = parseData;
|
|
parseOut.used = 0;
|
|
parseOut.size = sizeof( parseData );
|
|
|
|
if (!fxParseBlock( &parse, &parseOut ))
|
|
return 0;
|
|
|
|
/* Allocate the script struct and set it up */
|
|
allocSize = parseOut.used + sizeof( fxScript_t );
|
|
allocBuf = fxParseAlloc( allocSize );
|
|
script = (fxScript_t *) allocBuf;
|
|
script->remap = 0;
|
|
script->readMask = parse.readMask;
|
|
Com_Memcpy( &script->data, parseOut.data, parseOut.used );
|
|
entry->script = script;
|
|
script->entry = entry;
|
|
/* Register script in table an increase scriptcount */
|
|
fx.scripts[ fx.scriptCount ] = script;
|
|
script->handle = fx.scriptCount;
|
|
fx.scriptCount++;
|
|
return script;
|
|
}
|
|
|
|
|
|
#define SCAN_ENTRIES 256
|
|
typedef struct fxScanBlock_s {
|
|
fxHashEntry_t entries[SCAN_ENTRIES];
|
|
int entriesUsed;
|
|
struct fxScanBlock_s *next;
|
|
} fxScanBlock_t;
|
|
|
|
static char *scanSkipSpaces( char *p ) {
|
|
for (;*p;p++)
|
|
if ((*p != ' ') && (*p != '\t') && (*p != '\n'))
|
|
break;
|
|
return p;
|
|
}
|
|
/* This will modify the original string */
|
|
static char *scanMakeWord( char **input ) {
|
|
char *ret;
|
|
char *p = *input;
|
|
|
|
p = scanSkipSpaces( p );
|
|
ret = p;
|
|
for (;*p;p++) {
|
|
if (*p == ' ' || *p == '\t' || *p == '\n') {
|
|
*p++ = 0;
|
|
break;
|
|
}
|
|
}
|
|
*input = p;
|
|
return ret;
|
|
}
|
|
|
|
static char *scanMakeSection( char **input ) {
|
|
char *ret;
|
|
char *p = *input;
|
|
int braceDepth = 1;
|
|
|
|
p = scanSkipSpaces( p );
|
|
if ( p[0] != '{')
|
|
return 0;
|
|
ret = p;
|
|
p++;
|
|
for (;*p;p++) {
|
|
if ( p[0] == '{') {
|
|
braceDepth++;
|
|
} else if ( p[0] == '}') {
|
|
braceDepth--;
|
|
if (!braceDepth) {
|
|
if (p[1]) {
|
|
p[1] = 0;
|
|
p+=2;
|
|
} else {
|
|
p += 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if ( braceDepth )
|
|
return 0;
|
|
*input = p;
|
|
return ret;
|
|
}
|
|
|
|
static int scanCopyString( char *dst, const char *src, int left ) {
|
|
int done = 0;
|
|
if ( left <= 0)
|
|
return 0;
|
|
while (left > 1 && src[done]) {
|
|
dst[done] = src[done];
|
|
done++;
|
|
left--;
|
|
}
|
|
dst[done] = 0;
|
|
done++;
|
|
return done;
|
|
}
|
|
|
|
static void fxScanDir( const char *dirName, void **buffers, int *numFiles) {
|
|
int i, dirFiles;
|
|
char **fxFiles;
|
|
|
|
fxFiles = FS_ListFiles( "scripts", ".fx", &dirFiles );
|
|
|
|
// load the files first time
|
|
for ( i = 0; i < dirFiles; i++ ) {
|
|
char filename[MAX_QPATH];
|
|
if (*numFiles >= FX_MAX_SCRIPTS) {
|
|
Com_Error( ERR_DROP, "Too many .fx files" );
|
|
}
|
|
Com_sprintf( filename, sizeof( filename ), "%s/%s", dirName, fxFiles[i] );
|
|
FS_ReadFile( filename, &buffers[numFiles[0]] );
|
|
numFiles[0]++;
|
|
}
|
|
FS_FreeFileList( fxFiles );
|
|
}
|
|
|
|
static void *emptyScript[2];
|
|
static void fxScanFiles( void ) {
|
|
char *buffers[FX_MAX_SCRIPTS];
|
|
int numFiles;
|
|
int allocSize;
|
|
int i;
|
|
fxHashEntry_t *entryWrite;
|
|
int entrySize, nameSize, textSize;
|
|
char *allocBuf, *nameWrite, *textWrite;
|
|
fxScanBlock_t *scanBlock = 0;
|
|
fxHashEntry_t *tempHash[FX_HASH_SIZE];
|
|
|
|
/* Clear some stuff just to be safe */
|
|
fx.alloc.entries = 0;
|
|
memset( fx.scripts, 0, sizeof( fx.scripts ));
|
|
memset( fx.entryHash, 0, sizeof( fx.entryHash ));
|
|
/* Init the default handle for an invalid/empty script*/
|
|
fx.scripts[0] = (fxScript_t *)emptyScript;
|
|
fx.scriptCount = 1;
|
|
// scan for FX files
|
|
Com_Memset( tempHash, 0, sizeof( tempHash ));
|
|
numFiles = 0;
|
|
fxScanDir( "scripts", buffers, &numFiles );
|
|
if ( fx_Override->string[0] ) {
|
|
int i;
|
|
Cmd_TokenizeString( fx_Override->string );
|
|
for( i=0; i<Cmd_Argc();i++) {
|
|
const char *newDir = Cmd_Argv( i );
|
|
fxScanDir( newDir, buffers, &numFiles );
|
|
}
|
|
}
|
|
/* Scan the file and scan the braced scripts */
|
|
for ( i = 0; i < numFiles; i++ )
|
|
{
|
|
char *p = buffers[i];
|
|
COM_Compress( p );
|
|
while (1) {
|
|
char *name = scanMakeWord( &p );
|
|
char *section = scanMakeSection( &p );
|
|
unsigned int hashIndex;
|
|
fxHashEntry_t *entry;
|
|
if (!name || !name[0])
|
|
break;
|
|
if ( !section || !section[0] ) {
|
|
Com_Printf( "Error reading section for %s\n", name);
|
|
break;
|
|
}
|
|
/* Scan the hash for a name we've used before */
|
|
hashIndex = fxGenerateHash( name );
|
|
for ( entry = tempHash[hashIndex]; entry; entry = entry->next ) {
|
|
if ( Q_stricmp( entry->name, name ))
|
|
continue;
|
|
/* Override the current entry */
|
|
entry->name = name;
|
|
entry->text = section;
|
|
break;
|
|
}
|
|
/* Didn't find an entry, add a new one */
|
|
if (!entry) {
|
|
/* Get an entry from the block */
|
|
if ( !scanBlock || scanBlock->entriesUsed == SCAN_ENTRIES) {
|
|
fxScanBlock_t *newBlock = Hunk_AllocateTempMemory( sizeof( fxScanBlock_t ));
|
|
newBlock->entriesUsed = 0;
|
|
newBlock->next = scanBlock;
|
|
scanBlock = newBlock;
|
|
}
|
|
entry = &scanBlock->entries[scanBlock->entriesUsed++];
|
|
entry->name = name;
|
|
entry->text = section;
|
|
entry->next = tempHash[hashIndex];
|
|
tempHash[hashIndex] = entry;
|
|
}
|
|
}
|
|
}
|
|
/* Scan through our hash and determine allocation sizes */
|
|
nameSize = 0; textSize = 0; entrySize = 0;
|
|
allocSize = 0;
|
|
for ( i = 0; i < FX_HASH_SIZE; i++) {
|
|
fxHashEntry_t *entry;
|
|
for (entry = tempHash[i]; entry; entry = entry->next ) {
|
|
entrySize += sizeof( fxHashEntry_t );
|
|
nameSize += strlen( entry->name ) + 1;
|
|
textSize += strlen( entry->text ) + 1;
|
|
}
|
|
}
|
|
/* List of entries in front, then all the names, then all the text */
|
|
allocBuf = (char*) fxParseAlloc( entrySize + nameSize + textSize );
|
|
fx.alloc.entries = (fxHashEntry_t*)allocBuf;
|
|
entryWrite = fx.alloc.entries;
|
|
nameWrite = allocBuf + entrySize;
|
|
textWrite = allocBuf + entrySize + nameSize;
|
|
/* Silly overkill to ensure that names are read linearly, oh well */
|
|
for ( i = 0; i < FX_HASH_SIZE; i++) {
|
|
fxHashEntry_t *entry, *newEntry, *startEntry;
|
|
newEntry = 0;
|
|
startEntry = entryWrite;
|
|
for (entry = tempHash[i]; entry; entry = entry->next ) {
|
|
int len;
|
|
newEntry = entryWrite++;
|
|
|
|
memset( newEntry, 0, sizeof (*newEntry));
|
|
newEntry->next = entryWrite;
|
|
|
|
newEntry->name = nameWrite;
|
|
len = scanCopyString( nameWrite, entry->name, nameSize );
|
|
nameSize -= len; nameWrite += len;
|
|
|
|
newEntry->text = textWrite;
|
|
len = scanCopyString( textWrite, entry->text, textSize );
|
|
textSize -= len; textWrite += len;
|
|
}
|
|
if ( newEntry ) {
|
|
fx.entryHash[i] = startEntry;
|
|
newEntry->next = 0;
|
|
}
|
|
}
|
|
/* Free all allocated memory in the good order hopefully */
|
|
while (scanBlock) {
|
|
fxScanBlock_t *next = scanBlock->next;
|
|
Hunk_FreeTempMemory( scanBlock );
|
|
scanBlock = next;
|
|
}
|
|
for ( i = numFiles-1; i >=0; i-- )
|
|
FS_FreeFile( buffers[i] );
|
|
}
|
|
|
|
static fxScript_t *fxFindScript( const char *name ) {
|
|
unsigned int hashIndex;
|
|
fxHashEntry_t *entry;
|
|
if (!name)
|
|
return 0;
|
|
|
|
hashIndex = fxGenerateHash( name );
|
|
for ( entry = fx.entryHash[ hashIndex ]; entry; entry = entry->next ) {
|
|
if (!Q_stricmp( entry->name, name )) {
|
|
return fxParseScript( entry );
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
fxHandle_t FX_Register( const char *name ) {
|
|
const fxScript_t *script;
|
|
script = fxFindScript( name );
|
|
if ( fx_Debug->integer ) {
|
|
Com_Printf( "FX_Debug:Registering script %s, %s\n", name, script ? "success" : "failed" );
|
|
}
|
|
if ( script )
|
|
return script->handle;
|
|
return 0;
|
|
}
|
|
|
|
static void fxRemap( void ) {
|
|
const char *map1, *map2;
|
|
fxScript_t *script1, *script2;
|
|
|
|
if ( Cmd_Argc() < 1 ) {
|
|
Com_Printf("fxRemap oldScript newScript, will replace oldscript with new one\n" );
|
|
Com_Printf("fxRemap oldScript, will reset the remap\n" );
|
|
return;
|
|
}
|
|
map1 = Cmd_Argv( 1 );
|
|
script1 = fxFindScript( map1 );
|
|
if (!script1) {
|
|
Com_Printf( "fxScript %s not found\n", map1 );
|
|
return;
|
|
}
|
|
if ( Cmd_Argc() > 2) {
|
|
map2 = Cmd_Argv( 2 );
|
|
script2 = fxFindScript( map2 );
|
|
if (!script2) {
|
|
Com_Printf( "fxScript %s not found\n", map2 );
|
|
return;
|
|
}
|
|
script1->remap = script2;
|
|
Com_Printf("Remapped %s to %s", script1->entry->name, script2->entry->name );
|
|
} else {
|
|
Com_Printf("Cleared remapping on script %s", script1->entry->name );
|
|
script1->remap = 0;
|
|
}
|
|
}
|
|
|
|
static void fxReload( void ) {
|
|
fxScript_t *scripts[FX_MAX_SCRIPTS];
|
|
fxHashEntry_t *entries = fx.alloc.entries;
|
|
fxHashEntry_t *entryHash[FX_HASH_SIZE];
|
|
int scriptCount;
|
|
int i;
|
|
|
|
FX_Reset();
|
|
|
|
scriptCount = fx.scriptCount;
|
|
for ( i = 1; i < scriptCount;i++ ) {
|
|
scripts[i] = fx.scripts[i];
|
|
fx.scripts[i] = 0;
|
|
}
|
|
for ( i = 0; i < FX_HASH_SIZE;i++ ) {
|
|
entryHash[i] = fx.entryHash[i];
|
|
fx.entryHash[i] = 0;
|
|
}
|
|
fx.alloc.entries = 0;
|
|
/* Rescan all the files */
|
|
fxScanFiles();
|
|
/* Reload all the scripts in the same order */
|
|
for (i = 1; i < scriptCount;i++ ) {
|
|
int c;
|
|
const char *name = scripts[i]->entry->name;
|
|
fxScript_t *load = fxFindScript( name );
|
|
if ( load && load->handle == i )
|
|
continue;
|
|
if (!load)
|
|
Com_Printf( "Failed to reload scripts %s\n", name );
|
|
else
|
|
Com_Printf( "Script %s doesn't reload in the right handle\n", name );
|
|
|
|
/* Restore the old stuff and free the new stuff */
|
|
for ( c = 1; c < fx.scriptCount; c++ )
|
|
fxParseFree( fx.scripts[c] );
|
|
fxParseFree( fx.alloc.entries );
|
|
fx.scriptCount = scriptCount;
|
|
for ( c = 1; c < scriptCount; c++ )
|
|
fx.scripts[c] = scripts[c];
|
|
for ( c = 0; c< FX_HASH_SIZE; c++ )
|
|
fx.entryHash[c] = entryHash[c];
|
|
fx.alloc.entries = entries;
|
|
return;
|
|
}
|
|
/* Free the old stuff */
|
|
for ( i = 1; i < scriptCount; i++ )
|
|
fxParseFree( scripts[i] );
|
|
fxParseFree( entries );
|
|
}
|
|
|
|
|
|
static void fxMathTest( void ) {
|
|
char line[1024];
|
|
char *lineP = line;
|
|
fxMathOut_t mathOut;
|
|
fxParse_t parse;
|
|
fxRun_t run;
|
|
|
|
memset( &parse, 0, sizeof( parse ));
|
|
memset( &mathOut, 0, sizeof( mathOut ));
|
|
memset( &run, 0, sizeof( run ));
|
|
|
|
Q_strncpyz( line, Cmd_ArgsFrom( 1 ), sizeof( line ) );
|
|
if ( fxMathParse( &parse, mathParseLine, &lineP, 0, &mathOut ) ) {
|
|
fxMathInfo_t info;
|
|
info.data = (int *)mathOut.data;
|
|
info.run = &run;
|
|
Com_Printf( "result %f\n", fxMath( &info) );
|
|
}
|
|
}
|
|
|
|
void fxFreeMemory( void ) {
|
|
int i;
|
|
for ( i = 1; i < fx.scriptCount; i++ ) {
|
|
fxParseFree( fx.scripts[i] );
|
|
fx.scripts[i] = 0;
|
|
}
|
|
fx.scriptCount = 0;
|
|
if ( fx.alloc.entries )
|
|
fxParseFree( fx.alloc.entries );
|
|
fx.alloc.entries = 0;
|
|
}
|
|
|
|
void fxInitParser( void ) {
|
|
fxScanFiles();
|
|
Cmd_AddCommand( "fxReload", fxReload );
|
|
Cmd_AddCommand( "fxRemap", fxRemap );
|
|
Cmd_AddCommand( "fxMath", fxMathTest );
|
|
}
|
|
|